Commit fea17513 authored by Andrew Fontaine's avatar Andrew Fontaine Committed by Martin Wortschack

Refactor Strategy Component to Break Out Parameter

By splitting out strategy into one sub-component per strategy, we should
be able to make it easier to add more strategies down the road.

Strategy sub-components are responsible for knowing how they alter the
strategy object, and emit it upward when changes are made.

The parent strategy component keeps a map between strategy types and
their components, utilizing Vue's `component` component to dynamically
display the correct sub-component in the DOM.
parent abc46947
......@@ -3,7 +3,7 @@ import { GlBadge, GlButton, GlTooltipDirective, GlModal, GlToggle, GlIcon } from
import { sprintf, s__ } from '~/locale';
import glFeatureFlagMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { ROLLOUT_STRATEGY_PERCENT_ROLLOUT, NEW_VERSION_FLAG, LEGACY_FLAG } from '../constants';
import labelForStrategy from '../utils';
import { labelForStrategy } from '../utils';
export default {
components: {
......
......@@ -477,7 +477,7 @@ export default {
<label class="sr-only" :for="rolloutPercentageId(index)">
{{ s__('FeatureFlags|Rollout Percentage') }}
</label>
<div class="w-3rem">
<div class="gl-w-9">
<input
:id="rolloutPercentageId(index)"
v-model="scope.rolloutPercentage"
......
<script>
export default {
mounted() {
this.$emit('change', { parameters: {} });
},
render() {
return this.$slots.default;
},
};
</script>
<script>
import { GlFormSelect } from '@gitlab/ui';
import { s__ } from '~/locale';
import ParameterFormGroup from './parameter_form_group.vue';
export default {
components: {
GlFormSelect,
ParameterFormGroup,
},
props: {
strategy: {
required: true,
type: Object,
},
userLists: {
required: false,
type: Array,
default: () => [],
},
},
translations: {
rolloutUserListLabel: s__('FeatureFlag|List'),
rolloutUserListDescription: s__('FeatureFlag|Select a user list'),
rolloutUserListNoListError: s__('FeatureFlag|There are no configured user lists'),
},
computed: {
userListOptions() {
return this.userLists.map(({ name, id }) => ({ value: id, text: name }));
},
hasUserLists() {
return this.userListOptions.length > 0;
},
userListId() {
return this.strategy?.userListId ?? '';
},
},
methods: {
onUserListChange(list) {
this.$emit('change', {
userListId: list,
});
},
},
};
</script>
<template>
<parameter-form-group
:state="hasUserLists"
:invalid-feedback="$options.translations.rolloutUserListNoListError"
:label="$options.translations.rolloutUserListLabel"
:description="$options.translations.rolloutUserListDescription"
>
<template #default="{ inputId }">
<gl-form-select
:id="inputId"
:value="userListId"
:options="userListOptions"
@change="onUserListChange"
/>
</template>
</parameter-form-group>
</template>
<script>
import { uniqueId } from 'lodash';
import { GlFormGroup } from '@gitlab/ui';
export default {
components: {
GlFormGroup,
},
props: {
inputId: {
required: false,
type: String,
default: () => uniqueId('feature_flag_strategies_'),
},
},
};
</script>
<template>
<gl-form-group :label-for="inputId" v-bind="$attrs">
<slot v-bind="{ inputId }"></slot>
</gl-form-group>
</template>
<script>
import { GlFormInput } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { PERCENT_ROLLOUT_GROUP_ID } from '../../constants';
import ParameterFormGroup from './parameter_form_group.vue';
export default {
components: {
GlFormInput,
ParameterFormGroup,
},
props: {
strategy: {
required: true,
type: Object,
},
},
translations: {
rolloutPercentageDescription: __('Enter a whole number between 0 and 100'),
rolloutPercentageInvalid: s__(
'FeatureFlags|Percent rollout must be a whole number between 0 and 100',
),
rolloutPercentageLabel: s__('FeatureFlag|Percentage'),
},
computed: {
isValid() {
return Number(this.percentage) >= 0 && Number(this.percentage) <= 100;
},
percentage() {
return this.strategy?.parameters?.percentage ?? '';
},
},
methods: {
onPercentageChange(value) {
this.$emit('change', {
parameters: {
percentage: value,
groupId: PERCENT_ROLLOUT_GROUP_ID,
},
});
},
},
};
</script>
<template>
<parameter-form-group
:label="$options.translations.rolloutPercentageLabel"
:description="$options.translations.rolloutPercentageDescription"
:invalid-feedback="$options.translations.rolloutPercentageInvalid"
:state="isValid"
>
<template #default="{ inputId }">
<div class="gl-display-flex gl-align-items-center">
<gl-form-input
:id="inputId"
:value="percentage"
:state="isValid"
class="rollout-percentage gl-text-right gl-w-9"
type="number"
min="0"
max="100"
@input="onPercentageChange"
/>
<span class="gl-ml-2">%</span>
</div>
</template>
</parameter-form-group>
</template>
<script>
import { GlFormTextarea } from '@gitlab/ui';
import { __, s__ } from '~/locale';
import ParameterFormGroup from './parameter_form_group.vue';
export default {
components: {
ParameterFormGroup,
GlFormTextarea,
},
props: {
strategy: {
required: true,
type: Object,
},
},
translations: {
rolloutUserIdsDescription: __('Enter one or more user ID separated by commas'),
rolloutUserIdsLabel: s__('FeatureFlag|User IDs'),
},
computed: {
userIds() {
return this.strategy?.parameters?.userIds ?? '';
},
},
methods: {
onUserIdsChange(value) {
this.$emit('change', {
parameters: {
userIds: value,
},
});
},
},
};
</script>
<template>
<parameter-form-group
:label="$options.translations.rolloutUserIdsLabel"
:description="$options.translations.rolloutUserIdsDescription"
>
<template #default="{ inputId }">
<gl-form-textarea :id="inputId" :value="userIds" @input="onUserIdsChange" />
</template>
</parameter-form-group>
</template>
<script>
import Vue from 'vue';
import { isNumber } from 'lodash';
import {
GlButton,
GlFormSelect,
GlFormInput,
GlFormTextarea,
GlFormGroup,
GlIcon,
GlLink,
GlToken,
} from '@gitlab/ui';
import { GlButton, GlFormSelect, GlFormGroup, GlIcon, GlLink, GlToken } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import {
PERCENT_ROLLOUT_GROUP_ID,
ROLLOUT_STRATEGY_ALL_USERS,
ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
ROLLOUT_STRATEGY_USER_ID,
ROLLOUT_STRATEGY_GITLAB_USER_LIST,
} from '../constants';
import { EMPTY_PARAMETERS, STRATEGY_SELECTIONS } from '../constants';
import NewEnvironmentsDropdown from './new_environments_dropdown.vue';
import StrategyParameters from './strategy_parameters.vue';
export default {
components: {
GlButton,
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlFormSelect,
GlIcon,
GlLink,
GlToken,
NewEnvironmentsDropdown,
},
model: {
prop: 'strategy',
event: 'change',
StrategyParameters,
},
inject: {
strategyTypeDocsPagePath: {
......@@ -66,86 +47,35 @@ export default {
default: () => [],
},
},
ROLLOUT_STRATEGY_ALL_USERS,
ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
ROLLOUT_STRATEGY_USER_ID,
ROLLOUT_STRATEGY_GITLAB_USER_LIST,
i18n: {
allEnvironments: __('All environments'),
environmentsLabel: __('Environments'),
environmentsSelectDescription: __('Select the environment scope for this feature flag.'),
rolloutPercentageDescription: __('Enter a whole number between 0 and 100'),
rolloutPercentageInvalid: s__(
'FeatureFlags|Percent rollout must be a whole number between 0 and 100',
),
rolloutPercentageLabel: s__('FeatureFlag|Percentage'),
rolloutUserIdsDescription: __('Enter one or more user ID separated by commas'),
rolloutUserIdsLabel: s__('FeatureFlag|User IDs'),
rolloutUserListLabel: s__('FeatureFlag|List'),
rolloutUserListDescription: s__('FeatureFlag|Select a user list'),
rolloutUserListNoListError: s__('FeatureFlag|There are no configured user lists'),
strategyTypeDescription: __('Select strategy activation method.'),
strategyTypeLabel: s__('FeatureFlag|Type'),
environmentsSelectDescription: s__(
'FeatureFlag|Select the environment scope for this feature flag.',
),
},
strategies: STRATEGY_SELECTIONS,
data() {
return {
environments: this.strategy.scopes || [],
formStrategy: { ...this.strategy },
formPercentage:
this.strategy.name === ROLLOUT_STRATEGY_PERCENT_ROLLOUT
? this.strategy.parameters.percentage
: '',
formUserIds:
this.strategy.name === ROLLOUT_STRATEGY_USER_ID ? this.strategy.parameters.userIds : '',
formUserListId:
this.strategy.name === ROLLOUT_STRATEGY_GITLAB_USER_LIST ? this.strategy.userListId : '',
strategies: [
{
value: ROLLOUT_STRATEGY_ALL_USERS,
text: __('All users'),
},
{
value: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
text: __('Percent of users'),
},
{
value: ROLLOUT_STRATEGY_USER_ID,
text: __('User IDs'),
},
{
value: ROLLOUT_STRATEGY_GITLAB_USER_LIST,
text: __('User List'),
},
],
};
},
computed: {
strategyTypeId() {
return `strategy-type-${this.index}`;
},
strategyPercentageId() {
return `strategy-percentage-${this.index}`;
},
strategyUserIdsId() {
return `strategy-user-ids-${this.index}`;
},
strategyUserListId() {
return `strategy-user-list-${this.index}`;
},
environmentsDropdownId() {
return `environments-dropdown-${this.index}`;
},
isPercentRollout() {
return this.isStrategyType(ROLLOUT_STRATEGY_PERCENT_ROLLOUT);
},
isUserWithId() {
return this.isStrategyType(ROLLOUT_STRATEGY_USER_ID);
},
isUserList() {
return this.isStrategyType(ROLLOUT_STRATEGY_GITLAB_USER_LIST);
},
appliesToAllEnvironments() {
return (
this.filteredEnvironments.length === 1 &&
......@@ -155,12 +85,6 @@ export default {
filteredEnvironments() {
return this.environments.filter(e => !e.shouldBeDestroyed);
},
userListOptions() {
return this.userLists.map(({ name, id }) => ({ value: id, text: name }));
},
hasUserLists() {
return this.userListOptions.length > 0;
},
},
methods: {
addEnvironment(environment) {
......@@ -169,33 +93,19 @@ export default {
allEnvironmentsScope.shouldBeDestroyed = true;
}
this.environments.push({ environmentScope: environment });
this.onStrategyChange();
this.onStrategyChange({ ...this.formStrategy, scopes: this.environments });
},
onStrategyChange() {
const parameters = {};
const strategy = {
onStrategyTypeChange(name) {
this.onStrategyChange({
...this.formStrategy,
scopes: this.environments,
};
switch (this.formStrategy.name) {
case ROLLOUT_STRATEGY_PERCENT_ROLLOUT:
parameters.percentage = this.formPercentage;
parameters.groupId = PERCENT_ROLLOUT_GROUP_ID;
break;
case ROLLOUT_STRATEGY_USER_ID:
parameters.userIds = this.formUserIds;
break;
case ROLLOUT_STRATEGY_GITLAB_USER_LIST:
strategy.userListId = this.formUserListId;
break;
default:
break;
}
this.$emit('change', {
...strategy,
parameters,
...EMPTY_PARAMETERS,
name,
});
},
onStrategyChange(s) {
this.$emit('change', s);
this.formStrategy = s;
},
removeScope(environment) {
if (isNumber(environment.id)) {
Vue.set(environment, 'shouldBeDestroyed', true);
......@@ -205,10 +115,7 @@ export default {
if (this.filteredEnvironments.length === 0) {
this.environments.push({ environmentScope: '*' });
}
this.onStrategyChange();
},
isStrategyType(type) {
return this.formStrategy.name === type;
this.onStrategyChange({ ...this.formStrategy, scopes: this.environments });
},
},
};
......@@ -224,60 +131,19 @@ export default {
</gl-link>
<gl-form-select
:id="strategyTypeId"
v-model="formStrategy.name"
:options="strategies"
@change="onStrategyChange"
:value="formStrategy.name"
:options="$options.strategies"
@change="onStrategyTypeChange"
/>
</gl-form-group>
</div>
<div data-testid="strategy">
<gl-form-group
v-if="isPercentRollout"
:label="$options.i18n.rolloutPercentageLabel"
:description="$options.i18n.rolloutPercentageDescription"
:label-for="strategyPercentageId"
:invalid-feedback="$options.i18n.rolloutPercentageInvalid"
>
<div class="gl-display-flex gl-align-items-center">
<gl-form-input
:id="strategyPercentageId"
v-model="formPercentage"
class="rollout-percentage gl-text-right gl-w-9"
type="number"
@input="onStrategyChange"
/>
<span class="gl-ml-2">%</span>
</div>
</gl-form-group>
<gl-form-group
v-if="isUserWithId"
:label="$options.i18n.rolloutUserIdsLabel"
:description="$options.i18n.rolloutUserIdsDescription"
:label-for="strategyUserIdsId"
>
<gl-form-textarea
:id="strategyUserIdsId"
v-model="formUserIds"
@input="onStrategyChange"
/>
</gl-form-group>
<gl-form-group
v-if="isUserList"
:state="hasUserLists"
:invalid-feedback="$options.i18n.rolloutUserListNoListError"
:label="$options.i18n.rolloutUserListLabel"
:description="$options.i18n.rolloutUserListDescription"
:label-for="strategyUserListId"
>
<gl-form-select
:id="strategyUserListId"
v-model="formUserListId"
:options="userListOptions"
@change="onStrategyChange"
/>
</gl-form-group>
<strategy-parameters
:strategy="strategy"
:user-lists="userLists"
@change="onStrategyChange"
/>
</div>
<div
......
<script>
import {
ROLLOUT_STRATEGY_ALL_USERS,
ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
ROLLOUT_STRATEGY_USER_ID,
ROLLOUT_STRATEGY_GITLAB_USER_LIST,
} from '../constants';
import Default from './strategies/default.vue';
import PercentRollout from './strategies/percent_rollout.vue';
import UsersWithId from './strategies/users_with_id.vue';
import GitlabUserList from './strategies/gitlab_user_list.vue';
const STRATEGIES = Object.freeze({
[ROLLOUT_STRATEGY_ALL_USERS]: Default,
[ROLLOUT_STRATEGY_PERCENT_ROLLOUT]: PercentRollout,
[ROLLOUT_STRATEGY_USER_ID]: UsersWithId,
[ROLLOUT_STRATEGY_GITLAB_USER_LIST]: GitlabUserList,
});
export default {
props: {
strategy: {
type: Object,
required: true,
},
},
computed: {
strategyComponent() {
return STRATEGIES[(this.strategy?.name)];
},
},
methods: {
onChange(value) {
this.$emit('change', {
...this.strategy,
...value,
});
},
},
};
</script>
<template>
<component
:is="strategyComponent"
v-if="strategyComponent"
:strategy="strategy"
v-bind="$attrs"
@change="onChange"
/>
</template>
......@@ -26,3 +26,24 @@ export const NEW_FLAG_ALERT = s__(
export const FEATURE_FLAG_SCOPE = 'featureFlags';
export const USER_LIST_SCOPE = 'userLists';
export const EMPTY_PARAMETERS = { parameters: {}, userListId: undefined };
export const STRATEGY_SELECTIONS = [
{
value: ROLLOUT_STRATEGY_ALL_USERS,
text: s__('FeatureFlags|All users'),
},
{
value: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
text: s__('FeatureFlags|Percent of users'),
},
{
value: ROLLOUT_STRATEGY_USER_ID,
text: s__('FeatureFlags|User IDs'),
},
{
value: ROLLOUT_STRATEGY_GITLAB_USER_LIST,
text: s__('FeatureFlags|User List'),
},
];
......@@ -182,7 +182,7 @@ export const mapStrategiesToViewModel = strategiesFromRails =>
const mapStrategiesParametersToRails = params => {
if (params.userIds) {
return { ...params, userIds: params.userIds.split(', ').join(',') };
return { ...params, userIds: params.userIds.replace(/\s*,\s*/g, ',') };
}
return params;
};
......
......@@ -30,7 +30,7 @@ const badgeTextByType = {
const scopeName = ({ environment_scope: scope }) =>
scope === ALL_ENVIRONMENTS_NAME ? s__('FeatureFlags|All Environments') : scope;
export default strategy => {
export const labelForStrategy = strategy => {
const { name, parameters } = badgeTextByType[strategy.name];
if (parameters) {
......
......@@ -428,7 +428,6 @@ img.emoji {
/** COMMON SIZING CLASSES **/
.w-0 { width: 0; }
.w-8em { width: 8em; }
.w-3rem { width: 3rem; }
.w-15p { width: 15%; }
.w-30p { width: 30%; }
.w-60p { width: 60%; }
......
......@@ -2548,9 +2548,6 @@ msgstr ""
msgid "All threads resolved"
msgstr ""
msgid "All users"
msgstr ""
msgid "All users must have a name."
msgstr ""
......@@ -11125,6 +11122,9 @@ msgstr ""
msgid "FeatureFlag|Select a user list"
msgstr ""
msgid "FeatureFlag|Select the environment scope for this feature flag."
msgstr ""
msgid "FeatureFlag|There are no configured user lists"
msgstr ""
......@@ -18578,9 +18578,6 @@ msgstr ""
msgid "People without permission will never get a notification."
msgstr ""
msgid "Percent of users"
msgstr ""
msgid "Percentage"
msgstr ""
......@@ -23132,9 +23129,6 @@ msgstr ""
msgid "Select the custom project template source group."
msgstr ""
msgid "Select the environment scope for this feature flag."
msgstr ""
msgid "Select timezone"
msgstr ""
......@@ -27965,12 +27959,6 @@ msgstr ""
msgid "User %{username} was successfully removed."
msgstr ""
msgid "User IDs"
msgstr ""
msgid "User List"
msgstr ""
msgid "User OAuth applications"
msgstr ""
......
import { shallowMount } from '@vue/test-utils';
import Default from '~/feature_flags/components/strategies/default.vue';
describe('~/feature_flags/components/strategies/default.vue', () => {
it('should emit an empty parameter object on mount', () => {
const wrapper = shallowMount(Default);
expect(wrapper.emitted('change')).toEqual([[{ parameters: {} }]]);
});
});
import { mount } from '@vue/test-utils';
import { GlFormSelect } from '@gitlab/ui';
import GitlabUserList from '~/feature_flags/components/strategies/gitlab_user_list.vue';
import { userListStrategy, userList } from '../../mock_data';
const DEFAULT_PROPS = {
strategy: userListStrategy,
userLists: [userList],
};
describe('~/feature_flags/components/strategies/gitlab_user_list.vue', () => {
let wrapper;
const factory = (props = {}) =>
mount(GitlabUserList, { propsData: { ...DEFAULT_PROPS, ...props } });
describe('with user lists', () => {
beforeEach(() => {
wrapper = factory();
});
it('should show the input for userListId with the correct value', () => {
const inputWrapper = wrapper.find(GlFormSelect);
expect(inputWrapper.exists()).toBe(true);
expect(inputWrapper.element.value).toBe('2');
});
it('should emit a change event when altering the userListId', () => {
const inputWrapper = wrapper.find(GitlabUserList);
inputWrapper.vm.$emit('change', {
userListId: '3',
});
expect(wrapper.emitted('change')).toEqual([
[
{
userListId: '3',
},
],
]);
});
});
describe('without user lists', () => {
beforeEach(() => {
wrapper = factory({ userLists: [] });
});
it('should display a message that there are no user lists', () => {
expect(wrapper.text()).toContain('There are no configured user lists');
});
});
});
import { mount } from '@vue/test-utils';
import { GlFormGroup, GlFormInput } from '@gitlab/ui';
import ParameterFormGroup from '~/feature_flags/components/strategies/parameter_form_group.vue';
describe('~/feature_flags/strategies/parameter_form_group.vue', () => {
let wrapper;
let formGroup;
let slot;
beforeEach(() => {
wrapper = mount(ParameterFormGroup, {
propsData: { inputId: 'test-id', label: 'test' },
attrs: { description: 'test description' },
scopedSlots: {
default(props) {
return this.$createElement(GlFormInput, {
attrs: { id: props.inputId, 'data-testid': 'slot' },
});
},
},
});
formGroup = wrapper.find(GlFormGroup);
slot = wrapper.find('[data-testid="slot"]');
});
afterEach(() => {
if (wrapper?.destroy) {
wrapper.destroy();
}
wrapper = null;
});
it('should display the default slot', () => {
expect(slot.exists()).toBe(true);
});
it('should bind the input id to the slot', () => {
expect(slot.attributes('id')).toBe('test-id');
});
it('should bind the label-for to the input id', () => {
expect(formGroup.find('[for="test-id"]').exists()).toBe(true);
});
it('should bind extra attributes to the form group', () => {
expect(formGroup.attributes('description')).toBe('test description');
});
});
import { mount } from '@vue/test-utils';
import { GlFormInput } from '@gitlab/ui';
import PercentRollout from '~/feature_flags/components/strategies/percent_rollout.vue';
import ParameterFormGroup from '~/feature_flags/components/strategies/parameter_form_group.vue';
import { PERCENT_ROLLOUT_GROUP_ID } from '~/feature_flags/constants';
import { percentRolloutStrategy } from '../../mock_data';
const DEFAULT_PROPS = {
strategy: percentRolloutStrategy,
};
describe('~/feature_flags/components/strategies/percent_rollout.vue', () => {
let wrapper;
let input;
let formGroup;
const factory = (props = {}) =>
mount(PercentRollout, { propsData: { ...DEFAULT_PROPS, ...props } });
afterEach(() => {
if (wrapper?.destroy) {
wrapper.destroy();
}
wrapper = null;
});
describe('with valid percentage', () => {
beforeEach(() => {
wrapper = factory();
input = wrapper.find(GlFormInput);
formGroup = wrapper.find(ParameterFormGroup);
});
it('displays the current value', () => {
expect(input.element.value).toBe(percentRolloutStrategy.parameters.percentage);
});
it('emits a change when the value changes', async () => {
input.setValue('75');
await wrapper.vm.$nextTick();
expect(wrapper.emitted('change')).toEqual([
[{ parameters: { percentage: '75', groupId: PERCENT_ROLLOUT_GROUP_ID } }],
]);
});
it('does not show errors', () => {
expect(formGroup.attributes('state')).toBe('true');
});
});
describe('with percentage that is out of range', () => {
beforeEach(() => {
wrapper = factory({ strategy: { parameters: { percentage: '101' } } });
input = wrapper.find(GlFormInput);
formGroup = wrapper.find(ParameterFormGroup);
});
it('shows errors', () => {
expect(formGroup.attributes('state')).toBeUndefined();
});
});
});
import { mount } from '@vue/test-utils';
import { GlFormTextarea } from '@gitlab/ui';
import UsersWithId from '~/feature_flags/components/strategies/users_with_id.vue';
import { usersWithIdStrategy } from '../../mock_data';
const DEFAULT_PROPS = {
strategy: usersWithIdStrategy,
};
describe('~/feature_flags/components/users_with_id.vue', () => {
let wrapper;
let textarea;
const factory = (props = {}) => mount(UsersWithId, { propsData: { ...DEFAULT_PROPS, ...props } });
beforeEach(() => {
wrapper = factory();
textarea = wrapper.find(GlFormTextarea);
});
afterEach(() => {
if (wrapper?.destroy) {
wrapper.destroy();
}
wrapper = null;
});
it('should display the current value of the parameters', () => {
expect(textarea.element.value).toBe(usersWithIdStrategy.parameters.userIds);
});
it('should emit a change event when the IDs change', () => {
textarea.setValue('4,5,6');
expect(wrapper.emitted('change')).toEqual([[{ parameters: { userIds: '4,5,6' } }]]);
});
});
import { shallowMount } from '@vue/test-utils';
import { last } from 'lodash';
import {
ROLLOUT_STRATEGY_ALL_USERS,
ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
ROLLOUT_STRATEGY_USER_ID,
ROLLOUT_STRATEGY_GITLAB_USER_LIST,
} from '~/feature_flags/constants';
import Default from '~/feature_flags/components/strategies/default.vue';
import GitlabUserList from '~/feature_flags/components/strategies/gitlab_user_list.vue';
import PercentRollout from '~/feature_flags/components/strategies/percent_rollout.vue';
import UsersWithId from '~/feature_flags/components/strategies/users_with_id.vue';
import StrategyParameters from '~/feature_flags/components/strategy_parameters.vue';
import { allUsersStrategy, userList } from '../mock_data';
const DEFAULT_PROPS = {
strategy: allUsersStrategy,
userLists: [userList],
};
describe('~/feature_flags/components/strategy_parameters.vue', () => {
let wrapper;
const factory = (props = {}) =>
shallowMount(StrategyParameters, {
propsData: {
...DEFAULT_PROPS,
...props,
},
});
afterEach(() => {
if (wrapper?.destroy) {
wrapper.destroy();
}
wrapper = null;
});
describe.each`
name | component
${ROLLOUT_STRATEGY_ALL_USERS} | ${Default}
${ROLLOUT_STRATEGY_PERCENT_ROLLOUT} | ${PercentRollout}
${ROLLOUT_STRATEGY_USER_ID} | ${UsersWithId}
${ROLLOUT_STRATEGY_GITLAB_USER_LIST} | ${GitlabUserList}
`('with $name', ({ name, component }) => {
let strategy;
beforeEach(() => {
strategy = { name, parameters: {} };
wrapper = factory({ strategy });
});
it('should show the correct component', () => {
expect(wrapper.contains(component)).toBe(true);
});
it('should emit changes from the lower component', () => {
const strategyParameterWrapper = wrapper.find(component);
strategyParameterWrapper.vm.$emit('change', { parameters: { foo: 'bar' } });
expect(last(wrapper.emitted('change'))).toEqual([
{
name,
parameters: { foo: 'bar' },
},
]);
});
});
describe('pass through props', () => {
it('should pass through any extra props that might be needed', () => {
wrapper = factory({
strategy: {
name: ROLLOUT_STRATEGY_GITLAB_USER_LIST,
},
});
expect(wrapper.find(GitlabUserList).props('userLists')).toEqual([userList]);
});
});
});
import {
ROLLOUT_STRATEGY_ALL_USERS,
ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
ROLLOUT_STRATEGY_GITLAB_USER_LIST,
ROLLOUT_STRATEGY_USER_ID,
} from '~/feature_flags/constants';
export const featureFlag = {
......@@ -102,6 +104,25 @@ export const userList = {
edit_path: '/path/to/user/list/edit',
};
export const userListStrategy = {
name: ROLLOUT_STRATEGY_GITLAB_USER_LIST,
parameters: {},
scopes: [],
userListId: userList.id,
};
export const percentRolloutStrategy = {
name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
parameters: { percentage: '50', groupId: 'default' },
scopes: [],
};
export const usersWithIdStrategy = {
name: ROLLOUT_STRATEGY_USER_ID,
parameters: { userIds: '1,2,3' },
scopes: [],
};
export const allUsersStrategy = {
name: ROLLOUT_STRATEGY_ALL_USERS,
parameters: {},
......
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