Commit a71fe47f authored by Mark Florian's avatar Mark Florian

Merge branch 'ci-variables-ui-components' into 'master'

Refactor project/group variables UI into Vue - 3 / 4

See merge request gitlab-org/gitlab!25468
parents d94f8a61 f3990c7e
<script>
import { __ } from '~/locale';
import { mapActions, mapState } from 'vuex';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
import {
GlModal,
GlFormSelect,
GlFormGroup,
GlFormInput,
GlFormCheckbox,
GlLink,
GlIcon,
} from '@gitlab/ui';
export default {
modalId: ADD_CI_VARIABLE_MODAL_ID,
components: {
GlModal,
GlFormSelect,
GlFormGroup,
GlFormInput,
GlFormCheckbox,
GlLink,
GlIcon,
},
computed: {
...mapState([
'projectId',
'environments',
'typeOptions',
'variable',
'variableBeingEdited',
'isGroup',
'maskableRegex',
]),
canSubmit() {
return this.variableData.key !== '' && this.variableData.secret_value !== '';
},
canMask() {
const regex = RegExp(this.maskableRegex);
return regex.test(this.variableData.secret_value);
},
variableData() {
return this.variableBeingEdited || this.variable;
},
modalActionText() {
return this.variableBeingEdited ? __('Update Variable') : __('Add variable');
},
primaryAction() {
return {
text: this.modalActionText,
attributes: { variant: 'success', disabled: !this.canSubmit },
};
},
cancelAction() {
return {
text: __('Cancel'),
};
},
},
methods: {
...mapActions([
'addVariable',
'updateVariable',
'resetEditing',
'displayInputValue',
'clearModal',
]),
updateOrAddVariable() {
if (this.variableBeingEdited) {
this.updateVariable(this.variableBeingEdited);
} else {
this.addVariable();
}
},
resetModalHandler() {
if (this.variableBeingEdited) {
this.resetEditing();
} else {
this.clearModal();
}
},
},
};
</script>
<template>
<gl-modal
:modal-id="$options.modalId"
:title="modalActionText"
:action-primary="primaryAction"
:action-cancel="cancelAction"
@ok="updateOrAddVariable"
@hidden="resetModalHandler"
>
<form>
<gl-form-group label="Type" label-for="ci-variable-type">
<gl-form-select
id="ci-variable-type"
v-model="variableData.variable_type"
:options="typeOptions"
/>
</gl-form-group>
<div class="d-flex">
<gl-form-group label="Key" label-for="ci-variable-key" class="w-50 append-right-15">
<gl-form-input
id="ci-variable-key"
v-model="variableData.key"
type="text"
data-qa-selector="variable_key"
/>
</gl-form-group>
<gl-form-group label="Value" label-for="ci-variable-value" class="w-50">
<gl-form-input
id="ci-variable-value"
v-model="variableData.secret_value"
type="text"
data-qa-selector="variable_value"
/>
</gl-form-group>
</div>
<gl-form-group v-if="!isGroup" label="Environment scope" label-for="ci-variable-env">
<gl-form-select
id="ci-variable-env"
v-model="variableData.environment_scope"
:options="environments"
/>
</gl-form-group>
<gl-form-group label="Flags" label-for="ci-variable-flags">
<gl-form-checkbox v-model="variableData.protected" class="mb-0">
{{ __('Protect variable') }}
<gl-link href="/help/ci/variables/README#protected-environment-variables">
<gl-icon name="question" :size="12" />
</gl-link>
<p class="prepend-top-4 clgray">
{{ __('Allow variables to run on protected branches and tags.') }}
</p>
</gl-form-checkbox>
<gl-form-checkbox
ref="masked-ci-variable"
v-model="variableData.masked"
:disabled="!canMask"
data-qa-selector="variable_masked"
>
{{ __('Mask variable') }}
<gl-link href="/help/ci/variables/README#masked-variables">
<gl-icon name="question" :size="12" />
</gl-link>
<p class="prepend-top-4 append-bottom-0 clgray">
{{
__(
'Variables will be masked in job logs. Requires values to meet regular expression requirements.',
)
}}
<gl-link href="/help/ci/variables/README#masked-variables">{{
__('More information')
}}</gl-link>
</p>
</gl-form-checkbox>
</gl-form-group>
</form>
</gl-modal>
</template>
<script>
import CiVariableModal from './ci_variable_modal.vue';
import CiVariableTable from './ci_variable_table.vue';
import { mapState, mapActions } from 'vuex';
export default {
components: {
CiVariableModal,
CiVariableTable,
},
computed: {
...mapState(['isGroup']),
},
mounted() {
if (!this.isGroup) {
this.fetchEnvironments();
}
},
methods: {
...mapActions(['fetchEnvironments']),
},
};
</script>
<template>
<div class="row">
<div class="col-lg-12">
<ci-variable-table />
<ci-variable-modal />
</div>
</div>
</template>
<script>
import { GlTable, GlButton, GlModalDirective, GlIcon } from '@gitlab/ui';
import { s__, __ } from '~/locale';
import { mapState, mapActions } from 'vuex';
import { ADD_CI_VARIABLE_MODAL_ID } from '../constants';
export default {
modalId: ADD_CI_VARIABLE_MODAL_ID,
fields: [
{
key: 'variable_type',
label: s__('CiVariables|Type'),
},
{
key: 'key',
label: s__('CiVariables|Key'),
},
{
key: 'value',
label: s__('CiVariables|Value'),
tdClass: 'qa-ci-variable-input-value',
},
{
key: 'protected',
label: s__('CiVariables|Protected'),
},
{
key: 'masked',
label: s__('CiVariables|Masked'),
},
{
key: 'environment_scope',
label: s__('CiVariables|Environment Scope'),
},
{
key: 'actions',
label: '',
},
],
components: {
GlTable,
GlButton,
GlIcon,
},
directives: {
GlModalDirective,
},
computed: {
...mapState(['variables', 'valuesHidden', 'isGroup', 'isLoading', 'isDeleting']),
valuesButtonText() {
return this.valuesHidden ? __('Reveal values') : __('Hide values');
},
tableIsNotEmpty() {
return this.variables && this.variables.length > 0;
},
fields() {
if (this.isGroup) {
return this.$options.fields.filter(field => field.key !== 'environment_scope');
}
return this.$options.fields;
},
},
mounted() {
this.fetchVariables();
},
methods: {
...mapActions(['fetchVariables', 'deleteVariable', 'toggleValues', 'editVariable']),
},
};
</script>
<template>
<div class="ci-variable-table">
<gl-table
:fields="fields"
:items="variables"
responsive
show-empty
tbody-tr-class="js-ci-variable-row"
>
<template #cell(value)="data">
<span v-if="valuesHidden">*****************</span>
<span v-else>{{ data.value }}</span>
</template>
<template #cell(actions)="data">
<gl-button
ref="edit-ci-variable"
v-gl-modal-directive="$options.modalId"
@click="editVariable(data.item)"
>
<gl-icon name="pencil" />
</gl-button>
<gl-button
ref="delete-ci-variable"
category="secondary"
variant="danger"
@click="deleteVariable(data.item)"
>
<gl-icon name="remove" />
</gl-button>
</template>
<template #empty>
<p ref="empty-variables" class="settings-message text-center empty-variables">
{{
__(
'There are currently no variables, add a variable with the Add Variable button below.',
)
}}
</p>
</template>
</gl-table>
<div class="ci-variable-actions d-flex justify-content-end">
<gl-button
v-if="tableIsNotEmpty"
ref="secret-value-reveal-button"
data-qa-selector="reveal_ci_variable_value"
class="append-right-8"
@click="toggleValues(!valuesHidden)"
>{{ valuesButtonText }}</gl-button
>
<gl-button
ref="add-ci-variable"
v-gl-modal-directive="$options.modalId"
data-qa-selector="add_ci_variable"
variant="success"
>{{ __('Add Variable') }}</gl-button
>
</div>
</div>
</template>
// eslint-disable-next-line import/prefer-default-export
export const ADD_CI_VARIABLE_MODAL_ID = 'add-ci-variable';
import Vue from 'vue';
import CiVariableSettings from './components/ci_variable_settings.vue';
import createStore from './store';
import { parseBoolean } from '~/lib/utils/common_utils';
export default () => {
const el = document.getElementById('js-ci-project-variables');
const { endpoint, projectId, group, maskableRegex } = el.dataset;
const isGroup = parseBoolean(group);
const store = createStore({
endpoint,
projectId,
isGroup,
maskableRegex,
});
return new Vue({
el,
store,
render(createElement) {
return createElement(CiVariableSettings);
},
});
};
import { __ } from '~/locale'; import { __ } from '~/locale';
import { cloneDeep } from 'lodash';
const variableType = 'env_var'; const variableType = 'env_var';
const fileType = 'file'; const fileType = 'file';
...@@ -24,9 +25,9 @@ export const prepareDataForDisplay = variables => { ...@@ -24,9 +25,9 @@ export const prepareDataForDisplay = variables => {
}; };
export const prepareDataForApi = (variable, destroy = false) => { export const prepareDataForApi = (variable, destroy = false) => {
const variableCopy = variable; const variableCopy = cloneDeep(variable);
variableCopy.protected.toString(); variableCopy.protected = variableCopy.protected.toString();
variableCopy.masked.toString(); variableCopy.masked = variableCopy.masked.toString();
variableCopy.variable_type = variableTypeHandler(variableCopy.variable_type); variableCopy.variable_type = variableTypeHandler(variableCopy.variable_type);
if (variableCopy.environment_scope === __('All environments')) { if (variableCopy.environment_scope === __('All environments')) {
......
import initSettingsPanels from '~/settings_panels'; import initSettingsPanels from '~/settings_panels';
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list'; import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
import initVariableList from '~/ci_variable_list';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels // Initialize expandable settings panels
initSettingsPanels(); initSettingsPanels();
if (gon.features.newVariablesUi) {
initVariableList();
} else {
const variableListEl = document.querySelector('.js-ci-variable-list-section'); const variableListEl = document.querySelector('.js-ci-variable-list-section');
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new AjaxVariableList({ new AjaxVariableList({
...@@ -14,4 +18,5 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -14,4 +18,5 @@ document.addEventListener('DOMContentLoaded', () => {
saveEndpoint: variableListEl.dataset.saveEndpoint, saveEndpoint: variableListEl.dataset.saveEndpoint,
maskableRegex: variableListEl.dataset.maskableRegex, maskableRegex: variableListEl.dataset.maskableRegex,
}); });
}
}); });
...@@ -2,6 +2,7 @@ import initSettingsPanels from '~/settings_panels'; ...@@ -2,6 +2,7 @@ import initSettingsPanels from '~/settings_panels';
import SecretValues from '~/behaviors/secret_values'; import SecretValues from '~/behaviors/secret_values';
import AjaxVariableList from '~/ci_variable_list/ajax_variable_list'; import AjaxVariableList from '~/ci_variable_list/ajax_variable_list';
import registrySettingsApp from '~/registry/settings/registry_settings_bundle'; import registrySettingsApp from '~/registry/settings/registry_settings_bundle';
import initVariableList from '~/ci_variable_list';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
// Initialize expandable settings panels // Initialize expandable settings panels
...@@ -15,6 +16,9 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -15,6 +16,9 @@ document.addEventListener('DOMContentLoaded', () => {
runnerTokenSecretValue.init(); runnerTokenSecretValue.init();
} }
if (gon.features.newVariablesUi) {
initVariableList();
} else {
const variableListEl = document.querySelector('.js-ci-variable-list-section'); const variableListEl = document.querySelector('.js-ci-variable-list-section');
// eslint-disable-next-line no-new // eslint-disable-next-line no-new
new AjaxVariableList({ new AjaxVariableList({
...@@ -24,6 +28,7 @@ document.addEventListener('DOMContentLoaded', () => { ...@@ -24,6 +28,7 @@ document.addEventListener('DOMContentLoaded', () => {
saveEndpoint: variableListEl.dataset.saveEndpoint, saveEndpoint: variableListEl.dataset.saveEndpoint,
maskableRegex: variableListEl.dataset.maskableRegex, maskableRegex: variableListEl.dataset.maskableRegex,
}); });
}
// hide extra auto devops settings based checkbox state // hide extra auto devops settings based checkbox state
const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings'); const autoDevOpsExtraSettings = document.querySelector('.js-extra-settings');
......
...@@ -130,6 +130,10 @@ ...@@ -130,6 +130,10 @@
border-radius: $border-radius-base; border-radius: $border-radius-base;
} }
.empty-variables {
padding: 20px 0;
}
.warning-title { .warning-title {
color: $orange-500; color: $orange-500;
} }
...@@ -370,3 +374,10 @@ ...@@ -370,3 +374,10 @@
.push-pull-table { .push-pull-table {
margin-top: 1em; margin-top: 1em;
} }
.ci-variable-table {
table tr th {
background-color: transparent;
border: 0;
}
}
...@@ -1057,6 +1057,9 @@ msgstr "" ...@@ -1057,6 +1057,9 @@ msgstr ""
msgid "Add README" msgid "Add README"
msgstr "" msgstr ""
msgid "Add Variable"
msgstr ""
msgid "Add Zoom meeting" msgid "Add Zoom meeting"
msgstr "" msgstr ""
...@@ -1189,6 +1192,9 @@ msgstr "" ...@@ -1189,6 +1192,9 @@ msgstr ""
msgid "Add users to group" msgid "Add users to group"
msgstr "" msgstr ""
msgid "Add variable"
msgstr ""
msgid "Add webhook" msgid "Add webhook"
msgstr "" msgstr ""
...@@ -1638,6 +1644,9 @@ msgstr "" ...@@ -1638,6 +1644,9 @@ msgstr ""
msgid "Allow users to request access (if visibility is public or internal)" msgid "Allow users to request access (if visibility is public or internal)"
msgstr "" msgstr ""
msgid "Allow variables to run on protected branches and tags."
msgstr ""
msgid "Allowed email domain restriction only permitted for top-level groups" msgid "Allowed email domain restriction only permitted for top-level groups"
msgstr "" msgstr ""
...@@ -3702,6 +3711,9 @@ msgstr "" ...@@ -3702,6 +3711,9 @@ msgstr ""
msgid "CiVariables|Cannot use Masked Variable with current value" msgid "CiVariables|Cannot use Masked Variable with current value"
msgstr "" msgstr ""
msgid "CiVariables|Environment Scope"
msgstr ""
msgid "CiVariables|Input variable key" msgid "CiVariables|Input variable key"
msgstr "" msgstr ""
...@@ -3714,6 +3726,9 @@ msgstr "" ...@@ -3714,6 +3726,9 @@ msgstr ""
msgid "CiVariables|Masked" msgid "CiVariables|Masked"
msgstr "" msgstr ""
msgid "CiVariables|Protected"
msgstr ""
msgid "CiVariables|Remove variable row" msgid "CiVariables|Remove variable row"
msgstr "" msgstr ""
...@@ -11822,6 +11837,9 @@ msgstr "" ...@@ -11822,6 +11837,9 @@ msgstr ""
msgid "Marks this issue as related to %{issue_ref}." msgid "Marks this issue as related to %{issue_ref}."
msgstr "" msgstr ""
msgid "Mask variable"
msgstr ""
msgid "Match not found; try refining your search query." msgid "Match not found; try refining your search query."
msgstr "" msgstr ""
...@@ -15491,6 +15509,9 @@ msgstr "" ...@@ -15491,6 +15509,9 @@ msgstr ""
msgid "Prompt users to upload SSH keys" msgid "Prompt users to upload SSH keys"
msgstr "" msgstr ""
msgid "Protect variable"
msgstr ""
msgid "Protected" msgid "Protected"
msgstr "" msgstr ""
...@@ -19372,6 +19393,9 @@ msgstr "" ...@@ -19372,6 +19393,9 @@ msgstr ""
msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6." msgid "The value lying at the midpoint of a series of observed values. E.g., between 3, 5, 9, the median is 5. Between 3, 5, 7, 8, the median is (5+7)/2 = 6."
msgstr "" msgstr ""
msgid "There are currently no variables, add a variable with the Add Variable button below."
msgstr ""
msgid "There are no GPG keys associated with this account." msgid "There are no GPG keys associated with this account."
msgstr "" msgstr ""
...@@ -20831,6 +20855,9 @@ msgstr "" ...@@ -20831,6 +20855,9 @@ msgstr ""
msgid "Update" msgid "Update"
msgstr "" msgstr ""
msgid "Update Variable"
msgstr ""
msgid "Update all" msgid "Update all"
msgstr "" msgstr ""
...@@ -21443,6 +21470,9 @@ msgstr "" ...@@ -21443,6 +21470,9 @@ msgstr ""
msgid "Variables" msgid "Variables"
msgstr "" msgstr ""
msgid "Variables will be masked in job logs. Requires values to meet regular expression requirements."
msgstr ""
msgid "Various container registry settings." msgid "Various container registry settings."
msgstr "" msgstr ""
......
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import { GlModal } from '@gitlab/ui';
import CiVariableModal from '~/ci_variable_list/components/ci_variable_modal.vue';
import createStore from '~/ci_variable_list/store';
import mockData from '../services/mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Ci variable modal', () => {
let wrapper;
let store;
const createComponent = () => {
store = createStore();
wrapper = shallowMount(CiVariableModal, {
localVue,
store,
});
};
const findModal = () => wrapper.find(GlModal);
beforeEach(() => {
createComponent();
jest.spyOn(store, 'dispatch').mockImplementation();
});
afterEach(() => {
wrapper.destroy();
});
it('button is disabled when no key/value pair are present', () => {
expect(findModal().props('actionPrimary').attributes.disabled).toBeTruthy();
});
it('masked checkbox is disabled when value does not meet regex requirements', () => {
expect(wrapper.find({ ref: 'masked-ci-variable' }).attributes('disabled')).toBeTruthy();
});
describe('Adding a new variable', () => {
beforeEach(() => {
const [variable] = mockData.mockVariables;
store.state.variable = variable;
});
it('button is enabled when key/value pair are present', () => {
expect(findModal().props('actionPrimary').attributes.disabled).toBeFalsy();
});
it('masked checkbox is enabled when value meets regex requirements', () => {
store.state.maskableRegex = '^[a-zA-Z0-9_+=/@:-]{8,}$';
return wrapper.vm.$nextTick(() => {
expect(wrapper.find({ ref: 'masked-ci-variable' }).attributes('disabled')).toBeFalsy();
});
});
it('Add variable button dispatches addVariable action', () => {
findModal().vm.$emit('ok');
expect(store.dispatch).toHaveBeenCalledWith('addVariable');
});
it('Clears the modal state once modal is hidden', () => {
findModal().vm.$emit('hidden');
expect(store.dispatch).toHaveBeenCalledWith('clearModal');
});
});
describe('Editing a variable', () => {
beforeEach(() => {
const [variable] = mockData.mockVariables;
store.state.variableBeingEdited = variable;
});
it('button text is Update variable when updating', () => {
expect(wrapper.vm.modalActionText).toBe('Update Variable');
});
it('Update variable button dispatches updateVariable with correct variable', () => {
findModal().vm.$emit('ok');
expect(store.dispatch).toHaveBeenCalledWith(
'updateVariable',
store.state.variableBeingEdited,
);
});
it('Resets the editing state once modal is hidden', () => {
findModal().vm.$emit('hidden');
expect(store.dispatch).toHaveBeenCalledWith('resetEditing');
});
});
});
import Vuex from 'vuex';
import { createLocalVue, shallowMount } from '@vue/test-utils';
import CiVariableSettings from '~/ci_variable_list/components/ci_variable_settings.vue';
import createStore from '~/ci_variable_list/store';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Ci variable table', () => {
let wrapper;
let store;
let isGroup;
const createComponent = groupState => {
store = createStore();
store.state.isGroup = groupState;
jest.spyOn(store, 'dispatch').mockImplementation();
wrapper = shallowMount(CiVariableSettings, {
localVue,
store,
});
};
afterEach(() => {
wrapper.destroy();
});
it('dispatches fetchEnvironments when mounted', () => {
isGroup = false;
createComponent(isGroup);
expect(store.dispatch).toHaveBeenCalledWith('fetchEnvironments');
});
it('does not dispatch fetchenvironments when in group context', () => {
isGroup = true;
createComponent(isGroup);
expect(store.dispatch).not.toHaveBeenCalled();
});
});
import Vuex from 'vuex';
import { createLocalVue, mount } from '@vue/test-utils';
import { GlTable } from '@gitlab/ui';
import CiVariableTable from '~/ci_variable_list/components/ci_variable_table.vue';
import createStore from '~/ci_variable_list/store';
import mockData from '../services/mock_data';
const localVue = createLocalVue();
localVue.use(Vuex);
describe('Ci variable table', () => {
let wrapper;
let store;
const createComponent = () => {
store = createStore();
store.state.isGroup = true;
jest.spyOn(store, 'dispatch').mockImplementation();
wrapper = mount(CiVariableTable, {
localVue,
store,
});
};
const findDeleteButton = () => wrapper.find({ ref: 'delete-ci-variable' });
const findRevealButton = () => wrapper.find({ ref: 'secret-value-reveal-button' });
const findEditButton = () => wrapper.find({ ref: 'edit-ci-variable' });
const findEmptyVariablesPlaceholder = () => wrapper.find({ ref: 'empty-variables' });
const findTable = () => wrapper.find(GlTable);
beforeEach(() => {
createComponent();
});
afterEach(() => {
wrapper.destroy();
});
it('dispatches fetchVariables when mounted', () => {
expect(store.dispatch).toHaveBeenCalledWith('fetchVariables');
});
it('fields prop does not contain environment_scope if group', () => {
expect(findTable().props('fields')).not.toEqual(
expect.arrayContaining([
expect.objectContaining({
key: 'environment_scope',
label: 'Environment Scope',
}),
]),
);
});
describe('Renders correct data', () => {
it('displays empty message when variables are not present', () => {
expect(findEmptyVariablesPlaceholder().exists()).toBe(true);
});
it('displays correct amount of variables present and no empty message', () => {
store.state.variables = mockData.mockVariables;
return wrapper.vm.$nextTick(() => {
expect(wrapper.findAll('.js-ci-variable-row').length).toBe(1);
expect(findEmptyVariablesPlaceholder().exists()).toBe(false);
});
});
});
describe('Table click actions', () => {
beforeEach(() => {
store.state.variables = mockData.mockVariables;
});
it('dispatches deleteVariable with correct variable to delete', () => {
findDeleteButton().trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('deleteVariable', mockData.mockVariables[0]);
});
it('reveals secret values when button is clicked', () => {
findRevealButton().trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('toggleValues', false);
});
it('dispatches editVariable with correct variable to edit', () => {
findEditButton().trigger('click');
expect(store.dispatch).toHaveBeenCalledWith('editVariable', mockData.mockVariables[0]);
});
});
});
...@@ -9,24 +9,6 @@ export default { ...@@ -9,24 +9,6 @@ export default {
value: 'test_val', value: 'test_val',
variable_type: 'Variable', variable_type: 'Variable',
}, },
{
environment_scope: 'All environments',
id: 114,
key: 'test_var_2',
masked: false,
protected: false,
value: 'test_val_2',
variable_type: 'Variable',
},
{
environment_scope: 'All environments',
id: 115,
key: 'test_var_3',
masked: false,
protected: false,
value: 'test_val_3',
variable_type: 'Variable',
},
], ],
mockVariablesApi: [ mockVariablesApi: [
......
...@@ -17,8 +17,8 @@ describe('CI variables store utils', () => { ...@@ -17,8 +17,8 @@ describe('CI variables store utils', () => {
environment_scope: '*', environment_scope: '*',
id: 113, id: 113,
key: 'test_var', key: 'test_var',
masked: false, masked: 'false',
protected: false, protected: 'false',
value: 'test_val', value: 'test_val',
variable_type: 'env_var', variable_type: 'env_var',
}); });
...@@ -27,8 +27,8 @@ describe('CI variables store utils', () => { ...@@ -27,8 +27,8 @@ describe('CI variables store utils', () => {
environment_scope: '*', environment_scope: '*',
id: 114, id: 114,
key: 'test_var_2', key: 'test_var_2',
masked: false, masked: 'false',
protected: false, protected: 'false',
value: 'test_val_2', value: 'test_val_2',
variable_type: 'file', variable_type: 'file',
}); });
......
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