2021-02-14 13:09:20 -05:00
|
|
|
import { GlFilteredSearch, GlButton, GlLoadingIcon } from '@gitlab/ui';
|
2020-04-27 14:09:41 -04:00
|
|
|
import { mount } from '@vue/test-utils';
|
|
|
|
import MockAdapter from 'axios-mock-adapter';
|
2021-02-14 13:09:20 -05:00
|
|
|
import { nextTick } from 'vue';
|
2021-02-01 10:08:56 -05:00
|
|
|
import waitForPromises from 'helpers/wait_for_promises';
|
2020-08-17 17:09:56 -04:00
|
|
|
import Api from '~/api';
|
2021-02-14 13:09:20 -05:00
|
|
|
import { deprecatedCreateFlash as createFlash } from '~/flash';
|
2020-08-17 17:09:56 -04:00
|
|
|
import axios from '~/lib/utils/axios_utils';
|
2020-09-23 17:09:28 -04:00
|
|
|
import BlankState from '~/pipelines/components/pipelines_list/blank_state.vue';
|
2021-02-14 13:09:20 -05:00
|
|
|
import EmptyState from '~/pipelines/components/pipelines_list/empty_state.vue';
|
|
|
|
import NavigationControls from '~/pipelines/components/pipelines_list/nav_controls.vue';
|
2020-06-23 20:08:43 -04:00
|
|
|
import PipelinesComponent from '~/pipelines/components/pipelines_list/pipelines.vue';
|
2021-02-14 13:09:20 -05:00
|
|
|
import PipelinesTableComponent from '~/pipelines/components/pipelines_list/pipelines_table.vue';
|
2020-05-14 02:08:18 -04:00
|
|
|
import { RAW_TEXT_WARNING } from '~/pipelines/constants';
|
2021-02-14 13:09:20 -05:00
|
|
|
import Store from '~/pipelines/stores/pipelines_store';
|
|
|
|
import NavigationTabs from '~/vue_shared/components/navigation_tabs.vue';
|
|
|
|
import TablePagination from '~/vue_shared/components/pagination/table_pagination.vue';
|
|
|
|
|
2021-02-01 10:08:56 -05:00
|
|
|
import { pipelineWithStages, stageReply, users, mockSearch, branches } from './mock_data';
|
2020-05-14 02:08:18 -04:00
|
|
|
|
2020-08-20 05:09:55 -04:00
|
|
|
jest.mock('~/flash');
|
2020-04-27 14:09:41 -04:00
|
|
|
|
|
|
|
describe('Pipelines', () => {
|
|
|
|
const jsonFixtureName = 'pipelines/pipelines.json';
|
|
|
|
|
|
|
|
preloadFixtures(jsonFixtureName);
|
|
|
|
|
|
|
|
let pipelines;
|
|
|
|
let wrapper;
|
|
|
|
let mock;
|
|
|
|
|
|
|
|
const paths = {
|
|
|
|
endpoint: 'twitter/flight/pipelines.json',
|
2020-12-01 16:09:29 -05:00
|
|
|
autoDevopsHelpPath: '/help/topics/autodevops/index.md',
|
2020-04-27 14:09:41 -04:00
|
|
|
helpPagePath: '/help/ci/quick_start/README',
|
|
|
|
emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
|
|
|
|
errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
|
|
|
|
noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg',
|
|
|
|
ciLintPath: '/ci/lint',
|
|
|
|
resetCachePath: '/twitter/flight/settings/ci_cd/reset_cache',
|
|
|
|
newPipelinePath: '/twitter/flight/pipelines/new',
|
|
|
|
};
|
|
|
|
|
|
|
|
const noPermissions = {
|
|
|
|
endpoint: 'twitter/flight/pipelines.json',
|
2020-12-01 16:09:29 -05:00
|
|
|
autoDevopsHelpPath: '/help/topics/autodevops/index.md',
|
2020-04-27 14:09:41 -04:00
|
|
|
helpPagePath: '/help/ci/quick_start/README',
|
|
|
|
emptyStateSvgPath: '/assets/illustrations/pipelines_empty.svg',
|
|
|
|
errorStateSvgPath: '/assets/illustrations/pipelines_failed.svg',
|
|
|
|
noPipelinesSvgPath: '/assets/illustrations/pipelines_pending.svg',
|
|
|
|
};
|
|
|
|
|
|
|
|
const defaultProps = {
|
|
|
|
hasGitlabCi: true,
|
|
|
|
canCreatePipeline: true,
|
|
|
|
...paths,
|
|
|
|
};
|
|
|
|
|
2020-05-06 20:11:11 -04:00
|
|
|
const findFilteredSearch = () => wrapper.find(GlFilteredSearch);
|
2020-12-23 19:10:25 -05:00
|
|
|
const findByTestId = (id) => wrapper.find(`[data-testid="${id}"]`);
|
2020-09-23 17:09:28 -04:00
|
|
|
const findNavigationTabs = () => wrapper.find(NavigationTabs);
|
|
|
|
const findNavigationControls = () => wrapper.find(NavigationControls);
|
2020-12-23 19:10:25 -05:00
|
|
|
const findTab = (tab) => findByTestId(`pipelines-tab-${tab}`);
|
2020-09-23 17:09:28 -04:00
|
|
|
|
|
|
|
const findRunPipelineButton = () => findByTestId('run-pipeline-button');
|
|
|
|
const findCiLintButton = () => findByTestId('ci-lint-button');
|
|
|
|
const findCleanCacheButton = () => findByTestId('clear-cache-button');
|
2021-02-02 13:09:42 -05:00
|
|
|
const findStagesDropdown = () => findByTestId('mini-pipeline-graph-dropdown-toggle');
|
2020-09-23 17:09:28 -04:00
|
|
|
|
|
|
|
const findEmptyState = () => wrapper.find(EmptyState);
|
|
|
|
const findBlankState = () => wrapper.find(BlankState);
|
|
|
|
|
|
|
|
const findTablePagination = () => wrapper.find(TablePagination);
|
2020-05-06 20:11:11 -04:00
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
const createComponent = (props = defaultProps) => {
|
2020-04-27 14:09:41 -04:00
|
|
|
wrapper = mount(PipelinesComponent, {
|
|
|
|
propsData: {
|
|
|
|
store: new Store(),
|
2020-05-06 20:11:11 -04:00
|
|
|
projectId: '21',
|
2020-05-20 17:09:09 -04:00
|
|
|
params: {},
|
2020-04-27 14:09:41 -04:00
|
|
|
...props,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
beforeEach(() => {
|
2020-12-24 10:09:57 -05:00
|
|
|
delete window.location;
|
|
|
|
});
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
window.location = { search: '' };
|
2020-04-27 14:09:41 -04:00
|
|
|
mock = new MockAdapter(axios);
|
|
|
|
pipelines = getJSONFixture(jsonFixtureName);
|
2020-05-13 02:08:02 -04:00
|
|
|
|
2020-05-06 20:11:11 -04:00
|
|
|
jest.spyOn(Api, 'projectUsers').mockResolvedValue(users);
|
2020-05-13 02:08:02 -04:00
|
|
|
jest.spyOn(Api, 'branches').mockResolvedValue({ data: branches });
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
wrapper.destroy();
|
|
|
|
mock.restore();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('With permission', () => {
|
|
|
|
describe('With pipelines in main tab', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
|
|
|
|
createComponent();
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tabs', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findTab('all').text()).toContain('All');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders Run Pipeline link', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findRunPipelineButton().attributes('href')).toBe(paths.newPipelinePath);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders CI Lint link', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findCiLintButton().attributes('href')).toBe(paths.ciLintPath);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders Clear Runner Cache button', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findCleanCacheButton().text()).toBe('Clear Runner Caches');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders pipelines table', () => {
|
|
|
|
expect(wrapper.findAll('.gl-responsive-table-row')).toHaveLength(
|
|
|
|
pipelines.pipelines.length + 1,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Without pipelines on main tab with CI', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, {
|
|
|
|
pipelines: [],
|
|
|
|
count: {
|
|
|
|
all: 0,
|
|
|
|
pending: 0,
|
|
|
|
running: 0,
|
|
|
|
finished: 0,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
createComponent();
|
|
|
|
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tabs', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findTab('all').text()).toContain('All');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders Run Pipeline link', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findRunPipelineButton().attributes('href')).toBe(paths.newPipelinePath);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders CI Lint link', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findCiLintButton().attributes('href')).toBe(paths.ciLintPath);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders Clear Runner Cache button', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findCleanCacheButton().text()).toBe('Clear Runner Caches');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tab empty state', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findBlankState().text()).toBe('There are currently no pipelines.');
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tab empty state finished scope', () => {
|
|
|
|
wrapper.vm.scope = 'finished';
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findBlankState().text()).toBe('There are currently no finished pipelines.');
|
|
|
|
});
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Without pipelines nor CI', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, {
|
|
|
|
pipelines: [],
|
|
|
|
count: {
|
|
|
|
all: 0,
|
|
|
|
pending: 0,
|
|
|
|
running: 0,
|
|
|
|
finished: 0,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
|
|
|
|
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders empty state', () => {
|
2020-12-23 07:10:26 -05:00
|
|
|
expect(findEmptyState().find('h4').text()).toBe('Build with confidence');
|
|
|
|
expect(findEmptyState().find(GlButton).attributes('href')).toBe(paths.helpPagePath);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render tabs nor buttons', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findTab('all').exists()).toBe(false);
|
|
|
|
expect(findRunPipelineButton().exists()).toBeFalsy();
|
|
|
|
expect(findCiLintButton().exists()).toBeFalsy();
|
|
|
|
expect(findCleanCacheButton().exists()).toBeFalsy();
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('When API returns error', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(500, {});
|
|
|
|
createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
|
|
|
|
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tabs', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findTab('all').text()).toContain('All');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders buttons', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findRunPipelineButton().attributes('href')).toBe(paths.newPipelinePath);
|
2020-04-27 14:09:41 -04:00
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findCiLintButton().attributes('href')).toBe(paths.ciLintPath);
|
|
|
|
expect(findCleanCacheButton().text()).toBe('Clear Runner Caches');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders error state', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findBlankState().text()).toContain('There was an error fetching the pipelines.');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Without permission', () => {
|
|
|
|
describe('With pipelines in main tab', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
|
|
|
|
|
|
|
|
createComponent({ hasGitlabCi: false, canCreatePipeline: false, ...noPermissions });
|
|
|
|
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tabs', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findTab('all').text()).toContain('All');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render buttons', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findRunPipelineButton().exists()).toBeFalsy();
|
|
|
|
expect(findCiLintButton().exists()).toBeFalsy();
|
|
|
|
expect(findCleanCacheButton().exists()).toBeFalsy();
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders pipelines table', () => {
|
|
|
|
expect(wrapper.findAll('.gl-responsive-table-row')).toHaveLength(
|
|
|
|
pipelines.pipelines.length + 1,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Without pipelines on main tab with CI', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, {
|
|
|
|
pipelines: [],
|
|
|
|
count: {
|
|
|
|
all: 0,
|
|
|
|
pending: 0,
|
|
|
|
running: 0,
|
|
|
|
finished: 0,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
createComponent({ hasGitlabCi: true, canCreatePipeline: false, ...noPermissions });
|
|
|
|
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tabs', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findTab('all').text()).toContain('All');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render buttons', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findRunPipelineButton().exists()).toBeFalsy();
|
|
|
|
expect(findCiLintButton().exists()).toBeFalsy();
|
|
|
|
expect(findCleanCacheButton().exists()).toBeFalsy();
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tab empty state', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(wrapper.find('.empty-state h4').text()).toBe('There are currently no pipelines.');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('Without pipelines nor CI', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, {
|
|
|
|
pipelines: [],
|
|
|
|
count: {
|
|
|
|
all: 0,
|
|
|
|
pending: 0,
|
|
|
|
running: 0,
|
|
|
|
finished: 0,
|
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
createComponent({ hasGitlabCi: false, canCreatePipeline: false, ...noPermissions });
|
|
|
|
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders empty state without button to set CI', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findEmptyState().text()).toBe(
|
2020-04-27 14:09:41 -04:00
|
|
|
'This project is not currently set up to run pipelines.',
|
|
|
|
);
|
|
|
|
|
2020-12-23 07:10:26 -05:00
|
|
|
expect(findEmptyState().find(GlButton).exists()).toBeFalsy();
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('does not render tabs or buttons', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findTab('all').exists()).toBe(false);
|
|
|
|
expect(findRunPipelineButton().exists()).toBeFalsy();
|
|
|
|
expect(findCiLintButton().exists()).toBeFalsy();
|
|
|
|
expect(findCleanCacheButton().exists()).toBeFalsy();
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('When API returns error', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(500, {});
|
|
|
|
|
|
|
|
createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...noPermissions });
|
|
|
|
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('renders tabs', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findTab('all').text()).toContain('All');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('does not renders buttons', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findRunPipelineButton().exists()).toBeFalsy();
|
|
|
|
expect(findCiLintButton().exists()).toBeFalsy();
|
|
|
|
expect(findCleanCacheButton().exists()).toBeFalsy();
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('renders error state', () => {
|
|
|
|
expect(wrapper.find('.empty-state').text()).toContain(
|
|
|
|
'There was an error fetching the pipelines.',
|
|
|
|
);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('successful request', () => {
|
|
|
|
describe('with pipelines', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
|
|
|
|
|
|
|
|
createComponent();
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
it('should render table', () => {
|
|
|
|
expect(wrapper.findAll('.gl-responsive-table-row')).toHaveLength(
|
|
|
|
pipelines.pipelines.length + 1,
|
|
|
|
);
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
it('should set up navigation tabs', () => {
|
|
|
|
expect(findNavigationTabs().props('tabs')).toEqual([
|
|
|
|
{ name: 'All', scope: 'all', count: '3', isActive: true },
|
|
|
|
{ name: 'Finished', scope: 'finished', count: undefined, isActive: false },
|
|
|
|
{ name: 'Branches', scope: 'branches', isActive: false },
|
|
|
|
{ name: 'Tags', scope: 'tags', isActive: false },
|
|
|
|
]);
|
|
|
|
});
|
2020-04-27 14:09:41 -04:00
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
it('should render navigation tabs', () => {
|
|
|
|
expect(findTab('all').html()).toContain('All');
|
|
|
|
expect(findTab('finished').text()).toContain('Finished');
|
|
|
|
expect(findTab('branches').text()).toContain('Branches');
|
|
|
|
expect(findTab('tags').text()).toContain('Tags');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
it('should make an API request when using tabs', () => {
|
2020-12-24 10:09:57 -05:00
|
|
|
createComponent({ hasGitlabCi: true, canCreatePipeline: true, ...paths });
|
|
|
|
jest.spyOn(wrapper.vm.service, 'getPipelines');
|
2020-04-27 14:09:41 -04:00
|
|
|
|
|
|
|
return waitForPromises().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
findTab('finished').trigger('click');
|
2020-04-27 14:09:41 -04:00
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
expect(wrapper.vm.service.getPipelines).toHaveBeenCalledWith({
|
|
|
|
scope: 'finished',
|
|
|
|
page: '1',
|
|
|
|
});
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('with pagination', () => {
|
|
|
|
it('should make an API request when using pagination', () => {
|
2020-12-24 10:09:57 -05:00
|
|
|
createComponent({ hasGitlabCi: true, canCreatePipeline: true, ...paths });
|
|
|
|
jest.spyOn(wrapper.vm.service, 'getPipelines');
|
2020-04-27 14:09:41 -04:00
|
|
|
|
|
|
|
return waitForPromises()
|
|
|
|
.then(() => {
|
|
|
|
// Mock pagination
|
|
|
|
wrapper.vm.store.state.pageInfo = {
|
|
|
|
page: 1,
|
|
|
|
total: 10,
|
|
|
|
perPage: 2,
|
|
|
|
nextPage: 2,
|
|
|
|
totalPages: 5,
|
|
|
|
};
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick();
|
2020-04-27 14:09:41 -04:00
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
wrapper.find('.next-page-item').trigger('click');
|
2020-12-24 10:09:57 -05:00
|
|
|
expect(wrapper.vm.service.getPipelines).toHaveBeenCalledWith({
|
|
|
|
scope: 'all',
|
|
|
|
page: '2',
|
|
|
|
});
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
describe('User Interaction', () => {
|
|
|
|
let updateContentMock;
|
|
|
|
|
2020-04-27 14:09:41 -04:00
|
|
|
beforeEach(() => {
|
|
|
|
jest.spyOn(window.history, 'pushState').mockImplementation(() => null);
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet(paths.endpoint).reply(200, pipelines);
|
|
|
|
createComponent();
|
2020-04-27 14:09:41 -04:00
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
updateContentMock = jest.spyOn(wrapper.vm, 'updateContent');
|
|
|
|
|
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('when user changes tabs', () => {
|
|
|
|
it('should set page to 1', () => {
|
|
|
|
findNavigationTabs().vm.$emit('onChangeTab', 'running');
|
2020-04-27 14:09:41 -04:00
|
|
|
|
|
|
|
expect(updateContentMock).toHaveBeenCalledWith({ scope: 'running', page: '1' });
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
describe('when user changes page', () => {
|
2020-04-27 14:09:41 -04:00
|
|
|
it('should update page and keep scope', () => {
|
2020-09-23 17:09:28 -04:00
|
|
|
findTablePagination().vm.change(4);
|
2020-04-27 14:09:41 -04:00
|
|
|
|
|
|
|
expect(updateContentMock).toHaveBeenCalledWith({ scope: wrapper.vm.scope, page: '4' });
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
describe('updates results when a staged is clicked', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
const copyPipeline = { ...pipelineWithStages };
|
|
|
|
copyPipeline.id += 1;
|
|
|
|
mock
|
|
|
|
.onGet('twitter/flight/pipelines.json')
|
|
|
|
.reply(
|
|
|
|
200,
|
|
|
|
{
|
|
|
|
pipelines: [pipelineWithStages],
|
|
|
|
count: {
|
|
|
|
all: 1,
|
|
|
|
finished: 1,
|
|
|
|
pending: 0,
|
|
|
|
running: 0,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
'POLL-INTERVAL': 100,
|
|
|
|
},
|
|
|
|
)
|
|
|
|
.onGet(pipelineWithStages.details.stages[0].dropdown_path)
|
|
|
|
.reply(200, stageReply);
|
2020-04-27 14:09:41 -04:00
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
createComponent();
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
describe('when a request is being made', () => {
|
|
|
|
it('stops polling, cancels the request, & restarts polling', () => {
|
|
|
|
const stopMock = jest.spyOn(wrapper.vm.poll, 'stop');
|
|
|
|
const restartMock = jest.spyOn(wrapper.vm.poll, 'restart');
|
|
|
|
const cancelMock = jest.spyOn(wrapper.vm.service.cancelationSource, 'cancel');
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
|
2020-04-27 14:09:41 -04:00
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
return waitForPromises()
|
|
|
|
.then(() => {
|
|
|
|
wrapper.vm.isMakingRequest = true;
|
|
|
|
findStagesDropdown().trigger('click');
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
expect(cancelMock).toHaveBeenCalled();
|
|
|
|
expect(stopMock).toHaveBeenCalled();
|
|
|
|
expect(restartMock).toHaveBeenCalled();
|
|
|
|
});
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
describe('when no request is being made', () => {
|
|
|
|
it('stops polling & restarts polling', () => {
|
|
|
|
const stopMock = jest.spyOn(wrapper.vm.poll, 'stop');
|
|
|
|
const restartMock = jest.spyOn(wrapper.vm.poll, 'restart');
|
|
|
|
mock.onGet('twitter/flight/pipelines.json').reply(200, pipelines);
|
|
|
|
|
|
|
|
return waitForPromises()
|
|
|
|
.then(() => {
|
|
|
|
findStagesDropdown().trigger('click');
|
|
|
|
expect(stopMock).toHaveBeenCalled();
|
|
|
|
})
|
|
|
|
.then(() => {
|
|
|
|
expect(restartMock).toHaveBeenCalled();
|
|
|
|
});
|
|
|
|
});
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
2020-09-23 17:09:28 -04:00
|
|
|
});
|
2020-04-27 14:09:41 -04:00
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
describe('Rendered content', () => {
|
|
|
|
beforeEach(() => {
|
|
|
|
createComponent();
|
|
|
|
});
|
|
|
|
|
|
|
|
describe('displays different content', () => {
|
|
|
|
it('shows loading state when the app is loading', () => {
|
|
|
|
expect(wrapper.find(GlLoadingIcon).exists()).toBe(true);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
it('shows error state when app has error', () => {
|
2020-04-27 14:09:41 -04:00
|
|
|
wrapper.vm.hasError = true;
|
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findBlankState().props('message')).toBe(
|
|
|
|
'There was an error fetching the pipelines. Try again in a few moments or contact your support team.',
|
|
|
|
);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
it('shows table list when app has pipelines', () => {
|
2020-04-27 14:09:41 -04:00
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
wrapper.vm.hasError = false;
|
|
|
|
wrapper.vm.state.pipelines = pipelines.pipelines;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(wrapper.find(PipelinesTableComponent).exists()).toBe(true);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
it('shows empty tab when app does not have pipelines but project has pipelines', () => {
|
2020-04-27 14:09:41 -04:00
|
|
|
wrapper.vm.state.count.all = 10;
|
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findBlankState().exists()).toBe(true);
|
|
|
|
expect(findBlankState().props('message')).toBe('There are currently no pipelines.');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
it('shows empty tab when project has CI', () => {
|
2020-04-27 14:09:41 -04:00
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findBlankState().exists()).toBe(true);
|
|
|
|
expect(findBlankState().props('message')).toBe('There are currently no pipelines.');
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
it('shows empty state when project does not have pipelines nor CI', () => {
|
2020-04-27 14:09:41 -04:00
|
|
|
createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
|
|
|
|
|
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(wrapper.find(EmptyState).exists()).toBe(true);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
describe('displays tabs', () => {
|
2020-04-27 14:09:41 -04:00
|
|
|
it('returns true when state is loading & has already made the first request', () => {
|
|
|
|
wrapper.vm.isLoading = true;
|
|
|
|
wrapper.vm.hasMadeRequest = true;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findNavigationTabs().exists()).toBe(true);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns true when state is tableList & has already made the first request', () => {
|
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
wrapper.vm.state.pipelines = pipelines.pipelines;
|
|
|
|
wrapper.vm.hasMadeRequest = true;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findNavigationTabs().exists()).toBe(true);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns true when state is error & has already made the first request', () => {
|
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
wrapper.vm.hasError = true;
|
|
|
|
wrapper.vm.hasMadeRequest = true;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findNavigationTabs().exists()).toBe(true);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns true when state is empty tab & has already made the first request', () => {
|
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
wrapper.vm.state.count.all = 10;
|
|
|
|
wrapper.vm.hasMadeRequest = true;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findNavigationTabs().exists()).toBe(true);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns false when has not made first request', () => {
|
|
|
|
wrapper.vm.hasMadeRequest = false;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findNavigationTabs().exists()).toBe(false);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns false when state is empty state', () => {
|
|
|
|
createComponent({ hasGitlabCi: false, canCreatePipeline: true, ...paths });
|
|
|
|
|
|
|
|
wrapper.vm.isLoading = false;
|
|
|
|
wrapper.vm.hasMadeRequest = true;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findNavigationTabs().exists()).toBe(false);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-09-23 17:09:28 -04:00
|
|
|
describe('displays buttons', () => {
|
2020-04-27 14:09:41 -04:00
|
|
|
it('returns true when it has paths & has made the first request', () => {
|
|
|
|
wrapper.vm.hasMadeRequest = true;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findNavigationControls().exists()).toBe(true);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
it('returns false when it has not made the first request', () => {
|
|
|
|
wrapper.vm.hasMadeRequest = false;
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
return nextTick().then(() => {
|
2020-09-23 17:09:28 -04:00
|
|
|
expect(findNavigationControls().exists()).toBe(false);
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2020-05-06 20:11:11 -04:00
|
|
|
describe('Pipeline filters', () => {
|
2020-05-14 02:08:18 -04:00
|
|
|
let updateContentMock;
|
|
|
|
|
2020-05-06 20:11:11 -04:00
|
|
|
beforeEach(() => {
|
|
|
|
mock.onGet(paths.endpoint).reply(200, pipelines);
|
|
|
|
createComponent();
|
|
|
|
|
2020-05-14 02:08:18 -04:00
|
|
|
updateContentMock = jest.spyOn(wrapper.vm, 'updateContent');
|
|
|
|
|
2020-05-06 20:11:11 -04:00
|
|
|
return waitForPromises();
|
|
|
|
});
|
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
it('updates request data and query params on filter submit', async () => {
|
2020-06-05 17:08:27 -04:00
|
|
|
const expectedQueryParams = {
|
|
|
|
page: '1',
|
|
|
|
scope: 'all',
|
|
|
|
username: 'root',
|
|
|
|
ref: 'master',
|
|
|
|
status: 'pending',
|
|
|
|
};
|
2020-05-06 20:11:11 -04:00
|
|
|
|
2020-05-13 02:08:02 -04:00
|
|
|
findFilteredSearch().vm.$emit('submit', mockSearch);
|
2020-12-24 10:09:57 -05:00
|
|
|
await nextTick();
|
2020-05-06 20:11:11 -04:00
|
|
|
|
|
|
|
expect(wrapper.vm.requestData).toEqual(expectedQueryParams);
|
|
|
|
expect(updateContentMock).toHaveBeenCalledWith(expectedQueryParams);
|
|
|
|
});
|
2020-05-14 02:08:18 -04:00
|
|
|
|
2020-12-24 10:09:57 -05:00
|
|
|
it('does not add query params if raw text search is used', async () => {
|
2020-05-14 02:08:18 -04:00
|
|
|
const expectedQueryParams = { page: '1', scope: 'all' };
|
|
|
|
|
|
|
|
findFilteredSearch().vm.$emit('submit', ['rawText']);
|
2020-12-24 10:09:57 -05:00
|
|
|
await nextTick();
|
2020-05-14 02:08:18 -04:00
|
|
|
|
|
|
|
expect(wrapper.vm.requestData).toEqual(expectedQueryParams);
|
|
|
|
expect(updateContentMock).toHaveBeenCalledWith(expectedQueryParams);
|
|
|
|
});
|
|
|
|
|
|
|
|
it('displays a warning message if raw text search is used', () => {
|
|
|
|
findFilteredSearch().vm.$emit('submit', ['rawText']);
|
|
|
|
|
|
|
|
expect(createFlash).toHaveBeenCalledTimes(1);
|
|
|
|
expect(createFlash).toHaveBeenCalledWith(RAW_TEXT_WARNING, 'warning');
|
|
|
|
});
|
2020-05-06 20:11:11 -04:00
|
|
|
});
|
2020-04-27 14:09:41 -04:00
|
|
|
});
|