diff options
Diffstat (limited to 'spec/frontend/registry')
6 files changed, 430 insertions, 12 deletions
diff --git a/spec/frontend/registry/settings/components/expiration_dropdown_spec.js b/spec/frontend/registry/settings/components/expiration_dropdown_spec.js new file mode 100644 index 00000000000..e0cac317ad6 --- /dev/null +++ b/spec/frontend/registry/settings/components/expiration_dropdown_spec.js @@ -0,0 +1,83 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlFormGroup, GlFormSelect } from 'jest/registry/shared/stubs'; +import component from '~/registry/settings/components/expiration_dropdown.vue'; + +describe('ExpirationDropdown', () => { + let wrapper; + + const defaultProps = { + name: 'foo', + label: 'label-bar', + formOptions: [{ key: 'foo', label: 'bar' }, { key: 'baz', label: 'zab' }], + }; + + const findFormSelect = () => wrapper.find(GlFormSelect); + const findFormGroup = () => wrapper.find(GlFormGroup); + const findOptions = () => wrapper.findAll('[data-testid="option"]'); + + const mountComponent = props => { + wrapper = shallowMount(component, { + stubs: { + GlFormGroup, + GlFormSelect, + }, + propsData: { + ...defaultProps, + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('structure', () => { + it('has a form-select component', () => { + mountComponent(); + expect(findFormSelect().exists()).toBe(true); + }); + + it('has the correct options', () => { + mountComponent(); + + expect(findOptions()).toHaveLength(defaultProps.formOptions.length); + }); + }); + + describe('model', () => { + it('assign the right props to the form-select component', () => { + const value = 'foobar'; + const disabled = true; + + mountComponent({ value, disabled }); + + expect(findFormSelect().props()).toMatchObject({ + value, + disabled, + }); + expect(findFormSelect().attributes('id')).toBe(defaultProps.name); + }); + + it('assign the right props to the form-group component', () => { + mountComponent(); + + expect(findFormGroup().attributes()).toMatchObject({ + id: `${defaultProps.name}-form-group`, + 'label-for': defaultProps.name, + label: defaultProps.label, + }); + }); + + it('emits input event when form-select emits input', () => { + const emittedValue = 'barfoo'; + + mountComponent(); + + findFormSelect().vm.$emit('input', emittedValue); + + expect(wrapper.emitted('input')).toEqual([[emittedValue]]); + }); + }); +}); diff --git a/spec/frontend/registry/settings/components/expiration_run_text_spec.js b/spec/frontend/registry/settings/components/expiration_run_text_spec.js new file mode 100644 index 00000000000..d023f1fd05a --- /dev/null +++ b/spec/frontend/registry/settings/components/expiration_run_text_spec.js @@ -0,0 +1,66 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlFormInput } from '@gitlab/ui'; +import { GlFormGroup } from 'jest/registry/shared/stubs'; +import component from '~/registry/settings/components/expiration_run_text.vue'; +import { NEXT_CLEANUP_LABEL, NOT_SCHEDULED_POLICY_TEXT } from '~/registry/settings/constants'; + +describe('ExpirationToggle', () => { + let wrapper; + const value = 'foo'; + + const findInput = () => wrapper.find(GlFormInput); + const findFormGroup = () => wrapper.find(GlFormGroup); + + const mountComponent = propsData => { + wrapper = shallowMount(component, { + stubs: { + GlFormGroup, + }, + propsData, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('structure', () => { + it('has an input component', () => { + mountComponent(); + expect(findInput().exists()).toBe(true); + }); + }); + + describe('model', () => { + it('assigns the right props to the input component', () => { + mountComponent({ value, disabled: true }); + + expect(findInput().attributes()).toMatchObject({ + value, + }); + }); + + it('assigns the right props to the form-group component', () => { + mountComponent(); + + expect(findFormGroup().attributes()).toMatchObject({ + label: NEXT_CLEANUP_LABEL, + }); + }); + }); + + describe('formattedValue', () => { + it('displays the values when it exists', () => { + mountComponent({ value }); + + expect(findInput().attributes('value')).toBe(value); + }); + + it('displays a placeholder when no value is present', () => { + mountComponent(); + + expect(findInput().attributes('value')).toBe(NOT_SCHEDULED_POLICY_TEXT); + }); + }); +}); diff --git a/spec/frontend/registry/settings/components/expiration_textarea_spec.js b/spec/frontend/registry/settings/components/expiration_textarea_spec.js new file mode 100644 index 00000000000..80464c61117 --- /dev/null +++ b/spec/frontend/registry/settings/components/expiration_textarea_spec.js @@ -0,0 +1,169 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlSprintf, GlFormTextarea, GlLink } from '@gitlab/ui'; +import { GlFormGroup } from 'jest/registry/shared/stubs'; +import component from '~/registry/settings/components/expiration_textarea.vue'; +import { NAME_REGEX_LENGTH } from '~/registry/shared/constants'; + +describe('ExpirationTextarea', () => { + let wrapper; + + const defaultProps = { + name: 'foo', + label: 'label-bar', + placeholder: 'placeholder-baz', + description: '%{linkStart}description-foo%{linkEnd}', + }; + + const tagsRegexHelpPagePath = 'fooPath'; + + const findTextArea = () => wrapper.find(GlFormTextarea); + const findFormGroup = () => wrapper.find(GlFormGroup); + const findLabel = () => wrapper.find('[data-testid="label"]'); + const findDescription = () => wrapper.find('[data-testid="description"]'); + const findDescriptionLink = () => wrapper.find(GlLink); + + const mountComponent = props => { + wrapper = shallowMount(component, { + stubs: { + GlSprintf, + GlFormGroup, + }, + provide: { + tagsRegexHelpPagePath, + }, + propsData: { + ...defaultProps, + ...props, + }, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('structure', () => { + it('has a label', () => { + mountComponent(); + + expect(findLabel().text()).toBe(defaultProps.label); + }); + + it('has a textarea component', () => { + mountComponent(); + + expect(findTextArea().exists()).toBe(true); + }); + + it('has a description', () => { + mountComponent(); + + expect(findDescription().text()).toMatchInterpolatedText(defaultProps.description); + }); + + it('has a description link', () => { + mountComponent(); + + const link = findDescriptionLink(); + expect(link.exists()).toBe(true); + expect(link.attributes('href')).toBe(tagsRegexHelpPagePath); + }); + }); + + describe('model', () => { + it('assigns the right props to the textarea component', () => { + const value = 'foobar'; + const disabled = true; + + mountComponent({ value, disabled }); + + expect(findTextArea().attributes()).toMatchObject({ + id: defaultProps.name, + value, + placeholder: defaultProps.placeholder, + disabled: `${disabled}`, + trim: '', + }); + }); + + it('emits input event when textarea emits input', () => { + const emittedValue = 'barfoo'; + + mountComponent(); + + findTextArea().vm.$emit('input', emittedValue); + expect(wrapper.emitted('input')).toEqual([[emittedValue]]); + }); + }); + + describe('regex textarea validation', () => { + const invalidString = new Array(NAME_REGEX_LENGTH + 2).join(','); + + describe('when error contains an error message', () => { + const errorMessage = 'something went wrong'; + + it('shows the error message on the relevant field', () => { + mountComponent({ error: errorMessage }); + + expect(findFormGroup().attributes('invalid-feedback')).toBe(errorMessage); + }); + + it('gives precedence to API errors compared to local ones', () => { + mountComponent({ + error: errorMessage, + value: invalidString, + }); + + expect(findFormGroup().attributes('invalid-feedback')).toBe(errorMessage); + }); + }); + + describe('when error is empty', () => { + describe('if the user did not type', () => { + it('validation is not emitted', () => { + mountComponent(); + + expect(wrapper.emitted('validation')).toBeUndefined(); + }); + + it('no error message is shown', () => { + mountComponent(); + + expect(findFormGroup().props('state')).toBe(true); + expect(findFormGroup().attributes('invalid-feedback')).toBe(''); + }); + }); + + describe('when the user typed something', () => { + describe(`when name regex is longer than ${NAME_REGEX_LENGTH}`, () => { + beforeEach(() => { + // since the component has no state we both emit the event and set the prop + mountComponent({ value: invalidString }); + + findTextArea().vm.$emit('input', invalidString); + }); + + it('textAreaValidation state is false', () => { + expect(findFormGroup().props('state')).toBe(false); + expect(findTextArea().attributes('state')).toBeUndefined(); + }); + + it('emits the @validation event with false payload', () => { + expect(wrapper.emitted('validation')).toEqual([[false]]); + }); + }); + + it(`when user input is less than ${NAME_REGEX_LENGTH} state is "true"`, () => { + mountComponent(); + + findTextArea().vm.$emit('input', 'foo'); + + expect(findFormGroup().props('state')).toBe(true); + expect(findTextArea().attributes('state')).toBe('true'); + expect(wrapper.emitted('validation')).toEqual([[true]]); + }); + }); + }); + }); +}); diff --git a/spec/frontend/registry/settings/components/expiration_toggle_spec.js b/spec/frontend/registry/settings/components/expiration_toggle_spec.js new file mode 100644 index 00000000000..8b670c98dc1 --- /dev/null +++ b/spec/frontend/registry/settings/components/expiration_toggle_spec.js @@ -0,0 +1,80 @@ +import { shallowMount } from '@vue/test-utils'; +import { GlToggle, GlSprintf } from '@gitlab/ui'; +import { GlFormGroup } from 'jest/registry/shared/stubs'; +import component from '~/registry/settings/components/expiration_toggle.vue'; +import { + ENABLE_TOGGLE_DESCRIPTION, + ENABLED_TEXT, + DISABLED_TEXT, +} from '~/registry/settings/constants'; + +describe('ExpirationToggle', () => { + let wrapper; + + const findToggle = () => wrapper.find(GlToggle); + const findDescription = () => wrapper.find('[data-testid="description"]'); + + const mountComponent = propsData => { + wrapper = shallowMount(component, { + stubs: { + GlFormGroup, + GlSprintf, + }, + propsData, + }); + }; + + afterEach(() => { + wrapper.destroy(); + wrapper = null; + }); + + describe('structure', () => { + it('has a toggle component', () => { + mountComponent(); + + expect(findToggle().exists()).toBe(true); + }); + + it('has a description', () => { + mountComponent(); + + expect(findDescription().text()).toContain( + ENABLE_TOGGLE_DESCRIPTION.replace('%{toggleStatus}', ''), + ); + }); + }); + + describe('model', () => { + it('assigns the right props to the toggle component', () => { + mountComponent({ value: true, disabled: true }); + + expect(findToggle().props()).toMatchObject({ + value: true, + disabled: true, + }); + }); + + it('emits input event when toggle is updated', () => { + mountComponent(); + + findToggle().vm.$emit('change', false); + + expect(wrapper.emitted('input')).toEqual([[false]]); + }); + }); + + describe('toggle description', () => { + it('says enabled when the toggle is on', () => { + mountComponent({ value: true }); + + expect(findDescription().text()).toContain(ENABLED_TEXT); + }); + + it('says disabled when the toggle is off', () => { + mountComponent({ value: false }); + + expect(findDescription().text()).toContain(DISABLED_TEXT); + }); + }); +}); diff --git a/spec/frontend/registry/settings/mock_data.js b/spec/frontend/registry/settings/mock_data.js index 7f3772ce7fe..7cc645fcf55 100644 --- a/spec/frontend/registry/settings/mock_data.js +++ b/spec/frontend/registry/settings/mock_data.js @@ -1,13 +1,18 @@ +export const containerExpirationPolicyData = () => ({ + cadence: 'EVERY_DAY', + enabled: true, + keepN: 'TEN_TAGS', + nameRegex: 'asdasdssssdfdf', + nameRegexKeep: 'sss', + olderThan: 'FOURTEEN_DAYS', + nextRunAt: '2020-11-19T07:37:03.941Z', +}); + export const expirationPolicyPayload = override => ({ data: { project: { containerExpirationPolicy: { - cadence: 'EVERY_DAY', - enabled: true, - keepN: 'TEN_TAGS', - nameRegex: 'asdasdssssdfdf', - nameRegexKeep: 'sss', - olderThan: 'FOURTEEN_DAYS', + ...containerExpirationPolicyData(), ...override, }, }, @@ -26,12 +31,7 @@ export const expirationPolicyMutationPayload = ({ override, errors = [] } = {}) data: { updateContainerExpirationPolicy: { containerExpirationPolicy: { - cadence: 'EVERY_DAY', - enabled: true, - keepN: 'TEN_TAGS', - nameRegex: 'asdasdssssdfdf', - nameRegexKeep: 'sss', - olderThan: 'FOURTEEN_DAYS', + ...containerExpirationPolicyData(), ...override, }, errors, diff --git a/spec/frontend/registry/shared/stubs.js b/spec/frontend/registry/shared/stubs.js index f6b88d70e49..ad41eb42df4 100644 --- a/spec/frontend/registry/shared/stubs.js +++ b/spec/frontend/registry/shared/stubs.js @@ -9,3 +9,23 @@ export const GlCard = { </div> `, }; + +export const GlFormGroup = { + name: 'gl-form-group-stub', + props: ['state'], + template: ` + <div> + <slot name="label"></slot> + <slot></slot> + <slot name="description"></slot> + </div>`, +}; + +export const GlFormSelect = { + name: 'gl-form-select-stub', + props: ['disabled', 'value'], + template: ` + <div> + <slot></slot> + </div>`, +}; |