From cb3354240679d52754b4339657ceefb0b2ac2628 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 3 Sep 2025 09:46:18 -0400 Subject: [PATCH 1/7] fix: array row duplication --- next-env.d.ts | 1 - packages/ui/src/forms/Form/fieldReducer.ts | 32 +-- test/_community/collections/Posts/index.ts | 10 + test/_community/payload-types.ts | 12 ++ test/fields/payload-types.ts | 226 ++++++++++----------- test/versions/collections/Autosave.ts | 6 + test/versions/collections/Posts.ts | 6 + test/versions/payload-types.ts | 52 ++--- 8 files changed, 192 insertions(+), 153 deletions(-) diff --git a/next-env.d.ts b/next-env.d.ts index 830fb594ca2..1b3be0840f3 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,6 +1,5 @@ /// /// -/// // NOTE: This file should not be edited // see https://nextjs.org/docs/app/api-reference/config/typescript for more information. diff --git a/packages/ui/src/forms/Form/fieldReducer.ts b/packages/ui/src/forms/Form/fieldReducer.ts index f2aaef5732b..1a51efc079b 100644 --- a/packages/ui/src/forms/Form/fieldReducer.ts +++ b/packages/ui/src/forms/Form/fieldReducer.ts @@ -134,35 +134,37 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState { case 'DUPLICATE_ROW': { const { path, rowIndex } = action const { remainingFields, rows } = separateRows(path, state) - const rowsMetadata = [...(state[path].rows || [])] + const rowsWithDuplicate = [...(state[path].rows || [])] - const duplicateRowMetadata = deepCopyObjectSimpleWithoutReactComponents( - rowsMetadata[rowIndex], - ) + const newRow = deepCopyObjectSimpleWithoutReactComponents(rowsWithDuplicate[rowIndex]) + + const newRowID = new ObjectId().toHexString() - if (duplicateRowMetadata.id) { - duplicateRowMetadata.id = new ObjectId().toHexString() + if (newRow.id) { + newRow.id = newRowID } - if (rowsMetadata[rowIndex]?.customComponents?.RowLabel) { - duplicateRowMetadata.customComponents = { - RowLabel: rowsMetadata[rowIndex].customComponents.RowLabel, + if (rowsWithDuplicate[rowIndex]?.customComponents?.RowLabel) { + newRow.customComponents = { + RowLabel: rowsWithDuplicate[rowIndex].customComponents.RowLabel, } } const duplicateRowState = deepCopyObjectSimpleWithoutReactComponents(rows[rowIndex]) if (duplicateRowState.id) { - duplicateRowState.id.value = new ObjectId().toHexString() - duplicateRowState.id.initialValue = new ObjectId().toHexString() + duplicateRowState.id.value = newRowID + duplicateRowState.id.initialValue = newRowID } for (const key of Object.keys(duplicateRowState).filter((key) => key.endsWith('.id'))) { const idState = duplicateRowState[key] + const newNestedFieldID = new ObjectId().toHexString() + if (idState && typeof idState.value === 'string' && ObjectId.isValid(idState.value)) { - duplicateRowState[key].value = new ObjectId().toHexString() - duplicateRowState[key].initialValue = new ObjectId().toHexString() + duplicateRowState[key].value = newNestedFieldID + duplicateRowState[key].initialValue = newNestedFieldID } } @@ -170,7 +172,7 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState { if (Object.keys(duplicateRowState).length > 0) { // Add new object containing subfield names to unflattenedRows array rows.splice(rowIndex + 1, 0, duplicateRowState) - rowsMetadata.splice(rowIndex + 1, 0, duplicateRowMetadata) + rowsWithDuplicate.splice(rowIndex + 1, 0, newRow) } const newState = { @@ -179,7 +181,7 @@ export function fieldReducer(state: FormState, action: FieldAction): FormState { [path]: { ...state[path], disableFormData: true, - rows: rowsMetadata, + rows: rowsWithDuplicate, value: rows.length, }, } diff --git a/test/_community/collections/Posts/index.ts b/test/_community/collections/Posts/index.ts index 86a307b39c3..47cdc893fe0 100644 --- a/test/_community/collections/Posts/index.ts +++ b/test/_community/collections/Posts/index.ts @@ -14,6 +14,16 @@ export const PostsCollection: CollectionConfig = { name: 'title', type: 'text', }, + { + name: 'array', + type: 'array', + fields: [ + { + type: 'text', + name: 'textField', + }, + ], + }, { name: 'content', type: 'richText', diff --git a/test/_community/payload-types.ts b/test/_community/payload-types.ts index 599c9dec1d7..bfc08d163ac 100644 --- a/test/_community/payload-types.ts +++ b/test/_community/payload-types.ts @@ -126,6 +126,12 @@ export interface UserAuthOperations { export interface Post { id: string; title?: string | null; + array?: + | { + textField?: string | null; + id?: string | null; + }[] + | null; content?: { root: { type: string; @@ -279,6 +285,12 @@ export interface PayloadMigration { */ export interface PostsSelect { title?: T; + array?: + | T + | { + textField?: T; + id?: T; + }; content?: T; updatedAt?: T; createdAt?: T; diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 9a2b3ff50e5..43f1cff19e0 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -149,7 +149,7 @@ export interface Config { 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; }; db: { - defaultIDType: number; + defaultIDType: string; }; globals: {}; globalsSelect: {}; @@ -215,7 +215,7 @@ export interface LocalizedTextReference2 { * via the `definition` "users". */ export interface User { - id: number; + id: string; canViewConditionalField?: boolean | null; updatedAt: string; createdAt: string; @@ -240,7 +240,7 @@ export interface User { * via the `definition` "select-versions-fields". */ export interface SelectVersionsField { - id: number; + id: string; hasMany?: ('a' | 'b' | 'c' | 'd')[] | null; array?: | { @@ -265,7 +265,7 @@ export interface SelectVersionsField { * via the `definition` "array-fields". */ export interface ArrayField { - id: number; + id: string; title?: string | null; items: { text: string; @@ -369,7 +369,7 @@ export interface ArrayField { * via the `definition` "block-fields". */ export interface BlockField { - id: number; + id: string; blocks: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; duplicate: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; collapsedByDefaultBlocks: ( @@ -500,7 +500,7 @@ export interface BlockField { | null; relationshipBlocks?: | { - relationship?: (number | null) | TextField; + relationship?: (string | null) | TextField; id?: string | null; blockName?: string | null; blockType: 'relationships'; @@ -697,7 +697,7 @@ export interface LocalizedTabsBlock { * via the `definition` "text-fields". */ export interface TextField { - id: number; + id: string; text: string; hiddenTextField?: string | null; /** @@ -749,7 +749,7 @@ export interface TextField { * via the `definition` "checkbox-fields". */ export interface CheckboxField { - id: number; + id: string; checkbox: boolean; checkboxNotRequired?: boolean | null; updatedAt: string; @@ -760,7 +760,7 @@ export interface CheckboxField { * via the `definition` "code-fields". */ export interface CodeField { - id: number; + id: string; javascript?: string | null; typescript?: string | null; json?: string | null; @@ -775,7 +775,7 @@ export interface CodeField { * via the `definition` "collapsible-fields". */ export interface CollapsibleField { - id: number; + id: string; text: string; group: { textWithinGroup?: string | null; @@ -808,7 +808,7 @@ export interface CollapsibleField { * via the `definition` "conditional-logic". */ export interface ConditionalLogic { - id: number; + id: string; text: string; toggleField?: boolean | null; fieldWithDocIDCondition?: string | null; @@ -922,7 +922,7 @@ export interface CustomRowId { * via the `definition` "date-fields". */ export interface DateField { - id: number; + id: string; default: string; timeOnly?: string | null; timeOnlyWithMiliseconds?: string | null; @@ -967,7 +967,7 @@ export interface DateField { * via the `definition` "email-fields". */ export interface EmailField { - id: number; + id: string; email: string; localizedEmail?: string | null; emailWithAutocomplete?: string | null; @@ -992,7 +992,7 @@ export interface EmailField { * via the `definition` "radio-fields". */ export interface RadioField { - id: number; + id: string; radio?: ('one' | 'two' | 'three') | null; radioWithJsxLabelOption?: ('one' | 'two' | 'three') | null; updatedAt: string; @@ -1003,7 +1003,7 @@ export interface RadioField { * via the `definition` "group-fields". */ export interface GroupField { - id: number; + id: string; /** * This is a group. */ @@ -1085,22 +1085,22 @@ export interface GroupField { select?: ('one' | 'two')[] | null; }; localizedGroupRel?: { - email?: (number | null) | EmailField; + email?: (string | null) | EmailField; }; localizedGroupManyRel?: { - email?: (number | EmailField)[] | null; + email?: (string | EmailField)[] | null; }; localizedGroupPolyRel?: { email?: { relationTo: 'email-fields'; - value: number | EmailField; + value: string | EmailField; } | null; }; localizedGroupPolyHasManyRel?: { email?: | { relationTo: 'email-fields'; - value: number | EmailField; + value: string | EmailField; }[] | null; }; @@ -1154,30 +1154,30 @@ export interface RowField { * via the `definition` "indexed-fields". */ export interface IndexedField { - id: number; + id: string; text: string; uniqueText?: string | null; - uniqueRelationship?: (number | null) | TextField; - uniqueHasManyRelationship?: (number | TextField)[] | null; - uniqueHasManyRelationship_2?: (number | TextField)[] | null; + uniqueRelationship?: (string | null) | TextField; + uniqueHasManyRelationship?: (string | TextField)[] | null; + uniqueHasManyRelationship_2?: (string | TextField)[] | null; uniquePolymorphicRelationship?: { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null; uniquePolymorphicRelationship_2?: { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null; uniqueHasManyPolymorphicRelationship?: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; }[] | null; uniqueHasManyPolymorphicRelationship_2?: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; }[] | null; uniqueRequiredText: string; @@ -1213,7 +1213,7 @@ export interface IndexedField { * via the `definition` "json-fields". */ export interface JsonField { - id: number; + id: string; json?: { array?: { object?: { @@ -1254,7 +1254,7 @@ export interface JsonField { * via the `definition` "number-fields". */ export interface NumberField { - id: number; + id: string; number?: number | null; min?: number | null; max?: number | null; @@ -1289,7 +1289,7 @@ export interface NumberField { * via the `definition` "point-fields". */ export interface PointField { - id: number; + id: string; /** * @minItems 2 * @maxItems 2 @@ -1320,83 +1320,83 @@ export interface PointField { * via the `definition` "relationship-fields". */ export interface RelationshipField { - id: number; + id: string; text?: string | null; relationship: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | { relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; }; relationHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | { relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } )[] | null; - relationToSelf?: (number | null) | RelationshipField; - relationToSelfSelectOnly?: (number | null) | RelationshipField; - relationWithAllowCreateToFalse?: (number | null) | User; - relationWithAllowEditToFalse?: (number | null) | User; - relationWithDynamicDefault?: (number | null) | User; + relationToSelf?: (string | null) | RelationshipField; + relationToSelfSelectOnly?: (string | null) | RelationshipField; + relationWithAllowCreateToFalse?: (string | null) | User; + relationWithAllowEditToFalse?: (string | null) | User; + relationWithDynamicDefault?: (string | null) | User; relationHasManyWithDynamicDefault?: { relationTo: 'users'; - value: number | User; + value: string | User; } | null; - relationshipWithMin?: (number | TextField)[] | null; - relationshipWithMax?: (number | TextField)[] | null; - relationshipHasMany?: (number | TextField)[] | null; + relationshipWithMin?: (string | TextField)[] | null; + relationshipWithMax?: (string | TextField)[] | null; + relationshipHasMany?: (string | TextField)[] | null; array?: | { - relationship?: (number | null) | TextField; + relationship?: (string | null) | TextField; id?: string | null; }[] | null; relationshipWithMinRows?: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; }[] | null; relationToRow?: (string | null) | RowField; relationToRowMany?: (string | RowField)[] | null; - relationshipDrawer?: (number | null) | TextField; - relationshipDrawerReadOnly?: (number | null) | TextField; + relationshipDrawer?: (string | null) | TextField; + relationshipDrawerReadOnly?: (string | null) | TextField; polymorphicRelationshipDrawer?: | ({ relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null) | ({ relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } | null); - relationshipDrawerHasMany?: (number | TextField)[] | null; + relationshipDrawerHasMany?: (string | TextField)[] | null; relationshipDrawerHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | { relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } )[] | null; - relationshipDrawerWithAllowCreateFalse?: (number | null) | TextField; + relationshipDrawerWithAllowCreateFalse?: (string | null) | TextField; relationshipDrawerWithFilterOptions?: { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null; updatedAt: string; createdAt: string; @@ -1406,7 +1406,7 @@ export interface RelationshipField { * via the `definition` "select-fields". */ export interface SelectField { - id: number; + id: string; select?: ('one' | 'two' | 'three') | null; selectReadOnly?: ('one' | 'two' | 'three') | null; selectHasMany?: ('one' | 'two' | 'three' | 'four' | 'five' | 'six')[] | null; @@ -1436,7 +1436,7 @@ export interface SelectField { * via the `definition` "tabs-fields-2". */ export interface TabsFields2 { - id: number; + id: string; tabsInArray?: | { text?: string | null; @@ -1454,7 +1454,7 @@ export interface TabsFields2 { * via the `definition` "tabs-fields". */ export interface TabsField { - id: number; + id: string; /** * This should not collapse despite there being many tabs pushing the main fields open. */ @@ -1556,9 +1556,9 @@ export interface TabWithName { * via the `definition` "uploads". */ export interface Upload { - id: number; + id: string; text?: string | null; - media?: (number | null) | Upload; + media?: (string | null) | Upload; updatedAt: string; createdAt: string; url?: string | null; @@ -1576,9 +1576,9 @@ export interface Upload { * via the `definition` "uploads2". */ export interface Uploads2 { - id: number; + id: string; text?: string | null; - media?: (number | null) | Uploads2; + media?: (string | null) | Uploads2; updatedAt: string; createdAt: string; url?: string | null; @@ -1596,8 +1596,8 @@ export interface Uploads2 { * via the `definition` "uploads3". */ export interface Uploads3 { - id: number; - media?: (number | null) | Uploads3; + id: string; + media?: (string | null) | Uploads3; updatedAt: string; createdAt: string; url?: string | null; @@ -1615,9 +1615,9 @@ export interface Uploads3 { * via the `definition` "uploads-multi". */ export interface UploadsMulti { - id: number; + id: string; text?: string | null; - media?: (number | Upload)[] | null; + media?: (string | Upload)[] | null; updatedAt: string; createdAt: string; } @@ -1626,16 +1626,16 @@ export interface UploadsMulti { * via the `definition` "uploads-poly". */ export interface UploadsPoly { - id: number; + id: string; text?: string | null; media?: | ({ relationTo: 'uploads'; - value: number | Upload; + value: string | Upload; } | null) | ({ relationTo: 'uploads2'; - value: number | Uploads2; + value: string | Uploads2; } | null); updatedAt: string; createdAt: string; @@ -1645,17 +1645,17 @@ export interface UploadsPoly { * via the `definition` "uploads-multi-poly". */ export interface UploadsMultiPoly { - id: number; + id: string; text?: string | null; media?: | ( | { relationTo: 'uploads'; - value: number | Upload; + value: string | Upload; } | { relationTo: 'uploads2'; - value: number | Uploads2; + value: string | Uploads2; } )[] | null; @@ -1667,11 +1667,11 @@ export interface UploadsMultiPoly { * via the `definition` "uploads-restricted". */ export interface UploadsRestricted { - id: number; + id: string; text?: string | null; - uploadWithoutRestriction?: (number | null) | Upload; - uploadWithAllowCreateFalse?: (number | null) | Upload; - uploadMultipleWithAllowCreateFalse?: (number | Upload)[] | null; + uploadWithoutRestriction?: (string | null) | Upload; + uploadWithAllowCreateFalse?: (string | null) | Upload; + uploadMultipleWithAllowCreateFalse?: (string | Upload)[] | null; updatedAt: string; createdAt: string; } @@ -1680,7 +1680,7 @@ export interface UploadsRestricted { * via the `definition` "ui-fields". */ export interface UiField { - id: number; + id: string; text: string; updatedAt: string; createdAt: string; @@ -1690,39 +1690,39 @@ export interface UiField { * via the `definition` "payload-locked-documents". */ export interface PayloadLockedDocument { - id: number; + id: string; document?: | ({ relationTo: 'users'; - value: number | User; + value: string | User; } | null) | ({ relationTo: 'select-versions-fields'; - value: number | SelectVersionsField; + value: string | SelectVersionsField; } | null) | ({ relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } | null) | ({ relationTo: 'block-fields'; - value: number | BlockField; + value: string | BlockField; } | null) | ({ relationTo: 'checkbox-fields'; - value: number | CheckboxField; + value: string | CheckboxField; } | null) | ({ relationTo: 'code-fields'; - value: number | CodeField; + value: string | CodeField; } | null) | ({ relationTo: 'collapsible-fields'; - value: number | CollapsibleField; + value: string | CollapsibleField; } | null) | ({ relationTo: 'conditional-logic'; - value: number | ConditionalLogic; + value: string | ConditionalLogic; } | null) | ({ relationTo: 'custom-id'; @@ -1730,27 +1730,27 @@ export interface PayloadLockedDocument { } | null) | ({ relationTo: 'custom-tab-id'; - value: number | CustomTabId; + value: string | CustomTabId; } | null) | ({ relationTo: 'custom-row-id'; - value: number | CustomRowId; + value: string | CustomRowId; } | null) | ({ relationTo: 'date-fields'; - value: number | DateField; + value: string | DateField; } | null) | ({ relationTo: 'email-fields'; - value: number | EmailField; + value: string | EmailField; } | null) | ({ relationTo: 'radio-fields'; - value: number | RadioField; + value: string | RadioField; } | null) | ({ relationTo: 'group-fields'; - value: number | GroupField; + value: string | GroupField; } | null) | ({ relationTo: 'row-fields'; @@ -1758,76 +1758,76 @@ export interface PayloadLockedDocument { } | null) | ({ relationTo: 'indexed-fields'; - value: number | IndexedField; + value: string | IndexedField; } | null) | ({ relationTo: 'json-fields'; - value: number | JsonField; + value: string | JsonField; } | null) | ({ relationTo: 'number-fields'; - value: number | NumberField; + value: string | NumberField; } | null) | ({ relationTo: 'point-fields'; - value: number | PointField; + value: string | PointField; } | null) | ({ relationTo: 'relationship-fields'; - value: number | RelationshipField; + value: string | RelationshipField; } | null) | ({ relationTo: 'select-fields'; - value: number | SelectField; + value: string | SelectField; } | null) | ({ relationTo: 'tabs-fields-2'; - value: number | TabsFields2; + value: string | TabsFields2; } | null) | ({ relationTo: 'tabs-fields'; - value: number | TabsField; + value: string | TabsField; } | null) | ({ relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null) | ({ relationTo: 'uploads'; - value: number | Upload; + value: string | Upload; } | null) | ({ relationTo: 'uploads2'; - value: number | Uploads2; + value: string | Uploads2; } | null) | ({ relationTo: 'uploads3'; - value: number | Uploads3; + value: string | Uploads3; } | null) | ({ relationTo: 'uploads-multi'; - value: number | UploadsMulti; + value: string | UploadsMulti; } | null) | ({ relationTo: 'uploads-poly'; - value: number | UploadsPoly; + value: string | UploadsPoly; } | null) | ({ relationTo: 'uploads-multi-poly'; - value: number | UploadsMultiPoly; + value: string | UploadsMultiPoly; } | null) | ({ relationTo: 'uploads-restricted'; - value: number | UploadsRestricted; + value: string | UploadsRestricted; } | null) | ({ relationTo: 'ui-fields'; - value: number | UiField; + value: string | UiField; } | null); globalSlug?: string | null; user: { relationTo: 'users'; - value: number | User; + value: string | User; }; updatedAt: string; createdAt: string; @@ -1837,10 +1837,10 @@ export interface PayloadLockedDocument { * via the `definition` "payload-preferences". */ export interface PayloadPreference { - id: number; + id: string; user: { relationTo: 'users'; - value: number | User; + value: string | User; }; key?: string | null; value?: @@ -1860,7 +1860,7 @@ export interface PayloadPreference { * via the `definition` "payload-migrations". */ export interface PayloadMigration { - id: number; + id: string; name?: string | null; batch?: number | null; updatedAt: string; diff --git a/test/versions/collections/Autosave.ts b/test/versions/collections/Autosave.ts index 5f0df3b648c..a36e1e53a5c 100644 --- a/test/versions/collections/Autosave.ts +++ b/test/versions/collections/Autosave.ts @@ -53,6 +53,12 @@ const AutosavePosts: CollectionConfig = { unique: true, localized: true, }, + { + name: 'relationship', + label: 'Relationship', + type: 'relationship', + relationTo: 'users', + }, { name: 'computedTitle', label: 'Computed Title', diff --git a/test/versions/collections/Posts.ts b/test/versions/collections/Posts.ts index e95d8c4569d..d981c51baab 100644 --- a/test/versions/collections/Posts.ts +++ b/test/versions/collections/Posts.ts @@ -10,6 +10,12 @@ import { const Posts: CollectionConfig = { slug: postCollectionSlug, fields: [ + { + name: 'relationship', + label: 'Relationship', + type: 'relationship', + relationTo: 'users', + }, { name: 'relationToAutosaves', type: 'relationship', diff --git a/test/versions/payload-types.ts b/test/versions/payload-types.ts index d707fbf7064..a8261b52728 100644 --- a/test/versions/payload-types.ts +++ b/test/versions/payload-types.ts @@ -186,12 +186,37 @@ export interface DisablePublish { */ export interface Post { id: string; + relationship?: (string | null) | User; relationToAutosaves?: (string | null) | AutosavePost; relationToVersions?: (string | null) | VersionPost; relationToDrafts?: (string | null) | DraftPost; updatedAt: string; createdAt: string; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "users". + */ +export interface User { + id: string; + updatedAt: string; + createdAt: string; + email: string; + resetPasswordToken?: string | null; + resetPasswordExpiration?: string | null; + salt?: string | null; + hash?: string | null; + loginAttempts?: number | null; + lockUntil?: string | null; + sessions?: + | { + id: string; + createdAt?: string | null; + expiresAt: string; + }[] + | null; + password?: string | null; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "autosave-posts". @@ -199,6 +224,7 @@ export interface Post { export interface AutosavePost { id: string; title: string; + relationship?: (string | null) | User; computedTitle?: string | null; richText?: { root: { @@ -567,30 +593,6 @@ export interface Media2 { focalX?: number | null; focalY?: number | null; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "users". - */ -export interface User { - id: string; - updatedAt: string; - createdAt: string; - email: string; - resetPasswordToken?: string | null; - resetPasswordExpiration?: string | null; - salt?: string | null; - hash?: string | null; - loginAttempts?: number | null; - lockUntil?: string | null; - sessions?: - | { - id: string; - createdAt?: string | null; - expiresAt: string; - }[] - | null; - password?: string | null; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-jobs". @@ -823,6 +825,7 @@ export interface DisablePublishSelect { * via the `definition` "posts_select". */ export interface PostsSelect { + relationship?: T; relationToAutosaves?: T; relationToVersions?: T; relationToDrafts?: T; @@ -835,6 +838,7 @@ export interface PostsSelect { */ export interface AutosavePostsSelect { title?: T; + relationship?: T; computedTitle?: T; richText?: T; json?: T; From 858914fb19724c81852227bbb3aa3af5dcfe3494 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 3 Sep 2025 09:56:21 -0400 Subject: [PATCH 2/7] reset test dir --- test/_community/collections/Posts/index.ts | 10 - test/_community/payload-types.ts | 12 -- test/fields/payload-types.ts | 226 ++++++++++----------- test/versions/collections/Autosave.ts | 6 - test/versions/collections/Posts.ts | 6 - test/versions/payload-types.ts | 52 +++-- 6 files changed, 137 insertions(+), 175 deletions(-) diff --git a/test/_community/collections/Posts/index.ts b/test/_community/collections/Posts/index.ts index 47cdc893fe0..86a307b39c3 100644 --- a/test/_community/collections/Posts/index.ts +++ b/test/_community/collections/Posts/index.ts @@ -14,16 +14,6 @@ export const PostsCollection: CollectionConfig = { name: 'title', type: 'text', }, - { - name: 'array', - type: 'array', - fields: [ - { - type: 'text', - name: 'textField', - }, - ], - }, { name: 'content', type: 'richText', diff --git a/test/_community/payload-types.ts b/test/_community/payload-types.ts index bfc08d163ac..599c9dec1d7 100644 --- a/test/_community/payload-types.ts +++ b/test/_community/payload-types.ts @@ -126,12 +126,6 @@ export interface UserAuthOperations { export interface Post { id: string; title?: string | null; - array?: - | { - textField?: string | null; - id?: string | null; - }[] - | null; content?: { root: { type: string; @@ -285,12 +279,6 @@ export interface PayloadMigration { */ export interface PostsSelect { title?: T; - array?: - | T - | { - textField?: T; - id?: T; - }; content?: T; updatedAt?: T; createdAt?: T; diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 43f1cff19e0..9a2b3ff50e5 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -149,7 +149,7 @@ export interface Config { 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; }; db: { - defaultIDType: string; + defaultIDType: number; }; globals: {}; globalsSelect: {}; @@ -215,7 +215,7 @@ export interface LocalizedTextReference2 { * via the `definition` "users". */ export interface User { - id: string; + id: number; canViewConditionalField?: boolean | null; updatedAt: string; createdAt: string; @@ -240,7 +240,7 @@ export interface User { * via the `definition` "select-versions-fields". */ export interface SelectVersionsField { - id: string; + id: number; hasMany?: ('a' | 'b' | 'c' | 'd')[] | null; array?: | { @@ -265,7 +265,7 @@ export interface SelectVersionsField { * via the `definition` "array-fields". */ export interface ArrayField { - id: string; + id: number; title?: string | null; items: { text: string; @@ -369,7 +369,7 @@ export interface ArrayField { * via the `definition` "block-fields". */ export interface BlockField { - id: string; + id: number; blocks: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; duplicate: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; collapsedByDefaultBlocks: ( @@ -500,7 +500,7 @@ export interface BlockField { | null; relationshipBlocks?: | { - relationship?: (string | null) | TextField; + relationship?: (number | null) | TextField; id?: string | null; blockName?: string | null; blockType: 'relationships'; @@ -697,7 +697,7 @@ export interface LocalizedTabsBlock { * via the `definition` "text-fields". */ export interface TextField { - id: string; + id: number; text: string; hiddenTextField?: string | null; /** @@ -749,7 +749,7 @@ export interface TextField { * via the `definition` "checkbox-fields". */ export interface CheckboxField { - id: string; + id: number; checkbox: boolean; checkboxNotRequired?: boolean | null; updatedAt: string; @@ -760,7 +760,7 @@ export interface CheckboxField { * via the `definition` "code-fields". */ export interface CodeField { - id: string; + id: number; javascript?: string | null; typescript?: string | null; json?: string | null; @@ -775,7 +775,7 @@ export interface CodeField { * via the `definition` "collapsible-fields". */ export interface CollapsibleField { - id: string; + id: number; text: string; group: { textWithinGroup?: string | null; @@ -808,7 +808,7 @@ export interface CollapsibleField { * via the `definition` "conditional-logic". */ export interface ConditionalLogic { - id: string; + id: number; text: string; toggleField?: boolean | null; fieldWithDocIDCondition?: string | null; @@ -922,7 +922,7 @@ export interface CustomRowId { * via the `definition` "date-fields". */ export interface DateField { - id: string; + id: number; default: string; timeOnly?: string | null; timeOnlyWithMiliseconds?: string | null; @@ -967,7 +967,7 @@ export interface DateField { * via the `definition` "email-fields". */ export interface EmailField { - id: string; + id: number; email: string; localizedEmail?: string | null; emailWithAutocomplete?: string | null; @@ -992,7 +992,7 @@ export interface EmailField { * via the `definition` "radio-fields". */ export interface RadioField { - id: string; + id: number; radio?: ('one' | 'two' | 'three') | null; radioWithJsxLabelOption?: ('one' | 'two' | 'three') | null; updatedAt: string; @@ -1003,7 +1003,7 @@ export interface RadioField { * via the `definition` "group-fields". */ export interface GroupField { - id: string; + id: number; /** * This is a group. */ @@ -1085,22 +1085,22 @@ export interface GroupField { select?: ('one' | 'two')[] | null; }; localizedGroupRel?: { - email?: (string | null) | EmailField; + email?: (number | null) | EmailField; }; localizedGroupManyRel?: { - email?: (string | EmailField)[] | null; + email?: (number | EmailField)[] | null; }; localizedGroupPolyRel?: { email?: { relationTo: 'email-fields'; - value: string | EmailField; + value: number | EmailField; } | null; }; localizedGroupPolyHasManyRel?: { email?: | { relationTo: 'email-fields'; - value: string | EmailField; + value: number | EmailField; }[] | null; }; @@ -1154,30 +1154,30 @@ export interface RowField { * via the `definition` "indexed-fields". */ export interface IndexedField { - id: string; + id: number; text: string; uniqueText?: string | null; - uniqueRelationship?: (string | null) | TextField; - uniqueHasManyRelationship?: (string | TextField)[] | null; - uniqueHasManyRelationship_2?: (string | TextField)[] | null; + uniqueRelationship?: (number | null) | TextField; + uniqueHasManyRelationship?: (number | TextField)[] | null; + uniqueHasManyRelationship_2?: (number | TextField)[] | null; uniquePolymorphicRelationship?: { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null; uniquePolymorphicRelationship_2?: { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null; uniqueHasManyPolymorphicRelationship?: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; }[] | null; uniqueHasManyPolymorphicRelationship_2?: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; }[] | null; uniqueRequiredText: string; @@ -1213,7 +1213,7 @@ export interface IndexedField { * via the `definition` "json-fields". */ export interface JsonField { - id: string; + id: number; json?: { array?: { object?: { @@ -1254,7 +1254,7 @@ export interface JsonField { * via the `definition` "number-fields". */ export interface NumberField { - id: string; + id: number; number?: number | null; min?: number | null; max?: number | null; @@ -1289,7 +1289,7 @@ export interface NumberField { * via the `definition` "point-fields". */ export interface PointField { - id: string; + id: number; /** * @minItems 2 * @maxItems 2 @@ -1320,83 +1320,83 @@ export interface PointField { * via the `definition` "relationship-fields". */ export interface RelationshipField { - id: string; + id: number; text?: string | null; relationship: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | { relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; }; relationHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | { relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } )[] | null; - relationToSelf?: (string | null) | RelationshipField; - relationToSelfSelectOnly?: (string | null) | RelationshipField; - relationWithAllowCreateToFalse?: (string | null) | User; - relationWithAllowEditToFalse?: (string | null) | User; - relationWithDynamicDefault?: (string | null) | User; + relationToSelf?: (number | null) | RelationshipField; + relationToSelfSelectOnly?: (number | null) | RelationshipField; + relationWithAllowCreateToFalse?: (number | null) | User; + relationWithAllowEditToFalse?: (number | null) | User; + relationWithDynamicDefault?: (number | null) | User; relationHasManyWithDynamicDefault?: { relationTo: 'users'; - value: string | User; + value: number | User; } | null; - relationshipWithMin?: (string | TextField)[] | null; - relationshipWithMax?: (string | TextField)[] | null; - relationshipHasMany?: (string | TextField)[] | null; + relationshipWithMin?: (number | TextField)[] | null; + relationshipWithMax?: (number | TextField)[] | null; + relationshipHasMany?: (number | TextField)[] | null; array?: | { - relationship?: (string | null) | TextField; + relationship?: (number | null) | TextField; id?: string | null; }[] | null; relationshipWithMinRows?: | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; }[] | null; relationToRow?: (string | null) | RowField; relationToRowMany?: (string | RowField)[] | null; - relationshipDrawer?: (string | null) | TextField; - relationshipDrawerReadOnly?: (string | null) | TextField; + relationshipDrawer?: (number | null) | TextField; + relationshipDrawerReadOnly?: (number | null) | TextField; polymorphicRelationshipDrawer?: | ({ relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null) | ({ relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } | null); - relationshipDrawerHasMany?: (string | TextField)[] | null; + relationshipDrawerHasMany?: (number | TextField)[] | null; relationshipDrawerHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | { relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } )[] | null; - relationshipDrawerWithAllowCreateFalse?: (string | null) | TextField; + relationshipDrawerWithAllowCreateFalse?: (number | null) | TextField; relationshipDrawerWithFilterOptions?: { relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null; updatedAt: string; createdAt: string; @@ -1406,7 +1406,7 @@ export interface RelationshipField { * via the `definition` "select-fields". */ export interface SelectField { - id: string; + id: number; select?: ('one' | 'two' | 'three') | null; selectReadOnly?: ('one' | 'two' | 'three') | null; selectHasMany?: ('one' | 'two' | 'three' | 'four' | 'five' | 'six')[] | null; @@ -1436,7 +1436,7 @@ export interface SelectField { * via the `definition` "tabs-fields-2". */ export interface TabsFields2 { - id: string; + id: number; tabsInArray?: | { text?: string | null; @@ -1454,7 +1454,7 @@ export interface TabsFields2 { * via the `definition` "tabs-fields". */ export interface TabsField { - id: string; + id: number; /** * This should not collapse despite there being many tabs pushing the main fields open. */ @@ -1556,9 +1556,9 @@ export interface TabWithName { * via the `definition` "uploads". */ export interface Upload { - id: string; + id: number; text?: string | null; - media?: (string | null) | Upload; + media?: (number | null) | Upload; updatedAt: string; createdAt: string; url?: string | null; @@ -1576,9 +1576,9 @@ export interface Upload { * via the `definition` "uploads2". */ export interface Uploads2 { - id: string; + id: number; text?: string | null; - media?: (string | null) | Uploads2; + media?: (number | null) | Uploads2; updatedAt: string; createdAt: string; url?: string | null; @@ -1596,8 +1596,8 @@ export interface Uploads2 { * via the `definition` "uploads3". */ export interface Uploads3 { - id: string; - media?: (string | null) | Uploads3; + id: number; + media?: (number | null) | Uploads3; updatedAt: string; createdAt: string; url?: string | null; @@ -1615,9 +1615,9 @@ export interface Uploads3 { * via the `definition` "uploads-multi". */ export interface UploadsMulti { - id: string; + id: number; text?: string | null; - media?: (string | Upload)[] | null; + media?: (number | Upload)[] | null; updatedAt: string; createdAt: string; } @@ -1626,16 +1626,16 @@ export interface UploadsMulti { * via the `definition` "uploads-poly". */ export interface UploadsPoly { - id: string; + id: number; text?: string | null; media?: | ({ relationTo: 'uploads'; - value: string | Upload; + value: number | Upload; } | null) | ({ relationTo: 'uploads2'; - value: string | Uploads2; + value: number | Uploads2; } | null); updatedAt: string; createdAt: string; @@ -1645,17 +1645,17 @@ export interface UploadsPoly { * via the `definition` "uploads-multi-poly". */ export interface UploadsMultiPoly { - id: string; + id: number; text?: string | null; media?: | ( | { relationTo: 'uploads'; - value: string | Upload; + value: number | Upload; } | { relationTo: 'uploads2'; - value: string | Uploads2; + value: number | Uploads2; } )[] | null; @@ -1667,11 +1667,11 @@ export interface UploadsMultiPoly { * via the `definition` "uploads-restricted". */ export interface UploadsRestricted { - id: string; + id: number; text?: string | null; - uploadWithoutRestriction?: (string | null) | Upload; - uploadWithAllowCreateFalse?: (string | null) | Upload; - uploadMultipleWithAllowCreateFalse?: (string | Upload)[] | null; + uploadWithoutRestriction?: (number | null) | Upload; + uploadWithAllowCreateFalse?: (number | null) | Upload; + uploadMultipleWithAllowCreateFalse?: (number | Upload)[] | null; updatedAt: string; createdAt: string; } @@ -1680,7 +1680,7 @@ export interface UploadsRestricted { * via the `definition` "ui-fields". */ export interface UiField { - id: string; + id: number; text: string; updatedAt: string; createdAt: string; @@ -1690,39 +1690,39 @@ export interface UiField { * via the `definition` "payload-locked-documents". */ export interface PayloadLockedDocument { - id: string; + id: number; document?: | ({ relationTo: 'users'; - value: string | User; + value: number | User; } | null) | ({ relationTo: 'select-versions-fields'; - value: string | SelectVersionsField; + value: number | SelectVersionsField; } | null) | ({ relationTo: 'array-fields'; - value: string | ArrayField; + value: number | ArrayField; } | null) | ({ relationTo: 'block-fields'; - value: string | BlockField; + value: number | BlockField; } | null) | ({ relationTo: 'checkbox-fields'; - value: string | CheckboxField; + value: number | CheckboxField; } | null) | ({ relationTo: 'code-fields'; - value: string | CodeField; + value: number | CodeField; } | null) | ({ relationTo: 'collapsible-fields'; - value: string | CollapsibleField; + value: number | CollapsibleField; } | null) | ({ relationTo: 'conditional-logic'; - value: string | ConditionalLogic; + value: number | ConditionalLogic; } | null) | ({ relationTo: 'custom-id'; @@ -1730,27 +1730,27 @@ export interface PayloadLockedDocument { } | null) | ({ relationTo: 'custom-tab-id'; - value: string | CustomTabId; + value: number | CustomTabId; } | null) | ({ relationTo: 'custom-row-id'; - value: string | CustomRowId; + value: number | CustomRowId; } | null) | ({ relationTo: 'date-fields'; - value: string | DateField; + value: number | DateField; } | null) | ({ relationTo: 'email-fields'; - value: string | EmailField; + value: number | EmailField; } | null) | ({ relationTo: 'radio-fields'; - value: string | RadioField; + value: number | RadioField; } | null) | ({ relationTo: 'group-fields'; - value: string | GroupField; + value: number | GroupField; } | null) | ({ relationTo: 'row-fields'; @@ -1758,76 +1758,76 @@ export interface PayloadLockedDocument { } | null) | ({ relationTo: 'indexed-fields'; - value: string | IndexedField; + value: number | IndexedField; } | null) | ({ relationTo: 'json-fields'; - value: string | JsonField; + value: number | JsonField; } | null) | ({ relationTo: 'number-fields'; - value: string | NumberField; + value: number | NumberField; } | null) | ({ relationTo: 'point-fields'; - value: string | PointField; + value: number | PointField; } | null) | ({ relationTo: 'relationship-fields'; - value: string | RelationshipField; + value: number | RelationshipField; } | null) | ({ relationTo: 'select-fields'; - value: string | SelectField; + value: number | SelectField; } | null) | ({ relationTo: 'tabs-fields-2'; - value: string | TabsFields2; + value: number | TabsFields2; } | null) | ({ relationTo: 'tabs-fields'; - value: string | TabsField; + value: number | TabsField; } | null) | ({ relationTo: 'text-fields'; - value: string | TextField; + value: number | TextField; } | null) | ({ relationTo: 'uploads'; - value: string | Upload; + value: number | Upload; } | null) | ({ relationTo: 'uploads2'; - value: string | Uploads2; + value: number | Uploads2; } | null) | ({ relationTo: 'uploads3'; - value: string | Uploads3; + value: number | Uploads3; } | null) | ({ relationTo: 'uploads-multi'; - value: string | UploadsMulti; + value: number | UploadsMulti; } | null) | ({ relationTo: 'uploads-poly'; - value: string | UploadsPoly; + value: number | UploadsPoly; } | null) | ({ relationTo: 'uploads-multi-poly'; - value: string | UploadsMultiPoly; + value: number | UploadsMultiPoly; } | null) | ({ relationTo: 'uploads-restricted'; - value: string | UploadsRestricted; + value: number | UploadsRestricted; } | null) | ({ relationTo: 'ui-fields'; - value: string | UiField; + value: number | UiField; } | null); globalSlug?: string | null; user: { relationTo: 'users'; - value: string | User; + value: number | User; }; updatedAt: string; createdAt: string; @@ -1837,10 +1837,10 @@ export interface PayloadLockedDocument { * via the `definition` "payload-preferences". */ export interface PayloadPreference { - id: string; + id: number; user: { relationTo: 'users'; - value: string | User; + value: number | User; }; key?: string | null; value?: @@ -1860,7 +1860,7 @@ export interface PayloadPreference { * via the `definition` "payload-migrations". */ export interface PayloadMigration { - id: string; + id: number; name?: string | null; batch?: number | null; updatedAt: string; diff --git a/test/versions/collections/Autosave.ts b/test/versions/collections/Autosave.ts index a36e1e53a5c..5f0df3b648c 100644 --- a/test/versions/collections/Autosave.ts +++ b/test/versions/collections/Autosave.ts @@ -53,12 +53,6 @@ const AutosavePosts: CollectionConfig = { unique: true, localized: true, }, - { - name: 'relationship', - label: 'Relationship', - type: 'relationship', - relationTo: 'users', - }, { name: 'computedTitle', label: 'Computed Title', diff --git a/test/versions/collections/Posts.ts b/test/versions/collections/Posts.ts index d981c51baab..e95d8c4569d 100644 --- a/test/versions/collections/Posts.ts +++ b/test/versions/collections/Posts.ts @@ -10,12 +10,6 @@ import { const Posts: CollectionConfig = { slug: postCollectionSlug, fields: [ - { - name: 'relationship', - label: 'Relationship', - type: 'relationship', - relationTo: 'users', - }, { name: 'relationToAutosaves', type: 'relationship', diff --git a/test/versions/payload-types.ts b/test/versions/payload-types.ts index a8261b52728..d707fbf7064 100644 --- a/test/versions/payload-types.ts +++ b/test/versions/payload-types.ts @@ -186,37 +186,12 @@ export interface DisablePublish { */ export interface Post { id: string; - relationship?: (string | null) | User; relationToAutosaves?: (string | null) | AutosavePost; relationToVersions?: (string | null) | VersionPost; relationToDrafts?: (string | null) | DraftPost; updatedAt: string; createdAt: string; } -/** - * This interface was referenced by `Config`'s JSON-Schema - * via the `definition` "users". - */ -export interface User { - id: string; - updatedAt: string; - createdAt: string; - email: string; - resetPasswordToken?: string | null; - resetPasswordExpiration?: string | null; - salt?: string | null; - hash?: string | null; - loginAttempts?: number | null; - lockUntil?: string | null; - sessions?: - | { - id: string; - createdAt?: string | null; - expiresAt: string; - }[] - | null; - password?: string | null; -} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "autosave-posts". @@ -224,7 +199,6 @@ export interface User { export interface AutosavePost { id: string; title: string; - relationship?: (string | null) | User; computedTitle?: string | null; richText?: { root: { @@ -593,6 +567,30 @@ export interface Media2 { focalX?: number | null; focalY?: number | null; } +/** + * This interface was referenced by `Config`'s JSON-Schema + * via the `definition` "users". + */ +export interface User { + id: string; + updatedAt: string; + createdAt: string; + email: string; + resetPasswordToken?: string | null; + resetPasswordExpiration?: string | null; + salt?: string | null; + hash?: string | null; + loginAttempts?: number | null; + lockUntil?: string | null; + sessions?: + | { + id: string; + createdAt?: string | null; + expiresAt: string; + }[] + | null; + password?: string | null; +} /** * This interface was referenced by `Config`'s JSON-Schema * via the `definition` "payload-jobs". @@ -825,7 +823,6 @@ export interface DisablePublishSelect { * via the `definition` "posts_select". */ export interface PostsSelect { - relationship?: T; relationToAutosaves?: T; relationToVersions?: T; relationToDrafts?: T; @@ -838,7 +835,6 @@ export interface PostsSelect { */ export interface AutosavePostsSelect { title?: T; - relationship?: T; computedTitle?: T; richText?: T; json?: T; From 646cf7073295a7c9e4e7c96201d038c3dc842e46 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 3 Sep 2025 10:08:02 -0400 Subject: [PATCH 3/7] ensute row test helpers count rows after manipulating them --- test/helpers/e2e/fields/array/addArrayRow.ts | 15 ++++++++++----- .../helpers/e2e/fields/array/duplicateArrayRow.ts | 7 ++++++- test/helpers/e2e/fields/array/removeArrayRow.ts | 9 +++++++-- 3 files changed, 23 insertions(+), 8 deletions(-) diff --git a/test/helpers/e2e/fields/array/addArrayRow.ts b/test/helpers/e2e/fields/array/addArrayRow.ts index e47e513677f..68e6fc4a109 100644 --- a/test/helpers/e2e/fields/array/addArrayRow.ts +++ b/test/helpers/e2e/fields/array/addArrayRow.ts @@ -18,10 +18,12 @@ export const addArrayRow = async ( page: Page, { fieldName }: Omit[1], 'rowIndex'>, ) => { + const rowSelector = page.locator(`#field-${fieldName} .array-field__row`) + const numberOfCurrentRows = await rowSelector.count() + await addArrayRowAsync(page, fieldName) - // TODO: test the array row has appeared - await wait(300) + expect(await rowSelector.count()).toBe(numberOfCurrentRows + 1) } /** @@ -36,11 +38,14 @@ export const addArrayRowBelow = async ( rowIndex, }) - const addBelowButton = popupContentLocator.locator('.array-actions__action.array-actions__add') + const rowSelector = page.locator(`#field-${fieldName} .array-field__row`) + const numberOfCurrentRows = await rowSelector.count() + + await popupContentLocator.locator('.array-actions__action.array-actions__add').click() - await addBelowButton.click() + expect(await rowSelector.count()).toBe(numberOfCurrentRows + 1) - // TODO: test the array row has appeared + // TODO: test the array row has appeared in the _correct position_ (immediately below the original row) await wait(300) return { popupContentLocator, rowActionsButtonLocator } diff --git a/test/helpers/e2e/fields/array/duplicateArrayRow.ts b/test/helpers/e2e/fields/array/duplicateArrayRow.ts index 4019a13a3c0..827a36a6330 100644 --- a/test/helpers/e2e/fields/array/duplicateArrayRow.ts +++ b/test/helpers/e2e/fields/array/duplicateArrayRow.ts @@ -17,9 +17,14 @@ export const duplicateArrayRow = async ( rowIndex, }) + const rowSelector = page.locator(`#field-${fieldName} .array-field__row`) + const numberOfCurrentRows = await rowSelector.count() + await popupContentLocator.locator('.array-actions__action.array-actions__duplicate').click() - // TODO: test the array row has been duplicated + expect(await rowSelector.count()).toBe(numberOfCurrentRows + 1) + + // TODO: test the array row's field input values have been duplicated as well return { popupContentLocator, rowActionsButtonLocator } } diff --git a/test/helpers/e2e/fields/array/removeArrayRow.ts b/test/helpers/e2e/fields/array/removeArrayRow.ts index 0d69b4cb8f4..1862580020d 100644 --- a/test/helpers/e2e/fields/array/removeArrayRow.ts +++ b/test/helpers/e2e/fields/array/removeArrayRow.ts @@ -17,10 +17,15 @@ export const removeArrayRow = async ( rowIndex, }) + const rowSelector = page.locator(`#field-${fieldName} .array-field__row`) + const numberOfCurrentRows = await rowSelector.count() + await popupContentLocator.locator('.array-actions__action.array-actions__remove').click() - // TODO: test the array row has been removed - // another row may have been moved into its place, though + expect(await rowSelector.count()).toBe(numberOfCurrentRows - 1) + + // TODO: test the array row has been removed in the _correct position_ (original row index) + // another row may have been moved into its place, need to ensure the test accounts for this fact return { popupContentLocator, rowActionsButtonLocator } } From dcc84f375200233d4134b659c01eddb1e7014df7 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 3 Sep 2025 11:06:21 -0400 Subject: [PATCH 4/7] add test --- .../collections/Posts/ArrayRowLabel.tsx | 8 +- test/form-state/e2e.spec.ts | 35 +++++- test/helpers/e2e/fields/array/addArrayRow.ts | 1 + .../e2e/fields/array/duplicateArrayRow.ts | 2 + .../e2e/fields/array/removeArrayRow.ts | 2 + tsconfig.base.json | 114 +++++++++++++----- 6 files changed, 132 insertions(+), 30 deletions(-) diff --git a/test/form-state/collections/Posts/ArrayRowLabel.tsx b/test/form-state/collections/Posts/ArrayRowLabel.tsx index 22a5c9813b8..38f6b4b893f 100644 --- a/test/form-state/collections/Posts/ArrayRowLabel.tsx +++ b/test/form-state/collections/Posts/ArrayRowLabel.tsx @@ -1,5 +1,9 @@ import React from 'react' -export const ArrayRowLabel = () => { - return

This is a custom component

+export const ArrayRowLabel = (props) => { + return ( +

+ This is a custom component +

+ ) } diff --git a/test/form-state/e2e.spec.ts b/test/form-state/e2e.spec.ts index 27fb396099a..cc9bfafbbaf 100644 --- a/test/form-state/e2e.spec.ts +++ b/test/form-state/e2e.spec.ts @@ -6,7 +6,12 @@ import { expect, test } from '@playwright/test' import { assertElementStaysVisible } from 'helpers/e2e/assertElementStaysVisible.js' import { assertNetworkRequests } from 'helpers/e2e/assertNetworkRequests.js' import { assertRequestBody } from 'helpers/e2e/assertRequestBody.js' -import { addArrayRowAsync, removeArrayRow } from 'helpers/e2e/fields/array/index.js' +import { + addArrayRow, + addArrayRowAsync, + duplicateArrayRow, + removeArrayRow, +} from 'helpers/e2e/fields/array/index.js' import { addBlock } from 'helpers/e2e/fields/blocks/index.js' import { waitForAutoSaveToRunAndComplete } from 'helpers/e2e/waitForAutoSaveToRunAndComplete.js' import * as path from 'path' @@ -454,6 +459,34 @@ test.describe('Form State', () => { await expect(computedTitleField).toHaveValue('Test Title 2') }) + test('array and block rows and maintain consistent row IDs across duplication', async () => { + await page.goto(postsUrl.create) + await addArrayRow(page, { fieldName: 'array' }) + + const row0 = page.locator('#field-array #array-row-0') + + await expect(row0.locator('#custom-array-row-label')).toHaveAttribute('data-id') + + await expect(row0.locator('#field-array__0__id')).toHaveValue( + (await row0.locator('#custom-array-row-label').getAttribute('data-id'))!, + ) + + await duplicateArrayRow(page, { fieldName: 'array' }) + + const row1 = page.locator('#field-array #array-row-1') + + await expect(row1.locator('#custom-array-row-label')).toHaveAttribute('data-id') + + await expect(row1.locator('#custom-array-row-label')).not.toHaveAttribute( + 'data-id', + (await row0.locator('#custom-array-row-label').getAttribute('data-id'))!, + ) + + await expect(row1.locator('#field-array__1__id')).toHaveValue( + (await row1.locator('#custom-array-row-label').getAttribute('data-id'))!, + ) + }) + describe('Throttled tests', () => { let cdpSession: CDPSession diff --git a/test/helpers/e2e/fields/array/addArrayRow.ts b/test/helpers/e2e/fields/array/addArrayRow.ts index 68e6fc4a109..ff0e12da4b8 100644 --- a/test/helpers/e2e/fields/array/addArrayRow.ts +++ b/test/helpers/e2e/fields/array/addArrayRow.ts @@ -1,6 +1,7 @@ import type { Locator, Page } from 'playwright' import { wait } from 'payload/shared' +import { expect } from 'playwright/test' import { openArrayRowActions } from './openArrayRowActions.js' diff --git a/test/helpers/e2e/fields/array/duplicateArrayRow.ts b/test/helpers/e2e/fields/array/duplicateArrayRow.ts index 827a36a6330..37021f61945 100644 --- a/test/helpers/e2e/fields/array/duplicateArrayRow.ts +++ b/test/helpers/e2e/fields/array/duplicateArrayRow.ts @@ -1,5 +1,7 @@ import type { Locator, Page } from 'playwright' +import { expect } from 'playwright/test' + import { openArrayRowActions } from './openArrayRowActions.js' /** diff --git a/test/helpers/e2e/fields/array/removeArrayRow.ts b/test/helpers/e2e/fields/array/removeArrayRow.ts index 1862580020d..957b197627b 100644 --- a/test/helpers/e2e/fields/array/removeArrayRow.ts +++ b/test/helpers/e2e/fields/array/removeArrayRow.ts @@ -1,5 +1,7 @@ import type { Locator, Page } from 'playwright' +import { expect } from 'playwright/test' + import { openArrayRowActions } from './openArrayRowActions.js' /** diff --git a/tsconfig.base.json b/tsconfig.base.json index 0898ad390f3..d3328f379ff 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -21,8 +21,15 @@ "skipLibCheck": true, "emitDeclarationOnly": true, "sourceMap": true, - "lib": ["DOM", "DOM.Iterable", "ES2022"], - "types": ["node", "jest"], + "lib": [ + "DOM", + "DOM.Iterable", + "ES2022" + ], + "types": [ + "node", + "jest" + ], "incremental": true, "isolatedModules": true, "plugins": [ @@ -31,36 +38,72 @@ } ], "paths": { - "@payload-config": ["./test/_community/config.ts"], - "@payloadcms/admin-bar": ["./packages/admin-bar/src"], - "@payloadcms/live-preview": ["./packages/live-preview/src"], - "@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"], - "@payloadcms/live-preview-vue": ["./packages/live-preview-vue/src/index.ts"], - "@payloadcms/ui": ["./packages/ui/src/exports/client/index.ts"], - "@payloadcms/ui/shared": ["./packages/ui/src/exports/shared/index.ts"], - "@payloadcms/ui/rsc": ["./packages/ui/src/exports/rsc/index.ts"], - "@payloadcms/ui/scss": ["./packages/ui/src/scss.scss"], - "@payloadcms/ui/scss/app.scss": ["./packages/ui/src/scss/app.scss"], - "@payloadcms/next/*": ["./packages/next/src/exports/*.ts"], + "@payload-config": [ + "./test/form-state/config.ts" + ], + "@payloadcms/admin-bar": [ + "./packages/admin-bar/src" + ], + "@payloadcms/live-preview": [ + "./packages/live-preview/src" + ], + "@payloadcms/live-preview-react": [ + "./packages/live-preview-react/src/index.ts" + ], + "@payloadcms/live-preview-vue": [ + "./packages/live-preview-vue/src/index.ts" + ], + "@payloadcms/ui": [ + "./packages/ui/src/exports/client/index.ts" + ], + "@payloadcms/ui/shared": [ + "./packages/ui/src/exports/shared/index.ts" + ], + "@payloadcms/ui/rsc": [ + "./packages/ui/src/exports/rsc/index.ts" + ], + "@payloadcms/ui/scss": [ + "./packages/ui/src/scss.scss" + ], + "@payloadcms/ui/scss/app.scss": [ + "./packages/ui/src/scss/app.scss" + ], + "@payloadcms/next/*": [ + "./packages/next/src/exports/*.ts" + ], "@payloadcms/richtext-lexical/client": [ "./packages/richtext-lexical/src/exports/client/index.ts" ], - "@payloadcms/richtext-lexical/rsc": ["./packages/richtext-lexical/src/exports/server/rsc.ts"], - "@payloadcms/richtext-slate/rsc": ["./packages/richtext-slate/src/exports/server/rsc.ts"], + "@payloadcms/richtext-lexical/rsc": [ + "./packages/richtext-lexical/src/exports/server/rsc.ts" + ], + "@payloadcms/richtext-slate/rsc": [ + "./packages/richtext-slate/src/exports/server/rsc.ts" + ], "@payloadcms/richtext-slate/client": [ "./packages/richtext-slate/src/exports/client/index.ts" ], - "@payloadcms/plugin-seo/client": ["./packages/plugin-seo/src/exports/client.ts"], - "@payloadcms/plugin-sentry/client": ["./packages/plugin-sentry/src/exports/client.ts"], - "@payloadcms/plugin-stripe/client": ["./packages/plugin-stripe/src/exports/client.ts"], - "@payloadcms/plugin-search/client": ["./packages/plugin-search/src/exports/client.ts"], + "@payloadcms/plugin-seo/client": [ + "./packages/plugin-seo/src/exports/client.ts" + ], + "@payloadcms/plugin-sentry/client": [ + "./packages/plugin-sentry/src/exports/client.ts" + ], + "@payloadcms/plugin-stripe/client": [ + "./packages/plugin-stripe/src/exports/client.ts" + ], + "@payloadcms/plugin-search/client": [ + "./packages/plugin-search/src/exports/client.ts" + ], "@payloadcms/plugin-form-builder/client": [ "./packages/plugin-form-builder/src/exports/client.ts" ], "@payloadcms/plugin-import-export/rsc": [ "./packages/plugin-import-export/src/exports/rsc.ts" ], - "@payloadcms/plugin-multi-tenant/rsc": ["./packages/plugin-multi-tenant/src/exports/rsc.ts"], + "@payloadcms/plugin-multi-tenant/rsc": [ + "./packages/plugin-multi-tenant/src/exports/rsc.ts" + ], "@payloadcms/plugin-multi-tenant/utilities": [ "./packages/plugin-multi-tenant/src/exports/utilities.ts" ], @@ -70,25 +113,42 @@ "@payloadcms/plugin-multi-tenant/client": [ "./packages/plugin-multi-tenant/src/exports/client.ts" ], - "@payloadcms/plugin-multi-tenant": ["./packages/plugin-multi-tenant/src/index.ts"], + "@payloadcms/plugin-multi-tenant": [ + "./packages/plugin-multi-tenant/src/index.ts" + ], "@payloadcms/plugin-multi-tenant/translations/languages/all": [ "./packages/plugin-multi-tenant/src/translations/index.ts" ], "@payloadcms/plugin-multi-tenant/translations/languages/*": [ "./packages/plugin-multi-tenant/src/translations/languages/*.ts" ], - "@payloadcms/next": ["./packages/next/src/exports/*"], - "@payloadcms/storage-azure/client": ["./packages/storage-azure/src/exports/client.ts"], - "@payloadcms/storage-s3/client": ["./packages/storage-s3/src/exports/client.ts"], + "@payloadcms/next": [ + "./packages/next/src/exports/*" + ], + "@payloadcms/storage-azure/client": [ + "./packages/storage-azure/src/exports/client.ts" + ], + "@payloadcms/storage-s3/client": [ + "./packages/storage-s3/src/exports/client.ts" + ], "@payloadcms/storage-vercel-blob/client": [ "./packages/storage-vercel-blob/src/exports/client.ts" ], - "@payloadcms/storage-gcs/client": ["./packages/storage-gcs/src/exports/client.ts"], + "@payloadcms/storage-gcs/client": [ + "./packages/storage-gcs/src/exports/client.ts" + ], "@payloadcms/storage-uploadthing/client": [ "./packages/storage-uploadthing/src/exports/client.ts" ] } }, - "include": ["${configDir}/src"], - "exclude": ["${configDir}/dist", "${configDir}/build", "${configDir}/temp", "**/*.spec.ts"] + "include": [ + "${configDir}/src" + ], + "exclude": [ + "${configDir}/dist", + "${configDir}/build", + "${configDir}/temp", + "**/*.spec.ts" + ] } From 02782533d6f88178c84df7ce06ae2f9fb21101a1 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 3 Sep 2025 11:06:47 -0400 Subject: [PATCH 5/7] reset tsconfig --- tsconfig.base.json | 114 +++++++++++---------------------------------- 1 file changed, 27 insertions(+), 87 deletions(-) diff --git a/tsconfig.base.json b/tsconfig.base.json index d3328f379ff..0898ad390f3 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -21,15 +21,8 @@ "skipLibCheck": true, "emitDeclarationOnly": true, "sourceMap": true, - "lib": [ - "DOM", - "DOM.Iterable", - "ES2022" - ], - "types": [ - "node", - "jest" - ], + "lib": ["DOM", "DOM.Iterable", "ES2022"], + "types": ["node", "jest"], "incremental": true, "isolatedModules": true, "plugins": [ @@ -38,72 +31,36 @@ } ], "paths": { - "@payload-config": [ - "./test/form-state/config.ts" - ], - "@payloadcms/admin-bar": [ - "./packages/admin-bar/src" - ], - "@payloadcms/live-preview": [ - "./packages/live-preview/src" - ], - "@payloadcms/live-preview-react": [ - "./packages/live-preview-react/src/index.ts" - ], - "@payloadcms/live-preview-vue": [ - "./packages/live-preview-vue/src/index.ts" - ], - "@payloadcms/ui": [ - "./packages/ui/src/exports/client/index.ts" - ], - "@payloadcms/ui/shared": [ - "./packages/ui/src/exports/shared/index.ts" - ], - "@payloadcms/ui/rsc": [ - "./packages/ui/src/exports/rsc/index.ts" - ], - "@payloadcms/ui/scss": [ - "./packages/ui/src/scss.scss" - ], - "@payloadcms/ui/scss/app.scss": [ - "./packages/ui/src/scss/app.scss" - ], - "@payloadcms/next/*": [ - "./packages/next/src/exports/*.ts" - ], + "@payload-config": ["./test/_community/config.ts"], + "@payloadcms/admin-bar": ["./packages/admin-bar/src"], + "@payloadcms/live-preview": ["./packages/live-preview/src"], + "@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"], + "@payloadcms/live-preview-vue": ["./packages/live-preview-vue/src/index.ts"], + "@payloadcms/ui": ["./packages/ui/src/exports/client/index.ts"], + "@payloadcms/ui/shared": ["./packages/ui/src/exports/shared/index.ts"], + "@payloadcms/ui/rsc": ["./packages/ui/src/exports/rsc/index.ts"], + "@payloadcms/ui/scss": ["./packages/ui/src/scss.scss"], + "@payloadcms/ui/scss/app.scss": ["./packages/ui/src/scss/app.scss"], + "@payloadcms/next/*": ["./packages/next/src/exports/*.ts"], "@payloadcms/richtext-lexical/client": [ "./packages/richtext-lexical/src/exports/client/index.ts" ], - "@payloadcms/richtext-lexical/rsc": [ - "./packages/richtext-lexical/src/exports/server/rsc.ts" - ], - "@payloadcms/richtext-slate/rsc": [ - "./packages/richtext-slate/src/exports/server/rsc.ts" - ], + "@payloadcms/richtext-lexical/rsc": ["./packages/richtext-lexical/src/exports/server/rsc.ts"], + "@payloadcms/richtext-slate/rsc": ["./packages/richtext-slate/src/exports/server/rsc.ts"], "@payloadcms/richtext-slate/client": [ "./packages/richtext-slate/src/exports/client/index.ts" ], - "@payloadcms/plugin-seo/client": [ - "./packages/plugin-seo/src/exports/client.ts" - ], - "@payloadcms/plugin-sentry/client": [ - "./packages/plugin-sentry/src/exports/client.ts" - ], - "@payloadcms/plugin-stripe/client": [ - "./packages/plugin-stripe/src/exports/client.ts" - ], - "@payloadcms/plugin-search/client": [ - "./packages/plugin-search/src/exports/client.ts" - ], + "@payloadcms/plugin-seo/client": ["./packages/plugin-seo/src/exports/client.ts"], + "@payloadcms/plugin-sentry/client": ["./packages/plugin-sentry/src/exports/client.ts"], + "@payloadcms/plugin-stripe/client": ["./packages/plugin-stripe/src/exports/client.ts"], + "@payloadcms/plugin-search/client": ["./packages/plugin-search/src/exports/client.ts"], "@payloadcms/plugin-form-builder/client": [ "./packages/plugin-form-builder/src/exports/client.ts" ], "@payloadcms/plugin-import-export/rsc": [ "./packages/plugin-import-export/src/exports/rsc.ts" ], - "@payloadcms/plugin-multi-tenant/rsc": [ - "./packages/plugin-multi-tenant/src/exports/rsc.ts" - ], + "@payloadcms/plugin-multi-tenant/rsc": ["./packages/plugin-multi-tenant/src/exports/rsc.ts"], "@payloadcms/plugin-multi-tenant/utilities": [ "./packages/plugin-multi-tenant/src/exports/utilities.ts" ], @@ -113,42 +70,25 @@ "@payloadcms/plugin-multi-tenant/client": [ "./packages/plugin-multi-tenant/src/exports/client.ts" ], - "@payloadcms/plugin-multi-tenant": [ - "./packages/plugin-multi-tenant/src/index.ts" - ], + "@payloadcms/plugin-multi-tenant": ["./packages/plugin-multi-tenant/src/index.ts"], "@payloadcms/plugin-multi-tenant/translations/languages/all": [ "./packages/plugin-multi-tenant/src/translations/index.ts" ], "@payloadcms/plugin-multi-tenant/translations/languages/*": [ "./packages/plugin-multi-tenant/src/translations/languages/*.ts" ], - "@payloadcms/next": [ - "./packages/next/src/exports/*" - ], - "@payloadcms/storage-azure/client": [ - "./packages/storage-azure/src/exports/client.ts" - ], - "@payloadcms/storage-s3/client": [ - "./packages/storage-s3/src/exports/client.ts" - ], + "@payloadcms/next": ["./packages/next/src/exports/*"], + "@payloadcms/storage-azure/client": ["./packages/storage-azure/src/exports/client.ts"], + "@payloadcms/storage-s3/client": ["./packages/storage-s3/src/exports/client.ts"], "@payloadcms/storage-vercel-blob/client": [ "./packages/storage-vercel-blob/src/exports/client.ts" ], - "@payloadcms/storage-gcs/client": [ - "./packages/storage-gcs/src/exports/client.ts" - ], + "@payloadcms/storage-gcs/client": ["./packages/storage-gcs/src/exports/client.ts"], "@payloadcms/storage-uploadthing/client": [ "./packages/storage-uploadthing/src/exports/client.ts" ] } }, - "include": [ - "${configDir}/src" - ], - "exclude": [ - "${configDir}/dist", - "${configDir}/build", - "${configDir}/temp", - "**/*.spec.ts" - ] + "include": ["${configDir}/src"], + "exclude": ["${configDir}/dist", "${configDir}/build", "${configDir}/temp", "**/*.spec.ts"] } From 899594e48e043a1c3d2767acc7dac6461725abb5 Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 3 Sep 2025 12:50:47 -0400 Subject: [PATCH 6/7] passing test --- test/fields/collections/Blocks/e2e.spec.ts | 24 +- test/fields/payload-types.ts | 226 +++++++++--------- test/helpers/e2e/fields/array/addArrayRow.ts | 14 +- .../e2e/fields/array/duplicateArrayRow.ts | 8 +- .../e2e/fields/array/removeArrayRow.ts | 8 +- test/helpers/e2e/fields/blocks/addBlock.ts | 75 +++++- test/helpers/e2e/fields/blocks/index.ts | 2 +- 7 files changed, 208 insertions(+), 149 deletions(-) diff --git a/test/fields/collections/Blocks/e2e.spec.ts b/test/fields/collections/Blocks/e2e.spec.ts index 55f2d126252..2b5a40f152c 100644 --- a/test/fields/collections/Blocks/e2e.spec.ts +++ b/test/fields/collections/Blocks/e2e.spec.ts @@ -2,8 +2,13 @@ import type { BrowserContext, Page } from '@playwright/test' import { expect, test } from '@playwright/test' import { copyPasteField } from 'helpers/e2e/copyPasteField.js' -import { addArrayRowBelow, duplicateArrayRow } from 'helpers/e2e/fields/array/index.js' -import { addBlock, openBlocksDrawer, reorderBlocks } from 'helpers/e2e/fields/blocks/index.js' +import { duplicateArrayRow } from 'helpers/e2e/fields/array/index.js' +import { + addBlock, + addBlockBelow, + openBlocksDrawer, + reorderBlocks, +} from 'helpers/e2e/fields/blocks/index.js' import { scrollEntirePage } from 'helpers/e2e/scrollEntirePage.js' import { toggleBlockOrArrayRow } from 'helpers/e2e/toggleCollapsible.js' import path from 'path' @@ -127,22 +132,13 @@ describe('Block fields', () => { test('should open blocks drawer from block row and add below', async () => { await page.goto(url.create) - await addArrayRowBelow(page, { fieldName: 'blocks' }) - - const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]') - await expect(blocksDrawer).toBeVisible() - - // select the first block in the drawer - const firstBlockSelector = blocksDrawer - .locator('.blocks-drawer__blocks .blocks-drawer__block') - .first() - - await expect(firstBlockSelector).toContainText('Content') - await firstBlockSelector.click() + await addBlockBelow(page, { fieldName: 'blocks', blockToSelect: 'Content' }) // ensure the block was inserted beneath the first in the rows const addedRow = page.locator('#field-blocks #blocks-row-1') + await expect(addedRow).toBeVisible() + await expect(addedRow.locator('.blocks-field__block-header')).toHaveText( 'Custom Block Label: Content 02', ) // went from `Number` to `Content` diff --git a/test/fields/payload-types.ts b/test/fields/payload-types.ts index 9a2b3ff50e5..43f1cff19e0 100644 --- a/test/fields/payload-types.ts +++ b/test/fields/payload-types.ts @@ -149,7 +149,7 @@ export interface Config { 'payload-migrations': PayloadMigrationsSelect | PayloadMigrationsSelect; }; db: { - defaultIDType: number; + defaultIDType: string; }; globals: {}; globalsSelect: {}; @@ -215,7 +215,7 @@ export interface LocalizedTextReference2 { * via the `definition` "users". */ export interface User { - id: number; + id: string; canViewConditionalField?: boolean | null; updatedAt: string; createdAt: string; @@ -240,7 +240,7 @@ export interface User { * via the `definition` "select-versions-fields". */ export interface SelectVersionsField { - id: number; + id: string; hasMany?: ('a' | 'b' | 'c' | 'd')[] | null; array?: | { @@ -265,7 +265,7 @@ export interface SelectVersionsField { * via the `definition` "array-fields". */ export interface ArrayField { - id: number; + id: string; title?: string | null; items: { text: string; @@ -369,7 +369,7 @@ export interface ArrayField { * via the `definition` "block-fields". */ export interface BlockField { - id: number; + id: string; blocks: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; duplicate: (ContentBlock | NoBlockname | NumberBlock | SubBlocksBlock | TabsBlock)[]; collapsedByDefaultBlocks: ( @@ -500,7 +500,7 @@ export interface BlockField { | null; relationshipBlocks?: | { - relationship?: (number | null) | TextField; + relationship?: (string | null) | TextField; id?: string | null; blockName?: string | null; blockType: 'relationships'; @@ -697,7 +697,7 @@ export interface LocalizedTabsBlock { * via the `definition` "text-fields". */ export interface TextField { - id: number; + id: string; text: string; hiddenTextField?: string | null; /** @@ -749,7 +749,7 @@ export interface TextField { * via the `definition` "checkbox-fields". */ export interface CheckboxField { - id: number; + id: string; checkbox: boolean; checkboxNotRequired?: boolean | null; updatedAt: string; @@ -760,7 +760,7 @@ export interface CheckboxField { * via the `definition` "code-fields". */ export interface CodeField { - id: number; + id: string; javascript?: string | null; typescript?: string | null; json?: string | null; @@ -775,7 +775,7 @@ export interface CodeField { * via the `definition` "collapsible-fields". */ export interface CollapsibleField { - id: number; + id: string; text: string; group: { textWithinGroup?: string | null; @@ -808,7 +808,7 @@ export interface CollapsibleField { * via the `definition` "conditional-logic". */ export interface ConditionalLogic { - id: number; + id: string; text: string; toggleField?: boolean | null; fieldWithDocIDCondition?: string | null; @@ -922,7 +922,7 @@ export interface CustomRowId { * via the `definition` "date-fields". */ export interface DateField { - id: number; + id: string; default: string; timeOnly?: string | null; timeOnlyWithMiliseconds?: string | null; @@ -967,7 +967,7 @@ export interface DateField { * via the `definition` "email-fields". */ export interface EmailField { - id: number; + id: string; email: string; localizedEmail?: string | null; emailWithAutocomplete?: string | null; @@ -992,7 +992,7 @@ export interface EmailField { * via the `definition` "radio-fields". */ export interface RadioField { - id: number; + id: string; radio?: ('one' | 'two' | 'three') | null; radioWithJsxLabelOption?: ('one' | 'two' | 'three') | null; updatedAt: string; @@ -1003,7 +1003,7 @@ export interface RadioField { * via the `definition` "group-fields". */ export interface GroupField { - id: number; + id: string; /** * This is a group. */ @@ -1085,22 +1085,22 @@ export interface GroupField { select?: ('one' | 'two')[] | null; }; localizedGroupRel?: { - email?: (number | null) | EmailField; + email?: (string | null) | EmailField; }; localizedGroupManyRel?: { - email?: (number | EmailField)[] | null; + email?: (string | EmailField)[] | null; }; localizedGroupPolyRel?: { email?: { relationTo: 'email-fields'; - value: number | EmailField; + value: string | EmailField; } | null; }; localizedGroupPolyHasManyRel?: { email?: | { relationTo: 'email-fields'; - value: number | EmailField; + value: string | EmailField; }[] | null; }; @@ -1154,30 +1154,30 @@ export interface RowField { * via the `definition` "indexed-fields". */ export interface IndexedField { - id: number; + id: string; text: string; uniqueText?: string | null; - uniqueRelationship?: (number | null) | TextField; - uniqueHasManyRelationship?: (number | TextField)[] | null; - uniqueHasManyRelationship_2?: (number | TextField)[] | null; + uniqueRelationship?: (string | null) | TextField; + uniqueHasManyRelationship?: (string | TextField)[] | null; + uniqueHasManyRelationship_2?: (string | TextField)[] | null; uniquePolymorphicRelationship?: { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null; uniquePolymorphicRelationship_2?: { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null; uniqueHasManyPolymorphicRelationship?: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; }[] | null; uniqueHasManyPolymorphicRelationship_2?: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; }[] | null; uniqueRequiredText: string; @@ -1213,7 +1213,7 @@ export interface IndexedField { * via the `definition` "json-fields". */ export interface JsonField { - id: number; + id: string; json?: { array?: { object?: { @@ -1254,7 +1254,7 @@ export interface JsonField { * via the `definition` "number-fields". */ export interface NumberField { - id: number; + id: string; number?: number | null; min?: number | null; max?: number | null; @@ -1289,7 +1289,7 @@ export interface NumberField { * via the `definition` "point-fields". */ export interface PointField { - id: number; + id: string; /** * @minItems 2 * @maxItems 2 @@ -1320,83 +1320,83 @@ export interface PointField { * via the `definition` "relationship-fields". */ export interface RelationshipField { - id: number; + id: string; text?: string | null; relationship: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | { relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; }; relationHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | { relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } )[] | null; - relationToSelf?: (number | null) | RelationshipField; - relationToSelfSelectOnly?: (number | null) | RelationshipField; - relationWithAllowCreateToFalse?: (number | null) | User; - relationWithAllowEditToFalse?: (number | null) | User; - relationWithDynamicDefault?: (number | null) | User; + relationToSelf?: (string | null) | RelationshipField; + relationToSelfSelectOnly?: (string | null) | RelationshipField; + relationWithAllowCreateToFalse?: (string | null) | User; + relationWithAllowEditToFalse?: (string | null) | User; + relationWithDynamicDefault?: (string | null) | User; relationHasManyWithDynamicDefault?: { relationTo: 'users'; - value: number | User; + value: string | User; } | null; - relationshipWithMin?: (number | TextField)[] | null; - relationshipWithMax?: (number | TextField)[] | null; - relationshipHasMany?: (number | TextField)[] | null; + relationshipWithMin?: (string | TextField)[] | null; + relationshipWithMax?: (string | TextField)[] | null; + relationshipHasMany?: (string | TextField)[] | null; array?: | { - relationship?: (number | null) | TextField; + relationship?: (string | null) | TextField; id?: string | null; }[] | null; relationshipWithMinRows?: | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; }[] | null; relationToRow?: (string | null) | RowField; relationToRowMany?: (string | RowField)[] | null; - relationshipDrawer?: (number | null) | TextField; - relationshipDrawerReadOnly?: (number | null) | TextField; + relationshipDrawer?: (string | null) | TextField; + relationshipDrawerReadOnly?: (string | null) | TextField; polymorphicRelationshipDrawer?: | ({ relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null) | ({ relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } | null); - relationshipDrawerHasMany?: (number | TextField)[] | null; + relationshipDrawerHasMany?: (string | TextField)[] | null; relationshipDrawerHasManyPolymorphic?: | ( | { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | { relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } )[] | null; - relationshipDrawerWithAllowCreateFalse?: (number | null) | TextField; + relationshipDrawerWithAllowCreateFalse?: (string | null) | TextField; relationshipDrawerWithFilterOptions?: { relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null; updatedAt: string; createdAt: string; @@ -1406,7 +1406,7 @@ export interface RelationshipField { * via the `definition` "select-fields". */ export interface SelectField { - id: number; + id: string; select?: ('one' | 'two' | 'three') | null; selectReadOnly?: ('one' | 'two' | 'three') | null; selectHasMany?: ('one' | 'two' | 'three' | 'four' | 'five' | 'six')[] | null; @@ -1436,7 +1436,7 @@ export interface SelectField { * via the `definition` "tabs-fields-2". */ export interface TabsFields2 { - id: number; + id: string; tabsInArray?: | { text?: string | null; @@ -1454,7 +1454,7 @@ export interface TabsFields2 { * via the `definition` "tabs-fields". */ export interface TabsField { - id: number; + id: string; /** * This should not collapse despite there being many tabs pushing the main fields open. */ @@ -1556,9 +1556,9 @@ export interface TabWithName { * via the `definition` "uploads". */ export interface Upload { - id: number; + id: string; text?: string | null; - media?: (number | null) | Upload; + media?: (string | null) | Upload; updatedAt: string; createdAt: string; url?: string | null; @@ -1576,9 +1576,9 @@ export interface Upload { * via the `definition` "uploads2". */ export interface Uploads2 { - id: number; + id: string; text?: string | null; - media?: (number | null) | Uploads2; + media?: (string | null) | Uploads2; updatedAt: string; createdAt: string; url?: string | null; @@ -1596,8 +1596,8 @@ export interface Uploads2 { * via the `definition` "uploads3". */ export interface Uploads3 { - id: number; - media?: (number | null) | Uploads3; + id: string; + media?: (string | null) | Uploads3; updatedAt: string; createdAt: string; url?: string | null; @@ -1615,9 +1615,9 @@ export interface Uploads3 { * via the `definition` "uploads-multi". */ export interface UploadsMulti { - id: number; + id: string; text?: string | null; - media?: (number | Upload)[] | null; + media?: (string | Upload)[] | null; updatedAt: string; createdAt: string; } @@ -1626,16 +1626,16 @@ export interface UploadsMulti { * via the `definition` "uploads-poly". */ export interface UploadsPoly { - id: number; + id: string; text?: string | null; media?: | ({ relationTo: 'uploads'; - value: number | Upload; + value: string | Upload; } | null) | ({ relationTo: 'uploads2'; - value: number | Uploads2; + value: string | Uploads2; } | null); updatedAt: string; createdAt: string; @@ -1645,17 +1645,17 @@ export interface UploadsPoly { * via the `definition` "uploads-multi-poly". */ export interface UploadsMultiPoly { - id: number; + id: string; text?: string | null; media?: | ( | { relationTo: 'uploads'; - value: number | Upload; + value: string | Upload; } | { relationTo: 'uploads2'; - value: number | Uploads2; + value: string | Uploads2; } )[] | null; @@ -1667,11 +1667,11 @@ export interface UploadsMultiPoly { * via the `definition` "uploads-restricted". */ export interface UploadsRestricted { - id: number; + id: string; text?: string | null; - uploadWithoutRestriction?: (number | null) | Upload; - uploadWithAllowCreateFalse?: (number | null) | Upload; - uploadMultipleWithAllowCreateFalse?: (number | Upload)[] | null; + uploadWithoutRestriction?: (string | null) | Upload; + uploadWithAllowCreateFalse?: (string | null) | Upload; + uploadMultipleWithAllowCreateFalse?: (string | Upload)[] | null; updatedAt: string; createdAt: string; } @@ -1680,7 +1680,7 @@ export interface UploadsRestricted { * via the `definition` "ui-fields". */ export interface UiField { - id: number; + id: string; text: string; updatedAt: string; createdAt: string; @@ -1690,39 +1690,39 @@ export interface UiField { * via the `definition` "payload-locked-documents". */ export interface PayloadLockedDocument { - id: number; + id: string; document?: | ({ relationTo: 'users'; - value: number | User; + value: string | User; } | null) | ({ relationTo: 'select-versions-fields'; - value: number | SelectVersionsField; + value: string | SelectVersionsField; } | null) | ({ relationTo: 'array-fields'; - value: number | ArrayField; + value: string | ArrayField; } | null) | ({ relationTo: 'block-fields'; - value: number | BlockField; + value: string | BlockField; } | null) | ({ relationTo: 'checkbox-fields'; - value: number | CheckboxField; + value: string | CheckboxField; } | null) | ({ relationTo: 'code-fields'; - value: number | CodeField; + value: string | CodeField; } | null) | ({ relationTo: 'collapsible-fields'; - value: number | CollapsibleField; + value: string | CollapsibleField; } | null) | ({ relationTo: 'conditional-logic'; - value: number | ConditionalLogic; + value: string | ConditionalLogic; } | null) | ({ relationTo: 'custom-id'; @@ -1730,27 +1730,27 @@ export interface PayloadLockedDocument { } | null) | ({ relationTo: 'custom-tab-id'; - value: number | CustomTabId; + value: string | CustomTabId; } | null) | ({ relationTo: 'custom-row-id'; - value: number | CustomRowId; + value: string | CustomRowId; } | null) | ({ relationTo: 'date-fields'; - value: number | DateField; + value: string | DateField; } | null) | ({ relationTo: 'email-fields'; - value: number | EmailField; + value: string | EmailField; } | null) | ({ relationTo: 'radio-fields'; - value: number | RadioField; + value: string | RadioField; } | null) | ({ relationTo: 'group-fields'; - value: number | GroupField; + value: string | GroupField; } | null) | ({ relationTo: 'row-fields'; @@ -1758,76 +1758,76 @@ export interface PayloadLockedDocument { } | null) | ({ relationTo: 'indexed-fields'; - value: number | IndexedField; + value: string | IndexedField; } | null) | ({ relationTo: 'json-fields'; - value: number | JsonField; + value: string | JsonField; } | null) | ({ relationTo: 'number-fields'; - value: number | NumberField; + value: string | NumberField; } | null) | ({ relationTo: 'point-fields'; - value: number | PointField; + value: string | PointField; } | null) | ({ relationTo: 'relationship-fields'; - value: number | RelationshipField; + value: string | RelationshipField; } | null) | ({ relationTo: 'select-fields'; - value: number | SelectField; + value: string | SelectField; } | null) | ({ relationTo: 'tabs-fields-2'; - value: number | TabsFields2; + value: string | TabsFields2; } | null) | ({ relationTo: 'tabs-fields'; - value: number | TabsField; + value: string | TabsField; } | null) | ({ relationTo: 'text-fields'; - value: number | TextField; + value: string | TextField; } | null) | ({ relationTo: 'uploads'; - value: number | Upload; + value: string | Upload; } | null) | ({ relationTo: 'uploads2'; - value: number | Uploads2; + value: string | Uploads2; } | null) | ({ relationTo: 'uploads3'; - value: number | Uploads3; + value: string | Uploads3; } | null) | ({ relationTo: 'uploads-multi'; - value: number | UploadsMulti; + value: string | UploadsMulti; } | null) | ({ relationTo: 'uploads-poly'; - value: number | UploadsPoly; + value: string | UploadsPoly; } | null) | ({ relationTo: 'uploads-multi-poly'; - value: number | UploadsMultiPoly; + value: string | UploadsMultiPoly; } | null) | ({ relationTo: 'uploads-restricted'; - value: number | UploadsRestricted; + value: string | UploadsRestricted; } | null) | ({ relationTo: 'ui-fields'; - value: number | UiField; + value: string | UiField; } | null); globalSlug?: string | null; user: { relationTo: 'users'; - value: number | User; + value: string | User; }; updatedAt: string; createdAt: string; @@ -1837,10 +1837,10 @@ export interface PayloadLockedDocument { * via the `definition` "payload-preferences". */ export interface PayloadPreference { - id: number; + id: string; user: { relationTo: 'users'; - value: number | User; + value: string | User; }; key?: string | null; value?: @@ -1860,7 +1860,7 @@ export interface PayloadPreference { * via the `definition` "payload-migrations". */ export interface PayloadMigration { - id: number; + id: string; name?: string | null; batch?: number | null; updatedAt: string; diff --git a/test/helpers/e2e/fields/array/addArrayRow.ts b/test/helpers/e2e/fields/array/addArrayRow.ts index ff0e12da4b8..568c18a66d8 100644 --- a/test/helpers/e2e/fields/array/addArrayRow.ts +++ b/test/helpers/e2e/fields/array/addArrayRow.ts @@ -19,12 +19,12 @@ export const addArrayRow = async ( page: Page, { fieldName }: Omit[1], 'rowIndex'>, ) => { - const rowSelector = page.locator(`#field-${fieldName} .array-field__row`) - const numberOfCurrentRows = await rowSelector.count() + const rowLocator = page.locator(`#field-${fieldName} .array-field__row`) + const numberOfCurrentRows = await rowLocator.count() await addArrayRowAsync(page, fieldName) - expect(await rowSelector.count()).toBe(numberOfCurrentRows + 1) + expect(await rowLocator.count()).toBe(numberOfCurrentRows + 1) } /** @@ -34,17 +34,17 @@ export const addArrayRowBelow = async ( page: Page, { fieldName, rowIndex = 0 }: Parameters[1], ): Promise<{ popupContentLocator: Locator; rowActionsButtonLocator: Locator }> => { + const rowLocator = page.locator(`#field-${fieldName} .array-field__row`) + const numberOfCurrentRows = await rowLocator.count() + const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { fieldName, rowIndex, }) - const rowSelector = page.locator(`#field-${fieldName} .array-field__row`) - const numberOfCurrentRows = await rowSelector.count() - await popupContentLocator.locator('.array-actions__action.array-actions__add').click() - expect(await rowSelector.count()).toBe(numberOfCurrentRows + 1) + await expect(rowLocator).toHaveCount(numberOfCurrentRows + 1) // TODO: test the array row has appeared in the _correct position_ (immediately below the original row) await wait(300) diff --git a/test/helpers/e2e/fields/array/duplicateArrayRow.ts b/test/helpers/e2e/fields/array/duplicateArrayRow.ts index 37021f61945..d8d02869fa9 100644 --- a/test/helpers/e2e/fields/array/duplicateArrayRow.ts +++ b/test/helpers/e2e/fields/array/duplicateArrayRow.ts @@ -14,17 +14,17 @@ export const duplicateArrayRow = async ( popupContentLocator: Locator rowActionsButtonLocator: Locator }> => { + const rowLocator = page.locator(`#field-${fieldName} .array-field__row`) + const numberOfCurrentRows = await rowLocator.count() + const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { fieldName, rowIndex, }) - const rowSelector = page.locator(`#field-${fieldName} .array-field__row`) - const numberOfCurrentRows = await rowSelector.count() - await popupContentLocator.locator('.array-actions__action.array-actions__duplicate').click() - expect(await rowSelector.count()).toBe(numberOfCurrentRows + 1) + expect(await rowLocator.count()).toBe(numberOfCurrentRows + 1) // TODO: test the array row's field input values have been duplicated as well diff --git a/test/helpers/e2e/fields/array/removeArrayRow.ts b/test/helpers/e2e/fields/array/removeArrayRow.ts index 957b197627b..9347ebacee0 100644 --- a/test/helpers/e2e/fields/array/removeArrayRow.ts +++ b/test/helpers/e2e/fields/array/removeArrayRow.ts @@ -14,17 +14,17 @@ export const removeArrayRow = async ( popupContentLocator: Locator rowActionsButtonLocator: Locator }> => { + const rowLocator = page.locator(`#field-${fieldName} .array-field__row`) + const numberOfCurrentRows = await rowLocator.count() + const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { fieldName, rowIndex, }) - const rowSelector = page.locator(`#field-${fieldName} .array-field__row`) - const numberOfCurrentRows = await rowSelector.count() - await popupContentLocator.locator('.array-actions__action.array-actions__remove').click() - expect(await rowSelector.count()).toBe(numberOfCurrentRows - 1) + expect(await rowLocator.count()).toBe(numberOfCurrentRows - 1) // TODO: test the array row has been removed in the _correct position_ (original row index) // another row may have been moved into its place, need to ensure the test accounts for this fact diff --git a/test/helpers/e2e/fields/blocks/addBlock.ts b/test/helpers/e2e/fields/blocks/addBlock.ts index b086f0868dc..86b66803b88 100644 --- a/test/helpers/e2e/fields/blocks/addBlock.ts +++ b/test/helpers/e2e/fields/blocks/addBlock.ts @@ -1,10 +1,30 @@ -import type { Page } from '@playwright/test' +import type { Locator, Page } from '@playwright/test' import { expect } from '@playwright/test' import { exactText } from 'helpers.js' +import { openArrayRowActions } from '../array/openArrayRowActions.js' import { openBlocksDrawer } from './openBlocksDrawer.js' +const selectBlock = async ({ + blocksDrawer, + blockToSelect, +}: { + blocksDrawer: Locator + blockToSelect: string +}) => { + const blockCard = blocksDrawer.locator('.blocks-drawer__block .thumbnail-card__label', { + hasText: blockToSelect, + }) + + await expect(blockCard).toBeVisible() + + await blocksDrawer.getByRole('button', { name: exactText(blockToSelect) }).click() +} + +/** + * Adds a block to the end of the blocks array using the primary "Add Block" button. + */ export const addBlock = async ({ page, fieldName = 'blocks', @@ -19,13 +39,56 @@ export const addBlock = async ({ }) => { const blocksDrawer = await openBlocksDrawer({ page, fieldName }) - const blockCard = blocksDrawer.locator('.blocks-drawer__block .thumbnail-card__label', { - hasText: blockToSelect, + await selectBlock({ + blocksDrawer, + blockToSelect, }) - await expect(blockCard).toBeVisible() + // expect to see the block on the page +} - await blocksDrawer.getByRole('button', { name: exactText(blockToSelect) }).click() +/** + * Like `addBlock`, but inserts the block at the specified index using the row actions menu. + */ +export const addBlockBelow = async ( + page: Page, + { + fieldName = 'blocks', + blockToSelect = 'Block', + rowIndex = 0, + }: { + /** + * The name of the block to select from the blocks drawer. + */ + blockToSelect: string + fieldName: string + /** + * The index at which to insert the block. + */ + rowIndex?: number + }, +) => { + const rowLocator = page.locator( + `#field-${fieldName} > .blocks-field__rows > div > .blocks-field__row`, + ) - // expect to see the block on the page + const numberOfCurrentRows = await rowLocator.count() + + const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { + fieldName, + rowIndex, + }) + + await popupContentLocator.locator('.array-actions__action.array-actions__add').click() + + const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]') + + await selectBlock({ + blocksDrawer, + blockToSelect, + }) + + await expect(rowLocator).toHaveCount(numberOfCurrentRows + 1) + + return { popupContentLocator, rowActionsButtonLocator } } diff --git a/test/helpers/e2e/fields/blocks/index.ts b/test/helpers/e2e/fields/blocks/index.ts index 5b59954d0e8..4d6faabb5f5 100644 --- a/test/helpers/e2e/fields/blocks/index.ts +++ b/test/helpers/e2e/fields/blocks/index.ts @@ -1,4 +1,4 @@ -export { addBlock } from './addBlock.js' +export { addBlock, addBlockBelow } from './addBlock.js' export { openBlocksDrawer } from './openBlocksDrawer.js' export { removeAllBlocks } from './removeAllBlocks.js' export { reorderBlocks } from './reorderBlocks.js' From f761f11c30ae136b545b8ec1de3137532089b7bd Mon Sep 17 00:00:00 2001 From: Jacob Fletcher Date: Wed, 3 Sep 2025 13:23:25 -0400 Subject: [PATCH 7/7] fix block test, adds duplicateBlock helper --- test/_community/collections/Posts/index.ts | 15 ++++---- test/_community/payload-types.ts | 28 ++++++-------- test/fields/collections/Blocks/e2e.spec.ts | 11 +++--- test/helpers/e2e/fields/array/addArrayRow.ts | 8 ++-- .../e2e/fields/array/duplicateArrayRow.ts | 4 +- .../e2e/fields/array/removeArrayRow.ts | 4 +- test/helpers/e2e/fields/blocks/addBlock.ts | 18 ++++++--- .../e2e/fields/blocks/duplicateBlock.ts | 37 +++++++++++++++++++ test/helpers/e2e/fields/blocks/index.ts | 1 + 9 files changed, 84 insertions(+), 42 deletions(-) create mode 100644 test/helpers/e2e/fields/blocks/duplicateBlock.ts diff --git a/test/_community/collections/Posts/index.ts b/test/_community/collections/Posts/index.ts index 86a307b39c3..e49e89a924e 100644 --- a/test/_community/collections/Posts/index.ts +++ b/test/_community/collections/Posts/index.ts @@ -1,7 +1,5 @@ import type { CollectionConfig } from 'payload' -import { lexicalEditor } from '@payloadcms/richtext-lexical' - export const postsSlug = 'posts' export const PostsCollection: CollectionConfig = { @@ -15,11 +13,14 @@ export const PostsCollection: CollectionConfig = { type: 'text', }, { - name: 'content', - type: 'richText', - editor: lexicalEditor({ - features: ({ defaultFeatures }) => [...defaultFeatures], - }), + name: 'array', + type: 'array', + fields: [ + { + name: 'title', + type: 'text', + }, + ], }, ], } diff --git a/test/_community/payload-types.ts b/test/_community/payload-types.ts index 599c9dec1d7..ecebd076d12 100644 --- a/test/_community/payload-types.ts +++ b/test/_community/payload-types.ts @@ -126,21 +126,12 @@ export interface UserAuthOperations { export interface Post { id: string; title?: string | null; - content?: { - root: { - type: string; - children: { - type: string; - version: number; - [k: string]: unknown; - }[]; - direction: ('ltr' | 'rtl') | null; - format: 'left' | 'start' | 'center' | 'right' | 'end' | 'justify' | ''; - indent: number; - version: number; - }; - [k: string]: unknown; - } | null; + array?: + | { + title?: string | null; + id?: string | null; + }[] + | null; updatedAt: string; createdAt: string; } @@ -279,7 +270,12 @@ export interface PayloadMigration { */ export interface PostsSelect { title?: T; - content?: T; + array?: + | T + | { + title?: T; + id?: T; + }; updatedAt?: T; createdAt?: T; } diff --git a/test/fields/collections/Blocks/e2e.spec.ts b/test/fields/collections/Blocks/e2e.spec.ts index 2b5a40f152c..ed3e0405042 100644 --- a/test/fields/collections/Blocks/e2e.spec.ts +++ b/test/fields/collections/Blocks/e2e.spec.ts @@ -6,6 +6,7 @@ import { duplicateArrayRow } from 'helpers/e2e/fields/array/index.js' import { addBlock, addBlockBelow, + duplicateBlock, openBlocksDrawer, reorderBlocks, } from 'helpers/e2e/fields/blocks/index.js' @@ -147,19 +148,17 @@ describe('Block fields', () => { test('should duplicate block', async () => { await page.goto(url.create) - await duplicateArrayRow(page, { fieldName: 'blocks' }) + const { rowCount } = await duplicateBlock(page, { fieldName: 'blocks' }) - const blocks = page.locator('#field-blocks > .blocks-field__rows > div') - expect(await blocks.count()).toEqual(5) + expect(rowCount).toEqual(5) }) test('should save when duplicating subblocks', async () => { await page.goto(url.create) - await duplicateArrayRow(page, { fieldName: 'blocks', rowIndex: 2 }) + const { rowCount } = await duplicateBlock(page, { fieldName: 'blocks', rowIndex: 2 }) - const blocks = page.locator('#field-blocks > .blocks-field__rows > div') - expect(await blocks.count()).toEqual(5) + expect(rowCount).toEqual(5) await page.click('#action-save') await expect(page.locator('.payload-toast-container')).toContainText('successfully') diff --git a/test/helpers/e2e/fields/array/addArrayRow.ts b/test/helpers/e2e/fields/array/addArrayRow.ts index 568c18a66d8..5fc740492ad 100644 --- a/test/helpers/e2e/fields/array/addArrayRow.ts +++ b/test/helpers/e2e/fields/array/addArrayRow.ts @@ -20,11 +20,11 @@ export const addArrayRow = async ( { fieldName }: Omit[1], 'rowIndex'>, ) => { const rowLocator = page.locator(`#field-${fieldName} .array-field__row`) - const numberOfCurrentRows = await rowLocator.count() + const numberOfPrevRows = await rowLocator.count() await addArrayRowAsync(page, fieldName) - expect(await rowLocator.count()).toBe(numberOfCurrentRows + 1) + expect(await rowLocator.count()).toBe(numberOfPrevRows + 1) } /** @@ -35,7 +35,7 @@ export const addArrayRowBelow = async ( { fieldName, rowIndex = 0 }: Parameters[1], ): Promise<{ popupContentLocator: Locator; rowActionsButtonLocator: Locator }> => { const rowLocator = page.locator(`#field-${fieldName} .array-field__row`) - const numberOfCurrentRows = await rowLocator.count() + const numberOfPrevRows = await rowLocator.count() const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { fieldName, @@ -44,7 +44,7 @@ export const addArrayRowBelow = async ( await popupContentLocator.locator('.array-actions__action.array-actions__add').click() - await expect(rowLocator).toHaveCount(numberOfCurrentRows + 1) + await expect(rowLocator).toHaveCount(numberOfPrevRows + 1) // TODO: test the array row has appeared in the _correct position_ (immediately below the original row) await wait(300) diff --git a/test/helpers/e2e/fields/array/duplicateArrayRow.ts b/test/helpers/e2e/fields/array/duplicateArrayRow.ts index d8d02869fa9..0f7d87a14a0 100644 --- a/test/helpers/e2e/fields/array/duplicateArrayRow.ts +++ b/test/helpers/e2e/fields/array/duplicateArrayRow.ts @@ -15,7 +15,7 @@ export const duplicateArrayRow = async ( rowActionsButtonLocator: Locator }> => { const rowLocator = page.locator(`#field-${fieldName} .array-field__row`) - const numberOfCurrentRows = await rowLocator.count() + const numberOfPrevRows = await rowLocator.count() const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { fieldName, @@ -24,7 +24,7 @@ export const duplicateArrayRow = async ( await popupContentLocator.locator('.array-actions__action.array-actions__duplicate').click() - expect(await rowLocator.count()).toBe(numberOfCurrentRows + 1) + expect(await rowLocator.count()).toBe(numberOfPrevRows + 1) // TODO: test the array row's field input values have been duplicated as well diff --git a/test/helpers/e2e/fields/array/removeArrayRow.ts b/test/helpers/e2e/fields/array/removeArrayRow.ts index 9347ebacee0..dfc028de585 100644 --- a/test/helpers/e2e/fields/array/removeArrayRow.ts +++ b/test/helpers/e2e/fields/array/removeArrayRow.ts @@ -15,7 +15,7 @@ export const removeArrayRow = async ( rowActionsButtonLocator: Locator }> => { const rowLocator = page.locator(`#field-${fieldName} .array-field__row`) - const numberOfCurrentRows = await rowLocator.count() + const numberOfPrevRows = await rowLocator.count() const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { fieldName, @@ -24,7 +24,7 @@ export const removeArrayRow = async ( await popupContentLocator.locator('.array-actions__action.array-actions__remove').click() - expect(await rowLocator.count()).toBe(numberOfCurrentRows - 1) + expect(await rowLocator.count()).toBe(numberOfPrevRows - 1) // TODO: test the array row has been removed in the _correct position_ (original row index) // another row may have been moved into its place, need to ensure the test accounts for this fact diff --git a/test/helpers/e2e/fields/blocks/addBlock.ts b/test/helpers/e2e/fields/blocks/addBlock.ts index 86b66803b88..e6c76a23fe4 100644 --- a/test/helpers/e2e/fields/blocks/addBlock.ts +++ b/test/helpers/e2e/fields/blocks/addBlock.ts @@ -6,7 +6,7 @@ import { exactText } from 'helpers.js' import { openArrayRowActions } from '../array/openArrayRowActions.js' import { openBlocksDrawer } from './openBlocksDrawer.js' -const selectBlock = async ({ +const selectBlockFromDrawer = async ({ blocksDrawer, blockToSelect, }: { @@ -37,13 +37,21 @@ export const addBlock = async ({ fieldName: string page: Page }) => { + const rowLocator = page.locator( + `#field-${fieldName} > .blocks-field__rows > div > .blocks-field__row`, + ) + + const numberOfPrevRows = await rowLocator.count() + const blocksDrawer = await openBlocksDrawer({ page, fieldName }) - await selectBlock({ + await selectBlockFromDrawer({ blocksDrawer, blockToSelect, }) + await expect(rowLocator).toHaveCount(numberOfPrevRows + 1) + // expect to see the block on the page } @@ -72,7 +80,7 @@ export const addBlockBelow = async ( `#field-${fieldName} > .blocks-field__rows > div > .blocks-field__row`, ) - const numberOfCurrentRows = await rowLocator.count() + const numberOfPrevRows = await rowLocator.count() const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { fieldName, @@ -83,12 +91,12 @@ export const addBlockBelow = async ( const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]') - await selectBlock({ + await selectBlockFromDrawer({ blocksDrawer, blockToSelect, }) - await expect(rowLocator).toHaveCount(numberOfCurrentRows + 1) + await expect(rowLocator).toHaveCount(numberOfPrevRows + 1) return { popupContentLocator, rowActionsButtonLocator } } diff --git a/test/helpers/e2e/fields/blocks/duplicateBlock.ts b/test/helpers/e2e/fields/blocks/duplicateBlock.ts new file mode 100644 index 00000000000..db2c9a22508 --- /dev/null +++ b/test/helpers/e2e/fields/blocks/duplicateBlock.ts @@ -0,0 +1,37 @@ +import type { Locator, Page } from 'playwright' + +import { expect } from 'playwright/test' + +import { openArrayRowActions } from '../array/openArrayRowActions.js' + +/** + * Duplicates the block row at the specified index. + */ +export const duplicateBlock = async ( + page: Page, + { fieldName, rowIndex = 0 }: Parameters[1], +): Promise<{ + popupContentLocator: Locator + rowActionsButtonLocator: Locator + rowCount: number +}> => { + const rowLocator = page.locator( + `#field-${fieldName} > .blocks-field__rows > div > .blocks-field__row`, + ) + + const numberOfPrevRows = await rowLocator.count() + + const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, { + fieldName, + rowIndex, + }) + + await popupContentLocator.locator('.array-actions__action.array-actions__duplicate').click() + const numberOfCurrentRows = await rowLocator.count() + + expect(numberOfCurrentRows).toBe(numberOfPrevRows + 1) + + // TODO: test the array row's field input values have been duplicated as well + + return { popupContentLocator, rowActionsButtonLocator, rowCount: numberOfCurrentRows } +} diff --git a/test/helpers/e2e/fields/blocks/index.ts b/test/helpers/e2e/fields/blocks/index.ts index 4d6faabb5f5..88792f34b6a 100644 --- a/test/helpers/e2e/fields/blocks/index.ts +++ b/test/helpers/e2e/fields/blocks/index.ts @@ -1,4 +1,5 @@ export { addBlock, addBlockBelow } from './addBlock.js' +export { duplicateBlock } from './duplicateBlock.js' export { openBlocksDrawer } from './openBlocksDrawer.js' export { removeAllBlocks } from './removeAllBlocks.js' export { reorderBlocks } from './reorderBlocks.js'