summaryrefslogtreecommitdiff
path: root/spec/frontend/reports
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/reports')
-rw-r--r--spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js126
-rw-r--r--spec/frontend/reports/accessibility_report/mock_data.js55
-rw-r--r--spec/frontend/reports/accessibility_report/store/actions_spec.js121
-rw-r--r--spec/frontend/reports/accessibility_report/store/getters_spec.js149
-rw-r--r--spec/frontend/reports/accessibility_report/store/mutations_spec.js64
-rw-r--r--spec/frontend/reports/components/__snapshots__/grouped_issues_list_spec.js.snap25
-rw-r--r--spec/frontend/reports/components/__snapshots__/issue_status_icon_spec.js.snap37
-rw-r--r--spec/frontend/reports/components/grouped_issues_list_spec.js86
-rw-r--r--spec/frontend/reports/components/grouped_test_reports_app_spec.js260
-rw-r--r--spec/frontend/reports/components/issue_status_icon_spec.js29
-rw-r--r--spec/frontend/reports/components/modal_open_name_spec.js47
-rw-r--r--spec/frontend/reports/components/modal_spec.js54
-rw-r--r--spec/frontend/reports/components/summary_row_spec.js37
-rw-r--r--spec/frontend/reports/components/test_issue_body_spec.js72
-rw-r--r--spec/frontend/reports/mock_data/mock_data.js24
-rw-r--r--spec/frontend/reports/mock_data/new_and_fixed_failures_report.json55
-rw-r--r--spec/frontend/reports/mock_data/new_errors_report.json38
-rw-r--r--spec/frontend/reports/mock_data/new_failures_report.json38
-rw-r--r--spec/frontend/reports/mock_data/no_failures_report.json28
-rw-r--r--spec/frontend/reports/mock_data/resolved_failures.json58
-rw-r--r--spec/frontend/reports/store/actions_spec.js171
-rw-r--r--spec/frontend/reports/store/mutations_spec.js126
22 files changed, 1700 insertions, 0 deletions
diff --git a/spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js b/spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js
new file mode 100644
index 00000000000..a036588596a
--- /dev/null
+++ b/spec/frontend/reports/accessibility_report/grouped_accessibility_reports_app_spec.js
@@ -0,0 +1,126 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import GroupedAccessibilityReportsApp from '~/reports/accessibility_report/grouped_accessibility_reports_app.vue';
+import AccessibilityIssueBody from '~/reports/accessibility_report/components/accessibility_issue_body.vue';
+import store from '~/reports/accessibility_report/store';
+import { mockReport } from './mock_data';
+
+const localVue = createLocalVue();
+localVue.use(Vuex);
+
+describe('Grouped accessibility reports app', () => {
+ const Component = localVue.extend(GroupedAccessibilityReportsApp);
+ let wrapper;
+ let mockStore;
+
+ const mountComponent = () => {
+ wrapper = mount(Component, {
+ store: mockStore,
+ localVue,
+ propsData: {
+ endpoint: 'endpoint.json',
+ },
+ methods: {
+ fetchReport: () => {},
+ },
+ });
+ };
+
+ const findHeader = () => wrapper.find('[data-testid="report-section-code-text"]');
+
+ beforeEach(() => {
+ mockStore = store();
+ mountComponent();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('while loading', () => {
+ beforeEach(() => {
+ mockStore.state.isLoading = true;
+ mountComponent();
+ });
+
+ it('renders loading state', () => {
+ expect(findHeader().text()).toEqual('Accessibility scanning results are being parsed');
+ });
+ });
+
+ describe('with error', () => {
+ beforeEach(() => {
+ mockStore.state.isLoading = false;
+ mockStore.state.hasError = true;
+ mountComponent();
+ });
+
+ it('renders error state', () => {
+ expect(findHeader().text()).toEqual('Accessibility scanning failed loading results');
+ });
+ });
+
+ describe('with a report', () => {
+ describe('with no issues', () => {
+ beforeEach(() => {
+ mockStore.state.report = {
+ summary: {
+ errored: 0,
+ },
+ };
+ });
+
+ it('renders no issues header', () => {
+ expect(findHeader().text()).toContain(
+ 'Accessibility scanning detected no issues for the source branch only',
+ );
+ });
+ });
+
+ describe('with one issue', () => {
+ beforeEach(() => {
+ mockStore.state.report = {
+ summary: {
+ errored: 1,
+ },
+ };
+ });
+
+ it('renders one issue header', () => {
+ expect(findHeader().text()).toContain(
+ 'Accessibility scanning detected 1 issue for the source branch only',
+ );
+ });
+ });
+
+ describe('with multiple issues', () => {
+ beforeEach(() => {
+ mockStore.state.report = {
+ summary: {
+ errored: 2,
+ },
+ };
+ });
+
+ it('renders multiple issues header', () => {
+ expect(findHeader().text()).toContain(
+ 'Accessibility scanning detected 2 issues for the source branch only',
+ );
+ });
+ });
+
+ describe('with issues to show', () => {
+ beforeEach(() => {
+ mockStore.state.report = mockReport;
+ });
+
+ it('renders custom accessibility issue body', () => {
+ const issueBody = wrapper.find(AccessibilityIssueBody);
+
+ expect(issueBody.props('issue').code).toBe(mockReport.new_errors[0].code);
+ expect(issueBody.props('issue').message).toBe(mockReport.new_errors[0].message);
+ expect(issueBody.props('isNew')).toBe(true);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/reports/accessibility_report/mock_data.js b/spec/frontend/reports/accessibility_report/mock_data.js
new file mode 100644
index 00000000000..f8e832c1ce5
--- /dev/null
+++ b/spec/frontend/reports/accessibility_report/mock_data.js
@@ -0,0 +1,55 @@
+export const mockReport = {
+ status: 'failed',
+ summary: {
+ total: 2,
+ resolved: 0,
+ errored: 2,
+ },
+ new_errors: [
+ {
+ code: 'WCAG2AA.Principle1.Guideline1_4.1_4_3.G18.Fail',
+ type: 'error',
+ typeCode: 1,
+ message:
+ 'This element has insufficient contrast at this conformance level. Expected a contrast ratio of at least 4.5:1, but text in this element has a contrast ratio of 3.84:1. Recommendation: change text colour to #767676.',
+ context: '<a href="/stages-devops-lifecycle/" class="main-nav-link">Product</a>',
+ selector: '#main-nav > div:nth-child(2) > ul > li:nth-child(1) > a',
+ runner: 'htmlcs',
+ runnerExtras: {},
+ },
+ ],
+ new_notes: [],
+ new_warnings: [],
+ resolved_errors: [
+ {
+ code: 'WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent',
+ type: 'error',
+ typeCode: 1,
+ message:
+ 'Anchor element found with a valid href attribute, but no link content has been supplied.',
+ context: '<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>',
+ selector: '#main-nav > div:nth-child(1) > a',
+ runner: 'htmlcs',
+ runnerExtras: {},
+ },
+ ],
+ resolved_notes: [],
+ resolved_warnings: [],
+ existing_errors: [
+ {
+ code: 'WCAG2AA.Principle4.Guideline4_1.4_1_2.H91.A.NoContent',
+ type: 'error',
+ typeCode: 1,
+ message:
+ 'Anchor element found with a valid href attribute, but no link content has been supplied.',
+ context: '<a href="/" class="navbar-brand animated"><svg height="36" viewBox="0 0 1...</a>',
+ selector: '#main-nav > div:nth-child(1) > a',
+ runner: 'htmlcs',
+ runnerExtras: {},
+ },
+ ],
+ existing_notes: [],
+ existing_warnings: [],
+};
+
+export default () => {};
diff --git a/spec/frontend/reports/accessibility_report/store/actions_spec.js b/spec/frontend/reports/accessibility_report/store/actions_spec.js
new file mode 100644
index 00000000000..129a5bade86
--- /dev/null
+++ b/spec/frontend/reports/accessibility_report/store/actions_spec.js
@@ -0,0 +1,121 @@
+import axios from '~/lib/utils/axios_utils';
+import MockAdapter from 'axios-mock-adapter';
+import * as actions from '~/reports/accessibility_report/store/actions';
+import * as types from '~/reports/accessibility_report/store/mutation_types';
+import createStore from '~/reports/accessibility_report/store';
+import { TEST_HOST } from 'spec/test_constants';
+import testAction from 'helpers/vuex_action_helper';
+import { mockReport } from '../mock_data';
+
+describe('Accessibility Reports actions', () => {
+ let localState;
+ let localStore;
+
+ beforeEach(() => {
+ localStore = createStore();
+ localState = localStore.state;
+ });
+
+ describe('setEndpoints', () => {
+ it('should commit SET_ENDPOINTS mutation', done => {
+ const endpoint = 'endpoint.json';
+
+ testAction(
+ actions.setEndpoint,
+ endpoint,
+ localState,
+ [{ type: types.SET_ENDPOINT, payload: endpoint }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchReport', () => {
+ let mock;
+
+ beforeEach(() => {
+ localState.endpoint = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ actions.stopPolling();
+ actions.clearEtagPoll();
+ });
+
+ describe('success', () => {
+ it('should commit REQUEST_REPORT mutation and dispatch receiveReportSuccess', done => {
+ const data = { report: { summary: {} } };
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(200, data);
+
+ testAction(
+ actions.fetchReport,
+ null,
+ localState,
+ [{ type: types.REQUEST_REPORT }],
+ [
+ {
+ payload: { status: 200, data },
+ type: 'receiveReportSuccess',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ it('should commit REQUEST_REPORT and RECEIVE_REPORT_ERROR mutations', done => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+
+ testAction(
+ actions.fetchReport,
+ null,
+ localState,
+ [{ type: types.REQUEST_REPORT }],
+ [{ type: 'receiveReportError' }],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveReportSuccess', () => {
+ it('should commit RECEIVE_REPORT_SUCCESS mutation with 200', done => {
+ testAction(
+ actions.receiveReportSuccess,
+ { status: 200, data: mockReport },
+ localState,
+ [{ type: types.RECEIVE_REPORT_SUCCESS, payload: mockReport }],
+ [{ type: 'stopPolling' }],
+ done,
+ );
+ });
+
+ it('should not commit RECEIVE_REPORTS_SUCCESS mutation with 204', done => {
+ testAction(
+ actions.receiveReportSuccess,
+ { status: 204, data: mockReport },
+ localState,
+ [],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveReportError', () => {
+ it('should commit RECEIVE_REPORT_ERROR mutation', done => {
+ testAction(
+ actions.receiveReportError,
+ null,
+ localState,
+ [{ type: types.RECEIVE_REPORT_ERROR }],
+ [{ type: 'stopPolling' }],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/frontend/reports/accessibility_report/store/getters_spec.js b/spec/frontend/reports/accessibility_report/store/getters_spec.js
new file mode 100644
index 00000000000..d74c71cfa09
--- /dev/null
+++ b/spec/frontend/reports/accessibility_report/store/getters_spec.js
@@ -0,0 +1,149 @@
+import * as getters from '~/reports/accessibility_report/store/getters';
+import createStore from '~/reports/accessibility_report/store';
+import { LOADING, ERROR, SUCCESS, STATUS_FAILED } from '~/reports/constants';
+
+describe('Accessibility reports store getters', () => {
+ let localState;
+ let localStore;
+
+ beforeEach(() => {
+ localStore = createStore();
+ localState = localStore.state;
+ });
+
+ describe('summaryStatus', () => {
+ describe('when summary is loading', () => {
+ it('returns loading status', () => {
+ localState.isLoading = true;
+
+ expect(getters.summaryStatus(localState)).toEqual(LOADING);
+ });
+ });
+
+ describe('when summary has error', () => {
+ it('returns error status', () => {
+ localState.hasError = true;
+
+ expect(getters.summaryStatus(localState)).toEqual(ERROR);
+ });
+ });
+
+ describe('when summary has failed status', () => {
+ it('returns loading status', () => {
+ localState.status = STATUS_FAILED;
+
+ expect(getters.summaryStatus(localState)).toEqual(ERROR);
+ });
+ });
+
+ describe('when summary has successfully loaded', () => {
+ it('returns loading status', () => {
+ expect(getters.summaryStatus(localState)).toEqual(SUCCESS);
+ });
+ });
+ });
+
+ describe('groupedSummaryText', () => {
+ describe('when state is loading', () => {
+ it('returns the loading summary message', () => {
+ localState.isLoading = true;
+ const result = 'Accessibility scanning results are being parsed';
+
+ expect(getters.groupedSummaryText(localState)).toEqual(result);
+ });
+ });
+
+ describe('when state has error', () => {
+ it('returns the error summary message', () => {
+ localState.hasError = true;
+ const result = 'Accessibility scanning failed loading results';
+
+ expect(getters.groupedSummaryText(localState)).toEqual(result);
+ });
+ });
+
+ describe('when state has successfully loaded', () => {
+ describe('when report has errors', () => {
+ it('returns summary message containing number of errors', () => {
+ localState.report = {
+ summary: {
+ errored: 2,
+ },
+ };
+ const result = 'Accessibility scanning detected 2 issues for the source branch only';
+
+ expect(getters.groupedSummaryText(localState)).toEqual(result);
+ });
+ });
+
+ describe('when report has no errors', () => {
+ it('returns summary message containing no errors', () => {
+ localState.report = {
+ summary: {
+ errored: 0,
+ },
+ };
+ const result = 'Accessibility scanning detected no issues for the source branch only';
+
+ expect(getters.groupedSummaryText(localState)).toEqual(result);
+ });
+ });
+ });
+ });
+
+ describe('shouldRenderIssuesList', () => {
+ describe('when has issues to render', () => {
+ it('returns true', () => {
+ localState.report = {
+ existing_errors: [{ name: 'Issue' }],
+ };
+
+ expect(getters.shouldRenderIssuesList(localState)).toEqual(true);
+ });
+ });
+
+ describe('when does not have issues to render', () => {
+ it('returns false', () => {
+ localState.report = {
+ status: 'success',
+ summary: { errored: 0 },
+ };
+
+ expect(getters.shouldRenderIssuesList(localState)).toEqual(false);
+ });
+ });
+ });
+
+ describe('unresolvedIssues', () => {
+ it('returns the array unresolved errors', () => {
+ localState.report = {
+ existing_errors: [1],
+ };
+ const result = [1];
+
+ expect(getters.unresolvedIssues(localState)).toEqual(result);
+ });
+ });
+
+ describe('resolvedIssues', () => {
+ it('returns array of resolved errors', () => {
+ localState.report = {
+ resolved_errors: [1],
+ };
+ const result = [1];
+
+ expect(getters.resolvedIssues(localState)).toEqual(result);
+ });
+ });
+
+ describe('newIssues', () => {
+ it('returns array of new errors', () => {
+ localState.report = {
+ new_errors: [1],
+ };
+ const result = [1];
+
+ expect(getters.newIssues(localState)).toEqual(result);
+ });
+ });
+});
diff --git a/spec/frontend/reports/accessibility_report/store/mutations_spec.js b/spec/frontend/reports/accessibility_report/store/mutations_spec.js
new file mode 100644
index 00000000000..a4e9571b721
--- /dev/null
+++ b/spec/frontend/reports/accessibility_report/store/mutations_spec.js
@@ -0,0 +1,64 @@
+import mutations from '~/reports/accessibility_report/store/mutations';
+import createStore from '~/reports/accessibility_report/store';
+
+describe('Accessibility Reports mutations', () => {
+ let localState;
+ let localStore;
+
+ beforeEach(() => {
+ localStore = createStore();
+ localState = localStore.state;
+ });
+
+ describe('SET_ENDPOINT', () => {
+ it('sets endpoint to given value', () => {
+ const endpoint = 'endpoint.json';
+ mutations.SET_ENDPOINT(localState, endpoint);
+
+ expect(localState.endpoint).toEqual(endpoint);
+ });
+ });
+
+ describe('REQUEST_REPORT', () => {
+ it('sets isLoading to true', () => {
+ mutations.REQUEST_REPORT(localState);
+
+ expect(localState.isLoading).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_REPORT_SUCCESS', () => {
+ it('sets isLoading to false', () => {
+ mutations.RECEIVE_REPORT_SUCCESS(localState, {});
+
+ expect(localState.isLoading).toEqual(false);
+ });
+
+ it('sets hasError to false', () => {
+ mutations.RECEIVE_REPORT_SUCCESS(localState, {});
+
+ expect(localState.hasError).toEqual(false);
+ });
+
+ it('sets report to response report', () => {
+ const report = { data: 'testing' };
+ mutations.RECEIVE_REPORT_SUCCESS(localState, report);
+
+ expect(localState.report).toEqual(report);
+ });
+ });
+
+ describe('RECEIVE_REPORT_ERROR', () => {
+ it('sets isLoading to false', () => {
+ mutations.RECEIVE_REPORT_ERROR(localState);
+
+ expect(localState.isLoading).toEqual(false);
+ });
+
+ it('sets hasError to true', () => {
+ mutations.RECEIVE_REPORT_ERROR(localState);
+
+ expect(localState.hasError).toEqual(true);
+ });
+ });
+});
diff --git a/spec/frontend/reports/components/__snapshots__/grouped_issues_list_spec.js.snap b/spec/frontend/reports/components/__snapshots__/grouped_issues_list_spec.js.snap
new file mode 100644
index 00000000000..c932379a253
--- /dev/null
+++ b/spec/frontend/reports/components/__snapshots__/grouped_issues_list_spec.js.snap
@@ -0,0 +1,25 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Grouped Issues List renders a smart virtual list with the correct props 1`] = `
+Object {
+ "length": 4,
+ "remain": 20,
+ "rtag": "div",
+ "size": 32,
+ "wclass": "report-block-list",
+ "wtag": "ul",
+}
+`;
+
+exports[`Grouped Issues List with data renders a report item with the correct props 1`] = `
+Object {
+ "component": "TestIssueBody",
+ "isNew": false,
+ "issue": Object {
+ "name": "foo",
+ },
+ "showReportSectionStatusIcon": false,
+ "status": "none",
+ "statusIconSize": 24,
+}
+`;
diff --git a/spec/frontend/reports/components/__snapshots__/issue_status_icon_spec.js.snap b/spec/frontend/reports/components/__snapshots__/issue_status_icon_spec.js.snap
new file mode 100644
index 00000000000..70e1ff01323
--- /dev/null
+++ b/spec/frontend/reports/components/__snapshots__/issue_status_icon_spec.js.snap
@@ -0,0 +1,37 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`IssueStatusIcon renders "failed" state correctly 1`] = `
+<div
+ class="report-block-list-icon failed"
+>
+ <icon-stub
+ data-qa-selector="status_failed_icon"
+ name="status_failed_borderless"
+ size="24"
+ />
+</div>
+`;
+
+exports[`IssueStatusIcon renders "neutral" state correctly 1`] = `
+<div
+ class="report-block-list-icon neutral"
+>
+ <icon-stub
+ data-qa-selector="status_neutral_icon"
+ name="dash"
+ size="24"
+ />
+</div>
+`;
+
+exports[`IssueStatusIcon renders "success" state correctly 1`] = `
+<div
+ class="report-block-list-icon success"
+>
+ <icon-stub
+ data-qa-selector="status_success_icon"
+ name="status_success_borderless"
+ size="24"
+ />
+</div>
+`;
diff --git a/spec/frontend/reports/components/grouped_issues_list_spec.js b/spec/frontend/reports/components/grouped_issues_list_spec.js
new file mode 100644
index 00000000000..1f8f4a0e4c1
--- /dev/null
+++ b/spec/frontend/reports/components/grouped_issues_list_spec.js
@@ -0,0 +1,86 @@
+import { shallowMount } from '@vue/test-utils';
+import GroupedIssuesList from '~/reports/components/grouped_issues_list.vue';
+import ReportItem from '~/reports/components/report_item.vue';
+import SmartVirtualList from '~/vue_shared/components/smart_virtual_list.vue';
+
+describe('Grouped Issues List', () => {
+ let wrapper;
+
+ const createComponent = ({ propsData = {}, stubs = {} } = {}) => {
+ wrapper = shallowMount(GroupedIssuesList, {
+ propsData,
+ stubs,
+ });
+ };
+
+ const findHeading = groupName => wrapper.find(`[data-testid="${groupName}Heading"`);
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it('renders a smart virtual list with the correct props', () => {
+ createComponent({
+ propsData: {
+ resolvedIssues: [{ name: 'foo' }],
+ unresolvedIssues: [{ name: 'bar' }],
+ },
+ stubs: {
+ SmartVirtualList,
+ },
+ });
+
+ expect(wrapper.find(SmartVirtualList).props()).toMatchSnapshot();
+ });
+
+ describe('without data', () => {
+ beforeEach(createComponent);
+
+ it.each(['unresolved', 'resolved'])('does not a render a header for %s issues', issueName => {
+ expect(findHeading(issueName).exists()).toBe(false);
+ });
+
+ it.each('resolved', 'unresolved')('does not render report items for %s issues', () => {
+ expect(wrapper.contains(ReportItem)).toBe(false);
+ });
+ });
+
+ describe('with data', () => {
+ it.each`
+ givenIssues | givenHeading | groupName
+ ${[{ name: 'foo issue' }]} | ${'Foo Heading'} | ${'resolved'}
+ ${[{ name: 'bar issue' }]} | ${'Bar Heading'} | ${'unresolved'}
+ `('renders the heading for $groupName issues', ({ givenIssues, givenHeading, groupName }) => {
+ createComponent({
+ propsData: { [`${groupName}Issues`]: givenIssues, [`${groupName}Heading`]: givenHeading },
+ });
+
+ expect(findHeading(groupName).text()).toBe(givenHeading);
+ });
+
+ it.each(['resolved', 'unresolved'])('renders all %s issues', issueName => {
+ const issues = [{ name: 'foo' }, { name: 'bar' }];
+
+ createComponent({
+ propsData: { [`${issueName}Issues`]: issues },
+ });
+
+ expect(wrapper.findAll(ReportItem)).toHaveLength(issues.length);
+ });
+
+ it('renders a report item with the correct props', () => {
+ createComponent({
+ propsData: {
+ resolvedIssues: [{ name: 'foo' }],
+ component: 'TestIssueBody',
+ },
+ stubs: {
+ ReportItem,
+ },
+ });
+
+ expect(wrapper.find(ReportItem).props()).toMatchSnapshot();
+ });
+ });
+});
diff --git a/spec/frontend/reports/components/grouped_test_reports_app_spec.js b/spec/frontend/reports/components/grouped_test_reports_app_spec.js
new file mode 100644
index 00000000000..1a01db391da
--- /dev/null
+++ b/spec/frontend/reports/components/grouped_test_reports_app_spec.js
@@ -0,0 +1,260 @@
+import Vue from 'vue';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import state from '~/reports/store/state';
+import component from '~/reports/components/grouped_test_reports_app.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+import { failedReport } from '../mock_data/mock_data';
+import newFailedTestReports from '../mock_data/new_failures_report.json';
+import newErrorsTestReports from '../mock_data/new_errors_report.json';
+import successTestReports from '../mock_data/no_failures_report.json';
+import mixedResultsTestReports from '../mock_data/new_and_fixed_failures_report.json';
+import resolvedFailures from '../mock_data/resolved_failures.json';
+
+describe('Grouped Test Reports App', () => {
+ let vm;
+ let mock;
+ const Component = Vue.extend(component);
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ vm.$store.replaceState(state());
+ vm.$destroy();
+ mock.restore();
+ });
+
+ describe('with success result', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, successTestReports, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders success summary text', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained no changed test results out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain(
+ 'rspec:pg found no changed test results out of 8 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain(
+ 'java ant found no changed test results out of 3 total tests',
+ );
+ done();
+ });
+ });
+ });
+
+ describe('with 204 result', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(204, {}, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders success summary text', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary results are being parsed',
+ );
+
+ done();
+ });
+ });
+ });
+
+ describe('with new failed result', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, newFailedTestReports, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders failed summary text + new badge', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained 2 failed out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain('rspec:pg found 2 failed out of 8 total tests');
+
+ expect(vm.$el.textContent).toContain('New');
+ expect(vm.$el.textContent).toContain(
+ 'java ant found no changed test results out of 3 total tests',
+ );
+ done();
+ });
+ });
+ });
+
+ describe('with new error result', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, newErrorsTestReports, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders error summary text + new badge', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained 2 errors out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain('karma found 2 errors out of 3 total tests');
+
+ expect(vm.$el.textContent).toContain('New');
+ expect(vm.$el.textContent).toContain(
+ 'rspec:pg found no changed test results out of 8 total tests',
+ );
+ done();
+ });
+ });
+ });
+
+ describe('with mixed results', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, mixedResultsTestReports, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders summary text', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained 2 failed and 2 fixed test results out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain(
+ 'rspec:pg found 1 failed and 2 fixed test results out of 8 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain('New');
+ expect(vm.$el.textContent).toContain(' java ant found 1 failed out of 3 total tests');
+ done();
+ });
+ });
+ });
+
+ describe('with resolved failures and resolved errors', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, resolvedFailures, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders summary text', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.gl-spinner')).toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary contained 4 fixed test results out of 11 total tests',
+ );
+
+ expect(vm.$el.textContent).toContain(
+ 'rspec:pg found 4 fixed test results out of 8 total tests',
+ );
+ done();
+ });
+ });
+
+ it('renders resolved failures', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.report-block-container').textContent).toContain(
+ resolvedFailures.suites[0].resolved_failures[0].name,
+ );
+
+ expect(vm.$el.querySelector('.report-block-container').textContent).toContain(
+ resolvedFailures.suites[0].resolved_failures[1].name,
+ );
+ done();
+ });
+ });
+
+ it('renders resolved errors', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.report-block-container').textContent).toContain(
+ resolvedFailures.suites[0].resolved_errors[0].name,
+ );
+
+ expect(vm.$el.querySelector('.report-block-container').textContent).toContain(
+ resolvedFailures.suites[0].resolved_errors[1].name,
+ );
+ done();
+ });
+ });
+ });
+
+ describe('with a report that failed to load', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, failedReport, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders an error status for the report', done => {
+ setImmediate(() => {
+ const { name } = failedReport.suites[0];
+
+ expect(vm.$el.querySelector('.report-block-list-issue').textContent).toContain(
+ `An error occurred while loading ${name} results`,
+ );
+ done();
+ });
+ });
+ });
+
+ describe('with error', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(500, {}, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders loading summary text with loading icon', done => {
+ setImmediate(() => {
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary failed loading results',
+ );
+ done();
+ });
+ });
+ });
+
+ describe('while loading', () => {
+ beforeEach(() => {
+ mock.onGet('test_results.json').reply(200, {}, {});
+ vm = mountComponent(Component, {
+ endpoint: 'test_results.json',
+ });
+ });
+
+ it('renders loading summary text with loading icon', done => {
+ expect(vm.$el.querySelector('.gl-spinner')).not.toBeNull();
+ expect(vm.$el.querySelector('.js-code-text').textContent.trim()).toEqual(
+ 'Test summary results are being parsed',
+ );
+
+ setImmediate(() => {
+ done();
+ });
+ });
+ });
+});
diff --git a/spec/frontend/reports/components/issue_status_icon_spec.js b/spec/frontend/reports/components/issue_status_icon_spec.js
new file mode 100644
index 00000000000..3a55ff0a9e3
--- /dev/null
+++ b/spec/frontend/reports/components/issue_status_icon_spec.js
@@ -0,0 +1,29 @@
+import { shallowMount } from '@vue/test-utils';
+import ReportItem from '~/reports/components/issue_status_icon.vue';
+import { STATUS_FAILED, STATUS_NEUTRAL, STATUS_SUCCESS } from '~/reports/constants';
+
+describe('IssueStatusIcon', () => {
+ let wrapper;
+
+ const createComponent = ({ status }) => {
+ wrapper = shallowMount(ReportItem, {
+ propsData: {
+ status,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ it.each([STATUS_SUCCESS, STATUS_NEUTRAL, STATUS_FAILED])(
+ 'renders "%s" state correctly',
+ status => {
+ createComponent({ status });
+
+ expect(wrapper.element).toMatchSnapshot();
+ },
+ );
+});
diff --git a/spec/frontend/reports/components/modal_open_name_spec.js b/spec/frontend/reports/components/modal_open_name_spec.js
new file mode 100644
index 00000000000..d59f3571c4b
--- /dev/null
+++ b/spec/frontend/reports/components/modal_open_name_spec.js
@@ -0,0 +1,47 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { mountComponentWithStore } from 'helpers/vue_mount_component_helper';
+import component from '~/reports/components/modal_open_name.vue';
+
+Vue.use(Vuex);
+
+describe('Modal open name', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const store = new Vuex.Store({
+ actions: {
+ openModal: () => {},
+ },
+ state: {},
+ mutations: {},
+ });
+
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: {
+ issue: {
+ title: 'Issue',
+ },
+ status: 'failed',
+ },
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders the issue name', () => {
+ expect(vm.$el.textContent.trim()).toEqual('Issue');
+ });
+
+ it('calls openModal actions when button is clicked', () => {
+ jest.spyOn(vm, 'openModal').mockImplementation(() => {});
+
+ vm.$el.click();
+
+ expect(vm.openModal).toHaveBeenCalled();
+ });
+});
diff --git a/spec/frontend/reports/components/modal_spec.js b/spec/frontend/reports/components/modal_spec.js
new file mode 100644
index 00000000000..ff046e64b6e
--- /dev/null
+++ b/spec/frontend/reports/components/modal_spec.js
@@ -0,0 +1,54 @@
+import Vue from 'vue';
+import component from '~/reports/components/modal.vue';
+import state from '~/reports/store/state';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+import { trimText } from '../../helpers/text_helper';
+
+describe('Grouped Test Reports Modal', () => {
+ const Component = Vue.extend(component);
+ const modalDataStructure = state().modal.data;
+
+ // populate data
+ modalDataStructure.execution_time.value = 0.009411;
+ modalDataStructure.system_output.value = 'Failure/Error: is_expected.to eq(3)\n\n';
+ modalDataStructure.class.value = 'link';
+
+ let vm;
+
+ beforeEach(() => {
+ vm = mountComponent(Component, {
+ title: 'Test#sum when a is 1 and b is 2 returns summary',
+ modalData: modalDataStructure,
+ });
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders code block', () => {
+ expect(vm.$el.querySelector('code').textContent).toEqual(
+ modalDataStructure.system_output.value,
+ );
+ });
+
+ it('renders link', () => {
+ expect(vm.$el.querySelector('.js-modal-link').getAttribute('href')).toEqual(
+ modalDataStructure.class.value,
+ );
+
+ expect(trimText(vm.$el.querySelector('.js-modal-link').textContent)).toEqual(
+ modalDataStructure.class.value,
+ );
+ });
+
+ it('renders seconds', () => {
+ expect(vm.$el.textContent).toContain(`${modalDataStructure.execution_time.value} s`);
+ });
+
+ it('render title', () => {
+ expect(trimText(vm.$el.querySelector('.modal-title').textContent)).toEqual(
+ 'Test#sum when a is 1 and b is 2 returns summary',
+ );
+ });
+});
diff --git a/spec/frontend/reports/components/summary_row_spec.js b/spec/frontend/reports/components/summary_row_spec.js
new file mode 100644
index 00000000000..cb0cc025e80
--- /dev/null
+++ b/spec/frontend/reports/components/summary_row_spec.js
@@ -0,0 +1,37 @@
+import Vue from 'vue';
+import mountComponent from 'helpers/vue_mount_component_helper';
+import component from '~/reports/components/summary_row.vue';
+
+describe('Summary row', () => {
+ const Component = Vue.extend(component);
+ let vm;
+
+ const props = {
+ summary: 'SAST detected 1 new vulnerability and 1 fixed vulnerability',
+ popoverOptions: {
+ title: 'Static Application Security Testing (SAST)',
+ content: '<a>Learn more about SAST</a>',
+ },
+ statusIcon: 'warning',
+ };
+
+ beforeEach(() => {
+ vm = mountComponent(Component, props);
+ });
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ it('renders provided summary', () => {
+ expect(
+ vm.$el.querySelector('.report-block-list-issue-description-text').textContent.trim(),
+ ).toEqual(props.summary);
+ });
+
+ it('renders provided icon', () => {
+ expect(vm.$el.querySelector('.report-block-list-icon span').classList).toContain(
+ 'js-ci-status-icon-warning',
+ );
+ });
+});
diff --git a/spec/frontend/reports/components/test_issue_body_spec.js b/spec/frontend/reports/components/test_issue_body_spec.js
new file mode 100644
index 00000000000..ff81020a4eb
--- /dev/null
+++ b/spec/frontend/reports/components/test_issue_body_spec.js
@@ -0,0 +1,72 @@
+import Vue from 'vue';
+import component from '~/reports/components/test_issue_body.vue';
+import createStore from '~/reports/store';
+import { mountComponentWithStore } from '../../helpers/vue_mount_component_helper';
+import { trimText } from '../../helpers/text_helper';
+import { issue } from '../mock_data/mock_data';
+
+describe('Test Issue body', () => {
+ let vm;
+ const Component = Vue.extend(component);
+ const store = createStore();
+
+ const commonProps = {
+ issue,
+ status: 'failed',
+ };
+
+ afterEach(() => {
+ vm.$destroy();
+ });
+
+ describe('on click', () => {
+ it('calls openModal action', () => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: commonProps,
+ });
+
+ jest.spyOn(vm, 'openModal').mockImplementation(() => {});
+
+ vm.$el.querySelector('button').click();
+
+ expect(vm.openModal).toHaveBeenCalledWith({
+ issue: commonProps.issue,
+ });
+ });
+ });
+
+ describe('is new', () => {
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: { ...commonProps, isNew: true },
+ });
+ });
+
+ it('renders issue name', () => {
+ expect(vm.$el.textContent).toContain(commonProps.issue.name);
+ });
+
+ it('renders new badge', () => {
+ expect(trimText(vm.$el.querySelector('.badge').textContent)).toEqual('New');
+ });
+ });
+
+ describe('not new', () => {
+ beforeEach(() => {
+ vm = mountComponentWithStore(Component, {
+ store,
+ props: commonProps,
+ });
+ });
+
+ it('renders issue name', () => {
+ expect(vm.$el.textContent).toContain(commonProps.issue.name);
+ });
+
+ it('does not renders new badge', () => {
+ expect(vm.$el.querySelector('.badge')).toEqual(null);
+ });
+ });
+});
diff --git a/spec/frontend/reports/mock_data/mock_data.js b/spec/frontend/reports/mock_data/mock_data.js
new file mode 100644
index 00000000000..3caaab2fd79
--- /dev/null
+++ b/spec/frontend/reports/mock_data/mock_data.js
@@ -0,0 +1,24 @@
+export const issue = {
+ result: 'failure',
+ name: 'Test#sum when a is 1 and b is 2 returns summary',
+ execution_time: 0.009411,
+ system_output:
+ "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in \u003ctop (required)\u003e'",
+};
+
+export const failedReport = {
+ summary: { total: 11, resolved: 0, errored: 2, failed: 0 },
+ suites: [
+ {
+ name: 'rspec:pg',
+ status: 'error',
+ summary: { total: 0, resolved: 0, errored: 0, failed: 0 },
+ new_failures: [],
+ resolved_failures: [],
+ existing_failures: [],
+ new_errors: [],
+ resolved_errors: [],
+ existing_errors: [],
+ },
+ ],
+};
diff --git a/spec/frontend/reports/mock_data/new_and_fixed_failures_report.json b/spec/frontend/reports/mock_data/new_and_fixed_failures_report.json
new file mode 100644
index 00000000000..6141e5433a6
--- /dev/null
+++ b/spec/frontend/reports/mock_data/new_and_fixed_failures_report.json
@@ -0,0 +1,55 @@
+{
+ "status": "failed",
+ "summary": { "total": 11, "resolved": 2, "errored": 0, "failed": 2 },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "status": "failed",
+ "summary": { "total": 8, "resolved": 2, "errored": 0, "failed": 1 },
+ "new_failures": [
+ {
+ "status": "failed",
+ "name": "Test#subtract when a is 2 and b is 1 returns correct result",
+ "execution_time": 0.00908,
+ "system_output": "Failure/Error: is_expected.to eq(1)\n\n expected: 1\n got: 3\n\n (compared using ==)\n./spec/test_spec.rb:43:in `block (4 levels) in <top (required)>'"
+ }
+ ],
+ "resolved_failures": [
+ {
+ "status": "success",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.000318,
+ "system_output": null
+ },
+ {
+ "status": "success",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 0.000074,
+ "system_output": null
+ }
+ ],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "status": "failed",
+ "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 1 },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [
+ {
+ "status": "failed",
+ "name": "sumTest",
+ "execution_time": 0.004,
+ "system_output": "junit.framework.AssertionFailedError: expected:<3> but was:<-1>\n\tat CalculatorTest.sumTest(Unknown Source)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)\n\tat java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)\n\tat java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)\n"
+ }
+ ],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+}
diff --git a/spec/frontend/reports/mock_data/new_errors_report.json b/spec/frontend/reports/mock_data/new_errors_report.json
new file mode 100644
index 00000000000..cebf98fdb63
--- /dev/null
+++ b/spec/frontend/reports/mock_data/new_errors_report.json
@@ -0,0 +1,38 @@
+{
+ "summary": { "total": 11, "resolved": 0, "errored": 2, "failed": 0 },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "karma",
+ "summary": { "total": 3, "resolved": 0, "errored": 2, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [
+ {
+ "result": "error",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.009411,
+ "system_output": "Failed: Error in render: 'TypeError: Cannot read property 'status' of undefined'"
+ },
+ {
+ "result": "error",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 0.000162,
+ "system_output": "Failed: Error in render: 'TypeError: Cannot read property 'length' of undefined'"
+ }
+ ],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+}
diff --git a/spec/frontend/reports/mock_data/new_failures_report.json b/spec/frontend/reports/mock_data/new_failures_report.json
new file mode 100644
index 00000000000..8b9c12c6271
--- /dev/null
+++ b/spec/frontend/reports/mock_data/new_failures_report.json
@@ -0,0 +1,38 @@
+{
+ "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 2 },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 2 },
+ "new_failures": [
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.009411,
+ "system_output": "Failure/Error: is_expected.to eq(3)\n\n expected: 3\n got: -1\n\n (compared using ==)\n./spec/test_spec.rb:12:in `block (4 levels) in <top (required)>'"
+ },
+ {
+ "result": "failure",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 0.000162,
+ "system_output": "Failure/Error: is_expected.to eq(300)\n\n expected: 300\n got: -100\n\n (compared using ==)\n./spec/test_spec.rb:21:in `block (4 levels) in <top (required)>'"
+ }
+ ],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+}
diff --git a/spec/frontend/reports/mock_data/no_failures_report.json b/spec/frontend/reports/mock_data/no_failures_report.json
new file mode 100644
index 00000000000..7da9e0c6211
--- /dev/null
+++ b/spec/frontend/reports/mock_data/no_failures_report.json
@@ -0,0 +1,28 @@
+{
+ "status": "success",
+ "summary": { "total": 11, "resolved": 0, "errored": 0, "failed": 0 },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "status": "success",
+ "summary": { "total": 8, "resolved": 0, "errored": 0, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "status": "success",
+ "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+}
diff --git a/spec/frontend/reports/mock_data/resolved_failures.json b/spec/frontend/reports/mock_data/resolved_failures.json
new file mode 100644
index 00000000000..49de6aa840b
--- /dev/null
+++ b/spec/frontend/reports/mock_data/resolved_failures.json
@@ -0,0 +1,58 @@
+{
+ "status": "success",
+ "summary": { "total": 11, "resolved": 4, "errored": 0, "failed": 0 },
+ "suites": [
+ {
+ "name": "rspec:pg",
+ "status": "success",
+ "summary": { "total": 8, "resolved": 4, "errored": 0, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [
+ {
+ "status": "success",
+ "name": "Test#sum when a is 1 and b is 2 returns summary",
+ "execution_time": 0.000411,
+ "system_output": null,
+ "stack_trace": null
+ },
+ {
+ "status": "success",
+ "name": "Test#sum when a is 100 and b is 200 returns summary",
+ "execution_time": 7.6e-5,
+ "system_output": null,
+ "stack_trace": null
+ }
+ ],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [
+ {
+ "status": "success",
+ "name": "Test#sum when a is 4 and b is 4 returns summary",
+ "execution_time": 0.00342,
+ "system_output": null,
+ "stack_trace": null
+ },
+ {
+ "status": "success",
+ "name": "Test#sum when a is 40 and b is 400 returns summary",
+ "execution_time": 0.0000231,
+ "system_output": null,
+ "stack_trace": null
+ }
+ ],
+ "existing_errors": []
+ },
+ {
+ "name": "java ant",
+ "status": "success",
+ "summary": { "total": 3, "resolved": 0, "errored": 0, "failed": 0 },
+ "new_failures": [],
+ "resolved_failures": [],
+ "existing_failures": [],
+ "new_errors": [],
+ "resolved_errors": [],
+ "existing_errors": []
+ }
+ ]
+}
diff --git a/spec/frontend/reports/store/actions_spec.js b/spec/frontend/reports/store/actions_spec.js
new file mode 100644
index 00000000000..3f189736922
--- /dev/null
+++ b/spec/frontend/reports/store/actions_spec.js
@@ -0,0 +1,171 @@
+import MockAdapter from 'axios-mock-adapter';
+import testAction from 'helpers/vuex_action_helper';
+import { TEST_HOST } from 'helpers/test_constants';
+import axios from '~/lib/utils/axios_utils';
+import {
+ setEndpoint,
+ requestReports,
+ fetchReports,
+ stopPolling,
+ clearEtagPoll,
+ receiveReportsSuccess,
+ receiveReportsError,
+ openModal,
+ setModalData,
+} from '~/reports/store/actions';
+import state from '~/reports/store/state';
+import * as types from '~/reports/store/mutation_types';
+
+describe('Reports Store Actions', () => {
+ let mockedState;
+
+ beforeEach(() => {
+ mockedState = state();
+ });
+
+ describe('setEndpoint', () => {
+ it('should commit SET_ENDPOINT mutation', done => {
+ testAction(
+ setEndpoint,
+ 'endpoint.json',
+ mockedState,
+ [{ type: types.SET_ENDPOINT, payload: 'endpoint.json' }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('requestReports', () => {
+ it('should commit REQUEST_REPORTS mutation', done => {
+ testAction(requestReports, null, mockedState, [{ type: types.REQUEST_REPORTS }], [], done);
+ });
+ });
+
+ describe('fetchReports', () => {
+ let mock;
+
+ beforeEach(() => {
+ mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ stopPolling();
+ clearEtagPoll();
+ });
+
+ describe('success', () => {
+ it('dispatches requestReports and receiveReportsSuccess ', done => {
+ mock
+ .onGet(`${TEST_HOST}/endpoint.json`)
+ .replyOnce(200, { summary: {}, suites: [{ name: 'rspec' }] });
+
+ testAction(
+ fetchReports,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestReports',
+ },
+ {
+ payload: { data: { summary: {}, suites: [{ name: 'rspec' }] }, status: 200 },
+ type: 'receiveReportsSuccess',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+ });
+
+ it('dispatches requestReports and receiveReportsError ', done => {
+ testAction(
+ fetchReports,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestReports',
+ },
+ {
+ type: 'receiveReportsError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveReportsSuccess', () => {
+ it('should commit RECEIVE_REPORTS_SUCCESS mutation with 200', done => {
+ testAction(
+ receiveReportsSuccess,
+ { data: { summary: {} }, status: 200 },
+ mockedState,
+ [{ type: types.RECEIVE_REPORTS_SUCCESS, payload: { summary: {} } }],
+ [],
+ done,
+ );
+ });
+
+ it('should not commit RECEIVE_REPORTS_SUCCESS mutation with 204', done => {
+ testAction(
+ receiveReportsSuccess,
+ { data: { summary: {} }, status: 204 },
+ mockedState,
+ [],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveReportsError', () => {
+ it('should commit RECEIVE_REPORTS_ERROR mutation', done => {
+ testAction(
+ receiveReportsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_REPORTS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('openModal', () => {
+ it('should dispatch setModalData', done => {
+ testAction(
+ openModal,
+ { name: 'foo' },
+ mockedState,
+ [],
+ [{ type: 'setModalData', payload: { name: 'foo' } }],
+ done,
+ );
+ });
+ });
+
+ describe('setModalData', () => {
+ it('should commit SET_ISSUE_MODAL_DATA', done => {
+ testAction(
+ setModalData,
+ { name: 'foo' },
+ mockedState,
+ [{ type: types.SET_ISSUE_MODAL_DATA, payload: { name: 'foo' } }],
+ [],
+ done,
+ );
+ });
+ });
+});
diff --git a/spec/frontend/reports/store/mutations_spec.js b/spec/frontend/reports/store/mutations_spec.js
new file mode 100644
index 00000000000..9446cd454ab
--- /dev/null
+++ b/spec/frontend/reports/store/mutations_spec.js
@@ -0,0 +1,126 @@
+import state from '~/reports/store/state';
+import mutations from '~/reports/store/mutations';
+import * as types from '~/reports/store/mutation_types';
+import { issue } from '../mock_data/mock_data';
+
+describe('Reports Store Mutations', () => {
+ let stateCopy;
+
+ beforeEach(() => {
+ stateCopy = state();
+ });
+
+ describe('SET_ENDPOINT', () => {
+ it('should set endpoint', () => {
+ mutations[types.SET_ENDPOINT](stateCopy, 'endpoint.json');
+
+ expect(stateCopy.endpoint).toEqual('endpoint.json');
+ });
+ });
+
+ describe('REQUEST_REPORTS', () => {
+ it('should set isLoading to true', () => {
+ mutations[types.REQUEST_REPORTS](stateCopy);
+
+ expect(stateCopy.isLoading).toEqual(true);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_SUCCESS', () => {
+ const mockedResponse = {
+ summary: {
+ total: 14,
+ resolved: 0,
+ failed: 7,
+ },
+ suites: [
+ {
+ name: 'build:linux',
+ summary: {
+ total: 2,
+ resolved: 0,
+ failed: 1,
+ },
+ new_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 0.0092435,
+ system_output: "Failure/Error: is_expected.to eq('gitlab')",
+ },
+ ],
+ resolved_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 0.009235,
+ system_output: "Failure/Error: is_expected.to eq('gitlab')",
+ },
+ ],
+ existing_failures: [
+ {
+ name: 'StringHelper#concatenate when a is git and b is lab returns summary',
+ execution_time: 1232.08,
+ system_output: "Failure/Error: is_expected.to eq('gitlab')",
+ },
+ ],
+ },
+ ],
+ };
+
+ beforeEach(() => {
+ mutations[types.RECEIVE_REPORTS_SUCCESS](stateCopy, mockedResponse);
+ });
+
+ it('should reset isLoading', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should reset hasError', () => {
+ expect(stateCopy.hasError).toEqual(false);
+ });
+
+ it('should set summary counts', () => {
+ expect(stateCopy.summary.total).toEqual(mockedResponse.summary.total);
+ expect(stateCopy.summary.resolved).toEqual(mockedResponse.summary.resolved);
+ expect(stateCopy.summary.failed).toEqual(mockedResponse.summary.failed);
+ });
+
+ it('should set reports', () => {
+ expect(stateCopy.reports).toEqual(mockedResponse.suites);
+ });
+ });
+
+ describe('RECEIVE_REPORTS_ERROR', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_REPORTS_ERROR](stateCopy);
+ });
+
+ it('should reset isLoading', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should set hasError to true', () => {
+ expect(stateCopy.hasError).toEqual(true);
+ });
+
+ it('should reset reports', () => {
+ expect(stateCopy.reports).toEqual([]);
+ });
+ });
+
+ describe('SET_ISSUE_MODAL_DATA', () => {
+ beforeEach(() => {
+ mutations[types.SET_ISSUE_MODAL_DATA](stateCopy, {
+ issue,
+ });
+ });
+
+ it('should set modal title', () => {
+ expect(stateCopy.modal.title).toEqual(issue.name);
+ });
+
+ it('should set modal data', () => {
+ expect(stateCopy.modal.data.execution_time.value).toEqual(issue.execution_time);
+ expect(stateCopy.modal.data.system_output.value).toEqual(issue.system_output);
+ });
+ });
+});