diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 11:17:02 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2022-08-18 11:17:02 +0300 |
commit | b39512ed755239198a9c294b6a45e65c05900235 (patch) | |
tree | d234a3efade1de67c46b9e5a38ce813627726aa7 /spec/frontend/vue_shared/components | |
parent | d31474cf3b17ece37939d20082b07f6657cc79a9 (diff) |
Add latest changes from gitlab-org/gitlab@15-3-stable-eev15.3.0-rc42
Diffstat (limited to 'spec/frontend/vue_shared/components')
90 files changed, 773 insertions, 454 deletions
diff --git a/spec/frontend/vue_shared/components/actions_button_spec.js b/spec/frontend/vue_shared/components/actions_button_spec.js index e5b7b693cb5..07c53c04723 100644 --- a/spec/frontend/vue_shared/components/actions_button_spec.js +++ b/spec/frontend/vue_shared/components/actions_button_spec.js @@ -45,9 +45,9 @@ describe('Actions button component', () => { return directiveBinding.value; }; - const findButton = () => wrapper.find(GlButton); + const findButton = () => wrapper.findComponent(GlButton); const findButtonTooltip = () => getTooltip(findButton()); - const findDropdown = () => wrapper.find(GlDropdown); + const findDropdown = () => wrapper.findComponent(GlDropdown); const findDropdownTooltip = () => getTooltip(findDropdown()); const parseDropdownItems = () => findDropdown() diff --git a/spec/frontend/vue_shared/components/alert_details_table_spec.js b/spec/frontend/vue_shared/components/alert_details_table_spec.js index b9a8a5bee97..8a9ee4699bd 100644 --- a/spec/frontend/vue_shared/components/alert_details_table_spec.js +++ b/spec/frontend/vue_shared/components/alert_details_table_spec.js @@ -74,7 +74,7 @@ describe('AlertDetails', () => { }); it('displays a loading state when loading', () => { - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); }); @@ -130,7 +130,7 @@ describe('AlertDetails', () => { environmentData = { name: null, path: null }; mountComponent(); - expect(findTableFieldValueByKey('Environment').text()).toBeFalsy(); + expect(findTableFieldValueByKey('Environment').text()).toBe(''); }); }); diff --git a/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js b/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js index d14f3e5559f..ce7fd40937f 100644 --- a/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js +++ b/spec/frontend/vue_shared/components/blob_viewers/rich_viewer_spec.js @@ -43,6 +43,6 @@ describe('Blob Rich Viewer component', () => { }); it('is using Markdown View Field', () => { - expect(wrapper.find(MarkdownFieldView).exists()).toBe(true); + expect(wrapper.findComponent(MarkdownFieldView).exists()).toBe(true); }); }); diff --git a/spec/frontend/vue_shared/components/changed_file_icon_spec.js b/spec/frontend/vue_shared/components/changed_file_icon_spec.js index 6b9658a6d18..ea708b6f3fe 100644 --- a/spec/frontend/vue_shared/components/changed_file_icon_spec.js +++ b/spec/frontend/vue_shared/components/changed_file_icon_spec.js @@ -25,7 +25,7 @@ describe('Changed file icon', () => { wrapper.destroy(); }); - const findIcon = () => wrapper.find(GlIcon); + const findIcon = () => wrapper.findComponent(GlIcon); const findIconName = () => findIcon().props('name'); const findIconClasses = () => findIcon().classes(); const findTooltipText = () => wrapper.attributes('title'); @@ -51,7 +51,7 @@ describe('Changed file icon', () => { showTooltip: false, }); - expect(findTooltipText()).toBeFalsy(); + expect(findTooltipText()).toBeUndefined(); }); describe.each` @@ -87,7 +87,7 @@ describe('Changed file icon', () => { }); it('does not have tooltip text', () => { - expect(findTooltipText()).toBeFalsy(); + expect(findTooltipText()).toBeUndefined(); }); }); diff --git a/spec/frontend/vue_shared/components/ci_icon_spec.js b/spec/frontend/vue_shared/components/ci_icon_spec.js index 1b502f9587c..2064bee9673 100644 --- a/spec/frontend/vue_shared/components/ci_icon_spec.js +++ b/spec/frontend/vue_shared/components/ci_icon_spec.js @@ -22,7 +22,7 @@ describe('CI Icon component', () => { }); expect(wrapper.find('span').exists()).toBe(true); - expect(wrapper.find(GlIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlIcon).exists()).toBe(true); }); describe('active icons', () => { diff --git a/spec/frontend/vue_shared/components/clipboard_button_spec.js b/spec/frontend/vue_shared/components/clipboard_button_spec.js index fca5e664a96..b18b00e70bb 100644 --- a/spec/frontend/vue_shared/components/clipboard_button_spec.js +++ b/spec/frontend/vue_shared/components/clipboard_button_spec.js @@ -21,7 +21,7 @@ describe('clipboard button', () => { }); }; - const findButton = () => wrapper.find(GlButton); + const findButton = () => wrapper.findComponent(GlButton); const expectConfirmationTooltip = async ({ event, message }) => { const title = 'Copy this value'; diff --git a/spec/frontend/vue_shared/components/clone_dropdown_spec.js b/spec/frontend/vue_shared/components/clone_dropdown_spec.js index eefd1838988..31c08260dd0 100644 --- a/spec/frontend/vue_shared/components/clone_dropdown_spec.js +++ b/spec/frontend/vue_shared/components/clone_dropdown_spec.js @@ -38,9 +38,9 @@ describe('Clone Dropdown Button', () => { ${'HTTP'} | ${1} | ${httpLink} `('renders correct link and a copy-button for $name', ({ index, value }) => { createComponent(); - const group = wrapper.findAll(GlFormInputGroup).at(index); + const group = wrapper.findAllComponents(GlFormInputGroup).at(index); expect(group.props('value')).toBe(value); - expect(group.find(GlFormInputGroup).exists()).toBe(true); + expect(group.findComponent(GlFormInputGroup).exists()).toBe(true); }); it.each` @@ -50,8 +50,8 @@ describe('Clone Dropdown Button', () => { `('does not fail if only $name is set', ({ name, value }) => { createComponent({ [name]: value }); - expect(wrapper.find(GlFormInputGroup).props('value')).toBe(value); - expect(wrapper.findAll(GlDropdownSectionHeader).length).toBe(1); + expect(wrapper.findComponent(GlFormInputGroup).props('value')).toBe(value); + expect(wrapper.findAllComponents(GlDropdownSectionHeader).length).toBe(1); }); }); @@ -63,12 +63,12 @@ describe('Clone Dropdown Button', () => { `('allows null values for the props', ({ name, value }) => { createComponent({ ...defaultPropsData, [name]: value }); - expect(wrapper.findAll(GlDropdownSectionHeader).length).toBe(1); + expect(wrapper.findAllComponents(GlDropdownSectionHeader).length).toBe(1); }); it('correctly calculates httpLabel for HTTPS protocol', () => { createComponent({ httpLink: httpsLink }); - expect(wrapper.find(GlDropdownSectionHeader).text()).toContain('HTTPS'); + expect(wrapper.findComponent(GlDropdownSectionHeader).text()).toContain('HTTPS'); }); }); }); diff --git a/spec/frontend/vue_shared/components/color_picker/color_picker_spec.js b/spec/frontend/vue_shared/components/color_picker/color_picker_spec.js index 8cbe0630426..060048c4bbd 100644 --- a/spec/frontend/vue_shared/components/color_picker/color_picker_spec.js +++ b/spec/frontend/vue_shared/components/color_picker/color_picker_spec.js @@ -16,14 +16,14 @@ describe('ColorPicker', () => { const setColor = '#000000'; const invalidText = 'Please enter a valid hex (#RRGGBB or #RGB) color value'; - const findGlFormGroup = () => wrapper.find(GlFormGroup); + const findGlFormGroup = () => wrapper.findComponent(GlFormGroup); const colorPreview = () => wrapper.find('[data-testid="color-preview"]'); - const colorPicker = () => wrapper.find(GlFormInput); + const colorPicker = () => wrapper.findComponent(GlFormInput); const colorInput = () => wrapper.find('input[type="color"]'); - const colorTextInput = () => wrapper.find(GlFormInputGroup).find('input[type="text"]'); + const colorTextInput = () => wrapper.findComponent(GlFormInputGroup).find('input[type="text"]'); const invalidFeedback = () => wrapper.find('.invalid-feedback'); - const description = () => wrapper.find(GlFormGroup).attributes('description'); - const presetColors = () => wrapper.findAll(GlLink); + const description = () => wrapper.findComponent(GlFormGroup).attributes('description'); + const presetColors = () => wrapper.findAllComponents(GlLink); beforeEach(() => { gon.suggested_label_colors = { diff --git a/spec/frontend/vue_shared/components/commit_spec.js b/spec/frontend/vue_shared/components/commit_spec.js index d91853e7b79..1893e127f6f 100644 --- a/spec/frontend/vue_shared/components/commit_spec.js +++ b/spec/frontend/vue_shared/components/commit_spec.js @@ -9,11 +9,11 @@ describe('Commit component', () => { let wrapper; const findIcon = (name) => { - const icons = wrapper.findAll(GlIcon).filter((c) => c.attributes('name') === name); + const icons = wrapper.findAllComponents(GlIcon).filter((c) => c.attributes('name') === name); return icons.length ? icons.at(0) : icons; }; - const findUserAvatar = () => wrapper.find(UserAvatarLink); + const findUserAvatar = () => wrapper.findComponent(UserAvatarLink); const findRefName = () => wrapper.findByTestId('ref-name'); const createComponent = (propsData) => { @@ -47,7 +47,7 @@ describe('Commit component', () => { }, }); - expect(wrapper.find('.icon-container').find(GlIcon).exists()).toBe(true); + expect(wrapper.find('.icon-container').findComponent(GlIcon).exists()).toBe(true); }); describe('Given all the props', () => { diff --git a/spec/frontend/vue_shared/components/confirm_modal_spec.js b/spec/frontend/vue_shared/components/confirm_modal_spec.js index 3ca1c943398..c1e682a1aae 100644 --- a/spec/frontend/vue_shared/components/confirm_modal_spec.js +++ b/spec/frontend/vue_shared/components/confirm_modal_spec.js @@ -51,13 +51,13 @@ describe('vue_shared/components/confirm_modal', () => { wrapper.destroy(); }); - const findModal = () => wrapper.find(GlModalStub); + const findModal = () => wrapper.findComponent(GlModalStub); const findForm = () => wrapper.find('form'); const findFormData = () => findForm() .findAll('input') .wrappers.map((x) => ({ name: x.attributes('name'), value: x.attributes('value') })); - const findDomElementListener = () => wrapper.find(DomElementListener); + const findDomElementListener = () => wrapper.findComponent(DomElementListener); const triggerOpenWithEventHub = (modalData) => { eventHub.$emit(EVENT_OPEN_CONFIRM_MODAL, modalData); }; @@ -104,7 +104,7 @@ describe('vue_shared/components/confirm_modal', () => { }); it('renders GlModal with data', () => { - expect(findModal().exists()).toBeTruthy(); + expect(findModal().exists()).toBe(true); expect(findModal().attributes()).toEqual( expect.objectContaining({ oktitle: MOCK_MODAL_DATA.modalAttributes.okTitle, diff --git a/spec/frontend/vue_shared/components/dismissible_alert_spec.js b/spec/frontend/vue_shared/components/dismissible_alert_spec.js index 879d4aba441..8b1189f25d5 100644 --- a/spec/frontend/vue_shared/components/dismissible_alert_spec.js +++ b/spec/frontend/vue_shared/components/dismissible_alert_spec.js @@ -20,7 +20,7 @@ describe('vue_shared/components/dismissible_alert', () => { wrapper.destroy(); }); - const findAlert = () => wrapper.find(GlAlert); + const findAlert = () => wrapper.findComponent(GlAlert); describe('default', () => { beforeEach(() => { @@ -45,7 +45,7 @@ describe('vue_shared/components/dismissible_alert', () => { }); it('emmits alertDismissed', () => { - expect(wrapper.emitted('alertDismissed')).toBeTruthy(); + expect(wrapper.emitted()).toHaveProperty('alertDismissed'); }); }); }); diff --git a/spec/frontend/vue_shared/components/dismissible_container_spec.js b/spec/frontend/vue_shared/components/dismissible_container_spec.js index b8aeea38e77..f7030f38709 100644 --- a/spec/frontend/vue_shared/components/dismissible_container_spec.js +++ b/spec/frontend/vue_shared/components/dismissible_container_spec.js @@ -33,7 +33,7 @@ describe('DismissibleContainer', () => { button.trigger('click'); - expect(wrapper.emitted().dismiss).toBeTruthy(); + expect(wrapper.emitted().dismiss).toEqual(expect.any(Array)); }); }); diff --git a/spec/frontend/vue_shared/components/dropdown/dropdown_button_spec.js b/spec/frontend/vue_shared/components/dropdown/dropdown_button_spec.js index 08e5d828b8f..e34ed31b4bf 100644 --- a/spec/frontend/vue_shared/components/dropdown/dropdown_button_spec.js +++ b/spec/frontend/vue_shared/components/dropdown/dropdown_button_spec.js @@ -1,80 +1,71 @@ -import Vue from 'vue'; +import { mount } from '@vue/test-utils'; +import DropdownButton from '~/vue_shared/components/dropdown/dropdown_button.vue'; -import { mountComponentWithSlots } from 'helpers/vue_mount_component_helper'; -import dropdownButtonComponent from '~/vue_shared/components/dropdown/dropdown_button.vue'; +describe('DropdownButton component', () => { + let wrapper; -const defaultLabel = 'Select'; -const customLabel = 'Select project'; + const defaultLabel = 'Select'; + const customLabel = 'Select project'; -const createComponent = (props, slots = {}) => { - const Component = Vue.extend(dropdownButtonComponent); - - return mountComponentWithSlots(Component, { props, slots }); -}; - -describe('DropdownButtonComponent', () => { - let vm; - - beforeEach(() => { - vm = createComponent(); - }); + const createComponent = (props, slots = {}) => { + wrapper = mount(DropdownButton, { propsData: props, slots }); + }; afterEach(() => { - vm.$destroy(); + wrapper.destroy(); }); describe('computed', () => { describe('dropdownToggleText', () => { it('returns default toggle text', () => { - expect(vm.toggleText).toBe(defaultLabel); + createComponent(); + + expect(wrapper.vm.toggleText).toBe(defaultLabel); }); it('returns custom toggle text when provided via props', () => { - const vmEmptyLabels = createComponent({ toggleText: customLabel }); + createComponent({ toggleText: customLabel }); - expect(vmEmptyLabels.toggleText).toBe(customLabel); - vmEmptyLabels.$destroy(); + expect(wrapper.vm.toggleText).toBe(customLabel); }); }); }); describe('template', () => { it('renders component container element of type `button`', () => { - expect(vm.$el.nodeName).toBe('BUTTON'); + createComponent(); + + expect(wrapper.element.nodeName).toBe('BUTTON'); }); it('renders component container element with required data attributes', () => { - expect(vm.$el.dataset.abilityName).toBe(vm.abilityName); - expect(vm.$el.dataset.fieldName).toBe(vm.fieldName); - expect(vm.$el.dataset.issueUpdate).toBe(vm.updatePath); - expect(vm.$el.dataset.labels).toBe(vm.labelsPath); - expect(vm.$el.dataset.namespacePath).toBe(vm.namespace); - expect(vm.$el.dataset.showAny).not.toBeDefined(); + createComponent(); + + expect(wrapper.element.dataset.abilityName).toBe(wrapper.vm.abilityName); + expect(wrapper.element.dataset.fieldName).toBe(wrapper.vm.fieldName); + expect(wrapper.element.dataset.issueUpdate).toBe(wrapper.vm.updatePath); + expect(wrapper.element.dataset.labels).toBe(wrapper.vm.labelsPath); + expect(wrapper.element.dataset.namespacePath).toBe(wrapper.vm.namespace); + expect(wrapper.element.dataset.showAny).toBeUndefined(); }); it('renders dropdown toggle text element', () => { - const dropdownToggleTextEl = vm.$el.querySelector('.dropdown-toggle-text'); + createComponent(); - expect(dropdownToggleTextEl).not.toBeNull(); - expect(dropdownToggleTextEl.innerText.trim()).toBe(defaultLabel); + expect(wrapper.find('.dropdown-toggle-text').text()).toBe(defaultLabel); }); it('renders dropdown button icon', () => { - const dropdownIconEl = vm.$el.querySelector('[data-testid="chevron-down-icon"]'); + createComponent(); - expect(dropdownIconEl).not.toBeNull(); + expect(wrapper.find('[data-testid="chevron-down-icon"]').exists()).toBe(true); }); it('renders slot, if default slot exists', () => { - vm = createComponent( - {}, - { - default: ['Lorem Ipsum Dolar'], - }, - ); - - expect(vm.$el.querySelector('.dropdown-toggle-text')).toBeNull(); - expect(vm.$el).toHaveText('Lorem Ipsum Dolar'); + createComponent({}, { default: ['Lorem Ipsum Dolar'] }); + + expect(wrapper.find('.dropdown-toggle-text').exists()).toBe(false); + expect(wrapper.text()).toBe('Lorem Ipsum Dolar'); }); }); }); diff --git a/spec/frontend/vue_shared/components/dropdown/dropdown_widget_spec.js b/spec/frontend/vue_shared/components/dropdown/dropdown_widget_spec.js index 084d0559665..dd3e55c82bb 100644 --- a/spec/frontend/vue_shared/components/dropdown/dropdown_widget_spec.js +++ b/spec/frontend/vue_shared/components/dropdown/dropdown_widget_spec.js @@ -8,7 +8,7 @@ describe('DropdownWidget component', () => { let wrapper; const findDropdown = () => wrapper.findComponent(GlDropdown); - const findDropdownItems = () => wrapper.findAll(GlDropdownItem); + const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); const findSearch = () => wrapper.findComponent(GlSearchBoxByType); const createComponent = ({ props = {} } = {}) => { diff --git a/spec/frontend/vue_shared/components/expand_button_spec.js b/spec/frontend/vue_shared/components/expand_button_spec.js index 87d6ed6b21f..170c947e520 100644 --- a/spec/frontend/vue_shared/components/expand_button_spec.js +++ b/spec/frontend/vue_shared/components/expand_button_spec.js @@ -37,11 +37,11 @@ describe('Expand button', () => { }); it('renders no text when short text is not provided', () => { - expect(wrapper.find(ExpandButton).text()).toBe(''); + expect(wrapper.findComponent(ExpandButton).text()).toBe(''); }); it('does not render expanded text', () => { - expect(wrapper.find(ExpandButton).text().trim()).not.toBe(text.short); + expect(wrapper.findComponent(ExpandButton).text().trim()).not.toBe(text.short); }); describe('when short text is provided', () => { @@ -55,13 +55,13 @@ describe('Expand button', () => { }); it('renders short text', () => { - expect(wrapper.find(ExpandButton).text().trim()).toBe(text.short); + expect(wrapper.findComponent(ExpandButton).text().trim()).toBe(text.short); }); it('renders button before text', () => { expect(expanderPrependEl().isVisible()).toBe(true); expect(expanderAppendEl().isVisible()).toBe(false); - expect(wrapper.find(ExpandButton).element).toMatchSnapshot(); + expect(wrapper.findComponent(ExpandButton).element).toMatchSnapshot(); }); }); @@ -81,7 +81,7 @@ describe('Expand button', () => { }); it('renders the expanded text', () => { - expect(wrapper.find(ExpandButton).text()).toContain(text.expanded); + expect(wrapper.findComponent(ExpandButton).text()).toContain(text.expanded); }); describe('when short text is provided', () => { @@ -98,13 +98,13 @@ describe('Expand button', () => { }); it('only renders expanded text', () => { - expect(wrapper.find(ExpandButton).text().trim()).toBe(text.expanded); + expect(wrapper.findComponent(ExpandButton).text().trim()).toBe(text.expanded); }); it('renders button after text', () => { expect(expanderPrependEl().isVisible()).toBe(false); expect(expanderAppendEl().isVisible()).toBe(true); - expect(wrapper.find(ExpandButton).element).toMatchSnapshot(); + expect(wrapper.findComponent(ExpandButton).element).toMatchSnapshot(); }); }); }); @@ -124,11 +124,11 @@ describe('Expand button', () => { }); it('clicking hides expanded text', async () => { - expect(wrapper.find(ExpandButton).text().trim()).toBe(text.expanded); + expect(wrapper.findComponent(ExpandButton).text().trim()).toBe(text.expanded); expanderAppendEl().trigger('click'); await nextTick(); - expect(wrapper.find(ExpandButton).text().trim()).not.toBe(text.expanded); + expect(wrapper.findComponent(ExpandButton).text().trim()).not.toBe(text.expanded); }); describe('when short text is provided', () => { @@ -145,11 +145,11 @@ describe('Expand button', () => { }); it('clicking reveals short text', async () => { - expect(wrapper.find(ExpandButton).text().trim()).toBe(text.expanded); + expect(wrapper.findComponent(ExpandButton).text().trim()).toBe(text.expanded); expanderAppendEl().trigger('click'); await nextTick(); - expect(wrapper.find(ExpandButton).text().trim()).toBe(text.short); + expect(wrapper.findComponent(ExpandButton).text().trim()).toBe(text.short); }); }); }); diff --git a/spec/frontend/vue_shared/components/file_icon_spec.js b/spec/frontend/vue_shared/components/file_icon_spec.js index b0e623520a8..3f4bfc86b67 100644 --- a/spec/frontend/vue_shared/components/file_icon_spec.js +++ b/spec/frontend/vue_shared/components/file_icon_spec.js @@ -6,7 +6,7 @@ import { FILE_SYMLINK_MODE } from '~/vue_shared/constants'; describe('File Icon component', () => { let wrapper; const findSvgIcon = () => wrapper.find('svg'); - const findGlIcon = () => wrapper.find(GlIcon); + const findGlIcon = () => wrapper.findComponent(GlIcon); const getIconName = () => findSvgIcon() .find('use') @@ -61,7 +61,7 @@ describe('File Icon component', () => { loading: true, }); - expect(wrapper.find(GlLoadingIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); }); it('should add a special class and a size class', () => { diff --git a/spec/frontend/vue_shared/components/file_row_spec.js b/spec/frontend/vue_shared/components/file_row_spec.js index 62fb29c455c..f5a545891d5 100644 --- a/spec/frontend/vue_shared/components/file_row_spec.js +++ b/spec/frontend/vue_shared/components/file_row_spec.js @@ -119,7 +119,7 @@ describe('File row component', () => { level: 0, }); - expect(wrapper.find(FileHeader).exists()).toBe(true); + expect(wrapper.findComponent(FileHeader).exists()).toBe(true); }); it('matches the current route against encoded file URL', () => { @@ -164,6 +164,6 @@ describe('File row component', () => { level: 0, }); - expect(wrapper.find(FileIcon).props('submodule')).toBe(submodule); + expect(wrapper.findComponent(FileIcon).props('submodule')).toBe(submodule); }); }); diff --git a/spec/frontend/vue_shared/components/file_tree_spec.js b/spec/frontend/vue_shared/components/file_tree_spec.js index 39a7c7a2b3a..e8818e09dc0 100644 --- a/spec/frontend/vue_shared/components/file_tree_spec.js +++ b/spec/frontend/vue_shared/components/file_tree_spec.js @@ -25,8 +25,8 @@ describe('File Tree component', () => { }); }; - const findFileRow = () => wrapper.find(MockFileRow); - const findChildrenTrees = () => wrapper.findAll(FileTree).wrappers.slice(1); + const findFileRow = () => wrapper.findComponent(MockFileRow); + const findChildrenTrees = () => wrapper.findAllComponents(FileTree).wrappers.slice(1); const findChildrenTreeProps = () => findChildrenTrees().map((x) => ({ ...x.props(), diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js index e44bc8771f5..1b9ca8e6092 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/filtered_search_bar_root_spec.js @@ -88,10 +88,10 @@ describe('FilteredSearchBarRoot', () => { expect(wrapper.vm.filterValue).toEqual([]); expect(wrapper.vm.selectedSortOption).toBe(mockSortOptions[0]); expect(wrapper.vm.selectedSortDirection).toBe(SortDirection.descending); - expect(wrapper.find(GlButtonGroup).exists()).toBe(true); - expect(wrapper.find(GlButton).exists()).toBe(true); - expect(wrapper.find(GlDropdown).exists()).toBe(true); - expect(wrapper.find(GlDropdownItem).exists()).toBe(true); + expect(wrapper.findComponent(GlButtonGroup).exists()).toBe(true); + expect(wrapper.findComponent(GlButton).exists()).toBe(true); + expect(wrapper.findComponent(GlDropdown).exists()).toBe(true); + expect(wrapper.findComponent(GlDropdownItem).exists()).toBe(true); }); it('does not initialize `selectedSortOption` and `selectedSortDirection` when `sortOptions` is not applied and hides the sort dropdown', () => { @@ -99,10 +99,10 @@ describe('FilteredSearchBarRoot', () => { expect(wrapperNoSort.vm.filterValue).toEqual([]); expect(wrapperNoSort.vm.selectedSortOption).toBe(undefined); - expect(wrapperNoSort.find(GlButtonGroup).exists()).toBe(false); - expect(wrapperNoSort.find(GlButton).exists()).toBe(false); - expect(wrapperNoSort.find(GlDropdown).exists()).toBe(false); - expect(wrapperNoSort.find(GlDropdownItem).exists()).toBe(false); + expect(wrapperNoSort.findComponent(GlButtonGroup).exists()).toBe(false); + expect(wrapperNoSort.findComponent(GlButton).exists()).toBe(false); + expect(wrapperNoSort.findComponent(GlDropdown).exists()).toBe(false); + expect(wrapperNoSort.findComponent(GlDropdownItem).exists()).toBe(false); }); }); @@ -217,7 +217,7 @@ describe('FilteredSearchBarRoot', () => { it('emits component event `onFilter` with empty array and true when initially selected filter value was cleared', async () => { wrapper = createComponent({ initialFilterValue: [tokenValueLabel] }); - wrapper.find(GlFilteredSearch).vm.$emit('clear'); + wrapper.findComponent(GlFilteredSearch).vm.$emit('clear'); await nextTick(); expect(wrapper.emitted('onFilter')[0]).toEqual([[], true]); @@ -362,7 +362,7 @@ describe('FilteredSearchBarRoot', () => { it('calls `blurSearchInput` method to remove focus from filter input field', () => { jest.spyOn(wrapper.vm, 'blurSearchInput'); - wrapper.find(GlFilteredSearch).vm.$emit('submit', mockFilters); + wrapper.findComponent(GlFilteredSearch).vm.$emit('submit', mockFilters); expect(wrapper.vm.blurSearchInput).toHaveBeenCalled(); }); @@ -392,7 +392,7 @@ describe('FilteredSearchBarRoot', () => { }); it('renders gl-filtered-search component', () => { - const glFilteredSearchEl = wrapper.find(GlFilteredSearch); + const glFilteredSearchEl = wrapper.findComponent(GlFilteredSearch); expect(glFilteredSearchEl.props('placeholder')).toBe('Filter requirements'); expect(glFilteredSearchEl.props('availableTokens')).toEqual(mockAvailableTokens); @@ -404,8 +404,10 @@ describe('FilteredSearchBarRoot', () => { showCheckbox: true, }); - expect(wrapperWithCheckbox.find(GlFormCheckbox).exists()).toBe(true); - expect(wrapperWithCheckbox.find(GlFormCheckbox).attributes('checked')).not.toBeDefined(); + expect(wrapperWithCheckbox.findComponent(GlFormCheckbox).exists()).toBe(true); + expect( + wrapperWithCheckbox.findComponent(GlFormCheckbox).attributes('checked'), + ).not.toBeDefined(); wrapperWithCheckbox.destroy(); @@ -414,7 +416,7 @@ describe('FilteredSearchBarRoot', () => { checkboxChecked: true, }); - expect(wrapperWithCheckbox.find(GlFormCheckbox).attributes('checked')).toBe('true'); + expect(wrapperWithCheckbox.findComponent(GlFormCheckbox).attributes('checked')).toBe('true'); wrapperWithCheckbox.destroy(); }); @@ -448,7 +450,7 @@ describe('FilteredSearchBarRoot', () => { await nextTick(); - expect(wrapperFullMount.find(GlDropdownItem).text()).toBe('Membership := Direct'); + expect(wrapperFullMount.findComponent(GlDropdownItem).text()).toBe('Membership := Direct'); wrapperFullMount.destroy(); }); @@ -466,20 +468,20 @@ describe('FilteredSearchBarRoot', () => { await nextTick(); - expect(wrapperFullMount.find(GlDropdownItem).text()).toBe('Membership := exclude'); + expect(wrapperFullMount.findComponent(GlDropdownItem).text()).toBe('Membership := exclude'); wrapperFullMount.destroy(); }); }); it('renders sort dropdown component', () => { - expect(wrapper.find(GlButtonGroup).exists()).toBe(true); - expect(wrapper.find(GlDropdown).exists()).toBe(true); - expect(wrapper.find(GlDropdown).props('text')).toBe(mockSortOptions[0].title); + expect(wrapper.findComponent(GlButtonGroup).exists()).toBe(true); + expect(wrapper.findComponent(GlDropdown).exists()).toBe(true); + expect(wrapper.findComponent(GlDropdown).props('text')).toBe(mockSortOptions[0].title); }); it('renders sort dropdown items', () => { - const dropdownItemsEl = wrapper.findAll(GlDropdownItem); + const dropdownItemsEl = wrapper.findAllComponents(GlDropdownItem); expect(dropdownItemsEl).toHaveLength(mockSortOptions.length); expect(dropdownItemsEl.at(0).text()).toBe(mockSortOptions[0].title); @@ -488,7 +490,7 @@ describe('FilteredSearchBarRoot', () => { }); it('renders sort direction button', () => { - const sortButtonEl = wrapper.find(GlButton); + const sortButtonEl = wrapper.findComponent(GlButton); expect(sortButtonEl.attributes('title')).toBe('Sort direction: Descending'); expect(sortButtonEl.props('icon')).toBe('sort-highest'); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js index 86d1f21fd04..a6713b7e7e4 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/mock_data.js @@ -66,12 +66,14 @@ export const mockMilestones = [ export const mockCrmContacts = [ { + __typename: 'CustomerRelationsContact', id: 'gid://gitlab/CustomerRelations::Contact/1', firstName: 'John', lastName: 'Smith', email: 'john@smith.com', }, { + __typename: 'CustomerRelationsContact', id: 'gid://gitlab/CustomerRelations::Contact/2', firstName: 'Andy', lastName: 'Green', @@ -81,10 +83,12 @@ export const mockCrmContacts = [ export const mockCrmOrganizations = [ { + __typename: 'CustomerRelationsOrganization', id: 'gid://gitlab/CustomerRelations::Organization/1', name: 'First Org Ltd.', }, { + __typename: 'CustomerRelationsOrganization', id: 'gid://gitlab/CustomerRelations::Organization/2', name: 'Organizer S.p.a.', }, @@ -102,11 +106,9 @@ export const mockProjectCrmContactsQueryResponse = { __typename: 'CustomerRelationsContactConnection', nodes: [ { - __typename: 'CustomerRelationsContact', ...mockCrmContacts[0], }, { - __typename: 'CustomerRelationsContact', ...mockCrmContacts[1], }, ], @@ -128,11 +130,9 @@ export const mockProjectCrmOrganizationsQueryResponse = { __typename: 'CustomerRelationsOrganizationConnection', nodes: [ { - __typename: 'CustomerRelationsOrganization', ...mockCrmOrganizations[0], }, { - __typename: 'CustomerRelationsOrganization', ...mockCrmOrganizations[1], }, ], diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js index 3f24d5df858..302dfabffb2 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/author_token_spec.js @@ -195,7 +195,7 @@ describe('AuthorToken', () => { }); await nextTick(); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); expect(tokenSegments).toHaveLength(3); // Author, =, "Administrator" @@ -207,7 +207,7 @@ describe('AuthorToken', () => { it('renders token value with correct avatarUrl from author object', async () => { const getAvatarEl = () => - wrapper.findAll(GlFilteredSearchTokenSegment).at(2).findComponent(GlAvatar); + wrapper.findAllComponents(GlFilteredSearchTokenSegment).at(2).findComponent(GlAvatar); wrapper = createComponent({ value: { data: mockAuthors[0].username }, @@ -252,7 +252,7 @@ describe('AuthorToken', () => { await activateSuggestionsList(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(defaultAuthors.length + currentUserLength); defaultAuthors.forEach((label, index) => { @@ -266,12 +266,12 @@ describe('AuthorToken', () => { config: { ...mockAuthorToken, defaultAuthors: [] }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - expect(wrapper.find(GlDropdownDivider).exists()).toBe(false); + expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false); }); it('renders `DEFAULT_NONE_ANY` as default suggestions', async () => { @@ -283,7 +283,7 @@ describe('AuthorToken', () => { await activateSuggestionsList(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(2 + currentUserLength); expect(suggestions.at(0).text()).toBe(DEFAULT_NONE_ANY[0].text); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js index 7b495ec9bee..1de35daa3a5 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/branch_token_spec.js @@ -114,7 +114,7 @@ describe('BranchToken', () => { describe('template', () => { const defaultBranches = DEFAULT_NONE_ANY; async function showSuggestions() { - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); @@ -133,11 +133,11 @@ describe('BranchToken', () => { }); it('renders gl-filtered-search-token component', () => { - expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true); + expect(wrapper.findComponent(GlFilteredSearchToken).exists()).toBe(true); }); it('renders token item when value is selected', () => { - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); expect(tokenSegments).toHaveLength(3); expect(tokenSegments.at(2).text()).toBe(mockBranches[0].name); @@ -150,7 +150,7 @@ describe('BranchToken', () => { stubs: { Portal: true }, }); await showSuggestions(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(defaultBranches.length); defaultBranches.forEach((branch, index) => { @@ -166,8 +166,8 @@ describe('BranchToken', () => { }); await showSuggestions(); - expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false); - expect(wrapper.find(GlDropdownDivider).exists()).toBe(false); + expect(wrapper.findComponent(GlFilteredSearchSuggestion).exists()).toBe(false); + expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false); }); it('renders no suggestions as default', async () => { @@ -177,7 +177,7 @@ describe('BranchToken', () => { stubs: { Portal: true }, }); await showSuggestions(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(0); }); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js index 157e021fc60..c9879987931 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_contact_token_spec.js @@ -195,7 +195,7 @@ describe('CrmContactToken', () => { value: { data: '1' }, }); - const baseTokenEl = wrapper.find(BaseToken); + const baseTokenEl = wrapper.findComponent(BaseToken); expect(baseTokenEl.exists()).toBe(true); expect(baseTokenEl.props()).toMatchObject({ @@ -210,7 +210,7 @@ describe('CrmContactToken', () => { value: { data: `${getIdFromGraphQLId(contact.id)}` }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); expect(tokenSegments).toHaveLength(3); // Contact, =, Contact name expect(tokenSegments.at(2).text()).toBe(`${contact.firstName} ${contact.lastName}`); // Contact name @@ -222,12 +222,12 @@ describe('CrmContactToken', () => { config: { ...mockCrmContactToken, defaultContacts }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(defaultContacts.length); defaultContacts.forEach((contact, index) => { @@ -241,13 +241,13 @@ describe('CrmContactToken', () => { config: { ...mockCrmContactToken, defaultContacts: [] }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false); - expect(wrapper.find(GlDropdownDivider).exists()).toBe(false); + expect(wrapper.findComponent(GlFilteredSearchSuggestion).exists()).toBe(false); + expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false); }); it('renders `DEFAULT_NONE_ANY` as default suggestions', () => { @@ -256,11 +256,11 @@ describe('CrmContactToken', () => { config: { ...mockCrmContactToken }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(DEFAULT_NONE_ANY.length); DEFAULT_NONE_ANY.forEach((contact, index) => { diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js index 977f8bbef61..16333b052e6 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/crm_organization_token_spec.js @@ -194,7 +194,7 @@ describe('CrmOrganizationToken', () => { value: { data: '1' }, }); - const baseTokenEl = wrapper.find(BaseToken); + const baseTokenEl = wrapper.findComponent(BaseToken); expect(baseTokenEl.exists()).toBe(true); expect(baseTokenEl.props()).toMatchObject({ @@ -209,7 +209,7 @@ describe('CrmOrganizationToken', () => { value: { data: `${getIdFromGraphQLId(organization.id)}` }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); expect(tokenSegments).toHaveLength(3); // Organization, =, Organization name expect(tokenSegments.at(2).text()).toBe(organization.name); // Organization name @@ -221,12 +221,12 @@ describe('CrmOrganizationToken', () => { config: { ...mockCrmOrganizationToken, defaultOrganizations }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(defaultOrganizations.length); defaultOrganizations.forEach((organization, index) => { @@ -240,13 +240,13 @@ describe('CrmOrganizationToken', () => { config: { ...mockCrmOrganizationToken, defaultOrganizations: [] }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false); - expect(wrapper.find(GlDropdownDivider).exists()).toBe(false); + expect(wrapper.findComponent(GlFilteredSearchSuggestion).exists()).toBe(false); + expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false); }); it('renders `DEFAULT_NONE_ANY` as default suggestions', () => { @@ -255,11 +255,11 @@ describe('CrmOrganizationToken', () => { config: { ...mockCrmOrganizationToken }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(DEFAULT_NONE_ANY.length); DEFAULT_NONE_ANY.forEach((organization, index) => { diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js index dcb0d095b1b..bf4a6eb7635 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/emoji_token_spec.js @@ -135,14 +135,16 @@ describe('EmojiToken', () => { }); it('renders gl-filtered-search-token component', () => { - expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true); + expect(wrapper.findComponent(GlFilteredSearchToken).exists()).toBe(true); }); it('renders token item when value is selected', () => { - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); expect(tokenSegments).toHaveLength(3); // My Reaction, =, "thumbsup" - expect(tokenSegments.at(2).find(GlEmoji).attributes('data-name')).toEqual('thumbsup'); + expect(tokenSegments.at(2).findComponent(GlEmoji).attributes('data-name')).toEqual( + 'thumbsup', + ); }); it('renders provided defaultEmojis as suggestions', async () => { @@ -151,12 +153,12 @@ describe('EmojiToken', () => { config: { ...mockReactionEmojiToken, defaultEmojis }, stubs: { Portal: true, GlEmoji }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(defaultEmojis.length); defaultEmojis.forEach((emoji, index) => { @@ -170,13 +172,13 @@ describe('EmojiToken', () => { config: { ...mockReactionEmojiToken, defaultEmojis: [] }, stubs: { Portal: true, GlEmoji }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false); - expect(wrapper.find(GlDropdownDivider).exists()).toBe(false); + expect(wrapper.findComponent(GlFilteredSearchSuggestion).exists()).toBe(false); + expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false); }); it('renders `DEFAULT_LABEL_NONE` and `DEFAULT_LABEL_ANY` as default suggestions', async () => { @@ -185,12 +187,12 @@ describe('EmojiToken', () => { config: { ...mockReactionEmojiToken }, stubs: { Portal: true, GlEmoji }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(2); expect(suggestions.at(0).text()).toBe(DEFAULT_LABEL_NONE.text); diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js index 51161a1a0ef..01e281884ed 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/label_token_spec.js @@ -156,7 +156,7 @@ describe('LabelToken', () => { }); it('renders base-token component', () => { - const baseTokenEl = wrapper.find(BaseToken); + const baseTokenEl = wrapper.findComponent(BaseToken); expect(baseTokenEl.exists()).toBe(true); expect(baseTokenEl.props()).toMatchObject({ @@ -166,7 +166,7 @@ describe('LabelToken', () => { }); it('renders token item when value is selected', () => { - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); expect(tokenSegments).toHaveLength(3); // Label, =, "Foo Label" expect(tokenSegments.at(2).text()).toBe(`~${mockRegularLabel.title}`); // "Foo Label" @@ -181,12 +181,12 @@ describe('LabelToken', () => { config: { ...mockLabelToken, defaultLabels }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(defaultLabels.length); defaultLabels.forEach((label, index) => { @@ -200,13 +200,13 @@ describe('LabelToken', () => { config: { ...mockLabelToken, defaultLabels: [] }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false); - expect(wrapper.find(GlDropdownDivider).exists()).toBe(false); + expect(wrapper.findComponent(GlFilteredSearchSuggestion).exists()).toBe(false); + expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false); }); it('renders `DEFAULT_NONE_ANY` as default suggestions', () => { @@ -215,11 +215,11 @@ describe('LabelToken', () => { config: { ...mockLabelToken }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(DEFAULT_NONE_ANY.length); DEFAULT_NONE_ANY.forEach((label, index) => { diff --git a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js index 7c545f76c0b..f71ba51fc5b 100644 --- a/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js +++ b/spec/frontend/vue_shared/components/filtered_search_bar/tokens/milestone_token_spec.js @@ -155,11 +155,11 @@ describe('MilestoneToken', () => { }); it('renders gl-filtered-search-token component', () => { - expect(wrapper.find(GlFilteredSearchToken).exists()).toBe(true); + expect(wrapper.findComponent(GlFilteredSearchToken).exists()).toBe(true); }); it('renders token item when value is selected', () => { - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); expect(tokenSegments).toHaveLength(3); // Milestone, =, '%"4.0"' expect(tokenSegments.at(2).text()).toBe(`%${mockRegularMilestone.title}`); // "4.0 RC1" @@ -171,12 +171,12 @@ describe('MilestoneToken', () => { config: { ...mockMilestoneToken, defaultMilestones }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(defaultMilestones.length); defaultMilestones.forEach((milestone, index) => { @@ -190,13 +190,13 @@ describe('MilestoneToken', () => { config: { ...mockMilestoneToken, defaultMilestones: [] }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - expect(wrapper.find(GlFilteredSearchSuggestion).exists()).toBe(false); - expect(wrapper.find(GlDropdownDivider).exists()).toBe(false); + expect(wrapper.findComponent(GlFilteredSearchSuggestion).exists()).toBe(false); + expect(wrapper.findComponent(GlDropdownDivider).exists()).toBe(false); }); it('renders `DEFAULT_MILESTONES` as default suggestions', async () => { @@ -205,12 +205,12 @@ describe('MilestoneToken', () => { config: { ...mockMilestoneToken }, stubs: { Portal: true }, }); - const tokenSegments = wrapper.findAll(GlFilteredSearchTokenSegment); + const tokenSegments = wrapper.findAllComponents(GlFilteredSearchTokenSegment); const suggestionsSegment = tokenSegments.at(2); suggestionsSegment.vm.$emit('activate'); await nextTick(); - const suggestions = wrapper.findAll(GlFilteredSearchSuggestion); + const suggestions = wrapper.findAllComponents(GlFilteredSearchSuggestion); expect(suggestions).toHaveLength(DEFAULT_MILESTONES.length); DEFAULT_MILESTONES.forEach((milestone, index) => { diff --git a/spec/frontend/vue_shared/components/gitlab_version_check_spec.js b/spec/frontend/vue_shared/components/gitlab_version_check_spec.js index b180e8c12dd..6699ae5fb69 100644 --- a/spec/frontend/vue_shared/components/gitlab_version_check_spec.js +++ b/spec/frontend/vue_shared/components/gitlab_version_check_spec.js @@ -26,13 +26,44 @@ describe('GitlabVersionCheck', () => { wrapper = shallowMount(GitlabVersionCheck); }; + const dummyGon = { + relative_url_root: '/', + }; + + let originalGon; + afterEach(() => { wrapper.destroy(); mock.restore(); + window.gon = originalGon; }); const findGlBadge = () => wrapper.findComponent(GlBadge); + describe.each` + root | description + ${'/'} | ${'not used (uses its own (sub)domain)'} + ${'/gitlab'} | ${'custom path'} + ${'/service/gitlab'} | ${'custom path with 2 depth'} + `('path for version_check.json', ({ root, description }) => { + describe(`when relative url is ${description}: ${root}`, () => { + beforeEach(async () => { + originalGon = window.gon; + window.gon = { ...dummyGon }; + window.gon.relative_url_root = root; + createComponent(defaultResponse); + await waitForPromises(); // Ensure we wrap up the axios call + }); + + it('reflects the relative url setting', () => { + expect(mock.history.get.length).toBe(1); + + const pathRegex = new RegExp(`^${root}`); + expect(mock.history.get[0].url).toMatch(pathRegex); + }); + }); + }); + describe('template', () => { describe.each` description | mockResponse | renders diff --git a/spec/frontend/vue_shared/components/gl_modal_vuex_spec.js b/spec/frontend/vue_shared/components/gl_modal_vuex_spec.js index c0a6588833e..2dcd91f737f 100644 --- a/spec/frontend/vue_shared/components/gl_modal_vuex_spec.js +++ b/spec/frontend/vue_shared/components/gl_modal_vuex_spec.js @@ -59,7 +59,7 @@ describe('GlModalVuex', () => { default: `<div>${TEST_SLOT}</div>`, }, }); - const glModal = wrapper.find(GlModal); + const glModal = wrapper.findComponent(GlModal); expect(glModal.props('modalId')).toBe(TEST_MODAL_ID); expect(glModal.text()).toContain(TEST_SLOT); @@ -76,7 +76,7 @@ describe('GlModalVuex', () => { okVariant, }, }); - const glModal = wrapper.find(GlModal); + const glModal = wrapper.findComponent(GlModal); expect(glModal.attributes('title')).toEqual(title); expect(glModal.attributes('oktitle')).toEqual(title); @@ -90,7 +90,7 @@ describe('GlModalVuex', () => { listeners: { ok }, }); - const glModal = wrapper.find(GlModal); + const glModal = wrapper.findComponent(GlModal); glModal.vm.$emit('ok'); expect(ok).toHaveBeenCalledTimes(1); @@ -101,7 +101,7 @@ describe('GlModalVuex', () => { factory(); - const glModal = wrapper.find(GlModal); + const glModal = wrapper.findComponent(GlModal); glModal.vm.$emit('shown'); expect(actions.show).toHaveBeenCalledTimes(1); @@ -112,7 +112,7 @@ describe('GlModalVuex', () => { factory(); - const glModal = wrapper.find(GlModal); + const glModal = wrapper.findComponent(GlModal); glModal.vm.$emit('hidden'); expect(actions.hide).toHaveBeenCalledTimes(1); diff --git a/spec/frontend/vue_shared/components/help_popover_spec.js b/spec/frontend/vue_shared/components/help_popover_spec.js index 64dce194327..6fd5ae0e946 100644 --- a/spec/frontend/vue_shared/components/help_popover_spec.js +++ b/spec/frontend/vue_shared/components/help_popover_spec.js @@ -7,8 +7,8 @@ describe('HelpPopover', () => { const title = 'popover <strong>title</strong>'; const content = 'popover <b>content</b>'; - const findQuestionButton = () => wrapper.find(GlButton); - const findPopover = () => wrapper.find(GlPopover); + const findQuestionButton = () => wrapper.findComponent(GlButton); + const findPopover = () => wrapper.findComponent(GlPopover); const createComponent = ({ props, ...opts } = {}) => { wrapper = mount(HelpPopover, { diff --git a/spec/frontend/vue_shared/components/integration_help_text_spec.js b/spec/frontend/vue_shared/components/integration_help_text_spec.js index c0e8b719007..c63e46313b3 100644 --- a/spec/frontend/vue_shared/components/integration_help_text_spec.js +++ b/spec/frontend/vue_shared/components/integration_help_text_spec.js @@ -30,9 +30,9 @@ describe('IntegrationHelpText component', () => { it('should use the gl components', () => { wrapper = createComponent(); - expect(wrapper.find(GlSprintf).exists()).toBe(true); - expect(wrapper.find(GlIcon).exists()).toBe(true); - expect(wrapper.find(GlLink).exists()).toBe(true); + expect(wrapper.findComponent(GlSprintf).exists()).toBe(true); + expect(wrapper.findComponent(GlIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlLink).exists()).toBe(true); }); it('should render the help text', () => { @@ -44,9 +44,9 @@ describe('IntegrationHelpText component', () => { it('should not use the gl-link and gl-icon components', () => { wrapper = createComponent({ message: 'Click nowhere!' }); - expect(wrapper.find(GlSprintf).exists()).toBe(true); - expect(wrapper.find(GlIcon).exists()).toBe(false); - expect(wrapper.find(GlLink).exists()).toBe(false); + expect(wrapper.findComponent(GlSprintf).exists()).toBe(true); + expect(wrapper.findComponent(GlIcon).exists()).toBe(false); + expect(wrapper.findComponent(GlLink).exists()).toBe(false); }); it('should not render the link when start and end is not provided', () => { diff --git a/spec/frontend/vue_shared/components/markdown/field_spec.js b/spec/frontend/vue_shared/components/markdown/field_spec.js index 85a135d2b89..50864a4bf25 100644 --- a/spec/frontend/vue_shared/components/markdown/field_spec.js +++ b/spec/frontend/vue_shared/components/markdown/field_spec.js @@ -76,7 +76,7 @@ describe('Markdown field component', () => { const getMarkdownButton = () => subject.find('.js-md'); const getListBulletedButton = () => subject.findAll('.js-md[title="Add a bullet list"]'); const getVideo = () => subject.find('video'); - const getAttachButton = () => subject.find('.button-attach-file'); + const getAttachButton = () => subject.findByTestId('button-attach-file'); const clickAttachButton = () => getAttachButton().trigger('click'); const findDropzone = () => subject.find('.div-dropzone'); const findMarkdownHeader = () => subject.findComponent(MarkdownFieldHeader); @@ -232,13 +232,10 @@ describe('Markdown field component', () => { }); }); - it('should render attach a file button', () => { - expect(getAttachButton().text()).toBe('Attach a file'); - }); - it('should trigger dropzone when attach button is clicked', () => { expect(dropzoneSpy).not.toHaveBeenCalled(); + getAttachButton().trigger('click'); clickAttachButton(); expect(dropzoneSpy).toHaveBeenCalled(); diff --git a/spec/frontend/vue_shared/components/markdown/header_spec.js b/spec/frontend/vue_shared/components/markdown/header_spec.js index 67222cab247..9831908f806 100644 --- a/spec/frontend/vue_shared/components/markdown/header_spec.js +++ b/spec/frontend/vue_shared/components/markdown/header_spec.js @@ -21,7 +21,7 @@ describe('Markdown field header component', () => { const findWriteTab = () => wrapper.findByTestId('write-tab'); const findPreviewTab = () => wrapper.findByTestId('preview-tab'); const findToolbar = () => wrapper.findByTestId('md-header-toolbar'); - const findToolbarButtons = () => wrapper.findAll(ToolbarButton); + const findToolbarButtons = () => wrapper.findAllComponents(ToolbarButton); const findToolbarButtonByProp = (prop, value) => findToolbarButtons() .filter((button) => button.props(prop) === value) @@ -44,16 +44,16 @@ describe('Markdown field header component', () => { describe('markdown header buttons', () => { it('renders the buttons with the correct title', () => { const buttons = [ + 'Insert suggestion', 'Add bold text (⌘B)', 'Add italic text (⌘I)', 'Add strikethrough text (⌘⇧X)', 'Insert a quote', - 'Insert suggestion', 'Insert code', 'Add a link (⌘K)', 'Add a bullet list', 'Add a numbered list', - 'Add a task list', + 'Add a checklist', 'Add a collapsible section', 'Add a table', 'Go full screen', @@ -65,6 +65,13 @@ describe('Markdown field header component', () => { }); }); + it('renders "Attach a file or image" button using gl-button', () => { + const button = wrapper.findByTestId('button-attach-file'); + + expect(button.element.tagName).toBe('GL-BUTTON-STUB'); + expect(button.attributes('title')).toBe('Attach a file or image'); + }); + describe('when the user is on a non-Mac', () => { beforeEach(() => { delete window.gl.client.isMac; @@ -118,8 +125,8 @@ describe('Markdown field header component', () => { ), ]); - expect(wrapper.emitted('preview-markdown')).toBeFalsy(); - expect(wrapper.emitted('write-markdown')).toBeFalsy(); + expect(wrapper.emitted('preview-markdown')).toBeUndefined(); + expect(wrapper.emitted('write-markdown')).toBeUndefined(); }); it('blurs preview link after click', () => { diff --git a/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js b/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js index 9944267cf24..9db1b779a04 100644 --- a/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js +++ b/spec/frontend/vue_shared/components/markdown/suggestion_diff_header_spec.js @@ -38,13 +38,13 @@ describe('Suggestion Diff component', () => { wrapper.destroy(); }); - const findApplyButton = () => wrapper.find(ApplySuggestion); + const findApplyButton = () => wrapper.findComponent(ApplySuggestion); const findApplyBatchButton = () => wrapper.find('.js-apply-batch-btn'); const findAddToBatchButton = () => wrapper.find('.js-add-to-batch-btn'); const findRemoveFromBatchButton = () => wrapper.find('.js-remove-from-batch-btn'); const findHeader = () => wrapper.find('.js-suggestion-diff-header'); const findHelpButton = () => wrapper.find('.js-help-btn'); - const findLoading = () => wrapper.find(GlLoadingIcon); + const findLoading = () => wrapper.findComponent(GlLoadingIcon); it('renders a suggestion header', () => { createComponent(); diff --git a/spec/frontend/vue_shared/components/markdown/suggestion_diff_spec.js b/spec/frontend/vue_shared/components/markdown/suggestion_diff_spec.js index af27e953776..d84483c1663 100644 --- a/spec/frontend/vue_shared/components/markdown/suggestion_diff_spec.js +++ b/spec/frontend/vue_shared/components/markdown/suggestion_diff_spec.js @@ -71,7 +71,7 @@ describe('Suggestion Diff component', () => { }); it('renders a correct amount of suggestion diff rows', () => { - expect(wrapper.findAll(SuggestionDiffRow)).toHaveLength(3); + expect(wrapper.findAllComponents(SuggestionDiffRow)).toHaveLength(3); }); it.each` @@ -81,14 +81,14 @@ describe('Suggestion Diff component', () => { ${'addToBatch'} | ${[]} | ${[suggestionId]} ${'removeFromBatch'} | ${[]} | ${[suggestionId]} `('emits $event event on sugestion diff header $event', ({ event, childArgs, args }) => { - wrapper.find(SuggestionDiffHeader).vm.$emit(event, ...childArgs); + wrapper.findComponent(SuggestionDiffHeader).vm.$emit(event, ...childArgs); expect(wrapper.emitted(event)).toBeDefined(); expect(wrapper.emitted(event)).toEqual([args]); }); it('passes suggestion batch props to suggestion diff header', () => { - expect(wrapper.find(SuggestionDiffHeader).props()).toMatchObject({ + expect(wrapper.findComponent(SuggestionDiffHeader).props()).toMatchObject({ batchSuggestionsCount: 1, isBatched: true, isApplyingBatch: MOCK_DATA.suggestion.is_applying_batch, diff --git a/spec/frontend/vue_shared/components/markdown/toolbar_button_spec.js b/spec/frontend/vue_shared/components/markdown/toolbar_button_spec.js index 19e4f2d8c92..82210e79799 100644 --- a/spec/frontend/vue_shared/components/markdown/toolbar_button_spec.js +++ b/spec/frontend/vue_shared/components/markdown/toolbar_button_spec.js @@ -26,7 +26,7 @@ describe('toolbar_button', () => { }); const getButtonShortcutsAttr = () => { - return wrapper.find(GlButton).attributes('data-md-shortcuts'); + return wrapper.findComponent(GlButton).attributes('data-md-shortcuts'); }; describe('keyboard shortcuts', () => { diff --git a/spec/frontend/vue_shared/components/memory_graph_spec.js b/spec/frontend/vue_shared/components/memory_graph_spec.js index 53b96bd1b98..ae8d5ff78ba 100644 --- a/spec/frontend/vue_shared/components/memory_graph_spec.js +++ b/spec/frontend/vue_shared/components/memory_graph_spec.js @@ -47,7 +47,7 @@ describe('MemoryGraph', () => { it('should draw container with chart', () => { expect(wrapper.element).toMatchSnapshot(); expect(wrapper.find('.memory-graph-container').exists()).toBe(true); - expect(wrapper.find(GlSparklineChart).exists()).toBe(true); + expect(wrapper.findComponent(GlSparklineChart).exists()).toBe(true); }); }); }); diff --git a/spec/frontend/vue_shared/components/metric_images/metric_images_tab_spec.js b/spec/frontend/vue_shared/components/metric_images/metric_images_tab_spec.js index 2cefa77b72d..1789610dba9 100644 --- a/spec/frontend/vue_shared/components/metric_images/metric_images_tab_spec.js +++ b/spec/frontend/vue_shared/components/metric_images/metric_images_tab_spec.js @@ -114,7 +114,7 @@ describe('Metric images tab', () => { await waitForPromises(); - expect(findModal().attributes('visible')).toBeFalsy(); + expect(findModal().attributes('visible')).toBeUndefined(); }); it('should add files and url when selected', async () => { diff --git a/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js b/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js index c11b20a692e..2c14d65186b 100644 --- a/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js +++ b/spec/frontend/vue_shared/components/namespace_select/namespace_select_spec.js @@ -1,5 +1,12 @@ import { nextTick } from 'vue'; -import { GlDropdown, GlDropdownItem, GlDropdownSectionHeader, GlSearchBoxByType } from '@gitlab/ui'; +import { + GlDropdown, + GlDropdownItem, + GlDropdownSectionHeader, + GlSearchBoxByType, + GlIntersectionObserver, + GlLoadingIcon, +} from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import NamespaceSelect, { i18n, @@ -7,7 +14,7 @@ import NamespaceSelect, { } from '~/vue_shared/components/namespace_select/namespace_select.vue'; import { userNamespaces, groupNamespaces } from './mock_data'; -const FLAT_NAMESPACES = [...groupNamespaces, ...userNamespaces]; +const FLAT_NAMESPACES = [...userNamespaces, ...groupNamespaces]; const EMPTY_NAMESPACE_TITLE = 'Empty namespace TEST'; const EMPTY_NAMESPACE_ITEM = { id: EMPTY_NAMESPACE_ID, humanName: EMPTY_NAMESPACE_TITLE }; @@ -31,6 +38,8 @@ describe('Namespace Select', () => { const findDropdown = () => wrapper.findComponent(GlDropdown); const findDropdownText = () => findDropdown().props('text'); const findDropdownItems = () => wrapper.findAllComponents(GlDropdownItem); + const findGroupDropdownItems = () => + wrapper.findByTestId('namespace-list-groups').findAllComponents(GlDropdownItem); const findDropdownItemsTexts = () => findDropdownItems().wrappers.map((x) => x.text()); const findSectionHeaders = () => wrapper.findAllComponents(GlDropdownSectionHeader); const findSearchBox = () => wrapper.findComponent(GlSearchBoxByType); @@ -59,7 +68,7 @@ describe('Namespace Select', () => { it('splits group and user namespaces', () => { const headers = findSectionHeaders(); - expect(wrappersText(headers)).toEqual([i18n.GROUPS, i18n.USERS]); + expect(wrappersText(headers)).toEqual([i18n.USERS, i18n.GROUPS]); }); it('does not render wrapper as full width', () => { @@ -89,18 +98,20 @@ describe('Namespace Select', () => { describe('with search', () => { it.each` - term | includeEmptyNamespace | expectedItems - ${''} | ${false} | ${[...groupNamespaces, ...userNamespaces]} - ${'sub'} | ${false} | ${[groupNamespaces[1]]} - ${'User'} | ${false} | ${[...userNamespaces]} - ${'User'} | ${true} | ${[...userNamespaces]} - ${'namespace'} | ${true} | ${[EMPTY_NAMESPACE_ITEM, ...userNamespaces]} + term | includeEmptyNamespace | shouldFilterNamespaces | expectedItems + ${''} | ${false} | ${true} | ${[...userNamespaces, ...groupNamespaces]} + ${'sub'} | ${false} | ${true} | ${[groupNamespaces[1]]} + ${'User'} | ${false} | ${true} | ${[...userNamespaces]} + ${'User'} | ${true} | ${true} | ${[...userNamespaces]} + ${'namespace'} | ${true} | ${true} | ${[EMPTY_NAMESPACE_ITEM, ...userNamespaces]} + ${'sub'} | ${false} | ${false} | ${[...userNamespaces, ...groupNamespaces]} `( - 'with term=$term and includeEmptyNamespace=$includeEmptyNamespace, should show $expectedItems.length', - async ({ term, includeEmptyNamespace, expectedItems }) => { + 'with term=$term, includeEmptyNamespace=$includeEmptyNamespace, and shouldFilterNamespaces=$shouldFilterNamespaces should show $expectedItems.length', + async ({ term, includeEmptyNamespace, shouldFilterNamespaces, expectedItems }) => { wrapper = createComponent({ includeEmptyNamespace, emptyNamespaceTitle: EMPTY_NAMESPACE_TITLE, + shouldFilterNamespaces, }); search(term); @@ -114,6 +125,18 @@ describe('Namespace Select', () => { ); }); + describe('when search is typed in', () => { + it('emits `search` event', async () => { + wrapper = createComponent(); + + wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', 'foo'); + + await nextTick(); + + expect(wrapper.emitted('search')).toEqual([['foo']]); + }); + }); + describe('with a selected namespace', () => { const selectedGroupIndex = 1; const selectedItem = groupNamespaces[selectedGroupIndex]; @@ -121,7 +144,8 @@ describe('Namespace Select', () => { beforeEach(() => { wrapper = createComponent(); - findDropdownItems().at(selectedGroupIndex).vm.$emit('click'); + wrapper.findComponent(GlSearchBoxByType).vm.$emit('input', 'foo'); + findGroupDropdownItems().at(selectedGroupIndex).vm.$emit('click'); }); it('sets the dropdown text', () => { @@ -132,6 +156,10 @@ describe('Namespace Select', () => { const args = [selectedItem]; expect(wrapper.emitted('select')).toEqual([args]); }); + + it('clears search', () => { + expect(wrapper.findComponent(GlSearchBoxByType).props('value')).toBe(''); + }); }); describe('with an empty namespace option', () => { @@ -166,4 +194,33 @@ describe('Namespace Select', () => { expect(findDropdownItemsTexts().includes(EMPTY_NAMESPACE_TITLE)).toBe(shouldShow); }); }); + + describe('when `hasNextPageOfGroups` prop is `true`', () => { + it('renders `GlIntersectionObserver` and emits `load-more-groups` event when bottom is reached', () => { + wrapper = createComponent({ hasNextPageOfGroups: true }); + + const intersectionObserver = wrapper.findComponent(GlIntersectionObserver); + + intersectionObserver.vm.$emit('appear'); + + expect(intersectionObserver.exists()).toBe(true); + expect(wrapper.emitted('load-more-groups')).toEqual([[]]); + }); + + describe('when `isLoadingMoreGroups` prop is `true`', () => { + it('renders a loading icon', () => { + wrapper = createComponent({ hasNextPageOfGroups: true, isLoadingMoreGroups: true }); + + expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); + }); + }); + }); + + describe('when `isSearchLoading` prop is `true`', () => { + it('sets `isLoading` prop to `true`', () => { + wrapper = createComponent({ isSearchLoading: true }); + + expect(wrapper.findComponent(GlSearchBoxByType).props('isLoading')).toBe(true); + }); + }); }); diff --git a/spec/frontend/vue_shared/components/navigation_tabs_spec.js b/spec/frontend/vue_shared/components/navigation_tabs_spec.js index 30a89fed12f..b1bec28bffb 100644 --- a/spec/frontend/vue_shared/components/navigation_tabs_spec.js +++ b/spec/frontend/vue_shared/components/navigation_tabs_spec.js @@ -44,7 +44,7 @@ describe('navigation tabs component', () => { }); it('should render tabs', () => { - expect(wrapper.findAll(GlTab)).toHaveLength(data.length); + expect(wrapper.findAllComponents(GlTab)).toHaveLength(data.length); }); it('should render active tab', () => { diff --git a/spec/frontend/vue_shared/components/notes/noteable_warning_spec.js b/spec/frontend/vue_shared/components/notes/noteable_warning_spec.js index 99b65ca6937..17a62ae8a33 100644 --- a/spec/frontend/vue_shared/components/notes/noteable_warning_spec.js +++ b/spec/frontend/vue_shared/components/notes/noteable_warning_spec.js @@ -6,10 +6,11 @@ import NoteableWarning from '~/vue_shared/components/notes/noteable_warning.vue' describe('Issue Warning Component', () => { let wrapper; - const findIcon = (w = wrapper) => w.find(GlIcon); - const findLockedBlock = (w = wrapper) => w.find({ ref: 'locked' }); - const findConfidentialBlock = (w = wrapper) => w.find({ ref: 'confidential' }); - const findLockedAndConfidentialBlock = (w = wrapper) => w.find({ ref: 'lockedAndConfidential' }); + const findIcon = (w = wrapper) => w.findComponent(GlIcon); + const findLockedBlock = (w = wrapper) => w.findComponent({ ref: 'locked' }); + const findConfidentialBlock = (w = wrapper) => w.findComponent({ ref: 'confidential' }); + const findLockedAndConfidentialBlock = (w = wrapper) => + w.findComponent({ ref: 'lockedAndConfidential' }); const createComponent = (props) => shallowMount(NoteableWarning, { @@ -73,7 +74,7 @@ describe('Issue Warning Component', () => { }); it('renders warning icon', () => { - expect(wrapper.find(GlIcon).exists()).toBe(true); + expect(wrapper.findComponent(GlIcon).exists()).toBe(true); }); it('does not render information about locked noteable', () => { @@ -99,7 +100,7 @@ describe('Issue Warning Component', () => { }); it('does not render warning icon', () => { - expect(wrapper.find(GlIcon).exists()).toBe(false); + expect(wrapper.findComponent(GlIcon).exists()).toBe(false); }); it('does not render information about locked noteable', () => { diff --git a/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js b/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js index f951cfd5cd9..b86c8946e96 100644 --- a/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js +++ b/spec/frontend/vue_shared/components/notes/placeholder_note_spec.js @@ -14,7 +14,7 @@ const getters = { describe('Issue placeholder note component', () => { let wrapper; - const findNote = () => wrapper.find({ ref: 'note' }); + const findNote = () => wrapper.findComponent({ ref: 'note' }); const createComponent = (isIndividual = false, propsData = {}) => { wrapper = shallowMount(IssuePlaceholderNote, { diff --git a/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js b/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js index 51a936c0509..c0c3c4a9729 100644 --- a/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js +++ b/spec/frontend/vue_shared/components/paginated_table_with_search_and_tabs/paginated_table_with_search_and_tabs_spec.js @@ -92,15 +92,15 @@ describe('AlertManagementEmptyState', () => { const EmptyState = () => wrapper.find('.empty-state'); const ItemsTable = () => wrapper.find('.gl-table'); - const ErrorAlert = () => wrapper.find(GlAlert); - const Pagination = () => wrapper.find(GlPagination); - const Tabs = () => wrapper.find(GlTabs); + const ErrorAlert = () => wrapper.findComponent(GlAlert); + const Pagination = () => wrapper.findComponent(GlPagination); + const Tabs = () => wrapper.findComponent(GlTabs); const ActionButton = () => wrapper.find('.header-actions > button'); - const Filters = () => wrapper.find(FilteredSearchBar); - const findPagination = () => wrapper.find(GlPagination); - const findStatusFilterTabs = () => wrapper.findAll(GlTab); - const findStatusTabs = () => wrapper.find(GlTabs); - const findStatusFilterBadge = () => wrapper.findAll(GlBadge); + const Filters = () => wrapper.findComponent(FilteredSearchBar); + const findPagination = () => wrapper.findComponent(GlPagination); + const findStatusFilterTabs = () => wrapper.findAllComponents(GlTab); + const findStatusTabs = () => wrapper.findComponent(GlTabs); + const findStatusFilterBadge = () => wrapper.findAllComponents(GlBadge); describe('Snowplow tracking', () => { beforeEach(() => { @@ -213,7 +213,7 @@ describe('AlertManagementEmptyState', () => { }); it('should render pagination', () => { - expect(wrapper.find(GlPagination).exists()).toBe(true); + expect(wrapper.findComponent(GlPagination).exists()).toBe(true); }); describe('prevPage', () => { diff --git a/spec/frontend/vue_shared/components/pagination_bar/pagination_bar_spec.js b/spec/frontend/vue_shared/components/pagination_bar/pagination_bar_spec.js index 08119dee8af..b3be2f8a775 100644 --- a/spec/frontend/vue_shared/components/pagination_bar/pagination_bar_spec.js +++ b/spec/frontend/vue_shared/components/pagination_bar/pagination_bar_spec.js @@ -64,7 +64,7 @@ describe('Pagination bar', () => { }, }); - expect(wrapper.find(GlDropdown).find('button').text()).toMatchInterpolatedText( + expect(wrapper.findComponent(GlDropdown).find('button').text()).toMatchInterpolatedText( `${CURRENT_PAGE_SIZE} items per page`, ); }); diff --git a/spec/frontend/vue_shared/components/pagination_links_spec.js b/spec/frontend/vue_shared/components/pagination_links_spec.js index 83f1e2844f9..d444ad7a733 100644 --- a/spec/frontend/vue_shared/components/pagination_links_spec.js +++ b/spec/frontend/vue_shared/components/pagination_links_spec.js @@ -41,7 +41,7 @@ describe('Pagination links component', () => { beforeEach(() => { createComponent(); - glPagination = wrapper.find(GlPagination); + glPagination = wrapper.findComponent(GlPagination); }); afterEach(() => { diff --git a/spec/frontend/vue_shared/components/project_avatar_spec.js b/spec/frontend/vue_shared/components/project_avatar_spec.js index d55f3127a74..af828fbca51 100644 --- a/spec/frontend/vue_shared/components/project_avatar_spec.js +++ b/spec/frontend/vue_shared/components/project_avatar_spec.js @@ -42,6 +42,42 @@ describe('ProjectAvatar', () => { }); }); + describe('with `projectId` prop', () => { + const validatorFunc = ProjectAvatar.props.projectId.validator; + + it('prop validators return true for valid types', () => { + expect(validatorFunc(1)).toBe(true); + expect(validatorFunc('gid://gitlab/Project/1')).toBe(true); + }); + + it('prop validators return false for invalid types', () => { + expect(validatorFunc('1')).toBe(false); + }); + + it('renders GlAvatar with `entityId` 0 when `projectId` is not informed', () => { + createComponent({ props: { projectId: undefined } }); + + const avatar = findGlAvatar(); + expect(avatar.props('entityId')).toBe(0); + }); + + it('renders GlAvatar with specified `entityId` when `projectId` is a Number', () => { + const mockProjectId = 1; + createComponent({ props: { projectId: mockProjectId } }); + + const avatar = findGlAvatar(); + expect(avatar.props('entityId')).toBe(mockProjectId); + }); + + it('renders GlAvatar with specified `entityId` when `projectId` is a gid String', () => { + const mockProjectId = 'gid://gitlab/Project/1'; + createComponent({ props: { projectId: mockProjectId } }); + + const avatar = findGlAvatar(); + expect(avatar.props('entityId')).toBe(1); + }); + }); + describe('with `projectAvatarUrl` prop', () => { it('renders GlAvatar with specified `src` prop', () => { const mockProjectAvatarUrl = 'https://gitlab.com'; diff --git a/spec/frontend/vue_shared/components/project_selector/project_list_item_spec.js b/spec/frontend/vue_shared/components/project_selector/project_list_item_spec.js index 397ab2254b9..4e0c318c84e 100644 --- a/spec/frontend/vue_shared/components/project_selector/project_list_item_spec.js +++ b/spec/frontend/vue_shared/components/project_selector/project_list_item_spec.js @@ -56,6 +56,7 @@ describe('ProjectListItem component', () => { expect(avatar.exists()).toBe(true); expect(avatar.props()).toMatchObject({ + projectId: project.id, projectAvatarUrl: '', projectName: project.name_with_namespace, }); diff --git a/spec/frontend/vue_shared/components/project_selector/project_selector_spec.js b/spec/frontend/vue_shared/components/project_selector/project_selector_spec.js index 379e60c1b2d..a0832dd7030 100644 --- a/spec/frontend/vue_shared/components/project_selector/project_selector_spec.js +++ b/spec/frontend/vue_shared/components/project_selector/project_selector_spec.js @@ -15,7 +15,7 @@ describe('ProjectSelector component', () => { let selected = []; selected = selected.concat(allProjects.slice(0, 3)).concat(allProjects.slice(5, 8)); - const findSearchInput = () => wrapper.find(GlSearchBoxByType).find('input'); + const findSearchInput = () => wrapper.findComponent(GlSearchBoxByType).find('input'); const findLegendText = () => wrapper.find('[data-testid="legend-text"]').text(); const search = (query) => { const searchInput = findSearchInput(); @@ -65,14 +65,14 @@ describe('ProjectSelector component', () => { it(`triggers a "bottomReached" event when user has scrolled to the bottom of the list`, () => { jest.spyOn(vm, '$emit').mockImplementation(() => {}); - wrapper.find(GlInfiniteScroll).vm.$emit('bottomReached'); + wrapper.findComponent(GlInfiniteScroll).vm.$emit('bottomReached'); expect(vm.$emit).toHaveBeenCalledWith('bottomReached'); }); it(`triggers a "projectClicked" event when a project is clicked`, () => { jest.spyOn(vm, '$emit').mockImplementation(() => {}); - wrapper.find(ProjectListItem).vm.$emit('click', head(searchResults)); + wrapper.findComponent(ProjectListItem).vm.$emit('click', head(searchResults)); expect(vm.$emit).toHaveBeenCalledWith('projectClicked', head(searchResults)); }); diff --git a/spec/frontend/vue_shared/components/registry/code_instruction_spec.js b/spec/frontend/vue_shared/components/registry/code_instruction_spec.js index 3a2ea263a05..8f19f0ea14d 100644 --- a/spec/frontend/vue_shared/components/registry/code_instruction_spec.js +++ b/spec/frontend/vue_shared/components/registry/code_instruction_spec.js @@ -22,7 +22,7 @@ describe('Package code instruction', () => { }); } - const findCopyButton = () => wrapper.find(ClipboardButton); + const findCopyButton = () => wrapper.findComponent(ClipboardButton); const findInputElement = () => wrapper.find('[data-testid="instruction-input"]'); const findMultilineInstruction = () => wrapper.find('[data-testid="multiline-instruction"]'); diff --git a/spec/frontend/vue_shared/components/registry/details_row_spec.js b/spec/frontend/vue_shared/components/registry/details_row_spec.js index 3134e0d3e21..ebc9816f983 100644 --- a/spec/frontend/vue_shared/components/registry/details_row_spec.js +++ b/spec/frontend/vue_shared/components/registry/details_row_spec.js @@ -5,7 +5,7 @@ import component from '~/vue_shared/components/registry/details_row.vue'; describe('DetailsRow', () => { let wrapper; - const findIcon = () => wrapper.find(GlIcon); + const findIcon = () => wrapper.findComponent(GlIcon); const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]'); const mountComponent = (props) => { diff --git a/spec/frontend/vue_shared/components/registry/history_item_spec.js b/spec/frontend/vue_shared/components/registry/history_item_spec.js index f146f87342f..947520567e6 100644 --- a/spec/frontend/vue_shared/components/registry/history_item_spec.js +++ b/spec/frontend/vue_shared/components/registry/history_item_spec.js @@ -27,8 +27,8 @@ describe('History Item', () => { wrapper = null; }); - const findTimelineEntry = () => wrapper.find(TimelineEntryItem); - const findGlIcon = () => wrapper.find(GlIcon); + const findTimelineEntry = () => wrapper.findComponent(TimelineEntryItem); + const findGlIcon = () => wrapper.findComponent(GlIcon); const findDefaultSlot = () => wrapper.find('[data-testid="default-slot"]'); const findBodySlot = () => wrapper.find('[data-testid="body-slot"]'); diff --git a/spec/frontend/vue_shared/components/registry/list_item_spec.js b/spec/frontend/vue_shared/components/registry/list_item_spec.js index 6e9abb2bfb3..b941eb77c32 100644 --- a/spec/frontend/vue_shared/components/registry/list_item_spec.js +++ b/spec/frontend/vue_shared/components/registry/list_item_spec.js @@ -13,7 +13,7 @@ describe('list item', () => { const findRightSecondarySlot = () => wrapper.find('[data-testid="right-secondary"]'); const findRightActionSlot = () => wrapper.find('[data-testid="right-action"]'); const findDetailsSlot = (name) => wrapper.find(`[data-testid="${name}"]`); - const findToggleDetailsButton = () => wrapper.find(GlButton); + const findToggleDetailsButton = () => wrapper.findComponent(GlButton); const mountComponent = (propsData, slots) => { wrapper = shallowMount(component, { diff --git a/spec/frontend/vue_shared/components/registry/metadata_item_spec.js b/spec/frontend/vue_shared/components/registry/metadata_item_spec.js index e4abdc15fd5..a04e1e237d4 100644 --- a/spec/frontend/vue_shared/components/registry/metadata_item_spec.js +++ b/spec/frontend/vue_shared/components/registry/metadata_item_spec.js @@ -24,10 +24,10 @@ describe('Metadata Item', () => { wrapper = null; }); - const findIcon = () => wrapper.find(GlIcon); - const findLink = (w = wrapper) => w.find(GlLink); + const findIcon = () => wrapper.findComponent(GlIcon); + const findLink = (w = wrapper) => w.findComponent(GlLink); const findText = () => wrapper.find('[data-testid="metadata-item-text"]'); - const findTooltipOnTruncate = (w = wrapper) => w.find(TooltipOnTruncate); + const findTooltipOnTruncate = (w = wrapper) => w.findComponent(TooltipOnTruncate); const findTextTooltip = () => wrapper.find('[data-testid="text-tooltip-container"]'); describe.each(['xs', 's', 'm', 'l', 'xl'])('size class', (size) => { diff --git a/spec/frontend/vue_shared/components/registry/registry_search_spec.js b/spec/frontend/vue_shared/components/registry/registry_search_spec.js index 20716e79a04..70f4693ae81 100644 --- a/spec/frontend/vue_shared/components/registry/registry_search_spec.js +++ b/spec/frontend/vue_shared/components/registry/registry_search_spec.js @@ -6,9 +6,9 @@ import component from '~/vue_shared/components/registry/registry_search.vue'; describe('Registry Search', () => { let wrapper; - const findPackageListSorting = () => wrapper.find(GlSorting); - const findSortingItems = () => wrapper.findAll(GlSortingItem); - const findFilteredSearch = () => wrapper.find(GlFilteredSearch); + const findPackageListSorting = () => wrapper.findComponent(GlSorting); + const findSortingItems = () => wrapper.findAllComponents(GlSortingItem); + const findFilteredSearch = () => wrapper.findComponent(GlFilteredSearch); const defaultProps = { filters: [], diff --git a/spec/frontend/vue_shared/components/registry/title_area_spec.js b/spec/frontend/vue_shared/components/registry/title_area_spec.js index b62676b35be..efb57ddd310 100644 --- a/spec/frontend/vue_shared/components/registry/title_area_spec.js +++ b/spec/frontend/vue_shared/components/registry/title_area_spec.js @@ -199,7 +199,7 @@ describe('title area', () => { const message = findInfoMessages().at(0); - expect(message.find(GlLink).attributes('href')).toBe('bar'); + expect(message.findComponent(GlLink).attributes('href')).toBe('bar'); expect(message.text()).toBe('foo link'); }); diff --git a/spec/frontend/vue_shared/components/rich_timestamp_tooltip_spec.js b/spec/frontend/vue_shared/components/rich_timestamp_tooltip_spec.js new file mode 100644 index 00000000000..5d96fe27676 --- /dev/null +++ b/spec/frontend/vue_shared/components/rich_timestamp_tooltip_spec.js @@ -0,0 +1,41 @@ +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; + +import { formatDate } from '~/lib/utils/datetime_utility'; +import RichTimestampTooltip from '~/vue_shared/components/rich_timestamp_tooltip.vue'; + +describe('RichTimestampTooltip', () => { + const currentDate = new Date(); + const mockRawTimestamp = currentDate.toISOString(); + const mockTimestamp = formatDate(currentDate); + let wrapper; + + const createComponent = ({ + target = 'some-element', + rawTimestamp = mockRawTimestamp, + timestampTypeText = 'Created', + } = {}) => { + wrapper = shallowMountExtended(RichTimestampTooltip, { + propsData: { + target, + rawTimestamp, + timestampTypeText, + }, + }); + }; + + beforeEach(() => { + createComponent(); + }); + + afterEach(() => { + wrapper.destroy(); + }); + + it('renders the tooltip text header', () => { + expect(wrapper.findByTestId('header-text').text()).toBe('Created just now'); + }); + + it('renders the tooltip text body', () => { + expect(wrapper.findByTestId('body-text').text()).toBe(mockTimestamp); + }); +}); diff --git a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js index a38dcd626f4..7c5fc63856a 100644 --- a/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js +++ b/spec/frontend/vue_shared/components/runner_instructions/runner_instructions_modal_spec.js @@ -166,7 +166,7 @@ describe('RunnerInstructionsModal component', () => { }); it('sets the focus on the default selected platform', () => { - const findOsxPlatformButton = () => wrapper.find({ ref: 'osx' }); + const findOsxPlatformButton = () => wrapper.findComponent({ ref: 'osx' }); findOsxPlatformButton().element.focus = jest.fn(); @@ -234,14 +234,14 @@ describe('RunnerInstructionsModal component', () => { MockResizeObserver.mockResize('xs'); await nextTick(); - expect(findPlatformButtonGroup().attributes('vertical')).toBeTruthy(); + expect(findPlatformButtonGroup().attributes('vertical')).toEqual('true'); }); it('to a non-xs viewport', async () => { MockResizeObserver.mockResize('sm'); await nextTick(); - expect(findPlatformButtonGroup().props('vertical')).toBeFalsy(); + expect(findPlatformButtonGroup().props('vertical')).toBeUndefined(); }); }); }); diff --git a/spec/frontend/vue_shared/components/security_reports/artifact_downloads/merge_request_artifact_download_spec.js b/spec/frontend/vue_shared/components/security_reports/artifact_downloads/merge_request_artifact_download_spec.js index 71ebe561def..c5672bc28cc 100644 --- a/spec/frontend/vue_shared/components/security_reports/artifact_downloads/merge_request_artifact_download_spec.js +++ b/spec/frontend/vue_shared/components/security_reports/artifact_downloads/merge_request_artifact_download_spec.js @@ -50,7 +50,7 @@ describe('Merge request artifact Download', () => { return createMockApollo(requestHandlers); }; - const findDownloadDropdown = () => wrapper.find(SecurityReportDownloadDropdown); + const findDownloadDropdown = () => wrapper.findComponent(SecurityReportDownloadDropdown); afterEach(() => { wrapper.destroy(); diff --git a/spec/frontend/vue_shared/components/security_reports/help_icon_spec.js b/spec/frontend/vue_shared/components/security_reports/help_icon_spec.js index ae86106d86e..08d3d5b19d4 100644 --- a/spec/frontend/vue_shared/components/security_reports/help_icon_spec.js +++ b/spec/frontend/vue_shared/components/security_reports/help_icon_spec.js @@ -17,9 +17,9 @@ describe('HelpIcon component', () => { }); }; - const findLink = () => wrapper.find(GlLink); - const findPopover = () => wrapper.find(GlPopover); - const findPopoverTarget = () => wrapper.find({ ref: 'discoverProjectSecurity' }); + const findLink = () => wrapper.findComponent(GlLink); + const findPopover = () => wrapper.findComponent(GlPopover); + const findPopoverTarget = () => wrapper.findComponent({ ref: 'discoverProjectSecurity' }); afterEach(() => { wrapper.destroy(); diff --git a/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js b/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js index f213e37cbc1..9b1316677d7 100644 --- a/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/issuable_move_dropdown_spec.js @@ -173,15 +173,15 @@ describe('IssuableMoveDropdown', () => { }); describe('template', () => { - const findDropdownEl = () => wrapper.find(GlDropdown); + const findDropdownEl = () => wrapper.findComponent(GlDropdown); it('renders collapsed state element with icon', () => { const collapsedEl = wrapper.find('[data-testid="move-collapsed"]'); expect(collapsedEl.exists()).toBe(true); expect(collapsedEl.attributes('title')).toBe(mockProps.dropdownButtonTitle); - expect(collapsedEl.find(GlIcon).exists()).toBe(true); - expect(collapsedEl.find(GlIcon).props('name')).toBe('arrow-right'); + expect(collapsedEl.findComponent(GlIcon).exists()).toBe(true); + expect(collapsedEl.findComponent(GlIcon).props('name')).toBe('arrow-right'); }); describe('gl-dropdown component', () => { @@ -191,7 +191,7 @@ describe('IssuableMoveDropdown', () => { }); it('renders gl-dropdown-form component', () => { - expect(findDropdownEl().find(GlDropdownForm).exists()).toBe(true); + expect(findDropdownEl().findComponent(GlDropdownForm).exists()).toBe(true); }); it('renders header element', () => { @@ -199,11 +199,11 @@ describe('IssuableMoveDropdown', () => { expect(headerEl.exists()).toBe(true); expect(headerEl.find('span').text()).toBe(mockProps.dropdownHeaderTitle); - expect(headerEl.find(GlButton).props('icon')).toBe('close'); + expect(headerEl.findComponent(GlButton).props('icon')).toBe('close'); }); it('renders gl-search-box-by-type component', () => { - const searchEl = findDropdownEl().find(GlSearchBoxByType); + const searchEl = findDropdownEl().findComponent(GlSearchBoxByType); expect(searchEl.exists()).toBe(true); expect(searchEl.attributes()).toMatchObject({ @@ -221,7 +221,7 @@ describe('IssuableMoveDropdown', () => { await nextTick(); - expect(findDropdownEl().find(GlLoadingIcon).exists()).toBe(true); + expect(findDropdownEl().findComponent(GlLoadingIcon).exists()).toBe(true); }); it('renders gl-dropdown-item components for available projects', async () => { @@ -234,7 +234,7 @@ describe('IssuableMoveDropdown', () => { await nextTick(); - const dropdownItems = wrapper.findAll(GlDropdownItem); + const dropdownItems = wrapper.findAllComponents(GlDropdownItem); expect(dropdownItems).toHaveLength(mockProjects.length); expect(dropdownItems.at(0).props()).toMatchObject({ @@ -285,7 +285,7 @@ describe('IssuableMoveDropdown', () => { }); it('renders gl-button within footer', async () => { - const moveButtonEl = wrapper.find('[data-testid="footer"]').find(GlButton); + const moveButtonEl = wrapper.find('[data-testid="footer"]').findComponent(GlButton); expect(moveButtonEl.text()).toBe('Move'); expect(moveButtonEl.attributes('disabled')).toBe('true'); @@ -299,7 +299,7 @@ describe('IssuableMoveDropdown', () => { await nextTick(); expect( - wrapper.find('[data-testid="footer"]').find(GlButton).attributes('disabled'), + wrapper.find('[data-testid="footer"]').findComponent(GlButton).attributes('disabled'), ).not.toBeDefined(); }); }); @@ -308,7 +308,7 @@ describe('IssuableMoveDropdown', () => { it('collapsed state element emits `toggle-collapse` event on component when clicked', () => { wrapper.find('[data-testid="move-collapsed"]').trigger('click'); - expect(wrapper.emitted('toggle-collapse')).toBeTruthy(); + expect(wrapper.emitted('toggle-collapse')).toHaveLength(1); }); it('gl-dropdown component calls `fetchProjects` on `shown` event', () => { @@ -337,11 +337,11 @@ describe('IssuableMoveDropdown', () => { it('gl-dropdown component emits `dropdown-close` event on component from `hide` event', async () => { findDropdownEl().vm.$emit('hide'); - expect(wrapper.emitted('dropdown-close')).toBeTruthy(); + expect(wrapper.emitted('dropdown-close')).toHaveLength(1); }); it('close icon in dropdown header closes the dropdown when clicked', () => { - wrapper.find('[data-testid="header"]').find(GlButton).vm.$emit('click', mockEvent); + wrapper.find('[data-testid="header"]').findComponent(GlButton).vm.$emit('click', mockEvent); expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled(); }); @@ -355,7 +355,7 @@ describe('IssuableMoveDropdown', () => { await nextTick(); - wrapper.findAll(GlDropdownItem).at(0).vm.$emit('click', mockEvent); + wrapper.findAllComponents(GlDropdownItem).at(0).vm.$emit('click', mockEvent); expect(wrapper.vm.selectedProject).toBe(mockProjects[0]); }); @@ -369,10 +369,10 @@ describe('IssuableMoveDropdown', () => { await nextTick(); - wrapper.find('[data-testid="footer"]').find(GlButton).vm.$emit('click'); + wrapper.find('[data-testid="footer"]').findComponent(GlButton).vm.$emit('click'); expect(wrapper.vm.$refs.dropdown.hide).toHaveBeenCalled(); - expect(wrapper.emitted('move-issuable')).toBeTruthy(); + expect(wrapper.emitted('move-issuable')).toHaveLength(1); expect(wrapper.emitted('move-issuable')[0]).toEqual([mockProjects[0]]); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_button_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_button_spec.js index c05513a6d5f..c0e5408e1bd 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_button_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_button_spec.js @@ -33,9 +33,9 @@ describe('DropdownButton', () => { wrapper.destroy(); }); - const findDropdownButton = () => wrapper.find(GlButton); + const findDropdownButton = () => wrapper.findComponent(GlButton); const findDropdownText = () => wrapper.find('.dropdown-toggle-text'); - const findDropdownIcon = () => wrapper.find(GlIcon); + const findDropdownIcon = () => wrapper.findComponent(GlIcon); describe('methods', () => { describe('handleButtonClick', () => { @@ -61,7 +61,7 @@ describe('DropdownButton', () => { describe('template', () => { it('renders component container element', () => { - expect(wrapper.find(GlButton).element).toBe(wrapper.element); + expect(wrapper.findComponent(GlButton).element).toBe(wrapper.element); }); it('renders default button text element', () => { diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js index 0673ffee22b..799e2c1d08e 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_create_view_spec.js @@ -127,7 +127,7 @@ describe('DropdownContentsCreateView', () => { }); it('renders dropdown back button element', () => { - const backBtnEl = wrapper.find('.dropdown-title').findAll(GlButton).at(0); + const backBtnEl = wrapper.find('.dropdown-title').findAllComponents(GlButton).at(0); expect(backBtnEl.exists()).toBe(true); expect(backBtnEl.attributes('aria-label')).toBe('Go back'); @@ -142,7 +142,7 @@ describe('DropdownContentsCreateView', () => { }); it('renders dropdown close button element', () => { - const closeBtnEl = wrapper.find('.dropdown-title').findAll(GlButton).at(1); + const closeBtnEl = wrapper.find('.dropdown-title').findAllComponents(GlButton).at(1); expect(closeBtnEl.exists()).toBe(true); expect(closeBtnEl.attributes('aria-label')).toBe('Close'); @@ -150,7 +150,7 @@ describe('DropdownContentsCreateView', () => { }); it('renders label title input element', () => { - const titleInputEl = wrapper.find('.dropdown-input').find(GlFormInput); + const titleInputEl = wrapper.find('.dropdown-input').findComponent(GlFormInput); expect(titleInputEl.exists()).toBe(true); expect(titleInputEl.attributes('placeholder')).toBe('Name new label'); @@ -158,7 +158,7 @@ describe('DropdownContentsCreateView', () => { }); it('renders color block element for all suggested colors', () => { - const colorBlocksEl = wrapper.find('.dropdown-content').findAll(GlLink); + const colorBlocksEl = wrapper.find('.dropdown-content').findAllComponents(GlLink); colorBlocksEl.wrappers.forEach((colorBlock, index) => { expect(colorBlock.attributes('style')).toContain('background-color'); @@ -175,7 +175,7 @@ describe('DropdownContentsCreateView', () => { await nextTick(); const colorPreviewEl = wrapper.find('.color-input-container > .dropdown-label-color-preview'); - const colorInputEl = wrapper.find('.color-input-container').find(GlFormInput); + const colorInputEl = wrapper.find('.color-input-container').findComponent(GlFormInput); expect(colorPreviewEl.exists()).toBe(true); expect(colorPreviewEl.attributes('style')).toContain('background-color'); @@ -185,7 +185,7 @@ describe('DropdownContentsCreateView', () => { }); it('renders create button element', () => { - const createBtnEl = wrapper.find('.dropdown-actions').findAll(GlButton).at(0); + const createBtnEl = wrapper.find('.dropdown-actions').findAllComponents(GlButton).at(0); expect(createBtnEl.exists()).toBe(true); expect(createBtnEl.text()).toContain('Create'); @@ -195,14 +195,14 @@ describe('DropdownContentsCreateView', () => { wrapper.vm.$store.dispatch('requestCreateLabel'); await nextTick(); - const loadingIconEl = wrapper.find('.dropdown-actions').find(GlLoadingIcon); + const loadingIconEl = wrapper.find('.dropdown-actions').findComponent(GlLoadingIcon); expect(loadingIconEl.exists()).toBe(true); expect(loadingIconEl.isVisible()).toBe(true); }); it('renders cancel button element', () => { - const cancelBtnEl = wrapper.find('.dropdown-actions').findAll(GlButton).at(1); + const cancelBtnEl = wrapper.find('.dropdown-actions').findAllComponents(GlButton).at(1); expect(cancelBtnEl.exists()).toBe(true); expect(cancelBtnEl.text()).toContain('Cancel'); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js index 00c8e3a814a..cc9b9f393ce 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_contents_labels_view_spec.js @@ -58,7 +58,7 @@ describe('DropdownContentsLabelsView', () => { const findDropdownContent = () => wrapper.find('[data-testid="dropdown-content"]'); const findDropdownTitle = () => wrapper.find('[data-testid="dropdown-title"]'); const findDropdownFooter = () => wrapper.find('[data-testid="dropdown-footer"]'); - const findLoadingIcon = () => wrapper.find(GlLoadingIcon); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); describe('computed', () => { describe('visibleLabels', () => { @@ -285,7 +285,7 @@ describe('DropdownContentsLabelsView', () => { describe('template', () => { it('renders gl-intersection-observer as component root', () => { - expect(wrapper.find(GlIntersectionObserver).exists()).toBe(true); + expect(wrapper.findComponent(GlIntersectionObserver).exists()).toBe(true); }); it('renders gl-loading-icon component when `labelsFetchInProgress` prop is true', async () => { @@ -316,20 +316,20 @@ describe('DropdownContentsLabelsView', () => { }); it('renders dropdown close button element', () => { - const closeButtonEl = findDropdownTitle().find(GlButton); + const closeButtonEl = findDropdownTitle().findComponent(GlButton); expect(closeButtonEl.exists()).toBe(true); expect(closeButtonEl.props('icon')).toBe('close'); }); it('renders label search input element', () => { - const searchInputEl = wrapper.find(GlSearchBoxByType); + const searchInputEl = wrapper.findComponent(GlSearchBoxByType); expect(searchInputEl.exists()).toBe(true); }); it('renders label elements for all labels', () => { - expect(wrapper.findAll(LabelItem)).toHaveLength(mockLabels.length); + expect(wrapper.findAllComponents(LabelItem)).toHaveLength(mockLabels.length); }); it('renders label element with `highlight` set to true when value of `currentHighlightItem` is more than -1', async () => { @@ -340,7 +340,7 @@ describe('DropdownContentsLabelsView', () => { }); await nextTick(); - const labelItemEl = findDropdownContent().find(LabelItem); + const labelItemEl = findDropdownContent().findComponent(LabelItem); expect(labelItemEl.attributes('highlight')).toBe('true'); }); @@ -373,7 +373,7 @@ describe('DropdownContentsLabelsView', () => { }); it('renders footer list items', () => { - const footerLinks = findDropdownFooter().findAll(GlLink); + const footerLinks = findDropdownFooter().findAllComponents(GlLink); const createLabelLink = footerLinks.at(0); const manageLabelsLink = footerLinks.at(1); @@ -387,7 +387,7 @@ describe('DropdownContentsLabelsView', () => { wrapper.vm.$store.state.allowLabelCreate = false; await nextTick(); - const createLabelLink = findDropdownFooter().findAll(GlLink).at(0); + const createLabelLink = findDropdownFooter().findAllComponents(GlLink).at(0); expect(createLabelLink.text()).not.toBe('Create label'); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_title_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_title_spec.js index 84e9f3f41c3..54804f85f81 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_title_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/dropdown_title_spec.js @@ -41,7 +41,7 @@ describe('DropdownTitle', () => { }); it('renders edit link', () => { - const editBtnEl = wrapper.find(GlButton); + const editBtnEl = wrapper.findComponent(GlButton); expect(editBtnEl.exists()).toBe(true); expect(editBtnEl.text()).toBe('Edit'); @@ -53,7 +53,7 @@ describe('DropdownTitle', () => { }); await nextTick(); - expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true); + expect(wrapper.findComponent(GlLoadingIcon).isVisible()).toBe(true); }); }); }); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js index bedb6204088..bb0f1777de6 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/label_item_spec.js @@ -32,7 +32,7 @@ describe('LabelItem', () => { describe('template', () => { it('renders gl-link component', () => { - expect(wrapper.find(GlLink).exists()).toBe(true); + expect(wrapper.findComponent(GlLink).exists()).toBe(true); }); it('renders component root with class `is-focused` when `highlight` prop is true', () => { diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js index c150410ff8e..4c7ac6e9a6f 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_vue/labels_select_root_spec.js @@ -138,13 +138,13 @@ describe('LabelsSelectRoot', () => { it('renders `dropdown-value-collapsed` component when `allowLabelCreate` prop is `true`', async () => { createComponent(); await nextTick(); - expect(wrapper.find(DropdownValueCollapsed).exists()).toBe(true); + expect(wrapper.findComponent(DropdownValueCollapsed).exists()).toBe(true); }); it('renders `dropdown-title` component', async () => { createComponent(); await nextTick(); - expect(wrapper.find(DropdownTitle).exists()).toBe(true); + expect(wrapper.findComponent(DropdownTitle).exists()).toBe(true); }); it('renders `dropdown-value` component', async () => { @@ -153,7 +153,7 @@ describe('LabelsSelectRoot', () => { }); await nextTick(); - const valueComp = wrapper.find(DropdownValue); + const valueComp = wrapper.findComponent(DropdownValue); expect(valueComp.exists()).toBe(true); expect(valueComp.text()).toBe('None'); @@ -163,14 +163,14 @@ describe('LabelsSelectRoot', () => { createComponent(); wrapper.vm.$store.dispatch('toggleDropdownButton'); await nextTick(); - expect(wrapper.find(DropdownButton).exists()).toBe(true); + expect(wrapper.findComponent(DropdownButton).exists()).toBe(true); }); it('renders `dropdown-contents` component when `showDropdownButton` & `showDropdownContents` prop is `true`', async () => { createComponent(); wrapper.vm.$store.dispatch('toggleDropdownContents'); await nextTick(); - expect(wrapper.find(DropdownContents).exists()).toBe(true); + expect(wrapper.findComponent(DropdownContents).exists()).toBe(true); }); describe('sets content direction based on viewport', () => { @@ -187,7 +187,7 @@ describe('LabelsSelectRoot', () => { wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state); await nextTick(); - expect(wrapper.find(DropdownContents).props('renderOnTop')).toBe(true); + expect(wrapper.findComponent(DropdownContents).props('renderOnTop')).toBe(true); }); it('does not set direction when inside of viewport', async () => { @@ -195,7 +195,7 @@ describe('LabelsSelectRoot', () => { wrapper.vm.setContentIsOnViewport(wrapper.vm.$store.state); await nextTick(); - expect(wrapper.find(DropdownContents).props('renderOnTop')).toBe(false); + expect(wrapper.findComponent(DropdownContents).props('renderOnTop')).toBe(false); }); }, ); diff --git a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js index 1b27a294b90..cad401e0013 100644 --- a/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/labels_select_widget/labels_select_root_spec.js @@ -131,6 +131,7 @@ describe('LabelsSelectRoot', () => { expect(findDropdownValue().exists()).toBe(true); expect(findDropdownValue().props('selectedLabels')).toEqual([ { + __typename: 'Label', color: '#330066', description: null, id: 'gid://gitlab/ProjectLabel/1', diff --git a/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js b/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js index de3e1ccfb03..01958a144ed 100644 --- a/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js +++ b/spec/frontend/vue_shared/components/sidebar/todo_button_spec.js @@ -30,19 +30,19 @@ describe('Todo Button', () => { it('renders GlButton', () => { createComponent(); - expect(wrapper.find(GlButton).exists()).toBe(true); + expect(wrapper.findComponent(GlButton).exists()).toBe(true); }); it('emits click event when clicked', () => { createComponent({}, mount); - wrapper.find(GlButton).trigger('click'); + wrapper.findComponent(GlButton).trigger('click'); - expect(wrapper.emitted().click).toBeTruthy(); + expect(wrapper.emitted().click).toHaveLength(1); }); it('calls dispatchDocumentEvent to update global To-Do counter correctly', () => { createComponent({}, mount); - wrapper.find(GlButton).trigger('click'); + wrapper.findComponent(GlButton).trigger('click'); const dispatchedEvent = dispatchEventSpy.mock.calls[0][0]; expect(dispatchEventSpy).toHaveBeenCalledTimes(1); @@ -57,12 +57,12 @@ describe('Todo Button', () => { `('sets correct label when isTodo is $isTodo', ({ label, isTodo }) => { createComponent({ isTodo }); - expect(wrapper.find(GlButton).text()).toBe(label); + expect(wrapper.findComponent(GlButton).text()).toBe(label); }); it('binds additional props to GlButton', () => { createComponent({ loading: true }); - expect(wrapper.find(GlButton).props('loading')).toBe(true); + expect(wrapper.findComponent(GlButton).props('loading')).toBe(true); }); }); diff --git a/spec/frontend/vue_shared/components/source_editor_spec.js b/spec/frontend/vue_shared/components/source_editor_spec.js index dca4d60e23c..ca5b990bc29 100644 --- a/spec/frontend/vue_shared/components/source_editor_spec.js +++ b/spec/frontend/vue_shared/components/source_editor_spec.js @@ -3,6 +3,7 @@ import { nextTick } from 'vue'; import { EDITOR_READY_EVENT } from '~/editor/constants'; import Editor from '~/editor/source_editor'; import SourceEditor from '~/vue_shared/components/source_editor.vue'; +import * as helpers from 'jest/editor/helpers'; jest.mock('~/editor/source_editor'); @@ -13,6 +14,7 @@ describe('Source Editor component', () => { const value = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'; const fileName = 'lorem.txt'; const fileGlobalId = 'snippet_777'; + const useSpy = jest.fn(); const createInstanceMock = jest.fn().mockImplementation(() => { mockInstance = { onDidChangeModelContent: jest.fn(), @@ -20,6 +22,7 @@ describe('Source Editor component', () => { getValue: jest.fn(), setValue: jest.fn(), dispose: jest.fn(), + use: useSpy, }; return mockInstance; }); @@ -77,16 +80,33 @@ describe('Source Editor component', () => { }); it('initialises Source Editor instance', () => { - const el = wrapper.find({ ref: 'editor' }).element; + const el = wrapper.findComponent({ ref: 'editor' }).element; expect(createInstanceMock).toHaveBeenCalledWith({ el, blobPath: fileName, blobGlobalId: fileGlobalId, blobContent: value, - extensions: null, }); }); + it.each` + description | extensions | toBeCalled + ${'no extension when `undefined` is'} | ${undefined} | ${false} + ${'no extension when {} is'} | ${{}} | ${false} + ${'no extension when [] is'} | ${[]} | ${false} + ${'single extension'} | ${{ definition: helpers.SEClassExtension }} | ${true} + ${'single extension with options'} | ${{ definition: helpers.SEWithSetupExt, setupOptions: { foo: 'bar' } }} | ${true} + ${'multiple extensions'} | ${[{ definition: helpers.SEClassExtension }, { definition: helpers.SEWithSetupExt }]} | ${true} + ${'multiple extensions with options'} | ${[{ definition: helpers.SEClassExtension }, { definition: helpers.SEWithSetupExt, setupOptions: { foo: 'bar' } }]} | ${true} + `('installs $description passed as a prop', ({ extensions, toBeCalled }) => { + createComponent({ extensions }); + if (toBeCalled) { + expect(useSpy).toHaveBeenCalledWith(extensions); + } else { + expect(useSpy).not.toHaveBeenCalled(); + } + }); + it('reacts to the changes in fileName', () => { const newFileName = 'ipsum.txt'; @@ -112,7 +132,7 @@ describe('Source Editor component', () => { }); it('emits EDITOR_READY_EVENT event when the Source Editor is ready', async () => { - const el = wrapper.find({ ref: 'editor' }).element; + const el = wrapper.findComponent({ ref: 'editor' }).element; expect(wrapper.emitted()[EDITOR_READY_EVENT]).toBeUndefined(); await el.dispatchEvent(new Event(EDITOR_READY_EVENT)); diff --git a/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js b/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js index eb2eec92534..fd3ff9ce892 100644 --- a/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/components/chunk_line_spec.js @@ -1,4 +1,3 @@ -import { GlLink } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import ChunkLine from '~/vue_shared/components/source_viewer/components/chunk_line.vue'; import { @@ -11,16 +10,26 @@ const DEFAULT_PROPS = { number: 2, content: '// Line content', language: 'javascript', + blamePath: 'blame/file.js', }; describe('Chunk Line component', () => { let wrapper; + const fileLineBlame = true; const createComponent = (props = {}) => { - wrapper = shallowMountExtended(ChunkLine, { propsData: { ...DEFAULT_PROPS, ...props } }); + wrapper = shallowMountExtended(ChunkLine, { + propsData: { ...DEFAULT_PROPS, ...props }, + provide: { + glFeatures: { + fileLineBlame, + }, + }, + }); }; - const findLink = () => wrapper.findComponent(GlLink); + const findLineLink = () => wrapper.find('.file-line-num'); + const findBlameLink = () => wrapper.find('.file-line-blame'); const findContent = () => wrapper.findByTestId('content'); const findWrappedBidiChars = () => wrapper.findAllByTestId('bidi-wrapper'); @@ -47,14 +56,22 @@ describe('Chunk Line component', () => { }); }); + it('renders a blame link', () => { + expect(findBlameLink().attributes()).toMatchObject({ + href: `${DEFAULT_PROPS.blamePath}#L${DEFAULT_PROPS.number}`, + }); + + expect(findBlameLink().text()).toBe(''); + }); + it('renders a line number', () => { - expect(findLink().attributes()).toMatchObject({ + expect(findLineLink().attributes()).toMatchObject({ 'data-line-number': `${DEFAULT_PROPS.number}`, - to: `#L${DEFAULT_PROPS.number}`, + href: `#L${DEFAULT_PROPS.number}`, id: `L${DEFAULT_PROPS.number}`, }); - expect(findLink().text()).toBe(DEFAULT_PROPS.number.toString()); + expect(findLineLink().text()).toBe(DEFAULT_PROPS.number.toString()); }); it('renders content', () => { diff --git a/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js b/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js index 42c4f2eacb8..8dc3348acfa 100644 --- a/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/components/chunk_spec.js @@ -10,6 +10,7 @@ const DEFAULT_PROPS = { startingFrom: 140, totalLines: 50, language: 'javascript', + blamePath: 'blame/file.js', }; describe('Chunk component', () => { @@ -76,6 +77,7 @@ describe('Chunk component', () => { number: DEFAULT_PROPS.startingFrom + 1, content: splitContent[0], language: DEFAULT_PROPS.language, + blamePath: DEFAULT_PROPS.blamePath, }); }); }); diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js b/spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js index 3036ce43888..375b1307616 100644 --- a/spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/plugins/link_dependencies_spec.js @@ -1,8 +1,10 @@ import packageJsonLinker from '~/vue_shared/components/source_viewer/plugins/utils/package_json_linker'; +import gemspecLinker from '~/vue_shared/components/source_viewer/plugins/utils/gemspec_linker'; import linkDependencies from '~/vue_shared/components/source_viewer/plugins/link_dependencies'; -import { PACKAGE_JSON_FILE_TYPE, PACKAGE_JSON_CONTENT } from './mock_data'; +import { PACKAGE_JSON_FILE_TYPE, PACKAGE_JSON_CONTENT, GEMSPEC_FILE_TYPE } from './mock_data'; jest.mock('~/vue_shared/components/source_viewer/plugins/utils/package_json_linker'); +jest.mock('~/vue_shared/components/source_viewer/plugins/utils/gemspec_linker'); describe('Highlight.js plugin for linking dependencies', () => { const hljsResultMock = { value: 'test' }; @@ -11,4 +13,9 @@ describe('Highlight.js plugin for linking dependencies', () => { linkDependencies(hljsResultMock, PACKAGE_JSON_FILE_TYPE, PACKAGE_JSON_CONTENT); expect(packageJsonLinker).toHaveBeenCalled(); }); + + it('calls gemspecLinker for gemspec file types', () => { + linkDependencies(hljsResultMock, GEMSPEC_FILE_TYPE); + expect(gemspecLinker).toHaveBeenCalled(); + }); }); diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js b/spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js index 75659770e2c..aa874c9c081 100644 --- a/spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js +++ b/spec/frontend/vue_shared/components/source_viewer/plugins/mock_data.js @@ -1,2 +1,4 @@ export const PACKAGE_JSON_FILE_TYPE = 'package_json'; export const PACKAGE_JSON_CONTENT = '{ "dependencies": { "@babel/core": "^7.18.5" } }'; + +export const GEMSPEC_FILE_TYPE = 'gemspec'; diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/utils/dependency_linker_util_spec.js b/spec/frontend/vue_shared/components/source_viewer/plugins/utils/dependency_linker_util_spec.js index ee200747af9..8079d5ad99a 100644 --- a/spec/frontend/vue_shared/components/source_viewer/plugins/utils/dependency_linker_util_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/plugins/utils/dependency_linker_util_spec.js @@ -14,10 +14,11 @@ describe('createLink', () => { it('escapes the user-controlled content', () => { const unescapedXSS = '<script>XSS</script>'; - const escapedXSS = '&lt;script&gt;XSS&lt;/script&gt;'; + const escapedPackageName = '<script>XSS</script>'; + const escapedHref = '&lt;script&gt;XSS&lt;/script&gt;'; const href = `http://test.com/${unescapedXSS}`; const innerText = `testing${unescapedXSS}`; - const result = `<a href="http://test.com/${escapedXSS}" rel="nofollow noreferrer noopener">testing${escapedXSS}</a>`; + const result = `<a href="http://test.com/${escapedHref}" rel="nofollow noreferrer noopener">testing${escapedPackageName}</a>`; expect(createLink(href, innerText)).toBe(result); }); diff --git a/spec/frontend/vue_shared/components/source_viewer/plugins/utils/gemspec_linker_spec.js b/spec/frontend/vue_shared/components/source_viewer/plugins/utils/gemspec_linker_spec.js new file mode 100644 index 00000000000..3f74bfa117f --- /dev/null +++ b/spec/frontend/vue_shared/components/source_viewer/plugins/utils/gemspec_linker_spec.js @@ -0,0 +1,14 @@ +import gemspecLinker from '~/vue_shared/components/source_viewer/plugins/utils/gemspec_linker'; + +describe('Highlight.js plugin for linking gemspec dependencies', () => { + it('mutates the input value by wrapping dependency names in anchors', () => { + const inputValue = + 's.add_dependency(<span class="hljs-string">'rugged'</span>, <span class="hljs-string">'~> 0.24.0'</span>)'; + const outputValue = + 's.add_dependency(<span class="hljs-string linked">'<a href="https://rubygems.org/gems/rugged" rel="nofollow noreferrer noopener">rugged</a>'</span>, <span class="hljs-string">'~> 0.24.0'</span>)'; + const hljsResultMock = { value: inputValue }; + + const output = gemspecLinker(hljsResultMock); + expect(output).toBe(outputValue); + }); +}); diff --git a/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js b/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js index 2c03b7aa7d3..4fbc907a813 100644 --- a/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js +++ b/spec/frontend/vue_shared/components/source_viewer/source_viewer_spec.js @@ -40,8 +40,9 @@ describe('Source Viewer component', () => { const chunk2 = generateContent('// Some source code 2', 70); const content = chunk1 + chunk2; const path = 'some/path.js'; + const blamePath = 'some/blame/path.js'; const fileType = 'javascript'; - const DEFAULT_BLOB_DATA = { language, rawTextBlob: content, path, fileType }; + const DEFAULT_BLOB_DATA = { language, rawTextBlob: content, path, blamePath, fileType }; const highlightedContent = `<span data-testid='test-highlighted' id='LC1'>${content}</span><span id='LC2'></span>`; const createComponent = async (blob = {}) => { diff --git a/spec/frontend/vue_shared/components/split_button_spec.js b/spec/frontend/vue_shared/components/split_button_spec.js index 4965969bc3e..6b869db4058 100644 --- a/spec/frontend/vue_shared/components/split_button_spec.js +++ b/spec/frontend/vue_shared/components/split_button_spec.js @@ -26,8 +26,9 @@ describe('SplitButton', () => { }); }; - const findDropdown = () => wrapper.find(GlDropdown); - const findDropdownItem = (index = 0) => findDropdown().findAll(GlDropdownItem).at(index); + const findDropdown = () => wrapper.findComponent(GlDropdown); + const findDropdownItem = (index = 0) => + findDropdown().findAllComponents(GlDropdownItem).at(index); const selectItem = async (index) => { findDropdownItem(index).vm.$emit('click'); diff --git a/spec/frontend/vue_shared/components/table_pagination_spec.js b/spec/frontend/vue_shared/components/table_pagination_spec.js index ed23a47c328..99de26ce2ae 100644 --- a/spec/frontend/vue_shared/components/table_pagination_spec.js +++ b/spec/frontend/vue_shared/components/table_pagination_spec.js @@ -50,7 +50,7 @@ describe('Pagination component', () => { change: spy, }); - expect(wrapper.find(GlPagination).exists()).toBe(true); + expect(wrapper.findComponent(GlPagination).exists()).toBe(true); }); it('renders if there is a prev page', () => { @@ -66,7 +66,7 @@ describe('Pagination component', () => { change: spy, }); - expect(wrapper.find(GlPagination).exists()).toBe(true); + expect(wrapper.findComponent(GlPagination).exists()).toBe(true); }); }); @@ -83,7 +83,7 @@ describe('Pagination component', () => { }, change: spy, }); - wrapper.find(GlPagination).vm.$emit('input', 3); + wrapper.findComponent(GlPagination).vm.$emit('input', 3); expect(spy).toHaveBeenCalledWith(3); }); }); diff --git a/spec/frontend/vue_shared/components/tooltip_on_truncate_spec.js b/spec/frontend/vue_shared/components/tooltip_on_truncate_spec.js index 9e7e5c1263f..ca1f7996ad6 100644 --- a/spec/frontend/vue_shared/components/tooltip_on_truncate_spec.js +++ b/spec/frontend/vue_shared/components/tooltip_on_truncate_spec.js @@ -68,7 +68,7 @@ describe('TooltipOnTruncate component', () => { }, ); - wrapper = parent.find(WrappedTooltipOnTruncate); + wrapper = parent.findComponent(WrappedTooltipOnTruncate); }; const getTooltipValue = () => getBinding(wrapper.element, 'gl-tooltip')?.value; diff --git a/spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js b/spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js index 21e9b401215..a063a5591e3 100644 --- a/spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js +++ b/spec/frontend/vue_shared/components/upload_dropzone/upload_dropzone_spec.js @@ -14,7 +14,7 @@ describe('Upload dropzone component', () => { const findDropzoneCard = () => wrapper.find('.upload-dropzone-card'); const findDropzoneArea = () => wrapper.find('[data-testid="dropzone-area"]'); - const findIcon = () => wrapper.find(GlIcon); + const findIcon = () => wrapper.findComponent(GlIcon); const findUploadText = () => wrapper.find('[data-testid="upload-text"]').text(); const findFileInput = () => wrapper.find('input[type="file"]'); diff --git a/spec/frontend/vue_shared/components/user_access_role_badge_spec.js b/spec/frontend/vue_shared/components/user_access_role_badge_spec.js index 7f25f7c08e7..cea6fcac8c8 100644 --- a/spec/frontend/vue_shared/components/user_access_role_badge_spec.js +++ b/spec/frontend/vue_shared/components/user_access_role_badge_spec.js @@ -18,7 +18,7 @@ describe('UserAccessRoleBadge', () => { }, }); - const badge = wrapper.find(GlBadge); + const badge = wrapper.findComponent(GlBadge); expect(badge.exists()).toBe(true); expect(badge.html()).toContain('test slot content'); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js index 5e05b54cb8c..f87737ca86a 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_new_spec.js @@ -18,6 +18,8 @@ const PROVIDED_PROPS = { describe('User Avatar Image Component', () => { let wrapper; + const findAvatar = () => wrapper.findComponent(GlAvatar); + afterEach(() => { wrapper.destroy(); }); @@ -28,21 +30,14 @@ describe('User Avatar Image Component', () => { propsData: { ...PROVIDED_PROPS, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, }); }); it('should render `GlAvatar` and provide correct properties to it', () => { - const avatar = wrapper.findComponent(GlAvatar); - - expect(avatar.attributes('data-src')).toBe( + expect(findAvatar().attributes('data-src')).toBe( `${PROVIDED_PROPS.imgSrc}?width=${PROVIDED_PROPS.size}`, ); - expect(avatar.props()).toMatchObject({ + expect(findAvatar().props()).toMatchObject({ src: `${PROVIDED_PROPS.imgSrc}?width=${PROVIDED_PROPS.size}`, alt: PROVIDED_PROPS.imgAlt, size: PROVIDED_PROPS.size, @@ -63,23 +58,28 @@ describe('User Avatar Image Component', () => { ...PROVIDED_PROPS, lazy: true, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, }); }); it('should add lazy attributes', () => { - const avatar = wrapper.findComponent(GlAvatar); - - expect(avatar.classes()).toContain('lazy'); - expect(avatar.attributes()).toMatchObject({ + expect(findAvatar().classes()).toContain('lazy'); + expect(findAvatar().attributes()).toMatchObject({ src: placeholderImage, 'data-src': `${PROVIDED_PROPS.imgSrc}?width=${PROVIDED_PROPS.size}`, }); }); + + it('should use maximum number when size is provided as an object', () => { + wrapper = shallowMount(UserAvatarImage, { + propsData: { + ...PROVIDED_PROPS, + size: { default: 16, md: 64, lg: 24 }, + lazy: true, + }, + }); + + expect(findAvatar().attributes('data-src')).toBe(`${PROVIDED_PROPS.imgSrc}?width=${64}`); + }); }); describe('Initialization without src', () => { @@ -89,18 +89,11 @@ describe('User Avatar Image Component', () => { ...PROVIDED_PROPS, imgSrc: null, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, }); }); it('should have default avatar image', () => { - const avatar = wrapper.findComponent(GlAvatar); - - expect(avatar.props('src')).toBe(`${defaultAvatarUrl}?width=${PROVIDED_PROPS.size}`); + expect(findAvatar().props('src')).toBe(`${defaultAvatarUrl}?width=${PROVIDED_PROPS.size}`); }); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js index 75d2a936b34..6ad2ef226c2 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_image_spec.js @@ -15,47 +15,37 @@ const PROVIDED_PROPS = { describe('User Avatar Image Component', () => { let wrapper; - afterEach(() => { - wrapper.destroy(); - }); - - describe('when `glAvatarForAllUserAvatars` feature flag enabled', () => { - beforeEach(() => { - wrapper = shallowMount(UserAvatarImage, { - propsData: { - ...PROVIDED_PROPS, + const createWrapper = (props = {}, { glAvatarForAllUserAvatars } = {}) => { + wrapper = shallowMount(UserAvatarImage, { + propsData: { + ...PROVIDED_PROPS, + ...props, + }, + provide: { + glFeatures: { + glAvatarForAllUserAvatars, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, - }); + }, }); + }; - it('should render `UserAvatarImageNew` component', () => { - expect(wrapper.findComponent(UserAvatarImageNew).exists()).toBe(true); - expect(wrapper.findComponent(UserAvatarImageOld).exists()).toBe(false); - }); + afterEach(() => { + wrapper.destroy(); }); - describe('when `glAvatarForAllUserAvatars` feature flag disabled', () => { - beforeEach(() => { - wrapper = shallowMount(UserAvatarImage, { - propsData: { - ...PROVIDED_PROPS, - }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: false, - }, - }, + describe.each([ + [false, true, true], + [true, false, true], + [true, true, true], + [false, false, false], + ])( + 'when glAvatarForAllUserAvatars=%s and enforceGlAvatar=%s', + (glAvatarForAllUserAvatars, enforceGlAvatar, isUsingNewVersion) => { + it(`will render ${isUsingNewVersion ? 'new' : 'old'} version`, () => { + createWrapper({ enforceGlAvatar }, { glAvatarForAllUserAvatars }); + expect(wrapper.findComponent(UserAvatarImageNew).exists()).toBe(isUsingNewVersion); + expect(wrapper.findComponent(UserAvatarImageOld).exists()).toBe(!isUsingNewVersion); }); - }); - - it('should render `UserAvatarImageOld` component', () => { - expect(wrapper.findComponent(UserAvatarImageNew).exists()).toBe(false); - expect(wrapper.findComponent(UserAvatarImageOld).exists()).toBe(true); - }); - }); + }, + ); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js index 5ba80b31b99..f485a14cfea 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_new_spec.js @@ -54,6 +54,7 @@ describe('User Avatar Link Component', () => { size: defaultProps.imgSize, tooltipPlacement: defaultProps.tooltipPlacement, tooltipText: '', + enforceGlAvatar: false, }); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js index 2d513c46e77..cf7a1025dba 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_old_spec.js @@ -54,6 +54,7 @@ describe('User Avatar Link Component', () => { size: defaultProps.imgSize, tooltipPlacement: defaultProps.tooltipPlacement, tooltipText: '', + enforceGlAvatar: false, }); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_spec.js index b36b83d1fea..fd3f59008ec 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_link_spec.js @@ -15,47 +15,37 @@ const PROVIDED_PROPS = { describe('User Avatar Link Component', () => { let wrapper; - afterEach(() => { - wrapper.destroy(); - }); - - describe('when `glAvatarForAllUserAvatars` feature flag enabled', () => { - beforeEach(() => { - wrapper = shallowMount(UserAvatarLink, { - propsData: { - ...PROVIDED_PROPS, + const createWrapper = (props = {}, { glAvatarForAllUserAvatars } = {}) => { + wrapper = shallowMount(UserAvatarLink, { + propsData: { + ...PROVIDED_PROPS, + ...props, + }, + provide: { + glFeatures: { + glAvatarForAllUserAvatars, }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: true, - }, - }, - }); + }, }); + }; - it('should render `UserAvatarLinkNew` component', () => { - expect(wrapper.findComponent(UserAvatarLinkNew).exists()).toBe(true); - expect(wrapper.findComponent(UserAvatarLinkOld).exists()).toBe(false); - }); + afterEach(() => { + wrapper.destroy(); }); - describe('when `glAvatarForAllUserAvatars` feature flag disabled', () => { - beforeEach(() => { - wrapper = shallowMount(UserAvatarLink, { - propsData: { - ...PROVIDED_PROPS, - }, - provide: { - glFeatures: { - glAvatarForAllUserAvatars: false, - }, - }, + describe.each([ + [false, true, true], + [true, false, true], + [true, true, true], + [false, false, false], + ])( + 'when glAvatarForAllUserAvatars=%s and enforceGlAvatar=%s', + (glAvatarForAllUserAvatars, enforceGlAvatar, isUsingNewVersion) => { + it(`will render ${isUsingNewVersion ? 'new' : 'old'} version`, () => { + createWrapper({ enforceGlAvatar }, { glAvatarForAllUserAvatars }); + expect(wrapper.findComponent(UserAvatarLinkNew).exists()).toBe(isUsingNewVersion); + expect(wrapper.findComponent(UserAvatarLinkOld).exists()).toBe(!isUsingNewVersion); }); - }); - - it('should render `UserAvatarLinkOld` component', () => { - expect(wrapper.findComponent(UserAvatarLinkNew).exists()).toBe(false); - expect(wrapper.findComponent(UserAvatarLinkOld).exists()).toBe(true); - }); - }); + }, + ); }); diff --git a/spec/frontend/vue_shared/components/user_avatar/user_avatar_list_spec.js b/spec/frontend/vue_shared/components/user_avatar/user_avatar_list_spec.js index 20ff0848cff..b9accbf0373 100644 --- a/spec/frontend/vue_shared/components/user_avatar/user_avatar_list_spec.js +++ b/spec/frontend/vue_shared/components/user_avatar/user_avatar_list_spec.js @@ -38,7 +38,7 @@ describe('UserAvatarList', () => { }; const clickButton = () => { - const button = wrapper.find(GlButton); + const button = wrapper.findComponent(GlButton); button.vm.$emit('click'); }; @@ -79,7 +79,7 @@ describe('UserAvatarList', () => { const items = createList(20); factory({ propsData: { items } }); - const links = wrapper.findAll(UserAvatarLink); + const links = wrapper.findAllComponents(UserAvatarLink); const linkProps = links.wrappers.map((x) => x.props()); expect(linkProps).toEqual( @@ -105,7 +105,7 @@ describe('UserAvatarList', () => { it('renders all avatars if length is <= breakpoint', () => { factory(); - const links = wrapper.findAll(UserAvatarLink); + const links = wrapper.findAllComponents(UserAvatarLink); expect(links.length).toEqual(props.items.length); }); @@ -113,7 +113,7 @@ describe('UserAvatarList', () => { it('does not show button', () => { factory(); - expect(wrapper.find(GlButton).exists()).toBe(false); + expect(wrapper.findComponent(GlButton).exists()).toBe(false); }); }); @@ -126,7 +126,7 @@ describe('UserAvatarList', () => { it('renders avatars up to breakpoint', () => { factory(); - const links = wrapper.findAll(UserAvatarLink); + const links = wrapper.findAllComponents(UserAvatarLink); expect(links.length).toEqual(TEST_BREAKPOINT); }); @@ -138,7 +138,7 @@ describe('UserAvatarList', () => { }); it('renders all avatars', () => { - const links = wrapper.findAll(UserAvatarLink); + const links = wrapper.findAllComponents(UserAvatarLink); expect(links.length).toEqual(props.items.length); }); @@ -147,7 +147,7 @@ describe('UserAvatarList', () => { clickButton(); await nextTick(); - const links = wrapper.findAll(UserAvatarLink); + const links = wrapper.findAllComponents(UserAvatarLink); expect(links.length).toEqual(TEST_BREAKPOINT); }); diff --git a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js index 9550368eefc..b7ce3e47cef 100644 --- a/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js +++ b/spec/frontend/vue_shared/components/user_popover/user_popover_spec.js @@ -6,6 +6,7 @@ import UserPopover from '~/vue_shared/components/user_popover/user_popover.vue'; import axios from '~/lib/utils/axios_utils'; import createFlash from '~/flash'; import { followUser, unfollowUser } from '~/api/user_api'; +import { mockTracking } from 'helpers/tracking_helper'; jest.mock('~/flash'); jest.mock('~/api/user_api', () => ({ @@ -51,6 +52,18 @@ describe('User Popover Component', () => { const findUserLocalTime = () => wrapper.findByTestId('user-popover-local-time'); const findToggleFollowButton = () => wrapper.findByTestId('toggle-follow-button'); + const itTracksToggleFollowButtonClick = (expectedLabel) => { + it('tracks click', async () => { + const trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + + await findToggleFollowButton().trigger('click'); + + expect(trackingSpy).toHaveBeenCalledWith(undefined, 'click_button', { + label: expectedLabel, + }); + }); + }; + const createWrapper = (props = {}) => { wrapper = mountExtended(UserPopover, { propsData: { @@ -75,7 +88,7 @@ describe('User Popover Component', () => { }, }); - expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true); + expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true); }); }); @@ -89,7 +102,7 @@ describe('User Popover Component', () => { it('shows icon for location', () => { createWrapper(); - const iconEl = wrapper.find(GlIcon); + const iconEl = wrapper.findComponent(GlIcon); expect(iconEl.props('name')).toEqual('location'); }); @@ -102,8 +115,8 @@ describe('User Popover Component', () => { }); describe('job data', () => { - const findWorkInformation = () => wrapper.find({ ref: 'workInformation' }); - const findBio = () => wrapper.find({ ref: 'bio' }); + const findWorkInformation = () => wrapper.findComponent({ ref: 'workInformation' }); + const findBio = () => wrapper.findComponent({ ref: 'bio' }); const bio = 'My super interesting bio'; it('should show only bio if work information is not available', () => { @@ -159,7 +172,7 @@ describe('User Popover Component', () => { createWrapper({ user }); expect( - wrapper.findAll(GlIcon).filter((icon) => icon.props('name') === 'profile').length, + wrapper.findAllComponents(GlIcon).filter((icon) => icon.props('name') === 'profile').length, ).toEqual(1); }); @@ -172,7 +185,7 @@ describe('User Popover Component', () => { createWrapper({ user }); expect( - wrapper.findAll(GlIcon).filter((icon) => icon.props('name') === 'work').length, + wrapper.findAllComponents(GlIcon).filter((icon) => icon.props('name') === 'work').length, ).toEqual(1); }); }); @@ -338,9 +351,11 @@ describe('User Popover Component', () => { await axios.waitForAll(); expect(wrapper.emitted().follow.length).toBe(1); - expect(wrapper.emitted().unfollow).toBeFalsy(); + expect(wrapper.emitted().unfollow).toBeUndefined(); }); + itTracksToggleFollowButtonClick('follow_from_user_popover'); + describe('when an error occurs', () => { beforeEach(() => { followUser.mockRejectedValue({}); @@ -361,8 +376,8 @@ describe('User Popover Component', () => { it('emits no events', async () => { await axios.waitForAll(); - expect(wrapper.emitted().follow).toBe(undefined); - expect(wrapper.emitted().unfollow).toBe(undefined); + expect(wrapper.emitted().follow).toBeUndefined(); + expect(wrapper.emitted().unfollow).toBeUndefined(); }); }); }); @@ -388,6 +403,8 @@ describe('User Popover Component', () => { expect(wrapper.emitted().unfollow.length).toBe(1); }); + itTracksToggleFollowButtonClick('unfollow_from_user_popover'); + describe('when an error occurs', () => { beforeEach(async () => { unfollowUser.mockRejectedValue({}); @@ -406,8 +423,8 @@ describe('User Popover Component', () => { }); it('emits no events', () => { - expect(wrapper.emitted().follow).toBe(undefined); - expect(wrapper.emitted().unfollow).toBe(undefined); + expect(wrapper.emitted().follow).toBeUndefined(); + expect(wrapper.emitted().unfollow).toBeUndefined(); }); }); }); diff --git a/spec/frontend/vue_shared/components/user_select_spec.js b/spec/frontend/vue_shared/components/user_select_spec.js index ec9128d5e38..4188adc72a1 100644 --- a/spec/frontend/vue_shared/components/user_select_spec.js +++ b/spec/frontend/vue_shared/components/user_select_spec.js @@ -9,6 +9,7 @@ import searchUsersQuery from '~/graphql_shared/queries/users_search.query.graphq import searchUsersQueryOnMR from '~/graphql_shared/queries/users_search_with_mr_permissions.graphql'; import { IssuableType } from '~/issues/constants'; import { DEFAULT_DEBOUNCE_AND_THROTTLE_MS } from '~/lib/utils/constants'; +import SidebarParticipant from '~/sidebar/components/assignees/sidebar_participant.vue'; import getIssueParticipantsQuery from '~/vue_shared/components/sidebar/queries/get_issue_participants.query.graphql'; import UserSelect from '~/vue_shared/components/user_select/user_select.vue'; import { @@ -16,6 +17,8 @@ import { searchResponseOnMR, projectMembersResponse, participantsQueryResponse, + mockUser1, + mockUser2, } from 'jest/sidebar/mock_data'; const assignee = { @@ -45,9 +48,14 @@ describe('User select dropdown', () => { const findSearchField = () => wrapper.findComponent(GlSearchBoxByType); const findParticipantsLoading = () => wrapper.find('[data-testid="loading-participants"]'); const findSelectedParticipants = () => wrapper.findAll('[data-testid="selected-participant"]'); + const findSelectedParticipantByIndex = (index) => + findSelectedParticipants().at(index).findComponent(SidebarParticipant); const findUnselectedParticipants = () => wrapper.findAll('[data-testid="unselected-participant"]'); + const findUnselectedParticipantByIndex = (index) => + findUnselectedParticipants().at(index).findComponent(SidebarParticipant); const findCurrentUser = () => wrapper.findAll('[data-testid="current-user"]'); + const findIssuableAuthor = () => wrapper.findAll('[data-testid="issuable-author"]'); const findUnassignLink = () => wrapper.find('[data-testid="unassign"]'); const findEmptySearchResults = () => wrapper.find('[data-testid="empty-results"]'); @@ -136,6 +144,93 @@ describe('User select dropdown', () => { expect(findCurrentUser().exists()).toBe(true); }); + it('does not render current user if user is not logged in', async () => { + createComponent({ + props: { + currentUser: {}, + }, + }); + await waitForPromises(); + + expect(findCurrentUser().exists()).toBe(false); + }); + + it('does not render issuable author if author is not passed as a prop', async () => { + createComponent(); + await waitForPromises(); + + expect(findIssuableAuthor().exists()).toBe(false); + }); + + describe('when issuable author is passed as a prop', () => { + it('moves issuable author on top of assigned list, if author is assigned', async () => { + createComponent({ + props: { + value: [assignee, mockUser2], + issuableAuthor: mockUser2, + }, + }); + await waitForPromises(); + + expect(findSelectedParticipantByIndex(0).props('user')).toEqual(mockUser2); + }); + + it('moves issuable author on top of assigned list after current user, if author and current user are assigned', async () => { + const currentUser = mockUser1; + const issuableAuthor = mockUser2; + + createComponent({ + props: { + value: [assignee, issuableAuthor, currentUser], + issuableAuthor, + currentUser, + }, + }); + await waitForPromises(); + + expect(findSelectedParticipantByIndex(0).props('user')).toEqual(currentUser); + expect(findSelectedParticipantByIndex(1).props('user')).toEqual(issuableAuthor); + }); + + it('moves issuable author on top of unassigned list, if author is unassigned project member', async () => { + createComponent({ + props: { + issuableAuthor: mockUser2, + }, + }); + await waitForPromises(); + + expect(findUnselectedParticipantByIndex(0).props('user')).toEqual(mockUser2); + }); + + it('moves issuable author on top of unassigned list after current user, if author and current user are unassigned project members', async () => { + const currentUser = mockUser2; + const issuableAuthor = mockUser1; + + createComponent({ + props: { + issuableAuthor, + currentUser, + }, + }); + await waitForPromises(); + + expect(findUnselectedParticipantByIndex(0).props('user')).toEqual(currentUser); + expect(findUnselectedParticipantByIndex(1).props('user')).toMatchObject(issuableAuthor); + }); + + it('displays author in a designated position if author is not assigned and not a project member', async () => { + createComponent({ + props: { + issuableAuthor: assignee, + }, + }); + await waitForPromises(); + + expect(findIssuableAuthor().exists()).toBe(true); + }); + }); + it('displays correct amount of selected users', async () => { createComponent({ props: { diff --git a/spec/frontend/vue_shared/components/web_ide_link_spec.js b/spec/frontend/vue_shared/components/web_ide_link_spec.js index 040461f6be4..a0b868d1d52 100644 --- a/spec/frontend/vue_shared/components/web_ide_link_spec.js +++ b/spec/frontend/vue_shared/components/web_ide_link_spec.js @@ -3,7 +3,7 @@ import { nextTick } from 'vue'; import ActionsButton from '~/vue_shared/components/actions_button.vue'; import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue'; -import WebIdeLink from '~/vue_shared/components/web_ide_link.vue'; +import WebIdeLink, { i18n } from '~/vue_shared/components/web_ide_link.vue'; import ConfirmForkModal from '~/vue_shared/components/confirm_fork_modal.vue'; import { stubComponent } from 'helpers/stub_component'; @@ -37,8 +37,8 @@ const ACTION_EDIT_CONFIRM_FORK = { const ACTION_WEB_IDE = { href: TEST_WEB_IDE_URL, key: 'webide', - secondaryText: 'Quickly and easily edit multiple files in your project.', - tooltip: '', + secondaryText: i18n.webIdeText, + tooltip: i18n.webIdeTooltip, text: 'Web IDE', attrs: { 'data-qa-selector': 'web_ide_button', @@ -108,8 +108,8 @@ describe('Web IDE link component', () => { wrapper.destroy(); }); - const findActionsButton = () => wrapper.find(ActionsButton); - const findLocalStorageSync = () => wrapper.find(LocalStorageSync); + const findActionsButton = () => wrapper.findComponent(ActionsButton); + const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync); const findModal = () => wrapper.findComponent(GlModal); const findForkConfirmModal = () => wrapper.findComponent(ConfirmForkModal); |