Move admin-nav-util into models/views/admin.ts
We don't want utilities to depends on `models/`, so model specific
utilities must be moved into `models` directly.
Release-Notes: skip
Change-Id: I73aa8f18c1488539b55bdd282cc3ecc911dd94c1
diff --git a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
index 1be5fa3..eef2a44 100644
--- a/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
+++ b/polygerrit-ui/app/elements/admin/gr-admin-view/gr-admin-view.ts
@@ -20,12 +20,6 @@
import {getBaseUrl} from '../../../utils/url-util';
import {navigationToken} from '../../core/gr-navigation/gr-navigation';
import {
- AdminNavLinksOption,
- getAdminLinks,
- NavLink,
- SubsectionInterface,
-} from '../../../utils/admin-nav-util';
-import {
AccountDetailInfo,
GroupId,
GroupName,
@@ -48,6 +42,10 @@
AdminChildView,
adminViewModelToken,
AdminViewState,
+ AdminNavLinksOption,
+ getAdminLinks,
+ NavLink,
+ SubsectionInterface,
} from '../../../models/views/admin';
import {
GroupDetailView,
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.ts b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.ts
index 46fa062..53826d5 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.ts
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.ts
@@ -11,7 +11,7 @@
import '../gr-account-dropdown/gr-account-dropdown';
import '../gr-smart-search/gr-smart-search';
import {getBaseUrl, getDocsBaseUrl} from '../../../utils/url-util';
-import {getAdminLinks, NavLink} from '../../../utils/admin-nav-util';
+import {getAdminLinks, NavLink} from '../../../models/views/admin';
import {
AccountDetailInfo,
DropdownLink,
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.ts b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.ts
index 7eb19f0..eced28c 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.ts
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header_test.ts
@@ -17,7 +17,7 @@
createGerritInfo,
createServerInfo,
} from '../../../test/test-data-generators';
-import {NavLink} from '../../../utils/admin-nav-util';
+import {NavLink} from '../../../models/views/admin';
import {ServerInfo, TopMenuItemInfo} from '../../../types/common';
import {AuthType} from '../../../constants/constants';
import {fixture, html, assert} from '@open-wc/testing';
diff --git a/polygerrit-ui/app/models/views/admin.ts b/polygerrit-ui/app/models/views/admin.ts
index de9ec5d..3456e8f 100644
--- a/polygerrit-ui/app/models/views/admin.ts
+++ b/polygerrit-ui/app/models/views/admin.ts
@@ -8,6 +8,46 @@
import {define} from '../dependency';
import {Model} from '../model';
import {Route, ViewState} from './base';
+import {
+ RepoName,
+ GroupId,
+ AccountDetailInfo,
+ AccountCapabilityInfo,
+} from '../../types/common';
+import {hasOwnProperty} from '../../utils/common-util';
+import {MenuLink} from '../../api/admin';
+import {createGroupUrl, GroupDetailView} from './group';
+import {createRepoUrl, RepoDetailView} from './repo';
+
+export interface SubsectionInterface {
+ name: string;
+ view: GerritView;
+ detailType?: RepoDetailView | GroupDetailView;
+ url?: string;
+ children?: SubsectionInterface[];
+}
+
+export interface AdminNavLinksOption {
+ repoName?: RepoName;
+ groupId?: GroupId;
+ groupName?: string;
+ groupIsInternal?: boolean;
+ isAdmin?: boolean;
+ groupOwner?: boolean;
+}
+
+export interface NavLink {
+ name: string;
+ noBaseUrl: boolean;
+ url: string;
+ view?: GerritView | AdminChildView;
+ viewableToAll?: boolean;
+ section?: string;
+ capability?: string;
+ target?: string | null;
+ subsection?: SubsectionInterface;
+ children?: SubsectionInterface[];
+}
export const PLUGIN_LIST_ROUTE: Route<AdminViewState> = {
urlPattern: /^\/admin\/plugins(\/)?$/,
@@ -25,6 +65,208 @@
GROUPS = 'gr-admin-group-list',
PLUGINS = 'gr-plugin-list',
}
+const ADMIN_LINKS: NavLink[] = [
+ {
+ name: 'Repositories',
+ noBaseUrl: true,
+ url: createAdminUrl({adminView: AdminChildView.REPOS}),
+ view: 'gr-repo-list' as GerritView,
+ viewableToAll: true,
+ },
+ {
+ name: 'Groups',
+ section: 'Groups',
+ noBaseUrl: true,
+ url: createAdminUrl({adminView: AdminChildView.GROUPS}),
+ view: 'gr-admin-group-list' as GerritView,
+ },
+ {
+ name: 'Plugins',
+ capability: 'viewPlugins',
+ section: 'Plugins',
+ noBaseUrl: true,
+ url: createAdminUrl({adminView: AdminChildView.PLUGINS}),
+ view: 'gr-plugin-list' as GerritView,
+ },
+];
+
+export interface AdminLink {
+ url: string;
+ text: string;
+ capability: string | null;
+ noBaseUrl: boolean;
+ view: null;
+ viewableToAll: boolean;
+ target: '_blank' | null;
+}
+
+export interface AdminLinks {
+ links: NavLink[];
+ expandedSection?: SubsectionInterface;
+}
+
+export function getAdminLinks(
+ account: AccountDetailInfo | undefined,
+ getAccountCapabilities: () => Promise<AccountCapabilityInfo>,
+ getAdminMenuLinks: () => MenuLink[],
+ options?: AdminNavLinksOption
+): Promise<AdminLinks> {
+ if (!account) {
+ return Promise.resolve(
+ filterLinks(link => !!link.viewableToAll, getAdminMenuLinks, options)
+ );
+ }
+ return getAccountCapabilities().then(capabilities =>
+ filterLinks(
+ link => !link.capability || hasOwnProperty(capabilities, link.capability),
+ getAdminMenuLinks,
+ options
+ )
+ );
+}
+
+function filterLinks(
+ filterFn: (link: NavLink) => boolean,
+ getAdminMenuLinks: () => MenuLink[],
+ options?: AdminNavLinksOption
+): AdminLinks {
+ let links: NavLink[] = ADMIN_LINKS.slice(0);
+ let expandedSection: SubsectionInterface | undefined = undefined;
+
+ const isExternalLink = (link: MenuLink) => link.url[0] !== '/';
+
+ // Append top-level links that are defined by plugins.
+ links.push(
+ ...getAdminMenuLinks().map((link: MenuLink) => {
+ return {
+ url: link.url,
+ name: link.text,
+ capability: link.capability || undefined,
+ noBaseUrl: !isExternalLink(link),
+ view: undefined,
+ viewableToAll: !link.capability,
+ target: isExternalLink(link) ? '_blank' : null,
+ };
+ })
+ );
+
+ links = links.filter(filterFn);
+
+ const filteredLinks: NavLink[] = [];
+ const repoName = options && options.repoName;
+ const groupId = options && options.groupId;
+ const groupName = options && options.groupName;
+ const groupIsInternal = options && options.groupIsInternal;
+ const isAdmin = options && options.isAdmin;
+ const groupOwner = options && options.groupOwner;
+
+ // Don't bother to get sub-navigation items if only the top level links
+ // are needed. This is used by the main header dropdown.
+ if (!repoName && !groupId) {
+ return {links, expandedSection};
+ }
+
+ // Otherwise determine the full set of links and return both the full
+ // set in addition to the subsection that should be displayed if it
+ // exists.
+ for (const link of links) {
+ const linkCopy = {...link};
+ if (linkCopy.name === 'Repositories' && repoName) {
+ linkCopy.subsection = getRepoSubsections(repoName);
+ expandedSection = linkCopy.subsection;
+ } else if (linkCopy.name === 'Groups' && groupId && groupName) {
+ linkCopy.subsection = getGroupSubsections(
+ groupId,
+ groupName,
+ groupIsInternal,
+ isAdmin,
+ groupOwner
+ );
+ expandedSection = linkCopy.subsection;
+ }
+ filteredLinks.push(linkCopy);
+ }
+ return {links: filteredLinks, expandedSection};
+}
+
+export function getGroupSubsections(
+ groupId: GroupId,
+ groupName: string,
+ groupIsInternal?: boolean,
+ isAdmin?: boolean,
+ groupOwner?: boolean
+) {
+ const children: SubsectionInterface[] = [];
+ const subsection: SubsectionInterface = {
+ name: groupName,
+ view: GerritView.GROUP,
+ url: createGroupUrl({groupId}),
+ children,
+ };
+ if (groupIsInternal) {
+ children.push({
+ name: 'Members',
+ detailType: GroupDetailView.MEMBERS,
+ view: GerritView.GROUP,
+ url: createGroupUrl({groupId, detail: GroupDetailView.MEMBERS}),
+ });
+ }
+ if (groupIsInternal && (isAdmin || groupOwner)) {
+ children.push({
+ name: 'Audit Log',
+ detailType: GroupDetailView.LOG,
+ view: GerritView.GROUP,
+ url: createGroupUrl({groupId, detail: GroupDetailView.LOG}),
+ });
+ }
+ return subsection;
+}
+
+export function getRepoSubsections(repo: RepoName) {
+ return {
+ name: repo,
+ view: GerritView.REPO,
+ children: [
+ {
+ name: 'General',
+ view: GerritView.REPO,
+ detailType: RepoDetailView.GENERAL,
+ url: createRepoUrl({repo, detail: RepoDetailView.GENERAL}),
+ },
+ {
+ name: 'Access',
+ view: GerritView.REPO,
+ detailType: RepoDetailView.ACCESS,
+ url: createRepoUrl({repo, detail: RepoDetailView.ACCESS}),
+ },
+ {
+ name: 'Commands',
+ view: GerritView.REPO,
+ detailType: RepoDetailView.COMMANDS,
+ url: createRepoUrl({repo, detail: RepoDetailView.COMMANDS}),
+ },
+ {
+ name: 'Branches',
+ view: GerritView.REPO,
+ detailType: RepoDetailView.BRANCHES,
+ url: createRepoUrl({repo, detail: RepoDetailView.BRANCHES}),
+ },
+ {
+ name: 'Tags',
+ view: GerritView.REPO,
+ detailType: RepoDetailView.TAGS,
+ url: createRepoUrl({repo, detail: RepoDetailView.TAGS}),
+ },
+ {
+ name: 'Dashboards',
+ view: GerritView.REPO,
+ detailType: RepoDetailView.DASHBOARDS,
+ url: createRepoUrl({repo, detail: RepoDetailView.DASHBOARDS}),
+ },
+ ],
+ };
+}
+
export interface AdminViewState extends ViewState {
view: GerritView.ADMIN;
adminView: AdminChildView;
diff --git a/polygerrit-ui/app/models/views/admin_test.ts b/polygerrit-ui/app/models/views/admin_test.ts
index 0881018..5d142bf 100644
--- a/polygerrit-ui/app/models/views/admin_test.ts
+++ b/polygerrit-ui/app/models/views/admin_test.ts
@@ -3,15 +3,349 @@
* Copyright 2022 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
-import {GerritView} from '../../services/router/router-model';
+import {assert} from '@open-wc/testing';
import '../../test/common-test-setup';
import {assertRouteFalse, assertRouteState} from '../../test/test-utils';
+import {GerritView} from '../../services/router/router-model';
import {
AdminChildView,
AdminViewState,
createAdminUrl,
PLUGIN_LIST_ROUTE,
+ AdminNavLinksOption,
+ getAdminLinks,
} from './admin';
+import {
+ AccountDetailInfo,
+ GroupId,
+ RepoName,
+ Timestamp,
+} from '../../api/rest-api';
+
+suite('admin links', () => {
+ let capabilityStub: sinon.SinonStub;
+ let menuLinkStub: sinon.SinonStub;
+
+ setup(() => {
+ capabilityStub = sinon.stub();
+ menuLinkStub = sinon.stub().returns([]);
+ });
+
+ const testAdminLinks = async (
+ account: AccountDetailInfo | undefined,
+ options: AdminNavLinksOption | undefined,
+ expected: any
+ ) => {
+ const res = await getAdminLinks(
+ account,
+ capabilityStub,
+ menuLinkStub,
+ options
+ );
+
+ assert.equal(expected.totalLength, res.links.length);
+ assert.equal(res.links[0].name, 'Repositories');
+ // Repos
+ if (expected.groupListShown) {
+ assert.equal(res.links[1].name, 'Groups');
+ }
+
+ if (expected.pluginListShown) {
+ assert.equal(res.links[2].name, 'Plugins');
+ assert.isNotOk(res.links[2].subsection);
+ }
+
+ if (expected.projectPageShown) {
+ assert.isOk(res.links[0].subsection);
+ assert.equal(res.links[0].subsection!.children!.length, 6);
+ } else {
+ assert.isNotOk(res.links[0].subsection);
+ }
+ // Groups
+ if (expected.groupPageShown) {
+ assert.isOk(res.links[1].subsection);
+ assert.equal(
+ res.links[1].subsection!.children!.length,
+ expected.groupSubpageLength
+ );
+ } else if (expected.totalLength > 1) {
+ assert.isNotOk(res.links[1].subsection);
+ }
+
+ if (expected.pluginGeneratedLinks) {
+ for (const link of expected.pluginGeneratedLinks) {
+ const linkMatch = res.links.find(
+ l => l.url === link.url && l.name === link.text
+ );
+ assert.isOk(linkMatch);
+
+ // External links should open in new tab.
+ if (link.url[0] !== '/') {
+ assert.equal(linkMatch!.target, '_blank');
+ } else {
+ assert.isNotOk(linkMatch!.target);
+ }
+ }
+ }
+
+ // Current section
+ if (expected.projectPageShown || expected.groupPageShown) {
+ assert.isOk(res.expandedSection);
+ assert.isOk(res.expandedSection!.children);
+ } else {
+ assert.isNotOk(res.expandedSection);
+ }
+ if (expected.projectPageShown) {
+ assert.equal(res.expandedSection!.name, 'my-repo');
+ assert.equal(res.expandedSection!.children!.length, 6);
+ } else if (expected.groupPageShown) {
+ assert.equal(res.expandedSection!.name, 'my-group');
+ assert.equal(
+ res.expandedSection!.children!.length,
+ expected.groupSubpageLength
+ );
+ }
+ };
+
+ suite('logged out', () => {
+ let account: AccountDetailInfo;
+ let expected: any;
+
+ setup(() => {
+ expected = {
+ groupListShown: false,
+ groupPageShown: false,
+ pluginListShown: false,
+ };
+ });
+
+ test('without a specific repo or group', async () => {
+ let options;
+ expected = Object.assign(expected, {
+ totalLength: 1,
+ projectPageShown: false,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+
+ test('with a repo', async () => {
+ const options = {repoName: 'my-repo' as RepoName};
+ expected = Object.assign(expected, {
+ totalLength: 1,
+ projectPageShown: true,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+
+ test('with plugin generated links', async () => {
+ let options;
+ const generatedLinks = [
+ {text: 'internal link text', url: '/internal/link/url'},
+ {text: 'external link text', url: 'http://external/link/url'},
+ ];
+ menuLinkStub.returns(generatedLinks);
+ expected = Object.assign(expected, {
+ totalLength: 3,
+ projectPageShown: false,
+ pluginGeneratedLinks: generatedLinks,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+ });
+
+ suite('no plugin capability logged in', () => {
+ const account = {
+ name: 'test-user',
+ registered_on: '' as Timestamp,
+ };
+ let expected: any;
+
+ setup(() => {
+ expected = {
+ totalLength: 2,
+ pluginListShown: false,
+ };
+ capabilityStub.returns(Promise.resolve({}));
+ });
+
+ test('without a specific project or group', async () => {
+ let options;
+ expected = Object.assign(expected, {
+ projectPageShown: false,
+ groupListShown: true,
+ groupPageShown: false,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+
+ test('with a repo', async () => {
+ const account = {
+ name: 'test-user',
+ registered_on: '' as Timestamp,
+ };
+ const options = {repoName: 'my-repo' as RepoName};
+ expected = Object.assign(expected, {
+ projectPageShown: true,
+ groupListShown: true,
+ groupPageShown: false,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+ });
+
+ suite('view plugin capability logged in', () => {
+ const account = {
+ name: 'test-user',
+ registered_on: '' as Timestamp,
+ };
+ let expected: any;
+
+ setup(() => {
+ capabilityStub.returns(Promise.resolve({viewPlugins: true}));
+ expected = {
+ totalLength: 3,
+ groupListShown: true,
+ pluginListShown: true,
+ };
+ });
+
+ test('without a specific repo or group', async () => {
+ let options;
+ expected = Object.assign(expected, {
+ projectPageShown: false,
+ groupPageShown: false,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+
+ test('with a repo', async () => {
+ const options = {repoName: 'my-repo' as RepoName};
+ expected = Object.assign(expected, {
+ projectPageShown: true,
+ groupPageShown: false,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+
+ test('admin with internal group', async () => {
+ const options = {
+ groupId: 'a15262' as GroupId,
+ groupName: 'my-group',
+ groupIsInternal: true,
+ isAdmin: true,
+ groupOwner: false,
+ };
+ expected = Object.assign(expected, {
+ projectPageShown: false,
+ groupPageShown: true,
+ groupSubpageLength: 2,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+
+ test('group owner with internal group', async () => {
+ const options = {
+ groupId: 'a15262' as GroupId,
+ groupName: 'my-group',
+ groupIsInternal: true,
+ isAdmin: false,
+ groupOwner: true,
+ };
+ expected = Object.assign(expected, {
+ projectPageShown: false,
+ groupPageShown: true,
+ groupSubpageLength: 2,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+
+ test('non owner or admin with internal group', async () => {
+ const options = {
+ groupId: 'a15262' as GroupId,
+ groupName: 'my-group',
+ groupIsInternal: true,
+ isAdmin: false,
+ groupOwner: false,
+ };
+ expected = Object.assign(expected, {
+ projectPageShown: false,
+ groupPageShown: true,
+ groupSubpageLength: 1,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+
+ test('admin with external group', async () => {
+ const options = {
+ groupId: 'a15262' as GroupId,
+ groupName: 'my-group',
+ groupIsInternal: false,
+ isAdmin: true,
+ groupOwner: true,
+ };
+ expected = Object.assign(expected, {
+ projectPageShown: false,
+ groupPageShown: true,
+ groupSubpageLength: 0,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+ });
+
+ suite('view plugin screen with plugin capability', () => {
+ const account = {
+ name: 'test-user',
+ registered_on: '' as Timestamp,
+ };
+ let expected: any;
+
+ setup(() => {
+ capabilityStub.returns(Promise.resolve({pluginCapability: true}));
+ expected = {};
+ });
+
+ test('with plugin with capabilities', async () => {
+ let options;
+ const generatedLinks = [
+ {text: 'without capability', url: '/without'},
+ {text: 'with capability', url: '/with', capability: 'pluginCapability'},
+ ];
+ menuLinkStub.returns(generatedLinks);
+ expected = Object.assign(expected, {
+ totalLength: 4,
+ pluginGeneratedLinks: generatedLinks,
+ });
+ await testAdminLinks(account, options, expected);
+ });
+ });
+
+ suite('view plugin screen without plugin capability', () => {
+ const account = {
+ name: 'test-user',
+ registered_on: '' as Timestamp,
+ };
+ let expected: any;
+
+ setup(() => {
+ capabilityStub.returns(Promise.resolve({}));
+ expected = {};
+ });
+
+ test('with plugin with capabilities', async () => {
+ let options;
+ const generatedLinks = [
+ {text: 'without capability', url: '/without'},
+ {text: 'with capability', url: '/with', capability: 'pluginCapability'},
+ ];
+ menuLinkStub.returns(generatedLinks);
+ expected = Object.assign(expected, {
+ totalLength: 3,
+ pluginGeneratedLinks: [generatedLinks[0]],
+ });
+ await testAdminLinks(account, options, expected);
+ });
+ });
+});
suite('admin view model', () => {
suite('routes', () => {
diff --git a/polygerrit-ui/app/utils/admin-nav-util.ts b/polygerrit-ui/app/utils/admin-nav-util.ts
deleted file mode 100644
index 7916799..0000000
--- a/polygerrit-ui/app/utils/admin-nav-util.ts
+++ /dev/null
@@ -1,249 +0,0 @@
-/**
- * @license
- * Copyright 2018 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {
- RepoName,
- GroupId,
- AccountDetailInfo,
- AccountCapabilityInfo,
-} from '../types/common';
-import {hasOwnProperty} from './common-util';
-import {GerritView} from '../services/router/router-model';
-import {MenuLink} from '../api/admin';
-import {AdminChildView, createAdminUrl} from '../models/views/admin';
-import {createGroupUrl, GroupDetailView} from '../models/views/group';
-import {createRepoUrl, RepoDetailView} from '../models/views/repo';
-
-const ADMIN_LINKS: NavLink[] = [
- {
- name: 'Repositories',
- noBaseUrl: true,
- url: createAdminUrl({adminView: AdminChildView.REPOS}),
- view: 'gr-repo-list' as GerritView,
- viewableToAll: true,
- },
- {
- name: 'Groups',
- section: 'Groups',
- noBaseUrl: true,
- url: createAdminUrl({adminView: AdminChildView.GROUPS}),
- view: 'gr-admin-group-list' as GerritView,
- },
- {
- name: 'Plugins',
- capability: 'viewPlugins',
- section: 'Plugins',
- noBaseUrl: true,
- url: createAdminUrl({adminView: AdminChildView.PLUGINS}),
- view: 'gr-plugin-list' as GerritView,
- },
-];
-
-export interface AdminLink {
- url: string;
- text: string;
- capability: string | null;
- noBaseUrl: boolean;
- view: null;
- viewableToAll: boolean;
- target: '_blank' | null;
-}
-
-export interface AdminLinks {
- links: NavLink[];
- expandedSection?: SubsectionInterface;
-}
-
-export function getAdminLinks(
- account: AccountDetailInfo | undefined,
- getAccountCapabilities: () => Promise<AccountCapabilityInfo>,
- getAdminMenuLinks: () => MenuLink[],
- options?: AdminNavLinksOption
-): Promise<AdminLinks> {
- if (!account) {
- return Promise.resolve(
- filterLinks(link => !!link.viewableToAll, getAdminMenuLinks, options)
- );
- }
- return getAccountCapabilities().then(capabilities =>
- filterLinks(
- link => !link.capability || hasOwnProperty(capabilities, link.capability),
- getAdminMenuLinks,
- options
- )
- );
-}
-
-function filterLinks(
- filterFn: (link: NavLink) => boolean,
- getAdminMenuLinks: () => MenuLink[],
- options?: AdminNavLinksOption
-): AdminLinks {
- let links: NavLink[] = ADMIN_LINKS.slice(0);
- let expandedSection: SubsectionInterface | undefined = undefined;
-
- const isExternalLink = (link: MenuLink) => link.url[0] !== '/';
-
- // Append top-level links that are defined by plugins.
- links.push(
- ...getAdminMenuLinks().map((link: MenuLink) => {
- return {
- url: link.url,
- name: link.text,
- capability: link.capability || undefined,
- noBaseUrl: !isExternalLink(link),
- view: undefined,
- viewableToAll: !link.capability,
- target: isExternalLink(link) ? '_blank' : null,
- };
- })
- );
-
- links = links.filter(filterFn);
-
- const filteredLinks: NavLink[] = [];
- const repoName = options && options.repoName;
- const groupId = options && options.groupId;
- const groupName = options && options.groupName;
- const groupIsInternal = options && options.groupIsInternal;
- const isAdmin = options && options.isAdmin;
- const groupOwner = options && options.groupOwner;
-
- // Don't bother to get sub-navigation items if only the top level links
- // are needed. This is used by the main header dropdown.
- if (!repoName && !groupId) {
- return {links, expandedSection};
- }
-
- // Otherwise determine the full set of links and return both the full
- // set in addition to the subsection that should be displayed if it
- // exists.
- for (const link of links) {
- const linkCopy = {...link};
- if (linkCopy.name === 'Repositories' && repoName) {
- linkCopy.subsection = getRepoSubsections(repoName);
- expandedSection = linkCopy.subsection;
- } else if (linkCopy.name === 'Groups' && groupId && groupName) {
- linkCopy.subsection = getGroupSubsections(
- groupId,
- groupName,
- groupIsInternal,
- isAdmin,
- groupOwner
- );
- expandedSection = linkCopy.subsection;
- }
- filteredLinks.push(linkCopy);
- }
- return {links: filteredLinks, expandedSection};
-}
-
-export function getGroupSubsections(
- groupId: GroupId,
- groupName: string,
- groupIsInternal?: boolean,
- isAdmin?: boolean,
- groupOwner?: boolean
-) {
- const children: SubsectionInterface[] = [];
- const subsection: SubsectionInterface = {
- name: groupName,
- view: GerritView.GROUP,
- url: createGroupUrl({groupId}),
- children,
- };
- if (groupIsInternal) {
- children.push({
- name: 'Members',
- detailType: GroupDetailView.MEMBERS,
- view: GerritView.GROUP,
- url: createGroupUrl({groupId, detail: GroupDetailView.MEMBERS}),
- });
- }
- if (groupIsInternal && (isAdmin || groupOwner)) {
- children.push({
- name: 'Audit Log',
- detailType: GroupDetailView.LOG,
- view: GerritView.GROUP,
- url: createGroupUrl({groupId, detail: GroupDetailView.LOG}),
- });
- }
- return subsection;
-}
-
-export function getRepoSubsections(repo: RepoName) {
- return {
- name: repo,
- view: GerritView.REPO,
- children: [
- {
- name: 'General',
- view: GerritView.REPO,
- detailType: RepoDetailView.GENERAL,
- url: createRepoUrl({repo, detail: RepoDetailView.GENERAL}),
- },
- {
- name: 'Access',
- view: GerritView.REPO,
- detailType: RepoDetailView.ACCESS,
- url: createRepoUrl({repo, detail: RepoDetailView.ACCESS}),
- },
- {
- name: 'Commands',
- view: GerritView.REPO,
- detailType: RepoDetailView.COMMANDS,
- url: createRepoUrl({repo, detail: RepoDetailView.COMMANDS}),
- },
- {
- name: 'Branches',
- view: GerritView.REPO,
- detailType: RepoDetailView.BRANCHES,
- url: createRepoUrl({repo, detail: RepoDetailView.BRANCHES}),
- },
- {
- name: 'Tags',
- view: GerritView.REPO,
- detailType: RepoDetailView.TAGS,
- url: createRepoUrl({repo, detail: RepoDetailView.TAGS}),
- },
- {
- name: 'Dashboards',
- view: GerritView.REPO,
- detailType: RepoDetailView.DASHBOARDS,
- url: createRepoUrl({repo, detail: RepoDetailView.DASHBOARDS}),
- },
- ],
- };
-}
-
-export interface SubsectionInterface {
- name: string;
- view: GerritView;
- detailType?: RepoDetailView | GroupDetailView;
- url?: string;
- children?: SubsectionInterface[];
-}
-
-export interface AdminNavLinksOption {
- repoName?: RepoName;
- groupId?: GroupId;
- groupName?: string;
- groupIsInternal?: boolean;
- isAdmin?: boolean;
- groupOwner?: boolean;
-}
-
-export interface NavLink {
- name: string;
- noBaseUrl: boolean;
- url: string;
- view?: GerritView | AdminChildView;
- viewableToAll?: boolean;
- section?: string;
- capability?: string;
- target?: string | null;
- subsection?: SubsectionInterface;
- children?: SubsectionInterface[];
-}
diff --git a/polygerrit-ui/app/utils/admin-nav-util_test.ts b/polygerrit-ui/app/utils/admin-nav-util_test.ts
deleted file mode 100644
index a8600c7..0000000
--- a/polygerrit-ui/app/utils/admin-nav-util_test.ts
+++ /dev/null
@@ -1,334 +0,0 @@
-/**
- * @license
- * Copyright 2018 Google LLC
- * SPDX-License-Identifier: Apache-2.0
- */
-import {assert} from '@open-wc/testing';
-import {AccountDetailInfo, GroupId, RepoName, Timestamp} from '../api/rest-api';
-import '../test/common-test-setup';
-import {AdminNavLinksOption, getAdminLinks} from './admin-nav-util';
-
-suite('gr-admin-nav-behavior tests', () => {
- let capabilityStub: sinon.SinonStub;
- let menuLinkStub: sinon.SinonStub;
-
- setup(() => {
- capabilityStub = sinon.stub();
- menuLinkStub = sinon.stub().returns([]);
- });
-
- const testAdminLinks = async (
- account: AccountDetailInfo | undefined,
- options: AdminNavLinksOption | undefined,
- expected: any
- ) => {
- const res = await getAdminLinks(
- account,
- capabilityStub,
- menuLinkStub,
- options
- );
-
- assert.equal(expected.totalLength, res.links.length);
- assert.equal(res.links[0].name, 'Repositories');
- // Repos
- if (expected.groupListShown) {
- assert.equal(res.links[1].name, 'Groups');
- }
-
- if (expected.pluginListShown) {
- assert.equal(res.links[2].name, 'Plugins');
- assert.isNotOk(res.links[2].subsection);
- }
-
- if (expected.projectPageShown) {
- assert.isOk(res.links[0].subsection);
- assert.equal(res.links[0].subsection!.children!.length, 6);
- } else {
- assert.isNotOk(res.links[0].subsection);
- }
- // Groups
- if (expected.groupPageShown) {
- assert.isOk(res.links[1].subsection);
- assert.equal(
- res.links[1].subsection!.children!.length,
- expected.groupSubpageLength
- );
- } else if (expected.totalLength > 1) {
- assert.isNotOk(res.links[1].subsection);
- }
-
- if (expected.pluginGeneratedLinks) {
- for (const link of expected.pluginGeneratedLinks) {
- const linkMatch = res.links.find(
- l => l.url === link.url && l.name === link.text
- );
- assert.isTrue(!!linkMatch);
-
- // External links should open in new tab.
- if (link.url[0] !== '/') {
- assert.equal(linkMatch!.target, '_blank');
- } else {
- assert.isNotOk(linkMatch!.target);
- }
- }
- }
-
- // Current section
- if (expected.projectPageShown || expected.groupPageShown) {
- assert.isOk(res.expandedSection);
- assert.isOk(res.expandedSection!.children);
- } else {
- assert.isNotOk(res.expandedSection);
- }
- if (expected.projectPageShown) {
- assert.equal(res.expandedSection!.name, 'my-repo');
- assert.equal(res.expandedSection!.children!.length, 6);
- } else if (expected.groupPageShown) {
- assert.equal(res.expandedSection!.name, 'my-group');
- assert.equal(
- res.expandedSection!.children!.length,
- expected.groupSubpageLength
- );
- }
- };
-
- suite('logged out', () => {
- let account: AccountDetailInfo;
- let expected: any;
-
- setup(() => {
- expected = {
- groupListShown: false,
- groupPageShown: false,
- pluginListShown: false,
- };
- });
-
- test('without a specific repo or group', async () => {
- let options;
- expected = Object.assign(expected, {
- totalLength: 1,
- projectPageShown: false,
- });
- await testAdminLinks(account, options, expected);
- });
-
- test('with a repo', async () => {
- const options = {repoName: 'my-repo' as RepoName};
- expected = Object.assign(expected, {
- totalLength: 1,
- projectPageShown: true,
- });
- await testAdminLinks(account, options, expected);
- });
-
- test('with plugin generated links', async () => {
- let options;
- const generatedLinks = [
- {text: 'internal link text', url: '/internal/link/url'},
- {text: 'external link text', url: 'http://external/link/url'},
- ];
- menuLinkStub.returns(generatedLinks);
- expected = Object.assign(expected, {
- totalLength: 3,
- projectPageShown: false,
- pluginGeneratedLinks: generatedLinks,
- });
- await testAdminLinks(account, options, expected);
- });
- });
-
- suite('no plugin capability logged in', () => {
- const account = {
- name: 'test-user',
- registered_on: '' as Timestamp,
- };
- let expected: any;
-
- setup(() => {
- expected = {
- totalLength: 2,
- pluginListShown: false,
- };
- capabilityStub.returns(Promise.resolve({}));
- });
-
- test('without a specific project or group', async () => {
- let options;
- expected = Object.assign(expected, {
- projectPageShown: false,
- groupListShown: true,
- groupPageShown: false,
- });
- await testAdminLinks(account, options, expected);
- });
-
- test('with a repo', async () => {
- const account = {
- name: 'test-user',
- registered_on: '' as Timestamp,
- };
- const options = {repoName: 'my-repo' as RepoName};
- expected = Object.assign(expected, {
- projectPageShown: true,
- groupListShown: true,
- groupPageShown: false,
- });
- await testAdminLinks(account, options, expected);
- });
- });
-
- suite('view plugin capability logged in', () => {
- const account = {
- name: 'test-user',
- registered_on: '' as Timestamp,
- };
- let expected: any;
-
- setup(() => {
- capabilityStub.returns(Promise.resolve({viewPlugins: true}));
- expected = {
- totalLength: 3,
- groupListShown: true,
- pluginListShown: true,
- };
- });
-
- test('without a specific repo or group', async () => {
- let options;
- expected = Object.assign(expected, {
- projectPageShown: false,
- groupPageShown: false,
- });
- await testAdminLinks(account, options, expected);
- });
-
- test('with a repo', async () => {
- const options = {repoName: 'my-repo' as RepoName};
- expected = Object.assign(expected, {
- projectPageShown: true,
- groupPageShown: false,
- });
- await testAdminLinks(account, options, expected);
- });
-
- test('admin with internal group', async () => {
- const options = {
- groupId: 'a15262' as GroupId,
- groupName: 'my-group',
- groupIsInternal: true,
- isAdmin: true,
- groupOwner: false,
- };
- expected = Object.assign(expected, {
- projectPageShown: false,
- groupPageShown: true,
- groupSubpageLength: 2,
- });
- await testAdminLinks(account, options, expected);
- });
-
- test('group owner with internal group', async () => {
- const options = {
- groupId: 'a15262' as GroupId,
- groupName: 'my-group',
- groupIsInternal: true,
- isAdmin: false,
- groupOwner: true,
- };
- expected = Object.assign(expected, {
- projectPageShown: false,
- groupPageShown: true,
- groupSubpageLength: 2,
- });
- await testAdminLinks(account, options, expected);
- });
-
- test('non owner or admin with internal group', async () => {
- const options = {
- groupId: 'a15262' as GroupId,
- groupName: 'my-group',
- groupIsInternal: true,
- isAdmin: false,
- groupOwner: false,
- };
- expected = Object.assign(expected, {
- projectPageShown: false,
- groupPageShown: true,
- groupSubpageLength: 1,
- });
- await testAdminLinks(account, options, expected);
- });
-
- test('admin with external group', async () => {
- const options = {
- groupId: 'a15262' as GroupId,
- groupName: 'my-group',
- groupIsInternal: false,
- isAdmin: true,
- groupOwner: true,
- };
- expected = Object.assign(expected, {
- projectPageShown: false,
- groupPageShown: true,
- groupSubpageLength: 0,
- });
- await testAdminLinks(account, options, expected);
- });
- });
-
- suite('view plugin screen with plugin capability', () => {
- const account = {
- name: 'test-user',
- registered_on: '' as Timestamp,
- };
- let expected: any;
-
- setup(() => {
- capabilityStub.returns(Promise.resolve({pluginCapability: true}));
- expected = {};
- });
-
- test('with plugin with capabilities', async () => {
- let options;
- const generatedLinks = [
- {text: 'without capability', url: '/without'},
- {text: 'with capability', url: '/with', capability: 'pluginCapability'},
- ];
- menuLinkStub.returns(generatedLinks);
- expected = Object.assign(expected, {
- totalLength: 4,
- pluginGeneratedLinks: generatedLinks,
- });
- await testAdminLinks(account, options, expected);
- });
- });
-
- suite('view plugin screen without plugin capability', () => {
- const account = {
- name: 'test-user',
- registered_on: '' as Timestamp,
- };
- let expected: any;
-
- setup(() => {
- capabilityStub.returns(Promise.resolve({}));
- expected = {};
- });
-
- test('with plugin with capabilities', async () => {
- let options;
- const generatedLinks = [
- {text: 'without capability', url: '/without'},
- {text: 'with capability', url: '/with', capability: 'pluginCapability'},
- ];
- menuLinkStub.returns(generatedLinks);
- expected = Object.assign(expected, {
- totalLength: 3,
- pluginGeneratedLinks: [generatedLinks[0]],
- });
- await testAdminLinks(account, options, expected);
- });
- });
-});