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:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-01 15:10:15 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-01 15:10:15 +0300
commitcf19a51fc5711144b26f7123c14f9b64a7597195 (patch)
tree09c151fd3655213e87b1c25beb842a99510122cb /spec/frontend/ci/runner/components
parent3b1df712c7a15c9b6abadd61e9c8894fdeb0442a (diff)
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/frontend/ci/runner/components')
-rw-r--r--spec/frontend/ci/runner/components/runner_create_form_spec.js4
-rw-r--r--spec/frontend/ci/runner/components/runner_form_fields_spec.js117
-rw-r--r--spec/frontend/ci/runner/components/runner_update_form_spec.js180
3 files changed, 139 insertions, 162 deletions
diff --git a/spec/frontend/ci/runner/components/runner_create_form_spec.js b/spec/frontend/ci/runner/components/runner_create_form_spec.js
index 243d23aeb38..f11667ee415 100644
--- a/spec/frontend/ci/runner/components/runner_create_form_spec.js
+++ b/spec/frontend/ci/runner/components/runner_create_form_spec.js
@@ -126,8 +126,8 @@ describe('RunnerCreateForm', () => {
expect(wrapper.emitted('saved')[0]).toEqual([mockCreatedRunner]);
});
- it('does not show a saving state', () => {
- expect(findSubmitBtn().props('loading')).toBe(false);
+ it('maintains a saving state before navigating away', () => {
+ expect(findSubmitBtn().props('loading')).toBe(true);
});
});
diff --git a/spec/frontend/ci/runner/components/runner_form_fields_spec.js b/spec/frontend/ci/runner/components/runner_form_fields_spec.js
index 0e2f2aa2e91..98f170d8f18 100644
--- a/spec/frontend/ci/runner/components/runner_form_fields_spec.js
+++ b/spec/frontend/ci/runner/components/runner_form_fields_spec.js
@@ -1,4 +1,6 @@
import { nextTick } from 'vue';
+import { GlSkeletonLoader } from '@gitlab/ui';
+import { s__ } from '~/locale';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import RunnerFormFields from '~/ci/runner/components/runner_form_fields.vue';
import {
@@ -8,46 +10,97 @@ import {
} from '~/ci/runner/constants';
const mockDescription = 'My description';
+const mockNewDescription = 'My new description';
const mockMaxTimeout = 60;
const mockTags = 'tag, tag2';
describe('RunnerFormFields', () => {
let wrapper;
+ const findInputByLabel = (label) => wrapper.findByLabelText(label);
const findInput = (name) => wrapper.find(`input[name="${name}"]`);
- const createComponent = ({ runner } = {}) => {
+ const expectRendersFields = () => {
+ expect(wrapper.text()).toContain(s__('Runners|Details'));
+ expect(wrapper.text()).toContain(s__('Runners|Configuration'));
+
+ expect(wrapper.findAllComponents(GlSkeletonLoader)).toHaveLength(0);
+ expect(wrapper.findAll('input')).toHaveLength(6);
+ };
+
+ const createComponent = ({ ...props } = {}) => {
wrapper = mountExtended(RunnerFormFields, {
propsData: {
- value: runner,
+ ...props,
},
});
};
+ describe('when runner is loading', () => {
+ beforeEach(() => {
+ createComponent({ loading: true });
+ });
+
+ it('renders a loading frame', () => {
+ expect(wrapper.text()).toContain(s__('Runners|Details'));
+ expect(wrapper.text()).toContain(s__('Runners|Configuration'));
+
+ expect(wrapper.findAllComponents(GlSkeletonLoader)).toHaveLength(2);
+ expect(wrapper.findAll('input')).toHaveLength(0);
+ });
+
+ describe('and then is loaded', () => {
+ beforeEach(() => {
+ wrapper.setProps({ loading: false, value: { description: mockDescription } });
+ });
+
+ it('renders fields', () => {
+ expectRendersFields();
+ });
+ });
+ });
+
+ it('when runner is loaded, renders fields', () => {
+ createComponent({
+ value: { description: mockDescription },
+ });
+
+ expectRendersFields();
+ });
+
+ it('when runner is updated with the same value, only emits when changed (avoids infinite loop)', async () => {
+ createComponent({ value: null, loading: true });
+ await wrapper.setProps({ value: { description: mockDescription }, loading: false });
+ await wrapper.setProps({ value: { description: mockDescription }, loading: false });
+
+ expect(wrapper.emitted('input')).toHaveLength(1);
+ });
+
it('updates runner fields', async () => {
- createComponent();
+ createComponent({
+ value: { description: mockDescription },
+ });
expect(wrapper.emitted('input')).toBe(undefined);
- findInput('description').setValue(mockDescription);
+ findInputByLabel(s__('Runners|Runner description')).setValue(mockNewDescription);
findInput('max-timeout').setValue(mockMaxTimeout);
- findInput('paused').setChecked(true);
- findInput('protected').setChecked(true);
- findInput('run-untagged').setChecked(true);
findInput('tags').setValue(mockTags);
await nextTick();
- expect(wrapper.emitted('input')[0][0]).toMatchObject({
- description: mockDescription,
- maximumTimeout: mockMaxTimeout,
- tagList: mockTags,
- });
+ expect(wrapper.emitted('input').at(-1)).toEqual([
+ {
+ description: mockNewDescription,
+ maximumTimeout: mockMaxTimeout,
+ tagList: mockTags,
+ },
+ ]);
});
it('checks checkbox fields', async () => {
createComponent({
- runner: {
+ value: {
paused: false,
accessLevel: ACCESS_LEVEL_NOT_PROTECTED,
runUntagged: false,
@@ -60,11 +113,13 @@ describe('RunnerFormFields', () => {
await nextTick();
- expect(wrapper.emitted('input')[0][0]).toEqual({
- paused: true,
- accessLevel: ACCESS_LEVEL_REF_PROTECTED,
- runUntagged: true,
- });
+ expect(wrapper.emitted('input').at(-1)).toEqual([
+ {
+ paused: true,
+ accessLevel: ACCESS_LEVEL_REF_PROTECTED,
+ runUntagged: true,
+ },
+ ]);
});
it('locked checkbox is not shown', () => {
@@ -75,7 +130,7 @@ describe('RunnerFormFields', () => {
it('when runner is of project type, locked checkbox can be checked', async () => {
createComponent({
- runner: {
+ value: {
runnerType: PROJECT_TYPE,
locked: false,
},
@@ -85,15 +140,17 @@ describe('RunnerFormFields', () => {
await nextTick();
- expect(wrapper.emitted('input')[0][0]).toEqual({
- runnerType: PROJECT_TYPE,
- locked: true,
- });
+ expect(wrapper.emitted('input').at(-1)).toEqual([
+ {
+ runnerType: PROJECT_TYPE,
+ locked: true,
+ },
+ ]);
});
it('unchecks checkbox fields', async () => {
createComponent({
- runner: {
+ value: {
paused: true,
accessLevel: ACCESS_LEVEL_REF_PROTECTED,
runUntagged: true,
@@ -106,10 +163,12 @@ describe('RunnerFormFields', () => {
await nextTick();
- expect(wrapper.emitted('input')[0][0]).toEqual({
- paused: false,
- accessLevel: ACCESS_LEVEL_NOT_PROTECTED,
- runUntagged: false,
- });
+ expect(wrapper.emitted('input').at(-1)).toEqual([
+ {
+ paused: false,
+ accessLevel: ACCESS_LEVEL_NOT_PROTECTED,
+ runUntagged: false,
+ },
+ ]);
});
});
diff --git a/spec/frontend/ci/runner/components/runner_update_form_spec.js b/spec/frontend/ci/runner/components/runner_update_form_spec.js
index d1d4e38f47c..5851078a8d3 100644
--- a/spec/frontend/ci/runner/components/runner_update_form_spec.js
+++ b/spec/frontend/ci/runner/components/runner_update_form_spec.js
@@ -1,20 +1,17 @@
-import Vue, { nextTick } from 'vue';
-import { GlForm, GlSkeletonLoader } from '@gitlab/ui';
+import Vue from 'vue';
import VueApollo from 'vue-apollo';
+import { GlForm } from '@gitlab/ui';
import { __ } from '~/locale';
+import { createAlert, VARIANT_SUCCESS } from '~/alert';
+import { visitUrl } from '~/lib/utils/url_utility';
+
import createMockApollo from 'helpers/mock_apollo_helper';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import waitForPromises from 'helpers/wait_for_promises';
-import { createAlert, VARIANT_SUCCESS } from '~/alert';
-import { visitUrl } from '~/lib/utils/url_utility';
+
+import { runnerToModel } from 'ee_else_ce/ci/runner/runner_update_form_utils';
+import RunnerFormFields from '~/ci/runner/components/runner_form_fields.vue';
import RunnerUpdateForm from '~/ci/runner/components/runner_update_form.vue';
-import {
- INSTANCE_TYPE,
- GROUP_TYPE,
- PROJECT_TYPE,
- ACCESS_LEVEL_REF_PROTECTED,
- ACCESS_LEVEL_NOT_PROTECTED,
-} from '~/ci/runner/constants';
import runnerUpdateMutation from '~/ci/runner/graphql/edit/runner_update.mutation.graphql';
import { captureException } from '~/ci/runner/sentry_utils';
import { saveAlertToLocalStorage } from '~/ci/runner/local_storage_alert/save_alert_to_local_storage';
@@ -38,16 +35,7 @@ describe('RunnerUpdateForm', () => {
let runnerUpdateHandler;
const findForm = () => wrapper.findComponent(GlForm);
- const findPausedCheckbox = () => wrapper.findByTestId('runner-field-paused');
- const findProtectedCheckbox = () => wrapper.findByTestId('runner-field-protected');
- const findRunUntaggedCheckbox = () => wrapper.findByTestId('runner-field-run-untagged');
- const findLockedCheckbox = () => wrapper.findByTestId('runner-field-locked');
- const findFields = () => wrapper.findAll('[data-testid^="runner-field"');
-
- const findDescriptionInput = () => wrapper.findByTestId('runner-field-description').find('input');
- const findMaxJobTimeoutInput = () =>
- wrapper.findByTestId('runner-field-max-timeout').find('input');
- const findTagsInput = () => wrapper.findByTestId('runner-field-tags').find('input');
+ const findRunnerFormFields = () => wrapper.findComponent(RunnerFormFields);
const findSubmit = () => wrapper.find('[type="submit"]');
const findSubmitDisabledAttr = () => findSubmit().attributes('disabled');
@@ -55,21 +43,10 @@ describe('RunnerUpdateForm', () => {
const submitForm = () => findForm().trigger('submit');
const submitFormAndWait = () => submitForm().then(waitForPromises);
- const getFieldsModel = () => ({
- paused: findPausedCheckbox().element.checked,
- accessLevel: findProtectedCheckbox().element.checked
- ? ACCESS_LEVEL_REF_PROTECTED
- : ACCESS_LEVEL_NOT_PROTECTED,
- runUntagged: findRunUntaggedCheckbox().element.checked,
- locked: findLockedCheckbox().element?.checked || false,
- maximumTimeout: findMaxJobTimeoutInput().element.value || null,
- tagList: findTagsInput().element.value.split(',').filter(Boolean),
- });
-
const createComponent = ({ props } = {}) => {
wrapper = mountExtended(RunnerUpdateForm, {
propsData: {
- runner: mockRunner,
+ runner: null,
runnerPath: mockRunnerPath,
...props,
},
@@ -106,141 +83,82 @@ describe('RunnerUpdateForm', () => {
},
});
});
+ });
+ it('form has fields, submit and cancel buttons', () => {
createComponent();
- });
- it('Form has a submit button', () => {
+ expect(findRunnerFormFields().exists()).toBe(true);
expect(findSubmit().exists()).toBe(true);
- });
-
- it('Form fields match data', () => {
- expect(mockRunner).toMatchObject(getFieldsModel());
- });
-
- it('Form shows a cancel button', () => {
- expect(runnerUpdateHandler).not.toHaveBeenCalled();
expect(findCancelBtn().attributes('href')).toBe(mockRunnerPath);
});
- it('Form prevent multiple submissions', async () => {
- await submitForm();
-
- expect(findSubmitDisabledAttr()).toBe('disabled');
- });
-
- it('Updates runner with no changes', async () => {
- await submitFormAndWait();
-
- // Some read-only fields are not submitted
- const { __typename, shortSha, runnerType, createdAt, status, ...submitted } = mockRunner;
-
- expectToHaveSubmittedRunnerContaining(submitted);
- });
-
describe('When data is being loaded', () => {
beforeEach(() => {
createComponent({ props: { loading: true } });
});
- it('Form skeleton is shown', () => {
- expect(wrapper.findComponent(GlSkeletonLoader).exists()).toBe(true);
- expect(findFields()).toHaveLength(0);
+ it('form has no runner', () => {
+ expect(findRunnerFormFields().props('value')).toBe(null);
});
- it('Form cannot be submitted', () => {
+ it('form cannot be submitted', () => {
expect(findSubmit().props('loading')).toBe(true);
});
+ });
+
+ describe('When runner has loaded', () => {
+ beforeEach(async () => {
+ createComponent({ props: { loading: true } });
- it('Form is updated when data loads', async () => {
- wrapper.setProps({
+ await wrapper.setProps({
loading: false,
+ runner: mockRunner,
});
-
- await nextTick();
-
- expect(findFields()).not.toHaveLength(0);
- expect(mockRunner).toMatchObject(getFieldsModel());
});
- });
- it.each`
- runnerType | exists | outcome
- ${INSTANCE_TYPE} | ${false} | ${'hidden'}
- ${GROUP_TYPE} | ${false} | ${'hidden'}
- ${PROJECT_TYPE} | ${true} | ${'shown'}
- `(`When runner is $runnerType, locked field is $outcome`, ({ runnerType, exists }) => {
- const runner = { ...mockRunner, runnerType };
- createComponent({ props: { runner } });
+ it('shows runner fields', () => {
+ expect(findRunnerFormFields().props('value')).toEqual(runnerToModel(mockRunner));
+ });
- expect(findLockedCheckbox().exists()).toBe(exists);
- });
+ it('form has not been submitted', () => {
+ expect(runnerUpdateHandler).not.toHaveBeenCalled();
+ });
- describe('On submit, runner gets updated', () => {
- it.each`
- test | initialValue | findCheckbox | checked | submitted
- ${'pauses'} | ${{ paused: false }} | ${findPausedCheckbox} | ${true} | ${{ paused: true }}
- ${'activates'} | ${{ paused: true }} | ${findPausedCheckbox} | ${false} | ${{ paused: false }}
- ${'unprotects'} | ${{ accessLevel: ACCESS_LEVEL_NOT_PROTECTED }} | ${findProtectedCheckbox} | ${true} | ${{ accessLevel: ACCESS_LEVEL_REF_PROTECTED }}
- ${'protects'} | ${{ accessLevel: ACCESS_LEVEL_REF_PROTECTED }} | ${findProtectedCheckbox} | ${false} | ${{ accessLevel: ACCESS_LEVEL_NOT_PROTECTED }}
- ${'"runs untagged jobs"'} | ${{ runUntagged: true }} | ${findRunUntaggedCheckbox} | ${false} | ${{ runUntagged: false }}
- ${'"runs tagged jobs"'} | ${{ runUntagged: false }} | ${findRunUntaggedCheckbox} | ${true} | ${{ runUntagged: true }}
- ${'locks'} | ${{ runnerType: PROJECT_TYPE, locked: true }} | ${findLockedCheckbox} | ${false} | ${{ locked: false }}
- ${'unlocks'} | ${{ runnerType: PROJECT_TYPE, locked: false }} | ${findLockedCheckbox} | ${true} | ${{ locked: true }}
- `('Checkbox $test runner', async ({ initialValue, findCheckbox, checked, submitted }) => {
- const runner = { ...mockRunner, ...initialValue };
- createComponent({ props: { runner } });
-
- await findCheckbox().setChecked(checked);
- await submitFormAndWait();
+ it('Form prevents multiple submissions', async () => {
+ await submitForm();
- expectToHaveSubmittedRunnerContaining({
- id: runner.id,
- ...submitted,
- });
+ expect(findSubmitDisabledAttr()).toBe('disabled');
});
- it.each`
- test | initialValue | findInput | value | submitted
- ${'description'} | ${{ description: 'Desc. 1' }} | ${findDescriptionInput} | ${'Desc. 2'} | ${{ description: 'Desc. 2' }}
- ${'max timeout'} | ${{ maximumTimeout: 36000 }} | ${findMaxJobTimeoutInput} | ${'40000'} | ${{ maximumTimeout: 40000 }}
- ${'tags'} | ${{ tagList: ['tag1'] }} | ${findTagsInput} | ${'tag2, tag3'} | ${{ tagList: ['tag2', 'tag3'] }}
- `("Field updates runner's $test", async ({ initialValue, findInput, value, submitted }) => {
- const runner = { ...mockRunner, ...initialValue };
- createComponent({ props: { runner } });
-
- await findInput().setValue(value);
+ it('Updates runner with no changes', async () => {
await submitFormAndWait();
- expectToHaveSubmittedRunnerContaining({
- id: runner.id,
- ...submitted,
- });
+ // Some read-only fields are not submitted
+ const { __typename, shortSha, runnerType, createdAt, status, ...submitted } = mockRunner;
+
+ expectToHaveSubmittedRunnerContaining(submitted);
});
- it.each`
- value | submitted
- ${''} | ${{ tagList: [] }}
- ${'tag1, tag2'} | ${{ tagList: ['tag1', 'tag2'] }}
- ${'with spaces'} | ${{ tagList: ['with spaces'] }}
- ${'more ,,,,, commas'} | ${{ tagList: ['more', 'commas'] }}
- `('Field updates runner\'s tags for "$value"', async ({ value, submitted }) => {
- const runner = { ...mockRunner, tagList: ['tag1'] };
- createComponent({ props: { runner } });
-
- await findTagsInput().setValue(value);
+ it('Updates runner with changes', async () => {
+ findRunnerFormFields().vm.$emit(
+ 'input',
+ runnerToModel({ ...mockRunner, description: 'A new description' }),
+ );
await submitFormAndWait();
- expectToHaveSubmittedRunnerContaining({
- id: runner.id,
- ...submitted,
- });
+ expectToHaveSubmittedRunnerContaining({ description: 'A new description' });
});
});
describe('On error', () => {
- beforeEach(() => {
+ beforeEach(async () => {
createComponent();
+
+ await wrapper.setProps({
+ loading: false,
+ runner: mockRunner,
+ });
});
it('On network error, error message is shown', async () => {