Commit 9ca5cb32 authored by Paul Slaughter's avatar Paul Slaughter

Merge branch 'ce-xanf-commit-pipeline-status-to-jest' into 'master'

Migrate commit_pipeline_status_component_spec to Jest

Closes #31635

See merge request gitlab-org/gitlab!16728
parents f46b3f01 0705165e
...@@ -21,14 +21,6 @@ export default { ...@@ -21,14 +21,6 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
/* This prop can be used to replace some of the `render_commit_status`
used across GitLab, this way we could use this vue component and add a
realtime status where it makes sense
realtime: {
type: Boolean,
required: false,
default: true,
}, */
}, },
data() { data() {
return { return {
...@@ -47,6 +39,9 @@ export default { ...@@ -47,6 +39,9 @@ export default {
this.service = new CommitPipelineService(this.endpoint); this.service = new CommitPipelineService(this.endpoint);
this.initPolling(); this.initPolling();
}, },
beforeDestroy() {
this.poll.stop();
},
methods: { methods: {
successCallback(res) { successCallback(res) {
const { pipelines } = res.data; const { pipelines } = res.data;
...@@ -95,9 +90,6 @@ export default { ...@@ -95,9 +90,6 @@ export default {
.catch(this.errorCallback); .catch(this.errorCallback);
}, },
}, },
destroy() {
this.poll.stop();
},
}; };
</script> </script>
<template> <template>
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Commit pipeline status component when polling is not successful renders not found CI icon without loader 1`] = `
<div
class="ci-status-link"
>
<a>
<ciicon-stub
aria-label="Pipeline: not found"
cssclasses=""
data-container="body"
data-original-title="Pipeline: not found"
size="24"
status="[object Object]"
title=""
/>
</a>
</div>
`;
exports[`Commit pipeline status component when polling is successful renders CI icon without loader 1`] = `
<div
class="ci-status-link"
>
<a
href="/frontend-fixtures/pipelines-project/pipelines/47"
>
<ciicon-stub
aria-label="Pipeline: pending"
cssclasses=""
data-container="body"
data-original-title="Pipeline: pending"
size="24"
status="[object Object]"
title=""
/>
</a>
</div>
`;
import Visibility from 'visibilityjs';
import { GlLoadingIcon } from '@gitlab/ui';
import Poll from '~/lib/utils/poll';
import flash from '~/flash';
import CommitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import { shallowMount } from '@vue/test-utils';
import { getJSONFixture } from '../helpers/fixtures';
jest.mock('~/lib/utils/poll');
jest.mock('visibilityjs');
jest.mock('~/flash');
const mockFetchData = jest.fn();
jest.mock('~/projects/tree/services/commit_pipeline_service', () =>
jest.fn().mockImplementation(() => ({
fetchData: mockFetchData.mockReturnValue(Promise.resolve()),
})),
);
describe('Commit pipeline status component', () => {
let wrapper;
const { pipelines } = getJSONFixture('pipelines/pipelines.json');
const { status: mockCiStatus } = pipelines[0].details;
const defaultProps = {
endpoint: 'endpoint',
};
const createComponent = (props = {}) => {
wrapper = shallowMount(CommitPipelineStatus, {
propsData: {
...defaultProps,
...props,
},
sync: false,
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
jest.clearAllMocks();
});
describe('Visibility management', () => {
describe('when component is hidden', () => {
beforeEach(() => {
Visibility.hidden.mockReturnValue(true);
createComponent();
});
it('does not start polling', () => {
const [pollInstance] = Poll.mock.instances;
expect(pollInstance.makeRequest).not.toHaveBeenCalled();
});
it('requests pipeline data', () => {
expect(mockFetchData).toHaveBeenCalled();
});
});
describe('when component is visible', () => {
beforeEach(() => {
Visibility.hidden.mockReturnValue(false);
createComponent();
});
it('starts polling', () => {
const [pollInstance] = [...Poll.mock.instances].reverse();
expect(pollInstance.makeRequest).toHaveBeenCalled();
});
});
describe('when component changes its visibility', () => {
it.each`
visibility | action
${false} | ${'restart'}
${true} | ${'stop'}
`(
'$action polling when component visibility becomes $visibility',
({ visibility, action }) => {
Visibility.hidden.mockReturnValue(!visibility);
createComponent();
const [pollInstance] = Poll.mock.instances;
expect(pollInstance[action]).not.toHaveBeenCalled();
Visibility.hidden.mockReturnValue(visibility);
const [visibilityHandler] = Visibility.change.mock.calls[0];
visibilityHandler();
expect(pollInstance[action]).toHaveBeenCalled();
},
);
});
});
it('stops polling when component is destroyed', () => {
createComponent();
wrapper.destroy();
const [pollInstance] = Poll.mock.instances;
expect(pollInstance.stop).toHaveBeenCalled();
});
describe('when polling', () => {
let pollConfig;
beforeEach(() => {
Poll.mockImplementation(config => {
pollConfig = config;
return { makeRequest: jest.fn(), restart: jest.fn(), stop: jest.fn() };
});
createComponent();
});
it('shows the loading icon at start', () => {
createComponent();
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
pollConfig.successCallback({
data: { pipelines: [] },
});
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(GlLoadingIcon).exists()).toBe(false);
});
});
describe('is successful', () => {
beforeEach(() => {
pollConfig.successCallback({
data: { pipelines: [{ details: { status: mockCiStatus } }] },
});
return wrapper.vm.$nextTick();
});
it('renders CI icon without loader', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
describe('is not successful', () => {
beforeEach(() => {
pollConfig.errorCallback();
});
it('renders not found CI icon without loader', () => {
expect(wrapper.element).toMatchSnapshot();
});
it('displays flash error message', () => {
expect(flash).toHaveBeenCalled();
});
});
});
});
import Vue from 'vue';
import MockAdapter from 'axios-mock-adapter';
import axios from '~/lib/utils/axios_utils';
import commitPipelineStatus from '~/projects/tree/components/commit_pipeline_status_component.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('Commit pipeline status component', () => {
let vm;
let Component;
let mock;
const mockCiStatus = {
details_path: '/root/hello-world/pipelines/1',
favicon: 'canceled.ico',
group: 'canceled',
has_details: true,
icon: 'status_canceled',
label: 'canceled',
text: 'canceled',
};
beforeEach(() => {
Component = Vue.extend(commitPipelineStatus);
});
describe('While polling pipeline data successfully', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet('/dummy/endpoint').reply(() => {
const res = Promise.resolve([
200,
{
pipelines: [
{
details: {
status: mockCiStatus,
},
},
],
},
]);
return res;
});
vm = mountComponent(Component, {
endpoint: '/dummy/endpoint',
});
});
afterEach(() => {
vm.poll.stop();
vm.$destroy();
mock.restore();
});
it('shows the loading icon when polling is starting', done => {
expect(vm.$el.querySelector('.loading-container')).not.toBe(null);
setTimeout(() => {
expect(vm.$el.querySelector('.loading-container')).toBe(null);
done();
});
});
it('contains a ciStatus when the polling is successful ', done => {
setTimeout(() => {
expect(vm.ciStatus).toEqual(mockCiStatus);
done();
});
});
it('contains a ci-status icon when polling is successful', done => {
setTimeout(() => {
expect(vm.$el.querySelector('.ci-status-icon')).not.toBe(null);
expect(vm.$el.querySelector('.ci-status-icon').classList).toContain(
`ci-status-icon-${mockCiStatus.group}`,
);
done();
});
});
});
describe('When polling data was not successful', () => {
beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet('/dummy/endpoint').reply(502, {});
vm = new Component({
props: {
endpoint: '/dummy/endpoint',
},
});
});
afterEach(() => {
vm.poll.stop();
vm.$destroy();
mock.restore();
});
it('calls an errorCallback', done => {
spyOn(vm, 'errorCallback').and.callThrough();
vm.$mount();
setTimeout(() => {
expect(vm.errorCallback.calls.count()).toEqual(1);
done();
});
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment