;
};
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/form-state/collections/Posts/ArrayRowLabel.tsx b/test/form-state/collections/Posts/ArrayRowLabel.tsx
index 22a5c9813..38f6b4b89 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 27fb39609..cc9bfafbb 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 e47e51367..5fc740492 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'
@@ -18,10 +19,12 @@ export const addArrayRow = async (
page: Page,
{ fieldName }: Omit[1], 'rowIndex'>,
) => {
+ const rowLocator = page.locator(`#field-${fieldName} .array-field__row`)
+ const numberOfPrevRows = await rowLocator.count()
+
await addArrayRowAsync(page, fieldName)
- // TODO: test the array row has appeared
- await wait(300)
+ expect(await rowLocator.count()).toBe(numberOfPrevRows + 1)
}
/**
@@ -31,16 +34,19 @@ 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 numberOfPrevRows = await rowLocator.count()
+
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
fieldName,
rowIndex,
})
- const addBelowButton = popupContentLocator.locator('.array-actions__action.array-actions__add')
+ await popupContentLocator.locator('.array-actions__action.array-actions__add').click()
- await addBelowButton.click()
+ await expect(rowLocator).toHaveCount(numberOfPrevRows + 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 4019a13a3..0f7d87a14 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'
/**
@@ -12,6 +14,9 @@ export const duplicateArrayRow = async (
popupContentLocator: Locator
rowActionsButtonLocator: Locator
}> => {
+ const rowLocator = page.locator(`#field-${fieldName} .array-field__row`)
+ const numberOfPrevRows = await rowLocator.count()
+
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
fieldName,
rowIndex,
@@ -19,7 +24,9 @@ export const duplicateArrayRow = async (
await popupContentLocator.locator('.array-actions__action.array-actions__duplicate').click()
- // TODO: test the array row has been duplicated
+ expect(await rowLocator.count()).toBe(numberOfPrevRows + 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 0d69b4cb8..dfc028de5 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'
/**
@@ -12,6 +14,9 @@ export const removeArrayRow = async (
popupContentLocator: Locator
rowActionsButtonLocator: Locator
}> => {
+ const rowLocator = page.locator(`#field-${fieldName} .array-field__row`)
+ const numberOfPrevRows = await rowLocator.count()
+
const { popupContentLocator, rowActionsButtonLocator } = await openArrayRowActions(page, {
fieldName,
rowIndex,
@@ -19,8 +24,10 @@ export const removeArrayRow = async (
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 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
return { popupContentLocator, rowActionsButtonLocator }
}
diff --git a/test/helpers/e2e/fields/blocks/addBlock.ts b/test/helpers/e2e/fields/blocks/addBlock.ts
index b086f0868..e6c76a23f 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 selectBlockFromDrawer = 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',
@@ -17,15 +37,66 @@ 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 })
- const blockCard = blocksDrawer.locator('.blocks-drawer__block .thumbnail-card__label', {
- hasText: blockToSelect,
+ await selectBlockFromDrawer({
+ blocksDrawer,
+ blockToSelect,
})
- await expect(blockCard).toBeVisible()
-
- await blocksDrawer.getByRole('button', { name: exactText(blockToSelect) }).click()
+ await expect(rowLocator).toHaveCount(numberOfPrevRows + 1)
// expect to see the block on the page
}
+
+/**
+ * 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`,
+ )
+
+ const numberOfPrevRows = 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 selectBlockFromDrawer({
+ blocksDrawer,
+ blockToSelect,
+ })
+
+ 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 000000000..db2c9a225
--- /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 5b59954d0..88792f34b 100644
--- a/test/helpers/e2e/fields/blocks/index.ts
+++ b/test/helpers/e2e/fields/blocks/index.ts
@@ -1,4 +1,5 @@
-export { addBlock } from './addBlock.js'
+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'