Commit 9b2b9d72 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera Committed by Olena Horal-Koretska

Move shared constants to settings one

- add missing entries
- remove obsolete file
parent 27da16c9
...@@ -2,16 +2,16 @@ ...@@ -2,16 +2,16 @@
import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; import { GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { isEqual, get, isEmpty } from 'lodash'; import { isEqual, get, isEmpty } from 'lodash';
import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.graphql'; import expirationPolicyQuery from '../graphql/queries/get_expiration_policy.graphql';
import { FETCH_SETTINGS_ERROR_MESSAGE } from '../../shared/constants';
import SettingsForm from './settings_form.vue';
import { import {
FETCH_SETTINGS_ERROR_MESSAGE,
UNAVAILABLE_FEATURE_TITLE, UNAVAILABLE_FEATURE_TITLE,
UNAVAILABLE_FEATURE_INTRO_TEXT, UNAVAILABLE_FEATURE_INTRO_TEXT,
UNAVAILABLE_USER_FEATURE_TEXT, UNAVAILABLE_USER_FEATURE_TEXT,
UNAVAILABLE_ADMIN_FEATURE_TEXT, UNAVAILABLE_ADMIN_FEATURE_TEXT,
} from '../constants'; } from '../constants';
import SettingsForm from './settings_form.vue';
export default { export default {
components: { components: {
SettingsForm, SettingsForm,
......
...@@ -4,8 +4,6 @@ import Tracking from '~/tracking'; ...@@ -4,8 +4,6 @@ import Tracking from '~/tracking';
import { import {
UPDATE_SETTINGS_ERROR_MESSAGE, UPDATE_SETTINGS_ERROR_MESSAGE,
UPDATE_SETTINGS_SUCCESS_MESSAGE, UPDATE_SETTINGS_SUCCESS_MESSAGE,
} from '~/registry/shared/constants';
import {
SET_CLEANUP_POLICY_BUTTON, SET_CLEANUP_POLICY_BUTTON,
KEEP_HEADER_TEXT, KEEP_HEADER_TEXT,
KEEP_INFO_TEXT, KEEP_INFO_TEXT,
...@@ -21,7 +19,7 @@ import { ...@@ -21,7 +19,7 @@ import {
CADENCE_LABEL, CADENCE_LABEL,
EXPIRATION_POLICY_FOOTER_NOTE, EXPIRATION_POLICY_FOOTER_NOTE,
} from '~/registry/settings/constants'; } from '~/registry/settings/constants';
import { formOptionsGenerator } from '~/registry/shared/utils'; import { formOptionsGenerator } from '~/registry/settings/utils';
import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.graphql'; import updateContainerExpirationPolicyMutation from '~/registry/settings/graphql/mutations/update_container_expiration_policy.graphql';
import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update'; import { updateContainerExpirationPolicy } from '~/registry/settings/graphql/utils/cache_update';
import ExpirationDropdown from './expiration_dropdown.vue'; import ExpirationDropdown from './expiration_dropdown.vue';
......
...@@ -52,4 +52,40 @@ export const EXPIRATION_POLICY_FOOTER_NOTE = s__( ...@@ -52,4 +52,40 @@ export const EXPIRATION_POLICY_FOOTER_NOTE = s__(
'ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time', 'ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time',
); );
export const KEEP_N_OPTIONS = [
{ key: 'ONE_TAG', variable: 1, default: false },
{ key: 'FIVE_TAGS', variable: 5, default: false },
{ key: 'TEN_TAGS', variable: 10, default: true },
{ key: 'TWENTY_FIVE_TAGS', variable: 25, default: false },
{ key: 'FIFTY_TAGS', variable: 50, default: false },
{ key: 'ONE_HUNDRED_TAGS', variable: 100, default: false },
];
export const CADENCE_OPTIONS = [
{ key: 'EVERY_DAY', label: __('Every day'), default: true },
{ key: 'EVERY_WEEK', label: __('Every week'), default: false },
{ key: 'EVERY_TWO_WEEKS', label: __('Every two weeks'), default: false },
{ key: 'EVERY_MONTH', label: __('Every month'), default: false },
{ key: 'EVERY_THREE_MONTHS', label: __('Every three months'), default: false },
];
export const OLDER_THAN_OPTIONS = [
{ key: 'SEVEN_DAYS', variable: 7, default: false },
{ key: 'FOURTEEN_DAYS', variable: 14, default: false },
{ key: 'THIRTY_DAYS', variable: 30, default: false },
{ key: 'NINETY_DAYS', variable: 90, default: true },
];
export const FETCH_SETTINGS_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while fetching the cleanup policy.',
);
export const UPDATE_SETTINGS_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while updating the cleanup policy.',
);
export const UPDATE_SETTINGS_SUCCESS_MESSAGE = s__(
'ContainerRegistry|Cleanup policy successfully saved.',
);
export const NAME_REGEX_LENGTH = 255; export const NAME_REGEX_LENGTH = 255;
...@@ -6,21 +6,6 @@ export const findDefaultOption = options => { ...@@ -6,21 +6,6 @@ export const findDefaultOption = options => {
return item ? item.key : null; return item ? item.key : null;
}; };
export const mapComputedToEvent = (list, root) => {
const result = {};
list.forEach(e => {
result[e] = {
get() {
return this[root][e];
},
set(value) {
this.$emit('input', { newValue: { ...this[root], [e]: value }, modified: e });
},
};
});
return result;
};
export const olderThanTranslationGenerator = variable => n__('%d day', '%d days', variable); export const olderThanTranslationGenerator = variable => n__('%d day', '%d days', variable);
export const keepNTranslationGenerator = variable => export const keepNTranslationGenerator = variable =>
......
<script>
import { uniqueId } from 'lodash';
import { GlFormGroup, GlToggle, GlFormSelect, GlFormTextarea, GlSprintf } from '@gitlab/ui';
import {
NAME_REGEX_LENGTH,
ENABLED_TEXT,
DISABLED_TEXT,
TEXT_AREA_INVALID_FEEDBACK,
EXPIRATION_INTERVAL_LABEL,
EXPIRATION_SCHEDULE_LABEL,
KEEP_N_LABEL,
NAME_REGEX_LABEL,
NAME_REGEX_PLACEHOLDER,
NAME_REGEX_DESCRIPTION,
NAME_REGEX_KEEP_LABEL,
NAME_REGEX_KEEP_PLACEHOLDER,
NAME_REGEX_KEEP_DESCRIPTION,
ENABLE_TOGGLE_LABEL,
ENABLE_TOGGLE_DESCRIPTION,
} from '../constants';
import { mapComputedToEvent } from '../utils';
export default {
components: {
GlFormGroup,
GlToggle,
GlFormSelect,
GlFormTextarea,
GlSprintf,
},
props: {
formOptions: {
type: Object,
required: false,
default: () => ({}),
},
apiErrors: {
type: Object,
required: false,
default: null,
},
isLoading: {
type: Boolean,
required: false,
default: false,
},
value: {
type: Object,
required: false,
default: () => ({}),
},
labelCols: {
type: [Number, String],
required: false,
default: 3,
},
labelAlign: {
type: String,
required: false,
default: 'right',
},
},
i18n: {
ENABLE_TOGGLE_LABEL,
ENABLE_TOGGLE_DESCRIPTION,
},
selectList: [
{
name: 'expiration-policy-interval',
label: EXPIRATION_INTERVAL_LABEL,
model: 'olderThan',
},
{
name: 'expiration-policy-schedule',
label: EXPIRATION_SCHEDULE_LABEL,
model: 'cadence',
},
{
name: 'expiration-policy-latest',
label: KEEP_N_LABEL,
model: 'keepN',
},
],
textAreaList: [
{
name: 'expiration-policy-name-matching',
label: NAME_REGEX_LABEL,
model: 'nameRegex',
placeholder: NAME_REGEX_PLACEHOLDER,
description: NAME_REGEX_DESCRIPTION,
},
{
name: 'expiration-policy-keep-name',
label: NAME_REGEX_KEEP_LABEL,
model: 'nameRegexKeep',
placeholder: NAME_REGEX_KEEP_PLACEHOLDER,
description: NAME_REGEX_KEEP_DESCRIPTION,
},
],
data() {
return {
uniqueId: uniqueId(),
};
},
computed: {
...mapComputedToEvent(
['enabled', 'cadence', 'olderThan', 'keepN', 'nameRegex', 'nameRegexKeep'],
'value',
),
policyEnabledText() {
return this.enabled ? ENABLED_TEXT : DISABLED_TEXT;
},
textAreaValidation() {
const nameRegexErrors = this.apiErrors?.nameRegex || this.validateRegexLength(this.nameRegex);
const nameKeepRegexErrors =
this.apiErrors?.nameRegexKeep || this.validateRegexLength(this.nameRegexKeep);
return {
/*
* The state has this form:
* null: gray border, no message
* true: green border, no message ( because none is configured)
* false: red border, error message
* So in this function we keep null if the are no message otherwise we 'invert' the error message
*/
nameRegex: {
state: nameRegexErrors === null ? null : !nameRegexErrors,
message: nameRegexErrors,
},
nameRegexKeep: {
state: nameKeepRegexErrors === null ? null : !nameKeepRegexErrors,
message: nameKeepRegexErrors,
},
};
},
fieldsValidity() {
return (
this.textAreaValidation.nameRegex.state !== false &&
this.textAreaValidation.nameRegexKeep.state !== false
);
},
isFormElementDisabled() {
return !this.enabled || this.isLoading;
},
},
watch: {
fieldsValidity: {
immediate: true,
handler(valid) {
if (valid) {
this.$emit('validated');
} else {
this.$emit('invalidated');
}
},
},
},
methods: {
validateRegexLength(value) {
if (!value) {
return null;
}
return value.length <= NAME_REGEX_LENGTH ? '' : TEXT_AREA_INVALID_FEEDBACK;
},
idGenerator(id) {
return `${id}_${this.uniqueId}`;
},
updateModel(value, key) {
this[key] = value;
},
},
};
</script>
<template>
<div ref="form-elements" class="gl-line-height-20">
<gl-form-group
:id="idGenerator('expiration-policy-toggle-group')"
:label-cols="labelCols"
:label-align="labelAlign"
:label-for="idGenerator('expiration-policy-toggle')"
:label="$options.i18n.ENABLE_TOGGLE_LABEL"
>
<div class="gl-display-flex">
<gl-toggle
:id="idGenerator('expiration-policy-toggle')"
v-model="enabled"
:disabled="isLoading"
/>
<span class="gl-mb-3 gl-ml-3 gl-line-height-20">
<gl-sprintf :message="$options.i18n.ENABLE_TOGGLE_DESCRIPTION">
<template #toggleStatus>
<strong>{{ policyEnabledText }}</strong>
</template>
</gl-sprintf>
</span>
</div>
</gl-form-group>
<gl-form-group
v-for="select in $options.selectList"
:id="idGenerator(`${select.name}-group`)"
:key="select.name"
:label-cols="labelCols"
:label-align="labelAlign"
:label-for="idGenerator(select.name)"
:label="select.label"
>
<gl-form-select
:id="idGenerator(select.name)"
:value="value[select.model]"
:disabled="isFormElementDisabled"
@input="updateModel($event, select.model)"
>
<option v-for="option in formOptions[select.model]" :key="option.key" :value="option.key">
{{ option.label }}
</option>
</gl-form-select>
</gl-form-group>
<gl-form-group
v-for="textarea in $options.textAreaList"
:id="idGenerator(`${textarea.name}-group`)"
:key="textarea.name"
:label-cols="labelCols"
:label-align="labelAlign"
:label-for="idGenerator(textarea.name)"
:state="textAreaValidation[textarea.model].state"
:invalid-feedback="textAreaValidation[textarea.model].message"
>
<template #label>
<gl-sprintf :message="textarea.label">
<template #italic="{content}">
<i>{{ content }}</i>
</template>
</gl-sprintf>
</template>
<gl-form-textarea
:id="idGenerator(textarea.name)"
:value="value[textarea.model]"
:placeholder="textarea.placeholder"
:state="textAreaValidation[textarea.model].state"
:disabled="isFormElementDisabled"
trim
@input="updateModel($event, textarea.model)"
/>
<template #description>
<span ref="regex-description">
<gl-sprintf :message="textarea.description">
<template #code="{content}">
<code>{{ content }}</code>
</template>
</gl-sprintf>
</span>
</template>
</gl-form-group>
</div>
</template>
import { s__, __ } from '~/locale';
export const FETCH_SETTINGS_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while fetching the cleanup policy.',
);
export const UPDATE_SETTINGS_ERROR_MESSAGE = s__(
'ContainerRegistry|Something went wrong while updating the cleanup policy.',
);
export const UPDATE_SETTINGS_SUCCESS_MESSAGE = s__(
'ContainerRegistry|Cleanup policy successfully saved.',
);
export const NAME_REGEX_LENGTH = 255;
export const ENABLED_TEXT = __('Enabled');
export const DISABLED_TEXT = __('Disabled');
export const ENABLE_TOGGLE_LABEL = s__('ContainerRegistry|Cleanup policy:');
export const ENABLE_TOGGLE_DESCRIPTION = s__(
'ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion',
);
export const TEXT_AREA_INVALID_FEEDBACK = s__(
'ContainerRegistry|The value of this input should be less than 256 characters',
);
export const EXPIRATION_INTERVAL_LABEL = s__('ContainerRegistry|Expiration interval:');
export const EXPIRATION_SCHEDULE_LABEL = s__('ContainerRegistry|Expiration schedule:');
export const KEEP_N_LABEL = s__('ContainerRegistry|Number of tags to retain:');
export const NAME_REGEX_LABEL = s__(
'ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}',
);
export const NAME_REGEX_PLACEHOLDER = '';
export const NAME_REGEX_DESCRIPTION = s__(
'ContainerRegistry|Wildcards such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}',
);
export const NAME_REGEX_KEEP_LABEL = s__(
'ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}',
);
export const NAME_REGEX_KEEP_PLACEHOLDER = '';
export const NAME_REGEX_KEEP_DESCRIPTION = s__(
'ContainerRegistry|Wildcards such as %{codeStart}.*-master%{codeEnd} or %{codeStart}release-.*%{codeEnd} are supported',
);
export const KEEP_N_OPTIONS = [
{ variable: 1, key: 'ONE_TAG', default: false },
{ variable: 5, key: 'FIVE_TAGS', default: false },
{ variable: 10, key: 'TEN_TAGS', default: true },
{ variable: 25, key: 'TWENTY_FIVE_TAGS', default: false },
{ variable: 50, key: 'FIFTY_TAGS', default: false },
{ variable: 100, key: 'ONE_HUNDRED_TAGS', default: false },
];
export const CADENCE_OPTIONS = [
{ key: 'EVERY_DAY', label: __('Every day'), default: true },
{ key: 'EVERY_WEEK', label: __('Every week'), default: false },
{ key: 'EVERY_TWO_WEEKS', label: __('Every two weeks'), default: false },
{ key: 'EVERY_MONTH', label: __('Every month'), default: false },
{ key: 'EVERY_THREE_MONTHS', label: __('Every three months'), default: false },
];
export const OLDER_THAN_OPTIONS = [
{ key: 'SEVEN_DAYS', variable: 7, default: false },
{ key: 'FOURTEEN_DAYS', variable: 14, default: false },
{ key: 'THIRTY_DAYS', variable: 30, default: false },
{ key: 'NINETY_DAYS', variable: 90, default: true },
];
...@@ -7336,9 +7336,6 @@ msgstr "" ...@@ -7336,9 +7336,6 @@ msgstr ""
msgid "ContainerRegistry|%{title} was successfully scheduled for deletion" msgid "ContainerRegistry|%{title} was successfully scheduled for deletion"
msgstr "" msgstr ""
msgid "ContainerRegistry|%{toggleStatus} - Tags matching the patterns defined below will be scheduled for deletion"
msgstr ""
msgid "ContainerRegistry|Build an image" msgid "ContainerRegistry|Build an image"
msgstr "" msgstr ""
...@@ -7351,9 +7348,6 @@ msgstr "" ...@@ -7351,9 +7348,6 @@ msgstr ""
msgid "ContainerRegistry|Cleanup policy successfully saved." msgid "ContainerRegistry|Cleanup policy successfully saved."
msgstr "" msgstr ""
msgid "ContainerRegistry|Cleanup policy:"
msgstr ""
msgid "ContainerRegistry|Cleanup timed out before it could delete all tags" msgid "ContainerRegistry|Cleanup timed out before it could delete all tags"
msgstr "" msgstr ""
...@@ -7384,9 +7378,6 @@ msgstr "" ...@@ -7384,9 +7378,6 @@ msgstr ""
msgid "ContainerRegistry|Docker connection error" msgid "ContainerRegistry|Docker connection error"
msgstr "" msgstr ""
msgid "ContainerRegistry|Expiration interval:"
msgstr ""
msgid "ContainerRegistry|Expiration policies help manage the storage space used by the Container Registry, but the expiration policies for this registry are disabled. Contact your administrator to enable. %{docLinkStart}More information%{docLinkEnd}" msgid "ContainerRegistry|Expiration policies help manage the storage space used by the Container Registry, but the expiration policies for this registry are disabled. Contact your administrator to enable. %{docLinkStart}More information%{docLinkEnd}"
msgstr "" msgstr ""
...@@ -7396,9 +7387,6 @@ msgstr "" ...@@ -7396,9 +7387,6 @@ msgstr ""
msgid "ContainerRegistry|Expiration policy will run in %{time}" msgid "ContainerRegistry|Expiration policy will run in %{time}"
msgstr "" msgstr ""
msgid "ContainerRegistry|Expiration schedule:"
msgstr ""
msgid "ContainerRegistry|Filter by name" msgid "ContainerRegistry|Filter by name"
msgstr "" msgstr ""
...@@ -7441,9 +7429,6 @@ msgstr "" ...@@ -7441,9 +7429,6 @@ msgstr ""
msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time" msgid "ContainerRegistry|Note: Any policy update will result in a change to the scheduled run date and time"
msgstr "" msgstr ""
msgid "ContainerRegistry|Number of tags to retain:"
msgstr ""
msgid "ContainerRegistry|Published %{timeInfo}" msgid "ContainerRegistry|Published %{timeInfo}"
msgstr "" msgstr ""
...@@ -7518,12 +7503,6 @@ msgstr "" ...@@ -7518,12 +7503,6 @@ msgstr ""
msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them." msgid "ContainerRegistry|Tags that match these rules are %{strongStart}removed%{strongEnd}, unless a rule above says to keep them."
msgstr "" msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}be preserved:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names matching this regex pattern will %{italicStart}expire:%{italicEnd}"
msgstr ""
msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}" msgid "ContainerRegistry|Tags with names that match this regex pattern are kept. %{linkStart}More information%{linkEnd}"
msgstr "" msgstr ""
...@@ -7563,12 +7542,6 @@ msgstr "" ...@@ -7563,12 +7542,6 @@ msgstr ""
msgid "ContainerRegistry|We are having trouble connecting to the Registry, which could be due to an issue with your project name or path. %{docLinkStart}More information%{docLinkEnd}" msgid "ContainerRegistry|We are having trouble connecting to the Registry, which could be due to an issue with your project name or path. %{docLinkStart}More information%{docLinkEnd}"
msgstr "" msgstr ""
msgid "ContainerRegistry|Wildcards such as %{codeStart}.*-master%{codeEnd} or %{codeStart}release-.*%{codeEnd} are supported"
msgstr ""
msgid "ContainerRegistry|Wildcards such as %{codeStart}.*-test%{codeEnd} or %{codeStart}dev-.*%{codeEnd} are supported. To select all tags, use %{codeStart}.*%{codeEnd}"
msgstr ""
msgid "ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. %{docLinkStart}More Information%{docLinkEnd}" msgid "ContainerRegistry|With the Container Registry, every project can have its own space to store its Docker images. %{docLinkStart}More Information%{docLinkEnd}"
msgstr "" msgstr ""
......
...@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils'; ...@@ -2,7 +2,7 @@ import { shallowMount } from '@vue/test-utils';
import { GlSprintf, GlFormInput, GlLink } from '@gitlab/ui'; import { GlSprintf, GlFormInput, GlLink } from '@gitlab/ui';
import { GlFormGroup } from 'jest/registry/shared/stubs'; import { GlFormGroup } from 'jest/registry/shared/stubs';
import component from '~/registry/settings/components/expiration_input.vue'; import component from '~/registry/settings/components/expiration_input.vue';
import { NAME_REGEX_LENGTH } from '~/registry/shared/constants'; import { NAME_REGEX_LENGTH } from '~/registry/settings/constants';
describe('ExpirationInput', () => { describe('ExpirationInput', () => {
let wrapper; let wrapper;
......
...@@ -5,8 +5,8 @@ import createMockApollo from 'jest/helpers/mock_apollo_helper'; ...@@ -5,8 +5,8 @@ import createMockApollo from 'jest/helpers/mock_apollo_helper';
import component from '~/registry/settings/components/registry_settings_app.vue'; import component from '~/registry/settings/components/registry_settings_app.vue';
import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expiration_policy.graphql'; import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expiration_policy.graphql';
import SettingsForm from '~/registry/settings/components/settings_form.vue'; import SettingsForm from '~/registry/settings/components/settings_form.vue';
import { FETCH_SETTINGS_ERROR_MESSAGE } from '~/registry/shared/constants';
import { import {
FETCH_SETTINGS_ERROR_MESSAGE,
UNAVAILABLE_FEATURE_INTRO_TEXT, UNAVAILABLE_FEATURE_INTRO_TEXT,
UNAVAILABLE_USER_FEATURE_TEXT, UNAVAILABLE_USER_FEATURE_TEXT,
} from '~/registry/settings/constants'; } from '~/registry/settings/constants';
......
...@@ -9,7 +9,7 @@ import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expir ...@@ -9,7 +9,7 @@ import expirationPolicyQuery from '~/registry/settings/graphql/queries/get_expir
import { import {
UPDATE_SETTINGS_ERROR_MESSAGE, UPDATE_SETTINGS_ERROR_MESSAGE,
UPDATE_SETTINGS_SUCCESS_MESSAGE, UPDATE_SETTINGS_SUCCESS_MESSAGE,
} from '~/registry/shared/constants'; } from '~/registry/settings/constants';
import { GlCard, GlLoadingIcon } from '../../shared/stubs'; import { GlCard, GlLoadingIcon } from '../../shared/stubs';
import { expirationPolicyPayload, expirationPolicyMutationPayload } from '../mock_data'; import { expirationPolicyPayload, expirationPolicyMutationPayload } from '../mock_data';
......
...@@ -2,7 +2,7 @@ import { ...@@ -2,7 +2,7 @@ import {
formOptionsGenerator, formOptionsGenerator,
optionLabelGenerator, optionLabelGenerator,
olderThanTranslationGenerator, olderThanTranslationGenerator,
} from '~/registry/shared/utils'; } from '~/registry/settings/utils';
describe('Utils', () => { describe('Utils', () => {
describe('optionLabelGenerator', () => { describe('optionLabelGenerator', () => {
......
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Expiration Policy Form renders 1`] = `
<div
class="gl-line-height-20"
>
<gl-form-group-stub
id="expiration-policy-toggle-group"
label="Cleanup policy:"
label-align="right"
label-cols="3"
label-for="expiration-policy-toggle"
>
<div
class="gl-display-flex"
>
<gl-toggle-stub
id="expiration-policy-toggle"
labelposition="top"
/>
<span
class="gl-mb-3 gl-ml-3 gl-line-height-20"
>
<strong>
Disabled
</strong>
- Tags matching the patterns defined below will be scheduled for deletion
</span>
</div>
</gl-form-group-stub>
<gl-form-group-stub
id="expiration-policy-interval-group"
label="Expiration interval:"
label-align="right"
label-cols="3"
label-for="expiration-policy-interval"
>
<gl-form-select-stub
disabled="true"
id="expiration-policy-interval"
>
<option
value="foo"
>
Foo
</option>
<option
value="bar"
>
Bar
</option>
</gl-form-select-stub>
</gl-form-group-stub>
<gl-form-group-stub
id="expiration-policy-schedule-group"
label="Expiration schedule:"
label-align="right"
label-cols="3"
label-for="expiration-policy-schedule"
>
<gl-form-select-stub
disabled="true"
id="expiration-policy-schedule"
>
<option
value="foo"
>
Foo
</option>
<option
value="bar"
>
Bar
</option>
</gl-form-select-stub>
</gl-form-group-stub>
<gl-form-group-stub
id="expiration-policy-latest-group"
label="Number of tags to retain:"
label-align="right"
label-cols="3"
label-for="expiration-policy-latest"
>
<gl-form-select-stub
disabled="true"
id="expiration-policy-latest"
>
<option
value="foo"
>
Foo
</option>
<option
value="bar"
>
Bar
</option>
</gl-form-select-stub>
</gl-form-group-stub>
<gl-form-group-stub
id="expiration-policy-name-matching-group"
label-align="right"
label-cols="3"
label-for="expiration-policy-name-matching"
>
<gl-form-textarea-stub
disabled="true"
id="expiration-policy-name-matching"
noresize="true"
placeholder=""
trim=""
value=""
/>
</gl-form-group-stub>
<gl-form-group-stub
id="expiration-policy-keep-name-group"
label-align="right"
label-cols="3"
label-for="expiration-policy-keep-name"
>
<gl-form-textarea-stub
disabled="true"
id="expiration-policy-keep-name"
noresize="true"
placeholder=""
trim=""
value=""
/>
</gl-form-group-stub>
</div>
`;
import { shallowMount } from '@vue/test-utils';
import { GlSprintf } from '@gitlab/ui';
import component from '~/registry/shared/components/expiration_policy_fields.vue';
import { NAME_REGEX_LENGTH, ENABLED_TEXT, DISABLED_TEXT } from '~/registry/shared/constants';
import { formOptions } from '../mock_data';
describe('Expiration Policy Form', () => {
let wrapper;
const FORM_ELEMENTS_ID_PREFIX = '#expiration-policy';
const findFormGroup = name => wrapper.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}-group`);
const findFormElements = (name, parent = wrapper) =>
parent.find(`${FORM_ELEMENTS_ID_PREFIX}-${name}`);
const mountComponent = props => {
wrapper = shallowMount(component, {
stubs: {
GlSprintf,
},
propsData: {
formOptions,
...props,
},
methods: {
// override idGenerator to avoid having to test with dynamic uid
idGenerator: value => value,
},
});
};
afterEach(() => {
wrapper.destroy();
});
it('renders', () => {
mountComponent();
expect(wrapper.element).toMatchSnapshot();
});
describe.each`
elementName | modelName | value | disabledByToggle
${'toggle'} | ${'enabled'} | ${true} | ${'not disabled'}
${'interval'} | ${'olderThan'} | ${'foo'} | ${'disabled'}
${'schedule'} | ${'cadence'} | ${'foo'} | ${'disabled'}
${'latest'} | ${'keepN'} | ${'foo'} | ${'disabled'}
${'name-matching'} | ${'nameRegex'} | ${'foo'} | ${'disabled'}
${'keep-name'} | ${'nameRegexKeep'} | ${'bar'} | ${'disabled'}
`(
`${FORM_ELEMENTS_ID_PREFIX}-$elementName form element`,
({ elementName, modelName, value, disabledByToggle }) => {
it(`${elementName} form group exist in the dom`, () => {
mountComponent();
const formGroup = findFormGroup(elementName);
expect(formGroup.exists()).toBe(true);
});
it(`${elementName} form group has a label-for property`, () => {
mountComponent();
const formGroup = findFormGroup(elementName);
expect(formGroup.attributes('label-for')).toBe(`expiration-policy-${elementName}`);
});
it(`${elementName} form group has a label-cols property`, () => {
mountComponent({ labelCols: '1' });
const formGroup = findFormGroup(elementName);
return wrapper.vm.$nextTick().then(() => {
expect(formGroup.attributes('label-cols')).toBe('1');
});
});
it(`${elementName} form group has a label-align property`, () => {
mountComponent({ labelAlign: 'foo' });
const formGroup = findFormGroup(elementName);
return wrapper.vm.$nextTick().then(() => {
expect(formGroup.attributes('label-align')).toBe('foo');
});
});
it(`${elementName} form group contains an input element`, () => {
mountComponent();
const formGroup = findFormGroup(elementName);
expect(findFormElements(elementName, formGroup).exists()).toBe(true);
});
it(`${elementName} form element change updated ${modelName} with ${value}`, () => {
mountComponent();
const formGroup = findFormGroup(elementName);
const element = findFormElements(elementName, formGroup);
const modelUpdateEvent = element.vm.$options.model
? element.vm.$options.model.event
: 'input';
element.vm.$emit(modelUpdateEvent, value);
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted('input')).toEqual([
[{ newValue: { [modelName]: value }, modified: modelName }],
]);
});
});
it(`${elementName} is ${disabledByToggle} by enabled set to false`, () => {
mountComponent({ settings: { enabled: false } });
const formGroup = findFormGroup(elementName);
const expectation = disabledByToggle === 'disabled' ? 'true' : undefined;
expect(findFormElements(elementName, formGroup).attributes('disabled')).toBe(expectation);
});
},
);
describe('when isLoading is true', () => {
beforeEach(() => {
mountComponent({ isLoading: true });
});
it.each`
elementName
${'toggle'}
${'interval'}
${'schedule'}
${'latest'}
${'name-matching'}
${'keep-name'}
`(`${FORM_ELEMENTS_ID_PREFIX}-$elementName is disabled`, ({ elementName }) => {
expect(findFormElements(elementName).attributes('disabled')).toBe('true');
});
});
describe.each`
modelName | elementName
${'nameRegex'} | ${'name-matching'}
${'nameRegexKeep'} | ${'keep-name'}
`('regex textarea validation', ({ modelName, elementName }) => {
const invalidString = new Array(NAME_REGEX_LENGTH + 2).join(',');
describe('when apiError contains an error message', () => {
const errorMessage = 'something went wrong';
it('shows the error message on the relevant field', () => {
mountComponent({ apiErrors: { [modelName]: errorMessage } });
expect(findFormGroup(elementName).attributes('invalid-feedback')).toBe(errorMessage);
});
it('gives precedence to API errors compared to local ones', () => {
mountComponent({
apiErrors: { [modelName]: errorMessage },
value: { [modelName]: invalidString },
});
expect(findFormGroup(elementName).attributes('invalid-feedback')).toBe(errorMessage);
});
});
describe('when apiErrors is empty', () => {
it('if the user did not type validation is null', async () => {
mountComponent({ value: { [modelName]: '' } });
expect(findFormGroup(elementName).attributes('state')).toBeUndefined();
expect(wrapper.emitted('validated')).toBeTruthy();
});
it(`if the user typed and is less than ${NAME_REGEX_LENGTH} state is true`, () => {
mountComponent({ value: { [modelName]: 'foo' } });
const formGroup = findFormGroup(elementName);
const formElement = findFormElements(elementName, formGroup);
expect(formGroup.attributes('state')).toBeTruthy();
expect(formElement.attributes('state')).toBeTruthy();
});
describe(`when name regex is longer than ${NAME_REGEX_LENGTH}`, () => {
beforeEach(() => {
mountComponent({ value: { [modelName]: invalidString } });
});
it('textAreaValidation state is false', () => {
expect(findFormGroup(elementName).attributes('state')).toBeUndefined();
// we are forced to check the model attribute because falsy attrs are all casted to undefined in attrs
// while in this case false shows an error and null instead shows nothing.
expect(wrapper.vm.textAreaValidation[modelName].state).toBe(false);
});
it('emit the @invalidated event', () => {
expect(wrapper.emitted('invalidated')).toBeTruthy();
});
});
});
});
describe('help text', () => {
it('toggleDescriptionText show disabled when settings.enabled is false', () => {
mountComponent();
const toggleHelpText = findFormGroup('toggle').find('span');
expect(toggleHelpText.html()).toContain(DISABLED_TEXT);
});
it('toggleDescriptionText show enabled when settings.enabled is true', () => {
mountComponent({ value: { enabled: true } });
const toggleHelpText = findFormGroup('toggle').find('span');
expect(toggleHelpText.html()).toContain(ENABLED_TEXT);
});
});
});
export const options = [{ key: 'foo', label: 'Foo' }, { key: 'bar', label: 'Bar', default: true }];
export const stringifiedOptions = JSON.stringify(options);
export const stringifiedFormOptions = {
cadenceOptions: stringifiedOptions,
keepNOptions: stringifiedOptions,
olderThanOptions: stringifiedOptions,
};
export const formOptions = {
cadence: options,
keepN: options,
olderThan: options,
};
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment