Merge "Move PROJECT_DASHBOARD_ROUTE into view model"
diff --git a/polygerrit-ui/app/elements/core/gr-router/gr-page.ts b/polygerrit-ui/app/elements/core/gr-router/gr-page.ts
index 3af8207..1d2a272 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-page.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-page.ts
@@ -280,6 +280,7 @@
export class PageContext {
/**
* Includes everything: base, path, query and hash.
+ * NOT decoded.
*/
canonicalPath = '';
@@ -287,18 +288,21 @@
* Does not include base path.
* Does not include hash.
* Includes query string.
+ * NOT decoded.
*/
path = '';
- /** Does not include hash. */
+ /** Decoded. Does not include hash. */
querystring = '';
+ /** Decoded. */
hash = '';
/**
* Regular expression matches of capturing groups. The first entry params[0]
* corresponds to the first capturing group. The entire matched string is not
* returned in this array.
+ * Each param is double decoded.
*/
params: string[] = [];
@@ -346,17 +350,24 @@
replaceState() {
window.history.replaceState(this.state, this.title, this.canonicalPath);
}
+
+ match(re: RegExp) {
+ const qsIndex = this.path.indexOf('?');
+ const pathname = qsIndex !== -1 ? this.path.slice(0, qsIndex) : this.path;
+ const matches = re.exec(decodeURIComponent(pathname));
+ if (matches) {
+ this.params = matches
+ .slice(1)
+ .map(match => decodeURIComponentString(match));
+ }
+ return !!matches;
+ }
}
function createRoute(re: RegExp, fn: Function) {
return (ctx: PageContext, next: Function) => {
- const qsIndex = ctx.path.indexOf('?');
- const pathname = qsIndex !== -1 ? ctx.path.slice(0, qsIndex) : ctx.path;
- const matches = re.exec(decodeURIComponent(pathname));
+ const matches = ctx.match(re);
if (matches) {
- ctx.params = matches
- .slice(1)
- .map(match => decodeURIComponentString(match));
fn(ctx, next);
} else {
next();
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 997d9d5..da4a576 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -14,7 +14,6 @@
import {assertIsDefined} from '../../../utils/common-util';
import {
BasePatchSetNum,
- DashboardId,
GroupId,
NumericChangeId,
RevisionPatchSetNum,
@@ -73,6 +72,7 @@
import {
DashboardViewModel,
DashboardViewState,
+ PROJECT_DASHBOARD_ROUTE,
} from '../../../models/views/dashboard';
import {
SettingsViewModel,
@@ -107,7 +107,6 @@
DASHBOARD: /^\/dashboard\/(.+)$/,
CUSTOM_DASHBOARD: /^\/dashboard\/?$/,
- PROJECT_DASHBOARD: /^\/p\/(.+)\/\+\/dashboard\/(.+)/,
LEGACY_PROJECT_DASHBOARD: /^\/projects\/(.+),dashboards\/(.+)/,
AGREEMENTS: /^\/settings\/agreements\/?/,
@@ -635,10 +634,10 @@
ctx => this.handleCustomDashboardRoute(ctx)
);
- this.mapRoute(
- RoutePattern.PROJECT_DASHBOARD,
- 'handleProjectDashboardRoute',
- ctx => this.handleProjectDashboardRoute(ctx)
+ this.mapRouteState(
+ PROJECT_DASHBOARD_ROUTE,
+ this.dashboardViewModel,
+ 'handleProjectDashboardRoute'
);
this.mapRoute(
@@ -1008,19 +1007,6 @@
return Promise.resolve();
}
- handleProjectDashboardRoute(ctx: PageContext) {
- const project = ctx.params[0] as RepoName;
- const state: DashboardViewState = {
- view: GerritView.DASHBOARD,
- project,
- dashboard: decodeURIComponent(ctx.params[1]) as DashboardId,
- };
- // Note that router model view must be updated before view models.
- this.setState(state);
- this.dashboardViewModel.setState(state);
- this.reporting.setRepoName(project);
- }
-
handleLegacyProjectDashboardRoute(ctx: PageContext) {
this.redirect('/p/' + ctx.params[0] + '/+/dashboard/' + ctx.params[1]);
}
diff --git a/polygerrit-ui/app/models/views/admin_test.ts b/polygerrit-ui/app/models/views/admin_test.ts
index b6089af..0881018 100644
--- a/polygerrit-ui/app/models/views/admin_test.ts
+++ b/polygerrit-ui/app/models/views/admin_test.ts
@@ -3,28 +3,34 @@
* Copyright 2022 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
-import {assert} from '@open-wc/testing';
-import {PageContext} from '../../elements/core/gr-router/gr-page';
import {GerritView} from '../../services/router/router-model';
import '../../test/common-test-setup';
-import {AdminChildView, PLUGIN_LIST_ROUTE} from './admin';
+import {assertRouteFalse, assertRouteState} from '../../test/test-utils';
+import {
+ AdminChildView,
+ AdminViewState,
+ createAdminUrl,
+ PLUGIN_LIST_ROUTE,
+} from './admin';
suite('admin view model', () => {
suite('routes', () => {
test('PLUGIN_LIST', () => {
- const {urlPattern: pattern, createState} = PLUGIN_LIST_ROUTE;
+ assertRouteFalse(PLUGIN_LIST_ROUTE, 'admin/plugins');
+ assertRouteFalse(PLUGIN_LIST_ROUTE, '//admin/plugins');
+ assertRouteFalse(PLUGIN_LIST_ROUTE, '//admin/plugins?');
+ assertRouteFalse(PLUGIN_LIST_ROUTE, '/admin/plugins//');
- assert.isTrue(pattern.test('/admin/plugins'));
- assert.isTrue(pattern.test('/admin/plugins/'));
- assert.isFalse(pattern.test('admin/plugins'));
- assert.isFalse(pattern.test('//admin/plugins'));
- assert.isFalse(pattern.test('//admin/plugins?'));
- assert.isFalse(pattern.test('/admin/plugins//'));
-
- assert.deepEqual(createState(new PageContext('')), {
+ const state: AdminViewState = {
view: GerritView.ADMIN,
adminView: AdminChildView.PLUGINS,
- });
+ };
+ assertRouteState<AdminViewState>(
+ PLUGIN_LIST_ROUTE,
+ '/admin/plugins',
+ state,
+ createAdminUrl
+ );
});
});
});
diff --git a/polygerrit-ui/app/models/views/dashboard.ts b/polygerrit-ui/app/models/views/dashboard.ts
index 74523db..d2e7995 100644
--- a/polygerrit-ui/app/models/views/dashboard.ts
+++ b/polygerrit-ui/app/models/views/dashboard.ts
@@ -10,7 +10,21 @@
import {encodeURL, getBaseUrl} from '../../utils/url-util';
import {define} from '../dependency';
import {Model} from '../model';
-import {ViewState} from './base';
+import {Route, ViewState} from './base';
+
+export const PROJECT_DASHBOARD_ROUTE: Route<DashboardViewState> = {
+ urlPattern: /^\/p\/(.+)\/\+\/dashboard\/(.+)/,
+ createState: ctx => {
+ const project = (ctx.params[0] ?? '') as RepoName;
+ const dashboard = (ctx.params[1] ?? '') as DashboardId;
+ const state: DashboardViewState = {
+ view: GerritView.DASHBOARD,
+ project,
+ dashboard,
+ };
+ return state;
+ },
+};
export interface DashboardViewState extends ViewState {
view: GerritView.DASHBOARD;
diff --git a/polygerrit-ui/app/models/views/dashboard_test.ts b/polygerrit-ui/app/models/views/dashboard_test.ts
index a7620dd..9509977 100644
--- a/polygerrit-ui/app/models/views/dashboard_test.ts
+++ b/polygerrit-ui/app/models/views/dashboard_test.ts
@@ -5,11 +5,36 @@
*/
import {assert} from '@open-wc/testing';
import {RepoName} from '../../api/rest-api';
+import {GerritView} from '../../services/router/router-model';
import '../../test/common-test-setup';
+import {assertRouteFalse, assertRouteState} from '../../test/test-utils';
import {DashboardId} from '../../types/common';
-import {createDashboardUrl} from './dashboard';
+import {
+ createDashboardUrl,
+ DashboardViewState,
+ PROJECT_DASHBOARD_ROUTE,
+} from './dashboard';
suite('dashboard view state tests', () => {
+ suite('routes', () => {
+ test('PROJECT_DASHBOARD_ROUTE', () => {
+ assertRouteFalse(PROJECT_DASHBOARD_ROUTE, '/p//+/dashboard/qwer');
+ assertRouteFalse(PROJECT_DASHBOARD_ROUTE, '/p/asdf/+/dashboard/');
+
+ const state: DashboardViewState = {
+ view: GerritView.DASHBOARD,
+ project: 'asdf' as RepoName,
+ dashboard: 'qwer' as DashboardId,
+ };
+ assertRouteState(
+ PROJECT_DASHBOARD_ROUTE,
+ '/p/asdf/+/dashboard/qwer',
+ state,
+ createDashboardUrl
+ );
+ });
+ });
+
suite('createDashboardUrl()', () => {
test('self dashboard', () => {
assert.equal(createDashboardUrl({}), '/dashboard/self');
diff --git a/polygerrit-ui/app/test/test-utils.ts b/polygerrit-ui/app/test/test-utils.ts
index c400d9c..19c3a7b 100644
--- a/polygerrit-ui/app/test/test-utils.ts
+++ b/polygerrit-ui/app/test/test-utils.ts
@@ -14,6 +14,8 @@
import {Observable} from 'rxjs';
import {filter, take, timeout} from 'rxjs/operators';
import {assert} from '@open-wc/testing';
+import {Route, ViewState} from '../models/views/base';
+import {PageContext} from '../elements/core/gr-router/gr-page';
export {query, queryAll, queryAndAssert} from '../utils/common-util';
export interface MockPromise<T> extends Promise<T> {
@@ -328,3 +330,26 @@
};
return new Proxy(obj, handler) as unknown as T;
}
+
+export function assertRouteState<T extends ViewState>(
+ route: Route<T>,
+ path: string,
+ state: T,
+ createUrl: (state: T) => string
+) {
+ const {urlPattern, createState} = route;
+ const ctx = new PageContext(path);
+ const matches = ctx.match(urlPattern);
+ assert.isTrue(matches);
+ assert.deepEqual(createState(ctx), state);
+ assert.equal(path, createUrl(state));
+}
+
+export function assertRouteFalse<T extends ViewState>(
+ route: Route<T>,
+ path: string
+) {
+ const ctx = new PageContext(path);
+ const matches = ctx.match(route.urlPattern);
+ assert.isFalse(matches);
+}