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:
Diffstat (limited to 'spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js')
-rw-r--r--spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js362
1 files changed, 362 insertions, 0 deletions
diff --git a/spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js b/spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js
new file mode 100644
index 00000000000..4af292e3588
--- /dev/null
+++ b/spec/frontend/commit/pipelines/legacy_pipelines_table_wrapper_spec.js
@@ -0,0 +1,362 @@
+import { GlLoadingIcon, GlModal, GlTableLite } from '@gitlab/ui';
+import { mount } from '@vue/test-utils';
+import MockAdapter from 'axios-mock-adapter';
+import { nextTick } from 'vue';
+import fixture from 'test_fixtures/pipelines/pipelines.json';
+import { extendedWrapper } from 'helpers/vue_test_utils_helper';
+import { stubComponent } from 'helpers/stub_component';
+import waitForPromises from 'helpers/wait_for_promises';
+import Api from '~/api';
+import LegacyPipelinesTableWraper from '~/commit/pipelines/legacy_pipelines_table_wrapper.vue';
+import {
+ HTTP_STATUS_BAD_REQUEST,
+ HTTP_STATUS_INTERNAL_SERVER_ERROR,
+ HTTP_STATUS_OK,
+ HTTP_STATUS_UNAUTHORIZED,
+} from '~/lib/utils/http_status';
+import { createAlert } from '~/alert';
+import { TOAST_MESSAGE } from '~/ci/pipeline_details/constants';
+import axios from '~/lib/utils/axios_utils';
+
+const $toast = {
+ show: jest.fn(),
+};
+
+jest.mock('~/alert');
+
+describe('Pipelines table in Commits and Merge requests', () => {
+ let wrapper;
+ let pipeline;
+ let mock;
+ const showMock = jest.fn();
+
+ const findRunPipelineBtn = () => wrapper.findByTestId('run_pipeline_button');
+ const findRunPipelineBtnMobile = () => wrapper.findByTestId('run_pipeline_button_mobile');
+ const findLoadingState = () => wrapper.findComponent(GlLoadingIcon);
+ const findErrorEmptyState = () => wrapper.findByTestId('pipeline-error-empty-state');
+ const findEmptyState = () => wrapper.findByTestId('pipeline-empty-state');
+ const findTable = () => wrapper.findComponent(GlTableLite);
+ const findTableRows = () => wrapper.findAllByTestId('pipeline-table-row');
+ const findModal = () => wrapper.findComponent(GlModal);
+ const findMrPipelinesDocsLink = () => wrapper.findByTestId('mr-pipelines-docs-link');
+
+ const createComponent = ({ props = {} } = {}) => {
+ wrapper = extendedWrapper(
+ mount(LegacyPipelinesTableWraper, {
+ propsData: {
+ endpoint: 'endpoint.json',
+ emptyStateSvgPath: 'foo',
+ errorStateSvgPath: 'foo',
+ ...props,
+ },
+ mocks: {
+ $toast,
+ },
+ stubs: {
+ GlModal: stubComponent(GlModal, {
+ template: '<div />',
+ methods: { show: showMock },
+ }),
+ },
+ }),
+ );
+ };
+
+ beforeEach(() => {
+ mock = new MockAdapter(axios);
+
+ const { pipelines } = fixture;
+
+ pipeline = pipelines.find((p) => p.user !== null && p.commit !== null);
+ });
+
+ describe('successful request', () => {
+ describe('without pipelines', () => {
+ beforeEach(async () => {
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, []);
+
+ createComponent();
+
+ await waitForPromises();
+ });
+
+ it('should render the empty state', () => {
+ expect(findTableRows()).toHaveLength(0);
+ expect(findLoadingState().exists()).toBe(false);
+ expect(findErrorEmptyState().exists()).toBe(false);
+ expect(findEmptyState().exists()).toBe(true);
+ });
+
+ it('should render correct empty state content', () => {
+ expect(findRunPipelineBtn().exists()).toBe(true);
+ expect(findMrPipelinesDocsLink().attributes('href')).toBe(
+ '/help/ci/pipelines/merge_request_pipelines.md#prerequisites',
+ );
+ expect(findEmptyState().text()).toContain(
+ 'To run a merge request pipeline, the jobs in the CI/CD configuration file must be configured to run in merge request pipelines.',
+ );
+ });
+ });
+
+ describe('with pagination', () => {
+ beforeEach(async () => {
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, [pipeline], {
+ 'X-TOTAL': 10,
+ 'X-PER-PAGE': 2,
+ 'X-PAGE': 1,
+ 'X-TOTAL-PAGES': 5,
+ 'X-NEXT-PAGE': 2,
+ 'X-PREV-PAGE': 2,
+ });
+
+ createComponent();
+
+ await waitForPromises();
+ });
+
+ it('should make an API request when using pagination', async () => {
+ expect(mock.history.get).toHaveLength(1);
+ expect(mock.history.get[0].params.page).toBe('1');
+
+ wrapper.find('.next-page-item').trigger('click');
+
+ await waitForPromises();
+
+ expect(mock.history.get).toHaveLength(2);
+ expect(mock.history.get[1].params.page).toBe('2');
+ });
+ });
+
+ describe('with pipelines', () => {
+ beforeEach(async () => {
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, [pipeline], { 'x-total': 10 });
+
+ createComponent();
+
+ await waitForPromises();
+ });
+
+ it('should render a table with the received pipelines', () => {
+ expect(findTable().exists()).toBe(true);
+ expect(findTableRows()).toHaveLength(1);
+ expect(findLoadingState().exists()).toBe(false);
+ expect(findErrorEmptyState().exists()).toBe(false);
+ });
+
+ describe('pipeline badge counts', () => {
+ it('should receive update-pipelines-count event', () => {
+ const element = document.createElement('div');
+ document.body.appendChild(element);
+
+ return new Promise((resolve) => {
+ element.addEventListener('update-pipelines-count', (event) => {
+ expect(event.detail.pipelineCount).toEqual(10);
+ resolve();
+ });
+
+ createComponent();
+
+ element.appendChild(wrapper.vm.$el);
+ });
+ });
+ });
+ });
+ });
+
+ describe('run pipeline button', () => {
+ let pipelineCopy;
+
+ beforeEach(() => {
+ pipelineCopy = { ...pipeline };
+ });
+
+ describe('when latest pipeline has detached flag', () => {
+ it('renders the run pipeline button', async () => {
+ pipelineCopy.flags.detached_merge_request_pipeline = true;
+ pipelineCopy.flags.merge_request_pipeline = true;
+
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, [pipelineCopy]);
+
+ createComponent();
+
+ await waitForPromises();
+
+ expect(findRunPipelineBtn().exists()).toBe(true);
+ expect(findRunPipelineBtnMobile().exists()).toBe(true);
+ });
+ });
+
+ describe('when latest pipeline does not have detached flag', () => {
+ it('does not render the run pipeline button', async () => {
+ pipelineCopy.flags.detached_merge_request_pipeline = false;
+ pipelineCopy.flags.merge_request_pipeline = false;
+
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, [pipelineCopy]);
+
+ createComponent();
+
+ await waitForPromises();
+
+ expect(findRunPipelineBtn().exists()).toBe(false);
+ expect(findRunPipelineBtnMobile().exists()).toBe(false);
+ });
+ });
+
+ describe('on click', () => {
+ beforeEach(async () => {
+ pipelineCopy.flags.detached_merge_request_pipeline = true;
+
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, [pipelineCopy]);
+
+ createComponent({
+ props: {
+ canRunPipeline: true,
+ projectId: '5',
+ mergeRequestId: 3,
+ },
+ });
+
+ await waitForPromises();
+ });
+ describe('success', () => {
+ beforeEach(() => {
+ jest.spyOn(Api, 'postMergeRequestPipeline').mockResolvedValue();
+ });
+ it('displays a toast message during pipeline creation', async () => {
+ await findRunPipelineBtn().trigger('click');
+
+ expect($toast.show).toHaveBeenCalledWith(TOAST_MESSAGE);
+ });
+
+ it('on desktop, shows a loading button', async () => {
+ await findRunPipelineBtn().trigger('click');
+
+ expect(findRunPipelineBtn().props('loading')).toBe(true);
+
+ await waitForPromises();
+
+ expect(findRunPipelineBtn().props('loading')).toBe(false);
+ });
+
+ it('on mobile, shows a loading button', async () => {
+ await findRunPipelineBtnMobile().trigger('click');
+
+ expect(findRunPipelineBtn().props('loading')).toBe(true);
+
+ await waitForPromises();
+
+ expect(findRunPipelineBtn().props('disabled')).toBe(false);
+ expect(findRunPipelineBtn().props('loading')).toBe(false);
+ });
+ });
+
+ describe('failure', () => {
+ const permissionsMsg = 'You do not have permission to run a pipeline on this branch.';
+ const defaultMsg =
+ 'An error occurred while trying to run a new pipeline for this merge request.';
+
+ it.each`
+ status | message
+ ${HTTP_STATUS_BAD_REQUEST} | ${defaultMsg}
+ ${HTTP_STATUS_UNAUTHORIZED} | ${permissionsMsg}
+ ${HTTP_STATUS_INTERNAL_SERVER_ERROR} | ${defaultMsg}
+ `('displays permissions error message', async ({ status, message }) => {
+ const response = { response: { status } };
+
+ jest.spyOn(Api, 'postMergeRequestPipeline').mockRejectedValue(response);
+
+ await findRunPipelineBtn().trigger('click');
+
+ await waitForPromises();
+
+ expect(createAlert).toHaveBeenCalledWith({
+ message,
+ primaryButton: {
+ text: 'Learn more',
+ link: '/help/ci/pipelines/merge_request_pipelines.md',
+ },
+ });
+ });
+ });
+ });
+
+ describe('on click for fork merge request', () => {
+ beforeEach(async () => {
+ pipelineCopy.flags.detached_merge_request_pipeline = true;
+
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, [pipelineCopy]);
+
+ createComponent({
+ props: {
+ projectId: '5',
+ mergeRequestId: 3,
+ canCreatePipelineInTargetProject: true,
+ sourceProjectFullPath: 'test/parent-project',
+ targetProjectFullPath: 'test/fork-project',
+ },
+ });
+
+ jest.spyOn(Api, 'postMergeRequestPipeline').mockResolvedValue();
+
+ await waitForPromises();
+ });
+
+ it('on desktop, shows a security warning modal', async () => {
+ await findRunPipelineBtn().trigger('click');
+
+ await nextTick();
+
+ expect(findModal()).not.toBeNull();
+ });
+
+ it('on mobile, shows a security warning modal', async () => {
+ await findRunPipelineBtnMobile().trigger('click');
+
+ expect(findModal()).not.toBeNull();
+ });
+ });
+
+ describe('when no pipelines were created on a forked merge request', () => {
+ beforeEach(async () => {
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_OK, []);
+
+ createComponent({
+ props: {
+ projectId: '5',
+ mergeRequestId: 3,
+ canCreatePipelineInTargetProject: true,
+ sourceProjectFullPath: 'test/parent-project',
+ targetProjectFullPath: 'test/fork-project',
+ },
+ });
+
+ await waitForPromises();
+ });
+
+ it('should show security modal from empty state run pipeline button', () => {
+ expect(findEmptyState().exists()).toBe(true);
+ expect(findModal().exists()).toBe(true);
+
+ findRunPipelineBtn().trigger('click');
+
+ expect(showMock).toHaveBeenCalled();
+ });
+ });
+ });
+
+ describe('unsuccessfull request', () => {
+ beforeEach(async () => {
+ mock.onGet('endpoint.json').reply(HTTP_STATUS_INTERNAL_SERVER_ERROR, []);
+
+ createComponent();
+
+ await waitForPromises();
+ });
+
+ it('should render error state', () => {
+ expect(findErrorEmptyState().text()).toBe(
+ 'There was an error fetching the pipelines. Try again in a few moments or contact your support team.',
+ );
+ });
+ });
+});