Skip to content

Commit 8166784

Browse files
authored
test: blocks field helpers (#11259)
Similar to the goals of #11026. Adds helper utilities to make interacting with the blocks field easier within e2e tests. This will also standardize common functionality across tests and reduce the overall lines of code for each, making them easier to navigate and digest. The following helpers are now available: - `openBlocksDrawer`: self-explanatory - `addBlock`: opens the blocks drawer and selects the given block - `reorderBlocks`: similar to `reorderColumn`, moves blocks using the drag handle - `removeAllBlocks`: iterates all rows of a given blocks field and removes them
1 parent 6d36a28 commit 8166784

File tree

6 files changed

+163
-66
lines changed

6 files changed

+163
-66
lines changed

test/fields/collections/Blocks/e2e.spec.ts

Lines changed: 51 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import type { Page } from '@playwright/test'
1+
import type { BrowserContext, Page } from '@playwright/test'
22

33
import { expect, test } from '@playwright/test'
4+
import { addBlock } from 'helpers/e2e/addBlock.js'
5+
import { openBlocksDrawer } from 'helpers/e2e/openBlocksDrawer.js'
46
import path from 'path'
57
import { fileURLToPath } from 'url'
68

@@ -24,6 +26,8 @@ const { beforeAll, beforeEach, describe } = test
2426
let client: RESTClient
2527
let page: Page
2628
let serverURL: string
29+
let context: BrowserContext
30+
2731
// If we want to make this run in parallel: test.describe.configure({ mode: 'parallel' })
2832

2933
describe('Block fields', () => {
@@ -35,12 +39,13 @@ describe('Block fields', () => {
3539
dirname,
3640
}))
3741

38-
const context = await browser.newContext()
42+
context = await browser.newContext()
3943
page = await context.newPage()
4044
initPageConsoleErrorCatch(page)
4145

4246
await ensureCompilationIsDone({ page, serverURL })
4347
})
48+
4449
beforeEach(async () => {
4550
await reInitializeDB({
4651
serverURL,
@@ -58,25 +63,19 @@ describe('Block fields', () => {
5863
})
5964

6065
let url: AdminUrlUtil
66+
6167
beforeAll(() => {
6268
url = new AdminUrlUtil(serverURL, 'block-fields')
6369
})
6470

6571
test('should open blocks drawer and select first block', async () => {
6672
await page.goto(url.create)
67-
const addButton = page.locator('#field-blocks > .blocks-field__drawer-toggler')
68-
await expect(addButton).toContainText('Add Block')
69-
await addButton.click()
70-
71-
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
72-
await expect(blocksDrawer).toBeVisible()
7373

74-
// select the first block in the drawer
75-
const firstBlockSelector = blocksDrawer
76-
.locator('.blocks-drawer__blocks .blocks-drawer__block')
77-
.first()
78-
await expect(firstBlockSelector).toContainText('Content')
79-
await firstBlockSelector.click()
74+
await addBlock({
75+
page,
76+
fieldName: 'blocks',
77+
blockLabel: 'Content',
78+
})
8079

8180
// ensure the block was appended to the rows
8281
const addedRow = page.locator('#field-blocks .blocks-field__row').last()
@@ -88,24 +87,25 @@ describe('Block fields', () => {
8887

8988
test('should reset search state in blocks drawer on re-open', async () => {
9089
await page.goto(url.create)
91-
const addButton = page.locator('#field-blocks > .blocks-field__drawer-toggler')
92-
await expect(addButton).toContainText('Add Block')
93-
await addButton.click()
9490

95-
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
96-
await expect(blocksDrawer).toBeVisible()
91+
const blocksDrawer = await openBlocksDrawer({
92+
page,
93+
fieldName: 'blocks',
94+
})
9795

9896
const searchInput = page.locator('.block-search__input')
9997
await searchInput.fill('Number')
10098

10199
// select the first block in the drawer
100+
102101
const firstBlockSelector = blocksDrawer
103102
.locator('.blocks-drawer__blocks .blocks-drawer__block')
104103
.first()
105104

106105
await expect(firstBlockSelector).toContainText('Number')
107106

108107
await page.locator('.drawer__header__close').click()
108+
const addButton = page.locator('#field-blocks > .blocks-field__drawer-toggler')
109109
await addButton.click()
110110

111111
await expect(blocksDrawer).toBeVisible()
@@ -131,6 +131,7 @@ describe('Block fields', () => {
131131
const firstBlockSelector = blocksDrawer
132132
.locator('.blocks-drawer__blocks .blocks-drawer__block')
133133
.first()
134+
134135
await expect(firstBlockSelector).toContainText('Content')
135136
await firstBlockSelector.click()
136137

@@ -179,19 +180,11 @@ describe('Block fields', () => {
179180
await page.goto(url.create)
180181
await expect(page.locator('#field-i18nBlocks .blocks-field__header')).toContainText('Block en')
181182

182-
const addButton = page.locator('#field-i18nBlocks > .blocks-field__drawer-toggler')
183-
await expect(addButton).toContainText('Add Block en')
184-
await addButton.click()
185-
186-
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
187-
await expect(blocksDrawer).toBeVisible()
188-
189-
// select the first block in the drawer
190-
const firstBlockSelector = blocksDrawer
191-
.locator('.blocks-drawer__blocks .blocks-drawer__block')
192-
.first()
193-
await expect(firstBlockSelector).toContainText('Text en')
194-
await firstBlockSelector.click()
183+
await addBlock({
184+
page,
185+
fieldName: 'i18nBlocks',
186+
blockLabel: 'Text en',
187+
})
195188

196189
// ensure the block was appended to the rows
197190
const firstRow = page.locator('#field-i18nBlocks .blocks-field__row').first()
@@ -203,15 +196,12 @@ describe('Block fields', () => {
203196

204197
test('should render custom block row label', async () => {
205198
await page.goto(url.create)
206-
const addButton = page.locator('#field-blocks > .blocks-field__drawer-toggler')
207-
await addButton.click()
208-
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
209199

210-
await blocksDrawer
211-
.locator('.blocks-drawer__block .thumbnail-card__label', {
212-
hasText: 'Content',
213-
})
214-
.click()
200+
await addBlock({
201+
page,
202+
fieldName: 'blocks',
203+
blockLabel: 'Content',
204+
})
215205

216206
await expect(
217207
page.locator('#field-blocks .blocks-field__row .blocks-field__block-header', {
@@ -223,20 +213,17 @@ describe('Block fields', () => {
223213
test('should add different blocks with similar field configs', async () => {
224214
await page.goto(url.create)
225215

226-
async function addBlock(name: 'Block A' | 'Block B') {
227-
await page
228-
.locator('#field-blocksWithSimilarConfigs')
229-
.getByRole('button', { name: 'Add Blocks With Similar Config' })
230-
.click()
231-
await page.getByRole('button', { name }).click()
232-
}
233-
234-
await addBlock('Block A')
216+
await addBlock({
217+
page,
218+
fieldName: 'blocksWithSimilarConfigs',
219+
blockLabel: 'Block A',
220+
})
235221

236222
await page
237223
.locator('#blocksWithSimilarConfigs-row-0')
238224
.getByRole('button', { name: 'Add Item' })
239225
.click()
226+
240227
await page
241228
.locator('input[name="blocksWithSimilarConfigs.0.items.0.title"]')
242229
.fill('items>0>title')
@@ -245,12 +232,17 @@ describe('Block fields', () => {
245232
page.locator('input[name="blocksWithSimilarConfigs.0.items.0.title"]'),
246233
).toHaveValue('items>0>title')
247234

248-
await addBlock('Block B')
235+
await addBlock({
236+
page,
237+
fieldName: 'blocksWithSimilarConfigs',
238+
blockLabel: 'Block B',
239+
})
249240

250241
await page
251242
.locator('#blocksWithSimilarConfigs-row-1')
252243
.getByRole('button', { name: 'Add Item' })
253244
.click()
245+
254246
await page
255247
.locator('input[name="blocksWithSimilarConfigs.1.items.0.title2"]')
256248
.fill('items>1>title')
@@ -269,19 +261,11 @@ describe('Block fields', () => {
269261
test('should fail min rows validation when rows are present', async () => {
270262
await page.goto(url.create)
271263

272-
await page
273-
.locator('#field-blocksWithMinRows')
274-
.getByRole('button', { name: 'Add Blocks With Min Row' })
275-
.click()
276-
277-
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
278-
await expect(blocksDrawer).toBeVisible()
279-
280-
const firstBlockSelector = blocksDrawer
281-
.locator('.blocks-drawer__blocks .blocks-drawer__block')
282-
.first()
283-
284-
await firstBlockSelector.click()
264+
await addBlock({
265+
page,
266+
fieldName: 'blocksWithMinRows',
267+
blockLabel: 'Block With Min Row',
268+
})
285269

286270
const firstRow = page.locator('input[name="blocksWithMinRows.0.blockTitle"]')
287271
await expect(firstRow).toBeVisible()
@@ -328,6 +312,7 @@ describe('Block fields', () => {
328312
.locator('.custom-blocks-field-management')
329313
.getByRole('button', { name: 'Add Block 2' })
330314
.click()
315+
331316
await expect(
332317
page.locator('#field-customBlocks input[name="customBlocks.1.block2Title"]'),
333318
).toHaveValue('Block 2: Prefilled Title')
@@ -336,6 +321,7 @@ describe('Block fields', () => {
336321
.locator('.custom-blocks-field-management')
337322
.getByRole('button', { name: 'Replace Block 2' })
338323
.click()
324+
339325
await expect(
340326
page.locator('#field-customBlocks input[name="customBlocks.1.block1Title"]'),
341327
).toHaveValue('REPLACED BLOCK')
@@ -344,13 +330,13 @@ describe('Block fields', () => {
344330
})
345331

346332
describe('sortable blocks', () => {
347-
test('should have disabled admin sorting', async () => {
333+
test('should not render sort controls when sorting is disabled', async () => {
348334
await page.goto(url.create)
349335
const field = page.locator('#field-disableSort > div > div > .array-actions__action-chevron')
350336
expect(await field.count()).toEqual(0)
351337
})
352338

353-
test('the drag handle should be hidden', async () => {
339+
test('should not render drag handle when sorting is disabled', async () => {
354340
await page.goto(url.create)
355341
const field = page.locator(
356342
'#field-disableSort > .blocks-field__rows > div > div > .collapsible__drag',

test/helpers/e2e/addBlock.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import type { Page } from '@playwright/test'
2+
3+
import { expect } from '@playwright/test'
4+
import { exactText } from 'helpers.js'
5+
6+
import { openBlocksDrawer } from './openBlocksDrawer.js'
7+
8+
export const addBlock = async ({
9+
page,
10+
fieldName = 'blocks',
11+
blockLabel = 'Block',
12+
}: {
13+
blockLabel: string
14+
fieldName: string
15+
page: Page
16+
}) => {
17+
const blocksDrawer = await openBlocksDrawer({ page, fieldName })
18+
19+
const blockCard = blocksDrawer.locator('.blocks-drawer__block .thumbnail-card__label', {
20+
hasText: blockLabel,
21+
})
22+
23+
await expect(blockCard).toBeVisible()
24+
25+
await blocksDrawer.getByRole('button', { name: exactText(blockLabel) }).click()
26+
27+
// expect to see the block on the page
28+
}

test/helpers/e2e/openBlocksDrawer.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import type { Locator, Page } from '@playwright/test'
2+
3+
import { expect } from '@playwright/test'
4+
5+
export const openBlocksDrawer = async ({
6+
page,
7+
fieldName = 'blocks',
8+
}: {
9+
fieldName: string
10+
page: Page
11+
}): Promise<Locator> => {
12+
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
13+
14+
if (!(await blocksDrawer.isVisible())) {
15+
const addButton = page.locator(`#field-${fieldName} > .blocks-field__drawer-toggler`)
16+
await addButton.click()
17+
}
18+
19+
await expect(blocksDrawer).toBeVisible()
20+
21+
return blocksDrawer
22+
}

test/helpers/e2e/removeAllBlocks.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import type { Page } from '@playwright/test'
2+
3+
import { expect } from '@playwright/test'
4+
5+
export const removeAllBlocks = async ({
6+
page,
7+
fieldName = 'blocks',
8+
}: {
9+
fieldName: string
10+
page: Page
11+
}) => {
12+
const blocksField = page.locator(`#field-${fieldName}`)
13+
14+
const blocks = blocksField.locator(`[id^="${fieldName}-row-"]`)
15+
const count = await blocks.count()
16+
17+
expect(count).toBeGreaterThan(0)
18+
19+
for (let i = 0; i < count; i++) {
20+
// delete in reverse order to avoid index issues
21+
const block = blocksField.locator(`[id^="${fieldName}-row-${count - i - 1}"]`)
22+
await block.locator('.array-actions__button').first().click()
23+
await block.locator('.array-actions__action.array-actions__remove').first().click()
24+
}
25+
}

test/helpers/e2e/reorderBlocks.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import type { Page } from '@playwright/test'
2+
3+
import { wait } from 'payload/shared'
4+
5+
export const reorderBlocks = async ({
6+
page,
7+
fromBlockIndex = 1,
8+
toBlockIndex = 2,
9+
fieldName = 'blocks',
10+
}: {
11+
fieldName?: string
12+
fromBlockIndex: number
13+
page: Page
14+
toBlockIndex: number
15+
}) => {
16+
const blocksField = page.locator(`#field-${fieldName}`).first()
17+
18+
const fromField = blocksField.locator(`[id^="${fieldName}-row-${fromBlockIndex}"]`)
19+
20+
const fromBoundingBox = await fromField.locator(`.collapsible__drag`).boundingBox()
21+
22+
const toField = blocksField.locator(`[id^="${fieldName}-row-${toBlockIndex}"]`)
23+
24+
const toBoundingBox = await toField.locator(`.collapsible__drag`).boundingBox()
25+
26+
if (!fromBoundingBox || !toBoundingBox) {
27+
return
28+
}
29+
30+
// drag the "from" column to the left of the "to" column
31+
await page.mouse.move(fromBoundingBox.x + 2, fromBoundingBox.y + 2, { steps: 10 })
32+
await page.mouse.down()
33+
await wait(300)
34+
await page.mouse.move(toBoundingBox.x - 2, toBoundingBox.y - 2, { steps: 10 })
35+
await page.mouse.up()
36+
}

tsconfig.base.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
}
3232
],
3333
"paths": {
34-
"@payload-config": ["./test/_community/config.ts"],
34+
"@payload-config": ["./test/fields/config.ts"],
3535
"@payloadcms/live-preview": ["./packages/live-preview/src"],
3636
"@payloadcms/live-preview-react": ["./packages/live-preview-react/src/index.ts"],
3737
"@payloadcms/live-preview-vue": ["./packages/live-preview-vue/src/index.ts"],

0 commit comments

Comments
 (0)