| <!DOCTYPE html> |
| <!-- |
| 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. |
| --> |
| |
| <meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes"> |
| <title>gr-router</title> |
| |
| <script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script> |
| <script src="../../../bower_components/web-component-tester/browser.js"></script> |
| <link rel="import" href="../../../test/common-test-setup.html"/> |
| <link rel="import" href="gr-router.html"> |
| |
| <script>void(0);</script> |
| |
| <test-fixture id="basic"> |
| <template> |
| <gr-router></gr-router> |
| </template> |
| </test-fixture> |
| |
| <script> |
| suite('gr-router tests', () => { |
| let element; |
| let sandbox; |
| |
| setup(() => { |
| sandbox = sinon.sandbox.create(); |
| element = fixture('basic'); |
| }); |
| |
| teardown(() => { sandbox.restore(); }); |
| |
| 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 = {}; |
| sandbox.stub(Gerrit.Nav, 'setup'); |
| sandbox.stub(window.page, 'start'); |
| sandbox.stub(window.page, 'base'); |
| sandbox.stub(window, 'page'); |
| sandbox.stub(element, '_mapRoute', (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', |
| '_handleCreateGroupRoute', |
| '_handleCreateProjectRoute', |
| '_handleDiffEditRoute', |
| '_handleGroupAuditLogRoute', |
| '_handleGroupInfoRoute', |
| '_handleGroupListFilterOffsetRoute', |
| '_handleGroupListFilterRoute', |
| '_handleGroupListOffsetRoute', |
| '_handleGroupMembersRoute', |
| '_handleGroupRoute', |
| '_handlePluginListFilterOffsetRoute', |
| '_handlePluginListFilterRoute', |
| '_handlePluginListOffsetRoute', |
| '_handlePluginListRoute', |
| '_handleRepoCommandsRoute', |
| '_handleSettingsLegacyRoute', |
| '_handleSettingsRoute', |
| ]; |
| assert.deepEqual(actualRequiresAuth, shouldRequireAutoAuth); |
| |
| const unauthenticatedHandlers = [ |
| '_handleBranchListFilterOffsetRoute', |
| '_handleBranchListFilterRoute', |
| '_handleBranchListOffsetRoute', |
| '_handleChangeNumberLegacyRoute', |
| '_handleChangeOrDiffRoute', |
| '_handleDefaultRoute', |
| '_handleChangeLegacyRoute', |
| '_handleDiffLegacyRoute', |
| '_handleLegacyLinenum', |
| '_handleImproperlyEncodedPlusRoute', |
| '_handlePassThroughRoute', |
| '_handleProjectDashboardRoute', |
| '_handleProjectsOldRoute', |
| '_handleRepoAccessRoute', |
| '_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', () => { |
| sandbox.stub(element.$.restAPI, 'getLoggedIn') |
| .returns(Promise.resolve(true)); |
| const data = {canonicalPath: ''}; |
| const redirectStub = sandbox.stub(element, '_redirectToLogin'); |
| return element._redirectIfNotLoggedIn(data).then(() => { |
| assert.isFalse(redirectStub.called); |
| }); |
| }); |
| |
| test('_redirectIfNotLoggedIn while logged out', () => { |
| sandbox.stub(element.$.restAPI, 'getLoggedIn') |
| .returns(Promise.resolve(false)); |
| const redirectStub = sandbox.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: Gerrit.Nav.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'); |
| |
| // The presence of the query param overrides other params. |
| params.query = 'foo$bar'; |
| assert.equal(element._generateUrl(params), '/q/foo%2524bar'); |
| |
| params = { |
| view: Gerrit.Nav.View.SEARCH, |
| statuses: ['a', 'b', 'c'], |
| }; |
| assert.equal(element._generateUrl(params), |
| '/q/(status:a OR status:b OR status:c)'); |
| }); |
| |
| test('change', () => { |
| const params = { |
| view: Gerrit.Nav.View.CHANGE, |
| changeNum: '1234', |
| project: 'test', |
| }; |
| const paramsWithQuery = { |
| view: Gerrit.Nav.View.CHANGE, |
| changeNum: '1234', |
| project: 'test', |
| querystring: 'revert&foo=bar', |
| }; |
| |
| assert.equal(element._generateUrl(params), '/c/test/+/1234'); |
| assert.equal(element._generateUrl(paramsWithQuery), |
| '/c/test/+/1234?revert&foo=bar'); |
| |
| params.patchNum = 10; |
| assert.equal(element._generateUrl(params), '/c/test/+/1234/10'); |
| paramsWithQuery.patchNum = 10; |
| assert.equal(element._generateUrl(paramsWithQuery), |
| '/c/test/+/1234/10?revert&foo=bar'); |
| |
| params.basePatchNum = 5; |
| assert.equal(element._generateUrl(params), '/c/test/+/1234/5..10'); |
| paramsWithQuery.basePatchNum = 5; |
| assert.equal(element._generateUrl(paramsWithQuery), |
| '/c/test/+/1234/5..10?revert&foo=bar'); |
| }); |
| |
| test('diff', () => { |
| const params = { |
| view: Gerrit.Nav.View.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('edit', () => { |
| const params = { |
| view: Gerrit.Nav.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: Gerrit.Nav.View.DASHBOARD, |
| }; |
| assert.equal(element._generateUrl(params), '/dashboard/self'); |
| }); |
| |
| test('user dashboard', () => { |
| const params = { |
| view: Gerrit.Nav.View.DASHBOARD, |
| user: 'user', |
| }; |
| assert.equal(element._generateUrl(params), '/dashboard/user'); |
| }); |
| |
| test('custom self dashboard, no title', () => { |
| const params = { |
| view: Gerrit.Nav.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 user dashboard, with title', () => { |
| const params = { |
| view: Gerrit.Nav.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('project dashboard', () => { |
| const params = { |
| view: Gerrit.Nav.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: Gerrit.Nav.View.GROUP, |
| groupId: 1234, |
| }; |
| assert.equal(element._generateUrl(params), '/admin/groups/1234'); |
| }); |
| |
| test('group members', () => { |
| const params = { |
| view: Gerrit.Nav.View.GROUP, |
| groupId: 1234, |
| detail: 'members', |
| }; |
| assert.equal(element._generateUrl(params), |
| '/admin/groups/1234,members'); |
| }); |
| |
| test('group audit log', () => { |
| const params = { |
| view: Gerrit.Nav.View.GROUP, |
| groupId: 1234, |
| detail: 'log', |
| }; |
| assert.equal(element._generateUrl(params), |
| '/admin/groups/1234,audit-log'); |
| }); |
| }); |
| }); |
| |
| suite('param normalization', () => { |
| let projectLookupStub; |
| |
| setup(() => { |
| projectLookupStub = sandbox |
| .stub(element.$.restAPI, 'getFromProjectLookup'); |
| sandbox.stub(element, '_generateUrl'); |
| }); |
| |
| suite('_normalizeLegacyRouteParams', () => { |
| let rangeStub; |
| let redirectStub; |
| |
| setup(() => { |
| rangeStub = sandbox.stub(element, '_normalizePatchRangeParams') |
| .returns(Promise.resolve()); |
| redirectStub = sandbox.stub(element, '_redirect'); |
| }); |
| |
| test('w/o changeNum', () => { |
| projectLookupStub.returns(Promise.resolve('foo/bar')); |
| const params = {}; |
| return element._normalizeLegacyRouteParams(params).then(() => { |
| assert.isFalse(projectLookupStub.called); |
| assert.isFalse(rangeStub.called); |
| assert.isNotOk(params.project); |
| assert.isFalse(redirectStub.called); |
| }); |
| }); |
| |
| test('w/ changeNum', () => { |
| projectLookupStub.returns(Promise.resolve('foo/bar')); |
| const params = {changeNum: 1234}; |
| return element._normalizeLegacyRouteParams(params).then(() => { |
| assert.isTrue(projectLookupStub.called); |
| assert.isTrue(rangeStub.called); |
| assert.equal(params.project, 'foo/bar'); |
| assert.isTrue(redirectStub.calledOnce); |
| }); |
| }); |
| |
| test('halts on project lookup failure', () => { |
| projectLookupStub.returns(Promise.resolve(undefined)); |
| |
| const params = {changeNum: 1234}; |
| return element._normalizeLegacyRouteParams(params).then(() => { |
| assert.isTrue(projectLookupStub.called); |
| assert.isFalse(rangeStub.called); |
| assert.isUndefined(params.project); |
| assert.isFalse(redirectStub.called); |
| }); |
| }); |
| }); |
| |
| suite('_normalizePatchRangeParams', () => { |
| test('range n..n normalizes to n', () => { |
| const params = {basePatchNum: 4, patchNum: 4}; |
| const needsRedirect = element._normalizePatchRangeParams(params); |
| assert.isTrue(needsRedirect); |
| assert.isNotOk(params.basePatchNum); |
| assert.equal(params.patchNum, 4); |
| }); |
| |
| test('range n.. normalizes to n', () => { |
| const params = {basePatchNum: 4}; |
| const needsRedirect = element._normalizePatchRangeParams(params); |
| assert.isFalse(needsRedirect); |
| assert.isNotOk(params.basePatchNum); |
| assert.equal(params.patchNum, 4); |
| }); |
| |
| test('range 0..n normalizes to edit..n', () => { |
| const params = {basePatchNum: 0, patchNum: 4}; |
| const needsRedirect = element._normalizePatchRangeParams(params); |
| assert.isTrue(needsRedirect); |
| assert.equal(params.basePatchNum, 'edit'); |
| assert.equal(params.patchNum, 4); |
| }); |
| |
| test('range n..0 normalizes to n..edit', () => { |
| const params = {basePatchNum: 4, patchNum: 0}; |
| const needsRedirect = element._normalizePatchRangeParams(params); |
| assert.isTrue(needsRedirect); |
| assert.equal(params.basePatchNum, 4); |
| assert.equal(params.patchNum, 'edit'); |
| }); |
| |
| test('range 0..0 normalizes to edit', () => { |
| const params = {basePatchNum: 0, patchNum: 0}; |
| const needsRedirect = element._normalizePatchRangeParams(params); |
| assert.isTrue(needsRedirect); |
| assert.isNotOk(params.basePatchNum); |
| assert.equal(params.patchNum, 'edit'); |
| }); |
| }); |
| }); |
| |
| suite('route handlers', () => { |
| let redirectStub; |
| let setParamsStub; |
| |
| // 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 = sandbox.stub(element, '_redirect'); |
| setParamsStub = sandbox.stub(element, '_setParams'); |
| }); |
| |
| test('_handleAgreementsRoute', () => { |
| element._handleAgreementsRoute({params: {}}); |
| assert.isTrue(setParamsStub.calledOnce); |
| assert.equal(setParamsStub.lastCall.args[0].view, |
| Gerrit.Nav.View.AGREEMENTS); |
| }); |
| |
| test('_handleSettingsLegacyRoute', () => { |
| const data = {params: {0: 'my-token'}}; |
| assertDataToParams(data, '_handleSettingsLegacyRoute', { |
| view: Gerrit.Nav.View.SETTINGS, |
| emailToken: 'my-token', |
| }); |
| }); |
| |
| test('_handleSettingsRoute', () => { |
| const data = {}; |
| assertDataToParams(data, '_handleSettingsRoute', { |
| view: Gerrit.Nav.View.SETTINGS, |
| }); |
| }); |
| |
| test('_handleDefaultRoute', () => { |
| element._app = {dispatchEvent: sinon.stub()}; |
| element._handleDefaultRoute(); |
| assert.isTrue(element._app.dispatchEvent.calledOnce); |
| assert.equal( |
| element._app.dispatchEvent.lastCall.args[0].detail.response.status, |
| 404); |
| }); |
| |
| 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'); |
| |
| sandbox.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: Gerrit.Nav.View.SEARCH, |
| query: 'project:foo/bar/baz', |
| offset: undefined, |
| }); |
| |
| data.params.push(',123', '123'); |
| assertDataToParams(data, '_handleQueryRoute', { |
| view: Gerrit.Nav.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'); |
| }); |
| |
| 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 = sandbox.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', () => { |
| sandbox.stub(element.$.restAPI, 'getLoggedIn') |
| .returns(Promise.resolve(true)); |
| 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', () => { |
| sandbox.stub(element.$.restAPI, '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')); |
| }); |
| }); |
| |
| 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', |
| }; |
| sandbox.stub(element, 'getBaseUrl').returns('/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 = sandbox.stub(element, '_redirectToLogin'); |
| }); |
| |
| test('own dashboard but signed out redirects to login', () => { |
| sandbox.stub(element.$.restAPI, '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', () => { |
| sandbox.stub(element.$.restAPI, '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', () => { |
| sandbox.stub(element.$.restAPI, 'getLoggedIn') |
| .returns(Promise.resolve(true)); |
| 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: Gerrit.Nav.View.DASHBOARD, |
| user: 'foo', |
| }); |
| }); |
| }); |
| }); |
| |
| suite('_handleCustomDashboardRoute', () => { |
| let redirectToLoginStub; |
| |
| setup(() => { |
| redirectToLoginStub = sandbox.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: Gerrit.Nav.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: Gerrit.Nav.View.DASHBOARD, |
| user: 'self', |
| sections: [ |
| {name: 'a', query: 'b'}, |
| ], |
| title: 't', |
| }); |
| }); |
| }); |
| }); |
| |
| 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: Gerrit.Nav.View.GROUP, |
| detail: 'log', |
| groupId: 1234, |
| }); |
| }); |
| |
| test('_handleGroupMembersRoute', () => { |
| const data = {params: {0: 1234}}; |
| assertDataToParams(data, '_handleGroupMembersRoute', { |
| view: Gerrit.Nav.View.GROUP, |
| detail: 'members', |
| groupId: 1234, |
| }); |
| }); |
| |
| test('_handleGroupListOffsetRoute', () => { |
| const data = {params: {}}; |
| assertDataToParams(data, '_handleGroupListOffsetRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-admin-group-list', |
| offset: 0, |
| filter: null, |
| openCreateModal: false, |
| }); |
| |
| data.params[1] = 42; |
| assertDataToParams(data, '_handleGroupListOffsetRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-admin-group-list', |
| offset: 42, |
| filter: null, |
| openCreateModal: false, |
| }); |
| |
| data.hash = 'create'; |
| assertDataToParams(data, '_handleGroupListOffsetRoute', { |
| view: Gerrit.Nav.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: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-admin-group-list', |
| offset: 42, |
| filter: 'foo', |
| }); |
| }); |
| |
| test('_handleGroupListFilterRoute', () => { |
| const data = {params: {filter: 'foo'}}; |
| assertDataToParams(data, '_handleGroupListFilterRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-admin-group-list', |
| filter: 'foo', |
| }); |
| }); |
| |
| test('_handleGroupRoute', () => { |
| const data = {params: {0: 4321}}; |
| assertDataToParams(data, '_handleGroupRoute', { |
| view: Gerrit.Nav.View.GROUP, |
| groupId: 4321, |
| }); |
| }); |
| }); |
| |
| suite('repo routes', () => { |
| test('_handleRepoRoute', () => { |
| const data = {params: {0: 4321}}; |
| assertDataToParams(data, '_handleRepoRoute', { |
| view: Gerrit.Nav.View.REPO, |
| repo: 4321, |
| }); |
| }); |
| |
| test('_handleRepoCommandsRoute', () => { |
| const data = {params: {0: 4321}}; |
| assertDataToParams(data, '_handleRepoCommandsRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.COMMANDS, |
| repo: 4321, |
| }); |
| }); |
| |
| test('_handleRepoAccessRoute', () => { |
| const data = {params: {0: 4321}}; |
| assertDataToParams(data, '_handleRepoAccessRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.ACCESS, |
| repo: 4321, |
| }); |
| }); |
| |
| suite('branch list routes', () => { |
| test('_handleBranchListOffsetRoute', () => { |
| const data = {params: {0: 4321}}; |
| assertDataToParams(data, '_handleBranchListOffsetRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.BRANCHES, |
| detailType: 'branches', |
| repo: 4321, |
| offset: 0, |
| filter: null, |
| }); |
| |
| data.params[2] = 42; |
| assertDataToParams(data, '_handleBranchListOffsetRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.BRANCHES, |
| detailType: 'branches', |
| repo: 4321, |
| offset: 42, |
| filter: null, |
| }); |
| }); |
| |
| test('_handleBranchListFilterOffsetRoute', () => { |
| const data = {params: {repo: 4321, filter: 'foo', offset: 42}}; |
| assertDataToParams(data, '_handleBranchListFilterOffsetRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.BRANCHES, |
| detailType: 'branches', |
| repo: 4321, |
| offset: 42, |
| filter: 'foo', |
| }); |
| }); |
| |
| test('_handleBranchListFilterRoute', () => { |
| const data = {params: {repo: 4321, filter: 'foo'}}; |
| assertDataToParams(data, '_handleBranchListFilterRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.BRANCHES, |
| detailType: 'branches', |
| repo: 4321, |
| filter: 'foo', |
| }); |
| }); |
| }); |
| |
| suite('tag list routes', () => { |
| test('_handleTagListOffsetRoute', () => { |
| const data = {params: {0: 4321}}; |
| assertDataToParams(data, '_handleTagListOffsetRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.TAGS, |
| detailType: 'tags', |
| repo: 4321, |
| offset: 0, |
| filter: null, |
| }); |
| }); |
| |
| test('_handleTagListFilterOffsetRoute', () => { |
| const data = {params: {repo: 4321, filter: 'foo', offset: 42}}; |
| assertDataToParams(data, '_handleTagListFilterOffsetRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.TAGS, |
| detailType: 'tags', |
| repo: 4321, |
| offset: 42, |
| filter: 'foo', |
| }); |
| }); |
| |
| test('_handleTagListFilterRoute', () => { |
| const data = {params: {repo: 4321}}; |
| assertDataToParams(data, '_handleTagListFilterRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.TAGS, |
| detailType: 'tags', |
| repo: 4321, |
| filter: null, |
| }); |
| |
| data.params.filter = 'foo'; |
| assertDataToParams(data, '_handleTagListFilterRoute', { |
| view: Gerrit.Nav.View.REPO, |
| detail: Gerrit.Nav.RepoDetailView.TAGS, |
| detailType: 'tags', |
| repo: 4321, |
| filter: 'foo', |
| }); |
| }); |
| }); |
| |
| suite('repo list routes', () => { |
| test('_handleRepoListOffsetRoute', () => { |
| const data = {params: {}}; |
| assertDataToParams(data, '_handleRepoListOffsetRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-repo-list', |
| offset: 0, |
| filter: null, |
| openCreateModal: false, |
| }); |
| |
| data.params[1] = 42; |
| assertDataToParams(data, '_handleRepoListOffsetRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-repo-list', |
| offset: 42, |
| filter: null, |
| openCreateModal: false, |
| }); |
| |
| data.hash = 'create'; |
| assertDataToParams(data, '_handleRepoListOffsetRoute', { |
| view: Gerrit.Nav.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: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-repo-list', |
| offset: 42, |
| filter: 'foo', |
| }); |
| }); |
| |
| test('_handleRepoListFilterRoute', () => { |
| const data = {params: {}}; |
| assertDataToParams(data, '_handleRepoListFilterRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-repo-list', |
| filter: null, |
| }); |
| |
| data.params.filter = 'foo'; |
| assertDataToParams(data, '_handleRepoListFilterRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-repo-list', |
| filter: 'foo', |
| }); |
| }); |
| }); |
| }); |
| |
| suite('plugin routes', () => { |
| test('_handlePluginListOffsetRoute', () => { |
| const data = {params: {}}; |
| assertDataToParams(data, '_handlePluginListOffsetRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-plugin-list', |
| offset: 0, |
| filter: null, |
| }); |
| |
| data.params[1] = 42; |
| assertDataToParams(data, '_handlePluginListOffsetRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-plugin-list', |
| offset: 42, |
| filter: null, |
| }); |
| }); |
| |
| test('_handlePluginListFilterOffsetRoute', () => { |
| const data = {params: {filter: 'foo', offset: 42}}; |
| assertDataToParams(data, '_handlePluginListFilterOffsetRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-plugin-list', |
| offset: 42, |
| filter: 'foo', |
| }); |
| }); |
| |
| test('_handlePluginListFilterRoute', () => { |
| const data = {params: {}}; |
| assertDataToParams(data, '_handlePluginListFilterRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-plugin-list', |
| filter: null, |
| }); |
| |
| data.params.filter = 'foo'; |
| assertDataToParams(data, '_handlePluginListFilterRoute', { |
| view: Gerrit.Nav.View.ADMIN, |
| adminView: 'gr-plugin-list', |
| filter: 'foo', |
| }); |
| }); |
| |
| test('_handlePluginListRoute', () => { |
| const data = {params: {}}; |
| assertDataToParams(data, '_handlePluginListRoute', { |
| view: Gerrit.Nav.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', () => { |
| const normalizeRouteStub = sandbox.stub(element, |
| '_normalizeLegacyRouteParams'); |
| const ctx = { |
| params: [ |
| 1234, // 0 Change number |
| null, // 1 Unused |
| null, // 2 Unused |
| 6, // 3 Base patch number |
| null, // 4 Unused |
| 9, // 5 Patch number |
| ], |
| querystring: '', |
| }; |
| element._handleChangeLegacyRoute(ctx); |
| assert.isTrue(normalizeRouteStub.calledOnce); |
| assert.deepEqual(normalizeRouteStub.lastCall.args[0], { |
| changeNum: 1234, |
| basePatchNum: 6, |
| patchNum: 9, |
| view: Gerrit.Nav.View.CHANGE, |
| querystring: '', |
| }); |
| }); |
| |
| test('_handleDiffLegacyRoute', () => { |
| const normalizeRouteStub = sandbox.stub(element, |
| '_normalizeLegacyRouteParams'); |
| const ctx = { |
| params: [ |
| 1234, // 0 Change number |
| null, // 1 Unused |
| 3, // 2 Base patch number |
| null, // 3 Unused |
| 8, // 4 Patch number |
| 'foo/bar', // 5 Diff path |
| ], |
| path: '/c/1234/3..8/foo/bar', |
| hash: 'b123', |
| }; |
| element._handleDiffLegacyRoute(ctx); |
| assert.isFalse(redirectStub.called); |
| assert.isTrue(normalizeRouteStub.calledOnce); |
| assert.deepEqual(normalizeRouteStub.lastCall.args[0], { |
| changeNum: 1234, |
| basePatchNum: 3, |
| patchNum: 8, |
| view: Gerrit.Nav.View.DIFF, |
| path: 'foo/bar', |
| lineNum: 123, |
| leftSide: true, |
| }); |
| }); |
| |
| 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('_handleChangeOrDiffRoute', () => { |
| 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 = sandbox.stub(element, |
| '_normalizePatchRangeParams'); |
| sandbox.stub(element.$.restAPI, 'setInProjectLookup'); |
| }); |
| |
| test('needs redirect', () => { |
| normalizeRangeStub.returns(true); |
| sandbox.stub(element, '_generateUrl').returns('foo'); |
| const ctx = makeParams(null, ''); |
| element._handleChangeOrDiffRoute(ctx); |
| assert.isTrue(normalizeRangeStub.called); |
| assert.isFalse(setParamsStub.called); |
| assert.isTrue(redirectStub.calledOnce); |
| assert.isTrue(redirectStub.calledWithExactly('foo')); |
| }); |
| |
| test('change view', () => { |
| normalizeRangeStub.returns(false); |
| sandbox.stub(element, '_generateUrl').returns('foo'); |
| const ctx = makeParams(null, ''); |
| assertDataToParams(ctx, '_handleChangeOrDiffRoute', { |
| view: Gerrit.Nav.View.CHANGE, |
| project: 'foo/bar', |
| changeNum: 1234, |
| basePatchNum: 4, |
| patchNum: 7, |
| path: null, |
| }); |
| assert.isFalse(redirectStub.called); |
| assert.isTrue(normalizeRangeStub.called); |
| }); |
| |
| test('diff view', () => { |
| normalizeRangeStub.returns(false); |
| sandbox.stub(element, '_generateUrl').returns('foo'); |
| const ctx = makeParams('foo/bar/baz', 'b44'); |
| assertDataToParams(ctx, '_handleChangeOrDiffRoute', { |
| view: Gerrit.Nav.View.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('_handleDiffEditRoute', () => { |
| const normalizeRangeSpy = |
| sandbox.spy(element, '_normalizePatchRangeParams'); |
| sandbox.stub(element.$.restAPI, 'setInProjectLookup'); |
| const ctx = { |
| params: [ |
| 'foo/bar', // 0 Project |
| 1234, // 1 Change number |
| 'foo/bar/baz', // 2 File path |
| ], |
| }; |
| const appParams = { |
| project: 'foo/bar', |
| changeNum: 1234, |
| view: Gerrit.Nav.View.EDIT, |
| path: 'foo/bar/baz', |
| }; |
| |
| 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('_handlePluginScreen', () => { |
| const ctx = {params: ['foo', 'bar']}; |
| assertDataToParams(ctx, '_handlePluginScreen', { |
| view: Gerrit.Nav.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&'), |
| [['a', 'b'], ['e', 'f']]); |
| }); |
| }); |
| }); |
| </script> |