feat: draft validation (#6746)
Allows draft validation to be enabled at the config level.
You can enable this by:
```ts
// ...collectionConfig
versions: {
drafts: {
validate: true // defaults to false
}
}
```
This commit is contained in:
@@ -5,11 +5,16 @@ import { mediaSlug } from '../Media'
|
||||
export const postsSlug = 'posts'
|
||||
|
||||
export const PostsCollection: CollectionConfig = {
|
||||
defaultSort: 'title',
|
||||
fields: [
|
||||
{
|
||||
name: 'text',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
},
|
||||
{
|
||||
name: 'associatedMedia',
|
||||
access: {
|
||||
|
||||
@@ -32,6 +32,23 @@ export default buildConfigWithDefaults({
|
||||
collection: postsSlug,
|
||||
data: {
|
||||
text: 'example post',
|
||||
title: 'title1',
|
||||
},
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: postsSlug,
|
||||
data: {
|
||||
text: 'example post',
|
||||
title: 'title3',
|
||||
},
|
||||
})
|
||||
|
||||
await payload.create({
|
||||
collection: postsSlug,
|
||||
data: {
|
||||
text: 'example post',
|
||||
title: 'title2',
|
||||
},
|
||||
})
|
||||
},
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
|
||||
|
||||
import { slugs } from '../../shared'
|
||||
import { ValidateDraftsOn } from '../ValidateDraftsOn'
|
||||
|
||||
export const ValidateDraftsOff: CollectionConfig = {
|
||||
...ValidateDraftsOn,
|
||||
slug: slugs.validateDraftsOff,
|
||||
versions: {
|
||||
drafts: true,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
|
||||
|
||||
import { slugs } from '../../shared'
|
||||
|
||||
export const ValidateDraftsOn: CollectionConfig = {
|
||||
slug: slugs.validateDraftsOn,
|
||||
fields: [
|
||||
{
|
||||
name: 'title',
|
||||
type: 'text',
|
||||
required: true,
|
||||
},
|
||||
],
|
||||
versions: {
|
||||
drafts: {
|
||||
validate: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import type { CollectionConfig } from '../../../../packages/payload/src/collections/config/types'
|
||||
|
||||
import { slugs } from '../../shared'
|
||||
import { ValidateDraftsOn } from '../ValidateDraftsOn'
|
||||
|
||||
export const ValidateDraftsOnAndAutosave: CollectionConfig = {
|
||||
...ValidateDraftsOn,
|
||||
slug: slugs.validateDraftsOnAutosave,
|
||||
versions: {
|
||||
drafts: {
|
||||
autosave: true,
|
||||
validate: true,
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -2,9 +2,18 @@ import { buildConfigWithDefaults } from '../buildConfigWithDefaults'
|
||||
import { devUser } from '../credentials'
|
||||
import { ErrorFieldsCollection } from './collections/ErrorFields'
|
||||
import Uploads from './collections/Upload'
|
||||
import { ValidateDraftsOff } from './collections/ValidateDraftsOff'
|
||||
import { ValidateDraftsOn } from './collections/ValidateDraftsOn'
|
||||
import { ValidateDraftsOnAndAutosave } from './collections/ValidateDraftsOnAutosave'
|
||||
|
||||
export default buildConfigWithDefaults({
|
||||
collections: [ErrorFieldsCollection, Uploads],
|
||||
collections: [
|
||||
ErrorFieldsCollection,
|
||||
Uploads,
|
||||
ValidateDraftsOn,
|
||||
ValidateDraftsOff,
|
||||
ValidateDraftsOnAndAutosave,
|
||||
],
|
||||
graphQL: {
|
||||
schemaOutputFile: './test/field-error-states/schema.graphql',
|
||||
},
|
||||
|
||||
@@ -2,17 +2,26 @@ import type { Page } from '@playwright/test'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
|
||||
import { initPageConsoleErrorCatch } from '../helpers'
|
||||
import { initPageConsoleErrorCatch, saveDocAndAssert } from '../helpers'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
|
||||
import { initPayloadE2E } from '../helpers/configHelpers'
|
||||
import { slugs } from './shared'
|
||||
|
||||
const { beforeAll, describe } = test
|
||||
|
||||
let validateDraftsOff: AdminUrlUtil
|
||||
let validateDraftsOn: AdminUrlUtil
|
||||
let validateDraftsOnAutosave: AdminUrlUtil
|
||||
|
||||
describe('field error states', () => {
|
||||
let serverURL: string
|
||||
let page: Page
|
||||
|
||||
beforeAll(async ({ browser }) => {
|
||||
;({ serverURL } = await initPayloadE2E(__dirname))
|
||||
validateDraftsOff = new AdminUrlUtil(serverURL, slugs.validateDraftsOff)
|
||||
validateDraftsOn = new AdminUrlUtil(serverURL, slugs.validateDraftsOn)
|
||||
validateDraftsOnAutosave = new AdminUrlUtil(serverURL, slugs.validateDraftsOnAutosave)
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
initPageConsoleErrorCatch(page)
|
||||
@@ -49,4 +58,31 @@ describe('field error states', () => {
|
||||
)
|
||||
expect(errorPill).toBeNull()
|
||||
})
|
||||
|
||||
describe('draft validations', () => {
|
||||
// eslint-disable-next-line playwright/expect-expect
|
||||
test('should not validate drafts by default', async () => {
|
||||
await page.goto(validateDraftsOff.create)
|
||||
await page.locator('#field-title').fill('temp')
|
||||
await page.locator('#field-title').fill('')
|
||||
await saveDocAndAssert(page, '#action-save-draft')
|
||||
})
|
||||
|
||||
// eslint-disable-next-line playwright/expect-expect
|
||||
test('should validate drafts when enabled', async () => {
|
||||
await page.goto(validateDraftsOn.create)
|
||||
await page.locator('#field-title').fill('temp')
|
||||
await page.locator('#field-title').fill('')
|
||||
await saveDocAndAssert(page, '#action-save-draft', 'error')
|
||||
})
|
||||
|
||||
// eslint-disable-next-line playwright/expect-expect
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
5
test/field-error-states/shared.ts
Normal file
5
test/field-error-states/shared.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export const slugs = {
|
||||
validateDraftsOn: 'validate-drafts-on',
|
||||
validateDraftsOnAutosave: 'validate-drafts-on-autosave',
|
||||
validateDraftsOff: 'validate-drafts-off',
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
import type { Page } from '@playwright/test'
|
||||
|
||||
import { expect, test } from '@playwright/test'
|
||||
|
||||
import { initPageConsoleErrorCatch } from '../helpers'
|
||||
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
|
||||
import { initPayloadE2E } from '../helpers/configHelpers'
|
||||
|
||||
const { beforeAll, describe } = test
|
||||
|
||||
describe('Globals', () => {
|
||||
let page: Page
|
||||
let url: AdminUrlUtil
|
||||
|
||||
beforeAll(async ({ browser }) => {
|
||||
const { serverURL } = await initPayloadE2E(__dirname)
|
||||
url = new AdminUrlUtil(serverURL, 'media')
|
||||
|
||||
const context = await browser.newContext()
|
||||
page = await context.newPage()
|
||||
initPageConsoleErrorCatch(page)
|
||||
})
|
||||
|
||||
test('can edit media from field', async () => {
|
||||
await page.goto(url.create)
|
||||
|
||||
// const textCell = page.locator('.row-1 .cell-text')
|
||||
})
|
||||
})
|
||||
@@ -51,10 +51,18 @@ export async function saveDocHotkeyAndAssert(page: Page): Promise<void> {
|
||||
await expect(page.locator('.Toastify')).toContainText('successfully')
|
||||
}
|
||||
|
||||
export async function saveDocAndAssert(page: Page, selector = '#action-save'): Promise<void> {
|
||||
export async function saveDocAndAssert(
|
||||
page: Page,
|
||||
selector = '#action-save',
|
||||
expectation: 'error' | 'success' = 'success',
|
||||
): Promise<void> {
|
||||
await page.click(selector, { delay: 100 })
|
||||
await expect(page.locator('.Toastify')).toContainText('successfully')
|
||||
expect(page.url()).not.toContain('create')
|
||||
if (expectation === 'success') {
|
||||
await expect(page.locator('.Toastify')).toContainText('successfully')
|
||||
expect(page.url()).not.toContain('create')
|
||||
} else {
|
||||
await expect(page.locator('.Toastify .Toastify__toast--error')).toBeVisible()
|
||||
}
|
||||
}
|
||||
|
||||
export async function openNav(page: Page): Promise<void> {
|
||||
|
||||
@@ -359,18 +359,18 @@ describe('versions', () => {
|
||||
await page.goto(autosaveURL.create)
|
||||
await page.locator('#field-title').fill(title)
|
||||
await page.locator('#field-description').fill(description)
|
||||
await wait(500) // wait for autosave
|
||||
await wait(1000) // wait for autosave
|
||||
|
||||
await changeLocale(page, spanishLocale)
|
||||
await page.locator('#field-title').fill(spanishTitle)
|
||||
await wait(500) // wait for autosave
|
||||
await wait(1000) // wait for autosave
|
||||
|
||||
await changeLocale(page, locale)
|
||||
await page.locator('#field-description').fill(newDescription)
|
||||
await wait(500) // wait for autosave
|
||||
await wait(1000) // wait for autosave
|
||||
|
||||
await changeLocale(page, spanishLocale)
|
||||
await wait(500) // wait for autosave
|
||||
await wait(1000) // wait for autosave
|
||||
|
||||
await page.reload()
|
||||
await expect(page.locator('#field-title')).toHaveValue(spanishTitle)
|
||||
|
||||
Reference in New Issue
Block a user