diff options
Diffstat (limited to 'spec/frontend/issue_show/components')
5 files changed, 263 insertions, 63 deletions
diff --git a/spec/frontend/issue_show/components/app_spec.js b/spec/frontend/issue_show/components/app_spec.js index f76f42cb9ae..f4095d4de96 100644 --- a/spec/frontend/issue_show/components/app_spec.js +++ b/spec/frontend/issue_show/components/app_spec.js @@ -1,14 +1,22 @@ import { GlIntersectionObserver } from '@gitlab/ui'; import { mount } from '@vue/test-utils'; import MockAdapter from 'axios-mock-adapter'; -import { TEST_HOST } from 'helpers/test_constants'; import { useMockIntersectionObserver } from 'helpers/mock_dom_observer'; import axios from '~/lib/utils/axios_utils'; import { visitUrl } from '~/lib/utils/url_utility'; import '~/behaviors/markdown/render_gfm'; import IssuableApp from '~/issue_show/components/app.vue'; import eventHub from '~/issue_show/event_hub'; -import { initialRequest, secondRequest } from '../mock_data'; +import { + appProps, + initialRequest, + publishedIncidentUrl, + secondRequest, + zoomMeetingUrl, +} from '../mock_data'; +import IncidentTabs from '~/issue_show/components/incidents/incident_tabs.vue'; +import DescriptionComponent from '~/issue_show/components/description.vue'; +import PinnedLinks from '~/issue_show/components/pinned_links.vue'; function formatText(text) { return text.trim().replace(/\s\s+/g, ' '); @@ -19,9 +27,6 @@ jest.mock('~/issue_show/event_hub'); const REALTIME_REQUEST_STACK = [initialRequest, secondRequest]; -const zoomMeetingUrl = 'https://gitlab.zoom.us/j/95919234811'; -const publishedIncidentUrl = 'https://status.com/'; - describe('Issuable output', () => { useMockIntersectionObserver(); @@ -31,6 +36,21 @@ describe('Issuable output', () => { const findStickyHeader = () => wrapper.find('[data-testid="issue-sticky-header"]'); + const mountComponent = (props = {}, options = {}) => { + wrapper = mount(IssuableApp, { + propsData: { ...appProps, ...props }, + provide: { + fullPath: 'gitlab-org/incidents', + iid: '19', + }, + stubs: { + HighlightBar: true, + IncidentTabs: true, + }, + ...options, + }); + }; + beforeEach(() => { setFixtures(` <div> @@ -57,28 +77,9 @@ describe('Issuable output', () => { return res; }); - wrapper = mount(IssuableApp, { - propsData: { - canUpdate: true, - canDestroy: true, - endpoint: '/gitlab-org/gitlab-shell/-/issues/9/realtime_changes', - updateEndpoint: TEST_HOST, - issuableRef: '#1', - issuableStatus: 'opened', - initialTitleHtml: '', - initialTitleText: '', - initialDescriptionHtml: 'test', - initialDescriptionText: 'test', - lockVersion: 1, - markdownPreviewPath: '/', - markdownDocsPath: '/', - projectNamespace: '/', - projectPath: '/', - issuableTemplateNamesPath: '/issuable-templates-path', - zoomMeetingUrl, - publishedIncidentUrl, - }, - }); + mountComponent(); + + jest.advanceTimersByTime(2); }); afterEach(() => { @@ -134,7 +135,7 @@ describe('Issuable output', () => { wrapper.vm.showForm = true; return wrapper.vm.$nextTick().then(() => { - expect(wrapper.contains('.markdown-selector')).toBe(true); + expect(wrapper.find('.markdown-selector').exists()).toBe(true); }); }); @@ -143,7 +144,7 @@ describe('Issuable output', () => { wrapper.setProps({ canUpdate: false }); return wrapper.vm.$nextTick().then(() => { - expect(wrapper.contains('.markdown-selector')).toBe(false); + expect(wrapper.find('.markdown-selector').exists()).toBe(false); }); }); @@ -403,7 +404,7 @@ describe('Issuable output', () => { .then(() => { expect(wrapper.vm.formState.lockedWarningVisible).toEqual(true); expect(wrapper.vm.formState.lock_version).toEqual(1); - expect(wrapper.contains('.alert')).toBe(true); + expect(wrapper.find('.alert').exists()).toBe(true); }); }); }); @@ -441,14 +442,14 @@ describe('Issuable output', () => { describe('show inline edit button', () => { it('should not render by default', () => { - expect(wrapper.contains('.btn-edit')).toBe(true); + expect(wrapper.find('.btn-edit').exists()).toBe(true); }); it('should render if showInlineEditButton', () => { wrapper.setProps({ showInlineEditButton: true }); return wrapper.vm.$nextTick(() => { - expect(wrapper.contains('.btn-edit')).toBe(true); + expect(wrapper.find('.btn-edit').exists()).toBe(true); }); }); }); @@ -531,7 +532,7 @@ describe('Issuable output', () => { describe('sticky header', () => { describe('when title is in view', () => { it('is not shown', () => { - expect(wrapper.contains('.issue-sticky-header')).toBe(false); + expect(wrapper.find('.issue-sticky-header').exists()).toBe(false); }); }); @@ -562,4 +563,59 @@ describe('Issuable output', () => { }); }); }); + + describe('Composable description component', () => { + const findIncidentTabs = () => wrapper.find(IncidentTabs); + const findDescriptionComponent = () => wrapper.find(DescriptionComponent); + const findPinnedLinks = () => wrapper.find(PinnedLinks); + const borderClass = 'gl-border-b-1 gl-border-b-gray-100 gl-border-b-solid gl-mb-6'; + + describe('when using description component', () => { + it('renders the description component', () => { + expect(findDescriptionComponent().exists()).toBe(true); + }); + + it('does not render incident tabs', () => { + expect(findIncidentTabs().exists()).toBe(false); + }); + + it('adds a border below the header', () => { + expect(findPinnedLinks().attributes('class')).toContain(borderClass); + }); + }); + + describe('when using incident tabs description wrapper', () => { + beforeEach(() => { + mountComponent( + { + descriptionComponent: IncidentTabs, + showTitleBorder: false, + }, + { + mocks: { + $apollo: { + queries: { + alert: { + loading: false, + }, + }, + }, + }, + }, + ); + }); + + it('renders the description component', () => { + expect(findDescriptionComponent().exists()).toBe(true); + }); + + it('renders incident tabs', () => { + expect(findIncidentTabs().exists()).toBe(true); + }); + + it('does not add a border below the header', () => { + expect(findPinnedLinks().attributes('class')).not.toContain(borderClass); + }); + }); + }); }); diff --git a/spec/frontend/issue_show/components/description_spec.js b/spec/frontend/issue_show/components/description_spec.js index 0053475dd13..bc7511225a0 100644 --- a/spec/frontend/issue_show/components/description_spec.js +++ b/spec/frontend/issue_show/components/description_spec.js @@ -5,20 +5,13 @@ import mountComponent from 'helpers/vue_mount_component_helper'; import { TEST_HOST } from 'helpers/test_constants'; import Description from '~/issue_show/components/description.vue'; import TaskList from '~/task_list'; +import { descriptionProps as props } from '../mock_data'; jest.mock('~/task_list'); describe('Description component', () => { let vm; let DescriptionComponent; - const props = { - canUpdate: true, - descriptionHtml: 'test', - descriptionText: 'test', - updatedAt: new Date().toString(), - taskStatus: '', - updateUrl: TEST_HOST, - }; beforeEach(() => { DescriptionComponent = Vue.extend(Description); @@ -43,12 +36,27 @@ describe('Description component', () => { $('.issuable-meta .flash-container').remove(); }); - it('animates description changes', () => { + it('doesnt animate first description changes', () => { vm.descriptionHtml = 'changed'; + return vm.$nextTick().then(() => { + expect( + vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'), + ).toBeFalsy(); + jest.runAllTimers(); + return vm.$nextTick(); + }); + }); + + it('animates description changes on live update', () => { + vm.descriptionHtml = 'changed'; return vm .$nextTick() .then(() => { + vm.descriptionHtml = 'changed second time'; + return vm.$nextTick(); + }) + .then(() => { expect( vm.$el.querySelector('.md').classList.contains('issue-realtime-pre-pulse'), ).toBeTruthy(); diff --git a/spec/frontend/issue_show/components/edit_actions_spec.js b/spec/frontend/issue_show/components/edit_actions_spec.js index b0c1894058e..79a2bcd5eab 100644 --- a/spec/frontend/issue_show/components/edit_actions_spec.js +++ b/spec/frontend/issue_show/components/edit_actions_spec.js @@ -70,16 +70,6 @@ describe('Edit Actions components', () => { expect(eventHub.$emit).toHaveBeenCalledWith('update.issuable'); }); - it('shows loading icon after clicking save button', done => { - vm.$el.querySelector('.btn-success').click(); - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn-success .fa')).not.toBeNull(); - - done(); - }); - }); - it('disabled button after clicking save button', done => { vm.$el.querySelector('.btn-success').click(); @@ -107,17 +97,6 @@ describe('Edit Actions components', () => { expect(eventHub.$emit).toHaveBeenCalledWith('delete.issuable', { destroy_confirm: true }); }); - it('shows loading icon after clicking delete button', done => { - jest.spyOn(window, 'confirm').mockReturnValue(true); - vm.$el.querySelector('.btn-danger').click(); - - Vue.nextTick(() => { - expect(vm.$el.querySelector('.btn-danger .fa')).not.toBeNull(); - - done(); - }); - }); - it('does no actions when confirm is false', done => { jest.spyOn(window, 'confirm').mockReturnValue(false); vm.$el.querySelector('.btn-danger').click(); diff --git a/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js b/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js new file mode 100644 index 00000000000..8d50df5e406 --- /dev/null +++ b/spec/frontend/issue_show/components/incidents/highlight_bar_spec.js @@ -0,0 +1,56 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlLink } from '@gitlab/ui'; +import HighlightBar from '~/issue_show/components/incidents/highlight_bar.vue'; +import { formatDate } from '~/lib/utils/datetime_utility'; + +jest.mock('~/lib/utils/datetime_utility'); + +describe('Highlight Bar', () => { + let wrapper; + + const alert = { + startedAt: '2020-05-29T10:39:22Z', + detailsUrl: 'http://127.0.0.1:3000/root/unique-alerts/-/alert_management/1/details', + eventCount: 1, + title: 'Alert 1', + }; + + const mountComponent = () => { + wrapper = shallowMount(HighlightBar, { + propsData: { + alert, + }, + }); + }; + + beforeEach(() => { + mountComponent(); + }); + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + }); + + const findLink = () => wrapper.find(GlLink); + + it('renders a link to the alert page', () => { + expect(findLink().exists()).toBe(true); + expect(findLink().attributes('href')).toBe(alert.detailsUrl); + expect(findLink().text()).toContain(alert.title); + }); + + it('renders formatted start time of the alert', () => { + const formattedDate = '2020-05-29 UTC'; + formatDate.mockReturnValueOnce(formattedDate); + mountComponent(); + expect(formatDate).toHaveBeenCalledWith(alert.startedAt, 'yyyy-mm-dd Z'); + expect(wrapper.text()).toContain(formattedDate); + }); + + it('renders a number of alert events', () => { + expect(wrapper.text()).toContain(alert.eventCount); + }); +}); diff --git a/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js b/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js new file mode 100644 index 00000000000..a51b497cd79 --- /dev/null +++ b/spec/frontend/issue_show/components/incidents/incident_tabs_spec.js @@ -0,0 +1,101 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlTab } from '@gitlab/ui'; +import INVALID_URL from '~/lib/utils/invalid_url'; +import IncidentTabs from '~/issue_show/components/incidents/incident_tabs.vue'; +import { descriptionProps } from '../../mock_data'; +import DescriptionComponent from '~/issue_show/components/description.vue'; +import HighlightBar from '~/issue_show/components/incidents/highlight_bar.vue'; +import AlertDetailsTable from '~/vue_shared/components/alert_details_table.vue'; + +const mockAlert = { + __typename: 'AlertManagementAlert', + detailsUrl: INVALID_URL, + iid: '1', +}; + +describe('Incident Tabs component', () => { + let wrapper; + + const mountComponent = (data = {}) => { + wrapper = shallowMount(IncidentTabs, { + propsData: { + ...descriptionProps, + }, + stubs: { + DescriptionComponent: true, + }, + provide: { + fullPath: '', + iid: '', + }, + data() { + return { alert: mockAlert, ...data }; + }, + mocks: { + $apollo: { + queries: { + alert: { + loading: true, + }, + }, + }, + }, + }); + }; + + const findTabs = () => wrapper.findAll(GlTab); + const findSummaryTab = () => findTabs().at(0); + const findAlertDetailsTab = () => findTabs().at(1); + const findAlertDetailsComponent = () => wrapper.find(AlertDetailsTable); + const findDescriptionComponent = () => wrapper.find(DescriptionComponent); + const findHighlightBarComponent = () => wrapper.find(HighlightBar); + + describe('empty state', () => { + beforeEach(() => { + mountComponent({ alert: null }); + }); + + it('does not show the alert details tab', () => { + expect(findAlertDetailsComponent().exists()).toBe(false); + expect(findHighlightBarComponent().exists()).toBe(false); + }); + }); + + describe('with an alert present', () => { + beforeEach(() => { + mountComponent(); + }); + + it('renders the summary tab', () => { + expect(findSummaryTab().exists()).toBe(true); + expect(findSummaryTab().attributes('title')).toBe('Summary'); + }); + + it('renders the alert details tab', () => { + expect(findAlertDetailsTab().exists()).toBe(true); + expect(findAlertDetailsTab().attributes('title')).toBe('Alert details'); + }); + + it('renders the alert details table with the correct props', () => { + const alert = { iid: mockAlert.iid }; + + expect(findAlertDetailsComponent().props('alert')).toEqual(alert); + expect(findAlertDetailsComponent().props('loading')).toBe(true); + }); + + it('renders the description component with highlight bar', () => { + expect(findDescriptionComponent().exists()).toBe(true); + expect(findHighlightBarComponent().exists()).toBe(true); + }); + + it('renders the highlight bar component with the correct props', () => { + const alert = { detailsUrl: mockAlert.detailsUrl }; + + expect(findHighlightBarComponent().props('alert')).toMatchObject(alert); + }); + + it('passes all props to the description component', () => { + expect(findDescriptionComponent().props()).toMatchObject(descriptionProps); + }); + }); +}); |