Commit 259db448 authored by Andrew Fontaine's avatar Andrew Fontaine

Update Feature Flag Strategy Component, Match API

Now handles properly adding and deleting environments!
parent 82f259bc
<script> <script>
import Vue from 'vue';
import { isString } from 'lodash';
import { import {
GlFormSelect, GlFormSelect,
GlFormInput, GlFormInput,
...@@ -116,13 +118,24 @@ export default { ...@@ -116,13 +118,24 @@ export default {
isUserWithId() { isUserWithId() {
return this.isStrategyType(ROLLOUT_STRATEGY_USER_ID); return this.isStrategyType(ROLLOUT_STRATEGY_USER_ID);
}, },
hasNoDefinedEnvironments() { appliesToAllEnvironments() {
return this.environments.length === 0; return (
this.filteredEnvironments.length === 0 ||
(this.filteredEnvironments.length === 1 &&
this.filteredEnvironments[0].environment_scope === '*')
);
},
filteredEnvironments() {
return this.environments.filter(e => !e.shouldDestroy);
}, },
}, },
methods: { methods: {
addEnvironment(environment) { addEnvironment(environment) {
this.environments.push(environment); const allEnvironmentsScope = this.environments.find(scope => scope.environmentScope === '*');
if (allEnvironmentsScope) {
allEnvironmentsScope.shouldDestroy = true;
}
this.environments.push({ environmentScope: environment });
this.onStrategyChange(); this.onStrategyChange();
}, },
onStrategyChange() { onStrategyChange() {
...@@ -145,7 +158,11 @@ export default { ...@@ -145,7 +158,11 @@ export default {
}); });
}, },
removeScope(environment) { removeScope(environment) {
this.environments = this.environments.filter(e => e !== environment); if (isString(environment.id)) {
Vue.set(environment, 'shouldBeDestroyed', true);
} else {
this.environments = this.environments.filter(e => e !== environment);
}
this.onStrategyChange(); this.onStrategyChange();
}, },
isStrategyType(type) { isStrategyType(type) {
...@@ -155,7 +172,7 @@ export default { ...@@ -155,7 +172,7 @@ export default {
}; };
</script> </script>
<template> <template>
<div> <div class="border-top py-4">
<div class="flex flex-column flex-md-row flex-md-wrap"> <div class="flex flex-column flex-md-row flex-md-wrap">
<div class="mr-5"> <div class="mr-5">
<gl-form-group <gl-form-group
...@@ -207,7 +224,7 @@ export default { ...@@ -207,7 +224,7 @@ export default {
</div> </div>
<div class="align-self-end align-self-md-stretch order-first offset-md-0 order-md-0 ml-auto"> <div class="align-self-end align-self-md-stretch order-first offset-md-0 order-md-0 ml-auto">
<gl-button v-if="canDelete" variant="danger"> <gl-button v-if="canDelete" variant="danger" @click="$emit('delete')">
<span class="d-md-none"> <span class="d-md-none">
{{ $options.translations.removeLabel }} {{ $options.translations.removeLabel }}
</span> </span>
...@@ -224,17 +241,17 @@ export default { ...@@ -224,17 +241,17 @@ export default {
class="mr-2" class="mr-2"
@add="addEnvironment" @add="addEnvironment"
/> />
<span v-if="hasNoDefinedEnvironments" class="text-secondary mt-2 mt-md-0 ml-md-3"> <span v-if="appliesToAllEnvironments" class="text-secondary mt-2 mt-md-0 ml-md-3">
{{ $options.translations.allEnvironments }} {{ $options.translations.allEnvironments }}
</span> </span>
<div v-else class="flex align-items-center"> <div v-else class="flex align-items-center">
<gl-token <gl-token
v-for="environment in environments" v-for="environment in filteredEnvironments"
:key="environment" :key="environment.id"
class="mt-2 mr-2 mt-md-0 mr-md-0 ml-md-2 rounded-pill" class="mt-2 mr-2 mt-md-0 mr-md-0 ml-md-2 rounded-pill"
@close="removeScope(environment)" @close="removeScope(environment)"
> >
{{ environment }} {{ environment.environmentScope }}
</gl-token> </gl-token>
</div> </div>
</div> </div>
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlFormSelect, GlFormTextarea, GlFormInput, GlToken } from '@gitlab/ui'; import { GlFormSelect, GlFormTextarea, GlFormInput, GlToken, GlButton } from '@gitlab/ui';
import { import {
PERCENT_ROLLOUT_GROUP_ID, PERCENT_ROLLOUT_GROUP_ID,
ROLLOUT_STRATEGY_ALL_USERS, ROLLOUT_STRATEGY_ALL_USERS,
...@@ -22,6 +22,10 @@ describe('Feature flags strategy', () => { ...@@ -22,6 +22,10 @@ describe('Feature flags strategy', () => {
}, },
}, },
) => { ) => {
if (wrapper) {
wrapper.destroy();
wrapper = null;
}
wrapper = shallowMount(Strategy, opts); wrapper = shallowMount(Strategy, opts);
}; };
...@@ -76,62 +80,136 @@ describe('Feature flags strategy', () => { ...@@ -76,62 +80,136 @@ describe('Feature flags strategy', () => {
}); });
describe('with a strategy', () => { describe('with a strategy', () => {
beforeEach(() => { describe('with scopes defined', () => {
const strategy = { let strategy;
name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
parameters: { percentage: '50' },
scopes: [],
};
const propsData = { strategy, index: 0, endpoint: '', canDelete: true };
factory({ propsData });
});
it('should change the parameters if a different strategy is chosen', () => { beforeEach(() => {
const select = wrapper.find(GlFormSelect); strategy = {
select.vm.$emit('input', ROLLOUT_STRATEGY_ALL_USERS); name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
select.vm.$emit('change', ROLLOUT_STRATEGY_ALL_USERS); parameters: { percentage: '50' },
return wrapper.vm.$nextTick().then(() => { scopes: [{ environmentScope: '*' }],
expect(wrapper.find(GlFormInput).exists()).toBe(false); };
expect(wrapper.emitted('change')).toEqual([ const propsData = { strategy, index: 0, endpoint: '', canDelete: true };
[{ name: ROLLOUT_STRATEGY_ALL_USERS, parameters: {}, scopes: [] }], factory({ propsData });
]);
}); });
});
it('should display selected scopes', () => { it('should change the parameters if a different strategy is chosen', () => {
const dropdown = wrapper.find(NewEnvironmentsDropdown); const select = wrapper.find(GlFormSelect);
dropdown.vm.$emit('add', 'production'); select.vm.$emit('input', ROLLOUT_STRATEGY_ALL_USERS);
return wrapper.vm.$nextTick().then(() => { select.vm.$emit('change', ROLLOUT_STRATEGY_ALL_USERS);
expect(wrapper.findAll(GlToken)).toHaveLength(1); return wrapper.vm.$nextTick().then(() => {
expect(wrapper.find(GlToken).text()).toBe('production'); expect(wrapper.find(GlFormInput).exists()).toBe(false);
expect(wrapper.emitted('change')).toEqual([
[
{
name: ROLLOUT_STRATEGY_ALL_USERS,
parameters: {},
scopes: [{ environmentScope: '*' }],
},
],
]);
});
});
it('should display selected scopes', () => {
const dropdown = wrapper.find(NewEnvironmentsDropdown);
dropdown.vm.$emit('add', 'production');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.findAll(GlToken)).toHaveLength(1);
expect(wrapper.find(GlToken).text()).toBe('production');
});
});
it('should display all selected scopes', () => {
const dropdown = wrapper.find(NewEnvironmentsDropdown);
dropdown.vm.$emit('add', 'production');
dropdown.vm.$emit('add', 'staging');
return wrapper.vm.$nextTick().then(() => {
const tokens = wrapper.findAll(GlToken);
expect(tokens).toHaveLength(2);
expect(tokens.at(0).text()).toBe('production');
expect(tokens.at(1).text()).toBe('staging');
});
});
it('should emit selected scopes', () => {
const dropdown = wrapper.find(NewEnvironmentsDropdown);
dropdown.vm.$emit('add', 'production');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted('change')).toEqual([
[
{
name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
parameters: { percentage: '50', groupId: PERCENT_ROLLOUT_GROUP_ID },
scopes: [
{ environmentScope: '*', shouldDestroy: true },
{ environmentScope: 'production' },
],
},
],
]);
});
}); });
});
it('should display all selected scopes', () => { it('should emit a delete if the delete button is clicked', () => {
const dropdown = wrapper.find(NewEnvironmentsDropdown); wrapper.find(GlButton).vm.$emit('click');
dropdown.vm.$emit('add', 'production'); expect(wrapper.emitted('delete')).toEqual([[]]);
dropdown.vm.$emit('add', 'staging'); });
return wrapper.vm.$nextTick().then(() => {
const tokens = wrapper.findAll(GlToken); it('should not display the delete button if can delete is false', () => {
expect(tokens).toHaveLength(2); const propsData = { strategy, index: 0, endpoint: '', canDelete: false };
expect(tokens.at(0).text()).toBe('production'); factory({ propsData });
expect(tokens.at(1).text()).toBe('staging');
expect(wrapper.find(GlButton).exists()).toBe(false);
}); });
}); });
it('should emit selected scopes', () => { describe('wihtout scopes defined', () => {
const dropdown = wrapper.find(NewEnvironmentsDropdown); beforeEach(() => {
dropdown.vm.$emit('add', 'production'); const strategy = {
return wrapper.vm.$nextTick().then(() => { name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
expect(wrapper.emitted('change')).toEqual([ parameters: { percentage: '50' },
[ scopes: [],
{ };
name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT, const propsData = { strategy, index: 0, endpoint: '', canDelete: true };
parameters: { percentage: '50', groupId: PERCENT_ROLLOUT_GROUP_ID }, factory({ propsData });
scopes: ['production'], });
},
], it('should display selected scopes', () => {
]); const dropdown = wrapper.find(NewEnvironmentsDropdown);
dropdown.vm.$emit('add', 'production');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.findAll(GlToken)).toHaveLength(1);
expect(wrapper.find(GlToken).text()).toBe('production');
});
});
it('should display all selected scopes', () => {
const dropdown = wrapper.find(NewEnvironmentsDropdown);
dropdown.vm.$emit('add', 'production');
dropdown.vm.$emit('add', 'staging');
return wrapper.vm.$nextTick().then(() => {
const tokens = wrapper.findAll(GlToken);
expect(tokens).toHaveLength(2);
expect(tokens.at(0).text()).toBe('production');
expect(tokens.at(1).text()).toBe('staging');
});
});
it('should emit selected scopes', () => {
const dropdown = wrapper.find(NewEnvironmentsDropdown);
dropdown.vm.$emit('add', 'production');
return wrapper.vm.$nextTick().then(() => {
expect(wrapper.emitted('change')).toEqual([
[
{
name: ROLLOUT_STRATEGY_PERCENT_ROLLOUT,
parameters: { percentage: '50', groupId: PERCENT_ROLLOUT_GROUP_ID },
scopes: [{ environmentScope: 'production' }],
},
],
]);
});
}); });
}); });
}); });
......
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