diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-23 21:10:28 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2023-02-23 21:10:28 +0000 |
commit | 4e7abe540dbd1d170bfb2b3594e645cbfb48cac3 (patch) | |
tree | f3de940e069b4d927acfdf54247c9900113a4c79 /spec/frontend | |
parent | f6b95a66bc12adeb4fac7277d1eb345d9e7819fd (diff) | |
download | gitlab-ce-4e7abe540dbd1d170bfb2b3594e645cbfb48cac3.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
8 files changed, 231 insertions, 284 deletions
diff --git a/spec/frontend/issuable/components/issue_milestone_spec.js b/spec/frontend/issuable/components/issue_milestone_spec.js index eac53c5f761..232d6177862 100644 --- a/spec/frontend/issuable/components/issue_milestone_spec.js +++ b/spec/frontend/issuable/components/issue_milestone_spec.js @@ -1,160 +1,61 @@ -import { GlIcon } from '@gitlab/ui'; +import { GlIcon, GlTooltip } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import Vue, { nextTick } from 'vue'; - import { mockMilestone } from 'jest/boards/mock_data'; import IssueMilestone from '~/issuable/components/issue_milestone.vue'; -const createComponent = (milestone = mockMilestone) => { - const Component = Vue.extend(IssueMilestone); - - return shallowMount(Component, { - propsData: { - milestone, - }, - }); -}; - -describe('IssueMilestoneComponent', () => { +describe('IssueMilestone component', () => { let wrapper; - let vm; - beforeEach(async () => { - wrapper = createComponent(); + const findTooltip = () => wrapper.findComponent(GlTooltip); - ({ vm } = wrapper); + const createComponent = (milestone = mockMilestone) => + shallowMount(IssueMilestone, { propsData: { milestone } }); - await nextTick(); + beforeEach(() => { + wrapper = createComponent(); }); - afterEach(() => { - wrapper.destroy(); + it('renders milestone icon', () => { + expect(wrapper.findComponent(GlIcon).props('name')).toBe('clock'); }); - describe('computed', () => { - describe('isMilestoneStarted', () => { - it('should return `false` when milestoneStart prop is not defined', async () => { - wrapper.setProps({ - milestone: { ...mockMilestone, start_date: '' }, - }); - await nextTick(); - - expect(wrapper.vm.isMilestoneStarted).toBe(false); - }); - - it('should return `true` when milestone start date is past current date', async () => { - await wrapper.setProps({ - milestone: { ...mockMilestone, start_date: '1990-07-22' }, - }); - await nextTick(); + it('renders milestone title', () => { + expect(wrapper.find('.milestone-title').text()).toBe(mockMilestone.title); + }); - expect(wrapper.vm.isMilestoneStarted).toBe(true); - }); + describe('tooltip', () => { + it('renders `Milestone`', () => { + expect(findTooltip().text()).toContain('Milestone'); }); - describe('isMilestonePastDue', () => { - it('should return `false` when milestoneDue prop is not defined', async () => { - wrapper.setProps({ - milestone: { ...mockMilestone, due_date: '' }, - }); - await nextTick(); - - expect(wrapper.vm.isMilestonePastDue).toBe(false); - }); - - it('should return `true` when milestone due is past current date', () => { - wrapper.setProps({ - milestone: { ...mockMilestone, due_date: '1990-07-22' }, - }); - - expect(wrapper.vm.isMilestonePastDue).toBe(true); - }); + it('renders milestone title', () => { + expect(findTooltip().text()).toContain(mockMilestone.title); }); - describe('milestoneDatesAbsolute', () => { - it('returns string containing absolute milestone due date', () => { - expect(vm.milestoneDatesAbsolute).toBe('(December 31, 2019)'); - }); + describe('humanized dates', () => { + it('renders `Expired` when there is a due date in the past', () => { + wrapper = createComponent({ ...mockMilestone, due_date: '2019-12-31', start_date: '' }); - it('returns string containing absolute milestone start date when due date is not present', async () => { - wrapper.setProps({ - milestone: { ...mockMilestone, due_date: '' }, - }); - await nextTick(); - - expect(wrapper.vm.milestoneDatesAbsolute).toBe('(January 1, 2018)'); + expect(findTooltip().text()).toContain('Expired 6 months ago(December 31, 2019)'); }); - it('returns empty string when both milestone start and due dates are not present', async () => { - wrapper.setProps({ - milestone: { ...mockMilestone, start_date: '', due_date: '' }, - }); - await nextTick(); + it('renders `remaining` when there is a due date in the future', () => { + wrapper = createComponent({ ...mockMilestone, due_date: '2020-12-31', start_date: '' }); - expect(wrapper.vm.milestoneDatesAbsolute).toBe(''); + expect(findTooltip().text()).toContain('5 months remaining(December 31, 2020)'); }); - }); - describe('milestoneDatesHuman', () => { - it('returns string containing milestone due date when date is yet to be due', async () => { - wrapper.setProps({ - milestone: { ...mockMilestone, due_date: `${new Date().getFullYear() + 10}-01-01` }, - }); - await nextTick(); + it('renders `Started` when there is a start date in the past', () => { + wrapper = createComponent({ ...mockMilestone, due_date: '', start_date: '2019-12-31' }); - expect(wrapper.vm.milestoneDatesHuman).toContain('years remaining'); + expect(findTooltip().text()).toContain('Started 6 months ago(December 31, 2019)'); }); - it('returns string containing milestone start date when date has already started and due date is not present', async () => { - wrapper.setProps({ - milestone: { ...mockMilestone, start_date: '1990-07-22', due_date: '' }, - }); - await nextTick(); + it('renders `Starts` when there is a start date in the future', () => { + wrapper = createComponent({ ...mockMilestone, due_date: '', start_date: '2020-12-31' }); - expect(wrapper.vm.milestoneDatesHuman).toContain('Started'); + expect(findTooltip().text()).toContain('Starts in 5 months(December 31, 2020)'); }); - - it('returns string containing milestone start date when date is yet to start and due date is not present', async () => { - wrapper.setProps({ - milestone: { - ...mockMilestone, - start_date: `${new Date().getFullYear() + 10}-01-01`, - due_date: '', - }, - }); - await nextTick(); - - expect(wrapper.vm.milestoneDatesHuman).toContain('Starts'); - }); - - it('returns empty string when milestone start and due dates are not present', async () => { - wrapper.setProps({ - milestone: { ...mockMilestone, start_date: '', due_date: '' }, - }); - await nextTick(); - - expect(wrapper.vm.milestoneDatesHuman).toBe(''); - }); - }); - }); - - describe('template', () => { - it('renders component root element with class `issue-milestone-details`', () => { - expect(vm.$el.classList.contains('issue-milestone-details')).toBe(true); - }); - - it('renders milestone icon', () => { - expect(wrapper.findComponent(GlIcon).props('name')).toBe('clock'); - }); - - it('renders milestone title', () => { - expect(vm.$el.querySelector('.milestone-title').innerText.trim()).toBe(mockMilestone.title); - }); - - it('renders milestone tooltip', () => { - expect(vm.$el.querySelector('.js-item-milestone').innerText.trim()).toContain( - mockMilestone.title, - ); }); }); }); diff --git a/spec/frontend/lib/utils/text_markdown_spec.js b/spec/frontend/lib/utils/text_markdown_spec.js index 2180ea7e6c2..7ca9715430d 100644 --- a/spec/frontend/lib/utils/text_markdown_spec.js +++ b/spec/frontend/lib/utils/text_markdown_spec.js @@ -10,6 +10,7 @@ import { } from '~/lib/utils/text_markdown'; import { HTTP_STATUS_OK } from '~/lib/utils/http_status'; import '~/lib/utils/jquery_at_who'; +import { ENTER_KEY } from '~/lib/utils/keys'; import axios from '~/lib/utils/axios_utils'; import { setHTMLFixture, resetHTMLFixture } from 'helpers/fixtures'; @@ -208,7 +209,7 @@ describe('init markdown', () => { let enterEvent; beforeEach(() => { - enterEvent = new KeyboardEvent('keydown', { key: 'Enter', cancelable: true }); + enterEvent = new KeyboardEvent('keydown', { key: ENTER_KEY, cancelable: true }); textArea.addEventListener('keydown', keypressNoteText); textArea.addEventListener('compositionstart', compositionStartNoteText); textArea.addEventListener('compositionend', compositionEndNoteText); @@ -492,6 +493,53 @@ describe('init markdown', () => { }); }); + describe('adding a hard break using Shift+Enter', () => { + let enterEvent; + + beforeEach(() => { + enterEvent = new KeyboardEvent('keydown', { key: ENTER_KEY, shiftKey: true }); + textArea.addEventListener('keydown', keypressNoteText); + textArea.addEventListener('compositionstart', compositionStartNoteText); + textArea.addEventListener('compositionend', compositionEndNoteText); + }); + + it.each` + selectionStart | selectionEnd | expected | expectedSelectionStart + ${0} | ${0} | ${'\\\n0123456789'} | ${2} + ${3} | ${3} | ${'012\\\n3456789'} | ${5} + ${3} | ${6} | ${'012\\\n6789'} | ${5} + `( + 'adds a hard break', + ({ selectionStart, selectionEnd, expected, expectedSelectionStart }) => { + const text = '0123456789'; + textArea.value = text; + textArea.setSelectionRange(selectionStart, selectionEnd); + + textArea.dispatchEvent(enterEvent); + + expect(textArea.value).toEqual(expected); + expect(textArea.selectionStart).toEqual(expectedSelectionStart); + expect(textArea.selectionEnd).toEqual(expectedSelectionStart); + }, + ); + + it.each` + keyEvent + ${new KeyboardEvent('keydown', { key: ENTER_KEY, shiftKey: false })} + ${new KeyboardEvent('keydown', { key: ENTER_KEY, shiftKey: true, metaKey: true })} + ${new KeyboardEvent('keydown', { key: ENTER_KEY, shiftKey: true, altKey: true })} + ${new KeyboardEvent('keydown', { key: ENTER_KEY, shiftKey: true, ctrlKey: true })} + `('does not add when shift is pressed with other keys', ({ keyEvent }) => { + const text = '0123456789'; + textArea.value = text; + textArea.setSelectionRange(0, 0); + + textArea.dispatchEvent(keyEvent); + + expect(textArea.value).toEqual(text); + }); + }); + describe('with selection', () => { let text = 'initial selected value'; let selected = 'selected'; diff --git a/spec/frontend/profile/account/components/update_username_spec.js b/spec/frontend/profile/account/components/update_username_spec.js index 028c30cb291..b19db73459d 100644 --- a/spec/frontend/profile/account/components/update_username_spec.js +++ b/spec/frontend/profile/account/components/update_username_spec.js @@ -1,6 +1,7 @@ import { GlModal } from '@gitlab/ui'; import MockAdapter from 'axios-mock-adapter'; -import { nextTick } from 'vue'; +import Vue, { nextTick } from 'vue'; +import waitForPromises from 'helpers/wait_for_promises'; import { TEST_HOST } from 'helpers/test_constants'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { createAlert } from '~/flash'; @@ -43,6 +44,7 @@ describe('UpdateUsername component', () => { afterEach(() => { wrapper.destroy(); axiosMock.restore(); + Vue.config.errorHandler = null; }); const findElements = () => { @@ -58,6 +60,13 @@ describe('UpdateUsername component', () => { }; }; + const clickModalWithErrorResponse = () => { + Vue.config.errorHandler = jest.fn(); // silence thrown error + const { modal } = findElements(); + modal.vm.$emit('primary'); + return waitForPromises(); + }; + it('has a disabled button if the username was not changed', async () => { const { openModalBtn } = findElements(); @@ -98,14 +107,15 @@ describe('UpdateUsername component', () => { axiosMock.onPut(actionUrl).replyOnce(() => [HTTP_STATUS_OK, { message: 'Username changed' }]); jest.spyOn(axios, 'put'); - await wrapper.vm.onConfirm(); - await nextTick(); + const { modal } = findElements(); + modal.vm.$emit('primary'); + await waitForPromises(); expect(axios.put).toHaveBeenCalledWith(actionUrl, { user: { username: newUsername } }); }); it('sets the username after a successful update', async () => { - const { input, openModalBtn } = findElements(); + const { input, openModalBtn, modal } = findElements(); axiosMock.onPut(actionUrl).replyOnce(() => { expect(input.attributes('disabled')).toBe('disabled'); @@ -115,8 +125,8 @@ describe('UpdateUsername component', () => { return [HTTP_STATUS_OK, { message: 'Username changed' }]; }); - await wrapper.vm.onConfirm(); - await nextTick(); + modal.vm.$emit('primary'); + await waitForPromises(); expect(input.attributes('disabled')).toBe(undefined); expect(openModalBtn.props('disabled')).toBe(true); @@ -134,7 +144,8 @@ describe('UpdateUsername component', () => { return [HTTP_STATUS_BAD_REQUEST, { message: 'Invalid username' }]; }); - await expect(wrapper.vm.onConfirm()).rejects.toThrow(); + await clickModalWithErrorResponse(); + expect(input.attributes('disabled')).toBe(undefined); expect(openModalBtn.props('disabled')).toBe(false); expect(openModalBtn.props('loading')).toBe(false); @@ -145,7 +156,7 @@ describe('UpdateUsername component', () => { return [HTTP_STATUS_BAD_REQUEST, { message: 'Invalid username' }]; }); - await expect(wrapper.vm.onConfirm()).rejects.toThrow(); + await clickModalWithErrorResponse(); expect(createAlert).toHaveBeenCalledWith({ message: 'Invalid username', @@ -157,7 +168,7 @@ describe('UpdateUsername component', () => { return [HTTP_STATUS_BAD_REQUEST]; }); - await expect(wrapper.vm.onConfirm()).rejects.toThrow(); + await clickModalWithErrorResponse(); expect(createAlert).toHaveBeenCalledWith({ message: 'An error occurred while updating your username, please try again.', diff --git a/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js b/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js index 8d0fd390e35..8bea84f4429 100644 --- a/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js +++ b/spec/frontend/projects/settings/repository/branch_rules/components/branch_rule_spec.js @@ -71,8 +71,10 @@ describe('Branch rule', () => { }); it('renders a detail button with the correct href', () => { + const encodedBranchName = encodeURIComponent(branchRulePropsMock.name); + expect(findDetailsButton().attributes('href')).toBe( - `${branchRuleProvideMock.branchRulesPath}?branch=${branchRulePropsMock.name}`, + `${branchRuleProvideMock.branchRulesPath}?branch=${encodedBranchName}`, ); }); }); diff --git a/spec/frontend/projects/settings/repository/branch_rules/mock_data.js b/spec/frontend/projects/settings/repository/branch_rules/mock_data.js index de7f6c8b88d..d169397241d 100644 --- a/spec/frontend/projects/settings/repository/branch_rules/mock_data.js +++ b/spec/frontend/projects/settings/repository/branch_rules/mock_data.js @@ -74,7 +74,7 @@ export const branchRuleProvideMock = { }; export const branchRulePropsMock = { - name: 'main', + name: 'branch-with-$speci@l-#-chars', isDefault: true, matchingBranchesCount: 1, branchProtection: { diff --git a/spec/frontend/sidebar/components/participants/participants_spec.js b/spec/frontend/sidebar/components/participants/participants_spec.js index f7a626a189c..72d83ebeca4 100644 --- a/spec/frontend/sidebar/components/participants/participants_spec.js +++ b/spec/frontend/sidebar/components/participants/participants_spec.js @@ -1,203 +1,114 @@ -import { GlLoadingIcon } from '@gitlab/ui'; +import { GlButton, GlLoadingIcon } from '@gitlab/ui'; import { shallowMount } from '@vue/test-utils'; -import { nextTick } from 'vue'; import Participants from '~/sidebar/components/participants/participants.vue'; -const PARTICIPANT = { - id: 1, - state: 'active', - username: 'marcene', - name: 'Allie Will', - web_url: 'foo.com', - avatar_url: 'gravatar.com/avatar/xxx', -}; - -const PARTICIPANT_LIST = [PARTICIPANT, { ...PARTICIPANT, id: 2 }, { ...PARTICIPANT, id: 3 }]; - -describe('Participants', () => { +describe('Participants component', () => { let wrapper; - const getMoreParticipantsButton = () => wrapper.find('[data-testid="more-participants"]'); - const getCollapsedParticipantsCount = () => wrapper.find('[data-testid="collapsed-count"]'); + const participant = { + id: 1, + state: 'active', + username: 'marcene', + name: 'Allie Will', + web_url: 'foo.com', + avatar_url: 'gravatar.com/avatar/xxx', + }; - const mountComponent = (propsData) => - shallowMount(Participants, { - propsData, - }); + const participants = [participant, { ...participant, id: 2 }, { ...participant, id: 3 }]; - afterEach(() => { - wrapper.destroy(); - wrapper = null; - }); + const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findMoreParticipantsButton = () => wrapper.findComponent(GlButton); + const findCollapsedIcon = () => wrapper.find('.sidebar-collapsed-icon'); + const findParticipantsAuthor = () => wrapper.findAll('.participants-author'); + + const mountComponent = (propsData) => shallowMount(Participants, { propsData }); describe('collapsed sidebar state', () => { it('shows loading spinner when loading', () => { - wrapper = mountComponent({ - loading: true, - }); + wrapper = mountComponent({ loading: true }); - expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); + expect(findLoadingIcon().exists()).toBe(true); }); - it('does not show loading spinner not loading', () => { - wrapper = mountComponent({ - loading: false, - }); + it('does not show loading spinner when not loading', () => { + wrapper = mountComponent({ loading: false }); - expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(false); + expect(findLoadingIcon().exists()).toBe(false); }); it('shows participant count when given', () => { - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - }); + wrapper = mountComponent({ participants }); - expect(getCollapsedParticipantsCount().text()).toBe(`${PARTICIPANT_LIST.length}`); + expect(findCollapsedIcon().text()).toBe(participants.length.toString()); }); it('shows full participant count when there are hidden participants', () => { - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 1, - }); + wrapper = mountComponent({ participants, numberOfLessParticipants: 1 }); - expect(getCollapsedParticipantsCount().text()).toBe(`${PARTICIPANT_LIST.length}`); + expect(findCollapsedIcon().text()).toBe(participants.length.toString()); }); }); describe('expanded sidebar state', () => { it('shows loading spinner when loading', () => { - wrapper = mountComponent({ - loading: true, - }); + wrapper = mountComponent({ loading: true }); - expect(wrapper.findComponent(GlLoadingIcon).exists()).toBe(true); + expect(findLoadingIcon().exists()).toBe(true); }); - it('when only showing visible participants, shows an avatar only for each participant under the limit', async () => { + it('when only showing visible participants, shows an avatar only for each participant under the limit', () => { const numberOfLessParticipants = 2; - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants, - }); - - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - isShowingMoreParticipants: false, - }); - - await nextTick(); - expect(wrapper.findAll('.participants-author')).toHaveLength(numberOfLessParticipants); + wrapper = mountComponent({ participants, numberOfLessParticipants }); + + expect(findParticipantsAuthor()).toHaveLength(numberOfLessParticipants); }); it('when only showing all participants, each has an avatar', async () => { - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); - - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - isShowingMoreParticipants: true, - }); - - await nextTick(); - expect(wrapper.findAll('.participants-author')).toHaveLength(PARTICIPANT_LIST.length); + wrapper = mountComponent({ participants, numberOfLessParticipants: 2 }); + + await findMoreParticipantsButton().vm.$emit('click'); + + expect(findParticipantsAuthor()).toHaveLength(participants.length); }); it('does not have more participants link when they can all be shown', () => { const numberOfLessParticipants = 100; - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants, - }); - - expect(PARTICIPANT_LIST.length).toBeLessThan(numberOfLessParticipants); - expect(getMoreParticipantsButton().exists()).toBe(false); - }); + wrapper = mountComponent({ participants, numberOfLessParticipants }); - it('when too many participants, has more participants link to show more', async () => { - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); - - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - isShowingMoreParticipants: false, - }); - - await nextTick(); - expect(getMoreParticipantsButton().text()).toBe('+ 1 more'); + expect(participants.length).toBeLessThan(numberOfLessParticipants); + expect(findMoreParticipantsButton().exists()).toBe(false); }); - it('when too many participants and already showing them, has more participants link to show less', async () => { - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); - - // setData usage is discouraged. See https://gitlab.com/groups/gitlab-org/-/epics/7330 for details - // eslint-disable-next-line no-restricted-syntax - wrapper.setData({ - isShowingMoreParticipants: true, - }); - - await nextTick(); - expect(getMoreParticipantsButton().text()).toBe('- show less'); - }); + it('when too many participants, has more participants link to show more', () => { + wrapper = mountComponent({ participants, numberOfLessParticipants: 2 }); - it('clicking more participants link emits event', () => { - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); + expect(findMoreParticipantsButton().text()).toBe('+ 1 more'); + }); - expect(wrapper.vm.isShowingMoreParticipants).toBe(false); + it('when too many participants and already showing them, has more participants link to show less', async () => { + wrapper = mountComponent({ participants, numberOfLessParticipants: 2 }); - getMoreParticipantsButton().vm.$emit('click'); + await findMoreParticipantsButton().vm.$emit('click'); - expect(wrapper.vm.isShowingMoreParticipants).toBe(true); + expect(findMoreParticipantsButton().text()).toBe('- show less'); }); - it('clicking on participants icon emits `toggleSidebar` event', async () => { - wrapper = mountComponent({ - loading: false, - participants: PARTICIPANT_LIST, - numberOfLessParticipants: 2, - }); - - const spy = jest.spyOn(wrapper.vm, '$emit'); + it('clicking on participants icon emits `toggleSidebar` event', () => { + wrapper = mountComponent({ participants, numberOfLessParticipants: 2 }); - wrapper.find('.sidebar-collapsed-icon').trigger('click'); + findCollapsedIcon().trigger('click'); - await nextTick(); - expect(spy).toHaveBeenCalledWith('toggleSidebar'); - spy.mockRestore(); + expect(wrapper.emitted('toggleSidebar')).toEqual([[]]); }); }); describe('when not showing participants label', () => { beforeEach(() => { - wrapper = mountComponent({ - participants: PARTICIPANT_LIST, - showParticipantLabel: false, - }); + wrapper = mountComponent({ participants, showParticipantLabel: false }); }); it('does not show sidebar collapsed icon', () => { - expect(wrapper.find('.sidebar-collapsed-icon').exists()).toBe(false); + expect(findCollapsedIcon().exists()).toBe(false); }); it('does not show participants label title', () => { diff --git a/spec/frontend/super_sidebar/components/sidebar_portal_spec.js b/spec/frontend/super_sidebar/components/sidebar_portal_spec.js new file mode 100644 index 00000000000..3ef1cb7e692 --- /dev/null +++ b/spec/frontend/super_sidebar/components/sidebar_portal_spec.js @@ -0,0 +1,68 @@ +import { nextTick } from 'vue'; +import { mount } from '@vue/test-utils'; +import SidebarPortal from '~/super_sidebar/components/sidebar_portal.vue'; +import SidebarPortalTarget from '~/super_sidebar/components/sidebar_portal_target.vue'; + +describe('SidebarPortal', () => { + let targetWrapper; + + const Target = { + components: { SidebarPortalTarget }, + props: ['show'], + template: '<sidebar-portal-target v-if="show" />', + }; + + const Source = { + components: { SidebarPortal }, + template: '<sidebar-portal><br data-testid="test"></sidebar-portal>', + }; + + const mountSource = () => { + mount(Source); + }; + + const mountTarget = ({ show = true } = {}) => { + targetWrapper = mount(Target, { + propsData: { show }, + attachTo: document.body, + }); + }; + + const findTestContent = () => targetWrapper.find('[data-testid="test"]'); + + it('renders content into the target', async () => { + mountTarget(); + await nextTick(); + + mountSource(); + await nextTick(); + + expect(findTestContent().exists()).toBe(true); + }); + + it('waits for target to be available before rendering', async () => { + mountSource(); + await nextTick(); + + mountTarget(); + await nextTick(); + + expect(findTestContent().exists()).toBe(true); + }); + + it('supports conditional rendering of target', async () => { + mountTarget({ show: false }); + await nextTick(); + + mountSource(); + await nextTick(); + + expect(findTestContent().exists()).toBe(false); + + await targetWrapper.setProps({ show: true }); + expect(findTestContent().exists()).toBe(true); + + await targetWrapper.setProps({ show: false }); + expect(findTestContent().exists()).toBe(false); + }); +}); diff --git a/spec/frontend/super_sidebar/components/super_sidebar_spec.js b/spec/frontend/super_sidebar/components/super_sidebar_spec.js index 45fc30c08f0..57c84bc87a6 100644 --- a/spec/frontend/super_sidebar/components/super_sidebar_spec.js +++ b/spec/frontend/super_sidebar/components/super_sidebar_spec.js @@ -2,6 +2,7 @@ import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import SuperSidebar from '~/super_sidebar/components/super_sidebar.vue'; import HelpCenter from '~/super_sidebar/components/help_center.vue'; import UserBar from '~/super_sidebar/components/user_bar.vue'; +import SidebarPortalTarget from '~/super_sidebar/components/sidebar_portal_target.vue'; import { sidebarData } from '../mock_data'; describe('SuperSidebar component', () => { @@ -9,6 +10,7 @@ describe('SuperSidebar component', () => { const findUserBar = () => wrapper.findComponent(UserBar); const findHelpCenter = () => wrapper.findComponent(HelpCenter); + const findSidebarPortalTarget = () => wrapper.findComponent(SidebarPortalTarget); const createWrapper = (props = {}) => { wrapper = shallowMountExtended(SuperSidebar, { @@ -31,5 +33,9 @@ describe('SuperSidebar component', () => { it('renders HelpCenter with sidebarData', () => { expect(findHelpCenter().props('sidebarData')).toBe(sidebarData); }); + + it('renders SidebarPortalTarget', () => { + expect(findSidebarPortalTarget().exists()).toBe(true); + }); }); }); |