Files
payloadcms/test/live-preview/int.spec.ts
T. R. Bernstein 4a5f01a78f
Some checks failed
ci / changes (push) Has been cancelled
ci / lint (push) Has been cancelled
ci / build (push) Has been cancelled
ci / tests-unit (push) Has been cancelled
ci / tests-types (push) Has been cancelled
ci / int-cosmosdb (push) Has been cancelled
ci / int-documentdb (push) Has been cancelled
ci / int-firestore (push) Has been cancelled
ci / int-mongodb (push) Has been cancelled
ci / int-postgres (push) Has been cancelled
ci / int-postgres-custom-schema (push) Has been cancelled
ci / int-postgres-uuid (push) Has been cancelled
ci / int-sqlite (push) Has been cancelled
ci / int-sqlite-uuid (push) Has been cancelled
ci / int-supabase (push) Has been cancelled
ci / e2e-_community (push) Has been cancelled
ci / e2e-access-control (push) Has been cancelled
ci / e2e-admin-bar (push) Has been cancelled
ci / e2e-admin-root (push) Has been cancelled
ci / e2e-admin__e2e__document-view (push) Has been cancelled
ci / e2e-admin__e2e__general (push) Has been cancelled
ci / e2e-admin__e2e__list-view (push) Has been cancelled
ci / e2e-auth (push) Has been cancelled
ci / e2e-auth-basic (push) Has been cancelled
ci / e2e-bulk-edit (push) Has been cancelled
ci / e2e-field-error-states (push) Has been cancelled
ci / e2e-fields-relationship (push) Has been cancelled
ci / e2e-fields__collections__Array (push) Has been cancelled
ci / e2e-fields__collections__Blocks#config.blockreferences.ts (push) Has been cancelled
ci / e2e-fields__collections__Blocks (push) Has been cancelled
ci / e2e-fields__collections__Checkbox (push) Has been cancelled
ci / e2e-fields__collections__Collapsible (push) Has been cancelled
ci / e2e-fields__collections__ConditionalLogic (push) Has been cancelled
ci / e2e-fields__collections__CustomID (push) Has been cancelled
ci / e2e-fields__collections__Date (push) Has been cancelled
ci / e2e-fields__collections__Email (push) Has been cancelled
ci / e2e-fields__collections__Indexed (push) Has been cancelled
ci / e2e-fields__collections__JSON (push) Has been cancelled
ci / e2e-fields__collections__Number (push) Has been cancelled
ci / e2e-fields__collections__Point (push) Has been cancelled
ci / e2e-fields__collections__Radio (push) Has been cancelled
ci / e2e-fields__collections__Relationship (push) Has been cancelled
ci / e2e-fields__collections__Row (push) Has been cancelled
ci / e2e-fields__collections__Select (push) Has been cancelled
ci / e2e-fields__collections__Tabs (push) Has been cancelled
ci / e2e-fields__collections__Tabs2 (push) Has been cancelled
ci / e2e-fields__collections__Text (push) Has been cancelled
ci / e2e-fields__collections__UI (push) Has been cancelled
ci / e2e-fields__collections__Upload (push) Has been cancelled
ci / e2e-folders (push) Has been cancelled
ci / e2e-form-state (push) Has been cancelled
ci / e2e-group-by (push) Has been cancelled
ci / e2e-hooks (push) Has been cancelled
ci / e2e-i18n (push) Has been cancelled
ci / e2e-joins (push) Has been cancelled
ci / e2e-lexical__collections__LexicalHeadingFeature (push) Has been cancelled
ci / e2e-lexical__collections__LexicalJSXConverter (push) Has been cancelled
ci / e2e-lexical__collections__LexicalLinkFeature (push) Has been cancelled
ci / e2e-lexical__collections__Lexical__e2e__blocks#config.blockreferences.ts (push) Has been cancelled
ci / e2e-lexical__collections__Lexical__e2e__blocks (push) Has been cancelled
ci / e2e-lexical__collections__Lexical__e2e__main (push) Has been cancelled
ci / e2e-lexical__collections__OnDemandForm (push) Has been cancelled
ci / e2e-lexical__collections__RichText (push) Has been cancelled
ci / e2e-lexical__collections___LexicalFullyFeatured (push) Has been cancelled
ci / e2e-lexical__collections___LexicalFullyFeatured__db (push) Has been cancelled
ci / e2e-live-preview (push) Has been cancelled
ci / e2e-localization (push) Has been cancelled
ci / e2e-locked-documents (push) Has been cancelled
ci / e2e-plugin-cloud-storage (push) Has been cancelled
ci / e2e-plugin-form-builder (push) Has been cancelled
ci / e2e-plugin-import-export (push) Has been cancelled
ci / e2e-plugin-multi-tenant (push) Has been cancelled
ci / e2e-plugin-nested-docs (push) Has been cancelled
ci / e2e-plugin-seo (push) Has been cancelled
ci / e2e-query-presets (push) Has been cancelled
ci / e2e-sort (push) Has been cancelled
ci / e2e-trash (push) Has been cancelled
ci / e2e-uploads (push) Has been cancelled
ci / e2e-versions (push) Has been cancelled
ci / e2e-turbo-_community (push) Has been cancelled
ci / e2e-turbo-access-control (push) Has been cancelled
ci / e2e-turbo-admin-bar (push) Has been cancelled
ci / e2e-turbo-admin-root (push) Has been cancelled
ci / e2e-turbo-admin__e2e__document-view (push) Has been cancelled
ci / e2e-turbo-admin__e2e__general (push) Has been cancelled
ci / e2e-turbo-admin__e2e__list-view (push) Has been cancelled
ci / e2e-turbo-auth (push) Has been cancelled
ci / e2e-turbo-auth-basic (push) Has been cancelled
ci / e2e-turbo-bulk-edit (push) Has been cancelled
ci / e2e-turbo-field-error-states (push) Has been cancelled
ci / e2e-turbo-fields-relationship (push) Has been cancelled
ci / e2e-turbo-fields__collections__Array (push) Has been cancelled
ci / e2e-turbo-fields__collections__Blocks#config.blockreferences.ts (push) Has been cancelled
ci / e2e-turbo-fields__collections__Blocks (push) Has been cancelled
ci / e2e-turbo-fields__collections__Checkbox (push) Has been cancelled
ci / e2e-turbo-fields__collections__Collapsible (push) Has been cancelled
ci / e2e-turbo-fields__collections__ConditionalLogic (push) Has been cancelled
ci / e2e-turbo-fields__collections__CustomID (push) Has been cancelled
ci / e2e-turbo-fields__collections__Date (push) Has been cancelled
ci / e2e-turbo-fields__collections__Email (push) Has been cancelled
ci / e2e-turbo-fields__collections__Indexed (push) Has been cancelled
ci / e2e-turbo-fields__collections__JSON (push) Has been cancelled
ci / e2e-turbo-fields__collections__Number (push) Has been cancelled
ci / e2e-turbo-fields__collections__Point (push) Has been cancelled
ci / e2e-turbo-fields__collections__Radio (push) Has been cancelled
ci / e2e-turbo-fields__collections__Relationship (push) Has been cancelled
ci / e2e-turbo-fields__collections__Row (push) Has been cancelled
ci / e2e-turbo-fields__collections__Select (push) Has been cancelled
ci / e2e-turbo-fields__collections__Tabs (push) Has been cancelled
ci / e2e-turbo-fields__collections__Tabs2 (push) Has been cancelled
ci / e2e-turbo-fields__collections__Text (push) Has been cancelled
ci / e2e-turbo-fields__collections__UI (push) Has been cancelled
ci / e2e-turbo-fields__collections__Upload (push) Has been cancelled
ci / e2e-turbo-folders (push) Has been cancelled
ci / e2e-turbo-form-state (push) Has been cancelled
ci / e2e-turbo-group-by (push) Has been cancelled
ci / e2e-turbo-hooks (push) Has been cancelled
ci / e2e-turbo-i18n (push) Has been cancelled
ci / e2e-turbo-joins (push) Has been cancelled
ci / e2e-turbo-lexical__collections__LexicalHeadingFeature (push) Has been cancelled
ci / e2e-turbo-lexical__collections__LexicalJSXConverter (push) Has been cancelled
ci / e2e-turbo-lexical__collections__LexicalLinkFeature (push) Has been cancelled
ci / e2e-turbo-lexical__collections__Lexical__e2e__blocks#config.blockreferences.ts (push) Has been cancelled
ci / e2e-turbo-lexical__collections__Lexical__e2e__blocks (push) Has been cancelled
ci / e2e-turbo-lexical__collections__Lexical__e2e__main (push) Has been cancelled
ci / e2e-turbo-lexical__collections__OnDemandForm (push) Has been cancelled
ci / e2e-turbo-lexical__collections__RichText (push) Has been cancelled
ci / e2e-turbo-lexical__collections___LexicalFullyFeatured (push) Has been cancelled
ci / e2e-turbo-lexical__collections___LexicalFullyFeatured__db (push) Has been cancelled
ci / e2e-turbo-live-preview (push) Has been cancelled
ci / e2e-turbo-localization (push) Has been cancelled
ci / e2e-turbo-locked-documents (push) Has been cancelled
ci / e2e-turbo-plugin-cloud-storage (push) Has been cancelled
ci / e2e-turbo-plugin-form-builder (push) Has been cancelled
ci / e2e-turbo-plugin-import-export (push) Has been cancelled
ci / e2e-turbo-plugin-multi-tenant (push) Has been cancelled
ci / e2e-turbo-plugin-nested-docs (push) Has been cancelled
ci / e2e-turbo-plugin-seo (push) Has been cancelled
ci / e2e-turbo-query-presets (push) Has been cancelled
ci / e2e-turbo-sort (push) Has been cancelled
ci / e2e-turbo-trash (push) Has been cancelled
ci / e2e-turbo-uploads (push) Has been cancelled
ci / e2e-turbo-versions (push) Has been cancelled
ci / build-template-blank-mongodb (push) Has been cancelled
ci / build-template-website-mongodb (push) Has been cancelled
ci / build-template-with-payload-cloud-mongodb (push) Has been cancelled
ci / build-template-with-vercel-mongodb-mongodb (push) Has been cancelled
ci / build-template-plugin- (push) Has been cancelled
ci / build-template-with-postgres-postgres (push) Has been cancelled
ci / build-template-with-vercel-postgres-postgres (push) Has been cancelled
ci / tests-type-generation (push) Has been cancelled
ci / All Green (push) Has been cancelled
ci / Publish Canary (push) Has been cancelled
ci / analyze (push) Has been cancelled
publish-prerelease / publish-prerelease-${{ github.ref_name }}-${{ github.sha }} (push) Has been cancelled
lock-issues / lock_issues (push) Has been cancelled
stale / stale (push) Has been cancelled
audit-dependencies / audit (push) Has been cancelled
activity-notifications / run (push) Has been cancelled
chore: Update code to new repo
2025-10-08 23:27:45 +02:00

