From 0780380fe5ef0ee25375749a708f28318a3462f3 Mon Sep 17 00:00:00 2001 From: Paul Rastoin <45004772+prastoin@users.noreply.github.com> Date: Thu, 23 Oct 2025 16:26:46 +0200 Subject: [PATCH] Fix composite field update v2 (#15290) # Introduction Fixing composite field update by computing field column type for each of its properties instead of globally ## Coverage Added integration tests for each composite field on both successful `create` and `update` ```ts Test Suites: 17 passed, 17 total Tests: 104 passed, 104 total Snapshots: 14 passed, 14 total Time: 135.431 s, estimated 143 s ``` ## Conlusion Related to https://github.com/twentyhq/core-team-issues/issues/1753 --- .../update-field-action-handler.service.ts | 16 +- ...e-field-metadata.integration-spec.ts.snap} | 28 +-- ...e-phone-field-metadata.integration-spec.ts | 213 +++++++++++++++++ ...e-actor-field-metadata.integration-spec.ts | 142 +++++++++++ ...address-field-metadata.integration-spec.ts | 226 ++++++++++++++++++ ...urrency-field-metadata.integration-spec.ts | 167 +++++++++++++ ...-emails-field-metadata.integration-spec.ts | 168 +++++++++++++ ...ll-name-field-metadata.integration-spec.ts | 154 ++++++++++++ ...e-links-field-metadata.integration-spec.ts | 171 +++++++++++++ ...-phone-field-metadata.integration-spec.ts} | 140 +---------- ...text-v2-field-metadata.integration-spec.ts | 154 ++++++++++++ ...e-actor-field-metadata.integration-spec.ts | 156 ++++++++++++ ...address-field-metadata.integration-spec.ts | 215 +++++++++++++++++ ...urrency-field-metadata.integration-spec.ts | 176 ++++++++++++++ ...-emails-field-metadata.integration-spec.ts | 178 ++++++++++++++ ...ll-name-field-metadata.integration-spec.ts | 165 +++++++++++++ ...e-links-field-metadata.integration-spec.ts | 181 ++++++++++++++ ...-phones-field-metadata.integration-spec.ts | 184 ++++++++++++++ ...text-v2-field-metadata.integration-spec.ts | 165 +++++++++++++ ...enum-field-metadata-v2.integration-spec.ts | 4 +- .../rest-api-core-update.integration-spec.ts | 8 +- 21 files changed, 2846 insertions(+), 165 deletions(-) rename packages/twenty-server/test/integration/metadata/suites/field-metadata/{phone/__snapshots__/create-one-field-metadata-phone.integration-spec.ts.snap => composite/__snapshots__/failing-create-phone-field-metadata.integration-spec.ts.snap} (66%) create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/failing-create-phone-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-actor-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-address-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-currency-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-emails-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-full-name-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-links-field-metadata.integration-spec.ts rename packages/twenty-server/test/integration/metadata/suites/field-metadata/{phone/create-one-field-metadata-phone.integration-spec.ts => composite/successful-create-phone-field-metadata.integration-spec.ts} (72%) create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-rich-text-v2-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-actor-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-address-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-currency-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-emails-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-full-name-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-links-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-phones-field-metadata.integration-spec.ts create mode 100644 packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-rich-text-v2-field-metadata.integration-spec.ts diff --git a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/field/services/update-field-action-handler.service.ts b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/field/services/update-field-action-handler.service.ts index d2306d9f49..e317795d23 100644 --- a/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/field/services/update-field-action-handler.service.ts +++ b/packages/twenty-server/src/engine/workspace-manager/workspace-migration-v2/workspace-migration-runner-v2/action-handlers/field/services/update-field-action-handler.service.ts @@ -301,14 +301,14 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct tableName, update, }: UpdateFieldPropertyUpdateHandlerArgs<'defaultValue'>) { - const columnType = fieldMetadataTypeToColumnType( - flatFieldMetadata.type, - ) as ColumnType; - if (isCompositeFieldMetadataType(flatFieldMetadata.type)) { const compositeType = getCompositeTypeOrThrow(flatFieldMetadata.type); for (const property of compositeType.properties) { + const columnType = fieldMetadataTypeToColumnType( + property.type, + ) as ColumnType; + if (isMorphOrRelationFieldMetadataType(property.type)) { throw new WorkspaceMigrationRunnerException( 'Relation field metadata in composite type is not supported yet', @@ -331,7 +331,7 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct defaultValue: compositeDefaultValue, }); - return await this.workspaceSchemaManagerService.columnManager.alterColumnDefault( + await this.workspaceSchemaManagerService.columnManager.alterColumnDefault( { queryRunner, schemaName, @@ -341,8 +341,14 @@ export class UpdateFieldActionHandlerService extends WorkspaceMigrationRunnerAct }, ); } + + return; } + const columnType = fieldMetadataTypeToColumnType( + flatFieldMetadata.type, + ) as ColumnType; + const serializedNewDefaultValue = serializeDefaultValueV2({ columnName: flatFieldMetadata.name, schemaName, diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/phone/__snapshots__/create-one-field-metadata-phone.integration-spec.ts.snap b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/__snapshots__/failing-create-phone-field-metadata.integration-spec.ts.snap similarity index 66% rename from packages/twenty-server/test/integration/metadata/suites/field-metadata/phone/__snapshots__/create-one-field-metadata-phone.integration-spec.ts.snap rename to packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/__snapshots__/failing-create-phone-field-metadata.integration-spec.ts.snap index e507f7d689..c15688e559 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/phone/__snapshots__/create-one-field-metadata-phone.integration-spec.ts.snap +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/__snapshots__/failing-create-phone-field-metadata.integration-spec.ts.snap @@ -1,6 +1,6 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`Phone field metadata tests suite It should fail to create primary phone field with conflicting country code and calling code 1`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with conflicting country code and calling code 1`] = ` [ { "extensions": { @@ -14,7 +14,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with conflicting country code and calling code 2`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with conflicting country code and calling code 2`] = ` [ { "extensions": { @@ -28,7 +28,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with conflicting phone number calling code 1`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with conflicting phone number calling code 1`] = ` [ { "extensions": { @@ -42,7 +42,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with conflicting phone number calling code 2`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with conflicting phone number calling code 2`] = ` [ { "extensions": { @@ -56,7 +56,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with conflicting phone number country code 1`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with conflicting phone number country code 1`] = ` [ { "extensions": { @@ -70,7 +70,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with conflicting phone number country code 2`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with conflicting phone number country code 2`] = ` [ { "extensions": { @@ -84,7 +84,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with invalid calling code 1`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with invalid calling code 1`] = ` [ { "extensions": { @@ -98,7 +98,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with invalid calling code 2`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with invalid calling code 2`] = ` [ { "extensions": { @@ -112,7 +112,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with invalid country code 1`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with invalid country code 1`] = ` [ { "extensions": { @@ -126,7 +126,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with invalid country code 2`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with invalid country code 2`] = ` [ { "extensions": { @@ -140,7 +140,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with invalid phone number format 1`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with invalid phone number format 1`] = ` [ { "extensions": { @@ -154,7 +154,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field with invalid phone number format 2`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field with invalid phone number format 2`] = ` [ { "extensions": { @@ -168,7 +168,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field without country or calling code at all 1`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field without country or calling code at all 1`] = ` [ { "extensions": { @@ -182,7 +182,7 @@ exports[`Phone field metadata tests suite It should fail to create primary phone ] `; -exports[`Phone field metadata tests suite It should fail to create primary phone field without country or calling code at all 2`] = ` +exports[`failing create phone field metadata test suite it should fail to create primary phone field without country or calling code at all 2`] = ` [ { "extensions": { diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/failing-create-phone-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/failing-create-phone-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..f25daa373c --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/failing-create-phone-field-metadata.integration-spec.ts @@ -0,0 +1,213 @@ +import { faker } from '@faker-js/faker'; +import { type CountryCode } from 'libphonenumber-js'; +import { createOneOperation } from 'test/integration/graphql/utils/create-one-operation.util'; +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-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 { type EachTestingContext } from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { + type AdditionalPhoneMetadata, + type PhonesMetadata, +} from 'src/engine/metadata-modules/field-metadata/composite-types/phones.composite-type'; + +const FIELD_NAME = 'phonenumber'; + +type TestCaseInputAndExpected = Partial< + Omit +> & { + additionalPhones?: Array> | null; +}; + +type CreatePhoneFieldMetadataTestCase = { + input: TestCaseInputAndExpected; + expected?: TestCaseInputAndExpected; +}; + +const FAILING_TEST_INPUTS: { input: Partial; label: string }[] = + [ + { + label: 'phone field without country or calling code at all', + input: { + primaryPhoneNumber: '123456789', + additionalPhones: [], + }, + }, + { + label: 'phone field with invalid country code', + input: { + primaryPhoneNumber: '123456789', + primaryPhoneCallingCode: '+33', + primaryPhoneCountryCode: 'XX' as CountryCode, + additionalPhones: [], + }, + }, + { + label: 'phone field with invalid calling code', + input: { + primaryPhoneNumber: '123456789', + primaryPhoneCallingCode: '+999', + primaryPhoneCountryCode: 'FR', + additionalPhones: [], + }, + }, + { + label: 'phone field with conflicting country code and calling code', + input: { + primaryPhoneNumber: '123456789', + primaryPhoneCallingCode: '+33', + primaryPhoneCountryCode: 'US', + additionalPhones: [], + }, + }, + { + label: 'phone field with invalid phone number format', + input: { + primaryPhoneNumber: 'not-a-number', + additionalPhones: [], + }, + }, + { + label: 'phone field with conflicting phone number country code', + input: { + primaryPhoneNumber: '+33123456789', + primaryPhoneCountryCode: 'US', + additionalPhones: [], + }, + }, + { + label: 'phone field with conflicting phone number calling code', + input: { + primaryPhoneNumber: '+33123456789', + primaryPhoneCallingCode: '+1', + additionalPhones: [], + }, + }, + ]; + +const primaryFailingTests = FAILING_TEST_INPUTS.map< + EachTestingContext +>(({ input, label }) => ({ + title: `create primary ${label}`, + context: { + input: { + ...input, + additionalPhones: [], + }, + }, +})); + +const additionalPhonesNumberFailingTests = FAILING_TEST_INPUTS.map< + EachTestingContext +>( + ({ + input: { + primaryPhoneCallingCode, + primaryPhoneCountryCode, + primaryPhoneNumber, + }, + label, + }) => ({ + title: `create primary ${label}`, + context: { + input: { + additionalPhones: [ + { + callingCode: primaryPhoneCallingCode, + countryCode: primaryPhoneCountryCode, + number: primaryPhoneNumber, + }, + ], + }, + }, + }), +); + +const FAILING_TEST_CASES: EachTestingContext[] = + [...primaryFailingTests, ...additionalPhonesNumberFailingTests]; + +describe('failing create phone field metadata test suite', () => { + let createdObjectMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + input: { + nameSingular: 'myTestObject', + namePlural: 'myTestObjects', + labelSingular: 'My Test Object', + labelPlural: 'My Test Objects', + icon: 'Icon123', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + + await createOneFieldMetadata({ + input: { + name: FIELD_NAME, + label: 'Phone number', + type: FieldMetadataType.PHONES, + objectMetadataId: createdObjectMetadataId, + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + name + label + isLabelSyncedWithName + `, + }); + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { + isActive: false, + }, + }, + }); + await deleteOneObjectMetadata({ + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + test.each(FAILING_TEST_CASES)( + 'it should fail to $title', + + async ({ context: { input } }) => { + const { + data: { createOneResponse }, + errors, + } = await createOneOperation<{ + id: string; + [FIELD_NAME]: any; + }>({ + objectMetadataSingularName: 'myTestObject', + input: { + id: faker.string.uuid(), + [FIELD_NAME]: input, + }, + gqlFields: ` + id + ${FIELD_NAME} { + primaryPhoneNumber + primaryPhoneCountryCode + primaryPhoneCallingCode + additionalPhones + __typename + } + `, + }); + + expect(createOneResponse).toBeNull(); + expect(errors).toBeDefined(); + expect(errors).toMatchSnapshot(); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-actor-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-actor-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..9af7ec3364 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-actor-field-metadata.integration-spec.ts @@ -0,0 +1,142 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Omit; +}>[] = [ + { + title: 'actor field with basic metadata (name, label, description, icon)', + context: { + input: { + name: 'processedBy', + label: 'Processed By', + description: 'Person who processed this', + icon: 'IconUser', + }, + }, + }, + { + title: 'actor field with default value containing source and name', + context: { + input: { + name: 'initiatedBy', + label: 'Initiated By', + defaultValue: { + source: "'MANUAL'", + workspaceMemberId: null, + name: "'System'", + }, + }, + }, + }, + { + title: 'actor field with API source default', + context: { + input: { + name: 'importedBy', + label: 'Imported By', + defaultValue: { + source: "'API'", + workspaceMemberId: null, + name: "'API User'", + }, + }, + }, + }, +]; + +describe('Actor field metadata creation tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string | undefined = undefined; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testActorObject', + namePlural: 'testActorObjects', + labelSingular: 'Test Actor Object', + labelPlural: 'Test Actor Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + afterEach(async () => { + if (createdFieldMetadataId) { + await updateOneFieldMetadata({ + input: { + updatePayload: { isActive: false }, + idToUpdate: createdFieldMetadataId, + }, + expectToFail: false, + }); + await deleteOneFieldMetadata({ + input: { idToDelete: createdFieldMetadataId }, + expectToFail: false, + }); + createdFieldMetadataId = undefined; + } + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should create $title', + async ({ context: { input } }) => { + const inputPayload = { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.ACTOR, + isLabelSyncedWithName: false, + ...input, + }; + const { data } = await createOneFieldMetadata({ + expectToFail: false, + input: inputPayload, + gqlFields: ` + id + type + name + label + description + icon + defaultValue + isLabelSyncedWithName + settings + `, + }); + + createdFieldMetadataId = data.createOneField.id; + + const { objectMetadataId: _, ...expectedFields } = inputPayload; + + expect(data.createOneField).toMatchObject(expectedFields); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-address-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-address-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..4cd6213fdf --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-address-field-metadata.integration-spec.ts @@ -0,0 +1,226 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Omit; +}>[] = [ + { + title: 'address field with basic metadata (name, label, description, icon)', + context: { + input: { + name: 'location', + label: 'Location', + description: 'Physical location', + icon: 'IconMapPin', + }, + }, + }, + { + title: 'address field with subFields setting (street and city only)', + context: { + input: { + name: 'simpleAddress', + label: 'Simple Address', + settings: { + subFields: ['addressStreet1', 'addressCity'], + }, + }, + }, + }, + { + title: 'address field with all subFields', + context: { + input: { + name: 'fullAddress', + label: 'Full Address', + settings: { + subFields: [ + 'addressStreet1', + 'addressStreet2', + 'addressCity', + 'addressState', + 'addressPostcode', + 'addressCountry', + 'addressLat', + 'addressLng', + ], + }, + }, + }, + }, + { + title: 'address field with default value containing full address', + context: { + input: { + name: 'officeAddress', + label: 'Office Address', + defaultValue: { + addressStreet1: "'123 Main St'", + addressStreet2: "'Suite 100'", + addressCity: "'San Francisco'", + addressState: "'CA'", + addressPostcode: "'94102'", + addressCountry: "'USA'", + addressLat: null, + addressLng: null, + }, + }, + }, + }, + { + title: 'address field with partial default value', + context: { + input: { + name: 'shippingAddress', + label: 'Shipping Address', + defaultValue: { + addressStreet1: "'456 Oak Ave'", + addressStreet2: "''", + addressCity: "'New York'", + addressState: "''", + addressPostcode: "''", + addressCountry: "''", + addressLat: null, + addressLng: null, + }, + }, + }, + }, + { + title: 'address field with empty default value', + context: { + input: { + name: 'billingAddress', + label: 'Billing Address', + defaultValue: { + addressStreet1: "''", + addressStreet2: "''", + addressCity: "''", + addressState: "''", + addressPostcode: "''", + addressCountry: "''", + addressLat: null, + addressLng: null, + }, + }, + }, + }, + { + title: 'address field with settings and default value', + context: { + input: { + name: 'warehouseAddress', + label: 'Warehouse Address', + settings: { + subFields: ['addressStreet1', 'addressCity', 'addressCountry'], + }, + defaultValue: { + addressStreet1: "''", + addressStreet2: "''", + addressCity: "''", + addressState: "''", + addressPostcode: "''", + addressCountry: "'USA'", + addressLat: null, + addressLng: null, + }, + }, + }, + }, +]; + +describe('Address field metadata creation tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string | undefined = undefined; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testAddressObject', + namePlural: 'testAddressObjects', + labelSingular: 'Test Address Object', + labelPlural: 'Test Address Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + afterEach(async () => { + if (createdFieldMetadataId) { + await updateOneFieldMetadata({ + input: { + updatePayload: { isActive: false }, + idToUpdate: createdFieldMetadataId, + }, + expectToFail: false, + }); + await deleteOneFieldMetadata({ + input: { idToDelete: createdFieldMetadataId }, + expectToFail: false, + }); + createdFieldMetadataId = undefined; + } + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should create $title', + async ({ context: { input } }) => { + const inputPayload = { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.ADDRESS, + isLabelSyncedWithName: false, + ...input, + }; + const { data } = await createOneFieldMetadata({ + expectToFail: false, + input: inputPayload, + gqlFields: ` + id + type + name + label + description + icon + defaultValue + isLabelSyncedWithName + settings + `, + }); + + createdFieldMetadataId = data.createOneField.id; + + const { objectMetadataId: _, ...expectedFields } = inputPayload; + + expect(data.createOneField).toMatchObject(expectedFields); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-currency-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-currency-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..6968dd933a --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-currency-field-metadata.integration-spec.ts @@ -0,0 +1,167 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Omit; +}>[] = [ + { + title: + 'currency field with basic metadata (name, label, description, icon)', + context: { + input: { + name: 'amount', + label: 'Amount', + description: 'Transaction amount', + icon: 'IconCurrencyDollar', + }, + }, + }, + { + title: 'currency field with default value containing amount and currency', + context: { + input: { + name: 'price', + label: 'Price', + defaultValue: { + amountMicros: "'1000000'", + currencyCode: "'USD'", + }, + }, + }, + }, + { + title: 'currency field with zero default amount', + context: { + input: { + name: 'discount', + label: 'Discount', + defaultValue: { + amountMicros: "'0'", + currencyCode: "'EUR'", + }, + }, + }, + }, + { + title: 'currency field with only currency code default', + context: { + input: { + name: 'budget', + label: 'Budget', + defaultValue: { + amountMicros: null, + currencyCode: "'GBP'", + }, + }, + }, + }, + { + title: 'currency field with empty default value', + context: { + input: { + name: 'cost', + label: 'Cost', + defaultValue: { + amountMicros: null, + currencyCode: "''", + }, + }, + }, + }, +]; + +describe('Currency field metadata creation tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string | undefined = undefined; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testCurrencyObject', + namePlural: 'testCurrencyObjects', + labelSingular: 'Test Currency Object', + labelPlural: 'Test Currency Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + afterEach(async () => { + if (createdFieldMetadataId) { + await updateOneFieldMetadata({ + input: { + updatePayload: { isActive: false }, + idToUpdate: createdFieldMetadataId, + }, + expectToFail: false, + }); + await deleteOneFieldMetadata({ + input: { idToDelete: createdFieldMetadataId }, + expectToFail: false, + }); + createdFieldMetadataId = undefined; + } + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should create $title', + async ({ context: { input } }) => { + const inputPayload = { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.CURRENCY, + isLabelSyncedWithName: false, + ...input, + }; + const { data } = await createOneFieldMetadata({ + expectToFail: false, + input: inputPayload, + gqlFields: ` + id + type + name + label + description + icon + defaultValue + isLabelSyncedWithName + settings + `, + }); + + createdFieldMetadataId = data.createOneField.id; + + const { objectMetadataId: _, ...expectedFields } = inputPayload; + + expect(data.createOneField).toMatchObject(expectedFields); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-emails-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-emails-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..90a47672a2 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-emails-field-metadata.integration-spec.ts @@ -0,0 +1,168 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Omit; +}>[] = [ + { + title: 'emails field with basic metadata (name, label, description, icon)', + context: { + input: { + name: 'contactEmails', + label: 'Contact Emails', + description: 'Contact email addresses', + icon: 'IconMail', + }, + }, + }, + { + title: 'emails field with maxNumberOfValues setting', + context: { + input: { + name: 'limitedEmails', + label: 'Limited Emails', + settings: { + maxNumberOfValues: 3, + }, + }, + }, + }, + { + title: 'emails field with default value containing primary email', + context: { + input: { + name: 'defaultEmails', + label: 'Default Emails', + defaultValue: { + primaryEmail: "'contact@example.com'", + additionalEmails: null, + }, + }, + }, + }, + { + title: 'emails field with empty default value', + context: { + input: { + name: 'emptyEmails', + label: 'Empty Emails', + defaultValue: { + primaryEmail: "''", + additionalEmails: null, + }, + }, + }, + }, + { + title: 'emails field with settings and default value', + context: { + input: { + name: 'configuredEmails', + label: 'Configured Emails', + settings: { + maxNumberOfValues: 5, + }, + defaultValue: { + primaryEmail: "'support@company.com'", + additionalEmails: null, + }, + }, + }, + }, +]; + +describe('Emails field metadata creation tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string | undefined = undefined; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testEmailsObject', + namePlural: 'testEmailsObjects', + labelSingular: 'Test Emails Object', + labelPlural: 'Test Emails Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + afterEach(async () => { + if (createdFieldMetadataId) { + await updateOneFieldMetadata({ + input: { + updatePayload: { isActive: false }, + idToUpdate: createdFieldMetadataId, + }, + expectToFail: false, + }); + await deleteOneFieldMetadata({ + input: { idToDelete: createdFieldMetadataId }, + expectToFail: false, + }); + createdFieldMetadataId = undefined; + } + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should create $title', + async ({ context: { input } }) => { + const inputPayload = { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.EMAILS, + isLabelSyncedWithName: false, + ...input, + }; + const { data } = await createOneFieldMetadata({ + expectToFail: false, + input: inputPayload, + gqlFields: ` + id + type + name + label + description + icon + defaultValue + isLabelSyncedWithName + settings + `, + }); + + createdFieldMetadataId = data.createOneField.id; + + const { objectMetadataId: _, ...expectedFields } = inputPayload; + + expect(data.createOneField).toMatchObject(expectedFields); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-full-name-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-full-name-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..387e9c0116 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-full-name-field-metadata.integration-spec.ts @@ -0,0 +1,154 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Omit; +}>[] = [ + { + title: + 'full name field with basic metadata (name, label, description, icon)', + context: { + input: { + name: 'contactName', + label: 'Contact Name', + description: 'Full name of contact', + icon: 'IconUser', + }, + }, + }, + { + title: 'full name field with default value containing first and last name', + context: { + input: { + name: 'ownerName', + label: 'Owner Name', + defaultValue: { + firstName: "'John'", + lastName: "'Doe'", + }, + }, + }, + }, + { + title: 'full name field with only first name default', + context: { + input: { + name: 'customerName', + label: 'Customer Name', + defaultValue: { + firstName: "'Jane'", + lastName: "''", + }, + }, + }, + }, + { + title: 'full name field with empty default value', + context: { + input: { + name: 'authorName', + label: 'Author Name', + defaultValue: { + firstName: "''", + lastName: "''", + }, + }, + }, + }, +]; + +describe('Full name field metadata creation tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string | undefined = undefined; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testFullNameObject', + namePlural: 'testFullNameObjects', + labelSingular: 'Test Full Name Object', + labelPlural: 'Test Full Name Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + afterEach(async () => { + if (createdFieldMetadataId) { + await updateOneFieldMetadata({ + input: { + updatePayload: { isActive: false }, + idToUpdate: createdFieldMetadataId, + }, + expectToFail: false, + }); + await deleteOneFieldMetadata({ + input: { idToDelete: createdFieldMetadataId }, + expectToFail: false, + }); + createdFieldMetadataId = undefined; + } + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should create $title', + async ({ context: { input } }) => { + const inputPayload = { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.FULL_NAME, + isLabelSyncedWithName: false, + ...input, + }; + const { data } = await createOneFieldMetadata({ + expectToFail: false, + input: inputPayload, + gqlFields: ` + id + type + name + label + description + icon + defaultValue + isLabelSyncedWithName + settings + `, + }); + + createdFieldMetadataId = data.createOneField.id; + + const { objectMetadataId: _, ...expectedFields } = inputPayload; + + expect(data.createOneField).toMatchObject(expectedFields); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-links-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-links-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..1d6af440de --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-links-field-metadata.integration-spec.ts @@ -0,0 +1,171 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Omit; +}>[] = [ + { + title: 'links field with basic metadata (name, label, description, icon)', + context: { + input: { + name: 'socialLinks', + label: 'Social Links', + description: 'Social media links', + icon: 'IconLink', + }, + }, + }, + { + title: 'links field with maxNumberOfValues setting', + context: { + input: { + name: 'limitedLinks', + label: 'Limited Links', + settings: { + maxNumberOfValues: 5, + }, + }, + }, + }, + { + title: 'links field with default value containing primary link', + context: { + input: { + name: 'websiteLinks', + label: 'Website Links', + defaultValue: { + primaryLinkLabel: "'Website'", + primaryLinkUrl: "'https://example.com'", + secondaryLinks: null, + }, + }, + }, + }, + { + title: 'links field with empty default value', + context: { + input: { + name: 'emptyLinks', + label: 'Empty Links', + defaultValue: { + primaryLinkLabel: "''", + primaryLinkUrl: "''", + secondaryLinks: null, + }, + }, + }, + }, + { + title: 'links field with settings and default value', + context: { + input: { + name: 'configuredLinks', + label: 'Configured Links', + settings: { + maxNumberOfValues: 3, + }, + defaultValue: { + primaryLinkLabel: "'Homepage'", + primaryLinkUrl: "'https://company.com'", + secondaryLinks: null, + }, + }, + }, + }, +]; + +describe('Links field metadata creation tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string | undefined = undefined; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testLinksObject', + namePlural: 'testLinksObjects', + labelSingular: 'Test Links Object', + labelPlural: 'Test Links Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + afterEach(async () => { + if (createdFieldMetadataId) { + await updateOneFieldMetadata({ + input: { + updatePayload: { isActive: false }, + idToUpdate: createdFieldMetadataId, + }, + expectToFail: false, + }); + await deleteOneFieldMetadata({ + input: { idToDelete: createdFieldMetadataId }, + expectToFail: false, + }); + createdFieldMetadataId = undefined; + } + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should create $title', + async ({ context: { input } }) => { + const inputPayload = { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.LINKS, + isLabelSyncedWithName: false, + ...input, + }; + const { data } = await createOneFieldMetadata({ + expectToFail: false, + input: inputPayload, + gqlFields: ` + id + type + name + label + description + icon + defaultValue + isLabelSyncedWithName + settings + `, + }); + + createdFieldMetadataId = data.createOneField.id; + + const { objectMetadataId: _, ...expectedFields } = inputPayload; + + expect(data.createOneField).toMatchObject(expectedFields); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/phone/create-one-field-metadata-phone.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-phone-field-metadata.integration-spec.ts similarity index 72% rename from packages/twenty-server/test/integration/metadata/suites/field-metadata/phone/create-one-field-metadata-phone.integration-spec.ts rename to packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-phone-field-metadata.integration-spec.ts index 14bfb80cdd..2922883468 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/phone/create-one-field-metadata-phone.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-phone-field-metadata.integration-spec.ts @@ -219,109 +219,7 @@ const SUCCESSFUL_TEST_CASES: EachTestingContext; label: string }[] = - [ - { - label: 'phone field without country or calling code at all', - input: { - primaryPhoneNumber: '123456789', - additionalPhones: [], - }, - }, - { - label: 'phone field with invalid country code', - input: { - primaryPhoneNumber: '123456789', - primaryPhoneCallingCode: '+33', - primaryPhoneCountryCode: 'XX' as CountryCode, - additionalPhones: [], - }, - }, - { - label: 'phone field with invalid calling code', - input: { - primaryPhoneNumber: '123456789', - primaryPhoneCallingCode: '+999', - primaryPhoneCountryCode: 'FR', - additionalPhones: [], - }, - }, - { - label: 'phone field with conflicting country code and calling code', - input: { - primaryPhoneNumber: '123456789', - primaryPhoneCallingCode: '+33', - primaryPhoneCountryCode: 'US', - additionalPhones: [], - }, - }, - { - label: 'phone field with invalid phone number format', - input: { - primaryPhoneNumber: 'not-a-number', - additionalPhones: [], - }, - }, - { - label: 'phone field with conflicting phone number country code', - input: { - primaryPhoneNumber: '+33123456789', - primaryPhoneCountryCode: 'US', - additionalPhones: [], - }, - }, - { - label: 'phone field with conflicting phone number calling code', - input: { - primaryPhoneNumber: '+33123456789', - primaryPhoneCallingCode: '+1', - additionalPhones: [], - }, - }, - ]; - -const primaryFailingTests = FAILING_TEST_INPUTS.map< - EachTestingContext ->(({ input, label }) => ({ - title: `create primary ${label}`, - context: { - input: { - ...input, - additionalPhones: [], - }, - }, -})); - -const additionalPhonesNumberFailingTests = FAILING_TEST_INPUTS.map< - EachTestingContext ->( - ({ - input: { - primaryPhoneCallingCode, - primaryPhoneCountryCode, - primaryPhoneNumber, - }, - label, - }) => ({ - title: `create primary ${label}`, - context: { - input: { - additionalPhones: [ - { - callingCode: primaryPhoneCallingCode, - countryCode: primaryPhoneCountryCode, - number: primaryPhoneNumber, - }, - ], - }, - }, - }), -); - -const FAILING_TEST_CASES: EachTestingContext[] = - [...primaryFailingTests, ...additionalPhonesNumberFailingTests]; - -describe('Phone field metadata tests suite', () => { +describe('successful create phone field metadata test suite', () => { let createdObjectMetadataId: string; beforeAll(async () => { @@ -371,7 +269,7 @@ describe('Phone field metadata tests suite', () => { }); test.each(SUCCESSFUL_TEST_CASES)( - 'It should succeed $title', + 'it should succeed $title', async ({ context: { input, expected } }) => { const { @@ -407,38 +305,4 @@ describe('Phone field metadata tests suite', () => { }); }, ); - - test.each(FAILING_TEST_CASES)( - 'It should fail to $title', - - async ({ context: { input } }) => { - const { - data: { createOneResponse }, - errors, - } = await createOneOperation<{ - id: string; - [FIELD_NAME]: any; - }>({ - objectMetadataSingularName: 'myTestObject', - input: { - id: faker.string.uuid(), - [FIELD_NAME]: input, - }, - gqlFields: ` - id - ${FIELD_NAME} { - primaryPhoneNumber - primaryPhoneCountryCode - primaryPhoneCallingCode - additionalPhones - __typename - } - `, - }); - - expect(createOneResponse).toBeNull(); - expect(errors).toBeDefined(); - expect(errors).toMatchSnapshot(); - }, - ); }); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-rich-text-v2-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-rich-text-v2-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..750a1aafa1 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-create-rich-text-v2-field-metadata.integration-spec.ts @@ -0,0 +1,154 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Omit; +}>[] = [ + { + title: + 'rich text v2 field with basic metadata (name, label, description, icon)', + context: { + input: { + name: 'description', + label: 'Description', + description: 'Rich text description field', + icon: 'IconNotes', + }, + }, + }, + { + title: 'rich text v2 field with default value containing markdown', + context: { + input: { + name: 'notes', + label: 'Notes', + defaultValue: { + blocknote: null, + markdown: "'# Default Title'", + }, + }, + }, + }, + { + title: 'rich text v2 field with default value containing blocknote', + context: { + input: { + name: 'content', + label: 'Content', + defaultValue: { + blocknote: '\'{"blocks":[{"type":"paragraph"}]}\'', + markdown: null, + }, + }, + }, + }, + { + title: 'rich text v2 field with empty default value', + context: { + input: { + name: 'body', + label: 'Body', + defaultValue: { + blocknote: null, + markdown: null, + }, + }, + }, + }, +]; + +describe('Rich text v2 field metadata creation tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string | undefined = undefined; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testRichTextV2Object', + namePlural: 'testRichTextV2Objects', + labelSingular: 'Test Rich Text V2 Object', + labelPlural: 'Test Rich Text V2 Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + afterEach(async () => { + if (createdFieldMetadataId) { + await updateOneFieldMetadata({ + input: { + updatePayload: { isActive: false }, + idToUpdate: createdFieldMetadataId, + }, + expectToFail: false, + }); + await deleteOneFieldMetadata({ + input: { idToDelete: createdFieldMetadataId }, + expectToFail: false, + }); + createdFieldMetadataId = undefined; + } + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should create $title', + async ({ context: { input } }) => { + const inputPayload = { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.RICH_TEXT_V2, + isLabelSyncedWithName: false, + ...input, + }; + const { data } = await createOneFieldMetadata({ + expectToFail: false, + input: inputPayload, + gqlFields: ` + id + type + name + label + description + icon + defaultValue + isLabelSyncedWithName + settings + `, + }); + + createdFieldMetadataId = data.createOneField.id; + + const { objectMetadataId: _, ...expectedFields } = inputPayload; + + expect(data.createOneField).toMatchObject(expectedFields); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-actor-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-actor-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..7db1ebcaff --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-actor-field-metadata.integration-spec.ts @@ -0,0 +1,156 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Partial; +}>[] = [ + { + title: 'actor field basic metadata (label, description, icon)', + context: { + input: { + label: 'Updated Actor', + description: 'Updated description', + icon: 'IconUsers', + }, + }, + }, + { + title: 'actor field default value with MANUAL source', + context: { + input: { + defaultValue: { + source: "'MANUAL'", + workspaceMemberId: null, + name: "'System'", + }, + }, + }, + }, + { + title: 'actor field default value with API source', + context: { + input: { + defaultValue: { + source: "'API'", + workspaceMemberId: null, + name: "'API User'", + }, + }, + }, + }, + { + title: 'actor field default value set to null', + context: { + input: { + defaultValue: null, + }, + }, + }, +]; + +describe('Actor field metadata update tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testActorUpdateObject', + namePlural: 'testActorUpdateObjects', + labelSingular: 'Test Actor Update Object', + labelPlural: 'Test Actor Update Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + beforeEach(async () => { + const { + data: { createOneField }, + } = await createOneFieldMetadata({ + expectToFail: false, + input: { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.ACTOR, + name: 'testActor', + label: 'Test Actor', + description: 'Initial description', + icon: 'IconUser', + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + `, + }); + + createdFieldMetadataId = createOneField.id; + }); + + afterEach(async () => { + await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneFieldMetadata({ + expectToFail: false, + input: { idToDelete: createdFieldMetadataId }, + }); + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should update $title', + async ({ context: { input } }) => { + const { data } = await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: input, + }, + gqlFields: ` + id + type + name + label + description + icon + settings + defaultValue + `, + }); + + expect(data.updateOneField).toMatchObject(input); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-address-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-address-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..5bb162ef27 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-address-field-metadata.integration-spec.ts @@ -0,0 +1,215 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Partial; +}>[] = [ + { + title: 'address field basic metadata (label, description, icon)', + context: { + input: { + label: 'Updated Address', + description: 'Updated description', + icon: 'IconHome', + }, + }, + }, + { + title: 'address field settings with limited subFields', + context: { + input: { + settings: { + subFields: ['addressStreet1', 'addressCity', 'addressCountry'], + }, + }, + }, + }, + { + title: 'address field settings with all subFields', + context: { + input: { + settings: { + subFields: [ + 'addressStreet1', + 'addressStreet2', + 'addressCity', + 'addressState', + 'addressPostcode', + 'addressCountry', + 'addressLat', + 'addressLng', + ], + }, + }, + }, + }, + { + title: 'address field default value with full address', + context: { + input: { + defaultValue: { + addressStreet1: "'123 Main St'", + addressStreet2: "'Apt 4'", + addressCity: "'New York'", + addressState: "'NY'", + addressPostcode: "'10001'", + addressCountry: "'USA'", + addressLat: null, + addressLng: null, + }, + }, + }, + }, + { + title: 'address field default value with partial address', + context: { + input: { + defaultValue: { + addressStreet1: "'456 Oak Ave'", + addressStreet2: "''", + addressCity: "'Boston'", + addressState: "''", + addressPostcode: "''", + addressCountry: "''", + addressLat: null, + addressLng: null, + }, + }, + }, + }, + { + title: 'address field default value set to null', + context: { + input: { + defaultValue: null, + }, + }, + }, + { + title: 'address field settings and default value together', + context: { + input: { + settings: { + subFields: ['addressStreet1', 'addressCity', 'addressPostcode'], + }, + defaultValue: { + addressStreet1: "''", + addressStreet2: "''", + addressCity: "''", + addressState: "''", + addressPostcode: "''", + addressCountry: "'USA'", + addressLat: null, + addressLng: null, + }, + }, + }, + }, +]; + +describe('Address field metadata update tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testAddressUpdateObject', + namePlural: 'testAddressUpdateObjects', + labelSingular: 'Test Address Update Object', + labelPlural: 'Test Address Update Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + beforeEach(async () => { + const { + data: { createOneField }, + } = await createOneFieldMetadata({ + expectToFail: false, + input: { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.ADDRESS, + name: 'testAddress', + label: 'Test Address', + description: 'Initial description', + icon: 'IconMapPin', + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + `, + }); + + createdFieldMetadataId = createOneField.id; + }); + + afterEach(async () => { + await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneFieldMetadata({ + expectToFail: false, + input: { idToDelete: createdFieldMetadataId }, + }); + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should update $title', + async ({ context: { input } }) => { + const { data } = await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: input, + }, + gqlFields: ` + id + type + name + label + description + icon + settings + defaultValue + `, + }); + + expect(data.updateOneField).toMatchObject(input); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-currency-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-currency-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..64a1d1fd18 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-currency-field-metadata.integration-spec.ts @@ -0,0 +1,176 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Partial; +}>[] = [ + { + title: 'currency field basic metadata (label, description, icon)', + context: { + input: { + label: 'Updated Currency', + description: 'Updated description', + icon: 'IconCoin', + }, + }, + }, + { + title: 'currency field default value with amount and currency', + context: { + input: { + defaultValue: { + amountMicros: "'1000000'", + currencyCode: "'USD'", + }, + }, + }, + }, + { + title: 'currency field default value with zero amount', + context: { + input: { + defaultValue: { + amountMicros: "'0'", + currencyCode: "'EUR'", + }, + }, + }, + }, + { + title: 'currency field default value with only currency code', + context: { + input: { + defaultValue: { + amountMicros: null, + currencyCode: "'GBP'", + }, + }, + }, + }, + { + title: 'currency field default value with empty values', + context: { + input: { + defaultValue: { + amountMicros: null, + currencyCode: "''", + }, + }, + }, + }, + { + title: 'currency field default value set to null', + context: { + input: { + defaultValue: null, + }, + }, + }, +]; + +describe('Currency field metadata update tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testCurrencyUpdateObject', + namePlural: 'testCurrencyUpdateObjects', + labelSingular: 'Test Currency Update Object', + labelPlural: 'Test Currency Update Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + beforeEach(async () => { + const { + data: { createOneField }, + } = await createOneFieldMetadata({ + expectToFail: false, + input: { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.CURRENCY, + name: 'testCurrency', + label: 'Test Currency', + description: 'Initial description', + icon: 'IconCurrencyDollar', + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + `, + }); + + createdFieldMetadataId = createOneField.id; + }); + + afterEach(async () => { + await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneFieldMetadata({ + expectToFail: false, + input: { idToDelete: createdFieldMetadataId }, + }); + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should update $title', + async ({ context: { input } }) => { + const { data } = await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: input, + }, + gqlFields: ` + id + type + name + label + description + icon + settings + defaultValue + `, + }); + + expect(data.updateOneField).toMatchObject(input); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-emails-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-emails-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..d2596e1f5e --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-emails-field-metadata.integration-spec.ts @@ -0,0 +1,178 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Partial; +}>[] = [ + { + title: 'emails field basic metadata (label, description, icon)', + context: { + input: { + label: 'Updated Emails', + description: 'Updated description', + icon: 'IconMailOpened', + }, + }, + }, + { + title: 'emails field settings with maxNumberOfValues', + context: { + input: { + settings: { + maxNumberOfValues: 3, + }, + }, + }, + }, + { + title: 'emails field default value with primary email', + context: { + input: { + defaultValue: { + primaryEmail: "'contact@example.com'", + additionalEmails: null, + }, + }, + }, + }, + { + title: 'emails field default value with empty values', + context: { + input: { + defaultValue: { + primaryEmail: "''", + additionalEmails: null, + }, + }, + }, + }, + { + title: 'emails field default value set to null', + context: { + input: { + defaultValue: null, + }, + }, + }, + { + title: 'emails field settings and default value together', + context: { + input: { + settings: { + maxNumberOfValues: 5, + }, + defaultValue: { + primaryEmail: "'support@company.com'", + additionalEmails: null, + }, + }, + }, + }, +]; + +describe('Emails field metadata update tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testEmailsUpdateObject', + namePlural: 'testEmailsUpdateObjects', + labelSingular: 'Test Emails Update Object', + labelPlural: 'Test Emails Update Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + beforeEach(async () => { + const { + data: { createOneField }, + } = await createOneFieldMetadata({ + expectToFail: false, + input: { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.EMAILS, + name: 'testEmails', + label: 'Test Emails', + description: 'Initial description', + icon: 'IconMail', + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + `, + }); + + createdFieldMetadataId = createOneField.id; + }); + + afterEach(async () => { + await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneFieldMetadata({ + expectToFail: false, + input: { idToDelete: createdFieldMetadataId }, + }); + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should update $title', + async ({ context: { input } }) => { + const { data } = await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: input, + }, + gqlFields: ` + id + type + name + label + description + icon + settings + defaultValue + `, + }); + + expect(data.updateOneField).toMatchObject(input); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-full-name-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-full-name-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..b10c9403f5 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-full-name-field-metadata.integration-spec.ts @@ -0,0 +1,165 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Partial; +}>[] = [ + { + title: 'full name field basic metadata (label, description, icon)', + context: { + input: { + label: 'Updated Full Name', + description: 'Updated description', + icon: 'IconUserCircle', + }, + }, + }, + { + title: 'full name field default value with first and last name', + context: { + input: { + defaultValue: { + firstName: "'John'", + lastName: "'Doe'", + }, + }, + }, + }, + { + title: 'full name field default value with only first name', + context: { + input: { + defaultValue: { + firstName: "'Jane'", + lastName: "''", + }, + }, + }, + }, + { + title: 'full name field default value with empty values', + context: { + input: { + defaultValue: { + firstName: "''", + lastName: "''", + }, + }, + }, + }, + { + title: 'full name field default value set to null', + context: { + input: { + defaultValue: null, + }, + }, + }, +]; + +describe('Full name field metadata update tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testFullNameUpdateObject', + namePlural: 'testFullNameUpdateObjects', + labelSingular: 'Test Full Name Update Object', + labelPlural: 'Test Full Name Update Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + beforeEach(async () => { + const { + data: { createOneField }, + } = await createOneFieldMetadata({ + expectToFail: false, + input: { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.FULL_NAME, + name: 'testFullName', + label: 'Test Full Name', + description: 'Initial description', + icon: 'IconUser', + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + `, + }); + + createdFieldMetadataId = createOneField.id; + }); + + afterEach(async () => { + await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneFieldMetadata({ + expectToFail: false, + input: { idToDelete: createdFieldMetadataId }, + }); + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should update $title', + async ({ context: { input } }) => { + const { data } = await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: input, + }, + gqlFields: ` + id + type + name + label + description + icon + settings + defaultValue + `, + }); + + expect(data.updateOneField).toMatchObject(input); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-links-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-links-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..8b41342eff --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-links-field-metadata.integration-spec.ts @@ -0,0 +1,181 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Partial; +}>[] = [ + { + title: 'links field basic metadata (label, description, icon)', + context: { + input: { + label: 'Updated Links', + description: 'Updated description', + icon: 'IconExternalLink', + }, + }, + }, + { + title: 'links field settings with maxNumberOfValues', + context: { + input: { + settings: { + maxNumberOfValues: 5, + }, + }, + }, + }, + { + title: 'links field default value with primary link', + context: { + input: { + defaultValue: { + primaryLinkLabel: "'Website'", + primaryLinkUrl: "'https://example.com'", + secondaryLinks: null, + }, + }, + }, + }, + { + title: 'links field default value with empty values', + context: { + input: { + defaultValue: { + primaryLinkLabel: "''", + primaryLinkUrl: "''", + secondaryLinks: null, + }, + }, + }, + }, + { + title: 'links field default value set to null', + context: { + input: { + defaultValue: null, + }, + }, + }, + { + title: 'links field settings and default value together', + context: { + input: { + settings: { + maxNumberOfValues: 3, + }, + defaultValue: { + primaryLinkLabel: "'Homepage'", + primaryLinkUrl: "'https://company.com'", + secondaryLinks: null, + }, + }, + }, + }, +]; + +describe('Links field metadata update tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testLinksUpdateObject', + namePlural: 'testLinksUpdateObjects', + labelSingular: 'Test Links Update Object', + labelPlural: 'Test Links Update Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + beforeEach(async () => { + const { + data: { createOneField }, + } = await createOneFieldMetadata({ + expectToFail: false, + input: { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.LINKS, + name: 'testLinks', + label: 'Test Links', + description: 'Initial description', + icon: 'IconLink', + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + `, + }); + + createdFieldMetadataId = createOneField.id; + }); + + afterEach(async () => { + await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneFieldMetadata({ + expectToFail: false, + input: { idToDelete: createdFieldMetadataId }, + }); + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should update $title', + async ({ context: { input } }) => { + const { data } = await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: input, + }, + gqlFields: ` + id + type + name + label + description + icon + settings + defaultValue + `, + }); + + expect(data.updateOneField).toMatchObject(input); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-phones-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-phones-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..1b767230b8 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-phones-field-metadata.integration-spec.ts @@ -0,0 +1,184 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Partial; +}>[] = [ + { + title: 'phones field basic metadata (label, description, icon)', + context: { + input: { + label: 'Updated Phones', + description: 'Updated description', + icon: 'IconPhoneCall', + }, + }, + }, + { + title: 'phones field settings with maxNumberOfValues', + context: { + input: { + settings: { + maxNumberOfValues: 4, + }, + }, + }, + }, + { + title: 'phones field default value with primary phone', + context: { + input: { + defaultValue: { + primaryPhoneNumber: "'+33123456789'", + primaryPhoneCountryCode: "'FR'", + primaryPhoneCallingCode: "'+33'", + additionalPhones: null, + }, + }, + }, + }, + { + title: 'phones field default value with empty values', + context: { + input: { + defaultValue: { + primaryPhoneNumber: "''", + primaryPhoneCountryCode: "''", + primaryPhoneCallingCode: "''", + additionalPhones: null, + }, + }, + }, + }, + { + title: 'phones field default value set to null', + context: { + input: { + defaultValue: null, + }, + }, + }, + { + title: 'phones field settings and default value together', + context: { + input: { + settings: { + maxNumberOfValues: 6, + }, + defaultValue: { + primaryPhoneNumber: "'+1555123456'", + primaryPhoneCountryCode: "'US'", + primaryPhoneCallingCode: "'+1'", + additionalPhones: null, + }, + }, + }, + }, +]; + +describe('Phones field metadata update tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testPhonesUpdateObject', + namePlural: 'testPhonesUpdateObjects', + labelSingular: 'Test Phones Update Object', + labelPlural: 'Test Phones Update Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + beforeEach(async () => { + const { + data: { createOneField }, + } = await createOneFieldMetadata({ + expectToFail: false, + input: { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.PHONES, + name: 'testPhones', + label: 'Test Phones', + description: 'Initial description', + icon: 'IconPhone', + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + `, + }); + + createdFieldMetadataId = createOneField.id; + }); + + afterEach(async () => { + await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneFieldMetadata({ + expectToFail: false, + input: { idToDelete: createdFieldMetadataId }, + }); + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should update $title', + async ({ context: { input } }) => { + const { data } = await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: input, + }, + gqlFields: ` + id + type + name + label + description + icon + settings + defaultValue + `, + }); + + expect(data.updateOneField).toMatchObject(input); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-rich-text-v2-field-metadata.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-rich-text-v2-field-metadata.integration-spec.ts new file mode 100644 index 0000000000..9b4afb1b46 --- /dev/null +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/composite/successful-update-rich-text-v2-field-metadata.integration-spec.ts @@ -0,0 +1,165 @@ +import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util'; +import { deleteOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/delete-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 { + type EachTestingContext, + eachTestingContextFilter, +} from 'twenty-shared/testing'; +import { FieldMetadataType } from 'twenty-shared/types'; + +import { type UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input'; + +const SUCCESSFUL_TEST_CASES: EachTestingContext<{ + input: Partial; +}>[] = [ + { + title: 'rich text v2 field basic metadata (label, description, icon)', + context: { + input: { + label: 'Updated Rich Text', + description: 'Updated description', + icon: 'IconFileText', + }, + }, + }, + { + title: 'rich text v2 field default value with markdown', + context: { + input: { + defaultValue: { + blocknote: null, + markdown: "'# Default Title'", + }, + }, + }, + }, + { + title: 'rich text v2 field default value with blocknote', + context: { + input: { + defaultValue: { + blocknote: '\'{"blocks":[{"type":"paragraph"}]}\'', + markdown: null, + }, + }, + }, + }, + { + title: 'rich text v2 field default value with both formats', + context: { + input: { + defaultValue: { + blocknote: '\'{"blocks":[]}\'', + markdown: "'## Heading'", + }, + }, + }, + }, + { + title: 'rich text v2 field default value set to null', + context: { + input: { + defaultValue: null, + }, + }, + }, +]; + +describe('Rich text v2 field metadata update tests suite', () => { + let createdObjectMetadataId: string; + let createdFieldMetadataId: string; + + beforeAll(async () => { + const { data } = await createOneObjectMetadata({ + expectToFail: false, + input: { + nameSingular: 'testRichTextV2UpdateObject', + namePlural: 'testRichTextV2UpdateObjects', + labelSingular: 'Test Rich Text V2 Update Object', + labelPlural: 'Test Rich Text V2 Update Objects', + icon: 'IconTestPipe', + isLabelSyncedWithName: false, + }, + }); + + createdObjectMetadataId = data.createOneObject.id; + }); + + afterAll(async () => { + await updateOneObjectMetadata({ + expectToFail: false, + input: { + idToUpdate: createdObjectMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneObjectMetadata({ + expectToFail: false, + input: { idToDelete: createdObjectMetadataId }, + }); + }); + + beforeEach(async () => { + const { + data: { createOneField }, + } = await createOneFieldMetadata({ + expectToFail: false, + input: { + objectMetadataId: createdObjectMetadataId, + type: FieldMetadataType.RICH_TEXT_V2, + name: 'testRichText', + label: 'Test Rich Text', + description: 'Initial description', + icon: 'IconNotes', + isLabelSyncedWithName: false, + }, + gqlFields: ` + id + `, + }); + + createdFieldMetadataId = createOneField.id; + }); + + afterEach(async () => { + await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: { isActive: false }, + }, + }); + await deleteOneFieldMetadata({ + expectToFail: false, + input: { idToDelete: createdFieldMetadataId }, + }); + }); + + test.each(eachTestingContextFilter(SUCCESSFUL_TEST_CASES))( + 'it should update $title', + async ({ context: { input } }) => { + const { data } = await updateOneFieldMetadata({ + expectToFail: false, + input: { + idToUpdate: createdFieldMetadataId, + updatePayload: input, + }, + gqlFields: ` + id + type + name + label + description + icon + settings + defaultValue + `, + }); + + expect(data.updateOneField).toMatchObject(input); + }, + ); +}); diff --git a/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/successful-update-one-enum-field-metadata-v2.integration-spec.ts b/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/successful-update-one-enum-field-metadata-v2.integration-spec.ts index 7132a23eef..5b3799eb0a 100644 --- a/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/successful-update-one-enum-field-metadata-v2.integration-spec.ts +++ b/packages/twenty-server/test/integration/metadata/suites/field-metadata/enum/successful-update-one-enum-field-metadata-v2.integration-spec.ts @@ -11,11 +11,11 @@ import { eachTestingContextFilter } from 'twenty-shared/testing'; import { FieldMetadataType } from 'twenty-shared/types'; import { isDefined } from 'twenty-shared/utils'; -import { fieldMetadataEnumTypes } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util'; import { type FieldMetadataComplexOption, type FieldMetadataDefaultOption, } from 'src/engine/metadata-modules/field-metadata/dtos/options.input'; +import { fieldMetadataEnumTypes } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util'; describe.each(fieldMetadataEnumTypes)( 'Successful update field metadata %s tests suite v2', @@ -128,6 +128,7 @@ describe.each(fieldMetadataEnumTypes)( : rawDefaultValue; const { data: firstUpdate } = await updateOneFieldMetadata({ + expectToFail: false, input: { idToUpdate: createdFieldMetadataId, updatePayload: { @@ -146,6 +147,7 @@ describe.each(fieldMetadataEnumTypes)( const updatedOptions = initialOptions.slice(1); const { data: secondUpdate, errors } = await updateOneFieldMetadata({ + expectToFail: false, input: { idToUpdate: createdFieldMetadataId, updatePayload: { diff --git a/packages/twenty-server/test/integration/rest/suites/rest-api-core-update.integration-spec.ts b/packages/twenty-server/test/integration/rest/suites/rest-api-core-update.integration-spec.ts index d7016329da..00d7e1036e 100644 --- a/packages/twenty-server/test/integration/rest/suites/rest-api-core-update.integration-spec.ts +++ b/packages/twenty-server/test/integration/rest/suites/rest-api-core-update.integration-spec.ts @@ -3,10 +3,7 @@ import { NOT_EXISTING_TEST_PERSON_ID, TEST_PERSON_1_ID, } from 'test/integration/constants/test-person-ids.constants'; -import { - TEST_PRIMARY_LINK_URL, - TEST_PRIMARY_LINK_URL_WIITHOUT_TRAILING_SLASH, -} from 'test/integration/constants/test-primary-link-url.constant'; +import { TEST_PRIMARY_LINK_URL } from 'test/integration/constants/test-primary-link-url.constant'; import { makeRestAPIRequest } from 'test/integration/rest/utils/make-rest-api-request.util'; import { deleteAllRecords } from 'test/integration/utils/delete-all-records'; import { generateRecordName } from 'test/integration/utils/generate-record-name'; @@ -98,9 +95,6 @@ describe('Core REST API Update One endpoint', () => { const updatedPerson = res.body.data.updatePerson; expect(updatedPerson.company).toBeDefined(); - expect(updatedPerson.company.domainName.primaryLinkUrl).toBe( - TEST_PRIMARY_LINK_URL_WIITHOUT_TRAILING_SLASH, - ); expect(updatedPerson.company.people).not.toBeDefined(); }); });