Convert all REPO_LIST routes to one unified regex pattern

Our goal is that all routes become regex patterns. At the moment some of
them are strings, which means that they will get special handling by
page.js, most importantly it will construct its own regex pattern for
tokens such as `:filter`. Apart from not wanting to deal with as few as
possible page.js specialities and trying to be consistent with all the
route patterns, these special page.js tokens also come along with a
fundamental problem: They use `[^/]+` and thus don't match `/`, which at
least for `repo` matching is a problem and creates extra complexity.

Release-Notes: skip
Change-Id: Ie84ad41ccb95049480cfbb6b34089f2d092c479f
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 65abc33..36bbfa4 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router.ts
@@ -169,11 +169,6 @@
   // Matches /admin/repos/<repos>,access.
   REPO_DASHBOARDS: /^\/admin\/repos\/(.+),dashboards$/,
 
-  // Matches /admin/repos[,<offset>][/].
-  REPO_LIST_OFFSET: /^\/admin\/repos(,(\d+))?(\/)?$/,
-  REPO_LIST_FILTER: '/admin/repos/q/filter::filter',
-  REPO_LIST_FILTER_OFFSET: '/admin/repos/q/filter::filter,:offset',
-
   // Matches /admin/repos/<repo>,branches[,<offset>].
   BRANCH_LIST_OFFSET: /^\/admin\/repos\/(.+),branches(,(.+))?$/,
   BRANCH_LIST_FILTER: '/admin/repos/:repo,branches/q/filter::filter',
@@ -191,6 +186,8 @@
   PLUGIN_LIST: /^\/admin\/plugins\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
   // Matches /admin/groups with optional filter and offset.
   GROUP_LIST: /^\/admin\/groups\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
+  // Matches /admin/repos with optional filter and offset.
+  REPO_LIST: /^\/admin\/repos\/?(?:\/q\/filter:(.*?))?(?:,(\d+))?$/,
 
   QUERY: /^\/q\/([^,]+)(,(\d+))?$/,
 
@@ -791,22 +788,8 @@
       true
     );
 
-    this.mapRoute(
-      RoutePattern.REPO_LIST_OFFSET,
-      'handleRepoListOffsetRoute',
-      ctx => this.handleRepoListOffsetRoute(ctx)
-    );
-
-    this.mapRoute(
-      RoutePattern.REPO_LIST_FILTER_OFFSET,
-      'handleRepoListFilterOffsetRoute',
-      ctx => this.handleRepoListFilterOffsetRoute(ctx)
-    );
-
-    this.mapRoute(
-      RoutePattern.REPO_LIST_FILTER,
-      'handleRepoListFilterRoute',
-      ctx => this.handleRepoListFilterRoute(ctx)
+    this.mapRoute(RoutePattern.REPO_LIST, 'handleRepoListRoute', ctx =>
+      this.handleRepoListRoute(ctx)
     );
 
     this.mapRoute(RoutePattern.REPO, 'handleRepoRoute', ctx =>
@@ -1302,39 +1285,14 @@
     this.repoViewModel.setState(state);
   }
 
-  handleRepoListOffsetRoute(ctx: PageContext) {
+  handleRepoListRoute(ctx: PageContext) {
     const state: AdminViewState = {
       view: GerritView.ADMIN,
       adminView: AdminChildView.REPOS,
       offset: ctx.params[1] ?? '0',
-      filter: null,
-      openCreateModal: ctx.hash === 'create',
-    };
-    // Note that router model view must be updated before view models.
-    this.setState(state);
-    this.adminViewModel.setState(state);
-  }
-
-  handleRepoListFilterOffsetRoute(ctx: PageContext) {
-    const state: AdminViewState = {
-      view: GerritView.ADMIN,
-      adminView: AdminChildView.REPOS,
-      offset: ctx.params['offset'] ?? '0',
-      filter: ctx.params['filter'],
-      openCreateModal: false,
-    };
-    // Note that router model view must be updated before view models.
-    this.setState(state);
-    this.adminViewModel.setState(state);
-  }
-
-  handleRepoListFilterRoute(ctx: PageContext) {
-    const state: AdminViewState = {
-      view: GerritView.ADMIN,
-      adminView: AdminChildView.REPOS,
-      offset: '0',
-      filter: ctx.params['filter'] ?? null,
-      openCreateModal: false,
+      filter: ctx.params[0] ?? null,
+      openCreateModal:
+        !ctx.params[0] && !ctx.params[1] && ctx.hash === 'create',
     };
     // Note that router model view must be updated before view models.
     this.setState(state);
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
index 50030a5..c9eb52e 100644
--- a/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
+++ b/polygerrit-ui/app/elements/core/gr-router/gr-router_test.ts
@@ -188,9 +188,7 @@
       'handleRepoAccessRoute',
       'handleRepoDashboardsRoute',
       'handleRepoGeneralRoute',
-      'handleRepoListFilterOffsetRoute',
-      'handleRepoListFilterRoute',
-      'handleRepoListOffsetRoute',
+      'handleRepoListRoute',
       'handleRepoRoute',
       'handleQueryLegacySuffixRoute',
       'handleQueryRoute',
@@ -752,38 +750,33 @@
         });
       });
 
-      suite('REPO_LIST_*', () => {
-        test('REPO_LIST_OFFSET', async () => {
-          // REPO_LIST_OFFSET: /^\/admin\/repos(,(\d+))?(\/)?$/,
-          await checkUrlToState('/admin/repos', {
-            ...createAdminReposViewState(),
-          });
-          await checkUrlToState('/admin/repos,42', {
-            ...createAdminReposViewState(),
-            offset: '42',
-          });
-          await checkUrlToState('/admin/repos,42#create', {
-            ...createAdminReposViewState(),
-            offset: '42',
-            openCreateModal: true,
-          });
+      test('REPO_LIST', async () => {
+        await checkUrlToState('/admin/repos', {
+          ...createAdminReposViewState(),
         });
-
-        test('REPO_LIST_FILTER_OFFSET', async () => {
-          // REPO_LIST_FILTER_OFFSET: '/admin/repos/q/filter::filter,:offset',
-          await checkUrlToState('/admin/repos/q/filter:foo,42', {
-            ...createAdminReposViewState(),
-            offset: '42',
-            filter: 'foo',
-          });
+        await checkUrlToState('/admin/repos/', {
+          ...createAdminReposViewState(),
         });
-
-        test('REPO_LIST_FILTER', async () => {
-          // REPO_LIST_FILTER: '/admin/repos/q/filter::filter',
-          await checkUrlToState('/admin/repos/q/filter:foo', {
-            ...createAdminReposViewState(),
-            filter: 'foo',
-          });
+        await checkUrlToState('/admin/repos,42', {
+          ...createAdminReposViewState(),
+          offset: '42',
+        });
+        await checkUrlToState('/admin/repos#create', {
+          ...createAdminReposViewState(),
+          openCreateModal: true,
+        });
+        await checkUrlToState('/admin/repos/q/filter:foo', {
+          ...createAdminReposViewState(),
+          filter: 'foo',
+        });
+        await checkUrlToState('/admin/repos/q/filter:foo/%2F%20%2525%252F', {
+          ...createAdminReposViewState(),
+          filter: 'foo// %/',
+        });
+        await checkUrlToState('/admin/repos/q/filter:foo,42', {
+          ...createAdminReposViewState(),
+          filter: 'foo',
+          offset: '42',
         });
       });
     });