Skip to content
Snippets Groups Projects
Commit 745e1d09 authored by Zack Cuddy's avatar Zack Cuddy :two:
Browse files

Merge branch '360487-cascade-package-forward-settings-from-admin-area-to-group' into 'master'

Allows cascading package forward settings from admin to group

See merge request !103025



Merged-by: Zack Cuddy's avatarZack Cuddy <zcuddy@gitlab.com>
Approved-by: Alex Buijs's avatarAlex Buijs <abuijs@gitlab.com>
Approved-by: Emily Bauman's avatarEmily Bauman <ebauman@gitlab.com>
Approved-by: default avatarPayton Burdette <pburdette@gitlab.com>
Approved-by: default avatarMax Orefice <morefice@gitlab.com>
Approved-by: Zack Cuddy's avatarZack Cuddy <zcuddy@gitlab.com>
Co-authored-by: default avatarRahul Chanila <rchanila@gitlab.com>
parents f789c8c0 eb2a193b
No related branches found
No related tags found
1 merge request!103025Allows cascading package forward settings from admin to group
Pipeline #696695493 passed
Showing
with 889 additions and 42 deletions
<script>
import { GlFormCheckbox, GlFormGroup, GlSprintf } from '@gitlab/ui';
import { isEqual } from 'lodash';
import {
PACKAGE_FORWARDING_CHECKBOX_LABEL,
PACKAGE_FORWARDING_ENFORCE_LABEL,
} from '~/packages_and_registries/settings/group/constants';
export default {
name: 'ForwardingSettings',
i18n: {
PACKAGE_FORWARDING_CHECKBOX_LABEL,
PACKAGE_FORWARDING_ENFORCE_LABEL,
},
components: {
GlFormCheckbox,
GlFormGroup,
GlSprintf,
},
props: {
disabled: {
type: Boolean,
required: false,
default: true,
},
forwarding: {
type: Boolean,
required: false,
default: false,
},
label: {
type: String,
required: true,
},
lockForwarding: {
type: Boolean,
required: false,
default: false,
},
modelNames: {
type: Object,
required: true,
validator(value) {
return isEqual(Object.keys(value), ['forwarding', 'lockForwarding', 'isLocked']);
},
},
},
computed: {
fields() {
return [
{
testid: 'forwarding-checkbox',
label: PACKAGE_FORWARDING_CHECKBOX_LABEL,
updateField: this.modelNames.forwarding,
checked: this.forwarding,
},
{
testid: 'lock-forwarding-checkbox',
label: PACKAGE_FORWARDING_ENFORCE_LABEL,
updateField: this.modelNames.lockForwarding,
checked: this.lockForwarding,
},
];
},
},
methods: {
update(type, value) {
this.$emit('update', type, value);
},
},
};
</script>
<template>
<gl-form-group :label="label">
<gl-form-checkbox
v-for="field in fields"
:key="field.testid"
:checked="field.checked"
:disabled="disabled"
:data-testid="field.testid"
@change="update(field.updateField, $event)"
>
<gl-sprintf :message="field.label">
<template #packageType>
{{ label }}
</template>
</gl-sprintf>
</gl-form-checkbox>
</gl-form-group>
</template>
......@@ -2,6 +2,7 @@
import { GlAlert } from '@gitlab/ui';
import { n__ } from '~/locale';
import PackagesSettings from '~/packages_and_registries/settings/group/components/packages_settings.vue';
import PackagesForwardingSettings from '~/packages_and_registries/settings/group/components/packages_forwarding_settings.vue';
import DependencyProxySettings from '~/packages_and_registries/settings/group/components/dependency_proxy_settings.vue';
import getGroupPackagesSettingsQuery from '~/packages_and_registries/settings/group/graphql/queries/get_group_packages_settings.query.graphql';
......@@ -11,6 +12,7 @@ export default {
components: {
GlAlert,
PackagesSettings,
PackagesForwardingSettings,
DependencyProxySettings,
},
inject: ['groupPath'],
......@@ -82,6 +84,12 @@ export default {
@error="handleError(2)"
/>
<packages-forwarding-settings
:forward-settings="packageSettings"
@success="handleSuccess(2)"
@error="handleError(2)"
/>
<dependency-proxy-settings
:dependency-proxy-settings="dependencyProxySettings"
:dependency-proxy-image-ttl-policy="dependencyProxyImageTtlPolicy"
......
<script>
import { GlButton } from '@gitlab/ui';
import { isEqual } from 'lodash';
import {
PACKAGE_FORWARDING_SETTINGS_HEADER,
PACKAGE_FORWARDING_SETTINGS_DESCRIPTION,
PACKAGE_FORWARDING_FORM_BUTTON,
PACKAGE_FORWARDING_FIELDS,
MAVEN_FORWARDING_FIELDS,
} from '~/packages_and_registries/settings/group/constants';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import updateNamespacePackageSettings from '~/packages_and_registries/settings/group/graphql/mutations/update_group_packages_settings.mutation.graphql';
import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
import SettingsBlock from '~/packages_and_registries/shared/components/settings_block.vue';
import ForwardingSettings from '~/packages_and_registries/settings/group/components/forwarding_settings.vue';
export default {
name: 'PackageForwardingSettings',
i18n: {
PACKAGE_FORWARDING_FORM_BUTTON,
PACKAGE_FORWARDING_SETTINGS_HEADER,
PACKAGE_FORWARDING_SETTINGS_DESCRIPTION,
},
components: {
ForwardingSettings,
GlButton,
SettingsBlock,
},
mixins: [glFeatureFlagsMixin()],
inject: ['groupPath'],
props: {
forwardSettings: {
type: Object,
required: true,
},
},
data() {
return {
mutationLoading: false,
workingCopy: { ...this.forwardSettings },
};
},
computed: {
packageForwardingFields() {
const fields = PACKAGE_FORWARDING_FIELDS;
if (this.glFeatures.mavenCentralRequestForwarding) {
return fields.concat(MAVEN_FORWARDING_FIELDS);
}
return fields;
},
isEdited() {
return !isEqual(this.forwardSettings, this.workingCopy);
},
isDisabled() {
return !this.isEdited || this.mutationLoading;
},
npmMutation() {
if (this.workingCopy.npmPackageRequestsForwardingLocked) {
return {};
}
return {
npmPackageRequestsForwarding: this.workingCopy.npmPackageRequestsForwarding,
lockNpmPackageRequestsForwarding: this.workingCopy.lockNpmPackageRequestsForwarding,
};
},
pypiMutation() {
if (this.workingCopy.pypiPackageRequestsForwardingLocked) {
return {};
}
return {
pypiPackageRequestsForwarding: this.workingCopy.pypiPackageRequestsForwarding,
lockPypiPackageRequestsForwarding: this.workingCopy.lockPypiPackageRequestsForwarding,
};
},
mavenMutation() {
if (this.workingCopy.mavenPackageRequestsForwardingLocked) {
return {};
}
return {
mavenPackageRequestsForwarding: this.workingCopy.mavenPackageRequestsForwarding,
lockMavenPackageRequestsForwarding: this.workingCopy.lockMavenPackageRequestsForwarding,
};
},
mutationVariables() {
return {
...this.npmMutation,
...this.pypiMutation,
...this.mavenMutation,
};
},
},
watch: {
forwardSettings(newValue) {
this.workingCopy = { ...newValue };
},
},
methods: {
isForwardingFieldsDisabled(fields) {
const isLocked = fields?.modelNames?.isLocked;
return this.mutationLoading || this.workingCopy[isLocked];
},
forwardingFieldsForwarding(fields) {
const forwarding = fields?.modelNames?.forwarding;
return this.workingCopy[forwarding];
},
forwardingFieldsLockForwarding(fields) {
const lockForwarding = fields?.modelNames?.lockForwarding;
return this.workingCopy[lockForwarding];
},
async submit() {
this.mutationLoading = true;
try {
const { data } = await this.$apollo.mutate({
mutation: updateNamespacePackageSettings,
variables: {
input: {
namespacePath: this.groupPath,
...this.mutationVariables,
},
},
update: updateGroupPackageSettings(this.groupPath),
optimisticResponse: updateGroupPackagesSettingsOptimisticResponse({
...this.forwardSettings,
...this.mutationVariables,
}),
});
if (data.updateNamespacePackageSettings?.errors?.length > 0) {
throw new Error();
} else {
this.$emit('success');
}
} catch {
this.$emit('error');
} finally {
this.mutationLoading = false;
}
},
updateWorkingCopy(type, value) {
this.$set(this.workingCopy, type, value);
},
},
};
</script>
<template>
<settings-block>
<template #title> {{ $options.i18n.PACKAGE_FORWARDING_SETTINGS_HEADER }}</template>
<template #description>
<span data-testid="description">
{{ $options.i18n.PACKAGE_FORWARDING_SETTINGS_DESCRIPTION }}
</span>
</template>
<template #default>
<form @submit.prevent="submit">
<forwarding-settings
v-for="forwardingFields in packageForwardingFields"
:key="forwardingFields.label"
:data-testid="forwardingFields.testid"
:disabled="isForwardingFieldsDisabled(forwardingFields)"
:forwarding="forwardingFieldsForwarding(forwardingFields)"
:label="forwardingFields.label"
:lock-forwarding="forwardingFieldsLockForwarding(forwardingFields)"
:model-names="forwardingFields.modelNames"
@update="updateWorkingCopy"
/>
<gl-button
type="submit"
:disabled="isDisabled"
:loading="mutationLoading"
category="primary"
variant="confirm"
class="js-no-auto-disable gl-mr-4"
>
{{ $options.i18n.PACKAGE_FORWARDING_FORM_BUTTON }}
</gl-button>
</form>
</template>
</settings-block>
</template>
......@@ -7,6 +7,8 @@ export const PACKAGE_SETTINGS_DESCRIPTION = s__(
);
export const PACKAGE_FORMATS_TABLE_HEADER = s__('PackageRegistry|Package formats');
export const MAVEN_PACKAGE_FORMAT = s__('PackageRegistry|Maven');
export const NPM_PACKAGE_FORMAT = s__('PackageRegistry|npm');
export const PYPI_PACKAGE_FORMAT = s__('PackageRegistry|PyPI');
export const GENERIC_PACKAGE_FORMAT = s__('PackageRegistry|Generic');
export const DUPLICATES_TOGGLE_LABEL = s__('PackageRegistry|Allow duplicates');
......@@ -15,11 +17,65 @@ export const DUPLICATES_SETTINGS_EXCEPTION_LEGEND = s__(
'PackageRegistry|Publish packages if their name or version matches this regex.',
);
export const PACKAGE_FORWARDING_SETTINGS_HEADER = s__('PackageRegistry|Package forwarding');
export const PACKAGE_FORWARDING_SETTINGS_DESCRIPTION = s__(
'PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry.',
);
export const PACKAGE_FORWARDING_CHECKBOX_LABEL = s__(
`PackageRegistry|Forward %{packageType} package requests`,
);
export const PACKAGE_FORWARDING_ENFORCE_LABEL = s__(
`PackageRegistry|Enforce %{packageType} setting for all subgroups`,
);
const MAVEN_PACKAGE_REQUESTS_FORWARDING = 'mavenPackageRequestsForwarding';
const LOCK_MAVEN_PACKAGE_REQUESTS_FORWARDING = 'lockMavenPackageRequestsForwarding';
const MAVEN_PACKAGE_REQUESTS_FORWARDING_LOCKED = 'mavenPackageRequestsForwardingLocked';
const NPM_PACKAGE_REQUESTS_FORWARDING = 'npmPackageRequestsForwarding';
const LOCK_NPM_PACKAGE_REQUESTS_FORWARDING = 'lockNpmPackageRequestsForwarding';
const NPM_PACKAGE_REQUESTS_FORWARDING_LOCKED = 'npmPackageRequestsForwardingLocked';
const PYPI_PACKAGE_REQUESTS_FORWARDING = 'pypiPackageRequestsForwarding';
const LOCK_PYPI_PACKAGE_REQUESTS_FORWARDING = 'lockPypiPackageRequestsForwarding';
const PYPI_PACKAGE_REQUESTS_FORWARDING_LOCKED = 'pypiPackageRequestsForwardingLocked';
export const PACKAGE_FORWARDING_FORM_BUTTON = __('Save changes');
export const DEPENDENCY_PROXY_HEADER = s__('DependencyProxy|Dependency Proxy');
export const DEPENDENCY_PROXY_DESCRIPTION = s__(
'DependencyProxy|Enable the Dependency Proxy and settings for clearing the cache.',
);
export const PACKAGE_FORWARDING_FIELDS = [
{
label: NPM_PACKAGE_FORMAT,
testid: 'npm',
modelNames: {
forwarding: NPM_PACKAGE_REQUESTS_FORWARDING,
lockForwarding: LOCK_NPM_PACKAGE_REQUESTS_FORWARDING,
isLocked: NPM_PACKAGE_REQUESTS_FORWARDING_LOCKED,
},
},
{
label: PYPI_PACKAGE_FORMAT,
testid: 'pypi',
modelNames: {
forwarding: PYPI_PACKAGE_REQUESTS_FORWARDING,
lockForwarding: LOCK_PYPI_PACKAGE_REQUESTS_FORWARDING,
isLocked: PYPI_PACKAGE_REQUESTS_FORWARDING_LOCKED,
},
},
];
export const MAVEN_FORWARDING_FIELDS = {
label: MAVEN_PACKAGE_FORMAT,
testid: 'maven',
modelNames: {
forwarding: MAVEN_PACKAGE_REQUESTS_FORWARDING,
lockForwarding: LOCK_MAVEN_PACKAGE_REQUESTS_FORWARDING,
isLocked: MAVEN_PACKAGE_REQUESTS_FORWARDING_LOCKED,
},
};
// Parameters
export const PACKAGES_DOCS_PATH = helpPagePath('user/packages/index');
......
fragment PackageSettingsFields on PackageSettings {
mavenDuplicatesAllowed
mavenDuplicateExceptionRegex
genericDuplicatesAllowed
genericDuplicateExceptionRegex
mavenPackageRequestsForwarding
lockMavenPackageRequestsForwarding
mavenPackageRequestsForwardingLocked
npmPackageRequestsForwarding
lockNpmPackageRequestsForwarding
npmPackageRequestsForwardingLocked
pypiPackageRequestsForwarding
lockPypiPackageRequestsForwarding
pypiPackageRequestsForwardingLocked
}
#import "~/packages_and_registries/settings/group/graphql/fragments/package_settings_fields.fragment.graphql"
mutation updateNamespacePackageSettings($input: UpdateNamespacePackageSettingsInput!) {
updateNamespacePackageSettings(input: $input) {
packageSettings {
mavenDuplicatesAllowed
mavenDuplicateExceptionRegex
genericDuplicatesAllowed
genericDuplicateExceptionRegex
...PackageSettingsFields
}
errors
}
......
#import "~/packages_and_registries/settings/group/graphql/fragments/package_settings_fields.fragment.graphql"
query getGroupPackagesSettings($fullPath: ID!) {
group(fullPath: $fullPath) {
id
......@@ -9,10 +11,7 @@ query getGroupPackagesSettings($fullPath: ID!) {
enabled
}
packageSettings {
mavenDuplicatesAllowed
mavenDuplicateExceptionRegex
genericDuplicatesAllowed
genericDuplicateExceptionRegex
...PackageSettingsFields
}
}
}
......@@ -7,6 +7,10 @@ class PackagesAndRegistriesController < Groups::ApplicationController
before_action :authorize_admin_group!
before_action :verify_packages_enabled!
before_action do
push_frontend_feature_flag(:maven_central_request_forwarding, group)
end
feature_category :package_registry
urgency :low
......
......@@ -6,7 +6,7 @@
= render Pajamas::ButtonComponent.new(button_options: { class: 'js-settings-toggle' }) do
= expanded_by_default? ? _('Collapse') : _('Expand')
%p
= _("Control how the GitLab Package Registry functions.")
= s_('PackageRegistry|Configure package forwarding and package file size limits.')
= render_if_exists 'admin/application_settings/ee_package_registry'
......
......@@ -83,8 +83,11 @@ def visible_application_setting_attributes
required_ci_templates: :required_instance_ci_template,
disable_name_update_for_users: :updating_name_disabled_for_users,
package_forwarding: [:npm_package_requests_forwarding,
:lock_npm_package_requests_forwarding,
:pypi_package_requests_forwarding,
:maven_package_requests_forwarding],
:lock_pypi_package_requests_forwarding,
:maven_package_requests_forwarding,
:lock_maven_package_requests_forwarding],
default_branch_protection_restriction_in_groups: :group_owners_can_manage_default_branch_protection,
group_ip_restriction: :globally_allowed_ips
}.each do |license_feature, attribute_names|
......
.settings-content
%h5
= s_('PackageRegistry|Forward package requests')
%p
= s_('PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry.')
= gitlab_ui_form_for @application_setting, url: ci_cd_admin_application_settings_path(anchor: 'js-package-settings'), html: { class: 'fieldset-form' } do |f|
= form_errors(@application_setting)
%fieldset
.form-group
= f.gitlab_ui_checkbox_component :npm_package_requests_forwarding,
_('Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry') % { package_type: 'npm', registry_type: 'npm' }
s_('PackageRegistry|Forward %{package_type} package requests') % { package_type: 'npm' }
= f.gitlab_ui_checkbox_component :lock_npm_package_requests_forwarding,
s_('PackageRegistry|Enforce %{package_type} setting for all subgroups') % { package_type: 'npm' }
.form-group
= f.gitlab_ui_checkbox_component :pypi_package_requests_forwarding,
_('Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry') % { package_type: 'PyPI', registry_type: 'PyPI' }
s_('PackageRegistry|Forward %{package_type} package requests') % { package_type: 'PyPI' }
= f.gitlab_ui_checkbox_component :lock_pypi_package_requests_forwarding,
s_('PackageRegistry|Enforce %{package_type} setting for all subgroups') % { package_type: 'PyPI' }
- if Feature.enabled?(:maven_central_request_forwarding)
.form-group
= f.gitlab_ui_checkbox_component :maven_package_requests_forwarding,
_('Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry') % { package_type: 'Maven', registry_type: 'Maven' }
= f.submit _('Save changes'), pajamas_button: true
s_('PackageRegistry|Forward %{package_type} package requests') % { package_type: 'Maven' }
= f.gitlab_ui_checkbox_component :lock_maven_package_requests_forwarding,
s_('PackageRegistry|Enforce %{package_type} setting for all subgroups') % { package_type: 'Maven' }
= f.submit _('Save changes'), pajamas_button: true
......@@ -271,30 +271,57 @@
it 'allows you to change the maven_forwarding setting' do
page.within('#js-package-settings') do
check 'Forward Maven package requests to the Maven Registry if the packages are not found in the GitLab Package Registry'
check 'Forward Maven package requests'
click_button 'Save'
end
expect(current_settings.maven_package_requests_forwarding).to be true
end
it 'allows you to change the maven_lock setting' do
page.within('#js-package-settings') do
check 'Enforce Maven setting for all subgroups'
click_button 'Save'
end
expect(current_settings.lock_maven_package_requests_forwarding).to be true
end
it 'allows you to change the npm_forwarding setting' do
page.within('#js-package-settings') do
check 'Forward npm package requests to the npm Registry if the packages are not found in the GitLab Package Registry'
check 'Forward npm package requests'
click_button 'Save'
end
expect(current_settings.npm_package_requests_forwarding).to be true
end
it 'allows you to change the npm_lock setting' do
page.within('#js-package-settings') do
check 'Enforce npm setting for all subgroups'
click_button 'Save'
end
expect(current_settings.lock_npm_package_requests_forwarding).to be true
end
it 'allows you to change the pypi_forwarding setting' do
page.within('#js-package-settings') do
check 'Forward PyPI package requests to the PyPI Registry if the packages are not found in the GitLab Package Registry'
check 'Forward PyPI package requests'
click_button 'Save'
end
expect(current_settings.pypi_package_requests_forwarding).to be true
end
it 'allows you to change the pypi_lock setting' do
page.within('#js-package-settings') do
check 'Enforce PyPI setting for all subgroups'
click_button 'Save'
end
expect(current_settings.lock_pypi_package_requests_forwarding).to be true
end
end
context 'with free user cap settings', :saas do
......
......@@ -10857,9 +10857,6 @@ msgstr ""
msgid "Control how the CI_JOB_TOKEN CI/CD variable is used for API access between projects."
msgstr ""
 
msgid "Control how the GitLab Package Registry functions."
msgstr ""
msgid "Control whether to display customer experience improvement content and third-party offers in GitLab."
msgstr ""
 
......@@ -17382,9 +17379,6 @@ msgstr ""
msgid "Format: %{dateFormat}"
msgstr ""
 
msgid "Forward %{package_type} package requests to the %{registry_type} Registry if the packages are not found in the GitLab Package Registry"
msgstr ""
msgid "Found errors in your %{gitlab_ci_yml}:"
msgstr ""
 
......@@ -28871,6 +28865,9 @@ msgstr ""
msgid "PackageRegistry|Conan Command"
msgstr ""
 
msgid "PackageRegistry|Configure package forwarding and package file size limits."
msgstr ""
msgid "PackageRegistry|Copy .pypirc content"
msgstr ""
 
......@@ -28978,6 +28975,12 @@ msgstr ""
msgid "PackageRegistry|Duplicate packages"
msgstr ""
 
msgid "PackageRegistry|Enforce %{packageType} setting for all subgroups"
msgstr ""
msgid "PackageRegistry|Enforce %{package_type} setting for all subgroups"
msgstr ""
msgid "PackageRegistry|Error publishing"
msgstr ""
 
......@@ -29002,6 +29005,18 @@ msgstr ""
msgid "PackageRegistry|For more information on the PyPi registry, %{linkStart}see the documentation%{linkEnd}."
msgstr ""
 
msgid "PackageRegistry|Forward %{packageType} package requests"
msgstr ""
msgid "PackageRegistry|Forward %{package_type} package requests"
msgstr ""
msgid "PackageRegistry|Forward package requests"
msgstr ""
msgid "PackageRegistry|Forward package requests to a public registry if the packages are not found in the GitLab package registry."
msgstr ""
msgid "PackageRegistry|Generic"
msgstr ""
 
......@@ -29089,6 +29104,9 @@ msgstr ""
msgid "PackageRegistry|Package formats"
msgstr ""
 
msgid "PackageRegistry|Package forwarding"
msgstr ""
msgid "PackageRegistry|Package has %{updatesCount} archived update"
msgid_plural "PackageRegistry|Package has %{updatesCount} archived updates"
msgstr[0] ""
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');
});
});
});
......@@ -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', () => {
......
......@@ -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,
});
});
......
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);
});
});
});
});
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: {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment