diff --git a/blocksuite/affine/block-table/src/add-button.ts b/blocksuite/affine/block-table/src/add-button.ts
index 6fbbde780..48e01602f 100644
--- a/blocksuite/affine/block-table/src/add-button.ts
+++ b/blocksuite/affine/block-table/src/add-button.ts
@@ -246,6 +246,7 @@ export class AddButton extends SignalWatcher(
this.hoverColumnIndex$.value === this.columns$.value.length - 1;
const dragging = this.columnDragging$.value;
return html`
{
+ await openHomePage(pageA);
+ await clickNewPageButton(pageA);
+ await waitForEditorLoad(pageA);
+ await pageA.keyboard.press('Enter');
+
+ /**
+ * | Cell1 | Cell2 |
+ * | Cell3 | Cell4 |
+ */
+ await createTable(pageA);
+ const cellsInA = pageA.locator('affine-table-cell');
+ const cellCountInA = await cellsInA.count();
+ expect(cellCountInA).toBe(4);
+
+ const currentUrl = pageA.url();
+
+ const pageB = await context.newPage();
+ await pageB.goto(currentUrl);
+ await waitForEditorLoad(pageB);
+ await pageB.keyboard.press('Enter');
+
+ const tableInB = pageB.locator('affine-table');
+ await expect(tableInB).toBeVisible();
+ const cellsInB = tableInB.locator('affine-table-cell');
+ expect(await cellsInB.count()).toBe(cellCountInA);
+
+ expect(await getCellText(pageB, 0)).toBe('Cell1');
+ expect(await getCellText(pageB, 3)).toBe('Cell4');
+
+ await cellsInB.last().hover();
+ const addColumnButton = tableInB.locator('data-testid=add-column-button');
+ expect(await addColumnButton.isVisible()).toBe(true);
+ await addColumnButton.click();
+
+ expect(await cellsInA.count()).toBe(6);
+ expect(await cellsInB.count()).toBe(6);
+
+ // new created cells should be synced to both page
+ /**
+ * | Cell1 | Cell2 | Cell5 |
+ * | Cell3 | Cell4 | Cell6 |
+ */
+ await inputToCell(pageA, 2, 'Cell5');
+ await inputToCell(pageA, 5, 'Cell6');
+ expect(await getCellText(pageB, 2)).toBe('Cell5');
+ expect(await getCellText(pageB, 5)).toBe('Cell6');
+
+ await cellsInB.last().hover();
+ const addRowButton = tableInB.locator('data-testid=add-row-button');
+ expect(await addRowButton.isVisible()).toBe(true);
+ await addRowButton.click();
+
+ expect(await cellsInA.count()).toBe(9);
+ expect(await cellsInB.count()).toBe(9);
+
+ // new created cells should be synced to both page
+ /**
+ * | Cell1 | Cell2 | Cell5 |
+ * | Cell3 | Cell4 | Cell6 |
+ * | Cell7 | Cell8 | Cell9 |
+ */
+ await inputToCell(pageA, 6, 'Cell7');
+ await inputToCell(pageA, 7, 'Cell8');
+ await inputToCell(pageA, 8, 'Cell9');
+ expect(await getCellText(pageB, 6)).toBe('Cell7');
+ expect(await getCellText(pageB, 7)).toBe('Cell8');
+ expect(await getCellText(pageB, 8)).toBe('Cell9');
+
+ // delete a column
+ await cellsInA.nth(1).hover();
+ const dragColumnHandle = cellsInA
+ .nth(1)
+ .locator('data-testid=drag-column-handle');
+ expect(await dragColumnHandle.isVisible()).toBe(true);
+ await dragColumnHandle.click();
+
+ await clickDeleteButtonInTableMenu(pageA);
+ /**
+ * | Cell1 | Cell5 |
+ * | Cell3 | Cell6 |
+ * | Cell7 | Cell9 |
+ */
+ expect(await cellsInA.count()).toBe(6);
+ expect(await cellsInB.count()).toBe(6);
+
+ expect(await getCellText(pageB, 0)).toBe('Cell1');
+ expect(await getCellText(pageB, 1)).toBe('Cell5');
+ expect(await getCellText(pageB, 2)).toBe('Cell3');
+ expect(await getCellText(pageB, 3)).toBe('Cell6');
+ expect(await getCellText(pageB, 4)).toBe('Cell7');
+ expect(await getCellText(pageB, 5)).toBe('Cell9');
+
+ // delete a row
+ await cellsInB.nth(0).hover();
+ const dragRowHandle = cellsInB.nth(0).locator('data-testid=drag-row-handle');
+ expect(await dragRowHandle.isVisible()).toBe(true);
+ await dragRowHandle.click();
+
+ await clickDeleteButtonInTableMenu(pageB);
+ /**
+ * | Cell3 | Cell6 |
+ * | Cell7 | Cell9 |
+ */
+ expect(await cellsInA.count()).toBe(4);
+ expect(await cellsInB.count()).toBe(4);
+
+ expect(await getCellText(pageB, 0)).toBe('Cell3');
+ expect(await getCellText(pageB, 1)).toBe('Cell6');
+ expect(await getCellText(pageB, 2)).toBe('Cell7');
+ expect(await getCellText(pageB, 3)).toBe('Cell9');
+});
diff --git a/tests/kit/package.json b/tests/kit/package.json
index 47718e922..9be18de96 100644
--- a/tests/kit/package.json
+++ b/tests/kit/package.json
@@ -7,7 +7,8 @@
"./electron": "./src/electron.ts",
"./mobile": "./src/mobile.ts",
"./playwright": "./src/playwright.ts",
- "./utils/*": "./src/utils/*.ts"
+ "./utils/*": "./src/utils/*.ts",
+ "./bs/*": "./src/bs/*.ts"
},
"devDependencies": {
"@affine-tools/utils": "workspace:*",
diff --git a/tests/kit/src/bs/misc.ts b/tests/kit/src/bs/misc.ts
new file mode 100644
index 000000000..b321102f8
--- /dev/null
+++ b/tests/kit/src/bs/misc.ts
@@ -0,0 +1,7 @@
+import type { Page } from '@playwright/test';
+
+export async function waitNextFrame(page: Page) {
+ await page.evaluate(
+ () => new Promise(resolve => requestAnimationFrame(resolve))
+ );
+}
diff --git a/tests/kit/src/bs/table.ts b/tests/kit/src/bs/table.ts
new file mode 100644
index 000000000..bdd368bcb
--- /dev/null
+++ b/tests/kit/src/bs/table.ts
@@ -0,0 +1,53 @@
+import { expect, type Locator, type Page } from '@playwright/test';
+
+import { waitNextFrame } from './misc';
+
+export async function createTable(page: Page) {
+ await page.keyboard.press('/');
+ await expect(page.locator('affine-slash-menu .slash-menu')).toBeVisible();
+ await page.keyboard.type('table');
+ await page.keyboard.press('Enter');
+ await waitNextFrame(page);
+ const table = page.locator('affine-table');
+
+ await expect(table).toBeVisible();
+ const cells = table.locator('affine-table-cell');
+ const cellCount = await cells.count();
+ expect(cellCount).toBe(4);
+
+ for (let i = 0; i < cellCount; i++) {
+ await inputToCell(page, i, `Cell${i + 1}`);
+ }
+}
+
+export async function getCellText(page: Page, nth: number, table?: Locator) {
+ table = table ?? page.locator('affine-table');
+ const cell = table.locator('affine-table-cell').nth(nth);
+ await cell.hover();
+ await waitNextFrame(page);
+ return cell.locator('v-line').innerText();
+}
+
+export async function inputToCell(
+ page: Page,
+ nth: number,
+ text: string,
+ table?: Locator
+) {
+ table = table ?? page.locator('affine-table');
+ const cell = table.locator('affine-table-cell').nth(nth);
+ await cell.hover();
+ await waitNextFrame(page);
+ await cell.click();
+ await cell.click();
+ await waitNextFrame(page);
+ await page.keyboard.type(text, { delay: 20 });
+}
+
+export async function clickDeleteButtonInTableMenu(page: Page) {
+ const menu = page.locator('affine-menu');
+ await expect(menu).toBeVisible();
+ const deleteButton = menu.getByText('Delete');
+ await expect(deleteButton).toBeVisible();
+ await deleteButton.click();
+}