chore(richtext-lexical): add failing e2e test which ensures sub-richtext blocks work as intended
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import type { LexicalBlock } from '@payloadcms/richtext-lexical'
|
||||
import type { ArrayField } from 'payload/types'
|
||||
|
||||
import { BlocksFeature } from '@payloadcms/richtext-lexical'
|
||||
import { lexicalEditor } from '@payloadcms/richtext-lexical'
|
||||
|
||||
import { textFieldsSlug } from '../Text/shared.js'
|
||||
@@ -113,12 +114,35 @@ export const RadioButtonsBlock: LexicalBlock = {
|
||||
export const RichTextBlock: LexicalBlock = {
|
||||
fields: [
|
||||
{
|
||||
name: 'richText',
|
||||
name: 'richTextField',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor(),
|
||||
editor: lexicalEditor({
|
||||
features: ({ defaultFeatures }) => [
|
||||
...defaultFeatures,
|
||||
BlocksFeature({
|
||||
blocks: [
|
||||
{
|
||||
fields: [
|
||||
{
|
||||
name: 'subRichTextField',
|
||||
type: 'richText',
|
||||
editor: lexicalEditor({}),
|
||||
},
|
||||
{
|
||||
name: 'subUploadField',
|
||||
type: 'upload',
|
||||
relationTo: 'uploads',
|
||||
},
|
||||
],
|
||||
slug: 'lexicalAndUploadBlock',
|
||||
},
|
||||
],
|
||||
}),
|
||||
],
|
||||
}),
|
||||
},
|
||||
],
|
||||
slug: 'richText',
|
||||
slug: 'richTextBlock',
|
||||
}
|
||||
|
||||
export const UploadAndRichTextBlock: LexicalBlock = {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -110,7 +110,7 @@ export function generateLexicalRichText() {
|
||||
version: 2,
|
||||
fields: {
|
||||
id: '65298b1ddb4ef8c744a7faab',
|
||||
richText: {
|
||||
richTextField: {
|
||||
root: {
|
||||
type: 'root',
|
||||
format: '',
|
||||
@@ -149,7 +149,7 @@ export function generateLexicalRichText() {
|
||||
},
|
||||
},
|
||||
blockName: 'Block Node, with RichText Field, with Relationship Node',
|
||||
blockType: 'richText',
|
||||
blockType: 'richTextBlock',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user