UNPKG

@gitlab/ui

Version:
307 lines (242 loc) • 9.21 kB
import { mount } from '@vue/test-utils'; import { keyboard } from '~/utils/constants'; import GlToken from '../token/token.vue'; import GlTokenContainer from './token_container.vue'; describe('GlTokenContainer', () => { const tokens = [ { id: 1, name: 'Vue.js', }, { id: 2, name: 'Ruby On Rails', }, { id: 3, name: 'GraphQL', }, { id: 4, name: 'Redis', }, ]; const mockedRegisterFocusOnToken = jest.fn(); const defaultProps = { tokens, registerFocusOnToken: mockedRegisterFocusOnToken }; let wrapper; const createComponent = (options) => { wrapper = mount(GlTokenContainer, { ...options, propsData: { ...defaultProps, ...(options?.propsData || {}), }, attachTo: document.body, }); }; const findTokenByName = (name) => { const tokenWrappers = wrapper.findAll('[role=option]'); return tokenWrappers.wrappers.find((tokenWrapper) => tokenWrapper.text() === name); }; const findTokenContainer = () => wrapper.findComponent({ ref: 'tokenContainer' }); const blurActiveElement = () => document.activeElement?.blur?.(); afterEach(() => { blurActiveElement(); }); describe('props', () => { describe('tokens', () => { it('renders passed tokens', () => { createComponent(); expect(wrapper.findAllComponents(GlToken).length).toBe(4); }); }); describe('viewOnly', () => { it('passes viewOnly prop to tokens correctly', () => { createComponent({ propsData: { viewOnly: true } }); const tokenWrappers = wrapper.findAllComponents(GlToken); expect(tokenWrappers.wrappers.every((token) => token.props('viewOnly'))).toBe(true); }); }); describe('clearing all tokens', () => { const findClearAllButton = () => wrapper.find('[data-testid="clear-all-button"]'); it('does not render `Clear all` button by default', () => { createComponent(); expect(findClearAllButton().exists()).toBe(false); }); it('renders `Clear all` button when `showClearAllButton` prop is true', () => { createComponent({ propsData: { showClearAllButton: true } }); expect(findClearAllButton().exists()).toBe(true); }); it('emits `clear-all` event when `Clear all` button is clicked', () => { createComponent({ propsData: { showClearAllButton: true } }); findClearAllButton().vm.$emit('click', new MouseEvent('click')); expect(wrapper.emitted('clear-all')).toEqual([[]]); }); }); describe('state', () => { describe('when `state` is `false`', () => { it('adds `aria-invalid="true"` attribute`', () => { createComponent({ propsData: { state: false, }, }); expect(findTokenContainer().attributes('aria-invalid')).toBe('true'); }); }); describe.each` value ${true} ${null} `('when `state` is `$value`', ({ value }) => { it('does not add `aria-invalid` attribute', () => { createComponent({ propsData: { state: value, }, }); expect(findTokenContainer().attributes('aria-invalid')).toBeUndefined(); }); }); }); describe('registerFocusOnToken', () => { it('calls passed method', () => { createComponent(); expect(mockedRegisterFocusOnToken).toHaveBeenCalled(); }); }); }); describe('slots', () => { it('renders `token-content` slot', () => { createComponent({ scopedSlots: { 'token-content': '<span>Token id: {{ props.token.id }}</span>', }, }); const tokenWrappers = wrapper.findAllComponents(GlToken); expect( tokenWrappers.wrappers.every( (tokenWrapper, index) => `Token id: ${tokens[index].id}` === tokenWrapper.text() ) ).toBe(true); }); it('renders `text-input` slot', () => { createComponent({ slots: { 'text-input': '<input id="custom-text-input" type="text" />', }, }); expect(wrapper.find('#custom-text-input').exists()).toBe(true); }); }); describe('closing tokens', () => { beforeEach(() => { createComponent({ data() { return { focusedTokenIndex: 0, }; }, }); const firstToken = wrapper.findComponent(GlToken); firstToken.vm.$emit('close'); }); it('fires `token-remove` event', () => { expect(wrapper.emitted('token-remove')[0]).toEqual([tokens[0]]); }); it('cancels token focus', () => { wrapper.findAllComponents({ ref: 'tokens' }).wrappers.forEach((tokenWrapper) => { expect(tokenWrapper.element).not.toHaveFocus(); }); }); }); describe('keyboard navigation', () => { const setup = async (focusedTokenIndex, key) => { createComponent( { data() { return { focusedTokenIndex }; }, }, true ); const focusedToken = findTokenByName(tokens[focusedTokenIndex].name); await focusedToken.trigger('keydown', { key }); }; describe('when escape key is pressed', () => { it('fires `cancel-focus` event', async () => { await setup(0, keyboard.escape); expect(wrapper.emitted('cancel-focus')).toHaveLength(1); }); }); describe('when backspace/delete key is pressed', () => { const tokenIndex = 2; beforeEach(async () => { await setup(tokenIndex, keyboard.backspace); }); it('fires `token-remove` event', () => { expect(wrapper.emitted('token-remove')[0]).toEqual([tokens[tokenIndex]]); }); it('focuses on previous token after removing', () => { const expectedFocusedToken = findTokenByName(tokens[tokenIndex - 1].name); expect(expectedFocusedToken.element).toHaveFocus(); }); }); describe('arrow keys', () => { describe('when left arrow is pressed', () => { it.each` focusedTokenIndex | expectedFocusedTokenIndex | testName ${3} | ${2} | ${'focuses on previous token'} ${0} | ${3} | ${'focuses on last token if there is no previous token'} `('$testName', async ({ focusedTokenIndex, expectedFocusedTokenIndex }) => { await setup(focusedTokenIndex, keyboard.arrowLeft); const expectedFocusedToken = findTokenByName(tokens[expectedFocusedTokenIndex].name); expect(expectedFocusedToken.element).toHaveFocus(); }); }); describe('when right arrow is pressed', () => { it.each` focusedTokenIndex | expectedFocusedTokenIndex | testName ${0} | ${1} | ${'focuses on next token'} ${3} | ${0} | ${'focuses on first token if there is no next token'} `('$testName', async ({ focusedTokenIndex, expectedFocusedTokenIndex }) => { await setup(focusedTokenIndex, keyboard.arrowRight); const expectedFocusedToken = findTokenByName(tokens[expectedFocusedTokenIndex].name); expect(expectedFocusedToken.element).toHaveFocus(); }); }); it('focuses on the first token when home key is pressed', async () => { await setup(3, keyboard.home); const expectedFocusedToken = findTokenByName(tokens[0].name); expect(expectedFocusedToken.element).toHaveFocus(); }); it('focuses on the last token when end key is pressed', async () => { await setup(0, keyboard.end); const expectedFocusedToken = findTokenByName(tokens[3].name); expect(expectedFocusedToken.element).toHaveFocus(); }); it('keeps track of focused token when token is focused by click/tap', async () => { createComponent({}); const focusedToken = findTokenByName(tokens[3].name); await focusedToken.trigger('focus'); await focusedToken.trigger('keydown', { key: keyboard.arrowLeft }); const expectedFocusedToken = findTokenByName(tokens[2].name); expect(expectedFocusedToken.element).toHaveFocus(); }); it('emits the `cancel-focus` event when tab key is pressed without modifiers', async () => { createComponent({}); const focusedToken = findTokenByName(tokens[0].name); await focusedToken.trigger('focus'); await focusedToken.trigger('keydown', { key: keyboard.tab }); expect(wrapper.emitted('cancel-focus')).toEqual([[]]); }); it('does not emit the `cancel-focus` event when tab key is pressed with modifiers', async () => { createComponent({}); const focusedToken = findTokenByName(tokens[0].name); await focusedToken.trigger('focus'); await focusedToken.trigger('keydown', { key: keyboard.tab, shiftKey: true }); expect(wrapper.emitted('cancel-focus')).toBeUndefined(); }); }); }); });