mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-13 21:05:19 +00:00
### Changed
This pr split the old `edgeless-selected-rect` into four focused modules:
- `edgeless-selected-rect`: Provide an entry point for user operation on view layer only, no further logic here.
- `GfxViewInteractionExtension`: Allow you to plug in custom resize/rotate behaviors for block or canvas element. If you don’t register an extension, it falls back to the default behaviours.
- `InteractivityManager`: Provide the API that accepts resize/rotate requests, invokes any custom behaviors you’ve registered, tracks the lifecycle and intermediate state, then hands off to the math engine.
- `ResizeController`: A pure math engine that listens for pointer moves and pointer ups and calculates new sizes, positions, and angles. It doesn’t call any business APIs.
### Customizing an element’s resize/rotate behavior
Call `GfxViewInteractionExtension` with the element’s flavour or type plus a config object. In the config you can define:
- `resizeConstraint` (min/max width & height, lock ratio)
- `handleResize(context)` method that returns an object containing `beforeResize`、`onResizeStart`、`onResizeMove`、`onResizeEnd`
- `handleRotate(context)` method that returns an object containing `beforeRotate`、`onRotateStart`、`onRotateMove`、`onRotateEnd`
```typescript
import { GfxViewInteractionExtension } from '@blocksuite/std/gfx';
GfxViewInteractionExtension(
flavourOrElementType,
{
resizeConstraint: {
minWidth,
maxWidth,
lockRatio,
minHeight,
maxHeight
},
handleResize(context) {
return {
beforeResize(context) {},
onResizeStart(context) {},
onResizeMove(context) {},
onResizeEnd(context) {}
};
},
handleRotate(context) {
return {
beforeRotate(context) {},
onRotateStart(context) {},
onRotateMove(context) {},
onRotateEnd(context) {}
};
}
}
);
```
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit
- **New Features**
- Added interaction extensions for edgeless variants of attachment, bookmark, edgeless text, embedded docs, images, notes, frames, AI chat blocks, and various embed blocks (Figma, GitHub, HTML, iframe, Loom, YouTube).
- Introduced interaction extensions for graphical elements including connectors, groups, mind maps, shapes, and text, supporting constrained resizing and rotation disabling where applicable.
- Implemented a unified interaction extension framework enabling configurable resize and rotate lifecycle handlers.
- Enhanced autocomplete overlay behavior based on selection context.
- **Refactor**
- Removed legacy resize manager and element-specific resize/rotate logic, replacing with a centralized, extensible interaction system.
- Simplified resize handle rendering to a data-driven approach with improved cursor management.
- Replaced complex cursor rotation calculations with fixed-angle mappings for resize handles.
- Streamlined selection rectangle component to use interactivity services for resize and rotate handling.
- **Bug Fixes**
- Fixed connector update triggers to reduce unnecessary updates.
- Improved resize constraints enforcement and interaction state tracking.
- **Tests**
- Refined end-to-end tests to use higher-level resize utilities and added finer-grained assertions on element dimensions.
- Enhanced mouse movement granularity in drag tests for better simulation fidelity.
- **Chores**
- Added new workspace dependencies and project references for the interaction framework modules.
- Extended public API exports to include new interaction types and extensions.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
601 lines
17 KiB
TypeScript
601 lines
17 KiB
TypeScript
import type { EdgelessTextBlockComponent } from '@blocksuite/affine/blocks/edgeless-text';
|
|
import { Bound } from '@blocksuite/affine/global/gfx';
|
|
import { expect, type Page } from '@playwright/test';
|
|
|
|
import {
|
|
autoFit,
|
|
captureHistory,
|
|
cutByKeyboard,
|
|
dragBetweenIndices,
|
|
enterPlaygroundRoom,
|
|
getEdgelessSelectedRect,
|
|
getPageSnapshot,
|
|
initEmptyEdgelessState,
|
|
pasteByKeyboard,
|
|
pressArrowDown,
|
|
pressArrowLeft,
|
|
pressArrowRight,
|
|
pressArrowUp,
|
|
pressBackspace,
|
|
pressEnter,
|
|
pressEscape,
|
|
selectAllByKeyboard,
|
|
setEdgelessTool,
|
|
switchEditorMode,
|
|
toViewCoord,
|
|
type,
|
|
undoByKeyboard,
|
|
waitNextFrame,
|
|
} from '../utils/actions/index.js';
|
|
import {
|
|
assertBlockChildrenIds,
|
|
assertBlockFlavour,
|
|
assertBlockTextContent,
|
|
assertRichTextInlineDeltas,
|
|
assertRichTextInlineRange,
|
|
} from '../utils/asserts.js';
|
|
import { test } from '../utils/playwright.js';
|
|
import { getFormatBar } from '../utils/query.js';
|
|
|
|
async function assertEdgelessTextModelRect(
|
|
page: Page,
|
|
id: string,
|
|
bound: Bound
|
|
) {
|
|
const realXYWH = await page.evaluate(id => {
|
|
const block = window.host.view.getBlock(id) as EdgelessTextBlockComponent;
|
|
return block?.model.xywh;
|
|
}, id);
|
|
const realBound = Bound.deserialize(realXYWH);
|
|
expect(realBound.x).toBeCloseTo(bound.x, 0);
|
|
expect(realBound.y).toBeCloseTo(bound.y, 0);
|
|
expect(realBound.w).toBeCloseTo(bound.w, 0);
|
|
expect(realBound.h).toBeCloseTo(bound.h, 0);
|
|
}
|
|
|
|
test.describe('edgeless text block', () => {
|
|
test.beforeEach(async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyEdgelessState(page);
|
|
await switchEditorMode(page);
|
|
});
|
|
|
|
test('add text block in default mode', async ({ page }) => {
|
|
await setEdgelessTool(page, 'default');
|
|
await page.mouse.dblclick(130, 140, {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
|
|
// https://github.com/toeverything/blocksuite/pull/8574
|
|
await pressBackspace(page);
|
|
|
|
await type(page, 'aaa');
|
|
await pressEnter(page);
|
|
await type(page, 'bbb');
|
|
await pressEnter(page);
|
|
await type(page, 'ccc');
|
|
|
|
await assertBlockFlavour(page, 4, 'affine:edgeless-text');
|
|
await assertBlockFlavour(page, 5, 'affine:paragraph');
|
|
await assertBlockFlavour(page, 6, 'affine:paragraph');
|
|
await assertBlockFlavour(page, 7, 'affine:paragraph');
|
|
await assertBlockChildrenIds(page, '4', ['5', '6', '7']);
|
|
await assertBlockTextContent(page, 5, 'aaa');
|
|
await assertBlockTextContent(page, 6, 'bbb');
|
|
await assertBlockTextContent(page, 7, 'ccc');
|
|
|
|
await dragBetweenIndices(page, [1, 1], [3, 2]);
|
|
await captureHistory(page);
|
|
await pressBackspace(page);
|
|
await assertBlockChildrenIds(page, '4', ['5']);
|
|
await assertBlockTextContent(page, 5, 'ac');
|
|
|
|
await undoByKeyboard(page);
|
|
await assertBlockChildrenIds(page, '4', ['5', '6', '7']);
|
|
await assertBlockTextContent(page, 5, 'aaa');
|
|
await assertBlockTextContent(page, 6, 'bbb');
|
|
await assertBlockTextContent(page, 7, 'ccc');
|
|
|
|
const { boldBtn } = getFormatBar(page);
|
|
await boldBtn.click();
|
|
await assertRichTextInlineDeltas(
|
|
page,
|
|
[
|
|
{
|
|
insert: 'a',
|
|
},
|
|
{
|
|
insert: 'aa',
|
|
attributes: {
|
|
bold: true,
|
|
},
|
|
},
|
|
],
|
|
1
|
|
);
|
|
await assertRichTextInlineDeltas(
|
|
page,
|
|
[
|
|
{
|
|
insert: 'bbb',
|
|
attributes: {
|
|
bold: true,
|
|
},
|
|
},
|
|
],
|
|
2
|
|
);
|
|
await assertRichTextInlineDeltas(
|
|
page,
|
|
[
|
|
{
|
|
insert: 'cc',
|
|
attributes: {
|
|
bold: true,
|
|
},
|
|
},
|
|
{
|
|
insert: 'c',
|
|
},
|
|
],
|
|
3
|
|
);
|
|
|
|
await pressArrowRight(page);
|
|
await assertRichTextInlineRange(page, 3, 2);
|
|
await pressArrowUp(page);
|
|
await assertRichTextInlineRange(page, 2, 2);
|
|
});
|
|
|
|
test('edgeless text width auto-adjusting', async ({ page }) => {
|
|
await setEdgelessTool(page, 'default');
|
|
const point = await toViewCoord(page, [0, 0]);
|
|
await page.mouse.dblclick(point[0], point[1], {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 220, 26));
|
|
|
|
await type(page, 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa');
|
|
await waitNextFrame(page, 1000);
|
|
// just width changed
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 323, 26));
|
|
|
|
await type(page, '\nbbb');
|
|
// width not changed, height changed
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 323, 50));
|
|
await type(page, '\nccccccccccccccccccccccccccccccccccccccccccccccccc');
|
|
await waitNextFrame(page, 1000);
|
|
|
|
// width and height changed
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 395, 74));
|
|
|
|
// blur, max width set to true
|
|
await page.mouse.click(point[0] - 50, point[1], {
|
|
delay: 100,
|
|
});
|
|
await page.mouse.dblclick(point[0], point[1], {
|
|
delay: 100,
|
|
});
|
|
// to end of line
|
|
await pressArrowDown(page, 3);
|
|
await type(page, 'dddddddddddddddddddd');
|
|
await waitNextFrame(page, 1000);
|
|
// width not changed, height changed
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 395, 98));
|
|
});
|
|
|
|
test('edgeless text width fixed when drag moving', async ({ page }) => {
|
|
// https://github.com/toeverything/blocksuite/pull/7486
|
|
|
|
await setEdgelessTool(page, 'default');
|
|
await page.mouse.dblclick(130, 140, {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
await type(page, 'aaaaaa bbbb ');
|
|
await pressEscape(page);
|
|
await waitNextFrame(page);
|
|
await page.mouse.click(130, 140);
|
|
await page.mouse.down();
|
|
await page.mouse.move(800, 800, {
|
|
steps: 15,
|
|
});
|
|
|
|
const rect = await page.evaluate(() => {
|
|
const container = document.querySelector(
|
|
'.edgeless-text-block-container'
|
|
)!;
|
|
return container.getBoundingClientRect();
|
|
});
|
|
const modelXYWH = await page.evaluate(() => {
|
|
const block = window.host.view.getBlock(
|
|
'4'
|
|
) as EdgelessTextBlockComponent;
|
|
return block.model.xywh;
|
|
});
|
|
const bound = Bound.deserialize(modelXYWH);
|
|
expect(rect.width).toBeCloseTo(bound.w);
|
|
expect(rect.height).toBeCloseTo(bound.h);
|
|
});
|
|
|
|
test('When creating edgeless text, if the input is empty, it will be automatically deleted', async ({
|
|
page,
|
|
}) => {
|
|
await setEdgelessTool(page, 'default');
|
|
await page.mouse.dblclick(130, 140, {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
let block = page.locator('affine-edgeless-text[data-block-id="4"]');
|
|
expect(await block.isVisible()).toBe(true);
|
|
await page.mouse.click(0, 0);
|
|
expect(await block.isVisible()).toBe(false);
|
|
|
|
block = page.locator('affine-edgeless-text[data-block-id="6"]');
|
|
expect(await block.isVisible()).not.toBe(true);
|
|
await page.mouse.dblclick(130, 140, {
|
|
delay: 100,
|
|
});
|
|
expect(await block.isVisible()).toBe(true);
|
|
await type(page, '\na');
|
|
expect(await block.isVisible()).toBe(true);
|
|
await page.mouse.click(0, 0);
|
|
expect(await block.isVisible()).not.toBe(false);
|
|
});
|
|
|
|
test('edgeless text should maintain selection when deleting across multiple lines', async ({
|
|
page,
|
|
}) => {
|
|
// https://github.com/toeverything/blocksuite/pull/7443
|
|
|
|
await setEdgelessTool(page, 'default');
|
|
await page.mouse.dblclick(130, 140, {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
await type(page, 'aaaa\nbbbb');
|
|
await assertBlockTextContent(page, 5, 'aaaa');
|
|
await assertBlockTextContent(page, 6, 'bbbb');
|
|
|
|
await pressArrowLeft(page);
|
|
await page.keyboard.down('Shift');
|
|
await pressArrowLeft(page, 3);
|
|
await pressArrowUp(page);
|
|
await pressArrowRight(page);
|
|
await page.keyboard.up('Shift');
|
|
await pressBackspace(page);
|
|
await assertBlockTextContent(page, 5, 'ab');
|
|
await type(page, 'sss\n');
|
|
await assertBlockTextContent(page, 5, 'asss');
|
|
await assertBlockTextContent(page, 7, 'b');
|
|
});
|
|
|
|
test('edgeless text should not blur after pressing backspace', async ({
|
|
page,
|
|
}) => {
|
|
// https://github.com/toeverything/blocksuite/pull/7555
|
|
|
|
await setEdgelessTool(page, 'default');
|
|
await page.mouse.dblclick(130, 140, {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
await type(page, 'a');
|
|
await assertBlockTextContent(page, 5, 'a');
|
|
await pressBackspace(page);
|
|
await type(page, 'b');
|
|
await assertBlockTextContent(page, 5, 'b');
|
|
});
|
|
|
|
// FIXME(@flrande): This test fails randomly on CI
|
|
test.fixme('edgeless text max width', async ({ page }) => {
|
|
await setEdgelessTool(page, 'default');
|
|
const point = await toViewCoord(page, [0, 0]);
|
|
await page.mouse.dblclick(point[0], point[1], {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 50, 56));
|
|
|
|
await type(page, 'aaaaaa');
|
|
await waitNextFrame(page);
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 71, 56));
|
|
await type(page, 'bbb');
|
|
await waitNextFrame(page, 200);
|
|
// height not changed
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 98, 56));
|
|
|
|
// blur
|
|
await page.mouse.click(0, 0);
|
|
// select text element
|
|
await page.mouse.click(point[0] + 10, point[1] + 10);
|
|
|
|
let selectedRect = await getEdgelessSelectedRect(page);
|
|
|
|
// move cursor to the right edge and drag it to resize the width of text
|
|
|
|
// from left to right
|
|
await page.mouse.move(
|
|
selectedRect.x + selectedRect.width,
|
|
selectedRect.y + selectedRect.height / 2
|
|
);
|
|
await page.mouse.down();
|
|
await page.mouse.move(
|
|
selectedRect.x + selectedRect.width + 30,
|
|
selectedRect.y + selectedRect.height / 2,
|
|
{
|
|
steps: 10,
|
|
}
|
|
);
|
|
await page.mouse.up();
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 128, 56));
|
|
selectedRect = await getEdgelessSelectedRect(page);
|
|
let textRect = await page
|
|
.locator('affine-edgeless-text[data-block-id="4"]')
|
|
.boundingBox();
|
|
expect(selectedRect).not.toBeNull();
|
|
expect(selectedRect.width).toBeCloseTo(textRect!.width);
|
|
expect(selectedRect.height).toBeCloseTo(textRect!.height);
|
|
expect(selectedRect.x).toBeCloseTo(textRect!.x);
|
|
expect(selectedRect.y).toBeCloseTo(textRect!.y);
|
|
|
|
// from right to left
|
|
await page.mouse.move(
|
|
selectedRect.x + selectedRect.width,
|
|
selectedRect.y + selectedRect.height / 2
|
|
);
|
|
await page.mouse.down();
|
|
await page.mouse.move(
|
|
selectedRect.x + selectedRect.width - 45,
|
|
selectedRect.y + selectedRect.height / 2,
|
|
{
|
|
steps: 10,
|
|
}
|
|
);
|
|
await page.mouse.up();
|
|
// height changed
|
|
await assertEdgelessTextModelRect(page, '4', new Bound(-25, -25, 83, 80));
|
|
selectedRect = await getEdgelessSelectedRect(page);
|
|
textRect = await page
|
|
.locator('affine-edgeless-text[data-block-id="4"]')
|
|
.boundingBox();
|
|
expect(selectedRect).not.toBeNull();
|
|
expect(selectedRect.width).toBeCloseTo(textRect!.width);
|
|
expect(selectedRect.height).toBeCloseTo(textRect!.height);
|
|
expect(selectedRect.x).toBeCloseTo(textRect!.x);
|
|
expect(selectedRect.y).toBeCloseTo(textRect!.y);
|
|
});
|
|
|
|
test('min width limit for embed block', async ({ page }, testInfo) => {
|
|
await setEdgelessTool(page, 'default');
|
|
const point = await toViewCoord(page, [0, 0]);
|
|
await page.mouse.dblclick(point[0], point[1], {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page, 2000);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
await type(page, '@');
|
|
await pressEnter(page);
|
|
await waitNextFrame(page, 200);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_add_linked_doc.json`
|
|
);
|
|
|
|
await page.locator('affine-reference').hover();
|
|
await page.getByLabel('Switch view').click();
|
|
await page.getByTestId('link-to-card').click();
|
|
await autoFit(page);
|
|
|
|
await waitNextFrame(page);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_link_to_card.json`
|
|
);
|
|
|
|
// blur
|
|
await page.mouse.click(0, 0);
|
|
// select text element
|
|
await page.mouse.click(point[0] + 10, point[1] + 10);
|
|
await waitNextFrame(page, 200);
|
|
const selectedRect0 = await getEdgelessSelectedRect(page);
|
|
|
|
// from right to left
|
|
await page.mouse.move(
|
|
selectedRect0.x + selectedRect0.width,
|
|
selectedRect0.y + selectedRect0.height / 2
|
|
);
|
|
await page.mouse.down();
|
|
await page.mouse.move(
|
|
selectedRect0.x,
|
|
selectedRect0.y + selectedRect0.height / 2,
|
|
{
|
|
steps: 10,
|
|
}
|
|
);
|
|
await page.mouse.up();
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_link_to_card_min_width.json`
|
|
);
|
|
|
|
const selectedRect1 = await getEdgelessSelectedRect(page);
|
|
// from left to right
|
|
await page.mouse.move(
|
|
selectedRect1.x + selectedRect1.width,
|
|
selectedRect1.y + selectedRect1.height / 2
|
|
);
|
|
await page.mouse.down();
|
|
await page.mouse.move(
|
|
selectedRect0.x + selectedRect0.width + 45,
|
|
selectedRect1.y + selectedRect1.height / 2,
|
|
{
|
|
steps: 10,
|
|
}
|
|
);
|
|
await page.mouse.up();
|
|
|
|
const selectedRect2 = await getEdgelessSelectedRect(page);
|
|
expect(selectedRect2.width).toBeCloseTo(selectedRect1.width + 45);
|
|
expect(selectedRect2.height).toBeCloseTo(selectedRect1.height);
|
|
expect(selectedRect2.x).toBeCloseTo(selectedRect1.x);
|
|
expect(selectedRect2.y).toBeCloseTo(selectedRect1.y);
|
|
});
|
|
|
|
test('cut edgeless text', async ({ page }) => {
|
|
await setEdgelessTool(page, 'default');
|
|
await page.mouse.dblclick(130, 140, {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
await type(page, 'aaaa\nbbbb\ncccc');
|
|
|
|
const edgelessText = page.locator('affine-edgeless-text');
|
|
const paragraph = page.locator('affine-edgeless-text affine-paragraph');
|
|
|
|
expect(await edgelessText.count()).toBe(1);
|
|
expect(await paragraph.count()).toBe(3);
|
|
|
|
await page.mouse.click(50, 50, {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
await page.mouse.click(130, 140, {
|
|
delay: 100,
|
|
});
|
|
await cutByKeyboard(page);
|
|
expect(await edgelessText.count()).toBe(0);
|
|
expect(await paragraph.count()).toBe(0);
|
|
|
|
await pasteByKeyboard(page);
|
|
expect(await edgelessText.count()).toBe(1);
|
|
expect(await paragraph.count()).toBe(3);
|
|
});
|
|
|
|
test('latex in edgeless text', async ({ page }) => {
|
|
await setEdgelessTool(page, 'default');
|
|
await page.mouse.dblclick(130, 140, {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page);
|
|
await type(page, '$$bbb$$ ');
|
|
await assertRichTextInlineDeltas(
|
|
page,
|
|
[
|
|
{
|
|
insert: ' ',
|
|
attributes: {
|
|
latex: 'bbb',
|
|
},
|
|
},
|
|
],
|
|
1
|
|
);
|
|
|
|
await page.locator('affine-latex-node').click();
|
|
await waitNextFrame(page);
|
|
await type(page, 'ccc');
|
|
const menu = page.locator('latex-editor-menu');
|
|
const confirm = menu.locator('.latex-editor-confirm');
|
|
await confirm.click();
|
|
await assertRichTextInlineDeltas(
|
|
page,
|
|
[
|
|
{
|
|
insert: ' ',
|
|
attributes: {
|
|
latex: 'bbbccc',
|
|
},
|
|
},
|
|
],
|
|
1
|
|
);
|
|
|
|
await page.locator('affine-latex-node').click();
|
|
await page.locator('.latex-editor-hint').click();
|
|
await type(page, 'sss');
|
|
await assertRichTextInlineDeltas(
|
|
page,
|
|
[
|
|
{
|
|
insert: ' ',
|
|
attributes: {
|
|
latex: 'bbbccc',
|
|
},
|
|
},
|
|
],
|
|
1
|
|
);
|
|
await page.locator('latex-editor-unit').click();
|
|
await selectAllByKeyboard(page);
|
|
await type(page, 'sss');
|
|
await confirm.click();
|
|
await assertRichTextInlineDeltas(
|
|
page,
|
|
[
|
|
{
|
|
insert: ' ',
|
|
attributes: {
|
|
latex: 'sss',
|
|
},
|
|
},
|
|
],
|
|
1
|
|
);
|
|
});
|
|
});
|
|
|
|
test('press backspace at the start of first line when edgeless text exist', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await page.evaluate(() => {
|
|
const { doc } = window;
|
|
const rootId = doc.addBlock('affine:page', {
|
|
title: new window.$blocksuite.store.Text(),
|
|
});
|
|
doc.addBlock('affine:surface', {}, rootId);
|
|
doc.addBlock('affine:note', {}, rootId);
|
|
|
|
// do not add paragraph block
|
|
|
|
doc.resetHistory();
|
|
});
|
|
await switchEditorMode(page);
|
|
|
|
await setEdgelessTool(page, 'default');
|
|
const point = await toViewCoord(page, [0, 0]);
|
|
await page.mouse.dblclick(point[0], point[1], {
|
|
delay: 100,
|
|
});
|
|
await waitNextFrame(page, 2000);
|
|
await type(page, 'aaa');
|
|
|
|
await waitNextFrame(page);
|
|
await switchEditorMode(page);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_note_empty.json`
|
|
);
|
|
|
|
await page.locator('.affine-page-root-block-container').click();
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_note_not_empty.json`
|
|
);
|
|
|
|
await type(page, 'bbb');
|
|
await pressArrowLeft(page, 3);
|
|
await pressBackspace(page);
|
|
await waitNextFrame(page);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_finial.json`
|
|
);
|
|
});
|