Commit 150e6171 authored by Nicolò Maria Mezzopera's avatar Nicolò Maria Mezzopera

Merge branch 'fix-the-mysterious-case-of-the-disappearing-form' into 'master'

Fixed form disappearing when name is empty and general code improvements

See merge request gitlab-org/gitlab!53371
parents 5dd81ed6 36338c74
...@@ -2,7 +2,8 @@ ...@@ -2,7 +2,8 @@
import { visitUrl } from '~/lib/utils/url_utility'; import { visitUrl } from '~/lib/utils/url_utility';
import * as Sentry from '~/sentry/wrapper'; import * as Sentry from '~/sentry/wrapper';
import createComplianceFrameworkMutation from '../graphql/queries/create_compliance_framework.mutation.graphql'; import createComplianceFrameworkMutation from '../graphql/queries/create_compliance_framework.mutation.graphql';
import { initialiseFormData, SAVE_ERROR } from '../constants'; import { SAVE_ERROR } from '../constants';
import { initialiseFormData } from '../utils';
import SharedForm from './shared_form.vue'; import SharedForm from './shared_form.vue';
import FormStatus from './form_status.vue'; import FormStatus from './form_status.vue';
......
...@@ -5,7 +5,8 @@ import { convertToGraphQLId } from '~/graphql_shared/utils'; ...@@ -5,7 +5,8 @@ import { convertToGraphQLId } from '~/graphql_shared/utils';
import getComplianceFrameworkQuery from '../graphql/queries/get_compliance_framework.query.graphql'; import getComplianceFrameworkQuery from '../graphql/queries/get_compliance_framework.query.graphql';
import updateComplianceFrameworkMutation from '../graphql/queries/update_compliance_framework.mutation.graphql'; import updateComplianceFrameworkMutation from '../graphql/queries/update_compliance_framework.mutation.graphql';
import { initialiseFormData, FETCH_ERROR, SAVE_ERROR } from '../constants'; import { FETCH_ERROR, SAVE_ERROR } from '../constants';
import { initialiseFormData } from '../utils';
import SharedForm from './shared_form.vue'; import SharedForm from './shared_form.vue';
import FormStatus from './form_status.vue'; import FormStatus from './form_status.vue';
...@@ -35,7 +36,8 @@ export default { ...@@ -35,7 +36,8 @@ export default {
}, },
data() { data() {
return { return {
errorMessage: '', initErrorMessage: '',
saveErrorMessage: '',
formData: initialiseFormData(), formData: initialiseFormData(),
saving: false, saving: false,
}; };
...@@ -53,7 +55,7 @@ export default { ...@@ -53,7 +55,7 @@ export default {
this.formData = this.extractComplianceFramework(data); this.formData = this.extractComplianceFramework(data);
}, },
error(error) { error(error) {
this.setError(error, FETCH_ERROR); this.setInitError(error, FETCH_ERROR);
}, },
}, },
}, },
...@@ -64,8 +66,13 @@ export default { ...@@ -64,8 +66,13 @@ export default {
isLoading() { isLoading() {
return this.$apollo.loading || this.saving; return this.$apollo.loading || this.saving;
}, },
hasFormData() { showForm() {
return Boolean(this.formData?.name); return (
Object.values(this.formData).filter((d) => d !== null).length > 0 && !this.initErrorMessage
);
},
errorMessage() {
return this.initErrorMessage || this.saveErrorMessage;
}, },
}, },
methods: { methods: {
...@@ -73,7 +80,7 @@ export default { ...@@ -73,7 +80,7 @@ export default {
const complianceFrameworks = data.namespace?.complianceFrameworks?.nodes || []; const complianceFrameworks = data.namespace?.complianceFrameworks?.nodes || [];
if (!complianceFrameworks.length) { if (!complianceFrameworks.length) {
this.setError(new Error(FETCH_ERROR), FETCH_ERROR); this.setInitError(new Error(FETCH_ERROR), FETCH_ERROR);
return initialiseFormData(); return initialiseFormData();
} }
...@@ -86,13 +93,17 @@ export default { ...@@ -86,13 +93,17 @@ export default {
color, color,
}; };
}, },
setError(error, userFriendlyText) { setInitError(error, userFriendlyText) {
this.errorMessage = userFriendlyText; this.initErrorMessage = userFriendlyText;
Sentry.captureException(error);
},
setSavingError(error, userFriendlyText) {
this.saveErrorMessage = userFriendlyText;
Sentry.captureException(error); Sentry.captureException(error);
}, },
async onSubmit() { async onSubmit() {
this.saving = true; this.saving = true;
this.errorMessage = ''; this.saveErrorMessage = '';
try { try {
const { name, description, color } = this.formData; const { name, description, color } = this.formData;
...@@ -113,13 +124,13 @@ export default { ...@@ -113,13 +124,13 @@ export default {
const [error] = data?.updateComplianceFramework?.errors || []; const [error] = data?.updateComplianceFramework?.errors || [];
if (error) { if (error) {
this.setError(new Error(error), error); this.setSavingError(new Error(error), error);
} else { } else {
this.saving = false; this.saving = false;
visitUrl(this.groupEditPath); visitUrl(this.groupEditPath);
} }
} catch (e) { } catch (e) {
this.setError(e, SAVE_ERROR); this.setSavingError(e, SAVE_ERROR);
} }
this.saving = false; this.saving = false;
...@@ -130,7 +141,7 @@ export default { ...@@ -130,7 +141,7 @@ export default {
<template> <template>
<form-status :loading="isLoading" :error="errorMessage"> <form-status :loading="isLoading" :error="errorMessage">
<shared-form <shared-form
v-if="hasFormData" v-if="showForm"
:group-edit-path="groupEditPath" :group-edit-path="groupEditPath"
:name.sync="formData.name" :name.sync="formData.name"
:description.sync="formData.description" :description.sync="formData.description"
......
...@@ -103,7 +103,12 @@ export default { ...@@ -103,7 +103,12 @@ export default {
</gl-sprintf> </gl-sprintf>
</template> </template>
<gl-form-input :value="name" data-testid="name-input" @input="$emit('update:name', $event)" /> <gl-form-input
:value="name"
:state="isValidName"
data-testid="name-input"
@input="$emit('update:name', $event)"
/>
</gl-form-group> </gl-form-group>
<gl-form-group <gl-form-group
...@@ -114,6 +119,7 @@ export default { ...@@ -114,6 +119,7 @@ export default {
> >
<gl-form-input <gl-form-input
:value="description" :value="description"
:state="isValidDescription"
data-testid="description-input" data-testid="description-input"
@input="$emit('update:description', $event)" @input="$emit('update:description', $event)"
/> />
......
import { s__ } from '~/locale'; import { s__ } from '~/locale';
export const initialiseFormData = () => ({
name: null,
description: null,
color: null,
});
export const FETCH_ERROR = s__( export const FETCH_ERROR = s__(
'ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page', 'ComplianceFrameworks|Error fetching compliance frameworks data. Please refresh the page',
); );
......
export const initialiseFormData = () => ({
name: null,
description: null,
pipelineConfigurationFullPath: null,
color: null,
});
...@@ -21,14 +21,15 @@ jest.mock('~/lib/utils/url_utility'); ...@@ -21,14 +21,15 @@ jest.mock('~/lib/utils/url_utility');
describe('CreateForm', () => { describe('CreateForm', () => {
let wrapper; let wrapper;
const sentryError = new Error('Network error');
const sentrySaveError = new Error('Invalid values given');
const propsData = { const propsData = {
groupPath: 'group-1', groupPath: 'group-1',
groupEditPath: 'group-1/edit', groupEditPath: 'group-1/edit',
scopedLabelsHelpPath: 'help/scoped-labels',
}; };
const sentryError = new Error('Network error');
const sentrySaveError = new Error('Invalid values given');
const create = jest.fn().mockResolvedValue(validCreateResponse); const create = jest.fn().mockResolvedValue(validCreateResponse);
const createWithNetworkErrors = jest.fn().mockRejectedValue(sentryError); const createWithNetworkErrors = jest.fn().mockRejectedValue(sentryError);
const createWithErrors = jest.fn().mockResolvedValue(errorCreateResponse); const createWithErrors = jest.fn().mockResolvedValue(errorCreateResponse);
......
...@@ -28,16 +28,16 @@ jest.mock('~/lib/utils/url_utility'); ...@@ -28,16 +28,16 @@ jest.mock('~/lib/utils/url_utility');
describe('EditForm', () => { describe('EditForm', () => {
let wrapper; let wrapper;
const sentryError = new Error('Network error');
const sentrySaveError = new Error('Invalid values given');
const propsData = { const propsData = {
graphqlFieldName: 'ComplianceManagement::Framework', graphqlFieldName: 'ComplianceManagement::Framework',
groupPath: 'group-1',
groupEditPath: 'group-1/edit', groupEditPath: 'group-1/edit',
groupPath: 'group-1',
id: '1', id: '1',
scopedLabelsHelpPath: 'help/scoped-labels',
}; };
const sentryError = new Error('Network error');
const sentrySaveError = new Error('Invalid values given');
const fetchOne = jest.fn().mockResolvedValue(validFetchOneResponse); const fetchOne = jest.fn().mockResolvedValue(validFetchOneResponse);
const fetchEmpty = jest.fn().mockResolvedValue(emptyFetchResponse); const fetchEmpty = jest.fn().mockResolvedValue(emptyFetchResponse);
const fetchLoading = jest.fn().mockResolvedValue(new Promise(() => {})); const fetchLoading = jest.fn().mockResolvedValue(new Promise(() => {}));
...@@ -96,7 +96,7 @@ describe('EditForm', () => { ...@@ -96,7 +96,7 @@ describe('EditForm', () => {
await waitForPromises(); await waitForPromises();
expect(fetchOne).toHaveBeenCalledTimes(1); expect(fetchOne).toHaveBeenCalledTimes(1);
expect(findForm().props()).toMatchObject({ expect(findForm().props()).toStrictEqual({
name: frameworkFoundResponse.name, name: frameworkFoundResponse.name,
description: frameworkFoundResponse.description, description: frameworkFoundResponse.description,
color: frameworkFoundResponse.color, color: frameworkFoundResponse.color,
......
...@@ -28,6 +28,15 @@ describe('SharedForm', () => { ...@@ -28,6 +28,15 @@ describe('SharedForm', () => {
}, },
stubs: { stubs: {
GlFormGroup, GlFormGroup,
GlFormInput: {
name: 'gl-form-input-stub',
props: ['state'],
template: `
<div>
<slot></slot>
</div>
`,
},
GlSprintf, GlSprintf,
}, },
}); });
...@@ -67,10 +76,11 @@ describe('SharedForm', () => { ...@@ -67,10 +76,11 @@ describe('SharedForm', () => {
${null} | ${null} ${null} | ${null}
${''} | ${false} ${''} | ${false}
${'foobar'} | ${true} ${'foobar'} | ${true}
`('sets the correct state to the name input group', ({ name, validity }) => { `('sets the correct state to the name input and group', ({ name, validity }) => {
wrapper = createComponent({ name }); wrapper = createComponent({ name });
expect(findNameGroup().props('state')).toBe(validity); expect(findNameGroup().props('state')).toBe(validity);
expect(findNameInput().props('state')).toBe(validity);
}); });
it.each` it.each`
...@@ -78,10 +88,11 @@ describe('SharedForm', () => { ...@@ -78,10 +88,11 @@ describe('SharedForm', () => {
${null} | ${null} ${null} | ${null}
${''} | ${false} ${''} | ${false}
${'foobar'} | ${true} ${'foobar'} | ${true}
`('sets the correct state to the description input group', ({ description, validity }) => { `('sets the correct state to the description input and group', ({ description, validity }) => {
wrapper = createComponent({ description }); wrapper = createComponent({ description });
expect(findDescriptionGroup().props('state')).toBe(validity); expect(findDescriptionGroup().props('state')).toBe(validity);
expect(findDescriptionInput().props('state')).toBe(validity);
}); });
it.each` it.each`
......
...@@ -48,7 +48,7 @@ describe('createComplianceFrameworksFormApp', () => { ...@@ -48,7 +48,7 @@ describe('createComplianceFrameworksFormApp', () => {
}); });
it('parses and passes props', () => { it('parses and passes props', () => {
expect(findFormApp(CreateForm).props()).toMatchObject({ expect(findFormApp(CreateForm).props()).toStrictEqual({
groupEditPath, groupEditPath,
groupPath, groupPath,
}); });
...@@ -61,7 +61,8 @@ describe('createComplianceFrameworksFormApp', () => { ...@@ -61,7 +61,8 @@ describe('createComplianceFrameworksFormApp', () => {
}); });
it('parses and passes props', () => { it('parses and passes props', () => {
expect(findFormApp(EditForm).props()).toMatchObject({ expect(findFormApp(EditForm).props()).toStrictEqual({
graphqlFieldName,
groupEditPath, groupEditPath,
groupPath, groupPath,
id: testId, id: testId,
......
import * as Utils from 'ee/groups/settings/compliance_frameworks/utils';
describe('Utils', () => {
describe('initialiseFormData', () => {
it('returns the initial form data object', () => {
expect(Utils.initialiseFormData()).toStrictEqual({
name: null,
description: null,
pipelineConfigurationFullPath: null,
color: null,
});
});
});
});
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment