|
|
|
|
@@ -7,9 +7,10 @@ import { expect, test } from '@playwright/test'
|
|
|
|
|
import { initPayloadE2ENoConfig } from 'helpers/initPayloadE2ENoConfig.js'
|
|
|
|
|
import { reInitializeDB } from 'helpers/reInit.js'
|
|
|
|
|
import path from 'path'
|
|
|
|
|
import { wait } from 'payload/utilities'
|
|
|
|
|
import { fileURLToPath } from 'url'
|
|
|
|
|
|
|
|
|
|
import type { Config, LexicalField } from '../../payload-types.js'
|
|
|
|
|
import type { Config, LexicalField, Upload } from '../../payload-types.js'
|
|
|
|
|
|
|
|
|
|
import { initPageConsoleErrorCatch, saveDocAndAssert } from '../../../helpers.js'
|
|
|
|
|
import { AdminUrlUtil } from '../../../helpers/adminUrlUtil.js'
|
|
|
|
|
@@ -147,6 +148,7 @@ describe('lexical', () => {
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
@@ -216,6 +218,7 @@ describe('lexical', () => {
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
@@ -366,6 +369,7 @@ describe('lexical', () => {
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
@@ -375,8 +379,11 @@ describe('lexical', () => {
|
|
|
|
|
).docs[0] as never
|
|
|
|
|
|
|
|
|
|
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
|
|
|
|
|
|
|
|
|
const blockNode: SerializedBlockNode = lexicalField.root.children[4] as SerializedBlockNode
|
|
|
|
|
const textNodeInBlockNodeRichText = blockNode.fields.richText.root.children[1].children[0]
|
|
|
|
|
|
|
|
|
|
const textNodeInBlockNodeRichText =
|
|
|
|
|
blockNode.fields.richTextField.root.children[1].children[0]
|
|
|
|
|
|
|
|
|
|
expect(textNodeInBlockNodeRichText.text).toBe(
|
|
|
|
|
'Some text below relationship node 1 inserted text',
|
|
|
|
|
@@ -443,6 +450,7 @@ describe('lexical', () => {
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
@@ -453,7 +461,7 @@ describe('lexical', () => {
|
|
|
|
|
|
|
|
|
|
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
|
|
|
|
const blockNode: SerializedBlockNode = lexicalField.root.children[4] as SerializedBlockNode
|
|
|
|
|
const paragraphNodeInBlockNodeRichText = blockNode.fields.richText.root.children[1]
|
|
|
|
|
const paragraphNodeInBlockNodeRichText = blockNode.fields.richTextField.root.children[1]
|
|
|
|
|
|
|
|
|
|
expect(paragraphNodeInBlockNodeRichText.children).toHaveLength(2)
|
|
|
|
|
|
|
|
|
|
@@ -534,6 +542,7 @@ describe('lexical', () => {
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
@@ -680,6 +689,7 @@ describe('lexical', () => {
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
@@ -702,6 +712,189 @@ describe('lexical', () => {
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Big test which tests a bunch of things: Creation of blocks via slash commands, creation of deeply nested sub-lexical-block fields via slash commands, properly populated deeply nested fields within those
|
|
|
|
|
test('ensure creation of a lexical, lexical-field-block, which contains another lexical, lexical-and-upload-field-block, works and that the sub-upload field is properly populated', async () => {
|
|
|
|
|
await navigateToLexicalFields()
|
|
|
|
|
const richTextField = page.locator('.rich-text-lexical').nth(1) // second
|
|
|
|
|
await richTextField.scrollIntoViewIfNeeded()
|
|
|
|
|
await expect(richTextField).toBeVisible()
|
|
|
|
|
|
|
|
|
|
const lastParagraph = richTextField.locator('p').last()
|
|
|
|
|
await lastParagraph.scrollIntoViewIfNeeded()
|
|
|
|
|
await expect(lastParagraph).toBeVisible()
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Create new sub-block
|
|
|
|
|
*/
|
|
|
|
|
// type / to open the slash menu
|
|
|
|
|
await lastParagraph.click()
|
|
|
|
|
await page.keyboard.press('/')
|
|
|
|
|
await page.keyboard.type('Rich')
|
|
|
|
|
|
|
|
|
|
// Create Rich Text Block
|
|
|
|
|
const slashMenuPopover = page.locator('#slash-menu .slash-menu-popup')
|
|
|
|
|
await expect(slashMenuPopover).toBeVisible()
|
|
|
|
|
|
|
|
|
|
// Click 1. Button and ensure it's the Rich Text block creation button (it should be! Otherwise, sorting wouldn't work)
|
|
|
|
|
const richTextBlockSelectButton = slashMenuPopover.locator('button').first()
|
|
|
|
|
await expect(richTextBlockSelectButton).toBeVisible()
|
|
|
|
|
await expect(richTextBlockSelectButton).toContainText('Rich Text')
|
|
|
|
|
await richTextBlockSelectButton.click()
|
|
|
|
|
await expect(slashMenuPopover).toBeHidden()
|
|
|
|
|
|
|
|
|
|
const newRichTextBlock = richTextField
|
|
|
|
|
.locator('.lexical-block:not(.lexical-block .lexical-block)')
|
|
|
|
|
.last() // The :not(.lexical-block .lexical-block) makes sure this does not select sub-blocks
|
|
|
|
|
await newRichTextBlock.scrollIntoViewIfNeeded()
|
|
|
|
|
await expect(newRichTextBlock).toBeVisible()
|
|
|
|
|
|
|
|
|
|
// Ensure that sub-editor is empty
|
|
|
|
|
const newRichTextEditorParagraph = newRichTextBlock.locator('p').first()
|
|
|
|
|
await expect(newRichTextEditorParagraph).toBeVisible()
|
|
|
|
|
await expect(newRichTextEditorParagraph).toHaveText('')
|
|
|
|
|
|
|
|
|
|
await newRichTextEditorParagraph.click()
|
|
|
|
|
await page.keyboard.press('/')
|
|
|
|
|
await page.keyboard.type('Lexical')
|
|
|
|
|
await expect(slashMenuPopover).toBeVisible()
|
|
|
|
|
// Click 1. Button and ensure it's the Lexical And Upload block creation button (it should be! Otherwise, sorting wouldn't work)
|
|
|
|
|
const lexicalAndUploadBlockSelectButton = slashMenuPopover.locator('button').first()
|
|
|
|
|
await expect(lexicalAndUploadBlockSelectButton).toBeVisible()
|
|
|
|
|
await expect(lexicalAndUploadBlockSelectButton).toContainText('Lexical And Upload')
|
|
|
|
|
await lexicalAndUploadBlockSelectButton.click()
|
|
|
|
|
await expect(slashMenuPopover).toBeHidden()
|
|
|
|
|
|
|
|
|
|
// Ensure that sub-editor is created
|
|
|
|
|
const newSubLexicalAndUploadBlock = newRichTextBlock.locator('.lexical-block').first()
|
|
|
|
|
await newSubLexicalAndUploadBlock.scrollIntoViewIfNeeded()
|
|
|
|
|
await expect(newSubLexicalAndUploadBlock).toBeVisible()
|
|
|
|
|
|
|
|
|
|
// Type in newSubLexicalAndUploadBlock
|
|
|
|
|
const paragraphInSubEditor = newSubLexicalAndUploadBlock.locator('p').first()
|
|
|
|
|
await expect(paragraphInSubEditor).toBeVisible()
|
|
|
|
|
await paragraphInSubEditor.click()
|
|
|
|
|
await page.keyboard.type('Some subText')
|
|
|
|
|
// Upload something
|
|
|
|
|
const chooseExistingUploadButton = newSubLexicalAndUploadBlock
|
|
|
|
|
.locator('.upload__toggler.list-drawer__toggler')
|
|
|
|
|
.first()
|
|
|
|
|
await expect(chooseExistingUploadButton).toBeVisible()
|
|
|
|
|
await chooseExistingUploadButton.click()
|
|
|
|
|
await wait(500) // wait for drawer form state to initialize (it's a flake)
|
|
|
|
|
const uploadListDrawer = page.locator('dialog[id^=list-drawer_1_]').first() // IDs starting with list-drawer_1_ (there's some other symbol after the underscore)
|
|
|
|
|
await expect(uploadListDrawer).toBeVisible()
|
|
|
|
|
// find button which has a span with text "payload.jpg" and click it in playwright
|
|
|
|
|
const uploadButton = uploadListDrawer.locator('button').getByText('payload.jpg').first()
|
|
|
|
|
await expect(uploadButton).toBeVisible()
|
|
|
|
|
await uploadButton.click()
|
|
|
|
|
await expect(uploadListDrawer).toBeHidden()
|
|
|
|
|
// Check if the upload is there
|
|
|
|
|
await expect(
|
|
|
|
|
newSubLexicalAndUploadBlock.locator('.field-type.upload .file-meta__url a'),
|
|
|
|
|
).toHaveText('payload.jpg')
|
|
|
|
|
// save document and assert
|
|
|
|
|
await saveDocAndAssert(page)
|
|
|
|
|
await expect(
|
|
|
|
|
newSubLexicalAndUploadBlock.locator('.field-type.upload .file-meta__url a'),
|
|
|
|
|
).toHaveText('payload.jpg')
|
|
|
|
|
await expect(paragraphInSubEditor).toHaveText('Some subText')
|
|
|
|
|
|
|
|
|
|
// reload page and assert again
|
|
|
|
|
await page.reload()
|
|
|
|
|
await expect(
|
|
|
|
|
newSubLexicalAndUploadBlock.locator('.field-type.upload .file-meta__url a'),
|
|
|
|
|
).toHaveText('payload.jpg')
|
|
|
|
|
await expect(paragraphInSubEditor).toHaveText('Some subText')
|
|
|
|
|
|
|
|
|
|
// Check if the API result is populated correctly - Depth 0
|
|
|
|
|
await expect(async () => {
|
|
|
|
|
const lexicalDoc: LexicalField = (
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
).docs[0] as never
|
|
|
|
|
|
|
|
|
|
const uploadDoc: Upload = (
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: 'uploads',
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
filename: {
|
|
|
|
|
equals: 'payload.jpg',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
).docs[0] as never
|
|
|
|
|
|
|
|
|
|
const lexicalField: SerializedEditorState = lexicalDoc.lexicalWithBlocks
|
|
|
|
|
const richTextBlock: SerializedBlockNode = lexicalField.root
|
|
|
|
|
.children[12] as SerializedBlockNode
|
|
|
|
|
const subRichTextBlock: SerializedBlockNode = richTextBlock.fields.richTextField.root
|
|
|
|
|
.children[1] as SerializedBlockNode // index 0 and 2 are paragraphs created by default around the block node when a new block is added via slash command
|
|
|
|
|
|
|
|
|
|
const subSubRichTextField = subRichTextBlock.fields.subRichTextField
|
|
|
|
|
const subSubUploadField = subRichTextBlock.fields.subUploadField
|
|
|
|
|
|
|
|
|
|
expect(subSubRichTextField.root.children[0].children[0].text).toBe('Some subText')
|
|
|
|
|
expect(subSubUploadField).toBe(uploadDoc.id)
|
|
|
|
|
}).toPass({
|
|
|
|
|
timeout: POLL_TOPASS_TIMEOUT,
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// Check if the API result is populated correctly - Depth 1
|
|
|
|
|
await expect(async () => {
|
|
|
|
|
// Now with depth 1
|
|
|
|
|
const lexicalDocDepth1: LexicalField = (
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 1,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
).docs[0] as never
|
|
|
|
|
|
|
|
|
|
const uploadDoc: Upload = (
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: 'uploads',
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
filename: {
|
|
|
|
|
equals: 'payload.jpg',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
).docs[0] as never
|
|
|
|
|
|
|
|
|
|
const lexicalField2: SerializedEditorState = lexicalDocDepth1.lexicalWithBlocks
|
|
|
|
|
const richTextBlock2: SerializedBlockNode = lexicalField2.root
|
|
|
|
|
.children[12] as SerializedBlockNode
|
|
|
|
|
const subRichTextBlock2: SerializedBlockNode = richTextBlock2.fields.richTextField.root
|
|
|
|
|
.children[1] as SerializedBlockNode // index 0 and 2 are paragraphs created by default around the block node when a new block is added via slash command
|
|
|
|
|
|
|
|
|
|
const subSubRichTextField2 = subRichTextBlock2.fields.subRichTextField
|
|
|
|
|
const subSubUploadField2 = subRichTextBlock2.fields.subUploadField
|
|
|
|
|
|
|
|
|
|
expect(subSubRichTextField2.root.children[0].children[0].text).toBe('Some subText')
|
|
|
|
|
expect(subSubUploadField2.id).toBe(uploadDoc.id)
|
|
|
|
|
expect(subSubUploadField2.filename).toBe(uploadDoc.filename)
|
|
|
|
|
}).toPass({
|
|
|
|
|
timeout: POLL_TOPASS_TIMEOUT,
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('should allow changing values of two different radio button blocks independently', async () => {
|
|
|
|
|
// This test ensures that https://github.com/payloadcms/payload/issues/3911 does not happen again
|
|
|
|
|
|
|
|
|
|
@@ -750,6 +943,7 @@ describe('lexical', () => {
|
|
|
|
|
await payload.find({
|
|
|
|
|
collection: lexicalFieldsSlug,
|
|
|
|
|
depth: 0,
|
|
|
|
|
overrideAccess: true,
|
|
|
|
|
where: {
|
|
|
|
|
title: {
|
|
|
|
|
equals: lexicalDocData.title,
|
|
|
|
|
|