diff --git a/cypress/e2e/editor/document-editing.cy.ts b/cypress/e2e/editor/document-editing.cy.ts index 5e667e37..7d683472 100644 --- a/cypress/e2e/editor/document-editing.cy.ts +++ b/cypress/e2e/editor/document-editing.cy.ts @@ -1,5 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; import { AuthTestUtils } from '../../support/auth-utils'; +import { EditorSelectors } from '../../support/selectors'; describe('Document Editing with Formatting', () => { const generateRandomEmail = () => `${uuidv4()}@appflowy.io`; @@ -26,9 +27,8 @@ describe('Document Editing with Formatting', () => { cy.wait(5000); // Find and focus the editor - cy.get('[data-slate-editor="true"]', { timeout: 10000 }) + EditorSelectors.firstEditor() .should('exist') - .first() .click({ force: true }); cy.wait(1000); @@ -65,9 +65,9 @@ describe('Document Editing with Formatting', () => { cy.wait(1000); // Verify content - cy.get('[data-slate-editor="true"]').should('contain.text', 'Document Title'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Main Heading'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Some content text'); + EditorSelectors.slateEditor().should('contain.text', 'Document Title'); + EditorSelectors.slateEditor().should('contain.text', 'Main Heading'); + EditorSelectors.slateEditor().should('contain.text', 'Some content text'); }); }); @@ -88,9 +88,8 @@ describe('Document Editing with Formatting', () => { cy.wait(5000); // Find and focus the editor - cy.get('[data-slate-editor="true"]', { timeout: 10000 }) + EditorSelectors.firstEditor() .should('exist') - .first() .click({ force: true }); cy.wait(1000); @@ -136,10 +135,10 @@ describe('Document Editing with Formatting', () => { cy.wait(1000); // Verify content - cy.get('[data-slate-editor="true"]').should('contain.text', 'Shopping List'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Apples'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Bananas'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Oranges'); + EditorSelectors.slateEditor().should('contain.text', 'Shopping List'); + EditorSelectors.slateEditor().should('contain.text', 'Apples'); + EditorSelectors.slateEditor().should('contain.text', 'Bananas'); + EditorSelectors.slateEditor().should('contain.text', 'Oranges'); }); }); @@ -160,9 +159,8 @@ describe('Document Editing with Formatting', () => { cy.wait(5000); // Find and focus the editor - cy.get('[data-slate-editor="true"]', { timeout: 10000 }) + EditorSelectors.firstEditor() .should('exist') - .first() .click({ force: true }); cy.wait(1000); @@ -235,12 +233,12 @@ describe('Document Editing with Formatting', () => { cy.wait(1000); // Verify content - cy.get('[data-slate-editor="true"]').should('contain.text', 'Steps to Follow'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'First step'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Second step'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Tasks'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Complete report'); - cy.get('[data-slate-editor="true"]').should('contain.text', 'Review code'); + EditorSelectors.slateEditor().should('contain.text', 'Steps to Follow'); + EditorSelectors.slateEditor().should('contain.text', 'First step'); + EditorSelectors.slateEditor().should('contain.text', 'Second step'); + EditorSelectors.slateEditor().should('contain.text', 'Tasks'); + EditorSelectors.slateEditor().should('contain.text', 'Complete report'); + EditorSelectors.slateEditor().should('contain.text', 'Review code'); }); }); }); \ No newline at end of file diff --git a/cypress/e2e/editor/slash-menu.cy.ts b/cypress/e2e/editor/slash-menu.cy.ts index 84e9f94f..a7d73b69 100644 --- a/cypress/e2e/editor/slash-menu.cy.ts +++ b/cypress/e2e/editor/slash-menu.cy.ts @@ -1,6 +1,6 @@ import { v4 as uuidv4 } from 'uuid'; import { AuthTestUtils } from '../../support/auth-utils'; -import { PageSelectors, waitForReactUpdate } from '../../support/selectors'; +import { EditorSelectors, PageSelectors, waitForReactUpdate } from '../../support/selectors'; describe('Editor Slash Menu', () => { const generateRandomEmail = () => `${uuidv4()}@appflowy.io`; @@ -37,7 +37,7 @@ describe('Editor Slash Menu', () => { cy.wait(5000); // Give page time to fully load // Focus on editor - cy.get('[data-slate-editor="true"]').should('exist').click(); + EditorSelectors.slateEditor().should('exist').click(); waitForReactUpdate(1000); // Type slash to open menu diff --git a/cypress/e2e/editor/text-formatting.cy.ts b/cypress/e2e/editor/text-formatting.cy.ts new file mode 100644 index 00000000..3db2ee1d --- /dev/null +++ b/cypress/e2e/editor/text-formatting.cy.ts @@ -0,0 +1,155 @@ +import { v4 as uuidv4 } from 'uuid'; +import { AuthTestUtils } from '../../support/auth-utils'; +import { EditorSelectors, waitForReactUpdate } from '../../support/selectors'; + +describe('Text Formatting - Selection and Formatting', () => { + const generateRandomEmail = () => `${uuidv4()}@appflowy.io`; + + beforeEach(() => { + cy.on('uncaught:exception', () => false); + cy.viewport(1280, 720); + }); + + it('should apply all formatting styles to text', () => { + const testEmail = generateRandomEmail(); + + // 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 and ensure it's ready + EditorSelectors.slateEditor().should('exist').and('be.visible'); + cy.wait(2000); // Extra wait for CI environment + EditorSelectors.slateEditor().click(); + waitForReactUpdate(2000); + + // Clear existing content + cy.focused().type('{selectall}{backspace}'); + waitForReactUpdate(1000); // Give more time for content to clear + + // Test 1: Bold text using toolbar button + cy.log('Testing BOLD formatting'); + cy.focused().type('This line is bold'); + waitForReactUpdate(1000); + + // Select all text + cy.focused().type('{selectall}'); + waitForReactUpdate(1000); + + // Wait for selection toolbar to appear and click bold button + EditorSelectors.selectionToolbar() + .should('exist', { timeout: 10000 }) + .should('have.css', 'opacity', '1'); + + EditorSelectors.boldButton() + .should('exist', { timeout: 10000 }) + .should('be.visible') + .click({ force: true }); + + waitForReactUpdate(1000); + + // Wait for the strong element to appear with retry and longer timeout + cy.get('strong', { timeout: 15000 }).should('exist').and('be.visible'); + cy.focused().type('{end}{enter}'); + waitForReactUpdate(1000); + + // Test 2: Italic text using toolbar button + cy.log('Testing ITALIC formatting'); + cy.focused().type('This line is italic'); + waitForReactUpdate(1000); + + // Select all text + cy.focused().type('{selectall}'); + waitForReactUpdate(1000); + + // Wait for selection toolbar and click italic button + EditorSelectors.selectionToolbar() + .should('exist', { timeout: 10000 }) + .should('have.css', 'opacity', '1'); + + EditorSelectors.italicButton() + .should('exist', { timeout: 10000 }) + .should('be.visible') + .click({ force: true }); + + waitForReactUpdate(1000); + + // Wait for the em element to appear with retry and longer timeout + cy.get('em', { timeout: 15000 }).should('exist').and('be.visible'); + cy.focused().type('{end}{enter}'); + waitForReactUpdate(1000); + + // Test 3: Underline text using toolbar button + cy.log('Testing UNDERLINE formatting'); + cy.focused().type('This line is underlined'); + waitForReactUpdate(1000); + + // Select all text + cy.focused().type('{selectall}'); + waitForReactUpdate(1000); + + // Wait for selection toolbar and click underline button + EditorSelectors.selectionToolbar() + .should('exist', { timeout: 10000 }) + .should('have.css', 'opacity', '1'); + + EditorSelectors.underlineButton() + .should('exist', { timeout: 10000 }) + .should('be.visible') + .click({ force: true }); + + waitForReactUpdate(1000); + + // Wait for the u element to appear with retry and longer timeout + cy.get('u', { timeout: 15000 }).should('exist').and('be.visible'); + cy.focused().type('{end}{enter}'); + waitForReactUpdate(1000); + + // Test 4: Strikethrough text using toolbar button + cy.log('Testing STRIKETHROUGH formatting'); + cy.focused().type('This line has strikethrough'); + waitForReactUpdate(1000); + + // Select all text + cy.focused().type('{selectall}'); + waitForReactUpdate(1000); + + // Wait for selection toolbar and click strikethrough button + EditorSelectors.selectionToolbar() + .should('exist', { timeout: 10000 }) + .should('have.css', 'opacity', '1'); + + EditorSelectors.strikethroughButton() + .should('exist', { timeout: 10000 }) + .should('be.visible') + .click({ force: true }); + + waitForReactUpdate(1000); + + // Wait for the strikethrough element to appear with retry and longer timeout + cy.get('s, del, strike, [style*="text-decoration: line-through"]', { timeout: 15000 }).should('exist').and('be.visible'); + + // Final verification - all formatted elements should be visible + cy.log('Verifying all formatted text is visible'); + waitForReactUpdate(2000); + cy.get('strong', { timeout: 15000 }).should('exist').and('be.visible').and('contain.text', 'bold'); + cy.get('em', { timeout: 15000 }).should('exist').and('be.visible').and('contain.text', 'italic'); + cy.get('u', { timeout: 15000 }).should('exist').and('be.visible').and('contain.text', 'underlined'); + cy.get('s, del, strike, [style*="text-decoration: line-through"]', { timeout: 15000 }).should('exist').and('be.visible'); + + // Take screenshot to verify all lines are visible + cy.screenshot('all-formatted-lines-visible'); + + cy.log('All text formatting styles tested successfully - each line shows a different style'); + }); + }); +}); \ No newline at end of file diff --git a/cypress/support/selectors.ts b/cypress/support/selectors.ts index 131d5903..60f8ff40 100644 --- a/cypress/support/selectors.ts +++ b/cypress/support/selectors.ts @@ -303,6 +303,30 @@ export const CheckboxSelectors = { uncheckedCells: () => cy.get('[data-checked="false"]'), }; +/** + * Editor-related selectors + */ +export const EditorSelectors = { + // Main Slate editor + slateEditor: () => cy.get('[data-slate-editor="true"]'), + + // Get first editor + firstEditor: () => cy.get('[data-slate-editor="true"]').first(), + + // Get editor with specific content + editorWithText: (text: string) => cy.get('[data-slate-editor="true"]').contains(text), + + // Selection toolbar + selectionToolbar: () => cy.get('[data-testid="selection-toolbar"]'), + + // Formatting buttons in toolbar + boldButton: () => cy.get(byTestId('toolbar-bold-button')), + italicButton: () => cy.get(byTestId('toolbar-italic-button')), + underlineButton: () => cy.get(byTestId('toolbar-underline-button')), + strikethroughButton: () => cy.get(byTestId('toolbar-strikethrough-button')), + codeButton: () => cy.get(byTestId('toolbar-code-button')), +}; + /** * Helper function to wait for React to re-render after state changes */ diff --git a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx index a35e8711..8704bb37 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/SelectionToolbar.tsx @@ -43,6 +43,7 @@ export function SelectionToolbar() {
{t('toolbar.bold')}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/InlineCode.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/InlineCode.tsx index 0afe9537..f3ff2898 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/InlineCode.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/InlineCode.tsx @@ -24,6 +24,7 @@ export function InlineCode() {
{t('editor.embedCode')}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx index 0df1909a..38cc8eb8 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Italic.tsx @@ -24,6 +24,7 @@ export function Italic() {
{t('toolbar.italic')}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx index ee248ff6..58ff8c17 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/StrikeThrough.tsx @@ -24,6 +24,7 @@ export function StrikeThrough() {
{t('editor.strikethrough')}
diff --git a/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx b/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx index e6a45202..f9042239 100644 --- a/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx +++ b/src/components/editor/components/toolbar/selection-toolbar/actions/Underline.tsx @@ -24,6 +24,7 @@ export function Underline() {
{t('editor.underline')}