Skip to content
Snippets Groups Projects
Verified Commit 5b783fd9 authored by Alexander Turinske's avatar Alexander Turinske :gay_pride_flag: Committed by GitLab
Browse files

Update filtered search bar tests for vue 3

- bring them partially up to current standards
- remove vue 3 deprecated patterns
- reimplement !144947
- !145070
parent 19dcf177
No related branches found
No related tags found
1 merge request!145070Update filtered search bar tests for vue 3
import { GlDropdownItem, GlSorting, GlFilteredSearch, GlFormCheckbox } from '@gitlab/ui'; import { GlDropdownItem, GlSorting, GlFilteredSearch, GlFormCheckbox } from '@gitlab/ui';
import { shallowMount, mount } from '@vue/test-utils'; import { shallowMount, mount } from '@vue/test-utils';
import { nextTick } from 'vue'; import { nextTick } from 'vue';
import { useLocalStorageSpy } from 'helpers/local_storage_helper';
import waitForPromises from 'helpers/wait_for_promises';
import RecentSearchesService from '~/filtered_search/services/recent_searches_service'; import RecentSearchesService from '~/filtered_search/services/recent_searches_service';
import RecentSearchesStore from '~/filtered_search/stores/recent_searches_store'; import RecentSearchesStore from '~/filtered_search/stores/recent_searches_store';
import { import {
...@@ -34,45 +36,39 @@ jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils', ( ...@@ -34,45 +36,39 @@ jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils', (
).filterEmptySearchTerm, ).filterEmptySearchTerm,
})); }));
const createComponent = ({ const defaultProps = {
shallow = true, namespace: 'gitlab-org/gitlab-test',
namespace = 'gitlab-org/gitlab-test', recentSearchesStorageKey: 'issues',
recentSearchesStorageKey = 'requirements', tokens: mockAvailableTokens,
tokens = mockAvailableTokens, initialFilterValue: [],
sortOptions, showCheckbox: false,
initialSortBy, checkboxChecked: false,
initialFilterValue = [], searchInputPlaceholder: 'Filter requirements',
showCheckbox = false,
checkboxChecked = false,
searchInputPlaceholder = 'Filter requirements',
} = {}) => {
const mountMethod = shallow ? shallowMount : mount;
return mountMethod(FilteredSearchBarRoot, {
propsData: {
namespace,
recentSearchesStorageKey,
tokens,
sortOptions,
initialSortBy,
initialFilterValue,
showCheckbox,
checkboxChecked,
searchInputPlaceholder,
},
});
}; };
describe('FilteredSearchBarRoot', () => { describe('FilteredSearchBarRoot', () => {
useLocalStorageSpy();
let wrapper; let wrapper;
const createComponent = ({ shallow = true, propsData = {} } = {}) => {
const mountMethod = shallow ? shallowMount : mount;
wrapper = mountMethod(FilteredSearchBarRoot, { propsData: { ...defaultProps, ...propsData } });
};
const findGlSorting = () => wrapper.findComponent(GlSorting); const findGlSorting = () => wrapper.findComponent(GlSorting);
const findGlFilteredSearch = () => wrapper.findComponent(GlFilteredSearch); const findGlFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
const findGlFormCheckbox = () => wrapper.findComponent(GlFormCheckbox);
const findGlDropdownItem = () => wrapper.findComponent(GlDropdownItem);
afterEach(() => {
localStorage.clear();
});
describe('data', () => { describe('data', () => {
describe('when `sortOptions` are provided', () => { describe('when `sortOptions` are provided', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ sortOptions: mockSortOptions }); createComponent({ propsData: { sortOptions: mockSortOptions } });
}); });
it('sets a correct initial value for GlFilteredSearch', () => { it('sets a correct initial value for GlFilteredSearch', () => {
...@@ -95,7 +91,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -95,7 +91,7 @@ describe('FilteredSearchBarRoot', () => {
}); });
it('does not initialize the sort dropdown when `sortOptions` are not provided', () => { it('does not initialize the sort dropdown when `sortOptions` are not provided', () => {
wrapper = createComponent(); createComponent();
expect(findGlSorting().exists()).toBe(false); expect(findGlSorting().exists()).toBe(false);
}); });
...@@ -104,6 +100,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -104,6 +100,7 @@ describe('FilteredSearchBarRoot', () => {
describe('computed', () => { describe('computed', () => {
describe('tokenSymbols', () => { describe('tokenSymbols', () => {
it('returns a map containing type and symbols from `tokens` prop', () => { it('returns a map containing type and symbols from `tokens` prop', () => {
createComponent();
expect(wrapper.vm.tokenSymbols).toEqual({ expect(wrapper.vm.tokenSymbols).toEqual({
[TOKEN_TYPE_AUTHOR]: '@', [TOKEN_TYPE_AUTHOR]: '@',
[TOKEN_TYPE_LABEL]: '~', [TOKEN_TYPE_LABEL]: '~',
...@@ -114,6 +111,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -114,6 +111,7 @@ describe('FilteredSearchBarRoot', () => {
describe('tokenTitles', () => { describe('tokenTitles', () => {
it('returns a map containing type and title from `tokens` prop', () => { it('returns a map containing type and title from `tokens` prop', () => {
createComponent();
expect(wrapper.vm.tokenTitles).toEqual({ expect(wrapper.vm.tokenTitles).toEqual({
[TOKEN_TYPE_AUTHOR]: 'Author', [TOKEN_TYPE_AUTHOR]: 'Author',
[TOKEN_TYPE_LABEL]: 'Label', [TOKEN_TYPE_LABEL]: 'Label',
...@@ -124,7 +122,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -124,7 +122,7 @@ describe('FilteredSearchBarRoot', () => {
describe('sortDirectionIcon', () => { describe('sortDirectionIcon', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ sortOptions: mockSortOptions }); createComponent({ propsData: { sortOptions: mockSortOptions } });
}); });
it('passes isAscending=false to GlSorting by default', () => { it('passes isAscending=false to GlSorting by default', () => {
...@@ -141,7 +139,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -141,7 +139,7 @@ describe('FilteredSearchBarRoot', () => {
describe('filteredRecentSearches', () => { describe('filteredRecentSearches', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent(); createComponent();
}); });
it('returns array of recent searches filtering out any string type (unsupported) items', async () => { it('returns array of recent searches filtering out any string type (unsupported) items', async () => {
...@@ -187,7 +185,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -187,7 +185,7 @@ describe('FilteredSearchBarRoot', () => {
describe('events', () => { describe('events', () => {
it('emits component event `onFilter` with empty array and true when initially selected filter value was cleared', async () => { it('emits component event `onFilter` with empty array and true when initially selected filter value was cleared', async () => {
wrapper = createComponent({ initialFilterValue: [tokenValueLabel] }); createComponent({ propsData: { initialFilterValue: [tokenValueLabel] } });
wrapper.findComponent(GlFilteredSearch).vm.$emit('clear'); wrapper.findComponent(GlFilteredSearch).vm.$emit('clear');
...@@ -199,15 +197,16 @@ describe('FilteredSearchBarRoot', () => { ...@@ -199,15 +197,16 @@ describe('FilteredSearchBarRoot', () => {
describe('methods', () => { describe('methods', () => {
describe('setupRecentSearch', () => { describe('setupRecentSearch', () => {
it('initializes `recentSearchesService` and `recentSearchesStore` props when `recentSearchesStorageKey` is available', () => { it('initializes `recentSearchesService` and `recentSearchesStore` props when `recentSearchesStorageKey` is available', () => {
createComponent();
expect(wrapper.vm.recentSearchesService instanceof RecentSearchesService).toBe(true); expect(wrapper.vm.recentSearchesService instanceof RecentSearchesService).toBe(true);
expect(wrapper.vm.recentSearchesStore instanceof RecentSearchesStore).toBe(true); expect(wrapper.vm.recentSearchesStore instanceof RecentSearchesStore).toBe(true);
}); });
it('initializes `recentSearchesPromise` prop with a promise by using `recentSearchesService.fetch()`', () => { it('initializes `recentSearchesPromise` prop with a promise by using `recentSearchesService.fetch()`', () => {
jest.spyOn(wrapper.vm.recentSearchesService, 'fetch').mockResolvedValue([]); expect(localStorage.setItem).not.toHaveBeenCalled();
createComponent();
wrapper.vm.setupRecentSearch();
expect(localStorage.setItem).toHaveBeenCalledWith('canUseLocalStorage', 'true');
expect(wrapper.vm.recentSearchesPromise instanceof Promise).toBe(true); expect(wrapper.vm.recentSearchesPromise instanceof Promise).toBe(true);
}); });
}); });
...@@ -216,6 +215,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -216,6 +215,7 @@ describe('FilteredSearchBarRoot', () => {
const mockFilters = [tokenValueAuthor, tokenValueLabel, tokenValueConfidential, 'foo']; const mockFilters = [tokenValueAuthor, tokenValueLabel, tokenValueConfidential, 'foo'];
it('returns filter array with unescaped strings for values which have spaces', () => { it('returns filter array with unescaped strings for values which have spaces', () => {
createComponent();
expect(wrapper.vm.removeQuotesEnclosure(mockFilters)).toEqual([ expect(wrapper.vm.removeQuotesEnclosure(mockFilters)).toEqual([
tokenValueAuthor, tokenValueAuthor,
tokenValueLabel, tokenValueLabel,
...@@ -227,7 +227,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -227,7 +227,7 @@ describe('FilteredSearchBarRoot', () => {
describe('handleSortOptionChange', () => { describe('handleSortOptionChange', () => {
it('emits component event `onSort` with selected sort by value', async () => { it('emits component event `onSort` with selected sort by value', async () => {
wrapper = createComponent({ sortOptions: mockSortOptions }); createComponent({ propsData: { sortOptions: mockSortOptions } });
findGlSorting().vm.$emit('sortByChange', mockSortOptions[1].id); findGlSorting().vm.$emit('sortByChange', mockSortOptions[1].id);
await nextTick(); await nextTick();
...@@ -239,9 +239,11 @@ describe('FilteredSearchBarRoot', () => { ...@@ -239,9 +239,11 @@ describe('FilteredSearchBarRoot', () => {
describe('handleSortDirectionChange', () => { describe('handleSortDirectionChange', () => {
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ createComponent({
sortOptions: mockSortOptions, propsData: {
initialSortBy: mockSortOptions[0].sortDirection.descending, sortOptions: mockSortOptions,
initialSortBy: mockSortOptions[0].sortDirection.descending,
},
}); });
}); });
...@@ -263,24 +265,27 @@ describe('FilteredSearchBarRoot', () => { ...@@ -263,24 +265,27 @@ describe('FilteredSearchBarRoot', () => {
describe('handleHistoryItemSelected', () => { describe('handleHistoryItemSelected', () => {
it('emits `onFilter` event with provided filters param', () => { it('emits `onFilter` event with provided filters param', () => {
jest.spyOn(wrapper.vm, 'removeQuotesEnclosure'); createComponent();
expect(wrapper.emitted('onFilter')).toEqual(undefined);
wrapper.vm.handleHistoryItemSelected(mockHistoryItems[0]); findGlFilteredSearch().vm.$emit('history-item-selected', mockHistoryItems[0]);
expect(wrapper.emitted('onFilter')[0]).toEqual([mockHistoryItems[0]]); expect(wrapper.emitted('onFilter')[0]).toEqual([mockHistoryItems[0]]);
expect(wrapper.vm.removeQuotesEnclosure).toHaveBeenCalledWith(mockHistoryItems[0]);
}); });
}); });
describe('handleClearHistory', () => { describe('handleClearHistory', () => {
it('clears search history from recent searches store', () => { it('clears search history from recent searches store', () => {
createComponent();
jest.spyOn(wrapper.vm.recentSearchesStore, 'setRecentSearches').mockReturnValue([]); jest.spyOn(wrapper.vm.recentSearchesStore, 'setRecentSearches').mockReturnValue([]);
jest.spyOn(wrapper.vm.recentSearchesService, 'save');
wrapper.vm.handleClearHistory(); expect(localStorage.setItem).toHaveBeenCalledTimes(2);
findGlFilteredSearch().vm.$emit('clear-history');
expect(wrapper.vm.recentSearchesStore.setRecentSearches).toHaveBeenCalledWith([]); expect(wrapper.vm.recentSearchesStore.setRecentSearches).toHaveBeenCalledWith([]);
expect(wrapper.vm.recentSearchesService.save).toHaveBeenCalledWith([]); expect(localStorage.setItem).toHaveBeenCalledTimes(4);
expect(localStorage.setItem).toHaveBeenLastCalledWith(
'gitlab-org/gitlab-test-issue-recent-searches',
'[]',
);
expect(wrapper.vm.recentSearches).toEqual([]); expect(wrapper.vm.recentSearches).toEqual([]);
}); });
}); });
...@@ -289,7 +294,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -289,7 +294,7 @@ describe('FilteredSearchBarRoot', () => {
const mockFilters = [tokenValueAuthor, 'foo']; const mockFilters = [tokenValueAuthor, 'foo'];
beforeEach(async () => { beforeEach(async () => {
wrapper = createComponent(); createComponent();
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
...@@ -319,25 +324,27 @@ describe('FilteredSearchBarRoot', () => { ...@@ -319,25 +324,27 @@ describe('FilteredSearchBarRoot', () => {
}); });
it('calls `recentSearchesService.save` with array of searches', async () => { it('calls `recentSearchesService.save` with array of searches', async () => {
jest.spyOn(wrapper.vm.recentSearchesService, 'save'); expect(localStorage.setItem).toHaveBeenCalledTimes(4);
findGlFilteredSearch().vm.$emit('submit');
wrapper.vm.handleFilterSubmit(); await waitForPromises();
await nextTick();
return wrapper.vm.recentSearchesPromise.then(() => { expect(localStorage.setItem).toHaveBeenCalledTimes(6);
expect(wrapper.vm.recentSearchesService.save).toHaveBeenCalledWith([mockFilters]); expect(localStorage.setItem).toHaveBeenLastCalledWith(
}); 'gitlab-org/gitlab-test-issue-recent-searches',
JSON.stringify([mockFilters]),
);
}); });
it('sets `recentSearches` data prop with array of searches', () => { it('sets `recentSearches` data prop with array of searches', async () => {
jest.spyOn(wrapper.vm.recentSearchesService, 'save'); expect(localStorage.setItem).toHaveBeenCalledTimes(4);
findGlFilteredSearch().vm.$emit('submit');
wrapper.vm.handleFilterSubmit(); await waitForPromises();
return wrapper.vm.recentSearchesPromise.then(() => { expect(localStorage.setItem).toHaveBeenCalledTimes(6);
expect(wrapper.vm.recentSearches).toEqual([mockFilters]); expect(localStorage.setItem).toHaveBeenLastCalledWith(
}); 'gitlab-org/gitlab-test-issue-recent-searches',
JSON.stringify([mockFilters]),
);
}); });
it('calls `blurSearchInput` method to remove focus from filter input field', () => { it('calls `blurSearchInput` method to remove focus from filter input field', () => {
...@@ -349,20 +356,18 @@ describe('FilteredSearchBarRoot', () => { ...@@ -349,20 +356,18 @@ describe('FilteredSearchBarRoot', () => {
}); });
it('emits component event `onFilter` with provided filters param', async () => { it('emits component event `onFilter` with provided filters param', async () => {
jest.spyOn(wrapper.vm, 'removeQuotesEnclosure'); expect(wrapper.emitted('onFilter')).toEqual(undefined);
findGlFilteredSearch().vm.$emit('submit'); findGlFilteredSearch().vm.$emit('submit');
await nextTick(); await nextTick();
expect(wrapper.emitted('onFilter')[0]).toEqual([mockFilters]); expect(wrapper.emitted('onFilter')[0]).toEqual([mockFilters]);
expect(wrapper.vm.removeQuotesEnclosure).toHaveBeenCalledWith(mockFilters);
}); });
}); });
}); });
describe('template', () => { describe('template', () => {
it('renders gl-filtered-search component', async () => { it('renders gl-filtered-search component', async () => {
wrapper = createComponent(); createComponent();
// setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details
// eslint-disable-next-line no-restricted-syntax // eslint-disable-next-line no-restricted-syntax
await wrapper.setData({ await wrapper.setData({
...@@ -376,89 +381,70 @@ describe('FilteredSearchBarRoot', () => { ...@@ -376,89 +381,70 @@ describe('FilteredSearchBarRoot', () => {
expect(glFilteredSearchEl.props('historyItems')).toEqual(mockHistoryItems); expect(glFilteredSearchEl.props('historyItems')).toEqual(mockHistoryItems);
}); });
it('renders checkbox when `showCheckbox` prop is true', () => { it('renders unchecked checkbox when `showCheckbox` prop is true', () => {
let wrapperWithCheckbox = createComponent({ createComponent({ propsData: { showCheckbox: true } });
showCheckbox: true, expect(findGlFormCheckbox().exists()).toBe(true);
}); expect(findGlFormCheckbox().attributes('checked')).not.toBeDefined();
});
expect(wrapperWithCheckbox.findComponent(GlFormCheckbox).exists()).toBe(true);
expect(
wrapperWithCheckbox.findComponent(GlFormCheckbox).attributes('checked'),
).not.toBeDefined();
wrapperWithCheckbox.destroy();
wrapperWithCheckbox = createComponent({
showCheckbox: true,
checkboxChecked: true,
});
expect(wrapperWithCheckbox.findComponent(GlFormCheckbox).attributes('checked')).toBe('true');
wrapperWithCheckbox.destroy(); it('renders checked checkbox when `checkboxChecked` prop is true', () => {
createComponent({ propsData: { showCheckbox: true, checkboxChecked: true } });
expect(findGlFormCheckbox().attributes('checked')).toBe('true');
}); });
it('renders search history items dropdown with formatting done using token symbols', async () => { it('renders search history items dropdown with formatting done using token symbols', async () => {
const wrapperFullMount = createComponent({ sortOptions: mockSortOptions, shallow: false }); createComponent({ propsData: { sortOptions: mockSortOptions }, shallow: false });
wrapperFullMount.vm.recentSearchesStore.addRecentSearch(mockHistoryItems[0]); wrapper.vm.recentSearchesStore.addRecentSearch(mockHistoryItems[0]);
await nextTick(); await nextTick();
const searchHistoryItemsEl = wrapperFullMount.findAll( const searchHistoryItemsEl = wrapper.findAll(
'.gl-search-box-by-click-menu .gl-search-box-by-click-history-item', '.gl-search-box-by-click-menu .gl-search-box-by-click-history-item',
); );
expect(searchHistoryItemsEl.at(0).text()).toBe( expect(searchHistoryItemsEl.at(0).text()).toBe(
'Author := @rootLabel := ~bugMilestone := %v1.0"duo"', 'Author := @rootLabel := ~bugMilestone := %v1.0"duo"',
); );
wrapperFullMount.destroy();
}); });
describe('when token options have `title` attribute defined', () => { describe('when token options have `title` attribute defined', () => {
it('renders search history items using the provided `title` attribute', async () => { it('renders search history items using the provided `title` attribute', async () => {
const wrapperFullMount = createComponent({ createComponent({
sortOptions: mockSortOptions, propsData: {
tokens: [mockMembershipToken], sortOptions: mockSortOptions,
tokens: [mockMembershipToken],
},
shallow: false, shallow: false,
}); });
wrapperFullMount.vm.recentSearchesStore.addRecentSearch([tokenValueMembership]); wrapper.vm.recentSearchesStore.addRecentSearch([tokenValueMembership]);
await nextTick(); await nextTick();
expect(findGlDropdownItem().text()).toBe('Membership := Direct');
expect(wrapperFullMount.findComponent(GlDropdownItem).text()).toBe('Membership := Direct');
wrapperFullMount.destroy();
}); });
}); });
describe('when token options have do not have `title` attribute defined', () => { describe('when token options have do not have `title` attribute defined', () => {
it('renders search history items using the provided `value` attribute', async () => { it('renders search history items using the provided `value` attribute', async () => {
const wrapperFullMount = createComponent({ createComponent({
sortOptions: mockSortOptions, propsData: {
tokens: [mockMembershipTokenOptionsWithoutTitles], sortOptions: mockSortOptions,
tokens: [mockMembershipTokenOptionsWithoutTitles],
},
shallow: false, shallow: false,
}); });
wrapper.vm.recentSearchesStore.addRecentSearch([tokenValueMembership]);
wrapperFullMount.vm.recentSearchesStore.addRecentSearch([tokenValueMembership]);
await nextTick(); await nextTick();
expect(findGlDropdownItem().text()).toBe('Membership := exclude');
expect(wrapperFullMount.findComponent(GlDropdownItem).text()).toBe('Membership := exclude');
wrapperFullMount.destroy();
}); });
}); });
it('renders sort dropdown component', () => { it('renders sort dropdown component', () => {
wrapper = createComponent({ sortOptions: mockSortOptions }); createComponent({ propsData: { sortOptions: mockSortOptions } });
expect(findGlSorting().exists()).toBe(true); expect(findGlSorting().exists()).toBe(true);
}); });
it('renders sort dropdown items', () => { it('renders sort dropdown items', () => {
wrapper = createComponent({ sortOptions: mockSortOptions }); createComponent({ propsData: { sortOptions: mockSortOptions } });
const { sortOptions, sortBy } = findGlSorting().props(); const { sortOptions, sortBy } = findGlSorting().props();
...@@ -485,7 +471,7 @@ describe('FilteredSearchBarRoot', () => { ...@@ -485,7 +471,7 @@ describe('FilteredSearchBarRoot', () => {
}; };
beforeEach(() => { beforeEach(() => {
wrapper = createComponent({ sortOptions: mockSortOptions }); createComponent({ propsData: { sortOptions: mockSortOptions } });
}); });
it('syncs filter value', async () => { it('syncs filter value', async () => {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment