Introduce gr-admin-nav-behavior and use in gr-admin-view

gr-admin-nav-behavior will also be used by the main header. For this
change, the behavior replaces work previously done in gr-admin-view.

In a follow-up change, gr-admin-view will display the current section's
options in a gr-dropdown-list and the gr-main-header will display top
level admin links in a dropdown menu.

Change-Id: Ief0ca439528929f35335309c292512579bb696c3
This commit is contained in:
Becky Siegel
2018-04-18 16:43:05 +02:00
parent d391c0a9ff
commit cad4bf8108
6 changed files with 608 additions and 141 deletions

View File

@@ -0,0 +1,203 @@
<!--
@license
Copyright (C) 2018 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.
-->
<script>
(function(window) {
'use strict';
const ACCOUNT_CAPABILITIES = ['createProject', 'createGroup', 'viewPlugins'];
const ADMIN_LINKS = [{
name: 'Repositories',
noBaseUrl: true,
url: '/admin/repos',
view: 'gr-repo-list',
viewableToAll: true,
}, {
name: 'Groups',
section: 'Groups',
noBaseUrl: true,
url: '/admin/groups',
view: 'gr-admin-group-list',
}, {
name: 'Plugins',
capability: 'viewPlugins',
section: 'Plugins',
noBaseUrl: true,
url: '/admin/plugins',
view: 'gr-plugin-list',
}];
window.Gerrit = window.Gerrit || {};
/** @polymerBehavior Gerrit.AdminNavBehavior */
Gerrit.AdminNavBehavior = {
/**
* @param {!Object} account
* @param {!Function} getAccountCapabilities
* @param {!Function} getAdminMenuLinks
* Possible aguments in options:
* repoName?: string
* groupId?: string,
* groupName?: string,
* groupIsInternal?: boolean,
* isAdmin?: boolean,
* groupOwner?: boolean,
* @param {!Object=} opt_options
* @return {Promise<!Object>}
*/
getAdminLinks(account, getAccountCapabilities, getAdminMenuLinks,
opt_options) {
if (!account) {
return Promise.resolve(this._filterLinks(link => link.viewableToAll,
getAdminMenuLinks, opt_options));
}
return getAccountCapabilities(ACCOUNT_CAPABILITIES)
.then(capabilities => {
return this._filterLinks(link => {
return !link.capability ||
capabilities.hasOwnProperty(link.capability);
}, getAdminMenuLinks, opt_options);
});
},
/**
* @param {!Function} filterFn
* @param {!Function} getAdminMenuLinks
* Possible aguments in options:
* repoName?: string
* groupId?: string,
* groupName?: string,
* groupIsInternal?: boolean,
* isAdmin?: boolean,
* groupOwner?: boolean,
* @param {!Object|undefined} opt_options
* @return {Promise<!Object>}
*/
_filterLinks(filterFn, getAdminMenuLinks, opt_options) {
let links = ADMIN_LINKS.slice(0);
let expandedSection;
// Append top-level links that are defined by plugins.
links.push(...getAdminMenuLinks().map(link => ({
url: link.url,
name: link.text,
noBaseUrl: link.url[0] === '/',
view: null,
viewableToAll: true,
})));
links = links.filter(filterFn);
const filteredLinks = [];
const repoName = opt_options && opt_options.repoName;
const groupId = opt_options && opt_options.groupId;
const groupName = opt_options && opt_options.groupName;
const groupIsInternal = opt_options && opt_options.groupIsInternal;
const isAdmin = opt_options && opt_options.isAdmin;
const groupOwner = opt_options && opt_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 = Object.assign({}, link);
if (linkCopy.name === 'Repositories' && repoName) {
linkCopy.subsection = this.getRepoSubsections(repoName);
expandedSection = linkCopy.subsection;
} else if (linkCopy.name === 'Groups' && groupId && groupName) {
linkCopy.subsection = this.getGroupSubsections(groupId, groupName,
groupIsInternal, isAdmin, groupOwner);
expandedSection = linkCopy.subsection;
}
filteredLinks.push(linkCopy);
}
return {links: filteredLinks, expandedSection};
},
getGroupSubsections(groupId, groupName, groupIsInternal, isAdmin,
groupOwner) {
const subsection = {
name: groupName,
view: Gerrit.Nav.View.GROUP,
url: Gerrit.Nav.getUrlForGroup(groupId),
children: [],
};
if (groupIsInternal) {
subsection.children.push({
name: 'Members',
detailType: Gerrit.Nav.GroupDetailView.MEMBERS,
view: Gerrit.Nav.View.GROUP,
url: Gerrit.Nav.getUrlForGroupMembers(groupId),
});
}
if (groupIsInternal && (isAdmin || groupOwner)) {
subsection.children.push(
{
name: 'Audit Log',
detailType: Gerrit.Nav.GroupDetailView.LOG,
view: Gerrit.Nav.View.GROUP,
url: Gerrit.Nav.getUrlForGroupLog(groupId),
}
);
}
return subsection;
},
getRepoSubsections(repoName) {
return {
name: repoName,
view: Gerrit.Nav.View.REPO,
url: Gerrit.Nav.getUrlForRepo(repoName),
children: [{
name: 'Access',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.ACCESS,
url: Gerrit.Nav.getUrlForRepoAccess(repoName),
},
{
name: 'Commands',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.COMMANDS,
url: Gerrit.Nav.getUrlForRepoCommands(repoName),
},
{
name: 'Branches',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.BRANCHES,
url: Gerrit.Nav.getUrlForRepoBranches(repoName),
},
{
name: 'Tags',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.TAGS,
url: Gerrit.Nav.getUrlForRepoTags(repoName),
},
{
name: 'Dashboards',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.DASHBOARDS,
url: Gerrit.Nav.getUrlForRepoDashboards(repoName),
}],
};
},
};
})(window);
</script>

