diff options
Diffstat (limited to 'spec/frontend/integrations/edit/components')
6 files changed, 408 insertions, 118 deletions
diff --git a/spec/frontend/integrations/edit/components/active_toggle_spec.js b/spec/frontend/integrations/edit/components/active_toggle_spec.js index 5469b45f708..228d8f5fc30 100644 --- a/spec/frontend/integrations/edit/components/active_toggle_spec.js +++ b/spec/frontend/integrations/edit/components/active_toggle_spec.js @@ -1,8 +1,10 @@ import { mount } from '@vue/test-utils'; -import ActiveToggle from '~/integrations/edit/components/active_toggle.vue'; import { GlToggle } from '@gitlab/ui'; +import ActiveToggle from '~/integrations/edit/components/active_toggle.vue'; + const GL_TOGGLE_ACTIVE_CLASS = 'is-checked'; +const GL_TOGGLE_DISABLED_CLASS = 'is-disabled'; describe('ActiveToggle', () => { let wrapper; @@ -11,9 +13,12 @@ describe('ActiveToggle', () => { initialActivated: true, }; - const createComponent = props => { + const createComponent = (props = {}, isInheriting = false) => { wrapper = mount(ActiveToggle, { propsData: { ...defaultProps, ...props }, + computed: { + isInheriting: () => isInheriting, + }, }); }; @@ -29,6 +34,15 @@ describe('ActiveToggle', () => { const findInputInToggle = () => findGlToggle().find('input'); describe('template', () => { + describe('is inheriting adminSettings', () => { + it('renders GlToggle as disabled', () => { + createComponent({}, true); + + expect(findGlToggle().exists()).toBe(true); + expect(findButtonInToggle().classes()).toContain(GL_TOGGLE_DISABLED_CLASS); + }); + }); + describe('initialActivated is false', () => { it('renders GlToggle as inactive', () => { createComponent({ diff --git a/spec/frontend/integrations/edit/components/dynamic_field_spec.js b/spec/frontend/integrations/edit/components/dynamic_field_spec.js index e5710641f81..3a7a0efcab7 100644 --- a/spec/frontend/integrations/edit/components/dynamic_field_spec.js +++ b/spec/frontend/integrations/edit/components/dynamic_field_spec.js @@ -14,9 +14,12 @@ describe('DynamicField', () => { value: '1', }; - const createComponent = props => { + const createComponent = (props, isInheriting = false) => { wrapper = mount(DynamicField, { propsData: { ...defaultProps, ...props }, + computed: { + isInheriting: () => isInheriting, + }, }); }; @@ -34,108 +37,143 @@ describe('DynamicField', () => { const findGlFormTextarea = () => wrapper.find(GlFormTextarea); describe('template', () => { - describe('dynamic field', () => { - describe('type is checkbox', () => { - beforeEach(() => { - createComponent({ - type: 'checkbox', + describe.each([[true, 'disabled', 'readonly'], [false, undefined, undefined]])( + 'dynamic field, when isInheriting = `%p`', + (isInheriting, disabled, readonly) => { + describe('type is checkbox', () => { + beforeEach(() => { + createComponent( + { + type: 'checkbox', + }, + isInheriting, + ); }); - }); - it('renders GlFormCheckbox', () => { - expect(findGlFormCheckbox().exists()).toBe(true); - }); + it(`renders GlFormCheckbox, which ${isInheriting ? 'is' : 'is not'} disabled`, () => { + expect(findGlFormCheckbox().exists()).toBe(true); + expect( + findGlFormCheckbox() + .find('[type=checkbox]') + .attributes('disabled'), + ).toBe(disabled); + }); - it('does not render other types of input', () => { - expect(findGlFormSelect().exists()).toBe(false); - expect(findGlFormTextarea().exists()).toBe(false); - expect(findGlFormInput().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormSelect().exists()).toBe(false); + expect(findGlFormTextarea().exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); + }); }); - }); - describe('type is select', () => { - beforeEach(() => { - createComponent({ - type: 'select', - choices: [['all', 'All details'], ['standard', 'Standard']], + describe('type is select', () => { + beforeEach(() => { + createComponent( + { + type: 'select', + choices: [['all', 'All details'], ['standard', 'Standard']], + }, + isInheriting, + ); }); - }); - it('renders findGlFormSelect', () => { - expect(findGlFormSelect().exists()).toBe(true); - expect(findGlFormSelect().findAll('option')).toHaveLength(2); - }); + it(`renders GlFormSelect, which ${isInheriting ? 'is' : 'is not'} disabled`, () => { + expect(findGlFormSelect().exists()).toBe(true); + expect(findGlFormSelect().findAll('option')).toHaveLength(2); + expect( + findGlFormSelect() + .find('select') + .attributes('disabled'), + ).toBe(disabled); + }); - it('does not render other types of input', () => { - expect(findGlFormCheckbox().exists()).toBe(false); - expect(findGlFormTextarea().exists()).toBe(false); - expect(findGlFormInput().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormCheckbox().exists()).toBe(false); + expect(findGlFormTextarea().exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); + }); }); - }); - describe('type is textarea', () => { - beforeEach(() => { - createComponent({ - type: 'textarea', + describe('type is textarea', () => { + beforeEach(() => { + createComponent( + { + type: 'textarea', + }, + isInheriting, + ); }); - }); - it('renders findGlFormTextarea', () => { - expect(findGlFormTextarea().exists()).toBe(true); - }); + it(`renders GlFormTextarea, which ${isInheriting ? 'is' : 'is not'} readonly`, () => { + expect(findGlFormTextarea().exists()).toBe(true); + expect( + findGlFormTextarea() + .find('textarea') + .attributes('readonly'), + ).toBe(readonly); + }); - it('does not render other types of input', () => { - expect(findGlFormCheckbox().exists()).toBe(false); - expect(findGlFormSelect().exists()).toBe(false); - expect(findGlFormInput().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormCheckbox().exists()).toBe(false); + expect(findGlFormSelect().exists()).toBe(false); + expect(findGlFormInput().exists()).toBe(false); + }); }); - }); - describe('type is password', () => { - beforeEach(() => { - createComponent({ - type: 'password', + describe('type is password', () => { + beforeEach(() => { + createComponent( + { + type: 'password', + }, + isInheriting, + ); }); - }); - it('renders GlFormInput', () => { - expect(findGlFormInput().exists()).toBe(true); - expect(findGlFormInput().attributes('type')).toBe('password'); - }); + it(`renders GlFormInput, which ${isInheriting ? 'is' : 'is not'} readonly`, () => { + expect(findGlFormInput().exists()).toBe(true); + expect(findGlFormInput().attributes('type')).toBe('password'); + expect(findGlFormInput().attributes('readonly')).toBe(readonly); + }); - it('does not render other types of input', () => { - expect(findGlFormCheckbox().exists()).toBe(false); - expect(findGlFormSelect().exists()).toBe(false); - expect(findGlFormTextarea().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormCheckbox().exists()).toBe(false); + expect(findGlFormSelect().exists()).toBe(false); + expect(findGlFormTextarea().exists()).toBe(false); + }); }); - }); - describe('type is text', () => { - beforeEach(() => { - createComponent({ - type: 'text', - required: true, + describe('type is text', () => { + beforeEach(() => { + createComponent( + { + type: 'text', + required: true, + }, + isInheriting, + ); }); - }); - it('renders GlFormInput', () => { - expect(findGlFormInput().exists()).toBe(true); - expect(findGlFormInput().attributes()).toMatchObject({ - type: 'text', - id: 'service_project_url', - name: 'service[project_url]', - placeholder: defaultProps.placeholder, - required: 'required', + it(`renders GlFormInput, which ${isInheriting ? 'is' : 'is not'} readonly`, () => { + expect(findGlFormInput().exists()).toBe(true); + expect(findGlFormInput().attributes()).toMatchObject({ + type: 'text', + id: 'service_project_url', + name: 'service[project_url]', + placeholder: defaultProps.placeholder, + required: 'required', + }); + expect(findGlFormInput().attributes('readonly')).toBe(readonly); }); - }); - it('does not render other types of input', () => { - expect(findGlFormCheckbox().exists()).toBe(false); - expect(findGlFormSelect().exists()).toBe(false); - expect(findGlFormTextarea().exists()).toBe(false); + it('does not render other types of input', () => { + expect(findGlFormCheckbox().exists()).toBe(false); + expect(findGlFormSelect().exists()).toBe(false); + expect(findGlFormTextarea().exists()).toBe(false); + }); }); - }); - }); + }, + ); describe('help text', () => { it('renders description with help text', () => { @@ -147,6 +185,20 @@ describe('DynamicField', () => { .text(), ).toBe(defaultProps.help); }); + + it('renders description with help text as HTML', () => { + const helpHTML = 'The <strong>URL</strong> of the project'; + + createComponent({ + help: helpHTML, + }); + + expect( + findGlFormGroup() + .find('small') + .html(), + ).toContain(helpHTML); + }); }); describe('label text', () => { @@ -175,5 +227,39 @@ describe('DynamicField', () => { }); }); }); + + describe('validations', () => { + describe('password field', () => { + beforeEach(() => { + createComponent({ + type: 'password', + required: true, + value: null, + }); + + wrapper.vm.validated = true; + }); + + describe('without value', () => { + it('requires validation', () => { + expect(wrapper.vm.valid).toBe(false); + expect(findGlFormGroup().classes('is-invalid')).toBe(true); + expect(findGlFormInput().classes('is-invalid')).toBe(true); + }); + }); + + describe('with value', () => { + beforeEach(() => { + wrapper.setProps({ value: 'true' }); + }); + + it('does not require validation', () => { + expect(wrapper.vm.valid).toBe(true); + expect(findGlFormGroup().classes('is-valid')).toBe(true); + expect(findGlFormInput().classes('is-valid')).toBe(true); + }); + }); + }); + }); }); }); diff --git a/spec/frontend/integrations/edit/components/integration_form_spec.js b/spec/frontend/integrations/edit/components/integration_form_spec.js index b598a71cea8..482c6a439f2 100644 --- a/spec/frontend/integrations/edit/components/integration_form_spec.js +++ b/spec/frontend/integrations/edit/components/integration_form_spec.js @@ -1,32 +1,32 @@ import { shallowMount } from '@vue/test-utils'; +import { createStore } from '~/integrations/edit/store'; import IntegrationForm from '~/integrations/edit/components/integration_form.vue'; +import OverrideDropdown from '~/integrations/edit/components/override_dropdown.vue'; import ActiveToggle from '~/integrations/edit/components/active_toggle.vue'; import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue'; +import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; import TriggerFields from '~/integrations/edit/components/trigger_fields.vue'; import DynamicField from '~/integrations/edit/components/dynamic_field.vue'; +import { mockIntegrationProps } from 'jest/integrations/edit/mock_data'; describe('IntegrationForm', () => { let wrapper; - const defaultProps = { - activeToggleProps: { - initialActivated: true, - }, - showActive: true, - triggerFieldsProps: { - initialTriggerCommit: false, - initialTriggerMergeRequest: false, - initialEnableComments: false, - }, - type: '', - }; - - const createComponent = props => { + const createComponent = (customStateProps = {}, featureFlags = {}, initialState = {}) => { wrapper = shallowMount(IntegrationForm, { - propsData: { ...defaultProps, ...props }, + propsData: {}, + store: createStore({ + customState: { ...mockIntegrationProps, ...customStateProps }, + ...initialState, + }), stubs: { + OverrideDropdown, ActiveToggle, JiraTriggerFields, + TriggerFields, + }, + provide: { + glFeatures: featureFlags, }, }); }; @@ -38,8 +38,10 @@ describe('IntegrationForm', () => { } }); + const findOverrideDropdown = () => wrapper.find(OverrideDropdown); const findActiveToggle = () => wrapper.find(ActiveToggle); const findJiraTriggerFields = () => wrapper.find(JiraTriggerFields); + const findJiraIssuesFields = () => wrapper.find(JiraIssuesFields); const findTriggerFields = () => wrapper.find(TriggerFields); describe('template', () => { @@ -62,23 +64,41 @@ describe('IntegrationForm', () => { }); describe('type is "slack"', () => { - it('does not render JiraTriggerFields', () => { - createComponent({ - type: 'slack', - }); + beforeEach(() => { + createComponent({ type: 'slack' }); + }); + it('does not render JiraTriggerFields', () => { expect(findJiraTriggerFields().exists()).toBe(false); }); + + it('does not render JiraIssuesFields', () => { + expect(findJiraIssuesFields().exists()).toBe(false); + }); }); describe('type is "jira"', () => { it('renders JiraTriggerFields', () => { - createComponent({ - type: 'jira', - }); + createComponent({ type: 'jira' }); expect(findJiraTriggerFields().exists()).toBe(true); }); + + describe('featureFlag jiraIssuesIntegration is false', () => { + it('does not render JiraIssuesFields', () => { + createComponent({ type: 'jira' }, { jiraIssuesIntegration: false }); + + expect(findJiraIssuesFields().exists()).toBe(false); + }); + }); + + describe('featureFlag jiraIssuesIntegration is true', () => { + it('renders JiraIssuesFields', () => { + createComponent({ type: 'jira' }, { jiraIssuesIntegration: true }); + + expect(findJiraIssuesFields().exists()).toBe(true); + }); + }); }); describe('triggerEvents is present', () => { @@ -116,5 +136,35 @@ describe('IntegrationForm', () => { }); }); }); + + describe('adminState state is null', () => { + it('does not render OverrideDropdown', () => { + createComponent( + {}, + {}, + { + adminState: null, + }, + ); + + expect(findOverrideDropdown().exists()).toBe(false); + }); + }); + + describe('adminState state is an object', () => { + it('renders OverrideDropdown', () => { + createComponent( + {}, + {}, + { + adminState: { + ...mockIntegrationProps, + }, + }, + ); + + expect(findOverrideDropdown().exists()).toBe(true); + }); + }); }); }); diff --git a/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js b/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js new file mode 100644 index 00000000000..f58825f6297 --- /dev/null +++ b/spec/frontend/integrations/edit/components/jira_issues_fields_spec.js @@ -0,0 +1,96 @@ +import { mount } from '@vue/test-utils'; + +import { GlFormCheckbox, GlFormInput } from '@gitlab/ui'; + +import JiraIssuesFields from '~/integrations/edit/components/jira_issues_fields.vue'; + +describe('JiraIssuesFields', () => { + let wrapper; + + const defaultProps = { + showJiraIssuesIntegration: true, + editProjectPath: '/edit', + }; + + const createComponent = props => { + wrapper = mount(JiraIssuesFields, { + propsData: { ...defaultProps, ...props }, + }); + }; + + afterEach(() => { + if (wrapper) { + wrapper.destroy(); + wrapper = null; + } + }); + + const findEnableCheckbox = () => wrapper.find(GlFormCheckbox); + const findProjectKey = () => wrapper.find(GlFormInput); + const expectedBannerText = 'This is a Premium feature'; + + describe('template', () => { + describe('upgrade banner for non-Premium user', () => { + beforeEach(() => { + createComponent({ initialProjectKey: '', showJiraIssuesIntegration: false }); + }); + + it('shows upgrade banner', () => { + expect(wrapper.text()).toContain(expectedBannerText); + }); + + it('does not show checkbox and input field', () => { + expect(findEnableCheckbox().exists()).toBe(false); + expect(findProjectKey().exists()).toBe(false); + }); + }); + + describe('Enable Jira issues checkbox', () => { + beforeEach(() => { + createComponent({ initialProjectKey: '' }); + }); + + it('does not show upgrade banner', () => { + expect(wrapper.text()).not.toContain(expectedBannerText); + }); + + // As per https://vuejs.org/v2/guide/forms.html#Checkbox-1, + // browsers don't include unchecked boxes in form submissions. + it('includes issues_enabled as false even if unchecked', () => { + expect(wrapper.contains('input[name="service[issues_enabled]"]')).toBe(true); + }); + + it('disables project_key input', () => { + expect(findProjectKey().attributes('disabled')).toBe('disabled'); + }); + + it('does not require project_key', () => { + expect(findProjectKey().attributes('required')).toBeUndefined(); + }); + + describe('on enable issues', () => { + it('enables project_key input', () => { + findEnableCheckbox().vm.$emit('input', true); + + return wrapper.vm.$nextTick().then(() => { + expect(findProjectKey().attributes('disabled')).toBeUndefined(); + }); + }); + + it('requires project_key input', () => { + findEnableCheckbox().vm.$emit('input', true); + + return wrapper.vm.$nextTick().then(() => { + expect(findProjectKey().attributes('required')).toBe('required'); + }); + }); + }); + }); + + it('contains link to editProjectPath', () => { + createComponent(); + + expect(wrapper.contains(`a[href="${defaultProps.editProjectPath}"]`)).toBe(true); + }); + }); +}); diff --git a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js index e4c2a0be6a3..782930eb6a2 100644 --- a/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js +++ b/spec/frontend/integrations/edit/components/jira_trigger_fields_spec.js @@ -1,6 +1,6 @@ import { mount } from '@vue/test-utils'; -import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue'; import { GlFormCheckbox } from '@gitlab/ui'; +import JiraTriggerFields from '~/integrations/edit/components/jira_trigger_fields.vue'; describe('JiraTriggerFields', () => { let wrapper; @@ -11,9 +11,12 @@ describe('JiraTriggerFields', () => { initialEnableComments: false, }; - const createComponent = props => { + const createComponent = (props, isInheriting = false) => { wrapper = mount(JiraTriggerFields, { propsData: { ...defaultProps, ...props }, + computed: { + isInheriting: () => isInheriting, + }, }); }; @@ -93,5 +96,23 @@ describe('JiraTriggerFields', () => { expect(findCommentDetail().isVisible()).toBe(true); }); }); + + it('disables checkboxes and radios if inheriting', () => { + createComponent( + { + initialTriggerCommit: true, + initialEnableComments: true, + }, + true, + ); + + wrapper.findAll('[type=checkbox]').wrappers.forEach(checkbox => { + expect(checkbox.attributes('disabled')).toBe('disabled'); + }); + + wrapper.findAll('[type=radio]').wrappers.forEach(radio => { + expect(radio.attributes('disabled')).toBe('disabled'); + }); + }); }); }); diff --git a/spec/frontend/integrations/edit/components/trigger_fields_spec.js b/spec/frontend/integrations/edit/components/trigger_fields_spec.js index 337876c6d16..41bccb8ada0 100644 --- a/spec/frontend/integrations/edit/components/trigger_fields_spec.js +++ b/spec/frontend/integrations/edit/components/trigger_fields_spec.js @@ -9,9 +9,12 @@ describe('TriggerFields', () => { type: 'slack', }; - const createComponent = props => { + const createComponent = (props, isInheriting = false) => { wrapper = mount(TriggerFields, { propsData: { ...defaultProps, ...props }, + computed: { + isInheriting: () => isInheriting, + }, }); }; @@ -22,10 +25,11 @@ describe('TriggerFields', () => { } }); + const findAllGlFormGroups = () => wrapper.find('#trigger-fields').findAll(GlFormGroup); const findAllGlFormCheckboxes = () => wrapper.findAll(GlFormCheckbox); const findAllGlFormInputs = () => wrapper.findAll(GlFormInput); - describe('template', () => { + describe.each([true, false])('template, isInheriting = `%p`', isInheriting => { it('renders a label with text "Trigger"', () => { createComponent(); @@ -51,9 +55,12 @@ describe('TriggerFields', () => { ]; beforeEach(() => { - createComponent({ - events, - }); + createComponent( + { + events, + }, + isInheriting, + ); }); it('does not render GlFormInput for each event', () => { @@ -69,8 +76,10 @@ describe('TriggerFields', () => { }); }); - it('renders GlFormCheckbox for each event', () => { - const checkboxes = findAllGlFormCheckboxes(); + it(`renders GlFormCheckbox and corresponding hidden input for each event, which ${ + isInheriting ? 'is' : 'is not' + } disabled`, () => { + const checkboxes = findAllGlFormGroups(); const expectedResults = [ { labelText: 'Push', inputName: 'service[push_event]' }, { labelText: 'Merge Request', inputName: 'service[merge_requests_event]' }, @@ -78,14 +87,22 @@ describe('TriggerFields', () => { expect(checkboxes).toHaveLength(2); checkboxes.wrappers.forEach((checkbox, index) => { + const checkBox = checkbox.find(GlFormCheckbox); + expect(checkbox.find('label').text()).toBe(expectedResults[index].labelText); - expect(checkbox.find('input').attributes('name')).toBe(expectedResults[index].inputName); - expect(checkbox.vm.$attrs.checked).toBe(events[index].value); + expect(checkbox.find('[type=hidden]').attributes('name')).toBe( + expectedResults[index].inputName, + ); + expect(checkbox.find('[type=hidden]').attributes('value')).toBe( + events[index].value.toString(), + ); + expect(checkBox.vm.$attrs.disabled).toBe(isInheriting); + expect(checkBox.vm.$attrs.checked).toBe(events[index].value); }); }); }); - describe('events with field property', () => { + describe('events with field property, isInheriting = `%p`', () => { const events = [ { field: { @@ -102,16 +119,21 @@ describe('TriggerFields', () => { ]; beforeEach(() => { - createComponent({ - events, - }); + createComponent( + { + events, + }, + isInheriting, + ); }); it('renders GlFormCheckbox for each event', () => { expect(findAllGlFormCheckboxes()).toHaveLength(2); }); - it('renders GlFormInput for each event', () => { + it(`renders GlFormInput for each event, which ${ + isInheriting ? 'is' : 'is not' + } readonly`, () => { const fields = findAllGlFormInputs(); const expectedResults = [ { @@ -128,6 +150,7 @@ describe('TriggerFields', () => { fields.wrappers.forEach((field, index) => { expect(field.attributes()).toMatchObject(expectedResults[index]); + expect(field.vm.$attrs.readonly).toBe(isInheriting); expect(field.vm.$attrs.value).toBe(events[index].field.value); }); }); |