Convert gr-router_test.js to typescript Release-Notes: skip Change-Id: I1bbae1e7c3b1cd43afdb1e36b1e7a44f37b4346e
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts index 6c91333..fa737c1 100644 --- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts +++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -283,7 +283,7 @@ type QueryStringItem = [string, string]; // [key, value] -interface PatchRangeParams { +export interface PatchRangeParams { patchNum?: PatchSetNum; basePatchNum?: BasePatchSetNum; }
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js deleted file mode 100644 index 1115cae..0000000 --- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.js +++ /dev/null
@@ -1,1604 +0,0 @@ -/** - * @license - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import '../../../test/common-test-setup-karma.js'; -import './gr-router.js'; -import {page} from '../../../utils/page-wrapper-utils.js'; -import {GerritNav} from '../gr-navigation/gr-navigation.js'; -import {stubBaseUrl, stubRestApi, addListenerForTest} from '../../../test/test-utils.js'; -import {_testOnly_RoutePattern} from './gr-router.js'; -import {GerritView} from '../../../services/router/router-model.js'; -import {ParentPatchSetNum} from '../../../types/common.js'; - -const basicFixture = fixtureFromElement('gr-router'); - -suite('gr-router tests', () => { - let element; - - setup(() => { - element = basicFixture.instantiate(); - }); - - test('_firstCodeBrowserWeblink', () => { - assert.deepEqual(element._firstCodeBrowserWeblink([ - {name: 'gitweb'}, - {name: 'gitiles'}, - {name: 'browse'}, - {name: 'test'}]), {name: 'gitiles'}); - - assert.deepEqual(element._firstCodeBrowserWeblink([ - {name: 'gitweb'}, - {name: 'test'}]), {name: 'gitweb'}); - }); - - test('_getBrowseCommitWeblink', () => { - const browserLink = {name: 'browser', url: 'browser/url'}; - const link = {name: 'test', url: 'test/url'}; - const weblinks = [browserLink, link]; - const config = {gerrit: {primary_weblink_name: browserLink.name}}; - sinon.stub(element, '_firstCodeBrowserWeblink').returns(link); - - assert.deepEqual(element._getBrowseCommitWeblink(weblinks, config), - browserLink); - - assert.deepEqual(element._getBrowseCommitWeblink(weblinks, {}), link); - }); - - test('_getChangeWeblinks', () => { - const link = {name: 'test', url: 'test/url'}; - const browserLink = {name: 'browser', url: 'browser/url'}; - const mapLinksToConfig = weblinks => { return {options: {weblinks}}; }; - sinon.stub(element, '_getBrowseCommitWeblink').returns(browserLink); - - assert.deepEqual( - element._getChangeWeblinks(mapLinksToConfig([link, browserLink]))[0], - {name: 'test', url: 'test/url'}); - - assert.deepEqual(element._getChangeWeblinks(mapLinksToConfig([link]))[0], - {name: 'test', url: 'test/url'}); - - link.url = 'https://' + link.url; - assert.deepEqual(element._getChangeWeblinks(mapLinksToConfig([link]))[0], - {name: 'test', url: 'https://test/url'}); - }); - - test('_getHashFromCanonicalPath', () => { - let url = '/foo/bar'; - let hash = element._getHashFromCanonicalPath(url); - assert.equal(hash, ''); - - url = ''; - hash = element._getHashFromCanonicalPath(url); - assert.equal(hash, ''); - - url = '/foo#bar'; - hash = element._getHashFromCanonicalPath(url); - assert.equal(hash, 'bar'); - - url = '/foo#bar#baz'; - hash = element._getHashFromCanonicalPath(url); - assert.equal(hash, 'bar#baz'); - - url = '#foo#bar#baz'; - hash = element._getHashFromCanonicalPath(url); - assert.equal(hash, 'foo#bar#baz'); - }); - - suite('_parseLineAddress', () => { - test('returns null for empty and invalid hashes', () => { - let actual = element._parseLineAddress(''); - assert.isNull(actual); - - actual = element._parseLineAddress('foobar'); - assert.isNull(actual); - - actual = element._parseLineAddress('foo123'); - assert.isNull(actual); - - actual = element._parseLineAddress('123bar'); - assert.isNull(actual); - }); - - test('parses correctly', () => { - let actual = element._parseLineAddress('1234'); - assert.isOk(actual); - assert.equal(actual.lineNum, 1234); - assert.isFalse(actual.leftSide); - - actual = element._parseLineAddress('a4'); - assert.isOk(actual); - assert.equal(actual.lineNum, 4); - assert.isTrue(actual.leftSide); - - actual = element._parseLineAddress('b77'); - assert.isOk(actual); - assert.equal(actual.lineNum, 77); - assert.isTrue(actual.leftSide); - }); - }); - - test('_startRouter requires auth for the right handlers', () => { - // This test encodes the lists of route handler methods that gr-router - // automatically checks for authentication before triggering. - - const requiresAuth = {}; - const doesNotRequireAuth = {}; - sinon.stub(GerritNav, 'setup'); - sinon.stub(page, 'start'); - sinon.stub(page, 'base'); - sinon.stub(element, '_mapRoute').callsFake( - (pattern, methodName, usesAuth) => { - if (usesAuth) { - requiresAuth[methodName] = true; - } else { - doesNotRequireAuth[methodName] = true; - } - }); - element._startRouter(); - - const actualRequiresAuth = Object.keys(requiresAuth); - actualRequiresAuth.sort(); - const actualDoesNotRequireAuth = Object.keys(doesNotRequireAuth); - actualDoesNotRequireAuth.sort(); - - const shouldRequireAutoAuth = [ - '_handleAgreementsRoute', - '_handleChangeEditRoute', - '_handleCreateGroupRoute', - '_handleCreateProjectRoute', - '_handleDiffEditRoute', - '_handleGroupAuditLogRoute', - '_handleGroupInfoRoute', - '_handleGroupListFilterOffsetRoute', - '_handleGroupListFilterRoute', - '_handleGroupListOffsetRoute', - '_handleGroupMembersRoute', - '_handleGroupRoute', - '_handleGroupSelfRedirectRoute', - '_handleNewAgreementsRoute', - '_handlePluginListFilterOffsetRoute', - '_handlePluginListFilterRoute', - '_handlePluginListOffsetRoute', - '_handlePluginListRoute', - '_handleRepoCommandsRoute', - '_handleSettingsLegacyRoute', - '_handleSettingsRoute', - ]; - assert.deepEqual(actualRequiresAuth, shouldRequireAutoAuth); - - const unauthenticatedHandlers = [ - '_handleBranchListFilterOffsetRoute', - '_handleBranchListFilterRoute', - '_handleBranchListOffsetRoute', - '_handleChangeIdQueryRoute', - '_handleChangeNumberLegacyRoute', - '_handleChangeRoute', - '_handleCommentRoute', - '_handleCommentsRoute', - '_handleDiffRoute', - '_handleDefaultRoute', - '_handleChangeLegacyRoute', - '_handleDocumentationRedirectRoute', - '_handleDocumentationSearchRoute', - '_handleDocumentationSearchRedirectRoute', - '_handleLegacyLinenum', - '_handleImproperlyEncodedPlusRoute', - '_handlePassThroughRoute', - '_handleProjectDashboardRoute', - '_handleLegacyProjectDashboardRoute', - '_handleProjectsOldRoute', - '_handleRepoAccessRoute', - '_handleRepoDashboardsRoute', - '_handleRepoGeneralRoute', - '_handleRepoListFilterOffsetRoute', - '_handleRepoListFilterRoute', - '_handleRepoListOffsetRoute', - '_handleRepoRoute', - '_handleQueryLegacySuffixRoute', - '_handleQueryRoute', - '_handleRegisterRoute', - '_handleTagListFilterOffsetRoute', - '_handleTagListFilterRoute', - '_handleTagListOffsetRoute', - '_handlePluginScreen', - ]; - - // Handler names that check authentication themselves, and thus don't need - // it performed for them. - const selfAuthenticatingHandlers = [ - '_handleDashboardRoute', - '_handleCustomDashboardRoute', - '_handleRootRoute', - ]; - - const shouldNotRequireAuth = unauthenticatedHandlers - .concat(selfAuthenticatingHandlers); - shouldNotRequireAuth.sort(); - assert.deepEqual(actualDoesNotRequireAuth, shouldNotRequireAuth); - }); - - test('_redirectIfNotLoggedIn while logged in', () => { - stubRestApi('getLoggedIn') - .returns(Promise.resolve(true)); - const data = {canonicalPath: ''}; - const redirectStub = sinon.stub(element, '_redirectToLogin'); - return element._redirectIfNotLoggedIn(data).then(() => { - assert.isFalse(redirectStub.called); - }); - }); - - test('_redirectIfNotLoggedIn while logged out', () => { - stubRestApi('getLoggedIn') - .returns(Promise.resolve(false)); - const redirectStub = sinon.stub(element, '_redirectToLogin'); - const data = {canonicalPath: ''}; - return new Promise(resolve => { - element._redirectIfNotLoggedIn(data) - .then(() => { - assert.isTrue(false, 'Should never execute'); - }) - .catch(() => { - assert.isTrue(redirectStub.calledOnce); - resolve(); - }); - }); - }); - - suite('generateUrl', () => { - test('search', () => { - let params = { - view: GerritNav.View.SEARCH, - owner: 'a%b', - project: 'c%d', - branch: 'e%f', - topic: 'g%h', - statuses: ['op%en'], - }; - assert.equal(element._generateUrl(params), - '/q/owner:a%2525b+project:c%2525d+branch:e%2525f+' + - 'topic:g%2525h+status:op%2525en'); - - params.offset = 100; - assert.equal(element._generateUrl(params), - '/q/owner:a%2525b+project:c%2525d+branch:e%2525f+' + - 'topic:g%2525h+status:op%2525en,100'); - delete params.offset; - - // The presence of the query param overrides other params. - params.query = 'foo$bar'; - assert.equal(element._generateUrl(params), '/q/foo%2524bar'); - - params.offset = 100; - assert.equal(element._generateUrl(params), '/q/foo%2524bar,100'); - - params = { - view: GerritNav.View.SEARCH, - statuses: ['a', 'b', 'c'], - }; - assert.equal(element._generateUrl(params), - '/q/(status:a OR status:b OR status:c)'); - - params = { - view: GerritNav.View.SEARCH, - topic: 'test', - }; - assert.equal(element._generateUrl(params), - '/q/topic:test'); - params = { - view: GerritNav.View.SEARCH, - topic: 'test test', - }; - assert.equal(element._generateUrl(params), - '/q/topic:"test+test"'); - params = { - view: GerritNav.View.SEARCH, - topic: 'test:test', - }; - assert.equal(element._generateUrl(params), '/q/topic:"test:test"'); - }); - - test('change', () => { - const params = { - view: GerritView.CHANGE, - changeNum: '1234', - project: 'test', - }; - - assert.equal(element._generateUrl(params), '/c/test/+/1234'); - - params.patchNum = 10; - assert.equal(element._generateUrl(params), '/c/test/+/1234/10'); - - params.basePatchNum = 5; - assert.equal(element._generateUrl(params), '/c/test/+/1234/5..10'); - - params.messageHash = '#123'; - assert.equal(element._generateUrl(params), '/c/test/+/1234/5..10#123'); - }); - - test('change with repo name encoding', () => { - const params = { - view: GerritView.CHANGE, - changeNum: '1234', - project: 'x+/y+/z+/w', - }; - assert.equal(element._generateUrl(params), - '/c/x%252B/y%252B/z%252B/w/+/1234'); - }); - - test('diff', () => { - const params = { - view: GerritView.DIFF, - changeNum: '42', - path: 'x+y/path.cpp', - patchNum: 12, - }; - assert.equal(element._generateUrl(params), - '/c/42/12/x%252By/path.cpp'); - - params.project = 'test'; - assert.equal(element._generateUrl(params), - '/c/test/+/42/12/x%252By/path.cpp'); - - params.basePatchNum = 6; - assert.equal(element._generateUrl(params), - '/c/test/+/42/6..12/x%252By/path.cpp'); - - params.path = 'foo bar/my+file.txt%'; - params.patchNum = 2; - delete params.basePatchNum; - assert.equal(element._generateUrl(params), - '/c/test/+/42/2/foo+bar/my%252Bfile.txt%2525'); - - params.path = 'file.cpp'; - params.lineNum = 123; - assert.equal(element._generateUrl(params), - '/c/test/+/42/2/file.cpp#123'); - - params.leftSide = true; - assert.equal(element._generateUrl(params), - '/c/test/+/42/2/file.cpp#b123'); - }); - - test('diff with repo name encoding', () => { - const params = { - view: GerritView.DIFF, - changeNum: '42', - path: 'x+y/path.cpp', - patchNum: 12, - project: 'x+/y', - }; - assert.equal(element._generateUrl(params), - '/c/x%252B/y/+/42/12/x%252By/path.cpp'); - }); - - test('edit', () => { - const params = { - view: GerritNav.View.EDIT, - changeNum: '42', - project: 'test', - path: 'x+y/path.cpp', - }; - assert.equal(element._generateUrl(params), - '/c/test/+/42/x%252By/path.cpp,edit'); - }); - - test('_getPatchRangeExpression', () => { - const params = {}; - let actual = element._getPatchRangeExpression(params); - assert.equal(actual, ''); - - params.patchNum = 4; - actual = element._getPatchRangeExpression(params); - assert.equal(actual, '4'); - - params.basePatchNum = 2; - actual = element._getPatchRangeExpression(params); - assert.equal(actual, '2..4'); - - delete params.patchNum; - actual = element._getPatchRangeExpression(params); - assert.equal(actual, '2..'); - }); - - suite('dashboard', () => { - test('self dashboard', () => { - const params = { - view: GerritNav.View.DASHBOARD, - }; - assert.equal(element._generateUrl(params), '/dashboard/self'); - }); - - test('user dashboard', () => { - const params = { - view: GerritNav.View.DASHBOARD, - user: 'user', - }; - assert.equal(element._generateUrl(params), '/dashboard/user'); - }); - - test('custom self dashboard, no title', () => { - const params = { - view: GerritNav.View.DASHBOARD, - sections: [ - {name: 'section 1', query: 'query 1'}, - {name: 'section 2', query: 'query 2'}, - ], - }; - assert.equal( - element._generateUrl(params), - '/dashboard/?section%201=query%201§ion%202=query%202'); - }); - - test('custom repo dashboard', () => { - const params = { - view: GerritNav.View.DASHBOARD, - sections: [ - {name: 'section 1', query: 'query 1 ${project}'}, - {name: 'section 2', query: 'query 2 ${repo}'}, - ], - repo: 'repo-name', - }; - assert.equal( - element._generateUrl(params), - '/dashboard/?section%201=query%201%20repo-name&' + - 'section%202=query%202%20repo-name'); - }); - - test('custom user dashboard, with title', () => { - const params = { - view: GerritNav.View.DASHBOARD, - user: 'user', - sections: [{name: 'name', query: 'query'}], - title: 'custom dashboard', - }; - assert.equal( - element._generateUrl(params), - '/dashboard/user?name=query&title=custom%20dashboard'); - }); - - test('repo dashboard', () => { - const params = { - view: GerritNav.View.DASHBOARD, - repo: 'gerrit/repo', - dashboard: 'default:main', - }; - assert.equal( - element._generateUrl(params), - '/p/gerrit/repo/+/dashboard/default:main'); - }); - - test('project dashboard (legacy)', () => { - const params = { - view: GerritNav.View.DASHBOARD, - project: 'gerrit/project', - dashboard: 'default:main', - }; - assert.equal( - element._generateUrl(params), - '/p/gerrit/project/+/dashboard/default:main'); - }); - }); - - suite('groups', () => { - test('group info', () => { - const params = { - view: GerritNav.View.GROUP, - groupId: 1234, - }; - assert.equal(element._generateUrl(params), '/admin/groups/1234'); - }); - - test('group members', () => { - const params = { - view: GerritNav.View.GROUP, - groupId: 1234, - detail: 'members', - }; - assert.equal(element._generateUrl(params), - '/admin/groups/1234,members'); - }); - - test('group audit log', () => { - const params = { - view: GerritNav.View.GROUP, - groupId: 1234, - detail: 'log', - }; - assert.equal(element._generateUrl(params), - '/admin/groups/1234,audit-log'); - }); - }); - }); - - suite('param normalization', () => { - suite('_normalizePatchRangeParams', () => { - test('range n..n normalizes to n', () => { - const params = {basePatchNum: 4, patchNum: 4}; - const needsRedirect = element._normalizePatchRangeParams(params); - assert.isTrue(needsRedirect); - assert.equal(params.basePatchNum, ParentPatchSetNum); - assert.equal(params.patchNum, 4); - }); - - test('range n.. normalizes to n', () => { - const params = {basePatchNum: 4}; - const needsRedirect = element._normalizePatchRangeParams(params); - assert.isFalse(needsRedirect); - assert.equal(params.basePatchNum, ParentPatchSetNum); - assert.equal(params.patchNum, 4); - }); - }); - }); - - suite('route handlers', () => { - let redirectStub; - let setParamsStub; - let handlePassThroughRoute; - - // Simple route handlers are direct mappings from parsed route data to a - // new set of app.params. This test helper asserts that passing `data` - // into `methodName` results in setting the params specified in `params`. - function assertDataToParams(data, methodName, params) { - element[methodName](data); - assert.deepEqual(setParamsStub.lastCall.args[0], params); - } - - setup(() => { - redirectStub = sinon.stub(element, '_redirect'); - setParamsStub = sinon.stub(element, '_setParams'); - handlePassThroughRoute = sinon.stub(element, '_handlePassThroughRoute'); - }); - - test('_handleLegacyProjectDashboardRoute', () => { - const params = {0: 'gerrit/project', 1: 'dashboard:main'}; - element._handleLegacyProjectDashboardRoute({params}); - assert.isTrue(redirectStub.calledOnce); - assert.equal(redirectStub.lastCall.args[0], - '/p/gerrit/project/+/dashboard/dashboard:main'); - }); - - test('_handleAgreementsRoute', () => { - const data = {params: {}}; - element._handleAgreementsRoute(data); - assert.isTrue(redirectStub.calledOnce); - assert.equal(redirectStub.lastCall.args[0], '/settings/#Agreements'); - }); - - test('_handleNewAgreementsRoute', () => { - element._handleNewAgreementsRoute({params: {}}); - assert.isTrue(setParamsStub.calledOnce); - assert.equal(setParamsStub.lastCall.args[0].view, - GerritNav.View.AGREEMENTS); - }); - - test('_handleSettingsLegacyRoute', () => { - const data = {params: {0: 'my-token'}}; - assertDataToParams(data, '_handleSettingsLegacyRoute', { - view: GerritNav.View.SETTINGS, - emailToken: 'my-token', - }); - }); - - test('_handleSettingsLegacyRoute with +', () => { - const data = {params: {0: 'my-token test'}}; - assertDataToParams(data, '_handleSettingsLegacyRoute', { - view: GerritNav.View.SETTINGS, - emailToken: 'my-token+test', - }); - }); - - test('_handleSettingsRoute', () => { - const data = {}; - assertDataToParams(data, '_handleSettingsRoute', { - view: GerritNav.View.SETTINGS, - }); - }); - - test('_handleDefaultRoute on first load', () => { - const spy = sinon.spy(); - addListenerForTest(document, 'page-error', spy); - element._handleDefaultRoute(); - assert.isTrue(spy.calledOnce); - assert.equal(spy.lastCall.args[0].detail.response.status, 404); - }); - - test('_handleDefaultRoute after internal navigation', () => { - let onExit = null; - const onRegisteringExit = (match, _onExit) => { - onExit = _onExit; - }; - sinon.stub(page, 'exit').callsFake( onRegisteringExit); - sinon.stub(GerritNav, 'setup'); - sinon.stub(page, 'start'); - sinon.stub(page, 'base'); - element._startRouter(); - - element._handleDefaultRoute(); - - onExit('', () => {}); // we left page; - - element._handleDefaultRoute(); - assert.isTrue(handlePassThroughRoute.calledOnce); - }); - - test('_handleImproperlyEncodedPlusRoute', () => { - // Regression test for Issue 7100. - element._handleImproperlyEncodedPlusRoute( - {canonicalPath: '/c/test/%20/42', params: ['test', '42']}); - assert.isTrue(redirectStub.calledOnce); - assert.equal( - redirectStub.lastCall.args[0], - '/c/test/+/42'); - - sinon.stub(element, '_getHashFromCanonicalPath').returns('foo'); - element._handleImproperlyEncodedPlusRoute( - {canonicalPath: '/c/test/%20/42', params: ['test', '42']}); - assert.equal( - redirectStub.lastCall.args[0], - '/c/test/+/42#foo'); - }); - - test('_handleQueryRoute', () => { - const data = {params: ['project:foo/bar/baz']}; - assertDataToParams(data, '_handleQueryRoute', { - view: GerritNav.View.SEARCH, - query: 'project:foo/bar/baz', - offset: undefined, - }); - - data.params.push(',123', '123'); - assertDataToParams(data, '_handleQueryRoute', { - view: GerritNav.View.SEARCH, - query: 'project:foo/bar/baz', - offset: '123', - }); - }); - - test('_handleQueryLegacySuffixRoute', () => { - element._handleQueryLegacySuffixRoute({path: '/q/foo+bar,n,z'}); - assert.isTrue(redirectStub.calledOnce); - assert.equal(redirectStub.lastCall.args[0], '/q/foo+bar'); - }); - - test('_handleChangeIdQueryRoute', () => { - const data = {params: ['I0123456789abcdef0123456789abcdef01234567']}; - assertDataToParams(data, '_handleChangeIdQueryRoute', { - view: GerritNav.View.SEARCH, - query: 'I0123456789abcdef0123456789abcdef01234567', - }); - }); - - suite('_handleRegisterRoute', () => { - test('happy path', () => { - const ctx = {params: ['/foo/bar']}; - element._handleRegisterRoute(ctx); - assert.isTrue(redirectStub.calledWithExactly('/foo/bar')); - assert.isTrue(setParamsStub.calledOnce); - assert.isTrue(setParamsStub.lastCall.args[0].justRegistered); - }); - - test('no param', () => { - const ctx = {params: ['']}; - element._handleRegisterRoute(ctx); - assert.isTrue(redirectStub.calledWithExactly('/')); - assert.isTrue(setParamsStub.calledOnce); - assert.isTrue(setParamsStub.lastCall.args[0].justRegistered); - }); - - test('prevent redirect', () => { - const ctx = {params: ['/register']}; - element._handleRegisterRoute(ctx); - assert.isTrue(redirectStub.calledWithExactly('/')); - assert.isTrue(setParamsStub.calledOnce); - assert.isTrue(setParamsStub.lastCall.args[0].justRegistered); - }); - }); - - suite('_handleRootRoute', () => { - test('closes for closeAfterLogin', () => { - const data = {querystring: 'closeAfterLogin', canonicalPath: ''}; - const closeStub = sinon.stub(window, 'close'); - const result = element._handleRootRoute(data); - assert.isNotOk(result); - assert.isTrue(closeStub.called); - assert.isFalse(redirectStub.called); - }); - - test('redirects to dashboard if logged in', () => { - const data = { - canonicalPath: '/', path: '/', querystring: '', hash: '', - }; - const result = element._handleRootRoute(data); - assert.isOk(result); - return result.then(() => { - assert.isTrue(redirectStub.calledWithExactly('/dashboard/self')); - }); - }); - - test('redirects to open changes if not logged in', () => { - stubRestApi('getLoggedIn').returns(Promise.resolve(false)); - const data = { - canonicalPath: '/', path: '/', querystring: '', hash: '', - }; - const result = element._handleRootRoute(data); - assert.isOk(result); - return result.then(() => { - assert.isTrue( - redirectStub.calledWithExactly('/q/status:open+-is:wip')); - }); - }); - - suite('GWT hash-path URLs', () => { - test('redirects hash-path URLs', () => { - const data = { - canonicalPath: '/#/foo/bar/baz', - hash: '/foo/bar/baz', - querystring: '', - }; - const result = element._handleRootRoute(data); - assert.isNotOk(result); - assert.isTrue(redirectStub.called); - assert.isTrue(redirectStub.calledWithExactly('/foo/bar/baz')); - }); - - test('redirects hash-path URLs w/o leading slash', () => { - const data = { - canonicalPath: '/#foo/bar/baz', - querystring: '', - hash: 'foo/bar/baz', - }; - const result = element._handleRootRoute(data); - assert.isNotOk(result); - assert.isTrue(redirectStub.called); - assert.isTrue(redirectStub.calledWithExactly('/foo/bar/baz')); - }); - - test('normalizes "/ /" in hash to "/+/"', () => { - const data = { - canonicalPath: '/#/foo/bar/+/123/4', - querystring: '', - hash: '/foo/bar/ /123/4', - }; - const result = element._handleRootRoute(data); - assert.isNotOk(result); - assert.isTrue(redirectStub.called); - assert.isTrue(redirectStub.calledWithExactly('/foo/bar/+/123/4')); - }); - - test('prepends baseurl to hash-path', () => { - const data = { - canonicalPath: '/#/foo/bar', - querystring: '', - hash: '/foo/bar', - }; - stubBaseUrl('/baz'); - const result = element._handleRootRoute(data); - assert.isNotOk(result); - assert.isTrue(redirectStub.called); - assert.isTrue(redirectStub.calledWithExactly('/baz/foo/bar')); - }); - - test('normalizes /VE/ settings hash-paths', () => { - const data = { - canonicalPath: '/#/VE/foo/bar', - querystring: '', - hash: '/VE/foo/bar', - }; - const result = element._handleRootRoute(data); - assert.isNotOk(result); - assert.isTrue(redirectStub.called); - assert.isTrue(redirectStub.calledWithExactly( - '/settings/VE/foo/bar')); - }); - - test('does not drop "inner hashes"', () => { - const data = { - canonicalPath: '/#/foo/bar#baz', - querystring: '', - hash: '/foo/bar', - }; - const result = element._handleRootRoute(data); - assert.isNotOk(result); - assert.isTrue(redirectStub.called); - assert.isTrue(redirectStub.calledWithExactly('/foo/bar#baz')); - }); - }); - }); - - suite('_handleDashboardRoute', () => { - let redirectToLoginStub; - - setup(() => { - redirectToLoginStub = sinon.stub(element, '_redirectToLogin'); - }); - - test('own dashboard but signed out redirects to login', () => { - stubRestApi('getLoggedIn').returns(Promise.resolve(false)); - const data = {canonicalPath: '/dashboard/', params: {0: 'seLF'}}; - return element._handleDashboardRoute(data, '').then(() => { - assert.isTrue(redirectToLoginStub.calledOnce); - assert.isFalse(redirectStub.called); - assert.isFalse(setParamsStub.called); - }); - }); - - test('non-self dashboard but signed out does not redirect', () => { - stubRestApi('getLoggedIn').returns(Promise.resolve(false)); - const data = {canonicalPath: '/dashboard/', params: {0: 'foo'}}; - return element._handleDashboardRoute(data, '').then(() => { - assert.isFalse(redirectToLoginStub.called); - assert.isFalse(setParamsStub.called); - assert.isTrue(redirectStub.calledOnce); - assert.equal(redirectStub.lastCall.args[0], '/q/owner:foo'); - }); - }); - - test('dashboard while signed in sets params', () => { - const data = {canonicalPath: '/dashboard/', params: {0: 'foo'}}; - return element._handleDashboardRoute(data, '').then(() => { - assert.isFalse(redirectToLoginStub.called); - assert.isFalse(redirectStub.called); - assert.isTrue(setParamsStub.calledOnce); - assert.deepEqual(setParamsStub.lastCall.args[0], { - view: GerritNav.View.DASHBOARD, - user: 'foo', - }); - }); - }); - }); - - suite('_handleCustomDashboardRoute', () => { - let redirectToLoginStub; - - setup(() => { - redirectToLoginStub = sinon.stub(element, '_redirectToLogin'); - }); - - test('no user specified', () => { - const data = {canonicalPath: '/dashboard/', params: {0: ''}}; - return element._handleCustomDashboardRoute(data, '').then(() => { - assert.isFalse(setParamsStub.called); - assert.isTrue(redirectStub.called); - assert.equal(redirectStub.lastCall.args[0], '/dashboard/self'); - }); - }); - - test('custom dashboard without title', () => { - const data = {canonicalPath: '/dashboard/', params: {0: ''}}; - return element._handleCustomDashboardRoute(data, '?a=b&c&d=e') - .then(() => { - assert.isFalse(redirectStub.called); - assert.isTrue(setParamsStub.calledOnce); - assert.deepEqual(setParamsStub.lastCall.args[0], { - view: GerritNav.View.DASHBOARD, - user: 'self', - sections: [ - {name: 'a', query: 'b'}, - {name: 'd', query: 'e'}, - ], - title: 'Custom Dashboard', - }); - }); - }); - - test('custom dashboard with title', () => { - const data = {canonicalPath: '/dashboard/', params: {0: ''}}; - return element._handleCustomDashboardRoute(data, - '?a=b&c&d=&=e&title=t') - .then(() => { - assert.isFalse(redirectToLoginStub.called); - assert.isFalse(redirectStub.called); - assert.isTrue(setParamsStub.calledOnce); - assert.deepEqual(setParamsStub.lastCall.args[0], { - view: GerritNav.View.DASHBOARD, - user: 'self', - sections: [ - {name: 'a', query: 'b'}, - ], - title: 't', - }); - }); - }); - - test('custom dashboard with foreach', () => { - const data = {canonicalPath: '/dashboard/', params: {0: ''}}; - return element._handleCustomDashboardRoute(data, - '?a=b&c&d=&=e&foreach=is:open') - .then(() => { - assert.isFalse(redirectToLoginStub.called); - assert.isFalse(redirectStub.called); - assert.isTrue(setParamsStub.calledOnce); - assert.deepEqual(setParamsStub.lastCall.args[0], { - view: GerritNav.View.DASHBOARD, - user: 'self', - sections: [ - {name: 'a', query: 'is:open b'}, - ], - title: 'Custom Dashboard', - }); - }); - }); - }); - - suite('group routes', () => { - test('_handleGroupInfoRoute', () => { - const data = {params: {0: 1234}}; - element._handleGroupInfoRoute(data); - assert.isTrue(redirectStub.calledOnce); - assert.equal(redirectStub.lastCall.args[0], '/admin/groups/1234'); - }); - - test('_handleGroupAuditLogRoute', () => { - const data = {params: {0: 1234}}; - assertDataToParams(data, '_handleGroupAuditLogRoute', { - view: GerritNav.View.GROUP, - detail: 'log', - groupId: 1234, - }); - }); - - test('_handleGroupMembersRoute', () => { - const data = {params: {0: 1234}}; - assertDataToParams(data, '_handleGroupMembersRoute', { - view: GerritNav.View.GROUP, - detail: 'members', - groupId: 1234, - }); - }); - - test('_handleGroupListOffsetRoute', () => { - const data = {params: {}}; - assertDataToParams(data, '_handleGroupListOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-admin-group-list', - offset: 0, - filter: null, - openCreateModal: false, - }); - - data.params[1] = 42; - assertDataToParams(data, '_handleGroupListOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-admin-group-list', - offset: 42, - filter: null, - openCreateModal: false, - }); - - data.hash = 'create'; - assertDataToParams(data, '_handleGroupListOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-admin-group-list', - offset: 42, - filter: null, - openCreateModal: true, - }); - }); - - test('_handleGroupListFilterOffsetRoute', () => { - const data = {params: {filter: 'foo', offset: 42}}; - assertDataToParams(data, '_handleGroupListFilterOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-admin-group-list', - offset: 42, - filter: 'foo', - }); - }); - - test('_handleGroupListFilterRoute', () => { - const data = {params: {filter: 'foo'}}; - assertDataToParams(data, '_handleGroupListFilterRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-admin-group-list', - filter: 'foo', - }); - }); - - test('_handleGroupRoute', () => { - const data = {params: {0: 4321}}; - assertDataToParams(data, '_handleGroupRoute', { - view: GerritNav.View.GROUP, - groupId: 4321, - }); - }); - }); - - suite('repo routes', () => { - test('_handleProjectsOldRoute', () => { - const data = {params: {}}; - element._handleProjectsOldRoute(data); - assert.isTrue(redirectStub.calledOnce); - assert.equal(redirectStub.lastCall.args[0], '/admin/repos/'); - }); - - test('_handleProjectsOldRoute test', () => { - const data = {params: {1: 'test'}}; - element._handleProjectsOldRoute(data); - assert.isTrue(redirectStub.calledOnce); - assert.equal(redirectStub.lastCall.args[0], '/admin/repos/test'); - }); - - test('_handleProjectsOldRoute test,branches', () => { - const data = {params: {1: 'test,branches'}}; - element._handleProjectsOldRoute(data); - assert.isTrue(redirectStub.calledOnce); - assert.equal( - redirectStub.lastCall.args[0], '/admin/repos/test,branches'); - }); - - test('_handleRepoRoute', () => { - const data = {path: '/admin/repos/test'}; - element._handleRepoRoute(data); - assert.isTrue(redirectStub.calledOnce); - assert.equal( - redirectStub.lastCall.args[0], '/admin/repos/test,general'); - }); - - test('_handleRepoGeneralRoute', () => { - const data = {params: {0: 4321}}; - assertDataToParams(data, '_handleRepoGeneralRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.GENERAL, - repo: 4321, - }); - }); - - test('_handleRepoCommandsRoute', () => { - const data = {params: {0: 4321}}; - assertDataToParams(data, '_handleRepoCommandsRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.COMMANDS, - repo: 4321, - }); - }); - - test('_handleRepoAccessRoute', () => { - const data = {params: {0: 4321}}; - assertDataToParams(data, '_handleRepoAccessRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.ACCESS, - repo: 4321, - }); - }); - - suite('branch list routes', () => { - test('_handleBranchListOffsetRoute', () => { - const data = {params: {0: 4321}}; - assertDataToParams(data, '_handleBranchListOffsetRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.BRANCHES, - repo: 4321, - offset: 0, - filter: null, - }); - - data.params[2] = 42; - assertDataToParams(data, '_handleBranchListOffsetRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.BRANCHES, - repo: 4321, - offset: 42, - filter: null, - }); - }); - - test('_handleBranchListFilterOffsetRoute', () => { - const data = {params: {repo: 4321, filter: 'foo', offset: 42}}; - assertDataToParams(data, '_handleBranchListFilterOffsetRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.BRANCHES, - repo: 4321, - offset: 42, - filter: 'foo', - }); - }); - - test('_handleBranchListFilterRoute', () => { - const data = {params: {repo: 4321, filter: 'foo'}}; - assertDataToParams(data, '_handleBranchListFilterRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.BRANCHES, - repo: 4321, - filter: 'foo', - }); - }); - }); - - suite('tag list routes', () => { - test('_handleTagListOffsetRoute', () => { - const data = {params: {0: 4321}}; - assertDataToParams(data, '_handleTagListOffsetRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.TAGS, - repo: 4321, - offset: 0, - filter: null, - }); - }); - - test('_handleTagListFilterOffsetRoute', () => { - const data = {params: {repo: 4321, filter: 'foo', offset: 42}}; - assertDataToParams(data, '_handleTagListFilterOffsetRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.TAGS, - repo: 4321, - offset: 42, - filter: 'foo', - }); - }); - - test('_handleTagListFilterRoute', () => { - const data = {params: {repo: 4321}}; - assertDataToParams(data, '_handleTagListFilterRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.TAGS, - repo: 4321, - filter: null, - }); - - data.params.filter = 'foo'; - assertDataToParams(data, '_handleTagListFilterRoute', { - view: GerritNav.View.REPO, - detail: GerritNav.RepoDetailView.TAGS, - repo: 4321, - filter: 'foo', - }); - }); - }); - - suite('repo list routes', () => { - test('_handleRepoListOffsetRoute', () => { - const data = {params: {}}; - assertDataToParams(data, '_handleRepoListOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-repo-list', - offset: 0, - filter: null, - openCreateModal: false, - }); - - data.params[1] = 42; - assertDataToParams(data, '_handleRepoListOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-repo-list', - offset: 42, - filter: null, - openCreateModal: false, - }); - - data.hash = 'create'; - assertDataToParams(data, '_handleRepoListOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-repo-list', - offset: 42, - filter: null, - openCreateModal: true, - }); - }); - - test('_handleRepoListFilterOffsetRoute', () => { - const data = {params: {filter: 'foo', offset: 42}}; - assertDataToParams(data, '_handleRepoListFilterOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-repo-list', - offset: 42, - filter: 'foo', - }); - }); - - test('_handleRepoListFilterRoute', () => { - const data = {params: {}}; - assertDataToParams(data, '_handleRepoListFilterRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-repo-list', - filter: null, - }); - - data.params.filter = 'foo'; - assertDataToParams(data, '_handleRepoListFilterRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-repo-list', - filter: 'foo', - }); - }); - }); - }); - - suite('plugin routes', () => { - test('_handlePluginListOffsetRoute', () => { - const data = {params: {}}; - assertDataToParams(data, '_handlePluginListOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-plugin-list', - offset: 0, - filter: null, - }); - - data.params[1] = 42; - assertDataToParams(data, '_handlePluginListOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-plugin-list', - offset: 42, - filter: null, - }); - }); - - test('_handlePluginListFilterOffsetRoute', () => { - const data = {params: {filter: 'foo', offset: 42}}; - assertDataToParams(data, '_handlePluginListFilterOffsetRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-plugin-list', - offset: 42, - filter: 'foo', - }); - }); - - test('_handlePluginListFilterRoute', () => { - const data = {params: {}}; - assertDataToParams(data, '_handlePluginListFilterRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-plugin-list', - filter: null, - }); - - data.params.filter = 'foo'; - assertDataToParams(data, '_handlePluginListFilterRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-plugin-list', - filter: 'foo', - }); - }); - - test('_handlePluginListRoute', () => { - const data = {params: {}}; - assertDataToParams(data, '_handlePluginListRoute', { - view: GerritNav.View.ADMIN, - adminView: 'gr-plugin-list', - }); - }); - }); - - suite('change/diff routes', () => { - test('_handleChangeNumberLegacyRoute', () => { - const data = {params: {0: 12345}}; - element._handleChangeNumberLegacyRoute(data); - assert.isTrue(redirectStub.calledOnce); - assert.isTrue(redirectStub.calledWithExactly('/c/12345')); - }); - - test('_handleChangeLegacyRoute', async () => { - stubRestApi('getFromProjectLookup').returns(Promise.resolve('project')); - const ctx = { - params: [ - 1234, // 0 Change number - 'comment/6789', - ], - querystring: '', - }; - element._handleChangeLegacyRoute(ctx); - await flush(); - assert.isTrue(redirectStub.calledWithExactly('/c/project/+/1234' + - '/comment/6789')); - }); - - test('_handleLegacyLinenum w/ @321', () => { - const ctx = {path: '/c/1234/3..8/foo/bar@321'}; - element._handleLegacyLinenum(ctx); - assert.isTrue(redirectStub.calledOnce); - assert.isTrue(redirectStub.calledWithExactly( - '/c/1234/3..8/foo/bar#321')); - }); - - test('_handleLegacyLinenum w/ @b123', () => { - const ctx = {path: '/c/1234/3..8/foo/bar@b123'}; - element._handleLegacyLinenum(ctx); - assert.isTrue(redirectStub.calledOnce); - assert.isTrue(redirectStub.calledWithExactly( - '/c/1234/3..8/foo/bar#b123')); - }); - - suite('_handleChangeRoute', () => { - let normalizeRangeStub; - - function makeParams(path, hash) { - return { - params: [ - 'foo/bar', // 0 Project - 1234, // 1 Change number - null, // 2 Unused - null, // 3 Unused - 4, // 4 Base patch number - null, // 5 Unused - 7, // 6 Patch number - ], - queryMap: new Map(), - }; - } - - setup(() => { - normalizeRangeStub = sinon.stub(element, - '_normalizePatchRangeParams'); - stubRestApi('setInProjectLookup'); - }); - - test('needs redirect', () => { - normalizeRangeStub.returns(true); - sinon.stub(element, '_generateUrl').returns('foo'); - const ctx = makeParams(null, ''); - element._handleChangeRoute(ctx); - assert.isTrue(normalizeRangeStub.called); - assert.isFalse(setParamsStub.called); - assert.isTrue(redirectStub.calledOnce); - assert.isTrue(redirectStub.calledWithExactly('foo')); - }); - - test('change view', () => { - normalizeRangeStub.returns(false); - sinon.stub(element, '_generateUrl').returns('foo'); - const ctx = makeParams(null, ''); - assertDataToParams(ctx, '_handleChangeRoute', { - view: GerritView.CHANGE, - project: 'foo/bar', - changeNum: 1234, - basePatchNum: 4, - patchNum: 7, - }); - assert.isFalse(redirectStub.called); - assert.isTrue(normalizeRangeStub.called); - }); - - test('params', () => { - normalizeRangeStub.returns(false); - sinon.stub(element, '_generateUrl').returns('foo'); - const ctx = makeParams(null, ''); - ctx.queryMap.set('tab', 'checks'); - ctx.queryMap.set('filter', 'fff'); - ctx.queryMap.set('select', 'sss'); - ctx.queryMap.set('attempt', '1'); - assertDataToParams(ctx, '_handleChangeRoute', { - view: GerritView.CHANGE, - project: 'foo/bar', - changeNum: 1234, - basePatchNum: 4, - patchNum: 7, - attempt: 1, - filter: 'fff', - select: 'sss', - tab: 'checks', - }); - }); - }); - - suite('_handleDiffRoute', () => { - let normalizeRangeStub; - - function makeParams(path, hash) { - return { - params: [ - 'foo/bar', // 0 Project - 1234, // 1 Change number - null, // 2 Unused - null, // 3 Unused - 4, // 4 Base patch number - null, // 5 Unused - 7, // 6 Patch number - null, // 7 Unused, - path, // 8 Diff path - ], - hash, - }; - } - - setup(() => { - normalizeRangeStub = sinon.stub(element, - '_normalizePatchRangeParams'); - stubRestApi('setInProjectLookup'); - }); - - test('needs redirect', () => { - normalizeRangeStub.returns(true); - sinon.stub(element, '_generateUrl').returns('foo'); - const ctx = makeParams(null, ''); - element._handleDiffRoute(ctx); - assert.isTrue(normalizeRangeStub.called); - assert.isFalse(setParamsStub.called); - assert.isTrue(redirectStub.calledOnce); - assert.isTrue(redirectStub.calledWithExactly('foo')); - }); - - test('diff view', () => { - normalizeRangeStub.returns(false); - sinon.stub(element, '_generateUrl').returns('foo'); - const ctx = makeParams('foo/bar/baz', 'b44'); - assertDataToParams(ctx, '_handleDiffRoute', { - view: GerritView.DIFF, - project: 'foo/bar', - changeNum: 1234, - basePatchNum: 4, - patchNum: 7, - path: 'foo/bar/baz', - leftSide: true, - lineNum: 44, - }); - assert.isFalse(redirectStub.called); - assert.isTrue(normalizeRangeStub.called); - }); - - test('comment route', () => { - const url = '/c/gerrit/+/264833/comment/00049681_f34fd6a9/'; - const groups = url.match(_testOnly_RoutePattern.COMMENT); - assert.deepEqual(groups.slice(1), [ - 'gerrit', // project - '264833', // changeNum - '00049681_f34fd6a9', // commentId - ]); - assertDataToParams({params: groups.slice(1)}, '_handleCommentRoute', { - project: 'gerrit', - changeNum: 264833, - commentId: '00049681_f34fd6a9', - commentLink: true, - view: GerritView.DIFF, - }); - }); - - test('comments route', () => { - const url = '/c/gerrit/+/264833/comments/00049681_f34fd6a9/'; - const groups = url.match(_testOnly_RoutePattern.COMMENTS_TAB); - assert.deepEqual(groups.slice(1), [ - 'gerrit', // project - '264833', // changeNum - '00049681_f34fd6a9', // commentId - ]); - assertDataToParams({params: groups.slice(1)}, - '_handleCommentsRoute', { - project: 'gerrit', - changeNum: 264833, - commentId: '00049681_f34fd6a9', - view: GerritView.CHANGE, - }); - }); - }); - - test('_handleDiffEditRoute', () => { - const normalizeRangeSpy = - sinon.spy(element, '_normalizePatchRangeParams'); - stubRestApi('setInProjectLookup'); - const ctx = { - params: [ - 'foo/bar', // 0 Project - 1234, // 1 Change number - 3, // 2 Patch num - 'foo/bar/baz', // 3 File path - ], - }; - const appParams = { - project: 'foo/bar', - changeNum: 1234, - view: GerritNav.View.EDIT, - path: 'foo/bar/baz', - patchNum: 3, - lineNum: undefined, - }; - - element._handleDiffEditRoute(ctx); - assert.isFalse(redirectStub.called); - assert.isTrue(normalizeRangeSpy.calledOnce); - assert.deepEqual(normalizeRangeSpy.lastCall.args[0], appParams); - assert.isFalse(normalizeRangeSpy.lastCall.returnValue); - assert.deepEqual(setParamsStub.lastCall.args[0], appParams); - }); - - test('_handleDiffEditRoute with lineNum', () => { - const normalizeRangeSpy = - sinon.spy(element, '_normalizePatchRangeParams'); - stubRestApi('setInProjectLookup'); - const ctx = { - params: [ - 'foo/bar', // 0 Project - 1234, // 1 Change number - 3, // 2 Patch num - 'foo/bar/baz', // 3 File path - ], - hash: 4, - }; - const appParams = { - project: 'foo/bar', - changeNum: 1234, - view: GerritNav.View.EDIT, - path: 'foo/bar/baz', - patchNum: 3, - lineNum: 4, - }; - - element._handleDiffEditRoute(ctx); - assert.isFalse(redirectStub.called); - assert.isTrue(normalizeRangeSpy.calledOnce); - assert.deepEqual(normalizeRangeSpy.lastCall.args[0], appParams); - assert.isFalse(normalizeRangeSpy.lastCall.returnValue); - assert.deepEqual(setParamsStub.lastCall.args[0], appParams); - }); - - test('_handleChangeEditRoute', () => { - const normalizeRangeSpy = - sinon.spy(element, '_normalizePatchRangeParams'); - stubRestApi('setInProjectLookup'); - const ctx = { - params: [ - 'foo/bar', // 0 Project - 1234, // 1 Change number - null, - 3, // 3 Patch num - ], - queryMap: new Map(), - }; - const appParams = { - project: 'foo/bar', - changeNum: 1234, - view: GerritView.CHANGE, - patchNum: 3, - edit: true, - tab: '', - }; - - element._handleChangeEditRoute(ctx); - assert.isFalse(redirectStub.called); - assert.isTrue(normalizeRangeSpy.calledOnce); - assert.deepEqual(normalizeRangeSpy.lastCall.args[0], appParams); - assert.isFalse(normalizeRangeSpy.lastCall.returnValue); - assert.deepEqual(setParamsStub.lastCall.args[0], appParams); - }); - }); - - test('_handlePluginScreen', () => { - const ctx = {params: ['foo', 'bar']}; - assertDataToParams(ctx, '_handlePluginScreen', { - view: GerritNav.View.PLUGIN_SCREEN, - plugin: 'foo', - screen: 'bar', - }); - assert.isFalse(redirectStub.called); - }); - }); - - suite('_parseQueryString', () => { - test('empty queries', () => { - assert.deepEqual(element._parseQueryString(''), []); - assert.deepEqual(element._parseQueryString('?'), []); - assert.deepEqual(element._parseQueryString('??'), []); - assert.deepEqual(element._parseQueryString('&&&'), []); - }); - - test('url decoding', () => { - assert.deepEqual(element._parseQueryString('+'), [[' ', '']]); - assert.deepEqual(element._parseQueryString('???+%3d+'), [[' = ', '']]); - assert.deepEqual( - element._parseQueryString('%6e%61%6d%65=%76%61%6c%75%65'), - [['name', 'value']]); - }); - - test('multiple parameters', () => { - assert.deepEqual( - element._parseQueryString('a=b&c=d&e=f'), - [['a', 'b'], ['c', 'd'], ['e', 'f']]); - assert.deepEqual( - element._parseQueryString('&a=b&&&e=f&c'), - [['a', 'b'], ['e', 'f'], ['c', '']]); - }); - }); -}); -
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts new file mode 100644 index 0000000..0b9921c --- /dev/null +++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
@@ -0,0 +1,1837 @@ +/** + * @license + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import '../../../test/common-test-setup-karma'; +import './gr-router'; +import {page} from '../../../utils/page-wrapper-utils'; +import { + GenerateUrlChangeViewParameters, + GenerateUrlDashboardViewParameters, + GenerateUrlDiffViewParameters, + GenerateUrlEditViewParameters, + GenerateUrlGroupViewParameters, + GenerateUrlParameters, + GenerateUrlSearchViewParameters, + GerritNav, + GroupDetailView, + WeblinkType, +} from '../gr-navigation/gr-navigation'; +import { + stubBaseUrl, + stubRestApi, + addListenerForTest, +} from '../../../test/test-utils'; +import { + GrRouter, + PageContextWithQueryMap, + PatchRangeParams, + _testOnly_RoutePattern, +} from './gr-router'; +import {GerritView} from '../../../services/router/router-model'; +import { + BasePatchSetNum, + BranchName, + CommitId, + DashboardId, + GroupId, + NumericChangeId, + ParentPatchSetNum, + PatchSetNum, + RepoName, + RevisionPatchSetNum, + TopicName, + UrlEncodedCommentId, + WebLinkInfo, +} from '../../../types/common'; +import { + createGerritInfo, + createServerInfo, +} from '../../../test/test-data-generators'; +import {AppElementParams} from '../../gr-app-types'; + +const basicFixture = fixtureFromElement('gr-router'); + +suite('gr-router tests', () => { + let element: GrRouter; + + setup(() => { + element = basicFixture.instantiate(); + }); + + test('_firstCodeBrowserWeblink', () => { + assert.deepEqual( + element._firstCodeBrowserWeblink([ + {name: 'gitweb'}, + {name: 'gitiles'}, + {name: 'browse'}, + {name: 'test'}, + ]), + {name: 'gitiles'} + ); + + assert.deepEqual( + element._firstCodeBrowserWeblink([{name: 'gitweb'}, {name: 'test'}]), + {name: 'gitweb'} + ); + }); + + test('_getBrowseCommitWeblink', () => { + const browserLink = {name: 'browser', url: 'browser/url'}; + const link = {name: 'test', url: 'test/url'}; + const weblinks = [browserLink, link]; + const config = { + ...createServerInfo(), + gerrit: {...createGerritInfo(), primary_weblink_name: browserLink.name}, + }; + sinon.stub(element, '_firstCodeBrowserWeblink').returns(link); + + assert.deepEqual( + element._getBrowseCommitWeblink(weblinks, config), + browserLink + ); + + assert.deepEqual(element._getBrowseCommitWeblink(weblinks), link); + }); + + test('_getChangeWeblinks', () => { + const link = {name: 'test', url: 'test/url'}; + const browserLink = {name: 'browser', url: 'browser/url'}; + const mapLinksToConfig = (weblinks: WebLinkInfo[]) => { + return { + type: 'change' as WeblinkType.CHANGE, + repo: 'test' as RepoName, + commit: '111' as CommitId, + options: {weblinks}, + }; + }; + sinon.stub(element, '_getBrowseCommitWeblink').returns(browserLink); + + assert.deepEqual( + element._getChangeWeblinks(mapLinksToConfig([link, browserLink]))[0], + {name: 'test', url: 'test/url'} + ); + + assert.deepEqual(element._getChangeWeblinks(mapLinksToConfig([link]))[0], { + name: 'test', + url: 'test/url', + }); + + link.url = `https://${link.url}`; + assert.deepEqual(element._getChangeWeblinks(mapLinksToConfig([link]))[0], { + name: 'test', + url: 'https://test/url', + }); + }); + + test('_getHashFromCanonicalPath', () => { + let url = '/foo/bar'; + let hash = element._getHashFromCanonicalPath(url); + assert.equal(hash, ''); + + url = ''; + hash = element._getHashFromCanonicalPath(url); + assert.equal(hash, ''); + + url = '/foo#bar'; + hash = element._getHashFromCanonicalPath(url); + assert.equal(hash, 'bar'); + + url = '/foo#bar#baz'; + hash = element._getHashFromCanonicalPath(url); + assert.equal(hash, 'bar#baz'); + + url = '#foo#bar#baz'; + hash = element._getHashFromCanonicalPath(url); + assert.equal(hash, 'foo#bar#baz'); + }); + + suite('_parseLineAddress', () => { + test('returns null for empty and invalid hashes', () => { + let actual = element._parseLineAddress(''); + assert.isNull(actual); + + actual = element._parseLineAddress('foobar'); + assert.isNull(actual); + + actual = element._parseLineAddress('foo123'); + assert.isNull(actual); + + actual = element._parseLineAddress('123bar'); + assert.isNull(actual); + }); + + test('parses correctly', () => { + let actual = element._parseLineAddress('1234'); + assert.isOk(actual); + assert.equal(actual!.lineNum, 1234); + assert.isFalse(actual!.leftSide); + + actual = element._parseLineAddress('a4'); + assert.isOk(actual); + assert.equal(actual!.lineNum, 4); + assert.isTrue(actual!.leftSide); + + actual = element._parseLineAddress('b77'); + assert.isOk(actual); + assert.equal(actual!.lineNum, 77); + assert.isTrue(actual!.leftSide); + }); + }); + + test('_startRouter requires auth for the right handlers', () => { + // This test encodes the lists of route handler methods that gr-router + // automatically checks for authentication before triggering. + + const requiresAuth: any = {}; + const doesNotRequireAuth: any = {}; + sinon.stub(GerritNav, 'setup'); + sinon.stub(page, 'start'); + sinon.stub(page, 'base'); + sinon + .stub(element, '_mapRoute') + .callsFake((_pattern, methodName, usesAuth) => { + if (usesAuth) { + requiresAuth[methodName] = true; + } else { + doesNotRequireAuth[methodName] = true; + } + }); + element._startRouter(); + + const actualRequiresAuth = Object.keys(requiresAuth); + actualRequiresAuth.sort(); + const actualDoesNotRequireAuth = Object.keys(doesNotRequireAuth); + actualDoesNotRequireAuth.sort(); + + const shouldRequireAutoAuth = [ + '_handleAgreementsRoute', + '_handleChangeEditRoute', + '_handleCreateGroupRoute', + '_handleCreateProjectRoute', + '_handleDiffEditRoute', + '_handleGroupAuditLogRoute', + '_handleGroupInfoRoute', + '_handleGroupListFilterOffsetRoute', + '_handleGroupListFilterRoute', + '_handleGroupListOffsetRoute', + '_handleGroupMembersRoute', + '_handleGroupRoute', + '_handleGroupSelfRedirectRoute', + '_handleNewAgreementsRoute', + '_handlePluginListFilterOffsetRoute', + '_handlePluginListFilterRoute', + '_handlePluginListOffsetRoute', + '_handlePluginListRoute', + '_handleRepoCommandsRoute', + '_handleSettingsLegacyRoute', + '_handleSettingsRoute', + ]; + assert.deepEqual(actualRequiresAuth, shouldRequireAutoAuth); + + const unauthenticatedHandlers = [ + '_handleBranchListFilterOffsetRoute', + '_handleBranchListFilterRoute', + '_handleBranchListOffsetRoute', + '_handleChangeIdQueryRoute', + '_handleChangeNumberLegacyRoute', + '_handleChangeRoute', + '_handleCommentRoute', + '_handleCommentsRoute', + '_handleDiffRoute', + '_handleDefaultRoute', + '_handleChangeLegacyRoute', + '_handleDocumentationRedirectRoute', + '_handleDocumentationSearchRoute', + '_handleDocumentationSearchRedirectRoute', + '_handleLegacyLinenum', + '_handleImproperlyEncodedPlusRoute', + '_handlePassThroughRoute', + '_handleProjectDashboardRoute', + '_handleLegacyProjectDashboardRoute', + '_handleProjectsOldRoute', + '_handleRepoAccessRoute', + '_handleRepoDashboardsRoute', + '_handleRepoGeneralRoute', + '_handleRepoListFilterOffsetRoute', + '_handleRepoListFilterRoute', + '_handleRepoListOffsetRoute', + '_handleRepoRoute', + '_handleQueryLegacySuffixRoute', + '_handleQueryRoute', + '_handleRegisterRoute', + '_handleTagListFilterOffsetRoute', + '_handleTagListFilterRoute', + '_handleTagListOffsetRoute', + '_handlePluginScreen', + ]; + + // Handler names that check authentication themselves, and thus don't need + // it performed for them. + const selfAuthenticatingHandlers = [ + '_handleDashboardRoute', + '_handleCustomDashboardRoute', + '_handleRootRoute', + ]; + + const shouldNotRequireAuth = unauthenticatedHandlers.concat( + selfAuthenticatingHandlers + ); + shouldNotRequireAuth.sort(); + assert.deepEqual(actualDoesNotRequireAuth, shouldNotRequireAuth); + }); + + test('_redirectIfNotLoggedIn while logged in', () => { + stubRestApi('getLoggedIn').returns(Promise.resolve(true)); + const data = { + save() {}, + handled: true, + canonicalPath: '', + path: '', + querystring: '', + pathname: '', + state: '', + title: '', + hash: '', + params: {test: 'test'}, + }; + const redirectStub = sinon.stub(element, '_redirectToLogin'); + return element._redirectIfNotLoggedIn(data).then(() => { + assert.isFalse(redirectStub.called); + }); + }); + + test('_redirectIfNotLoggedIn while logged out', () => { + stubRestApi('getLoggedIn').returns(Promise.resolve(false)); + const redirectStub = sinon.stub(element, '_redirectToLogin'); + const data = { + save() {}, + handled: true, + canonicalPath: '', + path: '', + querystring: '', + pathname: '', + state: '', + title: '', + hash: '', + params: {test: 'test'}, + }; + return new Promise(resolve => { + element + ._redirectIfNotLoggedIn(data) + .then(() => { + assert.isTrue(false, 'Should never execute'); + }) + .catch(() => { + assert.isTrue(redirectStub.calledOnce); + resolve(Promise.resolve()); + }); + }); + }); + + suite('generateUrl', () => { + test('search', () => { + let params: GenerateUrlSearchViewParameters = { + view: GerritView.SEARCH, + owner: 'a%b', + project: 'c%d' as RepoName, + branch: 'e%f' as BranchName, + topic: 'g%h' as TopicName, + statuses: ['op%en'], + }; + assert.equal( + element._generateUrl(params), + '/q/owner:a%2525b+project:c%2525d+branch:e%2525f+' + + 'topic:g%2525h+status:op%2525en' + ); + + params.offset = 100; + assert.equal( + element._generateUrl(params), + '/q/owner:a%2525b+project:c%2525d+branch:e%2525f+' + + 'topic:g%2525h+status:op%2525en,100' + ); + delete params.offset; + + // The presence of the query param overrides other params. + params.query = 'foo$bar'; + assert.equal(element._generateUrl(params), '/q/foo%2524bar'); + + params.offset = 100; + assert.equal(element._generateUrl(params), '/q/foo%2524bar,100'); + + params = { + view: GerritNav.View.SEARCH, + statuses: ['a', 'b', 'c'], + }; + assert.equal( + element._generateUrl(params), + '/q/(status:a OR status:b OR status:c)' + ); + + params = { + view: GerritNav.View.SEARCH, + topic: 'test' as TopicName, + }; + assert.equal(element._generateUrl(params), '/q/topic:test'); + params = { + view: GerritNav.View.SEARCH, + topic: 'test test' as TopicName, + }; + assert.equal(element._generateUrl(params), '/q/topic:"test+test"'); + params = { + view: GerritNav.View.SEARCH, + topic: 'test:test' as TopicName, + }; + assert.equal(element._generateUrl(params), '/q/topic:"test:test"'); + }); + + test('change', () => { + const params: GenerateUrlChangeViewParameters = { + view: GerritView.CHANGE, + changeNum: 1234 as NumericChangeId, + project: 'test' as RepoName, + }; + + assert.equal(element._generateUrl(params), '/c/test/+/1234'); + + params.patchNum = 10 as PatchSetNum; + assert.equal(element._generateUrl(params), '/c/test/+/1234/10'); + + params.basePatchNum = 5 as BasePatchSetNum; + assert.equal(element._generateUrl(params), '/c/test/+/1234/5..10'); + + params.messageHash = '#123'; + assert.equal(element._generateUrl(params), '/c/test/+/1234/5..10#123'); + }); + + test('change with repo name encoding', () => { + const params: GenerateUrlChangeViewParameters = { + view: GerritView.CHANGE, + changeNum: 1234 as NumericChangeId, + project: 'x+/y+/z+/w' as RepoName, + }; + assert.equal( + element._generateUrl(params), + '/c/x%252B/y%252B/z%252B/w/+/1234' + ); + }); + + test('diff', () => { + const params: GenerateUrlDiffViewParameters = { + view: GerritView.DIFF, + changeNum: 42 as NumericChangeId, + path: 'x+y/path.cpp' as RepoName, + patchNum: 12 as PatchSetNum, + project: '' as RepoName, + }; + assert.equal(element._generateUrl(params), '/c/42/12/x%252By/path.cpp'); + + params.project = 'test' as RepoName; + assert.equal( + element._generateUrl(params), + '/c/test/+/42/12/x%252By/path.cpp' + ); + + params.basePatchNum = 6 as BasePatchSetNum; + assert.equal( + element._generateUrl(params), + '/c/test/+/42/6..12/x%252By/path.cpp' + ); + + params.path = 'foo bar/my+file.txt%'; + params.patchNum = 2 as PatchSetNum; + delete params.basePatchNum; + assert.equal( + element._generateUrl(params), + '/c/test/+/42/2/foo+bar/my%252Bfile.txt%2525' + ); + + params.path = 'file.cpp'; + params.lineNum = 123; + assert.equal(element._generateUrl(params), '/c/test/+/42/2/file.cpp#123'); + + params.leftSide = true; + assert.equal( + element._generateUrl(params), + '/c/test/+/42/2/file.cpp#b123' + ); + }); + + test('diff with repo name encoding', () => { + const params: GenerateUrlDiffViewParameters = { + view: GerritView.DIFF, + changeNum: 42 as NumericChangeId, + path: 'x+y/path.cpp', + patchNum: 12 as PatchSetNum, + project: 'x+/y' as RepoName, + }; + assert.equal( + element._generateUrl(params), + '/c/x%252B/y/+/42/12/x%252By/path.cpp' + ); + }); + + test('edit', () => { + const params: GenerateUrlEditViewParameters = { + view: GerritView.EDIT, + changeNum: 42 as NumericChangeId, + project: 'test' as RepoName, + path: 'x+y/path.cpp', + patchNum: 'edit' as PatchSetNum, + }; + assert.equal( + element._generateUrl(params), + '/c/test/+/42/edit/x%252By/path.cpp,edit' + ); + }); + + test('_getPatchRangeExpression', () => { + const params: PatchRangeParams = {}; + let actual = element._getPatchRangeExpression(params); + assert.equal(actual, ''); + + params.patchNum = 4 as PatchSetNum; + actual = element._getPatchRangeExpression(params); + assert.equal(actual, '4'); + + params.basePatchNum = 2 as BasePatchSetNum; + actual = element._getPatchRangeExpression(params); + assert.equal(actual, '2..4'); + + delete params.patchNum; + actual = element._getPatchRangeExpression(params); + assert.equal(actual, '2..'); + }); + + suite('dashboard', () => { + test('self dashboard', () => { + const params: GenerateUrlDashboardViewParameters = { + view: GerritView.DASHBOARD, + }; + assert.equal(element._generateUrl(params), '/dashboard/self'); + }); + + test('user dashboard', () => { + const params: GenerateUrlDashboardViewParameters = { + view: GerritView.DASHBOARD, + user: 'user', + }; + assert.equal(element._generateUrl(params), '/dashboard/user'); + }); + + test('custom self dashboard, no title', () => { + const params: GenerateUrlDashboardViewParameters = { + view: GerritView.DASHBOARD, + sections: [ + {name: 'section 1', query: 'query 1'}, + {name: 'section 2', query: 'query 2'}, + ], + }; + assert.equal( + element._generateUrl(params), + '/dashboard/?section%201=query%201§ion%202=query%202' + ); + }); + + test('custom repo dashboard', () => { + const params: GenerateUrlDashboardViewParameters = { + view: GerritView.DASHBOARD, + sections: [ + {name: 'section 1', query: 'query 1 ${project}'}, + {name: 'section 2', query: 'query 2 ${repo}'}, + ], + repo: 'repo-name' as RepoName, + }; + assert.equal( + element._generateUrl(params), + '/dashboard/?section%201=query%201%20repo-name&' + + 'section%202=query%202%20repo-name' + ); + }); + + test('custom user dashboard, with title', () => { + const params: GenerateUrlDashboardViewParameters = { + view: GerritView.DASHBOARD, + user: 'user', + sections: [{name: 'name', query: 'query'}], + title: 'custom dashboard', + }; + assert.equal( + element._generateUrl(params), + '/dashboard/user?name=query&title=custom%20dashboard' + ); + }); + + test('repo dashboard', () => { + const params: GenerateUrlDashboardViewParameters = { + view: GerritView.DASHBOARD, + repo: 'gerrit/repo' as RepoName, + dashboard: 'default:main' as DashboardId, + }; + assert.equal( + element._generateUrl(params), + '/p/gerrit/repo/+/dashboard/default:main' + ); + }); + + test('project dashboard (legacy)', () => { + const params: GenerateUrlDashboardViewParameters = { + view: GerritView.DASHBOARD, + project: 'gerrit/project' as RepoName, + dashboard: 'default:main' as DashboardId, + }; + assert.equal( + element._generateUrl(params), + '/p/gerrit/project/+/dashboard/default:main' + ); + }); + }); + + suite('groups', () => { + test('group info', () => { + const params: GenerateUrlGroupViewParameters = { + view: GerritView.GROUP, + groupId: '1234' as GroupId, + }; + assert.equal(element._generateUrl(params), '/admin/groups/1234'); + }); + + test('group members', () => { + const params: GenerateUrlGroupViewParameters = { + view: GerritView.GROUP, + groupId: '1234' as GroupId, + detail: 'members' as GroupDetailView, + }; + assert.equal( + element._generateUrl(params), + '/admin/groups/1234,members' + ); + }); + + test('group audit log', () => { + const params: GenerateUrlGroupViewParameters = { + view: GerritView.GROUP, + groupId: '1234' as GroupId, + detail: 'log' as GroupDetailView, + }; + assert.equal( + element._generateUrl(params), + '/admin/groups/1234,audit-log' + ); + }); + }); + }); + + suite('param normalization', () => { + suite('_normalizePatchRangeParams', () => { + test('range n..n normalizes to n', () => { + const params: PatchRangeParams = { + basePatchNum: 4 as BasePatchSetNum, + patchNum: 4 as PatchSetNum, + }; + const needsRedirect = element._normalizePatchRangeParams(params); + assert.isTrue(needsRedirect); + assert.equal(params.basePatchNum, ParentPatchSetNum); + assert.equal(params.patchNum, 4 as PatchSetNum); + }); + + test('range n.. normalizes to n', () => { + const params: PatchRangeParams = {basePatchNum: 4 as BasePatchSetNum}; + const needsRedirect = element._normalizePatchRangeParams(params); + assert.isFalse(needsRedirect); + assert.equal(params.basePatchNum, ParentPatchSetNum); + assert.equal(params.patchNum, 4 as PatchSetNum); + }); + }); + }); + + suite('route handlers', () => { + let redirectStub: sinon.SinonStub; + let setParamsStub: sinon.SinonStub; + let handlePassThroughRoute: sinon.SinonStub; + + // Simple route handlers are direct mappings from parsed route data to a + // new set of app.params. This test helper asserts that passing `data` + // into `methodName` results in setting the params specified in `params`. + function assertDataToParams( + data: PageContextWithQueryMap, + methodName: string, + params: AppElementParams | GenerateUrlParameters + ) { + (element as any)[methodName](data); + assert.deepEqual(setParamsStub.lastCall.args[0], params); + } + + function createPageContext(): PageContextWithQueryMap { + return { + queryMap: new Map(), + save() {}, + handled: true, + canonicalPath: '', + path: '', + querystring: '', + pathname: '', + state: '', + title: '', + hash: '', + params: {}, + }; + } + + setup(() => { + redirectStub = sinon.stub(element, '_redirect'); + setParamsStub = sinon.stub(element, '_setParams'); + handlePassThroughRoute = sinon.stub(element, '_handlePassThroughRoute'); + }); + + test('_handleLegacyProjectDashboardRoute', () => { + const params = { + ...createPageContext(), + params: {0: 'gerrit/project', 1: 'dashboard:main'}, + }; + element._handleLegacyProjectDashboardRoute(params); + assert.isTrue(redirectStub.calledOnce); + assert.equal( + redirectStub.lastCall.args[0], + '/p/gerrit/project/+/dashboard/dashboard:main' + ); + }); + + test('_handleAgreementsRoute', () => { + element._handleAgreementsRoute(); + assert.isTrue(redirectStub.calledOnce); + assert.equal(redirectStub.lastCall.args[0], '/settings/#Agreements'); + }); + + test('_handleNewAgreementsRoute', () => { + const params = createPageContext(); + element._handleNewAgreementsRoute(params); + assert.isTrue(setParamsStub.calledOnce); + assert.equal( + setParamsStub.lastCall.args[0].view, + GerritNav.View.AGREEMENTS + ); + }); + + test('_handleSettingsLegacyRoute', () => { + const data = {...createPageContext(), params: {0: 'my-token'}}; + assertDataToParams(data, '_handleSettingsLegacyRoute', { + view: GerritNav.View.SETTINGS, + emailToken: 'my-token', + }); + }); + + test('_handleSettingsLegacyRoute with +', () => { + const data = {...createPageContext(), params: {0: 'my-token test'}}; + assertDataToParams(data, '_handleSettingsLegacyRoute', { + view: GerritNav.View.SETTINGS, + emailToken: 'my-token+test', + }); + }); + + test('_handleSettingsRoute', () => { + const data = createPageContext(); + assertDataToParams(data, '_handleSettingsRoute', { + view: GerritNav.View.SETTINGS, + }); + }); + + test('_handleDefaultRoute on first load', () => { + const spy = sinon.spy(); + addListenerForTest(document, 'page-error', spy); + element._handleDefaultRoute(); + assert.isTrue(spy.calledOnce); + assert.equal(spy.lastCall.args[0].detail.response.status, 404); + }); + + test('_handleDefaultRoute after internal navigation', () => { + let onExit: Function | null = null; + const onRegisteringExit = ( + _match: string | RegExp, + _onExit: Function + ) => { + onExit = _onExit; + }; + sinon.stub(page, 'exit').callsFake(onRegisteringExit); + sinon.stub(GerritNav, 'setup'); + sinon.stub(page, 'start'); + sinon.stub(page, 'base'); + element._startRouter(); + + element._handleDefaultRoute(); + + onExit!('', () => {}); // we left page; + + element._handleDefaultRoute(); + assert.isTrue(handlePassThroughRoute.calledOnce); + }); + + test('_handleImproperlyEncodedPlusRoute', () => { + const params = { + ...createPageContext(), + canonicalPath: '/c/test/%20/42', + params: {0: 'test', 1: '42'}, + }; + // Regression test for Issue 7100. + element._handleImproperlyEncodedPlusRoute(params); + assert.isTrue(redirectStub.calledOnce); + assert.equal(redirectStub.lastCall.args[0], '/c/test/+/42'); + + sinon.stub(element, '_getHashFromCanonicalPath').returns('foo'); + element._handleImproperlyEncodedPlusRoute(params); + assert.equal(redirectStub.lastCall.args[0], '/c/test/+/42#foo'); + }); + + test('_handleQueryRoute', () => { + const data: PageContextWithQueryMap = { + ...createPageContext(), + params: {0: 'project:foo/bar/baz'}, + }; + assertDataToParams(data, '_handleQueryRoute', { + view: GerritNav.View.SEARCH, + query: 'project:foo/bar/baz', + offset: undefined, + }); + + data.params[1] = '123'; + data.params[2] = '123'; + assertDataToParams(data, '_handleQueryRoute', { + view: GerritNav.View.SEARCH, + query: 'project:foo/bar/baz', + offset: '123', + }); + }); + + test('_handleQueryLegacySuffixRoute', () => { + const params = {...createPageContext(), path: '/q/foo+bar,n,z'}; + element._handleQueryLegacySuffixRoute(params); + assert.isTrue(redirectStub.calledOnce); + assert.equal(redirectStub.lastCall.args[0], '/q/foo+bar'); + }); + + test('_handleChangeIdQueryRoute', () => { + const data = { + ...createPageContext(), + params: {0: 'I0123456789abcdef0123456789abcdef01234567'}, + }; + assertDataToParams(data, '_handleChangeIdQueryRoute', { + view: GerritNav.View.SEARCH, + query: 'I0123456789abcdef0123456789abcdef01234567', + }); + }); + + suite('_handleRegisterRoute', () => { + test('happy path', () => { + const ctx = {...createPageContext(), params: {0: '/foo/bar'}}; + element._handleRegisterRoute(ctx); + assert.isTrue(redirectStub.calledWithExactly('/foo/bar')); + assert.isTrue(setParamsStub.calledOnce); + assert.isTrue(setParamsStub.lastCall.args[0].justRegistered); + }); + + test('no param', () => { + const ctx = createPageContext(); + element._handleRegisterRoute(ctx); + assert.isTrue(redirectStub.calledWithExactly('/')); + assert.isTrue(setParamsStub.calledOnce); + assert.isTrue(setParamsStub.lastCall.args[0].justRegistered); + }); + + test('prevent redirect', () => { + const ctx = {...createPageContext(), params: {0: '/register'}}; + element._handleRegisterRoute(ctx); + assert.isTrue(redirectStub.calledWithExactly('/')); + assert.isTrue(setParamsStub.calledOnce); + assert.isTrue(setParamsStub.lastCall.args[0].justRegistered); + }); + }); + + suite('_handleRootRoute', () => { + test('closes for closeAfterLogin', () => { + const data = {...createPageContext(), querystring: 'closeAfterLogin'}; + const closeStub = sinon.stub(window, 'close'); + const result = element._handleRootRoute(data); + assert.isNotOk(result); + assert.isTrue(closeStub.called); + assert.isFalse(redirectStub.called); + }); + + test('redirects to dashboard if logged in', () => { + const data = {...createPageContext(), canonicalPath: '/', path: '/'}; + const result = element._handleRootRoute(data); + assert.isOk(result); + return result!.then(() => { + assert.isTrue(redirectStub.calledWithExactly('/dashboard/self')); + }); + }); + + test('redirects to open changes if not logged in', () => { + stubRestApi('getLoggedIn').returns(Promise.resolve(false)); + const data = {...createPageContext(), canonicalPath: '/', path: '/'}; + const result = element._handleRootRoute(data); + assert.isOk(result); + return result!.then(() => { + assert.isTrue( + redirectStub.calledWithExactly('/q/status:open+-is:wip') + ); + }); + }); + + suite('GWT hash-path URLs', () => { + test('redirects hash-path URLs', () => { + const data = { + ...createPageContext(), + canonicalPath: '/#/foo/bar/baz', + hash: '/foo/bar/baz', + }; + const result = element._handleRootRoute(data); + assert.isNotOk(result); + assert.isTrue(redirectStub.called); + assert.isTrue(redirectStub.calledWithExactly('/foo/bar/baz')); + }); + + test('redirects hash-path URLs w/o leading slash', () => { + const data = { + ...createPageContext(), + canonicalPath: '/#foo/bar/baz', + hash: 'foo/bar/baz', + }; + const result = element._handleRootRoute(data); + assert.isNotOk(result); + assert.isTrue(redirectStub.called); + assert.isTrue(redirectStub.calledWithExactly('/foo/bar/baz')); + }); + + test('normalizes "/ /" in hash to "/+/"', () => { + const data = { + ...createPageContext(), + canonicalPath: '/#/foo/bar/+/123/4', + hash: '/foo/bar/ /123/4', + }; + const result = element._handleRootRoute(data); + assert.isNotOk(result); + assert.isTrue(redirectStub.called); + assert.isTrue(redirectStub.calledWithExactly('/foo/bar/+/123/4')); + }); + + test('prepends baseurl to hash-path', () => { + const data = { + ...createPageContext(), + canonicalPath: '/#/foo/bar', + hash: '/foo/bar', + }; + stubBaseUrl('/baz'); + const result = element._handleRootRoute(data); + assert.isNotOk(result); + assert.isTrue(redirectStub.called); + assert.isTrue(redirectStub.calledWithExactly('/baz/foo/bar')); + }); + + test('normalizes /VE/ settings hash-paths', () => { + const data = { + ...createPageContext(), + canonicalPath: '/#/VE/foo/bar', + hash: '/VE/foo/bar', + }; + const result = element._handleRootRoute(data); + assert.isNotOk(result); + assert.isTrue(redirectStub.called); + assert.isTrue(redirectStub.calledWithExactly('/settings/VE/foo/bar')); + }); + + test('does not drop "inner hashes"', () => { + const data = { + ...createPageContext(), + canonicalPath: '/#/foo/bar#baz', + hash: '/foo/bar', + }; + const result = element._handleRootRoute(data); + assert.isNotOk(result); + assert.isTrue(redirectStub.called); + assert.isTrue(redirectStub.calledWithExactly('/foo/bar#baz')); + }); + }); + }); + + suite('_handleDashboardRoute', () => { + let redirectToLoginStub: sinon.SinonStub; + + setup(() => { + redirectToLoginStub = sinon.stub(element, '_redirectToLogin'); + }); + + test('own dashboard but signed out redirects to login', () => { + stubRestApi('getLoggedIn').returns(Promise.resolve(false)); + const data = { + ...createPageContext(), + canonicalPath: '/dashboard/', + params: {0: 'seLF'}, + }; + return element._handleDashboardRoute(data).then(() => { + assert.isTrue(redirectToLoginStub.calledOnce); + assert.isFalse(redirectStub.called); + assert.isFalse(setParamsStub.called); + }); + }); + + test('non-self dashboard but signed out does not redirect', () => { + stubRestApi('getLoggedIn').returns(Promise.resolve(false)); + const data = { + ...createPageContext(), + canonicalPath: '/dashboard/', + params: {0: 'foo'}, + }; + return element._handleDashboardRoute(data).then(() => { + assert.isFalse(redirectToLoginStub.called); + assert.isFalse(setParamsStub.called); + assert.isTrue(redirectStub.calledOnce); + assert.equal(redirectStub.lastCall.args[0], '/q/owner:foo'); + }); + }); + + test('dashboard while signed in sets params', () => { + const data = { + ...createPageContext(), + canonicalPath: '/dashboard/', + params: {0: 'foo'}, + }; + return element._handleDashboardRoute(data).then(() => { + assert.isFalse(redirectToLoginStub.called); + assert.isFalse(redirectStub.called); + assert.isTrue(setParamsStub.calledOnce); + assert.deepEqual(setParamsStub.lastCall.args[0], { + view: GerritNav.View.DASHBOARD, + user: 'foo', + }); + }); + }); + }); + + suite('_handleCustomDashboardRoute', () => { + let redirectToLoginStub: sinon.SinonStub; + + setup(() => { + redirectToLoginStub = sinon.stub(element, '_redirectToLogin'); + }); + + test('no user specified', () => { + const data = { + ...createPageContext(), + canonicalPath: '/dashboard/', + params: {0: ''}, + }; + return element._handleCustomDashboardRoute(data, '').then(() => { + assert.isFalse(setParamsStub.called); + assert.isTrue(redirectStub.called); + assert.equal(redirectStub.lastCall.args[0], '/dashboard/self'); + }); + }); + + test('custom dashboard without title', () => { + const data = { + ...createPageContext(), + canonicalPath: '/dashboard/', + params: {0: ''}, + }; + return element + ._handleCustomDashboardRoute(data, '?a=b&c&d=e') + .then(() => { + assert.isFalse(redirectStub.called); + assert.isTrue(setParamsStub.calledOnce); + assert.deepEqual(setParamsStub.lastCall.args[0], { + view: GerritNav.View.DASHBOARD, + user: 'self', + sections: [ + {name: 'a', query: 'b'}, + {name: 'd', query: 'e'}, + ], + title: 'Custom Dashboard', + }); + }); + }); + + test('custom dashboard with title', () => { + const data = { + ...createPageContext(), + canonicalPath: '/dashboard/', + params: {0: ''}, + }; + return element + ._handleCustomDashboardRoute(data, '?a=b&c&d=&=e&title=t') + .then(() => { + assert.isFalse(redirectToLoginStub.called); + assert.isFalse(redirectStub.called); + assert.isTrue(setParamsStub.calledOnce); + assert.deepEqual(setParamsStub.lastCall.args[0], { + view: GerritNav.View.DASHBOARD, + user: 'self', + sections: [{name: 'a', query: 'b'}], + title: 't', + }); + }); + }); + + test('custom dashboard with foreach', () => { + const data = { + ...createPageContext(), + canonicalPath: '/dashboard/', + params: {0: ''}, + }; + return element + ._handleCustomDashboardRoute(data, '?a=b&c&d=&=e&foreach=is:open') + .then(() => { + assert.isFalse(redirectToLoginStub.called); + assert.isFalse(redirectStub.called); + assert.isTrue(setParamsStub.calledOnce); + assert.deepEqual(setParamsStub.lastCall.args[0], { + view: GerritNav.View.DASHBOARD, + user: 'self', + sections: [{name: 'a', query: 'is:open b'}], + title: 'Custom Dashboard', + }); + }); + }); + }); + + suite('group routes', () => { + test('_handleGroupInfoRoute', () => { + const data = {...createPageContext(), params: {0: '1234'}}; + element._handleGroupInfoRoute(data); + assert.isTrue(redirectStub.calledOnce); + assert.equal(redirectStub.lastCall.args[0], '/admin/groups/1234'); + }); + + test('_handleGroupAuditLogRoute', () => { + const data = {...createPageContext(), params: {0: '1234'}}; + assertDataToParams(data, '_handleGroupAuditLogRoute', { + view: GerritView.GROUP, + detail: GroupDetailView.LOG, + groupId: '1234' as GroupId, + }); + }); + + test('_handleGroupMembersRoute', () => { + const data = {...createPageContext(), params: {0: '1234'}}; + assertDataToParams(data, '_handleGroupMembersRoute', { + view: GerritView.GROUP, + detail: GroupDetailView.MEMBERS, + groupId: '1234' as GroupId, + }); + }); + + test('_handleGroupListOffsetRoute', () => { + const data = createPageContext(); + assertDataToParams(data, '_handleGroupListOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-admin-group-list', + offset: 0, + filter: null, + openCreateModal: false, + }); + + data.params[1] = '42'; + assertDataToParams(data, '_handleGroupListOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-admin-group-list', + offset: '42', + filter: null, + openCreateModal: false, + }); + + data.hash = 'create'; + assertDataToParams(data, '_handleGroupListOffsetRoute', { + view: GerritNav.View.ADMIN, + adminView: 'gr-admin-group-list', + offset: '42', + filter: null, + openCreateModal: true, + }); + }); + + test('_handleGroupListFilterOffsetRoute', () => { + const data = { + ...createPageContext(), + params: {filter: 'foo', offset: '42'}, + }; + assertDataToParams(data, '_handleGroupListFilterOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-admin-group-list', + offset: '42', + filter: 'foo', + }); + }); + + test('_handleGroupListFilterRoute', () => { + const data = {...createPageContext(), params: {filter: 'foo'}}; + assertDataToParams(data, '_handleGroupListFilterRoute', { + view: GerritView.ADMIN, + adminView: 'gr-admin-group-list', + filter: 'foo', + }); + }); + + test('_handleGroupRoute', () => { + const data = {...createPageContext(), params: {0: '4321'}}; + assertDataToParams(data, '_handleGroupRoute', { + view: GerritView.GROUP, + groupId: '4321' as GroupId, + }); + }); + }); + + suite('repo routes', () => { + test('_handleProjectsOldRoute', () => { + const data = {...createPageContext(), params: {}}; + element._handleProjectsOldRoute(data); + assert.isTrue(redirectStub.calledOnce); + assert.equal(redirectStub.lastCall.args[0], '/admin/repos/'); + }); + + test('_handleProjectsOldRoute test', () => { + const data = {...createPageContext(), params: {1: 'test'}}; + element._handleProjectsOldRoute(data); + assert.isTrue(redirectStub.calledOnce); + assert.equal(redirectStub.lastCall.args[0], '/admin/repos/test'); + }); + + test('_handleProjectsOldRoute test,branches', () => { + const data = {...createPageContext(), params: {1: 'test,branches'}}; + element._handleProjectsOldRoute(data); + assert.isTrue(redirectStub.calledOnce); + assert.equal( + redirectStub.lastCall.args[0], + '/admin/repos/test,branches' + ); + }); + + test('_handleRepoRoute', () => { + const data = {...createPageContext(), path: '/admin/repos/test'}; + element._handleRepoRoute(data); + assert.isTrue(redirectStub.calledOnce); + assert.equal( + redirectStub.lastCall.args[0], + '/admin/repos/test,general' + ); + }); + + test('_handleRepoGeneralRoute', () => { + const data = {...createPageContext(), params: {0: '4321'}}; + assertDataToParams(data, '_handleRepoGeneralRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.GENERAL, + repo: '4321' as RepoName, + }); + }); + + test('_handleRepoCommandsRoute', () => { + const data = {...createPageContext(), params: {0: '4321'}}; + assertDataToParams(data, '_handleRepoCommandsRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.COMMANDS, + repo: '4321' as RepoName, + }); + }); + + test('_handleRepoAccessRoute', () => { + const data = {...createPageContext(), params: {0: '4321'}}; + assertDataToParams(data, '_handleRepoAccessRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.ACCESS, + repo: '4321' as RepoName, + }); + }); + + suite('branch list routes', () => { + test('_handleBranchListOffsetRoute', () => { + const data: PageContextWithQueryMap = { + ...createPageContext(), + params: {0: '4321'}, + }; + assertDataToParams(data, '_handleBranchListOffsetRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.BRANCHES, + repo: '4321' as RepoName, + offset: 0, + filter: null, + }); + + data.params[2] = '42'; + assertDataToParams(data, '_handleBranchListOffsetRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.BRANCHES, + repo: '4321' as RepoName, + offset: '42', + filter: null, + }); + }); + + test('_handleBranchListFilterOffsetRoute', () => { + const data = { + ...createPageContext(), + params: {repo: '4321', filter: 'foo', offset: '42'}, + }; + assertDataToParams(data, '_handleBranchListFilterOffsetRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.BRANCHES, + repo: '4321' as RepoName, + offset: '42', + filter: 'foo', + }); + }); + + test('_handleBranchListFilterRoute', () => { + const data = { + ...createPageContext(), + params: {repo: '4321', filter: 'foo'}, + }; + assertDataToParams(data, '_handleBranchListFilterRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.BRANCHES, + repo: '4321' as RepoName, + filter: 'foo', + }); + }); + }); + + suite('tag list routes', () => { + test('_handleTagListOffsetRoute', () => { + const data = {...createPageContext(), params: {0: '4321'}}; + assertDataToParams(data, '_handleTagListOffsetRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.TAGS, + repo: '4321' as RepoName, + offset: 0, + filter: null, + }); + }); + + test('_handleTagListFilterOffsetRoute', () => { + const data = { + ...createPageContext(), + params: {repo: '4321', filter: 'foo', offset: '42'}, + }; + assertDataToParams(data, '_handleTagListFilterOffsetRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.TAGS, + repo: '4321' as RepoName, + offset: '42', + filter: 'foo', + }); + }); + + test('_handleTagListFilterRoute', () => { + const data: PageContextWithQueryMap = { + ...createPageContext(), + params: {repo: '4321'}, + }; + assertDataToParams(data, '_handleTagListFilterRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.TAGS, + repo: '4321' as RepoName, + filter: null, + }); + + data.params.filter = 'foo'; + assertDataToParams(data, '_handleTagListFilterRoute', { + view: GerritView.REPO, + detail: GerritNav.RepoDetailView.TAGS, + repo: '4321' as RepoName, + filter: 'foo', + }); + }); + }); + + suite('repo list routes', () => { + test('_handleRepoListOffsetRoute', () => { + const data = createPageContext(); + assertDataToParams(data, '_handleRepoListOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-repo-list', + offset: 0, + filter: null, + openCreateModal: false, + }); + + data.params[1] = '42'; + assertDataToParams(data, '_handleRepoListOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-repo-list', + offset: '42', + filter: null, + openCreateModal: false, + }); + + data.hash = 'create'; + assertDataToParams(data, '_handleRepoListOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-repo-list', + offset: '42', + filter: null, + openCreateModal: true, + }); + }); + + test('_handleRepoListFilterOffsetRoute', () => { + const data = { + ...createPageContext(), + params: {filter: 'foo', offset: '42'}, + }; + assertDataToParams(data, '_handleRepoListFilterOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-repo-list', + offset: '42', + filter: 'foo', + }); + }); + + test('_handleRepoListFilterRoute', () => { + const data = createPageContext(); + assertDataToParams(data, '_handleRepoListFilterRoute', { + view: GerritView.ADMIN, + adminView: 'gr-repo-list', + filter: null, + }); + + data.params.filter = 'foo'; + assertDataToParams(data, '_handleRepoListFilterRoute', { + view: GerritView.ADMIN, + adminView: 'gr-repo-list', + filter: 'foo', + }); + }); + }); + }); + + suite('plugin routes', () => { + test('_handlePluginListOffsetRoute', () => { + const data = createPageContext(); + assertDataToParams(data, '_handlePluginListOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-plugin-list', + offset: 0, + filter: null, + }); + + data.params[1] = '42'; + assertDataToParams(data, '_handlePluginListOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-plugin-list', + offset: '42', + filter: null, + }); + }); + + test('_handlePluginListFilterOffsetRoute', () => { + const data = { + ...createPageContext(), + params: {filter: 'foo', offset: '42'}, + }; + assertDataToParams(data, '_handlePluginListFilterOffsetRoute', { + view: GerritView.ADMIN, + adminView: 'gr-plugin-list', + offset: '42', + filter: 'foo', + }); + }); + + test('_handlePluginListFilterRoute', () => { + const data = createPageContext(); + assertDataToParams(data, '_handlePluginListFilterRoute', { + view: GerritView.ADMIN, + adminView: 'gr-plugin-list', + filter: null, + }); + + data.params.filter = 'foo'; + assertDataToParams(data, '_handlePluginListFilterRoute', { + view: GerritView.ADMIN, + adminView: 'gr-plugin-list', + filter: 'foo', + }); + }); + + test('_handlePluginListRoute', () => { + const data = createPageContext(); + assertDataToParams(data, '_handlePluginListRoute', { + view: GerritView.ADMIN, + adminView: 'gr-plugin-list', + }); + }); + }); + + suite('change/diff routes', () => { + test('_handleChangeNumberLegacyRoute', () => { + const data = {...createPageContext(), params: {0: '12345'}}; + element._handleChangeNumberLegacyRoute(data); + assert.isTrue(redirectStub.calledOnce); + assert.isTrue(redirectStub.calledWithExactly('/c/12345')); + }); + + test('_handleChangeLegacyRoute', async () => { + stubRestApi('getFromProjectLookup').returns( + Promise.resolve('project' as RepoName) + ); + const ctx = { + ...createPageContext(), + params: {0: '1234', 1: 'comment/6789'}, + }; + element._handleChangeLegacyRoute(ctx); + await flush(); + assert.isTrue( + redirectStub.calledWithExactly('/c/project/+/1234' + '/comment/6789') + ); + }); + + test('_handleLegacyLinenum w/ @321', () => { + const ctx = {...createPageContext(), path: '/c/1234/3..8/foo/bar@321'}; + element._handleLegacyLinenum(ctx); + assert.isTrue(redirectStub.calledOnce); + assert.isTrue( + redirectStub.calledWithExactly('/c/1234/3..8/foo/bar#321') + ); + }); + + test('_handleLegacyLinenum w/ @b123', () => { + const ctx = {...createPageContext(), path: '/c/1234/3..8/foo/bar@b123'}; + element._handleLegacyLinenum(ctx); + assert.isTrue(redirectStub.calledOnce); + assert.isTrue( + redirectStub.calledWithExactly('/c/1234/3..8/foo/bar#b123') + ); + }); + + suite('_handleChangeRoute', () => { + let normalizeRangeStub: sinon.SinonStub; + + function makeParams( + _path: string, + _hash: string + ): PageContextWithQueryMap { + return { + ...createPageContext(), + params: { + 0: 'foo/bar', // 0 Project + 1: '1234', // 1 Change number + 2: '', // 2 Unused + 3: '', // 3 Unused + 4: '4', // 4 Base patch number + 5: '', // 5 Unused + 6: '7', // 6 Patch number + }, + }; + } + + setup(() => { + normalizeRangeStub = sinon.stub( + element, + '_normalizePatchRangeParams' + ); + stubRestApi('setInProjectLookup'); + }); + + test('needs redirect', () => { + normalizeRangeStub.returns(true); + sinon.stub(element, '_generateUrl').returns('foo'); + const ctx = makeParams('', ''); + element._handleChangeRoute(ctx); + assert.isTrue(normalizeRangeStub.called); + assert.isFalse(setParamsStub.called); + assert.isTrue(redirectStub.calledOnce); + assert.isTrue(redirectStub.calledWithExactly('foo')); + }); + + test('change view', () => { + normalizeRangeStub.returns(false); + sinon.stub(element, '_generateUrl').returns('foo'); + const ctx = makeParams('', ''); + assertDataToParams(ctx, '_handleChangeRoute', { + view: GerritView.CHANGE, + project: 'foo/bar' as RepoName, + changeNum: 1234 as NumericChangeId, + basePatchNum: 4 as BasePatchSetNum, + patchNum: 7 as RevisionPatchSetNum, + }); + assert.isFalse(redirectStub.called); + assert.isTrue(normalizeRangeStub.called); + }); + + test('params', () => { + normalizeRangeStub.returns(false); + sinon.stub(element, '_generateUrl').returns('foo'); + const ctx = makeParams('', ''); + ctx.queryMap.set('tab', 'checks'); + ctx.queryMap.set('filter', 'fff'); + ctx.queryMap.set('select', 'sss'); + ctx.queryMap.set('attempt', '1'); + assertDataToParams(ctx, '_handleChangeRoute', { + view: GerritView.CHANGE, + project: 'foo/bar' as RepoName, + changeNum: 1234 as NumericChangeId, + basePatchNum: 4 as BasePatchSetNum, + patchNum: 7 as RevisionPatchSetNum, + attempt: 1, + filter: 'fff', + select: 'sss', + tab: 'checks', + }); + }); + }); + + suite('_handleDiffRoute', () => { + let normalizeRangeStub: sinon.SinonStub; + + function makeParams( + path: string, + hash: string + ): PageContextWithQueryMap { + return { + ...createPageContext(), + hash, + params: { + 0: 'foo/bar', // 0 Project + 1: '1234', // 1 Change number + 2: '', // 2 Unused + 3: '', // 3 Unused + 4: '4', // 4 Base patch number + 5: '', // 5 Unused + 6: '7', // 6 Patch number + 7: '', // 7 Unused, + 8: path, // 8 Diff path + }, + }; + } + + setup(() => { + normalizeRangeStub = sinon.stub( + element, + '_normalizePatchRangeParams' + ); + stubRestApi('setInProjectLookup'); + }); + + test('needs redirect', () => { + normalizeRangeStub.returns(true); + sinon.stub(element, '_generateUrl').returns('foo'); + const ctx = makeParams('', ''); + element._handleDiffRoute(ctx); + assert.isTrue(normalizeRangeStub.called); + assert.isFalse(setParamsStub.called); + assert.isTrue(redirectStub.calledOnce); + assert.isTrue(redirectStub.calledWithExactly('foo')); + }); + + test('diff view', () => { + normalizeRangeStub.returns(false); + sinon.stub(element, '_generateUrl').returns('foo'); + const ctx = makeParams('foo/bar/baz', 'b44'); + assertDataToParams(ctx, '_handleDiffRoute', { + view: GerritView.DIFF, + project: 'foo/bar' as RepoName, + changeNum: 1234 as NumericChangeId, + basePatchNum: 4 as BasePatchSetNum, + patchNum: 7 as RevisionPatchSetNum, + path: 'foo/bar/baz', + leftSide: true, + lineNum: 44, + }); + assert.isFalse(redirectStub.called); + assert.isTrue(normalizeRangeStub.called); + }); + + test('comment route', () => { + const url = '/c/gerrit/+/264833/comment/00049681_f34fd6a9/'; + const groups = url.match(_testOnly_RoutePattern.COMMENT); + assert.deepEqual(groups!.slice(1), [ + 'gerrit', // project + '264833', // changeNum + '00049681_f34fd6a9', // commentId + ]); + assertDataToParams( + {params: groups!.slice(1)} as any, + '_handleCommentRoute', + { + project: 'gerrit' as RepoName, + changeNum: 264833 as NumericChangeId, + commentId: '00049681_f34fd6a9' as UrlEncodedCommentId, + commentLink: true, + view: GerritView.DIFF, + } + ); + }); + + test('comments route', () => { + const url = '/c/gerrit/+/264833/comments/00049681_f34fd6a9/'; + const groups = url.match(_testOnly_RoutePattern.COMMENTS_TAB); + assert.deepEqual(groups!.slice(1), [ + 'gerrit', // project + '264833', // changeNum + '00049681_f34fd6a9', // commentId + ]); + assertDataToParams( + {params: groups!.slice(1)} as any, + '_handleCommentsRoute', + { + project: 'gerrit' as RepoName, + changeNum: 264833 as NumericChangeId, + commentId: '00049681_f34fd6a9' as UrlEncodedCommentId, + view: GerritView.CHANGE, + } + ); + }); + }); + + test('_handleDiffEditRoute', () => { + const normalizeRangeSpy = sinon.spy( + element, + '_normalizePatchRangeParams' + ); + stubRestApi('setInProjectLookup'); + const ctx = { + ...createPageContext(), + hash: '', + params: { + 0: 'foo/bar', // 0 Project + 1: '1234', // 1 Change number + 2: '3', // 2 Patch num + 3: 'foo/bar/baz', // 3 File path + }, + }; + const appParams: GenerateUrlEditViewParameters = { + project: 'foo/bar' as RepoName, + changeNum: 1234 as NumericChangeId, + view: GerritNav.View.EDIT, + path: 'foo/bar/baz', + patchNum: 3 as PatchSetNum, + lineNum: '', + }; + + element._handleDiffEditRoute(ctx); + assert.isFalse(redirectStub.called); + assert.isTrue(normalizeRangeSpy.calledOnce); + assert.deepEqual(normalizeRangeSpy.lastCall.args[0], appParams); + assert.isFalse(normalizeRangeSpy.lastCall.returnValue); + assert.deepEqual(setParamsStub.lastCall.args[0], appParams); + }); + + test('_handleDiffEditRoute with lineNum', () => { + const normalizeRangeSpy = sinon.spy( + element, + '_normalizePatchRangeParams' + ); + stubRestApi('setInProjectLookup'); + const ctx = { + ...createPageContext(), + hash: '4', + params: { + 0: 'foo/bar', // 0 Project + 1: '1234', // 1 Change number + 2: '3', // 2 Patch num + 3: 'foo/bar/baz', // 3 File path + }, + }; + const appParams: GenerateUrlEditViewParameters = { + project: 'foo/bar' as RepoName, + changeNum: 1234 as NumericChangeId, + view: GerritNav.View.EDIT, + path: 'foo/bar/baz', + patchNum: 3 as PatchSetNum, + lineNum: '4', + }; + + element._handleDiffEditRoute(ctx); + assert.isFalse(redirectStub.called); + assert.isTrue(normalizeRangeSpy.calledOnce); + assert.deepEqual(normalizeRangeSpy.lastCall.args[0], appParams); + assert.isFalse(normalizeRangeSpy.lastCall.returnValue); + assert.deepEqual(setParamsStub.lastCall.args[0], appParams); + }); + + test('_handleChangeEditRoute', () => { + const normalizeRangeSpy = sinon.spy( + element, + '_normalizePatchRangeParams' + ); + stubRestApi('setInProjectLookup'); + const ctx = { + ...createPageContext(), + params: { + 0: 'foo/bar', // 0 Project + 1: '1234', // 1 Change number + 2: '', + 3: '3', // 3 Patch num + }, + }; + const appParams: GenerateUrlChangeViewParameters = { + project: 'foo/bar' as RepoName, + changeNum: 1234 as NumericChangeId, + view: GerritView.CHANGE, + patchNum: 3 as PatchSetNum, + edit: true, + tab: '', + }; + + element._handleChangeEditRoute(ctx); + assert.isFalse(redirectStub.called); + assert.isTrue(normalizeRangeSpy.calledOnce); + assert.deepEqual(normalizeRangeSpy.lastCall.args[0], appParams); + assert.isFalse(normalizeRangeSpy.lastCall.returnValue); + assert.deepEqual(setParamsStub.lastCall.args[0], appParams); + }); + }); + + test('_handlePluginScreen', () => { + const ctx = {...createPageContext(), params: {0: 'foo', 1: 'bar'}}; + assertDataToParams(ctx, '_handlePluginScreen', { + view: GerritNav.View.PLUGIN_SCREEN, + plugin: 'foo', + screen: 'bar', + }); + assert.isFalse(redirectStub.called); + }); + }); + + suite('_parseQueryString', () => { + test('empty queries', () => { + assert.deepEqual(element._parseQueryString(''), []); + assert.deepEqual(element._parseQueryString('?'), []); + assert.deepEqual(element._parseQueryString('??'), []); + assert.deepEqual(element._parseQueryString('&&&'), []); + }); + + test('url decoding', () => { + assert.deepEqual(element._parseQueryString('+'), [[' ', '']]); + assert.deepEqual(element._parseQueryString('???+%3d+'), [[' = ', '']]); + assert.deepEqual( + element._parseQueryString('%6e%61%6d%65=%76%61%6c%75%65'), + [['name', 'value']] + ); + }); + + test('multiple parameters', () => { + assert.deepEqual(element._parseQueryString('a=b&c=d&e=f'), [ + ['a', 'b'], + ['c', 'd'], + ['e', 'f'], + ]); + assert.deepEqual(element._parseQueryString('&a=b&&&e=f&c'), [ + ['a', 'b'], + ['e', 'f'], + ['c', ''], + ]); + }); + }); +});