From 35b5d931fe66cf4e32d889cd70faef3a7e3d9184 Mon Sep 17 00:00:00 2001 From: nathan Date: Tue, 23 Sep 2025 21:35:45 +0800 Subject: [PATCH] chore: editor test --- cypress/e2e/database/checkbox-column.cy.ts | 1 + cypress/e2e/database/datetime-column.cy.ts | 7 +- .../e2e/database/single-select-column.cy.ts | 16 +-- .../e2e/editor/slash-menu-formatting.cy.ts | 110 ++++++++++++++++++ cypress/e2e/editor/slash-menu-lists.cy.ts | 105 +++++++++++++++++ cypress/e2e/editor/slash-menu-media.cy.ts | 97 +++++++++++++++ cypress/e2e/editor/slash-menu.cy.ts | 62 ++++++++++ cypress/support/selectors.ts | 2 +- .../panels/slash-panel/SlashPanel.tsx | 1 + 9 files changed, 390 insertions(+), 11 deletions(-) create mode 100644 cypress/e2e/editor/slash-menu-formatting.cy.ts create mode 100644 cypress/e2e/editor/slash-menu-lists.cy.ts create mode 100644 cypress/e2e/editor/slash-menu-media.cy.ts create mode 100644 cypress/e2e/editor/slash-menu.cy.ts diff --git a/cypress/e2e/database/checkbox-column.cy.ts b/cypress/e2e/database/checkbox-column.cy.ts index 9265d5ff..395547a3 100644 --- a/cypress/e2e/database/checkbox-column.cy.ts +++ b/cypress/e2e/database/checkbox-column.cy.ts @@ -4,6 +4,7 @@ import { AddPageSelectors, DatabaseGridSelectors, CheckboxSelectors, + byTestId, waitForReactUpdate } from '../../support/selectors'; diff --git a/cypress/e2e/database/datetime-column.cy.ts b/cypress/e2e/database/datetime-column.cy.ts index fcc608f2..6ac6aa6a 100644 --- a/cypress/e2e/database/datetime-column.cy.ts +++ b/cypress/e2e/database/datetime-column.cy.ts @@ -7,6 +7,7 @@ import { PropertyMenuSelectors, GridFieldSelectors, FieldType, + byTestId, waitForReactUpdate } from '../../support/selectors'; @@ -55,7 +56,7 @@ describe('DateTime Column Type', () => { // Verify cells exist cy.log('[STEP 6] Verifying cells exist'); - cy.get('[data-testid^="grid-cell-"]', { timeout: 10000 }).should('exist'); + DatabaseGridSelectors.cells().should('exist'); // Add new column cy.log('[STEP 7] Adding new column by clicking new property button'); @@ -68,7 +69,7 @@ describe('DateTime Column Type', () => { cy.log('[STEP 8] Waiting for property menu to open'); cy.get('body').then($body => { // Check if property type trigger exists - if ($body.find('[data-testid="property-type-trigger"]').length > 0) { + if ($body.find(byTestId('property-type-trigger')).length > 0) { cy.log('[STEP 9] Property type trigger found, changing to DateTime'); PropertyMenuSelectors.propertyTypeTrigger().first().click({ force: true }); waitForReactUpdate(1000); @@ -112,7 +113,7 @@ describe('DateTime Column Type', () => { // Check if picker opens cy.get('body').then($body => { - if ($body.find('[data-testid="datetime-picker-popover"]').length > 0) { + if ($body.find(byTestId('datetime-picker-popover')).length > 0) { cy.log('[STEP 14] DateTime picker opened successfully'); // Enter a date diff --git a/cypress/e2e/database/single-select-column.cy.ts b/cypress/e2e/database/single-select-column.cy.ts index 82354986..6324cfb7 100644 --- a/cypress/e2e/database/single-select-column.cy.ts +++ b/cypress/e2e/database/single-select-column.cy.ts @@ -6,7 +6,9 @@ import { PropertyMenuSelectors, GridFieldSelectors, SingleSelectSelectors, + PageSelectors, FieldType, + byTestId, waitForReactUpdate } from '../../support/selectors'; @@ -53,8 +55,8 @@ describe('Single Select Column Type', () => { // Try to find either inline add button or new page button cy.get('body').then($body => { - const inlineAddExists = $body.find('[data-testid="inline-add-page"]').length > 0; - const newPageExists = $body.find('[data-testid="new-page-button"]').length > 0; + const inlineAddExists = $body.find(byTestId('inline-add-page')).length > 0; + const newPageExists = $body.find(byTestId('new-page-button')).length > 0; if (inlineAddExists) { cy.log('[STEP 4.2] Using inline add button'); @@ -64,7 +66,7 @@ describe('Single Select Column Type', () => { } else if (newPageExists) { cy.log('[STEP 4.2] Using new page button instead'); return cy.wrap(null).then(() => { - cy.get('[data-testid="new-page-button"]').first().click({ force: true }); + PageSelectors.newPageButton().first().click({ force: true }); }); } else { // Wait a bit more and try inline add button @@ -165,7 +167,7 @@ describe('Single Select Column Type', () => { // Check if property menu is open and change to SingleSelect cy.log('[STEP 7] Changing column type to SingleSelect'); cy.get('body').then($body => { - if ($body.find('[data-testid="property-type-trigger"]').length > 0) { + if ($body.find(byTestId('property-type-trigger')).length > 0) { PropertyMenuSelectors.propertyTypeTrigger().first().click({ force: true }); waitForReactUpdate(1000); PropertyMenuSelectors.propertyTypeOption(FieldType.SingleSelect).click({ force: true }); @@ -240,7 +242,7 @@ describe('Single Select Column Type', () => { // Click edit property if available cy.get('body').then($body => { - if ($body.find('[data-testid="grid-field-edit-property"]').length > 0) { + if ($body.find(byTestId('grid-field-edit-property')).length > 0) { PropertyMenuSelectors.editPropertyMenuItem().click(); waitForReactUpdate(1000); } @@ -284,7 +286,7 @@ describe('Single Select Column Type', () => { // Click edit property if available cy.get('body').then($body => { - if ($body.find('[data-testid="grid-field-edit-property"]').length > 0) { + if ($body.find(byTestId('grid-field-edit-property')).length > 0) { PropertyMenuSelectors.editPropertyMenuItem().click(); waitForReactUpdate(1000); } @@ -314,7 +316,7 @@ describe('Single Select Column Type', () => { // Check if select menu appears cy.get('body').then($body => { - if ($body.find('[data-testid="select-option-menu"]').length > 0) { + if ($body.find(byTestId('select-option-menu')).length > 0) { cy.log('[STEP 18] Select option menu opened - options preserved!'); } else { cy.log('[STEP 18] Select cells exist but menu behavior may differ'); diff --git a/cypress/e2e/editor/slash-menu-formatting.cy.ts b/cypress/e2e/editor/slash-menu-formatting.cy.ts new file mode 100644 index 00000000..66b3933d --- /dev/null +++ b/cypress/e2e/editor/slash-menu-formatting.cy.ts @@ -0,0 +1,110 @@ +import { v4 as uuidv4 } from 'uuid'; +import { AuthTestUtils } from '../../support/auth-utils'; +import { waitForReactUpdate } from '../../support/selectors'; + +describe('Slash Menu - Text Formatting', () => { + const generateRandomEmail = () => `${uuidv4()}@appflowy.io`; + + beforeEach(() => { + cy.on('uncaught:exception', (err) => { + if (err.message.includes('Minified React error') || + err.message.includes('View not found') || + err.message.includes('No workspace or service found')) { + return false; + } + return true; + }); + + cy.viewport(1280, 720); + }); + + it('should show text formatting options in slash menu', () => { + const testEmail = generateRandomEmail(); + + cy.log(`[TEST START] Testing text formatting options - Test email: ${testEmail}`); + + // Login + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); + + // Navigate to Getting started page + cy.contains('Getting started').click(); + cy.wait(5000); // Give page time to fully load + + // Focus on editor + cy.get('[data-slate-editor="true"]').should('exist').click(); + waitForReactUpdate(1000); + + // Type slash to open menu + cy.focused().type('/'); + waitForReactUpdate(1000); + + // Verify text formatting options are visible + cy.log('Verifying Text option'); + cy.contains('Text').should('be.visible'); + + cy.log('Verifying Heading 1 option'); + cy.contains('Heading 1').should('be.visible'); + + cy.log('Verifying Heading 2 option'); + cy.contains('Heading 2').should('be.visible'); + + cy.log('Verifying Heading 3 option'); + cy.contains('Heading 3').should('be.visible'); + + // Close menu + cy.get('body').type('{esc}'); + waitForReactUpdate(500); + + cy.log('Text formatting options verified successfully'); + }); + }); + + it('should allow selecting Heading 1 from slash menu', () => { + const testEmail = generateRandomEmail(); + + cy.log(`[TEST START] Testing Heading 1 selection - Test email: ${testEmail}`); + + // Login + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); + + // Navigate to Getting started page + cy.contains('Getting started').click(); + cy.wait(5000); + + // Focus on editor and move to end + cy.get('[data-slate-editor="true"]').should('exist').click(); + cy.focused().type('{end}'); + cy.focused().type('{enter}{enter}'); // Add some space + waitForReactUpdate(1000); + + // Type slash to open menu + cy.focused().type('/'); + waitForReactUpdate(1000); + + // Click Heading 1 + cy.contains('Heading 1').should('be.visible').click(); + waitForReactUpdate(1000); + + // Type some text + cy.focused().type('Test Heading'); + waitForReactUpdate(500); + + // Verify the text was added + cy.get('[data-slate-editor="true"]').should('contain.text', 'Test Heading'); + + cy.log('Heading 1 added successfully'); + }); + }); +}); \ No newline at end of file diff --git a/cypress/e2e/editor/slash-menu-lists.cy.ts b/cypress/e2e/editor/slash-menu-lists.cy.ts new file mode 100644 index 00000000..c556f12e --- /dev/null +++ b/cypress/e2e/editor/slash-menu-lists.cy.ts @@ -0,0 +1,105 @@ +import { v4 as uuidv4 } from 'uuid'; +import { AuthTestUtils } from '../../support/auth-utils'; +import { waitForReactUpdate } from '../../support/selectors'; + +describe('Slash Menu - List Actions', () => { + const generateRandomEmail = () => `${uuidv4()}@appflowy.io`; + + beforeEach(() => { + cy.on('uncaught:exception', (err) => { + if (err.message.includes('Minified React error') || + err.message.includes('View not found') || + err.message.includes('No workspace or service found')) { + return false; + } + return true; + }); + + cy.viewport(1280, 720); + }); + + it('should show list options in slash menu', () => { + const testEmail = generateRandomEmail(); + + cy.log(`[TEST START] Testing list options - Test email: ${testEmail}`); + + // Login + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); + + // Navigate to Getting started page + cy.contains('Getting started').click(); + cy.wait(5000); // Give page time to fully load + + // Focus on editor + cy.get('[data-slate-editor="true"]').should('exist').click(); + waitForReactUpdate(1000); + + // Type slash to open menu + cy.focused().type('/'); + waitForReactUpdate(1000); + + // Verify list options are visible + cy.log('Verifying Bulleted list option'); + cy.contains('Bulleted list').should('be.visible'); + + cy.log('Verifying Numbered list option'); + cy.contains('Numbered list').should('be.visible'); + + // Close menu + cy.get('body').type('{esc}'); + waitForReactUpdate(500); + + cy.log('List options verified successfully'); + }); + }); + + it('should allow selecting Bulleted list from slash menu', () => { + const testEmail = generateRandomEmail(); + + cy.log(`[TEST START] Testing Bulleted list selection - Test email: ${testEmail}`); + + // Login + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); + + // Navigate to Getting started page + cy.contains('Getting started').click(); + cy.wait(5000); + + // Focus on editor and move to end + cy.get('[data-slate-editor="true"]').should('exist').click(); + cy.focused().type('{end}'); + cy.focused().type('{enter}{enter}'); // Add some space + waitForReactUpdate(1000); + + // Type slash to open menu + cy.focused().type('/'); + waitForReactUpdate(1000); + + // Click Bulleted list + cy.contains('Bulleted list').should('be.visible').click(); + waitForReactUpdate(1000); + + // Type some text + cy.focused().type('Test bullet item'); + waitForReactUpdate(500); + + // Verify the text was added + cy.get('[data-slate-editor="true"]').should('contain.text', 'Test bullet item'); + + cy.log('Bulleted list added successfully'); + }); + }); + +}); \ No newline at end of file diff --git a/cypress/e2e/editor/slash-menu-media.cy.ts b/cypress/e2e/editor/slash-menu-media.cy.ts new file mode 100644 index 00000000..6b3d82db --- /dev/null +++ b/cypress/e2e/editor/slash-menu-media.cy.ts @@ -0,0 +1,97 @@ +import { v4 as uuidv4 } from 'uuid'; +import { AuthTestUtils } from '../../support/auth-utils'; +import { waitForReactUpdate } from '../../support/selectors'; + +describe('Slash Menu - Media Actions', () => { + const generateRandomEmail = () => `${uuidv4()}@appflowy.io`; + + beforeEach(() => { + cy.on('uncaught:exception', (err) => { + if (err.message.includes('Minified React error') || + err.message.includes('View not found') || + err.message.includes('No workspace or service found')) { + return false; + } + return true; + }); + + cy.viewport(1280, 720); + }); + + it('should show media options in slash menu', () => { + const testEmail = generateRandomEmail(); + + cy.log(`[TEST START] Testing media options - Test email: ${testEmail}`); + + // Login + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); + + // Navigate to Getting started page + cy.contains('Getting started').click(); + cy.wait(5000); // Give page time to fully load + + // Focus on editor + cy.get('[data-slate-editor="true"]').should('exist').click(); + waitForReactUpdate(1000); + + // Type slash to open menu + cy.focused().type('/'); + waitForReactUpdate(1000); + + // Verify media options are visible + cy.log('Verifying Image option'); + cy.contains('Image').should('be.visible'); + + cy.log('Verifying Embed video option'); + cy.contains('Embed video').should('be.visible'); + + // Close menu + cy.get('body').type('{esc}'); + waitForReactUpdate(500); + + cy.log('Media options verified successfully'); + }); + }); + + it('should allow selecting Image from slash menu', () => { + const testEmail = generateRandomEmail(); + + cy.log(`[TEST START] Testing Image selection - Test email: ${testEmail}`); + + // Login + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); + + // Navigate to Getting started page + cy.contains('Getting started').click(); + cy.wait(5000); + + // Focus on editor and move to end + cy.get('[data-slate-editor="true"]').should('exist').click(); + cy.focused().type('{end}'); + cy.focused().type('{enter}{enter}'); // Add some space + waitForReactUpdate(1000); + + // Type slash to open menu + cy.focused().type('/'); + waitForReactUpdate(1000); + + // Click Image + cy.contains('Image').should('be.visible').click(); + waitForReactUpdate(1000); + + cy.log('Image option clicked successfully'); + }); + }); +}); \ No newline at end of file diff --git a/cypress/e2e/editor/slash-menu.cy.ts b/cypress/e2e/editor/slash-menu.cy.ts new file mode 100644 index 00000000..84e9f94f --- /dev/null +++ b/cypress/e2e/editor/slash-menu.cy.ts @@ -0,0 +1,62 @@ +import { v4 as uuidv4 } from 'uuid'; +import { AuthTestUtils } from '../../support/auth-utils'; +import { PageSelectors, waitForReactUpdate } from '../../support/selectors'; + +describe('Editor Slash Menu', () => { + const generateRandomEmail = () => `${uuidv4()}@appflowy.io`; + + beforeEach(() => { + cy.on('uncaught:exception', (err) => { + if (err.message.includes('Minified React error') || + err.message.includes('View not found') || + err.message.includes('No workspace or service found')) { + return false; + } + return true; + }); + + cy.viewport(1280, 720); + }); + + it('should trigger slash menu when typing / and display menu options', () => { + const testEmail = generateRandomEmail(); + + cy.log(`[TEST START] Testing slash menu trigger - Test email: ${testEmail}`); + + // Login + cy.visit('/login', { failOnStatusCode: false }); + cy.wait(2000); + + const authUtils = new AuthTestUtils(); + authUtils.signInWithTestUrl(testEmail).then(() => { + cy.url({ timeout: 30000 }).should('include', '/app'); + cy.wait(3000); + + // Navigate to Getting started page + cy.contains('Getting started').click(); + cy.wait(5000); // Give page time to fully load + + // Focus on editor + cy.get('[data-slate-editor="true"]').should('exist').click(); + waitForReactUpdate(1000); + + // Type slash to open menu + cy.focused().type('/'); + waitForReactUpdate(1000); + + // Verify main menu items are visible + cy.contains('Ask AI Anything').should('be.visible'); + cy.contains('Text').should('be.visible'); + cy.contains('Heading 1').should('be.visible'); + cy.contains('Image').should('be.visible'); + cy.contains('Bulleted list').should('be.visible'); + + // Close menu + cy.get('body').type('{esc}'); + waitForReactUpdate(500); + + cy.log('Slash menu test completed successfully'); + }); + }); + +}); \ No newline at end of file diff --git a/cypress/support/selectors.ts b/cypress/support/selectors.ts index b03aefb3..131d5903 100644 --- a/cypress/support/selectors.ts +++ b/cypress/support/selectors.ts @@ -6,7 +6,7 @@ /** * Helper function to create a data-testid selector */ -function byTestId(id: string): string { +export function byTestId(id: string): string { return `[data-testid="${id}"]`; } diff --git a/src/components/editor/components/panels/slash-panel/SlashPanel.tsx b/src/components/editor/components/panels/slash-panel/SlashPanel.tsx index af501054..5a11a715 100644 --- a/src/components/editor/components/panels/slash-panel/SlashPanel.tsx +++ b/src/components/editor/components/panels/slash-panel/SlashPanel.tsx @@ -695,6 +695,7 @@ export function SlashPanel({ color={'inherit'} startIcon={option.icon} key={option.key} + data-testid={`slash-menu-${option.key}`} data-option-key={option.key} onClick={() => { handleSelectOption(option.key);