View File

@@ -0,0 +1,303 @@
<!DOCTYPE html>
<!--
@license
Copyright (C) 2018 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>keyboard-shortcut-behavior</title>
<script src="../../bower_components/webcomponentsjs/webcomponents.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-admin-nav-behavior.html">
<test-fixture id="basic">
<template>
<test-element></test-element>
</template>
</test-fixture>
<script>
suite('gr-admin-nav-behavior tests', () => {
let element;
let sandbox;
let capabilityStub;
let menuLinkStub;
suiteSetup(() => {
// Define a Polymer element that uses this behavior.
Polymer({
is: 'test-element',
behaviors: [
Gerrit.AdminNavBehavior,
],
});
});
setup(() => {
element = fixture('basic');
sandbox = sinon.sandbox.create();
capabilityStub = sinon.stub();
menuLinkStub = sinon.stub().returns([]);
});
teardown(() => {
sandbox.restore();
});
const testAdminLinks = (account, options, expected, done) => {
element.getAdminLinks(account,
capabilityStub,
menuLinkStub,
options)
.then(res => {
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, 5);
} 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) {
assert.isTrue(!!res.links.find(l => {
return (l.url === link.url && l.name === link.text);
}));
}
}
// 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, 5);
} else if (expected.groupPageShown) {
assert.equal(res.expandedSection.name, 'my-group');
assert.equal(res.expandedSection.children.length,
expected.groupSubpageLength);
}
done();
});
};
suite('logged out', () => {
let account;
let expected;
setup(() => {
expected = {
groupListShown: false,
groupPageShown: false,
pluginListShown: false,
};
});
test('without a specific repo or group', done => {
let options;
expected = Object.assign(expected, {
totalLength: 1,
projectPageShown: false,
});
testAdminLinks(account, options, expected, done);
});
test('with a repo', done => {
const options = {repoName: 'my-repo'};
expected = Object.assign(expected, {
totalLength: 1,
projectPageShown: true,
});
testAdminLinks(account, options, expected, done);
});
test('with plugin generated links', done => {
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,
});
testAdminLinks(account, options, expected, done);
});
});
suite('no plugin capability logged in', () => {
const account = {
name: 'test-user',
};
let expected;
setup(() => {
expected = {
totalLength: 2,
pluginListShown: false,
};
capabilityStub.returns(Promise.resolve({}));
});
test('without a specific project or group', done => {
let options;
expected = Object.assign(expected, {
projectPageShown: false,
groupListShown: true,
groupPageShown: false,
});
testAdminLinks(account, options, expected, done);
});
test('with a repo', done => {
const account = {
name: 'test-user',
};
const options = {repoName: 'my-repo'};
expected = Object.assign(expected, {
projectPageShown: true,
groupListShown: true,
groupPageShown: false,
});
testAdminLinks(account, options, expected, done);
});
});
suite('view plugin capability logged in', () => {
const account = {
name: 'test-user',
};
let expected;
setup(() => {
capabilityStub.returns(Promise.resolve({viewPlugins: true}));
expected = {
totalLength: 3,
groupListShown: true,
pluginListShown: true,
};
});
test('without a specific repo or group', done => {
let options;
expected = Object.assign(expected, {
projectPageShown: false,
groupPageShown: false,
});
testAdminLinks(account, options, expected, done);
});
test('with a repo', done => {
const options = {repoName: 'my-repo'};
expected = Object.assign(expected, {
projectPageShown: true,
groupPageShown: false,
});
testAdminLinks(account, options, expected, done);
});
test('admin with internal group', done => {
const options = {
groupId: 'a15262',
groupName: 'my-group',
groupIsInternal: true,
isAdmin: true,
groupOwner: false,
};
expected = Object.assign(expected, {
projectPageShown: false,
groupPageShown: true,
groupSubpageLength: 2,
});
testAdminLinks(account, options, expected, done);
});
test('group owner with internal group', done => {
const options = {
groupId: 'a15262',
groupName: 'my-group',
groupIsInternal: true,
isAdmin: false,
groupOwner: true,
};
expected = Object.assign(expected, {
projectPageShown: false,
groupPageShown: true,
groupSubpageLength: 2,
});
testAdminLinks(account, options, expected, done);
});
test('non owner or admin with internal group', done => {
const options = {
groupId: 'a15262',
groupName: 'my-group',
groupIsInternal: true,
isAdmin: false,
groupOwner: false,
};
expected = Object.assign(expected, {
projectPageShown: false,
groupPageShown: true,
groupSubpageLength: 1,
});
testAdminLinks(account, options, expected, done);
});
test('admin with external group', done => {
const options = {
groupId: 'a15262',
groupName: 'my-group',
groupIsInternal: false,
isAdmin: true,
groupOwner: true,
};
expected = Object.assign(expected, {
projectPageShown: false,
groupPageShown: true,
groupSubpageLength: 0,
});
testAdminLinks(account, options, expected, done);
});
});
});
</script>

