From bc9f0b08741fef7732d45f715ff0d294647837f2 Mon Sep 17 00:00:00 2001 From: Nathan Date: Thu, 14 Aug 2025 14:40:00 +0800 Subject: [PATCH] test: edit page --- cypress.config.ts | 2 +- cypress/e2e/page/create-delete-page.cy.ts | 82 +--- cypress/e2e/page/edit-page.cy.ts | Bin 0 -> 7009 bytes cypress/e2e/user/user.cy.ts | 11 +- cypress/support/auth-utils.ts | 2 +- cypress/support/commands.ts | 2 + cypress/support/page-utils.ts | 483 ++++++++++++++++++++++ package.json | 3 +- src/components/editor/Editable.tsx | 1 + 9 files changed, 513 insertions(+), 73 deletions(-) create mode 100644 cypress/e2e/page/edit-page.cy.ts create mode 100644 cypress/support/page-utils.ts diff --git a/cypress.config.ts b/cypress.config.ts index fb380741..d84a2129 100644 --- a/cypress.config.ts +++ b/cypress.config.ts @@ -62,7 +62,7 @@ export default defineConfig({ }, chromeWebSecurity: false, retries: { - runMode: 1, + runMode: 0, // Configure retry attempts for `cypress open` // Default is 0 openMode: 0, diff --git a/cypress/e2e/page/create-delete-page.cy.ts b/cypress/e2e/page/create-delete-page.cy.ts index 0a7e3563..0b94af1b 100644 --- a/cypress/e2e/page/create-delete-page.cy.ts +++ b/cypress/e2e/page/create-delete-page.cy.ts @@ -1,5 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; import { AuthTestUtils } from '../../support/auth-utils'; +import { PageUtils } from '../../support/page-utils'; describe('Page Create and Delete Tests', () => { const AF_BASE_URL = Cypress.env('AF_BASE_URL'); @@ -18,9 +19,6 @@ describe('Page Create and Delete Tests', () => { }); beforeEach(() => { - // Ensure viewport is set to MacBook Pro size for each test - cy.viewport(1440, 900); - // Generate unique test data for each test testEmail = generateRandomEmail(); testPageName = 'e2e test-create page'; @@ -47,98 +45,54 @@ describe('Page Create and Delete Tests', () => { cy.wait(3000); // Step 2: Create a new page - // Click on the New Page button using data-testid - cy.get('[data-testid="new-page-button"]').click(); + PageUtils.clickNewPageButton(); cy.task('log', 'Clicked New Page button'); // Wait for the modal to open cy.wait(1000); // Select the first space in the modal - cy.get('[role="dialog"]').should('be.visible').within(() => { - // Click on the first space item - cy.get('[data-testid="space-item"]').first().click(); - - // Click the Add button - cy.contains('button', 'Add').click(); - }); + PageUtils.selectFirstSpaceInModal(); // Wait for the page to be created and modal to open cy.wait(2000); - // The page modal should be open now with the page title input - // Clear any default text and enter the page name - cy.get('[data-testid="page-title-input"]', { timeout: 10000 }) - .should('be.visible') - .focus() - .clear() - .type(testPageName); - - // Press Escape to save the title and close the modal - cy.get('[data-testid="page-title-input"]').type('{esc}'); + // Enter the page name + cy.task('log', `Entering page title: ${testPageName}`); + PageUtils.enterPageTitle(testPageName); + + // Save the title and close the modal + PageUtils.savePageTitle(); cy.wait(1000); cy.task('log', `Created page with title: ${testPageName}`); // Step 3: Reload and verify the page exists cy.reload(); - cy.wait(3000); + PageUtils.waitForPageLoad(3000); - // Find and click on the first space to expand it (usually "General") - cy.get('[data-testid="space-name"]').first().parent().parent().click(); + // Expand the first space to see its pages + PageUtils.expandSpace(); cy.wait(1000); - // Now verify the page exists under the space with exact title - cy.get('[data-testid="page-name"]').contains('e2e test-create page').should('exist'); + // Verify the page exists + PageUtils.verifyPageExists('e2e test-create page'); cy.task('log', `Verified page exists after reload: ${testPageName}`); // Step 4: Delete the page - // First, open the page by clicking on it - cy.get('[data-testid="page-name"]').contains('e2e test-create page').click(); - cy.wait(1000); - - // Click on the more actions button - cy.get('[data-testid="page-more-actions"]').click(); - cy.wait(500); - - // Click on delete button - cy.get('[data-testid="delete-page-button"]').click(); - cy.wait(500); - - // Check if there's a confirmation modal (for published pages) - cy.get('body').then($body => { - if ($body.find('[data-testid="delete-page-confirm-modal"]').length > 0) { - // If confirmation modal exists, click Delete button - cy.get('[data-testid="delete-page-confirm-modal"]').within(() => { - cy.contains('button', 'Delete').click(); - }); - } - }); - - cy.wait(2000); + PageUtils.deletePageByName('e2e test-create page'); cy.task('log', `Deleted page: ${testPageName}`); // Step 5: Reload and verify the page is gone cy.reload(); - cy.wait(3000); + PageUtils.waitForPageLoad(3000); // Expand the space again to check if page is gone - cy.get('[data-testid="space-name"]').first().parent().parent().click(); + PageUtils.expandSpace(); cy.wait(1000); // Verify the page no longer exists - cy.get('body').then($body => { - // Check if any page-name elements exist - if ($body.find('[data-testid="page-name"]').length > 0) { - // Verify none of them contain our test page name - cy.get('[data-testid="page-name"]').each(($el) => { - cy.wrap($el).should('not.contain', 'e2e test-create page'); - }); - } else { - // No pages exist, which is also valid - cy.get('[data-testid="page-name"]').should('not.exist'); - } - }); + PageUtils.verifyPageNotExists('e2e test-create page'); cy.task('log', `Verified page is gone after reload: ${testPageName}`); }); }); diff --git a/cypress/e2e/page/edit-page.cy.ts b/cypress/e2e/page/edit-page.cy.ts new file mode 100644 index 0000000000000000000000000000000000000000..8e5093641134774284b0c19ab045c661c6588db8 GIT binary patch literal 7009 zcmb_gTXWmS74EZNI(h9wXQQ|VsY;~exM?ayZY)JfJmbi&CD#v*8!dq)xfY1UU>A}& zihf$BKdFDA-`NX@8##7cj%}L2x$QY;zjKj`Tos1hvFj&{YgU#r!T;{rv``Bc(a-4c zfeb{?N;AI@+PpC`)fLrfG{zrY<|Jy&L9<&DL7npzpNUOeIo?-X4-%o{LQcdWB0hGS zNW)04j&|5!$c~<}y9W%PSY_IAUWTN0kyyjV=7N-j?Q|NRyT|T^X{u>PMzvX#8P1VhEB-N!;oC?DX z4q=%Nm^OvXX3bZ0+B3co{clfHW<+Mf%XjY&AJ`L3#8egHl*jWy-8tl-N*|2uF@4~D z5=eu7P)Fjiu-nU_!!DVpCIV zUog0bt?-mL(#)C8MJ##B;yEvPY($|C*yrDV@$e7-c(gqjwz%VkZWCrnlrrGml;)Z>Plp733Mm;}&d2SO2Id)kc7nwk=xrYnwe_Hn^ zxRUM>qSAYnh+mpPr01$k6Q<=XV`aX>;zB?-<2D0!m`w(Iu0@#|nWw@Uu6G!cfL)71 zPM5*4t;9)nQ3Syd0@) z5Cs7Tl>pKyilT-QL>@kJYTu3)00<=XNM>4wtv9RT`ZV&J7<&+JdR69uBE0Jj#D+08C-D(%Vhd8J2%{v`a~r_7Hg|b6{pDk3+5XR-p6};dm)R$dv|#W z*Byc%SrZzypbjL&RuPXVk)CI7agpfWzA4f{g-OWgxyU-0RjFN$N{|mnE+q$%j~31q zjRCI#m;*h1;~0kPt3d=0fbNov1SZLtQ+6RS7syy6kl$q-BMGKunl3G3J-n;zy1%SJ zQIH-S^kt8Oh$h7A@c}ytSB4@*z=>XX1vQ99!ac6p22Zdw;{ZMJi&{Ho&Z_2`Mm> zuCC1N!-YzC+BxmY5N-L-iDeLZ32TCDbJT}`tgC<8?rYlOlnC5qU}VP_m*V8%3u%B% zM{H{@(o|WzQ^i*IZX@2OsCR`&MrJJER2I5X7`(-vmsGl=E7yf%(f0bO*(q%q%K9pZH`LeGNZ>uD?(Rz==bTZM4$Lcp8Y#j zExMWHOw=M0!1$DcTZmNgk8$+M6nm}c=kbzSxrQa3qar?M>c&Nfed`a+U9?MzGg)-qgSCz^FvruIWC=Mbv><8upa!t?Md?5JtYDImV+50$X)r1o5P8 zJV{l2)v=Da>GP4{U0rO#rhC^lG(yt_Kc}Ek9EOLs1TGK|Mkvi-vB-2;IP4*tt+~{Y zyOSinmUnxmPd|$bMLWZ~$LKU&UR48kpCt*B&9w(1=&8tNW`6HlUz#WUZK1}YbE^7P z^>pMM&@j1~9-gw6!iA z7U^y<@UII6jHr7dx}c$J)jsVytn}O#YQoVt`O8*WGPeGK;m2Id*o_InRx^irRGzF& zRwA`(G(6juyvIWjbO8oCm`x!ssBZYcAqVxpm?>~-1z5!8w9|#ux)i!EQs|njF)=1; zX9J>3J#3k?-KXIWT2t{8XRVYYuM@GLg#_&fdc)fA=m}{cBw=du0sWJ_7N|P$TnZ!w zW_+u2r|A`pV}8hnW}u(S);YQruX?-NId(hNr(xg^V{%{9+-?ErE`?w>?iGSnn=37* z`p`9IYMVm<(*-wnMny4n3E>V5SZeP!AecV*T0c%!CV65U^;70X&qZqfFQa2k9?9&V z9&1D^Fswc7ogw}Ew&zI;UVX#_=>@<+|J@L|;_n5o@Y*iys_77YHWh}oqhy22?_t8u z&GnFtOxxxMmpjQpk^R%}39pO*ZS1_TX3}!GBM}%;=E0fA3@mDw;Fe zX>HB3uJuQHh5nPePdNC}LIFLn+obN|?W^HEd&JJ&mg{rBtR#sw3yIxNjn+{5J!t8w z0YHrtUfYxqUJr_F1#NcCam@>7ZaE?H2<3Q81>~>jjrro}ny00Bq)u_xD5VEe7`^4^jh>KZ}mx-v4IhB5R(G!)Ss)_dKW(O`ybo(cw_6G4<3b|^o_s&0W5o$5&!@I literal 0 HcmV?d00001 diff --git a/cypress/e2e/user/user.cy.ts b/cypress/e2e/user/user.cy.ts index 21cba90c..82ec5887 100644 --- a/cypress/e2e/user/user.cy.ts +++ b/cypress/e2e/user/user.cy.ts @@ -1,5 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; import { AuthTestUtils } from '../../support/auth-utils'; +import { PageUtils } from '../../support/page-utils'; describe('User Feature Tests', () => { const AF_BASE_URL = Cypress.env('AF_BASE_URL'); @@ -49,10 +50,8 @@ describe('User Feature Tests', () => { // Wait for workspace to be fully loaded cy.wait(3000); - // Open workspace dropdown by clicking on the trigger - cy.get('[data-testid="workspace-dropdown-trigger"]', { timeout: 10000 }) - .should('be.visible') - .click(); + // Open workspace dropdown + PageUtils.openWorkspaceDropdown(); // Wait for dropdown to open cy.wait(500); @@ -64,12 +63,12 @@ describe('User Feature Tests', () => { cy.task('log', `Verified email ${randomEmail} is displayed in dropdown`); // Verify one member count - cy.get('[data-testid="workspace-member-count"]') + PageUtils.getWorkspaceMemberCounts() .should('contain', '1 member'); cy.task('log', 'Verified workspace has 1 member'); // Verify exactly one workspace exists - cy.get('[data-testid="workspace-item"]') + PageUtils.getWorkspaceItems() .should('have.length', 1); // Verify workspace name is present diff --git a/cypress/support/auth-utils.ts b/cypress/support/auth-utils.ts index 5d73d8f8..3c4ce8a5 100644 --- a/cypress/support/auth-utils.ts +++ b/cypress/support/auth-utils.ts @@ -172,7 +172,7 @@ export class AuthTestUtils { url: `${this.config.baseUrl}/api/user/verify/${accessToken}`, failOnStatusCode: false, }).then((verifyResponse) => { - cy.task('log', `Token verification response: ${JSON.stringify(verifyResponse)}`); + // cy.task('log', `Token verification response: ${JSON.stringify(verifyResponse)}`); if (verifyResponse.status !== 200) { throw new Error('Token verification failed'); diff --git a/cypress/support/commands.ts b/cypress/support/commands.ts index 324d0248..5fc7af8b 100644 --- a/cypress/support/commands.ts +++ b/cypress/support/commands.ts @@ -2,6 +2,8 @@ // Import auth utilities import './auth-utils'; +// Import page utilities +import './page-utils'; // *********************************************** // This example commands.ts shows you how to diff --git a/cypress/support/page-utils.ts b/cypress/support/page-utils.ts new file mode 100644 index 00000000..26958a54 --- /dev/null +++ b/cypress/support/page-utils.ts @@ -0,0 +1,483 @@ +/** + * Page utilities for Cypress E2E tests + * Provides reusable functions to interact with page elements using data-testid attributes + */ + +export class PageUtils { + // ========== Navigation & Sidebar ========== + + /** + * Click the New Page button in the sidebar + */ + static clickNewPageButton() { + return cy.get('[data-testid="new-page-button"]').click(); + } + + /** + * Get all space items in the outline + */ + static getSpaceItems() { + return cy.get('[data-testid="space-item"]'); + } + + /** + * Click on a specific space item by index + */ + static clickSpaceItem(index: number = 0) { + return this.getSpaceItems().eq(index).click(); + } + + /** + * Get a space by its view ID + */ + static getSpaceById(viewId: string) { + return cy.get(`[data-testid="space-${viewId}"]`); + } + + /** + * Get all space names + */ + static getSpaceNames() { + return cy.get('[data-testid="space-name"]'); + } + + /** + * Get a specific space name by text + */ + static getSpaceByName(name: string) { + return cy.get('[data-testid="space-name"]').contains(name); + } + + /** + * Click on a space to expand/collapse it + */ + static clickSpace(spaceName?: string) { + if (spaceName) { + return this.getSpaceByName(spaceName).parent().parent().click(); + } + return this.getSpaceNames().first().parent().parent().click(); + } + + // ========== Page Management ========== + + /** + * Get all page names in the outline + */ + static getPageNames() { + return cy.get('[data-testid="page-name"]'); + } + + /** + * Get a specific page by name + */ + static getPageByName(name: string) { + return cy.get('[data-testid="page-name"]').contains(name); + } + + /** + * Click on a page by name + */ + static clickPageByName(name: string) { + return this.getPageByName(name).click(); + } + + /** + * Get a page by its view ID + */ + static getPageById(viewId: string) { + return cy.get(`[data-testid="page-${viewId}"]`); + } + + /** + * Get the page title input field (in modal/editor) + */ + static getPageTitleInput() { + return cy.get('[data-testid="page-title-input"]'); + } + + /** + * Enter a page title in the input field + */ + static enterPageTitle(title: string) { + return this.getPageTitleInput() + .should('be.visible') + .first() // Use first() to ensure we only interact with one element + .focus() + .clear() + .type(title); + } + + /** + * Save page title and close modal (press Escape) + */ + static savePageTitle() { + return this.getPageTitleInput().first().type('{esc}'); + } + + // ========== Page Actions ========== + + /** + * Click the More Actions button for the current page + */ + static clickPageMoreActions() { + return cy.get('[data-testid="page-more-actions"]').click(); + } + + /** + * Click the Delete Page button + */ + static clickDeletePageButton() { + return cy.get('[data-testid="delete-page-button"]').click(); + } + + /** + * Confirm page deletion in modal (if present) + */ + static confirmPageDeletion() { + return cy.get('body').then($body => { + if ($body.find('[data-testid="delete-page-confirm-modal"]').length > 0) { + cy.get('[data-testid="delete-page-confirm-modal"]').within(() => { + cy.contains('button', 'Delete').click(); + }); + } + }); + } + + /** + * Delete a page by name (complete flow) + */ + static deletePageByName(pageName: string) { + this.clickPageByName(pageName); + cy.wait(1000); + this.clickPageMoreActions(); + cy.wait(500); + this.clickDeletePageButton(); + cy.wait(500); + this.confirmPageDeletion(); + return cy.wait(2000); + } + + // ========== Modal & Dialog ========== + + /** + * Get the modal/dialog element + */ + static getModal() { + return cy.get('[role="dialog"]'); + } + + /** + * Click Add button in modal + */ + static clickModalAddButton() { + return this.getModal().within(() => { + cy.contains('button', 'Add').click(); + }); + } + + /** + * Select first space in modal and click Add + */ + static selectFirstSpaceInModal() { + return this.getModal().should('be.visible').within(() => { + this.clickSpaceItem(0); + cy.contains('button', 'Add').click(); + }); + // Note: The dialog doesn't close, it transitions to show the page editor + } + + /** + * Select a specific space by name in modal and click Add + */ + static selectSpace(spaceName: string = 'General') { + return this.getModal().should('be.visible').within(($modal) => { + // First check what elements exist in the modal + const spaceNameElements = $modal.find('[data-testid="space-name"]'); + const spaceItemElements = $modal.find('[data-testid="space-item"]'); + + if (spaceNameElements.length > 0) { + // Log all available spaces + cy.task('log', `Looking for space: "${spaceName}"`); + cy.task('log', 'Available spaces with space-name:'); + spaceNameElements.each((index, elem) => { + cy.task('log', ` - "${elem.textContent}"`); + }); + + // Try to find and click the target space + cy.get('[data-testid="space-name"]').contains(spaceName).click(); + } else if (spaceItemElements.length > 0) { + // If no space-name elements but space-item elements exist + cy.task('log', `Found ${spaceItemElements.length} space-item elements but no space-name elements`); + // Check if any space-item contains the target space name + let foundSpace = false; + spaceItemElements.each((index, item) => { + if (item.textContent && item.textContent.includes(spaceName)) { + foundSpace = true; + cy.get('[data-testid="space-item"]').eq(index).click(); + return false; // break the loop + } + }); + + if (!foundSpace) { + cy.task('log', `Space "${spaceName}" not found, clicking first space-item as fallback`); + cy.get('[data-testid="space-item"]').first().click(); + } + } else { + // Debug: log what's actually in the modal + const allTestIds = $modal.find('[data-testid]'); + cy.task('log', 'No space elements found. Available data-testid elements in modal:'); + allTestIds.each((index, elem) => { + const testId = elem.getAttribute('data-testid'); + if (testId && index < 20) { // Limit output + cy.task('log', ` - ${testId}: "${elem.textContent?.slice(0, 50)}"`); + } + }); + + // As a last resort, try to find any clickable element that might be a space + cy.task('log', 'Attempting to find any clickable space element...'); + // Try to click the first item that looks like it could be a space + cy.get('[role="button"], [role="option"], .clickable, button').first().click(); + } + + // Click the Add button + cy.contains('button', 'Add').click(); + }); + // Note: The dialog doesn't close, it transitions to show the page editor + } + + // ========== Workspace ========== + + /** + * Get the workspace dropdown trigger + */ + static getWorkspaceDropdownTrigger() { + return cy.get('[data-testid="workspace-dropdown-trigger"]'); + } + + /** + * Click to open workspace dropdown + */ + static openWorkspaceDropdown() { + return this.getWorkspaceDropdownTrigger().click(); + } + + /** + * Get workspace list container + */ + static getWorkspaceList() { + return cy.get('[data-testid="workspace-list"]'); + } + + /** + * Get all workspace items + */ + static getWorkspaceItems() { + return cy.get('[data-testid="workspace-item"]'); + } + + /** + * Get workspace member count elements + */ + static getWorkspaceMemberCounts() { + return cy.get('[data-testid="workspace-member-count"]'); + } + + /** + * Get user email in dropdown + */ + static getUserEmailInDropdown() { + return cy.get('[data-testid="user-email"]'); + } + + // ========== Editor & Content ========== + + /** + * Get the editor element by view ID + */ + static getEditor(viewId?: string) { + if (viewId) { + return cy.get(`#editor-${viewId}`); + } + // Get any editor (when there's only one) + return cy.get('[id^="editor-"]').first(); + } + + /** + * Type content in the editor + */ + static typeInEditor(content: string) { + return this.getEditor() + .should('be.visible') + .focus() + .type(content); + } + + /** + * Type multiple lines in the editor + */ + static typeMultipleLinesInEditor(lines: string[]) { + return this.getEditor() + .should('be.visible') + .focus() + .then($editor => { + lines.forEach((line, index) => { + if (index > 0) { + cy.wrap($editor).type('{enter}'); + } + cy.wrap($editor).type(line); + }); + }); + } + + /** + * Get editor content as text + */ + static getEditorContent() { + return this.getEditor().invoke('text'); + } + + /** + * Verify editor contains specific text + */ + static verifyEditorContains(text: string) { + return this.getEditor().should('contain', text); + } + + /** + * Clear editor content + */ + static clearEditor() { + return this.getEditor() + .focus() + .type('{selectall}{backspace}'); + } + + // ========== Utility Functions ========== + + /** + * Wait for page to load after navigation + */ + static waitForPageLoad(timeout: number = 3000) { + return cy.wait(timeout); + } + + /** + * Verify a page exists by name + */ + static verifyPageExists(pageName: string) { + return this.getPageByName(pageName).should('exist'); + } + + /** + * Verify a page does not exist by name + */ + static verifyPageNotExists(pageName: string) { + return cy.get('body').then($body => { + if ($body.find('[data-testid="page-name"]').length > 0) { + cy.get('[data-testid="page-name"]').each(($el) => { + cy.wrap($el).should('not.contain', pageName); + }); + } else { + cy.get('[data-testid="page-name"]').should('not.exist'); + } + }); + } + + /** + * Create a new page with a specific name (complete flow) + */ + static createPage(pageName: string) { + this.clickNewPageButton(); + cy.wait(1000); + + // Select space in modal + this.getModal().should('be.visible').within(() => { + PageUtils.getSpaceItems().first().click(); + cy.contains('button', 'Add').click(); + }); + + cy.wait(2000); + + // Enter page title + this.enterPageTitle(pageName); + this.savePageTitle(); + + return cy.wait(1000); + } + + /** + * Expand a space to show its pages + */ + static expandSpace(spaceName?: string) { + return this.clickSpace(spaceName); + } + + /** + * Check if a space is expanded by checking if pages are visible + */ + static isSpaceExpanded(spaceName: string) { + return this.getSpaceByName(spaceName) + .parent() + .parent() + .parent() + .find('[data-testid="page-name"]') + .should('be.visible'); + } +} + +// Export individual utility functions for convenience +export const { + // Navigation + clickNewPageButton, + getSpaceItems, + clickSpaceItem, + getSpaceById, + getSpaceNames, + getSpaceByName, + clickSpace, + + // Page Management + getPageNames, + getPageByName, + clickPageByName, + getPageById, + getPageTitleInput, + enterPageTitle, + savePageTitle, + + // Page Actions + clickPageMoreActions, + clickDeletePageButton, + confirmPageDeletion, + deletePageByName, + + // Modal + getModal, + clickModalAddButton, + selectFirstSpaceInModal, + selectSpace, + + // Workspace + getWorkspaceDropdownTrigger, + openWorkspaceDropdown, + getWorkspaceList, + getWorkspaceItems, + getWorkspaceMemberCounts, + getUserEmailInDropdown, + + // Editor + getEditor, + typeInEditor, + typeMultipleLinesInEditor, + getEditorContent, + verifyEditorContains, + clearEditor, + + // Utilities + waitForPageLoad, + verifyPageExists, + verifyPageNotExists, + createPage, + expandSpace, + isSpaceExpanded, +} = PageUtils; \ No newline at end of file diff --git a/package.json b/package.json index 72e55bc0..9c8d0d06 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "test:cy": "cypress run", "test:integration": "cypress run --spec 'cypress/e2e/**/*.cy.ts'", "test:integration:user": "cypress run --spec 'cypress/e2e/user/**/*.cy.ts' --headed --browser chrome", - "test:integration:page": "cypress run --spec 'cypress/e2e/page/**/*.cy.ts' --headed --browser chrome", + "test:integration:page:create-delete": "cypress run --spec 'cypress/e2e/page/create-delete-page.cy.ts' --headed --browser chrome", + "test:integration:page:edit": "cypress run --spec 'cypress/e2e/page/edit-page.cy.ts'", "test:integration:publish": "cypress run --spec 'cypress/e2e/publish/**/*.cy.ts'", "wait:backend": "AF_BASE_URL=${AF_BASE_URL:-http://localhost} node scripts/wait-for-backend.js", "coverage": "cross-env COVERAGE=true pnpm run test:unit && cross-env COVERAGE=true pnpm run test:components", diff --git a/src/components/editor/Editable.tsx b/src/components/editor/Editable.tsx index 16271911..fba23a35 100644 --- a/src/components/editor/Editable.tsx +++ b/src/components/editor/Editable.tsx @@ -124,6 +124,7 @@ const EditorEditable = () => { { const codeDecoration = codeDecorate?.(entry); const decoration = decorate(entry);