From 9d2038fa389305b8fb58de843ce8b10a878e20e9 Mon Sep 17 00:00:00 2001 From: Joe Anderson Date: Sat, 11 Nov 2023 12:57:08 +0000 Subject: [PATCH] Add e2e tests for export modal --- client/components/Editor.tsx | 6 +- .../exportModalSections/ExportHTMLSection.tsx | 2 + client/e2e/basic-editor.spec.ts | 4 +- client/e2e/export-html.spec.ts | 75 +++++++++++++++++++ client/e2e/slate.ts | 8 +- client/e2e/utils.ts | 26 +++++-- ...ate-playwright.ts => slate-playwright.tsx} | 4 +- 7 files changed, 108 insertions(+), 17 deletions(-) create mode 100644 client/e2e/export-html.spec.ts rename client/lib/editor/{slate-playwright.ts => slate-playwright.tsx} (88%) diff --git a/client/components/Editor.tsx b/client/components/Editor.tsx index 407ddb98..5bf00309 100644 --- a/client/components/Editor.tsx +++ b/client/components/Editor.tsx @@ -29,7 +29,7 @@ import { useSaveSelection, } from '~/lib/editor/restoreSelection'; import { SelectionToolbar } from '~/lib/editor/SelectionToolbar'; -import { useSlatePlaywrightUtils } from '~/lib/editor/slate-playwright'; +import { SlatePlaywrightEffects } from '~/lib/editor/slate-playwright'; import { useInitialValue } from '~/lib/editor/useInitialValue'; import { useNavigateAwayOnDelete } from '~/lib/editor/useNavigateAwayOnDelete'; import { useSyncDocument } from '~/lib/editor/useSyncDocument'; @@ -208,6 +208,8 @@ export const Editor = ({ clientId, initialDocument }: EditorProps) => { debouncedUpdateBody={debouncedUpdateBody} /> + + {!isReadOnly && } ), @@ -354,8 +356,6 @@ const WithEditorState = ({ useSaveSelection(initialDocument.id, editor); - useSlatePlaywrightUtils(); - const [forceUpdateBodyKey, forceUpdateBody] = useReducer((x) => x + 1, 0); useGlobalEvent('s3File:uploadComplete', () => forceUpdateBody()); diff --git a/client/components/exportModalSections/ExportHTMLSection.tsx b/client/components/exportModalSections/ExportHTMLSection.tsx index c10cc734..bf41d56c 100644 --- a/client/components/exportModalSections/ExportHTMLSection.tsx +++ b/client/components/exportModalSections/ExportHTMLSection.tsx @@ -13,6 +13,7 @@ import { import { setLocalStorage, useLocalStorage } from '~/lib/browserStorage'; import { copyText } from '~/lib/copyText'; import { getHtmlForExport } from '~/lib/editor/getHtmlForExport'; +import { SlatePlaywrightEffects } from '~/lib/editor/slate-playwright'; import { useEffectAfterFirst } from '~/lib/useEffectAfterFirst'; import CopyIcon from '~/components/icons/CopyIcon'; import DownloadIcon from '~/components/icons/DownloadIcon'; @@ -87,6 +88,7 @@ export const ExportHTMLSection = ({ initialValue={initialValue} setIsModified={setIsModified} /> + diff --git a/client/e2e/basic-editor.spec.ts b/client/e2e/basic-editor.spec.ts index 4eb03015..e58c0c3d 100644 --- a/client/e2e/basic-editor.spec.ts +++ b/client/e2e/basic-editor.spec.ts @@ -23,7 +23,7 @@ test.describe('Basic editor', () => { }); test('save document to server and reload', async ({ page }) => { - const editable = await getEditable(page); + const editable = getEditable(page); const editorHandle = await getEditorHandle(page, editable); await clickAtPath(page, editorHandle, [0]); @@ -33,7 +33,7 @@ test.describe('Basic editor', () => { await expectUpToDate(page); await page.reload(); - await expect(await getEditable(page)).toContainText('Hello World!'); + await expect(getEditable(page)).toContainText('Hello World!'); }); test('create blocks with markdown and remove with backspace', async ({ diff --git a/client/e2e/export-html.spec.ts b/client/e2e/export-html.spec.ts new file mode 100644 index 00000000..bbb070f6 --- /dev/null +++ b/client/e2e/export-html.spec.ts @@ -0,0 +1,75 @@ +import { expect, Page, test } from '@playwright/test'; +import { clickAtPath, getEditable, getEditorHandle } from './slate'; +import { + createDocument, + createProject, + logIn, + openExportHTMLSection, +} from './utils'; + +const getDocumentTitleCheckbox = (page: Page) => + page.getByLabel('Include document title'); + +const getExportEditable = (page: Page) => getEditable(page.locator('pre')); +// const getExportEditorHandle = (page: Page) => getEditorHandle(page, getExportEditable(page)); + +const getUndoButton = (page: Page) => + page.getByRole('button', { name: 'Undo changes' }); + +test.describe('Export HTML modal', () => { + test.beforeEach(async ({ page }) => { + await logIn(page); + await createProject(page); + await createDocument(page, 'My document'); + + const editorHandle = await getEditorHandle(page); + await clickAtPath(page, editorHandle, [0]); + await page.keyboard.type('Hello World!'); + + await openExportHTMLSection(page); + await page.waitForTimeout(200); + }); + + test('shows the HTML for the document', async ({ page }) => { + const editable = getExportEditable(page); + await expect(editable).toContainText('

Hello World!

'); + }); + + // Too flaky + // test('undo changes', async ({ page }) => { + // const undoButton = getUndoButton(page); + // await expect(undoButton).not.toBeVisible(); + + // const editable = getExportEditable(page); + // const editorHandle = await getExportEditorHandle(page); + // await clickAtPath(page, editorHandle, [0]); + // await page.keyboard.type('MODIFIED'); + // await expect(editable).toContainText('MODIFIED'); + // await expect(undoButton).toBeVisible(); + + // await undoButton.click(); + // await expect(editable).not.toContainText('MODIFIED'); + // await expect(undoButton).not.toBeVisible(); + // }); + + test('includes the document title by default', async ({ page }) => { + const checkbox = getDocumentTitleCheckbox(page); + expect(await checkbox.isChecked()).toBe(true); + + const editable = getExportEditable(page); + await expect(editable).toContainText('

My document

'); + }); + + test('unticking the checkbox removes the document title', async ({ + page, + }) => { + const checkbox = getDocumentTitleCheckbox(page); + await checkbox.uncheck(); + + const editable = getExportEditable(page); + await expect(editable).not.toContainText('

My document

'); + + const undoButton = getUndoButton(page); + await expect(undoButton).not.toBeVisible(); + }); +}); diff --git a/client/e2e/slate.ts b/client/e2e/slate.ts index 00c5a6cf..eecae9d1 100644 --- a/client/e2e/slate.ts +++ b/client/e2e/slate.ts @@ -4,16 +4,14 @@ import { Path, Range } from 'slate'; import '../lib/globals.d'; -export const getEditable = async (page: Page) => - page.locator('[data-slate-editor]'); +export const getEditable = (context: Page | Locator) => + context.locator('[data-slate-editor]'); export const getEditorHandle = async ( page: Page, editable?: Locator ): Promise> => { - const editableHandle = await ( - editable || (await getEditable(page)) - ).elementHandle(); + const editableHandle = await (editable || getEditable(page)).elementHandle(); return page.evaluateHandle((editable) => { const editor = window.playwrightUtils.EDITABLE_TO_EDITOR.get( diff --git a/client/e2e/utils.ts b/client/e2e/utils.ts index d5f5ef5a..a7788fbf 100644 --- a/client/e2e/utils.ts +++ b/client/e2e/utils.ts @@ -45,13 +45,17 @@ export const createDocument = async (page: Page, title?: string) => { await fillDocumentTitle(page, title); } - const editable = await getEditable(page); + const editable = getEditable(page); await expect(editable).toHaveText('Write something...'); }; +export const openDocumentMenu = async (page: Page) => { + await page.getByLabel('Document menu').click(); +}; + export const expectSyncState = async (page: Page, state: string) => { await page.waitForTimeout(500); - await page.getByLabel('Document menu').click(); + await openDocumentMenu(page); await expect(page.getByText(state)).toBeVisible(); await page.keyboard.press('Escape'); }; @@ -59,6 +63,16 @@ export const expectSyncState = async (page: Page, state: string) => { export const expectUpToDate = async (page: Page) => expectSyncState(page, 'Up to date'); +export const openExportModal = async (page: Page) => { + await openDocumentMenu(page); + await page.getByText('Export document').click(); +}; + +export const openExportHTMLSection = async (page: Page) => { + await openExportModal(page); + await page.getByRole('tab', { name: 'Export HTML' }).click(); +}; + export type CreateDataTransfer = { filePath: string; fileName: string; @@ -106,6 +120,10 @@ export const dragAndDropFile = async ( await target.dispatchEvent('drop', eventOptions); }; +export const openSearchModal = async (page: Page) => { + await locateSidebar(page).getByText('Search').click(); +}; + export const openAccountModal = async (page: Page) => { await page.getByLabel('Account').hover(); await page.getByText('Account info').click(); @@ -115,7 +133,3 @@ export const openFileStorageSection = async (page: Page) => { await openAccountModal(page); await page.getByText('File storage').click(); }; - -export const openSearchModal = async (page: Page) => { - await locateSidebar(page).getByText('Search').click(); -}; diff --git a/client/lib/editor/slate-playwright.ts b/client/lib/editor/slate-playwright.tsx similarity index 88% rename from client/lib/editor/slate-playwright.ts rename to client/lib/editor/slate-playwright.tsx index fef87fec..2c5fee01 100644 --- a/client/lib/editor/slate-playwright.ts +++ b/client/lib/editor/slate-playwright.tsx @@ -3,7 +3,7 @@ import { getNode, PlateEditor, toDOMNode, useEditorRef } from '@udecode/plate'; const EDITABLE_TO_EDITOR = new WeakMap(); -export const useSlatePlaywrightUtils = () => { +export const SlatePlaywrightEffects = () => { const editor = useEditorRef(); useEffect(() => { @@ -14,6 +14,8 @@ export const useSlatePlaywrightUtils = () => { EDITABLE_TO_EDITOR.delete(editable); }; }, [editor]); + + return null; }; window.playwrightUtils = {