Files
payloadcms/test/field-error-states/e2e.spec.ts
Jacob Fletcher adb83b1e06 test: add array field helpers (#13493)
Adds various helpers to make it easier and more standard to manage array
fields within e2e tests. Retrofits existing tests to ensure consistent
interactions across the board, and also organizes existing blocks and
relationship field helpers in the same way.

---
- To see the specific tasks where the Asana app for GitHub is being
used, see below:
  - https://app.asana.com/0/0/1211094073049046
2025-08-19 19:44:59 +00:00

171 lines
6.6 KiB
TypeScript

import type { Page } from '@playwright/test'
import { expect, test } from '@playwright/test'
import { AdminUrlUtil } from 'helpers/adminUrlUtil.js'
import { addArrayRow, openArrayRowActions, removeArrayRow } from 'helpers/e2e/fields/array/index.js'
import path from 'path'
import { wait } from 'payload/shared'
import { fileURLToPath } from 'url'
import { ensureCompilationIsDone, initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers.js'
import { initPayloadE2ENoConfig } from '../helpers/initPayloadE2ENoConfig.js'
import { TEST_TIMEOUT_LONG } from '../playwright.config.js'
import { collectionSlugs } from './shared.js'
const { beforeAll, describe } = test
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
describe('Field Error States', () => {
let serverURL: string
let page: Page
let validateDraftsOff: AdminUrlUtil
let validateDraftsOn: AdminUrlUtil
let validateDraftsOnAutosave: AdminUrlUtil
let prevValue: AdminUrlUtil
let prevValueRelation: AdminUrlUtil
let errorFieldsURL: AdminUrlUtil
beforeAll(async ({ browser }, testInfo) => {
testInfo.setTimeout(TEST_TIMEOUT_LONG)
;({ serverURL } = await initPayloadE2ENoConfig({ dirname }))
validateDraftsOff = new AdminUrlUtil(serverURL, collectionSlugs.validateDraftsOff)
validateDraftsOn = new AdminUrlUtil(serverURL, collectionSlugs.validateDraftsOn)
validateDraftsOnAutosave = new AdminUrlUtil(serverURL, collectionSlugs.validateDraftsOnAutosave)
prevValue = new AdminUrlUtil(serverURL, collectionSlugs.prevValue)
prevValueRelation = new AdminUrlUtil(serverURL, collectionSlugs.prevValueRelation)
errorFieldsURL = new AdminUrlUtil(serverURL, collectionSlugs.errorFields)
const context = await browser.newContext()
page = await context.newPage()
initPageConsoleErrorCatch(page)
await ensureCompilationIsDone({ page, serverURL })
})
test('Remove row should remove error states from parent fields', async () => {
await page.goto(`${serverURL}/admin/collections/error-fields/create`)
// add parent array
await addArrayRow(page, { fieldName: 'parentArray' })
// add first child array
await page.locator('#parentArray-row-0 .collapsible__content .array-field__add-row').click()
await page.locator('#field-parentArray__0__childArray__0__childArrayText').focus()
await page.keyboard.type('T1')
// add second child array
await page.locator('#parentArray-row-0 .collapsible__content .array-field__add-row').click()
await page.locator('#field-parentArray__0__childArray__1__childArrayText').focus()
await page.keyboard.type('T2')
// add third child array
await page.locator('#parentArray-row-0 .collapsible__content .array-field__add-row').click()
await removeArrayRow(page, {
fieldName: 'parentArray__0__childArray',
rowIndex: 2,
})
await page.locator('#action-save').click()
const errorPill = await page.waitForSelector(
'#parentArray-row-0 > .collapsible > .collapsible__toggle-wrap .array-field__row-error-pill',
{ state: 'hidden', timeout: 500 },
)
expect(errorPill).toBeNull()
})
describe('draft validations', () => {
test('should not validate drafts by default', async () => {
await page.goto(validateDraftsOff.create)
await saveDocAndAssert(page, '#action-save-draft')
})
test('should validate drafts when enabled', async () => {
await page.goto(validateDraftsOn.create)
await saveDocAndAssert(page, '#action-save-draft', 'error')
})
test('should show validation errors when validate and autosave are enabled', async () => {
await page.goto(validateDraftsOnAutosave.create)
await page.locator('#field-title').fill('valid')
await saveDocAndAssert(page)
await page.locator('#field-title').fill('')
await saveDocAndAssert(page, '#action-save', 'error')
})
})
describe('previous values', () => {
test('should pass previous value into validate function', async () => {
// save original
await page.goto(prevValue.create)
await page.locator('#field-title').fill('original value')
await saveDocAndAssert(page)
await page.locator('#field-title').fill('original value 2')
await saveDocAndAssert(page)
await wait(500)
// create relation to doc
await page.goto(prevValueRelation.create)
await page.locator('#field-previousValueRelation .react-select').click()
await page.locator('#field-previousValueRelation .rs__option').first().click()
await saveDocAndAssert(page)
// go back to doc
await page.goto(prevValue.list)
await page.locator('.row-1 a').click()
await page.locator('#field-description').fill('some description')
await saveDocAndAssert(page)
await page.locator('#field-title').fill('changed')
await saveDocAndAssert(page, '#action-save', 'error')
// ensure value is the value before relationship association
await page.reload()
await expect(page.locator('#field-title')).toHaveValue('original value 2')
})
})
describe('error field types', () => {
async function prefillBaseRequiredFields() {
const homeTabLocator = page.locator('.tabs-field__tab-button', {
hasText: 'Home',
})
const heroTabLocator = page.locator('.tabs-field__tab-button', {
hasText: 'Hero',
})
await homeTabLocator.click()
// fill out all required fields in the home tab
await page.locator('#field-home__text').fill('Home Collapsible Text')
await page.locator('#field-home__tabText').fill('Home Tab Text')
await page.locator('#field-group__text').fill('Home Group Text')
await heroTabLocator.click()
// fill out all required fields in the hero tab
await page.locator('#field-tabText').fill('Hero Tab Text')
await page.locator('#field-text').fill('Hero Tab Collapsible Text')
}
test('group errors', async () => {
await page.goto(errorFieldsURL.create)
await prefillBaseRequiredFields()
// clear group and save
await page.locator('#field-group__text').fill('')
await saveDocAndAssert(page, '#action-save', 'error')
// should show the error pill and count
const groupFieldErrorPill = page.locator('#field-group .group-field__header .error-pill', {
hasText: '1 error',
})
await expect(groupFieldErrorPill).toBeVisible()
// finish filling out the group
await page.locator('#field-group__text').fill('filled out')
await expect(page.locator('#field-group .group-field__header .error-pill')).toBeHidden()
await saveDocAndAssert(page, '#action-save')
})
})
})