diff options
Diffstat (limited to 'spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js')
-rw-r--r-- | spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js | 195 |
1 files changed, 146 insertions, 49 deletions
diff --git a/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js b/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js index 610aae3946f..721e2b831fc 100644 --- a/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js +++ b/spec/frontend/ci/ci_variable_list/components/ci_variable_drawer_spec.js @@ -1,6 +1,16 @@ import { nextTick } from 'vue'; -import { GlDrawer, GlFormCombobox, GlFormInput, GlFormSelect, GlModal } from '@gitlab/ui'; +import { + GlDrawer, + GlFormCombobox, + GlFormGroup, + GlFormInput, + GlFormSelect, + GlLink, + GlModal, + GlSprintf, +} from '@gitlab/ui'; import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import { helpPagePath } from '~/helpers/help_page_helper'; import CiEnvironmentsDropdown from '~/ci/ci_variable_list/components/ci_environments_dropdown.vue'; import CiVariableDrawer from '~/ci/ci_variable_list/components/ci_variable_drawer.vue'; import { awsTokenList } from '~/ci/ci_variable_list/components/ci_variable_autocomplete_tokens'; @@ -20,6 +30,8 @@ describe('CI Variable Drawer', () => { let wrapper; let trackingSpy; + const itif = (condition) => (condition ? it : it.skip); + const mockProjectVariable = mockVariablesWithScopes(projectString)[0]; const mockProjectVariableFileType = mockVariablesWithScopes(projectString)[1]; const mockEnvScope = 'staging'; @@ -74,6 +86,7 @@ describe('CI Variable Drawer', () => { const findDrawer = () => wrapper.findComponent(GlDrawer); const findEnvironmentScopeDropdown = () => wrapper.findComponent(CiEnvironmentsDropdown); const findExpandedCheckbox = () => wrapper.findByTestId('ci-variable-expanded-checkbox'); + const findFlagsDocsLink = () => wrapper.findByTestId('ci-variable-flags-docs-link'); const findKeyField = () => wrapper.findComponent(GlFormCombobox); const findMaskedCheckbox = () => wrapper.findByTestId('ci-variable-masked-checkbox'); const findProtectedCheckbox = () => wrapper.findByTestId('ci-variable-protected-checkbox'); @@ -81,6 +94,26 @@ describe('CI Variable Drawer', () => { const findValueLabel = () => wrapper.findByTestId('ci-variable-value-label'); const findTitle = () => findDrawer().find('h2'); const findTypeDropdown = () => wrapper.findComponent(GlFormSelect); + const findVariablesPrecedenceDocsLink = () => + wrapper.findByTestId('ci-variable-precedence-docs-link'); + + describe('template', () => { + beforeEach(() => { + createComponent({ stubs: { GlFormGroup, GlLink, GlSprintf } }); + }); + + it('renders docs link for variables precendece', () => { + expect(findVariablesPrecedenceDocsLink().attributes('href')).toBe( + helpPagePath('ci/variables/index', { anchor: 'cicd-variable-precedence' }), + ); + }); + + it('renders docs link for flags', () => { + expect(findFlagsDocsLink().attributes('href')).toBe( + helpPagePath('ci/variables/index', { anchor: 'define-a-cicd-variable-in-the-ui' }), + ); + }); + }); describe('validations', () => { describe('type dropdown', () => { @@ -263,12 +296,22 @@ describe('CI Variable Drawer', () => { expect(findKeyField().props('tokenList')).toBe(awsTokenList); }); - it('cannot submit with empty key', async () => { - expect(findConfirmBtn().attributes('disabled')).toBeDefined(); - - await findKeyField().vm.$emit('input', 'NEW_VARIABLE'); - - expect(findConfirmBtn().attributes('disabled')).toBeUndefined(); + const keyFeedbackMessage = "A variable key can only contain letters, numbers, and '_'."; + describe.each` + key | feedbackMessage | submitButtonDisabledState + ${'validKey123'} | ${''} | ${undefined} + ${'VALID_KEY'} | ${''} | ${undefined} + ${''} | ${''} | ${'true'} + ${'invalid!!key'} | ${keyFeedbackMessage} | ${'true'} + ${'key with whitespace'} | ${keyFeedbackMessage} | ${'true'} + ${'multiline\nkey'} | ${keyFeedbackMessage} | ${'true'} + `('key validation', ({ key, feedbackMessage, submitButtonDisabledState }) => { + it(`validates key ${key} correctly`, async () => { + await findKeyField().vm.$emit('input', key); + + expect(findConfirmBtn().attributes('disabled')).toBe(submitButtonDisabledState); + expect(wrapper.text()).toContain(feedbackMessage); + }); }); }); @@ -284,52 +327,106 @@ describe('CI Variable Drawer', () => { expect(findConfirmBtn().attributes('disabled')).toBeUndefined(); }); - describe.each` - value | canSubmit | trackingErrorProperty - ${'secretValue'} | ${true} | ${null} - ${'~v@lid:symbols.'} | ${true} | ${null} - ${'short'} | ${false} | ${null} - ${'multiline\nvalue'} | ${false} | ${'\n'} - ${'dollar$ign'} | ${false} | ${'$'} - ${'unsupported|char'} | ${false} | ${'|'} - `('masking requirements', ({ value, canSubmit, trackingErrorProperty }) => { - beforeEach(async () => { - createComponent(); - - trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); - await findKeyField().vm.$emit('input', 'NEW_VARIABLE'); - await findValueField().vm.$emit('input', value); - await findMaskedCheckbox().vm.$emit('input', true); - }); + const invalidValues = { + short: 'short', + multiLine: 'multiline\nvalue', + unsupportedChar: 'unsupported|char', + twoUnsupportedChars: 'unsupported|chars!', + threeUnsupportedChars: '%unsupported|chars!', + shortAndMultiLine: 'sho\nrt', + shortAndUnsupportedChar: 'short!', + shortAndMultiLineAndUnsupportedChar: 'short\n!', + multiLineAndUnsupportedChar: 'multiline\nvalue!', + }; + const maskedValidationIssuesText = { + short: 'The value must have at least 8 characters.', + multiLine: + 'This value cannot be masked because it contains the following characters: whitespace characters.', + unsupportedChar: + 'This value cannot be masked because it contains the following characters: |.', + unsupportedDollarChar: + 'This value cannot be masked because it contains the following characters: $.', + twoUnsupportedChars: + 'This value cannot be masked because it contains the following characters: |, !.', + threeUnsupportedChars: + 'This value cannot be masked because it contains the following characters: %, |, !.', + shortAndMultiLine: + 'This value cannot be masked because it contains the following characters: whitespace characters. The value must have at least 8 characters.', + shortAndUnsupportedChar: + 'This value cannot be masked because it contains the following characters: !. The value must have at least 8 characters.', + shortAndMultiLineAndUnsupportedChar: + 'This value cannot be masked because it contains the following characters: ! and whitespace characters. The value must have at least 8 characters.', + multiLineAndUnsupportedChar: + 'This value cannot be masked because it contains the following characters: ! and whitespace characters.', + }; - it(`${ - canSubmit ? 'can submit' : 'shows validation errors and disables submit button' - } when value is '${value}'`, () => { - if (canSubmit) { + describe.each` + value | canSubmit | trackingErrorProperty | validationIssueKey + ${'secretValue'} | ${true} | ${null} | ${''} + ${'~v@lid:symbols.'} | ${true} | ${null} | ${''} + ${invalidValues.short} | ${false} | ${null} | ${'short'} + ${invalidValues.multiLine} | ${false} | ${'\n'} | ${'multiLine'} + ${'dollar$ign'} | ${false} | ${'$'} | ${'unsupportedDollarChar'} + ${invalidValues.unsupportedChar} | ${false} | ${'|'} | ${'unsupportedChar'} + ${invalidValues.twoUnsupportedChars} | ${false} | ${'|!'} | ${'twoUnsupportedChars'} + ${invalidValues.threeUnsupportedChars} | ${false} | ${'%|!'} | ${'threeUnsupportedChars'} + ${invalidValues.shortAndMultiLine} | ${false} | ${'\n'} | ${'shortAndMultiLine'} + ${invalidValues.shortAndUnsupportedChar} | ${false} | ${'!'} | ${'shortAndUnsupportedChar'} + ${invalidValues.shortAndMultiLineAndUnsupportedChar} | ${false} | ${'\n!'} | ${'shortAndMultiLineAndUnsupportedChar'} + ${invalidValues.multiLineAndUnsupportedChar} | ${false} | ${'\n!'} | ${'multiLineAndUnsupportedChar'} + `( + 'masking requirements', + ({ value, canSubmit, trackingErrorProperty, validationIssueKey }) => { + beforeEach(() => { + createComponent(); + + trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); + findKeyField().vm.$emit('input', 'NEW_VARIABLE'); + findValueField().vm.$emit('input', value); + findMaskedCheckbox().vm.$emit('input', true); + }); + + itif(canSubmit)(`can submit when value is ${value}`, () => { + /* eslint-disable jest/no-standalone-expect */ expect(findValueLabel().attributes('invalid-feedback')).toBe(''); expect(findConfirmBtn().attributes('disabled')).toBeUndefined(); - } else { - expect(findValueLabel().attributes('invalid-feedback')).toBe( - 'This variable value does not meet the masking requirements.', - ); - expect(findConfirmBtn().attributes('disabled')).toBeDefined(); - } - }); + /* eslint-enable jest/no-standalone-expect */ + }); + + itif(!canSubmit)( + `shows validation errors and disables submit button when value is ${value}`, + () => { + const validationIssueText = maskedValidationIssuesText[validationIssueKey] || ''; + + /* eslint-disable jest/no-standalone-expect */ + expect(findValueLabel().attributes('invalid-feedback')).toBe(validationIssueText); + expect(findConfirmBtn().attributes('disabled')).toBeDefined(); + /* eslint-enable jest/no-standalone-expect */ + }, + ); + + itif(trackingErrorProperty)( + `sends the correct variable validation tracking event when value is ${value}`, + () => { + /* eslint-disable jest/no-standalone-expect */ + expect(trackingSpy).toHaveBeenCalledTimes(1); + expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTION, { + label: DRAWER_EVENT_LABEL, + property: trackingErrorProperty, + }); + /* eslint-enable jest/no-standalone-expect */ + }, + ); - it(`${ - trackingErrorProperty ? 'sends the correct' : 'does not send the' - } variable validation tracking event when value is '${value}'`, () => { - const trackingEventSent = trackingErrorProperty ? 1 : 0; - expect(trackingSpy).toHaveBeenCalledTimes(trackingEventSent); - - if (trackingErrorProperty) { - expect(trackingSpy).toHaveBeenCalledWith(undefined, EVENT_ACTION, { - label: DRAWER_EVENT_LABEL, - property: trackingErrorProperty, - }); - } - }); - }); + itif(!trackingErrorProperty)( + `does not send the the correct variable validation tracking event when value is ${value}`, + () => { + // eslint-disable-next-line jest/no-standalone-expect + expect(trackingSpy).toHaveBeenCalledTimes(0); + }, + ); + }, + ); it('only sends the tracking event once', async () => { trackingSpy = mockTracking(undefined, wrapper.element, jest.spyOn); |