1144 lines
29 KiB
TypeScript

import type { Payload } from 'tbsh-cms'
import {
handleMessage as handleMessageImport,
type LivePreviewMessageEvent,
mergeData as mergeDataImport,
} from '@tabshiftcms/live-preview'
import path from 'path'
import { getFileByPath } from 'tbsh-cms'
import { fileURLToPath } from 'url'
import type { NextRESTClient } from '../helpers/NextRESTClient.js'
import type { Media, Page, Post, Tenant } from './payload-types.js'
import { pagesSlug, postsSlug, tenantsSlug } from './shared.js'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
let payload: Payload
let restClient: NextRESTClient
import type { CollectionPopulationRequestHandler } from '../../packages/live-preview/src/types.js'
import { initPayloadInt } from '../helpers/initPayloadInt.js'
const requestHandler: CollectionPopulationRequestHandler = ({ data, endpoint }) => {
const url = `/${endpoint}`
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'X-Payload-HTTP-Method-Override': 'GET',
}
return restClient.POST(url as any, {
body: JSON.stringify(data),
credentials: 'include',
headers,
})
}
describe('Collections - Live Preview', () => {
const serverURL: string = 'http://localhost:3000'
let testPost: Post
let tenant: Tenant
let media: Media
let handleMessage: (
args: Omit<Parameters<typeof handleMessageImport>[0], 'requestHandler' | 'serverURL'>,
) => Promise<Record<string, any>> = handleMessageImport as any
let mergeData: (
args: Omit<Parameters<typeof mergeDataImport<any>>[0], 'requestHandler' | 'serverURL'>,
) => Promise<Record<string, any>> = mergeDataImport as any
beforeAll(async () => {
;({ payload, restClient } = await initPayloadInt(dirname))
mergeData = async (args) => {
return await mergeDataImport({
...args,
serverURL,
requestHandler,
})
}
handleMessage = async (args) => {
const newArgs: Parameters<typeof handleMessageImport>[0] = args as any
newArgs.requestHandler = requestHandler
newArgs.serverURL = serverURL
if (newArgs.event) {
// @ts-expect-error need to overwrite serverURL
newArgs.event.origin = serverURL
}
return await handleMessageImport(newArgs)
}
tenant = await payload.create({
collection: tenantsSlug,
data: {
title: 'Tenant 1',
clientURL: 'http://localhost:3000',
},
})
// Create image
const filePath = path.resolve(dirname, './seed/image-1.jpg')
const file = (await getFileByPath(filePath))!
file.name = 'image-1.jpg'
media = await payload.create({
collection: 'media',
data: {
alt: 'Image 1',
},
file,
})
testPost = await payload.create({
collection: postsSlug,
data: {
slug: 'post-1',
title: 'Test Post',
tenant: tenant.id,
localizedTitle: 'Test Post',
hero: {
type: 'highImpact',
media: media.id,
},
},
})
})
afterAll(async () => {
await payload.destroy()
})
it('handles `postMessage`', async () => {
const handledMessage = await handleMessage({
depth: 1,
event: {
data: {
collectionSlug: postsSlug,
data: {
title: 'Test Page (Changed)',
},
type: 'payload-live-preview',
},
} as MessageEvent as LivePreviewMessageEvent<Page>,
initialData: {
title: 'Test Page',
id: 1 as number | string,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
hero: {
type: 'highImpact',
},
slug: 'testPage',
} as Page,
})
expect(handledMessage.title).toEqual('Test Page (Changed)')
})
async function createPageWithInitialData(initialData: Partial<Page>) {
await payload.db.deleteOne({
collection: pagesSlug,
where: {
slug: {
equals: 'testPage',
},
},
returning: false,
})
const page = await payload.create({
collection: pagesSlug,
depth: 0,
data: {
...initialData,
slug: 'testPage',
} as Page,
})
return page
}
it('— strings - merges data', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
const mergedData = await mergeData({
depth: 1,
incomingData: {
...initialData,
title: 'Test Page (Changed)',
},
initialData,
collectionSlug: pagesSlug,
})
expect(mergedData.title).toEqual('Test Page (Changed)')
})
it('— strings - merges localized data', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
const mergedData = await mergeData({
depth: 1,
incomingData: {
...initialData,
localizedTitle: 'Test Page (Changed)',
},
initialData,
collectionSlug: pagesSlug,
})
expect(mergedData.localizedTitle).toEqual('Test Page (Changed)')
})
it('— arrays - can clear all rows', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
arrayOfRelationships: [
{
id: '123',
relationshipInArrayMonoHasOne: testPost.id,
},
],
})
const mergedData = await mergeData({
depth: 1,
incomingData: {
...initialData,
arrayOfRelationships: [],
},
initialData,
collectionSlug: pagesSlug,
})
expect(mergedData.arrayOfRelationships).toEqual([])
// do the same but with arrayOfRelationships: 0
const mergedData2 = await mergeData({
depth: 1,
incomingData: {
...initialData,
arrayOfRelationships: 0, // this is how form state represents an empty array
},
initialData,
collectionSlug: pagesSlug,
})
expect(mergedData2.arrayOfRelationships).toEqual([])
})
it('— uploads - adds and removes media', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
// Add upload
const mergedData = await mergeData({
depth: 1,
incomingData: {
...initialData,
hero: {
type: 'highImpact',
media: media.id,
},
},
initialData,
collectionSlug: pagesSlug,
})
expect(mergedData.hero.media).toMatchObject(media)
// Add upload
const mergedDataWithoutUpload = await mergeData({
depth: 1,
incomingData: {
...mergedData,
hero: {
type: 'highImpact',
media: null,
},
},
initialData: mergedData,
collectionSlug: pagesSlug,
})
expect(mergedDataWithoutUpload.hero.media).toBeFalsy()
})
it('— uploads - populates within Slate rich text editor', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
// Add upload
const merge1 = await mergeData({
depth: 1,
incomingData: {
...initialData,
richTextSlate: [
{
type: 'upload',
relationTo: 'media',
value: {
id: media.id,
},
},
],
},
initialData,
collectionSlug: pagesSlug,
})
expect(merge1.richTextSlate).toHaveLength(1)
expect(merge1.richTextSlate[0].value).toMatchObject(media)
// Remove upload
const merge2 = await mergeData({
depth: 1,
incomingData: {
...merge1,
richTextSlate: [
{
type: 'paragraph',
children: [
{
text: 'Hello, world!',
},
],
},
],
},
initialData,
collectionSlug: pagesSlug,
})
expect(merge2.richTextSlate).toHaveLength(1)
expect(merge2.richTextSlate[0].value).toBeFalsy()
expect(merge2.richTextSlate[0].type).toEqual('paragraph')
})
it('— uploads - populates within Lexical rich text editor', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
// Add upload
const merge1 = await mergeData({
depth: 1,
collectionSlug: pagesSlug,
incomingData: {
...initialData,
richTextLexical: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
children: [
{
detail: 0,
format: 0,
mode: 'normal',
style: '',
text: 'Hello, world!',
type: 'text',
version: 1,
},
],
direction: 'ltr',
format: '',
indent: 0,
type: 'paragraph',
version: 1,
},
{
format: '',
type: 'upload',
relationTo: 'media',
version: 1,
value: media.id,
},
],
direction: 'ltr',
},
},
},
initialData,
})
expect(merge1.richTextLexical.root.children).toHaveLength(2)
expect(merge1.richTextLexical.root.children[1].value).toMatchObject(media)
// Remove upload
const merge2 = await mergeData({
depth: 1,
collectionSlug: pagesSlug,
incomingData: {
...merge1,
richTextLexical: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
children: [
{
detail: 0,
format: 0,
mode: 'normal',
style: '',
text: 'Hello, world!',
type: 'text',
version: 1,
},
],
direction: 'ltr',
format: '',
indent: 0,
type: 'paragraph',
version: 1,
},
],
direction: 'ltr',
},
},
},
initialData,
})
expect(merge2.richTextLexical.root.children).toHaveLength(1)
expect(merge2.richTextLexical.root.children[0].value).toBeFalsy()
expect(merge2.richTextLexical.root.children[0].type).toEqual('paragraph')
})
it('— relationships - populates monomorphic has one relationships', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
const merge1 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
...initialData,
relationshipMonoHasOne: testPost.id,
},
initialData,
})
expect(merge1.relationshipMonoHasOne).toMatchObject(testPost)
})
it('— relationships - populates monomorphic has many relationships', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
const merge1 = await mergeData({
depth: 2,
incomingData: {
...initialData,
relationshipMonoHasMany: [testPost.id],
},
initialData,
collectionSlug: pagesSlug,
})
expect(merge1.relationshipMonoHasMany).toMatchObject([testPost])
})
it('— relationships - populates polymorphic has one relationships', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
const merge1 = await mergeData({
depth: 2,
incomingData: {
...initialData,
relationshipPolyHasOne: { value: testPost.id, relationTo: postsSlug },
},
initialData,
collectionSlug: pagesSlug,
})
expect(merge1.relationshipPolyHasOne).toMatchObject({
value: testPost,
relationTo: postsSlug,
})
})
it('— relationships - populates polymorphic has many relationships', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
const merge1 = await mergeData({
depth: 2,
incomingData: {
...initialData,
relationshipPolyHasMany: [{ value: testPost.id, relationTo: postsSlug }],
},
initialData,
collectionSlug: pagesSlug,
})
expect(merge1.relationshipPolyHasMany).toMatchObject([
{ value: testPost, relationTo: postsSlug },
])
})
it('— relationships - can clear relationships', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
relationshipMonoHasOne: testPost.id,
relationshipMonoHasMany: [testPost.id],
relationshipPolyHasOne: { value: testPost.id, relationTo: postsSlug },
relationshipPolyHasMany: [{ value: testPost.id, relationTo: postsSlug }],
})
const merge2 = await mergeData({
depth: 1,
incomingData: {
relationshipMonoHasOne: null,
relationshipMonoHasMany: [],
relationshipPolyHasOne: null,
relationshipPolyHasMany: [],
},
initialData,
collectionSlug: pagesSlug,
})
expect(merge2.relationshipMonoHasOne).toBeFalsy()
expect(merge2.relationshipMonoHasMany).toEqual([])
expect(merge2.relationshipPolyHasOne).toBeFalsy()
expect(merge2.relationshipPolyHasMany).toEqual([])
})
it('— relationships - populates within tabs', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
const merge1 = await mergeData({
depth: 2,
incomingData: {
...initialData,
tab: {
relationshipInTab: testPost.id,
},
},
initialData,
collectionSlug: pagesSlug,
})
expect(merge1.tab.relationshipInTab).toMatchObject(testPost)
})
it('— relationships - populates within arrays', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
const merge1 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
...initialData,
arrayOfRelationships: [
{
id: '123',
relationshipInArrayMonoHasOne: testPost.id,
relationshipInArrayMonoHasMany: [testPost.id],
relationshipInArrayPolyHasOne: {
value: testPost.id,
relationTo: postsSlug,
},
relationshipInArrayPolyHasMany: [
{
value: testPost.id,
relationTo: postsSlug,
},
],
},
],
},
initialData,
})
expect(merge1.arrayOfRelationships).toHaveLength(1)
expect(merge1.arrayOfRelationships).toMatchObject([
{
id: '123',
relationshipInArrayMonoHasOne: testPost,
relationshipInArrayMonoHasMany: [testPost],
relationshipInArrayPolyHasOne: {
value: testPost,
relationTo: postsSlug,
},
relationshipInArrayPolyHasMany: [
{
value: testPost,
relationTo: postsSlug,
},
],
},
])
// Add a new block before the populated one, then check to see that the relationship is still populated
const merge2 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
...merge1,
arrayOfRelationships: [
{
id: '456',
relationshipInArrayMonoHasOne: undefined,
relationshipInArrayMonoHasMany: [],
relationshipInArrayPolyHasOne: undefined,
relationshipInArrayPolyHasMany: [],
},
{
id: '123',
relationshipInArrayMonoHasOne: testPost.id,
relationshipInArrayMonoHasMany: [testPost.id],
relationshipInArrayPolyHasOne: {
value: testPost.id,
relationTo: postsSlug,
},
relationshipInArrayPolyHasMany: [
{
value: testPost.id,
relationTo: postsSlug,
},
],
},
],
},
initialData: merge1,
})
expect(merge2.arrayOfRelationships).toHaveLength(2)
expect(merge2.arrayOfRelationships).toMatchObject([
{
id: '456',
},
{
id: '123',
relationshipInArrayMonoHasOne: testPost,
relationshipInArrayMonoHasMany: [testPost],
relationshipInArrayPolyHasOne: {
value: testPost,
relationTo: postsSlug,
},
relationshipInArrayPolyHasMany: [
{
value: testPost,
relationTo: postsSlug,
},
],
},
])
})
it('— relationships - populates within Slate rich text editor', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
// Add a relationship and an upload
const merge1 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
...initialData,
richTextSlate: [
{
children: [
{
text: ' ',
},
],
relationTo: postsSlug,
type: 'relationship',
value: {
id: testPost.id,
},
},
{
type: 'paragraph',
children: [
{
text: '',
},
],
},
{
children: [
{
text: '',
},
],
relationTo: 'media',
type: 'upload',
value: {
id: media.id,
},
},
],
},
initialData,
})
expect(merge1.richTextSlate).toHaveLength(3)
expect(merge1.richTextSlate[0].type).toEqual('relationship')
expect(merge1.richTextSlate[0].value).toMatchObject(testPost)
expect(merge1.richTextSlate[1].type).toEqual('paragraph')
expect(merge1.richTextSlate[2].type).toEqual('upload')
expect(merge1.richTextSlate[2].value).toMatchObject(media)
// Add a new node between the relationship and the upload
const merge2 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
...merge1,
richTextSlate: [
{
children: [
{
text: ' ',
},
],
relationTo: postsSlug,
type: 'relationship',
value: {
id: testPost.id,
},
},
{
type: 'paragraph',
children: [
{
text: '',
},
],
},
{
type: 'paragraph',
children: [
{
text: '',
},
],
},
{
children: [
{
text: '',
},
],
relationTo: 'media',
type: 'upload',
value: {
id: media.id,
},
},
],
},
initialData: merge1,
})
expect(merge2.richTextSlate).toHaveLength(4)
expect(merge2.richTextSlate[0].type).toEqual('relationship')
expect(merge2.richTextSlate[0].value).toMatchObject(testPost)
expect(merge2.richTextSlate[1].type).toEqual('paragraph')
expect(merge2.richTextSlate[2].type).toEqual('paragraph')
expect(merge2.richTextSlate[3].type).toEqual('upload')
expect(merge2.richTextSlate[3].value).toMatchObject(media)
})
async function lexicalTest(fieldName: string) {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
// Add a relationship
const merge1 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
...initialData,
[fieldName]: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
format: '',
type: 'relationship',
version: 1,
relationTo: postsSlug,
value: {
id: testPost.id,
},
},
{
children: [],
direction: null,
format: '',
indent: 0,
type: 'paragraph',
version: 1,
},
{
format: '',
type: 'upload',
version: 1,
fields: null,
relationTo: 'media',
value: {
id: media.id,
},
},
],
direction: null,
},
},
},
initialData,
})
expect(merge1[fieldName].root.children).toHaveLength(3)
expect(merge1[fieldName].root.children[0].type).toEqual('relationship')
expect(merge1[fieldName].root.children[0].value).toMatchObject(testPost)
expect(merge1[fieldName].root.children[1].type).toEqual('paragraph')
expect(merge1[fieldName].root.children[2].type).toEqual('upload')
expect(merge1[fieldName].root.children[2].value).toMatchObject(media)
// Add a node before the populated one
const merge2 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
...merge1,
[fieldName]: {
root: {
type: 'root',
format: '',
indent: 0,
version: 1,
children: [
{
format: '',
type: 'relationship',
version: 1,
relationTo: postsSlug,
value: {
id: testPost.id,
},
},
{
children: [],
direction: null,
format: '',
indent: 0,
type: 'paragraph',
version: 1,
},
{
children: [],
direction: null,
format: '',
indent: 0,
type: 'paragraph',
version: 1,
},
{
format: '',
type: 'upload',
version: 1,
fields: null,
relationTo: 'media',
value: {
id: media.id,
},
},
],
direction: null,
},
},
},
initialData: merge1,
})
expect(merge2[fieldName].root.children).toHaveLength(4)
expect(merge2[fieldName].root.children[0].type).toEqual('relationship')
expect(merge2[fieldName].root.children[0].value).toMatchObject(testPost)
expect(merge2[fieldName].root.children[1].type).toEqual('paragraph')
expect(merge2[fieldName].root.children[2].type).toEqual('paragraph')
expect(merge2[fieldName].root.children[3].type).toEqual('upload')
expect(merge2[fieldName].root.children[3].value).toMatchObject(media)
}
// eslint-disable-next-line jest/expect-expect
it('— relationships - populates within Lexical rich text editor', async () => {
await lexicalTest('richTextLexical')
})
// eslint-disable-next-line jest/expect-expect
it('— relationships - populates within Localized Lexical rich text editor', async () => {
await lexicalTest('richTextLexicalLocalized')
})
it('— relationships - re-populates externally updated relationships', async () => {
const initialData = await createPageWithInitialData({
title: 'Test Page',
})
// Populate the relationships
const merge1 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
title: 'Test Page',
relationshipMonoHasOne: testPost.id,
relationshipMonoHasMany: [testPost.id],
relationshipPolyHasOne: { value: testPost.id, relationTo: postsSlug },
relationshipPolyHasMany: [{ value: testPost.id, relationTo: postsSlug }],
},
initialData,
})
expect(merge1.relationshipMonoHasOne).toMatchObject(testPost)
expect(merge1.relationshipMonoHasMany).toMatchObject([testPost])
expect(merge1.relationshipPolyHasOne).toMatchObject({
value: testPost,
relationTo: postsSlug,
})
expect(merge1.relationshipPolyHasMany).toMatchObject([
{ value: testPost, relationTo: postsSlug },
])
// Update the test post
const updatedTestPost = await payload.update({
collection: postsSlug,
id: testPost.id,
data: {
title: 'Test Post (Recently Updated)',
},
})
const merge2 = await mergeData({
depth: 2,
collectionSlug: pagesSlug,
incomingData: {
title: 'Test Page',
relationshipMonoHasOne: testPost.id,
relationshipMonoHasMany: [testPost.id],
relationshipPolyHasOne: { value: testPost.id, relationTo: postsSlug },
relationshipPolyHasMany: [{ value: testPost.id, relationTo: postsSlug }],
},
initialData: merge1,
})
expect(merge2.relationshipMonoHasOne).toMatchObject(updatedTestPost)
expect(merge2.relationshipMonoHasMany).toMatchObject([updatedTestPost])
expect(merge2.relationshipPolyHasOne).toMatchObject({
value: updatedTestPost,
relationTo: postsSlug,
})
expect(merge2.relationshipPolyHasMany).toMatchObject([
{ value: updatedTestPost, relationTo: postsSlug },
])
})
it('— relationships - populates localized relationships', async () => {
const post = await payload.create({
collection: postsSlug,
data: {
title: 'Test Post',
slug: 'test-post',
hero: {
type: 'highImpact',
media: media.id,
},
localizedTitle: 'Test Post Spanish',
},
locale: 'es',
})
await payload.update({
id: post.id,
locale: 'en',
collection: postsSlug,
data: {
localizedTitle: 'Test Post English',
},
})
const page = await payload.create({
collection: pagesSlug,
data: {
title: 'Test Page',
hero: { type: 'none' },
slug: 'testpage',
},
locale: 'en',
})
const initialData = await createPageWithInitialData({
...page,
relationToLocalized: post.id,
})
// Populate the relationships
const merge1 = await mergeData({
depth: 1,
incomingData: {
...initialData,
relationToLocalized: post.id,
},
initialData,
locale: 'es',
collectionSlug: pagesSlug,
})
expect(merge1.relationToLocalized).toHaveProperty('localizedTitle', 'Test Post Spanish')
})
it('— blocks - adds, reorders, and removes blocks', async () => {
const block1ID = '123'
const block2ID = '456'
const initialData = await createPageWithInitialData({
title: 'Test Page',
layout: [
{
blockType: 'cta',
id: block1ID,
richText: [
{
type: 'paragraph',
text: 'Block 1 (Position 1)',
},
],
},
{
blockType: 'cta',
id: block2ID,
richText: [
{
type: 'paragraph',
text: 'Block 2 (Position 2)',
},
],
},
],
})
// Reorder the blocks
const merge1 = await mergeData({
depth: 1,
collectionSlug: pagesSlug,
incomingData: {
...initialData,
layout: [
{
blockType: 'cta',
id: block2ID,
richText: [
{
type: 'paragraph',
text: 'Block 2 (Position 1)',
},
],
},
{
blockType: 'cta',
id: block1ID,
richText: [
{
type: 'paragraph',
text: 'Block 1 (Position 2)',
},
],
},
],
},
initialData,
})
// Check that the blocks have been reordered
expect(merge1.layout).toHaveLength(2)
expect(merge1.layout[0].id).toEqual(block2ID)
expect(merge1.layout[1].id).toEqual(block1ID)
expect(merge1.layout[0].richText[0].text).toEqual('Block 2 (Position 1)')
expect(merge1.layout[1].richText[0].text).toEqual('Block 1 (Position 2)')
// Remove a block
const merge2 = await mergeData({
depth: 1,
collectionSlug: pagesSlug,
incomingData: {
...initialData,
layout: [
{
blockType: 'cta',
id: block2ID,
richText: [
{
type: 'paragraph',
text: 'Block 2 (Position 1)',
},
],
},
],
},
initialData,
})
// Check that the block has been removed
expect(merge2.layout).toHaveLength(1)
expect(merge2.layout[0].id).toEqual(block2ID)
expect(merge2.layout[0].richText[0].text).toEqual('Block 2 (Position 1)')
// Remove the last block to ensure that all blocks can be cleared
const merge3 = await mergeData({
depth: 1,
collectionSlug: pagesSlug,
incomingData: {
...initialData,
layout: [],
},
initialData,
})
// Check that the block has been removed
expect(merge3.layout).toHaveLength(0)
})
})