Commit 6cbc69aa authored by Phil Hughes's avatar Phil Hughes

Merge branch '32563-remove-duplicated-config-code-for-filtered-search-dropdowns' into 'master'

Resolve "Remove duplicated config code for filtered search dropdowns"

Closes #32563

See merge request !11512
parents a5f43509 681f3296
...@@ -13,13 +13,17 @@ export default { ...@@ -13,13 +13,17 @@ export default {
required: false, required: false,
default: true, default: true,
}, },
allowedKeys: {
type: Array,
required: true,
},
}, },
computed: { computed: {
processedItems() { processedItems() {
return this.items.map((item) => { return this.items.map((item) => {
const { tokens, searchToken } const { tokens, searchToken }
= gl.FilteredSearchTokenizer.processTokens(item); = gl.FilteredSearchTokenizer.processTokens(item, this.allowedKeys);
const resultantTokens = tokens.map(token => ({ const resultantTokens = tokens.map(token => ({
prefix: `${token.key}:`, prefix: `${token.key}:`,
......
...@@ -2,14 +2,18 @@ import Filter from '~/droplab/plugins/filter'; ...@@ -2,14 +2,18 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown'; import './filtered_search_dropdown';
class DropdownHint extends gl.FilteredSearchDropdown { class DropdownHint extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter) { constructor(droplab, dropdown, input, tokenKeys, filter) {
super(droplab, dropdown, input, filter); super(droplab, dropdown, input, filter);
this.config = { this.config = {
Filter: { Filter: {
template: 'hint', template: 'hint',
filterFunction: gl.DropdownUtils.filterHint.bind(null, input), filterFunction: gl.DropdownUtils.filterHint.bind(null, {
input,
allowedKeys: tokenKeys.getKeys(),
}),
}, },
}; };
this.tokenKeys = tokenKeys;
} }
itemClicked(e) { itemClicked(e) {
...@@ -52,20 +56,13 @@ class DropdownHint extends gl.FilteredSearchDropdown { ...@@ -52,20 +56,13 @@ class DropdownHint extends gl.FilteredSearchDropdown {
} }
renderContent() { renderContent() {
const dropdownData = []; const dropdownData = gl.FilteredSearchTokenKeys.get()
.map(tokenKey => ({
[].forEach.call(this.input.closest('.filtered-search-box-input-container').querySelectorAll('.dropdown-menu'), (dropdownMenu) => { icon: `fa-${tokenKey.icon}`,
const { icon, hint, tag, type } = dropdownMenu.dataset; hint: tokenKey.key,
if (icon && hint && tag) { tag: `<${tokenKey.symbol}${tokenKey.key}>`,
dropdownData.push( type: tokenKey.type,
Object.assign({ }));
icon: `fa-${icon}`,
hint,
tag: `<${tag}>`,
}, type && { type }),
);
}
});
this.droplab.changeHookList(this.hookId, this.dropdown, [Filter], this.config); this.droplab.changeHookList(this.hookId, this.dropdown, [Filter], this.config);
this.droplab.setData(this.hookId, dropdownData); this.droplab.setData(this.hookId, dropdownData);
......
...@@ -5,7 +5,7 @@ import Filter from '~/droplab/plugins/filter'; ...@@ -5,7 +5,7 @@ import Filter from '~/droplab/plugins/filter';
import './filtered_search_dropdown'; import './filtered_search_dropdown';
class DropdownNonUser extends gl.FilteredSearchDropdown { class DropdownNonUser extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter, endpoint, symbol) { constructor(droplab, dropdown, input, tokenKeys, filter, endpoint, symbol) {
super(droplab, dropdown, input, filter); super(droplab, dropdown, input, filter);
this.symbol = symbol; this.symbol = symbol;
this.config = { this.config = {
......
...@@ -4,7 +4,7 @@ import AjaxFilter from '~/droplab/plugins/ajax_filter'; ...@@ -4,7 +4,7 @@ import AjaxFilter from '~/droplab/plugins/ajax_filter';
import './filtered_search_dropdown'; import './filtered_search_dropdown';
class DropdownUser extends gl.FilteredSearchDropdown { class DropdownUser extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input, filter) { constructor(droplab, dropdown, input, tokenKeys, filter) {
super(droplab, dropdown, input, filter); super(droplab, dropdown, input, filter);
this.config = { this.config = {
AjaxFilter: { AjaxFilter: {
...@@ -25,6 +25,7 @@ class DropdownUser extends gl.FilteredSearchDropdown { ...@@ -25,6 +25,7 @@ class DropdownUser extends gl.FilteredSearchDropdown {
}, },
}, },
}; };
this.tokenKeys = tokenKeys;
} }
itemClicked(e) { itemClicked(e) {
...@@ -43,7 +44,7 @@ class DropdownUser extends gl.FilteredSearchDropdown { ...@@ -43,7 +44,7 @@ class DropdownUser extends gl.FilteredSearchDropdown {
getSearchInput() { getSearchInput() {
const query = gl.DropdownUtils.getSearchInput(this.input); const query = gl.DropdownUtils.getSearchInput(this.input);
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query); const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query, this.tokenKeys.get());
let value = lastToken || ''; let value = lastToken || '';
......
...@@ -50,10 +50,12 @@ class DropdownUtils { ...@@ -50,10 +50,12 @@ class DropdownUtils {
return updatedItem; return updatedItem;
} }
static filterHint(input, item) { static filterHint(config, item) {
const { input, allowedKeys } = config;
const updatedItem = item; const updatedItem = item;
const searchInput = gl.DropdownUtils.getSearchQuery(input); const searchInput = gl.DropdownUtils.getSearchQuery(input);
const { lastToken, tokens } = gl.FilteredSearchTokenizer.processTokens(searchInput); const { lastToken, tokens } =
gl.FilteredSearchTokenizer.processTokens(searchInput, allowedKeys);
const lastKey = lastToken.key || lastToken || ''; const lastKey = lastToken.key || lastToken || '';
const allowMultiple = item.type === 'array'; const allowMultiple = item.type === 'array';
const itemInExistingTokens = tokens.some(t => t.key === item.hint); const itemInExistingTokens = tokens.some(t => t.key === item.hint);
......
...@@ -2,10 +2,10 @@ import DropLab from '~/droplab/drop_lab'; ...@@ -2,10 +2,10 @@ import DropLab from '~/droplab/drop_lab';
import FilteredSearchContainer from './container'; import FilteredSearchContainer from './container';
class FilteredSearchDropdownManager { class FilteredSearchDropdownManager {
constructor(baseEndpoint = '', page) { constructor(baseEndpoint = '', tokenizer, page) {
this.container = FilteredSearchContainer.container; this.container = FilteredSearchContainer.container;
this.baseEndpoint = baseEndpoint.replace(/\/$/, ''); this.baseEndpoint = baseEndpoint.replace(/\/$/, '');
this.tokenizer = gl.FilteredSearchTokenizer; this.tokenizer = tokenizer;
this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys; this.filteredSearchTokenKeys = gl.FilteredSearchTokenKeys;
this.filteredSearchInput = this.container.querySelector('.filtered-search'); this.filteredSearchInput = this.container.querySelector('.filtered-search');
this.page = page; this.page = page;
...@@ -98,7 +98,8 @@ class FilteredSearchDropdownManager { ...@@ -98,7 +98,8 @@ class FilteredSearchDropdownManager {
if (!mappingKey.reference) { if (!mappingKey.reference) {
const dl = this.droplab; const dl = this.droplab;
const defaultArguments = [null, dl, element, this.filteredSearchInput, key]; const defaultArguments =
[null, dl, element, this.filteredSearchInput, this.filteredSearchTokenKeys, key];
const glArguments = defaultArguments.concat(mappingKey.extraArguments || []); const glArguments = defaultArguments.concat(mappingKey.extraArguments || []);
// Passing glArguments to `new gl[glClass](<arguments>)` // Passing glArguments to `new gl[glClass](<arguments>)`
...@@ -141,7 +142,8 @@ class FilteredSearchDropdownManager { ...@@ -141,7 +142,8 @@ class FilteredSearchDropdownManager {
setDropdown() { setDropdown() {
const query = gl.DropdownUtils.getSearchQuery(true); const query = gl.DropdownUtils.getSearchQuery(true);
const { lastToken, searchToken } = this.tokenizer.processTokens(query); const { lastToken, searchToken } =
this.tokenizer.processTokens(query, this.filteredSearchTokenKeys.getKeys());
if (this.currentDropdown) { if (this.currentDropdown) {
this.updateCurrentDropdownOffset(); this.updateCurrentDropdownOffset();
......
...@@ -15,6 +15,7 @@ class FilteredSearchManager { ...@@ -15,6 +15,7 @@ class FilteredSearchManager {
this.recentSearchesStore = new RecentSearchesStore({ this.recentSearchesStore = new RecentSearchesStore({
isLocalStorageAvailable: RecentSearchesService.isAvailable(), isLocalStorageAvailable: RecentSearchesService.isAvailable(),
allowedKeys: this.filteredSearchTokenKeys.getKeys(),
}); });
const searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown'); const searchHistoryDropdownElement = document.querySelector('.js-filtered-search-history-dropdown');
const projectPath = searchHistoryDropdownElement ? const projectPath = searchHistoryDropdownElement ?
...@@ -46,7 +47,7 @@ class FilteredSearchManager { ...@@ -46,7 +47,7 @@ class FilteredSearchManager {
if (this.filteredSearchInput) { if (this.filteredSearchInput) {
this.tokenizer = gl.FilteredSearchTokenizer; this.tokenizer = gl.FilteredSearchTokenizer;
this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', page); this.dropdownManager = new gl.FilteredSearchDropdownManager(this.filteredSearchInput.getAttribute('data-base-endpoint') || '', this.tokenizer, page);
this.recentSearchesRoot = new RecentSearchesRoot( this.recentSearchesRoot = new RecentSearchesRoot(
this.recentSearchesStore, this.recentSearchesStore,
...@@ -318,7 +319,7 @@ class FilteredSearchManager { ...@@ -318,7 +319,7 @@ class FilteredSearchManager {
handleInputVisualToken() { handleInputVisualToken() {
const input = this.filteredSearchInput; const input = this.filteredSearchInput;
const { tokens, searchToken } const { tokens, searchToken }
= gl.FilteredSearchTokenizer.processTokens(input.value); = this.tokenizer.processTokens(input.value, this.filteredSearchTokenKeys.getKeys());
const { isLastVisualTokenValid } const { isLastVisualTokenValid }
= gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput(); = gl.FilteredSearchVisualTokens.getLastVisualTokenBeforeInput();
...@@ -444,7 +445,7 @@ class FilteredSearchManager { ...@@ -444,7 +445,7 @@ class FilteredSearchManager {
this.saveCurrentSearchQuery(); this.saveCurrentSearchQuery();
const { tokens, searchToken } const { tokens, searchToken }
= this.tokenizer.processTokens(searchQuery); = this.tokenizer.processTokens(searchQuery, this.filteredSearchTokenKeys.getKeys());
const currentState = gl.utils.getParameterByName('state') || 'opened'; const currentState = gl.utils.getParameterByName('state') || 'opened';
paths.push(`state=${currentState}`); paths.push(`state=${currentState}`);
......
...@@ -3,21 +3,25 @@ const tokenKeys = [{ ...@@ -3,21 +3,25 @@ const tokenKeys = [{
type: 'string', type: 'string',
param: 'username', param: 'username',
symbol: '@', symbol: '@',
icon: 'pencil',
}, { }, {
key: 'assignee', key: 'assignee',
type: 'string', type: 'string',
param: 'username', param: 'username',
symbol: '@', symbol: '@',
icon: 'user',
}, { }, {
key: 'milestone', key: 'milestone',
type: 'string', type: 'string',
param: 'title', param: 'title',
symbol: '%', symbol: '%',
icon: 'clock-o',
}, { }, {
key: 'label', key: 'label',
type: 'array', type: 'array',
param: 'name[]', param: 'name[]',
symbol: '~', symbol: '~',
icon: 'tag',
}]; }];
const alternativeTokenKeys = [{ const alternativeTokenKeys = [{
...@@ -56,6 +60,10 @@ class FilteredSearchTokenKeys { ...@@ -56,6 +60,10 @@ class FilteredSearchTokenKeys {
return tokenKeys; return tokenKeys;
} }
static getKeys() {
return tokenKeys.map(i => i.key);
}
static getAlternatives() { static getAlternatives() {
return alternativeTokenKeys; return alternativeTokenKeys;
} }
......
import './filtered_search_token_keys'; import './filtered_search_token_keys';
class FilteredSearchTokenizer { class FilteredSearchTokenizer {
static processTokens(input) { static processTokens(input, allowedKeys) {
const allowedKeys = gl.FilteredSearchTokenKeys.get().map(i => i.key);
// Regex extracts `(token):(symbol)(value)` // Regex extracts `(token):(symbol)(value)`
// Values that start with a double quote must end in a double quote (same for single) // Values that start with a double quote must end in a double quote (same for single)
const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g'); const tokenRegex = new RegExp(`(${allowedKeys.join('|')}):([~%@]?)(?:('[^']*'{0,1})|("[^"]*"{0,1})|(\\S+))`, 'g');
......
...@@ -37,6 +37,7 @@ class RecentSearchesRoot { ...@@ -37,6 +37,7 @@ class RecentSearchesRoot {
<recent-searches-dropdown-content <recent-searches-dropdown-content
:items="recentSearches" :items="recentSearches"
:is-local-storage-available="isLocalStorageAvailable" :is-local-storage-available="isLocalStorageAvailable"
:allowed-keys="allowedKeys"
/> />
`, `,
components: { components: {
......
import _ from 'underscore'; import _ from 'underscore';
class RecentSearchesStore { class RecentSearchesStore {
constructor(initialState = {}) { constructor(initialState = {}, allowedKeys) {
this.state = Object.assign({ this.state = Object.assign({
isLocalStorageAvailable: true, isLocalStorageAvailable: true,
recentSearches: [], recentSearches: [],
allowedKeys,
}, initialState); }, initialState);
} }
......
...@@ -45,7 +45,7 @@ ...@@ -45,7 +45,7 @@
{{hint}} {{hint}}
%span.js-filter-tag.dropdown-light-content %span.js-filter-tag.dropdown-light-content
{{tag}} {{tag}}
#js-dropdown-author.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'pencil', hint: 'author', tag: '@author' } } #js-dropdown-author.filtered-search-input-dropdown-menu.dropdown-menu
%ul.filter-dropdown{ data: { dynamic: true, dropdown: true } } %ul.filter-dropdown{ data: { dynamic: true, dropdown: true } }
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link.dropdown-user %button.btn.btn-link.dropdown-user
...@@ -55,7 +55,7 @@ ...@@ -55,7 +55,7 @@
{{name}} {{name}}
%span.dropdown-light-content %span.dropdown-light-content
@{{username}} @{{username}}
#js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'user', hint: 'assignee', tag: '@assignee' } } #js-dropdown-assignee.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } } %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link %button.btn.btn-link
...@@ -70,7 +70,7 @@ ...@@ -70,7 +70,7 @@
{{name}} {{name}}
%span.dropdown-light-content %span.dropdown-light-content
@{{username}} @{{username}}
#js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'clock-o', hint: 'milestone', tag: '%milestone' } } #js-dropdown-milestone.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } } %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link %button.btn.btn-link
...@@ -86,7 +86,7 @@ ...@@ -86,7 +86,7 @@
%li.filter-dropdown-item %li.filter-dropdown-item
%button.btn.btn-link.js-data-value %button.btn.btn-link.js-data-value
{{title}} {{title}}
#js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu{ data: { icon: 'tag', hint: 'label', tag: '~label', type: 'array' } } #js-dropdown-label.filtered-search-input-dropdown-menu.dropdown-menu
%ul{ data: { dropdown: true } } %ul{ data: { dropdown: true } }
%li.filter-dropdown-item{ data: { value: 'none' } } %li.filter-dropdown-item{ data: { value: 'none' } }
%button.btn.btn-link %button.btn.btn-link
......
...@@ -2,6 +2,8 @@ import Vue from 'vue'; ...@@ -2,6 +2,8 @@ import Vue from 'vue';
import eventHub from '~/filtered_search/event_hub'; import eventHub from '~/filtered_search/event_hub';
import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content'; import RecentSearchesDropdownContent from '~/filtered_search/components/recent_searches_dropdown_content';
require('~/filtered_search/filtered_search_token_keys');
const createComponent = (propsData) => { const createComponent = (propsData) => {
const Component = Vue.extend(RecentSearchesDropdownContent); const Component = Vue.extend(RecentSearchesDropdownContent);
...@@ -17,12 +19,14 @@ const trimMarkupWhitespace = text => text.replace(/(\n|\s)+/gm, ' ').trim(); ...@@ -17,12 +19,14 @@ const trimMarkupWhitespace = text => text.replace(/(\n|\s)+/gm, ' ').trim();
describe('RecentSearchesDropdownContent', () => { describe('RecentSearchesDropdownContent', () => {
const propsDataWithoutItems = { const propsDataWithoutItems = {
items: [], items: [],
allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
}; };
const propsDataWithItems = { const propsDataWithItems = {
items: [ items: [
'foo', 'foo',
'author:@root label:~foo bar', 'author:@root label:~foo bar',
], ],
allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
}; };
let vm; let vm;
......
...@@ -12,7 +12,7 @@ describe('Dropdown User', () => { ...@@ -12,7 +12,7 @@ describe('Dropdown User', () => {
spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {}); spyOn(gl.DropdownUser.prototype, 'getProjectId').and.callFake(() => {});
spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {}); spyOn(gl.DropdownUtils, 'getSearchInput').and.callFake(() => {});
dropdownUser = new gl.DropdownUser(); dropdownUser = new gl.DropdownUser(null, null, null, gl.FilteredSearchTokenKeys);
}); });
it('should not return the double quote found in value', () => { it('should not return the double quote found in value', () => {
......
...@@ -122,6 +122,7 @@ describe('Dropdown Utils', () => { ...@@ -122,6 +122,7 @@ describe('Dropdown Utils', () => {
describe('filterHint', () => { describe('filterHint', () => {
let input; let input;
let allowedKeys;
beforeEach(() => { beforeEach(() => {
setFixtures(` setFixtures(`
...@@ -133,30 +134,38 @@ describe('Dropdown Utils', () => { ...@@ -133,30 +134,38 @@ describe('Dropdown Utils', () => {
`); `);
input = document.getElementById('test'); input = document.getElementById('test');
allowedKeys = gl.FilteredSearchTokenKeys.getKeys();
}); });
function config() {
return {
input,
allowedKeys,
};
}
it('should filter', () => { it('should filter', () => {
input.value = 'l'; input.value = 'l';
let updatedItem = gl.DropdownUtils.filterHint(input, { let updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'label', hint: 'label',
}); });
expect(updatedItem.droplab_hidden).toBe(false); expect(updatedItem.droplab_hidden).toBe(false);
input.value = 'o'; input.value = 'o';
updatedItem = gl.DropdownUtils.filterHint(input, { updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'label', hint: 'label',
}); });
expect(updatedItem.droplab_hidden).toBe(true); expect(updatedItem.droplab_hidden).toBe(true);
}); });
it('should return droplab_hidden false when item has no hint', () => { it('should return droplab_hidden false when item has no hint', () => {
const updatedItem = gl.DropdownUtils.filterHint(input, {}, ''); const updatedItem = gl.DropdownUtils.filterHint(config(), {}, '');
expect(updatedItem.droplab_hidden).toBe(false); expect(updatedItem.droplab_hidden).toBe(false);
}); });
it('should allow multiple if item.type is array', () => { it('should allow multiple if item.type is array', () => {
input.value = 'label:~first la'; input.value = 'label:~first la';
const updatedItem = gl.DropdownUtils.filterHint(input, { const updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'label', hint: 'label',
type: 'array', type: 'array',
}); });
...@@ -165,12 +174,12 @@ describe('Dropdown Utils', () => { ...@@ -165,12 +174,12 @@ describe('Dropdown Utils', () => {
it('should prevent multiple if item.type is not array', () => { it('should prevent multiple if item.type is not array', () => {
input.value = 'milestone:~first mile'; input.value = 'milestone:~first mile';
let updatedItem = gl.DropdownUtils.filterHint(input, { let updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'milestone', hint: 'milestone',
}); });
expect(updatedItem.droplab_hidden).toBe(true); expect(updatedItem.droplab_hidden).toBe(true);
updatedItem = gl.DropdownUtils.filterHint(input, { updatedItem = gl.DropdownUtils.filterHint(config(), {
hint: 'milestone', hint: 'milestone',
type: 'string', type: 'string',
}); });
......
...@@ -80,6 +80,7 @@ describe('Filtered Search Manager', () => { ...@@ -80,6 +80,7 @@ describe('Filtered Search Manager', () => {
expect(RecentSearchesService.isAvailable).toHaveBeenCalled(); expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({ expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({
isLocalStorageAvailable, isLocalStorageAvailable,
allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
}); });
}); });
......
...@@ -3,9 +3,11 @@ import '~/filtered_search/filtered_search_token_keys'; ...@@ -3,9 +3,11 @@ import '~/filtered_search/filtered_search_token_keys';
import '~/filtered_search/filtered_search_tokenizer'; import '~/filtered_search/filtered_search_tokenizer';
describe('Filtered Search Tokenizer', () => { describe('Filtered Search Tokenizer', () => {
const allowedKeys = gl.FilteredSearchTokenKeys.getKeys();
describe('processTokens', () => { describe('processTokens', () => {
it('returns for input containing only search value', () => { it('returns for input containing only search value', () => {
const results = gl.FilteredSearchTokenizer.processTokens('searchTerm'); const results = gl.FilteredSearchTokenizer.processTokens('searchTerm', allowedKeys);
expect(results.searchToken).toBe('searchTerm'); expect(results.searchToken).toBe('searchTerm');
expect(results.tokens.length).toBe(0); expect(results.tokens.length).toBe(0);
expect(results.lastToken).toBe(results.searchToken); expect(results.lastToken).toBe(results.searchToken);
...@@ -13,7 +15,7 @@ describe('Filtered Search Tokenizer', () => { ...@@ -13,7 +15,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input containing only tokens', () => { it('returns for input containing only tokens', () => {
const results = gl.FilteredSearchTokenizer const results = gl.FilteredSearchTokenizer
.processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none'); .processTokens('author:@root label:~"Very Important" milestone:%v1.0 assignee:none', allowedKeys);
expect(results.searchToken).toBe(''); expect(results.searchToken).toBe('');
expect(results.tokens.length).toBe(4); expect(results.tokens.length).toBe(4);
expect(results.tokens[3]).toBe(results.lastToken); expect(results.tokens[3]).toBe(results.lastToken);
...@@ -37,7 +39,7 @@ describe('Filtered Search Tokenizer', () => { ...@@ -37,7 +39,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input starting with search value and ending with tokens', () => { it('returns for input starting with search value and ending with tokens', () => {
const results = gl.FilteredSearchTokenizer const results = gl.FilteredSearchTokenizer
.processTokens('searchTerm anotherSearchTerm milestone:none'); .processTokens('searchTerm anotherSearchTerm milestone:none', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(1); expect(results.tokens.length).toBe(1);
expect(results.tokens[0]).toBe(results.lastToken); expect(results.tokens[0]).toBe(results.lastToken);
...@@ -48,7 +50,7 @@ describe('Filtered Search Tokenizer', () => { ...@@ -48,7 +50,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input starting with tokens and ending with search value', () => { it('returns for input starting with tokens and ending with search value', () => {
const results = gl.FilteredSearchTokenizer const results = gl.FilteredSearchTokenizer
.processTokens('assignee:@user searchTerm'); .processTokens('assignee:@user searchTerm', allowedKeys);
expect(results.searchToken).toBe('searchTerm'); expect(results.searchToken).toBe('searchTerm');
expect(results.tokens.length).toBe(1); expect(results.tokens.length).toBe(1);
...@@ -60,7 +62,7 @@ describe('Filtered Search Tokenizer', () => { ...@@ -60,7 +62,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input containing search value wrapped between tokens', () => { it('returns for input containing search value wrapped between tokens', () => {
const results = gl.FilteredSearchTokenizer const results = gl.FilteredSearchTokenizer
.processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none'); .processTokens('author:@root label:~"Won\'t fix" searchTerm anotherSearchTerm milestone:none', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(3); expect(results.tokens.length).toBe(3);
...@@ -81,7 +83,7 @@ describe('Filtered Search Tokenizer', () => { ...@@ -81,7 +83,7 @@ describe('Filtered Search Tokenizer', () => {
it('returns for input containing search value in between tokens', () => { it('returns for input containing search value in between tokens', () => {
const results = gl.FilteredSearchTokenizer const results = gl.FilteredSearchTokenizer
.processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing'); .processTokens('author:@root searchTerm assignee:none anotherSearchTerm label:~Doing', allowedKeys);
expect(results.searchToken).toBe('searchTerm anotherSearchTerm'); expect(results.searchToken).toBe('searchTerm anotherSearchTerm');
expect(results.tokens.length).toBe(3); expect(results.tokens.length).toBe(3);
expect(results.tokens[2]).toBe(results.lastToken); expect(results.tokens[2]).toBe(results.lastToken);
...@@ -100,14 +102,14 @@ describe('Filtered Search Tokenizer', () => { ...@@ -100,14 +102,14 @@ describe('Filtered Search Tokenizer', () => {
}); });
it('returns search value for invalid tokens', () => { it('returns search value for invalid tokens', () => {
const results = gl.FilteredSearchTokenizer.processTokens('fake:token'); const results = gl.FilteredSearchTokenizer.processTokens('fake:token', allowedKeys);
expect(results.lastToken).toBe('fake:token'); expect(results.lastToken).toBe('fake:token');
expect(results.searchToken).toBe('fake:token'); expect(results.searchToken).toBe('fake:token');
expect(results.tokens.length).toEqual(0); expect(results.tokens.length).toEqual(0);
}); });
it('returns search value and token for mix of valid and invalid tokens', () => { it('returns search value and token for mix of valid and invalid tokens', () => {
const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token'); const results = gl.FilteredSearchTokenizer.processTokens('label:real fake:token', allowedKeys);
expect(results.tokens.length).toEqual(1); expect(results.tokens.length).toEqual(1);
expect(results.tokens[0].key).toBe('label'); expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('real'); expect(results.tokens[0].value).toBe('real');
...@@ -117,13 +119,13 @@ describe('Filtered Search Tokenizer', () => { ...@@ -117,13 +119,13 @@ describe('Filtered Search Tokenizer', () => {
}); });
it('returns search value for invalid symbols', () => { it('returns search value for invalid symbols', () => {
const results = gl.FilteredSearchTokenizer.processTokens('std::includes'); const results = gl.FilteredSearchTokenizer.processTokens('std::includes', allowedKeys);
expect(results.lastToken).toBe('std::includes'); expect(results.lastToken).toBe('std::includes');
expect(results.searchToken).toBe('std::includes'); expect(results.searchToken).toBe('std::includes');
}); });
it('removes duplicated values', () => { it('removes duplicated values', () => {
const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo'); const results = gl.FilteredSearchTokenizer.processTokens('label:~foo label:~foo', allowedKeys);
expect(results.tokens.length).toBe(1); expect(results.tokens.length).toBe(1);
expect(results.tokens[0].key).toBe('label'); expect(results.tokens[0].key).toBe('label');
expect(results.tokens[0].value).toBe('foo'); expect(results.tokens[0].value).toBe('foo');
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment