@@ -7,6 +7,7 @@ import type {
7
7
import type { BrowserContext , Locator , Page } from '@playwright/test'
8
8
9
9
import { expect , test } from '@playwright/test'
10
+ import { except } from 'drizzle-orm/mysql-core'
10
11
import path from 'path'
11
12
import { wait } from 'payload/shared'
12
13
import { fileURLToPath } from 'url'
@@ -28,7 +29,6 @@ import { RESTClient } from '../../../../../helpers/rest.js'
28
29
import { POLL_TOPASS_TIMEOUT , TEST_TIMEOUT_LONG } from '../../../../../playwright.config.js'
29
30
import { lexicalFieldsSlug } from '../../../../slugs.js'
30
31
import { lexicalDocData } from '../../data.js'
31
- import { except } from 'drizzle-orm/mysql-core'
32
32
33
33
const filename = fileURLToPath ( import . meta. url )
34
34
const currentFolder = path . dirname ( filename )
@@ -937,6 +937,135 @@ describe('lexicalMain', () => {
937
937
} )
938
938
} )
939
939
940
+ test ( 'ensure internal links can be created' , async ( ) => {
941
+ await navigateToLexicalFields ( )
942
+ const richTextField = page . locator ( '.rich-text-lexical' ) . first ( )
943
+ await richTextField . scrollIntoViewIfNeeded ( )
944
+ await expect ( richTextField ) . toBeVisible ( )
945
+ // Wait until there at least 10 blocks visible in that richtext field - thus wait for it to be fully loaded
946
+ await expect ( page . locator ( '.rich-text-lexical' ) . nth ( 2 ) . locator ( '.lexical-block' ) ) . toHaveCount (
947
+ 10 ,
948
+ )
949
+ await expect ( page . locator ( '.shimmer-effect' ) ) . toHaveCount ( 0 )
950
+
951
+ const paragraph = richTextField . locator ( '.LexicalEditorTheme__paragraph' ) . first ( )
952
+ await paragraph . scrollIntoViewIfNeeded ( )
953
+ await expect ( paragraph ) . toBeVisible ( )
954
+ /**
955
+ * Type some text
956
+ */
957
+ await paragraph . click ( )
958
+ await page . keyboard . type ( 'Link' )
959
+
960
+ // Select "Link" by pressing shift + arrow left
961
+ for ( let i = 0 ; i < 4 ; i ++ ) {
962
+ await page . keyboard . press ( 'Shift+ArrowLeft' )
963
+ }
964
+ // Ensure inline toolbar appeared
965
+ const inlineToolbar = page . locator ( '.inline-toolbar-popup' )
966
+ await expect ( inlineToolbar ) . toBeVisible ( )
967
+
968
+ const linkButton = inlineToolbar . locator ( '.toolbar-popup__button-link' )
969
+ await expect ( linkButton ) . toBeVisible ( )
970
+ await linkButton . click ( )
971
+
972
+ /**
973
+ * Link Drawer
974
+ */
975
+ const linkDrawer = page . locator ( 'dialog[id^=drawer_1_lexical-rich-text-link-]' ) . first ( ) // IDs starting with drawer_1_lexical-rich-text-link- (there's some other symbol after the underscore)
976
+ await expect ( linkDrawer ) . toBeVisible ( )
977
+ await wait ( 500 )
978
+
979
+ // Check if has text "Internal Link"
980
+ await expect ( linkDrawer . locator ( '.radio-input' ) . nth ( 1 ) ) . toContainText ( 'Internal Link' )
981
+
982
+ // Get radio button for internal link with text "Internal Link"
983
+ const radioInternalLink = linkDrawer
984
+ . locator ( '.radio-input' )
985
+ . nth ( 1 )
986
+ . locator ( '.radio-input__styled-radio' )
987
+
988
+ await radioInternalLink . click ( )
989
+
990
+ const internalLinkSelect = linkDrawer
991
+ . locator ( '#field-doc .rs__control .value-container' )
992
+ . first ( )
993
+ await internalLinkSelect . click ( )
994
+
995
+ await expect ( linkDrawer . locator ( '.rs__option' ) . nth ( 0 ) ) . toBeVisible ( )
996
+ await expect ( linkDrawer . locator ( '.rs__option' ) . nth ( 0 ) ) . toContainText ( 'Rich Text' ) // Link to itself - that way we can also test if depth 0 works
997
+ await linkDrawer . locator ( '.rs__option' ) . nth ( 0 ) . click ( )
998
+ await expect ( internalLinkSelect ) . toContainText ( 'Rich Text' )
999
+
1000
+ await linkDrawer . locator ( 'button' ) . getByText ( 'Save' ) . first ( ) . click ( )
1001
+ await expect ( linkDrawer ) . toBeHidden ( )
1002
+ await wait ( 1500 )
1003
+
1004
+ await saveDocAndAssert ( page )
1005
+
1006
+ // Check if the text is bold. It's a self-relationship, so no need to follow relationship
1007
+ await expect ( async ( ) => {
1008
+ const lexicalDoc : LexicalField = (
1009
+ await payload . find ( {
1010
+ collection : lexicalFieldsSlug ,
1011
+ depth : 0 ,
1012
+ overrideAccess : true ,
1013
+ where : {
1014
+ title : {
1015
+ equals : lexicalDocData . title ,
1016
+ } ,
1017
+ } ,
1018
+ } )
1019
+ ) . docs [ 0 ] as never
1020
+
1021
+ const lexicalField : SerializedEditorState =
1022
+ lexicalDoc . lexicalRootEditor as SerializedEditorState
1023
+
1024
+ const firstParagraph : SerializedParagraphNode = lexicalField . root
1025
+ . children [ 0 ] as SerializedParagraphNode
1026
+
1027
+ expect ( firstParagraph . children ) . toHaveLength ( 1 )
1028
+
1029
+ const linkNode = firstParagraph . children [ 0 ] as SerializedLinkNode
1030
+ expect ( linkNode ?. fields ?. doc ?. relationTo ) . toBe ( 'lexical-fields' )
1031
+ // Expect to be string
1032
+ expect ( typeof linkNode ?. fields ?. doc ?. value ) . toBe ( 'string' )
1033
+ } ) . toPass ( {
1034
+ timeout : POLL_TOPASS_TIMEOUT ,
1035
+ } )
1036
+
1037
+ // Now check if depth 1 works
1038
+ await expect ( async ( ) => {
1039
+ const lexicalDoc : LexicalField = (
1040
+ await payload . find ( {
1041
+ collection : lexicalFieldsSlug ,
1042
+ depth : 1 ,
1043
+ overrideAccess : true ,
1044
+ where : {
1045
+ title : {
1046
+ equals : lexicalDocData . title ,
1047
+ } ,
1048
+ } ,
1049
+ } )
1050
+ ) . docs [ 0 ] as never
1051
+
1052
+ const lexicalField : SerializedEditorState =
1053
+ lexicalDoc . lexicalRootEditor as SerializedEditorState
1054
+
1055
+ const firstParagraph : SerializedParagraphNode = lexicalField . root
1056
+ . children [ 0 ] as SerializedParagraphNode
1057
+
1058
+ expect ( firstParagraph . children ) . toHaveLength ( 1 )
1059
+
1060
+ const linkNode = firstParagraph . children [ 0 ] as SerializedLinkNode
1061
+ expect ( linkNode ?. fields ?. doc ?. relationTo ) . toBe ( 'lexical-fields' )
1062
+ expect ( typeof linkNode ?. fields ?. doc ?. value ) . toBe ( 'object' )
1063
+ expect ( typeof ( linkNode ?. fields ?. doc ?. value as Record < string , unknown > ) ?. id ) . toBe ( 'string' )
1064
+ } ) . toPass ( {
1065
+ timeout : POLL_TOPASS_TIMEOUT ,
1066
+ } )
1067
+ } )
1068
+
940
1069
test ( 'ensure link drawer displays fields if document does not have `create` permission' , async ( ) => {
941
1070
await navigateToLexicalFields ( true , 'lexical-access-control' )
942
1071
const richTextField = page . locator ( '.rich-text-lexical' ) . first ( )
@@ -1243,20 +1372,20 @@ describe('lexicalMain', () => {
1243
1372
1244
1373
const relationshipInput = page . locator ( '.drawer__content .rs__input' ) . first ( )
1245
1374
await expect ( relationshipInput ) . toBeVisible ( )
1246
- await page . getByRole ( 'heading' , { name : 'Lexical Fields' } )
1375
+ page . getByRole ( 'heading' , { name : 'Lexical Fields' } )
1247
1376
await relationshipInput . click ( )
1248
- const user = await page . getByRole ( 'option' , { name : 'User' } )
1377
+ const user = page . getByRole ( 'option' , { name : 'User' } )
1249
1378
await user . click ( )
1250
1379
1251
1380
const userListDrawer = page
1252
1381
. locator ( 'div' )
1253
1382
. filter ( { hasText : / ^ U s e r $ / } )
1254
1383
. first ( )
1255
1384
await expect ( userListDrawer ) . toBeVisible ( )
1256
- await page . getByRole ( 'heading' , { name : 'Users' } )
1257
- const button = await page . getByLabel ( 'Add new User' )
1385
+ page . getByRole ( 'heading' , { name : 'Users' } )
1386
+ const button = page . getByLabel ( 'Add new User' )
1258
1387
await button . click ( )
1259
- await page . getByText ( 'Creating new User' )
1388
+ page . getByText ( 'Creating new User' )
1260
1389
} )
1261
1390
1262
1391
test ( 'ensure links can created from clipboard and deleted' , async ( ) => {
@@ -1276,7 +1405,7 @@ describe('lexicalMain', () => {
1276
1405
1277
1406
await lastParagraph . click ( )
1278
1407
1279
- page . context ( ) . grantPermissions ( [ 'clipboard-read' , 'clipboard-write' ] )
1408
+ await page . context ( ) . grantPermissions ( [ 'clipboard-read' , 'clipboard-write' ] )
1280
1409
1281
1410
// Paste in a link copied from a html page
1282
1411
const link = '<a href="https://www.google.com">Google</a>'
@@ -1305,17 +1434,17 @@ describe('lexicalMain', () => {
1305
1434
await expect ( linkInput ) . toBeVisible ( )
1306
1435
1307
1436
const linkInInput = linkInput . locator ( 'a' ) . first ( )
1308
- expect ( linkInInput ) . toBeVisible ( )
1437
+ await expect ( linkInInput ) . toBeVisible ( )
1309
1438
1310
- expect ( linkInInput ) . toContainText ( 'https://www.google.com/' )
1439
+ await expect ( linkInInput ) . toContainText ( 'https://www.google.com/' )
1311
1440
await expect ( linkInInput ) . toHaveAttribute ( 'href' , 'https://www.google.com/' )
1312
1441
1313
1442
// Click remove button
1314
1443
const removeButton = linkInput . locator ( '.link-trash' ) . first ( )
1315
1444
await removeButton . click ( )
1316
1445
1317
1446
// Expect link to be removed
1318
- await expect ( linkNode ) . not . toBeVisible ( )
1447
+ await expect ( linkNode ) . toBeHidden ( )
1319
1448
} )
1320
1449
1321
1450
describe ( 'localization' , ( ) => {
@@ -1355,39 +1484,39 @@ describe('lexicalMain', () => {
1355
1484
1356
1485
const textNode = page . getByText ( 'Upload Node:' , { exact : true } )
1357
1486
await textNode . click ( )
1358
- await expect ( decoratorLocator ) . not . toBeVisible ( )
1487
+ await expect ( decoratorLocator ) . toBeHidden ( )
1359
1488
1360
1489
const closeTagInMultiSelect = page
1361
1490
. getByRole ( 'button' , { name : 'payload.jpg Edit payload.jpg' } )
1362
1491
. getByLabel ( 'Remove' )
1363
1492
await closeTagInMultiSelect . click ( )
1364
- await expect ( decoratorLocator ) . not . toBeVisible ( )
1493
+ await expect ( decoratorLocator ) . toBeHidden ( )
1365
1494
1366
1495
const labelInsideCollapsableBody = page . locator ( 'label' ) . getByText ( 'Sub Blocks' )
1367
1496
await labelInsideCollapsableBody . click ( )
1368
1497
await expectInsideSelectedDecorator ( labelInsideCollapsableBody )
1369
1498
1370
1499
const textNodeInNestedEditor = page . getByText ( 'Some text below relationship node 1' )
1371
1500
await textNodeInNestedEditor . click ( )
1372
- await expect ( decoratorLocator ) . not . toBeVisible ( )
1501
+ await expect ( decoratorLocator ) . toBeHidden ( )
1373
1502
1374
1503
await page . getByRole ( 'button' , { name : 'Tab2' } ) . click ( )
1375
- await expect ( decoratorLocator ) . not . toBeVisible ( )
1504
+ await expect ( decoratorLocator ) . toBeHidden ( )
1376
1505
1377
1506
const labelInsideCollapsableBody2 = page . getByText ( 'Text2' )
1378
1507
await labelInsideCollapsableBody2 . click ( )
1379
1508
await expectInsideSelectedDecorator ( labelInsideCollapsableBody2 )
1380
1509
1381
1510
// TEST DELETE!
1382
1511
await page . keyboard . press ( 'Backspace' )
1383
- await expect ( labelInsideCollapsableBody2 ) . not . toBeVisible ( )
1512
+ await expect ( labelInsideCollapsableBody2 ) . toBeHidden ( )
1384
1513
1385
1514
const monacoLabel = page . locator ( 'label' ) . getByText ( 'Code' )
1386
1515
await monacoLabel . click ( )
1387
1516
await expectInsideSelectedDecorator ( monacoLabel )
1388
1517
1389
1518
const monacoCode = page . getByText ( 'Some code' )
1390
1519
await monacoCode . click ( )
1391
- await expect ( decoratorLocator ) . not . toBeVisible ( )
1520
+ await expect ( decoratorLocator ) . toBeHidden ( )
1392
1521
} )
1393
1522
} )
0 commit comments