From af4a95ea28b792df27af558a086460b71c140d0d Mon Sep 17 00:00:00 2001 From: Per-Kristian Nordnes Date: Tue, 27 Feb 2024 17:11:49 +0100 Subject: [PATCH] test(playwright-ct): add range decoration test for PT-input --- .../PortableText/RangeDecoration.spec.tsx | 65 +++++++++++++ .../PortableText/RangeDecorationStory.tsx | 91 +++++++++++++++++++ 2 files changed, 156 insertions(+) create mode 100644 packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/RangeDecoration.spec.tsx create mode 100644 packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/RangeDecorationStory.tsx diff --git a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/RangeDecoration.spec.tsx b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/RangeDecoration.spec.tsx new file mode 100644 index 000000000000..dbc8afcef20b --- /dev/null +++ b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/RangeDecoration.spec.tsx @@ -0,0 +1,65 @@ +import {expect, test} from '@playwright/experimental-ct-react' +import {type SanityDocument} from 'sanity' + +import {testHelpers} from '../../../utils/testHelpers' +import {type DecorationData, RangeDecorationStory} from './RangeDecorationStory' + +const document: SanityDocument = { + _id: '123', + _type: 'test', + _createdAt: new Date().toISOString(), + _updatedAt: new Date().toISOString(), + _rev: '123', + body: [ + { + _type: 'block', + _key: 'a', + children: [{_type: 'span', _key: 'a1', text: 'Hello there world'}], + markDefs: [], + }, + { + _type: 'block', + _key: 'b', + children: [{_type: 'span', _key: 'b1', text: "It's a beautiful day on planet earth"}], + markDefs: [], + }, + ], +} + +// Since we can't pass React components to our story, we'll just pass the selection data, +// and use a test component inside the Story to render the range decoration. +const decorationData: DecorationData[] = [ + { + word: 'there', + selection: { + anchor: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 6}, + focus: {path: [{_key: 'a'}, 'children', {_key: 'a1'}], offset: 11}, + }, + }, +] + +test.describe('Portable Text Input', () => { + test.describe('Range Decoration', () => { + // test.only('Manual testing can be performed with this test', async ({mount, page}) => { + // await mount() + // await page.waitForTimeout(360000) + // }) + test(`Draws range decoration around our selection`, async ({mount, page}) => { + await mount() + await expect(page.getByTestId('range-decoration')).toHaveText('there') + }) + + test(`Let's us move the range according to our edits`, async ({mount, page}) => { + const {getFocusedPortableTextEditor, insertPortableText} = testHelpers({page}) + + await mount() + + const $pte = await getFocusedPortableTextEditor('field-body') + + await insertPortableText('123 ', $pte) + await expect($pte).toHaveText("123 Hello there worldIt's a beautiful day on planet earth") + // Assert that the same word is decorated after the edit + await expect(page.getByTestId('range-decoration')).toHaveText('there') + }) + }) +}) diff --git a/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/RangeDecorationStory.tsx b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/RangeDecorationStory.tsx new file mode 100644 index 000000000000..f06f486692c4 --- /dev/null +++ b/packages/sanity/playwright-ct/tests/formBuilder/inputs/PortableText/RangeDecorationStory.tsx @@ -0,0 +1,91 @@ +/* eslint-disable max-nested-callbacks */ +import {type EditorSelection, type RangeDecoration} from '@sanity/portable-text-editor' +import {defineArrayMember, defineField, defineType, type SanityDocument} from '@sanity/types' +import {type PropsWithChildren, useEffect, useMemo, useState} from 'react' +import {type InputProps, PortableTextInput, type PortableTextInputProps} from 'sanity' + +import {TestForm} from '../../utils/TestForm' +import {TestWrapper} from '../../utils/TestWrapper' + +export type DecorationData = {selection: EditorSelection; word: string} + +const RangeDecorationTestComponent = (props: PropsWithChildren) => { + return ( + + {props.children} + + ) +} + +const CustomPortableTextInput = ( + props: PortableTextInputProps & {decorationData?: DecorationData[]}, +) => { + const {decorationData} = props + const [rangeDecorationsState, setRangeDecorationsState] = useState([]) + + useEffect(() => { + setRangeDecorationsState( + (decorationData?.map((data) => ({ + component: RangeDecorationTestComponent, + selection: data.selection, + onMoved: (movedProps) => { + const {newSelection, rangeDecoration} = movedProps + setRangeDecorationsState((prev) => + prev.map((decoration) => + data.selection === rangeDecoration.selection + ? {...decoration, selection: newSelection} + : decoration, + ), + ) + }, + payload: {word: data.word}, + })) || []) as RangeDecoration[], + ) + }, [decorationData]) + + return +} + +export function RangeDecorationStory({ + document, + decorationData, +}: { + document?: SanityDocument + decorationData?: DecorationData[] +}) { + const schemaTypes = useMemo( + () => [ + defineType({ + type: 'document', + name: 'test', + title: 'Test', + fields: [ + defineField({ + type: 'array', + name: 'body', + of: [ + defineArrayMember({ + type: 'block', + }), + ], + components: { + input: (props: InputProps) => ( + + ), + }, + }), + ], + }), + ], + [decorationData], + ) + + return ( + + + + ) +}