Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-05-08 15:09:37 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2020-05-08 15:09:37 +0300
commit96897f83e965318f70032eea0196c4c0b807b1d4 (patch)
tree80cd64c9ad08215adffdc3be89497ad1fdab690e /spec/frontend
parent5bdbc604c8a08f827c3833e2c28ec0c299bb41fc (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend')
-rw-r--r--spec/frontend/ide/components/new_dropdown/index_spec.js12
-rw-r--r--spec/frontend/ide/components/new_dropdown/modal_spec.js87
-rw-r--r--spec/frontend/ide/stores/mutations_spec.js17
-rw-r--r--spec/frontend/monitoring/store/variable_mapping_spec.js150
-rw-r--r--spec/frontend/reports/components/grouped_test_reports_app_spec.js260
-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
17 files changed, 1203 insertions, 71 deletions
diff --git a/spec/frontend/ide/components/new_dropdown/index_spec.js b/spec/frontend/ide/components/new_dropdown/index_spec.js
index f4fecb68b64..00781c16609 100644
--- a/spec/frontend/ide/components/new_dropdown/index_spec.js
+++ b/spec/frontend/ide/components/new_dropdown/index_spec.js
@@ -23,9 +23,9 @@ describe('new dropdown component', () => {
tree: [],
};
- jest.spyOn(vm, 'openNewEntryModal').mockImplementation(() => {});
-
vm.$mount();
+
+ jest.spyOn(vm.$refs.newModal, 'open').mockImplementation(() => {});
});
afterEach(() => {
@@ -43,16 +43,16 @@ describe('new dropdown component', () => {
});
describe('createNewItem', () => {
- it('sets modalType to blob when new file is clicked', () => {
+ it('opens modal for a blob when new file is clicked', () => {
vm.$el.querySelectorAll('.dropdown-menu button')[0].click();
- expect(vm.openNewEntryModal).toHaveBeenCalledWith({ type: 'blob', path: '' });
+ expect(vm.$refs.newModal.open).toHaveBeenCalledWith('blob', '');
});
- it('sets modalType to tree when new directory is clicked', () => {
+ it('opens modal for a tree when new directory is clicked', () => {
vm.$el.querySelectorAll('.dropdown-menu button')[2].click();
- expect(vm.openNewEntryModal).toHaveBeenCalledWith({ type: 'tree', path: '' });
+ expect(vm.$refs.newModal.open).toHaveBeenCalledWith('tree', '');
});
});
diff --git a/spec/frontend/ide/components/new_dropdown/modal_spec.js b/spec/frontend/ide/components/new_dropdown/modal_spec.js
index 2f10bf787b3..23da4df188b 100644
--- a/spec/frontend/ide/components/new_dropdown/modal_spec.js
+++ b/spec/frontend/ide/components/new_dropdown/modal_spec.js
@@ -14,55 +14,48 @@ describe('new file modal component', () => {
vm.$destroy();
});
- describe.each(['tree', 'blob'])('%s', type => {
- beforeEach(() => {
+ describe.each`
+ entryType | modalTitle | btnTitle | showsFileTemplates
+ ${'tree'} | ${'Create new directory'} | ${'Create directory'} | ${false}
+ ${'blob'} | ${'Create new file'} | ${'Create file'} | ${true}
+ `('$entryType', ({ entryType, modalTitle, btnTitle, showsFileTemplates }) => {
+ beforeEach(done => {
const store = createStore();
- store.state.entryModal = {
- type,
- path: '',
- entry: {
- path: '',
- },
- };
vm = createComponentWithStore(Component, store).$mount();
-
+ vm.open(entryType);
vm.name = 'testing';
- });
- it(`sets modal title as ${type}`, () => {
- const title = type === 'tree' ? 'directory' : 'file';
+ vm.$nextTick(done);
+ });
- expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(`Create new ${title}`);
+ afterEach(() => {
+ vm.close();
});
- it(`sets button label as ${type}`, () => {
- const title = type === 'tree' ? 'directory' : 'file';
+ it(`sets modal title as ${entryType}`, () => {
+ expect(document.querySelector('.modal-title').textContent.trim()).toBe(modalTitle);
+ });
- expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(`Create ${title}`);
+ it(`sets button label as ${entryType}`, () => {
+ expect(document.querySelector('.btn-success').textContent.trim()).toBe(btnTitle);
});
- it(`sets form label as ${type}`, () => {
- expect(vm.$el.querySelector('.label-bold').textContent.trim()).toBe('Name');
+ it(`sets form label as ${entryType}`, () => {
+ expect(document.querySelector('.label-bold').textContent.trim()).toBe('Name');
});
- it(`${type === 'tree' ? 'does not show' : 'shows'} file templates`, () => {
- const templateFilesEl = vm.$el.querySelector('.file-templates');
- if (type === 'tree') {
- expect(templateFilesEl).toBeNull();
- } else {
- expect(templateFilesEl instanceof Element).toBeTruthy();
- }
+ it(`shows file templates: ${showsFileTemplates}`, () => {
+ const templateFilesEl = document.querySelector('.file-templates');
+ expect(Boolean(templateFilesEl)).toBe(showsFileTemplates);
});
});
describe('rename entry', () => {
beforeEach(() => {
const store = createStore();
- store.state.entryModal = {
- type: 'rename',
- path: '',
- entry: {
+ store.state.entries = {
+ 'test-path': {
name: 'test',
type: 'blob',
path: 'test-path',
@@ -72,23 +65,29 @@ describe('new file modal component', () => {
vm = createComponentWithStore(Component, store).$mount();
});
- ['tree', 'blob'].forEach(type => {
- it(`renders title and button for renaming ${type}`, done => {
- const text = type === 'tree' ? 'folder' : 'file';
-
- vm.$store.state.entryModal.entry.type = type;
+ it.each`
+ entryType | modalTitle | btnTitle
+ ${'tree'} | ${'Rename folder'} | ${'Rename folder'}
+ ${'blob'} | ${'Rename file'} | ${'Rename file'}
+ `(
+ 'renders title and button for renaming $entryType',
+ ({ entryType, modalTitle, btnTitle }, done) => {
+ vm.$store.state.entries['test-path'].type = entryType;
+ vm.open('rename', 'test-path');
vm.$nextTick(() => {
- expect(vm.$el.querySelector('.modal-title').textContent.trim()).toBe(`Rename ${text}`);
- expect(vm.$el.querySelector('.btn-success').textContent.trim()).toBe(`Rename ${text}`);
+ expect(document.querySelector('.modal-title').textContent.trim()).toBe(modalTitle);
+ expect(document.querySelector('.btn-success').textContent.trim()).toBe(btnTitle);
done();
});
- });
- });
+ },
+ );
describe('entryName', () => {
it('returns entries name', () => {
+ vm.open('rename', 'test-path');
+
expect(vm.entryName).toBe('test-path');
});
@@ -115,15 +114,6 @@ describe('new file modal component', () => {
describe('submitForm', () => {
it('throws an error when target entry exists', () => {
const store = createStore();
- store.state.entryModal = {
- type: 'rename',
- path: 'test-path/test',
- entry: {
- name: 'test',
- type: 'blob',
- path: 'test-path/test',
- },
- };
store.state.entries = {
'test-path/test': {
name: 'test',
@@ -132,6 +122,7 @@ describe('new file modal component', () => {
};
vm = createComponentWithStore(Component, store).$mount();
+ vm.open('rename', 'test-path/test');
expect(createFlash).not.toHaveBeenCalled();
diff --git a/spec/frontend/ide/stores/mutations_spec.js b/spec/frontend/ide/stores/mutations_spec.js
index 5d0fe35a10e..bca355b820a 100644
--- a/spec/frontend/ide/stores/mutations_spec.js
+++ b/spec/frontend/ide/stores/mutations_spec.js
@@ -339,23 +339,6 @@ describe('Multi-file store mutations', () => {
});
});
- describe('OPEN_NEW_ENTRY_MODAL', () => {
- it('sets entryModal', () => {
- localState.entries.testPath = file();
-
- mutations.OPEN_NEW_ENTRY_MODAL(localState, {
- type: 'test',
- path: 'testPath',
- });
-
- expect(localState.entryModal).toEqual({
- type: 'test',
- path: 'testPath',
- entry: localState.entries.testPath,
- });
- });
- });
-
describe('RENAME_ENTRY', () => {
beforeEach(() => {
localState.trees = {
diff --git a/spec/frontend/monitoring/store/variable_mapping_spec.js b/spec/frontend/monitoring/store/variable_mapping_spec.js
new file mode 100644
index 00000000000..5081092a519
--- /dev/null
+++ b/spec/frontend/monitoring/store/variable_mapping_spec.js
@@ -0,0 +1,150 @@
+import { parseTemplatingVariables } from '~/monitoring/stores/variable_mapping';
+
+describe('parseTemplatingVariables', () => {
+ const generateMockTemplatingData = data => {
+ const vars = data
+ ? {
+ variables: {
+ ...data,
+ },
+ }
+ : {};
+ return {
+ dashboard: {
+ templating: vars,
+ },
+ };
+ };
+
+ const simpleVar = ['value1', 'value2', 'value3'];
+ const advVar = {
+ label: 'Advanced Var',
+ type: 'custom',
+ options: {
+ values: [
+ { value: 'value1', text: 'Var 1 Option 1' },
+ {
+ value: 'value2',
+ text: 'Var 1 Option 2',
+ default: true,
+ },
+ ],
+ },
+ };
+ const advVarWithoutOptions = {
+ type: 'custom',
+ options: {},
+ };
+ const advVarWithoutLabel = {
+ type: 'custom',
+ options: {
+ values: [
+ { value: 'value1', text: 'Var 1 Option 1' },
+ {
+ value: 'value2',
+ text: 'Var 1 Option 2',
+ default: true,
+ },
+ ],
+ },
+ };
+ const advVarWithoutType = {
+ label: 'Variable 2',
+ options: {
+ values: [
+ { value: 'value1', text: 'Var 1 Option 1' },
+ {
+ value: 'value2',
+ text: 'Var 1 Option 2',
+ default: true,
+ },
+ ],
+ },
+ };
+
+ const responseForSimpleCustomVariable = {
+ simpleVar: {
+ label: 'simpleVar',
+ options: [
+ {
+ default: false,
+ text: 'value1',
+ value: 'value1',
+ },
+ {
+ default: false,
+ text: 'value2',
+ value: 'value2',
+ },
+ {
+ default: false,
+ text: 'value3',
+ value: 'value3',
+ },
+ ],
+ type: 'custom',
+ },
+ };
+
+ const responseForAdvancedCustomVariableWithoutOptions = {
+ advVarWithoutOptions: {
+ label: 'advVarWithoutOptions',
+ options: [],
+ type: 'custom',
+ },
+ };
+
+ const responseForAdvancedCustomVariableWithoutLabel = {
+ advVarWithoutLabel: {
+ label: 'advVarWithoutLabel',
+ options: [
+ {
+ default: false,
+ text: 'Var 1 Option 1',
+ value: 'value1',
+ },
+ {
+ default: true,
+ text: 'Var 1 Option 2',
+ value: 'value2',
+ },
+ ],
+ type: 'custom',
+ },
+ };
+
+ const responseForAdvancedCustomVariable = {
+ ...responseForSimpleCustomVariable,
+ advVar: {
+ label: 'Advanced Var',
+ options: [
+ {
+ default: false,
+ text: 'Var 1 Option 1',
+ value: 'value1',
+ },
+ {
+ default: true,
+ text: 'Var 1 Option 2',
+ value: 'value2',
+ },
+ ],
+ type: 'custom',
+ },
+ };
+
+ it.each`
+ case | input | expected
+ ${'Returns empty object for no dashboard input'} | ${{}} | ${{}}
+ ${'Returns empty object for empty dashboard input'} | ${{ dashboard: {} }} | ${{}}
+ ${'Returns empty object for empty templating prop'} | ${generateMockTemplatingData()} | ${{}}
+ ${'Returns empty object for empty variables prop'} | ${generateMockTemplatingData({})} | ${{}}
+ ${'Returns parsed object for simple variable'} | ${generateMockTemplatingData({ simpleVar })} | ${responseForSimpleCustomVariable}
+ ${'Returns parsed object for advanced variable without options'} | ${generateMockTemplatingData({ advVarWithoutOptions })} | ${responseForAdvancedCustomVariableWithoutOptions}
+ ${'Returns parsed object for advanced variable without type'} | ${generateMockTemplatingData({ advVarWithoutType })} | ${{}}
+ ${'Returns parsed object for advanced variable without label'} | ${generateMockTemplatingData({ advVarWithoutLabel })} | ${responseForAdvancedCustomVariableWithoutLabel}
+ ${'Returns parsed object for simple and advanced variables'} | ${generateMockTemplatingData({ simpleVar, advVar })} | ${responseForAdvancedCustomVariable}
+ `('$case', ({ input, expected }) => {
+ expect(parseTemplatingVariables(input?.dashboard?.templating)).toEqual(expected);
+ });
+});
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/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);
+ });
+ });
+});