Editor for accounts/groups list.
To implement some new UI feature, we need a way to specify list of accounts and/or groups. Similar solutions already exists for 'change' view, but it can't be reused as-is in other places. In this commit the existing solution was reworked to use the same editor in different places. Editor was splitted to 2 parts - editor itself and suggestion provider. Change-Id: I43ce060e568a69f9842fbfad6f5fd62361ab2022
This commit is contained in:
@@ -15,33 +15,28 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<script src="../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
(function(window) {
|
(function(window) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const ANONYMOUS_NAME = 'Anonymous';
|
|
||||||
|
|
||||||
window.Gerrit = window.Gerrit || {};
|
window.Gerrit = window.Gerrit || {};
|
||||||
|
|
||||||
/** @polymerBehavior Gerrit.AnonymousNameBehavior */
|
/** @polymerBehavior Gerrit.DisplayNameBehavior */
|
||||||
Gerrit.AnonymousNameBehavior = {
|
Gerrit.DisplayNameBehavior = {
|
||||||
|
// TODO(dmfilippov) replace DisplayNameBehavior with GrDisplayNameUtils
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* enableEmail when true enables to fallback to using email if
|
* enableEmail when true enables to fallback to using email if
|
||||||
* the account name is not avilable.
|
* the account name is not avilable.
|
||||||
*/
|
*/
|
||||||
getUserName(config, account, enableEmail) {
|
getUserName(config, account, enableEmail) {
|
||||||
if (account && account.name) {
|
return GrDisplayNameUtils.getUserName(config, account, enableEmail);
|
||||||
return account.name;
|
},
|
||||||
} else if (account && account.username) {
|
|
||||||
return account.username;
|
|
||||||
} else if (enableEmail && account && account.email) {
|
|
||||||
return account.email;
|
|
||||||
} else if (config && config.user &&
|
|
||||||
config.user.anonymous_coward_name !== 'Anonymous Coward') {
|
|
||||||
return config.user.anonymous_coward_name;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ANONYMOUS_NAME;
|
getGroupDisplayName(group) {
|
||||||
|
return GrDisplayNameUtils.getGroupDisplayName(group);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
})(window);
|
})(window);
|
||||||
@@ -17,14 +17,14 @@ limitations under the License.
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
<meta name="viewport" content="width=device-width, minimum-scale=1.0, initial-scale=1.0, user-scalable=yes">
|
||||||
<title>gr-anonymous-name-behavior</title>
|
<title>gr-display-name-behavior</title>
|
||||||
<script src="/test/common-test-setup.js"></script>
|
<script src="/test/common-test-setup.js"></script>
|
||||||
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
||||||
|
|
||||||
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
|
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
|
||||||
<script src="/bower_components/web-component-tester/browser.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="../../test/common-test-setup.html"/>
|
||||||
<link rel="import" href="gr-anonymous-name-behavior.html">
|
<link rel="import" href="gr-display-name-behavior.html">
|
||||||
|
|
||||||
<test-fixture id="basic">
|
<test-fixture id="basic">
|
||||||
<template>
|
<template>
|
||||||
@@ -33,7 +33,7 @@ limitations under the License.
|
|||||||
</test-fixture>
|
</test-fixture>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
suite('gr-anonymous-name-behavior tests', () => {
|
suite('gr-display-name-behavior tests', () => {
|
||||||
let element;
|
let element;
|
||||||
// eslint-disable-next-line no-unused-vars
|
// eslint-disable-next-line no-unused-vars
|
||||||
const config = {
|
const config = {
|
||||||
@@ -48,7 +48,7 @@ limitations under the License.
|
|||||||
is: 'test-element-anon',
|
is: 'test-element-anon',
|
||||||
_legacyUndefinedCheck: true,
|
_legacyUndefinedCheck: true,
|
||||||
behaviors: [
|
behaviors: [
|
||||||
Gerrit.AnonymousNameBehavior,
|
Gerrit.DisplayNameBehavior,
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -57,21 +57,21 @@ limitations under the License.
|
|||||||
element = fixture('basic');
|
element = fixture('basic');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test for it to return name', () => {
|
test('getUserName name only', () => {
|
||||||
const account = {
|
const account = {
|
||||||
name: 'test-name',
|
name: 'test-name',
|
||||||
};
|
};
|
||||||
assert.deepEqual(element.getUserName(config, account, true), 'test-name');
|
assert.deepEqual(element.getUserName(config, account, true), 'test-name');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test for it to return username', () => {
|
test('getUserName username only', () => {
|
||||||
const account = {
|
const account = {
|
||||||
username: 'test-user',
|
username: 'test-user',
|
||||||
};
|
};
|
||||||
assert.deepEqual(element.getUserName(config, account, true), 'test-user');
|
assert.deepEqual(element.getUserName(config, account, true), 'test-user');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test for it to return email', () => {
|
test('getUserName email only', () => {
|
||||||
const account = {
|
const account = {
|
||||||
email: 'test-user@test-url.com',
|
email: 'test-user@test-url.com',
|
||||||
};
|
};
|
||||||
@@ -79,11 +79,11 @@ limitations under the License.
|
|||||||
'test-user@test-url.com');
|
'test-user@test-url.com');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test for it not to Anonymous Coward as the anon name', () => {
|
test('getUserName returns not Anonymous Coward as the anon name', () => {
|
||||||
assert.deepEqual(element.getUserName(config, null, true), 'Anonymous');
|
assert.deepEqual(element.getUserName(config, null, true), 'Anonymous');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('test for the config returning the anon name', () => {
|
test('getUserName for the config returning the anon name', () => {
|
||||||
const config = {
|
const config = {
|
||||||
user: {
|
user: {
|
||||||
anonymous_coward_name: 'Test Anon',
|
anonymous_coward_name: 'Test Anon',
|
||||||
@@ -91,5 +91,10 @@ limitations under the License.
|
|||||||
};
|
};
|
||||||
assert.deepEqual(element.getUserName(config, null, true), 'Test Anon');
|
assert.deepEqual(element.getUserName(config, null, true), 'Test Anon');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('getGroupDisplayName', () => {
|
||||||
|
assert.equal(element.getGroupDisplayName({name: 'Some user name'}),
|
||||||
|
'Some user name (group)');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
/**
|
|
||||||
* @license
|
|
||||||
* Copyright (C) 2016 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.
|
|
||||||
*/
|
|
||||||
(function() {
|
|
||||||
'use strict';
|
|
||||||
|
|
||||||
Polymer({
|
|
||||||
is: 'gr-account-entry',
|
|
||||||
_legacyUndefinedCheck: true,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fired when an account is entered.
|
|
||||||
*
|
|
||||||
* @event add
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When allowAnyInput is true, account-text-changed is fired when input text
|
|
||||||
* changed. This is needed so that the reply dialog's save button can be
|
|
||||||
* enabled for arbitrary cc's, which don't need a 'commit'.
|
|
||||||
*
|
|
||||||
* @event account-text-changed
|
|
||||||
*/
|
|
||||||
properties: {
|
|
||||||
allowAnyInput: Boolean,
|
|
||||||
borderless: Boolean,
|
|
||||||
change: Object,
|
|
||||||
filter: Function,
|
|
||||||
placeholder: String,
|
|
||||||
/**
|
|
||||||
* When true, account-entry uses the account suggest API endpoint, which
|
|
||||||
* suggests any account in that Gerrit instance (and does not suggest
|
|
||||||
* groups).
|
|
||||||
*
|
|
||||||
* When false/undefined, account-entry uses the suggest_reviewers API
|
|
||||||
* endpoint, which suggests any account or group in that Gerrit instance
|
|
||||||
* that is not already a reviewer (or is not CCed) on that change.
|
|
||||||
*/
|
|
||||||
allowAnyUser: Boolean,
|
|
||||||
|
|
||||||
// suggestFrom = 0 to enable default suggestions.
|
|
||||||
suggestFrom: {
|
|
||||||
type: Number,
|
|
||||||
value: 0,
|
|
||||||
},
|
|
||||||
|
|
||||||
query: {
|
|
||||||
type: Function,
|
|
||||||
value() {
|
|
||||||
return this._getReviewerSuggestions.bind(this);
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
_config: Object,
|
|
||||||
/** The value of the autocomplete entry. */
|
|
||||||
_inputText: {
|
|
||||||
type: String,
|
|
||||||
observer: '_inputTextChanged',
|
|
||||||
},
|
|
||||||
|
|
||||||
_loggedIn: Boolean,
|
|
||||||
},
|
|
||||||
|
|
||||||
behaviors: [
|
|
||||||
Gerrit.AnonymousNameBehavior,
|
|
||||||
Gerrit.FireBehavior,
|
|
||||||
],
|
|
||||||
|
|
||||||
attached() {
|
|
||||||
this.$.restAPI.getConfig().then(cfg => {
|
|
||||||
this._config = cfg;
|
|
||||||
});
|
|
||||||
this.$.restAPI.getLoggedIn().then(loggedIn => {
|
|
||||||
this._loggedIn = loggedIn;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
get focusStart() {
|
|
||||||
return this.$.input.focusStart;
|
|
||||||
},
|
|
||||||
|
|
||||||
focus() {
|
|
||||||
this.$.input.focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
clear() {
|
|
||||||
this.$.input.clear();
|
|
||||||
},
|
|
||||||
|
|
||||||
setText(text) {
|
|
||||||
this.$.input.setText(text);
|
|
||||||
},
|
|
||||||
|
|
||||||
getText() {
|
|
||||||
return this.$.input.text;
|
|
||||||
},
|
|
||||||
|
|
||||||
_handleInputCommit(e) {
|
|
||||||
this.fire('add', {value: e.detail.value});
|
|
||||||
this.$.input.focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
_accountOrAnon(reviewer) {
|
|
||||||
return this.getUserName(this._config, reviewer, false);
|
|
||||||
},
|
|
||||||
|
|
||||||
_inputTextChanged(text) {
|
|
||||||
if (text.length && this.allowAnyInput) {
|
|
||||||
this.dispatchEvent(new CustomEvent(
|
|
||||||
'account-text-changed', {bubbles: true, composed: true}));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_makeSuggestion(reviewer) {
|
|
||||||
let name;
|
|
||||||
let value;
|
|
||||||
const generateStatusStr = function(account) {
|
|
||||||
return account.status ? '(' + account.status + ')' : '';
|
|
||||||
};
|
|
||||||
if (reviewer.account) {
|
|
||||||
// Reviewer is an account suggestion from getChangeSuggestedReviewers.
|
|
||||||
const reviewerName = this._accountOrAnon(reviewer.account);
|
|
||||||
const reviewerEmail = this._reviewerEmail(reviewer.account.email);
|
|
||||||
const reviewerStatus = generateStatusStr(reviewer.account);
|
|
||||||
name = [reviewerName, reviewerEmail, reviewerStatus]
|
|
||||||
.filter(p => p.length > 0).join(' ');
|
|
||||||
value = reviewer;
|
|
||||||
} else if (reviewer.group) {
|
|
||||||
// Reviewer is a group suggestion from getChangeSuggestedReviewers.
|
|
||||||
name = reviewer.group.name + ' (group)';
|
|
||||||
value = reviewer;
|
|
||||||
} else if (reviewer._account_id) {
|
|
||||||
// Reviewer is an account suggestion from getSuggestedAccounts.
|
|
||||||
const reviewerName = this._accountOrAnon(reviewer);
|
|
||||||
const reviewerEmail = this._reviewerEmail(reviewer.email);
|
|
||||||
const reviewerStatus = generateStatusStr(reviewer);
|
|
||||||
name = [reviewerName, reviewerEmail, reviewerStatus]
|
|
||||||
.filter(p => p.length > 0).join(' ');
|
|
||||||
value = {account: reviewer, count: 1};
|
|
||||||
}
|
|
||||||
return {name, value};
|
|
||||||
},
|
|
||||||
|
|
||||||
_getReviewerSuggestions(input) {
|
|
||||||
if (!this.change || !this.change._number || !this._loggedIn) {
|
|
||||||
return Promise.resolve([]);
|
|
||||||
}
|
|
||||||
|
|
||||||
const api = this.$.restAPI;
|
|
||||||
const xhr = this.allowAnyUser ?
|
|
||||||
api.getSuggestedAccounts(`cansee:${this.change._number} ${input}`) :
|
|
||||||
api.getChangeSuggestedReviewers(this.change._number, input);
|
|
||||||
|
|
||||||
return xhr.then(reviewers => {
|
|
||||||
if (!reviewers) { return []; }
|
|
||||||
if (!this.filter) {
|
|
||||||
return reviewers.map(this._makeSuggestion.bind(this));
|
|
||||||
}
|
|
||||||
return reviewers
|
|
||||||
.filter(this.filter)
|
|
||||||
.map(this._makeSuggestion.bind(this));
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
_reviewerEmail(email) {
|
|
||||||
if (typeof email !== 'undefined') {
|
|
||||||
return '<' + email + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '';
|
|
||||||
},
|
|
||||||
});
|
|
||||||
})();
|
|
||||||
@@ -1,276 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<!--
|
|
||||||
@license
|
|
||||||
Copyright (C) 2016 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>gr-account-entry</title>
|
|
||||||
<script src="/test/common-test-setup.js"></script>
|
|
||||||
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
|
||||||
|
|
||||||
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
|
|
||||||
<script src="/bower_components/web-component-tester/browser.js"></script>
|
|
||||||
<link rel="import" href="../../../test/common-test-setup.html"/>
|
|
||||||
<script src="../../../scripts/util.js"></script>
|
|
||||||
|
|
||||||
<link rel="import" href="gr-account-entry.html">
|
|
||||||
|
|
||||||
<script>void(0);</script>
|
|
||||||
|
|
||||||
<test-fixture id="basic">
|
|
||||||
<template>
|
|
||||||
<gr-account-entry></gr-account-entry>
|
|
||||||
</template>
|
|
||||||
</test-fixture>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
suite('gr-account-entry tests', () => {
|
|
||||||
let sandbox;
|
|
||||||
let _nextAccountId = 0;
|
|
||||||
const makeAccount = function(opt_status) {
|
|
||||||
const accountId = ++_nextAccountId;
|
|
||||||
return {
|
|
||||||
_account_id: accountId,
|
|
||||||
name: 'name ' + accountId,
|
|
||||||
email: 'email ' + accountId,
|
|
||||||
status: opt_status,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let _nextAccountId2 = 0;
|
|
||||||
const makeAccount2 = function(opt_status) {
|
|
||||||
const accountId2 = ++_nextAccountId2;
|
|
||||||
return {
|
|
||||||
_account_id: accountId2,
|
|
||||||
email: 'email ' + accountId2,
|
|
||||||
status: opt_status,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
let _nextAccountId3 = 0;
|
|
||||||
const makeAccount3 = function(opt_status) {
|
|
||||||
const accountId3 = ++_nextAccountId3;
|
|
||||||
return {
|
|
||||||
_account_id: accountId3,
|
|
||||||
name: 'name ' + accountId3,
|
|
||||||
status: opt_status,
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
let owner;
|
|
||||||
let existingReviewer1;
|
|
||||||
let existingReviewer2;
|
|
||||||
let suggestion1;
|
|
||||||
let suggestion2;
|
|
||||||
let suggestion3;
|
|
||||||
let element;
|
|
||||||
|
|
||||||
setup(done => {
|
|
||||||
owner = makeAccount();
|
|
||||||
existingReviewer1 = makeAccount();
|
|
||||||
existingReviewer2 = makeAccount();
|
|
||||||
suggestion1 = {account: makeAccount()};
|
|
||||||
suggestion2 = {account: makeAccount()};
|
|
||||||
suggestion3 = {
|
|
||||||
group: {
|
|
||||||
id: 'suggested group id',
|
|
||||||
name: 'suggested group',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
stub('gr-rest-api-interface', {
|
|
||||||
getLoggedIn() { return Promise.resolve(true); },
|
|
||||||
});
|
|
||||||
|
|
||||||
element = fixture('basic');
|
|
||||||
element.change = {
|
|
||||||
_number: 42,
|
|
||||||
owner,
|
|
||||||
reviewers: {
|
|
||||||
CC: [existingReviewer1],
|
|
||||||
REVIEWER: [existingReviewer2],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
sandbox = sinon.sandbox.create();
|
|
||||||
return flush(done);
|
|
||||||
});
|
|
||||||
|
|
||||||
teardown(() => {
|
|
||||||
sandbox.restore();
|
|
||||||
});
|
|
||||||
|
|
||||||
suite('stubbed values for _getReviewerSuggestions', () => {
|
|
||||||
setup(() => {
|
|
||||||
stub('gr-rest-api-interface', {
|
|
||||||
getChangeSuggestedReviewers() {
|
|
||||||
const redundantSuggestion1 = {account: existingReviewer1};
|
|
||||||
const redundantSuggestion2 = {account: existingReviewer2};
|
|
||||||
const redundantSuggestion3 = {account: owner};
|
|
||||||
return Promise.resolve([redundantSuggestion1, redundantSuggestion2,
|
|
||||||
redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('_makeSuggestion formats account or group accordingly', () => {
|
|
||||||
let account = makeAccount();
|
|
||||||
const account2 = makeAccount2();
|
|
||||||
const account3 = makeAccount3();
|
|
||||||
let suggestion = element._makeSuggestion({account});
|
|
||||||
assert.deepEqual(suggestion, {
|
|
||||||
name: account.name + ' <' + account.email + '>',
|
|
||||||
value: {account},
|
|
||||||
});
|
|
||||||
|
|
||||||
const group = {name: 'test'};
|
|
||||||
suggestion = element._makeSuggestion({group});
|
|
||||||
assert.deepEqual(suggestion, {
|
|
||||||
name: group.name + ' (group)',
|
|
||||||
value: {group},
|
|
||||||
});
|
|
||||||
|
|
||||||
suggestion = element._makeSuggestion(account);
|
|
||||||
assert.deepEqual(suggestion, {
|
|
||||||
name: account.name + ' <' + account.email + '>',
|
|
||||||
value: {account, count: 1},
|
|
||||||
});
|
|
||||||
|
|
||||||
element._config = {
|
|
||||||
user: {
|
|
||||||
anonymous_coward_name: 'Anonymous Coward',
|
|
||||||
},
|
|
||||||
};
|
|
||||||
assert.deepEqual(element._accountOrAnon(account2), 'Anonymous');
|
|
||||||
|
|
||||||
account = makeAccount('OOO');
|
|
||||||
|
|
||||||
suggestion = element._makeSuggestion({account});
|
|
||||||
assert.deepEqual(suggestion, {
|
|
||||||
name: account.name + ' <' + account.email + '> (OOO)',
|
|
||||||
value: {account},
|
|
||||||
});
|
|
||||||
|
|
||||||
suggestion = element._makeSuggestion(account);
|
|
||||||
assert.deepEqual(suggestion, {
|
|
||||||
name: account.name + ' <' + account.email + '> (OOO)',
|
|
||||||
value: {account, count: 1},
|
|
||||||
});
|
|
||||||
|
|
||||||
sandbox.stub(element, '_reviewerEmail',
|
|
||||||
() => { return ''; });
|
|
||||||
|
|
||||||
suggestion = element._makeSuggestion(account3);
|
|
||||||
assert.deepEqual(suggestion, {
|
|
||||||
name: account3.name,
|
|
||||||
value: {account: account3, count: 1},
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('_reviewerEmail', () => {
|
|
||||||
assert.equal(
|
|
||||||
element._reviewerEmail('email@gerritreview.com'),
|
|
||||||
'<email@gerritreview.com>');
|
|
||||||
assert.equal(element._reviewerEmail(undefined), '');
|
|
||||||
});
|
|
||||||
|
|
||||||
test('_getReviewerSuggestions excludes owner+reviewers', done => {
|
|
||||||
element._getReviewerSuggestions().then(reviewers => {
|
|
||||||
// Default is no filtering.
|
|
||||||
assert.equal(reviewers.length, 6);
|
|
||||||
|
|
||||||
// Set up filter that only accepts suggestion1.
|
|
||||||
const accountId = suggestion1.account._account_id;
|
|
||||||
element.filter = function(suggestion) {
|
|
||||||
return suggestion.account &&
|
|
||||||
suggestion.account._account_id === accountId;
|
|
||||||
};
|
|
||||||
|
|
||||||
element._getReviewerSuggestions().then(reviewers => {
|
|
||||||
assert.deepEqual(reviewers, [element._makeSuggestion(suggestion1)]);
|
|
||||||
}).then(done);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('_getReviewerSuggestions short circuits when logged out', () => {
|
|
||||||
// API call is already stubbed.
|
|
||||||
const xhrSpy = element.$.restAPI.getChangeSuggestedReviewers;
|
|
||||||
element._loggedIn = false;
|
|
||||||
return element._getReviewerSuggestions('').then(() => {
|
|
||||||
assert.isFalse(xhrSpy.called);
|
|
||||||
element._loggedIn = true;
|
|
||||||
return element._getReviewerSuggestions('').then(() => {
|
|
||||||
assert.isTrue(xhrSpy.called);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test('allowAnyUser', done => {
|
|
||||||
const suggestReviewerStub =
|
|
||||||
sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
|
|
||||||
.returns(Promise.resolve([]));
|
|
||||||
const suggestAccountStub =
|
|
||||||
sandbox.stub(element.$.restAPI, 'getSuggestedAccounts')
|
|
||||||
.returns(Promise.resolve([]));
|
|
||||||
|
|
||||||
element._getReviewerSuggestions('').then(() => {
|
|
||||||
assert.isTrue(suggestReviewerStub.calledOnce);
|
|
||||||
assert.isTrue(suggestReviewerStub.calledWith(42, ''));
|
|
||||||
assert.isFalse(suggestAccountStub.called);
|
|
||||||
element.allowAnyUser = true;
|
|
||||||
|
|
||||||
element._getReviewerSuggestions('').then(() => {
|
|
||||||
assert.isTrue(suggestReviewerStub.calledOnce);
|
|
||||||
assert.isTrue(suggestAccountStub.calledOnce);
|
|
||||||
assert.isTrue(suggestAccountStub.calledWith('cansee:42 '));
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
test('account-text-changed fired when input text changed and allowAnyInput',
|
|
||||||
() => {
|
|
||||||
// Spy on query, as that is called when _updateSuggestions proceeds.
|
|
||||||
const changeStub = sandbox.stub();
|
|
||||||
element.allowAnyInput = true;
|
|
||||||
sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
|
|
||||||
.returns(Promise.resolve([]));
|
|
||||||
element.addEventListener('account-text-changed', changeStub);
|
|
||||||
element.$.input.text = 'a';
|
|
||||||
assert.isTrue(changeStub.calledOnce);
|
|
||||||
element.$.input.text = 'ab';
|
|
||||||
assert.isTrue(changeStub.calledTwice);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('account-text-changed not fired when input text changed without ' +
|
|
||||||
'allowAnyUser', () => {
|
|
||||||
// Spy on query, as that is called when _updateSuggestions proceeds.
|
|
||||||
const changeStub = sandbox.stub();
|
|
||||||
sandbox.stub(element.$.restAPI, 'getChangeSuggestedReviewers')
|
|
||||||
.returns(Promise.resolve([]));
|
|
||||||
element.addEventListener('account-text-changed', changeStub);
|
|
||||||
element.$.input.text = 'a';
|
|
||||||
assert.isFalse(changeStub.called);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('setText', () => {
|
|
||||||
// Spy on query, as that is called when _updateSuggestions proceeds.
|
|
||||||
const suggestSpy = sandbox.spy(element.$.input, 'query');
|
|
||||||
element.setText('test text');
|
|
||||||
flushAsynchronousOperations();
|
|
||||||
|
|
||||||
assert.equal(element.$.input.$.input.value, 'test text');
|
|
||||||
assert.isFalse(suggestSpy.called);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@@ -36,6 +36,8 @@ limitations under the License.
|
|||||||
<link rel="import" href="../gr-change-requirements/gr-change-requirements.html">
|
<link rel="import" href="../gr-change-requirements/gr-change-requirements.html">
|
||||||
<link rel="import" href="../gr-commit-info/gr-commit-info.html">
|
<link rel="import" href="../gr-commit-info/gr-commit-info.html">
|
||||||
<link rel="import" href="../gr-reviewer-list/gr-reviewer-list.html">
|
<link rel="import" href="../gr-reviewer-list/gr-reviewer-list.html">
|
||||||
|
<script src="../../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
|
||||||
|
<script src="../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js"></script>
|
||||||
|
|
||||||
<dom-module id="gr-change-metadata">
|
<dom-module id="gr-change-metadata">
|
||||||
<template>
|
<template>
|
||||||
@@ -172,9 +174,9 @@ limitations under the License.
|
|||||||
id="assigneeValue"
|
id="assigneeValue"
|
||||||
placeholder="Set assignee..."
|
placeholder="Set assignee..."
|
||||||
accounts="{{_assignee}}"
|
accounts="{{_assignee}}"
|
||||||
change="[[change]]"
|
|
||||||
readonly="[[_computeAssigneeReadOnly(_mutable, change)]]"
|
readonly="[[_computeAssigneeReadOnly(_mutable, change)]]"
|
||||||
allow-any-user></gr-account-list>
|
suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
|
||||||
|
</gr-account-list>
|
||||||
</span>
|
</span>
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
|
|||||||
@@ -471,5 +471,12 @@
|
|||||||
// dom-if.
|
// dom-if.
|
||||||
this.$$('.topicEditableLabel').open();
|
this.$$('.topicEditableLabel').open();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getReviewerSuggestionsProvider(change) {
|
||||||
|
const provider = new GrReviewerSuggestionsProvider(this.$.restAPI,
|
||||||
|
change._number, true);
|
||||||
|
provider.init();
|
||||||
|
return provider;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -32,9 +32,11 @@ limitations under the License.
|
|||||||
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
|
<link rel="import" href="../../shared/gr-overlay/gr-overlay.html">
|
||||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
<link rel="import" href="../../shared/gr-storage/gr-storage.html">
|
<link rel="import" href="../../shared/gr-storage/gr-storage.html">
|
||||||
<link rel="import" href="../gr-account-list/gr-account-list.html">
|
<link rel="import" href="../../shared/gr-account-list/gr-account-list.html">
|
||||||
<link rel="import" href="../gr-label-scores/gr-label-scores.html">
|
<link rel="import" href="../gr-label-scores/gr-label-scores.html">
|
||||||
<link rel="import" href="../../../styles/shared-styles.html">
|
<link rel="import" href="../../../styles/shared-styles.html">
|
||||||
|
<script src="../../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
|
||||||
|
<script src="../../../scripts/gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider.js"></script>
|
||||||
|
|
||||||
<dom-module id="gr-reply-dialog">
|
<dom-module id="gr-reply-dialog">
|
||||||
<template>
|
<template>
|
||||||
@@ -165,11 +167,11 @@ limitations under the License.
|
|||||||
id="reviewers"
|
id="reviewers"
|
||||||
accounts="{{_reviewers}}"
|
accounts="{{_reviewers}}"
|
||||||
removable-values="[[change.removable_reviewers]]"
|
removable-values="[[change.removable_reviewers]]"
|
||||||
change="[[change]]"
|
|
||||||
filter="[[filterReviewerSuggestion]]"
|
filter="[[filterReviewerSuggestion]]"
|
||||||
pending-confirmation="{{_reviewerPendingConfirmation}}"
|
pending-confirmation="{{_reviewerPendingConfirmation}}"
|
||||||
placeholder="Add reviewer..."
|
placeholder="Add reviewer..."
|
||||||
on-account-text-changed="_handleAccountTextEntry">
|
on-account-text-changed="_handleAccountTextEntry"
|
||||||
|
suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
|
||||||
</gr-account-list>
|
</gr-account-list>
|
||||||
</div>
|
</div>
|
||||||
<div class="peopleList">
|
<div class="peopleList">
|
||||||
@@ -177,12 +179,12 @@ limitations under the License.
|
|||||||
<gr-account-list
|
<gr-account-list
|
||||||
id="ccs"
|
id="ccs"
|
||||||
accounts="{{_ccs}}"
|
accounts="{{_ccs}}"
|
||||||
change="[[change]]"
|
|
||||||
filter="[[filterCCSuggestion]]"
|
filter="[[filterCCSuggestion]]"
|
||||||
pending-confirmation="{{_ccPendingConfirmation}}"
|
pending-confirmation="{{_ccPendingConfirmation}}"
|
||||||
allow-any-input
|
allow-any-input
|
||||||
placeholder="Add CC..."
|
placeholder="Add CC..."
|
||||||
on-account-text-changed="_handleAccountTextEntry">
|
on-account-text-changed="_handleAccountTextEntry"
|
||||||
|
suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
|
||||||
</gr-account-list>
|
</gr-account-list>
|
||||||
</div>
|
</div>
|
||||||
<gr-overlay
|
<gr-overlay
|
||||||
|
|||||||
@@ -895,5 +895,12 @@
|
|||||||
_sendDisabledChanged(sendDisabled) {
|
_sendDisabledChanged(sendDisabled) {
|
||||||
this.dispatchEvent(new CustomEvent('send-disabled-changed'));
|
this.dispatchEvent(new CustomEvent('send-disabled-changed'));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_getReviewerSuggestionsProvider(change) {
|
||||||
|
const provider = new GrReviewerSuggestionsProvider(this.$.restAPI,
|
||||||
|
change._number, false);
|
||||||
|
provider.init();
|
||||||
|
return provider;
|
||||||
|
},
|
||||||
});
|
});
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
|
<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.html">
|
||||||
<link rel="import" href="/bower_components/polymer/polymer.html">
|
<link rel="import" href="/bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
<link rel="import" href="../../shared/gr-button/gr-button.html">
|
||||||
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
|
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
|
||||||
|
|||||||
@@ -58,7 +58,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
behaviors: [
|
behaviors: [
|
||||||
Gerrit.AnonymousNameBehavior,
|
Gerrit.DisplayNameBehavior,
|
||||||
],
|
],
|
||||||
|
|
||||||
detached() {
|
detached() {
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ limitations under the License.
|
|||||||
-->
|
-->
|
||||||
<link rel="import" href="/bower_components/polymer/polymer.html">
|
<link rel="import" href="/bower_components/polymer/polymer.html">
|
||||||
|
|
||||||
<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
|
<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.html">
|
||||||
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
<link rel="import" href="../../core/gr-navigation/gr-navigation.html">
|
||||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
<link rel="import" href="../gr-search-bar/gr-search-bar.html">
|
<link rel="import" href="../gr-search-bar/gr-search-bar.html">
|
||||||
|
|||||||
@@ -49,7 +49,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
behaviors: [
|
behaviors: [
|
||||||
Gerrit.AnonymousNameBehavior,
|
Gerrit.DisplayNameBehavior,
|
||||||
],
|
],
|
||||||
|
|
||||||
attached() {
|
attached() {
|
||||||
|
|||||||
@@ -15,12 +15,11 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
|
|
||||||
<link rel="import" href="/bower_components/polymer/polymer.html">
|
<link rel="import" href="/bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
|
<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
|
||||||
<link rel="import" href="../../../styles/shared-styles.html">
|
<link rel="import" href="../../../styles/shared-styles.html">
|
||||||
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
|
<link rel="import" href="../gr-autocomplete/gr-autocomplete.html">
|
||||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
|
||||||
|
|
||||||
<dom-module id="gr-account-entry">
|
<dom-module id="gr-account-entry">
|
||||||
<template>
|
<template>
|
||||||
@@ -36,14 +35,13 @@ limitations under the License.
|
|||||||
borderless="[[borderless]]"
|
borderless="[[borderless]]"
|
||||||
placeholder="[[placeholder]]"
|
placeholder="[[placeholder]]"
|
||||||
threshold="[[suggestFrom]]"
|
threshold="[[suggestFrom]]"
|
||||||
query="[[query]]"
|
query="[[querySuggestions]]"
|
||||||
allow-non-suggested-values="[[allowAnyInput]]"
|
allow-non-suggested-values="[[allowAnyInput]]"
|
||||||
on-commit="_handleInputCommit"
|
on-commit="_handleInputCommit"
|
||||||
clear-on-commit
|
clear-on-commit
|
||||||
warn-uncommitted
|
warn-uncommitted
|
||||||
text="{{_inputText}}">
|
text="{{_inputText}}">
|
||||||
</gr-autocomplete>
|
</gr-autocomplete>
|
||||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
|
||||||
</template>
|
</template>
|
||||||
<script src="gr-account-entry.js"></script>
|
<script src="gr-account-entry.js"></script>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2016 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.
|
||||||
|
*/
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gr-account-entry is an element for entering account
|
||||||
|
* and/or group with autocomplete support.
|
||||||
|
*/
|
||||||
|
Polymer({
|
||||||
|
is: 'gr-account-entry',
|
||||||
|
_legacyUndefinedCheck: true,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fired when an account is entered.
|
||||||
|
*
|
||||||
|
* @event add
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When allowAnyInput is true, account-text-changed is fired when input text
|
||||||
|
* changed. This is needed so that the reply dialog's save button can be
|
||||||
|
* enabled for arbitrary cc's, which don't need a 'commit'.
|
||||||
|
*
|
||||||
|
* @event account-text-changed
|
||||||
|
*/
|
||||||
|
properties: {
|
||||||
|
allowAnyInput: Boolean,
|
||||||
|
borderless: Boolean,
|
||||||
|
placeholder: String,
|
||||||
|
|
||||||
|
// suggestFrom = 0 to enable default suggestions.
|
||||||
|
suggestFrom: {
|
||||||
|
type: Number,
|
||||||
|
value: 0,
|
||||||
|
},
|
||||||
|
|
||||||
|
/** @type {!function(string): !Promise<Array<{name, value}>>} */
|
||||||
|
querySuggestions: {
|
||||||
|
type: Function,
|
||||||
|
notify: true,
|
||||||
|
value() {
|
||||||
|
return input => Promise.resolve([]);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
_config: Object,
|
||||||
|
/** The value of the autocomplete entry. */
|
||||||
|
_inputText: {
|
||||||
|
type: String,
|
||||||
|
observer: '_inputTextChanged',
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
get focusStart() {
|
||||||
|
return this.$.input.focusStart;
|
||||||
|
},
|
||||||
|
|
||||||
|
focus() {
|
||||||
|
this.$.input.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
clear() {
|
||||||
|
this.$.input.clear();
|
||||||
|
},
|
||||||
|
|
||||||
|
setText(text) {
|
||||||
|
this.$.input.setText(text);
|
||||||
|
},
|
||||||
|
|
||||||
|
getText() {
|
||||||
|
return this.$.input.text;
|
||||||
|
},
|
||||||
|
|
||||||
|
_handleInputCommit(e) {
|
||||||
|
this.fire('add', {value: e.detail.value});
|
||||||
|
this.$.input.focus();
|
||||||
|
},
|
||||||
|
|
||||||
|
_inputTextChanged(text) {
|
||||||
|
if (text.length && this.allowAnyInput) {
|
||||||
|
this.dispatchEvent(new CustomEvent(
|
||||||
|
'account-text-changed', {bubbles: true, composed: true}));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
})();
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2016 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>gr-account-entry</title>
|
||||||
|
<script src="/test/common-test-setup.js"></script>
|
||||||
|
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
||||||
|
|
||||||
|
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
|
||||||
|
<script src="/bower_components/web-component-tester/browser.js"></script>
|
||||||
|
<link rel="import" href="../../../test/common-test-setup.html"/>
|
||||||
|
<script src="../../../scripts/util.js"></script>
|
||||||
|
|
||||||
|
<link rel="import" href="gr-account-entry.html">
|
||||||
|
|
||||||
|
<script>void(0);</script>
|
||||||
|
|
||||||
|
<test-fixture id="basic">
|
||||||
|
<template>
|
||||||
|
<gr-account-entry></gr-account-entry>
|
||||||
|
</template>
|
||||||
|
</test-fixture>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-account-entry tests', () => {
|
||||||
|
let sandbox;
|
||||||
|
|
||||||
|
const suggestion1 = {
|
||||||
|
email: 'email1@example.com',
|
||||||
|
_account_id: 1,
|
||||||
|
some_property: 'value',
|
||||||
|
};
|
||||||
|
const suggestion2 = {
|
||||||
|
email: 'email2@example.com',
|
||||||
|
_account_id: 2,
|
||||||
|
};
|
||||||
|
const suggestion3 = {
|
||||||
|
email: 'email25@example.com',
|
||||||
|
_account_id: 25,
|
||||||
|
some_other_property: 'other value',
|
||||||
|
};
|
||||||
|
|
||||||
|
setup(done => {
|
||||||
|
element = fixture('basic');
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
return flush(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('stubbed values for querySuggestions', () => {
|
||||||
|
setup(() => {
|
||||||
|
element.querySuggestions = input => {
|
||||||
|
return Promise.resolve([
|
||||||
|
suggestion1,
|
||||||
|
suggestion2,
|
||||||
|
suggestion3,
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('account-text-changed fired when input text changed and allowAnyInput',
|
||||||
|
() => {
|
||||||
|
// Spy on query, as that is called when _updateSuggestions proceeds.
|
||||||
|
const changeStub = sandbox.stub();
|
||||||
|
element.allowAnyInput = true;
|
||||||
|
element.querySuggestions = input => Promise.resolve([]);
|
||||||
|
element.addEventListener('account-text-changed', changeStub);
|
||||||
|
element.$.input.text = 'a';
|
||||||
|
assert.isTrue(changeStub.calledOnce);
|
||||||
|
element.$.input.text = 'ab';
|
||||||
|
assert.isTrue(changeStub.calledTwice);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('account-text-changed not fired when input text changed without ' +
|
||||||
|
'allowAnyInput', () => {
|
||||||
|
// Spy on query, as that is called when _updateSuggestions proceeds.
|
||||||
|
const changeStub = sandbox.stub();
|
||||||
|
element.querySuggestions = input => Promise.resolve([]);
|
||||||
|
element.addEventListener('account-text-changed', changeStub);
|
||||||
|
element.$.input.text = 'a';
|
||||||
|
assert.isFalse(changeStub.called);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('setText', () => {
|
||||||
|
// Spy on query, as that is called when _updateSuggestions proceeds.
|
||||||
|
const suggestSpy = sandbox.spy(element.$.input, 'query');
|
||||||
|
element.setText('test text');
|
||||||
|
flushAsynchronousOperations();
|
||||||
|
|
||||||
|
assert.equal(element.$.input.$.input.value, 'test text');
|
||||||
|
assert.isFalse(suggestSpy.called);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
|
<link rel="import" href="../../../behaviors/gr-display-name-behavior/gr-display-name-behavior.html">
|
||||||
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
|
<link rel="import" href="../../../behaviors/gr-tooltip-behavior/gr-tooltip-behavior.html">
|
||||||
<link rel="import" href="/bower_components/polymer/polymer.html">
|
<link rel="import" href="/bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../../../styles/shared-styles.html">
|
<link rel="import" href="../../../styles/shared-styles.html">
|
||||||
|
|||||||
@@ -52,7 +52,7 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
behaviors: [
|
behaviors: [
|
||||||
Gerrit.AnonymousNameBehavior,
|
Gerrit.DisplayNameBehavior,
|
||||||
Gerrit.TooltipBehavior,
|
Gerrit.TooltipBehavior,
|
||||||
],
|
],
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ limitations under the License.
|
|||||||
|
|
||||||
<link rel="import" href="/bower_components/polymer/polymer.html">
|
<link rel="import" href="/bower_components/polymer/polymer.html">
|
||||||
<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
|
<link rel="import" href="../../../behaviors/fire-behavior/fire-behavior.html">
|
||||||
<link rel="import" href="../../shared/gr-account-chip/gr-account-chip.html">
|
<link rel="import" href="../gr-account-chip/gr-account-chip.html">
|
||||||
<link rel="import" href="../gr-account-entry/gr-account-entry.html">
|
<link rel="import" href="../gr-account-entry/gr-account-entry.html">
|
||||||
<link rel="import" href="../../../styles/shared-styles.html">
|
<link rel="import" href="../../../styles/shared-styles.html">
|
||||||
|
|
||||||
@@ -56,7 +56,7 @@ limitations under the License.
|
|||||||
account="[[account]]"
|
account="[[account]]"
|
||||||
class$="[[_computeChipClass(account)]]"
|
class$="[[_computeChipClass(account)]]"
|
||||||
data-account-id$="[[account._account_id]]"
|
data-account-id$="[[account._account_id]]"
|
||||||
removable="[[_computeRemovable(account)]]"
|
removable="[[_computeRemovable(account, readonly)]]"
|
||||||
on-keydown="_handleChipKeydown"
|
on-keydown="_handleChipKeydown"
|
||||||
tabindex="-1">
|
tabindex="-1">
|
||||||
</gr-account-chip>
|
</gr-account-chip>
|
||||||
@@ -67,13 +67,13 @@ limitations under the License.
|
|||||||
hidden$="[[_computeEntryHidden(maxCount, accounts.*, readonly)]]"
|
hidden$="[[_computeEntryHidden(maxCount, accounts.*, readonly)]]"
|
||||||
id="entry"
|
id="entry"
|
||||||
change="[[change]]"
|
change="[[change]]"
|
||||||
filter="[[filter]]"
|
|
||||||
placeholder="[[placeholder]]"
|
placeholder="[[placeholder]]"
|
||||||
on-add="_handleAdd"
|
on-add="_handleAdd"
|
||||||
on-input-keydown="_handleInputKeydown"
|
on-input-keydown="_handleInputKeydown"
|
||||||
allow-any-input="[[allowAnyInput]]"
|
allow-any-input="[[allowAnyInput]]"
|
||||||
allow-any-user="[[allowAnyUser]]">
|
query-suggestions="[[_querySuggestions]]">
|
||||||
</gr-account-entry>
|
</gr-account-entry>
|
||||||
|
<slot></slot>
|
||||||
</template>
|
</template>
|
||||||
<script src="gr-account-list.js"></script>
|
<script src="gr-account-list.js"></script>
|
||||||
</dom-module>
|
</dom-module>
|
||||||
@@ -19,6 +19,24 @@
|
|||||||
|
|
||||||
const VALID_EMAIL_ALERT = 'Please input a valid email.';
|
const VALID_EMAIL_ALERT = 'Please input a valid email.';
|
||||||
|
|
||||||
|
const Defs = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* name: string,
|
||||||
|
* value: Object,
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
Defs.GrSuggestionItem;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {{
|
||||||
|
* getSuggestions: function(string): Promise<Array<Object>>,
|
||||||
|
* makeSuggestionItem: function(Object): Defs.GrSuggestionItem,
|
||||||
|
* }}
|
||||||
|
*/
|
||||||
|
Defs.GrSuggestionsProvider;
|
||||||
|
|
||||||
Polymer({
|
Polymer({
|
||||||
is: 'gr-account-list',
|
is: 'gr-account-list',
|
||||||
_legacyUndefinedCheck: true,
|
_legacyUndefinedCheck: true,
|
||||||
@@ -38,6 +56,19 @@
|
|||||||
change: Object,
|
change: Object,
|
||||||
filter: Function,
|
filter: Function,
|
||||||
placeholder: String,
|
placeholder: String,
|
||||||
|
disabled: {
|
||||||
|
type: Function,
|
||||||
|
value: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns suggestions and convert them to list item
|
||||||
|
* @type {Defs.GrSuggestionsProvider}
|
||||||
|
*/
|
||||||
|
suggestionsProvider: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Needed for template checking since value is initially set to null.
|
* Needed for template checking since value is initially set to null.
|
||||||
* @type {?Object} */
|
* @type {?Object} */
|
||||||
@@ -50,21 +81,6 @@
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
value: false,
|
value: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
|
||||||
* When true, the account-entry autocomplete uses the account suggest API
|
|
||||||
* endpoint, which suggests any account in that Gerrit instance (and does
|
|
||||||
* not suggest groups).
|
|
||||||
*
|
|
||||||
* When false/undefined, account-entry uses the suggest_reviewers API
|
|
||||||
* endpoint, which suggests any account or group in that Gerrit instance
|
|
||||||
* that is not already a reviewer (or is not CCed) on that change.
|
|
||||||
*/
|
|
||||||
allowAnyUser: {
|
|
||||||
type: Boolean,
|
|
||||||
value: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* When true, allows for non-suggested inputs to be added.
|
* When true, allows for non-suggested inputs to be added.
|
||||||
*/
|
*/
|
||||||
@@ -82,6 +98,16 @@
|
|||||||
type: Number,
|
type: Number,
|
||||||
value: 0,
|
value: 0,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/** Returns suggestion items
|
||||||
|
* @type {!function(string): Promise<Array<Defs.GrSuggestionItem>>}
|
||||||
|
*/
|
||||||
|
_querySuggestions: {
|
||||||
|
type: Function,
|
||||||
|
value() {
|
||||||
|
return this._getSuggestions.bind(this);
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
behaviors: [
|
behaviors: [
|
||||||
@@ -103,31 +129,46 @@
|
|||||||
return this.$.entry.focusStart;
|
return this.$.entry.focusStart;
|
||||||
},
|
},
|
||||||
|
|
||||||
_handleAdd(e) {
|
_getSuggestions(input) {
|
||||||
this._addReviewer(e.detail.value);
|
const provider = this.suggestionsProvider;
|
||||||
|
if (!provider) {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
return provider.getSuggestions(input).then(suggestions => {
|
||||||
|
if (!suggestions) { return []; }
|
||||||
|
if (this.filter) {
|
||||||
|
suggestions = suggestions.filter(this.filter);
|
||||||
|
}
|
||||||
|
return suggestions.map(suggestion =>
|
||||||
|
provider.makeSuggestionItem(suggestion));
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
_addReviewer(reviewer) {
|
_handleAdd(e) {
|
||||||
|
this._addAccountItem(e.detail.value);
|
||||||
|
},
|
||||||
|
|
||||||
|
_addAccountItem(item) {
|
||||||
// Append new account or group to the accounts property. We add our own
|
// Append new account or group to the accounts property. We add our own
|
||||||
// internal properties to the account/group here, so we clone the object
|
// internal properties to the account/group here, so we clone the object
|
||||||
// to avoid cluttering up the shared change object.
|
// to avoid cluttering up the shared change object.
|
||||||
if (reviewer.account) {
|
if (item.account) {
|
||||||
const account =
|
const account =
|
||||||
Object.assign({}, reviewer.account, {_pendingAdd: true});
|
Object.assign({}, item.account, {_pendingAdd: true});
|
||||||
this.push('accounts', account);
|
this.push('accounts', account);
|
||||||
} else if (reviewer.group) {
|
} else if (item.group) {
|
||||||
if (reviewer.confirm) {
|
if (item.confirm) {
|
||||||
this.pendingConfirmation = reviewer;
|
this.pendingConfirmation = item;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const group = Object.assign({}, reviewer.group,
|
const group = Object.assign({}, item.group,
|
||||||
{_pendingAdd: true, _group: true});
|
{_pendingAdd: true, _group: true});
|
||||||
this.push('accounts', group);
|
this.push('accounts', group);
|
||||||
} else if (this.allowAnyInput) {
|
} else if (this.allowAnyInput) {
|
||||||
if (!reviewer.includes('@')) {
|
if (!item.includes('@')) {
|
||||||
// Repopulate the input with what the user tried to enter and have
|
// Repopulate the input with what the user tried to enter and have
|
||||||
// a toast tell them why they can't enter it.
|
// a toast tell them why they can't enter it.
|
||||||
this.$.entry.setText(reviewer);
|
this.$.entry.setText(item);
|
||||||
this.dispatchEvent(new CustomEvent('show-alert', {
|
this.dispatchEvent(new CustomEvent('show-alert', {
|
||||||
detail: {message: VALID_EMAIL_ALERT},
|
detail: {message: VALID_EMAIL_ALERT},
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -135,7 +176,7 @@
|
|||||||
}));
|
}));
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
const account = {email: reviewer, _pendingAdd: true};
|
const account = {email: item, _pendingAdd: true};
|
||||||
this.push('accounts', account);
|
this.push('accounts', account);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -173,8 +214,8 @@
|
|||||||
return a === b;
|
return a === b;
|
||||||
},
|
},
|
||||||
|
|
||||||
_computeRemovable(account) {
|
_computeRemovable(account, readonly) {
|
||||||
if (this.readonly) { return false; }
|
if (readonly) { return false; }
|
||||||
if (this.removableValues) {
|
if (this.removableValues) {
|
||||||
for (let i = 0; i < this.removableValues.length; i++) {
|
for (let i = 0; i < this.removableValues.length; i++) {
|
||||||
if (this._accountMatches(this.removableValues[i], account)) {
|
if (this._accountMatches(this.removableValues[i], account)) {
|
||||||
@@ -193,7 +234,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
_removeAccount(toRemove) {
|
_removeAccount(toRemove) {
|
||||||
if (!toRemove || !this._computeRemovable(toRemove)) { return; }
|
if (!toRemove || !this._computeRemovable(toRemove, this.readonly)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (let i = 0; i < this.accounts.length; i++) {
|
for (let i = 0; i < this.accounts.length; i++) {
|
||||||
let matches;
|
let matches;
|
||||||
const account = this.accounts[i];
|
const account = this.accounts[i];
|
||||||
@@ -277,7 +320,7 @@
|
|||||||
submitEntryText() {
|
submitEntryText() {
|
||||||
const text = this.$.entry.getText();
|
const text = this.$.entry.getText();
|
||||||
if (!text.length) { return true; }
|
if (!text.length) { return true; }
|
||||||
const wasSubmitted = this._addReviewer(text);
|
const wasSubmitted = this._addAccountItem(text);
|
||||||
if (wasSubmitted) { this.$.entry.clear(); }
|
if (wasSubmitted) { this.$.entry.clear(); }
|
||||||
return wasSubmitted;
|
return wasSubmitted;
|
||||||
},
|
},
|
||||||
@@ -35,6 +35,15 @@ limitations under the License.
|
|||||||
</test-fixture>
|
</test-fixture>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
class MockSuggestionsProvider {
|
||||||
|
getSuggestions(input) {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSuggestionItem(item) {
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
suite('gr-account-list tests', () => {
|
suite('gr-account-list tests', () => {
|
||||||
let _nextAccountId = 0;
|
let _nextAccountId = 0;
|
||||||
const makeAccount = function() {
|
const makeAccount = function() {
|
||||||
@@ -51,10 +60,11 @@ limitations under the License.
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
let existingReviewer1;
|
let existingAccount1;
|
||||||
let existingReviewer2;
|
let existingAccount2;
|
||||||
let sandbox;
|
let sandbox;
|
||||||
let element;
|
let element;
|
||||||
|
let suggestionsProvider;
|
||||||
|
|
||||||
function getChips() {
|
function getChips() {
|
||||||
return Polymer.dom(element.root).querySelectorAll('gr-account-chip');
|
return Polymer.dom(element.root).querySelectorAll('gr-account-chip');
|
||||||
@@ -62,14 +72,16 @@ limitations under the License.
|
|||||||
|
|
||||||
setup(() => {
|
setup(() => {
|
||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
existingReviewer1 = makeAccount();
|
existingAccount1 = makeAccount();
|
||||||
existingReviewer2 = makeAccount();
|
existingAccount2 = makeAccount();
|
||||||
|
|
||||||
stub('gr-rest-api-interface', {
|
stub('gr-rest-api-interface', {
|
||||||
getConfig() { return Promise.resolve({}); },
|
getConfig() { return Promise.resolve({}); },
|
||||||
});
|
});
|
||||||
element = fixture('basic');
|
element = fixture('basic');
|
||||||
element.accounts = [existingReviewer1, existingReviewer2];
|
element.accounts = [existingAccount1, existingAccount2];
|
||||||
|
suggestionsProvider = new MockSuggestionsProvider();
|
||||||
|
element.suggestionsProvider = suggestionsProvider;
|
||||||
});
|
});
|
||||||
|
|
||||||
teardown(() => {
|
teardown(() => {
|
||||||
@@ -109,7 +121,7 @@ limitations under the License.
|
|||||||
assert.isTrue(chips[2].classList.contains('pendingAdd'));
|
assert.isTrue(chips[2].classList.contains('pendingAdd'));
|
||||||
|
|
||||||
// Removed accounts are taken out of the list.
|
// Removed accounts are taken out of the list.
|
||||||
element.fire('remove', {account: existingReviewer1});
|
element.fire('remove', {account: existingAccount1});
|
||||||
flushAsynchronousOperations();
|
flushAsynchronousOperations();
|
||||||
chips = getChips();
|
chips = getChips();
|
||||||
assert.equal(chips.length, 2);
|
assert.equal(chips.length, 2);
|
||||||
@@ -117,7 +129,7 @@ limitations under the License.
|
|||||||
assert.isTrue(chips[1].classList.contains('pendingAdd'));
|
assert.isTrue(chips[1].classList.contains('pendingAdd'));
|
||||||
|
|
||||||
// Invalid remove is ignored.
|
// Invalid remove is ignored.
|
||||||
element.fire('remove', {account: existingReviewer1});
|
element.fire('remove', {account: existingAccount1});
|
||||||
element.fire('remove', {account: newAccount});
|
element.fire('remove', {account: newAccount});
|
||||||
flushAsynchronousOperations();
|
flushAsynchronousOperations();
|
||||||
chips = getChips();
|
chips = getChips();
|
||||||
@@ -147,6 +159,52 @@ limitations under the License.
|
|||||||
assert.isFalse(chips[0].classList.contains('pendingAdd'));
|
assert.isFalse(chips[0].classList.contains('pendingAdd'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('_getSuggestions uses filter correctly', done => {
|
||||||
|
const originalSuggestions = [
|
||||||
|
{
|
||||||
|
email: 'abc@example.com',
|
||||||
|
text: 'abcd',
|
||||||
|
_account_id: 3,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
email: 'qwe@example.com',
|
||||||
|
text: 'qwer',
|
||||||
|
_account_id: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
email: 'xyz@example.com',
|
||||||
|
text: 'aaaaa',
|
||||||
|
_account_id: 25,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
sandbox.stub(suggestionsProvider, 'getSuggestions')
|
||||||
|
.returns(Promise.resolve(originalSuggestions));
|
||||||
|
sandbox.stub(suggestionsProvider, 'makeSuggestionItem', suggestion => {
|
||||||
|
return {
|
||||||
|
name: suggestion.email,
|
||||||
|
value: suggestion._account_id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
element._getSuggestions().then(suggestions => {
|
||||||
|
// Default is no filtering.
|
||||||
|
assert.equal(suggestions.length, 3);
|
||||||
|
|
||||||
|
// Set up filter that only accepts suggestion1.
|
||||||
|
const accountId = originalSuggestions[0]._account_id;
|
||||||
|
element.filter = function(suggestion) {
|
||||||
|
return suggestion._account_id === accountId;
|
||||||
|
};
|
||||||
|
|
||||||
|
element._getSuggestions().then(suggestions => {
|
||||||
|
assert.deepEqual(suggestions,
|
||||||
|
[{name: originalSuggestions[0].email,
|
||||||
|
value: originalSuggestions[0]._account_id}]);
|
||||||
|
}).then(done);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
test('_computeChipClass', () => {
|
test('_computeChipClass', () => {
|
||||||
const account = makeAccount();
|
const account = makeAccount();
|
||||||
assert.equal(element._computeChipClass(account), '');
|
assert.equal(element._computeChipClass(account), '');
|
||||||
@@ -163,18 +221,18 @@ limitations under the License.
|
|||||||
newAccount._pendingAdd = true;
|
newAccount._pendingAdd = true;
|
||||||
element.readonly = false;
|
element.readonly = false;
|
||||||
element.removableValues = [];
|
element.removableValues = [];
|
||||||
assert.isFalse(element._computeRemovable(existingReviewer1));
|
assert.isFalse(element._computeRemovable(existingAccount1, false));
|
||||||
assert.isTrue(element._computeRemovable(newAccount));
|
assert.isTrue(element._computeRemovable(newAccount, false));
|
||||||
|
|
||||||
|
|
||||||
element.removableValues = [existingReviewer1];
|
element.removableValues = [existingAccount1];
|
||||||
assert.isTrue(element._computeRemovable(existingReviewer1));
|
assert.isTrue(element._computeRemovable(existingAccount1, false));
|
||||||
assert.isTrue(element._computeRemovable(newAccount));
|
assert.isTrue(element._computeRemovable(newAccount, false));
|
||||||
assert.isFalse(element._computeRemovable(existingReviewer2));
|
assert.isFalse(element._computeRemovable(existingAccount2, false));
|
||||||
|
|
||||||
element.readonly = true;
|
element.readonly = true;
|
||||||
assert.isFalse(element._computeRemovable(existingReviewer1));
|
assert.isFalse(element._computeRemovable(existingAccount1, true));
|
||||||
assert.isFalse(element._computeRemovable(newAccount));
|
assert.isFalse(element._computeRemovable(newAccount, true));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('submitEntryText', () => {
|
test('submitEntryText', () => {
|
||||||
@@ -293,13 +351,40 @@ limitations under the License.
|
|||||||
assert.isTrue(element.$.entry.hasAttribute('hidden'));
|
assert.isTrue(element.$.entry.hasAttribute('hidden'));
|
||||||
});
|
});
|
||||||
|
|
||||||
suite('allowAnyInput', () => {
|
test('enter text calls suggestions provider', done => {
|
||||||
let entry;
|
const suggestions = [
|
||||||
|
{
|
||||||
|
email: 'abc@example.com',
|
||||||
|
text: 'abcd',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
email: 'qwe@example.com',
|
||||||
|
text: 'qwer',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const getSuggestionsStub =
|
||||||
|
sandbox.stub(suggestionsProvider, 'getSuggestions')
|
||||||
|
.returns(Promise.resolve(suggestions));
|
||||||
|
|
||||||
|
const makeSuggestionItemStub =
|
||||||
|
sandbox.stub(suggestionsProvider, 'makeSuggestionItem', item => item);
|
||||||
|
|
||||||
|
const input = element.$.entry.$.input;
|
||||||
|
|
||||||
|
input.text = 'newTest';
|
||||||
|
MockInteractions.focus(input.$.input);
|
||||||
|
input.noDebounce = true;
|
||||||
|
flushAsynchronousOperations();
|
||||||
|
flush(() => {
|
||||||
|
assert.isTrue(getSuggestionsStub.calledOnce);
|
||||||
|
assert.equal(getSuggestionsStub.lastCall.args[0], 'newTest');
|
||||||
|
assert.equal(makeSuggestionItemStub.getCalls().length, 2);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('allowAnyInput', () => {
|
||||||
setup(() => {
|
setup(() => {
|
||||||
entry = element.$.entry;
|
|
||||||
sandbox.stub(entry, '_getReviewerSuggestions');
|
|
||||||
sandbox.stub(entry.$.input, '_updateSuggestions');
|
|
||||||
element.allowAnyInput = true;
|
element.allowAnyInput = true;
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -334,7 +419,6 @@ limitations under the License.
|
|||||||
suite('keyboard interactions', () => {
|
suite('keyboard interactions', () => {
|
||||||
test('backspace at text input start removes last account', () => {
|
test('backspace at text input start removes last account', () => {
|
||||||
const input = element.$.entry.$.input;
|
const input = element.$.entry.$.input;
|
||||||
sandbox.stub(element.$.entry, '_getReviewerSuggestions');
|
|
||||||
sandbox.stub(input, '_updateSuggestions');
|
sandbox.stub(input, '_updateSuggestions');
|
||||||
sandbox.stub(element, '_computeRemovable').returns(true);
|
sandbox.stub(element, '_computeRemovable').returns(true);
|
||||||
// Next line is a workaround for Firefix not moving cursor
|
// Next line is a workaround for Firefix not moving cursor
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (window.GrDisplayNameUtils) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ANONYMOUS_NAME = 'Anonymous';
|
||||||
|
|
||||||
|
class GrDisplayNameUtils {
|
||||||
|
/**
|
||||||
|
* enableEmail when true enables to fallback to using email if
|
||||||
|
* the account name is not avilable.
|
||||||
|
*/
|
||||||
|
static getUserName(config, account, enableEmail) {
|
||||||
|
if (account && account.name) {
|
||||||
|
return account.name;
|
||||||
|
} else if (account && account.username) {
|
||||||
|
return account.username;
|
||||||
|
} else if (enableEmail && account && account.email) {
|
||||||
|
return account.email;
|
||||||
|
} else if (config && config.user &&
|
||||||
|
config.user.anonymous_coward_name !== 'Anonymous Coward') {
|
||||||
|
return config.user.anonymous_coward_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ANONYMOUS_NAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
static getAccountDisplayName(config, account, enableEmail) {
|
||||||
|
const reviewerName = this._accountOrAnon(config, account, enableEmail);
|
||||||
|
const reviewerEmail = this._accountEmail(account.email);
|
||||||
|
const reviewerStatus = account.status ? '(' + account.status + ')' : '';
|
||||||
|
return [reviewerName, reviewerEmail, reviewerStatus]
|
||||||
|
.filter(p => p.length > 0).join(' ');
|
||||||
|
}
|
||||||
|
|
||||||
|
static _accountOrAnon(config, reviewer, enableEmail) {
|
||||||
|
return this.getUserName(config, reviewer, !!enableEmail);
|
||||||
|
}
|
||||||
|
|
||||||
|
static _accountEmail(email) {
|
||||||
|
if (typeof email !== 'undefined') {
|
||||||
|
return '<' + email + '>';
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
static getGroupDisplayName(group) {
|
||||||
|
return group.name + ' (group)';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GrDisplayNameUtils = GrDisplayNameUtils;
|
||||||
|
})(window);
|
||||||
@@ -0,0 +1,140 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2019 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>gr-display-name-utils</title>
|
||||||
|
<script src="/test/common-test-setup.js"></script>
|
||||||
|
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
||||||
|
|
||||||
|
<script src="/bower_components/webcomponentsjs/webcomponents-lite.js"></script>
|
||||||
|
<script src="/bower_components/web-component-tester/browser.js"></script>
|
||||||
|
<link rel="import" href="../../test/common-test-setup.html"/>
|
||||||
|
<script src="gr-display-name-utils.js"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('gr-display-name-utils tests', () => {
|
||||||
|
// eslint-disable-next-line no-unused-vars
|
||||||
|
const config = {
|
||||||
|
user: {
|
||||||
|
anonymous_coward_name: 'Anonymous Coward',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
test('getUserName name only', () => {
|
||||||
|
const account = {
|
||||||
|
name: 'test-name',
|
||||||
|
};
|
||||||
|
assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
|
||||||
|
'test-name');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getUserName username only', () => {
|
||||||
|
const account = {
|
||||||
|
username: 'test-user',
|
||||||
|
};
|
||||||
|
assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
|
||||||
|
'test-user');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getUserName email only', () => {
|
||||||
|
const account = {
|
||||||
|
email: 'test-user@test-url.com',
|
||||||
|
};
|
||||||
|
assert.deepEqual(GrDisplayNameUtils.getUserName(config, account, true),
|
||||||
|
'test-user@test-url.com');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getUserName returns not Anonymous Coward as the anon name', () => {
|
||||||
|
assert.deepEqual(GrDisplayNameUtils.getUserName(config, null, true),
|
||||||
|
'Anonymous');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getUserName for the config returning the anon name', () => {
|
||||||
|
const config = {
|
||||||
|
user: {
|
||||||
|
anonymous_coward_name: 'Test Anon',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
assert.deepEqual(GrDisplayNameUtils.getUserName(config, null, true),
|
||||||
|
'Test Anon');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAccountDisplayName - account with name only', () => {
|
||||||
|
assert.equal(
|
||||||
|
GrDisplayNameUtils.getAccountDisplayName(config,
|
||||||
|
{name: 'Some user name'}),
|
||||||
|
'Some user name');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAccountDisplayName - account with email only', () => {
|
||||||
|
assert.equal(
|
||||||
|
GrDisplayNameUtils.getAccountDisplayName(config,
|
||||||
|
{email: 'my@example.com'}),
|
||||||
|
'Anonymous <my@example.com>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAccountDisplayName - account with email only - allowEmail', () => {
|
||||||
|
assert.equal(
|
||||||
|
GrDisplayNameUtils.getAccountDisplayName(config,
|
||||||
|
{email: 'my@example.com'}, true),
|
||||||
|
'my@example.com <my@example.com>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAccountDisplayName - account with name and status', () => {
|
||||||
|
assert.equal(
|
||||||
|
GrDisplayNameUtils.getAccountDisplayName(config, {
|
||||||
|
name: 'Some name',
|
||||||
|
status: 'OOO',
|
||||||
|
}),
|
||||||
|
'Some name (OOO)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAccountDisplayName - account with name and email', () => {
|
||||||
|
assert.equal(
|
||||||
|
GrDisplayNameUtils.getAccountDisplayName(config, {
|
||||||
|
name: 'Some name',
|
||||||
|
email: 'my@example.com',
|
||||||
|
}),
|
||||||
|
'Some name <my@example.com>');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getAccountDisplayName - account with name, email and status', () => {
|
||||||
|
assert.equal(
|
||||||
|
GrDisplayNameUtils.getAccountDisplayName(config, {
|
||||||
|
name: 'Some name',
|
||||||
|
email: 'my@example.com',
|
||||||
|
status: 'OOO',
|
||||||
|
}),
|
||||||
|
'Some name <my@example.com> (OOO)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getGroupDisplayName', () => {
|
||||||
|
assert.equal(
|
||||||
|
GrDisplayNameUtils.getGroupDisplayName({name: 'Some user name'}),
|
||||||
|
'Some user name (group)');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('_accountEmail', () => {
|
||||||
|
assert.equal(
|
||||||
|
GrDisplayNameUtils._accountEmail('email@gerritreview.com'),
|
||||||
|
'<email@gerritreview.com>');
|
||||||
|
assert.equal(GrDisplayNameUtils._accountEmail(undefined), '');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2019 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.
|
||||||
|
*/
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (window.GrEmailSuggestionsProvider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GrEmailSuggestionsProvider {
|
||||||
|
constructor(restAPI) {
|
||||||
|
this._restAPI = restAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSuggestions(input) {
|
||||||
|
return this._restAPI.getSuggestedAccounts(`${input}`)
|
||||||
|
.then(accounts => {
|
||||||
|
if (!accounts) { return []; }
|
||||||
|
return accounts;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSuggestionItem(account) {
|
||||||
|
return {
|
||||||
|
name: GrDisplayNameUtils.getAccountDisplayName(null, account, true),
|
||||||
|
value: {account, count: 1},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GrEmailSuggestionsProvider = GrEmailSuggestionsProvider;
|
||||||
|
})(window);
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2019 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>gr-email-suggestions-provider</title>
|
||||||
|
<script src="/test/common-test-setup.js"></script>
|
||||||
|
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
||||||
|
|
||||||
|
<script src="/bower_components/webcomponentsjs/webcomponents-lite.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="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
|
||||||
|
<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
|
||||||
|
<script src="gr-email-suggestions-provider.js"></script>
|
||||||
|
|
||||||
|
|
||||||
|
<script>void(0);</script>
|
||||||
|
|
||||||
|
<test-fixture id="basic">
|
||||||
|
<template>
|
||||||
|
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||||
|
</template>
|
||||||
|
</test-fixture>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('GrEmailSuggestionsProvider tests', () => {
|
||||||
|
let sandbox;
|
||||||
|
let restAPI;
|
||||||
|
let provider;
|
||||||
|
const account1 = {
|
||||||
|
name: 'Some name',
|
||||||
|
email: 'some@example.com',
|
||||||
|
};
|
||||||
|
const account2 = {
|
||||||
|
email: 'other@example.com',
|
||||||
|
_account_id: 3,
|
||||||
|
};
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
|
||||||
|
stub('gr-rest-api-interface', {
|
||||||
|
getConfig() { return Promise.resolve({}); },
|
||||||
|
});
|
||||||
|
restAPI = fixture('basic');
|
||||||
|
provider = new GrEmailSuggestionsProvider(restAPI);
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getSuggestions', done => {
|
||||||
|
const getSuggestedAccountsStub =
|
||||||
|
sandbox.stub(restAPI, 'getSuggestedAccounts')
|
||||||
|
.returns(Promise.resolve([account1, account2]));
|
||||||
|
|
||||||
|
provider.getSuggestions('Some input').then(res => {
|
||||||
|
assert.deepEqual(res, [account1, account2]);
|
||||||
|
assert.isTrue(getSuggestedAccountsStub.calledOnce);
|
||||||
|
assert.equal(getSuggestedAccountsStub.lastCall.args[0], 'Some input');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('makeSuggestionItem', () => {
|
||||||
|
assert.deepEqual(provider.makeSuggestionItem(account1), {
|
||||||
|
name: 'Some name <some@example.com>',
|
||||||
|
value: {
|
||||||
|
account: account1,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(provider.makeSuggestionItem(account2), {
|
||||||
|
name: 'other@example.com <other@example.com>',
|
||||||
|
value: {
|
||||||
|
account: account2,
|
||||||
|
count: 1,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2019 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.
|
||||||
|
*/
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (window.GrGroupSuggestionsProvider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GrGroupSuggestionsProvider {
|
||||||
|
constructor(restAPI) {
|
||||||
|
this._restAPI = restAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSuggestions(input) {
|
||||||
|
return this._restAPI.getSuggestedGroups(`${input}`)
|
||||||
|
.then(groups => {
|
||||||
|
if (!groups) { return []; }
|
||||||
|
const keys = Object.keys(groups);
|
||||||
|
return keys.map(key => {
|
||||||
|
return Object.assign({}, groups[key], {name: key});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSuggestionItem(suggestion) {
|
||||||
|
return {name: suggestion.name,
|
||||||
|
value: {group: {name: suggestion.name, id: suggestion.id}}};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GrGroupSuggestionsProvider = GrGroupSuggestionsProvider;
|
||||||
|
})(window);
|
||||||
@@ -0,0 +1,106 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2019 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>gr-group-suggestions-provider</title>
|
||||||
|
<script src="/test/common-test-setup.js"></script>
|
||||||
|
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
||||||
|
|
||||||
|
<script src="/bower_components/webcomponentsjs/webcomponents-lite.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="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
|
||||||
|
<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
|
||||||
|
<script src="gr-group-suggestions-provider.js"></script>
|
||||||
|
|
||||||
|
<script>void(0);</script>
|
||||||
|
|
||||||
|
<test-fixture id="basic">
|
||||||
|
<template>
|
||||||
|
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||||
|
</template>
|
||||||
|
</test-fixture>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('GrGroupSuggestionsProvider tests', () => {
|
||||||
|
let sandbox;
|
||||||
|
let restAPI;
|
||||||
|
let provider;
|
||||||
|
const group1 = {
|
||||||
|
name: 'Some name',
|
||||||
|
id: 1,
|
||||||
|
};
|
||||||
|
const group2 = {
|
||||||
|
name: 'Other name',
|
||||||
|
id: 3,
|
||||||
|
url: 'abcd',
|
||||||
|
};
|
||||||
|
|
||||||
|
setup(() => {
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
|
||||||
|
stub('gr-rest-api-interface', {
|
||||||
|
getConfig() { return Promise.resolve({}); },
|
||||||
|
});
|
||||||
|
restAPI = fixture('basic');
|
||||||
|
provider = new GrGroupSuggestionsProvider(restAPI);
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getSuggestions', done => {
|
||||||
|
const getSuggestedAccountsStub =
|
||||||
|
sandbox.stub(restAPI, 'getSuggestedGroups')
|
||||||
|
.returns(Promise.resolve({
|
||||||
|
'Some name': {id: 1},
|
||||||
|
'Other name': {id: 3, url: 'abcd'},
|
||||||
|
}));
|
||||||
|
|
||||||
|
provider.getSuggestions('Some input').then(res => {
|
||||||
|
assert.deepEqual(res, [group1, group2]);
|
||||||
|
assert.isTrue(getSuggestedAccountsStub.calledOnce);
|
||||||
|
assert.equal(getSuggestedAccountsStub.lastCall.args[0], 'Some input');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('makeSuggestionItem', () => {
|
||||||
|
assert.deepEqual(provider.makeSuggestionItem(group1), {
|
||||||
|
name: 'Some name',
|
||||||
|
value: {
|
||||||
|
group: {
|
||||||
|
name: 'Some name',
|
||||||
|
id: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(provider.makeSuggestionItem(group2), {
|
||||||
|
name: 'Other name',
|
||||||
|
value: {
|
||||||
|
group: {
|
||||||
|
name: 'Other name',
|
||||||
|
id: 3,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright (C) 2019 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.
|
||||||
|
*/
|
||||||
|
(function(window) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
if (window.GrReviewerSuggestionsProvider) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
class GrReviewerSuggestionsProvider {
|
||||||
|
constructor(restAPI, changeNumber, allowAnyUser) {
|
||||||
|
this._changeNumber = changeNumber;
|
||||||
|
this._allowAnyUser = allowAnyUser;
|
||||||
|
this._restAPI = restAPI;
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
if (this._initPromise) {
|
||||||
|
return this._initPromise;
|
||||||
|
}
|
||||||
|
const getConfigPromise = this._restAPI.getConfig().then(cfg => {
|
||||||
|
this._config = cfg;
|
||||||
|
});
|
||||||
|
const getLoggedInPromise = this._restAPI.getLoggedIn().then(loggedIn => {
|
||||||
|
this._loggedIn = loggedIn;
|
||||||
|
});
|
||||||
|
this._initPromise = Promise.all([getConfigPromise, getLoggedInPromise])
|
||||||
|
.then(() => {
|
||||||
|
this._initialized = true;
|
||||||
|
});
|
||||||
|
return this._initPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
getSuggestions(input) {
|
||||||
|
if (!this._initialized || !this._loggedIn) {
|
||||||
|
return Promise.resolve([]);
|
||||||
|
}
|
||||||
|
const api = this._restAPI;
|
||||||
|
const xhr = this._allowAnyUser ?
|
||||||
|
api.getSuggestedAccounts(`cansee:${this._changeNumber} ${input}`) :
|
||||||
|
api.getChangeSuggestedReviewers(this._changeNumber, input);
|
||||||
|
|
||||||
|
return xhr.then(reviewers => (reviewers || []));
|
||||||
|
}
|
||||||
|
|
||||||
|
makeSuggestionItem(suggestion) {
|
||||||
|
if (suggestion.account) {
|
||||||
|
// Reviewer is an account suggestion from getChangeSuggestedReviewers.
|
||||||
|
return {
|
||||||
|
name: GrDisplayNameUtils.getAccountDisplayName(this._config,
|
||||||
|
suggestion.account, false),
|
||||||
|
value: suggestion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suggestion.group) {
|
||||||
|
// Reviewer is a group suggestion from getChangeSuggestedReviewers.
|
||||||
|
return {
|
||||||
|
name: GrDisplayNameUtils.getGroupDisplayName(suggestion.group),
|
||||||
|
value: suggestion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (suggestion._account_id) {
|
||||||
|
// Reviewer is an account suggestion from getSuggestedAccounts.
|
||||||
|
return {
|
||||||
|
name: GrDisplayNameUtils.getAccountDisplayName(this._config,
|
||||||
|
suggestion, false),
|
||||||
|
value: {account: suggestion, count: 1},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.GrReviewerSuggestionsProvider = GrReviewerSuggestionsProvider;
|
||||||
|
})(window);
|
||||||
@@ -0,0 +1,260 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<!--
|
||||||
|
@license
|
||||||
|
Copyright (C) 2019 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>gr-reviewer-suggestions-provider</title>
|
||||||
|
<script src="/test/common-test-setup.js"></script>
|
||||||
|
<script src="/bower_components/webcomponentsjs/custom-elements-es5-adapter.js"></script>
|
||||||
|
|
||||||
|
<script src="/bower_components/webcomponentsjs/webcomponents-lite.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="../../elements/shared/gr-rest-api-interface/gr-rest-api-interface.html"/>
|
||||||
|
<script src="../gr-display-name-utils/gr-display-name-utils.js"></script>
|
||||||
|
<script src="gr-reviewer-suggestions-provider.js"></script>
|
||||||
|
|
||||||
|
<script>void(0);</script>
|
||||||
|
|
||||||
|
<test-fixture id="basic">
|
||||||
|
<template>
|
||||||
|
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||||
|
</template>
|
||||||
|
</test-fixture>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
suite('GrReviewerSuggestionsProvider tests', () => {
|
||||||
|
let sandbox;
|
||||||
|
let _nextAccountId = 0;
|
||||||
|
const makeAccount = function(opt_status) {
|
||||||
|
const accountId = ++_nextAccountId;
|
||||||
|
return {
|
||||||
|
_account_id: accountId,
|
||||||
|
name: 'name ' + accountId,
|
||||||
|
email: 'email ' + accountId,
|
||||||
|
status: opt_status,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
let _nextAccountId2 = 0;
|
||||||
|
const makeAccount2 = function(opt_status) {
|
||||||
|
const accountId2 = ++_nextAccountId2;
|
||||||
|
return {
|
||||||
|
_account_id: accountId2,
|
||||||
|
name: 'name ' + accountId2,
|
||||||
|
status: opt_status,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
let owner;
|
||||||
|
let existingReviewer1;
|
||||||
|
let existingReviewer2;
|
||||||
|
let suggestion1;
|
||||||
|
let suggestion2;
|
||||||
|
let suggestion3;
|
||||||
|
let restAPI;
|
||||||
|
let provider;
|
||||||
|
|
||||||
|
let redundantSuggestion1;
|
||||||
|
let redundantSuggestion2;
|
||||||
|
let redundantSuggestion3;
|
||||||
|
let change;
|
||||||
|
|
||||||
|
setup(done => {
|
||||||
|
owner = makeAccount();
|
||||||
|
existingReviewer1 = makeAccount();
|
||||||
|
existingReviewer2 = makeAccount();
|
||||||
|
suggestion1 = {account: makeAccount()};
|
||||||
|
suggestion2 = {account: makeAccount()};
|
||||||
|
suggestion3 = {
|
||||||
|
group: {
|
||||||
|
id: 'suggested group id',
|
||||||
|
name: 'suggested group',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
stub('gr-rest-api-interface', {
|
||||||
|
getLoggedIn() { return Promise.resolve(true); },
|
||||||
|
getConfig() { return Promise.resolve({}); },
|
||||||
|
});
|
||||||
|
|
||||||
|
restAPI = fixture('basic');
|
||||||
|
change = {
|
||||||
|
_number: 42,
|
||||||
|
owner,
|
||||||
|
reviewers: {
|
||||||
|
CC: [existingReviewer1],
|
||||||
|
REVIEWER: [existingReviewer2],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
sandbox = sinon.sandbox.create();
|
||||||
|
return flush(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
teardown(() => {
|
||||||
|
sandbox.restore();
|
||||||
|
});
|
||||||
|
suite('allowAnyUser set to false', () => {
|
||||||
|
setup(done => {
|
||||||
|
provider = new GrReviewerSuggestionsProvider(restAPI, change._number,
|
||||||
|
false);
|
||||||
|
provider.init().then(done);
|
||||||
|
});
|
||||||
|
suite('stubbed values for _getReviewerSuggestions', () => {
|
||||||
|
setup(() => {
|
||||||
|
stub('gr-rest-api-interface', {
|
||||||
|
getChangeSuggestedReviewers() {
|
||||||
|
redundantSuggestion1 = {account: existingReviewer1};
|
||||||
|
redundantSuggestion2 = {account: existingReviewer2};
|
||||||
|
redundantSuggestion3 = {account: owner};
|
||||||
|
return Promise.resolve([redundantSuggestion1, redundantSuggestion2,
|
||||||
|
redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('makeSuggestionItem formats account or group accordingly', () => {
|
||||||
|
let account = makeAccount();
|
||||||
|
const account3 = makeAccount2();
|
||||||
|
let suggestion = provider.makeSuggestionItem({account});
|
||||||
|
assert.deepEqual(suggestion, {
|
||||||
|
name: account.name + ' <' + account.email + '>',
|
||||||
|
value: {account},
|
||||||
|
});
|
||||||
|
|
||||||
|
const group = {name: 'test'};
|
||||||
|
suggestion = provider.makeSuggestionItem({group});
|
||||||
|
assert.deepEqual(suggestion, {
|
||||||
|
name: group.name + ' (group)',
|
||||||
|
value: {group},
|
||||||
|
});
|
||||||
|
|
||||||
|
suggestion = provider.makeSuggestionItem(account);
|
||||||
|
assert.deepEqual(suggestion, {
|
||||||
|
name: account.name + ' <' + account.email + '>',
|
||||||
|
value: {account, count: 1},
|
||||||
|
});
|
||||||
|
|
||||||
|
suggestion = provider.makeSuggestionItem({account: {}});
|
||||||
|
assert.deepEqual(suggestion, {
|
||||||
|
name: 'Anonymous',
|
||||||
|
value: {account: {}},
|
||||||
|
});
|
||||||
|
|
||||||
|
provider._config = {
|
||||||
|
user: {
|
||||||
|
anonymous_coward_name: 'Anonymous Coward Name',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
suggestion = provider.makeSuggestionItem({account: {}});
|
||||||
|
assert.deepEqual(suggestion, {
|
||||||
|
name: 'Anonymous Coward Name',
|
||||||
|
value: {account: {}},
|
||||||
|
});
|
||||||
|
|
||||||
|
account = makeAccount('OOO');
|
||||||
|
|
||||||
|
suggestion = provider.makeSuggestionItem({account});
|
||||||
|
assert.deepEqual(suggestion, {
|
||||||
|
name: account.name + ' <' + account.email + '> (OOO)',
|
||||||
|
value: {account},
|
||||||
|
});
|
||||||
|
|
||||||
|
suggestion = provider.makeSuggestionItem(account);
|
||||||
|
assert.deepEqual(suggestion, {
|
||||||
|
name: account.name + ' <' + account.email + '> (OOO)',
|
||||||
|
value: {account, count: 1},
|
||||||
|
});
|
||||||
|
|
||||||
|
sandbox.stub(GrDisplayNameUtils, '_accountEmail',
|
||||||
|
() => {
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
suggestion = provider.makeSuggestionItem(account3);
|
||||||
|
assert.deepEqual(suggestion, {
|
||||||
|
name: account3.name,
|
||||||
|
value: {account: account3, count: 1},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getSuggestions', done => {
|
||||||
|
provider.getSuggestions().then(reviewers => {
|
||||||
|
// Default is no filtering.
|
||||||
|
assert.equal(reviewers.length, 6);
|
||||||
|
assert.deepEqual(reviewers,
|
||||||
|
[redundantSuggestion1, redundantSuggestion2,
|
||||||
|
redundantSuggestion3, suggestion1, suggestion2, suggestion3]);
|
||||||
|
}).then(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getSuggestions short circuits when logged out', () => {
|
||||||
|
// API call is already stubbed.
|
||||||
|
const xhrSpy = restAPI.getChangeSuggestedReviewers;
|
||||||
|
provider._loggedIn = false;
|
||||||
|
return provider.getSuggestions('').then(() => {
|
||||||
|
assert.isFalse(xhrSpy.called);
|
||||||
|
provider._loggedIn = true;
|
||||||
|
return provider.getSuggestions('').then(() => {
|
||||||
|
assert.isTrue(xhrSpy.called);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getChangeSuggestedReviewers is used', done => {
|
||||||
|
const suggestReviewerStub =
|
||||||
|
sandbox.stub(restAPI, 'getChangeSuggestedReviewers')
|
||||||
|
.returns(Promise.resolve([]));
|
||||||
|
const suggestAccountStub =
|
||||||
|
sandbox.stub(restAPI, 'getSuggestedAccounts')
|
||||||
|
.returns(Promise.resolve([]));
|
||||||
|
|
||||||
|
provider.getSuggestions('').then(() => {
|
||||||
|
assert.isTrue(suggestReviewerStub.calledOnce);
|
||||||
|
assert.isTrue(suggestReviewerStub.calledWith(42, ''));
|
||||||
|
assert.isFalse(suggestAccountStub.called);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
suite('allowAnyUser set to true', () => {
|
||||||
|
setup(done => {
|
||||||
|
provider = new GrReviewerSuggestionsProvider(restAPI, change._number,
|
||||||
|
true);
|
||||||
|
provider.init().then(done);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('getSuggestedAccounts is used', done => {
|
||||||
|
const suggestReviewerStub =
|
||||||
|
sandbox.stub(restAPI, 'getChangeSuggestedReviewers')
|
||||||
|
.returns(Promise.resolve([]));
|
||||||
|
const suggestAccountStub =
|
||||||
|
sandbox.stub(restAPI, 'getSuggestedAccounts')
|
||||||
|
.returns(Promise.resolve([]));
|
||||||
|
|
||||||
|
provider.getSuggestions('').then(() => {
|
||||||
|
assert.isFalse(suggestReviewerStub.called);
|
||||||
|
assert.isTrue(suggestAccountStub.calledOnce);
|
||||||
|
assert.isTrue(suggestAccountStub.calledWith('cansee:42 '));
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
@@ -38,6 +38,8 @@ const EXTERN_NAMES = [
|
|||||||
'SiteBasedCache',
|
'SiteBasedCache',
|
||||||
'FetchPromisesCache',
|
'FetchPromisesCache',
|
||||||
'GrRestApiHelper',
|
'GrRestApiHelper',
|
||||||
|
'GrDisplayNameUtils',
|
||||||
|
'GrReviewerSuggestionsProvider',
|
||||||
'moment',
|
'moment',
|
||||||
'page',
|
'page',
|
||||||
'util',
|
'util',
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ limitations under the License.
|
|||||||
<script src="/bower_components/web-component-tester/browser.js"></script>
|
<script src="/bower_components/web-component-tester/browser.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const testFiles = [];
|
const testFiles = [];
|
||||||
|
const scriptsPath = '../scripts/';
|
||||||
const elementsPath = '../elements/';
|
const elementsPath = '../elements/';
|
||||||
const behaviorsPath = '../behaviors/';
|
const behaviorsPath = '../behaviors/';
|
||||||
|
|
||||||
@@ -61,9 +62,9 @@ limitations under the License.
|
|||||||
'change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html',
|
'change-list/gr-create-commands-dialog/gr-create-commands-dialog_test.html',
|
||||||
'change-list/gr-create-change-help/gr-create-change-help_test.html',
|
'change-list/gr-create-change-help/gr-create-change-help_test.html',
|
||||||
'change-list/gr-dashboard-view/gr-dashboard-view_test.html',
|
'change-list/gr-dashboard-view/gr-dashboard-view_test.html',
|
||||||
|
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
|
||||||
|
// 'change-list/gr-repo-header/gr-repo-header_test.html',
|
||||||
'change-list/gr-user-header/gr-user-header_test.html',
|
'change-list/gr-user-header/gr-user-header_test.html',
|
||||||
'change/gr-account-entry/gr-account-entry_test.html',
|
|
||||||
'change/gr-account-list/gr-account-list_test.html',
|
|
||||||
'change/gr-change-actions/gr-change-actions_test.html',
|
'change/gr-change-actions/gr-change-actions_test.html',
|
||||||
'change/gr-change-metadata/gr-change-metadata-it_test.html',
|
'change/gr-change-metadata/gr-change-metadata-it_test.html',
|
||||||
'change/gr-change-metadata/gr-change-metadata_test.html',
|
'change/gr-change-metadata/gr-change-metadata_test.html',
|
||||||
@@ -105,10 +106,13 @@ limitations under the License.
|
|||||||
'core/gr-search-bar/gr-search-bar_test.html',
|
'core/gr-search-bar/gr-search-bar_test.html',
|
||||||
'core/gr-smart-search/gr-smart-search_test.html',
|
'core/gr-smart-search/gr-smart-search_test.html',
|
||||||
'diff/gr-comment-api/gr-comment-api_test.html',
|
'diff/gr-comment-api/gr-comment-api_test.html',
|
||||||
|
'diff/gr-coverage-layer/gr-coverage-layer_test.html',
|
||||||
'diff/gr-diff-builder/gr-diff-builder_test.html',
|
'diff/gr-diff-builder/gr-diff-builder_test.html',
|
||||||
'diff/gr-diff-cursor/gr-diff-cursor_test.html',
|
'diff/gr-diff-cursor/gr-diff-cursor_test.html',
|
||||||
'diff/gr-diff-highlight/gr-annotation_test.html',
|
'diff/gr-diff-highlight/gr-annotation_test.html',
|
||||||
'diff/gr-diff-highlight/gr-diff-highlight_test.html',
|
'diff/gr-diff-highlight/gr-diff-highlight_test.html',
|
||||||
|
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
|
||||||
|
// 'diff/gr-diff-host/gr-diff-host_test.html',
|
||||||
'diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html',
|
'diff/gr-diff-mode-selector/gr-diff-mode-selector_test.html',
|
||||||
'diff/gr-diff-processor/gr-diff-processor_test.html',
|
'diff/gr-diff-processor/gr-diff-processor_test.html',
|
||||||
'diff/gr-diff-selection/gr-diff-selection_test.html',
|
'diff/gr-diff-selection/gr-diff-selection_test.html',
|
||||||
@@ -127,6 +131,8 @@ limitations under the License.
|
|||||||
'plugins/gr-admin-api/gr-admin-api_test.html',
|
'plugins/gr-admin-api/gr-admin-api_test.html',
|
||||||
'plugins/gr-styles-api/gr-styles-api_test.html',
|
'plugins/gr-styles-api/gr-styles-api_test.html',
|
||||||
'plugins/gr-attribute-helper/gr-attribute-helper_test.html',
|
'plugins/gr-attribute-helper/gr-attribute-helper_test.html',
|
||||||
|
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
|
||||||
|
// 'plugins/gr-dom-hooks/gr-dom-hooks_test.html',
|
||||||
'plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html',
|
'plugins/gr-endpoint-decorator/gr-endpoint-decorator_test.html',
|
||||||
'plugins/gr-event-helper/gr-event-helper_test.html',
|
'plugins/gr-event-helper/gr-event-helper_test.html',
|
||||||
'plugins/gr-external-style/gr-external-style_test.html',
|
'plugins/gr-external-style/gr-external-style_test.html',
|
||||||
@@ -135,7 +141,10 @@ limitations under the License.
|
|||||||
'plugins/gr-popup-interface/gr-popup-interface_test.html',
|
'plugins/gr-popup-interface/gr-popup-interface_test.html',
|
||||||
'plugins/gr-repo-api/gr-repo-api_test.html',
|
'plugins/gr-repo-api/gr-repo-api_test.html',
|
||||||
'plugins/gr-settings-api/gr-settings-api_test.html',
|
'plugins/gr-settings-api/gr-settings-api_test.html',
|
||||||
|
'plugins/gr-theme-api/gr-theme-api_test.html',
|
||||||
'settings/gr-account-info/gr-account-info_test.html',
|
'settings/gr-account-info/gr-account-info_test.html',
|
||||||
|
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
|
||||||
|
// 'settings/gr-agreements-list/gr-agreements-list_test.html',
|
||||||
'settings/gr-change-table-editor/gr-change-table-editor_test.html',
|
'settings/gr-change-table-editor/gr-change-table-editor_test.html',
|
||||||
'settings/gr-cla-view/gr-cla-view_test.html',
|
'settings/gr-cla-view/gr-cla-view_test.html',
|
||||||
'settings/gr-edit-preferences/gr-edit-preferences_test.html',
|
'settings/gr-edit-preferences/gr-edit-preferences_test.html',
|
||||||
@@ -149,7 +158,9 @@ limitations under the License.
|
|||||||
'settings/gr-settings-view/gr-settings-view_test.html',
|
'settings/gr-settings-view/gr-settings-view_test.html',
|
||||||
'settings/gr-ssh-editor/gr-ssh-editor_test.html',
|
'settings/gr-ssh-editor/gr-ssh-editor_test.html',
|
||||||
'settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html',
|
'settings/gr-watched-projects-editor/gr-watched-projects-editor_test.html',
|
||||||
|
'shared/gr-account-entry/gr-account-entry_test.html',
|
||||||
'shared/gr-account-label/gr-account-label_test.html',
|
'shared/gr-account-label/gr-account-label_test.html',
|
||||||
|
'shared/gr-account-list/gr-account-list_test.html',
|
||||||
'shared/gr-account-link/gr-account-link_test.html',
|
'shared/gr-account-link/gr-account-link_test.html',
|
||||||
'shared/gr-alert/gr-alert_test.html',
|
'shared/gr-alert/gr-alert_test.html',
|
||||||
'shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html',
|
'shared/gr-autocomplete-dropdown/gr-autocomplete-dropdown_test.html',
|
||||||
@@ -161,34 +172,48 @@ limitations under the License.
|
|||||||
'shared/gr-comment-thread/gr-comment-thread_test.html',
|
'shared/gr-comment-thread/gr-comment-thread_test.html',
|
||||||
'shared/gr-comment/gr-comment_test.html',
|
'shared/gr-comment/gr-comment_test.html',
|
||||||
'shared/gr-copy-clipboard/gr-copy-clipboard_test.html',
|
'shared/gr-copy-clipboard/gr-copy-clipboard_test.html',
|
||||||
|
'shared/gr-count-string-formatter/gr-count-string-formatter_test.html',
|
||||||
'shared/gr-cursor-manager/gr-cursor-manager_test.html',
|
'shared/gr-cursor-manager/gr-cursor-manager_test.html',
|
||||||
'shared/gr-date-formatter/gr-date-formatter_test.html',
|
'shared/gr-date-formatter/gr-date-formatter_test.html',
|
||||||
'shared/gr-dialog/gr-dialog_test.html',
|
'shared/gr-dialog/gr-dialog_test.html',
|
||||||
'shared/gr-diff-preferences/gr-diff-preferences_test.html',
|
'shared/gr-diff-preferences/gr-diff-preferences_test.html',
|
||||||
'shared/gr-download-commands/gr-download-commands_test.html',
|
'shared/gr-download-commands/gr-download-commands_test.html',
|
||||||
|
'shared/gr-dropdown/gr-dropdown_test.html',
|
||||||
'shared/gr-dropdown-list/gr-dropdown-list_test.html',
|
'shared/gr-dropdown-list/gr-dropdown-list_test.html',
|
||||||
'shared/gr-editable-content/gr-editable-content_test.html',
|
'shared/gr-editable-content/gr-editable-content_test.html',
|
||||||
'shared/gr-editable-label/gr-editable-label_test.html',
|
'shared/gr-editable-label/gr-editable-label_test.html',
|
||||||
'shared/gr-formatted-text/gr-formatted-text_test.html',
|
'shared/gr-formatted-text/gr-formatted-text_test.html',
|
||||||
|
'shared/gr-hovercard/gr-hovercard_test.html',
|
||||||
|
'shared/gr-js-api-interface/gr-annotation-actions-context_test.html',
|
||||||
|
'shared/gr-js-api-interface/gr-annotation-actions-js-api_test.html',
|
||||||
'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',
|
'shared/gr-js-api-interface/gr-change-actions-js-api_test.html',
|
||||||
'shared/gr-js-api-interface/gr-change-reply-js-api_test.html',
|
'shared/gr-js-api-interface/gr-change-reply-js-api_test.html',
|
||||||
'shared/gr-js-api-interface/gr-js-api-interface_test.html',
|
'shared/gr-js-api-interface/gr-js-api-interface_test.html',
|
||||||
|
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
|
||||||
|
// 'shared/gr-js-api-interface/gr-plugin-action-context_test.html',
|
||||||
'shared/gr-js-api-interface/gr-plugin-endpoints_test.html',
|
'shared/gr-js-api-interface/gr-plugin-endpoints_test.html',
|
||||||
'shared/gr-js-api-interface/gr-plugin-rest-api_test.html',
|
'shared/gr-js-api-interface/gr-plugin-rest-api_test.html',
|
||||||
'shared/gr-fixed-panel/gr-fixed-panel_test.html',
|
'shared/gr-fixed-panel/gr-fixed-panel_test.html',
|
||||||
'shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html',
|
'shared/gr-labeled-autocomplete/gr-labeled-autocomplete_test.html',
|
||||||
|
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
|
||||||
|
// 'shared/gr-label-info/gr-label-info_test.html',
|
||||||
'shared/gr-lib-loader/gr-lib-loader_test.html',
|
'shared/gr-lib-loader/gr-lib-loader_test.html',
|
||||||
'shared/gr-limited-text/gr-limited-text_test.html',
|
'shared/gr-limited-text/gr-limited-text_test.html',
|
||||||
'shared/gr-linked-chip/gr-linked-chip_test.html',
|
'shared/gr-linked-chip/gr-linked-chip_test.html',
|
||||||
'shared/gr-linked-text/gr-linked-text_test.html',
|
'shared/gr-linked-text/gr-linked-text_test.html',
|
||||||
'shared/gr-list-view/gr-list-view_test.html',
|
'shared/gr-list-view/gr-list-view_test.html',
|
||||||
|
'shared/gr-overlay/gr-overlay_test.html',
|
||||||
'shared/gr-page-nav/gr-page-nav_test.html',
|
'shared/gr-page-nav/gr-page-nav_test.html',
|
||||||
'shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html',
|
'shared/gr-repo-branch-picker/gr-repo-branch-picker_test.html',
|
||||||
'shared/gr-rest-api-interface/gr-auth_test.html',
|
'shared/gr-rest-api-interface/gr-auth_test.html',
|
||||||
|
'shared/gr-rest-api-interface/gr-etag-decorator_test.html',
|
||||||
'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
|
'shared/gr-rest-api-interface/gr-rest-api-interface_test.html',
|
||||||
'shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html',
|
'shared/gr-rest-api-interface/gr-reviewer-updates-parser_test.html',
|
||||||
'shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html',
|
'shared/gr-rest-api-interface/gr-rest-apis/gr-rest-api-helper_test.html',
|
||||||
|
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
|
||||||
|
// 'shared/gr-rest-api-interface/mock-diff-response_test.html',
|
||||||
'shared/gr-select/gr-select_test.html',
|
'shared/gr-select/gr-select_test.html',
|
||||||
|
'shared/gr-shell-command/gr-shell-command_test.html',
|
||||||
'shared/gr-storage/gr-storage_test.html',
|
'shared/gr-storage/gr-storage_test.html',
|
||||||
'shared/gr-textarea/gr-textarea_test.html',
|
'shared/gr-textarea/gr-textarea_test.html',
|
||||||
'shared/gr-tooltip-content/gr-tooltip-content_test.html',
|
'shared/gr-tooltip-content/gr-tooltip-content_test.html',
|
||||||
@@ -212,8 +237,10 @@ limitations under the License.
|
|||||||
'rest-client-behavior/rest-client-behavior_test.html',
|
'rest-client-behavior/rest-client-behavior_test.html',
|
||||||
'gr-access-behavior/gr-access-behavior_test.html',
|
'gr-access-behavior/gr-access-behavior_test.html',
|
||||||
'gr-admin-nav-behavior/gr-admin-nav-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-change-table-behavior/gr-change-table-behavior_test.html',
|
||||||
|
// TODO: uncomment file & fix tests. The file was missed in this list for a long time.
|
||||||
|
// 'gr-list-view-behavior/gr-list-view-behavior_test.html',
|
||||||
|
'gr-display-name-behavior/gr-display-name-behavior_test.html',
|
||||||
'gr-patch-set-behavior/gr-patch-set-behavior_test.html',
|
'gr-patch-set-behavior/gr-patch-set-behavior_test.html',
|
||||||
'gr-path-list-behavior/gr-path-list-behavior_test.html',
|
'gr-path-list-behavior/gr-path-list-behavior_test.html',
|
||||||
'gr-tooltip-behavior/gr-tooltip-behavior_test.html',
|
'gr-tooltip-behavior/gr-tooltip-behavior_test.html',
|
||||||
@@ -227,5 +254,17 @@ limitations under the License.
|
|||||||
testFiles.push(file);
|
testFiles.push(file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const scripts = [
|
||||||
|
'gr-reviewer-suggestions-provider/gr-reviewer-suggestions-provider_test.html',
|
||||||
|
'gr-group-suggestions-provider/gr-group-suggestions-provider_test.html',
|
||||||
|
'gr-display-name-utils/gr-display-name-utils_test.html',
|
||||||
|
'gr-email-suggestions-provider/gr-email-suggestions-provider_test.html',
|
||||||
|
];
|
||||||
|
/* eslint-enable max-len */
|
||||||
|
for (let file of scripts) {
|
||||||
|
file = scriptsPath + file;
|
||||||
|
testFiles.push(file);
|
||||||
|
}
|
||||||
|
|
||||||
WCT.loadSuites(testFiles);
|
WCT.loadSuites(testFiles);
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user