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/runner/components')
-rw-r--r--spec/frontend/runner/components/__snapshots__/runner_status_popover_spec.js.snap3
-rw-r--r--spec/frontend/runner/components/cells/link_cell_spec.js72
-rw-r--r--spec/frontend/runner/components/cells/runner_actions_cell_spec.js138
-rw-r--r--spec/frontend/runner/components/cells/runner_owner_cell_spec.js111
-rw-r--r--spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js164
-rw-r--r--spec/frontend/runner/components/cells/runner_status_cell_spec.js77
-rw-r--r--spec/frontend/runner/components/cells/runner_summary_field_spec.js49
-rw-r--r--spec/frontend/runner/components/registration/registration_dropdown_spec.js198
-rw-r--r--spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js209
-rw-r--r--spec/frontend/runner/components/registration/registration_token_spec.js62
-rw-r--r--spec/frontend/runner/components/runner_assigned_item_spec.js68
-rw-r--r--spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js140
-rw-r--r--spec/frontend/runner/components/runner_bulk_delete_spec.js243
-rw-r--r--spec/frontend/runner/components/runner_delete_button_spec.js266
-rw-r--r--spec/frontend/runner/components/runner_delete_modal_spec.js60
-rw-r--r--spec/frontend/runner/components/runner_details_spec.js130
-rw-r--r--spec/frontend/runner/components/runner_edit_button_spec.js41
-rw-r--r--spec/frontend/runner/components/runner_filtered_search_bar_spec.js188
-rw-r--r--spec/frontend/runner/components/runner_groups_spec.js67
-rw-r--r--spec/frontend/runner/components/runner_header_spec.js119
-rw-r--r--spec/frontend/runner/components/runner_jobs_spec.js155
-rw-r--r--spec/frontend/runner/components/runner_jobs_table_spec.js119
-rw-r--r--spec/frontend/runner/components/runner_list_empty_state_spec.js103
-rw-r--r--spec/frontend/runner/components/runner_list_spec.js231
-rw-r--r--spec/frontend/runner/components/runner_membership_toggle_spec.js57
-rw-r--r--spec/frontend/runner/components/runner_pagination_spec.js115
-rw-r--r--spec/frontend/runner/components/runner_pause_button_spec.js263
-rw-r--r--spec/frontend/runner/components/runner_paused_badge_spec.js46
-rw-r--r--spec/frontend/runner/components/runner_projects_spec.js251
-rw-r--r--spec/frontend/runner/components/runner_stacked_layout_banner_spec.js41
-rw-r--r--spec/frontend/runner/components/runner_status_badge_spec.js133
-rw-r--r--spec/frontend/runner/components/runner_status_popover_spec.js36
-rw-r--r--spec/frontend/runner/components/runner_tag_spec.js79
-rw-r--r--spec/frontend/runner/components/runner_tags_spec.js54
-rw-r--r--spec/frontend/runner/components/runner_type_badge_spec.js66
-rw-r--r--spec/frontend/runner/components/runner_type_tabs_spec.js214
-rw-r--r--spec/frontend/runner/components/runner_update_form_spec.js288
-rw-r--r--spec/frontend/runner/components/search_tokens/tag_token_spec.js208
-rw-r--r--spec/frontend/runner/components/stat/runner_count_spec.js148
-rw-r--r--spec/frontend/runner/components/stat/runner_single_stat_spec.js61
-rw-r--r--spec/frontend/runner/components/stat/runner_stats_spec.js81
41 files changed, 0 insertions, 5154 deletions
diff --git a/spec/frontend/runner/components/__snapshots__/runner_status_popover_spec.js.snap b/spec/frontend/runner/components/__snapshots__/runner_status_popover_spec.js.snap
deleted file mode 100644
index b27a1adf01b..00000000000
--- a/spec/frontend/runner/components/__snapshots__/runner_status_popover_spec.js.snap
+++ /dev/null
@@ -1,3 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`RunnerStatusPopover renders complete text 1`] = `"Never contacted: Runner has never contacted GitLab (when you register a runner, use gitlab-runner run to bring it online) Online: Runner has contacted GitLab within the last 2 hours Offline: Runner has not contacted GitLab in more than 2 hours Stale: Runner has not contacted GitLab in more than 3 months"`;
diff --git a/spec/frontend/runner/components/cells/link_cell_spec.js b/spec/frontend/runner/components/cells/link_cell_spec.js
deleted file mode 100644
index 46ab1adb6b6..00000000000
--- a/spec/frontend/runner/components/cells/link_cell_spec.js
+++ /dev/null
@@ -1,72 +0,0 @@
-import { GlLink } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import LinkCell from '~/runner/components/cells/link_cell.vue';
-
-describe('LinkCell', () => {
- let wrapper;
-
- const findGlLink = () => wrapper.findComponent(GlLink);
- const findSpan = () => wrapper.find('span');
-
- const createComponent = ({ props = {}, ...options } = {}) => {
- wrapper = shallowMountExtended(LinkCell, {
- propsData: {
- ...props,
- },
- ...options,
- });
- };
-
- it('when an href is provided, renders a link', () => {
- createComponent({ props: { href: '/url' } });
- expect(findGlLink().exists()).toBe(true);
- });
-
- it('when an href is not provided, renders no link', () => {
- createComponent();
- expect(findGlLink().exists()).toBe(false);
- });
-
- describe.each`
- href | findContent
- ${null} | ${findSpan}
- ${'/url'} | ${findGlLink}
- `('When href is $href', ({ href, findContent }) => {
- const content = 'My Text';
- const attrs = { foo: 'bar' };
- const listeners = {
- click: jest.fn(),
- };
-
- beforeEach(() => {
- createComponent({
- props: { href },
- slots: {
- default: content,
- },
- attrs,
- listeners,
- });
- });
-
- afterAll(() => {
- listeners.click.mockReset();
- });
-
- it('Renders content', () => {
- expect(findContent().text()).toBe(content);
- });
-
- it('Passes attributes', () => {
- expect(findContent().attributes()).toMatchObject(attrs);
- });
-
- it('Passes event listeners', () => {
- expect(listeners.click).toHaveBeenCalledTimes(0);
-
- findContent().vm.$emit('click');
-
- expect(listeners.click).toHaveBeenCalledTimes(1);
- });
- });
-});
diff --git a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
deleted file mode 100644
index 58974d4f85f..00000000000
--- a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-
-import RunnerActionsCell from '~/runner/components/cells/runner_actions_cell.vue';
-import RunnerPauseButton from '~/runner/components/runner_pause_button.vue';
-import RunnerEditButton from '~/runner/components/runner_edit_button.vue';
-import RunnerDeleteButton from '~/runner/components/runner_delete_button.vue';
-import { allRunnersData } from '../../mock_data';
-
-const mockRunner = allRunnersData.data.runners.nodes[0];
-
-describe('RunnerActionsCell', () => {
- let wrapper;
-
- const findEditBtn = () => wrapper.findComponent(RunnerEditButton);
- const findRunnerPauseBtn = () => wrapper.findComponent(RunnerPauseButton);
- const findDeleteBtn = () => wrapper.findComponent(RunnerDeleteButton);
-
- const createComponent = ({ runner = {}, ...props } = {}) => {
- wrapper = shallowMountExtended(RunnerActionsCell, {
- propsData: {
- editUrl: mockRunner.editAdminUrl,
- runner: {
- id: mockRunner.id,
- shortSha: mockRunner.shortSha,
- editAdminUrl: mockRunner.editAdminUrl,
- userPermissions: mockRunner.userPermissions,
- ...runner,
- },
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('Edit Action', () => {
- it('Displays the runner edit link with the correct href', () => {
- createComponent();
-
- expect(findEditBtn().attributes('href')).toBe(mockRunner.editAdminUrl);
- });
-
- it('Does not render the runner edit link when user cannot update', () => {
- createComponent({
- runner: {
- userPermissions: {
- ...mockRunner.userPermissions,
- updateRunner: false,
- },
- },
- });
-
- expect(findEditBtn().exists()).toBe(false);
- });
-
- it('Does not render the runner edit link when editUrl is not provided', () => {
- createComponent({
- editUrl: null,
- });
-
- expect(findEditBtn().exists()).toBe(false);
- });
- });
-
- describe('Pause action', () => {
- it('Renders a compact pause button', () => {
- createComponent();
-
- expect(findRunnerPauseBtn().props('compact')).toBe(true);
- });
-
- it('Does not render the runner pause button when user cannot update', () => {
- createComponent({
- runner: {
- userPermissions: {
- ...mockRunner.userPermissions,
- updateRunner: false,
- },
- },
- });
-
- expect(findRunnerPauseBtn().exists()).toBe(false);
- });
- });
-
- describe('Delete action', () => {
- it('Renders a compact delete button', () => {
- createComponent();
-
- expect(findDeleteBtn().props('compact')).toBe(true);
- });
-
- it('Passes runner data to delete button', () => {
- createComponent({
- runner: mockRunner,
- });
-
- expect(findDeleteBtn().props('runner')).toEqual(mockRunner);
- });
-
- it('Emits toggledPaused events', () => {
- createComponent();
-
- expect(wrapper.emitted('toggledPaused')).toBe(undefined);
-
- findRunnerPauseBtn().vm.$emit('toggledPaused');
-
- expect(wrapper.emitted('toggledPaused')).toHaveLength(1);
- });
-
- it('Emits delete events', () => {
- const value = { name: 'Runner' };
-
- createComponent();
-
- expect(wrapper.emitted('deleted')).toBe(undefined);
-
- findDeleteBtn().vm.$emit('deleted', value);
-
- expect(wrapper.emitted('deleted')).toEqual([[value]]);
- });
-
- it('Does not render the runner delete button when user cannot delete', () => {
- createComponent({
- runner: {
- userPermissions: {
- ...mockRunner.userPermissions,
- deleteRunner: false,
- },
- },
- });
-
- expect(findDeleteBtn().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/runner/components/cells/runner_owner_cell_spec.js b/spec/frontend/runner/components/cells/runner_owner_cell_spec.js
deleted file mode 100644
index e9965d8855d..00000000000
--- a/spec/frontend/runner/components/cells/runner_owner_cell_spec.js
+++ /dev/null
@@ -1,111 +0,0 @@
-import { shallowMount } from '@vue/test-utils';
-import { GlLink } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-
-import RunnerOwnerCell from '~/runner/components/cells/runner_owner_cell.vue';
-
-import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
-
-describe('RunnerOwnerCell', () => {
- let wrapper;
-
- const findLink = () => wrapper.findComponent(GlLink);
- const getLinkTooltip = () => getBinding(findLink().element, 'gl-tooltip').value;
-
- const createComponent = ({ runner } = {}) => {
- wrapper = shallowMount(RunnerOwnerCell, {
- directives: {
- GlTooltip: createMockDirective(),
- },
- propsData: {
- runner,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('When its an instance runner', () => {
- beforeEach(() => {
- createComponent({
- runner: {
- runnerType: INSTANCE_TYPE,
- },
- });
- });
-
- it('shows an administrator label', () => {
- expect(findLink().exists()).toBe(false);
- expect(wrapper.text()).toBe(s__('Runners|Administrator'));
- });
- });
-
- describe('When its a group runner', () => {
- const mockName = 'Group 2';
- const mockFullName = 'Group 1 / Group 2';
- const mockWebUrl = '/group-1/group-2';
-
- beforeEach(() => {
- createComponent({
- runner: {
- runnerType: GROUP_TYPE,
- groups: {
- nodes: [
- {
- name: mockName,
- fullName: mockFullName,
- webUrl: mockWebUrl,
- },
- ],
- },
- },
- });
- });
-
- it('Displays a group link', () => {
- expect(findLink().attributes('href')).toBe(mockWebUrl);
- expect(wrapper.text()).toBe(mockName);
- expect(getLinkTooltip()).toBe(mockFullName);
- });
- });
-
- describe('When its a project runner', () => {
- const mockName = 'Project 1';
- const mockNameWithNamespace = 'Group 1 / Project 1';
- const mockWebUrl = '/group-1/project-1';
-
- beforeEach(() => {
- createComponent({
- runner: {
- runnerType: PROJECT_TYPE,
- ownerProject: {
- name: mockName,
- nameWithNamespace: mockNameWithNamespace,
- webUrl: mockWebUrl,
- },
- },
- });
- });
-
- it('Displays a project link', () => {
- expect(findLink().attributes('href')).toBe(mockWebUrl);
- expect(wrapper.text()).toBe(mockName);
- expect(getLinkTooltip()).toBe(mockNameWithNamespace);
- });
- });
-
- describe('When its an empty runner', () => {
- beforeEach(() => {
- createComponent({
- runner: {},
- });
- });
-
- it('shows no label', () => {
- expect(wrapper.text()).toBe('');
- });
- });
-});
diff --git a/spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js b/spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js
deleted file mode 100644
index e7cadefc140..00000000000
--- a/spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js
+++ /dev/null
@@ -1,164 +0,0 @@
-import { __ } from '~/locale';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerStackedSummaryCell from '~/runner/components/cells/runner_stacked_summary_cell.vue';
-import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-import RunnerTags from '~/runner/components/runner_tags.vue';
-import RunnerSummaryField from '~/runner/components/cells/runner_summary_field.vue';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-
-import { INSTANCE_TYPE, I18N_INSTANCE_TYPE, PROJECT_TYPE } from '~/runner/constants';
-
-import { allRunnersData } from '../../mock_data';
-
-const mockRunner = allRunnersData.data.runners.nodes[0];
-
-describe('RunnerTypeCell', () => {
- let wrapper;
-
- const findLockIcon = () => wrapper.findByTestId('lock-icon');
- const findRunnerTags = () => wrapper.findComponent(RunnerTags);
- const findRunnerSummaryField = (icon) =>
- wrapper.findAllComponents(RunnerSummaryField).filter((w) => w.props('icon') === icon)
- .wrappers[0];
-
- const createComponent = (runner, options) => {
- wrapper = mountExtended(RunnerStackedSummaryCell, {
- propsData: {
- runner: {
- ...mockRunner,
- ...runner,
- },
- },
- stubs: {
- RunnerSummaryField,
- },
- ...options,
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays the runner name as id and short token', () => {
- expect(wrapper.text()).toContain(
- `#${getIdFromGraphQLId(mockRunner.id)} (${mockRunner.shortSha})`,
- );
- });
-
- it('Does not display the locked icon', () => {
- expect(findLockIcon().exists()).toBe(false);
- });
-
- it('Displays the locked icon for locked runners', () => {
- createComponent({
- runnerType: PROJECT_TYPE,
- locked: true,
- });
-
- expect(findLockIcon().exists()).toBe(true);
- });
-
- it('Displays the runner type', () => {
- createComponent({
- runnerType: INSTANCE_TYPE,
- locked: true,
- });
-
- expect(wrapper.text()).toContain(I18N_INSTANCE_TYPE);
- });
-
- it('Displays the runner version', () => {
- expect(wrapper.text()).toContain(mockRunner.version);
- });
-
- it('Displays the runner description', () => {
- expect(wrapper.text()).toContain(mockRunner.description);
- });
-
- it('Displays last contact', () => {
- createComponent({
- contactedAt: '2022-01-02',
- });
-
- expect(findRunnerSummaryField('clock').findComponent(TimeAgo).props('time')).toBe('2022-01-02');
- });
-
- it('Displays empty last contact', () => {
- createComponent({
- contactedAt: null,
- });
-
- expect(findRunnerSummaryField('clock').findComponent(TimeAgo).exists()).toBe(false);
- expect(findRunnerSummaryField('clock').text()).toContain(__('Never'));
- });
-
- it('Displays ip address', () => {
- createComponent({
- ipAddress: '127.0.0.1',
- });
-
- expect(findRunnerSummaryField('disk').text()).toContain('127.0.0.1');
- });
-
- it('Displays no ip address', () => {
- createComponent({
- ipAddress: null,
- });
-
- expect(findRunnerSummaryField('disk')).toBeUndefined();
- });
-
- it('Displays job count', () => {
- expect(findRunnerSummaryField('pipeline').text()).toContain(`${mockRunner.jobCount}`);
- });
-
- it('Formats large job counts', () => {
- createComponent({
- jobCount: 1000,
- });
-
- expect(findRunnerSummaryField('pipeline').text()).toContain('1,000');
- });
-
- it('Formats large job counts with a plus symbol', () => {
- createComponent({
- jobCount: 1001,
- });
-
- expect(findRunnerSummaryField('pipeline').text()).toContain('1,000+');
- });
-
- it('Displays created at', () => {
- expect(findRunnerSummaryField('calendar').findComponent(TimeAgo).props('time')).toBe(
- mockRunner.createdAt,
- );
- });
-
- it('Displays tag list', () => {
- createComponent({
- tagList: ['shell', 'linux'],
- });
-
- expect(findRunnerTags().props('tagList')).toEqual(['shell', 'linux']);
- });
-
- it('Displays a custom slot', () => {
- const slotContent = 'My custom runner name';
-
- createComponent(
- {},
- {
- slots: {
- 'runner-name': slotContent,
- },
- },
- );
-
- expect(wrapper.text()).toContain(slotContent);
- });
-});
diff --git a/spec/frontend/runner/components/cells/runner_status_cell_spec.js b/spec/frontend/runner/components/cells/runner_status_cell_spec.js
deleted file mode 100644
index 1d4e3762c91..00000000000
--- a/spec/frontend/runner/components/cells/runner_status_cell_spec.js
+++ /dev/null
@@ -1,77 +0,0 @@
-import { mount } from '@vue/test-utils';
-import RunnerStatusCell from '~/runner/components/cells/runner_status_cell.vue';
-
-import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
-import RunnerPausedBadge from '~/runner/components/runner_paused_badge.vue';
-import {
- I18N_PAUSED,
- I18N_STATUS_ONLINE,
- I18N_STATUS_OFFLINE,
- INSTANCE_TYPE,
- STATUS_ONLINE,
- STATUS_OFFLINE,
-} from '~/runner/constants';
-
-describe('RunnerStatusCell', () => {
- let wrapper;
-
- const findStatusBadge = () => wrapper.findComponent(RunnerStatusBadge);
- const findPausedBadge = () => wrapper.findComponent(RunnerPausedBadge);
-
- const createComponent = ({ runner = {} } = {}) => {
- wrapper = mount(RunnerStatusCell, {
- propsData: {
- runner: {
- runnerType: INSTANCE_TYPE,
- active: true,
- status: STATUS_ONLINE,
- ...runner,
- },
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays online status', () => {
- createComponent();
-
- expect(wrapper.text()).toContain(I18N_STATUS_ONLINE);
- expect(findStatusBadge().text()).toBe(I18N_STATUS_ONLINE);
- });
-
- it('Displays offline status', () => {
- createComponent({
- runner: {
- status: STATUS_OFFLINE,
- },
- });
-
- expect(wrapper.text()).toMatchInterpolatedText(I18N_STATUS_OFFLINE);
- expect(findStatusBadge().text()).toBe(I18N_STATUS_OFFLINE);
- });
-
- it('Displays paused status', () => {
- createComponent({
- runner: {
- active: false,
- status: STATUS_ONLINE,
- },
- });
-
- expect(wrapper.text()).toMatchInterpolatedText(`${I18N_STATUS_ONLINE} ${I18N_PAUSED}`);
- expect(findPausedBadge().text()).toBe(I18N_PAUSED);
- });
-
- it('Is empty when data is missing', () => {
- createComponent({
- runner: {
- status: null,
- },
- });
-
- expect(wrapper.text()).toBe('');
- });
-});
diff --git a/spec/frontend/runner/components/cells/runner_summary_field_spec.js b/spec/frontend/runner/components/cells/runner_summary_field_spec.js
deleted file mode 100644
index b49addf112f..00000000000
--- a/spec/frontend/runner/components/cells/runner_summary_field_spec.js
+++ /dev/null
@@ -1,49 +0,0 @@
-import { GlIcon } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RunnerSummaryField from '~/runner/components/cells/runner_summary_field.vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-
-describe('RunnerSummaryField', () => {
- let wrapper;
-
- const findIcon = () => wrapper.findComponent(GlIcon);
- const getTooltipValue = () => getBinding(wrapper.element, 'gl-tooltip').value;
-
- const createComponent = ({ props, ...options } = {}) => {
- wrapper = shallowMount(RunnerSummaryField, {
- propsData: {
- icon: '',
- tooltip: '',
- ...props,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('shows content in slot', () => {
- createComponent({
- slots: { default: 'content' },
- });
-
- expect(wrapper.text()).toBe('content');
- });
-
- it('shows icon', () => {
- createComponent({ props: { icon: 'git' } });
-
- expect(findIcon().props('name')).toBe('git');
- });
-
- it('shows tooltip', () => {
- createComponent({ props: { tooltip: 'tooltip' } });
-
- expect(getTooltipValue()).toBe('tooltip');
- });
-});
diff --git a/spec/frontend/runner/components/registration/registration_dropdown_spec.js b/spec/frontend/runner/components/registration/registration_dropdown_spec.js
deleted file mode 100644
index d3f38bc1d26..00000000000
--- a/spec/frontend/runner/components/registration/registration_dropdown_spec.js
+++ /dev/null
@@ -1,198 +0,0 @@
-import { GlModal, GlDropdown, GlDropdownItem, GlDropdownForm } from '@gitlab/ui';
-import { mount, shallowMount, createWrapper } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-
-import VueApollo from 'vue-apollo';
-import { extendedWrapper } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-
-import RegistrationDropdown from '~/runner/components/registration/registration_dropdown.vue';
-import RegistrationToken from '~/runner/components/registration/registration_token.vue';
-import RegistrationTokenResetDropdownItem from '~/runner/components/registration/registration_token_reset_dropdown_item.vue';
-
-import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
-
-import getRunnerPlatformsQuery from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_platforms.query.graphql';
-import getRunnerSetupInstructionsQuery from '~/vue_shared/components/runner_instructions/graphql/queries/get_runner_setup.query.graphql';
-
-import {
- mockGraphqlRunnerPlatforms,
- mockGraphqlInstructions,
-} from 'jest/vue_shared/components/runner_instructions/mock_data';
-
-const mockToken = '0123456789';
-const maskToken = '**********';
-
-Vue.use(VueApollo);
-
-describe('RegistrationDropdown', () => {
- let wrapper;
-
- const findDropdown = () => wrapper.findComponent(GlDropdown);
-
- const findRegistrationInstructionsDropdownItem = () => wrapper.findComponent(GlDropdownItem);
- const findTokenDropdownItem = () => wrapper.findComponent(GlDropdownForm);
- const findRegistrationToken = () => wrapper.findComponent(RegistrationToken);
- const findRegistrationTokenInput = () =>
- wrapper.findByLabelText(RegistrationToken.i18n.registrationToken);
- const findTokenResetDropdownItem = () =>
- wrapper.findComponent(RegistrationTokenResetDropdownItem);
- const findModal = () => wrapper.findComponent(GlModal);
- const findModalContent = () =>
- createWrapper(document.body)
- .find('[data-testid="runner-instructions-modal"]')
- .text()
- .replace(/[\n\t\s]+/g, ' ');
-
- const openModal = async () => {
- await findRegistrationInstructionsDropdownItem().trigger('click');
- findModal().vm.$emit('shown');
-
- await waitForPromises();
- };
-
- const createComponent = ({ props = {}, ...options } = {}, mountFn = shallowMount) => {
- wrapper = extendedWrapper(
- mountFn(RegistrationDropdown, {
- propsData: {
- registrationToken: mockToken,
- type: INSTANCE_TYPE,
- ...props,
- },
- ...options,
- }),
- );
- };
-
- const createComponentWithModal = () => {
- const requestHandlers = [
- [getRunnerPlatformsQuery, jest.fn().mockResolvedValue(mockGraphqlRunnerPlatforms)],
- [getRunnerSetupInstructionsQuery, jest.fn().mockResolvedValue(mockGraphqlInstructions)],
- ];
-
- createComponent(
- {
- // Mock load modal contents from API
- apolloProvider: createMockApollo(requestHandlers),
- // Use `attachTo` to find the modal
- attachTo: document.body,
- },
- mount,
- );
- };
-
- it.each`
- type | text
- ${INSTANCE_TYPE} | ${'Register an instance runner'}
- ${GROUP_TYPE} | ${'Register a group runner'}
- ${PROJECT_TYPE} | ${'Register a project runner'}
- `('Dropdown text for type $type is "$text"', () => {
- createComponent({ props: { type: INSTANCE_TYPE } }, mount);
-
- expect(wrapper.text()).toContain('Register an instance runner');
- });
-
- it('Passes attributes to the dropdown component', () => {
- createComponent({ attrs: { right: true } });
-
- expect(findDropdown().attributes()).toMatchObject({ right: 'true' });
- });
-
- describe('Instructions dropdown item', () => {
- it('Displays "Show runner" dropdown item', () => {
- createComponent();
-
- expect(findRegistrationInstructionsDropdownItem().text()).toBe(
- 'Show runner installation and registration instructions',
- );
- });
-
- describe('When the dropdown item is clicked', () => {
- beforeEach(async () => {
- createComponentWithModal({}, mount);
-
- await openModal();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('opens the modal with contents', () => {
- const modalText = findModalContent();
-
- expect(modalText).toContain('Install a runner');
-
- // Environment selector
- expect(modalText).toContain('Environment');
- expect(modalText).toContain('Linux macOS Windows Docker Kubernetes');
-
- // Architecture selector
- expect(modalText).toContain('Architecture');
- expect(modalText).toContain('amd64 amd64 386 arm arm64');
-
- expect(modalText).toContain('Download and install binary');
- });
- });
- });
-
- describe('Registration token', () => {
- it('Displays dropdown form for the registration token', () => {
- createComponent();
-
- expect(findTokenDropdownItem().exists()).toBe(true);
- });
-
- it('Displays masked value by default', () => {
- createComponent({}, mount);
-
- expect(findRegistrationTokenInput().element.value).toBe(maskToken);
- });
- });
-
- describe('Reset token item', () => {
- it('Displays registration token reset item', () => {
- createComponent();
-
- expect(findTokenResetDropdownItem().exists()).toBe(true);
- });
-
- it.each([INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE])('Set up token reset for %s', (type) => {
- createComponent({ props: { type } });
-
- expect(findTokenResetDropdownItem().props('type')).toBe(type);
- });
- });
-
- describe('When token is reset', () => {
- const newToken = 'mock1';
-
- const resetToken = async () => {
- findTokenResetDropdownItem().vm.$emit('tokenReset', newToken);
- await nextTick();
- };
-
- it('Updates token input', async () => {
- createComponent({}, mount);
-
- expect(findRegistrationToken().props('value')).not.toBe(newToken);
-
- await resetToken();
-
- expect(findRegistrationToken().props('value')).toBe(newToken);
- });
-
- it('Updates token in modal', async () => {
- createComponentWithModal({}, mount);
-
- await openModal();
-
- expect(findModalContent()).toContain(mockToken);
-
- await resetToken();
-
- expect(findModalContent()).toContain(newToken);
- });
- });
-});
diff --git a/spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js b/spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js
deleted file mode 100644
index 2510aaf0334..00000000000
--- a/spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js
+++ /dev/null
@@ -1,209 +0,0 @@
-import { GlDropdownItem, GlLoadingIcon, GlToast, GlModal } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import Vue, { nextTick } from 'vue';
-
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import RegistrationTokenResetDropdownItem from '~/runner/components/registration/registration_token_reset_dropdown_item.vue';
-import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
-import runnersRegistrationTokenResetMutation from '~/runner/graphql/list/runners_registration_token_reset.mutation.graphql';
-import { captureException } from '~/runner/sentry_utils';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-
-jest.mock('~/flash');
-jest.mock('~/runner/sentry_utils');
-
-Vue.use(VueApollo);
-Vue.use(GlToast);
-
-const mockNewToken = 'NEW_TOKEN';
-const modalID = 'token-reset-modal';
-
-describe('RegistrationTokenResetDropdownItem', () => {
- let wrapper;
- let runnersRegistrationTokenResetMutationHandler;
- let showToast;
-
- const mockEvent = { preventDefault: jest.fn() };
- const findDropdownItem = () => wrapper.findComponent(GlDropdownItem);
- const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- const findModal = () => wrapper.findComponent(GlModal);
- const clickSubmit = () => findModal().vm.$emit('primary', mockEvent);
-
- const createComponent = ({ props, provide = {} } = {}) => {
- wrapper = shallowMount(RegistrationTokenResetDropdownItem, {
- provide,
- propsData: {
- type: INSTANCE_TYPE,
- ...props,
- },
- apolloProvider: createMockApollo([
- [runnersRegistrationTokenResetMutation, runnersRegistrationTokenResetMutationHandler],
- ]),
- directives: {
- GlModal: createMockDirective(),
- },
- });
-
- showToast = wrapper.vm.$toast ? jest.spyOn(wrapper.vm.$toast, 'show') : null;
- };
-
- beforeEach(() => {
- runnersRegistrationTokenResetMutationHandler = jest.fn().mockResolvedValue({
- data: {
- runnersRegistrationTokenReset: {
- token: mockNewToken,
- errors: [],
- },
- },
- });
-
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays reset button', () => {
- expect(findDropdownItem().exists()).toBe(true);
- });
-
- describe('modal directive integration', () => {
- it('has the correct ID on the dropdown', () => {
- const binding = getBinding(findDropdownItem().element, 'gl-modal');
-
- expect(binding.value).toBe(modalID);
- });
-
- it('has the correct ID on the modal', () => {
- expect(findModal().props('modalId')).toBe(modalID);
- });
- });
-
- describe('On click and confirmation', () => {
- const mockGroupId = '11';
- const mockProjectId = '22';
-
- describe.each`
- type | provide | expectedInput
- ${INSTANCE_TYPE} | ${{}} | ${{ type: INSTANCE_TYPE }}
- ${GROUP_TYPE} | ${{ groupId: mockGroupId }} | ${{ type: GROUP_TYPE, id: `gid://gitlab/Group/${mockGroupId}` }}
- ${PROJECT_TYPE} | ${{ projectId: mockProjectId }} | ${{ type: PROJECT_TYPE, id: `gid://gitlab/Project/${mockProjectId}` }}
- `('Resets token of type $type', ({ type, provide, expectedInput }) => {
- beforeEach(async () => {
- createComponent({
- provide,
- props: { type },
- });
-
- findDropdownItem().trigger('click');
- clickSubmit();
- await waitForPromises();
- });
-
- it('resets token', () => {
- expect(runnersRegistrationTokenResetMutationHandler).toHaveBeenCalledTimes(1);
- expect(runnersRegistrationTokenResetMutationHandler).toHaveBeenCalledWith({
- input: expectedInput,
- });
- });
-
- it('emits result', () => {
- expect(wrapper.emitted('tokenReset')).toHaveLength(1);
- expect(wrapper.emitted('tokenReset')[0]).toEqual([mockNewToken]);
- });
-
- it('does not show a loading state', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('shows confirmation', () => {
- expect(showToast).toHaveBeenLastCalledWith(
- expect.stringContaining('registration token generated'),
- );
- });
- });
- });
-
- describe('On click without confirmation', () => {
- beforeEach(async () => {
- findDropdownItem().vm.$emit('click');
- await waitForPromises();
- });
-
- it('does not reset token', () => {
- expect(runnersRegistrationTokenResetMutationHandler).not.toHaveBeenCalled();
- });
-
- it('does not emit any result', () => {
- expect(wrapper.emitted('tokenReset')).toBeUndefined();
- });
-
- it('does not show a loading state', () => {
- expect(findLoadingIcon().exists()).toBe(false);
- });
-
- it('does not shows confirmation', () => {
- expect(showToast).not.toHaveBeenCalled();
- });
- });
-
- describe('On error', () => {
- it('On network error, error message is shown', async () => {
- const mockErrorMsg = 'Token reset failed!';
-
- runnersRegistrationTokenResetMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
-
- findDropdownItem().trigger('click');
- clickSubmit();
- await waitForPromises();
-
- expect(createAlert).toHaveBeenLastCalledWith({
- message: mockErrorMsg,
- });
- expect(captureException).toHaveBeenCalledWith({
- error: new Error(mockErrorMsg),
- component: 'RunnerRegistrationTokenReset',
- });
- });
-
- it('On validation error, error message is shown', async () => {
- const mockErrorMsg = 'User not allowed!';
- const mockErrorMsg2 = 'Type is not valid!';
-
- runnersRegistrationTokenResetMutationHandler.mockResolvedValue({
- data: {
- runnersRegistrationTokenReset: {
- token: null,
- errors: [mockErrorMsg, mockErrorMsg2],
- },
- },
- });
-
- findDropdownItem().trigger('click');
- clickSubmit();
- await waitForPromises();
-
- expect(createAlert).toHaveBeenLastCalledWith({
- message: `${mockErrorMsg} ${mockErrorMsg2}`,
- });
- expect(captureException).toHaveBeenCalledWith({
- error: new Error(`${mockErrorMsg} ${mockErrorMsg2}`),
- component: 'RunnerRegistrationTokenReset',
- });
- });
- });
-
- describe('Immediately after click', () => {
- it('shows loading state', async () => {
- findDropdownItem().trigger('click');
- clickSubmit();
- await nextTick();
-
- expect(findLoadingIcon().exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/runner/components/registration/registration_token_spec.js b/spec/frontend/runner/components/registration/registration_token_spec.js
deleted file mode 100644
index 19344a68f79..00000000000
--- a/spec/frontend/runner/components/registration/registration_token_spec.js
+++ /dev/null
@@ -1,62 +0,0 @@
-import { GlToast } from '@gitlab/ui';
-import Vue from 'vue';
-import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import RegistrationToken from '~/runner/components/registration/registration_token.vue';
-import InputCopyToggleVisibility from '~/vue_shared/components/form/input_copy_toggle_visibility.vue';
-
-const mockToken = '01234567890';
-const mockMasked = '***********';
-
-describe('RegistrationToken', () => {
- let wrapper;
- let showToast;
-
- Vue.use(GlToast);
-
- const findInputCopyToggleVisibility = () => wrapper.findComponent(InputCopyToggleVisibility);
-
- const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => {
- wrapper = mountFn(RegistrationToken, {
- propsData: {
- value: mockToken,
- inputId: 'token-value',
- ...props,
- },
- });
-
- showToast = wrapper.vm.$toast ? jest.spyOn(wrapper.vm.$toast, 'show') : null;
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays value and copy button', () => {
- createComponent();
-
- expect(findInputCopyToggleVisibility().props('value')).toBe(mockToken);
- expect(findInputCopyToggleVisibility().props('copyButtonTitle')).toBe(
- 'Copy registration token',
- );
- });
-
- // Component integration test to ensure secure masking
- it('Displays masked value by default', () => {
- createComponent({ mountFn: mountExtended });
-
- expect(wrapper.find('input').element.value).toBe(mockMasked);
- });
-
- describe('When the copy to clipboard button is clicked', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('shows a copied message', () => {
- findInputCopyToggleVisibility().vm.$emit('copy');
-
- expect(showToast).toHaveBeenCalledTimes(1);
- expect(showToast).toHaveBeenCalledWith('Registration token copied!');
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_assigned_item_spec.js b/spec/frontend/runner/components/runner_assigned_item_spec.js
deleted file mode 100644
index cc09046c000..00000000000
--- a/spec/frontend/runner/components/runner_assigned_item_spec.js
+++ /dev/null
@@ -1,68 +0,0 @@
-import { GlAvatar, GlBadge } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerAssignedItem from '~/runner/components/runner_assigned_item.vue';
-import { AVATAR_SHAPE_OPTION_RECT } from '~/vue_shared/constants';
-
-const mockHref = '/group/project';
-const mockName = 'Project';
-const mockDescription = 'Project description';
-const mockFullName = 'Group / Project';
-const mockAvatarUrl = '/avatar.png';
-
-describe('RunnerAssignedItem', () => {
- let wrapper;
-
- const findAvatar = () => wrapper.findByTestId('item-avatar');
- const findBadge = () => wrapper.findComponent(GlBadge);
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMountExtended(RunnerAssignedItem, {
- propsData: {
- href: mockHref,
- name: mockName,
- fullName: mockFullName,
- avatarUrl: mockAvatarUrl,
- description: mockDescription,
- ...props,
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Shows an avatar', () => {
- const avatar = findAvatar();
-
- expect(avatar.attributes('href')).toBe(mockHref);
- expect(avatar.findComponent(GlAvatar).props()).toMatchObject({
- alt: mockName,
- entityName: mockName,
- src: mockAvatarUrl,
- shape: AVATAR_SHAPE_OPTION_RECT,
- size: 48,
- });
- });
-
- it('Shows an item link', () => {
- const groupFullName = wrapper.findByText(mockFullName);
-
- expect(groupFullName.attributes('href')).toBe(mockHref);
- });
-
- it('Shows description', () => {
- expect(wrapper.text()).toContain(mockDescription);
- });
-
- it('Shows owner badge', () => {
- createComponent({ props: { isOwner: true } });
-
- expect(findBadge().text()).toBe(s__('Runner|Owner'));
- });
-});
diff --git a/spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js b/spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js
deleted file mode 100644
index 424a4e61ccd..00000000000
--- a/spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import Vue from 'vue';
-import { GlFormCheckbox } from '@gitlab/ui';
-import VueApollo from 'vue-apollo';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerBulkDeleteCheckbox from '~/runner/components/runner_bulk_delete_checkbox.vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { createLocalState } from '~/runner/graphql/list/local_state';
-
-Vue.use(VueApollo);
-
-const makeRunner = (id, deleteRunner = true) => ({
- id,
- userPermissions: { deleteRunner },
-});
-
-// Multi-select checkbox possible states:
-const stateToAttrs = {
- unchecked: { disabled: undefined, checked: undefined, indeterminate: undefined },
- checked: { disabled: undefined, checked: 'true', indeterminate: undefined },
- indeterminate: { disabled: undefined, checked: undefined, indeterminate: 'true' },
- disabled: { disabled: 'true', checked: undefined, indeterminate: undefined },
-};
-
-describe('RunnerBulkDeleteCheckbox', () => {
- let wrapper;
- let mockState;
- let mockCheckedRunnerIds;
-
- const findCheckbox = () => wrapper.findComponent(GlFormCheckbox);
-
- const expectCheckboxToBe = (state) => {
- const expected = stateToAttrs[state];
- expect(findCheckbox().attributes('disabled')).toBe(expected.disabled);
- expect(findCheckbox().attributes('checked')).toBe(expected.checked);
- expect(findCheckbox().attributes('indeterminate')).toBe(expected.indeterminate);
- };
-
- const createComponent = ({ runners = [] } = {}) => {
- const { cacheConfig, localMutations } = mockState;
- const apolloProvider = createMockApollo(undefined, undefined, cacheConfig);
-
- wrapper = shallowMountExtended(RunnerBulkDeleteCheckbox, {
- apolloProvider,
- provide: {
- localMutations,
- },
- propsData: {
- runners,
- },
- });
- };
-
- beforeEach(() => {
- mockState = createLocalState();
-
- jest
- .spyOn(mockState.cacheConfig.typePolicies.Query.fields, 'checkedRunnerIds')
- .mockImplementation(() => mockCheckedRunnerIds);
-
- jest.spyOn(mockState.localMutations, 'setRunnersChecked');
- });
-
- describe('when all runners can be deleted', () => {
- const mockIds = ['1', '2', '3'];
- const mockIdAnotherPage = '4';
- const mockRunners = mockIds.map((id) => makeRunner(id));
-
- it.each`
- case | checkedRunnerIds | state
- ${'no runners'} | ${[]} | ${'unchecked'}
- ${'no runners in this page'} | ${[mockIdAnotherPage]} | ${'unchecked'}
- ${'all runners'} | ${mockIds} | ${'checked'}
- ${'some runners'} | ${[mockIds[0]]} | ${'indeterminate'}
- ${'all plus other runners'} | ${[...mockIds, mockIdAnotherPage]} | ${'checked'}
- `('if $case are checked, checkbox is $state', ({ checkedRunnerIds, state }) => {
- mockCheckedRunnerIds = checkedRunnerIds;
-
- createComponent({ runners: mockRunners });
- expectCheckboxToBe(state);
- });
- });
-
- describe('when some runners cannot be deleted', () => {
- it('all allowed runners are selected, checkbox is checked', () => {
- mockCheckedRunnerIds = ['a', 'b', 'c'];
- createComponent({
- runners: [makeRunner('a'), makeRunner('b'), makeRunner('c', false)],
- });
-
- expectCheckboxToBe('checked');
- });
-
- it('some allowed runners are selected, checkbox is indeterminate', () => {
- mockCheckedRunnerIds = ['a', 'b'];
- createComponent({
- runners: [makeRunner('a'), makeRunner('b'), makeRunner('c')],
- });
-
- expectCheckboxToBe('indeterminate');
- });
-
- it('no allowed runners are selected, checkbox is disabled', () => {
- mockCheckedRunnerIds = ['a', 'b'];
- createComponent({
- runners: [makeRunner('a', false), makeRunner('b', false)],
- });
-
- expectCheckboxToBe('disabled');
- });
- });
-
- describe('When user selects', () => {
- const mockRunners = [makeRunner('1'), makeRunner('2')];
-
- beforeEach(() => {
- mockCheckedRunnerIds = ['1', '2'];
- createComponent({ runners: mockRunners });
- });
-
- it.each([[true], [false]])('sets checked to %s', (checked) => {
- findCheckbox().vm.$emit('change', checked);
-
- expect(mockState.localMutations.setRunnersChecked).toHaveBeenCalledTimes(1);
- expect(mockState.localMutations.setRunnersChecked).toHaveBeenCalledWith({
- isChecked: checked,
- runners: mockRunners,
- });
- });
- });
-
- describe('When runners are loading', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('is disabled', () => {
- expectCheckboxToBe('disabled');
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_bulk_delete_spec.js b/spec/frontend/runner/components/runner_bulk_delete_spec.js
deleted file mode 100644
index 6df918c684f..00000000000
--- a/spec/frontend/runner/components/runner_bulk_delete_spec.js
+++ /dev/null
@@ -1,243 +0,0 @@
-import Vue from 'vue';
-import { GlModal, GlSprintf } from '@gitlab/ui';
-import VueApollo from 'vue-apollo';
-import { createAlert } from '~/flash';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { s__ } from '~/locale';
-import RunnerBulkDelete from '~/runner/components/runner_bulk_delete.vue';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import BulkRunnerDeleteMutation from '~/runner/graphql/list/bulk_runner_delete.mutation.graphql';
-import { createLocalState } from '~/runner/graphql/list/local_state';
-import waitForPromises from 'helpers/wait_for_promises';
-import { allRunnersData } from '../mock_data';
-
-Vue.use(VueApollo);
-
-jest.mock('~/flash');
-
-describe('RunnerBulkDelete', () => {
- let wrapper;
- let apolloCache;
- let mockState;
- let mockCheckedRunnerIds;
-
- const findClearBtn = () => wrapper.findByText(s__('Runners|Clear selection'));
- const findDeleteBtn = () => wrapper.findByText(s__('Runners|Delete selected'));
- const findModal = () => wrapper.findComponent(GlModal);
-
- const mockRunners = allRunnersData.data.runners.nodes;
- const mockId1 = allRunnersData.data.runners.nodes[0].id;
- const mockId2 = allRunnersData.data.runners.nodes[1].id;
-
- const bulkRunnerDeleteHandler = jest.fn();
-
- const createComponent = () => {
- const { cacheConfig, localMutations } = mockState;
- const apolloProvider = createMockApollo(
- [[BulkRunnerDeleteMutation, bulkRunnerDeleteHandler]],
- undefined,
- cacheConfig,
- );
-
- wrapper = shallowMountExtended(RunnerBulkDelete, {
- apolloProvider,
- provide: {
- localMutations,
- },
- propsData: {
- runners: mockRunners,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- stubs: {
- GlSprintf,
- GlModal,
- },
- });
-
- apolloCache = apolloProvider.defaultClient.cache;
- jest.spyOn(apolloCache, 'evict');
- jest.spyOn(apolloCache, 'gc');
- };
-
- beforeEach(() => {
- mockState = createLocalState();
-
- jest
- .spyOn(mockState.cacheConfig.typePolicies.Query.fields, 'checkedRunnerIds')
- .mockImplementation(() => mockCheckedRunnerIds);
- });
-
- afterEach(() => {
- bulkRunnerDeleteHandler.mockReset();
- wrapper.destroy();
- });
-
- describe('When no runners are checked', () => {
- beforeEach(async () => {
- mockCheckedRunnerIds = [];
-
- createComponent();
-
- await waitForPromises();
- });
-
- it('shows no contents', () => {
- expect(wrapper.html()).toBe('');
- });
- });
-
- describe.each`
- count | ids | text
- ${1} | ${[mockId1]} | ${'1 runner'}
- ${2} | ${[mockId1, mockId2]} | ${'2 runners'}
- `('When $count runner(s) are checked', ({ ids, text }) => {
- beforeEach(() => {
- mockCheckedRunnerIds = ids;
-
- createComponent();
-
- jest.spyOn(mockState.localMutations, 'clearChecked').mockImplementation(() => {});
- });
-
- it(`shows "${text}"`, () => {
- expect(wrapper.text()).toContain(text);
- });
-
- it('clears selection', () => {
- expect(mockState.localMutations.clearChecked).toHaveBeenCalledTimes(0);
-
- findClearBtn().vm.$emit('click');
-
- expect(mockState.localMutations.clearChecked).toHaveBeenCalledTimes(1);
- });
-
- it('shows confirmation modal', () => {
- const modalId = getBinding(findDeleteBtn().element, 'gl-modal');
-
- expect(findModal().props('modal-id')).toBe(modalId);
- expect(findModal().text()).toContain(text);
- });
- });
-
- describe('when runners are deleted', () => {
- let evt;
- let mockHideModal;
-
- beforeEach(() => {
- mockCheckedRunnerIds = [mockId1, mockId2];
-
- createComponent();
-
- jest.spyOn(mockState.localMutations, 'clearChecked').mockImplementation(() => {});
- mockHideModal = jest.spyOn(findModal().vm, 'hide');
- });
-
- describe('when deletion is successful', () => {
- beforeEach(() => {
- bulkRunnerDeleteHandler.mockResolvedValue({
- data: {
- bulkRunnerDelete: { deletedIds: mockCheckedRunnerIds, errors: [] },
- },
- });
-
- evt = {
- preventDefault: jest.fn(),
- };
- findModal().vm.$emit('primary', evt);
- });
-
- it('has loading state', async () => {
- expect(findModal().props('actionPrimary').attributes.loading).toBe(true);
- expect(findModal().props('actionCancel').attributes.loading).toBe(true);
-
- await waitForPromises();
-
- expect(findModal().props('actionPrimary').attributes.loading).toBe(false);
- expect(findModal().props('actionCancel').attributes.loading).toBe(false);
- });
-
- it('modal is not prevented from closing', () => {
- expect(evt.preventDefault).toHaveBeenCalledTimes(1);
- });
-
- it('mutation is called', async () => {
- expect(bulkRunnerDeleteHandler).toHaveBeenCalledWith({
- input: { ids: mockCheckedRunnerIds },
- });
- });
-
- it('user interface is updated', async () => {
- const { evict, gc } = apolloCache;
-
- expect(evict).toHaveBeenCalledTimes(mockCheckedRunnerIds.length);
- expect(evict).toHaveBeenCalledWith({
- id: expect.stringContaining(mockCheckedRunnerIds[0]),
- });
- expect(evict).toHaveBeenCalledWith({
- id: expect.stringContaining(mockCheckedRunnerIds[1]),
- });
-
- expect(gc).toHaveBeenCalledTimes(1);
- });
-
- it('modal is hidden', () => {
- expect(mockHideModal).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('when deletion fails', () => {
- beforeEach(() => {
- bulkRunnerDeleteHandler.mockRejectedValue(new Error('error!'));
-
- evt = {
- preventDefault: jest.fn(),
- };
- findModal().vm.$emit('primary', evt);
- });
-
- it('has loading state', async () => {
- expect(findModal().props('actionPrimary').attributes.loading).toBe(true);
- expect(findModal().props('actionCancel').attributes.loading).toBe(true);
-
- await waitForPromises();
-
- expect(findModal().props('actionPrimary').attributes.loading).toBe(false);
- expect(findModal().props('actionCancel').attributes.loading).toBe(false);
- });
-
- it('modal is not prevented from closing', () => {
- expect(evt.preventDefault).toHaveBeenCalledTimes(1);
- });
-
- it('mutation is called', () => {
- expect(bulkRunnerDeleteHandler).toHaveBeenCalledWith({
- input: { ids: mockCheckedRunnerIds },
- });
- });
-
- it('user interface is not updated', async () => {
- await waitForPromises();
-
- const { evict, gc } = apolloCache;
-
- expect(evict).not.toHaveBeenCalled();
- expect(gc).not.toHaveBeenCalled();
- expect(mockState.localMutations.clearChecked).not.toHaveBeenCalled();
- });
-
- it('alert is called', async () => {
- await waitForPromises();
-
- expect(createAlert).toHaveBeenCalled();
- expect(createAlert).toHaveBeenCalledWith({
- message: expect.any(String),
- captureError: true,
- error: expect.any(Error),
- });
- });
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_delete_button_spec.js b/spec/frontend/runner/components/runner_delete_button_spec.js
deleted file mode 100644
index c8fb7a69379..00000000000
--- a/spec/frontend/runner/components/runner_delete_button_spec.js
+++ /dev/null
@@ -1,266 +0,0 @@
-import Vue from 'vue';
-import { GlButton } from '@gitlab/ui';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
-import runnerDeleteMutation from '~/runner/graphql/shared/runner_delete.mutation.graphql';
-import waitForPromises from 'helpers/wait_for_promises';
-import { captureException } from '~/runner/sentry_utils';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { createAlert } from '~/flash';
-import { I18N_DELETE_RUNNER } from '~/runner/constants';
-
-import RunnerDeleteButton from '~/runner/components/runner_delete_button.vue';
-import RunnerDeleteModal from '~/runner/components/runner_delete_modal.vue';
-import { allRunnersData } from '../mock_data';
-
-const mockRunner = allRunnersData.data.runners.nodes[0];
-const mockRunnerId = getIdFromGraphQLId(mockRunner.id);
-
-Vue.use(VueApollo);
-
-jest.mock('~/flash');
-jest.mock('~/runner/sentry_utils');
-
-describe('RunnerDeleteButton', () => {
- let wrapper;
- let apolloProvider;
- let apolloCache;
- let runnerDeleteHandler;
-
- const findBtn = () => wrapper.findComponent(GlButton);
- const findModal = () => wrapper.findComponent(RunnerDeleteModal);
-
- const getTooltip = () => getBinding(wrapper.element, 'gl-tooltip').value;
- const getModal = () => getBinding(findBtn().element, 'gl-modal').value;
-
- const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => {
- const { runner, ...propsData } = props;
-
- wrapper = mountFn(RunnerDeleteButton, {
- propsData: {
- runner: {
- // We need typename so that cache.identify works
- // eslint-disable-next-line no-underscore-dangle
- __typename: mockRunner.__typename,
- id: mockRunner.id,
- shortSha: mockRunner.shortSha,
- ...runner,
- },
- ...propsData,
- },
- apolloProvider,
- directives: {
- GlTooltip: createMockDirective(),
- GlModal: createMockDirective(),
- },
- });
- };
-
- const clickOkAndWait = async () => {
- findModal().vm.$emit('primary');
- await waitForPromises();
- };
-
- beforeEach(() => {
- runnerDeleteHandler = jest.fn().mockImplementation(() => {
- return Promise.resolve({
- data: {
- runnerDelete: {
- errors: [],
- },
- },
- });
- });
- apolloProvider = createMockApollo([[runnerDeleteMutation, runnerDeleteHandler]]);
- apolloCache = apolloProvider.defaultClient.cache;
-
- jest.spyOn(apolloCache, 'evict');
- jest.spyOn(apolloCache, 'gc');
-
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays a delete button without an icon', () => {
- expect(findBtn().props()).toMatchObject({
- loading: false,
- icon: '',
- });
- expect(findBtn().classes('btn-icon')).toBe(false);
- expect(findBtn().text()).toBe(I18N_DELETE_RUNNER);
- });
-
- it('Displays a modal with the runner name', () => {
- expect(findModal().props('runnerName')).toBe(`#${mockRunnerId} (${mockRunner.shortSha})`);
- });
-
- it('Does not have tabindex when button is enabled', () => {
- expect(wrapper.attributes('tabindex')).toBeUndefined();
- });
-
- it('Displays a modal when clicked', () => {
- const modalId = `delete-runner-modal-${mockRunnerId}`;
-
- expect(getModal()).toBe(modalId);
- expect(findModal().attributes('modal-id')).toBe(modalId);
- });
-
- it('Does not display redundant text for screen readers', () => {
- expect(findBtn().attributes('aria-label')).toBe(undefined);
- });
-
- it('Passes other attributes to the button', () => {
- createComponent({ props: { category: 'secondary' } });
-
- expect(findBtn().props('category')).toBe('secondary');
- });
-
- describe(`Before the delete button is clicked`, () => {
- it('The mutation has not been called', () => {
- expect(runnerDeleteHandler).toHaveBeenCalledTimes(0);
- });
- });
-
- describe('Immediately after the delete button is clicked', () => {
- beforeEach(async () => {
- findModal().vm.$emit('primary');
- });
-
- it('The button has a loading state', async () => {
- expect(findBtn().props('loading')).toBe(true);
- });
-
- it('The stale tooltip is removed', async () => {
- expect(getTooltip()).toBe('');
- });
- });
-
- describe('After clicking on the delete button', () => {
- beforeEach(async () => {
- await clickOkAndWait();
- });
-
- it('The mutation to delete is called', () => {
- expect(runnerDeleteHandler).toHaveBeenCalledTimes(1);
- expect(runnerDeleteHandler).toHaveBeenCalledWith({
- input: {
- id: mockRunner.id,
- },
- });
- });
-
- it('The user can be notified with an event', () => {
- const deleted = wrapper.emitted('deleted');
-
- expect(deleted).toHaveLength(1);
- expect(deleted[0][0].message).toMatch(`#${mockRunnerId}`);
- expect(deleted[0][0].message).toMatch(`${mockRunner.shortSha}`);
- });
-
- it('evicts runner from apollo cache', () => {
- expect(apolloCache.evict).toHaveBeenCalledWith({
- id: apolloCache.identify(mockRunner),
- });
- expect(apolloCache.gc).toHaveBeenCalled();
- });
- });
-
- describe('When update fails', () => {
- describe('On a network error', () => {
- const mockErrorMsg = 'Update error!';
-
- beforeEach(async () => {
- runnerDeleteHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
-
- await clickOkAndWait();
- });
-
- it('error is reported to sentry', () => {
- expect(captureException).toHaveBeenCalledWith({
- error: new Error(mockErrorMsg),
- component: 'RunnerDeleteButton',
- });
- });
-
- it('error is shown to the user', () => {
- expect(createAlert).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('On a validation error', () => {
- const mockErrorMsg = 'Runner not found!';
- const mockErrorMsg2 = 'User not allowed!';
-
- beforeEach(async () => {
- runnerDeleteHandler.mockResolvedValueOnce({
- data: {
- runnerDelete: {
- errors: [mockErrorMsg, mockErrorMsg2],
- },
- },
- });
-
- await clickOkAndWait();
- });
-
- it('error is reported to sentry', () => {
- expect(captureException).toHaveBeenCalledWith({
- error: new Error(`${mockErrorMsg} ${mockErrorMsg2}`),
- component: 'RunnerDeleteButton',
- });
- });
-
- it('error is shown to the user', () => {
- expect(createAlert).toHaveBeenCalledTimes(1);
- });
-
- it('does not evict runner from apollo cache', () => {
- expect(apolloCache.evict).not.toHaveBeenCalled();
- expect(apolloCache.gc).not.toHaveBeenCalled();
- });
- });
- });
-
- describe('When displaying a compact button for an active runner', () => {
- beforeEach(() => {
- createComponent({
- props: {
- runner: {
- active: true,
- },
- compact: true,
- },
- mountFn: mountExtended,
- });
- });
-
- it('Displays no text', () => {
- expect(findBtn().text()).toBe('');
- expect(findBtn().classes('btn-icon')).toBe(true);
- });
-
- it('Display correctly for screen readers', () => {
- expect(findBtn().attributes('aria-label')).toBe(I18N_DELETE_RUNNER);
- expect(getTooltip()).toBe(I18N_DELETE_RUNNER);
- });
-
- describe('Immediately after the button is clicked', () => {
- beforeEach(async () => {
- findModal().vm.$emit('primary');
- });
-
- it('The button has a loading state', async () => {
- expect(findBtn().props('loading')).toBe(true);
- });
-
- it('The stale tooltip is removed', async () => {
- expect(getTooltip()).toBe('');
- });
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_delete_modal_spec.js b/spec/frontend/runner/components/runner_delete_modal_spec.js
deleted file mode 100644
index 3e5b634d815..00000000000
--- a/spec/frontend/runner/components/runner_delete_modal_spec.js
+++ /dev/null
@@ -1,60 +0,0 @@
-import { GlModal } from '@gitlab/ui';
-import { mount, shallowMount } from '@vue/test-utils';
-import RunnerDeleteModal from '~/runner/components/runner_delete_modal.vue';
-
-describe('RunnerDeleteModal', () => {
- let wrapper;
-
- const findGlModal = () => wrapper.findComponent(GlModal);
-
- const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => {
- wrapper = mountFn(RunnerDeleteModal, {
- attachTo: document.body,
- propsData: {
- runnerName: '#99 (AABBCCDD)',
- ...props,
- },
- attrs: {
- modalId: 'delete-runner-modal-99',
- },
- });
- };
-
- it('Displays title', () => {
- createComponent();
-
- expect(findGlModal().props('title')).toBe('Delete runner #99 (AABBCCDD)?');
- });
-
- it('Displays buttons', () => {
- createComponent();
-
- expect(findGlModal().props('actionPrimary')).toMatchObject({ text: 'Delete runner' });
- expect(findGlModal().props('actionCancel')).toMatchObject({ text: 'Cancel' });
- });
-
- it('Displays contents', () => {
- createComponent();
-
- expect(findGlModal().html()).toContain(
- 'The runner will be permanently deleted and no longer available for projects or groups in the instance. Are you sure you want to continue?',
- );
- });
-
- describe('When modal is confirmed by the user', () => {
- let hideModalSpy;
-
- beforeEach(() => {
- createComponent({}, mount);
- hideModalSpy = jest.spyOn(wrapper.vm.$refs.modal, 'hide').mockImplementation(() => {});
- });
-
- it('Modal gets hidden', () => {
- expect(hideModalSpy).toHaveBeenCalledTimes(0);
-
- findGlModal().vm.$emit('primary');
-
- expect(hideModalSpy).toHaveBeenCalledTimes(1);
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_details_spec.js b/spec/frontend/runner/components/runner_details_spec.js
deleted file mode 100644
index e6cc936e260..00000000000
--- a/spec/frontend/runner/components/runner_details_spec.js
+++ /dev/null
@@ -1,130 +0,0 @@
-import { GlSprintf, GlIntersperse } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-import { useFakeDate } from 'helpers/fake_date';
-import { findDd } from 'helpers/dl_locator_helper';
-import { ACCESS_LEVEL_REF_PROTECTED, ACCESS_LEVEL_NOT_PROTECTED } from '~/runner/constants';
-
-import RunnerDetails from '~/runner/components/runner_details.vue';
-import RunnerDetail from '~/runner/components/runner_detail.vue';
-import RunnerGroups from '~/runner/components/runner_groups.vue';
-import RunnerTags from '~/runner/components/runner_tags.vue';
-import RunnerTag from '~/runner/components/runner_tag.vue';
-
-import { runnerData, runnerWithGroupData } from '../mock_data';
-
-const mockRunner = runnerData.data.runner;
-const mockGroupRunner = runnerWithGroupData.data.runner;
-
-describe('RunnerDetails', () => {
- let wrapper;
- const mockNow = '2021-01-15T12:00:00Z';
- const mockOneHourAgo = '2021-01-15T11:00:00Z';
-
- useFakeDate(mockNow);
-
- const findDetailGroups = () => wrapper.findComponent(RunnerGroups);
-
- const createComponent = ({ props = {}, stubs, mountFn = shallowMountExtended } = {}) => {
- wrapper = mountFn(RunnerDetails, {
- propsData: {
- ...props,
- },
- stubs: {
- RunnerDetail,
- ...stubs,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('Details tab', () => {
- describe.each`
- field | runner | expectedValue
- ${'Description'} | ${{ description: 'My runner' }} | ${'My runner'}
- ${'Description'} | ${{ description: null }} | ${'None'}
- ${'Last contact'} | ${{ contactedAt: mockOneHourAgo }} | ${'1 hour ago'}
- ${'Last contact'} | ${{ contactedAt: null }} | ${'Never contacted'}
- ${'Version'} | ${{ version: '12.3' }} | ${'12.3'}
- ${'Version'} | ${{ version: null }} | ${'None'}
- ${'Executor'} | ${{ executorName: 'shell' }} | ${'shell'}
- ${'Architecture'} | ${{ architectureName: 'amd64' }} | ${'amd64'}
- ${'Platform'} | ${{ platformName: 'darwin' }} | ${'darwin'}
- ${'IP Address'} | ${{ ipAddress: '127.0.0.1' }} | ${'127.0.0.1'}
- ${'IP Address'} | ${{ ipAddress: null }} | ${'None'}
- ${'Configuration'} | ${{ accessLevel: ACCESS_LEVEL_REF_PROTECTED, runUntagged: true }} | ${'Protected, Runs untagged jobs'}
- ${'Configuration'} | ${{ accessLevel: ACCESS_LEVEL_REF_PROTECTED, runUntagged: false }} | ${'Protected'}
- ${'Configuration'} | ${{ accessLevel: ACCESS_LEVEL_NOT_PROTECTED, runUntagged: true }} | ${'Runs untagged jobs'}
- ${'Configuration'} | ${{ accessLevel: ACCESS_LEVEL_NOT_PROTECTED, runUntagged: false }} | ${'None'}
- ${'Maximum job timeout'} | ${{ maximumTimeout: null }} | ${'None'}
- ${'Maximum job timeout'} | ${{ maximumTimeout: 0 }} | ${'0 seconds'}
- ${'Maximum job timeout'} | ${{ maximumTimeout: 59 }} | ${'59 seconds'}
- ${'Maximum job timeout'} | ${{ maximumTimeout: 10 * 60 + 5 }} | ${'10 minutes 5 seconds'}
- ${'Token expiry'} | ${{ tokenExpiresAt: mockOneHourAgo }} | ${'1 hour ago'}
- ${'Token expiry'} | ${{ tokenExpiresAt: null }} | ${'Never expires'}
- `('"$field" field', ({ field, runner, expectedValue }) => {
- beforeEach(() => {
- createComponent({
- props: {
- runner: {
- ...mockRunner,
- ...runner,
- },
- },
- stubs: {
- GlIntersperse,
- GlSprintf,
- TimeAgo,
- },
- });
- });
-
- it(`displays expected value "${expectedValue}"`, () => {
- expect(findDd(field, wrapper).text()).toBe(expectedValue);
- });
- });
-
- describe('"Tags" field', () => {
- const stubs = { RunnerTags, RunnerTag };
-
- it('displays expected value "tag-1 tag-2"', () => {
- createComponent({
- props: {
- runner: { ...mockRunner, tagList: ['tag-1', 'tag-2'] },
- },
- stubs,
- });
-
- expect(findDd('Tags', wrapper).text().replace(/\s+/g, ' ')).toBe('tag-1 tag-2');
- });
-
- it('displays "None" when runner has no tags', () => {
- createComponent({
- props: {
- runner: { ...mockRunner, tagList: [] },
- },
- stubs,
- });
-
- expect(findDd('Tags', wrapper).text().replace(/\s+/g, ' ')).toBe('None');
- });
- });
-
- describe('Group runners', () => {
- beforeEach(() => {
- createComponent({
- props: {
- runner: mockGroupRunner,
- },
- });
- });
-
- it('Shows a group runner details', () => {
- expect(findDetailGroups().props('runner')).toEqual(mockGroupRunner);
- });
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_edit_button_spec.js b/spec/frontend/runner/components/runner_edit_button_spec.js
deleted file mode 100644
index 428c1ef07e9..00000000000
--- a/spec/frontend/runner/components/runner_edit_button_spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { shallowMount, mount } from '@vue/test-utils';
-import RunnerEditButton from '~/runner/components/runner_edit_button.vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-
-describe('RunnerEditButton', () => {
- let wrapper;
-
- const getTooltipValue = () => getBinding(wrapper.element, 'gl-tooltip').value;
-
- const createComponent = ({ attrs = {}, mountFn = shallowMount } = {}) => {
- wrapper = mountFn(RunnerEditButton, {
- attrs,
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays Edit text', () => {
- expect(wrapper.attributes('aria-label')).toBe('Edit');
- });
-
- it('Displays Edit tooltip', () => {
- expect(getTooltipValue()).toBe('Edit');
- });
-
- it('Renders a link and adds an href attribute', () => {
- createComponent({ attrs: { href: '/edit' }, mountFn: mount });
-
- expect(wrapper.element.tagName).toBe('A');
- expect(wrapper.attributes('href')).toBe('/edit');
- });
-});
diff --git a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
deleted file mode 100644
index c92e19f9263..00000000000
--- a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
+++ /dev/null
@@ -1,188 +0,0 @@
-import { GlFilteredSearch, GlDropdown, GlDropdownItem } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue';
-import { statusTokenConfig } from '~/runner/components/search_tokens/status_token_config';
-import TagToken from '~/runner/components/search_tokens/tag_token.vue';
-import { tagTokenConfig } from '~/runner/components/search_tokens/tag_token_config';
-import {
- PARAM_KEY_STATUS,
- PARAM_KEY_TAG,
- STATUS_ONLINE,
- INSTANCE_TYPE,
- DEFAULT_MEMBERSHIP,
- DEFAULT_SORT,
- CONTACTED_DESC,
-} from '~/runner/constants';
-import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import BaseToken from '~/vue_shared/components/filtered_search_bar/tokens/base_token.vue';
-
-const mockSearch = {
- runnerType: null,
- membership: DEFAULT_MEMBERSHIP,
- filters: [],
- pagination: { page: 1 },
- sort: DEFAULT_SORT,
-};
-
-describe('RunnerList', () => {
- let wrapper;
-
- const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
- const findGlFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
- const findSortOptions = () => wrapper.findAllComponents(GlDropdownItem);
-
- const mockOtherSort = CONTACTED_DESC;
- const mockFilters = [
- { type: PARAM_KEY_STATUS, value: { data: STATUS_ONLINE, operator: '=' } },
- { type: 'filtered-search-term', value: { data: '' } },
- ];
-
- const expectToHaveLastEmittedInput = (value) => {
- const inputs = wrapper.emitted('input');
- expect(inputs[inputs.length - 1][0]).toEqual(value);
- };
-
- const createComponent = ({ props = {}, options = {} } = {}) => {
- wrapper = shallowMountExtended(RunnerFilteredSearchBar, {
- propsData: {
- namespace: 'runners',
- tokens: [],
- value: mockSearch,
- ...props,
- },
- stubs: {
- FilteredSearch,
- GlFilteredSearch,
- GlDropdown,
- GlDropdownItem,
- },
- ...options,
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('binds a namespace to the filtered search', () => {
- expect(findFilteredSearch().props('namespace')).toBe('runners');
- });
-
- it('sets sorting options', () => {
- const SORT_OPTIONS_COUNT = 2;
-
- expect(findSortOptions()).toHaveLength(SORT_OPTIONS_COUNT);
- expect(findSortOptions().at(0).text()).toBe('Created date');
- expect(findSortOptions().at(1).text()).toBe('Last contact');
- });
-
- it('sets tokens to the filtered search', () => {
- createComponent({
- props: {
- tokens: [statusTokenConfig, tagTokenConfig],
- },
- });
-
- expect(findFilteredSearch().props('tokens')).toEqual([
- expect.objectContaining({
- type: PARAM_KEY_STATUS,
- token: BaseToken,
- options: expect.any(Array),
- }),
- expect.objectContaining({
- type: PARAM_KEY_TAG,
- token: TagToken,
- }),
- ]);
- });
-
- it('can be configured with null or undefined tokens, which are ignored', () => {
- createComponent({
- props: {
- tokens: [statusTokenConfig, null, undefined],
- },
- });
-
- expect(findFilteredSearch().props('tokens')).toEqual([statusTokenConfig]);
- });
-
- it('fails validation for v-model with the wrong shape', () => {
- expect(() => {
- createComponent({ props: { value: { filters: 'wrong_filters', sort: 'sort' } } });
- }).toThrow('Invalid prop: custom validator check failed');
-
- expect(() => {
- createComponent({ props: { value: { sort: 'sort' } } });
- }).toThrow('Invalid prop: custom validator check failed');
- });
-
- describe('when a search is preselected', () => {
- beforeEach(() => {
- createComponent({
- props: {
- value: {
- runnerType: INSTANCE_TYPE,
- membership: DEFAULT_MEMBERSHIP,
- sort: mockOtherSort,
- filters: mockFilters,
- },
- },
- });
- });
-
- it('filter values are shown', () => {
- expect(findGlFilteredSearch().props('value')).toMatchObject(mockFilters);
- });
-
- it('sort option is selected', () => {
- expect(
- findSortOptions()
- .filter((w) => w.props('isChecked'))
- .at(0)
- .text(),
- ).toEqual('Last contact');
- });
-
- it('when the user sets a filter, the "search" preserves the other filters', () => {
- findGlFilteredSearch().vm.$emit('input', mockFilters);
- findGlFilteredSearch().vm.$emit('submit');
-
- expectToHaveLastEmittedInput({
- runnerType: INSTANCE_TYPE,
- membership: DEFAULT_MEMBERSHIP,
- filters: mockFilters,
- sort: mockOtherSort,
- pagination: {},
- });
- });
- });
-
- it('when the user sets a filter, the "search" is emitted with filters', () => {
- findGlFilteredSearch().vm.$emit('input', mockFilters);
- findGlFilteredSearch().vm.$emit('submit');
-
- expectToHaveLastEmittedInput({
- runnerType: null,
- membership: DEFAULT_MEMBERSHIP,
- filters: mockFilters,
- sort: DEFAULT_SORT,
- pagination: {},
- });
- });
-
- it('when the user sets a sorting method, the "search" is emitted with the sort', () => {
- findSortOptions().at(1).vm.$emit('click');
-
- expectToHaveLastEmittedInput({
- runnerType: null,
- membership: DEFAULT_MEMBERSHIP,
- filters: [],
- sort: mockOtherSort,
- pagination: {},
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_groups_spec.js b/spec/frontend/runner/components/runner_groups_spec.js
deleted file mode 100644
index b83733b9972..00000000000
--- a/spec/frontend/runner/components/runner_groups_spec.js
+++ /dev/null
@@ -1,67 +0,0 @@
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-
-import RunnerGroups from '~/runner/components/runner_groups.vue';
-import RunnerAssignedItem from '~/runner/components/runner_assigned_item.vue';
-
-import { runnerData, runnerWithGroupData } from '../mock_data';
-
-const mockInstanceRunner = runnerData.data.runner;
-const mockGroupRunner = runnerWithGroupData.data.runner;
-const mockGroup = mockGroupRunner.groups.nodes[0];
-
-describe('RunnerGroups', () => {
- let wrapper;
-
- const findHeading = () => wrapper.find('h3');
- const findRunnerAssignedItems = () => wrapper.findAllComponents(RunnerAssignedItem);
-
- const createComponent = ({ runner = mockGroupRunner, mountFn = shallowMountExtended } = {}) => {
- wrapper = mountFn(RunnerGroups, {
- propsData: {
- runner,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Shows a heading', () => {
- createComponent();
-
- expect(findHeading().text()).toBe('Assigned Group');
- });
-
- describe('When there is a group runner', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('Shows a project', () => {
- createComponent();
-
- const item = findRunnerAssignedItems().at(0);
- const { webUrl, name, fullName, avatarUrl } = mockGroup;
-
- expect(item.props()).toMatchObject({
- href: webUrl,
- name,
- fullName,
- avatarUrl,
- });
- });
- });
-
- describe('When there are no groups', () => {
- beforeEach(() => {
- createComponent({
- runner: mockInstanceRunner,
- });
- });
-
- it('Shows a "None" label', () => {
- expect(wrapper.findByText('None').exists()).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_header_spec.js b/spec/frontend/runner/components/runner_header_spec.js
deleted file mode 100644
index 701d39108cb..00000000000
--- a/spec/frontend/runner/components/runner_header_spec.js
+++ /dev/null
@@ -1,119 +0,0 @@
-import { GlSprintf } from '@gitlab/ui';
-import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { I18N_STATUS_ONLINE, I18N_GROUP_TYPE, GROUP_TYPE, STATUS_ONLINE } from '~/runner/constants';
-import { TYPE_CI_RUNNER } from '~/graphql_shared/constants';
-import { convertToGraphQLId } from '~/graphql_shared/utils';
-import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue';
-
-import RunnerHeader from '~/runner/components/runner_header.vue';
-import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
-import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
-
-import { runnerData } from '../mock_data';
-
-const mockRunner = runnerData.data.runner;
-
-describe('RunnerHeader', () => {
- let wrapper;
-
- const findRunnerTypeBadge = () => wrapper.findComponent(RunnerTypeBadge);
- const findRunnerStatusBadge = () => wrapper.findComponent(RunnerStatusBadge);
- const findRunnerLockedIcon = () => wrapper.findByTestId('lock-icon');
- const findTimeAgo = () => wrapper.findComponent(TimeAgo);
-
- const createComponent = ({ runner = {}, options = {}, mountFn = shallowMountExtended } = {}) => {
- wrapper = mountFn(RunnerHeader, {
- propsData: {
- runner: {
- ...mockRunner,
- ...runner,
- },
- },
- stubs: {
- GlSprintf,
- TimeAgo,
- },
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('displays the runner status', () => {
- createComponent({
- mountFn: mountExtended,
- runner: {
- status: STATUS_ONLINE,
- },
- });
-
- expect(findRunnerStatusBadge().text()).toContain(I18N_STATUS_ONLINE);
- });
-
- it('displays the runner type', () => {
- createComponent({
- mountFn: mountExtended,
- runner: {
- runnerType: GROUP_TYPE,
- },
- });
-
- expect(findRunnerTypeBadge().text()).toContain(I18N_GROUP_TYPE);
- });
-
- it('displays the runner id', () => {
- createComponent({
- runner: {
- id: convertToGraphQLId(TYPE_CI_RUNNER, 99),
- },
- });
-
- expect(wrapper.text()).toContain('Runner #99');
- });
-
- it('displays the runner locked icon', () => {
- createComponent({
- runner: {
- locked: true,
- },
- mountFn: mountExtended,
- });
-
- expect(findRunnerLockedIcon().exists()).toBe(true);
- });
-
- it('displays the runner creation time', () => {
- createComponent();
-
- expect(wrapper.text()).toMatch(/created .+/);
- expect(findTimeAgo().props('time')).toBe(mockRunner.createdAt);
- });
-
- it('does not display runner creation time if "createdAt" is missing', () => {
- createComponent({
- runner: {
- id: convertToGraphQLId(TYPE_CI_RUNNER, 99),
- createdAt: null,
- },
- });
-
- expect(wrapper.text()).toContain('Runner #99');
- expect(wrapper.text()).not.toMatch(/created .+/);
- expect(findTimeAgo().exists()).toBe(false);
- });
-
- it('displays actions in a slot', () => {
- createComponent({
- options: {
- slots: {
- actions: '<div data-testid="actions-content">My Actions</div>',
- },
- mountFn: mountExtended,
- },
- });
-
- expect(wrapper.findByTestId('actions-content').text()).toBe('My Actions');
- });
-});
diff --git a/spec/frontend/runner/components/runner_jobs_spec.js b/spec/frontend/runner/components/runner_jobs_spec.js
deleted file mode 100644
index 4d38afb25ee..00000000000
--- a/spec/frontend/runner/components/runner_jobs_spec.js
+++ /dev/null
@@ -1,155 +0,0 @@
-import { GlSkeletonLoader } from '@gitlab/ui';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import RunnerJobs from '~/runner/components/runner_jobs.vue';
-import RunnerJobsTable from '~/runner/components/runner_jobs_table.vue';
-import RunnerPagination from '~/runner/components/runner_pagination.vue';
-import { captureException } from '~/runner/sentry_utils';
-import { I18N_NO_JOBS_FOUND, RUNNER_DETAILS_JOBS_PAGE_SIZE } from '~/runner/constants';
-
-import runnerJobsQuery from '~/runner/graphql/show/runner_jobs.query.graphql';
-
-import { runnerData, runnerJobsData } from '../mock_data';
-
-jest.mock('~/flash');
-jest.mock('~/runner/sentry_utils');
-
-const mockRunner = runnerData.data.runner;
-const mockRunnerWithJobs = runnerJobsData.data.runner;
-const mockJobs = mockRunnerWithJobs.jobs.nodes;
-
-Vue.use(VueApollo);
-
-describe('RunnerJobs', () => {
- let wrapper;
- let mockRunnerJobsQuery;
-
- const findGlSkeletonLoading = () => wrapper.findComponent(GlSkeletonLoader);
- const findRunnerJobsTable = () => wrapper.findComponent(RunnerJobsTable);
- const findRunnerPagination = () => wrapper.findComponent(RunnerPagination);
-
- const createComponent = ({ mountFn = shallowMountExtended } = {}) => {
- wrapper = mountFn(RunnerJobs, {
- apolloProvider: createMockApollo([[runnerJobsQuery, mockRunnerJobsQuery]]),
- propsData: {
- runner: mockRunner,
- },
- });
- };
-
- beforeEach(() => {
- mockRunnerJobsQuery = jest.fn();
- });
-
- afterEach(() => {
- mockRunnerJobsQuery.mockReset();
- wrapper.destroy();
- });
-
- it('Requests runner jobs', async () => {
- createComponent();
-
- await waitForPromises();
-
- expect(mockRunnerJobsQuery).toHaveBeenCalledTimes(1);
- expect(mockRunnerJobsQuery).toHaveBeenCalledWith({
- id: mockRunner.id,
- first: RUNNER_DETAILS_JOBS_PAGE_SIZE,
- });
- });
-
- describe('When there are jobs assigned', () => {
- beforeEach(async () => {
- mockRunnerJobsQuery.mockResolvedValueOnce(runnerJobsData);
-
- createComponent();
- await waitForPromises();
- });
-
- it('Shows jobs', () => {
- const jobs = findRunnerJobsTable().props('jobs');
-
- expect(jobs).toEqual(mockJobs);
- });
-
- describe('When "Next" page is clicked', () => {
- beforeEach(async () => {
- findRunnerPagination().vm.$emit('input', { page: 2, after: 'AFTER_CURSOR' });
-
- await waitForPromises();
- });
-
- it('A new page is requested', () => {
- expect(mockRunnerJobsQuery).toHaveBeenCalledTimes(2);
- expect(mockRunnerJobsQuery).toHaveBeenLastCalledWith({
- id: mockRunner.id,
- first: RUNNER_DETAILS_JOBS_PAGE_SIZE,
- after: 'AFTER_CURSOR',
- });
- });
- });
- });
-
- describe('When loading', () => {
- it('shows loading indicator and no other content', () => {
- createComponent();
-
- expect(findGlSkeletonLoading().exists()).toBe(true);
- expect(findRunnerJobsTable().exists()).toBe(false);
- expect(findRunnerPagination().attributes('disabled')).toBe('true');
- });
- });
-
- describe('When there are no jobs', () => {
- beforeEach(async () => {
- mockRunnerJobsQuery.mockResolvedValueOnce({
- data: {
- runner: {
- id: mockRunner.id,
- projectCount: 0,
- jobs: {
- nodes: [],
- pageInfo: {
- hasNextPage: false,
- hasPreviousPage: false,
- startCursor: '',
- endCursor: '',
- },
- },
- },
- },
- });
-
- createComponent();
- await waitForPromises();
- });
-
- it('Shows a "None" label', () => {
- expect(wrapper.text()).toBe(I18N_NO_JOBS_FOUND);
- });
- });
-
- describe('When an error occurs', () => {
- beforeEach(async () => {
- mockRunnerJobsQuery.mockRejectedValue(new Error('Error!'));
-
- createComponent();
- await waitForPromises();
- });
-
- it('shows an error', () => {
- expect(createAlert).toHaveBeenCalled();
- });
-
- it('reports an error', () => {
- expect(captureException).toHaveBeenCalledWith({
- component: 'RunnerJobs',
- error: expect.any(Error),
- });
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_jobs_table_spec.js b/spec/frontend/runner/components/runner_jobs_table_spec.js
deleted file mode 100644
index 5f4905ad2a8..00000000000
--- a/spec/frontend/runner/components/runner_jobs_table_spec.js
+++ /dev/null
@@ -1,119 +0,0 @@
-import { GlTableLite } from '@gitlab/ui';
-import {
- extendedWrapper,
- shallowMountExtended,
- mountExtended,
-} from 'helpers/vue_test_utils_helper';
-import { __, s__ } from '~/locale';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import RunnerJobsTable from '~/runner/components/runner_jobs_table.vue';
-import { useFakeDate } from 'helpers/fake_date';
-import { runnerJobsData } from '../mock_data';
-
-const mockJobs = runnerJobsData.data.runner.jobs.nodes;
-
-describe('RunnerJobsTable', () => {
- let wrapper;
- const mockNow = '2021-01-15T12:00:00Z';
- const mockOneHourAgo = '2021-01-15T11:00:00Z';
-
- useFakeDate(mockNow);
-
- const findTable = () => wrapper.findComponent(GlTableLite);
- const findHeaders = () => wrapper.findAll('th');
- const findRows = () => wrapper.findAll('[data-testid^="job-row-"]');
- const findCell = ({ field }) =>
- extendedWrapper(findRows().at(0).find(`[data-testid="td-${field}"]`));
-
- const createComponent = ({ props = {} } = {}, mountFn = shallowMountExtended) => {
- wrapper = mountFn(RunnerJobsTable, {
- propsData: {
- jobs: mockJobs,
- ...props,
- },
- stubs: {
- GlTableLite,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Sets job id as a row key', () => {
- createComponent();
-
- expect(findTable().attributes('primarykey')).toBe('id');
- });
-
- describe('Table data', () => {
- beforeEach(() => {
- createComponent({}, mountExtended);
- });
-
- it('Displays headers', () => {
- const headerLabels = findHeaders().wrappers.map((w) => w.text());
-
- expect(headerLabels).toEqual([
- s__('Job|Status'),
- __('Job'),
- __('Project'),
- __('Commit'),
- s__('Job|Finished at'),
- s__('Runners|Tags'),
- ]);
- });
-
- it('Displays a list of jobs', () => {
- expect(findRows()).toHaveLength(1);
- });
-
- it('Displays details of a job', () => {
- const { id, detailedStatus, pipeline, shortSha, commitPath } = mockJobs[0];
-
- expect(findCell({ field: 'status' }).text()).toMatchInterpolatedText(detailedStatus.text);
-
- expect(findCell({ field: 'job' }).text()).toContain(`#${getIdFromGraphQLId(id)}`);
- expect(findCell({ field: 'job' }).find('a').attributes('href')).toBe(
- detailedStatus.detailsPath,
- );
-
- expect(findCell({ field: 'project' }).text()).toBe(pipeline.project.name);
- expect(findCell({ field: 'project' }).find('a').attributes('href')).toBe(
- pipeline.project.webUrl,
- );
-
- expect(findCell({ field: 'commit' }).text()).toBe(shortSha);
- expect(findCell({ field: 'commit' }).find('a').attributes('href')).toBe(commitPath);
- });
- });
-
- describe('Table data formatting', () => {
- let mockJobsCopy;
-
- beforeEach(() => {
- mockJobsCopy = [
- {
- ...mockJobs[0],
- },
- ];
- });
-
- it('Formats finishedAt time', () => {
- mockJobsCopy[0].finishedAt = mockOneHourAgo;
-
- createComponent({ props: { jobs: mockJobsCopy } }, mountExtended);
-
- expect(findCell({ field: 'finished_at' }).text()).toBe('1 hour ago');
- });
-
- it('Formats tags', () => {
- mockJobsCopy[0].tags = ['tag-1', 'tag-2'];
-
- createComponent({ props: { jobs: mockJobsCopy } }, mountExtended);
-
- expect(findCell({ field: 'tags' }).text()).toMatchInterpolatedText('tag-1 tag-2');
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_list_empty_state_spec.js b/spec/frontend/runner/components/runner_list_empty_state_spec.js
deleted file mode 100644
index 038162b889e..00000000000
--- a/spec/frontend/runner/components/runner_list_empty_state_spec.js
+++ /dev/null
@@ -1,103 +0,0 @@
-import { GlEmptyState, GlLink, GlSprintf } from '@gitlab/ui';
-import { s__ } from '~/locale';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import RunnerInstructionsModal from '~/vue_shared/components/runner_instructions/runner_instructions_modal.vue';
-
-import RunnerListEmptyState from '~/runner/components/runner_list_empty_state.vue';
-
-const mockSvgPath = 'mock-svg-path.svg';
-const mockFilteredSvgPath = 'mock-filtered-svg-path.svg';
-const mockRegistrationToken = 'REGISTRATION_TOKEN';
-
-describe('RunnerListEmptyState', () => {
- let wrapper;
-
- const findEmptyState = () => wrapper.findComponent(GlEmptyState);
- const findLink = () => wrapper.findComponent(GlLink);
- const findRunnerInstructionsModal = () => wrapper.findComponent(RunnerInstructionsModal);
-
- const createComponent = ({ props, mountFn = shallowMountExtended } = {}) => {
- wrapper = mountFn(RunnerListEmptyState, {
- propsData: {
- svgPath: mockSvgPath,
- filteredSvgPath: mockFilteredSvgPath,
- registrationToken: mockRegistrationToken,
- ...props,
- },
- directives: {
- GlModal: createMockDirective(),
- },
- stubs: {
- GlEmptyState,
- GlSprintf,
- GlLink,
- },
- });
- };
-
- describe('when search is not filtered', () => {
- const title = s__('Runners|Get started with runners');
-
- describe('when there is a registration token', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('renders an illustration', () => {
- expect(findEmptyState().props('svgPath')).toBe(mockSvgPath);
- });
-
- it('displays "no results" text with instructions', () => {
- const desc = s__(
- 'Runners|Runners are the agents that run your CI/CD jobs. Follow the %{linkStart}installation and registration instructions%{linkEnd} to set up a runner.',
- );
-
- expect(findEmptyState().text()).toMatchInterpolatedText(`${title} ${desc}`);
- });
-
- it('opens a runner registration instructions modal with a link', () => {
- const { value } = getBinding(findLink().element, 'gl-modal');
-
- expect(findRunnerInstructionsModal().props('modalId')).toEqual(value);
- });
- });
-
- describe('when there is no registration token', () => {
- beforeEach(() => {
- createComponent({ props: { registrationToken: null } });
- });
-
- it('renders an illustration', () => {
- expect(findEmptyState().props('svgPath')).toBe(mockSvgPath);
- });
-
- it('displays "no results" text', () => {
- const desc = s__(
- 'Runners|Runners are the agents that run your CI/CD jobs. To register new runners, please contact your administrator.',
- );
-
- expect(findEmptyState().text()).toMatchInterpolatedText(`${title} ${desc}`);
- });
-
- it('has no registration instructions link', () => {
- expect(findLink().exists()).toBe(false);
- });
- });
- });
-
- describe('when search is filtered', () => {
- beforeEach(() => {
- createComponent({ props: { isSearchFiltered: true } });
- });
-
- it('renders a "filtered search" illustration', () => {
- expect(findEmptyState().props('svgPath')).toBe(mockFilteredSvgPath);
- });
-
- it('displays "no filtered results" text', () => {
- expect(findEmptyState().text()).toContain(s__('Runners|No results found'));
- expect(findEmptyState().text()).toContain(s__('Runners|Edit your search and try again'));
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_list_spec.js b/spec/frontend/runner/components/runner_list_spec.js
deleted file mode 100644
index a31990f8f7e..00000000000
--- a/spec/frontend/runner/components/runner_list_spec.js
+++ /dev/null
@@ -1,231 +0,0 @@
-import { GlTableLite, GlSkeletonLoader } from '@gitlab/ui';
-import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import {
- extendedWrapper,
- shallowMountExtended,
- mountExtended,
-} from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { s__ } from '~/locale';
-import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import { createLocalState } from '~/runner/graphql/list/local_state';
-
-import RunnerList from '~/runner/components/runner_list.vue';
-import RunnerBulkDelete from '~/runner/components/runner_bulk_delete.vue';
-import RunnerBulkDeleteCheckbox from '~/runner/components/runner_bulk_delete_checkbox.vue';
-
-import { I18N_PROJECT_TYPE, I18N_STATUS_NEVER_CONTACTED } from '~/runner/constants';
-import { allRunnersData, onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
-
-const mockRunners = allRunnersData.data.runners.nodes;
-const mockActiveRunnersCount = mockRunners.length;
-
-describe('RunnerList', () => {
- let wrapper;
- let cacheConfig;
- let localMutations;
-
- const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
- const findTable = () => wrapper.findComponent(GlTableLite);
- const findHeaders = () => wrapper.findAll('th');
- const findRows = () => wrapper.findAll('[data-testid^="runner-row-"]');
- const findCell = ({ row = 0, fieldKey }) =>
- extendedWrapper(findRows().at(row).find(`[data-testid="td-${fieldKey}"]`));
- const findRunnerBulkDelete = () => wrapper.findComponent(RunnerBulkDelete);
- const findRunnerBulkDeleteCheckbox = () => wrapper.findComponent(RunnerBulkDeleteCheckbox);
-
- const createComponent = (
- { props = {}, provide = {}, ...options } = {},
- mountFn = shallowMountExtended,
- ) => {
- ({ cacheConfig, localMutations } = createLocalState());
-
- wrapper = mountFn(RunnerList, {
- apolloProvider: createMockApollo([], {}, cacheConfig),
- propsData: {
- runners: mockRunners,
- activeRunnersCount: mockActiveRunnersCount,
- ...props,
- },
- provide: {
- localMutations,
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
- ...provide,
- },
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays headers', () => {
- createComponent(
- {
- stubs: {
- HelpPopover: {
- template: '<div/>',
- },
- },
- },
- mountExtended,
- );
-
- const headerLabels = findHeaders().wrappers.map((w) => w.text());
-
- expect(findHeaders().at(0).findComponent(HelpPopover).exists()).toBe(true);
- expect(findHeaders().at(2).findComponent(HelpPopover).exists()).toBe(true);
-
- expect(headerLabels).toEqual([
- s__('Runners|Status'),
- s__('Runners|Runner'),
- s__('Runners|Owner'),
- '', // actions has no label
- ]);
- });
-
- it('Sets runner id as a row key', () => {
- createComponent();
-
- expect(findTable().attributes('primary-key')).toBe('id');
- });
-
- it('Displays a list of runners', () => {
- createComponent({}, mountExtended);
-
- expect(findRows()).toHaveLength(4);
-
- expect(findSkeletonLoader().exists()).toBe(false);
- });
-
- it('Displays details of a runner', () => {
- createComponent({}, mountExtended);
-
- const { id, description, version, shortSha } = mockRunners[0];
- const numericId = getIdFromGraphQLId(id);
-
- // Badges
- expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText(
- I18N_STATUS_NEVER_CONTACTED,
- );
-
- // Runner summary
- const summary = findCell({ fieldKey: 'summary' }).text();
-
- expect(summary).toContain(`#${numericId} (${shortSha})`);
- expect(summary).toContain(I18N_PROJECT_TYPE);
-
- expect(summary).toContain(version);
- expect(summary).toContain(description);
-
- expect(summary).toContain('Last contact');
- expect(summary).toContain('0'); // job count
- expect(summary).toContain('Created');
-
- // Actions
- expect(findCell({ fieldKey: 'actions' }).exists()).toBe(true);
- });
-
- describe('When the list is checkable', () => {
- beforeEach(() => {
- createComponent(
- {
- props: {
- checkable: true,
- },
- },
- mountExtended,
- );
- });
-
- it('runner bulk delete is available', () => {
- expect(findRunnerBulkDelete().props('runners')).toEqual(mockRunners);
- });
-
- it('runner bulk delete checkbox is available', () => {
- expect(findRunnerBulkDeleteCheckbox().props('runners')).toEqual(mockRunners);
- });
-
- it('Displays a checkbox field', () => {
- expect(findCell({ fieldKey: 'checkbox' }).find('input').exists()).toBe(true);
- });
-
- it('Sets a runner as checked', async () => {
- const runner = mockRunners[0];
- const setRunnerCheckedMock = jest
- .spyOn(localMutations, 'setRunnerChecked')
- .mockImplementation(() => {});
-
- const checkbox = findCell({ fieldKey: 'checkbox' }).find('input');
- await checkbox.setChecked();
-
- expect(setRunnerCheckedMock).toHaveBeenCalledTimes(1);
- expect(setRunnerCheckedMock).toHaveBeenCalledWith({
- runner,
- isChecked: true,
- });
- });
-
- it('Emits a deleted event', async () => {
- const event = { message: 'Deleted!' };
- findRunnerBulkDelete().vm.$emit('deleted', event);
-
- expect(wrapper.emitted('deleted')).toEqual([[event]]);
- });
- });
-
- describe('Scoped cell slots', () => {
- it('Render #runner-name slot in "summary" cell', () => {
- createComponent(
- {
- scopedSlots: { 'runner-name': ({ runner }) => `Summary: ${runner.id}` },
- },
- mountExtended,
- );
-
- expect(findCell({ fieldKey: 'summary' }).text()).toContain(`Summary: ${mockRunners[0].id}`);
- });
-
- it('Render #runner-actions-cell slot in "actions" cell', () => {
- createComponent(
- {
- scopedSlots: { 'runner-actions-cell': ({ runner }) => `Actions: ${runner.id}` },
- },
- mountExtended,
- );
-
- expect(findCell({ fieldKey: 'actions' }).text()).toBe(`Actions: ${mockRunners[0].id}`);
- });
- });
-
- it('Shows runner identifier', () => {
- const { id, shortSha } = mockRunners[0];
- const numericId = getIdFromGraphQLId(id);
-
- createComponent({}, mountExtended);
-
- expect(findCell({ fieldKey: 'summary' }).text()).toContain(`#${numericId} (${shortSha})`);
- });
-
- describe('When data is loading', () => {
- it('shows a busy state', () => {
- createComponent({ props: { runners: [], loading: true } });
-
- expect(findTable().classes('gl-opacity-6')).toBe(true);
- });
-
- it('when there are no runners, shows an skeleton loader', () => {
- createComponent({ props: { runners: [], loading: true } }, mountExtended);
-
- expect(findSkeletonLoader().exists()).toBe(true);
- });
-
- it('when there are runners, shows a busy indicator skeleton loader', () => {
- createComponent({ props: { loading: true } }, mountExtended);
-
- expect(findSkeletonLoader().exists()).toBe(false);
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_membership_toggle_spec.js b/spec/frontend/runner/components/runner_membership_toggle_spec.js
deleted file mode 100644
index 1a7ae22618a..00000000000
--- a/spec/frontend/runner/components/runner_membership_toggle_spec.js
+++ /dev/null
@@ -1,57 +0,0 @@
-import { GlToggle } from '@gitlab/ui';
-import { shallowMount, mount } from '@vue/test-utils';
-import RunnerMembershipToggle from '~/runner/components/runner_membership_toggle.vue';
-import {
- I18N_SHOW_ONLY_INHERITED,
- MEMBERSHIP_DESCENDANTS,
- MEMBERSHIP_ALL_AVAILABLE,
-} from '~/runner/constants';
-
-describe('RunnerMembershipToggle', () => {
- let wrapper;
-
- const findToggle = () => wrapper.findComponent(GlToggle);
-
- const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
- wrapper = mountFn(RunnerMembershipToggle, {
- propsData: props,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays text', () => {
- createComponent({ mountFn: mount });
-
- expect(wrapper.text()).toBe(I18N_SHOW_ONLY_INHERITED);
- });
-
- it.each`
- membershipValue | toggleValue
- ${MEMBERSHIP_DESCENDANTS} | ${true}
- ${MEMBERSHIP_ALL_AVAILABLE} | ${false}
- `(
- 'Displays a membership of $membershipValue as enabled=$toggleValue',
- ({ membershipValue, toggleValue }) => {
- createComponent({ props: { value: membershipValue } });
-
- expect(findToggle().props('value')).toBe(toggleValue);
- },
- );
-
- it.each`
- changeEvt | membershipValue
- ${true} | ${MEMBERSHIP_DESCENDANTS}
- ${false} | ${MEMBERSHIP_ALL_AVAILABLE}
- `(
- 'Emits $changeEvt when value is changed to $membershipValue',
- ({ changeEvt, membershipValue }) => {
- createComponent();
- findToggle().vm.$emit('change', changeEvt);
-
- expect(wrapper.emitted('input')).toStrictEqual([[membershipValue]]);
- },
- );
-});
diff --git a/spec/frontend/runner/components/runner_pagination_spec.js b/spec/frontend/runner/components/runner_pagination_spec.js
deleted file mode 100644
index 499cc59250d..00000000000
--- a/spec/frontend/runner/components/runner_pagination_spec.js
+++ /dev/null
@@ -1,115 +0,0 @@
-import { GlKeysetPagination } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RunnerPagination from '~/runner/components/runner_pagination.vue';
-
-const mockStartCursor = 'START_CURSOR';
-const mockEndCursor = 'END_CURSOR';
-
-describe('RunnerPagination', () => {
- let wrapper;
-
- const findPagination = () => wrapper.findComponent(GlKeysetPagination);
-
- const createComponent = (propsData = {}) => {
- wrapper = shallowMount(RunnerPagination, {
- propsData,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('When in between pages', () => {
- const mockPageInfo = {
- startCursor: mockStartCursor,
- endCursor: mockEndCursor,
- hasPreviousPage: true,
- hasNextPage: true,
- };
-
- beforeEach(() => {
- createComponent({
- pageInfo: mockPageInfo,
- });
- });
-
- it('Contains the current page information', () => {
- expect(findPagination().props()).toMatchObject(mockPageInfo);
- });
-
- it('Goes to the prev page', () => {
- findPagination().vm.$emit('prev');
-
- expect(wrapper.emitted('input')[0]).toEqual([
- {
- before: mockStartCursor,
- },
- ]);
- });
-
- it('Goes to the next page', () => {
- findPagination().vm.$emit('next');
-
- expect(wrapper.emitted('input')[0]).toEqual([
- {
- after: mockEndCursor,
- },
- ]);
- });
- });
-
- describe.each`
- page | hasPreviousPage | hasNextPage
- ${'first'} | ${false} | ${true}
- ${'last'} | ${true} | ${false}
- `('When on the $page page', ({ page, hasPreviousPage, hasNextPage }) => {
- const mockPageInfo = {
- startCursor: mockStartCursor,
- endCursor: mockEndCursor,
- hasPreviousPage,
- hasNextPage,
- };
-
- beforeEach(() => {
- createComponent({
- pageInfo: mockPageInfo,
- });
- });
-
- it(`Contains the ${page} page information`, () => {
- expect(findPagination().props()).toMatchObject(mockPageInfo);
- });
- });
-
- describe('When no other pages', () => {
- beforeEach(() => {
- createComponent({
- pageInfo: {
- hasPreviousPage: false,
- hasNextPage: false,
- },
- });
- });
-
- it('is not shown', () => {
- expect(findPagination().exists()).toBe(false);
- });
- });
-
- describe('When adding more attributes', () => {
- beforeEach(() => {
- createComponent({
- pageInfo: {
- hasPreviousPage: true,
- hasNextPage: false,
- },
- disabled: true,
- });
- });
-
- it('attributes are passed', () => {
- expect(findPagination().props('disabled')).toBe(true);
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_pause_button_spec.js b/spec/frontend/runner/components/runner_pause_button_spec.js
deleted file mode 100644
index 61476007571..00000000000
--- a/spec/frontend/runner/components/runner_pause_button_spec.js
+++ /dev/null
@@ -1,263 +0,0 @@
-import Vue, { nextTick } from 'vue';
-import { GlButton } from '@gitlab/ui';
-import VueApollo from 'vue-apollo';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import { shallowMountExtended, mountExtended } from 'helpers/vue_test_utils_helper';
-import runnerToggleActiveMutation from '~/runner/graphql/shared/runner_toggle_active.mutation.graphql';
-import waitForPromises from 'helpers/wait_for_promises';
-import { captureException } from '~/runner/sentry_utils';
-import { createAlert } from '~/flash';
-import {
- I18N_PAUSE,
- I18N_PAUSE_TOOLTIP,
- I18N_RESUME,
- I18N_RESUME_TOOLTIP,
-} from '~/runner/constants';
-
-import RunnerPauseButton from '~/runner/components/runner_pause_button.vue';
-import { allRunnersData } from '../mock_data';
-
-const mockRunner = allRunnersData.data.runners.nodes[0];
-
-Vue.use(VueApollo);
-
-jest.mock('~/flash');
-jest.mock('~/runner/sentry_utils');
-
-describe('RunnerPauseButton', () => {
- let wrapper;
- let runnerToggleActiveHandler;
-
- const getTooltip = () => getBinding(wrapper.element, 'gl-tooltip').value;
- const findBtn = () => wrapper.findComponent(GlButton);
-
- const createComponent = ({ props = {}, mountFn = shallowMountExtended } = {}) => {
- const { runner, ...propsData } = props;
-
- wrapper = mountFn(RunnerPauseButton, {
- propsData: {
- runner: {
- id: mockRunner.id,
- active: mockRunner.active,
- ...runner,
- },
- ...propsData,
- },
- apolloProvider: createMockApollo([[runnerToggleActiveMutation, runnerToggleActiveHandler]]),
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- const clickAndWait = async () => {
- findBtn().vm.$emit('click');
- await waitForPromises();
- };
-
- beforeEach(() => {
- runnerToggleActiveHandler = jest.fn().mockImplementation(({ input }) => {
- return Promise.resolve({
- data: {
- runnerUpdate: {
- runner: {
- id: input.id,
- active: input.active,
- },
- errors: [],
- },
- },
- });
- });
-
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe('Pause/Resume action', () => {
- describe.each`
- runnerState | icon | content | tooltip | isActive | newActiveValue
- ${'paused'} | ${'play'} | ${I18N_RESUME} | ${I18N_RESUME_TOOLTIP} | ${false} | ${true}
- ${'active'} | ${'pause'} | ${I18N_PAUSE} | ${I18N_PAUSE_TOOLTIP} | ${true} | ${false}
- `('When the runner is $runnerState', ({ icon, content, tooltip, isActive, newActiveValue }) => {
- beforeEach(() => {
- createComponent({
- props: {
- runner: {
- active: isActive,
- },
- },
- });
- });
-
- it(`Displays a ${icon} button`, () => {
- expect(findBtn().props('loading')).toBe(false);
- expect(findBtn().props('icon')).toBe(icon);
- });
-
- it('Displays button content', () => {
- expect(findBtn().text()).toBe(content);
- expect(getTooltip()).toBe(tooltip);
- });
-
- it('Does not display redundant text for screen readers', () => {
- expect(findBtn().attributes('aria-label')).toBe(undefined);
- });
-
- describe(`Before the ${icon} button is clicked`, () => {
- it('The mutation has not been called', () => {
- expect(runnerToggleActiveHandler).toHaveBeenCalledTimes(0);
- });
- });
-
- describe(`Immediately after the ${icon} button is clicked`, () => {
- const setup = async () => {
- findBtn().vm.$emit('click');
- await nextTick();
- };
-
- it('The button has a loading state', async () => {
- await setup();
-
- expect(findBtn().props('loading')).toBe(true);
- });
-
- it('The stale tooltip is removed', async () => {
- await setup();
-
- expect(getTooltip()).toBe('');
- });
- });
-
- describe(`After clicking on the ${icon} button`, () => {
- beforeEach(async () => {
- await clickAndWait();
- });
-
- it(`The mutation to that sets active to ${newActiveValue} is called`, async () => {
- expect(runnerToggleActiveHandler).toHaveBeenCalledTimes(1);
- expect(runnerToggleActiveHandler).toHaveBeenCalledWith({
- input: {
- id: mockRunner.id,
- active: newActiveValue,
- },
- });
- });
-
- it('The button does not have a loading state', () => {
- expect(findBtn().props('loading')).toBe(false);
- });
-
- it('The button emits toggledPaused', () => {
- expect(wrapper.emitted('toggledPaused')).toHaveLength(1);
- });
- });
-
- describe('When update fails', () => {
- describe('On a network error', () => {
- const mockErrorMsg = 'Update error!';
-
- beforeEach(async () => {
- runnerToggleActiveHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
-
- await clickAndWait();
- });
-
- it('error is reported to sentry', () => {
- expect(captureException).toHaveBeenCalledWith({
- error: new Error(mockErrorMsg),
- component: 'RunnerPauseButton',
- });
- });
-
- it('error is shown to the user', () => {
- expect(createAlert).toHaveBeenCalledTimes(1);
- });
- });
-
- describe('On a validation error', () => {
- const mockErrorMsg = 'Runner not found!';
- const mockErrorMsg2 = 'User not allowed!';
-
- beforeEach(async () => {
- runnerToggleActiveHandler.mockResolvedValueOnce({
- data: {
- runnerUpdate: {
- runner: {
- id: mockRunner.id,
- active: isActive,
- },
- errors: [mockErrorMsg, mockErrorMsg2],
- },
- },
- });
-
- await clickAndWait();
- });
-
- it('error is reported to sentry', () => {
- expect(captureException).toHaveBeenCalledWith({
- error: new Error(`${mockErrorMsg} ${mockErrorMsg2}`),
- component: 'RunnerPauseButton',
- });
- });
-
- it('error is shown to the user', () => {
- expect(createAlert).toHaveBeenCalledTimes(1);
- });
- });
- });
- });
- });
-
- describe('When displaying a compact button for an active runner', () => {
- beforeEach(() => {
- createComponent({
- props: {
- runner: {
- active: true,
- },
- compact: true,
- },
- mountFn: mountExtended,
- });
- });
-
- it('Displays no text', () => {
- expect(findBtn().text()).toBe('');
-
- // Note: Use <template v-if> to ensure rendering a
- // text-less button. Ensure we don't send even empty an
- // content slot to prevent a distorted/rectangular button.
- expect(wrapper.find('.gl-button-text').exists()).toBe(false);
- });
-
- it('Display correctly for screen readers', () => {
- expect(findBtn().attributes('aria-label')).toBe(I18N_PAUSE);
- expect(getTooltip()).toBe(I18N_PAUSE_TOOLTIP);
- });
-
- describe('Immediately after the button is clicked', () => {
- const setup = async () => {
- findBtn().vm.$emit('click');
- await nextTick();
- };
-
- it('The button has a loading state', async () => {
- await setup();
-
- expect(findBtn().props('loading')).toBe(true);
- });
-
- it('The stale tooltip is removed', async () => {
- await setup();
-
- expect(getTooltip()).toBe('');
- });
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_paused_badge_spec.js b/spec/frontend/runner/components/runner_paused_badge_spec.js
deleted file mode 100644
index c1c7351aab2..00000000000
--- a/spec/frontend/runner/components/runner_paused_badge_spec.js
+++ /dev/null
@@ -1,46 +0,0 @@
-import { GlBadge } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RunnerStatePausedBadge from '~/runner/components/runner_paused_badge.vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import { I18N_PAUSED } from '~/runner/constants';
-
-describe('RunnerTypeBadge', () => {
- let wrapper;
-
- const findBadge = () => wrapper.findComponent(GlBadge);
- const getTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMount(RunnerStatePausedBadge, {
- propsData: {
- ...props,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('renders paused state', () => {
- expect(wrapper.text()).toBe(I18N_PAUSED);
- expect(findBadge().props('variant')).toBe('warning');
- });
-
- it('renders tooltip', () => {
- expect(getTooltip().value).toBeDefined();
- });
-
- it('passes arbitrary attributes to the badge', () => {
- createComponent({ props: { size: 'sm' } });
-
- expect(findBadge().props('size')).toBe('sm');
- });
-});
diff --git a/spec/frontend/runner/components/runner_projects_spec.js b/spec/frontend/runner/components/runner_projects_spec.js
deleted file mode 100644
index eca042cae86..00000000000
--- a/spec/frontend/runner/components/runner_projects_spec.js
+++ /dev/null
@@ -1,251 +0,0 @@
-import { GlSearchBoxByType, GlSkeletonLoader } from '@gitlab/ui';
-import Vue from 'vue';
-import VueApollo from 'vue-apollo';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import { sprintf } from '~/locale';
-import {
- I18N_ASSIGNED_PROJECTS,
- I18N_CLEAR_FILTER_PROJECTS,
- I18N_FILTER_PROJECTS,
- I18N_NO_PROJECTS_FOUND,
- RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
-} from '~/runner/constants';
-import RunnerProjects from '~/runner/components/runner_projects.vue';
-import RunnerAssignedItem from '~/runner/components/runner_assigned_item.vue';
-import RunnerPagination from '~/runner/components/runner_pagination.vue';
-import { captureException } from '~/runner/sentry_utils';
-
-import runnerProjectsQuery from '~/runner/graphql/show/runner_projects.query.graphql';
-
-import { runnerData, runnerProjectsData } from '../mock_data';
-
-jest.mock('~/flash');
-jest.mock('~/runner/sentry_utils');
-
-const mockRunner = runnerData.data.runner;
-const mockRunnerWithProjects = runnerProjectsData.data.runner;
-const mockProjects = mockRunnerWithProjects.projects.nodes;
-
-Vue.use(VueApollo);
-
-describe('RunnerProjects', () => {
- let wrapper;
- let mockRunnerProjectsQuery;
-
- const findHeading = () => wrapper.find('h3');
- const findGlSkeletonLoading = () => wrapper.findComponent(GlSkeletonLoader);
- const findGlSearchBoxByType = () => wrapper.findComponent(GlSearchBoxByType);
- const findRunnerAssignedItems = () => wrapper.findAllComponents(RunnerAssignedItem);
- const findRunnerPagination = () => wrapper.findComponent(RunnerPagination);
-
- const createComponent = ({ mountFn = shallowMountExtended } = {}) => {
- wrapper = mountFn(RunnerProjects, {
- apolloProvider: createMockApollo([[runnerProjectsQuery, mockRunnerProjectsQuery]]),
- propsData: {
- runner: mockRunner,
- },
- });
- };
-
- beforeEach(() => {
- mockRunnerProjectsQuery = jest.fn();
- });
-
- afterEach(() => {
- mockRunnerProjectsQuery.mockReset();
- wrapper.destroy();
- });
-
- it('Requests runner projects', async () => {
- createComponent();
-
- await waitForPromises();
-
- expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(1);
- expect(mockRunnerProjectsQuery).toHaveBeenCalledWith({
- id: mockRunner.id,
- search: '',
- first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
- });
- });
-
- it('Shows a filter box', () => {
- createComponent();
-
- expect(findGlSearchBoxByType().attributes()).toMatchObject({
- clearbuttontitle: I18N_CLEAR_FILTER_PROJECTS,
- debounce: '500',
- placeholder: I18N_FILTER_PROJECTS,
- });
- });
-
- describe('When there are projects assigned', () => {
- beforeEach(async () => {
- mockRunnerProjectsQuery.mockResolvedValueOnce(runnerProjectsData);
-
- createComponent();
- await waitForPromises();
- });
-
- it('Shows a heading', async () => {
- const expected = sprintf(I18N_ASSIGNED_PROJECTS, { projectCount: mockProjects.length });
-
- expect(findHeading().text()).toBe(expected);
- });
-
- it('Shows projects', () => {
- expect(findRunnerAssignedItems().length).toBe(mockProjects.length);
- });
-
- it('Shows a project', () => {
- const item = findRunnerAssignedItems().at(0);
- const { webUrl, name, nameWithNamespace, avatarUrl } = mockProjects[0];
-
- expect(item.props()).toMatchObject({
- href: webUrl,
- name,
- fullName: nameWithNamespace,
- avatarUrl,
- isOwner: true, // first project is always owner
- });
- });
-
- describe('When "Next" page is clicked', () => {
- beforeEach(async () => {
- findRunnerPagination().vm.$emit('input', { page: 3, after: 'AFTER_CURSOR' });
-
- await waitForPromises();
- });
-
- it('A new page is requested', () => {
- expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(2);
- expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
- id: mockRunner.id,
- search: '',
- first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
- after: 'AFTER_CURSOR',
- });
- });
-
- it('When "Prev" page is clicked, the previous page is requested', async () => {
- findRunnerPagination().vm.$emit('input', { page: 2, before: 'BEFORE_CURSOR' });
-
- await waitForPromises();
-
- expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(3);
- expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
- id: mockRunner.id,
- search: '',
- last: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
- before: 'BEFORE_CURSOR',
- });
- });
-
- it('When user filters after paginating, the first page is requested', async () => {
- findGlSearchBoxByType().vm.$emit('input', 'my search');
- await waitForPromises();
-
- expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(3);
- expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
- id: mockRunner.id,
- search: 'my search',
- first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
- });
- });
- });
-
- describe('When user filters', () => {
- it('Filtered results are requested', async () => {
- expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(1);
-
- findGlSearchBoxByType().vm.$emit('input', 'my search');
- await waitForPromises();
-
- expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(2);
- expect(mockRunnerProjectsQuery).toHaveBeenLastCalledWith({
- id: mockRunner.id,
- search: 'my search',
- first: RUNNER_DETAILS_PROJECTS_PAGE_SIZE,
- });
- });
-
- it('Filtered results are not requested for short searches', async () => {
- expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(1);
-
- findGlSearchBoxByType().vm.$emit('input', 'm');
- await waitForPromises();
-
- findGlSearchBoxByType().vm.$emit('input', 'my');
- await waitForPromises();
-
- expect(mockRunnerProjectsQuery).toHaveBeenCalledTimes(1);
- });
- });
- });
-
- describe('When loading', () => {
- it('shows loading indicator and no other content', () => {
- createComponent();
-
- expect(findGlSkeletonLoading().exists()).toBe(true);
-
- expect(wrapper.findByText(I18N_NO_PROJECTS_FOUND).exists()).toBe(false);
- expect(findRunnerAssignedItems().length).toBe(0);
-
- expect(findRunnerPagination().attributes('disabled')).toBe('true');
- expect(findGlSearchBoxByType().props('isLoading')).toBe(true);
- });
- });
-
- describe('When there are no projects', () => {
- beforeEach(async () => {
- mockRunnerProjectsQuery.mockResolvedValueOnce({
- data: {
- runner: {
- id: mockRunner.id,
- projectCount: 0,
- projects: {
- nodes: [],
- pageInfo: {
- hasNextPage: false,
- hasPreviousPage: false,
- startCursor: '',
- endCursor: '',
- },
- },
- },
- },
- });
-
- createComponent();
- await waitForPromises();
- });
-
- it('Shows a "None" label', () => {
- expect(wrapper.findByText(I18N_NO_PROJECTS_FOUND).exists()).toBe(true);
- });
- });
-
- describe('When an error occurs', () => {
- beforeEach(async () => {
- mockRunnerProjectsQuery.mockRejectedValue(new Error('Error!'));
-
- createComponent();
- await waitForPromises();
- });
-
- it('shows an error', () => {
- expect(createAlert).toHaveBeenCalled();
- });
-
- it('reports an error', () => {
- expect(captureException).toHaveBeenCalledWith({
- component: 'RunnerProjects',
- error: expect.any(Error),
- });
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js b/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js
deleted file mode 100644
index d1f04f0ee37..00000000000
--- a/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js
+++ /dev/null
@@ -1,41 +0,0 @@
-import { nextTick } from 'vue';
-import { GlBanner } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RunnerStackedLayoutBanner from '~/runner/components/runner_stacked_layout_banner.vue';
-import LocalStorageSync from '~/vue_shared/components/local_storage_sync.vue';
-
-describe('RunnerStackedLayoutBanner', () => {
- let wrapper;
-
- const findBanner = () => wrapper.findComponent(GlBanner);
- const findLocalStorageSync = () => wrapper.findComponent(LocalStorageSync);
-
- const createComponent = ({ ...options } = {}, mountFn = shallowMount) => {
- wrapper = mountFn(RunnerStackedLayoutBanner, {
- ...options,
- });
- };
-
- it('Displays a banner', () => {
- createComponent();
-
- expect(findBanner().props()).toMatchObject({
- svgPath: expect.stringContaining('data:image/svg+xml;utf8,'),
- title: expect.any(String),
- buttonText: expect.any(String),
- buttonLink: expect.stringContaining('https://gitlab.com/gitlab-org/gitlab/-/issues/'),
- });
- expect(findLocalStorageSync().exists()).toBe(true);
- });
-
- it('Does not display a banner when dismissed', async () => {
- createComponent();
-
- findLocalStorageSync().vm.$emit('input', true);
-
- await nextTick();
-
- expect(findBanner().exists()).toBe(false);
- expect(findLocalStorageSync().exists()).toBe(true); // continues syncing after removal
- });
-});
diff --git a/spec/frontend/runner/components/runner_status_badge_spec.js b/spec/frontend/runner/components/runner_status_badge_spec.js
deleted file mode 100644
index 9ab6378304f..00000000000
--- a/spec/frontend/runner/components/runner_status_badge_spec.js
+++ /dev/null
@@ -1,133 +0,0 @@
-import { GlBadge } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RunnerStatusBadge from '~/runner/components/runner_status_badge.vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import {
- I18N_STATUS_ONLINE,
- I18N_STATUS_NEVER_CONTACTED,
- I18N_STATUS_OFFLINE,
- I18N_STATUS_STALE,
- I18N_NEVER_CONTACTED_TOOLTIP,
- I18N_STALE_NEVER_CONTACTED_TOOLTIP,
- STATUS_ONLINE,
- STATUS_OFFLINE,
- STATUS_STALE,
- STATUS_NEVER_CONTACTED,
-} from '~/runner/constants';
-
-describe('RunnerTypeBadge', () => {
- let wrapper;
-
- const findBadge = () => wrapper.findComponent(GlBadge);
- const getTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
-
- const createComponent = (props = {}) => {
- wrapper = shallowMount(RunnerStatusBadge, {
- propsData: {
- runner: {
- contactedAt: '2020-12-31T23:59:00Z',
- status: STATUS_ONLINE,
- },
- ...props,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- beforeEach(() => {
- jest.useFakeTimers('modern');
- jest.setSystemTime(new Date('2021-01-01T00:00:00Z'));
- });
-
- afterEach(() => {
- jest.useFakeTimers('legacy');
-
- wrapper.destroy();
- });
-
- it('renders online state', () => {
- createComponent();
-
- expect(wrapper.text()).toBe(I18N_STATUS_ONLINE);
- expect(findBadge().props('variant')).toBe('success');
- expect(getTooltip().value).toBe('Runner is online; last contact was 1 minute ago');
- });
-
- it('renders never contacted state', () => {
- createComponent({
- runner: {
- contactedAt: null,
- status: STATUS_NEVER_CONTACTED,
- },
- });
-
- expect(wrapper.text()).toBe(I18N_STATUS_NEVER_CONTACTED);
- expect(findBadge().props('variant')).toBe('muted');
- expect(getTooltip().value).toBe(I18N_NEVER_CONTACTED_TOOLTIP);
- });
-
- it('renders offline state', () => {
- createComponent({
- runner: {
- contactedAt: '2020-12-31T00:00:00Z',
- status: STATUS_OFFLINE,
- },
- });
-
- expect(wrapper.text()).toBe(I18N_STATUS_OFFLINE);
- expect(findBadge().props('variant')).toBe('muted');
- expect(getTooltip().value).toBe('Runner is offline; last contact was 1 day ago');
- });
-
- it('renders stale state', () => {
- createComponent({
- runner: {
- contactedAt: '2020-01-01T00:00:00Z',
- status: STATUS_STALE,
- },
- });
-
- expect(wrapper.text()).toBe(I18N_STATUS_STALE);
- expect(findBadge().props('variant')).toBe('warning');
- expect(getTooltip().value).toBe('Runner is stale; last contact was 1 year ago');
- });
-
- it('renders stale state with no contact time', () => {
- createComponent({
- runner: {
- contactedAt: null,
- status: STATUS_STALE,
- },
- });
-
- expect(wrapper.text()).toBe(I18N_STATUS_STALE);
- expect(findBadge().props('variant')).toBe('warning');
- expect(getTooltip().value).toBe(I18N_STALE_NEVER_CONTACTED_TOOLTIP);
- });
-
- describe('does not fail when data is missing', () => {
- it('contacted_at is missing', () => {
- createComponent({
- runner: {
- contactedAt: null,
- status: STATUS_ONLINE,
- },
- });
-
- expect(wrapper.text()).toBe(I18N_STATUS_ONLINE);
- expect(getTooltip().value).toBe('Runner is online; last contact was never');
- });
-
- it('status is missing', () => {
- createComponent({
- runner: {
- status: null,
- },
- });
-
- expect(wrapper.text()).toBe('');
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_status_popover_spec.js b/spec/frontend/runner/components/runner_status_popover_spec.js
deleted file mode 100644
index 789283d1245..00000000000
--- a/spec/frontend/runner/components/runner_status_popover_spec.js
+++ /dev/null
@@ -1,36 +0,0 @@
-import { GlSprintf } from '@gitlab/ui';
-import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
-import RunnerStatusPopover from '~/runner/components/runner_status_popover.vue';
-import HelpPopover from '~/vue_shared/components/help_popover.vue';
-import { onlineContactTimeoutSecs, staleTimeoutSecs } from '../mock_data';
-
-describe('RunnerStatusPopover', () => {
- let wrapper;
-
- const createComponent = ({ provide = {} } = {}) => {
- wrapper = shallowMountExtended(RunnerStatusPopover, {
- provide: {
- onlineContactTimeoutSecs,
- staleTimeoutSecs,
- ...provide,
- },
- stubs: {
- GlSprintf,
- },
- });
- };
-
- const findHelpPopover = () => wrapper.findComponent(HelpPopover);
-
- it('renders popoover', () => {
- createComponent();
-
- expect(findHelpPopover().exists()).toBe(true);
- });
-
- it('renders complete text', () => {
- createComponent();
-
- expect(findHelpPopover().text()).toMatchSnapshot();
- });
-});
diff --git a/spec/frontend/runner/components/runner_tag_spec.js b/spec/frontend/runner/components/runner_tag_spec.js
deleted file mode 100644
index 391c17f81cb..00000000000
--- a/spec/frontend/runner/components/runner_tag_spec.js
+++ /dev/null
@@ -1,79 +0,0 @@
-import { GlBadge } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import { nextTick } from 'vue';
-
-import { RUNNER_TAG_BADGE_VARIANT } from '~/runner/constants';
-import RunnerTag from '~/runner/components/runner_tag.vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-
-const mockTag = 'tag1';
-
-describe('RunnerTag', () => {
- let wrapper;
-
- const findBadge = () => wrapper.findComponent(GlBadge);
- const getTooltipValue = () => getBinding(findBadge().element, 'gl-tooltip').value;
-
- const setDimensions = ({ scrollWidth, offsetWidth }) => {
- jest.spyOn(findBadge().element, 'scrollWidth', 'get').mockReturnValue(scrollWidth);
- jest.spyOn(findBadge().element, 'offsetWidth', 'get').mockReturnValue(offsetWidth);
-
- // Mock trigger resize
- getBinding(findBadge().element, 'gl-resize-observer').value();
- };
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMount(RunnerTag, {
- propsData: {
- tag: mockTag,
- ...props,
- },
- directives: {
- GlTooltip: createMockDirective(),
- GlResizeObserver: createMockDirective(),
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays tag text', () => {
- expect(wrapper.text()).toBe(mockTag);
- });
-
- it('Displays tags with correct style', () => {
- expect(findBadge().props()).toMatchObject({
- size: 'sm',
- variant: RUNNER_TAG_BADGE_VARIANT,
- });
- });
-
- it('Displays tags with md size', () => {
- createComponent({
- props: { size: 'md' },
- });
-
- expect(findBadge().props('size')).toBe('md');
- });
-
- it.each`
- case | scrollWidth | offsetWidth | expectedTooltip
- ${'overflowing'} | ${110} | ${100} | ${mockTag}
- ${'not overflowing'} | ${90} | ${100} | ${''}
- ${'almost overflowing'} | ${100} | ${100} | ${''}
- `(
- 'Sets "$expectedTooltip" as tooltip when $case',
- async ({ scrollWidth, offsetWidth, expectedTooltip }) => {
- setDimensions({ scrollWidth, offsetWidth });
- await nextTick();
-
- expect(getTooltipValue()).toBe(expectedTooltip);
- },
- );
-});
diff --git a/spec/frontend/runner/components/runner_tags_spec.js b/spec/frontend/runner/components/runner_tags_spec.js
deleted file mode 100644
index c6bfabdb18a..00000000000
--- a/spec/frontend/runner/components/runner_tags_spec.js
+++ /dev/null
@@ -1,54 +0,0 @@
-import { GlBadge } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import RunnerTags from '~/runner/components/runner_tags.vue';
-
-describe('RunnerTags', () => {
- let wrapper;
-
- const findBadge = () => wrapper.findComponent(GlBadge);
- const findBadgesAt = (i = 0) => wrapper.findAllComponents(GlBadge).at(i);
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = mount(RunnerTags, {
- propsData: {
- tagList: ['tag1', 'tag2'],
- ...props,
- },
- });
- };
-
- beforeEach(() => {
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays tags text', () => {
- expect(wrapper.text()).toMatchInterpolatedText('tag1 tag2');
-
- expect(findBadgesAt(0).text()).toBe('tag1');
- expect(findBadgesAt(1).text()).toBe('tag2');
- });
-
- it('Displays tags with correct style', () => {
- expect(findBadge().props('size')).toBe('sm');
- });
-
- it('Displays tags with md size', () => {
- createComponent({
- props: { size: 'md' },
- });
-
- expect(findBadge().props('size')).toBe('md');
- });
-
- it('Is empty when there are no tags', () => {
- createComponent({
- props: { tagList: null },
- });
-
- expect(wrapper.html()).toEqual('');
- });
-});
diff --git a/spec/frontend/runner/components/runner_type_badge_spec.js b/spec/frontend/runner/components/runner_type_badge_spec.js
deleted file mode 100644
index fe922fb9d18..00000000000
--- a/spec/frontend/runner/components/runner_type_badge_spec.js
+++ /dev/null
@@ -1,66 +0,0 @@
-import { GlBadge } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
-import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import {
- INSTANCE_TYPE,
- GROUP_TYPE,
- PROJECT_TYPE,
- I18N_INSTANCE_TYPE,
- I18N_GROUP_TYPE,
- I18N_PROJECT_TYPE,
-} from '~/runner/constants';
-
-describe('RunnerTypeBadge', () => {
- let wrapper;
-
- const findBadge = () => wrapper.findComponent(GlBadge);
- const getTooltip = () => getBinding(findBadge().element, 'gl-tooltip');
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMount(RunnerTypeBadge, {
- propsData: {
- ...props,
- },
- directives: {
- GlTooltip: createMockDirective(),
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe.each`
- type | text
- ${INSTANCE_TYPE} | ${I18N_INSTANCE_TYPE}
- ${GROUP_TYPE} | ${I18N_GROUP_TYPE}
- ${PROJECT_TYPE} | ${I18N_PROJECT_TYPE}
- `('displays $type runner', ({ type, text }) => {
- beforeEach(() => {
- createComponent({ props: { type } });
- });
-
- it(`as "${text}" with an "info" variant`, () => {
- expect(findBadge().text()).toBe(text);
- expect(findBadge().props('variant')).toBe('muted');
- });
-
- it('with a tooltip', () => {
- expect(getTooltip().value).toBeDefined();
- });
- });
-
- it('validation fails for an incorrect type', () => {
- expect(() => {
- createComponent({ props: { type: 'AN_UNKNOWN_VALUE' } });
- }).toThrow();
- });
-
- it('does not render content when type is missing', () => {
- createComponent({ props: { type: undefined } });
-
- expect(findBadge().exists()).toBe(false);
- });
-});
diff --git a/spec/frontend/runner/components/runner_type_tabs_spec.js b/spec/frontend/runner/components/runner_type_tabs_spec.js
deleted file mode 100644
index dde35533bc3..00000000000
--- a/spec/frontend/runner/components/runner_type_tabs_spec.js
+++ /dev/null
@@ -1,214 +0,0 @@
-import { GlTab } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RunnerTypeTabs from '~/runner/components/runner_type_tabs.vue';
-import RunnerCount from '~/runner/components/stat/runner_count.vue';
-import {
- INSTANCE_TYPE,
- GROUP_TYPE,
- PROJECT_TYPE,
- DEFAULT_MEMBERSHIP,
- DEFAULT_SORT,
-} from '~/runner/constants';
-
-const mockSearch = {
- runnerType: null,
- membership: DEFAULT_MEMBERSHIP,
- filters: [],
- pagination: { page: 1 },
- sort: DEFAULT_SORT,
-};
-
-const mockCount = (type, multiplier = 1) => {
- let count;
- switch (type) {
- case INSTANCE_TYPE:
- count = 3;
- break;
- case GROUP_TYPE:
- count = 2;
- break;
- case PROJECT_TYPE:
- count = 1;
- break;
- default:
- count = 6;
- break;
- }
- return count * multiplier;
-};
-
-describe('RunnerTypeTabs', () => {
- let wrapper;
-
- const findTabs = () => wrapper.findAllComponents(GlTab);
- const findActiveTab = () =>
- findTabs()
- .filter((tab) => tab.attributes('active') === 'true')
- .at(0);
- const getTabsTitles = () => findTabs().wrappers.map((tab) => tab.text().replace(/\s+/g, ' '));
-
- const createComponent = ({ props, stubs, ...options } = {}) => {
- wrapper = shallowMount(RunnerTypeTabs, {
- propsData: {
- value: mockSearch,
- countScope: INSTANCE_TYPE,
- countVariables: {},
- ...props,
- },
- stubs: {
- GlTab,
- ...stubs,
- },
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Renders all options to filter runners by default', () => {
- createComponent();
-
- expect(getTabsTitles()).toEqual(['All', 'Instance', 'Group', 'Project']);
- });
-
- it('Shows count when receiving a number', () => {
- createComponent({
- stubs: {
- RunnerCount: {
- props: ['variables'],
- render() {
- return this.$scopedSlots.default({
- count: mockCount(this.variables.type),
- });
- },
- },
- },
- });
-
- expect(getTabsTitles()).toEqual([`All 6`, `Instance 3`, `Group 2`, `Project 1`]);
- });
-
- it('Shows formatted count when receiving a large number', () => {
- createComponent({
- stubs: {
- RunnerCount: {
- props: ['variables'],
- render() {
- return this.$scopedSlots.default({
- count: mockCount(this.variables.type, 1000),
- });
- },
- },
- },
- });
-
- expect(getTabsTitles()).toEqual([
- `All 6,000`,
- `Instance 3,000`,
- `Group 2,000`,
- `Project 1,000`,
- ]);
- });
-
- it('Renders a count next to each tab', () => {
- const mockVariables = {
- paused: true,
- status: 'ONLINE',
- };
-
- createComponent({
- props: {
- countVariables: mockVariables,
- },
- });
-
- findTabs().wrappers.forEach((tab) => {
- expect(tab.findComponent(RunnerCount).props()).toEqual({
- scope: INSTANCE_TYPE,
- skip: false,
- variables: expect.objectContaining(mockVariables),
- });
- });
- });
-
- it('Renders fewer options to filter runners', () => {
- createComponent({
- props: {
- runnerTypes: [GROUP_TYPE, PROJECT_TYPE],
- },
- });
-
- expect(getTabsTitles()).toEqual(['All', 'Group', 'Project']);
- });
-
- it('"All" is selected by default', () => {
- createComponent();
-
- expect(findActiveTab().text()).toBe('All');
- });
-
- it('Another tab can be preselected by the user', () => {
- createComponent({
- props: {
- value: {
- ...mockSearch,
- runnerType: INSTANCE_TYPE,
- },
- },
- });
-
- expect(findActiveTab().text()).toBe('Instance');
- });
-
- describe('When the user selects a tab', () => {
- const emittedValue = () => wrapper.emitted('input')[0][0];
-
- beforeEach(() => {
- createComponent();
- findTabs().at(2).vm.$emit('click');
- });
-
- it(`Runner type is emitted`, () => {
- expect(emittedValue()).toEqual({
- ...mockSearch,
- runnerType: GROUP_TYPE,
- });
- });
-
- it('Runner type is selected', async () => {
- const newValue = emittedValue();
- await wrapper.setProps({ value: newValue });
-
- expect(findActiveTab().text()).toBe('Group');
- });
- });
-
- describe('Component API', () => {
- describe('When .refetch() is called', () => {
- let mockRefetch;
-
- beforeEach(() => {
- mockRefetch = jest.fn();
-
- createComponent({
- stubs: {
- RunnerCount: {
- methods: {
- refetch: mockRefetch,
- },
- render() {},
- },
- },
- });
-
- wrapper.vm.refetch();
- });
-
- it('refetch is called for each count', () => {
- expect(mockRefetch).toHaveBeenCalledTimes(4);
- });
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_update_form_spec.js b/spec/frontend/runner/components/runner_update_form_spec.js
deleted file mode 100644
index e12736216a0..00000000000
--- a/spec/frontend/runner/components/runner_update_form_spec.js
+++ /dev/null
@@ -1,288 +0,0 @@
-import Vue, { nextTick } from 'vue';
-import { GlForm, GlSkeletonLoader } from '@gitlab/ui';
-import VueApollo from 'vue-apollo';
-import { __ } from '~/locale';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import { mountExtended } from 'helpers/vue_test_utils_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert, VARIANT_SUCCESS } from '~/flash';
-import { redirectTo } from '~/lib/utils/url_utility';
-import RunnerUpdateForm from '~/runner/components/runner_update_form.vue';
-import {
- INSTANCE_TYPE,
- GROUP_TYPE,
- PROJECT_TYPE,
- ACCESS_LEVEL_REF_PROTECTED,
- ACCESS_LEVEL_NOT_PROTECTED,
-} from '~/runner/constants';
-import runnerUpdateMutation from '~/runner/graphql/edit/runner_update.mutation.graphql';
-import { captureException } from '~/runner/sentry_utils';
-import { saveAlertToLocalStorage } from '~/runner/local_storage_alert/save_alert_to_local_storage';
-import { runnerFormData } from '../mock_data';
-
-jest.mock('~/runner/local_storage_alert/save_alert_to_local_storage');
-jest.mock('~/flash');
-jest.mock('~/runner/sentry_utils');
-jest.mock('~/lib/utils/url_utility');
-
-const mockRunner = runnerFormData.data.runner;
-const mockRunnerPath = '/admin/runners/1';
-
-Vue.use(VueApollo);
-
-describe('RunnerUpdateForm', () => {
- let wrapper;
- let runnerUpdateHandler;
-
- const findForm = () => wrapper.findComponent(GlForm);
- const findPausedCheckbox = () => wrapper.findByTestId('runner-field-paused');
- const findProtectedCheckbox = () => wrapper.findByTestId('runner-field-protected');
- const findRunUntaggedCheckbox = () => wrapper.findByTestId('runner-field-run-untagged');
- const findLockedCheckbox = () => wrapper.findByTestId('runner-field-locked');
- const findFields = () => wrapper.findAll('[data-testid^="runner-field"');
-
- const findDescriptionInput = () => wrapper.findByTestId('runner-field-description').find('input');
- const findMaxJobTimeoutInput = () =>
- wrapper.findByTestId('runner-field-max-timeout').find('input');
- const findTagsInput = () => wrapper.findByTestId('runner-field-tags').find('input');
-
- const findSubmit = () => wrapper.find('[type="submit"]');
- const findSubmitDisabledAttr = () => findSubmit().attributes('disabled');
- const findCancelBtn = () => wrapper.findByRole('link', { name: __('Cancel') });
- const submitForm = () => findForm().trigger('submit');
- const submitFormAndWait = () => submitForm().then(waitForPromises);
-
- const getFieldsModel = () => ({
- active: !findPausedCheckbox().element.checked,
- accessLevel: findProtectedCheckbox().element.checked
- ? ACCESS_LEVEL_REF_PROTECTED
- : ACCESS_LEVEL_NOT_PROTECTED,
- runUntagged: findRunUntaggedCheckbox().element.checked,
- locked: findLockedCheckbox().element?.checked || false,
- maximumTimeout: findMaxJobTimeoutInput().element.value || null,
- tagList: findTagsInput().element.value.split(',').filter(Boolean),
- });
-
- const createComponent = ({ props } = {}) => {
- wrapper = mountExtended(RunnerUpdateForm, {
- propsData: {
- runner: mockRunner,
- runnerPath: mockRunnerPath,
- ...props,
- },
- apolloProvider: createMockApollo([[runnerUpdateMutation, runnerUpdateHandler]]),
- });
- };
-
- const expectToHaveSubmittedRunnerContaining = (submittedRunner) => {
- expect(runnerUpdateHandler).toHaveBeenCalledTimes(1);
- expect(runnerUpdateHandler).toHaveBeenCalledWith({
- input: expect.objectContaining(submittedRunner),
- });
-
- expect(saveAlertToLocalStorage).toHaveBeenCalledWith(
- expect.objectContaining({
- message: expect.any(String),
- variant: VARIANT_SUCCESS,
- }),
- );
- expect(redirectTo).toHaveBeenCalledWith(mockRunnerPath);
- };
-
- beforeEach(() => {
- runnerUpdateHandler = jest.fn().mockImplementation(({ input }) => {
- return Promise.resolve({
- data: {
- runnerUpdate: {
- runner: {
- ...mockRunner,
- ...input,
- },
- errors: [],
- },
- },
- });
- });
-
- createComponent();
- });
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Form has a submit button', () => {
- expect(findSubmit().exists()).toBe(true);
- });
-
- it('Form fields match data', () => {
- expect(mockRunner).toMatchObject(getFieldsModel());
- });
-
- it('Form shows a cancel button', () => {
- expect(runnerUpdateHandler).not.toHaveBeenCalled();
- expect(findCancelBtn().attributes('href')).toBe(mockRunnerPath);
- });
-
- it('Form prevent multiple submissions', async () => {
- await submitForm();
-
- expect(findSubmitDisabledAttr()).toBe('disabled');
- });
-
- it('Updates runner with no changes', async () => {
- await submitFormAndWait();
-
- // Some read-only fields are not submitted
- const { __typename, shortSha, runnerType, createdAt, status, ...submitted } = mockRunner;
-
- expectToHaveSubmittedRunnerContaining(submitted);
- });
-
- describe('When data is being loaded', () => {
- beforeEach(() => {
- createComponent({ props: { loading: true } });
- });
-
- it('Form skeleton is shown', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
- expect(findFields()).toHaveLength(0);
- });
-
- it('Form cannot be submitted', () => {
- expect(findSubmit().props('loading')).toBe(true);
- });
-
- it('Form is updated when data loads', async () => {
- wrapper.setProps({
- loading: false,
- });
-
- await nextTick();
-
- expect(findFields()).not.toHaveLength(0);
- expect(mockRunner).toMatchObject(getFieldsModel());
- });
- });
-
- it.each`
- runnerType | exists | outcome
- ${INSTANCE_TYPE} | ${false} | ${'hidden'}
- ${GROUP_TYPE} | ${false} | ${'hidden'}
- ${PROJECT_TYPE} | ${true} | ${'shown'}
- `(`When runner is $runnerType, locked field is $outcome`, ({ runnerType, exists }) => {
- const runner = { ...mockRunner, runnerType };
- createComponent({ props: { runner } });
-
- expect(findLockedCheckbox().exists()).toBe(exists);
- });
-
- describe('On submit, runner gets updated', () => {
- it.each`
- test | initialValue | findCheckbox | checked | submitted
- ${'pauses'} | ${{ active: true }} | ${findPausedCheckbox} | ${true} | ${{ active: false }}
- ${'activates'} | ${{ active: false }} | ${findPausedCheckbox} | ${false} | ${{ active: true }}
- ${'unprotects'} | ${{ accessLevel: ACCESS_LEVEL_NOT_PROTECTED }} | ${findProtectedCheckbox} | ${true} | ${{ accessLevel: ACCESS_LEVEL_REF_PROTECTED }}
- ${'protects'} | ${{ accessLevel: ACCESS_LEVEL_REF_PROTECTED }} | ${findProtectedCheckbox} | ${false} | ${{ accessLevel: ACCESS_LEVEL_NOT_PROTECTED }}
- ${'"runs untagged jobs"'} | ${{ runUntagged: true }} | ${findRunUntaggedCheckbox} | ${false} | ${{ runUntagged: false }}
- ${'"runs tagged jobs"'} | ${{ runUntagged: false }} | ${findRunUntaggedCheckbox} | ${true} | ${{ runUntagged: true }}
- ${'locks'} | ${{ runnerType: PROJECT_TYPE, locked: true }} | ${findLockedCheckbox} | ${false} | ${{ locked: false }}
- ${'unlocks'} | ${{ runnerType: PROJECT_TYPE, locked: false }} | ${findLockedCheckbox} | ${true} | ${{ locked: true }}
- `('Checkbox $test runner', async ({ initialValue, findCheckbox, checked, submitted }) => {
- const runner = { ...mockRunner, ...initialValue };
- createComponent({ props: { runner } });
-
- await findCheckbox().setChecked(checked);
- await submitFormAndWait();
-
- expectToHaveSubmittedRunnerContaining({
- id: runner.id,
- ...submitted,
- });
- });
-
- it.each`
- test | initialValue | findInput | value | submitted
- ${'description'} | ${{ description: 'Desc. 1' }} | ${findDescriptionInput} | ${'Desc. 2'} | ${{ description: 'Desc. 2' }}
- ${'max timeout'} | ${{ maximumTimeout: 36000 }} | ${findMaxJobTimeoutInput} | ${'40000'} | ${{ maximumTimeout: 40000 }}
- ${'tags'} | ${{ tagList: ['tag1'] }} | ${findTagsInput} | ${'tag2, tag3'} | ${{ tagList: ['tag2', 'tag3'] }}
- `("Field updates runner's $test", async ({ initialValue, findInput, value, submitted }) => {
- const runner = { ...mockRunner, ...initialValue };
- createComponent({ props: { runner } });
-
- await findInput().setValue(value);
- await submitFormAndWait();
-
- expectToHaveSubmittedRunnerContaining({
- id: runner.id,
- ...submitted,
- });
- });
-
- it.each`
- value | submitted
- ${''} | ${{ tagList: [] }}
- ${'tag1, tag2'} | ${{ tagList: ['tag1', 'tag2'] }}
- ${'with spaces'} | ${{ tagList: ['with spaces'] }}
- ${'more ,,,,, commas'} | ${{ tagList: ['more', 'commas'] }}
- `('Field updates runner\'s tags for "$value"', async ({ value, submitted }) => {
- const runner = { ...mockRunner, tagList: ['tag1'] };
- createComponent({ props: { runner } });
-
- await findTagsInput().setValue(value);
- await submitFormAndWait();
-
- expectToHaveSubmittedRunnerContaining({
- id: runner.id,
- ...submitted,
- });
- });
- });
-
- describe('On error', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('On network error, error message is shown', async () => {
- const mockErrorMsg = 'Update error!';
-
- runnerUpdateHandler.mockRejectedValue(new Error(mockErrorMsg));
-
- await submitFormAndWait();
-
- expect(createAlert).toHaveBeenLastCalledWith({
- message: mockErrorMsg,
- });
- expect(captureException).toHaveBeenCalledWith({
- component: 'RunnerUpdateForm',
- error: new Error(mockErrorMsg),
- });
- expect(findSubmitDisabledAttr()).toBeUndefined();
- });
-
- it('On validation error, error message is shown and it is not sent to sentry', async () => {
- const mockErrorMsg = 'Invalid value!';
-
- runnerUpdateHandler.mockResolvedValue({
- data: {
- runnerUpdate: {
- runner: mockRunner,
- errors: [mockErrorMsg],
- },
- },
- });
-
- await submitFormAndWait();
-
- expect(createAlert).toHaveBeenLastCalledWith({
- message: mockErrorMsg,
- });
- expect(findSubmitDisabledAttr()).toBeUndefined();
-
- expect(captureException).not.toHaveBeenCalled();
- expect(saveAlertToLocalStorage).not.toHaveBeenCalled();
- expect(redirectTo).not.toHaveBeenCalled();
- });
- });
-});
diff --git a/spec/frontend/runner/components/search_tokens/tag_token_spec.js b/spec/frontend/runner/components/search_tokens/tag_token_spec.js
deleted file mode 100644
index a7363eb11cd..00000000000
--- a/spec/frontend/runner/components/search_tokens/tag_token_spec.js
+++ /dev/null
@@ -1,208 +0,0 @@
-import { GlFilteredSearchSuggestion, GlLoadingIcon, GlToken } from '@gitlab/ui';
-import { mount } from '@vue/test-utils';
-import MockAdapter from 'axios-mock-adapter';
-import { nextTick } from 'vue';
-import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert } from '~/flash';
-import axios from '~/lib/utils/axios_utils';
-
-import TagToken, { TAG_SUGGESTIONS_PATH } from '~/runner/components/search_tokens/tag_token.vue';
-import { OPERATOR_IS_ONLY } from '~/vue_shared/components/filtered_search_bar/constants';
-import { getRecentlyUsedSuggestions } from '~/vue_shared/components/filtered_search_bar/filtered_search_utils';
-
-jest.mock('~/flash');
-
-jest.mock('~/vue_shared/components/filtered_search_bar/filtered_search_utils', () => ({
- ...jest.requireActual('~/vue_shared/components/filtered_search_bar/filtered_search_utils'),
- getRecentlyUsedSuggestions: jest.fn(),
-}));
-
-const mockStorageKey = 'stored-recent-tags';
-
-const mockTags = [
- { id: 1, name: 'linux' },
- { id: 2, name: 'windows' },
- { id: 3, name: 'mac' },
-];
-
-const mockTagsFiltered = [mockTags[0]];
-
-const mockSearchTerm = mockTags[0].name;
-
-const GlFilteredSearchTokenStub = {
- template: `<div>
- <slot name="view-token"></slot>
- <slot name="suggestions"></slot>
- </div>`,
-};
-
-const mockTagTokenConfig = {
- icon: 'tag',
- title: 'Tags',
- type: 'tag',
- token: TagToken,
- recentSuggestionsStorageKey: mockStorageKey,
- operators: OPERATOR_IS_ONLY,
-};
-
-describe('TagToken', () => {
- let mock;
- let wrapper;
-
- const createComponent = (props = {}) => {
- wrapper = mount(TagToken, {
- propsData: {
- config: mockTagTokenConfig,
- value: { data: '' },
- active: false,
- ...props,
- },
- provide: {
- portalName: 'fake target',
- alignSuggestions: function fakeAlignSuggestions() {},
- filteredSearchSuggestionListInstance: {
- register: jest.fn(),
- unregister: jest.fn(),
- },
- },
- stubs: {
- GlFilteredSearchToken: GlFilteredSearchTokenStub,
- },
- });
- };
-
- const findGlFilteredSearchSuggestions = () =>
- wrapper.findAllComponents(GlFilteredSearchSuggestion);
- const findGlFilteredSearchToken = () => wrapper.findComponent(GlFilteredSearchTokenStub);
- const findToken = () => wrapper.findComponent(GlToken);
- const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
-
- beforeEach(() => {
- mock = new MockAdapter(axios);
-
- mock.onGet(TAG_SUGGESTIONS_PATH, { params: { search: '' } }).reply(200, mockTags);
- mock
- .onGet(TAG_SUGGESTIONS_PATH, { params: { search: mockSearchTerm } })
- .reply(200, mockTagsFiltered);
-
- getRecentlyUsedSuggestions.mockReturnValue([]);
- });
-
- afterEach(() => {
- getRecentlyUsedSuggestions.mockReset();
- wrapper.destroy();
- });
-
- describe('when the tags token is displayed', () => {
- beforeEach(() => {
- createComponent();
- });
-
- it('requests tags suggestions', () => {
- expect(mock.history.get[0].params).toEqual({ search: '' });
- });
-
- it('displays tags suggestions', async () => {
- await waitForPromises();
-
- mockTags.forEach(({ name }, i) => {
- expect(findGlFilteredSearchSuggestions().at(i).text()).toBe(name);
- });
- });
- });
-
- describe('when suggestions are stored', () => {
- const storedSuggestions = [{ id: 4, value: 'docker', text: 'docker' }];
-
- beforeEach(async () => {
- getRecentlyUsedSuggestions.mockReturnValue(storedSuggestions);
-
- createComponent();
- await waitForPromises();
- });
-
- it('suggestions are loaded from a correct key', () => {
- expect(getRecentlyUsedSuggestions).toHaveBeenCalledWith(mockStorageKey);
- });
-
- it('displays stored tags suggestions', () => {
- expect(findGlFilteredSearchSuggestions()).toHaveLength(
- mockTags.length + storedSuggestions.length,
- );
-
- expect(findGlFilteredSearchSuggestions().at(0).text()).toBe(storedSuggestions[0].text);
- });
- });
-
- describe('when the users filters suggestions', () => {
- beforeEach(() => {
- createComponent();
-
- findGlFilteredSearchToken().vm.$emit('input', { data: mockSearchTerm });
- });
-
- it('requests filtered tags suggestions', () => {
- expect(mock.history.get[1].params).toEqual({ search: mockSearchTerm });
- });
-
- it('shows the loading icon', async () => {
- findGlFilteredSearchToken().vm.$emit('input', { data: mockSearchTerm });
- await nextTick();
-
- expect(findGlLoadingIcon().exists()).toBe(true);
- });
-
- it('displays filtered tags suggestions', async () => {
- await waitForPromises();
-
- expect(findGlFilteredSearchSuggestions()).toHaveLength(mockTagsFiltered.length);
-
- expect(findGlFilteredSearchSuggestions().at(0).text()).toBe(mockTagsFiltered[0].name);
- });
- });
-
- describe('when suggestions cannot be loaded', () => {
- beforeEach(async () => {
- mock.onGet(TAG_SUGGESTIONS_PATH, { params: { search: '' } }).reply(500);
-
- createComponent();
- await waitForPromises();
- });
-
- it('error is shown', () => {
- expect(createAlert).toHaveBeenCalledTimes(1);
- expect(createAlert).toHaveBeenCalledWith({ message: expect.any(String) });
- });
- });
-
- describe('when the user selects a value', () => {
- beforeEach(async () => {
- createComponent({ value: { data: mockTags[0].name } });
- findGlFilteredSearchToken().vm.$emit('select');
-
- await waitForPromises();
- });
-
- it('selected tag is displayed', () => {
- expect(findToken().exists()).toBe(true);
- });
- });
-
- describe('when suggestions are disabled', () => {
- beforeEach(async () => {
- createComponent({
- config: {
- ...mockTagTokenConfig,
- suggestionsDisabled: true,
- },
- });
-
- await waitForPromises();
- });
-
- it('displays no suggestions', () => {
- expect(findGlFilteredSearchSuggestions()).toHaveLength(0);
- expect(mock.history.get).toHaveLength(0);
- });
- });
-});
diff --git a/spec/frontend/runner/components/stat/runner_count_spec.js b/spec/frontend/runner/components/stat/runner_count_spec.js
deleted file mode 100644
index 2a6a745099f..00000000000
--- a/spec/frontend/runner/components/stat/runner_count_spec.js
+++ /dev/null
@@ -1,148 +0,0 @@
-import Vue, { nextTick } from 'vue';
-import VueApollo from 'vue-apollo';
-import { shallowMount } from '@vue/test-utils';
-import RunnerCount from '~/runner/components/stat/runner_count.vue';
-import { INSTANCE_TYPE, GROUP_TYPE } from '~/runner/constants';
-import createMockApollo from 'helpers/mock_apollo_helper';
-import waitForPromises from 'helpers/wait_for_promises';
-import { captureException } from '~/runner/sentry_utils';
-
-import allRunnersCountQuery from 'ee_else_ce/runner/graphql/list/all_runners_count.query.graphql';
-import groupRunnersCountQuery from 'ee_else_ce/runner/graphql/list/group_runners_count.query.graphql';
-
-import { runnersCountData, groupRunnersCountData } from '../../mock_data';
-
-jest.mock('~/runner/sentry_utils');
-
-Vue.use(VueApollo);
-
-describe('RunnerCount', () => {
- let wrapper;
- let mockRunnersCountHandler;
- let mockGroupRunnersCountHandler;
-
- const createComponent = ({ props = {}, ...options } = {}) => {
- const handlers = [
- [allRunnersCountQuery, mockRunnersCountHandler],
- [groupRunnersCountQuery, mockGroupRunnersCountHandler],
- ];
-
- wrapper = shallowMount(RunnerCount, {
- apolloProvider: createMockApollo(handlers),
- propsData: {
- ...props,
- },
- scopedSlots: {
- default: '<strong>{{props.count}}</strong>',
- },
- ...options,
- });
-
- return waitForPromises();
- };
-
- beforeEach(() => {
- mockRunnersCountHandler = jest.fn().mockResolvedValue(runnersCountData);
- mockGroupRunnersCountHandler = jest.fn().mockResolvedValue(groupRunnersCountData);
- });
-
- describe('in admin scope', () => {
- const mockVariables = { status: 'ONLINE' };
-
- beforeEach(async () => {
- await createComponent({ props: { scope: INSTANCE_TYPE } });
- });
-
- it('fetches data from admin query', () => {
- expect(mockRunnersCountHandler).toHaveBeenCalledTimes(1);
- expect(mockRunnersCountHandler).toHaveBeenCalledWith({});
- });
-
- it('fetches data with filters', async () => {
- await createComponent({ props: { scope: INSTANCE_TYPE, variables: mockVariables } });
-
- expect(mockRunnersCountHandler).toHaveBeenCalledTimes(2);
- expect(mockRunnersCountHandler).toHaveBeenCalledWith(mockVariables);
-
- expect(wrapper.html()).toBe(`<strong>${runnersCountData.data.runners.count}</strong>`);
- });
-
- it('does not fetch from the group query', async () => {
- expect(mockGroupRunnersCountHandler).not.toHaveBeenCalled();
- });
-
- describe('when this query is skipped after data was loaded', () => {
- beforeEach(async () => {
- wrapper.setProps({ skip: true });
-
- await nextTick();
- });
-
- it('clears current data', () => {
- expect(wrapper.html()).toBe('<strong></strong>');
- });
- });
- });
-
- describe('when skipping query', () => {
- beforeEach(async () => {
- await createComponent({ props: { scope: INSTANCE_TYPE, skip: true } });
- });
-
- it('does not fetch data', async () => {
- expect(mockRunnersCountHandler).not.toHaveBeenCalled();
- expect(mockGroupRunnersCountHandler).not.toHaveBeenCalled();
-
- expect(wrapper.html()).toBe('<strong></strong>');
- });
- });
-
- describe('when runners query fails', () => {
- const mockError = new Error('error!');
-
- beforeEach(async () => {
- mockRunnersCountHandler.mockRejectedValue(mockError);
-
- await createComponent({ props: { scope: INSTANCE_TYPE } });
- });
-
- it('data is not shown and error is reported', async () => {
- expect(wrapper.html()).toBe('<strong></strong>');
-
- expect(captureException).toHaveBeenCalledWith({
- component: 'RunnerCount',
- error: mockError,
- });
- });
- });
-
- describe('in group scope', () => {
- beforeEach(async () => {
- await createComponent({ props: { scope: GROUP_TYPE } });
- });
-
- it('fetches data from the group query', async () => {
- expect(mockGroupRunnersCountHandler).toHaveBeenCalledTimes(1);
- expect(mockGroupRunnersCountHandler).toHaveBeenCalledWith({});
-
- expect(wrapper.html()).toBe(
- `<strong>${groupRunnersCountData.data.group.runners.count}</strong>`,
- );
- });
-
- it('does not fetch from the group query', () => {
- expect(mockRunnersCountHandler).not.toHaveBeenCalled();
- });
- });
-
- describe('when .refetch() is called', () => {
- beforeEach(async () => {
- await createComponent({ props: { scope: INSTANCE_TYPE } });
- wrapper.vm.refetch();
- });
-
- it('data is not shown and error is reported', async () => {
- expect(mockRunnersCountHandler).toHaveBeenCalledTimes(2);
- });
- });
-});
diff --git a/spec/frontend/runner/components/stat/runner_single_stat_spec.js b/spec/frontend/runner/components/stat/runner_single_stat_spec.js
deleted file mode 100644
index 964a6a6ff71..00000000000
--- a/spec/frontend/runner/components/stat/runner_single_stat_spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { GlSingleStat } from '@gitlab/ui/dist/charts';
-import { shallowMount } from '@vue/test-utils';
-import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue';
-import RunnerCount from '~/runner/components/stat/runner_count.vue';
-import { INSTANCE_TYPE, GROUP_TYPE } from '~/runner/constants';
-
-describe('RunnerStats', () => {
- let wrapper;
-
- const findRunnerCount = () => wrapper.findComponent(RunnerCount);
- const findGlSingleStat = () => wrapper.findComponent(GlSingleStat);
-
- const createComponent = ({ props = {}, count, mountFn = shallowMount, ...options } = {}) => {
- wrapper = mountFn(RunnerSingleStat, {
- propsData: {
- scope: INSTANCE_TYPE,
- title: 'My title',
- variables: {},
- ...props,
- },
- stubs: {
- RunnerCount: {
- props: ['scope', 'variables', 'skip'],
- render() {
- return this.$scopedSlots.default({
- count,
- });
- },
- },
- },
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it.each`
- case | count | value
- ${'number'} | ${99} | ${'99'}
- ${'long number'} | ${1000} | ${'1,000'}
- ${'empty number'} | ${null} | ${'-'}
- `('formats $case', ({ count, value }) => {
- createComponent({ count });
-
- expect(findGlSingleStat().props('value')).toBe(value);
- });
-
- it('Passes runner count props', () => {
- const props = {
- scope: GROUP_TYPE,
- variables: { paused: true },
- skip: true,
- };
-
- createComponent({ props });
-
- expect(findRunnerCount().props()).toEqual(props);
- });
-});
diff --git a/spec/frontend/runner/components/stat/runner_stats_spec.js b/spec/frontend/runner/components/stat/runner_stats_spec.js
deleted file mode 100644
index 4afbe453903..00000000000
--- a/spec/frontend/runner/components/stat/runner_stats_spec.js
+++ /dev/null
@@ -1,81 +0,0 @@
-import { shallowMount, mount } from '@vue/test-utils';
-import RunnerStats from '~/runner/components/stat/runner_stats.vue';
-import RunnerSingleStat from '~/runner/components/stat/runner_single_stat.vue';
-import {
- I18N_STATUS_ONLINE,
- I18N_STATUS_OFFLINE,
- I18N_STATUS_STALE,
- INSTANCE_TYPE,
- STATUS_ONLINE,
- STATUS_OFFLINE,
- STATUS_STALE,
-} from '~/runner/constants';
-
-describe('RunnerStats', () => {
- let wrapper;
-
- const findSingleStats = () => wrapper.findAllComponents(RunnerSingleStat);
-
- const createComponent = ({ props = {}, mountFn = shallowMount, ...options } = {}) => {
- wrapper = mountFn(RunnerStats, {
- propsData: {
- scope: INSTANCE_TYPE,
- variables: {},
- ...props,
- },
- ...options,
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Displays all the stats', () => {
- const mockCounts = {
- [STATUS_ONLINE]: 3,
- [STATUS_OFFLINE]: 2,
- [STATUS_STALE]: 1,
- };
-
- createComponent({
- mountFn: mount,
- stubs: {
- RunnerCount: {
- props: ['variables'],
- render() {
- return this.$scopedSlots.default({
- count: mockCounts[this.variables.status],
- });
- },
- },
- },
- });
-
- const text = wrapper.text();
- expect(text).toContain(`${I18N_STATUS_ONLINE} 3`);
- expect(text).toContain(`${I18N_STATUS_OFFLINE} 2`);
- expect(text).toContain(`${I18N_STATUS_STALE} 1`);
- });
-
- it('Skips query for other stats', () => {
- createComponent({
- props: {
- variables: { status: STATUS_ONLINE },
- },
- });
-
- expect(findSingleStats().at(0).props('skip')).toBe(false);
- expect(findSingleStats().at(1).props('skip')).toBe(true);
- expect(findSingleStats().at(2).props('skip')).toBe(true);
- });
-
- it('Displays all counts for filtered searches', () => {
- const mockVariables = { paused: true };
- createComponent({ props: { variables: mockVariables } });
-
- findSingleStats().wrappers.forEach((stat) => {
- expect(stat.props('variables')).toMatchObject(mockVariables);
- });
- });
-});