Fix javascript errors in gr-admin-view components

To produce these errors, the repo must not exist, so it's as simple as
going to /admin/repos/<project-that-does-not-exist>.

Also this throws a 404 if it cannot fetch the configs.

Change-Id: I59b3bcb6ca78d2cc717993687d6560c0f2d96dbb
This commit is contained in:
Paladox none
2018-02-17 19:12:09 +00:00
parent cdd5560477
commit 70cb10c1d8
17 changed files with 397 additions and 145 deletions

View File

@@ -286,17 +286,23 @@
_computeGroupName(groupId) {
if (!groupId) { return ''; }
const promises = [];
this.$.restAPI.getGroupConfig(groupId).then(group => {
if (!group || !group.name) { return; }
this._groupName = group.name;
this.reload();
promises.push(this.$.restAPI.getIsAdmin().then(isAdmin => {
this._isAdmin = isAdmin;
}));
promises.push(this.$.restAPI.getIsGroupOwner(group.name).then(
isOwner => {
this._groupOwner = isOwner;
}));
return Promise.all(promises).then(() => {
this.reload();
});

View File

@@ -39,17 +39,21 @@
},
_getAuditLogs() {
if (!this.groupId) {
return '';
}
return this.$.restAPI.getGroupAuditLog(this.groupId).then(auditLog => {
if (!auditLog) {
this._auditLog = [];
return;
}
this._auditLog = auditLog;
this._loading = false;
});
if (!this.groupId) { return ''; }
const errFn = response => {
this.fire('page-error', {response});
};
return this.$.restAPI.getGroupAuditLog(this.groupId, errFn)
.then(auditLog => {
if (!auditLog) {
this._auditLog = [];
return;
}
this._auditLog = auditLog;
this._loading = false;
});
},
_status(item) {
@@ -57,9 +61,8 @@
},
_computeGroupUrl(id) {
if (!id) {
return '';
}
if (!id) { return ''; }
return this.getBaseUrl() + '/admin/groups/' + id;
},

View File

@@ -34,11 +34,17 @@ limitations under the License.
<script>
suite('gr-group-audit-log tests', () => {
let element;
let sandbox;
setup(() => {
sandbox = sinon.sandbox.create();
element = fixture('basic');
});
teardown(() => {
sandbox.restore();
});
suite('members', () => {
test('test getNameForMember', () => {
let account = {
@@ -117,5 +123,24 @@ limitations under the License.
assert.equal(element._getNameForUser(account.user), 'test-email');
});
});
suite('404', () => {
test('fires page-error', done => {
element.groupId = 1;
const response = {status: 404};
sandbox.stub(
element.$.restAPI, 'getGroupAuditLog', (group, errFn) => {
errFn(response);
});
element.addEventListener('page-error', e => {
assert.deepEqual(e.detail.response, response);
done();
});
element._getAuditLogs();
});
});
});
</script>

View File

@@ -75,9 +75,13 @@
const promises = [];
return this.$.restAPI.getGroupConfig(this.groupId).then(
config => {
if (!config.name) { return; }
const errFn = response => {
this.fire('page-error', {response});
};
return this.$.restAPI.getGroupConfig(this.groupId, errFn)
.then(config => {
if (!config || !config.name) { return Promise.resolve(); }
this._groupName = config.name;

View File

@@ -38,9 +38,11 @@ limitations under the License.
let groups;
let groupMembers;
let includedGroups;
let groupStub;
setup(() => {
sandbox = sinon.sandbox.create();
groups = {
name: 'Administrators',
owner: 'Administrators',
@@ -118,9 +120,6 @@ limitations under the License.
getConfig() {
return Promise.resolve();
},
getGroupConfig() {
return Promise.resolve(groups);
},
getGroupMembers() {
return Promise.resolve(groupMembers);
},
@@ -137,6 +136,9 @@ limitations under the License.
element = fixture('basic');
sandbox.stub(element, 'getBaseUrl').returns('https://test/site');
element.groupId = 1;
groupStub = sandbox.stub(element.$.restAPI, 'getGroupConfig', () => {
return Promise.resolve(groups);
});
return element._loadGroupDetails();
});
@@ -251,5 +253,29 @@ limitations under the License.
assert.equal(element._itemId, '1000098');
assert.equal(element._itemName, '1000098');
});
test('_computeLoadingClass', () => {
assert.equal(element._computeLoadingClass(true), 'loading');
assert.equal(element._computeLoadingClass(false), '');
});
test('fires page-error', done => {
groupStub.restore();
element.groupId = 1;
const response = {status: 404};
sandbox.stub(
element.$.restAPI, 'getGroupConfig', (group, errFn) => {
errFn(response);
});
element.addEventListener('page-error', e => {
assert.deepEqual(e.detail.response, response);
done();
});
element._loadGroupDetails();
});
});
</script>

View File

@@ -95,18 +95,26 @@
_loadGroup() {
if (!this.groupId) { return; }
return this.$.restAPI.getGroupConfig(this.groupId).then(
config => {
const promises = [];
const errFn = response => {
this.fire('page-error', {response});
};
return this.$.restAPI.getGroupConfig(this.groupId, errFn)
.then(config => {
if (!config || !config.name) { return Promise.resolve(); }
this._groupName = config.name;
this.$.restAPI.getIsAdmin().then(isAdmin => {
promises.push(this.$.restAPI.getIsAdmin().then(isAdmin => {
this._isAdmin = isAdmin ? true : false;
});
}));
this.$.restAPI.getIsGroupOwner(config.name)
promises.push(this.$.restAPI.getIsGroupOwner(config.name)
.then(isOwner => {
this._groupOwner = isOwner ? true : false;
});
}));
// If visible to all is undefined, set to false. If it is defined
// as false, setting to false is fine. If any optional values
@@ -119,7 +127,9 @@
this.fire('title-change', {title: config.name});
this._loading = false;
return Promise.all(promises).then(() => {
this._loading = false;
});
});
},

View File

@@ -35,25 +35,28 @@ limitations under the License.
suite('gr-group tests', () => {
let element;
let sandbox;
let groupStub;
const group = {
id: '6a1e70e1a88782771a91808c8af9bbb7a9871389',
url: '#/admin/groups/uuid-6a1e70e1a88782771a91808c8af9bbb7a9871389',
options: {
},
description: 'Gerrit Site Administrators',
group_id: 1,
owner: 'Administrators',
owner_id: '6a1e70e1a88782771a91808c8af9bbb7a9871389',
name: 'Administrators',
};
setup(() => {
sandbox = sinon.sandbox.create();
stub('gr-rest-api-interface', {
getLoggedIn() { return Promise.resolve(true); },
getGroupConfig() {
return Promise.resolve({
id: '6a1e70e1a88782771a91808c8af9bbb7a9871389',
url: '#/admin/groups/uuid-6a1e70e1a88782771a91808c8af9bbb7a9871389',
options: {
},
description: 'Gerrit Site Administrators',
group_id: 1,
owner: 'Administrators',
owner_id: '6a1e70e1a88782771a91808c8af9bbb7a9871389',
});
},
});
element = fixture('basic');
groupStub = sandbox.stub(element.$.restAPI, 'getGroupConfig', () => {
return Promise.resolve(group);
});
});
teardown(() => {
@@ -117,6 +120,30 @@ limitations under the License.
});
});
test('test for undefined group name', done => {
groupStub.restore();
sandbox.stub(element.$.restAPI, 'getGroupConfig', () => {
return Promise.resolve({});
});
assert.isUndefined(element.groupId);
element.groupId = 1;
assert.isDefined(element.groupId);
// Test that loading shows instead of filling
// in group details
element._loadGroup().then(() => {
assert.isTrue(element.$.loading.classList.contains('loading'));
assert.isTrue(element._loading);
done();
});
});
test('test fire event', done => {
element._groupConfig = {
name: 'test-group',
@@ -156,5 +183,29 @@ limitations under the License.
const owner = false;
assert.equal(element._computeGroupDisabled(owner, admin), true);
});
test('_computeLoadingClass', () => {
assert.equal(element._computeLoadingClass(true), 'loading');
assert.equal(element._computeLoadingClass(false), '');
});
test('fires page-error', done => {
groupStub.restore();
element.groupId = 1;
const response = {status: 404};
sandbox.stub(
element.$.restAPI, 'getGroupConfig', (group, errFn) => {
errFn(response);
});
element.addEventListener('page-error', e => {
assert.deepEqual(e.detail.response, response);
done();
});
element._loadGroup();
});
});
</script>

View File

@@ -126,25 +126,40 @@
*/
_repoChanged(repo) {
if (!repo) { return Promise.resolve(); }
const promises = [];
const errFn = response => {
this.fire('page-error', {response});
};
// Always reset sections when a project changes.
this._sections = [];
promises.push(this.$.restAPI.getRepoAccessRights(repo).then(res => {
this._inheritsFrom = res.inherits_from;
this._local = res.local;
this._groups = res.groups;
this._weblinks = res.config_web_links || [];
this._canUpload = res.can_upload;
return this.toSortedArray(this._local);
}));
promises.push(this.$.restAPI.getRepoAccessRights(repo, errFn)
.then(res => {
if (!res) { return Promise.resolve(); }
promises.push(this.$.restAPI.getCapabilities().then(res => {
return res;
}));
this._inheritsFrom = res.inherits_from;
this._local = res.local;
this._groups = res.groups;
this._weblinks = res.config_web_links || [];
this._canUpload = res.can_upload;
return this.toSortedArray(this._local);
}));
promises.push(this.$.restAPI.getRepo(repo).then(res => {
return res.labels;
}));
promises.push(this.$.restAPI.getCapabilities(errFn)
.then(res => {
if (!res) { return Promise.resolve(); }
return res;
}));
promises.push(this.$.restAPI.getRepo(repo, errFn)
.then(res => {
if (!res) { return Promise.resolve(); }
return res.labels;
}));
promises.push(this.$.restAPI.getIsAdmin().then(isAdmin => {
this._isAdmin = isAdmin;

View File

@@ -217,6 +217,22 @@ limitations under the License.
assert.equal(element._computeLoadingClass(false), '');
});
test('fires page-error', done => {
const response = {status: 404};
sandbox.stub(
element.$.restAPI, 'getRepoAccessRights', (repoName, errFn) => {
errFn(response);
});
element.addEventListener('page-error', e => {
assert.deepEqual(e.detail.response, response);
done();
});
element.repo = 'test';
});
suite('with defined sections', () => {
const testEditSaveCancelBtns = () => {
// Edit button is visible and Save button is hidden.

View File

@@ -47,10 +47,17 @@
_loadRepo() {
if (!this.repo) { return Promise.resolve(); }
return this.$.restAPI.getProjectConfig(this.repo).then(config => {
this._repoConfig = config;
this._loading = false;
});
const errFn = response => {
this.fire('page-error', {response});
};
return this.$.restAPI.getProjectConfig(this.repo, errFn)
.then(config => {
if (!config) { return Promise.resolve(); }
this._repoConfig = config;
this._loading = false;
});
},
_computeLoadingClass(loading) {

View File

@@ -36,13 +36,14 @@ limitations under the License.
suite('gr-repo-commands tests', () => {
let element;
let sandbox;
let repoStub;
setup(() => {
sandbox = sinon.sandbox.create();
stub('gr-rest-api-interface', {
getProjectConfig() { return Promise.resolve({}); },
});
element = fixture('basic');
repoStub = sandbox.stub(element.$.restAPI, 'getProjectConfig', () => {
return Promise.resolve({});
});
});
teardown(() => {
@@ -113,5 +114,25 @@ limitations under the License.
});
});
});
suite('404', () => {
test('fires page-error', done => {
repoStub.restore();
element.repo = 'test';
const response = {status: 404};
sandbox.stub(
element.$.restAPI, 'getProjectConfig', (repo, errFn) => {
errFn(response);
});
element.addEventListener('page-error', e => {
assert.deepEqual(e.detail.response, response);
done();
});
element._loadRepo();
});
});
});
</script>

View File

@@ -32,7 +32,14 @@
_repoChanged(repo) {
this._loading = true;
if (!repo) { return Promise.resolve(); }
this.$.restAPI.getRepoDashboards(this.repo).then(res => {
const errFn = response => {
this.fire('page-error', {response});
};
this.$.restAPI.getRepoDashboards(this.repo, errFn).then(res => {
if (!res) { return Promise.resolve(); }
// Flatten 2 dimenional array, and sort by id.
const dashboards = res.concat.apply([], res).sort((a, b) =>
a.id > b.id);

View File

@@ -241,5 +241,22 @@ limitations under the License.
});
});
});
suite('404', () => {
test('fires page-error', done => {
const response = {status: 404};
sandbox.stub(
element.$.restAPI, 'getRepoDashboards', (repo, errFn) => {
errFn(response);
});
element.addEventListener('page-error', e => {
assert.deepEqual(e.detail.response, response);
done();
});
element.repo = 'test';
});
});
});
</script>

View File

@@ -118,18 +118,27 @@
if (!this.repo) { return Promise.resolve(); }
const promises = [];
const errFn = response => {
this.fire('page-error', {response});
};
promises.push(this._getLoggedIn().then(loggedIn => {
this._loggedIn = loggedIn;
if (loggedIn) {
this.$.restAPI.getRepoAccess(this.repo).then(access => {
if (!access) { return Promise.resolve(); }
// If the user is not an owner, is_owner is not a property.
this._readOnly = !access[this.repo].is_owner;
});
}
}));
promises.push(this.$.restAPI.getProjectConfig(this.repo).then(
config => {
promises.push(this.$.restAPI.getProjectConfig(this.repo, errFn)
.then(config => {
if (!config) { return Promise.resolve(); }
if (config.default_submit_type) {
// The gr-select is bound to submit_type, which needs to be the
// *configured* submit type. When default_submit_type is
@@ -147,6 +156,8 @@
}));
promises.push(this.$.restAPI.getConfig().then(config => {
if (!config) { return Promise.resolve(); }
this._schemesObj = config.download.schemes;
this._noteDbEnabled = !!config.note_db_enabled;
}));

View File

@@ -35,6 +35,66 @@ limitations under the License.
suite('gr-repo tests', () => {
let element;
let sandbox;
let repoStub;
const repoConf = {
description: 'Access inherited by all other projects.',
use_contributor_agreements: {
value: false,
configured_value: 'FALSE',
},
use_content_merge: {
value: false,
configured_value: 'FALSE',
},
use_signed_off_by: {
value: false,
configured_value: 'FALSE',
},
create_new_change_for_all_not_in_target: {
value: false,
configured_value: 'FALSE',
},
require_change_id: {
value: false,
configured_value: 'FALSE',
},
enable_signed_push: {
value: false,
configured_value: 'FALSE',
},
require_signed_push: {
value: false,
configured_value: 'FALSE',
},
reject_implicit_merges: {
value: false,
configured_value: 'FALSE',
},
private_by_default: {
value: false,
configured_value: 'FALSE',
},
match_author_to_committer_date: {
value: false,
configured_value: 'FALSE',
},
reject_empty_commit: {
value: false,
configured_value: 'FALSE',
},
enable_reviewer_by_email: {
value: false,
configured_value: 'FALSE',
},
max_object_size_limit: {},
submit_type: 'MERGE_IF_NECESSARY',
default_submit_type: {
value: 'MERGE_IF_NECESSARY',
configured_value: 'INHERIT',
inherited_value: 'MERGE_IF_NECESSARY',
},
};
const REPO = 'test-repo';
const SCHEMES = {http: {}, repo: {}, ssh: {}};
@@ -50,71 +110,14 @@ limitations under the License.
sandbox = sinon.sandbox.create();
stub('gr-rest-api-interface', {
getLoggedIn() { return Promise.resolve(false); },
getProjectConfig() {
return Promise.resolve({
description: 'Access inherited by all other projects.',
use_contributor_agreements: {
value: false,
configured_value: 'FALSE',
},
use_content_merge: {
value: false,
configured_value: 'FALSE',
},
use_signed_off_by: {
value: false,
configured_value: 'FALSE',
},
create_new_change_for_all_not_in_target: {
value: false,
configured_value: 'FALSE',
},
require_change_id: {
value: false,
configured_value: 'FALSE',
},
enable_signed_push: {
value: false,
configured_value: 'FALSE',
},
require_signed_push: {
value: false,
configured_value: 'FALSE',
},
reject_implicit_merges: {
value: false,
configured_value: 'FALSE',
},
private_by_default: {
value: false,
configured_value: 'FALSE',
},
match_author_to_committer_date: {
value: false,
configured_value: 'FALSE',
},
reject_empty_commit: {
value: false,
configured_value: 'FALSE',
},
enable_reviewer_by_email: {
value: false,
configured_value: 'FALSE',
},
max_object_size_limit: {},
submit_type: 'MERGE_IF_NECESSARY',
default_submit_type: {
value: 'MERGE_IF_NECESSARY',
configured_value: 'INHERIT',
inherited_value: 'MERGE_IF_NECESSARY',
},
});
},
getConfig() {
return Promise.resolve({download: {}});
},
});
element = fixture('basic');
repoStub = sandbox.stub(element.$.restAPI, 'getProjectConfig', () => {
return Promise.resolve(repoConf);
});
});
teardown(() => {
@@ -230,6 +233,24 @@ limitations under the License.
]);
});
test('fires page-error', done => {
repoStub.restore();
element.repo = 'test';
const response = {status: 404};
sandbox.stub(
element.$.restAPI, 'getProjectConfig', (repo, errFn) => {
errFn(response);
});
element.addEventListener('page-error', e => {
assert.deepEqual(e.detail.response, response);
done();
});
element._loadRepo();
});
suite('admin', () => {
setup(() => {
element.repo = REPO;

View File

@@ -196,6 +196,7 @@
'_showChangeView',
'_showDiffView',
'_showSettingsView',
'_showAdminView',
];
for (const showProp of props) {
this.set(showProp, false);

View File

@@ -238,18 +238,18 @@
return this.fetchJSON('/config/server/info');
},
getRepo(repo) {
getRepo(repo, opt_errFn) {
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
return this._fetchSharedCacheURL(
'/projects/' + encodeURIComponent(repo));
'/projects/' + encodeURIComponent(repo), opt_errFn);
},
getProjectConfig(repo) {
getProjectConfig(repo, opt_errFn) {
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
return this._fetchSharedCacheURL(
'/projects/' + encodeURIComponent(repo) + '/config');
'/projects/' + encodeURIComponent(repo) + '/config', opt_errFn);
},
getRepoAccess(repo) {
@@ -259,11 +259,12 @@
'/access/?project=' + encodeURIComponent(repo));
},
getRepoDashboards(repo) {
getRepoDashboards(repo, opt_errFn) {
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
return this._fetchSharedCacheURL(
`/projects/${encodeURIComponent(repo)}/dashboards?inherited`);
`/projects/${encodeURIComponent(repo)}/dashboards?inherited`,
opt_errFn);
},
saveRepoConfig(repo, config, opt_errFn, opt_ctx) {
@@ -309,9 +310,9 @@
opt_ctx);
},
getGroupConfig(group) {
getGroupConfig(group, opt_errFn) {
const encodeName = encodeURIComponent(group);
return this.fetchJSON(`/groups/${encodeName}/detail`);
return this.fetchJSON(`/groups/${encodeName}/detail`, opt_errFn);
},
/**
@@ -393,9 +394,9 @@
.then(configs => configs.hasOwnProperty(groupName));
},
getGroupMembers(groupName) {
getGroupMembers(groupName, opt_errFn) {
const encodeName = encodeURIComponent(groupName);
return this.send('GET', `/groups/${encodeName}/members/`)
return this.send('GET', `/groups/${encodeName}/members/`, null, opt_errFn)
.then(response => this.getResponseObject(response));
},
@@ -426,8 +427,9 @@
return this.send('PUT', `/groups/${encodeId}/options`, options);
},
getGroupAuditLog(group) {
return this._fetchSharedCacheURL('/groups/' + group + '/log.audit');
getGroupAuditLog(group, opt_errFn) {
return this._fetchSharedCacheURL(
'/groups/' + group + '/log.audit', opt_errFn);
},
saveGroupMembers(groupName, groupMembers) {
@@ -1160,9 +1162,10 @@
* @param {string} repo
* @param {number} reposBranchesPerPage
* @param {number=} opt_offset
* @param {?function(?Response, string=)=} opt_errFn
* @return {!Promise<?Object>}
*/
getRepoBranches(filter, repo, reposBranchesPerPage, opt_offset) {
getRepoBranches(filter, repo, reposBranchesPerPage, opt_offset, opt_errFn) {
const offset = opt_offset || 0;
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
@@ -1170,7 +1173,8 @@
return this.fetchJSON(
`/projects/${encodeURIComponent(repo)}/branches` +
`?n=${reposBranchesPerPage + 1}&S=${offset}` +
this._computeFilter(filter)
this._computeFilter(filter),
opt_errFn
);
},
@@ -1179,9 +1183,10 @@
* @param {string} repo
* @param {number} reposTagsPerPage
* @param {number=} opt_offset
* @param {?function(?Response, string=)=} opt_errFn
* @return {!Promise<?Object>}
*/
getRepoTags(filter, repo, reposTagsPerPage, opt_offset) {
getRepoTags(filter, repo, reposTagsPerPage, opt_offset, opt_errFn) {
const offset = opt_offset || 0;
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
@@ -1189,7 +1194,8 @@
return this.fetchJSON(
`/projects/${encodeURIComponent(repo)}/tags` +
`?n=${reposTagsPerPage + 1}&S=${offset}` +
this._computeFilter(filter)
this._computeFilter(filter),
opt_errFn
);
},
@@ -1197,21 +1203,26 @@
* @param {string} filter
* @param {number} pluginsPerPage
* @param {number=} opt_offset
* @param {?function(?Response, string=)=} opt_errFn
* @return {!Promise<?Object>}
*/
getPlugins(filter, pluginsPerPage, opt_offset) {
getPlugins(filter, pluginsPerPage, opt_offset, opt_errFn) {
const offset = opt_offset || 0;
return this.fetchJSON(
`/plugins/?all&n=${pluginsPerPage + 1}&S=${offset}` +
this._computeFilter(filter)
this._computeFilter(filter),
opt_errFn
);
},
getRepoAccessRights(repoName) {
getRepoAccessRights(repoName, opt_errFn) {
// TODO(kaspern): Rename rest api from /projects/ to /repos/ once backend
// supports it.
return this.fetchJSON(`/projects/${encodeURIComponent(repoName)}/access`);
return this.fetchJSON(
`/projects/${encodeURIComponent(repoName)}/access`,
opt_errFn
);
},
setRepoAccessRights(repoName, repoInfo) {
@@ -1995,8 +2006,8 @@
});
},
getCapabilities(token) {
return this.fetchJSON('/config/server/capabilities');
getCapabilities(token, opt_errFn) {
return this.fetchJSON('/config/server/capabilities', opt_errFn);
},
setAssignee(changeNum, assignee) {