Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static_src/elements/mr-app/mr-app.test.js b/static_src/elements/mr-app/mr-app.test.js
new file mode 100644
index 0000000..47b953b
--- /dev/null
+++ b/static_src/elements/mr-app/mr-app.test.js
@@ -0,0 +1,300 @@
+// 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);
+ });
+ });
+});