Fix: multi-select default values validation (#12271)
https://github.com/user-attachments/assets/3bea63cc-b098-4252-8787-fc6263f01e8d Closes #12277 --------- Co-authored-by: prastoin <paul@twenty.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -11,8 +11,6 @@ import {
|
|||||||
} from 'class-validator';
|
} from 'class-validator';
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
|
||||||
import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface';
|
|
||||||
import { FieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface';
|
|
||||||
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
import { FieldMetadataSettings } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-settings.interface';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
@ -91,48 +89,4 @@ export class FieldMetadataValidationService<
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async validateDefaultValueOrThrow({
|
|
||||||
fieldType,
|
|
||||||
defaultValue,
|
|
||||||
options,
|
|
||||||
}: {
|
|
||||||
fieldType: FieldMetadataType;
|
|
||||||
defaultValue: FieldMetadataDefaultValue<T>;
|
|
||||||
options: FieldMetadataOptions<T>;
|
|
||||||
}) {
|
|
||||||
if (
|
|
||||||
fieldType === FieldMetadataType.SELECT ||
|
|
||||||
fieldType === FieldMetadataType.MULTI_SELECT
|
|
||||||
) {
|
|
||||||
this.validateEnumDefaultValue(options, defaultValue);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private validateEnumDefaultValue(
|
|
||||||
options: FieldMetadataOptions<T>,
|
|
||||||
defaultValue: FieldMetadataDefaultValue<T>,
|
|
||||||
) {
|
|
||||||
if (typeof defaultValue === 'string') {
|
|
||||||
const formattedDefaultValue = defaultValue.replace(
|
|
||||||
/^['"](.*)['"]$/,
|
|
||||||
'$1',
|
|
||||||
);
|
|
||||||
|
|
||||||
const enumOptions = options.map((option) => option.value);
|
|
||||||
|
|
||||||
if (
|
|
||||||
enumOptions &&
|
|
||||||
(enumOptions.includes(formattedDefaultValue) ||
|
|
||||||
// @ts-expect-error legacy noImplicitAny
|
|
||||||
enumOptions.some((option) => option.to === formattedDefaultValue))
|
|
||||||
) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new FieldMetadataException(
|
|
||||||
`Default value for existing options is invalid: ${defaultValue}`,
|
|
||||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -68,6 +68,7 @@ import { TwentyORMGlobalManager } from 'src/engine/twenty-orm/twenty-orm-global.
|
|||||||
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
import { computeObjectTargetTable } from 'src/engine/utils/compute-object-target-table.util';
|
||||||
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
import { WorkspaceMigrationRunnerService } from 'src/engine/workspace-manager/workspace-migration-runner/workspace-migration-runner.service';
|
||||||
import { ViewService } from 'src/modules/view/services/view.service';
|
import { ViewService } from 'src/modules/view/services/view.service';
|
||||||
|
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||||
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'src/utils/trim-and-remove-duplicated-whitespaces-from-object-string-properties';
|
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'src/utils/trim-and-remove-duplicated-whitespaces-from-object-string-properties';
|
||||||
|
|
||||||
import { FieldMetadataValidationService } from './field-metadata-validation.service';
|
import { FieldMetadataValidationService } from './field-metadata-validation.service';
|
||||||
@ -662,13 +663,15 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.fieldMetadataEnumValidationService.validateEnumFieldMetadataInput(
|
if (isEnumFieldMetadataType(fieldMetadataType)) {
|
||||||
{
|
await this.fieldMetadataEnumValidationService.validateEnumFieldMetadataInput(
|
||||||
fieldMetadataInput,
|
{
|
||||||
fieldMetadataType,
|
fieldMetadataInput,
|
||||||
existingFieldMetadata,
|
fieldMetadataType,
|
||||||
},
|
existingFieldMetadata,
|
||||||
);
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (fieldMetadataInput.settings) {
|
if (fieldMetadataInput.settings) {
|
||||||
await this.fieldMetadataValidationService.validateSettingsOrThrow({
|
await this.fieldMetadataValidationService.validateSettingsOrThrow({
|
||||||
@ -732,7 +735,9 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private prepareCustomFieldMetadata(fieldMetadataInput: CreateFieldInput) {
|
private prepareCustomFieldMetadataForCreation(
|
||||||
|
fieldMetadataInput: CreateFieldInput,
|
||||||
|
) {
|
||||||
const options = fieldMetadataInput.options
|
const options = fieldMetadataInput.options
|
||||||
? this.prepareCustomFieldMetadataOptions(fieldMetadataInput.options)
|
? this.prepareCustomFieldMetadataOptions(fieldMetadataInput.options)
|
||||||
: undefined;
|
: undefined;
|
||||||
@ -787,7 +792,7 @@ export class FieldMetadataService extends TypeOrmQueryService<FieldMetadataEntit
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fieldMetadataForCreate =
|
const fieldMetadataForCreate =
|
||||||
this.prepareCustomFieldMetadata(fieldMetadataInput);
|
this.prepareCustomFieldMetadataForCreation(fieldMetadataInput);
|
||||||
|
|
||||||
await this.validateFieldMetadata({
|
await this.validateFieldMetadata({
|
||||||
fieldMetadataType: fieldMetadataForCreate.type,
|
fieldMetadataType: fieldMetadataForCreate.type,
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import {
|
|||||||
FieldMetadataDefaultValueRawJson,
|
FieldMetadataDefaultValueRawJson,
|
||||||
FieldMetadataDefaultValueRichText,
|
FieldMetadataDefaultValueRichText,
|
||||||
FieldMetadataDefaultValueString,
|
FieldMetadataDefaultValueString,
|
||||||
|
FieldMetadataDefaultValueStringArray,
|
||||||
FieldMetadataDefaultValueUuidFunction,
|
FieldMetadataDefaultValueUuidFunction,
|
||||||
} from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input';
|
} from 'src/engine/metadata-modules/field-metadata/dtos/default-value.input';
|
||||||
|
|
||||||
@ -46,7 +47,7 @@ type FieldMetadataDefaultValueMapping = {
|
|||||||
[FieldMetadataType.ADDRESS]: FieldMetadataDefaultValueAddress;
|
[FieldMetadataType.ADDRESS]: FieldMetadataDefaultValueAddress;
|
||||||
[FieldMetadataType.RATING]: FieldMetadataDefaultValueString;
|
[FieldMetadataType.RATING]: FieldMetadataDefaultValueString;
|
||||||
[FieldMetadataType.SELECT]: FieldMetadataDefaultValueString;
|
[FieldMetadataType.SELECT]: FieldMetadataDefaultValueString;
|
||||||
[FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueString;
|
[FieldMetadataType.MULTI_SELECT]: FieldMetadataDefaultValueStringArray;
|
||||||
[FieldMetadataType.RAW_JSON]: FieldMetadataDefaultValueRawJson;
|
[FieldMetadataType.RAW_JSON]: FieldMetadataDefaultValueRawJson;
|
||||||
[FieldMetadataType.RICH_TEXT]: FieldMetadataDefaultValueRichText;
|
[FieldMetadataType.RICH_TEXT]: FieldMetadataDefaultValueRichText;
|
||||||
[FieldMetadataType.ACTOR]: FieldMetadataDefaultActor;
|
[FieldMetadataType.ACTOR]: FieldMetadataDefaultActor;
|
||||||
@ -60,13 +61,21 @@ export type FieldMetadataFunctionDefaultValue = ExtractValueType<
|
|||||||
FieldMetadataDefaultValueUuidFunction | FieldMetadataDefaultValueNowFunction
|
FieldMetadataDefaultValueUuidFunction | FieldMetadataDefaultValueNowFunction
|
||||||
>;
|
>;
|
||||||
|
|
||||||
|
export type FieldMetadataDefaultValueForType<
|
||||||
|
T extends keyof FieldMetadataDefaultValueMapping,
|
||||||
|
> = ExtractValueType<FieldMetadataDefaultValueMapping[T]> | null;
|
||||||
|
|
||||||
|
export type FieldMetadataDefaultValueForAnyType = ExtractValueType<
|
||||||
|
UnionOfValues<FieldMetadataDefaultValueMapping>
|
||||||
|
> | null;
|
||||||
|
|
||||||
export type FieldMetadataDefaultValue<
|
export type FieldMetadataDefaultValue<
|
||||||
T extends FieldMetadataType = FieldMetadataType,
|
T extends FieldMetadataType = FieldMetadataType,
|
||||||
> =
|
> =
|
||||||
IsExactly<T, FieldMetadataType> extends true
|
IsExactly<T, FieldMetadataType> extends true
|
||||||
? ExtractValueType<UnionOfValues<FieldMetadataDefaultValueMapping>> | null
|
? FieldMetadataDefaultValueForAnyType
|
||||||
: T extends keyof FieldMetadataDefaultValueMapping
|
: T extends keyof FieldMetadataDefaultValueMapping
|
||||||
? ExtractValueType<FieldMetadataDefaultValueMapping[T]> | null
|
? FieldMetadataDefaultValueForType<T>
|
||||||
: never;
|
: never;
|
||||||
|
|
||||||
type FieldMetadataDefaultValueExtractedTypes = {
|
type FieldMetadataDefaultValueExtractedTypes = {
|
||||||
|
|||||||
@ -2,24 +2,28 @@ import { Injectable } from '@nestjs/common';
|
|||||||
|
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { assertUnreachable, isDefined } from 'twenty-shared/utils';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { FieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface';
|
import { FieldMetadataOptions } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-options.interface';
|
||||||
|
|
||||||
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
import { CreateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/create-field.input';
|
||||||
|
import {
|
||||||
|
FieldMetadataComplexOption,
|
||||||
|
FieldMetadataDefaultOption,
|
||||||
|
} from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||||
import { UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
import { UpdateFieldInput } from 'src/engine/metadata-modules/field-metadata/dtos/update-field.input';
|
||||||
import { FieldMetadataValidationService } from 'src/engine/metadata-modules/field-metadata/field-metadata-validation.service';
|
|
||||||
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
import { FieldMetadataEntity } from 'src/engine/metadata-modules/field-metadata/field-metadata.entity';
|
||||||
import {
|
import {
|
||||||
FieldMetadataException,
|
FieldMetadataException,
|
||||||
FieldMetadataExceptionCode,
|
FieldMetadataExceptionCode,
|
||||||
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
} from 'src/engine/metadata-modules/field-metadata/field-metadata.exception';
|
||||||
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
import { EnumFieldMetadataUnionType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||||
import {
|
import {
|
||||||
beneathDatabaseIdentifierMinimumLength,
|
beneathDatabaseIdentifierMinimumLength,
|
||||||
exceedsDatabaseIdentifierMaximumLength,
|
exceedsDatabaseIdentifierMaximumLength,
|
||||||
} from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils';
|
} from 'src/engine/metadata-modules/utils/validate-database-identifier-length.utils';
|
||||||
|
import { EnumFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/enum-column-action.factory';
|
||||||
import { isSnakeCaseString } from 'src/utils/is-snake-case-string';
|
import { isSnakeCaseString } from 'src/utils/is-snake-case-string';
|
||||||
|
|
||||||
type Validator<T> = { validator: (str: T) => boolean; message: string };
|
type Validator<T> = { validator: (str: T) => boolean; message: string };
|
||||||
@ -29,14 +33,14 @@ type FieldMetadataUpdateCreateInput = CreateFieldInput | UpdateFieldInput;
|
|||||||
type ValidateEnumFieldMetadataArgs = {
|
type ValidateEnumFieldMetadataArgs = {
|
||||||
existingFieldMetadata?: FieldMetadataEntity;
|
existingFieldMetadata?: FieldMetadataEntity;
|
||||||
fieldMetadataInput: FieldMetadataUpdateCreateInput;
|
fieldMetadataInput: FieldMetadataUpdateCreateInput;
|
||||||
fieldMetadataType: FieldMetadataType;
|
fieldMetadataType: EnumFieldMetadataUnionType;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const QUOTED_STRING_REGEX = /^['"](.*)['"]$/;
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class FieldMetadataEnumValidationService {
|
export class FieldMetadataEnumValidationService {
|
||||||
constructor(
|
constructor() {}
|
||||||
private readonly fieldMetadataValidationService: FieldMetadataValidationService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
private validatorRunner<T>(
|
private validatorRunner<T>(
|
||||||
elementToValidate: T,
|
elementToValidate: T,
|
||||||
@ -124,38 +128,22 @@ export class FieldMetadataEnumValidationService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private validateDuplicates(options: FieldMetadataOptions) {
|
private validateDuplicates(options: FieldMetadataOptions) {
|
||||||
const seenOptionIds = new Set<FieldMetadataOptions[number]['id']>();
|
const fieldsToCheckForDuplicates = [
|
||||||
const seenOptionValues = new Set<FieldMetadataOptions[number]['value']>();
|
'position',
|
||||||
const seenOptionPositions = new Set<
|
'id',
|
||||||
FieldMetadataOptions[number]['position']
|
'value',
|
||||||
>();
|
] as const satisfies (keyof FieldMetadataOptions[number])[];
|
||||||
|
const duplicatedValidators = fieldsToCheckForDuplicates.map<
|
||||||
|
Validator<FieldMetadataDefaultOption[] | FieldMetadataComplexOption[]>
|
||||||
|
>((field) => ({
|
||||||
|
message: `Duplicated option ${field}`,
|
||||||
|
validator: () =>
|
||||||
|
new Set(options.map((option) => option[field])).size !== options.length,
|
||||||
|
}));
|
||||||
|
|
||||||
for (const option of options) {
|
duplicatedValidators.forEach((validator) =>
|
||||||
if (seenOptionIds.has(option.id)) {
|
this.validatorRunner(options, validator),
|
||||||
throw new FieldMetadataException(
|
);
|
||||||
`Duplicated option id "${option.id}"`,
|
|
||||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seenOptionValues.has(option.value)) {
|
|
||||||
throw new FieldMetadataException(
|
|
||||||
`Duplicated option value "${option.value}"`,
|
|
||||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (seenOptionPositions.has(option.position)) {
|
|
||||||
throw new FieldMetadataException(
|
|
||||||
`Duplicated option position "${option.position}"`,
|
|
||||||
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
seenOptionIds.add(option.id);
|
|
||||||
seenOptionValues.add(option.value);
|
|
||||||
seenOptionPositions.add(option.position);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private validateFieldMetadataInputOptions(
|
private validateFieldMetadataInputOptions(
|
||||||
@ -179,15 +167,97 @@ export class FieldMetadataEnumValidationService {
|
|||||||
this.validateDuplicates(options);
|
this.validateDuplicates(options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private validateSelectDefaultValue(
|
||||||
|
options: FieldMetadataOptions,
|
||||||
|
defaultValue: unknown,
|
||||||
|
) {
|
||||||
|
if (typeof defaultValue !== 'string') {
|
||||||
|
throw new FieldMetadataException(
|
||||||
|
'Default value for multi-select must be a stringified array',
|
||||||
|
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validators: Validator<string>[] = [
|
||||||
|
{
|
||||||
|
validator: (value: string) => !QUOTED_STRING_REGEX.test(value),
|
||||||
|
message: 'Default value should be as quoted string',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (value: string) =>
|
||||||
|
!options.some(
|
||||||
|
(option) =>
|
||||||
|
option.value === value.replace(QUOTED_STRING_REGEX, '$1'),
|
||||||
|
),
|
||||||
|
message: `Default value "${defaultValue}" must be one of the option values`,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
validators.forEach((validator) =>
|
||||||
|
this.validatorRunner(defaultValue, validator),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateMultiSelectDefaultValue(
|
||||||
|
options: FieldMetadataOptions,
|
||||||
|
defaultValue: unknown,
|
||||||
|
) {
|
||||||
|
if (!Array.isArray(defaultValue)) {
|
||||||
|
throw new FieldMetadataException(
|
||||||
|
'Default value for multi-select must be an array',
|
||||||
|
FieldMetadataExceptionCode.INVALID_FIELD_INPUT,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const validators: Validator<string[]>[] = [
|
||||||
|
{
|
||||||
|
validator: (values) => values.length === 0,
|
||||||
|
message: 'If defined default value must contain at least one value',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: (values) => new Set(values).size !== values.length,
|
||||||
|
message: 'Default values must be unique',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
validators.forEach((validator) =>
|
||||||
|
this.validatorRunner(defaultValue, validator),
|
||||||
|
);
|
||||||
|
|
||||||
|
defaultValue.forEach((value) => {
|
||||||
|
this.validateSelectDefaultValue(options, value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private validateFieldMetadataDefaultValue(
|
||||||
|
fieldType: EnumFieldMetadataType,
|
||||||
|
options: FieldMetadataOptions,
|
||||||
|
defaultValue: unknown,
|
||||||
|
) {
|
||||||
|
switch (fieldType) {
|
||||||
|
case FieldMetadataType.SELECT:
|
||||||
|
this.validateSelectDefaultValue(options, defaultValue);
|
||||||
|
break;
|
||||||
|
case FieldMetadataType.MULTI_SELECT:
|
||||||
|
this.validateMultiSelectDefaultValue(options, defaultValue);
|
||||||
|
break;
|
||||||
|
case FieldMetadataType.RATING:
|
||||||
|
// TODO: Determine if RATING should be handled here
|
||||||
|
break;
|
||||||
|
default: {
|
||||||
|
assertUnreachable(
|
||||||
|
fieldType,
|
||||||
|
'Should never occur, unknown field metadata enum type',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async validateEnumFieldMetadataInput({
|
async validateEnumFieldMetadataInput({
|
||||||
fieldMetadataInput,
|
fieldMetadataInput,
|
||||||
fieldMetadataType,
|
fieldMetadataType,
|
||||||
existingFieldMetadata,
|
existingFieldMetadata,
|
||||||
}: ValidateEnumFieldMetadataArgs) {
|
}: ValidateEnumFieldMetadataArgs) {
|
||||||
if (!isEnumFieldMetadataType(fieldMetadataType)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const isUpdate = isDefined(existingFieldMetadata);
|
const isUpdate = isDefined(existingFieldMetadata);
|
||||||
const shouldSkipFieldMetadataInputOptionsValidation =
|
const shouldSkipFieldMetadataInputOptionsValidation =
|
||||||
isUpdate && fieldMetadataInput.options === undefined;
|
isUpdate && fieldMetadataInput.options === undefined;
|
||||||
@ -207,11 +277,11 @@ export class FieldMetadataEnumValidationService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.fieldMetadataValidationService.validateDefaultValueOrThrow({
|
this.validateFieldMetadataDefaultValue(
|
||||||
fieldType: fieldMetadataType,
|
fieldMetadataType,
|
||||||
options,
|
options,
|
||||||
defaultValue: fieldMetadataInput.defaultValue,
|
fieldMetadataInput.defaultValue,
|
||||||
});
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,15 +1,15 @@
|
|||||||
|
import { isDefined } from 'class-validator';
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
export const fieldMetadataEnumTypes = [
|
||||||
|
FieldMetadataType.MULTI_SELECT,
|
||||||
|
FieldMetadataType.SELECT,
|
||||||
|
FieldMetadataType.RATING,
|
||||||
|
] as const;
|
||||||
|
|
||||||
export type EnumFieldMetadataUnionType =
|
export type EnumFieldMetadataUnionType =
|
||||||
| FieldMetadataType.RATING
|
(typeof fieldMetadataEnumTypes)[number];
|
||||||
| FieldMetadataType.SELECT
|
|
||||||
| FieldMetadataType.MULTI_SELECT;
|
|
||||||
|
|
||||||
export const isEnumFieldMetadataType = (
|
export const isEnumFieldMetadataType = (
|
||||||
type: FieldMetadataType,
|
type: FieldMetadataType,
|
||||||
): type is EnumFieldMetadataUnionType => {
|
): type is EnumFieldMetadataUnionType =>
|
||||||
return (
|
isDefined(fieldMetadataEnumTypes.find((el) => type === el));
|
||||||
type === FieldMetadataType.RATING ||
|
|
||||||
type === FieldMetadataType.SELECT ||
|
|
||||||
type === FieldMetadataType.MULTI_SELECT
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|||||||
@ -85,7 +85,7 @@ export const validateDefaultValueForType = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
const validationResults = validators.map((validator) => {
|
const validationResults = validators.map((validator) => {
|
||||||
const conputedDefaultValue = isCompositeFieldMetadataType(type)
|
const computedDefaultValue = isCompositeFieldMetadataType(type)
|
||||||
? defaultValue
|
? defaultValue
|
||||||
: { value: defaultValue };
|
: { value: defaultValue };
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ export const validateDefaultValueForType = (
|
|||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
any,
|
any,
|
||||||
FieldMetadataClassValidation
|
FieldMetadataClassValidation
|
||||||
>(validator, conputedDefaultValue as FieldMetadataClassValidation);
|
>(validator, computedDefaultValue as FieldMetadataClassValidation);
|
||||||
|
|
||||||
const errors = validateSync(defaultValueInstance, {
|
const errors = validateSync(defaultValueInstance, {
|
||||||
whitelist: true,
|
whitelist: true,
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
import { FieldMetadataDefaultValue } from 'src/engine/metadata-modules/field-metadata/interfaces/field-metadata-default-value.interface';
|
|
||||||
import {
|
import {
|
||||||
WorkspaceHealthIssue,
|
WorkspaceHealthIssue,
|
||||||
WorkspaceHealthIssueType,
|
WorkspaceHealthIssueType,
|
||||||
@ -17,10 +17,7 @@ import {
|
|||||||
computeCompositeColumnName,
|
computeCompositeColumnName,
|
||||||
} from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
} from 'src/engine/metadata-modules/field-metadata/utils/compute-column-name.util';
|
||||||
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
import { isCompositeFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-composite-field-metadata-type.util';
|
||||||
import {
|
import { isEnumFieldMetadataType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||||
EnumFieldMetadataUnionType,
|
|
||||||
isEnumFieldMetadataType,
|
|
||||||
} from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
|
||||||
import { serializeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-default-value';
|
import { serializeDefaultValue } from 'src/engine/metadata-modules/field-metadata/utils/serialize-default-value';
|
||||||
import { validateDefaultValueForType } from 'src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util';
|
import { validateDefaultValueForType } from 'src/engine/metadata-modules/field-metadata/utils/validate-default-value-for-type.util';
|
||||||
import { validateOptionsForType } from 'src/engine/metadata-modules/field-metadata/utils/validate-options-for-type.util';
|
import { validateOptionsForType } from 'src/engine/metadata-modules/field-metadata/utils/validate-options-for-type.util';
|
||||||
@ -291,15 +288,34 @@ export class FieldMetadataHealthService {
|
|||||||
const enumValues = fieldMetadata.options?.map((option) =>
|
const enumValues = fieldMetadata.options?.map((option) =>
|
||||||
serializeDefaultValue(`'${option.value}'`),
|
serializeDefaultValue(`'${option.value}'`),
|
||||||
);
|
);
|
||||||
const metadataDefaultValue =
|
const metadataDefaultValue = fieldMetadata.defaultValue;
|
||||||
fieldMetadata.defaultValue as FieldMetadataDefaultValue<EnumFieldMetadataUnionType>;
|
|
||||||
|
|
||||||
if (metadataDefaultValue && !enumValues.includes(metadataDefaultValue)) {
|
if (isDefined(metadataDefaultValue)) {
|
||||||
issues.push({
|
if (fieldMetadata.type === FieldMetadataType.MULTI_SELECT) {
|
||||||
type: WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_NOT_VALID,
|
if (!Array.isArray(metadataDefaultValue)) {
|
||||||
fieldMetadata,
|
issues.push({
|
||||||
message: `Column default value is not in the enum values "${metadataDefaultValue}" NOT IN "${enumValues}"`,
|
type: WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_NOT_VALID,
|
||||||
});
|
fieldMetadata,
|
||||||
|
message: `Column default value for multi-select must be an array, got "${metadataDefaultValue}"`,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
metadataDefaultValue.forEach((value) => {
|
||||||
|
if (!enumValues.includes(value)) {
|
||||||
|
issues.push({
|
||||||
|
type: WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_NOT_VALID,
|
||||||
|
fieldMetadata,
|
||||||
|
message: `Column default value "${value}" is not in the enum values "${enumValues}"`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else if (enumValues.includes(metadataDefaultValue as string)) {
|
||||||
|
issues.push({
|
||||||
|
type: WorkspaceHealthIssueType.COLUMN_DEFAULT_VALUE_NOT_VALID,
|
||||||
|
fieldMetadata,
|
||||||
|
message: `Column default value is not in the enum values "${metadataDefaultValue}" NOT IN "${enumValues}"`,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,68 @@
|
|||||||
|
import { EachTestingContext } from 'twenty-shared/testing';
|
||||||
|
|
||||||
|
import { trimAndRemoveDuplicatedWhitespacesFromString } from 'src/utils/trim-and-remove-duplicated-whitespaces-from-string';
|
||||||
|
|
||||||
|
type TrimAndRemoveWhitespacesTestCase = EachTestingContext<{
|
||||||
|
input: string;
|
||||||
|
expected: string;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
describe('trim-and-remove-duplicated-whitespaces-from-string', () => {
|
||||||
|
const testCases: TrimAndRemoveWhitespacesTestCase[] = [
|
||||||
|
{
|
||||||
|
title: 'should trim and remove duplicated whitespaces from a string',
|
||||||
|
context: {
|
||||||
|
input: ' Hello World ',
|
||||||
|
expected: 'Hello World',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should handle string with multiple spaces between words',
|
||||||
|
context: {
|
||||||
|
input: 'This is a test',
|
||||||
|
expected: 'This is a test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should handle string with only whitespaces',
|
||||||
|
context: {
|
||||||
|
input: ' ',
|
||||||
|
expected: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should handle empty string',
|
||||||
|
context: {
|
||||||
|
input: '',
|
||||||
|
expected: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should handle string with tabs and newlines',
|
||||||
|
context: {
|
||||||
|
input: 'Hello\t\t\tWorld\n\n Test',
|
||||||
|
expected: 'Hello World Test',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should handle string with leading and trailing spaces',
|
||||||
|
context: {
|
||||||
|
input: ' Leading and trailing spaces ',
|
||||||
|
expected: 'Leading and trailing spaces',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should preserve single spaces between words',
|
||||||
|
context: {
|
||||||
|
input: 'This is already properly spaced',
|
||||||
|
expected: 'This is already properly spaced',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
test.each(testCases)('$title', ({ context: { input, expected } }) => {
|
||||||
|
const result = trimAndRemoveDuplicatedWhitespacesFromString(input);
|
||||||
|
|
||||||
|
expect(result).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -1,4 +1,4 @@
|
|||||||
import { isDefined } from 'twenty-shared/utils';
|
import { trimAndRemoveDuplicatedWhitespacesFromString } from 'src/utils/trim-and-remove-duplicated-whitespaces-from-string';
|
||||||
|
|
||||||
type OnlyStringPropertiesKey<T> = Extract<keyof T, string>;
|
type OnlyStringPropertiesKey<T> = Extract<keyof T, string>;
|
||||||
|
|
||||||
@ -8,9 +8,6 @@ type StringPropertyKeys<T> = {
|
|||||||
: never;
|
: never;
|
||||||
}[OnlyStringPropertiesKey<T>];
|
}[OnlyStringPropertiesKey<T>];
|
||||||
|
|
||||||
const sanitizeString = (str: string | null) =>
|
|
||||||
isDefined(str) ? str.trim().replace(/\s+/g, ' ') : str;
|
|
||||||
|
|
||||||
export const trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties = <T>(
|
export const trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties = <T>(
|
||||||
obj: T,
|
obj: T,
|
||||||
keys: StringPropertyKeys<T>[],
|
keys: StringPropertyKeys<T>[],
|
||||||
@ -18,13 +15,17 @@ export const trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties = <T>(
|
|||||||
return keys.reduce((acc, key) => {
|
return keys.reduce((acc, key) => {
|
||||||
const occurrence = acc[key];
|
const occurrence = acc[key];
|
||||||
|
|
||||||
if (occurrence === undefined || typeof occurrence !== 'string') {
|
if (
|
||||||
|
occurrence === undefined ||
|
||||||
|
typeof occurrence !== 'string' ||
|
||||||
|
occurrence === null
|
||||||
|
) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...acc,
|
...acc,
|
||||||
[key]: sanitizeString(acc[key] as string | null),
|
[key]: trimAndRemoveDuplicatedWhitespacesFromString(occurrence),
|
||||||
};
|
};
|
||||||
}, obj);
|
}, obj);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,4 @@
|
|||||||
|
const MULTIPLE_WHITESPACE_REGEX = /\s+/g;
|
||||||
|
|
||||||
|
export const trimAndRemoveDuplicatedWhitespacesFromString = (str: string) =>
|
||||||
|
str.trim().replace(MULTIPLE_WHITESPACE_REGEX, ' ');
|
||||||
@ -1,321 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with an invalid default value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Default value for existing options is invalid: invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with an unknown default value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Default value for existing options is invalid: 'OPTION_424242'",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with comma in option label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Label must not contain a comma",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with duplicated option ids 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Duplicated option id "fd1f11fd-3f05-4a33-bddf-800c3412ce98"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with duplicated option positions 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Duplicated option position "1"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with duplicated option values 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Duplicated option value "OPTION_1"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with duplicated trimmed option values 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Duplicated option value "OPTION_1"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with empty options 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Options are required for enum fields",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with empty string id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with empty string label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label "" is beneath 1 character",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with empty string value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value "" is beneath 1 character",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with invalid option id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with invalid option value format 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Value must be in UPPER_CASE and follow snake_case "Option 1 and some other things, /"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with not a string id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with not a string label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "INTERNAL_SERVER_ERROR",
|
|
||||||
"exceptionEventId": "mocked-exception-id",
|
|
||||||
},
|
|
||||||
"message": "label.includes is not a function",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with not a string value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Value must be in UPPER_CASE and follow snake_case "22222"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with null id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with null label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with null options 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Options are required for enum fields",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with null value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with only white spaces id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with only white spaces label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label "" is beneath 1 character",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with only white spaces value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value "" is beneath 1 character",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with too long id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with too long label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with too long value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with undefined option label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with undefined option value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select creation tests group Create should fail with undefined options 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Options are required for enum fields",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
@ -1,310 +0,0 @@
|
|||||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with an invalid default value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Default value for existing options is invalid: invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with an unknown default value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Default value for existing options is invalid: 'OPTION_424242'",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with comma in option label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Label must not contain a comma",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with duplicated option ids 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Duplicated option id "fd1f11fd-3f05-4a33-bddf-800c3412ce98"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with duplicated option positions 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Duplicated option position "1"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with duplicated option values 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Duplicated option value "OPTION_1"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with duplicated trimmed option values 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Duplicated option value "OPTION_1"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with empty options 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Options are required for enum fields",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with empty string id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with empty string label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label "" is beneath 1 character",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with empty string value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value "" is beneath 1 character",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with invalid option id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with invalid option value format 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Value must be in UPPER_CASE and follow snake_case "Option 1 and some other things, /"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with not a string id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with not a string label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "INTERNAL_SERVER_ERROR",
|
|
||||||
"exceptionEventId": "mocked-exception-id",
|
|
||||||
},
|
|
||||||
"message": "label.includes is not a function",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with not a string value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Value must be in UPPER_CASE and follow snake_case "22222"",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with null id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with null label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with null value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with only white spaces id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with only white spaces label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label "" is beneath 1 character",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with only white spaces value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value "" is beneath 1 character",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with too long id 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option id is invalid",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with too long label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with too long value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with undefined option label 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option label is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with undefined option value 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Option value is required",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
|
|
||||||
exports[`Field metadata select update tests group Update should fail with unknown default value and no options 1`] = `
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"extensions": {
|
|
||||||
"code": "BAD_USER_INPUT",
|
|
||||||
},
|
|
||||||
"message": "Default value for existing options is invalid: 'OPTION_42'",
|
|
||||||
},
|
|
||||||
]
|
|
||||||
`;
|
|
||||||
@ -1,128 +0,0 @@
|
|||||||
import {
|
|
||||||
UPDATE_CREATE_ONE_FIELD_METADATA_SELECT_TEST_CASES,
|
|
||||||
UpdateCreateFieldMetadataSelectTestCase,
|
|
||||||
} from 'test/integration/metadata/suites/field-metadata/update-create-one-field-metadata-select-tests-cases';
|
|
||||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
|
||||||
import {
|
|
||||||
LISTING_NAME_PLURAL,
|
|
||||||
LISTING_NAME_SINGULAR,
|
|
||||||
} from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant';
|
|
||||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
|
||||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
|
|
||||||
import {
|
|
||||||
FieldMetadataComplexOption,
|
|
||||||
FieldMetadataDefaultOption,
|
|
||||||
} from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
|
||||||
|
|
||||||
const { failingTestCases, successfulTestCases } =
|
|
||||||
UPDATE_CREATE_ONE_FIELD_METADATA_SELECT_TEST_CASES;
|
|
||||||
|
|
||||||
describe('Field metadata select creation tests group', () => {
|
|
||||||
let createdObjectMetadataId: string;
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const { data } = await createOneObjectMetadata({
|
|
||||||
input: {
|
|
||||||
labelSingular: LISTING_NAME_SINGULAR,
|
|
||||||
labelPlural: LISTING_NAME_PLURAL,
|
|
||||||
nameSingular: LISTING_NAME_SINGULAR,
|
|
||||||
namePlural: LISTING_NAME_PLURAL,
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isLabelSyncedWithName: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createdObjectMetadataId = data.createOneObject.id;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await deleteOneObjectMetadata({
|
|
||||||
input: { idToDelete: createdObjectMetadataId },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
test.each(successfulTestCases)(
|
|
||||||
'Create $title',
|
|
||||||
async ({ context: { input, expectedOptions } }) => {
|
|
||||||
const { data, errors } = await createOneFieldMetadata({
|
|
||||||
input: {
|
|
||||||
objectMetadataId: createdObjectMetadataId,
|
|
||||||
type: FieldMetadataType.SELECT,
|
|
||||||
name: 'testField',
|
|
||||||
label: 'Test Field',
|
|
||||||
isLabelSyncedWithName: false,
|
|
||||||
...input,
|
|
||||||
},
|
|
||||||
gqlFields: `
|
|
||||||
id
|
|
||||||
options
|
|
||||||
defaultValue
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(data).not.toBeNull();
|
|
||||||
expect(data.createOneField).toBeDefined();
|
|
||||||
const createdOptions:
|
|
||||||
| FieldMetadataDefaultOption[]
|
|
||||||
| FieldMetadataComplexOption[] = data.createOneField.options;
|
|
||||||
|
|
||||||
const optionsToCompare = expectedOptions ?? input.options;
|
|
||||||
|
|
||||||
expect(errors).toBeUndefined();
|
|
||||||
expect(createdOptions.length).toBe(optionsToCompare.length);
|
|
||||||
createdOptions.forEach((option) => expect(option.id).toBeDefined());
|
|
||||||
expect(createdOptions).toMatchObject(optionsToCompare);
|
|
||||||
|
|
||||||
if (isDefined(input.defaultValue)) {
|
|
||||||
expect(data.createOneField.defaultValue).toEqual(input.defaultValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const createSpecificFailingTestCases: UpdateCreateFieldMetadataSelectTestCase[] =
|
|
||||||
[
|
|
||||||
{
|
|
||||||
title: 'should fail with null options',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: null as unknown as FieldMetadataComplexOption[],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with undefined options',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: undefined as unknown as FieldMetadataComplexOption[],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
test.each([...failingTestCases, ...createSpecificFailingTestCases])(
|
|
||||||
'Create $title',
|
|
||||||
async ({ context: { input } }) => {
|
|
||||||
const { data, errors } = await createOneFieldMetadata({
|
|
||||||
input: {
|
|
||||||
objectMetadataId: createdObjectMetadataId,
|
|
||||||
type: FieldMetadataType.SELECT,
|
|
||||||
name: 'testField',
|
|
||||||
label: 'Test Field',
|
|
||||||
isLabelSyncedWithName: false,
|
|
||||||
...input,
|
|
||||||
},
|
|
||||||
gqlFields: `
|
|
||||||
id
|
|
||||||
options
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(data).toBeNull();
|
|
||||||
expect(errors).toBeDefined();
|
|
||||||
expect(errors).toMatchSnapshot();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@ -0,0 +1,773 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with an invalid default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with an unknown default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with comma in option label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Label must not contain a comma",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with duplicated option ids 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option id",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with duplicated option positions 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option position",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with duplicated option values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with duplicated trimmed option values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with empty multi-select default values array 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "If defined default value must contain at least one value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with empty options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Options are required for enum fields",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with empty string defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with empty string id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with empty string label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with empty string value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with invalid multi-select default value format 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with invalid option id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with invalid option value format 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Value must be in UPPER_CASE and follow snake_case "Option 1 and some other things, /"",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with non stringified array default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with not a string defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with not a string id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with not a string label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "INTERNAL_SERVER_ERROR",
|
||||||
|
"exceptionEventId": "mocked-exception-id",
|
||||||
|
},
|
||||||
|
"message": "label.includes is not a function",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with not a string value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Value must be in UPPER_CASE and follow snake_case "22222"",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with null id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with null label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with null options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Options are required for enum fields",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with null value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with only white spaces defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with only white spaces id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with only white spaces label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with only white spaces value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with too long defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with too long id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with too long label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with too long value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with undefined option label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with undefined option value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with undefined options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Options are required for enum fields",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata MULTI_SELECT tests suite Create should fail with unknown multi-select default values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value "'UNKNOWN_OPTION'" must be one of the option values",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with an invalid default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with an unknown default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value "'OPTION_424242'" must be one of the option values",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with comma in option label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Label must not contain a comma",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with duplicated option ids 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option id",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with duplicated option positions 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option position",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with duplicated option values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with duplicated trimmed option values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with empty options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Options are required for enum fields",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with empty string defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with empty string id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with empty string label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with empty string value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with invalid option id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with invalid option value format 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Value must be in UPPER_CASE and follow snake_case "Option 1 and some other things, /"",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with not a string defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be a stringified array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with not a string id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with not a string label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "INTERNAL_SERVER_ERROR",
|
||||||
|
"exceptionEventId": "mocked-exception-id",
|
||||||
|
},
|
||||||
|
"message": "label.includes is not a function",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with not a string value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Value must be in UPPER_CASE and follow snake_case "22222"",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with null id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with null label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with null options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Options are required for enum fields",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with null value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with only white spaces defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with only white spaces id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with only white spaces label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with only white spaces value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with too long defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with too long id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with too long label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with too long value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with undefined option label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with undefined option value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Create field metadata SELECT tests suite Create should fail with undefined options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Options are required for enum fields",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
@ -0,0 +1,751 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with an invalid default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with an unknown default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with comma in option label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Label must not contain a comma",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with duplicated option ids 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option id",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with duplicated option positions 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option position",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with duplicated option values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with duplicated trimmed option values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with empty multi-select default values array 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "If defined default value must contain at least one value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with empty options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Options are required for enum fields",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with empty string defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with empty string id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with empty string label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with empty string value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with invalid multi-select default value format 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with invalid option id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with invalid option value format 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Value must be in UPPER_CASE and follow snake_case "Option 1 and some other things, /"",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with non stringified array default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with not a string defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with not a string id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with not a string label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "INTERNAL_SERVER_ERROR",
|
||||||
|
"exceptionEventId": "mocked-exception-id",
|
||||||
|
},
|
||||||
|
"message": "label.includes is not a function",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with not a string value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Value must be in UPPER_CASE and follow snake_case "22222"",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with null id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with null label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with null value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with only white spaces defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with only white spaces id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with only white spaces label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with only white spaces value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with too long defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be an array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with too long id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with too long label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with too long value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with undefined option label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with undefined option value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with unknown default value and no options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value "'OPTION_42'" must be one of the option values",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata MULTI_SELECT tests suite Update should fail with unknown multi-select default values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value "'UNKNOWN_OPTION'" must be one of the option values",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with an invalid default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with an unknown default value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value "'OPTION_424242'" must be one of the option values",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with comma in option label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Label must not contain a comma",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with duplicated option ids 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option id",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with duplicated option positions 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option position",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with duplicated option values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with duplicated trimmed option values 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Duplicated option value",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with empty options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Options are required for enum fields",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with empty string defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with empty string id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with empty string label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with empty string value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with invalid option id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with invalid option value format 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Value must be in UPPER_CASE and follow snake_case "Option 1 and some other things, /"",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with not a string defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value for multi-select must be a stringified array",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with not a string id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with not a string label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "INTERNAL_SERVER_ERROR",
|
||||||
|
"exceptionEventId": "mocked-exception-id",
|
||||||
|
},
|
||||||
|
"message": "label.includes is not a function",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with not a string value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Value must be in UPPER_CASE and follow snake_case "22222"",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with null id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with null label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with null value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with only white spaces defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with only white spaces id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with only white spaces label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with only white spaces value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "" is beneath 1 character",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with too long defaultValue 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value should be as quoted string",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with too long id 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option id is invalid",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with too long label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with too long value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" exceeds 63 characters",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with undefined option label 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option label is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with undefined option value 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Option value is required",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`Update field metadata SELECT tests suite Update should fail with unknown default value and no options 1`] = `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"extensions": {
|
||||||
|
"code": "BAD_USER_INPUT",
|
||||||
|
},
|
||||||
|
"message": "Default value "'OPTION_42'" must be one of the option values",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
@ -0,0 +1,98 @@
|
|||||||
|
import { SELECT_AND_MULTI_SELECT_OPERATION_AGNOSTIC_SUCCESSFUL_AND_FAILING_TEST_CASES } from 'test/integration/metadata/suites/field-metadata/enum/common/select-and-multi-select-operation-agnostic-tests-cases';
|
||||||
|
import { FieldMetadataEnumSuccessfulAndFailingTestCases } from 'test/integration/metadata/suites/field-metadata/enum/types/fieldMetadataEnumSuccessfulAndFailingTestCases';
|
||||||
|
|
||||||
|
export const MUTLI_SELECT_OPERATION_AGNOSTIC_TEST_CASES: FieldMetadataEnumSuccessfulAndFailingTestCases =
|
||||||
|
{
|
||||||
|
successful: [
|
||||||
|
...SELECT_AND_MULTI_SELECT_OPERATION_AGNOSTIC_SUCCESSFUL_AND_FAILING_TEST_CASES.successful,
|
||||||
|
{
|
||||||
|
title: 'should succeed with valid multi-select default values',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: ["'OPTION_1'", "'OPTION_2'"],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Option 2',
|
||||||
|
value: 'OPTION_2',
|
||||||
|
color: 'blue',
|
||||||
|
position: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
failing: [
|
||||||
|
...SELECT_AND_MULTI_SELECT_OPERATION_AGNOSTIC_SUCCESSFUL_AND_FAILING_TEST_CASES.failing,
|
||||||
|
{
|
||||||
|
title: 'should fail with non stringified array default value',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: 'Option_1',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with empty multi-select default values array',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: [],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with unknown multi-select default values',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: ["'OPTION_1'", "'UNKNOWN_OPTION'"],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with invalid multi-select default value format',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: ['invalid', 'format'],
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -0,0 +1,354 @@
|
|||||||
|
import { v4 } from 'uuid';
|
||||||
|
import { FieldMetadataEnumSuccessfulAndFailingTestCases } from 'test/integration/metadata/suites/field-metadata/enum/types/fieldMetadataEnumSuccessfulAndFailingTestCases';
|
||||||
|
import { UpdateCreateFieldMetadataSelectTestCase } from 'test/integration/metadata/suites/field-metadata/enum/types/update-create-field-metadata-enum-test-case';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
import { FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||||
|
|
||||||
|
const basicFailingStringEdgeCaseInputs: {
|
||||||
|
label: string;
|
||||||
|
input: string | undefined | number | null;
|
||||||
|
}[] = [
|
||||||
|
{ input: ' ', label: 'only white spaces' },
|
||||||
|
{ input: '', label: 'empty string' },
|
||||||
|
{ input: null, label: 'null' },
|
||||||
|
{ input: 22222, label: 'not a string' },
|
||||||
|
{ input: 'a'.repeat(64), label: 'too long' },
|
||||||
|
];
|
||||||
|
|
||||||
|
const stringFields: (keyof FieldMetadataComplexOption)[] = [
|
||||||
|
'id',
|
||||||
|
'label',
|
||||||
|
'value',
|
||||||
|
];
|
||||||
|
const fuzzedOptionsStringFieldFailingTestCases: UpdateCreateFieldMetadataSelectTestCase[] =
|
||||||
|
stringFields.flatMap((field) => {
|
||||||
|
return basicFailingStringEdgeCaseInputs.map<UpdateCreateFieldMetadataSelectTestCase>(
|
||||||
|
({ input, label }) => ({
|
||||||
|
title: `should fail with ${label} ${field}`,
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
[field]: input,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
const fuzzedDefaultValueFailingTestCases: UpdateCreateFieldMetadataSelectTestCase[] =
|
||||||
|
basicFailingStringEdgeCaseInputs
|
||||||
|
.filter((el) => isDefined(el.input))
|
||||||
|
.map(({ input, label }) => ({
|
||||||
|
title: `should fail with ${label} defaultValue`,
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: input,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
export const SELECT_AND_MULTI_SELECT_OPERATION_AGNOSTIC_SUCCESSFUL_AND_FAILING_TEST_CASES: FieldMetadataEnumSuccessfulAndFailingTestCases =
|
||||||
|
{
|
||||||
|
failing: [
|
||||||
|
...fuzzedDefaultValueFailingTestCases,
|
||||||
|
...fuzzedOptionsStringFieldFailingTestCases,
|
||||||
|
{
|
||||||
|
title: 'should fail with invalid option id',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
id: 'not a uuid',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with empty options',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with invalid option value format',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'Option 1 and some other things, /',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with comma in option label',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option ,1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with duplicated option values',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with duplicated option ids',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
id: 'fd1f11fd-3f05-4a33-bddf-800c3412ce98',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Option 2',
|
||||||
|
value: 'OPTION_2',
|
||||||
|
color: 'green',
|
||||||
|
position: 2,
|
||||||
|
id: 'fd1f11fd-3f05-4a33-bddf-800c3412ce98',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with duplicated option positions',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Option 2',
|
||||||
|
value: 'OPTION_2',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with duplicated trimmed option values',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: ' OPTION_1 ',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Option 2',
|
||||||
|
value: ' OPTION_1 ',
|
||||||
|
color: 'green',
|
||||||
|
position: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with undefined option label',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: undefined as unknown as string,
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with an invalid default value',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: 'invalid',
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with an unknown default value',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: "'OPTION_424242'",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with undefined option value',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: undefined as unknown as string,
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
successful: [
|
||||||
|
{
|
||||||
|
title: 'should succeed with provided option id',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
id: '26c602c3-cba9-4d83-92d4-4ba7dbae2f31',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should succeed with various options id',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: Array.from({ length: 42 }, (_value, index) => {
|
||||||
|
const optionWithoutId: FieldMetadataComplexOption = {
|
||||||
|
label: `Option ${index}`,
|
||||||
|
value: `OPTION_${index}`,
|
||||||
|
color: 'green',
|
||||||
|
position: index,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (index % 2 === 0) {
|
||||||
|
return {
|
||||||
|
...optionWithoutId,
|
||||||
|
id: v4(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return optionWithoutId;
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should succeed without option id',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should trim option values',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: ' Option 1 ',
|
||||||
|
value: ' OPTION_1 ',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
expectedOptions: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
import { SELECT_AND_MULTI_SELECT_OPERATION_AGNOSTIC_SUCCESSFUL_AND_FAILING_TEST_CASES } from 'test/integration/metadata/suites/field-metadata/enum/common/select-and-multi-select-operation-agnostic-tests-cases';
|
||||||
|
import { FieldMetadataEnumSuccessfulAndFailingTestCases } from 'test/integration/metadata/suites/field-metadata/enum/types/fieldMetadataEnumSuccessfulAndFailingTestCases';
|
||||||
|
|
||||||
|
export const SELECT_OPERATION_AGNOSTIC_TEST_CASES: FieldMetadataEnumSuccessfulAndFailingTestCases =
|
||||||
|
{
|
||||||
|
failing: [
|
||||||
|
...SELECT_AND_MULTI_SELECT_OPERATION_AGNOSTIC_SUCCESSFUL_AND_FAILING_TEST_CASES.failing,
|
||||||
|
],
|
||||||
|
successful: [
|
||||||
|
...SELECT_AND_MULTI_SELECT_OPERATION_AGNOSTIC_SUCCESSFUL_AND_FAILING_TEST_CASES.successful,
|
||||||
|
{
|
||||||
|
title: 'should succeed with valid default value',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: "'OPTION_1'",
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
id: '26c602c3-cba9-4d83-92d4-4ba7dbae2f31',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should succeed with null default value',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: null,
|
||||||
|
options: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
id: '26c602c3-cba9-4d83-92d4-4ba7dbae2f31',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
@ -0,0 +1,49 @@
|
|||||||
|
import { MUTLI_SELECT_OPERATION_AGNOSTIC_TEST_CASES } from 'test/integration/metadata/suites/field-metadata/enum/common/multi-select-operation-agnostic-test-cases';
|
||||||
|
import { SELECT_OPERATION_AGNOSTIC_TEST_CASES } from 'test/integration/metadata/suites/field-metadata/enum/common/select-operation-agnostic-test-cases';
|
||||||
|
import { FieldMetadataEnumSuccessfulAndFailingTestCases } from 'test/integration/metadata/suites/field-metadata/enum/types/fieldMetadataEnumSuccessfulAndFailingTestCases';
|
||||||
|
import { UpdateCreateFieldMetadataSelectTestCase } from 'test/integration/metadata/suites/field-metadata/enum/types/update-create-field-metadata-enum-test-case';
|
||||||
|
|
||||||
|
import { FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||||
|
import { EnumFieldMetadataUnionType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||||
|
|
||||||
|
const fieldMetadataTypeAgnosticCreateFailingTestCases: UpdateCreateFieldMetadataSelectTestCase[] =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
title: 'should fail with null options',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: null as unknown as FieldMetadataComplexOption[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'should fail with undefined options',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
options: undefined as unknown as FieldMetadataComplexOption[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const CREATE_ENUM_FIELD_METADATA_TEST_CASES: Partial<
|
||||||
|
Record<
|
||||||
|
EnumFieldMetadataUnionType,
|
||||||
|
FieldMetadataEnumSuccessfulAndFailingTestCases
|
||||||
|
>
|
||||||
|
> = {
|
||||||
|
MULTI_SELECT: {
|
||||||
|
failing: [
|
||||||
|
...MUTLI_SELECT_OPERATION_AGNOSTIC_TEST_CASES.failing,
|
||||||
|
...fieldMetadataTypeAgnosticCreateFailingTestCases,
|
||||||
|
],
|
||||||
|
successful: [...MUTLI_SELECT_OPERATION_AGNOSTIC_TEST_CASES.successful],
|
||||||
|
},
|
||||||
|
SELECT: {
|
||||||
|
failing: [
|
||||||
|
...SELECT_OPERATION_AGNOSTIC_TEST_CASES.failing,
|
||||||
|
...fieldMetadataTypeAgnosticCreateFailingTestCases,
|
||||||
|
],
|
||||||
|
successful: [...SELECT_OPERATION_AGNOSTIC_TEST_CASES.successful],
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,116 @@
|
|||||||
|
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||||
|
import {
|
||||||
|
LISTING_NAME_PLURAL,
|
||||||
|
LISTING_NAME_SINGULAR,
|
||||||
|
} from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant';
|
||||||
|
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||||
|
import { forceCreateOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/force-create-one-object-metadata.util';
|
||||||
|
import { CREATE_ENUM_FIELD_METADATA_TEST_CASES } from 'test/integration/metadata/suites/field-metadata/enum/create-enum-field-metadata-test-cases';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FieldMetadataComplexOption,
|
||||||
|
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)(
|
||||||
|
'Create field metadata %s tests suite',
|
||||||
|
(testedFieldMetadataType) => {
|
||||||
|
let createdObjectMetadataId: string;
|
||||||
|
const testCases =
|
||||||
|
CREATE_ENUM_FIELD_METADATA_TEST_CASES[testedFieldMetadataType];
|
||||||
|
|
||||||
|
if (!isDefined(testCases)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { failing: failingTestCases, successful: successfulTestCases } =
|
||||||
|
testCases;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const { data } = await forceCreateOneObjectMetadata({
|
||||||
|
input: {
|
||||||
|
labelSingular: LISTING_NAME_SINGULAR,
|
||||||
|
labelPlural: LISTING_NAME_PLURAL,
|
||||||
|
nameSingular: LISTING_NAME_SINGULAR,
|
||||||
|
namePlural: LISTING_NAME_PLURAL,
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
createdObjectMetadataId = data.createOneObject.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await deleteOneObjectMetadata({
|
||||||
|
input: { idToDelete: createdObjectMetadataId },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(successfulTestCases)(
|
||||||
|
'Create $title',
|
||||||
|
async ({ context: { input, expectedOptions } }) => {
|
||||||
|
const { data, errors } = await createOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
objectMetadataId: createdObjectMetadataId,
|
||||||
|
type: testedFieldMetadataType,
|
||||||
|
name: 'testField',
|
||||||
|
label: 'Test Field',
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
...input,
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
options
|
||||||
|
defaultValue
|
||||||
|
type
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(data).not.toBeNull();
|
||||||
|
expect(data.createOneField).toBeDefined();
|
||||||
|
expect(data.createOneField.type).toEqual(testedFieldMetadataType);
|
||||||
|
const createdOptions:
|
||||||
|
| FieldMetadataDefaultOption[]
|
||||||
|
| FieldMetadataComplexOption[] = data.createOneField.options;
|
||||||
|
|
||||||
|
const optionsToCompare = expectedOptions ?? input.options;
|
||||||
|
|
||||||
|
expect(errors).toBeUndefined();
|
||||||
|
expect(createdOptions.length).toBe(optionsToCompare.length);
|
||||||
|
createdOptions.forEach((option) => expect(option.id).toBeDefined());
|
||||||
|
expect(createdOptions).toMatchObject(optionsToCompare);
|
||||||
|
|
||||||
|
if (isDefined(input.defaultValue)) {
|
||||||
|
expect(data.createOneField.defaultValue).toEqual(input.defaultValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each(failingTestCases)(
|
||||||
|
'Create $title',
|
||||||
|
async ({ context: { input } }) => {
|
||||||
|
const { data, errors } = await createOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
objectMetadataId: createdObjectMetadataId,
|
||||||
|
type: testedFieldMetadataType,
|
||||||
|
name: 'testField',
|
||||||
|
label: 'Test Field',
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
...input,
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
options
|
||||||
|
defaultValue
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(data).toBeNull();
|
||||||
|
expect(errors).toBeDefined();
|
||||||
|
expect(errors).toMatchSnapshot();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { UpdateCreateFieldMetadataSelectTestCase } from 'test/integration/metadata/suites/field-metadata/enum/types/update-create-field-metadata-enum-test-case';
|
||||||
|
import { SuccessfulAndFailingTestCases } from 'twenty-shared/testing';
|
||||||
|
|
||||||
|
export type FieldMetadataEnumSuccessfulAndFailingTestCases =
|
||||||
|
SuccessfulAndFailingTestCases<
|
||||||
|
UpdateCreateFieldMetadataSelectTestCase['context']
|
||||||
|
>;
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
import { CreateOneFieldFactoryInput } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util';
|
||||||
|
import { EachTestingContext } from 'twenty-shared/testing';
|
||||||
|
|
||||||
|
import { FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||||
|
|
||||||
|
export type UpdateCreateFieldMetadataSelectTestCase = EachTestingContext<{
|
||||||
|
input: Partial<Pick<CreateOneFieldFactoryInput, 'defaultValue'>> &
|
||||||
|
Required<Pick<CreateOneFieldFactoryInput, 'options'>>;
|
||||||
|
expectedOptions?: FieldMetadataComplexOption[];
|
||||||
|
}>;
|
||||||
@ -0,0 +1,94 @@
|
|||||||
|
import { MUTLI_SELECT_OPERATION_AGNOSTIC_TEST_CASES } from 'test/integration/metadata/suites/field-metadata/enum/common/multi-select-operation-agnostic-test-cases';
|
||||||
|
import { SELECT_OPERATION_AGNOSTIC_TEST_CASES } from 'test/integration/metadata/suites/field-metadata/enum/common/select-operation-agnostic-test-cases';
|
||||||
|
import { FieldMetadataEnumSuccessfulAndFailingTestCases } from 'test/integration/metadata/suites/field-metadata/enum/types/fieldMetadataEnumSuccessfulAndFailingTestCases';
|
||||||
|
|
||||||
|
import { FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||||
|
import { EnumFieldMetadataUnionType } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||||
|
|
||||||
|
export const UPDATE_ENUM_FIELD_METADATA_TEST_CASES: Partial<
|
||||||
|
Record<
|
||||||
|
EnumFieldMetadataUnionType,
|
||||||
|
FieldMetadataEnumSuccessfulAndFailingTestCases
|
||||||
|
>
|
||||||
|
> = {
|
||||||
|
MULTI_SELECT: {
|
||||||
|
failing: [
|
||||||
|
...MUTLI_SELECT_OPERATION_AGNOSTIC_TEST_CASES.failing,
|
||||||
|
{
|
||||||
|
title: 'should fail with unknown default value and no options',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: ["'OPTION_42'"],
|
||||||
|
options: undefined as unknown as FieldMetadataComplexOption[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
successful: [
|
||||||
|
...MUTLI_SELECT_OPERATION_AGNOSTIC_TEST_CASES.successful,
|
||||||
|
{
|
||||||
|
title: 'should succeed with default value and no options',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: ["'OPTION_2'"],
|
||||||
|
options: undefined as unknown as FieldMetadataComplexOption[],
|
||||||
|
},
|
||||||
|
expectedOptions: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Option 2',
|
||||||
|
value: 'OPTION_2',
|
||||||
|
color: 'green',
|
||||||
|
position: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
SELECT: {
|
||||||
|
failing: [
|
||||||
|
...SELECT_OPERATION_AGNOSTIC_TEST_CASES.failing,
|
||||||
|
{
|
||||||
|
title: 'should fail with unknown default value and no options',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: "'OPTION_42'",
|
||||||
|
options: undefined as unknown as FieldMetadataComplexOption[],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
successful: [
|
||||||
|
...SELECT_OPERATION_AGNOSTIC_TEST_CASES.successful,
|
||||||
|
{
|
||||||
|
title: 'should succeed with default value and no options',
|
||||||
|
context: {
|
||||||
|
input: {
|
||||||
|
defaultValue: "'OPTION_2'",
|
||||||
|
options: undefined as unknown as FieldMetadataComplexOption[],
|
||||||
|
},
|
||||||
|
expectedOptions: [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Option 2',
|
||||||
|
value: 'OPTION_2',
|
||||||
|
color: 'green',
|
||||||
|
position: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
@ -0,0 +1,228 @@
|
|||||||
|
import { UPDATE_ENUM_FIELD_METADATA_TEST_CASES } from 'test/integration/metadata/suites/field-metadata/enum/update-enum-field-metadata-test-cases';
|
||||||
|
import { CreateOneFieldFactoryInput } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util';
|
||||||
|
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
||||||
|
import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util';
|
||||||
|
import {
|
||||||
|
LISTING_NAME_PLURAL,
|
||||||
|
LISTING_NAME_SINGULAR,
|
||||||
|
} from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant';
|
||||||
|
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
||||||
|
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
||||||
|
import { FieldMetadataType } from 'twenty-shared/types';
|
||||||
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
|
||||||
|
import {
|
||||||
|
FieldMetadataComplexOption,
|
||||||
|
FieldMetadataDefaultOption,
|
||||||
|
} from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
||||||
|
import { fieldMetadataEnumTypes } from 'src/engine/metadata-modules/field-metadata/utils/is-enum-field-metadata-type.util';
|
||||||
|
|
||||||
|
describe.each(fieldMetadataEnumTypes)(
|
||||||
|
'Update field metadata %s tests suite',
|
||||||
|
(testedFieldMetadataType) => {
|
||||||
|
let createdObjectMetadataId: string;
|
||||||
|
const testCases =
|
||||||
|
UPDATE_ENUM_FIELD_METADATA_TEST_CASES[testedFieldMetadataType];
|
||||||
|
|
||||||
|
if (!isDefined(testCases)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const { failing: failingTestCases, successful: successfulTestCases } =
|
||||||
|
testCases;
|
||||||
|
const initialOptions: CreateOneFieldFactoryInput['options'] = [
|
||||||
|
{
|
||||||
|
label: 'Option 1',
|
||||||
|
value: 'OPTION_1',
|
||||||
|
color: 'green',
|
||||||
|
position: 1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: 'Option 2',
|
||||||
|
value: 'OPTION_2',
|
||||||
|
color: 'green',
|
||||||
|
position: 2,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const { data } = await createOneObjectMetadata({
|
||||||
|
input: {
|
||||||
|
labelSingular: LISTING_NAME_SINGULAR,
|
||||||
|
labelPlural: LISTING_NAME_PLURAL,
|
||||||
|
nameSingular: LISTING_NAME_SINGULAR,
|
||||||
|
namePlural: LISTING_NAME_PLURAL,
|
||||||
|
icon: 'IconBuildingSkyscraper',
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
createdObjectMetadataId = data.createOneObject.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(async () => {
|
||||||
|
await deleteOneObjectMetadata({
|
||||||
|
input: { idToDelete: createdObjectMetadataId },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('Should update default value to null even if it was set before', async () => {
|
||||||
|
const {
|
||||||
|
data: { createOneField },
|
||||||
|
} = await createOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
objectMetadataId: createdObjectMetadataId,
|
||||||
|
type: testedFieldMetadataType,
|
||||||
|
name: 'testField',
|
||||||
|
label: 'Test Field',
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
options: initialOptions,
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
type
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdFieldMetadata = createOneField.id;
|
||||||
|
const isMultiSelect =
|
||||||
|
testedFieldMetadataType === FieldMetadataType.MULTI_SELECT;
|
||||||
|
const rawDefaultValue = `'${initialOptions[0].value}'`;
|
||||||
|
const expectedDefaultValue = isMultiSelect
|
||||||
|
? [rawDefaultValue]
|
||||||
|
: rawDefaultValue;
|
||||||
|
|
||||||
|
const { data: firstUpdate } = await updateOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
idToUpdate: createdFieldMetadata,
|
||||||
|
updatePayload: {
|
||||||
|
defaultValue: expectedDefaultValue,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
defaultValue
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(firstUpdate.updateOneField.defaultValue).toEqual(
|
||||||
|
expectedDefaultValue,
|
||||||
|
);
|
||||||
|
|
||||||
|
const updatedOptions = initialOptions.slice(1);
|
||||||
|
const { data: secondUpdate, errors } = await updateOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
idToUpdate: createdFieldMetadata,
|
||||||
|
updatePayload: {
|
||||||
|
defaultValue: null,
|
||||||
|
options: updatedOptions,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
options
|
||||||
|
defaultValue
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(errors).toBeUndefined();
|
||||||
|
expect(secondUpdate.updateOneField.defaultValue).toBeNull();
|
||||||
|
expect(secondUpdate.updateOneField.options).toMatchObject(updatedOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
test.each(successfulTestCases)(
|
||||||
|
'Update $title',
|
||||||
|
async ({ context: { input, expectedOptions } }) => {
|
||||||
|
const {
|
||||||
|
data: { createOneField },
|
||||||
|
} = await createOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
objectMetadataId: createdObjectMetadataId,
|
||||||
|
type: testedFieldMetadataType,
|
||||||
|
name: 'testField',
|
||||||
|
label: 'Test Field',
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
options: initialOptions,
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
type
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdFieldMetadata = createOneField.id;
|
||||||
|
|
||||||
|
const { ...updatePayload } = input;
|
||||||
|
|
||||||
|
const { data, errors } = await updateOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
idToUpdate: createdFieldMetadata,
|
||||||
|
updatePayload,
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
options
|
||||||
|
defaultValue
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(data.updateOneField).toBeDefined();
|
||||||
|
const updatedOptions:
|
||||||
|
| FieldMetadataComplexOption[]
|
||||||
|
| FieldMetadataDefaultOption[] = data.updateOneField.options;
|
||||||
|
|
||||||
|
expect(errors).toBeUndefined();
|
||||||
|
updatedOptions.forEach((option) => expect(option.id).toBeDefined());
|
||||||
|
|
||||||
|
const optionsToCompare = expectedOptions ?? input.options;
|
||||||
|
|
||||||
|
expect(updatedOptions.length).toBe(optionsToCompare.length);
|
||||||
|
expect(updatedOptions).toMatchObject(optionsToCompare);
|
||||||
|
|
||||||
|
if (isDefined(input.defaultValue)) {
|
||||||
|
expect(data.updateOneField.defaultValue).toEqual(input.defaultValue);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
test.each(failingTestCases)(
|
||||||
|
'Update $title',
|
||||||
|
async ({ context: { input } }) => {
|
||||||
|
const {
|
||||||
|
data: { createOneField },
|
||||||
|
} = await createOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
objectMetadataId: createdObjectMetadataId,
|
||||||
|
type: testedFieldMetadataType,
|
||||||
|
name: 'testField',
|
||||||
|
label: 'Test Field',
|
||||||
|
isLabelSyncedWithName: false,
|
||||||
|
options: initialOptions,
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
type
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdFieldMetadata = createOneField.id;
|
||||||
|
|
||||||
|
const { ...updatePayload } = input;
|
||||||
|
|
||||||
|
const { data, errors } = await updateOneFieldMetadata({
|
||||||
|
input: {
|
||||||
|
idToUpdate: createdFieldMetadata,
|
||||||
|
updatePayload,
|
||||||
|
},
|
||||||
|
gqlFields: `
|
||||||
|
id
|
||||||
|
options
|
||||||
|
`,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(data).toBeNull();
|
||||||
|
expect(errors).toBeDefined();
|
||||||
|
expect(errors).toMatchSnapshot();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
@ -1,374 +0,0 @@
|
|||||||
import { CreateOneFieldFactoryInput } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util';
|
|
||||||
import { EachTestingContext } from 'twenty-shared/testing';
|
|
||||||
import { v4 } from 'uuid';
|
|
||||||
|
|
||||||
import { FieldMetadataComplexOption } from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
|
||||||
|
|
||||||
export type UpdateCreateFieldMetadataSelectTestCase = EachTestingContext<{
|
|
||||||
input: Partial<CreateOneFieldFactoryInput> &
|
|
||||||
Required<Pick<CreateOneFieldFactoryInput, 'options'>>;
|
|
||||||
expectedOptions?: FieldMetadataComplexOption[];
|
|
||||||
}>;
|
|
||||||
|
|
||||||
const successfulTestCases: UpdateCreateFieldMetadataSelectTestCase[] = [
|
|
||||||
{
|
|
||||||
title: 'should succeed with provided option id',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
id: '26c602c3-cba9-4d83-92d4-4ba7dbae2f31',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should succeed with valid default value',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
defaultValue: "'OPTION_1'",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
id: '26c602c3-cba9-4d83-92d4-4ba7dbae2f31',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should succeed with various options id',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: Array.from({ length: 42 }, (_value, index) => {
|
|
||||||
const optionWithoutId: FieldMetadataComplexOption = {
|
|
||||||
label: `Option ${index}`,
|
|
||||||
value: `OPTION_${index}`,
|
|
||||||
color: 'green',
|
|
||||||
position: index,
|
|
||||||
};
|
|
||||||
|
|
||||||
if (index % 2 === 0) {
|
|
||||||
return {
|
|
||||||
...optionWithoutId,
|
|
||||||
id: v4(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return optionWithoutId;
|
|
||||||
}),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should succeed without option id',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should trim option values',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: ' Option 1 ',
|
|
||||||
value: ' OPTION_1 ',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
expectedOptions: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should succeed with null default value',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
defaultValue: null,
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
id: '26c602c3-cba9-4d83-92d4-4ba7dbae2f31',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const basicFailingStringEdgeCaseInputs: {
|
|
||||||
label: string;
|
|
||||||
input: string | undefined | number | null;
|
|
||||||
}[] = [
|
|
||||||
{ input: ' ', label: 'only white spaces' },
|
|
||||||
{ input: '', label: 'empty string' },
|
|
||||||
{ input: null, label: 'null' },
|
|
||||||
{ input: 22222, label: 'not a string' },
|
|
||||||
{ input: 'a'.repeat(64), label: 'too long' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const stringFields: (keyof FieldMetadataComplexOption)[] = [
|
|
||||||
'id',
|
|
||||||
'label',
|
|
||||||
'value',
|
|
||||||
];
|
|
||||||
const autoGeneratedStringFailingTestsCases: UpdateCreateFieldMetadataSelectTestCase[] =
|
|
||||||
stringFields.flatMap((field) => {
|
|
||||||
return basicFailingStringEdgeCaseInputs.map<UpdateCreateFieldMetadataSelectTestCase>(
|
|
||||||
({ input, label }) => ({
|
|
||||||
title: `should fail with ${label} ${field}`,
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
[field]: input,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
const failingTestCases: UpdateCreateFieldMetadataSelectTestCase[] = [
|
|
||||||
...autoGeneratedStringFailingTestsCases,
|
|
||||||
{
|
|
||||||
title: 'should fail with invalid option id',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
id: 'not a uuid',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with empty options',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with invalid option value format',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'Option 1 and some other things, /',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with comma in option label',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option ,1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with duplicated option values',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with duplicated option ids',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
id: 'fd1f11fd-3f05-4a33-bddf-800c3412ce98',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Option 2',
|
|
||||||
value: 'OPTION_2',
|
|
||||||
color: 'green',
|
|
||||||
position: 2,
|
|
||||||
id: 'fd1f11fd-3f05-4a33-bddf-800c3412ce98',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with duplicated option positions',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Option 2',
|
|
||||||
value: 'OPTION_2',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with duplicated trimmed option values',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: ' OPTION_1 ',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Option 2',
|
|
||||||
value: ' OPTION_1 ',
|
|
||||||
color: 'green',
|
|
||||||
position: 2,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with undefined option label',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: undefined as unknown as string,
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with an invalid default value',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
defaultValue: 'invalid',
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with an unknown default value',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
defaultValue: "'OPTION_424242'",
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'should fail with undefined option value',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
options: [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: undefined as unknown as string,
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const UPDATE_CREATE_ONE_FIELD_METADATA_SELECT_TEST_CASES = {
|
|
||||||
successfulTestCases,
|
|
||||||
failingTestCases,
|
|
||||||
};
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
import {
|
|
||||||
UPDATE_CREATE_ONE_FIELD_METADATA_SELECT_TEST_CASES,
|
|
||||||
UpdateCreateFieldMetadataSelectTestCase,
|
|
||||||
} from 'test/integration/metadata/suites/field-metadata/update-create-one-field-metadata-select-tests-cases';
|
|
||||||
import { CreateOneFieldFactoryInput } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata-query-factory.util';
|
|
||||||
import { createOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/create-one-field-metadata.util';
|
|
||||||
import { updateOneFieldMetadata } from 'test/integration/metadata/suites/field-metadata/utils/update-one-field-metadata.util';
|
|
||||||
import {
|
|
||||||
LISTING_NAME_PLURAL,
|
|
||||||
LISTING_NAME_SINGULAR,
|
|
||||||
} from 'test/integration/metadata/suites/object-metadata/constants/test-object-names.constant';
|
|
||||||
import { createOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/create-one-object-metadata.util';
|
|
||||||
import { deleteOneObjectMetadata } from 'test/integration/metadata/suites/object-metadata/utils/delete-one-object-metadata.util';
|
|
||||||
import { FieldMetadataType } from 'twenty-shared/types';
|
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
|
||||||
|
|
||||||
import {
|
|
||||||
FieldMetadataComplexOption,
|
|
||||||
FieldMetadataDefaultOption,
|
|
||||||
} from 'src/engine/metadata-modules/field-metadata/dtos/options.input';
|
|
||||||
|
|
||||||
const { failingTestCases, successfulTestCases } =
|
|
||||||
UPDATE_CREATE_ONE_FIELD_METADATA_SELECT_TEST_CASES;
|
|
||||||
|
|
||||||
describe('Field metadata select update tests group', () => {
|
|
||||||
let createdObjectMetadataId: string;
|
|
||||||
let createdFieldMetadata: string;
|
|
||||||
const initialOptions: CreateOneFieldFactoryInput['options'] = [
|
|
||||||
{
|
|
||||||
label: 'Option 1',
|
|
||||||
value: 'OPTION_1',
|
|
||||||
color: 'green',
|
|
||||||
position: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Option 2',
|
|
||||||
value: 'OPTION_2',
|
|
||||||
color: 'green',
|
|
||||||
position: 2,
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
beforeEach(async () => {
|
|
||||||
const { data } = await createOneObjectMetadata({
|
|
||||||
input: {
|
|
||||||
labelSingular: LISTING_NAME_SINGULAR,
|
|
||||||
labelPlural: LISTING_NAME_PLURAL,
|
|
||||||
nameSingular: LISTING_NAME_SINGULAR,
|
|
||||||
namePlural: LISTING_NAME_PLURAL,
|
|
||||||
icon: 'IconBuildingSkyscraper',
|
|
||||||
isLabelSyncedWithName: false,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
createdObjectMetadataId = data.createOneObject.id;
|
|
||||||
|
|
||||||
const {
|
|
||||||
data: { createOneField },
|
|
||||||
} = await createOneFieldMetadata({
|
|
||||||
input: {
|
|
||||||
objectMetadataId: createdObjectMetadataId,
|
|
||||||
type: FieldMetadataType.SELECT,
|
|
||||||
name: 'testField',
|
|
||||||
label: 'Test Field',
|
|
||||||
isLabelSyncedWithName: false,
|
|
||||||
options: initialOptions,
|
|
||||||
},
|
|
||||||
gqlFields: `
|
|
||||||
id
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
createdFieldMetadata = createOneField.id;
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(async () => {
|
|
||||||
await deleteOneObjectMetadata({
|
|
||||||
input: { idToDelete: createdObjectMetadataId },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should update default value to null even if it was set before', async () => {
|
|
||||||
const expectedDefaultValue = `'${initialOptions[0].value}'`;
|
|
||||||
const { data: firstUdpate } = await updateOneFieldMetadata({
|
|
||||||
input: {
|
|
||||||
idToUpdate: createdFieldMetadata,
|
|
||||||
updatePayload: {
|
|
||||||
defaultValue: expectedDefaultValue,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
gqlFields: `
|
|
||||||
id
|
|
||||||
defaultValue
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(firstUdpate.updateOneField.defaultValue).toEqual(
|
|
||||||
expectedDefaultValue,
|
|
||||||
);
|
|
||||||
|
|
||||||
const updatedOptions = initialOptions.slice(1);
|
|
||||||
const { data: secondUpdate, errors } = await updateOneFieldMetadata({
|
|
||||||
input: {
|
|
||||||
idToUpdate: createdFieldMetadata,
|
|
||||||
updatePayload: {
|
|
||||||
defaultValue: null,
|
|
||||||
options: updatedOptions,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
gqlFields: `
|
|
||||||
id
|
|
||||||
options
|
|
||||||
defaultValue
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(errors).toBeUndefined();
|
|
||||||
expect(secondUpdate.updateOneField.defaultValue).toBeNull();
|
|
||||||
expect(secondUpdate.updateOneField.options).toMatchObject(updatedOptions);
|
|
||||||
});
|
|
||||||
|
|
||||||
const updateSpecificSuccessfulTestCases: UpdateCreateFieldMetadataSelectTestCase[] =
|
|
||||||
[
|
|
||||||
{
|
|
||||||
title: 'should succeed with default value and no options',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
defaultValue: "'OPTION_2'",
|
|
||||||
options: undefined as unknown as FieldMetadataComplexOption[],
|
|
||||||
},
|
|
||||||
expectedOptions: initialOptions,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
test.each([...successfulTestCases, ...updateSpecificSuccessfulTestCases])(
|
|
||||||
'Update $title',
|
|
||||||
async ({ context: { input, expectedOptions } }) => {
|
|
||||||
const { data, errors } = await updateOneFieldMetadata({
|
|
||||||
input: {
|
|
||||||
idToUpdate: createdFieldMetadata,
|
|
||||||
updatePayload: input,
|
|
||||||
},
|
|
||||||
gqlFields: `
|
|
||||||
id
|
|
||||||
options
|
|
||||||
defaultValue
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(data.updateOneField).toBeDefined();
|
|
||||||
const updatedOptions:
|
|
||||||
| FieldMetadataComplexOption[]
|
|
||||||
| FieldMetadataDefaultOption[] = data.updateOneField.options;
|
|
||||||
|
|
||||||
expect(errors).toBeUndefined();
|
|
||||||
updatedOptions.forEach((option) => expect(option.id).toBeDefined());
|
|
||||||
|
|
||||||
const optionsToCompare = expectedOptions ?? input.options;
|
|
||||||
|
|
||||||
expect(updatedOptions.length).toBe(optionsToCompare.length);
|
|
||||||
expect(updatedOptions).toMatchObject(optionsToCompare);
|
|
||||||
|
|
||||||
if (isDefined(input.defaultValue)) {
|
|
||||||
expect(data.updateOneField.defaultValue).toEqual(input.defaultValue);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
const updateSpecificFailingTestCases: UpdateCreateFieldMetadataSelectTestCase[] =
|
|
||||||
[
|
|
||||||
{
|
|
||||||
title: 'should fail with unknown default value and no options',
|
|
||||||
context: {
|
|
||||||
input: {
|
|
||||||
defaultValue: "'OPTION_42'",
|
|
||||||
options: undefined as unknown as FieldMetadataComplexOption[],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
test.each([...updateSpecificFailingTestCases, ...failingTestCases])(
|
|
||||||
'Update $title',
|
|
||||||
async ({ context: { input } }) => {
|
|
||||||
const { data, errors } = await updateOneFieldMetadata({
|
|
||||||
input: {
|
|
||||||
idToUpdate: createdFieldMetadata,
|
|
||||||
updatePayload: input,
|
|
||||||
},
|
|
||||||
gqlFields: `
|
|
||||||
id
|
|
||||||
options
|
|
||||||
`,
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(data).toBeNull();
|
|
||||||
expect(errors).toBeDefined();
|
|
||||||
expect(errors).toMatchSnapshot();
|
|
||||||
},
|
|
||||||
);
|
|
||||||
});
|
|
||||||
@ -164,7 +164,7 @@ describe('updateOne', () => {
|
|||||||
input: {
|
input: {
|
||||||
idToUpdate: createdFieldMetadata.createOneField.id,
|
idToUpdate: createdFieldMetadata.createOneField.id,
|
||||||
updatePayload: {
|
updatePayload: {
|
||||||
defaultValue: 'OPTION_2',
|
defaultValue: "'OPTION_2'",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
gqlFields: `
|
gqlFields: `
|
||||||
@ -182,7 +182,7 @@ describe('updateOne', () => {
|
|||||||
"extensions": {
|
"extensions": {
|
||||||
"code": "BAD_USER_INPUT",
|
"code": "BAD_USER_INPUT",
|
||||||
},
|
},
|
||||||
"message": "Default value for existing options is invalid: OPTION_2",
|
"message": "Default value "'OPTION_2'" must be one of the option values",
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
`);
|
`);
|
||||||
|
|||||||
@ -8,3 +8,4 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
export type { EachTestingContext } from './types/EachTestingContext.type';
|
export type { EachTestingContext } from './types/EachTestingContext.type';
|
||||||
|
export type { SuccessfulAndFailingTestCases } from './types/SuccessfulAndFailingTestCases';
|
||||||
|
|||||||
@ -0,0 +1,6 @@
|
|||||||
|
import { EachTestingContext } from '@/testing/types/EachTestingContext.type';
|
||||||
|
|
||||||
|
export type SuccessfulAndFailingTestCases<T> = {
|
||||||
|
successful: EachTestingContext<T>[];
|
||||||
|
failing: EachTestingContext<T>[];
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user