mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-04 08:38:34 +00:00
Closes: BS-2290 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit ## Summary by CodeRabbit - **Bug Fixes** - Improved focus behavior when switching between tabs to prevent unwanted automatic focusing of the content-editable area. - Enhanced selection clearing to avoid unnecessary blurring when the main editable element is already focused. - Refined focus checks in tests to specifically target contenteditable elements, ensuring more accurate validation of focus behavior. - Adjusted test assertions for block selection to be less strict and removed redundant blur operations for smoother test execution. - Updated toolbar dropdown closing method to use keyboard interaction for better reliability. - **New Features** - Added a recoverable property to selection types, improving selection state management and recovery. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
767 lines
20 KiB
TypeScript
767 lines
20 KiB
TypeScript
import { expect } from '@playwright/test';
|
|
|
|
import {
|
|
dragBetweenCoords,
|
|
dragBetweenIndices,
|
|
dragHandleFromBlockToBlockBottomById,
|
|
enterPlaygroundRoom,
|
|
focusRichText,
|
|
initEmptyParagraphState,
|
|
initThreeLists,
|
|
initThreeParagraphs,
|
|
pressEnter,
|
|
pressShiftTab,
|
|
pressTab,
|
|
type,
|
|
} from './utils/actions/index.js';
|
|
import {
|
|
getEditorHostLocator,
|
|
getPageSnapshot,
|
|
initParagraphsByCount,
|
|
} from './utils/actions/misc.js';
|
|
import { assertRichTexts } from './utils/asserts.js';
|
|
import { BLOCK_CHILDREN_CONTAINER_PADDING_LEFT } from './utils/bs-alternative.js';
|
|
import { test } from './utils/playwright.js';
|
|
|
|
test('only have one drag handle in screen', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
const topLeft = await page.evaluate(() => {
|
|
const paragraph = document.querySelector('[data-block-id="2"]');
|
|
const box = paragraph?.getBoundingClientRect();
|
|
if (!box) {
|
|
throw new Error();
|
|
}
|
|
return { x: box.left, y: box.top + 2 };
|
|
}, []);
|
|
|
|
const bottomRight = await page.evaluate(() => {
|
|
const paragraph = document.querySelector('[data-block-id="4"]');
|
|
const box = paragraph?.getBoundingClientRect();
|
|
if (!box) {
|
|
throw new Error();
|
|
}
|
|
return { x: box.right, y: box.bottom - 2 };
|
|
}, []);
|
|
|
|
await page.mouse.move(topLeft.x, topLeft.y);
|
|
const length1 = await page.evaluate(() => {
|
|
const handles = document.querySelectorAll('affine-drag-handle-widget');
|
|
return handles.length;
|
|
}, []);
|
|
expect(length1).toBe(1);
|
|
await page.mouse.move(bottomRight.x, bottomRight.y);
|
|
const length2 = await page.evaluate(() => {
|
|
const handles = document.querySelectorAll('affine-drag-handle-widget');
|
|
return handles.length;
|
|
}, []);
|
|
expect(length2).toBe(1);
|
|
});
|
|
|
|
test('move drag handle in paragraphs', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
await dragHandleFromBlockToBlockBottomById(page, '2', '4');
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
await assertRichTexts(page, ['456', '789', '123']);
|
|
});
|
|
|
|
test('move drag handle in list', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeLists(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
await dragHandleFromBlockToBlockBottomById(page, '5', '3', false);
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
await assertRichTexts(page, ['123', '789', '456']);
|
|
});
|
|
|
|
test('move drag handle in nested block', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
|
|
await focusRichText(page);
|
|
await type(page, '-');
|
|
await page.keyboard.press('Space', { delay: 50 });
|
|
await type(page, '1');
|
|
await pressEnter(page);
|
|
await type(page, '2');
|
|
|
|
await pressEnter(page);
|
|
await pressTab(page);
|
|
await type(page, '21');
|
|
await pressEnter(page);
|
|
await type(page, '22');
|
|
await pressEnter(page);
|
|
await type(page, '23');
|
|
await pressEnter(page);
|
|
await pressShiftTab(page);
|
|
|
|
await type(page, '3');
|
|
|
|
await assertRichTexts(page, ['1', '2', '21', '22', '23', '3']);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(page, '5', '7');
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
await assertRichTexts(page, ['1', '2', '22', '23', '21', '3']);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(page, '3', '8');
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
await assertRichTexts(page, ['2', '22', '23', '21', '3', '1']);
|
|
});
|
|
|
|
test('move drag handle into another block', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
|
|
await focusRichText(page);
|
|
await type(page, '-');
|
|
await page.keyboard.press('Space', { delay: 50 });
|
|
await type(page, '1');
|
|
await pressEnter(page);
|
|
await type(page, '2');
|
|
|
|
await pressEnter(page);
|
|
await pressTab(page);
|
|
await type(page, '21');
|
|
await pressEnter(page);
|
|
await type(page, '22');
|
|
await pressEnter(page);
|
|
await type(page, '23');
|
|
await pressEnter(page);
|
|
await pressShiftTab(page);
|
|
|
|
await type(page, '3');
|
|
|
|
await assertRichTexts(page, ['1', '2', '21', '22', '23', '3']);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(
|
|
page,
|
|
'5',
|
|
'7',
|
|
true,
|
|
2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT
|
|
);
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
await assertRichTexts(page, ['1', '2', '22', '23', '21', '3']);
|
|
// FIXME(DND)
|
|
// await assertBlockChildrenIds(page, '7', ['5']);
|
|
|
|
// await dragHandleFromBlockToBlockBottomById(
|
|
// page,
|
|
// '3',
|
|
// '8',
|
|
// true,
|
|
// 2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT
|
|
// );
|
|
// await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
// await assertRichTexts(page, ['2', '22', '23', '21', '3', '1']);
|
|
// await assertBlockChildrenIds(page, '8', ['3']);
|
|
});
|
|
|
|
test('move to the last block of each level in multi-level nesting', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
|
|
await focusRichText(page);
|
|
await type(page, '-');
|
|
await page.keyboard.press('Space', { delay: 50 });
|
|
await type(page, 'A');
|
|
await pressEnter(page);
|
|
await type(page, 'B');
|
|
await pressEnter(page);
|
|
await type(page, 'C');
|
|
await pressEnter(page);
|
|
await pressTab(page);
|
|
await type(page, 'D');
|
|
await pressEnter(page);
|
|
await type(page, 'E');
|
|
await pressEnter(page);
|
|
await pressTab(page);
|
|
await type(page, 'F');
|
|
await pressEnter(page);
|
|
await type(page, 'G');
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(page, '3', '9');
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_drag_3_9.json`
|
|
);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(
|
|
page,
|
|
'4',
|
|
'3',
|
|
true,
|
|
-(1 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT)
|
|
);
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_drag_4_3.json`
|
|
);
|
|
|
|
await assertRichTexts(page, ['C', 'D', 'E', 'F', 'G', 'A', 'B']);
|
|
await dragHandleFromBlockToBlockBottomById(
|
|
page,
|
|
'3',
|
|
'4',
|
|
true,
|
|
-(2 * BLOCK_CHILDREN_CONTAINER_PADDING_LEFT)
|
|
);
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
|
|
// FIXME(DND)
|
|
// expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
// `${testInfo.title}_drag_3_4.json`
|
|
// );
|
|
//
|
|
// await assertRichTexts(page, ['C', 'D', 'E', 'F', 'G', 'B', 'A']);
|
|
});
|
|
|
|
test('should sync selected-blocks to session-manager when clicking drag handle', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await focusRichText(page, 1);
|
|
const rect = await page.locator('[data-block-id="1"]').boundingBox();
|
|
if (!rect) {
|
|
throw new Error();
|
|
}
|
|
await page.mouse.move(rect.x + 10, rect.y + 10, { steps: 2 });
|
|
|
|
const handle = page.locator('.affine-drag-handle-container');
|
|
await handle.click();
|
|
|
|
await page.keyboard.press('Backspace');
|
|
await assertRichTexts(page, ['456', '789']);
|
|
});
|
|
|
|
test.fixme(
|
|
'should be able to drag & drop multiple blocks',
|
|
async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[0, 0],
|
|
[1, 3],
|
|
{ x: -60, y: 0 },
|
|
{ x: 80, y: 0 },
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(2);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(page, '2', '4', true);
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
|
|
await assertRichTexts(page, ['789', '123', '456']);
|
|
|
|
// Selection is still 2 after drop
|
|
await expect(blockSelections).toHaveCount(2);
|
|
}
|
|
);
|
|
|
|
test.fixme(
|
|
'should be able to drag & drop multiple blocks to nested block',
|
|
async ({ page }, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
|
|
await focusRichText(page);
|
|
await type(page, '-');
|
|
await page.keyboard.press('Space', { delay: 50 });
|
|
await type(page, 'A');
|
|
await pressEnter(page);
|
|
await type(page, 'B');
|
|
await pressEnter(page);
|
|
await type(page, 'C');
|
|
await pressEnter(page);
|
|
await pressTab(page);
|
|
await type(page, 'D');
|
|
await pressEnter(page);
|
|
await type(page, 'E');
|
|
await pressEnter(page);
|
|
await pressTab(page);
|
|
await type(page, 'F');
|
|
await pressEnter(page);
|
|
await type(page, 'G');
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[0, 0],
|
|
[1, 1],
|
|
{ x: -80, y: 0 },
|
|
{ x: 80, y: 0 },
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(2);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(page, '3', '8');
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_finial.json`
|
|
);
|
|
}
|
|
);
|
|
|
|
test('should blur rich-text first on starting block selection', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await expect(page.locator('[contenteditable="true"]:focus')).toHaveCount(1);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(page, '2', '4');
|
|
await expect(page.locator('.affine-drop-indicator')).toBeHidden();
|
|
await assertRichTexts(page, ['456', '789', '123']);
|
|
|
|
await expect(page.locator('[contenteditable="true"]:focus')).toHaveCount(0);
|
|
});
|
|
|
|
test('hide drag handle when mouse is hovering over the title', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
const rect = await page.locator('.affine-note-block-container').boundingBox();
|
|
if (!rect) {
|
|
throw new Error();
|
|
}
|
|
const dragHandle = page.locator('.affine-drag-handle-container');
|
|
// When there is a gap between paragraph blocks, it is the correct behavior for the drag handle to appear
|
|
// when the mouse is over the gap. Therefore, we use rect.y - 20 to make the Y offset greater than the gap between the
|
|
// paragraph blocks.
|
|
await page.mouse.move(rect.x, rect.y - 20, { steps: 2 });
|
|
await expect(dragHandle).toBeHidden();
|
|
|
|
await page.mouse.move(rect.x, rect.y, { steps: 2 });
|
|
expect(await dragHandle.isVisible()).toBe(true);
|
|
await expect(dragHandle).toBeVisible();
|
|
});
|
|
|
|
test.fixme('should create preview when dragging', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
const dragPreview = page.locator('affine-drag-preview');
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[0, 0],
|
|
[1, 3],
|
|
{ x: -60, y: 0 },
|
|
{ x: 80, y: 0 },
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(2);
|
|
|
|
await dragHandleFromBlockToBlockBottomById(
|
|
page,
|
|
'2',
|
|
'4',
|
|
true,
|
|
undefined,
|
|
async () => {
|
|
await expect(dragPreview).toBeVisible();
|
|
await expect(dragPreview.locator('[data-block-id]')).toHaveCount(4);
|
|
}
|
|
);
|
|
});
|
|
|
|
test.fixme(
|
|
'should drag and drop blocks under block-level selection',
|
|
async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[0, 0],
|
|
[1, 3],
|
|
{ x: -60, y: 0 },
|
|
{ x: 80, y: 0 },
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(2);
|
|
|
|
const editorHost = getEditorHostLocator(page);
|
|
const editors = editorHost.locator('rich-text');
|
|
const editorRect0 = await editors.nth(0).boundingBox();
|
|
const editorRect2 = await editors.nth(2).boundingBox();
|
|
if (!editorRect0 || !editorRect2) {
|
|
throw new Error();
|
|
}
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: editorRect0.x - 10,
|
|
y: editorRect0.y + editorRect0.height / 2,
|
|
},
|
|
{
|
|
x: editorRect2.x + 10,
|
|
y: editorRect2.y + editorRect2.height / 2 + 10,
|
|
},
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
await assertRichTexts(page, ['789', '123', '456']);
|
|
await expect(blockSelections).toHaveCount(2);
|
|
}
|
|
);
|
|
|
|
test('should trigger click event on editor container when clicking on blocks under block-level selection', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[0, 0],
|
|
[1, 3],
|
|
{ x: -60, y: 0 },
|
|
{ x: 80, y: 0 },
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(2);
|
|
await expect(page.locator('[contenteditable="true"]:focus')).toHaveCount(0);
|
|
|
|
const editorHost = getEditorHostLocator(page);
|
|
const editors = editorHost.locator('rich-text');
|
|
const editorRect0 = await editors.nth(0).boundingBox();
|
|
if (!editorRect0) {
|
|
throw new Error();
|
|
}
|
|
|
|
await page.mouse.move(
|
|
editorRect0.x + 10,
|
|
editorRect0.y + editorRect0.height / 2
|
|
);
|
|
await page.mouse.down();
|
|
await page.mouse.up();
|
|
await expect(blockSelections).toHaveCount(0);
|
|
await expect(page.locator('[contenteditable="true"]:focus')).toHaveCount(1);
|
|
});
|
|
|
|
test('should get to selected block when dragging unselected block', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await type(page, '123');
|
|
await pressEnter(page);
|
|
await type(page, '456');
|
|
await assertRichTexts(page, ['123', '456']);
|
|
|
|
const editorHost = getEditorHostLocator(page);
|
|
const editors = editorHost.locator('rich-text');
|
|
const editorRect0 = await editors.nth(0).boundingBox();
|
|
const editorRect1 = await editors.nth(1).boundingBox();
|
|
|
|
if (!editorRect0 || !editorRect1) {
|
|
throw new Error();
|
|
}
|
|
|
|
await page.mouse.move(editorRect1.x - 5, editorRect0.y);
|
|
await page.mouse.down();
|
|
await page.mouse.up();
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(1);
|
|
|
|
await page.mouse.move(editorRect1.x - 5, editorRect0.y);
|
|
await page.mouse.down();
|
|
await page.mouse.move(
|
|
editorRect1.x - 5,
|
|
editorRect1.y + editorRect1.height / 2 + 1,
|
|
{
|
|
steps: 10,
|
|
}
|
|
);
|
|
await page.mouse.up();
|
|
|
|
await expect(blockSelections).toHaveCount(1);
|
|
|
|
// FIXME(DND)
|
|
// await assertRichTexts(page, ['456', '123']);
|
|
});
|
|
|
|
test.fixme(
|
|
'should clear the currently selected block when clicked again',
|
|
async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await type(page, '123');
|
|
await pressEnter(page);
|
|
await type(page, '456');
|
|
await assertRichTexts(page, ['123', '456']);
|
|
|
|
const editorHost = getEditorHostLocator(page);
|
|
const editors = editorHost.locator('rich-text');
|
|
const editorRect0 = await editors.nth(0).boundingBox();
|
|
const editorRect1 = await editors.nth(1).boundingBox();
|
|
|
|
if (!editorRect0 || !editorRect1) {
|
|
throw new Error();
|
|
}
|
|
|
|
await page.mouse.move(
|
|
editorRect1.x + 5,
|
|
editorRect1.y + editorRect1.height / 2
|
|
);
|
|
|
|
await page.mouse.move(
|
|
editorRect1.x - 10,
|
|
editorRect1.y + editorRect1.height / 2
|
|
);
|
|
await page.mouse.down();
|
|
await page.mouse.up();
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(1);
|
|
|
|
let selectedBlockRect = await blockSelections.nth(0).boundingBox();
|
|
|
|
if (!selectedBlockRect) {
|
|
throw new Error();
|
|
}
|
|
|
|
expect(editorRect1).toEqual(selectedBlockRect);
|
|
|
|
await page.mouse.move(
|
|
editorRect0.x - 10,
|
|
editorRect0.y + editorRect0.height / 2
|
|
);
|
|
await page.mouse.down();
|
|
await page.mouse.up();
|
|
|
|
await expect(blockSelections).toHaveCount(1);
|
|
|
|
selectedBlockRect = await blockSelections.nth(0).boundingBox();
|
|
|
|
if (!selectedBlockRect) {
|
|
throw new Error();
|
|
}
|
|
|
|
expect(editorRect0).toEqual(selectedBlockRect);
|
|
}
|
|
);
|
|
|
|
test.fixme(
|
|
'should support moving blocks from multiple notes',
|
|
async ({ page }) => {
|
|
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);
|
|
|
|
['123', '456', '789', '987', '654', '321'].forEach(text => {
|
|
const noteId = doc.addBlock('affine:note', {}, rootId);
|
|
doc.addBlock(
|
|
'affine:paragraph',
|
|
{
|
|
text: new window.$blocksuite.store.Text(text),
|
|
},
|
|
noteId
|
|
);
|
|
});
|
|
|
|
doc.resetHistory();
|
|
});
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[1, 0],
|
|
[2, 3],
|
|
{ x: -60, y: 0 },
|
|
{ x: 80, y: 0 },
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(2);
|
|
|
|
const editorHost = getEditorHostLocator(page);
|
|
const editors = editorHost.locator('rich-text');
|
|
const editorRect1 = await editors.nth(1).boundingBox();
|
|
const editorRect3 = await editors.nth(3).boundingBox();
|
|
if (!editorRect1 || !editorRect3) {
|
|
throw new Error();
|
|
}
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: editorRect1.x - 10,
|
|
y: editorRect1.y + editorRect1.height / 2,
|
|
},
|
|
{
|
|
x: editorRect3.x + 10,
|
|
y: editorRect3.y + editorRect3.height / 2 + 10,
|
|
},
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
await assertRichTexts(page, ['123', '987', '456', '789', '654', '321']);
|
|
await expect(blockSelections).toHaveCount(2);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[5, 0],
|
|
[4, 3],
|
|
{ x: -60, y: 0 },
|
|
{ x: 80, y: 0 },
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
const editorRect0 = await editors.nth(0).boundingBox();
|
|
const editorRect5 = await editors.nth(5).boundingBox();
|
|
if (!editorRect0 || !editorRect5) {
|
|
throw new Error();
|
|
}
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: editorRect5.x - 10,
|
|
y: editorRect5.y + editorRect5.height / 2,
|
|
},
|
|
{
|
|
x: editorRect0.x + 10,
|
|
y: editorRect0.y + editorRect0.height / 2 - 5,
|
|
},
|
|
{
|
|
steps: 50,
|
|
}
|
|
);
|
|
|
|
await assertRichTexts(page, ['654', '321', '123', '987', '456', '789']);
|
|
await expect(blockSelections).toHaveCount(2);
|
|
}
|
|
);
|
|
|
|
test('drag handle should show on right block when scroll viewport', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initParagraphsByCount(page, 30);
|
|
|
|
await page.mouse.wheel(0, 200);
|
|
|
|
const editorHost = getEditorHostLocator(page);
|
|
const editors = editorHost.locator('rich-text');
|
|
const blockRect28 = await editors.nth(28).boundingBox();
|
|
if (!blockRect28) {
|
|
throw new Error();
|
|
}
|
|
|
|
await page.mouse.move(blockRect28.x + 10, blockRect28.y + 10);
|
|
const dragHandle = page.locator('.affine-drag-handle-container');
|
|
await expect(dragHandle).toBeVisible();
|
|
|
|
await page.mouse.move(
|
|
blockRect28.x - 10,
|
|
blockRect28.y + blockRect28.height / 2
|
|
);
|
|
await page.mouse.down();
|
|
await page.mouse.up();
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(1);
|
|
|
|
const selectedBlockRect = await blockSelections.nth(0).boundingBox();
|
|
|
|
if (!selectedBlockRect) {
|
|
throw new Error();
|
|
}
|
|
|
|
expect(blockRect28).toEqual(selectedBlockRect);
|
|
});
|