mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
feat(editor): support linked-doc in rich-text column (#9634)
close: BS-2345
This commit is contained in:
@@ -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']);
|
||||
|
||||
99
tests/affine-local/e2e/blocksuite/database/rich-text.spec.ts
Normal file
99
tests/affine-local/e2e/blocksuite/database/rich-text.spec.ts
Normal 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');
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user