diff options
Diffstat (limited to 'spec/frontend/packages_and_registries/settings')
5 files changed, 446 insertions, 18 deletions
diff --git a/spec/frontend/packages_and_registries/settings/group/components/forwarding_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/forwarding_settings_spec.js new file mode 100644 index 00000000000..8f229182fe5 --- /dev/null +++ b/spec/frontend/packages_and_registries/settings/group/components/forwarding_settings_spec.js @@ -0,0 +1,78 @@ +import { GlFormGroup, GlSprintf } from '@gitlab/ui'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import component from '~/packages_and_registries/settings/group/components/forwarding_settings.vue'; + +describe('Forwarding Settings', () => { + let wrapper; + + const defaultProps = { + disabled: false, + forwarding: false, + label: 'label', + lockForwarding: false, + modelNames: { + forwarding: 'forwardField', + lockForwarding: 'lockForwardingField', + isLocked: 'lockedField', + }, + }; + + const mountComponent = (propsData = defaultProps) => { + wrapper = shallowMountExtended(component, { + propsData, + stubs: { + GlSprintf, + }, + }); + }; + + const findFormGroup = () => wrapper.findComponent(GlFormGroup); + const findForwardingCheckbox = () => wrapper.findByTestId('forwarding-checkbox'); + const findLockForwardingCheckbox = () => wrapper.findByTestId('lock-forwarding-checkbox'); + + it('has a form group', () => { + mountComponent(); + + expect(findFormGroup().exists()).toBe(true); + expect(findFormGroup().attributes()).toMatchObject({ + label: defaultProps.label, + }); + }); + + describe.each` + name | finder | label | extraProps | field + ${'forwarding'} | ${findForwardingCheckbox} | ${'Forward label package requests'} | ${{ forwarding: true }} | ${defaultProps.modelNames.forwarding} + ${'lock forwarding'} | ${findLockForwardingCheckbox} | ${'Enforce label setting for all subgroups'} | ${{ lockForwarding: true }} | ${defaultProps.modelNames.lockForwarding} + `('$name checkbox', ({ name, finder, label, extraProps, field }) => { + it('is rendered', () => { + mountComponent(); + expect(finder().exists()).toBe(true); + expect(finder().text()).toMatchInterpolatedText(label); + expect(finder().attributes('disabled')).toBeUndefined(); + expect(finder().attributes('checked')).toBeUndefined(); + }); + + it(`is checked when ${name} set`, () => { + mountComponent({ ...defaultProps, ...extraProps }); + + expect(finder().attributes('checked')).toBe('true'); + }); + + it(`emits an update event with field ${field} set`, () => { + mountComponent(); + + finder().vm.$emit('change', true); + + expect(wrapper.emitted('update')).toStrictEqual([[field, true]]); + }); + }); + + describe('disabled', () => { + it('disables both checkboxes', () => { + mountComponent({ ...defaultProps, disabled: true }); + + expect(findForwardingCheckbox().attributes('disabled')).toEqual('true'); + expect(findLockForwardingCheckbox().attributes('disabled')).toEqual('true'); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js b/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js index 31fc3ad419c..7edc321867c 100644 --- a/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js +++ b/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js @@ -7,6 +7,7 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import PackagesSettings from '~/packages_and_registries/settings/group/components/packages_settings.vue'; import DependencyProxySettings from '~/packages_and_registries/settings/group/components/dependency_proxy_settings.vue'; +import PackagesForwardingSettings from '~/packages_and_registries/settings/group/components/packages_forwarding_settings.vue'; import component from '~/packages_and_registries/settings/group/components/group_settings_app.vue'; @@ -60,6 +61,7 @@ describe('Group Settings App', () => { const findAlert = () => wrapper.findComponent(GlAlert); const findPackageSettings = () => wrapper.findComponent(PackagesSettings); + const findPackageForwardingSettings = () => wrapper.findComponent(PackagesForwardingSettings); const findDependencyProxySettings = () => wrapper.findComponent(DependencyProxySettings); const waitForApolloQueryAndRender = async () => { @@ -67,16 +69,18 @@ describe('Group Settings App', () => { await nextTick(); }; - const packageSettingsProps = { packageSettings: packageSettings() }; + const packageSettingsProps = { packageSettings }; + const packageForwardingSettingsProps = { forwardSettings: { ...packageSettings } }; const dependencyProxyProps = { dependencyProxySettings: dependencyProxySettings(), dependencyProxyImageTtlPolicy: dependencyProxyImageTtlPolicy(), }; describe.each` - finder | entitySpecificProps | successMessage | errorMessage - ${findPackageSettings} | ${packageSettingsProps} | ${'Settings saved successfully'} | ${'An error occurred while saving the settings'} - ${findDependencyProxySettings} | ${dependencyProxyProps} | ${'Setting saved successfully'} | ${'An error occurred while saving the setting'} + finder | entitySpecificProps | successMessage | errorMessage + ${findPackageSettings} | ${packageSettingsProps} | ${'Settings saved successfully'} | ${'An error occurred while saving the settings'} + ${findPackageForwardingSettings} | ${packageForwardingSettingsProps} | ${'Settings saved successfully'} | ${'An error occurred while saving the settings'} + ${findDependencyProxySettings} | ${dependencyProxyProps} | ${'Setting saved successfully'} | ${'An error occurred while saving the setting'} `('settings blocks', ({ finder, entitySpecificProps, successMessage, errorMessage }) => { beforeEach(() => { mountComponent(); @@ -88,10 +92,7 @@ describe('Group Settings App', () => { }); it('binds the correctProps', () => { - expect(finder().props()).toMatchObject({ - isLoading: false, - ...entitySpecificProps, - }); + expect(finder().props()).toMatchObject(entitySpecificProps); }); describe('success event', () => { diff --git a/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js index 13eba39ec8c..807f332f4d3 100644 --- a/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js +++ b/spec/frontend/packages_and_registries/settings/group/components/package_settings_spec.js @@ -48,7 +48,7 @@ describe('Packages Settings', () => { apolloProvider, provide: defaultProvide, propsData: { - packageSettings: packageSettings(), + packageSettings, }, stubs: { SettingsBlock, @@ -83,7 +83,7 @@ describe('Packages Settings', () => { }; const emitMavenSettingsUpdate = (override) => { - findGenericDuplicatedSettingsExceptionsInput().vm.$emit('update', { + findMavenDuplicatedSettingsExceptionsInput().vm.$emit('update', { mavenDuplicateExceptionRegex: ')', ...override, }); @@ -117,7 +117,7 @@ describe('Packages Settings', () => { it('renders toggle', () => { mountComponent({ mountFn: mountExtended }); - const { mavenDuplicatesAllowed } = packageSettings(); + const { mavenDuplicatesAllowed } = packageSettings; expect(findMavenDuplicatedSettingsToggle().exists()).toBe(true); @@ -132,7 +132,7 @@ describe('Packages Settings', () => { it('renders ExceptionsInput and assigns duplication allowness and exception props', () => { mountComponent({ mountFn: mountExtended }); - const { mavenDuplicatesAllowed, mavenDuplicateExceptionRegex } = packageSettings(); + const { mavenDuplicatesAllowed, mavenDuplicateExceptionRegex } = packageSettings; expect(findMavenDuplicatedSettingsExceptionsInput().exists()).toBe(true); @@ -170,7 +170,7 @@ describe('Packages Settings', () => { it('renders toggle', () => { mountComponent({ mountFn: mountExtended }); - const { genericDuplicatesAllowed } = packageSettings(); + const { genericDuplicatesAllowed } = packageSettings; expect(findGenericDuplicatedSettingsToggle().exists()).toBe(true); expect(findGenericDuplicatedSettingsToggle().props()).toMatchObject({ @@ -184,7 +184,7 @@ describe('Packages Settings', () => { it('renders ExceptionsInput and assigns duplication allowness and exception props', async () => { mountComponent({ mountFn: mountExtended }); - const { genericDuplicatesAllowed, genericDuplicateExceptionRegex } = packageSettings(); + const { genericDuplicatesAllowed, genericDuplicateExceptionRegex } = packageSettings; expect(findGenericDuplicatedSettingsExceptionsInput().props()).toMatchObject({ duplicatesAllowed: genericDuplicatesAllowed, @@ -239,7 +239,7 @@ describe('Packages Settings', () => { emitMavenSettingsUpdate({ mavenDuplicateExceptionRegex }); expect(updateGroupPackagesSettingsOptimisticResponse).toHaveBeenCalledWith({ - ...packageSettings(), + ...packageSettings, mavenDuplicateExceptionRegex, }); }); diff --git a/spec/frontend/packages_and_registries/settings/group/components/packages_forwarding_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/packages_forwarding_settings_spec.js new file mode 100644 index 00000000000..a0b257a9496 --- /dev/null +++ b/spec/frontend/packages_and_registries/settings/group/components/packages_forwarding_settings_spec.js @@ -0,0 +1,280 @@ +import Vue from 'vue'; +import { GlButton } from '@gitlab/ui'; +import VueApollo from 'vue-apollo'; +import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; +import createMockApollo from 'helpers/mock_apollo_helper'; +import waitForPromises from 'helpers/wait_for_promises'; +import component from '~/packages_and_registries/settings/group/components/packages_forwarding_settings.vue'; +import { + PACKAGE_FORWARDING_SETTINGS_DESCRIPTION, + PACKAGE_FORWARDING_SETTINGS_HEADER, +} from '~/packages_and_registries/settings/group/constants'; + +import updateNamespacePackageSettings from '~/packages_and_registries/settings/group/graphql/mutations/update_group_packages_settings.mutation.graphql'; +import getGroupPackagesSettingsQuery from '~/packages_and_registries/settings/group/graphql/queries/get_group_packages_settings.query.graphql'; +import SettingsBlock from '~/vue_shared/components/settings/settings_block.vue'; +import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses'; +import { + packageSettings, + packageForwardingSettings, + groupPackageSettingsMock, + groupPackageForwardSettingsMutationMock, + mutationErrorMock, + npmProps, + pypiProps, + mavenProps, +} from '../mock_data'; + +jest.mock('~/flash'); +jest.mock('~/packages_and_registries/settings/group/graphql/utils/optimistic_responses'); + +describe('Packages Forwarding Settings', () => { + let wrapper; + let apolloProvider; + const mutationResolverFn = jest.fn().mockResolvedValue(groupPackageForwardSettingsMutationMock()); + + const defaultProvide = { + groupPath: 'foo_group_path', + }; + + const mountComponent = ({ + forwardSettings = { ...packageSettings }, + features = {}, + mutationResolver = mutationResolverFn, + } = {}) => { + Vue.use(VueApollo); + + const requestHandlers = [[updateNamespacePackageSettings, mutationResolver]]; + + apolloProvider = createMockApollo(requestHandlers); + + wrapper = shallowMountExtended(component, { + apolloProvider, + provide: { + ...defaultProvide, + glFeatures: { + ...features, + }, + }, + propsData: { + forwardSettings, + }, + stubs: { + SettingsBlock, + }, + }); + }; + + const findSettingsBlock = () => wrapper.findComponent(SettingsBlock); + const findForm = () => wrapper.find('form'); + const findSubmitButton = () => findForm().findComponent(GlButton); + const findDescription = () => wrapper.findByTestId('description'); + const findMavenForwardingSettings = () => wrapper.findByTestId('maven'); + const findNpmForwardingSettings = () => wrapper.findByTestId('npm'); + const findPyPiForwardingSettings = () => wrapper.findByTestId('pypi'); + + const fillApolloCache = () => { + apolloProvider.defaultClient.cache.writeQuery({ + query: getGroupPackagesSettingsQuery, + variables: { + fullPath: defaultProvide.groupPath, + }, + ...groupPackageSettingsMock, + }); + }; + + const updateNpmSettings = () => { + findNpmForwardingSettings().vm.$emit('update', 'npmPackageRequestsForwarding', false); + }; + + const submitForm = () => { + findForm().trigger('submit'); + return waitForPromises(); + }; + + afterEach(() => { + apolloProvider = null; + }); + + it('renders a settings block', () => { + mountComponent(); + + expect(findSettingsBlock().exists()).toBe(true); + }); + + it('has the correct header text', () => { + mountComponent(); + + expect(wrapper.text()).toContain(PACKAGE_FORWARDING_SETTINGS_HEADER); + }); + + it('has the correct description text', () => { + mountComponent(); + + expect(findDescription().text()).toMatchInterpolatedText( + PACKAGE_FORWARDING_SETTINGS_DESCRIPTION, + ); + }); + + it('watches changes to props', async () => { + mountComponent(); + + expect(findNpmForwardingSettings().props()).toMatchObject(npmProps); + + await wrapper.setProps({ + forwardSettings: { + ...packageSettings, + npmPackageRequestsForwardingLocked: true, + }, + }); + + expect(findNpmForwardingSettings().props()).toMatchObject({ ...npmProps, disabled: true }); + }); + + it('submit button is disabled', () => { + mountComponent(); + + expect(findSubmitButton().props('disabled')).toBe(true); + }); + + describe.each` + type | finder | props | field + ${'npm'} | ${findNpmForwardingSettings} | ${npmProps} | ${'npmPackageRequestsForwarding'} + ${'pypi'} | ${findPyPiForwardingSettings} | ${pypiProps} | ${'pypiPackageRequestsForwarding'} + ${'maven'} | ${findMavenForwardingSettings} | ${mavenProps} | ${'mavenPackageRequestsForwarding'} + `('$type settings', ({ finder, props, field }) => { + beforeEach(() => { + mountComponent({ features: { mavenCentralRequestForwarding: true } }); + }); + + it('assigns forwarding settings props', () => { + expect(finder().props()).toMatchObject(props); + }); + + it('on update event enables submit button', async () => { + finder().vm.$emit('update', field, false); + + await waitForPromises(); + + expect(findSubmitButton().props('disabled')).toBe(false); + }); + }); + + describe('maven settings', () => { + describe('with feature turned off', () => { + it('does not exist', () => { + mountComponent(); + + expect(findMavenForwardingSettings().exists()).toBe(false); + }); + }); + }); + + describe('settings update', () => { + describe('success state', () => { + it('calls the mutation with the right variables', async () => { + const { + mavenPackageRequestsForwardingLocked, + npmPackageRequestsForwardingLocked, + pypiPackageRequestsForwardingLocked, + ...packageSettingsInput + } = packageForwardingSettings; + + mountComponent(); + + fillApolloCache(); + updateNpmSettings(); + + await submitForm(); + + expect(mutationResolverFn).toHaveBeenCalledWith({ + input: { + namespacePath: defaultProvide.groupPath, + ...packageSettingsInput, + npmPackageRequestsForwarding: false, + }, + }); + }); + + it('when field are locked calls the mutation with the right variables', async () => { + mountComponent({ + forwardSettings: { + ...packageSettings, + mavenPackageRequestsForwardingLocked: true, + pypiPackageRequestsForwardingLocked: true, + }, + }); + + fillApolloCache(); + updateNpmSettings(); + + await submitForm(); + + expect(mutationResolverFn).toHaveBeenCalledWith({ + input: { + namespacePath: defaultProvide.groupPath, + lockNpmPackageRequestsForwarding: false, + npmPackageRequestsForwarding: false, + }, + }); + }); + + it('emits a success event', async () => { + mountComponent(); + fillApolloCache(); + updateNpmSettings(); + + await submitForm(); + + expect(wrapper.emitted('success')).toHaveLength(1); + }); + + it('has an optimistic response', async () => { + const npmPackageRequestsForwarding = false; + mountComponent(); + + fillApolloCache(); + + expect(findNpmForwardingSettings().props('forwarding')).toBe(true); + + updateNpmSettings(); + await submitForm(); + + expect(updateGroupPackagesSettingsOptimisticResponse).toHaveBeenCalledWith({ + ...packageSettings, + npmPackageRequestsForwarding, + }); + expect(findNpmForwardingSettings().props('forwarding')).toBe(npmPackageRequestsForwarding); + }); + }); + + describe('errors', () => { + it('mutation payload with root level errors', async () => { + const mutationResolver = jest.fn().mockResolvedValue(mutationErrorMock); + mountComponent({ mutationResolver }); + + fillApolloCache(); + + updateNpmSettings(); + await submitForm(); + + expect(wrapper.emitted('error')).toHaveLength(1); + }); + + it.each` + type | mutationResolver + ${'local'} | ${jest.fn().mockResolvedValue(groupPackageForwardSettingsMutationMock({ errors: ['foo'] }))} + ${'network'} | ${jest.fn().mockRejectedValue()} + `('mutation payload with $type error', async ({ mutationResolver }) => { + mountComponent({ mutationResolver }); + + fillApolloCache(); + + updateNpmSettings(); + await submitForm(); + + expect(wrapper.emitted('error')).toHaveLength(1); + }); + }); + }); +}); diff --git a/spec/frontend/packages_and_registries/settings/group/mock_data.js b/spec/frontend/packages_and_registries/settings/group/mock_data.js index d53446de910..1ca9dc6daeb 100644 --- a/spec/frontend/packages_and_registries/settings/group/mock_data.js +++ b/spec/frontend/packages_and_registries/settings/group/mock_data.js @@ -1,9 +1,26 @@ -export const packageSettings = () => ({ +const packageDuplicateSettings = { mavenDuplicatesAllowed: true, mavenDuplicateExceptionRegex: '', genericDuplicatesAllowed: true, genericDuplicateExceptionRegex: '', -}); +}; + +export const packageForwardingSettings = { + mavenPackageRequestsForwarding: true, + lockMavenPackageRequestsForwarding: false, + npmPackageRequestsForwarding: true, + lockNpmPackageRequestsForwarding: false, + pypiPackageRequestsForwarding: true, + lockPypiPackageRequestsForwarding: false, + mavenPackageRequestsForwardingLocked: false, + npmPackageRequestsForwardingLocked: false, + pypiPackageRequestsForwardingLocked: false, +}; + +export const packageSettings = { + ...packageDuplicateSettings, + ...packageForwardingSettings, +}; export const dependencyProxySettings = (extend) => ({ enabled: true, @@ -21,13 +38,52 @@ export const groupPackageSettingsMock = { group: { id: '1', fullPath: 'foo_group_path', - packageSettings: packageSettings(), + packageSettings: { + ...packageSettings, + __typename: 'PackageSettings', + }, dependencyProxySetting: dependencyProxySettings(), dependencyProxyImageTtlPolicy: dependencyProxyImageTtlPolicy(), }, }, }; +export const npmProps = { + forwarding: packageForwardingSettings.npmPackageRequestsForwarding, + lockForwarding: packageForwardingSettings.lockNpmPackageRequestsForwarding, + label: 'npm', + disabled: false, + modelNames: { + forwarding: 'npmPackageRequestsForwarding', + lockForwarding: 'lockNpmPackageRequestsForwarding', + isLocked: 'npmPackageRequestsForwardingLocked', + }, +}; + +export const pypiProps = { + forwarding: packageForwardingSettings.pypiPackageRequestsForwarding, + lockForwarding: packageForwardingSettings.lockPypiPackageRequestsForwarding, + label: 'PyPI', + disabled: false, + modelNames: { + forwarding: 'pypiPackageRequestsForwarding', + lockForwarding: 'lockPypiPackageRequestsForwarding', + isLocked: 'pypiPackageRequestsForwardingLocked', + }, +}; + +export const mavenProps = { + forwarding: packageForwardingSettings.mavenPackageRequestsForwarding, + lockForwarding: packageForwardingSettings.lockMavenPackageRequestsForwarding, + label: 'Maven', + disabled: false, + modelNames: { + forwarding: 'mavenPackageRequestsForwarding', + lockForwarding: 'lockMavenPackageRequestsForwarding', + isLocked: 'mavenPackageRequestsForwardingLocked', + }, +}; + export const groupPackageSettingsMutationMock = (override) => ({ data: { updateNamespacePackageSettings: { @@ -43,6 +99,19 @@ export const groupPackageSettingsMutationMock = (override) => ({ }, }); +export const groupPackageForwardSettingsMutationMock = (override) => ({ + data: { + updateNamespacePackageSettings: { + packageSettings: { + npmPackageRequestsForwarding: true, + lockNpmPackageRequestsForwarding: false, + }, + errors: [], + ...override, + }, + }, +}); + export const dependencyProxySettingMutationMock = (override) => ({ data: { updateDependencyProxySettings: { |