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')
-rw-r--r--spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js4
-rw-r--r--spec/frontend/runner/admin_runners/admin_runners_app_spec.js75
-rw-r--r--spec/frontend/runner/components/cells/link_cell_spec.js2
-rw-r--r--spec/frontend/runner/components/cells/runner_actions_cell_spec.js4
-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.js6
-rw-r--r--spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js99
-rw-r--r--spec/frontend/runner/components/runner_delete_button_spec.js31
-rw-r--r--spec/frontend/runner/components/runner_details_spec.js28
-rw-r--r--spec/frontend/runner/components/runner_filtered_search_bar_spec.js33
-rw-r--r--spec/frontend/runner/components/runner_list_empty_state_spec.js57
-rw-r--r--spec/frontend/runner/components/runner_list_spec.js56
-rw-r--r--spec/frontend/runner/components/runner_membership_toggle_spec.js57
-rw-r--r--spec/frontend/runner/components/runner_stacked_layout_banner_spec.js2
-rw-r--r--spec/frontend/runner/components/runner_type_tabs_spec.js20
-rw-r--r--spec/frontend/runner/components/runner_update_form_spec.js2
-rw-r--r--spec/frontend/runner/components/search_tokens/tag_token_spec.js43
-rw-r--r--spec/frontend/runner/graphql/local_state_spec.js51
-rw-r--r--spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js5
-rw-r--r--spec/frontend/runner/group_runners/group_runners_app_spec.js69
-rw-r--r--spec/frontend/runner/mock_data.js96
21 files changed, 632 insertions, 219 deletions
diff --git a/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js b/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js
index 7ab4aeee9bc..64f66d8f3ba 100644
--- a/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js
+++ b/spec/frontend/runner/admin_runner_show/admin_runner_show_app_spec.js
@@ -104,6 +104,10 @@ describe('AdminRunnerShowApp', () => {
Platform darwin
Configuration Runs untagged jobs
Maximum job timeout None
+ Token expiry
+ Runner authentication token expiration
+ Runner authentication tokens will expire based on a set interval.
+ They will automatically rotate once expired. Learn more Never expires
Tags None`.replace(/\s+/g, ' ');
expect(wrapper.text().replace(/\s+/g, ' ')).toContain(expected);
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 55a298e1695..7afde3bdc96 100644
--- a/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
+++ b/spec/frontend/runner/admin_runners/admin_runners_app_spec.js
@@ -20,8 +20,6 @@ import AdminRunnersApp from '~/runner/admin_runners/admin_runners_app.vue';
import RunnerStackedLayoutBanner from '~/runner/components/runner_stacked_layout_banner.vue';
import RunnerTypeTabs from '~/runner/components/runner_type_tabs.vue';
import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_bar.vue';
-import RunnerBulkDelete from '~/runner/components/runner_bulk_delete.vue';
-import RunnerBulkDeleteCheckbox from '~/runner/components/runner_bulk_delete_checkbox.vue';
import RunnerList from '~/runner/components/runner_list.vue';
import RunnerListEmptyState from '~/runner/components/runner_list_empty_state.vue';
import RunnerStats from '~/runner/components/stat/runner_stats.vue';
@@ -45,6 +43,7 @@ import {
PARAM_KEY_STATUS,
PARAM_KEY_TAG,
STATUS_ONLINE,
+ DEFAULT_MEMBERSHIP,
RUNNER_PAGE_SIZE,
} from '~/runner/constants';
import allRunnersQuery from 'ee_else_ce/runner/graphql/list/all_runners.query.graphql';
@@ -83,8 +82,6 @@ const COUNT_QUERIES = 7; // 4 tabs + 3 status queries
describe('AdminRunnersApp', () => {
let wrapper;
- let cacheConfig;
- let localMutations;
let showToast;
const findRunnerStackedLayoutBanner = () => wrapper.findComponent(RunnerStackedLayoutBanner);
@@ -92,8 +89,6 @@ describe('AdminRunnersApp', () => {
const findRunnerActionsCell = () => wrapper.findComponent(RunnerActionsCell);
const findRegistrationDropdown = () => wrapper.findComponent(RegistrationDropdown);
const findRunnerTypeTabs = () => wrapper.findComponent(RunnerTypeTabs);
- const findRunnerBulkDelete = () => wrapper.findComponent(RunnerBulkDelete);
- const findRunnerBulkDeleteCheckbox = () => wrapper.findComponent(RunnerBulkDeleteCheckbox);
const findRunnerList = () => wrapper.findComponent(RunnerList);
const findRunnerListEmptyState = () => wrapper.findComponent(RunnerListEmptyState);
const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination));
@@ -106,7 +101,7 @@ describe('AdminRunnersApp', () => {
provide,
...options
} = {}) => {
- ({ cacheConfig, localMutations } = createLocalState());
+ const { cacheConfig, localMutations } = createLocalState();
const handlers = [
[allRunnersQuery, mockRunnersHandler],
@@ -195,7 +190,7 @@ describe('AdminRunnersApp', () => {
const { id, shortSha } = mockRunners[0];
const numericId = getIdFromGraphQLId(id);
- const runnerLink = wrapper.find('tr [data-testid="td-summary"]').find(GlLink);
+ const runnerLink = wrapper.find('tr [data-testid="td-summary"]').findComponent(GlLink);
expect(runnerLink.text()).toBe(`#${numericId} (${shortSha})`);
expect(runnerLink.attributes('href')).toBe(`http://localhost/admin/runners/${numericId}`);
@@ -204,7 +199,9 @@ describe('AdminRunnersApp', () => {
it('renders runner actions for each runner', async () => {
await createComponent({ mountFn: mountExtended });
- const runnerActions = wrapper.find('tr [data-testid="td-actions"]').find(RunnerActionsCell);
+ const runnerActions = wrapper
+ .find('tr [data-testid="td-actions"]')
+ .findComponent(RunnerActionsCell);
const runner = mockRunners[0];
expect(runnerActions.props()).toEqual({
@@ -219,6 +216,7 @@ describe('AdminRunnersApp', () => {
expect(mockRunnersHandler).toHaveBeenLastCalledWith({
status: undefined,
type: undefined,
+ membership: DEFAULT_MEMBERSHIP,
sort: DEFAULT_SORT,
first: RUNNER_PAGE_SIZE,
});
@@ -255,7 +253,7 @@ describe('AdminRunnersApp', () => {
});
it('Links to the runner page', async () => {
- const runnerLink = wrapper.find('tr [data-testid="td-summary"]').find(GlLink);
+ const runnerLink = wrapper.find('tr [data-testid="td-summary"]').findComponent(GlLink);
expect(runnerLink.text()).toBe(`#${id} (${shortSha})`);
expect(runnerLink.attributes('href')).toBe(`http://localhost/admin/runners/${id}`);
@@ -288,6 +286,7 @@ describe('AdminRunnersApp', () => {
it('sets the filters in the search bar', () => {
expect(findRunnerFilteredSearchBar().props('value')).toEqual({
runnerType: INSTANCE_TYPE,
+ membership: DEFAULT_MEMBERSHIP,
filters: [
{ type: PARAM_KEY_STATUS, value: { data: STATUS_ONLINE, operator: '=' } },
{ type: PARAM_KEY_PAUSED, value: { data: 'true', operator: '=' } },
@@ -301,6 +300,7 @@ describe('AdminRunnersApp', () => {
expect(mockRunnersHandler).toHaveBeenLastCalledWith({
status: STATUS_ONLINE,
type: INSTANCE_TYPE,
+ membership: DEFAULT_MEMBERSHIP,
paused: true,
sort: DEFAULT_SORT,
first: RUNNER_PAGE_SIZE,
@@ -310,6 +310,7 @@ describe('AdminRunnersApp', () => {
it('fetches count results for requested status', () => {
expect(mockRunnersCountHandler).toHaveBeenCalledWith({
type: INSTANCE_TYPE,
+ membership: DEFAULT_MEMBERSHIP,
status: STATUS_ONLINE,
paused: true,
});
@@ -322,6 +323,7 @@ describe('AdminRunnersApp', () => {
findRunnerFilteredSearchBar().vm.$emit('input', {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [{ type: PARAM_KEY_STATUS, value: { data: STATUS_ONLINE, operator: '=' } }],
sort: CREATED_ASC,
});
@@ -339,6 +341,7 @@ describe('AdminRunnersApp', () => {
it('requests the runners with filters', () => {
expect(mockRunnersHandler).toHaveBeenLastCalledWith({
status: STATUS_ONLINE,
+ membership: DEFAULT_MEMBERSHIP,
sort: CREATED_ASC,
first: RUNNER_PAGE_SIZE,
});
@@ -347,6 +350,7 @@ describe('AdminRunnersApp', () => {
it('fetches count results for requested status', () => {
expect(mockRunnersCountHandler).toHaveBeenCalledWith({
status: STATUS_ONLINE,
+ membership: DEFAULT_MEMBERSHIP,
});
});
});
@@ -357,65 +361,26 @@ describe('AdminRunnersApp', () => {
expect(findRunnerPagination().attributes('disabled')).toBe('true');
});
- describe('when bulk delete is enabled', () => {
+ describe('Bulk delete', () => {
describe('Before runners are deleted', () => {
beforeEach(async () => {
- await createComponent({
- mountFn: mountExtended,
- provide: {
- glFeatures: { adminRunnersBulkDelete: true },
- },
- });
- });
-
- 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);
+ await createComponent({ mountFn: mountExtended });
});
it('runner list is checkable', () => {
expect(findRunnerList().props('checkable')).toBe(true);
});
-
- it('responds to checked items by updating the local cache', () => {
- const setRunnerCheckedMock = jest
- .spyOn(localMutations, 'setRunnerChecked')
- .mockImplementation(() => {});
-
- const runner = mockRunners[0];
-
- expect(setRunnerCheckedMock).toHaveBeenCalledTimes(0);
-
- findRunnerList().vm.$emit('checked', {
- runner,
- isChecked: true,
- });
-
- expect(setRunnerCheckedMock).toHaveBeenCalledTimes(1);
- expect(setRunnerCheckedMock).toHaveBeenCalledWith({
- runner,
- isChecked: true,
- });
- });
});
describe('When runners are deleted', () => {
beforeEach(async () => {
- await createComponent({
- mountFn: mountExtended,
- provide: {
- glFeatures: { adminRunnersBulkDelete: true },
- },
- });
+ await createComponent({ mountFn: mountExtended });
});
it('count data is refetched', async () => {
expect(mockRunnersCountHandler).toHaveBeenCalledTimes(COUNT_QUERIES);
- findRunnerBulkDelete().vm.$emit('deleted', { message: 'Runners deleted' });
+ findRunnerList().vm.$emit('deleted', { message: 'Runners deleted' });
expect(mockRunnersCountHandler).toHaveBeenCalledTimes(COUNT_QUERIES * 2);
});
@@ -423,7 +388,7 @@ describe('AdminRunnersApp', () => {
it('toast is shown', async () => {
expect(showToast).toHaveBeenCalledTimes(0);
- findRunnerBulkDelete().vm.$emit('deleted', { message: 'Runners deleted' });
+ findRunnerList().vm.$emit('deleted', { message: 'Runners deleted' });
expect(showToast).toHaveBeenCalledTimes(1);
expect(showToast).toHaveBeenCalledWith('Runners deleted');
@@ -457,6 +422,7 @@ describe('AdminRunnersApp', () => {
beforeEach(async () => {
findRunnerFilteredSearchBar().vm.$emit('input', {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [{ type: PARAM_KEY_STATUS, value: { data: STATUS_ONLINE, operator: '=' } }],
sort: CREATED_ASC,
});
@@ -504,6 +470,7 @@ describe('AdminRunnersApp', () => {
await findRunnerPaginationNext().trigger('click');
expect(mockRunnersHandler).toHaveBeenLastCalledWith({
+ membership: DEFAULT_MEMBERSHIP,
sort: CREATED_DESC,
first: RUNNER_PAGE_SIZE,
after: pageInfo.endCursor,
diff --git a/spec/frontend/runner/components/cells/link_cell_spec.js b/spec/frontend/runner/components/cells/link_cell_spec.js
index a59a0eaa5d8..46ab1adb6b6 100644
--- a/spec/frontend/runner/components/cells/link_cell_spec.js
+++ b/spec/frontend/runner/components/cells/link_cell_spec.js
@@ -5,7 +5,7 @@ import LinkCell from '~/runner/components/cells/link_cell.vue';
describe('LinkCell', () => {
let wrapper;
- const findGlLink = () => wrapper.find(GlLink);
+ const findGlLink = () => wrapper.findComponent(GlLink);
const findSpan = () => wrapper.find('span');
const createComponent = ({ props = {}, ...options } = {}) => {
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 ffd6f126627..58974d4f85f 100644
--- a/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
+++ b/spec/frontend/runner/components/cells/runner_actions_cell_spec.js
@@ -122,7 +122,7 @@ describe('RunnerActionsCell', () => {
expect(wrapper.emitted('deleted')).toEqual([[value]]);
});
- it('Renders the runner delete disabled button when user cannot delete', () => {
+ it('Does not render the runner delete button when user cannot delete', () => {
createComponent({
runner: {
userPermissions: {
@@ -132,7 +132,7 @@ describe('RunnerActionsCell', () => {
},
});
- expect(findDeleteBtn().props('disabled')).toBe(true);
+ 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
new file mode 100644
index 00000000000..e9965d8855d
--- /dev/null
+++ b/spec/frontend/runner/components/cells/runner_owner_cell_spec.js
@@ -0,0 +1,111 @@
+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
index 21ec9f61f37..e7cadefc140 100644
--- a/spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js
+++ b/spec/frontend/runner/components/cells/runner_stacked_summary_cell_spec.js
@@ -85,7 +85,7 @@ describe('RunnerTypeCell', () => {
contactedAt: '2022-01-02',
});
- expect(findRunnerSummaryField('clock').find(TimeAgo).props('time')).toBe('2022-01-02');
+ expect(findRunnerSummaryField('clock').findComponent(TimeAgo).props('time')).toBe('2022-01-02');
});
it('Displays empty last contact', () => {
@@ -93,7 +93,7 @@ describe('RunnerTypeCell', () => {
contactedAt: null,
});
- expect(findRunnerSummaryField('clock').find(TimeAgo).exists()).toBe(false);
+ expect(findRunnerSummaryField('clock').findComponent(TimeAgo).exists()).toBe(false);
expect(findRunnerSummaryField('clock').text()).toContain(__('Never'));
});
@@ -134,7 +134,7 @@ describe('RunnerTypeCell', () => {
});
it('Displays created at', () => {
- expect(findRunnerSummaryField('calendar').find(TimeAgo).props('time')).toBe(
+ expect(findRunnerSummaryField('calendar').findComponent(TimeAgo).props('time')).toBe(
mockRunner.createdAt,
);
});
diff --git a/spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js b/spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js
index 0ac89e82314..424a4e61ccd 100644
--- a/spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js
+++ b/spec/frontend/runner/components/runner_bulk_delete_checkbox_spec.js
@@ -5,11 +5,21 @@ 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';
-import { allRunnersData } from '../mock_data';
Vue.use(VueApollo);
-jest.mock('~/flash');
+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;
@@ -18,12 +28,14 @@ describe('RunnerBulkDeleteCheckbox', () => {
const findCheckbox = () => wrapper.findComponent(GlFormCheckbox);
- const mockRunners = allRunnersData.data.runners.nodes;
- const mockIds = allRunnersData.data.runners.nodes.map(({ id }) => id);
- const mockId = mockIds[0];
- const mockIdAnotherPage = 'RUNNER_IN_ANOTHER_PAGE_ID';
+ 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 = ({ props = {} } = {}) => {
+ const createComponent = ({ runners = [] } = {}) => {
const { cacheConfig, localMutations } = mockState;
const apolloProvider = createMockApollo(undefined, undefined, cacheConfig);
@@ -33,8 +45,7 @@ describe('RunnerBulkDeleteCheckbox', () => {
localMutations,
},
propsData: {
- runners: mockRunners,
- ...props,
+ runners,
},
});
};
@@ -49,31 +60,61 @@ describe('RunnerBulkDeleteCheckbox', () => {
jest.spyOn(mockState.localMutations, 'setRunnersChecked');
});
- describe.each`
- case | is | checkedRunnerIds | disabled | checked | indeterminate
- ${'no runners'} | ${'unchecked'} | ${[]} | ${undefined} | ${undefined} | ${undefined}
- ${'no runners in this page'} | ${'unchecked'} | ${[mockIdAnotherPage]} | ${undefined} | ${undefined} | ${undefined}
- ${'all runners'} | ${'checked'} | ${mockIds} | ${undefined} | ${'true'} | ${undefined}
- ${'some runners'} | ${'indeterminate'} | ${[mockId]} | ${undefined} | ${undefined} | ${'true'}
- ${'all plus other runners'} | ${'checked'} | ${[...mockIds, mockIdAnotherPage]} | ${undefined} | ${'true'} | ${undefined}
- `('When $case are checked', ({ is, checkedRunnerIds, disabled, checked, indeterminate }) => {
- beforeEach(async () => {
+ 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();
+ 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)],
+ });
- it(`is ${is}`, () => {
- expect(findCheckbox().attributes('disabled')).toBe(disabled);
- expect(findCheckbox().attributes('checked')).toBe(checked);
- expect(findCheckbox().attributes('indeterminate')).toBe(indeterminate);
+ 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 = mockIds;
- createComponent();
+ mockCheckedRunnerIds = ['1', '2'];
+ createComponent({ runners: mockRunners });
});
it.each([[true], [false]])('sets checked to %s', (checked) => {
@@ -89,13 +130,11 @@ describe('RunnerBulkDeleteCheckbox', () => {
describe('When runners are loading', () => {
beforeEach(() => {
- createComponent({ props: { runners: [] } });
+ createComponent();
});
- it(`is disabled`, () => {
- expect(findCheckbox().attributes('disabled')).toBe('true');
- expect(findCheckbox().attributes('checked')).toBe(undefined);
- expect(findCheckbox().attributes('indeterminate')).toBe(undefined);
+ it('is disabled', () => {
+ expectCheckboxToBe('disabled');
});
});
});
diff --git a/spec/frontend/runner/components/runner_delete_button_spec.js b/spec/frontend/runner/components/runner_delete_button_spec.js
index 52fe803c536..c8fb7a69379 100644
--- a/spec/frontend/runner/components/runner_delete_button_spec.js
+++ b/spec/frontend/runner/components/runner_delete_button_spec.js
@@ -9,11 +9,7 @@ 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,
- I18N_DELETE_DISABLED_MANY_PROJECTS,
- I18N_DELETE_DISABLED_UNKNOWN_REASON,
-} from '~/runner/constants';
+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';
@@ -267,29 +263,4 @@ describe('RunnerDeleteButton', () => {
});
});
});
-
- describe.each`
- reason | runner | tooltip
- ${'runner belongs to more than 1 project'} | ${{ projectCount: 2 }} | ${I18N_DELETE_DISABLED_MANY_PROJECTS}
- ${'unknown reason'} | ${{}} | ${I18N_DELETE_DISABLED_UNKNOWN_REASON}
- `('When button is disabled because $reason', ({ runner, tooltip }) => {
- beforeEach(() => {
- createComponent({
- props: {
- disabled: true,
- runner,
- },
- });
- });
-
- it('Displays a disabled delete button', () => {
- expect(findBtn().props('disabled')).toBe(true);
- });
-
- it(`Tooltip "${tooltip}" is shown`, () => {
- // tabindex is required for a11y
- expect(wrapper.attributes('tabindex')).toBe('0');
- expect(getTooltip()).toBe(tooltip);
- });
- });
});
diff --git a/spec/frontend/runner/components/runner_details_spec.js b/spec/frontend/runner/components/runner_details_spec.js
index f2281223a25..e6cc936e260 100644
--- a/spec/frontend/runner/components/runner_details_spec.js
+++ b/spec/frontend/runner/components/runner_details_spec.js
@@ -25,12 +25,7 @@ describe('RunnerDetails', () => {
const findDetailGroups = () => wrapper.findComponent(RunnerGroups);
- const createComponent = ({
- props = {},
- stubs,
- mountFn = shallowMountExtended,
- enforceRunnerTokenExpiresAt = false,
- } = {}) => {
+ const createComponent = ({ props = {}, stubs, mountFn = shallowMountExtended } = {}) => {
wrapper = mountFn(RunnerDetails, {
propsData: {
...props,
@@ -39,9 +34,6 @@ describe('RunnerDetails', () => {
RunnerDetail,
...stubs,
},
- provide: {
- glFeatures: { enforceRunnerTokenExpiresAt },
- },
});
};
@@ -82,7 +74,6 @@ describe('RunnerDetails', () => {
...runner,
},
},
- enforceRunnerTokenExpiresAt: true,
stubs: {
GlIntersperse,
GlSprintf,
@@ -135,22 +126,5 @@ describe('RunnerDetails', () => {
expect(findDetailGroups().props('runner')).toEqual(mockGroupRunner);
});
});
-
- describe('Token expiration field', () => {
- it.each`
- case | flag | shown
- ${'is shown when feature flag is enabled'} | ${true} | ${true}
- ${'is not shown when feature flag is disabled'} | ${false} | ${false}
- `('$case', ({ flag, shown }) => {
- createComponent({
- props: {
- runner: mockGroupRunner,
- },
- enforceRunnerTokenExpiresAt: flag,
- });
-
- expect(findDd('Token expiry', wrapper).exists()).toBe(shown);
- });
- });
});
});
diff --git a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
index e35bec3aa38..c92e19f9263 100644
--- a/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
+++ b/spec/frontend/runner/components/runner_filtered_search_bar_spec.js
@@ -4,10 +4,26 @@ import RunnerFilteredSearchBar from '~/runner/components/runner_filtered_search_
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 } from '~/runner/constants';
+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;
@@ -15,8 +31,7 @@ describe('RunnerList', () => {
const findGlFilteredSearch = () => wrapper.findComponent(GlFilteredSearch);
const findSortOptions = () => wrapper.findAllComponents(GlDropdownItem);
- const mockDefaultSort = 'CREATED_DESC';
- const mockOtherSort = 'CONTACTED_DESC';
+ const mockOtherSort = CONTACTED_DESC;
const mockFilters = [
{ type: PARAM_KEY_STATUS, value: { data: STATUS_ONLINE, operator: '=' } },
{ type: 'filtered-search-term', value: { data: '' } },
@@ -32,11 +47,7 @@ describe('RunnerList', () => {
propsData: {
namespace: 'runners',
tokens: [],
- value: {
- runnerType: null,
- filters: [],
- sort: mockDefaultSort,
- },
+ value: mockSearch,
...props,
},
stubs: {
@@ -115,6 +126,7 @@ describe('RunnerList', () => {
props: {
value: {
runnerType: INSTANCE_TYPE,
+ membership: DEFAULT_MEMBERSHIP,
sort: mockOtherSort,
filters: mockFilters,
},
@@ -141,6 +153,7 @@ describe('RunnerList', () => {
expectToHaveLastEmittedInput({
runnerType: INSTANCE_TYPE,
+ membership: DEFAULT_MEMBERSHIP,
filters: mockFilters,
sort: mockOtherSort,
pagination: {},
@@ -154,8 +167,9 @@ describe('RunnerList', () => {
expectToHaveLastEmittedInput({
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: mockFilters,
- sort: mockDefaultSort,
+ sort: DEFAULT_SORT,
pagination: {},
});
});
@@ -165,6 +179,7 @@ describe('RunnerList', () => {
expectToHaveLastEmittedInput({
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [],
sort: mockOtherSort,
pagination: {},
diff --git a/spec/frontend/runner/components/runner_list_empty_state_spec.js b/spec/frontend/runner/components/runner_list_empty_state_spec.js
index 59cff863106..038162b889e 100644
--- a/spec/frontend/runner/components/runner_list_empty_state_spec.js
+++ b/spec/frontend/runner/components/runner_list_empty_state_spec.js
@@ -8,6 +8,7 @@ import RunnerListEmptyState from '~/runner/components/runner_list_empty_state.vu
const mockSvgPath = 'mock-svg-path.svg';
const mockFilteredSvgPath = 'mock-filtered-svg-path.svg';
+const mockRegistrationToken = 'REGISTRATION_TOKEN';
describe('RunnerListEmptyState', () => {
let wrapper;
@@ -21,6 +22,7 @@ describe('RunnerListEmptyState', () => {
propsData: {
svgPath: mockSvgPath,
filteredSvgPath: mockFilteredSvgPath,
+ registrationToken: mockRegistrationToken,
...props,
},
directives: {
@@ -35,27 +37,52 @@ describe('RunnerListEmptyState', () => {
};
describe('when search is not filtered', () => {
- beforeEach(() => {
- createComponent();
- });
+ const title = s__('Runners|Get started with runners');
- it('renders an illustration', () => {
- expect(findEmptyState().props('svgPath')).toBe(mockSvgPath);
- });
+ 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.',
+ );
- it('displays "no results" text', () => {
- const title = s__('Runners|Get started with runners');
- 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}`);
+ });
- 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);
+ });
});
- it('opens a runner registration instructions modal with a link', () => {
- const { value } = getBinding(findLink().element, 'gl-modal');
+ 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}`);
+ });
- expect(findRunnerInstructionsModal().props('modalId')).toEqual(value);
+ it('has no registration instructions link', () => {
+ expect(findLink().exists()).toBe(false);
+ });
});
});
diff --git a/spec/frontend/runner/components/runner_list_spec.js b/spec/frontend/runner/components/runner_list_spec.js
index 54a9e713721..a31990f8f7e 100644
--- a/spec/frontend/runner/components/runner_list_spec.js
+++ b/spec/frontend/runner/components/runner_list_spec.js
@@ -1,12 +1,19 @@
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 RunnerStatusPopover from '~/runner/components/runner_status_popover.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';
@@ -15,6 +22,8 @@ const mockActiveRunnersCount = mockRunners.length;
describe('RunnerList', () => {
let wrapper;
+ let cacheConfig;
+ let localMutations;
const findSkeletonLoader = () => wrapper.findComponent(GlSkeletonLoader);
const findTable = () => wrapper.findComponent(GlTableLite);
@@ -22,18 +31,24 @@ describe('RunnerList', () => {
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,
@@ -50,7 +65,7 @@ describe('RunnerList', () => {
createComponent(
{
stubs: {
- RunnerStatusPopover: {
+ HelpPopover: {
template: '<div/>',
},
},
@@ -60,11 +75,13 @@ describe('RunnerList', () => {
const headerLabels = findHeaders().wrappers.map((w) => w.text());
- expect(findHeaders().at(0).findComponent(RunnerStatusPopover).exists()).toBe(true);
+ expect(findHeaders().at(0).findComponent(HelpPopover).exists()).toBe(true);
+ expect(findHeaders().at(2).findComponent(HelpPopover).exists()).toBe(true);
expect(headerLabels).toEqual([
- 'Status',
- 'Runner',
+ s__('Runners|Status'),
+ s__('Runners|Runner'),
+ s__('Runners|Owner'),
'', // actions has no label
]);
});
@@ -123,21 +140,40 @@ describe('RunnerList', () => {
);
});
+ 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('Emits a checked event', async () => {
- const checkbox = findCell({ fieldKey: 'checkbox' }).find('input');
+ 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(wrapper.emitted('checked')).toHaveLength(1);
- expect(wrapper.emitted('checked')[0][0]).toEqual({
+ expect(setRunnerCheckedMock).toHaveBeenCalledTimes(1);
+ expect(setRunnerCheckedMock).toHaveBeenCalledWith({
+ runner,
isChecked: true,
- runner: mockRunners[0],
});
});
+
+ 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', () => {
diff --git a/spec/frontend/runner/components/runner_membership_toggle_spec.js b/spec/frontend/runner/components/runner_membership_toggle_spec.js
new file mode 100644
index 00000000000..1a7ae22618a
--- /dev/null
+++ b/spec/frontend/runner/components/runner_membership_toggle_spec.js
@@ -0,0 +1,57 @@
+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_stacked_layout_banner_spec.js b/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js
index 1a8aced9292..d1f04f0ee37 100644
--- a/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js
+++ b/spec/frontend/runner/components/runner_stacked_layout_banner_spec.js
@@ -29,6 +29,8 @@ describe('RunnerStackedLayoutBanner', () => {
});
it('Does not display a banner when dismissed', async () => {
+ createComponent();
+
findLocalStorageSync().vm.$emit('input', true);
await nextTick();
diff --git a/spec/frontend/runner/components/runner_type_tabs_spec.js b/spec/frontend/runner/components/runner_type_tabs_spec.js
index 45ab8684332..dde35533bc3 100644
--- a/spec/frontend/runner/components/runner_type_tabs_spec.js
+++ b/spec/frontend/runner/components/runner_type_tabs_spec.js
@@ -2,9 +2,21 @@ 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 } from '~/runner/constants';
-
-const mockSearch = { runnerType: null, filters: [], pagination: { page: 1 }, sort: 'CREATED_DESC' };
+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;
@@ -113,7 +125,7 @@ describe('RunnerTypeTabs', () => {
});
findTabs().wrappers.forEach((tab) => {
- expect(tab.find(RunnerCount).props()).toEqual({
+ expect(tab.findComponent(RunnerCount).props()).toEqual({
scope: INSTANCE_TYPE,
skip: false,
variables: expect.objectContaining(mockVariables),
diff --git a/spec/frontend/runner/components/runner_update_form_spec.js b/spec/frontend/runner/components/runner_update_form_spec.js
index 7b67a89f989..e12736216a0 100644
--- a/spec/frontend/runner/components/runner_update_form_spec.js
+++ b/spec/frontend/runner/components/runner_update_form_spec.js
@@ -145,7 +145,7 @@ describe('RunnerUpdateForm', () => {
});
it('Form skeleton is shown', () => {
- expect(wrapper.find(GlSkeletonLoader).exists()).toBe(true);
+ expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
expect(findFields()).toHaveLength(0);
});
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 22f0561ca5f..a7363eb11cd 100644
--- a/spec/frontend/runner/components/search_tokens/tag_token_spec.js
+++ b/spec/frontend/runner/components/search_tokens/tag_token_spec.js
@@ -77,7 +77,7 @@ describe('TagToken', () => {
const findToken = () => wrapper.findComponent(GlToken);
const findGlLoadingIcon = () => wrapper.findComponent(GlLoadingIcon);
- beforeEach(async () => {
+ beforeEach(() => {
mock = new MockAdapter(axios);
mock.onGet(TAG_SUGGESTIONS_PATH, { params: { search: '' } }).reply(200, mockTags);
@@ -86,9 +86,6 @@ describe('TagToken', () => {
.reply(200, mockTagsFiltered);
getRecentlyUsedSuggestions.mockReturnValue([]);
-
- createComponent();
- await waitForPromises();
});
afterEach(() => {
@@ -97,11 +94,17 @@ describe('TagToken', () => {
});
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', () => {
+ it('displays tags suggestions', async () => {
+ await waitForPromises();
+
mockTags.forEach(({ name }, i) => {
expect(findGlFilteredSearchSuggestions().at(i).text()).toBe(name);
});
@@ -132,13 +135,13 @@ describe('TagToken', () => {
});
describe('when the users filters suggestions', () => {
- beforeEach(async () => {
+ beforeEach(() => {
+ createComponent();
+
findGlFilteredSearchToken().vm.$emit('input', { data: mockSearchTerm });
});
- it('requests filtered tags suggestions', async () => {
- await waitForPromises();
-
+ it('requests filtered tags suggestions', () => {
expect(mock.history.get[1].params).toEqual({ search: mockSearchTerm });
});
@@ -166,7 +169,7 @@ describe('TagToken', () => {
await waitForPromises();
});
- it('error is shown', async () => {
+ it('error is shown', () => {
expect(createAlert).toHaveBeenCalledTimes(1);
expect(createAlert).toHaveBeenCalledWith({ message: expect.any(String) });
});
@@ -180,8 +183,26 @@ describe('TagToken', () => {
await waitForPromises();
});
- it('selected tag is displayed', async () => {
+ 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/graphql/local_state_spec.js b/spec/frontend/runner/graphql/local_state_spec.js
index ae874fef00d..915170b53f9 100644
--- a/spec/frontend/runner/graphql/local_state_spec.js
+++ b/spec/frontend/runner/graphql/local_state_spec.js
@@ -4,6 +4,13 @@ import { createLocalState } from '~/runner/graphql/list/local_state';
import getCheckedRunnerIdsQuery from '~/runner/graphql/list/checked_runner_ids.query.graphql';
import { RUNNER_TYPENAME } from '~/runner/constants';
+const makeRunner = (id, deleteRunner = true) => ({
+ id,
+ userPermissions: {
+ deleteRunner,
+ },
+});
+
describe('~/runner/graphql/list/local_state', () => {
let localState;
let apolloClient;
@@ -57,16 +64,21 @@ describe('~/runner/graphql/list/local_state', () => {
});
it('returns checked runners that have a reference in the cache', () => {
- addMockRunnerToCache('a');
- localState.localMutations.setRunnerChecked({ runner: { id: 'a' }, isChecked: true });
+ const id = 'a';
+
+ addMockRunnerToCache(id);
+ localState.localMutations.setRunnerChecked({
+ runner: makeRunner(id),
+ isChecked: true,
+ });
expect(queryCheckedRunnerIds()).toEqual(['a']);
});
it('return checked runners that are not dangling references', () => {
addMockRunnerToCache('a'); // 'b' is missing from the cache, perhaps because it was deleted
- localState.localMutations.setRunnerChecked({ runner: { id: 'a' }, isChecked: true });
- localState.localMutations.setRunnerChecked({ runner: { id: 'b' }, isChecked: true });
+ localState.localMutations.setRunnerChecked({ runner: makeRunner('a'), isChecked: true });
+ localState.localMutations.setRunnerChecked({ runner: makeRunner('b'), isChecked: true });
expect(queryCheckedRunnerIds()).toEqual(['a']);
});
@@ -81,7 +93,7 @@ describe('~/runner/graphql/list/local_state', () => {
beforeEach(() => {
inputs.forEach(([id, isChecked]) => {
addMockRunnerToCache(id);
- localState.localMutations.setRunnerChecked({ runner: { id }, isChecked });
+ localState.localMutations.setRunnerChecked({ runner: makeRunner(id), isChecked });
});
});
it(`for inputs="${inputs}" has a ids="[${expected}]"`, () => {
@@ -102,7 +114,7 @@ describe('~/runner/graphql/list/local_state', () => {
ids.forEach(addMockRunnerToCache);
localState.localMutations.setRunnersChecked({
- runners: ids.map((id) => ({ id })),
+ runners: ids.map((id) => makeRunner(id)),
isChecked,
});
});
@@ -117,7 +129,7 @@ describe('~/runner/graphql/list/local_state', () => {
it('clears all checked items', () => {
['a', 'b', 'c'].forEach((id) => {
addMockRunnerToCache(id);
- localState.localMutations.setRunnerChecked({ runner: { id }, isChecked: true });
+ localState.localMutations.setRunnerChecked({ runner: makeRunner(id), isChecked: true });
});
expect(queryCheckedRunnerIds()).toEqual(['a', 'b', 'c']);
@@ -127,4 +139,29 @@ describe('~/runner/graphql/list/local_state', () => {
expect(queryCheckedRunnerIds()).toEqual([]);
});
});
+
+ describe('when some runners cannot be deleted', () => {
+ beforeEach(() => {
+ addMockRunnerToCache('a');
+ addMockRunnerToCache('b');
+ });
+
+ it('setRunnerChecked does not check runner that cannot be deleted', () => {
+ localState.localMutations.setRunnerChecked({
+ runner: makeRunner('a', false),
+ isChecked: true,
+ });
+
+ expect(queryCheckedRunnerIds()).toEqual([]);
+ });
+
+ it('setRunnersChecked does not check runner that cannot be deleted', () => {
+ localState.localMutations.setRunnersChecked({
+ runners: [makeRunner('a', false), makeRunner('b', false)],
+ isChecked: true,
+ });
+
+ expect(queryCheckedRunnerIds()).toEqual([]);
+ });
+ });
});
diff --git a/spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js b/spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js
index cee1d436942..a3b67674c94 100644
--- a/spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js
+++ b/spec/frontend/runner/group_runner_show/group_runner_show_app_spec.js
@@ -101,6 +101,11 @@ describe('GroupRunnerShowApp', () => {
Platform darwin
Configuration Runs untagged jobs
Maximum job timeout None
+ Token expiry
+ Runner authentication token expiration
+ Runner authentication tokens will expire based on a set interval.
+ They will automatically rotate once expired. Learn more
+ Never expires
Tags None`.replace(/\s+/g, ' ');
expect(wrapper.text().replace(/\s+/g, ' ')).toContain(expected);
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 a17502c7eec..7482926e151 100644
--- a/spec/frontend/runner/group_runners/group_runners_app_spec.js
+++ b/spec/frontend/runner/group_runners/group_runners_app_spec.js
@@ -14,6 +14,7 @@ import { s__ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { updateHistory } from '~/lib/utils/url_utility';
import { upgradeStatusTokenConfig } from 'ee_else_ce/runner/components/search_tokens/upgrade_status_token_config';
+import { createLocalState } from '~/runner/graphql/list/local_state';
import RunnerStackedLayoutBanner from '~/runner/components/runner_stacked_layout_banner.vue';
import RunnerTypeTabs from '~/runner/components/runner_type_tabs.vue';
@@ -24,6 +25,7 @@ import RunnerStats from '~/runner/components/stat/runner_stats.vue';
import RunnerActionsCell from '~/runner/components/cells/runner_actions_cell.vue';
import RegistrationDropdown from '~/runner/components/registration/registration_dropdown.vue';
import RunnerPagination from '~/runner/components/runner_pagination.vue';
+import RunnerMembershipToggle from '~/runner/components/runner_membership_toggle.vue';
import {
CREATED_ASC,
@@ -36,9 +38,12 @@ import {
GROUP_TYPE,
PARAM_KEY_PAUSED,
PARAM_KEY_STATUS,
+ PARAM_KEY_TAG,
STATUS_ONLINE,
STATUS_OFFLINE,
STATUS_STALE,
+ MEMBERSHIP_ALL_AVAILABLE,
+ MEMBERSHIP_DESCENDANTS,
RUNNER_PAGE_SIZE,
I18N_EDIT,
} from '~/runner/constants';
@@ -89,15 +94,23 @@ describe('GroupRunnersApp', () => {
const findRunnerPagination = () => extendedWrapper(wrapper.findComponent(RunnerPagination));
const findRunnerPaginationNext = () => findRunnerPagination().findByText(s__('Pagination|Next'));
const findRunnerFilteredSearchBar = () => wrapper.findComponent(RunnerFilteredSearchBar);
+ const findRunnerMembershipToggle = () => wrapper.findComponent(RunnerMembershipToggle);
+
+ const createComponent = ({
+ props = {},
+ provide = {},
+ mountFn = shallowMountExtended,
+ ...options
+ } = {}) => {
+ const { cacheConfig, localMutations } = createLocalState();
- const createComponent = ({ props = {}, mountFn = shallowMountExtended, ...options } = {}) => {
const handlers = [
[groupRunnersQuery, mockGroupRunnersHandler],
[groupRunnersCountQuery, mockGroupRunnersCountHandler],
];
wrapper = mountFn(GroupRunnersApp, {
- apolloProvider: createMockApollo(handlers),
+ apolloProvider: createMockApollo(handlers, {}, cacheConfig),
propsData: {
registrationToken: mockRegistrationToken,
groupFullPath: mockGroupFullPath,
@@ -105,10 +118,12 @@ describe('GroupRunnersApp', () => {
...props,
},
provide: {
+ localMutations,
onlineContactTimeoutSecs,
staleTimeoutSecs,
emptyStateSvgPath,
emptyStateFilteredSvgPath,
+ ...provide,
},
...options,
});
@@ -147,19 +162,50 @@ describe('GroupRunnersApp', () => {
expect(findRegistrationDropdown().props('type')).toBe(GROUP_TYPE);
});
+ describe('show all available runners toggle', () => {
+ it('shows the membership toggle', () => {
+ createComponent();
+ expect(findRunnerMembershipToggle().exists()).toBe(true);
+ });
+
+ it('sets the membership toggle', () => {
+ setWindowLocation(`?membership[]=${MEMBERSHIP_ALL_AVAILABLE}`);
+
+ createComponent();
+
+ expect(findRunnerMembershipToggle().props('value')).toBe(MEMBERSHIP_ALL_AVAILABLE);
+ });
+
+ it('requests filter', async () => {
+ createComponent();
+ findRunnerMembershipToggle().vm.$emit('input', MEMBERSHIP_ALL_AVAILABLE);
+
+ await waitForPromises();
+
+ expect(mockGroupRunnersHandler).toHaveBeenLastCalledWith(
+ expect.objectContaining({
+ membership: MEMBERSHIP_ALL_AVAILABLE,
+ }),
+ );
+ });
+ });
+
it('shows total runner counts', async () => {
await createComponent({ mountFn: mountExtended });
expect(mockGroupRunnersCountHandler).toHaveBeenCalledWith({
status: STATUS_ONLINE,
+ membership: MEMBERSHIP_DESCENDANTS,
groupFullPath: mockGroupFullPath,
});
expect(mockGroupRunnersCountHandler).toHaveBeenCalledWith({
status: STATUS_OFFLINE,
+ membership: MEMBERSHIP_DESCENDANTS,
groupFullPath: mockGroupFullPath,
});
expect(mockGroupRunnersCountHandler).toHaveBeenCalledWith({
status: STATUS_STALE,
+ membership: MEMBERSHIP_DESCENDANTS,
groupFullPath: mockGroupFullPath,
});
@@ -183,6 +229,7 @@ describe('GroupRunnersApp', () => {
groupFullPath: mockGroupFullPath,
status: undefined,
type: undefined,
+ membership: MEMBERSHIP_DESCENDANTS,
sort: DEFAULT_SORT,
first: RUNNER_PAGE_SIZE,
});
@@ -202,6 +249,10 @@ describe('GroupRunnersApp', () => {
type: PARAM_KEY_STATUS,
options: expect.any(Array),
}),
+ expect.objectContaining({
+ type: PARAM_KEY_TAG,
+ suggestionsDisabled: true,
+ }),
upgradeStatusTokenConfig,
]);
});
@@ -213,7 +264,7 @@ describe('GroupRunnersApp', () => {
const { id: graphqlId, shortSha } = node;
const id = getIdFromGraphQLId(graphqlId);
const COUNT_QUERIES = 6; // Smart queries that display a filtered count of runners
- const FILTERED_COUNT_QUERIES = 3; // Smart queries that display a count of runners in tabs
+ const FILTERED_COUNT_QUERIES = 6; // Smart queries that display a count of runners in tabs and single stats
beforeEach(async () => {
await createComponent({ mountFn: mountExtended });
@@ -266,6 +317,7 @@ describe('GroupRunnersApp', () => {
it('sets the filters in the search bar', () => {
expect(findRunnerFilteredSearchBar().props('value')).toEqual({
runnerType: INSTANCE_TYPE,
+ membership: MEMBERSHIP_DESCENDANTS,
filters: [{ type: 'status', value: { data: STATUS_ONLINE, operator: '=' } }],
sort: 'CREATED_DESC',
pagination: {},
@@ -277,6 +329,7 @@ describe('GroupRunnersApp', () => {
groupFullPath: mockGroupFullPath,
status: STATUS_ONLINE,
type: INSTANCE_TYPE,
+ membership: MEMBERSHIP_DESCENDANTS,
sort: DEFAULT_SORT,
first: RUNNER_PAGE_SIZE,
});
@@ -286,6 +339,7 @@ describe('GroupRunnersApp', () => {
expect(mockGroupRunnersCountHandler).toHaveBeenCalledWith({
groupFullPath: mockGroupFullPath,
type: INSTANCE_TYPE,
+ membership: MEMBERSHIP_DESCENDANTS,
status: STATUS_ONLINE,
});
});
@@ -297,6 +351,7 @@ describe('GroupRunnersApp', () => {
findRunnerFilteredSearchBar().vm.$emit('input', {
runnerType: null,
+ membership: MEMBERSHIP_DESCENDANTS,
filters: [{ type: PARAM_KEY_STATUS, value: { data: STATUS_ONLINE, operator: '=' } }],
sort: CREATED_ASC,
});
@@ -315,6 +370,7 @@ describe('GroupRunnersApp', () => {
expect(mockGroupRunnersHandler).toHaveBeenLastCalledWith({
groupFullPath: mockGroupFullPath,
status: STATUS_ONLINE,
+ membership: MEMBERSHIP_DESCENDANTS,
sort: CREATED_ASC,
first: RUNNER_PAGE_SIZE,
});
@@ -324,6 +380,7 @@ describe('GroupRunnersApp', () => {
expect(mockGroupRunnersCountHandler).toHaveBeenCalledWith({
groupFullPath: mockGroupFullPath,
status: STATUS_ONLINE,
+ membership: MEMBERSHIP_DESCENDANTS,
});
});
});
@@ -334,6 +391,11 @@ describe('GroupRunnersApp', () => {
expect(findRunnerPagination().attributes('disabled')).toBe('true');
});
+ it('runners cannot be deleted in bulk', () => {
+ createComponent();
+ expect(findRunnerList().props('checkable')).toBe(false);
+ });
+
describe('when no runners are found', () => {
beforeEach(async () => {
mockGroupRunnersHandler.mockResolvedValue({
@@ -395,6 +457,7 @@ describe('GroupRunnersApp', () => {
expect(mockGroupRunnersHandler).toHaveBeenLastCalledWith({
groupFullPath: mockGroupFullPath,
+ membership: MEMBERSHIP_DESCENDANTS,
sort: CREATED_DESC,
first: RUNNER_PAGE_SIZE,
after: pageInfo.endCursor,
diff --git a/spec/frontend/runner/mock_data.js b/spec/frontend/runner/mock_data.js
index 555ec40184f..da0c0433b3e 100644
--- a/spec/frontend/runner/mock_data.js
+++ b/spec/frontend/runner/mock_data.js
@@ -17,7 +17,7 @@ import groupRunnersData from 'test_fixtures/graphql/runner/list/group_runners.qu
import groupRunnersDataPaginated from 'test_fixtures/graphql/runner/list/group_runners.query.graphql.paginated.json';
import groupRunnersCountData from 'test_fixtures/graphql/runner/list/group_runners_count.query.graphql.json';
-import { RUNNER_PAGE_SIZE } from '~/runner/constants';
+import { DEFAULT_MEMBERSHIP, RUNNER_PAGE_SIZE } from '~/runner/constants';
const emptyPageInfo = {
__typename: 'PageInfo',
@@ -34,8 +34,18 @@ export const mockSearchExamples = [
{
name: 'a default query',
urlQuery: '',
- search: { runnerType: null, filters: [], pagination: {}, sort: 'CREATED_DESC' },
- graphqlVariables: { sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
+ search: {
+ runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
+ filters: [],
+ pagination: {},
+ sort: 'CREATED_DESC',
+ },
+ graphqlVariables: {
+ membership: DEFAULT_MEMBERSHIP,
+ sort: 'CREATED_DESC',
+ first: RUNNER_PAGE_SIZE,
+ },
isDefault: true,
},
{
@@ -43,17 +53,24 @@ export const mockSearchExamples = [
urlQuery: '?status[]=ACTIVE',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [{ type: 'status', value: { data: 'ACTIVE', operator: '=' } }],
pagination: {},
sort: 'CREATED_DESC',
},
- graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ membership: DEFAULT_MEMBERSHIP,
+ status: 'ACTIVE',
+ sort: 'CREATED_DESC',
+ first: RUNNER_PAGE_SIZE,
+ },
},
{
name: 'a single term text search',
urlQuery: '?search=something',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [
{
type: 'filtered-search-term',
@@ -63,13 +80,19 @@ export const mockSearchExamples = [
pagination: {},
sort: 'CREATED_DESC',
},
- graphqlVariables: { search: 'something', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ membership: DEFAULT_MEMBERSHIP,
+ search: 'something',
+ sort: 'CREATED_DESC',
+ first: RUNNER_PAGE_SIZE,
+ },
},
{
name: 'a two terms text search',
urlQuery: '?search=something+else',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [
{
type: 'filtered-search-term',
@@ -83,24 +106,36 @@ export const mockSearchExamples = [
pagination: {},
sort: 'CREATED_DESC',
},
- graphqlVariables: { search: 'something else', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ membership: DEFAULT_MEMBERSHIP,
+ search: 'something else',
+ sort: 'CREATED_DESC',
+ first: RUNNER_PAGE_SIZE,
+ },
},
{
name: 'single instance type',
urlQuery: '?runner_type[]=INSTANCE_TYPE',
search: {
runnerType: 'INSTANCE_TYPE',
+ membership: DEFAULT_MEMBERSHIP,
filters: [],
pagination: {},
sort: 'CREATED_DESC',
},
- graphqlVariables: { type: 'INSTANCE_TYPE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ type: 'INSTANCE_TYPE',
+ membership: DEFAULT_MEMBERSHIP,
+ sort: 'CREATED_DESC',
+ first: RUNNER_PAGE_SIZE,
+ },
},
{
name: 'multiple runner status',
urlQuery: '?status[]=ACTIVE&status[]=PAUSED',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [
{ type: 'status', value: { data: 'ACTIVE', operator: '=' } },
{ type: 'status', value: { data: 'PAUSED', operator: '=' } },
@@ -108,13 +143,19 @@ export const mockSearchExamples = [
pagination: {},
sort: 'CREATED_DESC',
},
- graphqlVariables: { status: 'ACTIVE', sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ status: 'ACTIVE',
+ membership: DEFAULT_MEMBERSHIP,
+ sort: 'CREATED_DESC',
+ first: RUNNER_PAGE_SIZE,
+ },
},
{
name: 'multiple status, a single instance type and a non default sort',
urlQuery: '?status[]=ACTIVE&runner_type[]=INSTANCE_TYPE&sort=CREATED_ASC',
search: {
runnerType: 'INSTANCE_TYPE',
+ membership: DEFAULT_MEMBERSHIP,
filters: [{ type: 'status', value: { data: 'ACTIVE', operator: '=' } }],
pagination: {},
sort: 'CREATED_ASC',
@@ -122,6 +163,7 @@ export const mockSearchExamples = [
graphqlVariables: {
status: 'ACTIVE',
type: 'INSTANCE_TYPE',
+ membership: DEFAULT_MEMBERSHIP,
sort: 'CREATED_ASC',
first: RUNNER_PAGE_SIZE,
},
@@ -131,11 +173,13 @@ export const mockSearchExamples = [
urlQuery: '?tag[]=tag-1',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [{ type: 'tag', value: { data: 'tag-1', operator: '=' } }],
pagination: {},
sort: 'CREATED_DESC',
},
graphqlVariables: {
+ membership: DEFAULT_MEMBERSHIP,
tagList: ['tag-1'],
first: 20,
sort: 'CREATED_DESC',
@@ -146,6 +190,7 @@ export const mockSearchExamples = [
urlQuery: '?tag[]=tag-1&tag[]=tag-2',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [
{ type: 'tag', value: { data: 'tag-1', operator: '=' } },
{ type: 'tag', value: { data: 'tag-2', operator: '=' } },
@@ -154,6 +199,7 @@ export const mockSearchExamples = [
sort: 'CREATED_DESC',
},
graphqlVariables: {
+ membership: DEFAULT_MEMBERSHIP,
tagList: ['tag-1', 'tag-2'],
first: 20,
sort: 'CREATED_DESC',
@@ -164,22 +210,34 @@ export const mockSearchExamples = [
urlQuery: '?after=AFTER_CURSOR',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [],
pagination: { after: 'AFTER_CURSOR' },
sort: 'CREATED_DESC',
},
- graphqlVariables: { sort: 'CREATED_DESC', after: 'AFTER_CURSOR', first: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ membership: DEFAULT_MEMBERSHIP,
+ sort: 'CREATED_DESC',
+ after: 'AFTER_CURSOR',
+ first: RUNNER_PAGE_SIZE,
+ },
},
{
name: 'the previous page',
urlQuery: '?before=BEFORE_CURSOR',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [],
pagination: { before: 'BEFORE_CURSOR' },
sort: 'CREATED_DESC',
},
- graphqlVariables: { sort: 'CREATED_DESC', before: 'BEFORE_CURSOR', last: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ membership: DEFAULT_MEMBERSHIP,
+ sort: 'CREATED_DESC',
+ before: 'BEFORE_CURSOR',
+ last: RUNNER_PAGE_SIZE,
+ },
},
{
name: 'the next page filtered by a status, an instance type, tags and a non default sort',
@@ -187,6 +245,7 @@ export const mockSearchExamples = [
'?status[]=ACTIVE&runner_type[]=INSTANCE_TYPE&tag[]=tag-1&tag[]=tag-2&sort=CREATED_ASC&after=AFTER_CURSOR',
search: {
runnerType: 'INSTANCE_TYPE',
+ membership: DEFAULT_MEMBERSHIP,
filters: [
{ type: 'status', value: { data: 'ACTIVE', operator: '=' } },
{ type: 'tag', value: { data: 'tag-1', operator: '=' } },
@@ -198,6 +257,7 @@ export const mockSearchExamples = [
graphqlVariables: {
status: 'ACTIVE',
type: 'INSTANCE_TYPE',
+ membership: DEFAULT_MEMBERSHIP,
tagList: ['tag-1', 'tag-2'],
sort: 'CREATED_ASC',
after: 'AFTER_CURSOR',
@@ -209,22 +269,34 @@ export const mockSearchExamples = [
urlQuery: '?paused[]=true',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [{ type: 'paused', value: { data: 'true', operator: '=' } }],
pagination: {},
sort: 'CREATED_DESC',
},
- graphqlVariables: { paused: true, sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ paused: true,
+ membership: DEFAULT_MEMBERSHIP,
+ sort: 'CREATED_DESC',
+ first: RUNNER_PAGE_SIZE,
+ },
},
{
name: 'active runners',
urlQuery: '?paused[]=false',
search: {
runnerType: null,
+ membership: DEFAULT_MEMBERSHIP,
filters: [{ type: 'paused', value: { data: 'false', operator: '=' } }],
pagination: {},
sort: 'CREATED_DESC',
},
- graphqlVariables: { paused: false, sort: 'CREATED_DESC', first: RUNNER_PAGE_SIZE },
+ graphqlVariables: {
+ paused: false,
+ membership: DEFAULT_MEMBERSHIP,
+ sort: 'CREATED_DESC',
+ first: RUNNER_PAGE_SIZE,
+ },
},
];