chore: improve test suites, upgrade jest and playwright, add debug utilities for lexical (#4011)

* feat(richtext-lexical): 'bottom' position value for plugins

* feat: TestRecorderFeature

* chore: restructuring to seed and clear db before each test

* chore: make sure all tests pass

* chore: make sure indexes are created in seed.ts - this fixes one erroring test

* chore: speed up test runs through db snapshots

* chore: support drizzle when resetting db

* chore: simplify seeding process, by moving boilerplate db reset / snapshot logic into a wrapper function

* chore: add new seeding process to admin test suite

* chore(deps): upgrade jest and playwright

* chore: make sure mongoose-specific tests are not skipped

* chore: fix point test, which was depending on another test (that's bad!)

* chore: fix incorrect import

* chore: remove unnecessary comments

* chore: clearly label lexicalE2E test file as todo

* chore: simplify seed logic

* chore: move versions test suite to new seed system
This commit is contained in:
Alessio Gravili
2023-11-06 16:38:40 +01:00
committed by GitHub
parent 04850694c1
commit 17f7b94555
90 changed files with 2929 additions and 762 deletions

View File

@@ -1,11 +1,10 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import CustomEditView from '../components/views/CustomEdit'
export const customViews1Slug = 'custom-views-one'
import { customViews1CollectionSlug } from '../slugs'
export const CustomViews1: CollectionConfig = {
slug: customViews1Slug,
slug: customViews1CollectionSlug,
versions: true,
admin: {
components: {

View File

@@ -10,11 +10,11 @@ import {
customNestedTabViewPath,
customTabLabel,
customTabViewPath,
customViews2Slug,
} from '../shared'
import { customViews2CollectionSlug } from '../slugs'
export const CustomViews2: CollectionConfig = {
slug: customViews2Slug,
slug: customViews2CollectionSlug,
versions: true,
admin: {
components: {

View File

@@ -1,7 +1,9 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { geoCollectionSlug } from '../slugs'
export const Geo: CollectionConfig = {
slug: 'geo',
slug: geoCollectionSlug,
fields: [
{
name: 'point',

View File

@@ -1,6 +1,6 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { group1Collection1Slug } from '../shared'
import { group1Collection1Slug } from '../slugs'
export const CollectionGroup1A: CollectionConfig = {
slug: group1Collection1Slug,

View File

@@ -1,6 +1,6 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { group1Collection2Slug } from '../shared'
import { group1Collection2Slug } from '../slugs'
export const CollectionGroup1B: CollectionConfig = {
slug: group1Collection2Slug,

View File

@@ -1,7 +1,9 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { group2Collection1Slug } from '../slugs'
export const CollectionGroup2A: CollectionConfig = {
slug: 'group-two-collection-ones',
slug: group2Collection1Slug,
admin: {
group: 'One',
},

View File

@@ -1,7 +1,9 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { group2Collection2Slug } from '../slugs'
export const CollectionGroup2B: CollectionConfig = {
slug: 'group-two-collection-twos',
slug: group2Collection2Slug,
admin: {
group: 'One',
},

View File

@@ -1,7 +1,9 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { hiddenCollectionSlug } from '../slugs'
export const CollectionHidden: CollectionConfig = {
slug: 'hidden-collection',
slug: hiddenCollectionSlug,
admin: {
hidden: () => true,
},

View File

@@ -1,9 +1,9 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { noApiViewCollection } from '../shared'
import { noApiViewCollectionSlug } from '../slugs'
export const CollectionNoApiView: CollectionConfig = {
slug: noApiViewCollection,
slug: noApiViewCollectionSlug,
admin: {
hideAPIURL: true,
},

View File

@@ -3,10 +3,11 @@ import type { CollectionConfig } from '../../../packages/payload/src/collections
import { slateEditor } from '../../../packages/richtext-slate/src'
import DemoUIFieldCell from '../components/DemoUIField/Cell'
import DemoUIFieldField from '../components/DemoUIField/Field'
import { postsSlug, slugPluralLabel, slugSingularLabel } from '../shared'
import { slugPluralLabel, slugSingularLabel } from '../shared'
import { postsCollectionSlug } from '../slugs'
export const Posts: CollectionConfig = {
slug: postsSlug,
slug: postsCollectionSlug,
labels: {
singular: slugSingularLabel,
plural: slugPluralLabel,

View File

@@ -1,7 +1,9 @@
import type { CollectionConfig } from '../../../packages/payload/src/collections/config/types'
import { usersCollectionSlug } from '../slugs'
export const Users: CollectionConfig = {
slug: 'users',
slug: usersCollectionSlug,
auth: true,
admin: {
useAsTitle: 'email',

View File

@@ -27,7 +27,7 @@ import { GlobalGroup1A } from './globals/Group1A'
import { GlobalGroup1B } from './globals/Group1B'
import { GlobalHidden } from './globals/Hidden'
import { GlobalNoApiView } from './globals/NoApiView'
import { seed } from './seed'
import { clearAndSeedEverything } from './seed'
import { customNestedViewPath, customViewPath } from './shared'
export default buildConfigWithDefaults({
@@ -63,6 +63,16 @@ export default buildConfigWithDefaults({
},
},
},
webpack: (config) => ({
...config,
resolve: {
...config.resolve,
alias: {
...config?.resolve?.alias,
fs: path.resolve(__dirname, './mocks/emptyModule.js'),
},
},
}),
},
i18n: {
resources: {
@@ -99,5 +109,7 @@ export default buildConfigWithDefaults({
GlobalGroup1A,
GlobalGroup1B,
],
onInit: seed,
onInit: async (payload) => {
await clearAndSeedEverything(payload)
},
})

View File

@@ -3,7 +3,6 @@ import type { Page } from '@playwright/test'
import { expect, test } from '@playwright/test'
import qs from 'qs'
import type { PayloadRequest } from '../../packages/payload/src/express/types'
import type { Post } from './payload-types'
import payload from '../../packages/payload/src'
@@ -20,6 +19,7 @@ import {
} from '../helpers'
import { AdminUrlUtil } from '../helpers/adminUrlUtil'
import { initPayloadE2E } from '../helpers/configHelpers'
import { clearAndSeedEverything } from './seed'
import {
customEditLabel,
customNestedTabViewPath,
@@ -31,17 +31,19 @@ import {
customTabViewTitle,
customViewPath,
customViewTitle,
customViews2Slug,
slugPluralLabel,
} from './shared'
import {
customViews2CollectionSlug,
globalSlug,
group1Collection1Slug,
group1GlobalSlug,
noApiViewCollection,
noApiViewGlobal,
postsSlug,
slugPluralLabel,
} from './shared'
noApiViewCollectionSlug,
noApiViewGlobalSlug,
postsCollectionSlug,
} from './slugs'
const { afterEach, beforeAll, beforeEach, describe } = test
const { beforeAll, beforeEach, describe } = test
const title = 'Title'
const description = 'Description'
@@ -54,29 +56,21 @@ describe('admin', () => {
beforeAll(async ({ browser }) => {
serverURL = (await initPayloadE2E(__dirname)).serverURL
await clearDocs() // Clear any seeded data from onInit
url = new AdminUrlUtil(serverURL, postsSlug)
customViewsURL = new AdminUrlUtil(serverURL, customViews2Slug)
url = new AdminUrlUtil(serverURL, postsCollectionSlug)
customViewsURL = new AdminUrlUtil(serverURL, customViews2CollectionSlug)
const context = await browser.newContext()
page = await context.newPage()
})
afterEach(async () => {
await clearDocs()
// clear preferences
await payload.db.deleteMany({
collection: 'payload-preferences',
req: {} as PayloadRequest,
where: {},
})
beforeEach(async () => {
await clearAndSeedEverything(payload)
})
describe('Nav', () => {
test('should nav to collection - nav', async () => {
await page.goto(url.admin)
await openNav(page)
await page.locator(`#nav-${postsSlug}`).click()
await page.locator(`#nav-${postsCollectionSlug}`).click()
expect(page.url()).toContain(url.list)
})
@@ -90,7 +84,7 @@ describe('admin', () => {
test('should navigate to collection - card', async () => {
await page.goto(url.admin)
await wait(200)
await page.locator(`#card-${postsSlug}`).click()
await page.locator(`#card-${postsCollectionSlug}`).click()
expect(page.url()).toContain(url.list)
})
@@ -147,7 +141,7 @@ describe('admin', () => {
const { id } = await createPost()
await page.goto(url.edit(id))
const collectionBreadcrumb = page.locator(
`.step-nav a[href="/admin/collections/${postsSlug}"]`,
`.step-nav a[href="/admin/collections/${postsCollectionSlug}"]`,
)
await expect(collectionBreadcrumb).toBeVisible()
await expect(collectionBreadcrumb).toHaveText(slugPluralLabel)
@@ -239,35 +233,35 @@ describe('admin', () => {
})
test('collection - should not show API tab when disabled in config', async () => {
await page.goto(url.collection(noApiViewCollection))
await page.goto(url.collection(noApiViewCollectionSlug))
await page.locator('.collection-list .table a').click()
await expect(page.locator('.doc-tabs__tabs-container')).not.toContainText('API')
})
test('collection - should not enable API route when disabled in config', async () => {
const collectionItems = await payload.find({
collection: noApiViewCollection,
collection: noApiViewCollectionSlug,
limit: 1,
})
expect(collectionItems.docs.length).toBe(1)
await page.goto(`${url.collection(noApiViewCollection)}/${collectionItems.docs[0].id}/api`)
await page.goto(`${url.collection(noApiViewGlobalSlug)}/${collectionItems.docs[0].id}/api`)
await expect(page.locator('.not-found')).toHaveCount(1)
})
test('global - should not show API tab when disabled in config', async () => {
await page.goto(url.global(noApiViewGlobal))
await page.goto(url.global(noApiViewGlobalSlug))
await expect(page.locator('.doc-tabs__tabs-container')).not.toContainText('API')
})
test('global - should not enable API route when disabled in config', async () => {
await page.goto(`${url.global(noApiViewGlobal)}/api`)
await page.goto(`${url.global(noApiViewGlobalSlug)}/api`)
await expect(page.locator('.not-found')).toHaveCount(1)
})
})
describe('ui', () => {
test('collection - should render preview button when `admin.preview` is set', async () => {
const collectionWithPreview = new AdminUrlUtil(serverURL, postsSlug)
const collectionWithPreview = new AdminUrlUtil(serverURL, postsCollectionSlug)
await page.goto(collectionWithPreview.create)
await page.locator('#field-title').fill(title)
await saveDocAndAssert(page)
@@ -406,9 +400,11 @@ describe('admin', () => {
})
test('should bulk delete', async () => {
await createPost()
await createPost()
await createPost()
// First, delete all posts created by the seed
await deleteAllPosts()
await Promise.all([createPost(), createPost(), createPost()])
await page.goto(url.list)
await page.locator('input#select-all').check()
await page.locator('.delete-documents__toggle').click()
@@ -420,9 +416,10 @@ describe('admin', () => {
})
test('should bulk update', async () => {
await createPost()
await createPost()
await createPost()
// First, delete all posts created by the seed
await deleteAllPosts()
await Promise.all([createPost(), createPost(), createPost()])
const bulkTitle = 'Bulk update title'
await page.goto(url.list)
@@ -514,6 +511,9 @@ describe('admin', () => {
})
test('search by id', async () => {
// delete all posts created by the seed
await deleteAllPosts()
const { id } = await createPost()
await page.locator('.search-filter__input').fill(id)
const tableItems = page.locator(tableRowLocator)
@@ -534,6 +534,9 @@ describe('admin', () => {
})
test('toggle columns', async () => {
// delete all posts created by the seed
await deleteAllPosts()
const columnCountLocator = 'table > thead > tr > th'
await createPost()
@@ -592,6 +595,9 @@ describe('admin', () => {
})
test('filter rows', async () => {
// delete all posts created by the seed
await deleteAllPosts()
const { id } = await createPost({ title: 'post1' })
await createPost({ title: 'post2' })
@@ -923,6 +929,9 @@ describe('admin', () => {
describe('multi-select', () => {
beforeEach(async () => {
// delete all posts created by the seed
await deleteAllPosts()
await mapAsync([...Array(3)], async () => {
await createPost()
})
@@ -962,12 +971,6 @@ describe('admin', () => {
})
describe('pagination', () => {
beforeAll(async () => {
await mapAsync([...Array(11)], async () => {
await createPost()
})
})
test('should paginate', async () => {
const pageInfo = page.locator('.collection-list__page-info')
const perPage = page.locator('.per-page')
@@ -999,13 +1002,18 @@ describe('admin', () => {
// TODO: Troubleshoot flaky suite
describe('sorting', () => {
beforeAll(async () => {
await createPost({
number: 1,
})
await createPost({
number: 2,
})
beforeEach(async () => {
// delete all posts created by the seed
await deleteAllPosts()
await Promise.all([
createPost({
number: 1,
}),
createPost({
number: 2,
}),
])
})
test('should sort', async () => {
@@ -1082,18 +1090,26 @@ describe('admin', () => {
async function createPost(overrides?: Partial<Post>): Promise<Post> {
return payload.create({
collection: postsSlug,
collection: postsCollectionSlug,
data: {
description,
title,
...overrides,
},
})
}) as unknown as Promise<Post>
}
async function clearDocs(): Promise<void> {
await payload.delete({
collection: postsSlug,
where: { id: { exists: true } },
async function deleteAllPosts() {
const posts = await payload.find({
collection: postsCollectionSlug,
limit: 100,
})
await Promise.all([
...posts.docs.map((post) => {
return payload.delete({
collection: postsCollectionSlug,
id: post.id,
})
}),
])
}

View File

@@ -1,9 +1,10 @@
import type { GlobalConfig } from '../../../packages/payload/src/globals/config/types'
import CustomEditView from '../components/views/CustomEdit'
import { customGlobalViews1GlobalSlug } from '../slugs'
export const CustomGlobalViews1: GlobalConfig = {
slug: 'custom-global-views-one',
slug: customGlobalViews1GlobalSlug,
versions: true,
admin: {
components: {

View File

@@ -4,9 +4,10 @@ import CustomTabComponent from '../components/CustomTabComponent'
import CustomDefaultEditView from '../components/views/CustomEditDefault'
import CustomView from '../components/views/CustomTab'
import CustomVersionsView from '../components/views/CustomVersions'
import { customGlobalViews2GlobalSlug } from '../slugs'
export const CustomGlobalViews2: GlobalConfig = {
slug: 'custom-global-views-two',
slug: customGlobalViews2GlobalSlug,
versions: true,
admin: {
components: {

View File

@@ -1,6 +1,6 @@
import type { GlobalConfig } from '../../../packages/payload/src/globals/config/types'
import { globalSlug } from '../shared'
import { globalSlug } from '../slugs'
export const Global: GlobalConfig = {
slug: globalSlug,

View File

@@ -1,6 +1,6 @@
import type { GlobalConfig } from '../../../packages/payload/src/globals/config/types'
import { group1GlobalSlug } from '../shared'
import { group1GlobalSlug } from '../slugs'
export const GlobalGroup1A: GlobalConfig = {
slug: group1GlobalSlug,

View File

@@ -1,7 +1,9 @@
import type { GlobalConfig } from '../../../packages/payload/src/globals/config/types'
import { group2GlobalSlug } from '../slugs'
export const GlobalGroup1B: GlobalConfig = {
slug: 'group-globals-two',
slug: group2GlobalSlug,
admin: {
group: 'Group',
},

View File

@@ -1,7 +1,9 @@
import type { GlobalConfig } from '../../../packages/payload/src/globals/config/types'
import { hiddenGlobalSlug } from '../slugs'
export const GlobalHidden: GlobalConfig = {
slug: 'hidden-global',
slug: hiddenGlobalSlug,
admin: {
hidden: () => true,
},

View File

@@ -1,8 +1,9 @@
import type { GlobalConfig } from '../../../packages/payload/src/globals/config/types'
import { noApiViewGlobal } from '../shared'
import { noApiViewGlobalSlug } from '../slugs'
export const GlobalNoApiView: GlobalConfig = {
slug: noApiViewGlobal,
slug: noApiViewGlobalSlug,
admin: {
hideAPIURL: true,
},

View File

@@ -0,0 +1 @@
export default {}

70
test/admin/seed.ts Normal file
View File

@@ -0,0 +1,70 @@
import type { Payload } from '../../packages/payload/src'
import { devUser } from '../credentials'
import { seedDB } from '../helpers/seed'
import {
collectionSlugs,
customViews1CollectionSlug,
customViews2CollectionSlug,
geoCollectionSlug,
noApiViewCollectionSlug,
postsCollectionSlug,
usersCollectionSlug,
} from './slugs'
export async function clearAndSeedEverything(_payload: Payload) {
return await seedDB({
snapshotKey: 'adminTest',
shouldResetDB: true,
collectionSlugs,
_payload,
seedFunction: async (_payload) => {
await Promise.all([
_payload.create({
collection: usersCollectionSlug,
data: {
email: devUser.email,
password: devUser.password,
},
}),
...[...Array(11)].map(() => {
_payload.create({
collection: postsCollectionSlug,
data: {
title: 'Title',
description: 'Description',
},
})
}),
_payload.create({
collection: customViews1CollectionSlug,
data: {
title: 'Custom View',
},
}),
_payload.create({
collection: customViews2CollectionSlug,
data: {
title: 'Custom View',
},
}),
_payload.create({
collection: geoCollectionSlug,
data: {
point: [7, -7],
},
}),
_payload.create({
collection: geoCollectionSlug,
data: {
point: [5, -5],
},
}),
_payload.create({
collection: noApiViewCollectionSlug,
data: {},
}),
])
},
})
}

View File

@@ -1,59 +0,0 @@
import type { Config } from '../../../packages/payload/src/config/types'
import { mapAsync } from '../../../packages/payload/src/utilities/mapAsync'
import { devUser } from '../../credentials'
import { customViews1Slug } from '../collections/CustomViews1'
import { customViews2Slug, noApiViewCollection, postsSlug } from '../shared'
export const seed: Config['onInit'] = async (payload) => {
await payload.create({
collection: 'users',
data: {
email: devUser.email,
password: devUser.password,
},
})
await mapAsync([...Array(11)], async () => {
await payload.create({
collection: postsSlug,
data: {
title: 'Title',
description: 'Description',
},
})
})
await payload.create({
collection: customViews1Slug,
data: {
title: 'Custom View',
},
})
await payload.create({
collection: customViews2Slug,
data: {
title: 'Custom View',
},
})
await payload.create({
collection: 'geo',
data: {
point: [7, -7],
},
})
await payload.create({
collection: 'geo',
data: {
point: [5, -5],
},
})
await payload.create({
collection: noApiViewCollection,
data: {},
})
}

View File

@@ -1,21 +1,7 @@
export const postsSlug = 'posts'
export const group1Collection1Slug = 'group-one-collection-ones'
export const group1Collection2Slug = 'group-one-collection-twos'
export const slugSingularLabel = 'Post'
export const slugPluralLabel = 'Posts'
export const globalSlug = 'global'
export const group1GlobalSlug = 'group-globals-one'
export const noApiViewCollection = 'collection-no-api-view'
export const noApiViewGlobal = 'global-no-api-view'
export const customViewPath = '/custom-view'
export const customViewTitle = 'Custom View'
@@ -24,8 +10,6 @@ export const customNestedViewPath = `${customViewPath}/nested-view`
export const customNestedViewTitle = 'Custom Nested View'
export const customViews2Slug = 'custom-views-two'
export const customEditLabel = 'Custom Edit Label'
export const customTabLabel = 'Custom Tab Label'

41
test/admin/slugs.ts Normal file
View File

@@ -0,0 +1,41 @@
export const usersCollectionSlug = 'users' as const
export const customViews1CollectionSlug = 'custom-views-one' as const
export const customViews2CollectionSlug = 'custom-views-two' as const
export const geoCollectionSlug = 'geo' as const
export const postsCollectionSlug = 'posts' as const
export const group1Collection1Slug = 'group-one-collection-ones' as const
export const group1Collection2Slug = 'group-one-collection-twos' as const
export const group2Collection1Slug = 'group-two-collection-ones' as const
export const group2Collection2Slug = 'group-two-collection-twos' as const
export const hiddenCollectionSlug = 'hidden-collection' as const
export const noApiViewCollectionSlug = 'collection-no-api-view' as const
export const collectionSlugs = [
usersCollectionSlug,
customViews1CollectionSlug,
customViews2CollectionSlug,
geoCollectionSlug,
postsCollectionSlug,
group1Collection1Slug,
group1Collection2Slug,
group2Collection1Slug,
group2Collection2Slug,
hiddenCollectionSlug,
noApiViewCollectionSlug,
]
export const customGlobalViews1GlobalSlug = 'custom-global-views-one' as const
export const customGlobalViews2GlobalSlug = 'custom-global-views-two' as const
export const globalSlug = 'global' as const
export const group1GlobalSlug = 'group-globals-one' as const
export const group2GlobalSlug = 'group-globals-two' as const
export const hiddenGlobalSlug = 'hidden-global' as const
export const noApiViewGlobalSlug = 'global-no-api-view'
export const globalSlugs = [
customGlobalViews1GlobalSlug,
customGlobalViews2GlobalSlug,
globalSlug,
group1GlobalSlug,
group2GlobalSlug,
hiddenGlobalSlug,
noApiViewGlobalSlug,
]