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
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
import type { BrowserContext, Page } from '@playwright/test'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
import { addBlock } from 'helpers/e2e/addBlock.js'
|
||||
import { openBlocksDrawer } from 'helpers/e2e/openBlocksDrawer.js'
|
||||
import path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
|
||||
@@ -24,6 +26,8 @@ const { beforeAll, beforeEach, describe } = test
|
||||
let client: RESTClient
|
||||
let page: Page
|
||||
let serverURL: string
|
||||
let context: BrowserContext
|
||||
|
||||
// If we want to make this run in parallel: test.describe.configure({ mode: 'parallel' })
|
||||
|
||||
describe('Block fields', () => {
|
||||
@@ -35,12 +39,13 @@ describe('Block fields', () => {
|
||||
dirname,
|
||||
}))
|
||||
|
||||
const context = await browser.newContext()
|
||||
context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
initPageConsoleErrorCatch(page)
|
||||
|
||||
await ensureCompilationIsDone({ page, serverURL })
|
||||
})
|
||||
|
||||
beforeEach(async () => {
|
||||
await reInitializeDB({
|
||||
serverURL,
|
||||
@@ -58,25 +63,19 @@ describe('Block fields', () => {
|
||||
})
|
||||
|
||||
let url: AdminUrlUtil
|
||||
|
||||
beforeAll(() => {
|
||||
url = new AdminUrlUtil(serverURL, 'block-fields')
|
||||
})
|
||||
|
||||
test('should open blocks drawer and select first block', async () => {
|
||||
await page.goto(url.create)
|
||||
const addButton = page.locator('#field-blocks > .blocks-field__drawer-toggler')
|
||||
await expect(addButton).toContainText('Add Block')
|
||||
await addButton.click()
|
||||
|
||||
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 addBlock({
|
||||
page,
|
||||
fieldName: 'blocks',
|
||||
blockLabel: 'Content',
|
||||
})
|
||||
|
||||
// ensure the block was appended to the rows
|
||||
const addedRow = page.locator('#field-blocks .blocks-field__row').last()
|
||||
@@ -88,17 +87,17 @@ describe('Block fields', () => {
|
||||
|
||||
test('should reset search state in blocks drawer on re-open', async () => {
|
||||
await page.goto(url.create)
|
||||
const addButton = page.locator('#field-blocks > .blocks-field__drawer-toggler')
|
||||
await expect(addButton).toContainText('Add Block')
|
||||
await addButton.click()
|
||||
|
||||
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
|
||||
await expect(blocksDrawer).toBeVisible()
|
||||
const blocksDrawer = await openBlocksDrawer({
|
||||
page,
|
||||
fieldName: 'blocks',
|
||||
})
|
||||
|
||||
const searchInput = page.locator('.block-search__input')
|
||||
await searchInput.fill('Number')
|
||||
|
||||
// select the first block in the drawer
|
||||
|
||||
const firstBlockSelector = blocksDrawer
|
||||
.locator('.blocks-drawer__blocks .blocks-drawer__block')
|
||||
.first()
|
||||
@@ -106,6 +105,7 @@ describe('Block fields', () => {
|
||||
await expect(firstBlockSelector).toContainText('Number')
|
||||
|
||||
await page.locator('.drawer__header__close').click()
|
||||
const addButton = page.locator('#field-blocks > .blocks-field__drawer-toggler')
|
||||
await addButton.click()
|
||||
|
||||
await expect(blocksDrawer).toBeVisible()
|
||||
@@ -131,6 +131,7 @@ describe('Block fields', () => {
|
||||
const firstBlockSelector = blocksDrawer
|
||||
.locator('.blocks-drawer__blocks .blocks-drawer__block')
|
||||
.first()
|
||||
|
||||
await expect(firstBlockSelector).toContainText('Content')
|
||||
await firstBlockSelector.click()
|
||||
|
||||
@@ -179,19 +180,11 @@ describe('Block fields', () => {
|
||||
await page.goto(url.create)
|
||||
await expect(page.locator('#field-i18nBlocks .blocks-field__header')).toContainText('Block en')
|
||||
|
||||
const addButton = page.locator('#field-i18nBlocks > .blocks-field__drawer-toggler')
|
||||
await expect(addButton).toContainText('Add Block en')
|
||||
await addButton.click()
|
||||
|
||||
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('Text en')
|
||||
await firstBlockSelector.click()
|
||||
await addBlock({
|
||||
page,
|
||||
fieldName: 'i18nBlocks',
|
||||
blockLabel: 'Text en',
|
||||
})
|
||||
|
||||
// ensure the block was appended to the rows
|
||||
const firstRow = page.locator('#field-i18nBlocks .blocks-field__row').first()
|
||||
@@ -203,15 +196,12 @@ describe('Block fields', () => {
|
||||
|
||||
test('should render custom block row label', async () => {
|
||||
await page.goto(url.create)
|
||||
const addButton = page.locator('#field-blocks > .blocks-field__drawer-toggler')
|
||||
await addButton.click()
|
||||
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
|
||||
|
||||
await blocksDrawer
|
||||
.locator('.blocks-drawer__block .thumbnail-card__label', {
|
||||
hasText: 'Content',
|
||||
await addBlock({
|
||||
page,
|
||||
fieldName: 'blocks',
|
||||
blockLabel: 'Content',
|
||||
})
|
||||
.click()
|
||||
|
||||
await expect(
|
||||
page.locator('#field-blocks .blocks-field__row .blocks-field__block-header', {
|
||||
@@ -223,20 +213,17 @@ describe('Block fields', () => {
|
||||
test('should add different blocks with similar field configs', async () => {
|
||||
await page.goto(url.create)
|
||||
|
||||
async function addBlock(name: 'Block A' | 'Block B') {
|
||||
await page
|
||||
.locator('#field-blocksWithSimilarConfigs')
|
||||
.getByRole('button', { name: 'Add Blocks With Similar Config' })
|
||||
.click()
|
||||
await page.getByRole('button', { name }).click()
|
||||
}
|
||||
|
||||
await addBlock('Block A')
|
||||
await addBlock({
|
||||
page,
|
||||
fieldName: 'blocksWithSimilarConfigs',
|
||||
blockLabel: 'Block A',
|
||||
})
|
||||
|
||||
await page
|
||||
.locator('#blocksWithSimilarConfigs-row-0')
|
||||
.getByRole('button', { name: 'Add Item' })
|
||||
.click()
|
||||
|
||||
await page
|
||||
.locator('input[name="blocksWithSimilarConfigs.0.items.0.title"]')
|
||||
.fill('items>0>title')
|
||||
@@ -245,12 +232,17 @@ describe('Block fields', () => {
|
||||
page.locator('input[name="blocksWithSimilarConfigs.0.items.0.title"]'),
|
||||
).toHaveValue('items>0>title')
|
||||
|
||||
await addBlock('Block B')
|
||||
await addBlock({
|
||||
page,
|
||||
fieldName: 'blocksWithSimilarConfigs',
|
||||
blockLabel: 'Block B',
|
||||
})
|
||||
|
||||
await page
|
||||
.locator('#blocksWithSimilarConfigs-row-1')
|
||||
.getByRole('button', { name: 'Add Item' })
|
||||
.click()
|
||||
|
||||
await page
|
||||
.locator('input[name="blocksWithSimilarConfigs.1.items.0.title2"]')
|
||||
.fill('items>1>title')
|
||||
@@ -269,19 +261,11 @@ describe('Block fields', () => {
|
||||
test('should fail min rows validation when rows are present', async () => {
|
||||
await page.goto(url.create)
|
||||
|
||||
await page
|
||||
.locator('#field-blocksWithMinRows')
|
||||
.getByRole('button', { name: 'Add Blocks With Min Row' })
|
||||
.click()
|
||||
|
||||
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
|
||||
await expect(blocksDrawer).toBeVisible()
|
||||
|
||||
const firstBlockSelector = blocksDrawer
|
||||
.locator('.blocks-drawer__blocks .blocks-drawer__block')
|
||||
.first()
|
||||
|
||||
await firstBlockSelector.click()
|
||||
await addBlock({
|
||||
page,
|
||||
fieldName: 'blocksWithMinRows',
|
||||
blockLabel: 'Block With Min Row',
|
||||
})
|
||||
|
||||
const firstRow = page.locator('input[name="blocksWithMinRows.0.blockTitle"]')
|
||||
await expect(firstRow).toBeVisible()
|
||||
@@ -328,6 +312,7 @@ describe('Block fields', () => {
|
||||
.locator('.custom-blocks-field-management')
|
||||
.getByRole('button', { name: 'Add Block 2' })
|
||||
.click()
|
||||
|
||||
await expect(
|
||||
page.locator('#field-customBlocks input[name="customBlocks.1.block2Title"]'),
|
||||
).toHaveValue('Block 2: Prefilled Title')
|
||||
@@ -336,6 +321,7 @@ describe('Block fields', () => {
|
||||
.locator('.custom-blocks-field-management')
|
||||
.getByRole('button', { name: 'Replace Block 2' })
|
||||
.click()
|
||||
|
||||
await expect(
|
||||
page.locator('#field-customBlocks input[name="customBlocks.1.block1Title"]'),
|
||||
).toHaveValue('REPLACED BLOCK')
|
||||
@@ -344,13 +330,13 @@ describe('Block fields', () => {
|
||||
})
|
||||
|
||||
describe('sortable blocks', () => {
|
||||
test('should have disabled admin sorting', async () => {
|
||||
test('should not render sort controls when sorting is disabled', async () => {
|
||||
await page.goto(url.create)
|
||||
const field = page.locator('#field-disableSort > div > div > .array-actions__action-chevron')
|
||||
expect(await field.count()).toEqual(0)
|
||||
})
|
||||
|
||||
test('the drag handle should be hidden', async () => {
|
||||
test('should not render drag handle when sorting is disabled', async () => {
|
||||
await page.goto(url.create)
|
||||
const field = page.locator(
|
||||
'#field-disableSort > .blocks-field__rows > div > div > .collapsible__drag',
|
||||
|
||||
28
test/helpers/e2e/addBlock.ts
Normal file
28
test/helpers/e2e/addBlock.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import { expect } from '@playwright/test'
|
||||
import { exactText } from 'helpers.js'
|
||||
|
||||
import { openBlocksDrawer } from './openBlocksDrawer.js'
|
||||
|
||||
export const addBlock = async ({
|
||||
page,
|
||||
fieldName = 'blocks',
|
||||
blockLabel = 'Block',
|
||||
}: {
|
||||
blockLabel: string
|
||||
fieldName: string
|
||||
page: Page
|
||||
}) => {
|
||||
const blocksDrawer = await openBlocksDrawer({ page, fieldName })
|
||||
|
||||
const blockCard = blocksDrawer.locator('.blocks-drawer__block .thumbnail-card__label', {
|
||||
hasText: blockLabel,
|
||||
})
|
||||
|
||||
await expect(blockCard).toBeVisible()
|
||||
|
||||
await blocksDrawer.getByRole('button', { name: exactText(blockLabel) }).click()
|
||||
|
||||
// expect to see the block on the page
|
||||
}
|
||||
22
test/helpers/e2e/openBlocksDrawer.ts
Normal file
22
test/helpers/e2e/openBlocksDrawer.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import type { Locator, Page } from '@playwright/test'
|
||||
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
export const openBlocksDrawer = async ({
|
||||
page,
|
||||
fieldName = 'blocks',
|
||||
}: {
|
||||
fieldName: string
|
||||
page: Page
|
||||
}): Promise<Locator> => {
|
||||
const blocksDrawer = page.locator('[id^=drawer_1_blocks-drawer-]')
|
||||
|
||||
if (!(await blocksDrawer.isVisible())) {
|
||||
const addButton = page.locator(`#field-${fieldName} > .blocks-field__drawer-toggler`)
|
||||
await addButton.click()
|
||||
}
|
||||
|
||||
await expect(blocksDrawer).toBeVisible()
|
||||
|
||||
return blocksDrawer
|
||||
}
|
||||
25
test/helpers/e2e/removeAllBlocks.ts
Normal file
25
test/helpers/e2e/removeAllBlocks.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import { expect } from '@playwright/test'
|
||||
|
||||
export const removeAllBlocks = async ({
|
||||
page,
|
||||
fieldName = 'blocks',
|
||||
}: {
|
||||
fieldName: string
|
||||
page: Page
|
||||
}) => {
|
||||
const blocksField = page.locator(`#field-${fieldName}`)
|
||||
|
||||
const blocks = blocksField.locator(`[id^="${fieldName}-row-"]`)
|
||||
const count = await blocks.count()
|
||||
|
||||
expect(count).toBeGreaterThan(0)
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
// delete in reverse order to avoid index issues
|
||||
const block = blocksField.locator(`[id^="${fieldName}-row-${count - i - 1}"]`)
|
||||
await block.locator('.array-actions__button').first().click()
|
||||
await block.locator('.array-actions__action.array-actions__remove').first().click()
|
||||
}
|
||||
}
|
||||
36
test/helpers/e2e/reorderBlocks.ts
Normal file
36
test/helpers/e2e/reorderBlocks.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import { wait } from 'payload/shared'
|
||||
|
||||
export const reorderBlocks = async ({
|
||||
page,
|
||||
fromBlockIndex = 1,
|
||||
toBlockIndex = 2,
|
||||
fieldName = 'blocks',
|
||||
}: {
|
||||
fieldName?: string
|
||||
fromBlockIndex: number
|
||||
page: Page
|
||||
toBlockIndex: number
|
||||
}) => {
|
||||
const blocksField = page.locator(`#field-${fieldName}`).first()
|
||||
|
||||
const fromField = blocksField.locator(`[id^="${fieldName}-row-${fromBlockIndex}"]`)
|
||||
|
||||
const fromBoundingBox = await fromField.locator(`.collapsible__drag`).boundingBox()
|
||||
|
||||
const toField = blocksField.locator(`[id^="${fieldName}-row-${toBlockIndex}"]`)
|
||||
|
||||
const toBoundingBox = await toField.locator(`.collapsible__drag`).boundingBox()
|
||||
|
||||
if (!fromBoundingBox || !toBoundingBox) {
|
||||
return
|
||||
}
|
||||
|
||||
// drag the "from" column to the left of the "to" column
|
||||
await page.mouse.move(fromBoundingBox.x + 2, fromBoundingBox.y + 2, { steps: 10 })
|
||||
await page.mouse.down()
|
||||
await wait(300)
|
||||
await page.mouse.move(toBoundingBox.x - 2, toBoundingBox.y - 2, { steps: 10 })
|
||||
await page.mouse.up()
|
||||
}
|
||||
@@ -31,7 +31,7 @@
|
||||
}
|
||||
],
|
||||
"paths": {
|
||||
"@payload-config": ["./test/_community/config.ts"],
|
||||
"@payload-config": ["./test/fields/config.ts"],
|
||||
"@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"],
|
||||
|
||||
Reference in New Issue
Block a user