summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js121
-rw-r--r--spec/frontend/vue_mr_widget/components/artifacts_list_spec.js61
-rw-r--r--spec/frontend/vue_mr_widget/components/mock_data.js15
-rw-r--r--spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js105
-rw-r--r--spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js32
-rw-r--r--spec/frontend/vue_mr_widget/stores/artifacts_list/mutations_spec.js78
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js7
-rw-r--r--spec/javascripts/vue_mr_widget/mock_data.js1
-rw-r--r--spec/javascripts/vue_mr_widget/stores/artifacts_list/actions_spec.js165
9 files changed, 585 insertions, 0 deletions
diff --git a/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js b/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js
new file mode 100644
index 00000000000..49ed796d9a8
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/components/artifacts_list_app_spec.js
@@ -0,0 +1,121 @@
+import { mount, createLocalVue } from '@vue/test-utils';
+import Vuex from 'vuex';
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import { GlLoadingIcon } from '@gitlab/ui';
+import { TEST_HOST } from 'helpers/test_constants';
+import ArtifactsListApp from '~/vue_merge_request_widget/components/artifacts_list_app.vue';
+import createStore from '~/vue_merge_request_widget/stores/artifacts_list';
+import { artifactsList } from './mock_data';
+
+describe('Merge Requests Artifacts list app', () => {
+ let wrapper;
+ let mock;
+ const store = createStore();
+ const localVue = createLocalVue();
+ localVue.use(Vuex);
+
+ const actionSpies = {
+ fetchArtifacts: jest.fn(),
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ mock.restore();
+ });
+
+ const createComponent = () => {
+ wrapper = mount(localVue.extend(ArtifactsListApp), {
+ propsData: {
+ endpoint: TEST_HOST,
+ },
+ store,
+ methods: {
+ ...actionSpies,
+ },
+ localVue,
+ sync: false,
+ });
+ };
+
+ const findButtons = () => wrapper.findAll('button');
+ const findTitle = () => wrapper.find('.js-title');
+ const findTableRows = () => wrapper.findAll('tbody tr');
+
+ describe('while loading', () => {
+ beforeEach(() => {
+ createComponent();
+ store.dispatch('requestArtifacts');
+ return wrapper.vm.$nextTick();
+ });
+
+ it('renders a loading icon', () => {
+ const loadingIcon = wrapper.find(GlLoadingIcon);
+ expect(loadingIcon.exists()).toBe(true);
+ });
+
+ it('renders loading text', () => {
+ expect(findTitle().text()).toBe('Loading artifacts');
+ });
+
+ it('renders disabled buttons', () => {
+ const buttons = findButtons();
+ expect(buttons.at(0).attributes('disabled')).toBe('disabled');
+ expect(buttons.at(1).attributes('disabled')).toBe('disabled');
+ });
+ });
+
+ describe('with results', () => {
+ beforeEach(() => {
+ createComponent();
+ mock.onGet(wrapper.vm.$store.state.endpoint).reply(200, artifactsList, {});
+ store.dispatch('receiveArtifactsSuccess', {
+ data: artifactsList,
+ status: 200,
+ });
+ return wrapper.vm.$nextTick();
+ });
+
+ it('renders a title with the number of artifacts', () => {
+ expect(findTitle().text()).toBe('View 2 exposed artifacts');
+ });
+
+ it('renders both buttons enabled', () => {
+ const buttons = findButtons();
+ expect(buttons.at(0).attributes('disabled')).toBe(undefined);
+ expect(buttons.at(1).attributes('disabled')).toBe(undefined);
+ });
+
+ describe('on click', () => {
+ it('renders the list of artifacts', () => {
+ findTitle().trigger('click');
+ wrapper.vm.$nextTick(() => {
+ expect(findTableRows().length).toEqual(2);
+ });
+ });
+ });
+ });
+
+ describe('with error', () => {
+ beforeEach(() => {
+ createComponent();
+ mock.onGet(wrapper.vm.$store.state.endpoint).reply(500, {}, {});
+ store.dispatch('receiveArtifactsError');
+ return wrapper.vm.$nextTick();
+ });
+
+ it('renders the error state', () => {
+ expect(findTitle().text()).toBe('An error occurred while fetching the artifacts');
+ });
+
+ it('renders disabled buttons', () => {
+ const buttons = findButtons();
+ expect(buttons.at(0).attributes('disabled')).toBe('disabled');
+ expect(buttons.at(1).attributes('disabled')).toBe('disabled');
+ });
+ });
+});
diff --git a/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js b/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js
new file mode 100644
index 00000000000..8c805faf574
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/components/artifacts_list_spec.js
@@ -0,0 +1,61 @@
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import { GlLink } from '@gitlab/ui';
+import ArtifactsList from '~/vue_merge_request_widget/components/artifacts_list.vue';
+import { artifactsList } from './mock_data';
+
+describe('Artifacts List', () => {
+ let wrapper;
+ const localVue = createLocalVue();
+
+ const data = {
+ artifacts: artifactsList,
+ };
+
+ const mountComponent = props => {
+ wrapper = shallowMount(localVue.extend(ArtifactsList), {
+ propsData: {
+ ...props,
+ },
+ sync: false,
+ localVue,
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ beforeEach(() => {
+ mountComponent(data);
+ });
+
+ it('renders list of artifacts', () => {
+ expect(wrapper.findAll('tbody tr').length).toEqual(data.artifacts.length);
+ });
+
+ it('renders link for the artifact', () => {
+ expect(wrapper.find(GlLink).attributes('href')).toEqual(data.artifacts[0].url);
+ });
+
+ it('renders artifact name', () => {
+ expect(wrapper.find(GlLink).text()).toEqual(data.artifacts[0].text);
+ });
+
+ it('renders job url', () => {
+ expect(
+ wrapper
+ .findAll(GlLink)
+ .at(1)
+ .attributes('href'),
+ ).toEqual(data.artifacts[0].job_path);
+ });
+
+ it('renders job name', () => {
+ expect(
+ wrapper
+ .findAll(GlLink)
+ .at(1)
+ .text(),
+ ).toEqual(data.artifacts[0].job_name);
+ });
+});
diff --git a/spec/frontend/vue_mr_widget/components/mock_data.js b/spec/frontend/vue_mr_widget/components/mock_data.js
new file mode 100644
index 00000000000..39c7d75cda5
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/components/mock_data.js
@@ -0,0 +1,15 @@
+// eslint-disable-next-line import/prefer-default-export
+export const artifactsList = [
+ {
+ text: 'result.txt',
+ url: 'bar',
+ job_name: 'generate-artifact',
+ job_path: 'bar',
+ },
+ {
+ text: 'foo.txt',
+ url: 'foo',
+ job_name: 'foo-artifact',
+ job_path: 'foo',
+ },
+];
diff --git a/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
new file mode 100644
index 00000000000..4c9507223a1
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/components/mr_collapsible_extension_spec.js
@@ -0,0 +1,105 @@
+import { mount } from '@vue/test-utils';
+import MrCollapsibleSection from '~/vue_merge_request_widget/components/mr_collapsible_extension.vue';
+import { GlLoadingIcon } from '@gitlab/ui';
+
+describe('Merge Request Collapsible Extension', () => {
+ let wrapper;
+ const data = {
+ title: 'View artifacts',
+ };
+
+ const mountComponent = props => {
+ wrapper = mount(MrCollapsibleSection, {
+ propsData: {
+ ...props,
+ },
+ slots: {
+ default: '<div class="js-slot">Foo</div>',
+ },
+ });
+ };
+
+ const findTitle = () => wrapper.find('.js-title');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('while collapsed', () => {
+ beforeEach(() => {
+ mountComponent(data);
+ });
+
+ it('renders provided title', () => {
+ expect(findTitle().text()).toBe(data.title);
+ });
+
+ it('renders angle-right icon', () => {
+ expect(wrapper.vm.arrowIconName).toBe('angle-right');
+ });
+
+ describe('onClick', () => {
+ beforeEach(() => {
+ wrapper.find('button').trigger('click');
+ });
+
+ it('rendes the provided slot', () => {
+ expect(wrapper.find('.js-slot').isVisible()).toBe(true);
+ });
+
+ it('renders `Collapse` as the title', () => {
+ expect(findTitle().text()).toBe('Collapse');
+ });
+
+ it('renders angle-down icon', () => {
+ expect(wrapper.vm.arrowIconName).toBe('angle-down');
+ });
+ });
+ });
+
+ describe('while loading', () => {
+ beforeEach(() => {
+ mountComponent(Object.assign({}, data, { isLoading: true }));
+ });
+
+ it('renders the buttons disabled', () => {
+ expect(
+ wrapper
+ .findAll('button')
+ .at(0)
+ .attributes('disabled'),
+ ).toEqual('disabled');
+ expect(
+ wrapper
+ .findAll('button')
+ .at(1)
+ .attributes('disabled'),
+ ).toEqual('disabled');
+ });
+
+ it('renders loading spinner', () => {
+ expect(wrapper.find(GlLoadingIcon).isVisible()).toBe(true);
+ });
+ });
+
+ describe('with error', () => {
+ beforeEach(() => {
+ mountComponent(Object.assign({}, data, { hasError: true }));
+ });
+
+ it('renders the buttons disabled', () => {
+ expect(
+ wrapper
+ .findAll('button')
+ .at(0)
+ .attributes('disabled'),
+ ).toEqual('disabled');
+ expect(
+ wrapper
+ .findAll('button')
+ .at(1)
+ .attributes('disabled'),
+ ).toEqual('disabled');
+ });
+ });
+});
diff --git a/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js b/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js
new file mode 100644
index 00000000000..62ee6f5f189
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/stores/artifacts_list/getters_spec.js
@@ -0,0 +1,32 @@
+import { title } from '~/vue_merge_request_widget/stores/artifacts_list/getters';
+import state from '~/vue_merge_request_widget/stores/artifacts_list/state';
+import { artifactsList } from '../../components/mock_data';
+
+describe('Artifacts Store Getters', () => {
+ let localState;
+
+ beforeEach(() => {
+ localState = state();
+ });
+
+ describe('title', () => {
+ describe('when is loading', () => {
+ it('returns loading message', () => {
+ localState.isLoading = true;
+ expect(title(localState)).toBe('Loading artifacts');
+ });
+ });
+ describe('when has error', () => {
+ it('returns error message', () => {
+ localState.hasError = true;
+ expect(title(localState)).toBe('An error occurred while fetching the artifacts');
+ });
+ });
+ describe('when it has artifacts', () => {
+ it('returns artifacts message', () => {
+ localState.artifacts = artifactsList;
+ expect(title(localState)).toBe('View 2 exposed artifacts');
+ });
+ });
+ });
+});
diff --git a/spec/frontend/vue_mr_widget/stores/artifacts_list/mutations_spec.js b/spec/frontend/vue_mr_widget/stores/artifacts_list/mutations_spec.js
new file mode 100644
index 00000000000..ea89fdb72e9
--- /dev/null
+++ b/spec/frontend/vue_mr_widget/stores/artifacts_list/mutations_spec.js
@@ -0,0 +1,78 @@
+import state from '~/vue_merge_request_widget/stores/artifacts_list/state';
+import mutations from '~/vue_merge_request_widget/stores/artifacts_list/mutations';
+import * as types from '~/vue_merge_request_widget/stores/artifacts_list/mutation_types';
+
+describe('Artifacts 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_ARTIFACTS', () => {
+ it('should set isLoading to true', () => {
+ mutations[types.REQUEST_ARTIFACTS](stateCopy);
+
+ expect(stateCopy.isLoading).toEqual(true);
+ });
+ });
+
+ describe('REECEIVE_ARTIFACTS_SUCCESS', () => {
+ const artifacts = [
+ {
+ text: 'result.txt',
+ url: 'asda',
+ job_name: 'generate-artifact',
+ job_path: 'asda',
+ },
+ {
+ text: 'file.txt',
+ url: 'asda',
+ job_name: 'generate-artifact',
+ job_path: 'asda',
+ },
+ ];
+
+ beforeEach(() => {
+ mutations[types.RECEIVE_ARTIFACTS_SUCCESS](stateCopy, artifacts);
+ });
+
+ it('should set isLoading to false', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should set hasError to false', () => {
+ expect(stateCopy.hasError).toEqual(false);
+ });
+
+ it('should set list of artifacts', () => {
+ expect(stateCopy.artifacts).toEqual(artifacts);
+ });
+ });
+
+ describe('RECEIVE_ARTIFACTS_ERROR', () => {
+ beforeEach(() => {
+ mutations[types.RECEIVE_ARTIFACTS_ERROR](stateCopy);
+ });
+
+ it('should set isLoading to false', () => {
+ expect(stateCopy.isLoading).toEqual(false);
+ });
+
+ it('should set hasError to true', () => {
+ expect(stateCopy.hasError).toEqual(true);
+ });
+
+ it('should set list of artifacts as empty array', () => {
+ expect(stateCopy.artifacts).toEqual([]);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
index dfbc68c48b9..6cdf60f3535 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_container_spec.js
@@ -1,6 +1,7 @@
import { mount, createLocalVue } from '@vue/test-utils';
import MrWidgetPipelineContainer from '~/vue_merge_request_widget/components/mr_widget_pipeline_container.vue';
import MrWidgetPipeline from '~/vue_merge_request_widget/components/mr_widget_pipeline.vue';
+import ArtifactsApp from '~/vue_merge_request_widget/components/artifacts_list_app.vue';
import { mockStore } from '../mock_data';
describe('MrWidgetPipelineContainer', () => {
@@ -87,4 +88,10 @@ describe('MrWidgetPipelineContainer', () => {
expect(deployments.wrappers.map(x => x.props())).toEqual(expectedProps);
});
});
+
+ describe('with artifacts path', () => {
+ it('renders the artifacts app', () => {
+ expect(wrapper.find(ArtifactsApp).isVisible()).toBe(true);
+ });
+ });
});
diff --git a/spec/javascripts/vue_mr_widget/mock_data.js b/spec/javascripts/vue_mr_widget/mock_data.js
index 2f79806652b..089ec08fbf9 100644
--- a/spec/javascripts/vue_mr_widget/mock_data.js
+++ b/spec/javascripts/vue_mr_widget/mock_data.js
@@ -289,4 +289,5 @@ export const mockStore = {
troubleshootingDocsPath: 'troubleshooting-docs-path',
ciStatus: 'ci-status',
hasCI: true,
+ exposedArtifactsPath: 'exposed_artifacts.json',
};
diff --git a/spec/javascripts/vue_mr_widget/stores/artifacts_list/actions_spec.js b/spec/javascripts/vue_mr_widget/stores/artifacts_list/actions_spec.js
new file mode 100644
index 00000000000..4c4bebcb4cd
--- /dev/null
+++ b/spec/javascripts/vue_mr_widget/stores/artifacts_list/actions_spec.js
@@ -0,0 +1,165 @@
+import MockAdapter from 'axios-mock-adapter';
+import axios from '~/lib/utils/axios_utils';
+import {
+ setEndpoint,
+ requestArtifacts,
+ clearEtagPoll,
+ stopPolling,
+ fetchArtifacts,
+ receiveArtifactsSuccess,
+ receiveArtifactsError,
+} from '~/vue_merge_request_widget/stores/artifacts_list/actions';
+import state from '~/vue_merge_request_widget/stores/artifacts_list/state';
+import * as types from '~/vue_merge_request_widget/stores/artifacts_list/mutation_types';
+import testAction from 'spec/helpers/vuex_action_helper';
+import { TEST_HOST } from 'spec/test_constants';
+
+describe('Artifacts App 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('requestArtifacts', () => {
+ it('should commit REQUEST_ARTIFACTS mutation', done => {
+ testAction(
+ requestArtifacts,
+ null,
+ mockedState,
+ [{ type: types.REQUEST_ARTIFACTS }],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('fetchArtifacts', () => {
+ let mock;
+
+ beforeEach(() => {
+ mockedState.endpoint = `${TEST_HOST}/endpoint.json`;
+ mock = new MockAdapter(axios);
+ });
+
+ afterEach(() => {
+ mock.restore();
+ stopPolling();
+ clearEtagPoll();
+ });
+
+ describe('success', () => {
+ it('dispatches requestArtifacts and receiveArtifactsSuccess ', done => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).replyOnce(200, [
+ {
+ text: 'result.txt',
+ url: 'asda',
+ job_name: 'generate-artifact',
+ job_path: 'asda',
+ },
+ ]);
+
+ testAction(
+ fetchArtifacts,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestArtifacts',
+ },
+ {
+ payload: {
+ data: [
+ {
+ text: 'result.txt',
+ url: 'asda',
+ job_name: 'generate-artifact',
+ job_path: 'asda',
+ },
+ ],
+ status: 200,
+ },
+ type: 'receiveArtifactsSuccess',
+ },
+ ],
+ done,
+ );
+ });
+ });
+
+ describe('error', () => {
+ beforeEach(() => {
+ mock.onGet(`${TEST_HOST}/endpoint.json`).reply(500);
+ });
+
+ it('dispatches requestArtifacts and receiveArtifactsError ', done => {
+ testAction(
+ fetchArtifacts,
+ null,
+ mockedState,
+ [],
+ [
+ {
+ type: 'requestArtifacts',
+ },
+ {
+ type: 'receiveArtifactsError',
+ },
+ ],
+ done,
+ );
+ });
+ });
+ });
+
+ describe('receiveArtifactsSuccess', () => {
+ it('should commit RECEIVE_ARTIFACTS_SUCCESS mutation with 200', done => {
+ testAction(
+ receiveArtifactsSuccess,
+ { data: { summary: {} }, status: 200 },
+ mockedState,
+ [{ type: types.RECEIVE_ARTIFACTS_SUCCESS, payload: { summary: {} } }],
+ [],
+ done,
+ );
+ });
+
+ it('should not commit RECEIVE_ARTIFACTS_SUCCESS mutation with 204', done => {
+ testAction(
+ receiveArtifactsSuccess,
+ { data: { summary: {} }, status: 204 },
+ mockedState,
+ [],
+ [],
+ done,
+ );
+ });
+ });
+
+ describe('receiveArtifactsError', () => {
+ it('should commit RECEIVE_ARTIFACTS_ERROR mutation', done => {
+ testAction(
+ receiveArtifactsError,
+ null,
+ mockedState,
+ [{ type: types.RECEIVE_ARTIFACTS_ERROR }],
+ [],
+ done,
+ );
+ });
+ });
+});