mirror of
https://github.com/AppFlowy-IO/AppFlowy-Web.git
synced 2026-03-13 10:00:26 +08:00
224 lines
8.0 KiB
TypeScript
224 lines
8.0 KiB
TypeScript
/**
|
|
* Row Document Tests (Board view)
|
|
*
|
|
* Tests for row document content persistence, focus behavior,
|
|
* and document indicator on cards.
|
|
* Migrated from: cypress/e2e/database/row-document.cy.ts
|
|
*/
|
|
import { test, expect } from '@playwright/test';
|
|
import { v4 as uuidv4 } from 'uuid';
|
|
import {
|
|
BoardSelectors,
|
|
RowDetailSelectors,
|
|
} from '../../support/selectors';
|
|
import { signInAndCreateDatabaseView } from '../../support/database-ui-helpers';
|
|
import { closeRowDetailWithEscape } from '../../support/row-detail-helpers';
|
|
import { generateRandomEmail } from '../../support/test-config';
|
|
|
|
test.describe('Row Document Test', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
page.on('pageerror', (err) => {
|
|
if (
|
|
err.message.includes('Minified React error') ||
|
|
err.message.includes('View not found') ||
|
|
err.message.includes('No workspace or service found') ||
|
|
err.message.includes('ResizeObserver loop')
|
|
) {
|
|
return;
|
|
}
|
|
});
|
|
|
|
await page.setViewportSize({ width: 1280, height: 720 });
|
|
});
|
|
|
|
/**
|
|
* Helper: create a Board and wait for it to be ready
|
|
*/
|
|
async function createBoardAndWait(
|
|
page: import('@playwright/test').Page,
|
|
request: import('@playwright/test').APIRequestContext,
|
|
testEmail: string
|
|
) {
|
|
await signInAndCreateDatabaseView(page, request, testEmail, 'Board', {
|
|
createWaitMs: 7000,
|
|
verify: async (p) => {
|
|
await expect(BoardSelectors.boardContainer(p)).toBeVisible({ timeout: 15000 });
|
|
await p.waitForTimeout(3000);
|
|
await expect(BoardSelectors.cards(p).first()).toBeVisible({ timeout: 15000 });
|
|
},
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Helper: add a new card to the "To Do" column and return the card name
|
|
*/
|
|
async function addNewCard(page: import('@playwright/test').Page, cardName: string) {
|
|
// Find the "To Do" column and click "New"
|
|
const todoColumn = BoardSelectors.boardContainer(page)
|
|
.locator('[data-column-id]')
|
|
.filter({ hasText: 'To Do' });
|
|
await todoColumn.getByText('New').click({ force: true });
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Type the card name
|
|
await page.keyboard.type(cardName, { delay: 30 });
|
|
await page.keyboard.press('Enter');
|
|
await page.waitForTimeout(2000);
|
|
}
|
|
|
|
/**
|
|
* Helper: open a card's row detail modal by clicking on it
|
|
*/
|
|
async function openCard(page: import('@playwright/test').Page, cardName: string) {
|
|
await BoardSelectors.boardContainer(page).getByText(cardName).click({ force: true });
|
|
await expect(RowDetailSelectors.modal(page)).toBeVisible();
|
|
}
|
|
|
|
/**
|
|
* Helper: click into the row document editor
|
|
*/
|
|
async function clickIntoEditor(page: import('@playwright/test').Page) {
|
|
// Wait for editor to load
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Scroll down to make sure editor is visible
|
|
const scrollContainer = page.locator('[role="dialog"]').locator('.appflowy-scroll-container');
|
|
if ((await scrollContainer.count()) > 0) {
|
|
await scrollContainer.evaluate(el => el.scrollTo(0, 9999));
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
// Wait for editor to be ready and click into it
|
|
const editor = page
|
|
.locator('[role="dialog"]')
|
|
.locator('[data-testid="editor-content"], [role="textbox"][contenteditable="true"]')
|
|
.first();
|
|
await expect(editor).toBeVisible({ timeout: 15000 });
|
|
await editor.click({ force: true });
|
|
}
|
|
|
|
test('should persist row document content after closing and reopening modal', async ({
|
|
page,
|
|
request,
|
|
}) => {
|
|
const testEmail = generateRandomEmail();
|
|
const cardName = `Persist-${uuidv4().substring(0, 6)}`;
|
|
const docText = `persist-test-${uuidv4().substring(0, 6)}`;
|
|
|
|
// Given: a board with a new card and its row detail editor open
|
|
await createBoardAndWait(page, request, testEmail);
|
|
await addNewCard(page, cardName);
|
|
await openCard(page, cardName);
|
|
await clickIntoEditor(page);
|
|
|
|
// When: typing multiple lines into the row document
|
|
const line1 = `Line1-${docText}`;
|
|
const line2 = `Line2-${docText}`;
|
|
const line3 = `Line3-${docText}`;
|
|
await page.keyboard.type(`${line1}`, { delay: 50 });
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type(`${line2}`, { delay: 50 });
|
|
await page.keyboard.press('Enter');
|
|
await page.keyboard.type(`${line3}`, { delay: 50 });
|
|
await page.waitForTimeout(2000);
|
|
|
|
// Then: all lines should be visible in the dialog
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await expect(dialog).toContainText(line1);
|
|
await expect(dialog).toContainText(line2);
|
|
await expect(dialog).toContainText(line3);
|
|
|
|
// When: closing the modal
|
|
await dialog
|
|
.locator('.MuiDialogTitle-root, [data-testid="row-detail-header"]')
|
|
.first()
|
|
.click({ force: true });
|
|
await page.waitForTimeout(500);
|
|
await closeRowDetailWithEscape(page);
|
|
await expect(dialog).toHaveCount(0);
|
|
await page.waitForTimeout(3000);
|
|
|
|
// And: reopening the same card
|
|
await openCard(page, cardName);
|
|
await page.waitForTimeout(3000);
|
|
|
|
const scrollContainer = page.locator('[role="dialog"]').locator('.appflowy-scroll-container');
|
|
if ((await scrollContainer.count()) > 0) {
|
|
await scrollContainer.evaluate(el => el.scrollTo(0, 9999));
|
|
await page.waitForTimeout(1000);
|
|
}
|
|
|
|
// Then: the document content should have persisted
|
|
await expect(page.locator('[role="dialog"]')).toContainText(`Line1-${docText}`);
|
|
await expect(page.locator('[role="dialog"]')).toContainText(`Line2-${docText}`);
|
|
await expect(page.locator('[role="dialog"]')).toContainText(`Line3-${docText}`);
|
|
});
|
|
|
|
test('should maintain focus while typing continuously', async ({ page, request }) => {
|
|
const testEmail = generateRandomEmail();
|
|
const cardName = `Focus-${uuidv4().substring(0, 6)}`;
|
|
|
|
// Given: a board with a new card and the editor focused
|
|
await createBoardAndWait(page, request, testEmail);
|
|
await addNewCard(page, cardName);
|
|
await openCard(page, cardName);
|
|
await clickIntoEditor(page);
|
|
|
|
// When: typing a long sentence with delays to simulate real typing
|
|
const longText =
|
|
'This is a test sentence that should be typed without losing focus even after several seconds of typing';
|
|
await page.keyboard.type(longText, { delay: 50 });
|
|
|
|
// Then: the full text should appear in the dialog (focus was maintained)
|
|
await expect(page.locator('[role="dialog"]')).toContainText(longText);
|
|
|
|
// When: closing and reopening the modal
|
|
const dialog = page.locator('[role="dialog"]');
|
|
await dialog
|
|
.locator('.MuiDialogTitle-root, [data-testid="row-detail-header"]')
|
|
.first()
|
|
.click({ force: true });
|
|
await page.waitForTimeout(500);
|
|
await closeRowDetailWithEscape(page);
|
|
await page.waitForTimeout(2000);
|
|
|
|
// And: reopening the same card
|
|
await openCard(page, cardName);
|
|
await page.waitForTimeout(3000);
|
|
|
|
// Then: the content should have persisted
|
|
await expect(page.locator('[role="dialog"]')).toContainText(longText);
|
|
});
|
|
|
|
test('shows row document indicator after editing row document', async ({ page, request }) => {
|
|
const testEmail = generateRandomEmail();
|
|
const cardName = `RowDoc-${uuidv4().substring(0, 6)}`;
|
|
const docText = `row-doc-${uuidv4().substring(0, 6)}`;
|
|
|
|
// Given: a board with a new card visible
|
|
await createBoardAndWait(page, request, testEmail);
|
|
await addNewCard(page, cardName);
|
|
await expect(BoardSelectors.boardContainer(page).getByText(cardName)).toBeVisible({
|
|
timeout: 10000,
|
|
});
|
|
|
|
// When: opening the card and typing into the row document editor
|
|
await openCard(page, cardName);
|
|
await clickIntoEditor(page);
|
|
await page.keyboard.type(docText, { delay: 30 });
|
|
await page.waitForTimeout(1000);
|
|
|
|
// And: closing the modal
|
|
await closeRowDetailWithEscape(page);
|
|
await page.waitForTimeout(1000);
|
|
|
|
// Then: the document indicator icon should appear on the card
|
|
await expect(
|
|
BoardSelectors.boardContainer(page)
|
|
.locator('.board-card')
|
|
.filter({ hasText: cardName })
|
|
.locator('.custom-icon')
|
|
).toBeVisible({ timeout: 15000 });
|
|
});
|
|
});
|