summaryrefslogtreecommitdiff
path: root/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js
diff options
context:
space:
mode:
Diffstat (limited to 'spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js')
-rw-r--r--spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js323
1 files changed, 323 insertions, 0 deletions
diff --git a/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js b/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js
new file mode 100644
index 00000000000..42c6501dcce
--- /dev/null
+++ b/spec/frontend/ci_variable_list/components/legacy_ci_variable_modal_spec.js
@@ -0,0 +1,323 @@
+import { GlButton, GlFormInput } from '@gitlab/ui';
+import { shallowMount, mount } from '@vue/test-utils';
+import Vue from 'vue';
+import Vuex from 'vuex';
+import { mockTracking } from 'helpers/tracking_helper';
+import CiEnvironmentsDropdown from '~/ci_variable_list/components/ci_environments_dropdown.vue';
+import LegacyCiVariableModal from '~/ci_variable_list/components/legacy_ci_variable_modal.vue';
+import {
+ AWS_ACCESS_KEY_ID,
+ EVENT_LABEL,
+ EVENT_ACTION,
+ ENVIRONMENT_SCOPE_LINK_TITLE,
+} from '~/ci_variable_list/constants';
+import createStore from '~/ci_variable_list/store';
+import mockData from '../services/mock_data';
+import ModalStub from '../stubs';
+
+Vue.use(Vuex);
+
+describe('Ci variable modal', () => {
+ let wrapper;
+ let store;
+ let trackingSpy;
+
+ const maskableRegex = '^[a-zA-Z0-9_+=/@:.~-]{8,}$';
+
+ const createComponent = (method, options = {}) => {
+ store = createStore({
+ maskableRegex,
+ isGroup: options.isGroup,
+ environmentScopeLink: '/help/environments',
+ });
+ wrapper = method(LegacyCiVariableModal, {
+ attachTo: document.body,
+ stubs: {
+ GlModal: ModalStub,
+ },
+ store,
+ ...options,
+ });
+ };
+
+ const findCiEnvironmentsDropdown = () => wrapper.find(CiEnvironmentsDropdown);
+ const findModal = () => wrapper.find(ModalStub);
+ const findAddorUpdateButton = () => findModal().find('[data-testid="ciUpdateOrAddVariableBtn"]');
+ const deleteVariableButton = () =>
+ findModal()
+ .findAll(GlButton)
+ .wrappers.find((button) => button.props('variant') === 'danger');
+
+ afterEach(() => {
+ wrapper.destroy();
+ });
+
+ describe('Basic interactions', () => {
+ beforeEach(() => {
+ createComponent(shallowMount);
+ });
+
+ it('button is disabled when no key/value pair are present', () => {
+ expect(findAddorUpdateButton().attributes('disabled')).toBeTruthy();
+ });
+ });
+
+ describe('Adding a new variable', () => {
+ beforeEach(() => {
+ const [variable] = mockData.mockVariables;
+ createComponent(shallowMount);
+ jest.spyOn(store, 'dispatch').mockImplementation();
+ store.state.variable = variable;
+ });
+
+ it('button is enabled when key/value pair are present', () => {
+ expect(findAddorUpdateButton().attributes('disabled')).toBeFalsy();
+ });
+
+ it('Add variable button dispatches addVariable action', () => {
+ findAddorUpdateButton().vm.$emit('click');
+ expect(store.dispatch).toHaveBeenCalledWith('addVariable');
+ });
+
+ it('Clears the modal state once modal is hidden', () => {
+ findModal().vm.$emit('hidden');
+ expect(store.dispatch).toHaveBeenCalledWith('clearModal');
+ });
+
+ it('should dispatch setVariableProtected when admin settings are configured to protect variables', () => {
+ store.state.isProtectedByDefault = true;
+ findModal().vm.$emit('shown');
+
+ expect(store.dispatch).toHaveBeenCalledWith('setVariableProtected');
+ });
+ });
+
+ describe('Adding a new non-AWS variable', () => {
+ beforeEach(() => {
+ const [variable] = mockData.mockVariables;
+ const invalidKeyVariable = {
+ ...variable,
+ key: 'key',
+ value: 'value',
+ secret_value: 'secret_value',
+ };
+ createComponent(mount);
+ store.state.variable = invalidKeyVariable;
+ });
+
+ it('does not show AWS guidance tip', () => {
+ const tip = wrapper.find(`div[data-testid='aws-guidance-tip']`);
+ expect(tip.exists()).toBe(true);
+ expect(tip.isVisible()).toBe(false);
+ });
+ });
+
+ describe('Adding a new AWS variable', () => {
+ beforeEach(() => {
+ const [variable] = mockData.mockVariables;
+ const invalidKeyVariable = {
+ ...variable,
+ key: AWS_ACCESS_KEY_ID,
+ value: 'AKIAIOSFODNN7EXAMPLEjdhy',
+ secret_value: 'AKIAIOSFODNN7EXAMPLEjdhy',
+ };
+ createComponent(mount);
+ store.state.variable = invalidKeyVariable;
+ });
+
+ it('shows AWS guidance tip', () => {
+ const tip = wrapper.find(`[data-testid='aws-guidance-tip']`);
+ expect(tip.exists()).toBe(true);
+ expect(tip.isVisible()).toBe(true);
+ });
+ });
+
+ describe.each`
+ value | secret | rendered
+ ${'value'} | ${'secret_value'} | ${false}
+ ${'dollar$ign'} | ${'dollar$ign'} | ${true}
+ `('Adding a new variable', ({ value, secret, rendered }) => {
+ beforeEach(() => {
+ const [variable] = mockData.mockVariables;
+ const invalidKeyVariable = {
+ ...variable,
+ key: 'key',
+ value,
+ secret_value: secret,
+ };
+ createComponent(mount);
+ store.state.variable = invalidKeyVariable;
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ it(`${rendered ? 'renders' : 'does not render'} the variable reference warning`, () => {
+ const warning = wrapper.find(`[data-testid='contains-variable-reference']`);
+ expect(warning.exists()).toBe(rendered);
+ });
+ });
+
+ describe('Editing a variable', () => {
+ beforeEach(() => {
+ const [variable] = mockData.mockVariables;
+ createComponent(shallowMount);
+ jest.spyOn(store, 'dispatch').mockImplementation();
+ store.state.variableBeingEdited = variable;
+ });
+
+ it('button text is Update variable when updating', () => {
+ expect(findAddorUpdateButton().text()).toBe('Update variable');
+ });
+
+ it('Update variable button dispatches updateVariable with correct variable', () => {
+ findAddorUpdateButton().vm.$emit('click');
+ expect(store.dispatch).toHaveBeenCalledWith('updateVariable');
+ });
+
+ it('Resets the editing state once modal is hidden', () => {
+ findModal().vm.$emit('hidden');
+ expect(store.dispatch).toHaveBeenCalledWith('resetEditing');
+ });
+
+ it('dispatches deleteVariable with correct variable to delete', () => {
+ deleteVariableButton().vm.$emit('click');
+ expect(store.dispatch).toHaveBeenCalledWith('deleteVariable');
+ });
+ });
+
+ describe('Environment scope', () => {
+ describe('group level variables', () => {
+ it('renders the environment dropdown', () => {
+ createComponent(shallowMount, {
+ isGroup: true,
+ provide: {
+ glFeatures: {
+ groupScopedCiVariables: true,
+ },
+ },
+ });
+
+ expect(findCiEnvironmentsDropdown().exists()).toBe(true);
+ expect(findCiEnvironmentsDropdown().isVisible()).toBe(true);
+ });
+
+ describe('licensed feature is not available', () => {
+ it('disables the dropdown', () => {
+ createComponent(mount, {
+ isGroup: true,
+ provide: {
+ glFeatures: {
+ groupScopedCiVariables: false,
+ },
+ },
+ });
+
+ const environmentScopeInput = wrapper
+ .find('[data-testid="environment-scope"]')
+ .find(GlFormInput);
+ expect(findCiEnvironmentsDropdown().exists()).toBe(false);
+ expect(environmentScopeInput.attributes('readonly')).toBe('readonly');
+ });
+ });
+ });
+
+ it('renders a link to documentation on scopes', () => {
+ createComponent(mount);
+
+ const link = wrapper.find('[data-testid="environment-scope-link"]');
+
+ expect(link.attributes('title')).toBe(ENVIRONMENT_SCOPE_LINK_TITLE);
+ expect(link.attributes('href')).toBe('/help/environments');
+ });
+ });
+
+ describe('Validations', () => {
+ const maskError = 'This variable can not be masked.';
+
+ describe('when the mask state is invalid', () => {
+ beforeEach(() => {
+ const [variable] = mockData.mockVariables;
+ const invalidMaskVariable = {
+ ...variable,
+ key: 'qs',
+ value: 'd:;',
+ secret_value: 'd:;',
+ masked: true,
+ };
+ createComponent(mount);
+ store.state.variable = invalidMaskVariable;
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ it('disables the submit button', () => {
+ expect(findAddorUpdateButton().attributes('disabled')).toBeTruthy();
+ });
+
+ it('shows the correct error text', () => {
+ expect(findModal().text()).toContain(maskError);
+ });
+
+ it('sends the correct tracking event', () => {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTION, {
+ label: EVENT_LABEL,
+ property: ';',
+ });
+ });
+ });
+
+ describe.each`
+ value | secret | masked | eventSent | trackingErrorProperty
+ ${'value'} | ${'secretValue'} | ${false} | ${0} | ${null}
+ ${'shortMasked'} | ${'short'} | ${true} | ${0} | ${null}
+ ${'withDollar$Sign'} | ${'dollar$ign'} | ${false} | ${1} | ${'$'}
+ ${'withDollar$Sign'} | ${'dollar$ign'} | ${true} | ${1} | ${'$'}
+ ${'unsupported'} | ${'unsupported|char'} | ${true} | ${1} | ${'|'}
+ ${'unsupportedMasked'} | ${'unsupported|char'} | ${false} | ${0} | ${null}
+ `('Adding a new variable', ({ value, secret, masked, eventSent, trackingErrorProperty }) => {
+ beforeEach(() => {
+ const [variable] = mockData.mockVariables;
+ const invalidKeyVariable = {
+ ...variable,
+ key: 'key',
+ value,
+ secret_value: secret,
+ masked,
+ };
+ createComponent(mount);
+ store.state.variable = invalidKeyVariable;
+ trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn);
+ });
+
+ it(`${
+ eventSent > 0 ? 'sends the correct' : 'does not send the'
+ } variable validation tracking event`, () => {
+ expect(trackingSpy).toHaveBeenCalledTimes(eventSent);
+
+ if (eventSent > 0) {
+ expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTION, {
+ label: EVENT_LABEL,
+ property: trackingErrorProperty,
+ });
+ }
+ });
+ });
+
+ describe('when both states are valid', () => {
+ beforeEach(() => {
+ const [variable] = mockData.mockVariables;
+ const validMaskandKeyVariable = {
+ ...variable,
+ key: AWS_ACCESS_KEY_ID,
+ value: '12345678',
+ secret_value: '87654321',
+ masked: true,
+ };
+ createComponent(mount);
+ store.state.variable = validMaskandKeyVariable;
+ });
+
+ it('does not disable the submit button', () => {
+ expect(findAddorUpdateButton().attributes('disabled')).toBeFalsy();
+ });
+ });
+ });
+});