summaryrefslogtreecommitdiff
path: root/spec/frontend/issue_show/components
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/issue_show/components')
-rw-r--r--spec/frontend/issue_show/components/app_spec.js122
-rw-r--r--spec/frontend/issue_show/components/description_spec.js26
-rw-r--r--spec/frontend/issue_show/components/edit_actions_spec.js21
-rw-r--r--spec/frontend/issue_show/components/incidents/highlight_bar_spec.js56
-rw-r--r--spec/frontend/issue_show/components/incidents/incident_tabs_spec.js101
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);
+ });
+ });
+});