View File

@@ -18,6 +18,7 @@ limitations under the License.
<link rel="import" href="../../../bower_components/polymer/polymer.html">
<link rel="import" href="../../../behaviors/base-url-behavior/base-url-behavior.html">
<link rel="import" href="../../../behaviors/gr-admin-nav-behavior/gr-admin-nav-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
<link rel="import" href="../../../styles/gr-menu-page-styles.html">
<link rel="import" href="../../../styles/gr-page-nav-styles.html">

View File

@@ -17,35 +17,9 @@
(function() {
'use strict';
// Note: noBaseUrl: true is set on entries where the URL is not yet supported
// by router abstraction.
const ADMIN_LINKS = [{
name: 'Repositories',
noBaseUrl: true,
url: '/admin/repos',
view: 'gr-repo-list',
viewableToAll: true,
children: [],
}, {
name: 'Groups',
section: 'Groups',
noBaseUrl: true,
url: '/admin/groups',
view: 'gr-admin-group-list',
children: [],
}, {
name: 'Plugins',
capability: 'viewPlugins',
section: 'Plugins',
noBaseUrl: true,
url: '/admin/plugins',
view: 'gr-plugin-list',
}];
const INTERNAL_GROUP_REGEX = /^[\da-f]{40}$/;
const ACCOUNT_CAPABILITIES = ['createProject', 'createGroup', 'viewPlugins'];
Polymer({
is: 'gr-admin-view',
@@ -66,6 +40,7 @@
type: Boolean,
value: false,
},
_subsectionLinks: Array,
_filteredLinks: Array,
_showDownload: {
type: Boolean,
@@ -89,6 +64,7 @@
},
behaviors: [
Gerrit.AdminNavBehavior,
Gerrit.BaseUrlBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -108,113 +84,54 @@
];
return Promise.all(promises).then(result => {
this._account = result[0];
if (!this._account) {
// Return so that account capabilities don't load with no account.
return this._filteredLinks = this._filterLinks(link => {
return link.viewableToAll;
});
let options;
if (this._repoName) {
options = {repoName: this._repoName};
} else if (this._groupId) {
options = {
groupId: this._groupId,
groupName: this._groupName,
groupIsInternal: this._groupIsInternal,
isAdmin: this._isAdmin,
groupOwner: this._groupOwner,
};
}
this._loadAccountCapabilities();
return this.getAdminLinks(this._account,
this.$.restAPI.getAccountCapabilities.bind(this.$.restAPI),
this.$.jsAPI.getAdminMenuLinks.bind(this.$.jsAPI),
options)
.then(res => {
this._filteredLinks = res.links;
if (!res.expandedSection) {
this._subsectionLinks = [];
return;
}
this._subsectionLinks = [res.expandedSection]
.concat(res.expandedSection.children).map(section => {
return {
text: !section.detailType ? 'Home' : section.name,
value: section.view + section.detailType,
view: section.view,
url: section.url,
detailType: section.detailType,
parent: this._groupId || this._repoName || '',
};
});
});
});
},
_filterLinks(filterFn) {
let links = ADMIN_LINKS.slice(0);
_handleSubsectionChange(e) {
const selected = this._subsectionLinks
.find(section => section.value === e.detail.value);
// Append top-level links that are defined by plugins.
links.push(...this.$.jsAPI.getAdminMenuLinks().map(link => ({
url: link.url,
name: link.text,
children: [],
noBaseUrl: link.url[0] === '/',
view: null,
viewableToAll: true,
})));
links = links.filter(filterFn);
const filteredLinks = [];
for (const link of links) {
const linkCopy = Object.assign({}, link);
linkCopy.children = linkCopy.children ?
linkCopy.children.filter(filterFn) : [];
if (linkCopy.name === 'Repositories' && this._repoName) {
linkCopy.subsection = {
name: this._repoName,
view: Gerrit.Nav.View.REPO,
url: Gerrit.Nav.getUrlForRepo(this._repoName),
children: [{
name: 'Access',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.ACCESS,
url: Gerrit.Nav.getUrlForRepoAccess(this._repoName),
},
{
name: 'Commands',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.COMMANDS,
url: Gerrit.Nav.getUrlForRepoCommands(this._repoName),
},
{
name: 'Branches',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.BRANCHES,
url: Gerrit.Nav.getUrlForRepoBranches(this._repoName),
},
{
name: 'Tags',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.TAGS,
url: Gerrit.Nav.getUrlForRepoTags(this._repoName),
},
{
name: 'Dashboards',
view: Gerrit.Nav.View.REPO,
detailType: Gerrit.Nav.RepoDetailView.DASHBOARDS,
url: Gerrit.Nav.getUrlForRepoDashboards(this._repoName),
}],
};
}
if (linkCopy.name === 'Groups' && this._groupId && this._groupName) {
linkCopy.subsection = {
name: this._groupName,
view: Gerrit.Nav.View.GROUP,
url: Gerrit.Nav.getUrlForGroup(this._groupId),
children: [],
};
if (this._groupIsInternal) {
linkCopy.subsection.children.push({
name: 'Members',
detailType: Gerrit.Nav.GroupDetailView.MEMBERS,
view: Gerrit.Nav.View.GROUP,
url: Gerrit.Nav.getUrlForGroupMembers(this._groupId),
});
}
if (this._groupIsInternal && (this._isAdmin || this._groupOwner)) {
linkCopy.subsection.children.push(
{
name: 'Audit Log',
detailType: Gerrit.Nav.GroupDetailView.LOG,
view: Gerrit.Nav.View.GROUP,
url: Gerrit.Nav.getUrlForGroupLog(this._groupId),
}
);
}
}
filteredLinks.push(linkCopy);
// This is when it gets set initially.
if (this._selectedIsCurrentPage(selected)) {
return;
}
return filteredLinks;
},
_loadAccountCapabilities() {
return this.$.restAPI.getAccountCapabilities(ACCOUNT_CAPABILITIES)
.then(capabilities => {
this._filteredLinks = this._filterLinks(link => {
return !link.capability ||
capabilities.hasOwnProperty(link.capability);
});
});
Gerrit.Nav.navigateToRelativeUrl(selected.url);
},
_paramsChanged(params) {
@@ -260,6 +177,10 @@
}
},
_computeSelectedTitle(params) {
return this.getSelectedTitle(params.view);
},
// TODO (beckysiegel): Update these functions after router abstraction is
// updated. They are currently copied from gr-dropdown (and should be
// updated there as well once complete).

View File

@@ -95,6 +95,9 @@ limitations under the License.
});
test('_filteredLinks admin', done => {
sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
name: 'test-user',
}));
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
return Promise.resolve({
createGroup: true,
@@ -102,35 +105,36 @@ limitations under the License.
viewPlugins: true,
});
});
element._loadAccountCapabilities().then(() => {
element.reload().then(() => {
assert.equal(element._filteredLinks.length, 3);
// Repos
assert.equal(element._filteredLinks[0].children.length, 0);
assert.isNotOk(element._filteredLinks[0].subsection);
// Groups
assert.equal(element._filteredLinks[1].children.length, 0);
assert.isNotOk(element._filteredLinks[0].subsection);
// Plugins
assert.equal(element._filteredLinks[2].children.length, 0);
assert.isNotOk(element._filteredLinks[0].subsection);
done();
});
});
test('_filteredLinks non admin authenticated', done => {
sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
name: 'test-user',
}));
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
return Promise.resolve({});
});
element._loadAccountCapabilities().then(() => {
element.reload().then(() => {
assert.equal(element._filteredLinks.length, 2);
// Repos
assert.equal(element._filteredLinks[0].children.length, 0);
assert.isNotOk(element._filteredLinks[0].subsection);
// Groups
assert.equal(element._filteredLinks[1].children.length, 0);
assert.isNotOk(element._filteredLinks[0].subsection);
done();
});
});
@@ -140,7 +144,6 @@ limitations under the License.
assert.equal(element._filteredLinks.length, 1);
// Repos
assert.equal(element._filteredLinks[0].children.length, 0);
assert.isNotOk(element._filteredLinks[0].subsection);
done();
});
@@ -156,7 +159,6 @@ limitations under the License.
assert.deepEqual(element._filteredLinks[1], {
url: '/internal/link/url',
name: 'internal link text',
children: [],
noBaseUrl: true,
view: null,
viewableToAll: true,
@@ -164,7 +166,6 @@ limitations under the License.
assert.deepEqual(element._filteredLinks[2], {
url: 'http://external/link/url',
name: 'external link text',
children: [],
noBaseUrl: false,
view: null,
viewableToAll: true,
@@ -174,6 +175,9 @@ limitations under the License.
test('Repo shows up in nav', done => {
element._repoName = 'Test Repo';
sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
name: 'test-user',
}));
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
return Promise.resolve({
createGroup: true,
@@ -181,18 +185,52 @@ limitations under the License.
viewPlugins: true,
});
});
element._loadAccountCapabilities().then(() => {
element.reload().then(() => {
flushAsynchronousOperations();
assert.equal(element._filteredLinks.length, 3);
// Repos
assert.equal(element._filteredLinks[0].children.length, 0);
assert.equal(element._filteredLinks[0].subsection.children.length, 5);
assert.equal(element._filteredLinks[0].subsection.name, 'Test Repo');
// Groups
assert.equal(element._filteredLinks[1].children.length, 0);
assert.isNotOk(element._filteredLinks[1].subsection);
// Plugins
assert.equal(element._filteredLinks[2].children.length, 0);
assert.isNotOk(element._filteredLinks[2].subsection);
done();
});
});
test('Group shows up in nav', done => {
element._groupId = 'a15262';
element._groupName = 'my-group';
element._groupIsInternal = true;
element._isAdmin = true;
element._groupOwner = false;
sandbox.stub(element.$.restAPI, 'getAccount').returns(Promise.resolve({
name: 'test-user',
}));
sandbox.stub(element.$.restAPI, 'getAccountCapabilities', () => {
return Promise.resolve({
createGroup: true,
createProject: true,
viewPlugins: true,
});
});
element.reload().then(() => {
flushAsynchronousOperations();
assert.equal(element._filteredLinks.length, 3);
// Repos
assert.isNotOk(element._filteredLinks[0].subsection);
// Groups
assert.equal(element._filteredLinks[1].subsection.children.length, 2);
assert.equal(element._filteredLinks[1].subsection.name, 'my-group');
// Plugins
assert.isNotOk(element._filteredLinks[2].subsection);
done();
});
});

View File

@@ -196,6 +196,7 @@ limitations under the License.
'keyboard-shortcut-behavior/keyboard-shortcut-behavior_test.html',
'rest-client-behavior/rest-client-behavior_test.html',
'gr-access-behavior/gr-access-behavior_test.html',
'gr-admin-nav-behavior/gr-admin-nav-behavior_test.html',
'gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html',
'gr-change-table-behavior/gr-change-table-behavior_test.html',
'gr-patch-set-behavior/gr-patch-set-behavior_test.html',