gitlab-org--gitlab-foss/spec/javascripts/filtered_search/filtered_search_manager_spe...

376 lines
12 KiB
JavaScript
Raw Normal View History

2017-05-05 17:59:41 +00:00
import * as recentSearchesStoreSrc from '~/filtered_search/stores/recent_searches_store';
import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesServiceError from '~/filtered_search/services/recent_searches_service_error';
2017-06-20 08:05:55 +00:00
import RecentSearchesRoot from '~/filtered_search/recent_searches_root';
import '~/lib/utils/url_utility';
import '~/lib/utils/common_utils';
import '~/filtered_search/filtered_search_token_keys';
import '~/filtered_search/filtered_search_tokenizer';
import '~/filtered_search/filtered_search_dropdown_manager';
import '~/filtered_search/filtered_search_manager';
import FilteredSearchSpecHelper from '../helpers/filtered_search_spec_helper';
2017-01-24 17:57:39 +00:00
describe('Filtered Search Manager', () => {
let input;
let manager;
let tokensContainer;
const placeholder = 'Search or filter results...';
function dispatchBackspaceEvent(element, eventType) {
const backspaceKey = 8;
const event = new Event(eventType);
event.keyCode = backspaceKey;
element.dispatchEvent(event);
}
function dispatchDeleteEvent(element, eventType) {
const deleteKey = 46;
const event = new Event(eventType);
event.keyCode = deleteKey;
element.dispatchEvent(event);
}
function getVisualTokens() {
return tokensContainer.querySelectorAll('.js-visual-token');
}
beforeEach(() => {
setFixtures(`
<div class="filtered-search-box">
<form>
<ul class="tokens-container list-unstyled">
${FilteredSearchSpecHelper.createInputHTML(placeholder)}
</ul>
<button class="clear-search" type="button">
<i class="fa fa-times"></i>
</button>
</form>
</div>
`);
spyOn(gl.FilteredSearchManager.prototype, 'loadSearchParamsFromURL').and.callFake(() => {});
spyOn(gl.FilteredSearchManager.prototype, 'tokenChange').and.callFake(() => {});
spyOn(gl.FilteredSearchDropdownManager.prototype, 'setDropdown').and.callFake(() => {});
spyOn(gl.FilteredSearchDropdownManager.prototype, 'updateDropdownOffset').and.callFake(() => {});
spyOn(gl.utils, 'getParameterByName').and.returnValue(null);
spyOn(gl.FilteredSearchVisualTokens, 'unselectTokens').and.callThrough();
input = document.querySelector('.filtered-search');
tokensContainer = document.querySelector('.tokens-container');
manager = new gl.FilteredSearchManager();
manager.setup();
});
2017-01-24 17:57:39 +00:00
afterEach(() => {
manager.cleanup();
});
2017-01-24 17:57:39 +00:00
2017-05-05 17:59:41 +00:00
describe('class constructor', () => {
const isLocalStorageAvailable = 'isLocalStorageAvailable';
let filteredSearchManager;
beforeEach(() => {
spyOn(RecentSearchesService, 'isAvailable').and.returnValue(isLocalStorageAvailable);
spyOn(recentSearchesStoreSrc, 'default');
2017-06-20 08:05:55 +00:00
spyOn(RecentSearchesRoot.prototype, 'render');
2017-05-05 17:59:41 +00:00
filteredSearchManager = new gl.FilteredSearchManager();
filteredSearchManager.setup();
2017-05-05 17:59:41 +00:00
return filteredSearchManager;
});
it('should instantiate RecentSearchesStore with isLocalStorageAvailable', () => {
expect(RecentSearchesService.isAvailable).toHaveBeenCalled();
expect(recentSearchesStoreSrc.default).toHaveBeenCalledWith({
isLocalStorageAvailable,
2017-05-23 16:35:54 +00:00
allowedKeys: gl.FilteredSearchTokenKeys.getKeys(),
2017-05-05 17:59:41 +00:00
});
});
it('should not instantiate Flash if an RecentSearchesServiceError is caught', () => {
spyOn(RecentSearchesService.prototype, 'fetch').and.callFake(() => Promise.reject(new RecentSearchesServiceError()));
spyOn(window, 'Flash');
filteredSearchManager = new gl.FilteredSearchManager();
filteredSearchManager.setup();
2017-05-05 17:59:41 +00:00
expect(window.Flash).not.toHaveBeenCalled();
});
});
2017-06-06 23:18:31 +00:00
describe('searchState', () => {
beforeEach(() => {
spyOn(gl.FilteredSearchManager.prototype, 'search').and.callFake(() => {});
});
it('should blur button', () => {
const e = {
currentTarget: {
blur: () => {},
},
};
spyOn(e.currentTarget, 'blur').and.callThrough();
manager.searchState(e);
expect(e.currentTarget.blur).toHaveBeenCalled();
});
it('should not call search if there is no state', () => {
const e = {
currentTarget: {
blur: () => {},
},
};
manager.searchState(e);
expect(gl.FilteredSearchManager.prototype.search).not.toHaveBeenCalled();
});
it('should call search when there is state', () => {
const e = {
currentTarget: {
blur: () => {},
2017-06-07 15:54:54 +00:00
dataset: {
state: 'opened',
},
2017-06-06 23:18:31 +00:00
},
};
manager.searchState(e);
expect(gl.FilteredSearchManager.prototype.search).toHaveBeenCalledWith('opened');
});
});
describe('search', () => {
const defaultParams = '?scope=all&utf8=%E2%9C%93&state=opened';
2017-01-24 17:57:39 +00:00
it('should search with a single word', (done) => {
input.value = 'searchTerm';
2017-01-24 17:57:39 +00:00
spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=searchTerm`);
done();
});
2017-01-24 17:57:39 +00:00
manager.search();
});
it('should search with multiple words', (done) => {
input.value = 'awesome search terms';
2017-01-24 17:57:39 +00:00
spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=awesome+search+terms`);
done();
2017-01-24 17:57:39 +00:00
});
manager.search();
});
2017-01-24 17:57:39 +00:00
it('should search with special characters', (done) => {
input.value = '~!@#$%^&*()_+{}:<>,.?/';
2017-01-24 17:57:39 +00:00
spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&search=~!%40%23%24%25%5E%26*()_%2B%7B%7D%3A%3C%3E%2C.%3F%2F`);
done();
2017-01-24 17:57:39 +00:00
});
manager.search();
});
2017-01-24 17:57:39 +00:00
it('removes duplicated tokens', (done) => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(`
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
${FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug')}
`);
2017-01-24 17:57:39 +00:00
spyOn(gl.utils, 'visitUrl').and.callFake((url) => {
expect(url).toEqual(`${defaultParams}&label_name[]=bug`);
done();
2017-01-24 17:57:39 +00:00
});
manager.search();
});
});
describe('handleInputPlaceholder', () => {
it('should render placeholder when there is no input', () => {
expect(input.placeholder).toEqual(placeholder);
2017-01-24 17:57:39 +00:00
});
2017-01-30 22:53:18 +00:00
it('should not render placeholder when there is input', () => {
input.value = 'test words';
const event = new Event('input');
input.dispatchEvent(event);
2017-01-30 22:53:18 +00:00
expect(input.placeholder).toEqual('');
});
2017-01-30 22:53:18 +00:00
it('should not render placeholder when there are tokens and no input', () => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug'),
);
2017-01-30 22:53:18 +00:00
const event = new Event('input');
input.dispatchEvent(event);
2017-01-30 22:53:18 +00:00
expect(input.placeholder).toEqual('');
});
});
describe('checkForBackspace', () => {
describe('tokens and no input', () => {
beforeEach(() => {
2017-01-30 22:53:18 +00:00
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('label', '~bug'),
);
});
it('removes last token', () => {
spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
dispatchBackspaceEvent(input, 'keyup');
2017-01-30 22:53:18 +00:00
expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).toHaveBeenCalled();
2017-01-30 22:53:18 +00:00
});
it('sets the input', () => {
2017-01-30 22:53:18 +00:00
spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
dispatchDeleteEvent(input, 'keyup');
expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).toHaveBeenCalled();
expect(input.value).toEqual('~bug');
2017-01-30 22:53:18 +00:00
});
});
it('does not remove token or change input when there is existing input', () => {
spyOn(gl.FilteredSearchVisualTokens, 'removeLastTokenPartial').and.callThrough();
spyOn(gl.FilteredSearchVisualTokens, 'getLastTokenPartial').and.callThrough();
2017-01-30 22:53:18 +00:00
input.value = 'text';
dispatchDeleteEvent(input, 'keyup');
2017-01-30 22:53:18 +00:00
expect(gl.FilteredSearchVisualTokens.removeLastTokenPartial).not.toHaveBeenCalled();
expect(gl.FilteredSearchVisualTokens.getLastTokenPartial).not.toHaveBeenCalled();
expect(input.value).toEqual('text');
});
});
2017-01-30 22:53:18 +00:00
describe('removeToken', () => {
it('removes token even when it is already selected', () => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
);
tokensContainer.querySelector('.js-visual-token .remove-token').click();
expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null);
});
2017-01-30 22:53:18 +00:00
describe('unselected token', () => {
beforeEach(() => {
spyOn(gl.FilteredSearchManager.prototype, 'removeSelectedToken').and.callThrough();
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none'),
);
tokensContainer.querySelector('.js-visual-token .remove-token').click();
});
it('removes token when remove button is selected', () => {
expect(tokensContainer.querySelector('.js-visual-token')).toEqual(null);
});
it('calls removeSelectedToken', () => {
expect(manager.removeSelectedToken).toHaveBeenCalled();
});
});
});
describe('removeSelectedTokenKeydown', () => {
beforeEach(() => {
tokensContainer.innerHTML = FilteredSearchSpecHelper.createTokensContainerHTML(
FilteredSearchSpecHelper.createFilterVisualTokenHTML('milestone', 'none', true),
);
});
2017-01-30 22:53:18 +00:00
it('removes selected token when the backspace key is pressed', () => {
expect(getVisualTokens().length).toEqual(1);
2017-01-30 22:53:18 +00:00
dispatchBackspaceEvent(document, 'keydown');
2017-01-30 22:53:18 +00:00
expect(getVisualTokens().length).toEqual(0);
});
2017-01-30 22:53:18 +00:00
it('removes selected token when the delete key is pressed', () => {
expect(getVisualTokens().length).toEqual(1);
2017-01-30 22:53:18 +00:00
dispatchDeleteEvent(document, 'keydown');
2017-01-30 22:53:18 +00:00
expect(getVisualTokens().length).toEqual(0);
});
2017-01-30 22:53:18 +00:00
it('updates the input placeholder after removal', () => {
manager.handleInputPlaceholder();
2017-01-30 22:53:18 +00:00
expect(input.placeholder).toEqual('');
expect(getVisualTokens().length).toEqual(1);
2017-01-30 22:53:18 +00:00
dispatchBackspaceEvent(document, 'keydown');
2017-01-30 22:53:18 +00:00
expect(input.placeholder).not.toEqual('');
expect(getVisualTokens().length).toEqual(0);
});
2017-01-30 22:53:18 +00:00
it('updates the clear button after removal', () => {
manager.toggleClearSearchButton();
2017-01-30 22:53:18 +00:00
const clearButton = document.querySelector('.clear-search');
expect(clearButton.classList.contains('hidden')).toEqual(false);
expect(getVisualTokens().length).toEqual(1);
dispatchBackspaceEvent(document, 'keydown');
expect(clearButton.classList.contains('hidden')).toEqual(true);
expect(getVisualTokens().length).toEqual(0);
2017-01-30 22:53:18 +00:00
});
});
2017-01-30 22:53:18 +00:00
describe('removeSelectedToken', () => {
beforeEach(() => {
spyOn(gl.FilteredSearchVisualTokens, 'removeSelectedToken').and.callThrough();
spyOn(gl.FilteredSearchManager.prototype, 'handleInputPlaceholder').and.callThrough();
spyOn(gl.FilteredSearchManager.prototype, 'toggleClearSearchButton').and.callThrough();
manager.removeSelectedToken();
});
it('calls FilteredSearchVisualTokens.removeSelectedToken', () => {
expect(gl.FilteredSearchVisualTokens.removeSelectedToken).toHaveBeenCalled();
});
it('calls handleInputPlaceholder', () => {
expect(manager.handleInputPlaceholder).toHaveBeenCalled();
});
it('calls toggleClearSearchButton', () => {
expect(manager.toggleClearSearchButton).toHaveBeenCalled();
});
it('calls update dropdown offset', () => {
expect(manager.dropdownManager.updateDropdownOffset).toHaveBeenCalled();
});
});
describe('toggleInputContainerFocus', () => {
it('toggles on focus', () => {
input.focus();
expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(true);
});
it('toggles on blur', () => {
input.blur();
expect(document.querySelector('.filtered-search-box').classList.contains('focus')).toEqual(false);
});
2017-01-24 17:57:39 +00:00
});
});