summaryrefslogtreecommitdiff
path: root/spec/frontend/runner
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/runner')
-rw-r--r--spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js (renamed from spec/frontend/runner/runner_detail/runner_details_app_spec.js)31
-rw-r--r--spec/frontend/runner/admin_runners/admin_runners_app_spec.js116
-rw-r--r--spec/frontend/runner/components/cells/runner_actions_cell_spec.js66
-rw-r--r--spec/frontend/runner/components/registration/registration_token_reset_dropdown_item_spec.js40
-rw-r--r--spec/frontend/runner/components/runner_header_spec.js93
-rw-r--r--spec/frontend/runner/components/runner_list_spec.js4
-rw-r--r--spec/frontend/runner/components/runner_status_badge_spec.js20
-rw-r--r--spec/frontend/runner/components/runner_type_alert_spec.js61
-rw-r--r--spec/frontend/runner/components/runner_update_form_spec.js12
-rw-r--r--spec/frontend/runner/components/search_tokens/tag_token_spec.js6
-rw-r--r--spec/frontend/runner/components/stat/runner_online_stat_spec.js34
-rw-r--r--spec/frontend/runner/components/stat/runner_stats_spec.js46
-rw-r--r--spec/frontend/runner/components/stat/runner_status_stat_spec.js67
-rw-r--r--spec/frontend/runner/group_runners/group_runners_app_spec.js50
-rw-r--r--spec/frontend/runner/mock_data.js4
-rw-r--r--spec/frontend/runner/runner_search_utils_spec.js18
-rw-r--r--spec/frontend/runner/runner_update_form_utils_spec.js (renamed from spec/frontend/runner/runner_detail/runner_update_form_utils_spec.js)7
17 files changed, 455 insertions, 220 deletions
diff --git a/spec/frontend/runner/runner_detail/runner_details_app_spec.js b/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js
index 1a1428e8cb1..ad0bce5c9af 100644
--- a/spec/frontend/runner/runner_detail/runner_details_app_spec.js
+++ b/spec/frontend/runner/admin_runner_edit/admin_runner_edit_app_spec.js
@@ -2,12 +2,12 @@ import { createLocalVue, mount, shallowMount } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
-import RunnerTypeBadge from '~/runner/components/runner_type_badge.vue';
+import RunnerHeader from '~/runner/components/runner_header.vue';
import getRunnerQuery from '~/runner/graphql/get_runner.query.graphql';
-import RunnerDetailsApp from '~/runner/runner_details/runner_details_app.vue';
+import AdminRunnerEditApp from '~//runner/admin_runner_edit/admin_runner_edit_app.vue';
import { captureException } from '~/runner/sentry_utils';
import { runnerData } from '../mock_data';
@@ -21,14 +21,14 @@ const mockRunnerId = `${getIdFromGraphQLId(mockRunnerGraphqlId)}`;
const localVue = createLocalVue();
localVue.use(VueApollo);
-describe('RunnerDetailsApp', () => {
+describe('AdminRunnerEditApp', () => {
let wrapper;
let mockRunnerQuery;
- const findRunnerTypeBadge = () => wrapper.findComponent(RunnerTypeBadge);
+ const findRunnerHeader = () => wrapper.findComponent(RunnerHeader);
const createComponentWithApollo = ({ props = {}, mountFn = shallowMount } = {}) => {
- wrapper = mountFn(RunnerDetailsApp, {
+ wrapper = mountFn(AdminRunnerEditApp, {
localVue,
apolloProvider: createMockApollo([[getRunnerQuery, mockRunnerQuery]]),
propsData: {
@@ -40,7 +40,7 @@ describe('RunnerDetailsApp', () => {
return waitForPromises();
};
- beforeEach(async () => {
+ beforeEach(() => {
mockRunnerQuery = jest.fn().mockResolvedValue(runnerData);
});
@@ -56,15 +56,16 @@ describe('RunnerDetailsApp', () => {
});
it('displays the runner id', async () => {
- await createComponentWithApollo();
+ await createComponentWithApollo({ mountFn: mount });
- expect(wrapper.text()).toContain(`Runner #${mockRunnerId}`);
+ expect(findRunnerHeader().text()).toContain(`Runner #${mockRunnerId} created`);
});
- it('displays the runner type', async () => {
+ it('displays the runner type and status', async () => {
await createComponentWithApollo({ mountFn: mount });
- expect(findRunnerTypeBadge().text()).toBe('shared');
+ expect(findRunnerHeader().text()).toContain(`never contacted`);
+ expect(findRunnerHeader().text()).toContain(`shared`);
});
describe('When there is an error', () => {
@@ -73,15 +74,15 @@ describe('RunnerDetailsApp', () => {
await createComponentWithApollo();
});
- it('error is reported to sentry', async () => {
+ it('error is reported to sentry', () => {
expect(captureException).toHaveBeenCalledWith({
error: new Error('Network error: Error!'),
- component: 'RunnerDetailsApp',
+ component: 'AdminRunnerEditApp',
});
});
- it('error is shown to the user', async () => {
- expect(createFlash).toHaveBeenCalled();
+ it('error is shown to the user', () => {
+ expect(createAlert).toHaveBeenCalled();
});
});
});
diff --git a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
index 7015fe809b0..42be691ba4c 100644
--- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
@@ -5,7 +5,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import setWindowLocation from 'helpers/set_window_location_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { updateHistory } from '~/lib/utils/url_utility';
@@ -13,6 +13,7 @@ import AdminRunnersApp from '~/runner/admin_runners/admin_runners_app.vue';
import RunnerTypeTabs from '~/runner/components/runner_type_tabs.vue';
import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue';
import RunnerList from '~/runner/components/runner_list.vue';
+import RunnerStats from '~/runner/components/stat/runner_stats.vue';
import RegistrationDropdown from '~/runner/components/registration/registration_dropdown.vue';
import RunnerPagination from '~/runner/components/runner_pagination.vue';
@@ -22,23 +23,21 @@ import {
CREATED_DESC,
DEFAULT_SORT,
INSTANCE_TYPE,
+ GROUP_TYPE,
+ PROJECT_TYPE,
PARAM_KEY_STATUS,
PARAM_KEY_TAG,
STATUS_ACTIVE,
RUNNER_PAGE_SIZE,
} from '~/runner/constants';
import getRunnersQuery from '~/runner/graphql/get_runners.query.graphql';
+import getRunnersCountQuery from '~/runner/graphql/get_runners_count.query.graphql';
import { captureException } from '~/runner/sentry_utils';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import { runnersData, runnersDataPaginated } from '../mock_data';
+import { runnersData, runnersCountData, runnersDataPaginated } from '../mock_data';
const mockRegistrationToken = 'MOCK_REGISTRATION_TOKEN';
-const mockActiveRunnersCount = '2';
-const mockAllRunnersCount = '6';
-const mockInstanceRunnersCount = '3';
-const mockGroupRunnersCount = '2';
-const mockProjectRunnersCount = '1';
jest.mock('~/flash');
jest.mock('~/runner/sentry_utils');
@@ -53,7 +52,9 @@ localVue.use(VueApollo);
describe('AdminRunnersApp', () => {
let wrapper;
let mockRunnersQuery;
+ let mockRunnersCountQuery;
+ const findRunnerStats = () => wrapper.findComponent(RunnerStats);
const findRegistrationDropdown = () => wrapper.findComponent(RegistrationDropdown);
const findRunnerTypeTabs = () => wrapper.findComponent(RunnerTypeTabs);
const findRunnerList = () => wrapper.findComponent(RunnerList);
@@ -65,27 +66,28 @@ describe('AdminRunnersApp', () => {
const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
- const handlers = [[getRunnersQuery, mockRunnersQuery]];
-
- wrapper = mountFn(AdminRunnersApp, {
- localVue,
- apolloProvider: createMockApollo(handlers),
- propsData: {
- registrationToken: mockRegistrationToken,
- activeRunnersCount: mockActiveRunnersCount,
- allRunnersCount: mockAllRunnersCount,
- instanceRunnersCount: mockInstanceRunnersCount,
- groupRunnersCount: mockGroupRunnersCount,
- projectRunnersCount: mockProjectRunnersCount,
- ...props,
- },
- });
+ const handlers = [
+ [getRunnersQuery, mockRunnersQuery],
+ [getRunnersCountQuery, mockRunnersCountQuery],
+ ];
+
+ wrapper = extendedWrapper(
+ mountFn(AdminRunnersApp, {
+ localVue,
+ apolloProvider: createMockApollo(handlers),
+ propsData: {
+ registrationToken: mockRegistrationToken,
+ ...props,
+ },
+ }),
+ );
};
beforeEach(async () => {
setWindowLocation('/admin/runners');
mockRunnersQuery = jest.fn().mockResolvedValue(runnersData);
+ mockRunnersCountQuery = jest.fn().mockResolvedValue(runnersCountData);
createComponent();
await waitForPromises();
});
@@ -95,13 +97,71 @@ describe('AdminRunnersApp', () => {
wrapper.destroy();
});
- it('shows the runner tabs with a runner count', async () => {
+ it('shows total runner counts', async () => {
createComponent({ mountFn: mount });
await waitForPromises();
+ const stats = findRunnerStats().text();
+
+ expect(stats).toMatch('Online runners 4');
+ expect(stats).toMatch('Offline runners 4');
+ expect(stats).toMatch('Stale runners 4');
+ });
+
+ it('shows the runner tabs with a runner count for each type', async () => {
+ mockRunnersCountQuery.mockImplementation(({ type }) => {
+ 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 Promise.resolve({ data: { runners: { count } } });
+ });
+
+ createComponent({ mountFn: mount });
+ await waitForPromises();
+
expect(findRunnerTypeTabs().text()).toMatchInterpolatedText(
- `All ${mockAllRunnersCount} Instance ${mockInstanceRunnersCount} Group ${mockGroupRunnersCount} Project ${mockProjectRunnersCount}`,
+ `All 6 Instance 3 Group 2 Project 1`,
+ );
+ });
+
+ it('shows the runner tabs with a formatted runner count', async () => {
+ mockRunnersCountQuery.mockImplementation(({ type }) => {
+ let count;
+ switch (type) {
+ case INSTANCE_TYPE:
+ count = 3000;
+ break;
+ case GROUP_TYPE:
+ count = 2000;
+ break;
+ case PROJECT_TYPE:
+ count = 1000;
+ break;
+ default:
+ count = 6000;
+ break;
+ }
+ return Promise.resolve({ data: { runners: { count } } });
+ });
+
+ createComponent({ mountFn: mount });
+ await waitForPromises();
+
+ expect(findRunnerTypeTabs().text()).toMatchInterpolatedText(
+ `All 6,000 Instance 3,000 Group 2,000 Project 1,000`,
);
});
@@ -152,12 +212,6 @@ describe('AdminRunnersApp', () => {
]);
});
- it('shows the active runner count', () => {
- createComponent({ mountFn: mount });
-
- expect(wrapper.text()).toMatch(new RegExp(`Online Runners ${mockActiveRunnersCount}`));
- });
-
describe('when a filter is preselected', () => {
beforeEach(async () => {
setWindowLocation(`?status[]=${STATUS_ACTIVE}&runner_type[]=${INSTANCE_TYPE}&tag[]=tag1`);
@@ -241,7 +295,7 @@ describe('AdminRunnersApp', () => {
});
it('error is shown to the user', async () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
it('error is reported to sentry', async () => {
diff --git a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
index 95c212cb0a9..4233d86c24c 100644
--- a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
+++ b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
@@ -4,7 +4,7 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
import { createMockDirective, getBinding } from 'helpers/vue_mock_directive';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { captureException } from '~/runner/sentry_utils';
@@ -40,15 +40,17 @@ describe('RunnerTypeCell', () => {
const findDeleteBtn = () => wrapper.findByTestId('delete-runner');
const getTooltip = (w) => getBinding(w.element, 'gl-tooltip')?.value;
- const createComponent = ({ active = true } = {}, options) => {
+ const createComponent = (runner = {}, options) => {
wrapper = extendedWrapper(
shallowMount(RunnerActionCell, {
propsData: {
runner: {
id: mockRunner.id,
shortSha: mockRunner.shortSha,
- adminUrl: mockRunner.adminUrl,
- active,
+ editAdminUrl: mockRunner.editAdminUrl,
+ userPermissions: mockRunner.userPermissions,
+ active: mockRunner.active,
+ ...runner,
},
},
localVue,
@@ -101,7 +103,26 @@ describe('RunnerTypeCell', () => {
it('Displays the runner edit link with the correct href', () => {
createComponent();
- expect(findEditBtn().attributes('href')).toBe(mockRunner.adminUrl);
+ expect(findEditBtn().attributes('href')).toBe(mockRunner.editAdminUrl);
+ });
+
+ it('Does not render the runner edit link when user cannot update', () => {
+ createComponent({
+ userPermissions: {
+ ...mockRunner.userPermissions,
+ updateRunner: false,
+ },
+ });
+
+ expect(findEditBtn().exists()).toBe(false);
+ });
+
+ it('Does not render the runner edit link when editAdminUrl is not provided', () => {
+ createComponent({
+ editAdminUrl: null,
+ });
+
+ expect(findEditBtn().exists()).toBe(false);
});
});
@@ -179,7 +200,7 @@ describe('RunnerTypeCell', () => {
});
it('error is shown to the user', () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
});
@@ -208,11 +229,22 @@ describe('RunnerTypeCell', () => {
});
it('error is shown to the user', () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
});
});
});
+
+ it('Does not render the runner toggle active button when user cannot update', () => {
+ createComponent({
+ userPermissions: {
+ ...mockRunner.userPermissions,
+ updateRunner: false,
+ },
+ });
+
+ expect(findToggleActiveBtn().exists()).toBe(false);
+ });
});
describe('Delete action', () => {
@@ -225,6 +257,10 @@ describe('RunnerTypeCell', () => {
);
});
+ it('Renders delete button', () => {
+ expect(findDeleteBtn().exists()).toBe(true);
+ });
+
it('Delete button opens delete modal', () => {
const modalId = getBinding(findDeleteBtn().element, 'gl-modal').value;
@@ -259,6 +295,18 @@ describe('RunnerTypeCell', () => {
});
});
+ it('Does not render the runner delete button when user cannot delete', () => {
+ createComponent({
+ userPermissions: {
+ ...mockRunner.userPermissions,
+ deleteRunner: false,
+ },
+ });
+
+ expect(findDeleteBtn().exists()).toBe(false);
+ expect(findRunnerDeleteModal().exists()).toBe(false);
+ });
+
describe('When delete is clicked', () => {
beforeEach(() => {
findRunnerDeleteModal().vm.$emit('primary');
@@ -302,7 +350,7 @@ describe('RunnerTypeCell', () => {
});
it('error is shown to the user', () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
it('toast notification is not shown', () => {
@@ -334,7 +382,7 @@ describe('RunnerTypeCell', () => {
});
it('error is shown to the user', () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
});
});
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
index 0d002c272b4..e75decddf70 100644
--- 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
@@ -1,14 +1,15 @@
-import { GlDropdownItem, GlLoadingIcon, GlToast } from '@gitlab/ui';
+import { GlDropdownItem, GlLoadingIcon, GlToast, GlModal } from '@gitlab/ui';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { nextTick } from 'vue';
import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
+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/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');
@@ -18,14 +19,18 @@ localVue.use(VueApollo);
localVue.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, {
@@ -38,6 +43,9 @@ describe('RegistrationTokenResetDropdownItem', () => {
apolloProvider: createMockApollo([
[runnersRegistrationTokenResetMutation, runnersRegistrationTokenResetMutationHandler],
]),
+ directives: {
+ GlModal: createMockDirective(),
+ },
});
showToast = wrapper.vm.$toast ? jest.spyOn(wrapper.vm.$toast, 'show') : null;
@@ -54,8 +62,6 @@ describe('RegistrationTokenResetDropdownItem', () => {
});
createComponent();
-
- jest.spyOn(window, 'confirm');
});
afterEach(() => {
@@ -66,6 +72,18 @@ describe('RegistrationTokenResetDropdownItem', () => {
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';
@@ -82,9 +100,8 @@ describe('RegistrationTokenResetDropdownItem', () => {
props: { type },
});
- window.confirm.mockReturnValueOnce(true);
-
findDropdownItem().trigger('click');
+ clickSubmit();
await waitForPromises();
});
@@ -114,7 +131,6 @@ describe('RegistrationTokenResetDropdownItem', () => {
describe('On click without confirmation', () => {
beforeEach(async () => {
- window.confirm.mockReturnValueOnce(false);
findDropdownItem().vm.$emit('click');
await waitForPromises();
});
@@ -142,11 +158,11 @@ describe('RegistrationTokenResetDropdownItem', () => {
runnersRegistrationTokenResetMutationHandler.mockRejectedValueOnce(new Error(mockErrorMsg));
- window.confirm.mockReturnValueOnce(true);
findDropdownItem().trigger('click');
+ clickSubmit();
await waitForPromises();
- expect(createFlash).toHaveBeenLastCalledWith({
+ expect(createAlert).toHaveBeenLastCalledWith({
message: `Network error: ${mockErrorMsg}`,
});
expect(captureException).toHaveBeenCalledWith({
@@ -168,11 +184,11 @@ describe('RegistrationTokenResetDropdownItem', () => {
},
});
- window.confirm.mockReturnValueOnce(true);
findDropdownItem().trigger('click');
+ clickSubmit();
await waitForPromises();
- expect(createFlash).toHaveBeenLastCalledWith({
+ expect(createAlert).toHaveBeenLastCalledWith({
message: `${mockErrorMsg} ${mockErrorMsg2}`,
});
expect(captureException).toHaveBeenCalledWith({
@@ -184,8 +200,8 @@ describe('RegistrationTokenResetDropdownItem', () => {
describe('Immediately after click', () => {
it('shows loading state', async () => {
- window.confirm.mockReturnValue(true);
findDropdownItem().trigger('click');
+ clickSubmit();
await nextTick();
expect(findLoadingIcon().exists()).toBe(true);
diff --git a/spec/frontend/runner/components/runner_header_spec.js b/spec/frontend/runner/components/runner_header_spec.js
new file mode 100644
index 00000000000..50699df3a44
--- /dev/null
+++ b/spec/frontend/runner/components/runner_header_spec.js
@@ -0,0 +1,93 @@
+import { GlSprintf } from '@gitlab/ui';
+import { mount, shallowMount } from '@vue/test-utils';
+import { 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 findTimeAgo = () => wrapper.findComponent(TimeAgo);
+
+ const createComponent = ({ runner = {}, mountFn = shallowMount } = {}) => {
+ wrapper = mountFn(RunnerHeader, {
+ propsData: {
+ runner: {
+ ...mockRunner,
+ ...runner,
+ },
+ },
+ stubs: {
+ GlSprintf,
+ TimeAgo,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('displays the runner status', () => {
+ createComponent({
+ mountFn: mount,
+ runner: {
+ status: STATUS_ONLINE,
+ },
+ });
+
+ expect(findRunnerStatusBadge().text()).toContain(`online`);
+ });
+
+ it('displays the runner type', () => {
+ createComponent({
+ mountFn: mount,
+ runner: {
+ runnerType: GROUP_TYPE,
+ },
+ });
+
+ expect(findRunnerTypeBadge().text()).toContain(`group`);
+ });
+
+ it('displays the runner id', () => {
+ createComponent({
+ runner: {
+ id: convertToGraphQLId(TYPE_CI_RUNNER, 99),
+ },
+ });
+
+ expect(wrapper.text()).toContain(`Runner #99`);
+ });
+
+ 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 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);
+ });
+});
diff --git a/spec/frontend/runner/components/runner_list_spec.js b/spec/frontend/runner/components/runner_list_spec.js
index 5a14fa5a2d5..452430b7237 100644
--- a/spec/frontend/runner/components/runner_list_spec.js
+++ b/spec/frontend/runner/components/runner_list_spec.js
@@ -69,7 +69,9 @@ describe('RunnerList', () => {
const { id, description, version, ipAddress, shortSha } = mockRunners[0];
// Badges
- expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText('not connected paused');
+ expect(findCell({ fieldKey: 'status' }).text()).toMatchInterpolatedText(
+ 'never contacted paused',
+ );
// Runner summary
expect(findCell({ fieldKey: 'summary' }).text()).toContain(
diff --git a/spec/frontend/runner/components/runner_status_badge_spec.js b/spec/frontend/runner/components/runner_status_badge_spec.js
index a19515d6ed2..c470c6bb989 100644
--- a/spec/frontend/runner/components/runner_status_badge_spec.js
+++ b/spec/frontend/runner/components/runner_status_badge_spec.js
@@ -6,7 +6,6 @@ import {
STATUS_ONLINE,
STATUS_OFFLINE,
STATUS_STALE,
- STATUS_NOT_CONNECTED,
STATUS_NEVER_CONTACTED,
} from '~/runner/constants';
@@ -50,20 +49,7 @@ describe('RunnerTypeBadge', () => {
expect(getTooltip().value).toBe('Runner is online; last contact was 1 minute ago');
});
- it('renders not connected state', () => {
- createComponent({
- runner: {
- contactedAt: null,
- status: STATUS_NOT_CONNECTED,
- },
- });
-
- expect(wrapper.text()).toBe('not connected');
- expect(findBadge().props('variant')).toBe('muted');
- expect(getTooltip().value).toMatch('This runner has never connected');
- });
-
- it('renders never contacted state as not connected, for backwards compatibility', () => {
+ it('renders never contacted state', () => {
createComponent({
runner: {
contactedAt: null,
@@ -71,9 +57,9 @@ describe('RunnerTypeBadge', () => {
},
});
- expect(wrapper.text()).toBe('not connected');
+ expect(wrapper.text()).toBe('never contacted');
expect(findBadge().props('variant')).toBe('muted');
- expect(getTooltip().value).toMatch('This runner has never connected');
+ expect(getTooltip().value).toMatch('This runner has never contacted');
});
it('renders offline state', () => {
diff --git a/spec/frontend/runner/components/runner_type_alert_spec.js b/spec/frontend/runner/components/runner_type_alert_spec.js
deleted file mode 100644
index 4023c75c9a8..00000000000
--- a/spec/frontend/runner/components/runner_type_alert_spec.js
+++ /dev/null
@@ -1,61 +0,0 @@
-import { GlAlert, GlLink } from '@gitlab/ui';
-import { shallowMount } from '@vue/test-utils';
-import RunnerTypeAlert from '~/runner/components/runner_type_alert.vue';
-import { INSTANCE_TYPE, GROUP_TYPE, PROJECT_TYPE } from '~/runner/constants';
-
-describe('RunnerTypeAlert', () => {
- let wrapper;
-
- const findAlert = () => wrapper.findComponent(GlAlert);
- const findLink = () => wrapper.findComponent(GlLink);
-
- const createComponent = ({ props = {} } = {}) => {
- wrapper = shallowMount(RunnerTypeAlert, {
- propsData: {
- type: INSTANCE_TYPE,
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- describe.each`
- type | exampleText | anchor
- ${INSTANCE_TYPE} | ${'This runner is available to all groups and projects'} | ${'#shared-runners'}
- ${GROUP_TYPE} | ${'This runner is available to all projects and subgroups in a group'} | ${'#group-runners'}
- ${PROJECT_TYPE} | ${'This runner is associated with one or more projects'} | ${'#specific-runners'}
- `('When it is an $type level runner', ({ type, exampleText, anchor }) => {
- beforeEach(() => {
- createComponent({ props: { type } });
- });
-
- it('Describes runner type', () => {
- expect(wrapper.text()).toMatch(exampleText);
- });
-
- it(`Shows an "info" variant`, () => {
- expect(findAlert().props('variant')).toBe('info');
- });
-
- it(`Links to anchor "${anchor}"`, () => {
- expect(findLink().attributes('href')).toBe(`/help/ci/runners/runners_scope${anchor}`);
- });
- });
-
- describe('When runner type is not correct', () => {
- it('Does not render content when type is missing', () => {
- createComponent({ props: { type: undefined } });
-
- expect(wrapper.html()).toBe('');
- });
-
- it('Validation fails for an incorrect type', () => {
- expect(() => {
- createComponent({ props: { type: 'NOT_A_TYPE' } });
- }).toThrow();
- });
- });
-});
diff --git a/spec/frontend/runner/components/runner_update_form_spec.js b/spec/frontend/runner/components/runner_update_form_spec.js
index 0e0844a785b..ebb2e67d1e2 100644
--- a/spec/frontend/runner/components/runner_update_form_spec.js
+++ b/spec/frontend/runner/components/runner_update_form_spec.js
@@ -5,7 +5,7 @@ import VueApollo from 'vue-apollo';
import createMockApollo from 'helpers/mock_apollo_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash, { FLASH_TYPES } from '~/flash';
+import { createAlert, VARIANT_SUCCESS } from '~/flash';
import RunnerUpdateForm from '~/runner/components/runner_update_form.vue';
import {
INSTANCE_TYPE,
@@ -79,9 +79,9 @@ describe('RunnerUpdateForm', () => {
input: expect.objectContaining(submittedRunner),
});
- expect(createFlash).toHaveBeenLastCalledWith({
+ expect(createAlert).toHaveBeenLastCalledWith({
message: expect.stringContaining('saved'),
- type: FLASH_TYPES.SUCCESS,
+ variant: VARIANT_SUCCESS,
});
expect(findSubmitDisabledAttr()).toBeUndefined();
@@ -127,7 +127,7 @@ describe('RunnerUpdateForm', () => {
await submitFormAndWait();
// Some fields are not submitted
- const { ipAddress, runnerType, ...submitted } = mockRunner;
+ const { ipAddress, runnerType, createdAt, status, ...submitted } = mockRunner;
expectToHaveSubmittedRunnerContaining(submitted);
});
@@ -238,7 +238,7 @@ describe('RunnerUpdateForm', () => {
await submitFormAndWait();
- expect(createFlash).toHaveBeenLastCalledWith({
+ expect(createAlert).toHaveBeenLastCalledWith({
message: `Network error: ${mockErrorMsg}`,
});
expect(captureException).toHaveBeenCalledWith({
@@ -262,7 +262,7 @@ describe('RunnerUpdateForm', () => {
await submitFormAndWait();
- expect(createFlash).toHaveBeenLastCalledWith({
+ expect(createAlert).toHaveBeenLastCalledWith({
message: mockErrorMsg,
});
expect(captureException).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
index 89c06ba2df4..52557ff716d 100644
--- a/spec/frontend/runner/components/search_tokens/tag_token_spec.js
+++ b/spec/frontend/runner/components/search_tokens/tag_token_spec.js
@@ -3,7 +3,7 @@ import { mount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import { nextTick } from 'vue';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import TagToken, { TAG_SUGGESTIONS_PATH } from '~/runner/components/search_tokens/tag_token.vue';
@@ -168,8 +168,8 @@ describe('TagToken', () => {
});
it('error is shown', async () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
- expect(createFlash).toHaveBeenCalledWith({ message: expect.any(String) });
+ expect(createAlert).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledWith({ message: expect.any(String) });
});
});
diff --git a/spec/frontend/runner/components/stat/runner_online_stat_spec.js b/spec/frontend/runner/components/stat/runner_online_stat_spec.js
deleted file mode 100644
index 18f865aa22c..00000000000
--- a/spec/frontend/runner/components/stat/runner_online_stat_spec.js
+++ /dev/null
@@ -1,34 +0,0 @@
-import { GlSingleStat } from '@gitlab/ui/dist/charts';
-import { shallowMount, mount } from '@vue/test-utils';
-import RunnerOnlineBadge from '~/runner/components/stat/runner_online_stat.vue';
-
-describe('RunnerOnlineBadge', () => {
- let wrapper;
-
- const findSingleStat = () => wrapper.findComponent(GlSingleStat);
-
- const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => {
- wrapper = mountFn(RunnerOnlineBadge, {
- propsData: {
- value: '99',
- ...props,
- },
- });
- };
-
- afterEach(() => {
- wrapper.destroy();
- });
-
- it('Uses a success appearance', () => {
- createComponent({}, shallowMount);
-
- expect(findSingleStat().props('variant')).toBe('success');
- });
-
- it('Renders a value', () => {
- createComponent({}, mount);
-
- expect(wrapper.text()).toMatch(new RegExp(`Online Runners 99\\s+online`));
- });
-});
diff --git a/spec/frontend/runner/components/stat/runner_stats_spec.js b/spec/frontend/runner/components/stat/runner_stats_spec.js
new file mode 100644
index 00000000000..68db8621ef0
--- /dev/null
+++ b/spec/frontend/runner/components/stat/runner_stats_spec.js
@@ -0,0 +1,46 @@
+import { shallowMount, mount } from '@vue/test-utils';
+import RunnerStats from '~/runner/components/stat/runner_stats.vue';
+import RunnerStatusStat from '~/runner/components/stat/runner_status_stat.vue';
+import { STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '~/runner/constants';
+
+describe('RunnerStats', () => {
+ let wrapper;
+
+ const findRunnerStatusStatAt = (i) => wrapper.findAllComponents(RunnerStatusStat).at(i);
+
+ const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
+ wrapper = mountFn(RunnerStats, {
+ propsData: {
+ onlineRunnersCount: 3,
+ offlineRunnersCount: 2,
+ staleRunnersCount: 1,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ it('Displays all the stats', () => {
+ createComponent({ mountFn: mount });
+
+ const stats = wrapper.text();
+
+ expect(stats).toMatch('Online runners 3');
+ expect(stats).toMatch('Offline runners 2');
+ expect(stats).toMatch('Stale runners 1');
+ });
+
+ it.each`
+ i | status
+ ${0} | ${STATUS_ONLINE}
+ ${1} | ${STATUS_OFFLINE}
+ ${2} | ${STATUS_STALE}
+ `('Displays status types at index $i', ({ i, status }) => {
+ createComponent();
+
+ expect(findRunnerStatusStatAt(i).props('status')).toBe(status);
+ });
+});
diff --git a/spec/frontend/runner/components/stat/runner_status_stat_spec.js b/spec/frontend/runner/components/stat/runner_status_stat_spec.js
new file mode 100644
index 00000000000..3218272eac7
--- /dev/null
+++ b/spec/frontend/runner/components/stat/runner_status_stat_spec.js
@@ -0,0 +1,67 @@
+import { GlSingleStat } from '@gitlab/ui/dist/charts';
+import { shallowMount, mount } from '@vue/test-utils';
+import RunnerStatusStat from '~/runner/components/stat/runner_status_stat.vue';
+import { STATUS_ONLINE, STATUS_OFFLINE, STATUS_STALE } from '~/runner/constants';
+
+describe('RunnerStatusStat', () => {
+ let wrapper;
+
+ const findSingleStat = () => wrapper.findComponent(GlSingleStat);
+
+ const createComponent = ({ props = {} } = {}, mountFn = shallowMount) => {
+ wrapper = mountFn(RunnerStatusStat, {
+ propsData: {
+ status: STATUS_ONLINE,
+ value: 99,
+ ...props,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe.each`
+ status | variant | title | badge
+ ${STATUS_ONLINE} | ${'success'} | ${'Online runners'} | ${'online'}
+ ${STATUS_OFFLINE} | ${'muted'} | ${'Offline runners'} | ${'offline'}
+ ${STATUS_STALE} | ${'warning'} | ${'Stale runners'} | ${'stale'}
+ `('Renders a stat for status "$status"', ({ status, variant, title, badge }) => {
+ beforeEach(() => {
+ createComponent({ props: { status } }, mount);
+ });
+
+ it('Renders text', () => {
+ expect(wrapper.text()).toMatch(new RegExp(`${title} 99\\s+${badge}`));
+ });
+
+ it(`Uses variant ${variant}`, () => {
+ expect(findSingleStat().props('variant')).toBe(variant);
+ });
+ });
+
+ it('Formats stat number', () => {
+ createComponent({ props: { value: 1000 } }, mount);
+
+ expect(wrapper.text()).toMatch('Online runners 1,000');
+ });
+
+ it('Shows a null result', () => {
+ createComponent({ props: { value: null } }, mount);
+
+ expect(wrapper.text()).toMatch('Online runners -');
+ });
+
+ it('Shows an undefined result', () => {
+ createComponent({ props: { value: undefined } }, mount);
+
+ expect(wrapper.text()).toMatch('Online runners -');
+ });
+
+ it('Shows result for an unknown status', () => {
+ createComponent({ props: { status: 'UNKNOWN' } }, mount);
+
+ expect(wrapper.text()).toMatch('Runners 99');
+ });
+});
diff --git a/spec/frontend/runner/group_runners/group_runners_app_spec.js b/spec/frontend/runner/group_runners/group_runners_app_spec.js
index 4451100de19..034b7848f35 100644
--- a/spec/frontend/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js
@@ -6,12 +6,13 @@ import createMockApollo from 'helpers/mock_apollo_helper';
import setWindowLocation from 'helpers/set_window_location_helper';
import { extendedWrapper } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import createFlash from '~/flash';
+import { createAlert } from '~/flash';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { updateHistory } from '~/lib/utils/url_utility';
import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue';
import RunnerList from '~/runner/components/runner_list.vue';
+import RunnerStats from '~/runner/components/stat/runner_stats.vue';
import RegistrationDropdown from '~/runner/components/registration/registration_dropdown.vue';
import RunnerPagination from '~/runner/components/runner_pagination.vue';
@@ -26,10 +27,11 @@ import {
RUNNER_PAGE_SIZE,
} from '~/runner/constants';
import getGroupRunnersQuery from '~/runner/graphql/get_group_runners.query.graphql';
+import getGroupRunnersCountQuery from '~/runner/graphql/get_group_runners_count.query.graphql';
import GroupRunnersApp from '~/runner/group_runners/group_runners_app.vue';
import { captureException } from '~/runner/sentry_utils';
import FilteredSearch from '~/vue_shared/components/filtered_search_bar/filtered_search_bar_root.vue';
-import { groupRunnersData, groupRunnersDataPaginated } from '../mock_data';
+import { groupRunnersData, groupRunnersDataPaginated, groupRunnersCountData } from '../mock_data';
const localVue = createLocalVue();
localVue.use(VueApollo);
@@ -48,7 +50,9 @@ jest.mock('~/lib/utils/url_utility', () => ({
describe('GroupRunnersApp', () => {
let wrapper;
let mockGroupRunnersQuery;
+ let mockGroupRunnersCountQuery;
+ const findRunnerStats = () => wrapper.findComponent(RunnerStats);
const findRegistrationDropdown = () => wrapper.findComponent(RegistrationDropdown);
const findRunnerList = () => wrapper.findComponent(RunnerList);
const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination));
@@ -59,7 +63,10 @@ describe('GroupRunnersApp', () => {
const findFilteredSearch = () => wrapper.findComponent(FilteredSearch);
const createComponent = ({ props = {}, mountFn = shallowMount } = {}) => {
- const handlers = [[getGroupRunnersQuery, mockGroupRunnersQuery]];
+ const handlers = [
+ [getGroupRunnersQuery, mockGroupRunnersQuery],
+ [getGroupRunnersCountQuery, mockGroupRunnersCountQuery],
+ ];
wrapper = mountFn(GroupRunnersApp, {
localVue,
@@ -77,11 +84,24 @@ describe('GroupRunnersApp', () => {
setWindowLocation(`/groups/${mockGroupFullPath}/-/runners`);
mockGroupRunnersQuery = jest.fn().mockResolvedValue(groupRunnersData);
+ mockGroupRunnersCountQuery = jest.fn().mockResolvedValue(groupRunnersCountData);
createComponent();
await waitForPromises();
});
+ it('shows total runner counts', async () => {
+ createComponent({ mountFn: mount });
+
+ await waitForPromises();
+
+ const stats = findRunnerStats().text();
+
+ expect(stats).toMatch('Online runners 2');
+ expect(stats).toMatch('Offline runners 2');
+ expect(stats).toMatch('Stale runners 2');
+ });
+
it('shows the runner setup instructions', () => {
expect(findRegistrationDropdown().props('registrationToken')).toBe(mockRegistrationToken);
expect(findRegistrationDropdown().props('type')).toBe(GROUP_TYPE);
@@ -129,28 +149,6 @@ describe('GroupRunnersApp', () => {
);
});
- describe('shows the active runner count', () => {
- const expectedOnlineCount = (count) => new RegExp(`Online Runners ${count}`);
-
- it('with a regular value', () => {
- createComponent({ mountFn: mount });
-
- expect(wrapper.text()).toMatch(expectedOnlineCount(mockGroupRunnersLimitedCount));
- });
-
- it('at the limit', () => {
- createComponent({ props: { groupRunnersLimitedCount: 1000 }, mountFn: mount });
-
- expect(wrapper.text()).toMatch(expectedOnlineCount('1,000'));
- });
-
- it('over the limit', () => {
- createComponent({ props: { groupRunnersLimitedCount: 1001 }, mountFn: mount });
-
- expect(wrapper.text()).toMatch(expectedOnlineCount('1,000\\+'));
- });
- });
-
describe('when a filter is preselected', () => {
beforeEach(async () => {
setWindowLocation(`?status[]=${STATUS_ACTIVE}&runner_type[]=${INSTANCE_TYPE}`);
@@ -236,7 +234,7 @@ describe('GroupRunnersApp', () => {
});
it('error is shown to the user', async () => {
- expect(createFlash).toHaveBeenCalledTimes(1);
+ expect(createAlert).toHaveBeenCalledTimes(1);
});
it('error is reported to sentry', async () => {
diff --git a/spec/frontend/runner/mock_data.js b/spec/frontend/runner/mock_data.js
index b8d0f1273c7..9c430e205ea 100644
--- a/spec/frontend/runner/mock_data.js
+++ b/spec/frontend/runner/mock_data.js
@@ -2,17 +2,21 @@
// Admin queries
import runnersData from 'test_fixtures/graphql/runner/get_runners.query.graphql.json';
+import runnersCountData from 'test_fixtures/graphql/runner/get_runners_count.query.graphql.json';
import runnersDataPaginated from 'test_fixtures/graphql/runner/get_runners.query.graphql.paginated.json';
import runnerData from 'test_fixtures/graphql/runner/get_runner.query.graphql.json';
// Group queries
import groupRunnersData from 'test_fixtures/graphql/runner/get_group_runners.query.graphql.json';
+import groupRunnersCountData from 'test_fixtures/graphql/runner/get_group_runners_count.query.graphql.json';
import groupRunnersDataPaginated from 'test_fixtures/graphql/runner/get_group_runners.query.graphql.paginated.json';
export {
runnerData,
+ runnersCountData,
runnersDataPaginated,
runnersData,
groupRunnersData,
+ groupRunnersCountData,
groupRunnersDataPaginated,
};
diff --git a/spec/frontend/runner/runner_search_utils_spec.js b/spec/frontend/runner/runner_search_utils_spec.js
index 0fc7917663e..aff1ec882bb 100644
--- a/spec/frontend/runner/runner_search_utils_spec.js
+++ b/spec/frontend/runner/runner_search_utils_spec.js
@@ -1,6 +1,7 @@
import { RUNNER_PAGE_SIZE } from '~/runner/constants';
import {
searchValidator,
+ updateOutdatedUrl,
fromUrlQueryToSearch,
fromSearchToUrl,
fromSearchToVariables,
@@ -190,6 +191,23 @@ describe('search_params.js', () => {
});
});
+ describe('updateOutdatedUrl', () => {
+ it('returns null for urls that do not need updating', () => {
+ expect(updateOutdatedUrl('http://test.host/')).toBe(null);
+ expect(updateOutdatedUrl('http://test.host/?a=b')).toBe(null);
+ });
+
+ it('returns updated url for updating NOT_CONNECTED to NEVER_CONTACTED', () => {
+ expect(updateOutdatedUrl('http://test.host/admin/runners?status[]=NOT_CONNECTED')).toBe(
+ 'http://test.host/admin/runners?status[]=NEVER_CONTACTED',
+ );
+
+ expect(updateOutdatedUrl('http://test.host/admin/runners?status[]=NOT_CONNECTED&a=b')).toBe(
+ 'http://test.host/admin/runners?status[]=NEVER_CONTACTED&a=b',
+ );
+ });
+ });
+
describe('fromUrlQueryToSearch', () => {
examples.forEach(({ name, urlQuery, search }) => {
it(`Converts ${name} to a search object`, () => {
diff --git a/spec/frontend/runner/runner_detail/runner_update_form_utils_spec.js b/spec/frontend/runner/runner_update_form_utils_spec.js
index 510b4e604ac..a633aee92f7 100644
--- a/spec/frontend/runner/runner_detail/runner_update_form_utils_spec.js
+++ b/spec/frontend/runner/runner_update_form_utils_spec.js
@@ -1,8 +1,5 @@
import { ACCESS_LEVEL_NOT_PROTECTED } from '~/runner/constants';
-import {
- modelToUpdateMutationVariables,
- runnerToModel,
-} from '~/runner/runner_details/runner_update_form_utils';
+import { modelToUpdateMutationVariables, runnerToModel } from '~/runner/runner_update_form_utils';
const mockId = 'gid://gitlab/Ci::Runner/1';
const mockDescription = 'Runner Desc.';
@@ -23,7 +20,7 @@ const mockModel = {
tagList: 'tag-1, tag-2',
};
-describe('~/runner/runner_details/runner_update_form_utils', () => {
+describe('~/runner/runner_update_form_utils', () => {
describe('runnerToModel', () => {
it('collects all model data', () => {
expect(runnerToModel(mockRunner)).toEqual(mockModel);