# Introduction Diff description: ~500 tests and +500 additions close https://github.com/twentyhq/core-team-issues/issues/731 ## What has been done here In a nutshell on a field metadata type ( `SELECT MULTI_SELECT` ) update, we will be browsing all `ViewFilters` in a post hook searching for some referencing related updated `fieldMetadata` select. In order to update or delete the `viewFilter` depending on the associated mutations. ## How to test: - Add FieldMetadata `SELECT | MULTI_SELECT` to an existing or a new `objectMetadata` - Create a filtered view on created `fieldMetadata` with any options you would like - Remove some options ( in the best of the world some that are selected by the filter ) from the `fieldMetadata` settings page - Go back to the filtered view, removed or updated options should have been hydrated in the `displayValue` and the filtered data should make sense ## All filtered options are deleted edge case If an update implies that a viewFilter does not have any existing related options anymore, then we remove the viewFilter ## Testing ```sh PASS test/integration/metadata/suites/field-metadata/update-one-field-metadata-related-record.integration-spec.ts (27 s) update-one-field-metadata-related-record SELECT ✓ should delete related view filter if all select field options got deleted (2799 ms) ✓ should update related multi selected options view filter (1244 ms) ✓ should update related solo selected option view filter (1235 ms) ✓ should handle partial deletion of selected options in view filter (1210 ms) ✓ should handle reordering of options while maintaining view filter values (1487 ms) ✓ should handle no changes update of options while maintaining existing view filter values (1174 ms) ✓ should handle adding new options while maintaining existing view filter (1174 ms) ✓ should update display value with options label if less than 3 options are selected (1249 ms) ✓ should throw error if view filter value is not a stringified JSON array (1300 ms) MULTI_SELECT ✓ should delete related view filter if all select field options got deleted (1127 ms) ✓ should update related multi selected options view filter (1215 ms) ✓ should update related solo selected option view filter (1404 ms) ✓ should handle partial deletion of selected options in view filter (1936 ms) ✓ should handle reordering of options while maintaining view filter values (1261 ms) ✓ should handle no changes update of options while maintaining existing view filter values (1831 ms) ✓ should handle adding new options while maintaining existing view filter (1610 ms) ✓ should update display value with options label if less than 3 options are selected (1889 ms) ✓ should throw error if view filter value is not a stringified JSON array (1365 ms) Test Suites: 1 passed, 1 total Tests: 18 passed, 18 total Snapshots: 18 passed, 18 total Time: 27.039 s ``` ## Out of scope - We should handle ViewFilter validation when extracting its definition from the metadata https://github.com/twentyhq/core-team-issues/issues/1009 ## Concerns - Are we able through the api to update an RATING fieldMetadata ? ( if yes than that's an issue and we should handle RATING the same way than for SELECT and MULTI_SELECT ) - It's not possible to group a view from a MULTI_SELECT field The above points create a double nor a triple "lecture" to the post hook effect: - ViewGroup -> only SELECT - VIewFilter -> only SELECT || MULTI_SELECT - Rating nothing I think we should determine the scope of all of that --------- Co-authored-by: Charles Bochet <charles@twenty.com>
129 lines
4.0 KiB
TypeScript
129 lines
4.0 KiB
TypeScript
import {
|
|
UPDATE_CREATE_ONE_FIELD_METADATA_SELECT_TEST_CASES,
|
|
UpdateCreateFieldMetadataSelectTestCase,
|
|
} from 'test/integration/metadata/suites/field-metadata/update-create-one-field-metadata-select-tests-cases';
|
|
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
|
import {
|
|
LISTING_NAME_PLURAL,
|
|
LISTING_NAME_SINGULAR,
|
|
} from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant';
|
|
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 { FieldMetadataType } from 'twenty-shared/types';
|
|
import { isDefined } from 'twenty-shared/utils';
|
|
|
|
import {
|
|
FieldMetadataComplexOption,
|
|
FieldMetadataDefaultOption,
|
|
} from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
|
|
|
const { failingTestCases, successfulTestCases } =
|
|
UPDATE_CREATE_ONE_FIELD_METADATA_SELECT_TEST_CASES;
|
|
|
|
describe('Field metadata select creation tests group', () => {
|
|
let createdObjectMetadataId: string;
|
|
|
|
beforeEach(async () => {
|
|
const { data } = await createOneObjectMetadata({
|
|
input: {
|
|
labelSingular: LISTING_NAME_SINGULAR,
|
|
labelPlural: LISTING_NAME_PLURAL,
|
|
nameSingular: LISTING_NAME_SINGULAR,
|
|
namePlural: LISTING_NAME_PLURAL,
|
|
icon: 'IconBuildingSkyscraper',
|
|
isLabelSyncedWithName: false,
|
|
},
|
|
});
|
|
|
|
createdObjectMetadataId = data.createOneObject.id;
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await deleteOneObjectMetadata({
|
|
input: { idToDelete: createdObjectMetadataId },
|
|
});
|
|
});
|
|
|
|
test.each(successfulTestCases)(
|
|
'Create $title',
|
|
async ({ context: { input, expectedOptions } }) => {
|
|
const { data, errors } = await createOneFieldMetadata({
|
|
input: {
|
|
objectMetadataId: createdObjectMetadataId,
|
|
type: FieldMetadataType.SELECT,
|
|
name: 'testField',
|
|
label: 'Test Field',
|
|
isLabelSyncedWithName: false,
|
|
...input,
|
|
},
|
|
gqlFields: `
|
|
id
|
|
options
|
|
defaultValue
|
|
`,
|
|
});
|
|
|
|
expect(data).not.toBeNull();
|
|
expect(data.createOneField).toBeDefined();
|
|
const createdOptions:
|
|
| FieldMetadataDefaultOption[]
|
|
| FieldMetadataComplexOption[] = data.createOneField.options;
|
|
|
|
const optionsToCompare = expectedOptions ?? input.options;
|
|
|
|
expect(errors).toBeUndefined();
|
|
expect(createdOptions.length).toBe(optionsToCompare.length);
|
|
createdOptions.forEach((option) => expect(option.id).toBeDefined());
|
|
expect(createdOptions).toMatchObject(optionsToCompare);
|
|
|
|
if (isDefined(input.defaultValue)) {
|
|
expect(data.createOneField.defaultValue).toEqual(input.defaultValue);
|
|
}
|
|
},
|
|
);
|
|
|
|
const createSpecificFailingTestCases: UpdateCreateFieldMetadataSelectTestCase[] =
|
|
[
|
|
{
|
|
title: 'should fail with null options',
|
|
context: {
|
|
input: {
|
|
options: null as unknown as FieldMetadataComplexOption[],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
title: 'should fail with undefined options',
|
|
context: {
|
|
input: {
|
|
options: undefined as unknown as FieldMetadataComplexOption[],
|
|
},
|
|
},
|
|
},
|
|
];
|
|
|
|
test.each([...failingTestCases, ...createSpecificFailingTestCases])(
|
|
'Create $title',
|
|
async ({ context: { input } }) => {
|
|
const { data, errors } = await createOneFieldMetadata({
|
|
input: {
|
|
objectMetadataId: createdObjectMetadataId,
|
|
type: FieldMetadataType.SELECT,
|
|
name: 'testField',
|
|
label: 'Test Field',
|
|
isLabelSyncedWithName: false,
|
|
...input,
|
|
},
|
|
gqlFields: `
|
|
id
|
|
options
|
|
`,
|
|
});
|
|
|
|
expect(data).toBeNull();
|
|
expect(errors).toBeDefined();
|
|
expect(errors).toMatchSnapshot();
|
|
},
|
|
);
|
|
});
|