feat(editor): support linked-doc in rich-text column (#9634)

close: BS-2345
This commit is contained in:
zzj3720
2025-01-10 14:43:40 +00:00
parent cc08094b17
commit c016f8e37e
6 changed files with 488 additions and 152 deletions

View File

@@ -1,11 +1,10 @@
import { test } from '@affine-test/kit/playwright';
import { openHomePage } from '@affine-test/kit/utils/load-page';
import { waitForEditorLoad } from '@affine-test/kit/utils/page-logic';
import {
initDatabaseWithRows,
pasteExcelData,
selectFirstCell,
addRows,
initDatabaseByOneStep,
pasteString,
selectCell,
verifyCellContents,
} from './utils';
@@ -14,16 +13,14 @@ test.describe('Database Clipboard Operations', () => {
page,
}) => {
// Open the home page and wait for the editor to load
await openHomePage(page);
await waitForEditorLoad(page);
await initDatabaseByOneStep(page);
// Create a database block with two rows
await initDatabaseWithRows(page, 2);
await addRows(page, 2);
// Select the first cell and paste data
await selectFirstCell(page);
await selectCell(page, 0, false);
const mockExcelData = 'Cell 1A\tCell 1B\nCell 2A\tCell 2B';
await pasteExcelData(page, mockExcelData);
await pasteString(page, mockExcelData);
// Verify cell contents
await verifyCellContents(page, [
@@ -38,16 +35,14 @@ test.describe('Database Clipboard Operations', () => {
page,
}) => {
// Open the home page and wait for the editor to load
await openHomePage(page);
await waitForEditorLoad(page);
await initDatabaseByOneStep(page);
// Create a database block with two rows
await initDatabaseWithRows(page, 2);
await addRows(page, 2);
// Select the first cell and paste data with empty cells
await selectFirstCell(page);
await selectCell(page, 0, false);
const mockExcelData = 'Cell 1A\t\nCell 2A\tCell 2B';
await pasteExcelData(page, mockExcelData);
await pasteString(page, mockExcelData);
// Verify cell contents including empty cells
await verifyCellContents(page, ['Cell 1A', '', 'Cell 2A', 'Cell 2B']);
@@ -55,16 +50,14 @@ test.describe('Database Clipboard Operations', () => {
test('handle pasting data larger than selected area', async ({ page }) => {
// Open the home page and wait for the editor to load
await openHomePage(page);
await waitForEditorLoad(page);
await initDatabaseByOneStep(page);
// Create a database block with one row
await initDatabaseWithRows(page, 1);
await addRows(page, 1);
// Select the first cell and paste data larger than table
await selectFirstCell(page);
await selectCell(page, 0, false);
const mockExcelData = 'Cell 1A\tCell 1B\nCell 2A\tCell 2B';
await pasteExcelData(page, mockExcelData);
await pasteString(page, mockExcelData);
// Verify only the cells that exist are filled
await verifyCellContents(page, ['Cell 1A', 'Cell 1B']);

View File

@@ -0,0 +1,99 @@
import { test } from '@affine-test/kit/playwright';
import { openHomePage } from '@affine-test/kit/utils/load-page';
import {
clickNewPageButton,
waitForEditorLoad,
} from '@affine-test/kit/utils/page-logic';
import { expect } from '@playwright/test';
import {
addColumn,
createDatabaseBlock,
createNewPage,
gotoContentFromTitle,
selectCell,
} from './utils';
test.describe('Database Rich Text Column', () => {
test('paste document link into rich text cell', async ({ page }) => {
// Step 1: Open home page
await openHomePage(page);
await waitForEditorLoad(page);
// Step 2: Create a new page
await createNewPage(page);
await gotoContentFromTitle(page);
// Step 3: Create a database in the page
await createDatabaseBlock(page);
// Step 4: Add a text column
await addColumn(page, 'Text');
// Step 5: Create a new page to get its link
await clickNewPageButton(page, 'Test Page');
const pageUrl = page.url();
// Step 6: Go back to database page
await page.goBack();
await waitForEditorLoad(page);
// Step 7: Select and edit the rich text cell
const richTextCell = await selectCell(page, 2);
// Step 8: Paste the document link
await page.evaluate(url => {
const clipboardData = new DataTransfer();
clipboardData.setData('text/plain', url);
const pasteEvent = new ClipboardEvent('paste', {
clipboardData,
bubbles: true,
cancelable: true,
});
document.activeElement?.dispatchEvent(pasteEvent);
}, pageUrl);
// Step 9: Verify the result
const referenceTitle = richTextCell.locator('.affine-reference-title');
await expect(referenceTitle).toBeVisible();
await expect(referenceTitle).toContainText('Test Page');
});
test('add document link via @ in rich text cell', async ({ page }) => {
// Step 1: Open home page
await openHomePage(page);
await waitForEditorLoad(page);
// Step 2: Create a new page
await createNewPage(page);
await gotoContentFromTitle(page);
// Step 3: Create a database in the page
await createDatabaseBlock(page);
// Step 4: Add a text column
await addColumn(page, 'Text');
// Step 5: Create a new page as reference target
await clickNewPageButton(page, 'Reference Target');
await page.goBack();
await waitForEditorLoad(page);
// Step 6: Select and edit the rich text cell
const richTextCell = await selectCell(page, 2);
await richTextCell.click();
await page.keyboard.type('@');
// Step 7: Wait for reference picker and select the page
const linkedDocPopover = page.locator('.linked-doc-popover');
await expect(linkedDocPopover).toBeVisible();
const targetPage = linkedDocPopover.getByText('Reference Target');
await targetPage.click();
// Step 8: Verify the result
const referenceTitle = richTextCell.locator('.affine-reference-title');
await expect(referenceTitle).toBeVisible();
await expect(referenceTitle).toContainText('Reference Target');
});
});

View File

@@ -1,45 +1,36 @@
import { openHomePage } from '@affine-test/kit/utils/load-page';
import {
addDatabase,
clickNewPageButton,
waitForEditorLoad,
} from '@affine-test/kit/utils/page-logic';
import type { Page } from '@playwright/test';
import { expect } from '@playwright/test';
/**
* Create a new database block in the current page
*/
export async function createDatabaseBlock(page: Page) {
export async function createNewPage(page: Page) {
await clickNewPageButton(page);
await page.waitForTimeout(500);
}
export const gotoContentFromTitle = async (page: Page) => {
await page.keyboard.press('Enter');
};
export async function createDatabaseBlock(page: Page) {
await addDatabase(page);
}
/**
* Initialize a database with specified number of rows
*/
export async function initDatabaseWithRows(page: Page, rowCount: number) {
await createDatabaseBlock(page);
export async function addRows(page: Page, rowCount: number) {
for (let i = 0; i < rowCount; i++) {
await addDatabaseRow(page);
}
}
/**
* Add a new row to the database
*/
export async function addDatabaseRow(page: Page) {
const addButton = page.locator('.data-view-table-group-add-row');
await addButton.waitFor();
await addButton.click();
}
/**
* Simulate pasting Excel data into database
* @param page Playwright page object
* @param data Tab-separated text data with newlines for rows
*/
export async function pasteExcelData(page: Page, data: string) {
export async function pasteString(page: Page, data: string) {
await page.evaluate(data => {
const clipboardData = new DataTransfer();
clipboardData.setData('text/plain', data);
@@ -48,24 +39,25 @@ export async function pasteExcelData(page: Page, data: string) {
bubbles: true,
cancelable: true,
});
document.activeElement?.dispatchEvent(pasteEvent);
const activeElement = document.activeElement;
if (activeElement) {
pasteEvent.preventDefault();
activeElement.dispatchEvent(pasteEvent);
}
}, data);
}
/**
* Select the first cell in the database
*/
export async function selectFirstCell(page: Page) {
const firstCell = page.locator('affine-database-cell-container').first();
await firstCell.waitFor();
await firstCell.click();
export async function selectCell(page: Page, nth: number, editing = true) {
const firstCell = page.locator('affine-database-cell-container').nth(nth);
// First click for focus
await firstCell.click({ delay: 100 });
// Second click for edit mode
if (editing) {
await firstCell.click({ delay: 100 });
}
return firstCell;
}
/**
* Verify the contents of multiple cells in sequence
* @param page Playwright page object
* @param expectedContents Array of expected cell contents in order
*/
export async function verifyCellContents(
page: Page,
expectedContents: string[]
@@ -78,3 +70,43 @@ export async function verifyCellContents(
);
}
}
export async function selectColumnType(page: Page, columnType: string) {
const typeMenu = page.locator('affine-menu').getByText('Type');
await page.waitForTimeout(100);
await typeMenu.hover();
await page.waitForTimeout(100);
await page.keyboard.type(columnType);
await page.waitForTimeout(100);
await page.keyboard.press('ArrowDown');
await page.waitForTimeout(100);
await page.keyboard.press('Enter');
await page.waitForTimeout(100);
}
export async function addColumn(page: Page, type: string) {
await clickAddColumnButton(page);
await selectColumnType(page, type);
}
export async function clickAddColumnButton(page: Page) {
const addColumnButton = page.locator('.header-add-column-button');
await addColumnButton.click();
}
export async function changeColumnType(
page: Page,
columnIndex: number,
columnType: string
) {
const header = page.locator('affine-database-header-column').nth(columnIndex);
await header.click();
await selectColumnType(page, columnType);
}
export const initDatabaseByOneStep = async (page: Page) => {
await openHomePage(page);
await createNewPage(page);
await waitForEditorLoad(page);
await gotoContentFromTitle(page);
await createDatabaseBlock(page);
};