Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Léo-Paul Géneau
gitlab-ce
Commits
e197f27f
Commit
e197f27f
authored
Dec 17, 2016
by
Clement Ho
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Refactor and use regex for string processing
parent
0e40c952
Changes
10
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
189 additions
and
536 deletions
+189
-536
app/assets/javascripts/filtered_search/dropdown_hint.js.es6
app/assets/javascripts/filtered_search/dropdown_hint.js.es6
+21
-37
app/assets/javascripts/filtered_search/dropdown_user.js.es6
app/assets/javascripts/filtered_search/dropdown_user.js.es6
+3
-6
app/assets/javascripts/filtered_search/dropdown_utils.js.es6
app/assets/javascripts/filtered_search/dropdown_utils.js.es6
+16
-14
app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6
...vascripts/filtered_search/filtered_search_dropdown.js.es6
+1
-1
app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
...s/filtered_search/filtered_search_dropdown_manager.js.es6
+20
-14
app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
...avascripts/filtered_search/filtered_search_manager.js.es6
+3
-11
app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6
...ascripts/filtered_search/filtered_search_tokenizer.js.es6
+26
-152
spec/javascripts/filtered_search/dropdown_utils_spec.js.es6
spec/javascripts/filtered_search/dropdown_utils_spec.js.es6
+5
-20
spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js.es6
...tered_search/filtered_search_dropdown_manager_spec.js.es6
+1
-21
spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js.es6
...pts/filtered_search/filtered_search_tokenizer_spec.js.es6
+93
-260
No files found.
app/assets/javascripts/filtered_search/dropdown_hint.js.es6
View file @
e197f27f
...
@@ -3,31 +3,13 @@
...
@@ -3,31 +3,13 @@
/* global droplabFilter */
/* global droplabFilter */
(() => {
(() => {
const dropdownData = [{
icon: 'fa-pencil',
hint: 'author:',
tag: '<author>',
}, {
icon: 'fa-user',
hint: 'assignee:',
tag: '<assignee>',
}, {
icon: 'fa-clock-o',
hint: 'milestone:',
tag: '<milestone>',
}, {
icon: 'fa-tag',
hint: 'label:',
tag: '<label>',
}];
class DropdownHint extends gl.FilteredSearchDropdown {
class DropdownHint extends gl.FilteredSearchDropdown {
constructor(droplab, dropdown, input) {
constructor(droplab, dropdown, input) {
super(droplab, dropdown, input);
super(droplab, dropdown, input);
this.config = {
this.config = {
droplabFilter: {
droplabFilter: {
template: 'hint',
template: 'hint',
filterFunction: gl.DropdownUtils.filter
Method
,
filterFunction: gl.DropdownUtils.filter
Hint
,
},
},
};
};
}
}
...
@@ -43,8 +25,7 @@
...
@@ -43,8 +25,7 @@
const tag = selected.querySelector('.js-filter-tag').innerText.trim();
const tag = selected.querySelector('.js-filter-tag').innerText.trim();
if (tag.length) {
if (tag.length) {
gl.FilteredSearchDropdownManager
gl.FilteredSearchDropdownManager.addWordToInput(token);
.addWordToInput(this.getSelectedTextWithoutEscaping(token));
}
}
this.dismissDropdown();
this.dismissDropdown();
this.dispatchInputEvent();
this.dispatchInputEvent();
...
@@ -52,24 +33,27 @@
...
@@ -52,24 +33,27 @@
}
}
}
}
getSelectedTextWithoutEscaping(selectedToken) {
const lastWord = this.input.value.split(' ').last();
const lastWordIndex = selectedToken.indexOf(lastWord);
return lastWordIndex === -1 ? selectedToken : selectedToken.slice(lastWord.length);
}
renderContent() {
renderContent() {
this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config);
const dropdownData = [{
icon: 'fa-pencil',
// Clone dropdownData to prevent it from being
hint: 'author:',
// changed due to pass by reference
tag: '<author>',
const data = [];
}, {
dropdownData.forEach((item) => {
icon: 'fa-user',
data.push(Object.assign({}, item));
hint: 'assignee:',
});
tag: '<assignee>',
}, {
icon: 'fa-clock-o',
hint: 'milestone:',
tag: '<milestone>',
}, {
icon: 'fa-tag',
hint: 'label:',
tag: '<label>',
}];
this.droplab.setData(this.hookId, data);
this.droplab.changeHookList(this.hookId, this.dropdown, [droplabFilter], this.config);
this.droplab.setData(this.hookId, dropdownData);
}
}
init() {
init() {
...
...
app/assets/javascripts/filtered_search/dropdown_user.js.es6
View file @
e197f27f
...
@@ -37,13 +37,10 @@
...
@@ -37,13 +37,10 @@
}
}
getSearchInput() {
getSearchInput() {
const query = this.input.value;
const query = this.input.value.trim();
const { value } = gl.FilteredSearchTokenizer.getLastTokenObject(query);
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(query);
const valueWithoutColon = value.slice(1);
const hasPrefix = valueWithoutColon[0] === '@';
const valueWithoutPrefix = valueWithoutColon.slice(1);
return
hasPrefix ? valueWithoutPrefix : valueWithoutColon
;
return
lastToken.value || ''
;
}
}
init() {
init() {
...
...
app/assets/javascripts/filtered_search/dropdown_utils.js.es6
View file @
e197f27f
...
@@ -22,30 +22,32 @@
...
@@ -22,30 +22,32 @@
static filterWithSymbol(filterSymbol, item, query) {
static filterWithSymbol(filterSymbol, item, query) {
const updatedItem = item;
const updatedItem = item;
const { value } = gl.FilteredSearchTokenizer.getLastTokenObject(query);
const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(query);
const valueWithoutColon = value.slice(1).toLowerCase();
const prefix = valueWithoutColon[0];
const valueWithoutPrefix = valueWithoutColon.slice(1);
if (lastToken !== searchToken) {
const value = lastToken.value.toLowerCase();
const title = updatedItem.title.toLowerCase();
const title = updatedItem.title.toLowerCase();
// Eg. filterSymbol = ~ for labels
// Eg. filterSymbol = ~ for labels
const matchWithoutPrefix =
const matchWithoutSymbol = lastToken.symbol === filterSymbol && title.indexOf(value) !== -1;
prefix === filterSymbol && title.indexOf(valueWithoutPrefix) !== -1;
const match = title.indexOf(`${lastToken.symbol}${value}`) !== -1;
const match = title.indexOf(valueWithoutColon) !== -1;
updatedItem.droplab_hidden = !match && !matchWithoutSymbol;
} else {
updatedItem.droplab_hidden = false;
}
updatedItem.droplab_hidden = !match && !matchWithoutPrefix;
return updatedItem;
return updatedItem;
}
}
static filter
Method
(item, query) {
static filter
Hint
(item, query) {
const updatedItem = item;
const updatedItem = item;
const {
value } = gl.FilteredSearchTokenizer.getLastTokenObject
(query);
const {
lastToken } = gl.FilteredSearchTokenizer.processTokens
(query);
if (
value === ''
) {
if (
!lastToken
) {
updatedItem.droplab_hidden = false;
updatedItem.droplab_hidden = false;
} else {
} else {
updatedItem.droplab_hidden = updatedItem.hint.indexOf(
value
) === -1;
updatedItem.droplab_hidden = updatedItem.hint.indexOf(
lastToken.toLowerCase()
) === -1;
}
}
return updatedItem;
return updatedItem;
...
...
app/assets/javascripts/filtered_search/filtered_search_dropdown.js.es6
View file @
e197f27f
...
@@ -29,7 +29,7 @@
...
@@ -29,7 +29,7 @@
itemClicked(e, getValueFunction) {
itemClicked(e, getValueFunction) {
const { selected } = e.detail;
const { selected } = e.detail;
if (selected.tagName === 'LI') {
if (selected.tagName === 'LI'
&& selected.innerHTML
) {
const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(selected);
const dataValueSet = gl.DropdownUtils.setDataValueIfSelected(selected);
if (!dataValueSet) {
if (!dataValueSet) {
...
...
app/assets/javascripts/filtered_search/filtered_search_dropdown_manager.js.es6
View file @
e197f27f
...
@@ -57,17 +57,25 @@
...
@@ -57,17 +57,25 @@
static addWordToInput(word, addSpace = false) {
static addWordToInput(word, addSpace = false) {
const input = document.querySelector('.filtered-search');
const input = document.querySelector('.filtered-search');
input.value = input.value.trim();
const value = input.value;
const value = input.value;
const hasExistingValue = value.length !== 0;
const hasExistingValue = value.length !== 0;
const { lastToken } = gl.FilteredSearchTokenizer.processTokens(value);
const { lastToken, searchToken } = gl.FilteredSearchTokenizer.processTokens(value);
// Find out what part of the token value the user has typed
// and remove it from input before appending the selected token value
if (lastToken !== searchToken) {
const lastTokenString = `${lastToken.symbol}${lastToken.value}`;
if ({}.hasOwnProperty.call(lastToken, 'key')) {
// Spaces inside the token means that the token value will be escaped by quotes
// Spaces inside the token means that the token value will be escaped by quotes
const hasQuotes = lastToken
.value
.indexOf(' ') !== -1;
const hasQuotes = lastToken
String
.indexOf(' ') !== -1;
// Add 2 length to account for the length of the front and back quotes
// Add 2 length to account for the length of the front and back quotes
const lengthToRemove = hasQuotes ? lastToken
.value.length + 2 : lastToken.value
.length;
const lengthToRemove = hasQuotes ? lastToken
String.length + 2 : lastTokenString
.length;
input.value = value.slice(0, -1 * (lengthToRemove));
input.value = value.slice(0, -1 * (lengthToRemove));
} else if (searchToken !== '' && word.indexOf(searchToken) !== -1) {
input.value = value.slice(0, -1 * searchToken.length);
}
}
input.value += hasExistingValue && addSpace ? ` ${word}` : word;
input.value += hasExistingValue && addSpace ? ` ${word}` : word;
...
@@ -129,27 +137,25 @@
...
@@ -129,27 +137,25 @@
const match = gl.FilteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const match = gl.FilteredSearchTokenKeys.searchByKey(dropdownName.toLowerCase());
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
const shouldOpenFilterDropdown = match && this.currentDropdown !== match.key
&&
{}.hasOwnProperty.call(this.mapping, match.key)
;
&&
this.mapping[match.key]
;
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
const shouldOpenHintDropdown = !match && this.currentDropdown !== 'hint';
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
if (shouldOpenFilterDropdown || shouldOpenHintDropdown) {
// `hint` is not listed as a tokenKey (since it is not a real `filter`)
const key = match && match.key ? match.key : 'hint';
const key = match && {}.hasOwnProperty.call(match, 'key') ? match.key : 'hint';
this.load(key, firstLoad);
this.load(key, firstLoad);
}
}
gl.droplab = this.droplab;
}
}
setDropdown() {
setDropdown() {
const { lastToken } = this.tokenizer.processTokens(this.filteredSearchInput.value);
const { lastToken, searchToken } = this.tokenizer
.processTokens(this.filteredSearchInput.value);
if (
typeof lastToken === 'string'
) {
if (
lastToken === searchToken
) {
// Token is not fully initialized yet because it has no value
// Token is not fully initialized yet because it has no value
// Eg. token = 'label:'
// Eg. token = 'label:'
const
{ tokenKey } = this.tokenizer.parseToken(lastToken
);
const
split = lastToken.split(':'
);
this.loadDropdown(
tokenKey
);
this.loadDropdown(
split.length > 1 ? split[0] : ''
);
} else if (
{}.hasOwnProperty.call(lastToken, 'key')
) {
} else if (
lastToken
) {
// Token has been initialized into an object because it has a value
// Token has been initialized into an object because it has a value
this.loadDropdown(lastToken.key);
this.loadDropdown(lastToken.key);
} else {
} else {
...
...
app/assets/javascripts/filtered_search/filtered_search_manager.js.es6
View file @
e197f27f
...
@@ -136,21 +136,13 @@
...
@@ -136,21 +136,13 @@
const condition = gl.FilteredSearchTokenKeys
const condition = gl.FilteredSearchTokenKeys
.searchByConditionKeyValue(token.key, token.value.toLowerCase());
.searchByConditionKeyValue(token.key, token.value.toLowerCase());
const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key);
const { param } = gl.FilteredSearchTokenKeys.searchByKey(token.key);
const keyParam = param ? `${token.key}_${param}` : token.key;
let tokenPath = '';
let tokenPath = '';
let keyParam = token.key;
if (condition) {
if (param) {
keyParam += `_${param}`;
}
if (token.wildcard && condition) {
tokenPath = condition.url;
tokenPath = condition.url;
} else if (token.wildcard) {
// wildcard means that the token does not have a symbol
tokenPath = `${keyParam}=${encodeURIComponent(token.value)}`;
} else {
} else {
// Remove the token symbol
tokenPath = `${keyParam}=${encodeURIComponent(token.value)}`;
tokenPath = `${keyParam}=${encodeURIComponent(token.value.slice(1))}`;
}
}
paths.push(tokenPath);
paths.push(tokenPath);
...
...
app/assets/javascripts/filtered_search/filtered_search_tokenizer.js.es6
View file @
e197f27f
(() => {
(() => {
class FilteredSearchTokenizer {
class FilteredSearchTokenizer {
static parseToken(input) {
const colonIndex = input.indexOf(':');
let tokenKey;
let tokenValue;
let tokenSymbol;
if (colonIndex !== -1) {
tokenKey = input.slice(0, colonIndex).toLowerCase();
tokenValue = input.slice(colonIndex + 1);
tokenSymbol = tokenValue[0];
}
return {
tokenKey,
tokenValue,
tokenSymbol,
};
}
static getLastTokenObject(input) {
const token = FilteredSearchTokenizer.getLastToken(input);
const colonIndex = token.indexOf(':');
const key = colonIndex !== -1 ? token.slice(0, colonIndex) : '';
const value = colonIndex !== -1 ? token.slice(colonIndex) : token;
return {
key,
value,
};
}
static getLastToken(input) {
let completeToken = false;
let completeQuotation = true;
let lastQuotation = '';
let i = input.length;
const doubleQuote = '"';
const singleQuote = '\'';
while (!completeToken && i >= 0) {
const isDoubleQuote = input[i] === doubleQuote;
const isSingleQuote = input[i] === singleQuote;
// If the second quotation is found
if ((lastQuotation === doubleQuote && isDoubleQuote) ||
(lastQuotation === singleQuote && isSingleQuote)) {
completeQuotation = true;
}
// Save the first quotation
if ((isDoubleQuote && lastQuotation === '') ||
(isSingleQuote && lastQuotation === '')) {
lastQuotation = input[i];
completeQuotation = false;
}
if (completeQuotation && input[i] === ' ') {
completeToken = true;
} else {
i -= 1;
}
}
// Adjust by 1 because of empty space
return input.slice(i + 1);
}
static processTokens(input) {
static processTokens(input) {
const tokenRegex = /(\w+):([~%@]?)(?:"(.*?)"|'(.*?)'|(\S+))/g;
const tokens = [];
const tokens = [];
let searchToken = '';
let lastToken = null;
let lastToken = '';
const searchToken = input.replace(tokenRegex, (match, key, symbol, v1, v2, v3) => {
let tokenValue = v1 || v2 || v3;
const inputs = input.split(' ');
let tokenSymbol = symbol;
let searchTerms = '';
let lastQuotation = '';
let incompleteToken = false;
// Iterate through each word (broken up by spaces)
inputs.forEach((i) => {
if (incompleteToken) {
// Continue previous token as it had an escaped
// quote in the beginning
const prevToken = tokens.last();
prevToken.value += ` ${i}`;
// Remove last quotation from the value
const lastQuotationRegex = new RegExp(lastQuotation, 'g');
prevToken.value = prevToken.value.replace(lastQuotationRegex, '');
tokens[tokens.length - 1] = prevToken;
// Check to see if this quotation completes the token value
if (i.indexOf(lastQuotation) !== -1) {
lastToken = tokens.last();
incompleteToken = !incompleteToken;
}
return;
}
const colonIndex = i.indexOf(':');
if (colonIndex !== -1) {
const { tokenKey, tokenValue, tokenSymbol } = gl.FilteredSearchTokenizer.parseToken(i);
const keyMatch = gl.FilteredSearchTokenKeys.searchByKey(tokenKey);
const symbolMatch = gl.FilteredSearchTokenKeys.searchBySymbol(tokenSymbol);
const doubleQuoteOccurrences = tokenValue.split('"').length - 1;
const singleQuoteOccurrences = tokenValue.split('\'').length - 1;
const doubleQuoteIndex = tokenValue.indexOf('"');
if (tokenValue === '~' || tokenValue === '%' || tokenValue === '@') {
const singleQuoteIndex = tokenValue.indexOf('\'');
tokenSymbol = tokenValue;
tokenValue = '';
const doubleQuoteExist = doubleQuoteIndex !== -1;
const singleQuoteExist = singleQuoteIndex !== -1;
const doubleQuoteExistOnly = doubleQuoteExist && !singleQuoteExist;
const doubleQuoteIsBeforeSingleQuote =
doubleQuoteExist && singleQuoteExist && doubleQuoteIndex < singleQuoteIndex;
const singleQuoteExistOnly = singleQuoteExist && !doubleQuoteExist;
const singleQuoteIsBeforeDoubleQuote =
doubleQuoteExist && singleQuoteExist && singleQuoteIndex < doubleQuoteIndex;
if ((doubleQuoteExistOnly || doubleQuoteIsBeforeSingleQuote)
&& doubleQuoteOccurrences % 2 !== 0) {
// " is found and is in front of ' (if any)
lastQuotation = '"';
incompleteToken = true;
} else if ((singleQuoteExistOnly || singleQuoteIsBeforeDoubleQuote)
&& singleQuoteOccurrences % 2 !== 0) {
// ' is found and is in front of " (if any)
lastQuotation = '\'';
incompleteToken = true;
}
}
if (keyMatch && tokenValue.length > 0) {
tokens.push({
tokens.push({
key: keyMatch.
key,
key,
value: tokenValue
,
value: tokenValue || ''
,
wildcard: !symbolMatch
,
symbol: tokenSymbol || ''
,
});
});
lastToken = tokens.last();
return '';
}).replace(/\s{2,}/g, ' ').trim() || '';
return;
}
if (tokens.length > 0) {
const last = tokens[tokens.length - 1];
const lastString = `${last.key}:${last.symbol}${last.value}`;
lastToken = input.lastIndexOf(lastString) ===
input.length - lastString.length ? last : searchToken;
} else {
lastToken = searchToken;
}
}
// Add space for next term
searchTerms += `${i} `;
lastToken = i;
}, this);
searchToken = searchTerms.trim();
return {
return {
tokens,
tokens,
searchToken,
lastToken,
lastToken,
searchToken,
};
};
}
}
}
}
...
...
spec/javascripts/filtered_search/dropdown_utils_spec.js.es6
View file @
e197f27f
...
@@ -34,11 +34,6 @@
...
@@ -34,11 +34,6 @@
title: '@root',
title: '@root',
};
};
beforeEach(() => {
spyOn(gl.FilteredSearchTokenizer, 'getLastTokenObject')
.and.callFake(query => ({ value: query }));
});
it('should filter without symbol', () => {
it('should filter without symbol', () => {
const updatedItem = gl.DropdownUtils.filterWithSymbol('@', item, ':roo');
const updatedItem = gl.DropdownUtils.filterWithSymbol('@', item, ':roo');
expect(updatedItem.droplab_hidden).toBe(false);
expect(updatedItem.droplab_hidden).toBe(false);
...
@@ -49,37 +44,27 @@
...
@@ -49,37 +44,27 @@
expect(updatedItem.droplab_hidden).toBe(false);
expect(updatedItem.droplab_hidden).toBe(false);
});
});
it('should filter with invalid symbol', () => {
const updatedItem = gl.DropdownUtils.filterWithSymbol('@', item, ':#');
expect(updatedItem.droplab_hidden).toBe(true);
});
it('should filter with colon', () => {
it('should filter with colon', () => {
const updatedItem = gl.DropdownUtils.filterWithSymbol('@', item, ':');
const updatedItem = gl.DropdownUtils.filterWithSymbol('@', item, ':');
expect(updatedItem.droplab_hidden).toBe(false);
expect(updatedItem.droplab_hidden).toBe(false);
});
});
});
});
describe('filterMethod', () => {
describe('filterHint', () => {
beforeEach(() => {
it('should filter', () => {
spyOn(gl.FilteredSearchTokenizer, 'getLastTokenObject')
let updatedItem = gl.DropdownUtils.filterHint({
.and.callFake(query => ({ value: query }));
});
it('should filter by hint', () => {
let updatedItem = gl.DropdownUtils.filterMethod({
hint: 'label',
hint: 'label',
}, 'l');
}, 'l');
expect(updatedItem.droplab_hidden).toBe(false);
expect(updatedItem.droplab_hidden).toBe(false);
updatedItem = gl.DropdownUtils.filter
Method
({
updatedItem = gl.DropdownUtils.filter
Hint
({
hint: 'label',
hint: 'label',
}, 'o');
}, 'o');
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.filter
Method
({}, '');
const updatedItem = gl.DropdownUtils.filter
Hint
({}, '');
expect(updatedItem.droplab_hidden).toBe(false);
expect(updatedItem.droplab_hidden).toBe(false);
});
});
});
});
...
...
spec/javascripts/filtered_search/filtered_search_dropdown_manager_spec.js.es6
View file @
e197f27f
...
@@ -21,13 +21,6 @@
...
@@ -21,13 +21,6 @@
});
});
describe('input has no existing value', () => {
describe('input has no existing value', () => {
beforeEach(() => {
spyOn(gl.FilteredSearchTokenizer, 'processTokens')
.and.callFake(() => ({
lastToken: {},
}));
});
it('should add word', () => {
it('should add word', () => {
gl.FilteredSearchDropdownManager.addWordToInput('firstWord');
gl.FilteredSearchDropdownManager.addWordToInput('firstWord');
expect(getInputValue()).toBe('firstWord');
expect(getInputValue()).toBe('firstWord');
...
@@ -61,26 +54,13 @@
...
@@ -61,26 +54,13 @@
value: 'roo',
value: 'roo',
};
};
spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.callFake(() => ({
lastToken,
}));
document.querySelector('.filtered-search').value = `${lastToken.key}:${lastToken.value}`;
document.querySelector('.filtered-search').value = `${lastToken.key}:${lastToken.value}`;
gl.FilteredSearchDropdownManager.addWordToInput('root');
gl.FilteredSearchDropdownManager.addWordToInput('root');
expect(getInputValue()).toBe('author:root');
expect(getInputValue()).toBe('author:root');
});
});
it('should only add the remaining characters of the word (contains space)', () => {
it('should only add the remaining characters of the word (contains space)', () => {
const lastToken = {
document.querySelector('.filtered-search').value = 'label:~"test';
key: 'label',
value: 'test me',
};
spyOn(gl.FilteredSearchTokenizer, 'processTokens').and.callFake(() => ({
lastToken,
}));
document.querySelector('.filtered-search').value = `${lastToken.key}:"${lastToken.value}"`;
gl.FilteredSearchDropdownManager.addWordToInput('~\'"test me"\'');
gl.FilteredSearchDropdownManager.addWordToInput('~\'"test me"\'');
expect(getInputValue()).toBe('label:~\'"test me"\'');
expect(getInputValue()).toBe('label:~\'"test me"\'');
});
});
...
...
spec/javascripts/filtered_search/filtered_search_tokenizer_spec.js.es6
View file @
e197f27f
This diff is collapsed.
Click to expand it.
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment