import { GlFilteredSearchToken } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import { mockRegularLabel, mockLabels, } from 'jest/vue_shared/components/sidebar/labels_select_vue/mock_data'; import { DEFAULT_LABELS } from '~/vue_shared/components/filtered_search_bar/constants'; import { getRecentlyUsedTokenValues, setTokenValueToRecentlyUsed, } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils'; import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue'; import { mockLabelToken } from '../mock_data'; jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils'); const mockStorageKey = 'recent-tokens-label_name'; const defaultStubs = { Portal: true, GlFilteredSearchToken: { template: `
`, }, GlFilteredSearchSuggestionList: { template: '
', methods: { getValue: () => '=', }, }, }; const defaultSlots = { 'view-token': `
${mockRegularLabel.title}
`, view: `
${mockRegularLabel.title}
`, }; const mockProps = { tokenConfig: mockLabelToken, tokenValue: { data: '' }, tokenActive: false, tokensListLoading: false, tokenValues: [], fnActiveTokenValue: jest.fn(), defaultTokenValues: DEFAULT_LABELS, recentTokenValuesStorageKey: mockStorageKey, fnCurrentTokenValue: jest.fn(), }; function createComponent({ props = { ...mockProps }, stubs = defaultStubs, slots = defaultSlots, } = {}) { return mount(BaseToken, { propsData: { ...props, }, provide: { portalName: 'fake target', alignSuggestions: jest.fn(), suggestionsListClass: 'custom-class', }, stubs, slots, }); } describe('BaseToken', () => { let wrapper; beforeEach(() => { wrapper = createComponent({ props: { ...mockProps, tokenValue: { data: `"${mockRegularLabel.title}"` }, tokenValues: mockLabels, }, }); }); afterEach(() => { wrapper.destroy(); }); describe('data', () => { it('calls `getRecentlyUsedTokenValues` to populate `recentTokenValues` when `recentTokenValuesStorageKey` is defined', () => { expect(getRecentlyUsedTokenValues).toHaveBeenCalledWith(mockStorageKey); }); }); describe('computed', () => { describe('currentTokenValue', () => { it('calls `fnCurrentTokenValue` when it is provided', () => { // We're disabling lint to trigger computed prop execution for this test. // eslint-disable-next-line no-unused-vars const { currentTokenValue } = wrapper.vm; expect(wrapper.vm.fnCurrentTokenValue).toHaveBeenCalledWith(`"${mockRegularLabel.title}"`); }); }); describe('activeTokenValue', () => { it('calls `fnActiveTokenValue` when it is provided', async () => { wrapper.setProps({ fnCurrentTokenValue: undefined, }); await wrapper.vm.$nextTick(); // We're disabling lint to trigger computed prop execution for this test. // eslint-disable-next-line no-unused-vars const { activeTokenValue } = wrapper.vm; expect(wrapper.vm.fnActiveTokenValue).toHaveBeenCalledWith( mockLabels, `"${mockRegularLabel.title.toLowerCase()}"`, ); }); }); }); describe('watch', () => { describe('tokenActive', () => { let wrapperWithTokenActive; beforeEach(() => { wrapperWithTokenActive = createComponent({ props: { ...mockProps, tokenActive: true, tokenValue: { data: `"${mockRegularLabel.title}"` }, }, }); }); afterEach(() => { wrapperWithTokenActive.destroy(); }); it('emits `fetch-token-values` event on the component when value of this prop is changed to false and `tokenValues` array is empty', async () => { wrapperWithTokenActive.setProps({ tokenActive: false, }); await wrapperWithTokenActive.vm.$nextTick(); expect(wrapperWithTokenActive.emitted('fetch-token-values')).toBeTruthy(); expect(wrapperWithTokenActive.emitted('fetch-token-values')).toEqual([ [`"${mockRegularLabel.title}"`], ]); }); }); }); describe('methods', () => { describe('handleTokenValueSelected', () => { it('calls `setTokenValueToRecentlyUsed` when `recentTokenValuesStorageKey` is defined', () => { const mockTokenValue = { id: 1, title: 'Foo', }; wrapper.vm.handleTokenValueSelected(mockTokenValue); expect(setTokenValueToRecentlyUsed).toHaveBeenCalledWith(mockStorageKey, mockTokenValue); }); it('does not add token from preloadedTokenValues', async () => { const mockTokenValue = { id: 1, title: 'Foo', }; wrapper.setProps({ preloadedTokenValues: [mockTokenValue], }); await wrapper.vm.$nextTick(); wrapper.vm.handleTokenValueSelected(mockTokenValue); expect(setTokenValueToRecentlyUsed).not.toHaveBeenCalled(); }); }); }); describe('template', () => { it('renders gl-filtered-search-token component', () => { const wrapperWithNoStubs = createComponent({ stubs: {}, }); const glFilteredSearchToken = wrapperWithNoStubs.find(GlFilteredSearchToken); expect(glFilteredSearchToken.exists()).toBe(true); expect(glFilteredSearchToken.props('config')).toBe(mockLabelToken); wrapperWithNoStubs.destroy(); }); it('renders `view-token` slot when present', () => { expect(wrapper.find('.js-view-token').exists()).toBe(true); }); it('renders `view` slot when present', () => { expect(wrapper.find('.js-view').exists()).toBe(true); }); describe('events', () => { let wrapperWithNoStubs; beforeEach(() => { wrapperWithNoStubs = createComponent({ stubs: { Portal: true }, }); }); afterEach(() => { wrapperWithNoStubs.destroy(); }); it('emits `fetch-token-values` event on component after a delay when component emits `input` event', async () => { jest.useFakeTimers(); wrapperWithNoStubs.find(GlFilteredSearchToken).vm.$emit('input', { data: 'foo' }); await wrapperWithNoStubs.vm.$nextTick(); jest.runAllTimers(); expect(wrapperWithNoStubs.emitted('fetch-token-values')).toBeTruthy(); expect(wrapperWithNoStubs.emitted('fetch-token-values')[1]).toEqual(['foo']); }); }); }); });