[Followup] Fix defaultValue with enum options update in migration v2 integration test + polish (#15300)
# Introduction Followup of https://github.com/twentyhq/twenty/pull/15286 - Polish replacing sorting by conditional filter and push - Integration test
This commit is contained in:
+6
-4
@@ -156,10 +156,12 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct
|
||||
|
||||
let wasDefaultValueHandledByEnumUpdate = false;
|
||||
|
||||
const sortedUpdatesWithDefaultValuesUpdateLast = [...updates].sort(
|
||||
(a, b) =>
|
||||
+(a.property === 'defaultValue') - +(b.property === 'defaultValue'),
|
||||
);
|
||||
const sortedUpdatesWithDefaultValuesUpdateLast = hasDefaultValueUpdate
|
||||
? [
|
||||
...updates.filter((update) => update.property !== 'defaultValue'),
|
||||
defaultValueUpdate,
|
||||
]
|
||||
: updates;
|
||||
|
||||
for (const update of sortedUpdatesWithDefaultValuesUpdateLast) {
|
||||
if (isPropertyUpdate(update, 'name')) {
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`successful update default value option value and side effect on records should update existing records when default value option value is changed and not mutate when default option is changed 1`] = `
|
||||
[
|
||||
{
|
||||
"color": "green",
|
||||
"id": Any<String>,
|
||||
"label": "Option 1",
|
||||
"position": 0,
|
||||
"value": "OPTION_1_MODIFIED",
|
||||
},
|
||||
{
|
||||
"color": "blue",
|
||||
"id": Any<String>,
|
||||
"label": "Option 2",
|
||||
"position": 1,
|
||||
"value": "OPTION_2",
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"id": Any<String>,
|
||||
"label": "Option 3",
|
||||
"position": 2,
|
||||
"value": "OPTION_3",
|
||||
},
|
||||
]
|
||||
`;
|
||||
+237
@@ -0,0 +1,237 @@
|
||||
import { createOneOperation } from 'test/integration/graphql/utils/create-one-operation.util';
|
||||
import { findOneOperation } from 'test/integration/graphql/utils/find-one-operation.util';
|
||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||
import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util';
|
||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||
import { updateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/update-one-object-metadata.util';
|
||||
import { extractRecordIdsAndDatesAsExpectAny } from 'test/utils/extract-record-ids-and-dates-as-expect-any';
|
||||
import { FieldMetadataType } from 'twenty-shared/types';
|
||||
|
||||
const TEST_OBJECT_NAME_SINGULAR = 'testSelectObject';
|
||||
const TEST_OBJECT_NAME_PLURAL = 'testSelectObjects';
|
||||
|
||||
describe('successful update default value option value and side effect on records', () => {
|
||||
let createdObjectMetadataId: string;
|
||||
let createdFieldMetadataId: string;
|
||||
let firstOptionId: string;
|
||||
let secondOptionId: string;
|
||||
let thirdOptionId: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
const { data: objectData } = await createOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
nameSingular: TEST_OBJECT_NAME_SINGULAR,
|
||||
namePlural: TEST_OBJECT_NAME_PLURAL,
|
||||
labelSingular: 'Test Select Object',
|
||||
labelPlural: 'Test Select Objects',
|
||||
icon: 'IconTestPipe',
|
||||
isLabelSyncedWithName: false,
|
||||
},
|
||||
});
|
||||
|
||||
createdObjectMetadataId = objectData.createOneObject.id;
|
||||
|
||||
const { data: fieldData } = await createOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
objectMetadataId: createdObjectMetadataId,
|
||||
type: FieldMetadataType.SELECT,
|
||||
name: 'statusField',
|
||||
label: 'Status Field',
|
||||
isLabelSyncedWithName: false,
|
||||
options: [
|
||||
{
|
||||
label: 'Option 1',
|
||||
value: 'OPTION_1',
|
||||
color: 'green',
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
label: 'Option 2',
|
||||
value: 'OPTION_2',
|
||||
color: 'blue',
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
label: 'Option 3',
|
||||
value: 'OPTION_3',
|
||||
color: 'red',
|
||||
position: 2,
|
||||
},
|
||||
],
|
||||
defaultValue: "'OPTION_1'",
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
options
|
||||
defaultValue
|
||||
`,
|
||||
});
|
||||
|
||||
createdFieldMetadataId = fieldData.createOneField.id;
|
||||
const options = fieldData.createOneField.options;
|
||||
|
||||
if (!options) {
|
||||
throw new Error('Options should be defined');
|
||||
}
|
||||
|
||||
const option1 = options.find((opt) => opt.value === 'OPTION_1');
|
||||
const option2 = options.find((opt) => opt.value === 'OPTION_2');
|
||||
const option3 = options.find((opt) => opt.value === 'OPTION_3');
|
||||
|
||||
if (!option1?.id || !option2?.id || !option3?.id) {
|
||||
throw new Error('All options and their IDs should be defined');
|
||||
}
|
||||
|
||||
firstOptionId = option1.id;
|
||||
secondOptionId = option2.id;
|
||||
thirdOptionId = option3.id;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await updateOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
idToUpdate: createdObjectMetadataId,
|
||||
updatePayload: {
|
||||
isActive: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
await deleteOneObjectMetadata({
|
||||
expectToFail: false,
|
||||
input: { idToDelete: createdObjectMetadataId },
|
||||
});
|
||||
});
|
||||
|
||||
it('should update existing records when default value option value is changed and not mutate when default option is changed', async () => {
|
||||
const { data: createFirstRecordData } = await createOneOperation({
|
||||
expectToFail: false,
|
||||
objectMetadataSingularName: TEST_OBJECT_NAME_SINGULAR,
|
||||
input: {},
|
||||
gqlFields: `
|
||||
id
|
||||
statusField
|
||||
`,
|
||||
});
|
||||
|
||||
const firstRecordId = createFirstRecordData.createOneResponse.id;
|
||||
|
||||
expect(createFirstRecordData.createOneResponse.statusField).toBe(
|
||||
'OPTION_1',
|
||||
);
|
||||
|
||||
const { data: updateOptionValueData } = await updateOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
idToUpdate: createdFieldMetadataId,
|
||||
updatePayload: {
|
||||
defaultValue: "'OPTION_1_MODIFIED'",
|
||||
options: [
|
||||
{
|
||||
id: firstOptionId,
|
||||
label: 'Option 1',
|
||||
value: 'OPTION_1_MODIFIED',
|
||||
color: 'green',
|
||||
position: 0,
|
||||
},
|
||||
{
|
||||
id: secondOptionId,
|
||||
label: 'Option 2',
|
||||
value: 'OPTION_2',
|
||||
color: 'blue',
|
||||
position: 1,
|
||||
},
|
||||
{
|
||||
id: thirdOptionId,
|
||||
label: 'Option 3',
|
||||
value: 'OPTION_3',
|
||||
color: 'red',
|
||||
position: 2,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
options
|
||||
defaultValue
|
||||
`,
|
||||
});
|
||||
|
||||
expect(updateOptionValueData.updateOneField.defaultValue).toBe(
|
||||
"'OPTION_1_MODIFIED'",
|
||||
);
|
||||
|
||||
const { data: queryFirstRecordAfterOptionValueUpdate } =
|
||||
await findOneOperation({
|
||||
expectToFail: false,
|
||||
objectMetadataSingularName: TEST_OBJECT_NAME_SINGULAR,
|
||||
gqlFields: `
|
||||
id
|
||||
statusField
|
||||
`,
|
||||
filter: { id: { eq: firstRecordId } },
|
||||
});
|
||||
|
||||
expect(
|
||||
queryFirstRecordAfterOptionValueUpdate.findResponse.statusField,
|
||||
).toBe('OPTION_1_MODIFIED');
|
||||
|
||||
const { data: updateDefaultOptionData } = await updateOneFieldMetadata({
|
||||
expectToFail: false,
|
||||
input: {
|
||||
idToUpdate: createdFieldMetadataId,
|
||||
updatePayload: {
|
||||
defaultValue: "'OPTION_2'",
|
||||
},
|
||||
},
|
||||
gqlFields: `
|
||||
id
|
||||
options
|
||||
defaultValue
|
||||
`,
|
||||
});
|
||||
|
||||
expect(updateDefaultOptionData.updateOneField.defaultValue).toBe(
|
||||
"'OPTION_2'",
|
||||
);
|
||||
expect(updateDefaultOptionData.updateOneField.options).toMatchSnapshot(
|
||||
extractRecordIdsAndDatesAsExpectAny(
|
||||
updateDefaultOptionData.updateOneField.options as any,
|
||||
),
|
||||
);
|
||||
|
||||
const { data: createSecondRecordData } = await createOneOperation({
|
||||
expectToFail: false,
|
||||
objectMetadataSingularName: TEST_OBJECT_NAME_SINGULAR,
|
||||
input: {},
|
||||
gqlFields: `
|
||||
id
|
||||
statusField
|
||||
`,
|
||||
});
|
||||
|
||||
expect(createSecondRecordData.createOneResponse.statusField).toBe(
|
||||
'OPTION_2',
|
||||
);
|
||||
|
||||
const { data: queryFirstRecordAfterDefaultChange } = await findOneOperation(
|
||||
{
|
||||
expectToFail: false,
|
||||
objectMetadataSingularName: TEST_OBJECT_NAME_SINGULAR,
|
||||
gqlFields: `
|
||||
id
|
||||
statusField
|
||||
`,
|
||||
filter: { id: { eq: firstRecordId } },
|
||||
},
|
||||
);
|
||||
|
||||
expect(queryFirstRecordAfterDefaultChange.findResponse.statusField).toBe(
|
||||
'OPTION_1_MODIFIED',
|
||||
);
|
||||
});
|
||||
});
|
||||
+8
-1
@@ -1,5 +1,5 @@
|
||||
import { isDefined } from 'class-validator';
|
||||
import { type Response } from 'supertest';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
type WarnIfErrorButNotExpectedToFailInput = {
|
||||
response: Response;
|
||||
@@ -11,6 +11,13 @@ export const warnIfErrorButNotExpectedToFail = ({
|
||||
errorMessage,
|
||||
}: WarnIfErrorButNotExpectedToFailInput) => {
|
||||
if (isDefined(response.body.errors) && response.body.errors.length > 0) {
|
||||
if (
|
||||
isDefined(process.env.LOG_LEVELS) &&
|
||||
process.env.LOG_LEVELS.includes('debug')
|
||||
) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(JSON.stringify(response.body.errors, null, 2));
|
||||
}
|
||||
expect(response.body.errors).toEqual(errorMessage);
|
||||
}
|
||||
expect(response.body.data).toBeDefined();
|
||||
|
||||
Reference in New Issue
Block a user