Add gr-smart-search to handle rest API calls and navigation
This allows gr-search-bar to be more flexible, and can be used with a
separate router and rest API.
Change-Id: Ife602e0caab8cc52e4977b80931bea0b242820a1
diff --git a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
index 4823ef93..7af5fd5 100644
--- a/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
+++ b/polygerrit-ui/app/elements/core/gr-main-header/gr-main-header.html
@@ -22,7 +22,7 @@
<link rel="import" href="../../shared/gr-dropdown/gr-dropdown.html">
<link rel="import" href="../../shared/gr-rest-api-interface/gr-rest-api-interface.html">
<link rel="import" href="../gr-account-dropdown/gr-account-dropdown.html">
-<link rel="import" href="../gr-search-bar/gr-search-bar.html">
+<link rel="import" href="../gr-smart-search/gr-smart-search.html">
<dom-module id="gr-main-header">
<template>
@@ -86,7 +86,7 @@
.rightItems gr-endpoint-decorator:not(:empty) {
margin-left: 1em;
}
- gr-search-bar {
+ gr-smart-search {
flex-grow: 1;
margin-left: .5em;
max-width: 500px;
@@ -129,7 +129,7 @@
font-size: var(--font-size-large);
font-family: var(--font-family-bold);
}
- gr-search-bar,
+ gr-smart-search,
.browse,
.rightItems .hideOnMobile,
.links > li.hideOnMobile {
@@ -171,7 +171,7 @@
</li>
</ul>
<div class="rightItems">
- <gr-search-bar value="{{searchQuery}}" role="search"></gr-search-bar>
+ <gr-smart-search id="search" value="{{searchQuery}}"></gr-smart-search>
<gr-endpoint-decorator
class="hideOnMobile"
name="header-browse-source"></gr-endpoint-decorator>
diff --git a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
index b97c056..8332706 100644
--- a/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
+++ b/polygerrit-ui/app/elements/core/gr-navigation/gr-navigation.html
@@ -238,6 +238,15 @@
},
/**
+ * Navigate to a search query
+ * @param {string} query
+ * @param {number=} opt_offset
+ */
+ navigateToSearchQuery(query, opt_offset) {
+ return this._navigate(this.getUrlForSearchQuery(query, opt_offset));
+ },
+
+ /**
* @param {!Object} change The change object.
* @param {number=} opt_patchNum
* @param {number|string=} opt_basePatchNum The string 'PARENT' can be
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
index fdd730d..b7b2147 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.html
@@ -15,20 +15,15 @@
limitations under the License.
-->
-<link rel="import" href="../../../behaviors/gr-anonymous-name-behavior/gr-anonymous-name-behavior.html">
<link rel="import" href="../../../behaviors/gr-url-encoding-behavior.html">
<link rel="import" href="../../../behaviors/keyboard-shortcut-behavior/keyboard-shortcut-behavior.html">
<link rel="import" href="../../../bower_components/polymer/polymer.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="../../../styles/shared-styles.html">
<dom-module id="gr-search-bar">
<template>
<style include="shared-styles">
- :host {
- display: inline-block;
- }
form {
display: flex;
}
@@ -55,7 +50,6 @@
threshold="[[_threshold]]"
tab-complete
vertical-offset="30"></gr-autocomplete>
- <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
</form>
</template>
<script src="gr-search-bar.js"></script>
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
index c410486..2d0a759 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar.js
@@ -92,9 +92,6 @@
const SEARCH_OPERATORS_WITH_NEGATIONS =
SEARCH_OPERATORS.concat(SEARCH_OPERATORS.map(op => `-${op}`));
- const SELF_EXPRESSION = 'self';
- const ME_EXPRESSION = 'me';
-
const MAX_AUTOCOMPLETE_RESULTS = 10;
const TOKENIZE_REGEX = /(?:[^\s"]+|"[^"]*")+\s*/g;
@@ -102,8 +99,13 @@
Polymer({
is: 'gr-search-bar',
+ /**
+ * Fired when a search is committed
+ *
+ * @event handle-search
+ */
+
behaviors: [
- Gerrit.AnonymousNameBehavior,
Gerrit.KeyboardShortcutBehavior,
Gerrit.URLEncodingBehavior,
],
@@ -129,18 +131,29 @@
return this._getSearchSuggestions.bind(this);
},
},
+ projectSuggestions: {
+ type: Function,
+ value() {
+ return () => Promise.resolve([]);
+ },
+ },
+ groupSuggestions: {
+ type: Function,
+ value() {
+ return () => Promise.resolve([]);
+ },
+ },
+ accountSuggestions: {
+ type: Function,
+ value() {
+ return () => Promise.resolve([]);
+ },
+ },
_inputVal: String,
_threshold: {
type: Number,
value: 1,
},
- _config: Object,
- },
-
- attached() {
- this.$.restAPI.getConfig().then(cfg => {
- this._config = cfg;
- });
},
_valueChanged(value) {
@@ -170,87 +183,12 @@
target.blur();
}
if (this._inputVal) {
- page.show('/q/' + this.encodeURL(this._inputVal, false));
+ this.dispatchEvent(new CustomEvent('handle-search', {
+ detail: {inputVal: this._inputVal},
+ }));
}
},
- _accountOrAnon(name) {
- return this.getUserName(this._config, name, false);
- },
-
- /**
- * Fetch from the API the predicted accounts.
- * @param {string} predicate - The first part of the search term, e.g.
- * 'owner'
- * @param {string} expression - The second part of the search term, e.g.
- * 'kasp'
- * @return {!Promise} This returns a promise that resolves to an array of
- * strings.
- */
- _fetchAccounts(predicate, expression) {
- if (expression.length === 0) { return Promise.resolve([]); }
- return this.$.restAPI.getSuggestedAccounts(
- expression,
- MAX_AUTOCOMPLETE_RESULTS)
- .then(accounts => {
- if (!accounts) { return []; }
- return accounts.map(acct => acct.email ?
- `${predicate}:${acct.email}` :
- `${predicate}:"${this._accountOrAnon(acct)}"`);
- }).then(accounts => {
- // When the expression supplied is a beginning substring of 'self',
- // add it as an autocomplete option.
- if (SELF_EXPRESSION.startsWith(expression)) {
- return accounts.concat([predicate + ':' + SELF_EXPRESSION]);
- } else if (ME_EXPRESSION.startsWith(expression)) {
- return accounts.concat([predicate + ':' + ME_EXPRESSION]);
- } else {
- return accounts;
- }
- });
- },
-
- /**
- * Fetch from the API the predicted groups.
- * @param {string} predicate - The first part of the search term, e.g.
- * 'ownerin'
- * @param {string} expression - The second part of the search term, e.g.
- * 'polyger'
- * @return {!Promise} This returns a promise that resolves to an array of
- * strings.
- */
- _fetchGroups(predicate, expression) {
- if (expression.length === 0) { return Promise.resolve([]); }
- return this.$.restAPI.getSuggestedGroups(
- expression,
- MAX_AUTOCOMPLETE_RESULTS)
- .then(groups => {
- if (!groups) { return []; }
- const keys = Object.keys(groups);
- return keys.map(key => predicate + ':' + key);
- });
- },
-
- /**
- * Fetch from the API the predicted projects.
- * @param {string} predicate - The first part of the search term, e.g.
- * 'project'
- * @param {string} expression - The second part of the search term, e.g.
- * 'gerr'
- * @return {!Promise} This returns a promise that resolves to an array of
- * strings.
- */
- _fetchProjects(predicate, expression) {
- return this.$.restAPI.getSuggestedProjects(
- expression,
- MAX_AUTOCOMPLETE_RESULTS)
- .then(projects => {
- if (!projects) { return []; }
- const keys = Object.keys(projects);
- return keys.map(key => predicate + ':' + key);
- });
- },
-
/**
* Determine what array of possible suggestions should be provided
* to _getSearchSuggestions.
@@ -268,12 +206,12 @@
case 'ownerin':
case 'reviewerin':
// Fetch groups.
- return this._fetchGroups(predicate, expression);
+ return this.groupSuggestions(predicate, expression);
case 'parentproject':
case 'project':
// Fetch projects.
- return this._fetchProjects(predicate, expression);
+ return this.projectSuggestions(predicate, expression);
case 'author':
case 'cc':
@@ -284,7 +222,7 @@
case 'reviewedby':
case 'reviewer':
// Fetch accounts.
- return this._fetchAccounts(predicate, expression);
+ return this.accountSuggestions(predicate, expression);
default:
return Promise.resolve(SEARCH_OPERATORS_WITH_NEGATIONS
diff --git a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
index 60e8a7e..60d6237 100644
--- a/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
+++ b/polygerrit-ui/app/elements/core/gr-search-bar/gr-search-bar_test.html
@@ -60,9 +60,8 @@
document.activeElement;
};
- test('enter in search input triggers nav', done => {
- sandbox.stub(page, 'show', () => {
- page.show.restore();
+ test('enter in search input fires event', done => {
+ element.addEventListener('handle-search', () => {
assert.notEqual(getActiveElement(), element.$.searchInput);
assert.notEqual(getActiveElement(), element.$.searchButton);
done();
@@ -72,16 +71,7 @@
null, 'enter');
});
- test('search query should be double-escaped', () => {
- const showStub = sandbox.stub(page, 'show');
- element.$.searchInput.text = 'fate/stay';
- MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
- null, 'enter');
- assert.equal(showStub.lastCall.args[0], '/q/fate%252Fstay');
- });
-
test('input blurred after commit', () => {
- sandbox.stub(page, 'show');
const blurSpy = sandbox.spy(element.$.searchInput.$.input, 'blur');
element.$.searchInput.text = 'fate/stay';
MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
@@ -90,11 +80,12 @@
});
test('empty search query does not trigger nav', () => {
- const showSpy = sandbox.spy(page, 'show');
+ const searchSpy = sandbox.spy();
+ element.addEventListener('handle-search', searchSpy);
element.value = '';
MockInteractions.pressAndReleaseKeyOn(element.$.searchInput.$.input, 13,
null, 'enter');
- assert.isFalse(showSpy.called);
+ assert.isFalse(searchSpy.called);
});
test('keyboard shortcuts', () => {
@@ -107,64 +98,20 @@
suite('_getSearchSuggestions', () => {
test('Autocompletes accounts', () => {
- sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
- Promise.resolve([
- {
- name: 'fred',
- email: 'fred@goog.co',
- },
- ])
+ sandbox.stub(element, 'accountSuggestions', () =>
+ Promise.resolve(['owner:fred@goog.co'])
);
return element._getSearchSuggestions('owner:fr').then(s => {
assert.equal(s[0].value, 'owner:fred@goog.co');
});
});
- test('Inserts self as option when valid', done => {
- sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
- Promise.resolve([
- {
- name: 'fred',
- email: 'fred@goog.co',
- },
- ])
- );
- element._getSearchSuggestions('owner:s').then(s => {
- assert.equal(s[0].value, 'owner:self');
- }).then(() => {
- element._getSearchSuggestions('owner:selfs').then(s => {
- assert.notEqual(s[0].value, 'owner:self');
- done();
- });
- });
- });
-
- test('Inserts me as option when valid', done => {
- sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
- Promise.resolve([
- {
- name: 'fred',
- email: 'fred@goog.co',
- },
- ])
- );
- element._getSearchSuggestions('owner:m').then(s => {
- assert.equal(s[0].value, 'owner:me');
- }).then(() => {
- element._getSearchSuggestions('owner:meme').then(s => {
- assert.notEqual(s[0].value, 'owner:me');
- done();
- });
- });
- });
-
test('Autocompletes groups', done => {
- sandbox.stub(element.$.restAPI, 'getSuggestedGroups', () =>
- Promise.resolve({
- Polygerrit: 0,
- gerrit: 0,
- gerrittest: 0,
- })
+ sandbox.stub(element, 'groupSuggestions', () =>
+ Promise.resolve([
+ 'ownerin:Polygerrit',
+ 'ownerin:gerrit',
+ ])
);
element._getSearchSuggestions('ownerin:pol').then(s => {
assert.equal(s[0].value, 'ownerin:Polygerrit');
@@ -173,10 +120,12 @@
});
test('Autocompletes projects', done => {
- sandbox.stub(element.$.restAPI, 'getSuggestedProjects', () =>
- Promise.resolve({
- Polygerrit: 0,
- })
+ sandbox.stub(element, 'projectSuggestions', () =>
+ Promise.resolve([
+ 'project:Polygerrit',
+ 'project:gerrit',
+ 'project:gerrittest',
+ ])
);
element._getSearchSuggestions('project:pol').then(s => {
assert.equal(s[0].value, 'project:Polygerrit');
@@ -200,67 +149,6 @@
done();
});
});
-
- test('Autocomplete doesnt override exact matches to input', done => {
- sandbox.stub(element.$.restAPI, 'getSuggestedGroups', () =>
- Promise.resolve({
- Polygerrit: 0,
- gerrit: 0,
- gerrittest: 0,
- })
- );
- element._getSearchSuggestions('ownerin:gerrit').then(s => {
- assert.equal(s[0].value, 'ownerin:gerrit');
- done();
- });
- });
-
- test('Autocomplete respects spaces', done => {
- sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
- Promise.resolve([
- {
- name: 'fred',
- email: 'fred@goog.co',
- },
- ])
- );
- element._getSearchSuggestions('is:ope').then(s => {
- assert.equal(s[0].name, 'is:open');
- assert.equal(s[0].value, 'is:open');
- element._getSearchSuggestions('is:ope ').then(s => {
- assert.equal(s.length, 0);
- done();
- });
- });
- });
-
- test('Autocompletes accounts with no email', done => {
- sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
- Promise.resolve([
- {
- name: 'fred',
- },
- ])
- );
- element._getSearchSuggestions('owner:fr').then(s => {
- assert.equal(s[0].value, 'owner:"fred"');
- done();
- });
- });
-
- test('Autocompletes accounts with email', done => {
- sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
- Promise.resolve([
- {
- email: 'fred@goog.co',
- },
- ])
- );
- element._getSearchSuggestions('owner:fr').then(s => {
- assert.equal(s[0].value, 'owner:fred@goog.co');
- done();
- });
- });
});
});
</script>
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html
new file mode 100644
index 0000000..4c98068
--- /dev/null
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.html
@@ -0,0 +1,38 @@
+<!--
+@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.
+-->
+<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="../../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">
+
+<dom-module id="gr-smart-search">
+ <template>
+ <style include="shared-styles">
+
+ </style>
+ <gr-search-bar id="search"
+ value="{{searchQuery}}"
+ on-handle-search="_handleSearch"
+ project-suggestions="[[_projectSuggestions]]"
+ group-suggestions="[[_groupSuggestions]]"
+ account-suggestions="[[_accountSuggestions]]"></gr-search-bar>
+ <gr-rest-api-interface id="restAPI"></gr-rest-api-interface>
+ </template>
+ <script src="gr-smart-search.js"></script>
+</dom-module>
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
new file mode 100644
index 0000000..5dcd2bb
--- /dev/null
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search.js
@@ -0,0 +1,144 @@
+/**
+ * @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';
+
+ const MAX_AUTOCOMPLETE_RESULTS = 10;
+ const SELF_EXPRESSION = 'self';
+ const ME_EXPRESSION = 'me';
+
+ Polymer({
+ is: 'gr-smart-search',
+
+ properties: {
+ searchQuery: String,
+ _config: Object,
+ _projectSuggestions: {
+ type: Function,
+ value() {
+ return this._fetchProjects.bind(this);
+ },
+ },
+ _groupSuggestions: {
+ type: Function,
+ value() {
+ return this._fetchGroups.bind(this);
+ },
+ },
+ _accountSuggestions: {
+ type: Function,
+ value() {
+ return this._fetchAccounts.bind(this);
+ },
+ },
+ },
+
+ behaviors: [
+ Gerrit.AnonymousNameBehavior,
+ ],
+
+ attached() {
+ this.$.restAPI.getConfig().then(cfg => {
+ this._config = cfg;
+ });
+ },
+
+ _handleSearch(e) {
+ const input = e.detail.inputVal;
+ if (input) {
+ Gerrit.Nav.navigateToSearchQuery(input);
+ }
+ },
+
+ _accountOrAnon(name) {
+ return this.getUserName(this._serverConfig, name, false);
+ },
+
+ /**
+ * Fetch from the API the predicted projects.
+ * @param {string} predicate - The first part of the search term, e.g.
+ * 'project'
+ * @param {string} expression - The second part of the search term, e.g.
+ * 'gerr'
+ * @return {!Promise} This returns a promise that resolves to an array of
+ * strings.
+ */
+ _fetchProjects(predicate, expression) {
+ return this.$.restAPI.getSuggestedProjects(
+ expression,
+ MAX_AUTOCOMPLETE_RESULTS)
+ .then(projects => {
+ if (!projects) { return []; }
+ const keys = Object.keys(projects);
+ return keys.map(key => predicate + ':' + key);
+ });
+ },
+
+ /**
+ * Fetch from the API the predicted groups.
+ * @param {string} predicate - The first part of the search term, e.g.
+ * 'ownerin'
+ * @param {string} expression - The second part of the search term, e.g.
+ * 'polyger'
+ * @return {!Promise} This returns a promise that resolves to an array of
+ * strings.
+ */
+ _fetchGroups(predicate, expression) {
+ if (expression.length === 0) { return Promise.resolve([]); }
+ return this.$.restAPI.getSuggestedGroups(
+ expression,
+ MAX_AUTOCOMPLETE_RESULTS)
+ .then(groups => {
+ if (!groups) { return []; }
+ const keys = Object.keys(groups);
+ return keys.map(key => predicate + ':' + key);
+ });
+ },
+
+ /**
+ * Fetch from the API the predicted accounts.
+ * @param {string} predicate - The first part of the search term, e.g.
+ * 'owner'
+ * @param {string} expression - The second part of the search term, e.g.
+ * 'kasp'
+ * @return {!Promise} This returns a promise that resolves to an array of
+ * strings.
+ */
+ _fetchAccounts(predicate, expression) {
+ if (expression.length === 0) { return Promise.resolve([]); }
+ return this.$.restAPI.getSuggestedAccounts(
+ expression,
+ MAX_AUTOCOMPLETE_RESULTS)
+ .then(accounts => {
+ if (!accounts) { return []; }
+ return accounts.map(acct => acct.email ?
+ `${predicate}:${acct.email}` :
+ `${predicate}:"${this._accountOrAnon(acct)}"`);
+ }).then(accounts => {
+ // When the expression supplied is a beginning substring of 'self',
+ // add it as an autocomplete option.
+ if (SELF_EXPRESSION.startsWith(expression)) {
+ return accounts.concat([predicate + ':' + SELF_EXPRESSION]);
+ } else if (ME_EXPRESSION.startsWith(expression)) {
+ return accounts.concat([predicate + ':' + ME_EXPRESSION]);
+ } else {
+ return accounts;
+ }
+ });
+ },
+ });
+})();
diff --git a/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html
new file mode 100644
index 0000000..66dc0f0
--- /dev/null
+++ b/polygerrit-ui/app/elements/core/gr-smart-search/gr-smart-search_test.html
@@ -0,0 +1,174 @@
+<!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-smart-search</title>
+
+<script src="../../../bower_components/webcomponentsjs/webcomponents-lite.min.js"></script>
+<script src="../../../bower_components/web-component-tester/browser.js"></script>
+<link rel="import" href="../../../test/common-test-setup.html"/>
+<link rel="import" href="gr-smart-search.html">
+
+<script>void(0);</script>
+
+<test-fixture id="basic">
+ <template>
+ <gr-smart-search></gr-smart-search>
+ </template>
+</test-fixture>
+
+<script>
+ suite('gr-smart-search tests', () => {
+ let element;
+ let sandbox;
+
+ setup(() => {
+ sandbox = sinon.sandbox.create();
+ element = fixture('basic');
+ });
+
+ teardown(() => {
+ sandbox.restore();
+ });
+
+
+ test('Autocompletes accounts', () => {
+ sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+ Promise.resolve([
+ {
+ name: 'fred',
+ email: 'fred@goog.co',
+ },
+ ])
+ );
+ return element._fetchAccounts('owner', 'fr').then(s => {
+ assert.equal(s[0], 'owner:fred@goog.co');
+ });
+ });
+
+ test('Inserts self as option when valid', done => {
+ sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+ Promise.resolve([
+ {
+ name: 'fred',
+ email: 'fred@goog.co',
+ },
+ ])
+ );
+ element._fetchAccounts('owner', 's').then(s => {
+ assert.equal(s[0], 'owner:fred@goog.co');
+ assert.equal(s[1], 'owner:self');
+ }).then(() => {
+ element._fetchAccounts('owner', 'selfs').then(s => {
+ assert.notEqual(s[0], 'owner:self');
+ done();
+ });
+ });
+ });
+
+ test('Inserts me as option when valid', done => {
+ sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+ Promise.resolve([
+ {
+ name: 'fred',
+ email: 'fred@goog.co',
+ },
+ ])
+ );
+ element._fetchAccounts('owner', 'm').then(s => {
+ assert.equal(s[0], 'owner:fred@goog.co');
+ assert.equal(s[1], 'owner:me');
+ }).then(() => {
+ element._fetchAccounts('owner', 'meme').then(s => {
+ assert.notEqual(s[0], 'owner:me');
+ done();
+ });
+ });
+ });
+
+ test('Autocompletes groups', done => {
+ sandbox.stub(element.$.restAPI, 'getSuggestedGroups', () =>
+ Promise.resolve({
+ Polygerrit: 0,
+ gerrit: 0,
+ gerrittest: 0,
+ })
+ );
+ element._fetchGroups('ownerin', 'pol').then(s => {
+ assert.equal(s[0], 'ownerin:Polygerrit');
+ done();
+ });
+ });
+
+ test('Autocompletes projects', done => {
+ sandbox.stub(element.$.restAPI, 'getSuggestedProjects', () =>
+ Promise.resolve({
+ Polygerrit: 0,
+ })
+ );
+ element._fetchProjects('project', 'pol').then(s => {
+ assert.equal(s[0], 'project:Polygerrit');
+ done();
+ });
+ });
+
+ test('Autocomplete doesnt override exact matches to input', done => {
+ sandbox.stub(element.$.restAPI, 'getSuggestedGroups', () =>
+ Promise.resolve({
+ Polygerrit: 0,
+ gerrit: 0,
+ gerrittest: 0,
+ })
+ );
+ element._fetchGroups('ownerin', 'gerrit').then(s => {
+ assert.equal(s[0], 'ownerin:Polygerrit');
+ assert.equal(s[1], 'ownerin:gerrit');
+ assert.equal(s[2], 'ownerin:gerrittest');
+ done();
+ });
+ });
+
+ test('Autocompletes accounts with no email', done => {
+ sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+ Promise.resolve([
+ {
+ name: 'fred',
+ },
+ ])
+ );
+ element._fetchAccounts('owner', 'fr').then(s => {
+ assert.equal(s[0], 'owner:"fred"');
+ done();
+ });
+ });
+
+ test('Autocompletes accounts with email', done => {
+ sandbox.stub(element.$.restAPI, 'getSuggestedAccounts', () =>
+ Promise.resolve([
+ {
+ email: 'fred@goog.co',
+ },
+ ])
+ );
+ element._fetchAccounts('owner', 'fr').then(s => {
+ assert.equal(s[0], 'owner:fred@goog.co');
+ done();
+ });
+ });
+ });
+</script>
diff --git a/polygerrit-ui/app/test/index.html b/polygerrit-ui/app/test/index.html
index dbc7dc9..c29e3b5 100644
--- a/polygerrit-ui/app/test/index.html
+++ b/polygerrit-ui/app/test/index.html
@@ -91,6 +91,7 @@
'core/gr-reporting/gr-reporting_test.html',
'core/gr-router/gr-router_test.html',
'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-diff-builder/gr-diff-builder_test.html',
'diff/gr-diff-comment-thread-group/gr-diff-comment-thread-group_test.html',