diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 14:18:50 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-06-18 14:18:50 +0300 |
commit | 8c7f4e9d5f36cff46365a7f8c4b9c21578c1e781 (patch) | |
tree | a77e7fe7a93de11213032ed4ab1f33a3db51b738 /spec/frontend/filtered_search | |
parent | 00b35af3db1abfe813a778f643dad221aad51fca (diff) |
Add latest changes from gitlab-org/gitlab@13-1-stable-ee
Diffstat (limited to 'spec/frontend/filtered_search')
-rw-r--r-- | spec/frontend/filtered_search/filtered_search_dropdown_manager_spec.js | 130 | ||||
-rw-r--r-- | spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js | 732 |
2 files changed, 862 insertions, 0 deletions
diff --git a/spec/frontend/filtered_search/filtered_search_dropdown_manager_spec.js b/spec/frontend/filtered_search/filtered_search_dropdown_manager_spec.js new file mode 100644 index 00000000000..e9ee69ca163 --- /dev/null +++ b/spec/frontend/filtered_search/filtered_search_dropdown_manager_spec.js @@ -0,0 +1,130 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import FilteredSearchDropdownManager from '~/filtered_search/filtered_search_dropdown_manager'; + +describe('Filtered Search Dropdown Manager', () => { + let mock; + + beforeEach(() => { + mock = new MockAdapter(axios); + mock.onGet().reply(200); + }); + + describe('addWordToInput', () => { + function getInputValue() { + return document.querySelector('.filtered-search').value; + } + + function setInputValue(value) { + document.querySelector('.filtered-search').value = value; + } + + beforeEach(() => { + setFixtures(` + <ul class="tokens-container"> + <li class="input-token"> + <input class="filtered-search"> + </li> + </ul> + `); + }); + + describe('input has no existing value', () => { + it('should add just tokenName', () => { + FilteredSearchDropdownManager.addWordToInput({ tokenName: 'milestone' }); + + const token = document.querySelector('.tokens-container .js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').textContent).toBe('milestone'); + expect(getInputValue()).toBe(''); + }); + + it('should add tokenName, tokenOperator, and tokenValue', () => { + FilteredSearchDropdownManager.addWordToInput({ tokenName: 'label' }); + + let token = document.querySelector('.tokens-container .js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').textContent).toBe('label'); + expect(getInputValue()).toBe(''); + + FilteredSearchDropdownManager.addWordToInput({ tokenName: 'label', tokenOperator: '=' }); + + token = document.querySelector('.tokens-container .js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').textContent).toBe('label'); + expect(token.querySelector('.operator').textContent).toBe('='); + expect(getInputValue()).toBe(''); + + FilteredSearchDropdownManager.addWordToInput({ + tokenName: 'label', + tokenOperator: '=', + tokenValue: 'none', + }); + // We have to get that reference again + // Because FilteredSearchDropdownManager deletes the previous token + token = document.querySelector('.tokens-container .js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').textContent).toBe('label'); + expect(token.querySelector('.operator').textContent).toBe('='); + expect(token.querySelector('.value').textContent).toBe('none'); + expect(getInputValue()).toBe(''); + }); + }); + + describe('input has existing value', () => { + it('should be able to just add tokenName', () => { + setInputValue('a'); + FilteredSearchDropdownManager.addWordToInput({ tokenName: 'author' }); + + const token = document.querySelector('.tokens-container .js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').textContent).toBe('author'); + expect(getInputValue()).toBe(''); + }); + + it('should replace tokenValue', () => { + FilteredSearchDropdownManager.addWordToInput({ tokenName: 'author' }); + FilteredSearchDropdownManager.addWordToInput({ tokenName: 'author', tokenOperator: '=' }); + + setInputValue('roo'); + FilteredSearchDropdownManager.addWordToInput({ + tokenName: null, + tokenOperator: '=', + tokenValue: '@root', + }); + + const token = document.querySelector('.tokens-container .js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').textContent).toBe('author'); + expect(token.querySelector('.operator').textContent).toBe('='); + expect(token.querySelector('.value').textContent).toBe('@root'); + expect(getInputValue()).toBe(''); + }); + + it('should add tokenValues containing spaces', () => { + FilteredSearchDropdownManager.addWordToInput({ tokenName: 'label' }); + + setInputValue('"test '); + FilteredSearchDropdownManager.addWordToInput({ + tokenName: 'label', + tokenOperator: '=', + tokenValue: '~\'"test me"\'', + }); + + const token = document.querySelector('.tokens-container .js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').textContent).toBe('label'); + expect(token.querySelector('.operator').textContent).toBe('='); + expect(token.querySelector('.value').textContent).toBe('~\'"test me"\''); + expect(getInputValue()).toBe(''); + }); + }); + }); +}); diff --git a/spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js b/spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js new file mode 100644 index 00000000000..e59ee925cc7 --- /dev/null +++ b/spec/frontend/filtered_search/filtered_search_visual_tokens_spec.js @@ -0,0 +1,732 @@ +import axios from 'axios'; +import MockAdapter from 'axios-mock-adapter'; +import FilteredSearchVisualTokens from '~/filtered_search/filtered_search_visual_tokens'; +import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper'; + +describe('Filtered Search Visual Tokens', () => { + let mock; + const subject = FilteredSearchVisualTokens; + + const findElements = tokenElement => { + const tokenNameElement = tokenElement.querySelector('.name'); + const tokenOperatorElement = tokenElement.querySelector('.operator'); + const tokenValueContainer = tokenElement.querySelector('.value-container'); + const tokenValueElement = tokenValueContainer.querySelector('.value'); + return { tokenNameElement, tokenOperatorElement, tokenValueContainer, tokenValueElement }; + }; + + let tokensContainer; + let authorToken; + let bugLabelToken; + + beforeEach(() => { + mock = new MockAdapter(axios); + mock.onGet().reply(200); + + setFixtures(` + <ul class="tokens-container"> + ${FilteredSearchSpecHelper.createInputHTML()} + </ul> + `); + tokensContainer = document.querySelector('.tokens-container'); + + authorToken = FilteredSearchSpecHelper.createFilterVisualToken('author', '=', '@user'); + bugLabelToken = FilteredSearchSpecHelper.createFilterVisualToken('label', '=', '~bug'); + }); + + describe('getLastVisualTokenBeforeInput', () => { + it('returns when there are no visual tokens', () => { + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); + + expect(lastVisualToken).toEqual(null); + expect(isLastVisualTokenValid).toEqual(true); + }); + + describe('input is the last item in tokensContainer', () => { + it('returns when there is one visual token', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + bugLabelToken.outerHTML, + ); + + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); + + expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token')); + expect(isLastVisualTokenValid).toEqual(true); + }); + + it('returns when there is an incomplete visual token', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('Author'), + ); + + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); + + expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token')); + expect(isLastVisualTokenValid).toEqual(false); + }); + + it('returns when there are multiple visual tokens', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${bugLabelToken.outerHTML} + ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')} + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '=', '@root')} + `); + + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); + const items = document.querySelectorAll('.tokens-container .js-visual-token'); + + expect(lastVisualToken.isEqualNode(items[items.length - 1])).toEqual(true); + expect(isLastVisualTokenValid).toEqual(true); + }); + + it('returns when there are multiple visual tokens and an incomplete visual token', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${bugLabelToken.outerHTML} + ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')} + ${FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('assignee')} + `); + + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); + const items = document.querySelectorAll('.tokens-container .js-visual-token'); + + expect(lastVisualToken.isEqualNode(items[items.length - 1])).toEqual(true); + expect(isLastVisualTokenValid).toEqual(false); + }); + }); + + describe('input is a middle item in tokensContainer', () => { + it('returns last token before input', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${bugLabelToken.outerHTML} + ${FilteredSearchSpecHelper.createInputHTML()} + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '=', '@root')} + `); + + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); + + expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token')); + expect(isLastVisualTokenValid).toEqual(true); + }); + + it('returns last partial token before input', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('label')} + ${FilteredSearchSpecHelper.createInputHTML()} + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '=', '@root')} + `); + + const { lastVisualToken, isLastVisualTokenValid } = subject.getLastVisualTokenBeforeInput(); + + expect(lastVisualToken).toEqual(document.querySelector('.filtered-search-token')); + expect(isLastVisualTokenValid).toEqual(false); + }); + }); + }); + + describe('getEndpointWithQueryParams', () => { + it('returns `endpoint` string as is when second param `endpointQueryParams` is undefined, null or empty string', () => { + const endpoint = 'foo/bar/-/labels.json'; + + expect(subject.getEndpointWithQueryParams(endpoint)).toBe(endpoint); + expect(subject.getEndpointWithQueryParams(endpoint, null)).toBe(endpoint); + expect(subject.getEndpointWithQueryParams(endpoint, '')).toBe(endpoint); + }); + + it('returns `endpoint` string with values of `endpointQueryParams`', () => { + const endpoint = 'foo/bar/-/labels.json'; + const singleQueryParams = '{"foo":"true"}'; + const multipleQueryParams = '{"foo":"true","bar":"true"}'; + + expect(subject.getEndpointWithQueryParams(endpoint, singleQueryParams)).toBe( + `${endpoint}?foo=true`, + ); + + expect(subject.getEndpointWithQueryParams(endpoint, multipleQueryParams)).toBe( + `${endpoint}?foo=true&bar=true`, + ); + }); + }); + + describe('unselectTokens', () => { + it('does nothing when there are no tokens', () => { + const beforeHTML = tokensContainer.innerHTML; + subject.unselectTokens(); + + expect(tokensContainer.innerHTML).toEqual(beforeHTML); + }); + + it('removes the selected class from buttons', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '=', '@author')} + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', '%123', true)} + `); + + const selected = tokensContainer.querySelector('.js-visual-token .selected'); + + expect(selected.classList.contains('selected')).toEqual(true); + + subject.unselectTokens(); + + expect(selected.classList.contains('selected')).toEqual(false); + }); + }); + + describe('selectToken', () => { + beforeEach(() => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${bugLabelToken.outerHTML} + ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')} + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', '~awesome')} + `); + }); + + it('removes the selected class if it has selected class', () => { + const firstTokenButton = tokensContainer.querySelector('.js-visual-token .selectable'); + firstTokenButton.classList.add('selected'); + + subject.selectToken(firstTokenButton); + + expect(firstTokenButton.classList.contains('selected')).toEqual(false); + }); + + describe('has no selected class', () => { + it('adds selected class', () => { + const firstTokenButton = tokensContainer.querySelector('.js-visual-token .selectable'); + + subject.selectToken(firstTokenButton); + + expect(firstTokenButton.classList.contains('selected')).toEqual(true); + }); + + it('removes selected class from other tokens', () => { + const tokenButtons = tokensContainer.querySelectorAll('.js-visual-token .selectable'); + tokenButtons[1].classList.add('selected'); + + subject.selectToken(tokenButtons[0]); + + expect(tokenButtons[0].classList.contains('selected')).toEqual(true); + expect(tokenButtons[1].classList.contains('selected')).toEqual(false); + }); + }); + }); + + describe('removeSelectedToken', () => { + it('does not remove when there are no selected tokens', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'none'), + ); + + expect(tokensContainer.querySelector('.js-visual-token .selectable')).not.toEqual(null); + + subject.removeSelectedToken(); + + expect(tokensContainer.querySelector('.js-visual-token .selectable')).not.toEqual(null); + }); + + it('removes selected token', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'none', true), + ); + + expect(tokensContainer.querySelector('.js-visual-token .selectable')).not.toEqual(null); + + subject.removeSelectedToken(); + + expect(tokensContainer.querySelector('.js-visual-token .selectable')).toEqual(null); + }); + }); + + describe('createVisualTokenElementHTML', () => { + let tokenElement; + + beforeEach(() => { + setFixtures(` + <div class="test-area"> + ${subject.createVisualTokenElementHTML('custom-token')} + </div> + `); + + tokenElement = document.querySelector('.test-area').firstElementChild; + }); + + it('should add class name to token element', () => { + expect(document.querySelector('.test-area .custom-token')).toBeDefined(); + }); + + it('contains name div', () => { + expect(tokenElement.querySelector('.name')).toEqual(expect.anything()); + }); + + it('contains value container div', () => { + expect(tokenElement.querySelector('.value-container')).toEqual(expect.anything()); + }); + + it('contains value div', () => { + expect(tokenElement.querySelector('.value-container .value')).toEqual(expect.anything()); + }); + + it('contains selectable class', () => { + expect(tokenElement.classList.contains('selectable')).toEqual(true); + }); + + it('contains button role', () => { + expect(tokenElement.getAttribute('role')).toEqual('button'); + }); + + describe('remove token', () => { + it('contains remove-token button', () => { + expect(tokenElement.querySelector('.value-container .remove-token')).toEqual( + expect.anything(), + ); + }); + + it('contains fa-close icon', () => { + expect(tokenElement.querySelector('.remove-token .fa-close')).toEqual(expect.anything()); + }); + }); + }); + + describe('addVisualTokenElement', () => { + it('renders search visual tokens', () => { + subject.addVisualTokenElement({ + name: 'search term', + operator: '=', + value: null, + options: { isSearchTerm: true }, + }); + const token = tokensContainer.querySelector('.js-visual-token'); + + expect(token.classList.contains('filtered-search-term')).toEqual(true); + expect(token.querySelector('.name').innerText).toEqual('search term'); + expect(token.querySelector('.operator').innerText).toEqual('='); + expect(token.querySelector('.value')).toEqual(null); + }); + + it('renders filter visual token name', () => { + subject.addVisualTokenElement({ name: 'milestone' }); + const token = tokensContainer.querySelector('.js-visual-token'); + + expect(token.classList.contains('search-token-milestone')).toEqual(true); + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').innerText).toEqual('milestone'); + expect(token.querySelector('.value')).toEqual(null); + }); + + it('renders filter visual token name, operator, and value', () => { + subject.addVisualTokenElement({ name: 'label', operator: '!=', value: 'Frontend' }); + const token = tokensContainer.querySelector('.js-visual-token'); + + expect(token.classList.contains('search-token-label')).toEqual(true); + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').innerText).toEqual('label'); + expect(token.querySelector('.operator').innerText).toEqual('!='); + expect(token.querySelector('.value').innerText).toEqual('Frontend'); + }); + + it('inserts visual token before input', () => { + tokensContainer.appendChild( + FilteredSearchSpecHelper.createFilterVisualToken('assignee', '=', '@root'), + ); + + subject.addVisualTokenElement({ name: 'label', operator: '!=', value: 'Frontend' }); + const tokens = tokensContainer.querySelectorAll('.js-visual-token'); + const labelToken = tokens[0]; + const assigneeToken = tokens[1]; + + expect(labelToken.classList.contains('search-token-label')).toEqual(true); + expect(labelToken.classList.contains('filtered-search-token')).toEqual(true); + expect(labelToken.querySelector('.name').innerText).toEqual('label'); + expect(labelToken.querySelector('.value').innerText).toEqual('Frontend'); + expect(labelToken.querySelector('.operator').innerText).toEqual('!='); + + expect(assigneeToken.classList.contains('search-token-assignee')).toEqual(true); + expect(assigneeToken.classList.contains('filtered-search-token')).toEqual(true); + expect(assigneeToken.querySelector('.name').innerText).toEqual('assignee'); + expect(assigneeToken.querySelector('.value').innerText).toEqual('@root'); + expect(assigneeToken.querySelector('.operator').innerText).toEqual('='); + }); + }); + + describe('addValueToPreviousVisualTokenElement', () => { + it('does not add when previous visual token element has no value', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '=', '@root'), + ); + + const original = tokensContainer.innerHTML; + subject.addValueToPreviousVisualTokenElement('value'); + + expect(original).toEqual(tokensContainer.innerHTML); + }); + + it('does not add when previous visual token element is a search', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '=', '@root')} + ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')} + `); + + const original = tokensContainer.innerHTML; + subject.addValueToPreviousVisualTokenElement('value'); + + expect(original).toEqual(tokensContainer.innerHTML); + }); + + it('adds value to previous visual filter token', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createNameOperatorFilterVisualTokenHTML('label', '='), + ); + + const original = tokensContainer.innerHTML; + subject.addValueToPreviousVisualTokenElement('value'); + const updatedToken = tokensContainer.querySelector('.js-visual-token'); + + expect(updatedToken.querySelector('.name').innerText).toEqual('label'); + expect(updatedToken.querySelector('.value').innerText).toEqual('value'); + expect(original).not.toEqual(tokensContainer.innerHTML); + }); + }); + + describe('addFilterVisualToken', () => { + it('creates visual token with just tokenName', () => { + subject.addFilterVisualToken('milestone'); + const token = tokensContainer.querySelector('.js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').innerText).toEqual('milestone'); + expect(token.querySelector('.operator')).toEqual(null); + expect(token.querySelector('.value')).toEqual(null); + }); + + it('creates visual token with just tokenValue', () => { + subject.addFilterVisualToken('milestone', '='); + subject.addFilterVisualToken('%8.17'); + const token = tokensContainer.querySelector('.js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').innerText).toEqual('milestone'); + expect(token.querySelector('.operator').innerText).toEqual('='); + expect(token.querySelector('.value').innerText).toEqual('%8.17'); + }); + + it('creates full visual token', () => { + subject.addFilterVisualToken('assignee', '=', '@john'); + const token = tokensContainer.querySelector('.js-visual-token'); + + expect(token.classList.contains('filtered-search-token')).toEqual(true); + expect(token.querySelector('.name').innerText).toEqual('assignee'); + expect(token.querySelector('.operator').innerText).toEqual('='); + expect(token.querySelector('.value').innerText).toEqual('@john'); + }); + }); + + describe('addSearchVisualToken', () => { + it('creates search visual token', () => { + subject.addSearchVisualToken('search term'); + const token = tokensContainer.querySelector('.js-visual-token'); + + expect(token.classList.contains('filtered-search-term')).toEqual(true); + expect(token.querySelector('.name').innerText).toEqual('search term'); + expect(token.querySelector('.value')).toEqual(null); + }); + + it('appends to previous search visual token if previous token was a search token', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('author', '=', '@root')} + ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search term')} + `); + + subject.addSearchVisualToken('append this'); + const token = tokensContainer.querySelector('.filtered-search-term'); + + expect(token.querySelector('.name').innerText).toEqual('search term append this'); + expect(token.querySelector('.value')).toEqual(null); + }); + }); + + describe('getLastTokenPartial', () => { + it('should get last token value', () => { + const value = '~bug'; + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + bugLabelToken.outerHTML, + ); + + expect(subject.getLastTokenPartial()).toEqual(value); + }); + + it('should get last token original value if available', () => { + const originalValue = '@user'; + const valueContainer = authorToken.querySelector('.value-container'); + valueContainer.dataset.originalValue = originalValue; + const avatar = document.createElement('img'); + const valueElement = valueContainer.querySelector('.value'); + valueElement.appendChild(avatar); + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + authorToken.outerHTML, + ); + + const lastTokenValue = subject.getLastTokenPartial(); + + expect(lastTokenValue).toEqual(originalValue); + }); + + it('should get last token name if there is no value', () => { + const name = 'assignee'; + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createNameFilterVisualTokenHTML(name), + ); + + expect(subject.getLastTokenPartial()).toEqual(name); + }); + + it('should return empty when there are no tokens', () => { + expect(subject.getLastTokenPartial()).toEqual(''); + }); + }); + + describe('removeLastTokenPartial', () => { + it('should remove the last token value if it exists', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createFilterVisualTokenHTML( + 'label', + '=', + '~"Community Contribution"', + ), + ); + + expect(tokensContainer.querySelector('.js-visual-token .value')).not.toEqual(null); + + subject.removeLastTokenPartial(); + + expect(tokensContainer.querySelector('.js-visual-token .value')).toEqual(null); + }); + + it('should remove the last token name if there is no value', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createNameFilterVisualTokenHTML('milestone'), + ); + + expect(tokensContainer.querySelector('.js-visual-token .name')).not.toEqual(null); + + subject.removeLastTokenPartial(); + + expect(tokensContainer.querySelector('.js-visual-token .name')).toEqual(null); + }); + + it('should not remove anything when there are no tokens', () => { + const html = tokensContainer.innerHTML; + subject.removeLastTokenPartial(); + + expect(tokensContainer.innerHTML).toEqual(html); + }); + }); + + describe('tokenizeInput', () => { + it('does not do anything if there is no input', () => { + const original = tokensContainer.innerHTML; + subject.tokenizeInput(); + + expect(tokensContainer.innerHTML).toEqual(original); + }); + + it('adds search visual token if previous visual token is valid', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createFilterVisualTokenHTML('assignee', '=', 'none'), + ); + + const input = document.querySelector('.filtered-search'); + input.value = 'some value'; + subject.tokenizeInput(); + + const newToken = tokensContainer.querySelector('.filtered-search-term'); + + expect(input.value).toEqual(''); + expect(newToken.querySelector('.name').innerText).toEqual('some value'); + expect(newToken.querySelector('.value')).toEqual(null); + }); + + it('adds value to previous visual token element if previous visual token is invalid', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createNameOperatorFilterVisualTokenHTML('assignee', '='), + ); + + const input = document.querySelector('.filtered-search'); + input.value = '@john'; + subject.tokenizeInput(); + + const updatedToken = tokensContainer.querySelector('.filtered-search-token'); + + expect(input.value).toEqual(''); + expect(updatedToken.querySelector('.name').innerText).toEqual('assignee'); + expect(updatedToken.querySelector('.operator').innerText).toEqual('='); + expect(updatedToken.querySelector('.value').innerText).toEqual('@john'); + }); + }); + + describe('editToken', () => { + let input; + let token; + + beforeEach(() => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', 'none')} + ${FilteredSearchSpecHelper.createSearchVisualTokenHTML('search')} + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', '=', 'upcoming')} + `); + + input = document.querySelector('.filtered-search'); + token = document.querySelector('.js-visual-token'); + }); + + it("tokenize's existing input", () => { + input.value = 'some text'; + jest.spyOn(subject, 'tokenizeInput'); + + subject.editToken(token); + + expect(subject.tokenizeInput).toHaveBeenCalled(); + expect(input.value).not.toEqual('some text'); + }); + + it('moves input to the token position', () => { + expect(tokensContainer.children[3].querySelector('.filtered-search')).not.toEqual(null); + + subject.editToken(token); + + expect(tokensContainer.children[1].querySelector('.filtered-search')).not.toEqual(null); + expect(tokensContainer.children[3].querySelector('.filtered-search')).toEqual(null); + }); + + it('input contains the visual token value', () => { + subject.editToken(token); + + expect(input.value).toEqual('none'); + }); + + it('input contains the original value if present', () => { + const originalValue = '@user'; + const valueContainer = token.querySelector('.value-container'); + valueContainer.dataset.originalValue = originalValue; + + subject.editToken(token); + + expect(input.value).toEqual(originalValue); + }); + + describe('selected token is a search term token', () => { + beforeEach(() => { + token = document.querySelector('.filtered-search-term'); + }); + + it('token is removed', () => { + expect(tokensContainer.querySelector('.filtered-search-term')).not.toEqual(null); + + subject.editToken(token); + + expect(tokensContainer.querySelector('.filtered-search-term')).toEqual(null); + }); + + it('input has the same value as removed token', () => { + expect(input.value).toEqual(''); + + subject.editToken(token); + + expect(input.value).toEqual('search'); + }); + }); + }); + + describe('moveInputTotheRight', () => { + it('does nothing if the input is already the right most element', () => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML( + FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', 'none'), + ); + + jest.spyOn(subject, 'tokenizeInput').mockImplementation(() => {}); + jest.spyOn(subject, 'getLastVisualTokenBeforeInput'); + + subject.moveInputToTheRight(); + + expect(subject.tokenizeInput).toHaveBeenCalled(); + expect(subject.getLastVisualTokenBeforeInput).not.toHaveBeenCalled(); + }); + + it("tokenize's input", () => { + tokensContainer.innerHTML = ` + ${FilteredSearchSpecHelper.createNameOperatorFilterVisualTokenHTML('label', '=')} + ${FilteredSearchSpecHelper.createInputHTML()} + ${bugLabelToken.outerHTML} + `; + + tokensContainer.querySelector('.filtered-search').value = 'none'; + + subject.moveInputToTheRight(); + const value = tokensContainer.querySelector('.js-visual-token .value'); + + expect(value.innerText).toEqual('none'); + }); + + it('converts input into search term token if last token is valid', () => { + tokensContainer.innerHTML = ` + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', 'none')} + ${FilteredSearchSpecHelper.createInputHTML()} + ${bugLabelToken.outerHTML} + `; + + document.querySelector('.filtered-search').value = 'test'; + + subject.moveInputToTheRight(); + const searchValue = tokensContainer.querySelector('.filtered-search-term .name'); + + expect(searchValue.innerText).toEqual('test'); + }); + + it('moves the input to the right most element', () => { + tokensContainer.innerHTML = ` + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', 'none')} + ${FilteredSearchSpecHelper.createInputHTML()} + ${bugLabelToken.outerHTML} + `; + + subject.moveInputToTheRight(); + + expect(tokensContainer.children[2].querySelector('.filtered-search')).not.toEqual(null); + }); + + it('tokenizes input even if input is the right most element', () => { + tokensContainer.innerHTML = ` + ${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '=', 'none')} + ${FilteredSearchSpecHelper.createNameOperatorFilterVisualTokenHTML('label')} + ${FilteredSearchSpecHelper.createInputHTML('', '~bug')} + `; + + subject.moveInputToTheRight(); + + const token = tokensContainer.children[1]; + + expect(token.querySelector('.value').innerText).toEqual('~bug'); + }); + }); + + describe('renderVisualTokenValue', () => { + beforeEach(() => { + tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(` + ${authorToken.outerHTML} + ${bugLabelToken.outerHTML} + `); + }); + + it('renders a author token value element', () => { + const { tokenNameElement, tokenValueElement } = findElements(authorToken); + const tokenName = tokenNameElement.textContent; + const tokenValue = 'new value'; + + subject.renderVisualTokenValue(authorToken, tokenName, tokenValue); + + jest.runOnlyPendingTimers(); + + setImmediate(() => { + expect(tokenValueElement.textContent).toBe(tokenValue); + }); + }); + }); +}); |