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.
|
||||
-->
|
||||
|
||||
<script src="../../scripts/gr-display-name-utils/gr-display-name-utils.js"></script>
|
||||
|
||||
<script>
|
||||
(function(window) {
|
||||
'use strict';
|
||||
|
||||
const ANONYMOUS_NAME = 'Anonymous';
|
||||
|
||||
window.Gerrit = window.Gerrit || {};
|
||||
|
||||
/** @polymerBehavior Gerrit.AnonymousNameBehavior */
|
||||
Gerrit.AnonymousNameBehavior = {
|
||||
/** @polymerBehavior Gerrit.DisplayNameBehavior */
|
||||
Gerrit.DisplayNameBehavior = {
|
||||
// TODO(dmfilippov) replace DisplayNameBehavior with GrDisplayNameUtils
|
||||
|
||||
/**
|
||||
* enableEmail when true enables to fallback to using email if
|
||||
* the account name is not avilable.
|
||||
*/
|
||||
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 GrDisplayNameUtils.getUserName(config, account, enableEmail);
|
||||
},
|
||||
|
||||
return ANONYMOUS_NAME;
|
||||
getGroupDisplayName(group) {
|
||||
return GrDisplayNameUtils.getGroupDisplayName(group);
|
||||
},
|
||||
};
|
||||
})(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">
|
||||
<title>gr-anonymous-name-behavior</title>
|
||||
<title>gr-display-name-behavior</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="gr-anonymous-name-behavior.html">
|
||||
<link rel="import" href="gr-display-name-behavior.html">
|
||||
|
||||
<test-fixture id="basic">
|
||||
<template>
|
||||
@@ -33,7 +33,7 @@ limitations under the License.
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
suite('gr-anonymous-name-behavior tests', () => {
|
||||
suite('gr-display-name-behavior tests', () => {
|
||||
let element;
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const config = {
|
||||
@@ -48,7 +48,7 @@ limitations under the License.
|
||||
is: 'test-element-anon',
|
||||
_legacyUndefinedCheck: true,
|
||||
behaviors: [
|
||||
Gerrit.AnonymousNameBehavior,
|
||||
Gerrit.DisplayNameBehavior,
|
||||
],
|
||||
});
|
||||
});
|
||||
@@ -57,21 +57,21 @@ limitations under the License.
|
||||
element = fixture('basic');
|
||||
});
|
||||
|
||||
test('test for it to return name', () => {
|
||||
test('getUserName name only', () => {
|
||||
const account = {
|
||||
name: 'test-name',
|
||||
};
|
||||
assert.deepEqual(element.getUserName(config, account, true), 'test-name');
|
||||
});
|
||||
|
||||
test('test for it to return username', () => {
|
||||
test('getUserName username only', () => {
|
||||
const account = {
|
||||
username: 'test-user',
|
||||
};
|
||||
assert.deepEqual(element.getUserName(config, account, true), 'test-user');
|
||||
});
|
||||
|
||||
test('test for it to return email', () => {
|
||||
test('getUserName email only', () => {
|
||||
const account = {
|
||||
email: 'test-user@test-url.com',
|
||||
};
|
||||
@@ -79,11 +79,11 @@ limitations under the License.
|
||||
'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');
|
||||
});
|
||||
|
||||
test('test for the config returning the anon name', () => {
|
||||
test('getUserName for the config returning the anon name', () => {
|
||||
const config = {
|
||||
user: {
|
||||
anonymous_coward_name: 'Test Anon',
|
||||
@@ -91,5 +91,10 @@ limitations under the License.
|
||||
};
|
||||
assert.deepEqual(element.getUserName(config, null, true), 'Test Anon');
|
||||
});
|
||||
|
||||
test('getGroupDisplayName', () => {
|
||||
assert.equal(element.getGroupDisplayName({name: 'Some user name'}),
|
||||
'Some user name (group)');
|
||||
});
|
||||
});
|
||||
</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-commit-info/gr-commit-info.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">
|
||||
<template>
|
||||
@@ -172,9 +174,9 @@ limitations under the License.
|
||||
id="assigneeValue"
|
||||
placeholder="Set assignee..."
|
||||
accounts="{{_assignee}}"
|
||||
change="[[change]]"
|
||||
readonly="[[_computeAssigneeReadOnly(_mutable, change)]]"
|
||||
allow-any-user></gr-account-list>
|
||||
suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
|
||||
</gr-account-list>
|
||||
</span>
|
||||
</section>
|
||||
<section>
|
||||
|
||||
@@ -471,5 +471,12 @@
|
||||
// dom-if.
|
||||
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-rest-api-interface/gr-rest-api-interface.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="../../../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">
|
||||
<template>
|
||||
@@ -165,11 +167,11 @@ limitations under the License.
|
||||
id="reviewers"
|
||||
accounts="{{_reviewers}}"
|
||||
removable-values="[[change.removable_reviewers]]"
|
||||
change="[[change]]"
|
||||
filter="[[filterReviewerSuggestion]]"
|
||||
pending-confirmation="{{_reviewerPendingConfirmation}}"
|
||||
placeholder="Add reviewer..."
|
||||
on-account-text-changed="_handleAccountTextEntry">
|
||||
on-account-text-changed="_handleAccountTextEntry"
|
||||
suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
|
||||
</gr-account-list>
|
||||
</div>
|
||||
<div class="peopleList">
|
||||
@@ -177,12 +179,12 @@ limitations under the License.
|
||||
<gr-account-list
|
||||
id="ccs"
|
||||
accounts="{{_ccs}}"
|
||||
change="[[change]]"
|
||||
filter="[[filterCCSuggestion]]"
|
||||
pending-confirmation="{{_ccPendingConfirmation}}"
|
||||
allow-any-input
|
||||
placeholder="Add CC..."
|
||||
on-account-text-changed="_handleAccountTextEntry">
|
||||
on-account-text-changed="_handleAccountTextEntry"
|
||||
suggestions-provider="[[_getReviewerSuggestionsProvider(change)]]">
|
||||
</gr-account-list>
|
||||
</div>
|
||||
<gr-overlay
|
||||
|
||||
@@ -895,5 +895,12 @@
|
||||
_sendDisabledChanged(sendDisabled) {
|
||||
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.
|
||||
-->
|
||||
|
||||
<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="../../shared/gr-button/gr-button.html">
|
||||
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
|
||||
|
||||
@@ -58,7 +58,7 @@
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.AnonymousNameBehavior,
|
||||
Gerrit.DisplayNameBehavior,
|
||||
],
|
||||
|
||||
detached() {
|
||||
|
||||
@@ -16,7 +16,7 @@ limitations under the License.
|
||||
-->
|
||||
<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="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-search-bar/gr-search-bar.html">
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.AnonymousNameBehavior,
|
||||
Gerrit.DisplayNameBehavior,
|
||||
],
|
||||
|
||||
attached() {
|
||||
|
||||
@@ -15,12 +15,11 @@ See the License for the specific language governing permissions and
|
||||
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="../../../behaviors/fire-behavior/fire-behavior.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
<link rel="import" href="../../shared/gr-autocomplete/gr-autocomplete.html">
|
||||
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
<link rel="import" href="../gr-autocomplete/gr-autocomplete.html">
|
||||
<link rel="import" href="../gr-rest-api-interface/gr-rest-api-interface.html">
|
||||
|
||||
<dom-module id="gr-account-entry">
|
||||
<template>
|
||||
@@ -36,14 +35,13 @@ limitations under the License.
|
||||
borderless="[[borderless]]"
|
||||
placeholder="[[placeholder]]"
|
||||
threshold="[[suggestFrom]]"
|
||||
query="[[query]]"
|
||||
query="[[querySuggestions]]"
|
||||
allow-non-suggested-values="[[allowAnyInput]]"
|
||||
on-commit="_handleInputCommit"
|
||||
clear-on-commit
|
||||
warn-uncommitted
|
||||
text="{{_inputText}}">
|
||||
</gr-autocomplete>
|
||||
<gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
|
||||
</template>
|
||||
<script src="gr-account-entry.js"></script>
|
||||
</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.
|
||||
-->
|
||||
|
||||
<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="/bower_components/polymer/polymer.html">
|
||||
<link rel="import" href="../../../styles/shared-styles.html">
|
||||
|
||||
@@ -52,7 +52,7 @@
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
Gerrit.AnonymousNameBehavior,
|
||||
Gerrit.DisplayNameBehavior,
|
||||
Gerrit.TooltipBehavior,
|
||||
],
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
|
||||
<link rel="import" href="/bower_components/polymer/polymer.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="../../../styles/shared-styles.html">
|
||||
|
||||
@@ -56,7 +56,7 @@ limitations under the License.
|
||||
account="[[account]]"
|
||||
class$="[[_computeChipClass(account)]]"
|
||||
data-account-id$="[[account._account_id]]"
|
||||
removable="[[_computeRemovable(account)]]"
|
||||
removable="[[_computeRemovable(account, readonly)]]"
|
||||
on-keydown="_handleChipKeydown"
|
||||
tabindex="-1">
|
||||
</gr-account-chip>
|
||||
@@ -67,13 +67,13 @@ limitations under the License.
|
||||
hidden$="[[_computeEntryHidden(maxCount, accounts.*, readonly)]]"
|
||||
id="entry"
|
||||
change="[[change]]"
|
||||
filter="[[filter]]"
|
||||
placeholder="[[placeholder]]"
|
||||
on-add="_handleAdd"
|
||||
on-input-keydown="_handleInputKeydown"
|
||||
allow-any-input="[[allowAnyInput]]"
|
||||
allow-any-user="[[allowAnyUser]]">
|
||||
query-suggestions="[[_querySuggestions]]">
|
||||
</gr-account-entry>
|
||||
<slot></slot>
|
||||
</template>
|
||||
<script src="gr-account-list.js"></script>
|
||||
</dom-module>
|
||||
@@ -19,6 +19,24 @@
|
||||
|
||||
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({
|
||||
is: 'gr-account-list',
|
||||
_legacyUndefinedCheck: true,
|
||||
@@ -38,6 +56,19 @@
|
||||
change: Object,
|
||||
filter: Function,
|
||||
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.
|
||||
* @type {?Object} */
|
||||
@@ -50,21 +81,6 @@
|
||||
type: Boolean,
|
||||
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.
|
||||
*/
|
||||
@@ -82,6 +98,16 @@
|
||||
type: Number,
|
||||
value: 0,
|
||||
},
|
||||
|
||||
/** Returns suggestion items
|
||||
* @type {!function(string): Promise<Array<Defs.GrSuggestionItem>>}
|
||||
*/
|
||||
_querySuggestions: {
|
||||
type: Function,
|
||||
value() {
|
||||
return this._getSuggestions.bind(this);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
behaviors: [
|
||||
@@ -103,31 +129,46 @@
|
||||
return this.$.entry.focusStart;
|
||||
},
|
||||
|
||||
_handleAdd(e) {
|
||||
this._addReviewer(e.detail.value);
|
||||
_getSuggestions(input) {
|
||||
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
|
||||
// internal properties to the account/group here, so we clone the object
|
||||
// to avoid cluttering up the shared change object.
|
||||
if (reviewer.account) {
|
||||
if (item.account) {
|
||||
const account =
|
||||
Object.assign({}, reviewer.account, {_pendingAdd: true});
|
||||
Object.assign({}, item.account, {_pendingAdd: true});
|
||||
this.push('accounts', account);
|
||||
} else if (reviewer.group) {
|
||||
if (reviewer.confirm) {
|
||||
this.pendingConfirmation = reviewer;
|
||||
} else if (item.group) {
|
||||
if (item.confirm) {
|
||||
this.pendingConfirmation = item;
|
||||
return;
|
||||
}
|
||||
const group = Object.assign({}, reviewer.group,
|
||||
const group = Object.assign({}, item.group,
|
||||
{_pendingAdd: true, _group: true});
|
||||
this.push('accounts', group);
|
||||
} else if (this.allowAnyInput) {
|
||||
if (!reviewer.includes('@')) {
|
||||
if (!item.includes('@')) {
|
||||
// Repopulate the input with what the user tried to enter and have
|
||||
// a toast tell them why they can't enter it.
|
||||
this.$.entry.setText(reviewer);
|
||||
this.$.entry.setText(item);
|
||||
this.dispatchEvent(new CustomEvent('show-alert', {
|
||||
detail: {message: VALID_EMAIL_ALERT},
|
||||
bubbles: true,
|
||||
@@ -135,7 +176,7 @@
|
||||
}));
|
||||
return false;
|
||||
} else {
|
||||
const account = {email: reviewer, _pendingAdd: true};
|
||||
const account = {email: item, _pendingAdd: true};
|
||||
this.push('accounts', account);
|
||||
}
|
||||
}
|
||||
@@ -173,8 +214,8 @@
|
||||
return a === b;
|
||||
},
|
||||
|
||||
_computeRemovable(account) {
|
||||
if (this.readonly) { return false; }
|
||||
_computeRemovable(account, readonly) {
|
||||
if (readonly) { return false; }
|
||||
if (this.removableValues) {
|
||||
for (let i = 0; i < this.removableValues.length; i++) {
|
||||
if (this._accountMatches(this.removableValues[i], account)) {
|
||||
@@ -193,7 +234,9 @@
|
||||
},
|
||||
|
||||
_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++) {
|
||||
let matches;
|
||||
const account = this.accounts[i];
|
||||
@@ -277,7 +320,7 @@
|
||||
submitEntryText() {
|
||||
const text = this.$.entry.getText();
|
||||
if (!text.length) { return true; }
|
||||
const wasSubmitted = this._addReviewer(text);
|
||||
const wasSubmitted = this._addAccountItem(text);
|
||||
if (wasSubmitted) { this.$.entry.clear(); }
|
||||
return wasSubmitted;
|
||||
},
|
||||
@@ -35,6 +35,15 @@ limitations under the License.
|
||||
</test-fixture>
|
||||
|
||||
<script>
|
||||
class MockSuggestionsProvider {
|
||||
getSuggestions(input) {
|
||||
return Promise.resolve([]);
|
||||
}
|
||||
|
||||
makeSuggestionItem(item) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
suite('gr-account-list tests', () => {
|
||||
let _nextAccountId = 0;
|
||||
const makeAccount = function() {
|
||||
@@ -51,10 +60,11 @@ limitations under the License.
|
||||
};
|
||||
};
|
||||
|
||||
let existingReviewer1;
|
||||
let existingReviewer2;
|
||||
let existingAccount1;
|
||||
let existingAccount2;
|
||||
let sandbox;
|
||||
let element;
|
||||
let suggestionsProvider;
|
||||
|
||||
function getChips() {
|
||||
return Polymer.dom(element.root).querySelectorAll('gr-account-chip');
|
||||
@@ -62,14 +72,16 @@ limitations under the License.
|
||||
|
||||
setup(() => {
|
||||
sandbox = sinon.sandbox.create();
|
||||
existingReviewer1 = makeAccount();
|
||||
existingReviewer2 = makeAccount();
|
||||
existingAccount1 = makeAccount();
|
||||
existingAccount2 = makeAccount();
|
||||
|
||||
stub('gr-rest-api-interface', {
|
||||
getConfig() { return Promise.resolve({}); },
|
||||
});
|
||||
element = fixture('basic');
|
||||
element.accounts = [existingReviewer1, existingReviewer2];
|
||||
element.accounts = [existingAccount1, existingAccount2];
|
||||
suggestionsProvider = new MockSuggestionsProvider();
|
||||
element.suggestionsProvider = suggestionsProvider;
|
||||
});
|
||||
|
||||
teardown(() => {
|
||||
@@ -109,7 +121,7 @@ limitations under the License.
|
||||
assert.isTrue(chips[2].classList.contains('pendingAdd'));
|
||||
|
||||
// Removed accounts are taken out of the list.
|
||||
element.fire('remove', {account: existingReviewer1});
|
||||
element.fire('remove', {account: existingAccount1});
|
||||
flushAsynchronousOperations();
|
||||
chips = getChips();
|
||||
assert.equal(chips.length, 2);
|
||||
@@ -117,7 +129,7 @@ limitations under the License.
|
||||
assert.isTrue(chips[1].classList.contains('pendingAdd'));
|
||||
|
||||
// Invalid remove is ignored.
|
||||
element.fire('remove', {account: existingReviewer1});
|
||||
element.fire('remove', {account: existingAccount1});
|
||||
element.fire('remove', {account: newAccount});
|
||||
flushAsynchronousOperations();
|
||||
chips = getChips();
|
||||
@@ -147,6 +159,52 @@ limitations under the License.
|
||||
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', () => {
|
||||
const account = makeAccount();
|
||||
assert.equal(element._computeChipClass(account), '');
|
||||
@@ -163,18 +221,18 @@ limitations under the License.
|
||||
newAccount._pendingAdd = true;
|
||||
element.readonly = false;
|
||||
element.removableValues = [];
|
||||
assert.isFalse(element._computeRemovable(existingReviewer1));
|
||||
assert.isTrue(element._computeRemovable(newAccount));
|
||||
assert.isFalse(element._computeRemovable(existingAccount1, false));
|
||||
assert.isTrue(element._computeRemovable(newAccount, false));
|
||||
|
||||
|
||||
element.removableValues = [existingReviewer1];
|
||||
assert.isTrue(element._computeRemovable(existingReviewer1));
|
||||
assert.isTrue(element._computeRemovable(newAccount));
|
||||
assert.isFalse(element._computeRemovable(existingReviewer2));
|
||||
element.removableValues = [existingAccount1];
|
||||
assert.isTrue(element._computeRemovable(existingAccount1, false));
|
||||
assert.isTrue(element._computeRemovable(newAccount, false));
|
||||
assert.isFalse(element._computeRemovable(existingAccount2, false));
|
||||
|
||||
element.readonly = true;
|
||||
assert.isFalse(element._computeRemovable(existingReviewer1));
|
||||
assert.isFalse(element._computeRemovable(newAccount));
|
||||
assert.isFalse(element._computeRemovable(existingAccount1, true));
|
||||
assert.isFalse(element._computeRemovable(newAccount, true));
|
||||
});
|
||||
|
||||
test('submitEntryText', () => {
|
||||
@@ -293,13 +351,40 @@ limitations under the License.
|
||||
assert.isTrue(element.$.entry.hasAttribute('hidden'));
|
||||
});
|
||||
|
||||
suite('allowAnyInput', () => {
|
||||
let entry;
|
||||
test('enter text calls suggestions provider', done => {
|
||||
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(() => {
|
||||
entry = element.$.entry;
|
||||
sandbox.stub(entry, '_getReviewerSuggestions');
|
||||
sandbox.stub(entry.$.input, '_updateSuggestions');
|
||||
element.allowAnyInput = true;
|
||||
});
|
||||
|
||||
@@ -334,7 +419,6 @@ limitations under the License.
|
||||
suite('keyboard interactions', () => {
|
||||
test('backspace at text input start removes last account', () => {
|
||||
const input = element.$.entry.$.input;
|
||||
sandbox.stub(element.$.entry, '_getReviewerSuggestions');
|
||||
sandbox.stub(input, '_updateSuggestions');
|
||||
sandbox.stub(element, '_computeRemovable').returns(true);
|
||||
// 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',
|
||||
'FetchPromisesCache',
|
||||
'GrRestApiHelper',
|
||||
'GrDisplayNameUtils',
|
||||
'GrReviewerSuggestionsProvider',
|
||||
'moment',
|
||||
'page',
|
||||
'util',
|
||||
|
||||
@@ -23,6 +23,7 @@ limitations under the License.
|
||||
<script src="/bower_components/web-component-tester/browser.js"></script>
|
||||
<script>
|
||||
const testFiles = [];
|
||||
const scriptsPath = '../scripts/';
|
||||
const elementsPath = '../elements/';
|
||||
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-change-help/gr-create-change-help_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/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-metadata/gr-change-metadata-it_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-smart-search/gr-smart-search_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-cursor/gr-diff-cursor_test.html',
|
||||
'diff/gr-diff-highlight/gr-annotation_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-processor/gr-diff-processor_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-styles-api/gr-styles-api_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-event-helper/gr-event-helper_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-repo-api/gr-repo-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',
|
||||
// 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-cla-view/gr-cla-view_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-ssh-editor/gr-ssh-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-list/gr-account-list_test.html',
|
||||
'shared/gr-account-link/gr-account-link_test.html',
|
||||
'shared/gr-alert/gr-alert_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/gr-comment_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-date-formatter/gr-date-formatter_test.html',
|
||||
'shared/gr-dialog/gr-dialog_test.html',
|
||||
'shared/gr-diff-preferences/gr-diff-preferences_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-editable-content/gr-editable-content_test.html',
|
||||
'shared/gr-editable-label/gr-editable-label_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-reply-js-api_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-rest-api_test.html',
|
||||
'shared/gr-fixed-panel/gr-fixed-panel_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-limited-text/gr-limited-text_test.html',
|
||||
'shared/gr-linked-chip/gr-linked-chip_test.html',
|
||||
'shared/gr-linked-text/gr-linked-text_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-repo-branch-picker/gr-repo-branch-picker_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-reviewer-updates-parser_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-shell-command/gr-shell-command_test.html',
|
||||
'shared/gr-storage/gr-storage_test.html',
|
||||
'shared/gr-textarea/gr-textarea_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',
|
||||
'gr-access-behavior/gr-access-behavior_test.html',
|
||||
'gr-admin-nav-behavior/gr-admin-nav-behavior_test.html',
|
||||
'gr-anonymous-name-behavior/gr-anonymous-name-behavior_test.html',
|
||||
'gr-change-table-behavior/gr-change-table-behavior_test.html',
|
||||
// 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-path-list-behavior/gr-path-list-behavior_test.html',
|
||||
'gr-tooltip-behavior/gr-tooltip-behavior_test.html',
|
||||
@@ -227,5 +254,17 @@ limitations under the License.
|
||||
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);
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user