summaryrefslogtreecommitdiff
path: root/spec/frontend/packages_and_registries
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 10:34:06 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2021-02-18 10:34:06 +0000
commit859a6fb938bb9ee2a317c46dfa4fcc1af49608f0 (patch)
treed7f2700abe6b4ffcb2dcfc80631b2d87d0609239 /spec/frontend/packages_and_registries
parent446d496a6d000c73a304be52587cd9bbc7493136 (diff)
downloadgitlab-ce-859a6fb938bb9ee2a317c46dfa4fcc1af49608f0.tar.gz
Add latest changes from gitlab-org/gitlab@13-9-stable-eev13.9.0-rc42
Diffstat (limited to 'spec/frontend/packages_and_registries')
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js309
-rw-r--r--spec/frontend/packages_and_registries/settings/group/components/maven_settings_spec.js153
-rw-r--r--spec/frontend/packages_and_registries/settings/group/graphl/utils/cache_update_spec.js56
-rw-r--r--spec/frontend/packages_and_registries/settings/group/graphl/utils/optimistic_responses_spec.js20
-rw-r--r--spec/frontend/packages_and_registries/settings/group/mock_data.js48
5 files changed, 586 insertions, 0 deletions
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
new file mode 100644
index 00000000000..be0d7114e6e
--- /dev/null
+++ b/spec/frontend/packages_and_registries/settings/group/components/group_settings_app_spec.js
@@ -0,0 +1,309 @@
+import { GlSprintf, GlLink, GlAlert } from '@gitlab/ui';
+import { shallowMount, createLocalVue } from '@vue/test-utils';
+import VueApollo from 'vue-apollo';
+import createMockApollo from 'helpers/mock_apollo_helper';
+import waitForPromises from 'helpers/wait_for_promises';
+import component from '~/packages_and_registries/settings/group/components/group_settings_app.vue';
+import MavenSettings from '~/packages_and_registries/settings/group/components/maven_settings.vue';
+import {
+ PACKAGE_SETTINGS_HEADER,
+ PACKAGE_SETTINGS_DESCRIPTION,
+ PACKAGES_DOCS_PATH,
+ ERROR_UPDATING_SETTINGS,
+ SUCCESS_UPDATING_SETTINGS,
+} 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 {
+ groupPackageSettingsMock,
+ groupPackageSettingsMutationMock,
+ groupPackageSettingsMutationErrorMock,
+} from '../mock_data';
+
+jest.mock('~/flash');
+
+const localVue = createLocalVue();
+
+describe('Group Settings App', () => {
+ let wrapper;
+ let apolloProvider;
+ let show;
+
+ const defaultProvide = {
+ defaultExpanded: false,
+ groupPath: 'foo_group_path',
+ };
+
+ const mountComponent = ({
+ provide = defaultProvide,
+ resolver = jest.fn().mockResolvedValue(groupPackageSettingsMock),
+ mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock()),
+ data = {},
+ } = {}) => {
+ localVue.use(VueApollo);
+
+ const requestHandlers = [
+ [getGroupPackagesSettingsQuery, resolver],
+ [updateNamespacePackageSettings, mutationResolver],
+ ];
+
+ apolloProvider = createMockApollo(requestHandlers);
+
+ wrapper = shallowMount(component, {
+ localVue,
+ apolloProvider,
+ provide,
+ data() {
+ return {
+ ...data,
+ };
+ },
+ stubs: {
+ GlSprintf,
+ SettingsBlock,
+ },
+ mocks: {
+ $toast: {
+ show,
+ },
+ },
+ });
+ };
+
+ beforeEach(() => {
+ show = jest.fn();
+ });
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findSettingsBlock = () => wrapper.find(SettingsBlock);
+ const findDescription = () => wrapper.find('[data-testid="description"');
+ const findLink = () => wrapper.find(GlLink);
+ const findMavenSettings = () => wrapper.find(MavenSettings);
+ const findAlert = () => wrapper.find(GlAlert);
+
+ const waitForApolloQueryAndRender = async () => {
+ await waitForPromises();
+ await wrapper.vm.$nextTick();
+ };
+
+ const emitSettingsUpdate = (override) => {
+ findMavenSettings().vm.$emit('update', {
+ mavenDuplicateExceptionRegex: ')',
+ ...override,
+ });
+ };
+
+ it('renders a settings block', () => {
+ mountComponent();
+
+ expect(findSettingsBlock().exists()).toBe(true);
+ });
+
+ it('passes the correct props to settings block', () => {
+ mountComponent();
+
+ expect(findSettingsBlock().props('defaultExpanded')).toBe(false);
+ });
+
+ it('has the correct header text', () => {
+ mountComponent();
+
+ expect(wrapper.text()).toContain(PACKAGE_SETTINGS_HEADER);
+ });
+
+ it('has the correct description text', () => {
+ mountComponent();
+
+ expect(findDescription().text()).toMatchInterpolatedText(PACKAGE_SETTINGS_DESCRIPTION);
+ });
+
+ it('has the correct link', () => {
+ mountComponent();
+
+ expect(findLink().attributes()).toMatchObject({
+ href: PACKAGES_DOCS_PATH,
+ target: '_blank',
+ });
+ expect(findLink().text()).toBe('More Information');
+ });
+
+ it('calls the graphql API with the proper variables', () => {
+ const resolver = jest.fn().mockResolvedValue(groupPackageSettingsMock);
+ mountComponent({ resolver });
+
+ expect(resolver).toHaveBeenCalledWith({
+ fullPath: defaultProvide.groupPath,
+ });
+ });
+
+ describe('maven settings', () => {
+ it('exists', () => {
+ mountComponent();
+
+ expect(findMavenSettings().exists()).toBe(true);
+ });
+
+ it('assigns duplication allowness and exception props', async () => {
+ mountComponent();
+
+ expect(findMavenSettings().props('loading')).toBe(true);
+
+ await waitForApolloQueryAndRender();
+
+ const {
+ mavenDuplicatesAllowed,
+ mavenDuplicateExceptionRegex,
+ } = groupPackageSettingsMock.data.group.packageSettings;
+
+ expect(findMavenSettings().props()).toMatchObject({
+ mavenDuplicatesAllowed,
+ mavenDuplicateExceptionRegex,
+ mavenDuplicateExceptionRegexError: '',
+ loading: false,
+ });
+ });
+
+ it('on update event calls the mutation', async () => {
+ const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationMock());
+ mountComponent({ mutationResolver });
+
+ await waitForApolloQueryAndRender();
+
+ emitSettingsUpdate();
+
+ expect(mutationResolver).toHaveBeenCalledWith({
+ input: { mavenDuplicateExceptionRegex: ')', namespacePath: 'foo_group_path' },
+ });
+ });
+ });
+
+ describe('settings update', () => {
+ describe('success state', () => {
+ it('shows a success alert', async () => {
+ mountComponent();
+
+ await waitForApolloQueryAndRender();
+
+ emitSettingsUpdate();
+
+ await waitForPromises();
+
+ expect(show).toHaveBeenCalledWith(SUCCESS_UPDATING_SETTINGS, {
+ type: 'success',
+ });
+ });
+
+ it('has an optimistic response', async () => {
+ const mavenDuplicateExceptionRegex = 'latest[master]something';
+ mountComponent();
+
+ await waitForApolloQueryAndRender();
+
+ expect(findMavenSettings().props('mavenDuplicateExceptionRegex')).toBe('');
+
+ emitSettingsUpdate({ mavenDuplicateExceptionRegex });
+
+ // wait for apollo to update the model with the optimistic response
+ await wrapper.vm.$nextTick();
+
+ expect(findMavenSettings().props('mavenDuplicateExceptionRegex')).toBe(
+ mavenDuplicateExceptionRegex,
+ );
+
+ // wait for the call to resolve
+ await waitForPromises();
+
+ expect(findMavenSettings().props('mavenDuplicateExceptionRegex')).toBe(
+ mavenDuplicateExceptionRegex,
+ );
+ });
+ });
+
+ describe('errors', () => {
+ const verifyAlert = () => {
+ expect(findAlert().exists()).toBe(true);
+ expect(findAlert().text()).toBe(ERROR_UPDATING_SETTINGS);
+ expect(findAlert().props('variant')).toBe('warning');
+ };
+
+ it('mutation payload with root level errors', async () => {
+ // note this is a complex test that covers all the path around errors that are shown in the form
+ // it's one single it case, due to the expensive preparation and execution
+ const mutationResolver = jest.fn().mockResolvedValue(groupPackageSettingsMutationErrorMock);
+ mountComponent({ mutationResolver });
+
+ await waitForApolloQueryAndRender();
+
+ emitSettingsUpdate();
+
+ await waitForApolloQueryAndRender();
+
+ // errors are bound to the component
+ expect(findMavenSettings().props('mavenDuplicateExceptionRegexError')).toBe(
+ groupPackageSettingsMutationErrorMock.errors[0].extensions.problems[0].message,
+ );
+
+ // general error message is shown
+
+ verifyAlert();
+
+ emitSettingsUpdate();
+
+ await wrapper.vm.$nextTick();
+
+ // errors are reset on mutation call
+ expect(findMavenSettings().props('mavenDuplicateExceptionRegexError')).toBe('');
+ });
+
+ it.each`
+ type | mutationResolver
+ ${'local'} | ${jest.fn().mockResolvedValue(groupPackageSettingsMutationMock({ errors: ['foo'] }))}
+ ${'network'} | ${jest.fn().mockRejectedValue()}
+ `('mutation payload with $type error', async ({ mutationResolver }) => {
+ mountComponent({ mutationResolver });
+
+ await waitForApolloQueryAndRender();
+
+ emitSettingsUpdate();
+
+ await waitForPromises();
+
+ verifyAlert();
+ });
+
+ it('a successful request dismisses the alert', async () => {
+ mountComponent({ data: { alertMessage: 'foo' } });
+
+ await waitForApolloQueryAndRender();
+
+ expect(findAlert().exists()).toBe(true);
+
+ emitSettingsUpdate();
+
+ await waitForPromises();
+
+ expect(findAlert().exists()).toBe(false);
+ });
+
+ it('dismiss event from alert dismiss it from the page', async () => {
+ mountComponent({ data: { alertMessage: 'foo' } });
+
+ await waitForApolloQueryAndRender();
+
+ expect(findAlert().exists()).toBe(true);
+
+ findAlert().vm.$emit('dismiss');
+
+ await wrapper.vm.$nextTick();
+
+ expect(findAlert().exists()).toBe(false);
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/settings/group/components/maven_settings_spec.js b/spec/frontend/packages_and_registries/settings/group/components/maven_settings_spec.js
new file mode 100644
index 00000000000..2433c50ff24
--- /dev/null
+++ b/spec/frontend/packages_and_registries/settings/group/components/maven_settings_spec.js
@@ -0,0 +1,153 @@
+import { GlSprintf, GlToggle, GlFormGroup, GlFormInput } from '@gitlab/ui';
+import { shallowMount } from '@vue/test-utils';
+import component from '~/packages_and_registries/settings/group/components/maven_settings.vue';
+
+import {
+ MAVEN_TITLE,
+ MAVEN_SETTINGS_SUBTITLE,
+ MAVEN_DUPLICATES_ALLOWED_DISABLED,
+ MAVEN_DUPLICATES_ALLOWED_ENABLED,
+ MAVEN_SETTING_EXCEPTION_TITLE,
+ MAVEN_SETTINGS_EXCEPTION_LEGEND,
+} from '~/packages_and_registries/settings/group/constants';
+
+describe('Maven Settings', () => {
+ let wrapper;
+
+ const defaultProps = {
+ mavenDuplicatesAllowed: false,
+ mavenDuplicateExceptionRegex: 'foo',
+ };
+
+ const mountComponent = (propsData = defaultProps) => {
+ wrapper = shallowMount(component, {
+ propsData,
+ stubs: {
+ GlSprintf,
+ },
+ });
+ };
+
+ afterEach(() => {
+ wrapper.destroy();
+ wrapper = null;
+ });
+
+ const findTitle = () => wrapper.find('h5');
+ const findSubTitle = () => wrapper.find('p');
+ const findToggle = () => wrapper.find(GlToggle);
+ const findToggleLabel = () => wrapper.find('[data-testid="toggle-label"');
+
+ const findInputGroup = () => wrapper.find(GlFormGroup);
+ const findInput = () => wrapper.find(GlFormInput);
+
+ it('has a title', () => {
+ mountComponent();
+
+ expect(findTitle().exists()).toBe(true);
+ expect(findTitle().text()).toBe(MAVEN_TITLE);
+ });
+
+ it('has a subtitle', () => {
+ mountComponent();
+
+ expect(findSubTitle().exists()).toBe(true);
+ expect(findSubTitle().text()).toBe(MAVEN_SETTINGS_SUBTITLE);
+ });
+
+ it('has a toggle', () => {
+ mountComponent();
+
+ expect(findToggle().exists()).toBe(true);
+ expect(findToggle().props('value')).toBe(defaultProps.mavenDuplicatesAllowed);
+ });
+
+ it('toggle emits an update event', () => {
+ mountComponent();
+
+ findToggle().vm.$emit('change', false);
+
+ expect(wrapper.emitted('update')).toEqual([[{ mavenDuplicatesAllowed: false }]]);
+ });
+
+ describe('when the duplicates are disabled', () => {
+ it('the toggle has the disabled message', () => {
+ mountComponent();
+
+ expect(findToggleLabel().exists()).toBe(true);
+ expect(findToggleLabel().text()).toMatchInterpolatedText(MAVEN_DUPLICATES_ALLOWED_DISABLED);
+ });
+
+ it('shows a form group with an input field', () => {
+ mountComponent();
+
+ expect(findInputGroup().exists()).toBe(true);
+
+ expect(findInputGroup().attributes()).toMatchObject({
+ 'label-for': 'maven-duplicated-settings-regex-input',
+ label: MAVEN_SETTING_EXCEPTION_TITLE,
+ description: MAVEN_SETTINGS_EXCEPTION_LEGEND,
+ });
+ });
+
+ it('shows an input field', () => {
+ mountComponent();
+
+ expect(findInput().exists()).toBe(true);
+
+ expect(findInput().attributes()).toMatchObject({
+ id: 'maven-duplicated-settings-regex-input',
+ value: defaultProps.mavenDuplicateExceptionRegex,
+ });
+ });
+
+ it('input change event emits an update event', () => {
+ mountComponent();
+
+ findInput().vm.$emit('change', 'bar');
+
+ expect(wrapper.emitted('update')).toEqual([[{ mavenDuplicateExceptionRegex: 'bar' }]]);
+ });
+
+ describe('valid state', () => {
+ it('form group has correct props', () => {
+ mountComponent();
+
+ expect(findInputGroup().attributes()).toMatchObject({
+ state: 'true',
+ 'invalid-feedback': '',
+ });
+ });
+ });
+
+ describe('invalid state', () => {
+ it('form group has correct props', () => {
+ const propsWithError = {
+ ...defaultProps,
+ mavenDuplicateExceptionRegexError: 'some error string',
+ };
+
+ mountComponent(propsWithError);
+
+ expect(findInputGroup().attributes()).toMatchObject({
+ 'invalid-feedback': propsWithError.mavenDuplicateExceptionRegexError,
+ });
+ });
+ });
+ });
+
+ describe('when the duplicates are enabled', () => {
+ it('has the correct toggle label', () => {
+ mountComponent({ ...defaultProps, mavenDuplicatesAllowed: true });
+
+ expect(findToggleLabel().exists()).toBe(true);
+ expect(findToggleLabel().text()).toMatchInterpolatedText(MAVEN_DUPLICATES_ALLOWED_ENABLED);
+ });
+
+ it('hides the form input group', () => {
+ mountComponent({ ...defaultProps, mavenDuplicatesAllowed: true });
+
+ expect(findInputGroup().exists()).toBe(false);
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/settings/group/graphl/utils/cache_update_spec.js b/spec/frontend/packages_and_registries/settings/group/graphl/utils/cache_update_spec.js
new file mode 100644
index 00000000000..e1a46f97318
--- /dev/null
+++ b/spec/frontend/packages_and_registries/settings/group/graphl/utils/cache_update_spec.js
@@ -0,0 +1,56 @@
+import expirationPolicyQuery from '~/packages_and_registries/settings/group/graphql/queries/get_group_packages_settings.query.graphql';
+import { updateGroupPackageSettings } from '~/packages_and_registries/settings/group/graphql/utils/cache_update';
+
+describe('Package and Registries settings group cache updates', () => {
+ let client;
+
+ const payload = {
+ data: {
+ updateNamespacePackageSettings: {
+ packageSettings: {
+ mavenDuplicatesAllowed: false,
+ mavenDuplicateExceptionRegex: 'latest[master]something',
+ },
+ },
+ },
+ };
+
+ const cacheMock = {
+ group: {
+ packageSettings: {
+ mavenDuplicatesAllowed: true,
+ mavenDuplicateExceptionRegex: '',
+ },
+ },
+ };
+
+ const queryAndVariables = {
+ query: expirationPolicyQuery,
+ variables: { fullPath: 'foo' },
+ };
+
+ beforeEach(() => {
+ client = {
+ readQuery: jest.fn().mockReturnValue(cacheMock),
+ writeQuery: jest.fn(),
+ };
+ });
+ describe('updateGroupPackageSettings', () => {
+ it('calls readQuery', () => {
+ updateGroupPackageSettings('foo')(client, payload);
+ expect(client.readQuery).toHaveBeenCalledWith(queryAndVariables);
+ });
+
+ it('writes the correct result in the cache', () => {
+ updateGroupPackageSettings('foo')(client, payload);
+ expect(client.writeQuery).toHaveBeenCalledWith({
+ ...queryAndVariables,
+ data: {
+ group: {
+ ...payload.data.updateNamespacePackageSettings,
+ },
+ },
+ });
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/settings/group/graphl/utils/optimistic_responses_spec.js b/spec/frontend/packages_and_registries/settings/group/graphl/utils/optimistic_responses_spec.js
new file mode 100644
index 00000000000..a3c53d5768a
--- /dev/null
+++ b/spec/frontend/packages_and_registries/settings/group/graphl/utils/optimistic_responses_spec.js
@@ -0,0 +1,20 @@
+import { updateGroupPackagesSettingsOptimisticResponse } from '~/packages_and_registries/settings/group/graphql/utils/optimistic_responses';
+
+describe('Optimistic responses', () => {
+ describe('updateGroupPackagesSettingsOptimisticResponse', () => {
+ it('returns the correct structure', () => {
+ expect(updateGroupPackagesSettingsOptimisticResponse({ foo: 'bar' })).toMatchInlineSnapshot(`
+ Object {
+ "__typename": "Mutation",
+ "updateNamespacePackageSettings": Object {
+ "__typename": "UpdateNamespacePackageSettingsPayload",
+ "errors": Array [],
+ "packageSettings": Object {
+ "foo": "bar",
+ },
+ },
+ }
+ `);
+ });
+ });
+});
diff --git a/spec/frontend/packages_and_registries/settings/group/mock_data.js b/spec/frontend/packages_and_registries/settings/group/mock_data.js
new file mode 100644
index 00000000000..777c0898de0
--- /dev/null
+++ b/spec/frontend/packages_and_registries/settings/group/mock_data.js
@@ -0,0 +1,48 @@
+export const groupPackageSettingsMock = {
+ data: {
+ group: {
+ packageSettings: {
+ mavenDuplicatesAllowed: true,
+ mavenDuplicateExceptionRegex: '',
+ },
+ },
+ },
+};
+
+export const groupPackageSettingsMutationMock = (override) => ({
+ data: {
+ updateNamespacePackageSettings: {
+ packageSettings: {
+ mavenDuplicatesAllowed: true,
+ mavenDuplicateExceptionRegex: 'latest[master]something',
+ },
+ errors: [],
+ ...override,
+ },
+ },
+});
+
+export const groupPackageSettingsMutationErrorMock = {
+ errors: [
+ {
+ message:
+ 'Variable $input of type UpdateNamespacePackageSettingsInput! was provided invalid value for mavenDuplicateExceptionRegex (latest[master]somethingj)) is an invalid regexp: unexpected ): latest[master]somethingj)))',
+ locations: [{ line: 1, column: 41 }],
+ extensions: {
+ value: {
+ namespacePath: 'gitlab-org',
+ mavenDuplicateExceptionRegex: 'latest[master]something))',
+ },
+ problems: [
+ {
+ path: ['mavenDuplicateExceptionRegex'],
+ explanation:
+ 'latest[master]somethingj)) is an invalid regexp: unexpected ): latest[master]something))',
+ message:
+ 'latest[master]somethingj)) is an invalid regexp: unexpected ): latest[master]something))',
+ },
+ ],
+ },
+ },
+ ],
+};