Merge pull request #87 from AppFlowy-IO/editor_test2

Editor test2
This commit is contained in:
Nathan.fooo
2025-09-24 23:21:18 +08:00
committed by GitHub
10 changed files with 204 additions and 21 deletions

View File

@@ -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');
});
});
});

View File

@@ -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

View File

@@ -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');
});
});
});

View File

@@ -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
*/

View File

@@ -43,6 +43,7 @@ export function SelectionToolbar() {
<SelectionToolbarContext.Provider value={{ visible, forceShow, rePosition, getDecorateState }}>
<div
ref={ref}
data-testid="selection-toolbar"
className={
'selection-toolbar pointer-events-none transform transition-opacity duration-200 absolute z-[100] flex min-h-[32px] w-fit flex-grow items-center rounded-lg bg-[var(--fill-toolbar)] px-2 opacity-0 shadow-lg'
}

View File

@@ -25,6 +25,7 @@ export function Bold() {
<ActionButton
onClick={onClick}
active={isActivated}
data-testid="toolbar-bold-button"
tooltip={
<>
<div>{t('toolbar.bold')}</div>

View File

@@ -24,6 +24,7 @@ export function InlineCode() {
<ActionButton
onClick={onClick}
active={isActivated}
data-testid="toolbar-code-button"
tooltip={
<>
<div>{t('editor.embedCode')}</div>

View File

@@ -24,6 +24,7 @@ export function Italic() {
<ActionButton
onClick={onClick}
active={isActivated}
data-testid="toolbar-italic-button"
tooltip={
<>
<div>{t('toolbar.italic')}</div>

View File

@@ -24,6 +24,7 @@ export function StrikeThrough() {
<ActionButton
onClick={onClick}
active={isActivated}
data-testid="toolbar-strikethrough-button"
tooltip={
<>
<div>{t('editor.strikethrough')}</div>

View File

@@ -24,6 +24,7 @@ export function Underline() {
<ActionButton
onClick={onClick}
active={isActivated}
data-testid="toolbar-underline-button"
tooltip={
<>
<div>{t('editor.underline')}</div>