blob: 47b953b4122d71324f5a40d5c7f06c37c9dbe71c [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import {assert} from 'chai';
import sinon from 'sinon';
import {MrApp} from './mr-app.js';
import {store, resetState} from 'reducers/base.js';
import {select} from 'reducers/projectV0.js';
let element;
let next;
window.CS_env = {
token: 'foo-token',
};
describe('mr-app', () => {
beforeEach(() => {
global.ga = sinon.spy();
store.dispatch(resetState());
element = document.createElement('mr-app');
document.body.appendChild(element);
element.formsToCheck = [];
next = sinon.stub();
});
afterEach(() => {
global.ga.resetHistory();
document.body.removeChild(element);
next.reset();
});
it('initializes', () => {
assert.instanceOf(element, MrApp);
});
describe('snackbar handling', () => {
beforeEach(() => {
sinon.spy(store, 'dispatch');
});
afterEach(() => {
store.dispatch.restore();
});
it('renders no snackbars', async () => {
element._snackbars = [];
await element.updateComplete;
const snackbars = element.querySelectorAll('chops-snackbar');
assert.equal(snackbars.length, 0);
});
it('renders multiple snackbars', async () => {
element._snackbars = [
{text: 'Snackbar one', id: 'one'},
{text: 'Snackbar two', id: 'two'},
{text: 'Snackbar three', id: 'thre'},
];
await element.updateComplete;
const snackbars = element.querySelectorAll('chops-snackbar');
assert.equal(snackbars.length, 3);
assert.include(snackbars[0].textContent, 'Snackbar one');
assert.include(snackbars[1].textContent, 'Snackbar two');
assert.include(snackbars[2].textContent, 'Snackbar three');
});
it('closing snackbar hides snackbar', async () => {
element._snackbars = [
{text: 'Snackbar', id: 'one'},
];
await element.updateComplete;
const snackbar = element.querySelector('chops-snackbar');
snackbar.close();
sinon.assert.calledWith(store.dispatch,
{type: 'HIDE_SNACKBAR', id: 'one'});
});
});
it('_preRouteHandler calls next()', () => {
const ctx = {params: {}};
element._preRouteHandler(ctx, next);
sinon.assert.calledOnce(next);
});
it('_preRouteHandler does not call next() on same page nav', () => {
element._lastContext = {path: '123'};
const ctx = {params: {}, path: '123'};
element._preRouteHandler(ctx, next);
assert.isFalse(ctx.handled);
sinon.assert.notCalled(next);
});
it('_preRouteHandler parses queryParams', () => {
const ctx = {params: {}, querystring: 'q=owner:me&colspec=Summary'};
element._preRouteHandler(ctx, next);
assert.deepEqual(ctx.queryParams, {q: 'owner:me', colspec: 'Summary'});
});
it('_preRouteHandler ignores case for queryParams keys', () => {
const ctx = {params: {},
querystring: 'Q=owner:me&ColSpeC=Summary&x=owner'};
element._preRouteHandler(ctx, next);
assert.deepEqual(ctx.queryParams, {q: 'owner:me', colspec: 'Summary',
x: 'owner'});
});
it('_preRouteHandler ignores case for queryParams keys', () => {
const ctx = {params: {},
querystring: 'Q=owner:me&ColSpeC=Summary&x=owner'};
element._preRouteHandler(ctx, next);
assert.deepEqual(ctx.queryParams, {q: 'owner:me', colspec: 'Summary',
x: 'owner'});
});
it('_postRouteHandler saves ctx.queryParams to Redux', () => {
const ctx = {queryParams: {q: '1234'}};
element._postRouteHandler(ctx, next);
assert.deepEqual(element.queryParams, {q: '1234'});
});
it('_postRouteHandler saves ctx to this._lastContext', () => {
const ctx = {path: '1234'};
element._postRouteHandler(ctx, next);
assert.deepEqual(element._lastContext, {path: '1234'});
});
describe('scroll to the top on page changes', () => {
beforeEach(() => {
sinon.stub(window, 'scrollTo');
});
afterEach(() => {
window.scrollTo.restore();
});
it('scrolls page to top on initial load', () => {
element._lastContext = null;
const ctx = {params: {}, path: '1234'};
element._postRouteHandler(ctx, next);
sinon.assert.calledWith(window.scrollTo, 0, 0);
});
it('scrolls page to top on parh change', () => {
element._lastContext = {params: {}, pathname: '/list',
path: '/list?q=123', querystring: '?q=123', queryParams: {q: '123'}};
const ctx = {params: {}, pathname: '/other',
path: '/other?q=123', querystring: '?q=123', queryParams: {q: '123'}};
element._postRouteHandler(ctx, next);
sinon.assert.calledWith(window.scrollTo, 0, 0);
});
it('does not scroll to top when on the same path', () => {
element._lastContext = {pathname: '/list', path: '/list?q=123',
querystring: '?a=123', queryParams: {a: '123'}};
const ctx = {pathname: '/list', path: '/list?q=456',
querystring: '?a=456', queryParams: {a: '456'}};
element._postRouteHandler(ctx, next);
sinon.assert.notCalled(window.scrollTo);
});
it('scrolls to the top on same path when q param changes', () => {
element._lastContext = {pathname: '/list', path: '/list?q=123',
querystring: '?q=123', queryParams: {q: '123'}};
const ctx = {pathname: '/list', path: '/list?q=456',
querystring: '?q=456', queryParams: {q: '456'}};
element._postRouteHandler(ctx, next);
sinon.assert.calledWith(window.scrollTo, 0, 0);
});
});
it('_postRouteHandler does not call next', () => {
const ctx = {path: '1234'};
element._postRouteHandler(ctx, next);
sinon.assert.notCalled(next);
});
it('_loadIssuePage loads issue page', async () => {
await element._loadIssuePage({
queryParams: {id: '234'},
params: {project: 'chromium'},
}, next);
await element.updateComplete;
// Check that only one page element is rendering at a time.
const main = element.querySelector('main');
assert.equal(main.children.length, 1);
const issuePage = element.querySelector('mr-issue-page');
assert.isDefined(issuePage, 'issue page is defined');
assert.equal(issuePage.issueRef.projectName, 'chromium');
assert.equal(issuePage.issueRef.localId, 234);
});
it('_loadListPage loads list page', async () => {
await element._loadListPage({
params: {project: 'chromium'},
}, next);
await element.updateComplete;
// Check that only one page element is rendering at a time.
const main = element.querySelector('main');
assert.equal(main.children.length, 1);
const listPage = element.querySelector('mr-list-page');
assert.isDefined(listPage, 'list page is defined');
});
it('_loadListPage loads grid page', async () => {
element.queryParams = {mode: 'grid'};
await element._loadListPage({
params: {project: 'chromium'},
}, next);
await element.updateComplete;
// Check that only one page element is rendering at a time.
const main = element.querySelector('main');
assert.equal(main.children.length, 1);
const gridPage = element.querySelector('mr-grid-page');
assert.isDefined(gridPage, 'grid page is defined');
});
describe('_selectProject', () => {
beforeEach(() => {
sinon.spy(store, 'dispatch');
});
afterEach(() => {
store.dispatch.restore();
});
it('selects and fetches project', () => {
const projectName = 'chromium';
assert.notEqual(store.getState().projectV0.name, projectName);
element._selectProject(projectName);
sinon.assert.calledTwice(store.dispatch);
});
it('skips selecting and fetching when project isn\'t changing', () => {
const projectName = 'chromium';
store.dispatch.restore();
store.dispatch(select(projectName));
sinon.spy(store, 'dispatch');
assert.equal(store.getState().projectV0.name, projectName);
element._selectProject(projectName);
sinon.assert.notCalled(store.dispatch);
});
it('selects without fetching when transitioning to null', () => {
const projectName = 'chromium';
store.dispatch.restore();
store.dispatch(select(projectName));
sinon.spy(store, 'dispatch');
assert.equal(store.getState().projectV0.name, projectName);
element._selectProject(null);
sinon.assert.calledOnce(store.dispatch);
});
});
});