mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
1426 lines
38 KiB
TypeScript
1426 lines
38 KiB
TypeScript
import type { Page } from '@playwright/test';
|
|
import { expect } from '@playwright/test';
|
|
|
|
import {
|
|
activeEmbed,
|
|
clickBlockDragHandle,
|
|
copyByKeyboard,
|
|
dragBetweenCoords,
|
|
dragBetweenIndices,
|
|
dragEmbedResizeByTopLeft,
|
|
enterPlaygroundRoom,
|
|
focusRichText,
|
|
getIndexCoordinate,
|
|
getPageSnapshot,
|
|
getRichTextBoundingBox,
|
|
initEmptyParagraphState,
|
|
initImageState,
|
|
initMultipleNoteWithParagraphState,
|
|
initParagraphsByCount,
|
|
initThreeLists,
|
|
initThreeParagraphs,
|
|
pasteByKeyboard,
|
|
pressBackspace,
|
|
pressEnter,
|
|
pressEscape,
|
|
pressForwardDelete,
|
|
pressShiftTab,
|
|
pressSpace,
|
|
pressTab,
|
|
redoByClick,
|
|
redoByKeyboard,
|
|
resetHistory,
|
|
selectAllByKeyboard,
|
|
shamefullyBlurActiveElement,
|
|
type,
|
|
undoByClick,
|
|
undoByKeyboard,
|
|
waitNextFrame,
|
|
} from '../utils/actions/index.js';
|
|
import {
|
|
assertAlmostEqual,
|
|
assertBlockCount,
|
|
assertRichTexts,
|
|
} from '../utils/asserts.js';
|
|
import { test } from '../utils/playwright.js';
|
|
|
|
test('block level range delete', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
await resetHistory(page);
|
|
|
|
const box123 = await getRichTextBoundingBox(page, '2');
|
|
const above123 = { x: box123.left, y: box123.top - 10 };
|
|
|
|
const box789 = await getRichTextBoundingBox(page, '4');
|
|
const below789 = { x: box789.right - 10, y: box789.bottom + 10 };
|
|
|
|
await dragBetweenCoords(page, below789, above123);
|
|
await pressBackspace(page);
|
|
await assertBlockCount(page, 'paragraph', 1);
|
|
await assertRichTexts(page, ['']);
|
|
|
|
await waitNextFrame(page);
|
|
await undoByClick(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await redoByClick(page);
|
|
await assertRichTexts(page, ['']);
|
|
});
|
|
|
|
test('block level range delete by forwardDelete', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
await resetHistory(page);
|
|
|
|
const box123 = await getRichTextBoundingBox(page, '2');
|
|
const above123 = { x: box123.left, y: box123.top - 10 };
|
|
|
|
const box789 = await getRichTextBoundingBox(page, '4');
|
|
const below789 = { x: box789.right - 10, y: box789.bottom + 10 };
|
|
|
|
await dragBetweenCoords(page, below789, above123);
|
|
await pressForwardDelete(page);
|
|
await waitNextFrame(page);
|
|
await assertBlockCount(page, 'paragraph', 1);
|
|
await assertRichTexts(page, ['']);
|
|
|
|
await waitNextFrame(page);
|
|
await undoByClick(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await redoByClick(page);
|
|
await assertRichTexts(page, ['']);
|
|
});
|
|
|
|
// XXX: Doesn't simulate full user operation due to backspace cursor issue in Playwright.
|
|
test('select all and delete', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
await selectAllByKeyboard(page);
|
|
await selectAllByKeyboard(page);
|
|
await selectAllByKeyboard(page);
|
|
await shamefullyBlurActiveElement(page);
|
|
await pressBackspace(page);
|
|
await focusRichText(page, 0);
|
|
await type(page, 'abc');
|
|
await assertRichTexts(page, ['abc']);
|
|
});
|
|
|
|
test('select all and delete by forwardDelete', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
await selectAllByKeyboard(page);
|
|
await selectAllByKeyboard(page);
|
|
await selectAllByKeyboard(page);
|
|
await shamefullyBlurActiveElement(page);
|
|
await pressForwardDelete(page);
|
|
await focusRichText(page, 0);
|
|
await type(page, 'abc');
|
|
await assertRichTexts(page, ['abc']);
|
|
});
|
|
|
|
test('select all should work for multiple notes in doc mode', async ({
|
|
page,
|
|
}) => {
|
|
const n = 4;
|
|
await enterPlaygroundRoom(page);
|
|
await initMultipleNoteWithParagraphState(page, undefined, n);
|
|
|
|
await focusRichText(page, 0);
|
|
await type(page, '123');
|
|
await focusRichText(page, 1);
|
|
await type(page, '456');
|
|
await selectAllByKeyboard(page);
|
|
await selectAllByKeyboard(page);
|
|
await selectAllByKeyboard(page);
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(n);
|
|
});
|
|
|
|
async function clickListIcon(page: Page, i = 0) {
|
|
const locator = page.locator('.affine-list-block__prefix').nth(i);
|
|
await locator.click({ force: true });
|
|
}
|
|
|
|
test('click the list icon can select and copy', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeLists(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
await clickListIcon(page, 0);
|
|
// copy 123
|
|
await copyByKeyboard(page);
|
|
|
|
await focusRichText(page, 2);
|
|
await pasteByKeyboard(page);
|
|
await assertRichTexts(page, ['123', '456', '789123']);
|
|
|
|
// copy 789123
|
|
await clickListIcon(page, 2);
|
|
await copyByKeyboard(page);
|
|
|
|
await focusRichText(page, 0);
|
|
await pasteByKeyboard(page);
|
|
await assertRichTexts(page, ['123789123', '456', '789123']);
|
|
});
|
|
|
|
test('click the list icon can select and delete', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeLists(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await clickListIcon(page, 0);
|
|
await waitNextFrame(page);
|
|
await pressBackspace(page);
|
|
await assertRichTexts(page, ['', '456', '789']);
|
|
await clickListIcon(page, 0);
|
|
await waitNextFrame(page);
|
|
await pressBackspace(page);
|
|
await assertRichTexts(page, ['', '']);
|
|
});
|
|
|
|
test('click the list icon can select and delete by forwardDelete', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeLists(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await clickListIcon(page, 0);
|
|
await waitNextFrame(page);
|
|
await pressForwardDelete(page);
|
|
await assertRichTexts(page, ['', '456', '789']);
|
|
await clickListIcon(page, 0);
|
|
await waitNextFrame(page);
|
|
await pressForwardDelete(page);
|
|
await assertRichTexts(page, ['', '']);
|
|
});
|
|
|
|
test('selection on heavy page', async ({ page }) => {
|
|
await page
|
|
.locator('body')
|
|
.evaluate(element => (element.style.padding = '50px'));
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
for (let i = 0; i < 5; i++) {
|
|
await type(page, `Line ${i + 1}`);
|
|
await pressEnter(page);
|
|
}
|
|
const [first, last] = await page.evaluate(() => {
|
|
const first = document.querySelector('[data-block-id="2"]');
|
|
if (!first) {
|
|
throw new Error();
|
|
}
|
|
|
|
const last = document.querySelector('[data-block-id="6"]');
|
|
if (!last) {
|
|
throw new Error();
|
|
}
|
|
return [first.getBoundingClientRect(), last.getBoundingClientRect()];
|
|
});
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: first.x - 1,
|
|
y: first.y - 1,
|
|
},
|
|
{
|
|
x: last.x + 1,
|
|
y: last.y + 1,
|
|
},
|
|
{
|
|
beforeMouseUp: async () => {
|
|
const rect = await page
|
|
.locator('.affine-page-dragging-area')
|
|
.evaluate(element => element.getBoundingClientRect());
|
|
assertAlmostEqual(rect.x, first.x - 1, 1);
|
|
assertAlmostEqual(rect.y, first.y - 1, 1);
|
|
assertAlmostEqual(rect.right, last.x + 1, 1);
|
|
assertAlmostEqual(rect.bottom, last.y + 1, 1);
|
|
},
|
|
}
|
|
);
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(5);
|
|
});
|
|
|
|
test('should indent multi-selection block', async ({ page }, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
const coord = await getIndexCoordinate(page, [1, 2]);
|
|
|
|
// blur
|
|
await page.mouse.click(0, 0);
|
|
await page.mouse.move(coord.x - 26 - 24, coord.y - 10, { steps: 20 });
|
|
await page.mouse.down();
|
|
// ←
|
|
await page.mouse.move(coord.x + 20, coord.y + 50, { steps: 20 });
|
|
await page.mouse.up();
|
|
|
|
await page.keyboard.press('Tab');
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}.json`
|
|
);
|
|
});
|
|
|
|
test('should unindent multi-selection block', async ({ page }, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
let coord = await getIndexCoordinate(page, [1, 2]);
|
|
|
|
// blur
|
|
await page.mouse.click(0, 0);
|
|
await page.mouse.move(coord.x - 26 - 24, coord.y - 10, { steps: 20 });
|
|
await page.mouse.down();
|
|
// ←
|
|
await page.mouse.move(coord.x + 20, coord.y + 50, { steps: 20 });
|
|
await page.mouse.up();
|
|
|
|
await page.keyboard.press('Tab');
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
coord = await getIndexCoordinate(page, [1, 2]);
|
|
|
|
// blur
|
|
await page.mouse.click(0, 0);
|
|
await page.mouse.move(coord.x - 26 - 50, coord.y, { steps: 20 });
|
|
await page.mouse.down();
|
|
// ←
|
|
await page.mouse.move(coord.x + 20, coord.y + 30, { steps: 20 });
|
|
await page.mouse.up();
|
|
|
|
await pressShiftTab(page);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_final.json`
|
|
);
|
|
});
|
|
|
|
// ↑
|
|
test('should keep selection state when scrolling backward', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
await pressEnter(page);
|
|
}
|
|
|
|
await type(page, '987');
|
|
await pressEnter(page);
|
|
await type(page, '654');
|
|
await pressEnter(page);
|
|
await type(page, '321');
|
|
|
|
const data = Array.from({ length: 5 }, () => '');
|
|
data.unshift('123', '456', '789');
|
|
data.push('987', '654', '321');
|
|
await assertRichTexts(page, data);
|
|
|
|
const [, container, distance] = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const distance = viewport.scrollHeight - viewport.clientHeight;
|
|
viewport.scrollTo(0, distance);
|
|
|
|
const container = viewport.querySelector(
|
|
'.affine-block-children-container'
|
|
);
|
|
if (!container) {
|
|
throw new Error();
|
|
}
|
|
return [
|
|
viewport.getBoundingClientRect(),
|
|
container.getBoundingClientRect(),
|
|
distance,
|
|
] as const;
|
|
});
|
|
|
|
await page.mouse.move(0, 0);
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: container.right + 1,
|
|
y: container.bottom,
|
|
},
|
|
{
|
|
x: container.right - 1,
|
|
y: 1,
|
|
},
|
|
{
|
|
// dont release mouse
|
|
beforeMouseUp: async () => {
|
|
const count = distance / (10 * 0.25);
|
|
await page.waitForTimeout((1000 / 60) * count);
|
|
},
|
|
}
|
|
);
|
|
|
|
const scrollTop = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(3 + 5 + 3);
|
|
expect(scrollTop).toBe(0);
|
|
});
|
|
|
|
// ↓
|
|
test('should keep selection state when scrolling forward', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
await pressEnter(page);
|
|
}
|
|
|
|
await type(page, '987');
|
|
await pressEnter(page);
|
|
await type(page, '654');
|
|
await pressEnter(page);
|
|
await type(page, '321');
|
|
|
|
const data = Array.from({ length: 5 }, () => '');
|
|
data.unshift('123', '456', '789');
|
|
data.push('987', '654', '321');
|
|
await assertRichTexts(page, data);
|
|
|
|
const [viewport, container, distance] = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const distance = viewport.scrollHeight - viewport.clientHeight;
|
|
const container = viewport.querySelector(
|
|
'.affine-block-children-container'
|
|
);
|
|
if (!container) {
|
|
throw new Error();
|
|
}
|
|
return [
|
|
viewport.getBoundingClientRect(),
|
|
container.getBoundingClientRect(),
|
|
distance,
|
|
] as const;
|
|
});
|
|
|
|
await page.mouse.move(0, 0);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: container.right + 1,
|
|
y: container.top + 1,
|
|
},
|
|
{
|
|
x: container.right - 1,
|
|
y: viewport.height - 1,
|
|
},
|
|
{
|
|
// dont release mouse
|
|
beforeMouseUp: async () => {
|
|
const count = distance / (10 * 0.25);
|
|
await page.waitForTimeout((1000 / 60) * count);
|
|
},
|
|
}
|
|
);
|
|
|
|
const scrollTop = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(3 + 5 + 3);
|
|
// See https://jestjs.io/docs/expect#tobeclosetonumber-numdigits
|
|
// Math.abs(scrollTop - distance) < Math.pow(10, -1 * -0.01)/2 = 0.511646496140377
|
|
expect(scrollTop).toBeCloseTo(distance, -0.01);
|
|
});
|
|
|
|
// ↑
|
|
test('should keep selection state when scrolling backward with the scroll wheel', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
await pressEnter(page);
|
|
}
|
|
|
|
await type(page, '987');
|
|
await pressEnter(page);
|
|
await type(page, '654');
|
|
await pressEnter(page);
|
|
await type(page, '321');
|
|
|
|
const data = Array.from({ length: 5 }, () => '');
|
|
data.unshift('123', '456', '789');
|
|
data.push('987', '654', '321');
|
|
await assertRichTexts(page, data);
|
|
|
|
const [last, distance] = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const distance = viewport.scrollHeight - viewport.clientHeight;
|
|
viewport.scrollTo(0, distance);
|
|
const container = viewport.querySelector(
|
|
'affine-note .affine-block-children-container'
|
|
);
|
|
if (!container) {
|
|
throw new Error();
|
|
}
|
|
const last = container.lastElementChild;
|
|
if (!last) {
|
|
throw new Error();
|
|
}
|
|
return [last.getBoundingClientRect(), distance] as const;
|
|
});
|
|
await page.waitForTimeout(250);
|
|
|
|
await page.mouse.move(0, 0);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: last.right + 1,
|
|
y: last.top + 1,
|
|
},
|
|
{
|
|
x: last.right - 1,
|
|
y: last.top - 1,
|
|
},
|
|
{
|
|
// dont release mouse
|
|
beforeMouseUp: async () => {
|
|
await page.mouse.wheel(0, -distance * 2);
|
|
await page.waitForTimeout(250);
|
|
},
|
|
}
|
|
);
|
|
|
|
// get count with scroll wheel
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
const count0 = await rects.count();
|
|
const scrollTop0 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
await page.mouse.move(0, 0);
|
|
|
|
await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const distance = viewport.scrollHeight - viewport.clientHeight;
|
|
viewport.scrollTo(0, distance);
|
|
});
|
|
await page.waitForTimeout(250);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: last.right + 1,
|
|
y: last.top + 1,
|
|
},
|
|
{
|
|
x: last.right - 1,
|
|
y: last.top - 1 - distance,
|
|
}
|
|
);
|
|
|
|
// get count with moving mouse
|
|
const count1 = await rects.count();
|
|
const scrollTop1 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
expect(count0).toBe(count1);
|
|
expect(scrollTop0).toBe(0);
|
|
expect(scrollTop1).toBeCloseTo(distance, -0.5);
|
|
});
|
|
|
|
// ↓
|
|
test('should keep selection state when scrolling forward with the scroll wheel', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
await pressEnter(page);
|
|
}
|
|
|
|
await type(page, '987');
|
|
await pressEnter(page);
|
|
await type(page, '654');
|
|
await pressEnter(page);
|
|
await type(page, '321');
|
|
|
|
const data = Array.from({ length: 5 }, () => '');
|
|
data.unshift('123', '456', '789');
|
|
data.push('987', '654', '321');
|
|
await assertRichTexts(page, data);
|
|
|
|
const [first, distance] = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const distance = viewport.scrollHeight - viewport.clientHeight;
|
|
const container = viewport.querySelector(
|
|
'affine-note .affine-block-children-container'
|
|
);
|
|
if (!container) {
|
|
throw new Error();
|
|
}
|
|
const first = container.firstElementChild;
|
|
if (!first) {
|
|
throw new Error();
|
|
}
|
|
return [first.getBoundingClientRect(), distance] as const;
|
|
});
|
|
|
|
await page.mouse.move(0, 0);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: first.left - 10,
|
|
y: first.top - 10,
|
|
},
|
|
{
|
|
x: first.left + 1,
|
|
y: first.top + 1,
|
|
},
|
|
{
|
|
// don't release mouse
|
|
beforeMouseUp: async () => {
|
|
await page.mouse.wheel(0, distance * 2);
|
|
await page.waitForTimeout(250);
|
|
},
|
|
}
|
|
);
|
|
|
|
// get count with scroll wheel
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
const count0 = await rects.count();
|
|
const scrollTop0 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
await page.mouse.move(0, 0);
|
|
|
|
await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
viewport.scrollTo(0, 0);
|
|
});
|
|
await page.waitForTimeout(250);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: first.left - 10,
|
|
y: first.top - 10,
|
|
},
|
|
{
|
|
x: first.left + 1,
|
|
y: first.top + 1 + distance,
|
|
}
|
|
);
|
|
|
|
// get count with moving mouse
|
|
const count1 = await rects.count();
|
|
const scrollTop1 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
expect(count0).toBe(count1);
|
|
expect(scrollTop0).toBeCloseTo(distance, -0.8);
|
|
expect(scrollTop1).toBe(0);
|
|
});
|
|
|
|
test('should not clear selected rects when clicking on scrollbar', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
await pressEnter(page);
|
|
}
|
|
|
|
await type(page, '987');
|
|
await pressEnter(page);
|
|
await type(page, '654');
|
|
await pressEnter(page);
|
|
await type(page, '321');
|
|
|
|
const [viewport, first, distance] = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const distance = viewport.scrollHeight - viewport.clientHeight;
|
|
viewport.scrollTo(0, distance / 2);
|
|
const container = viewport.querySelector(
|
|
'affine-note .affine-block-children-container'
|
|
);
|
|
if (!container) {
|
|
throw new Error();
|
|
}
|
|
const first = container.firstElementChild;
|
|
if (!first) {
|
|
throw new Error();
|
|
}
|
|
return [
|
|
viewport.getBoundingClientRect(),
|
|
first.getBoundingClientRect(),
|
|
distance,
|
|
] as const;
|
|
});
|
|
|
|
await page.mouse.move(0, 0);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: first.left - 10,
|
|
y: first.top - 10,
|
|
},
|
|
{
|
|
x: first.right + 10,
|
|
y: first.bottom + distance / 2,
|
|
}
|
|
);
|
|
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
const count0 = await rects.count();
|
|
const scrollTop0 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
await page.mouse.click(viewport.right, distance / 2);
|
|
|
|
const count1 = await rects.count();
|
|
const scrollTop1 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
expect(count0).toBeGreaterThan(0);
|
|
expect(scrollTop0).toBeCloseTo(distance / 2, -0.01);
|
|
expect(count0).toBe(count1);
|
|
expect(scrollTop0).toBeCloseTo(scrollTop1, -0.01);
|
|
});
|
|
|
|
test('should not clear selected rects when scrolling the wheel', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
await pressEnter(page);
|
|
}
|
|
|
|
await type(page, '987');
|
|
await pressEnter(page);
|
|
await type(page, '654');
|
|
await pressEnter(page);
|
|
await type(page, '321');
|
|
|
|
const [viewport, first, distance] = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const distance = viewport.scrollHeight - viewport.clientHeight;
|
|
viewport.scrollTo(0, distance / 2);
|
|
const container = viewport.querySelector(
|
|
'affine-note .affine-block-children-container'
|
|
);
|
|
if (!container) {
|
|
throw new Error();
|
|
}
|
|
const first = container.firstElementChild;
|
|
if (!first) {
|
|
throw new Error();
|
|
}
|
|
return [
|
|
viewport.getBoundingClientRect(),
|
|
first.getBoundingClientRect(),
|
|
distance,
|
|
] as const;
|
|
});
|
|
|
|
await page.mouse.move(0, 0);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: first.left - 10,
|
|
y: first.top - 10,
|
|
},
|
|
{
|
|
x: first.right + 10,
|
|
y: first.bottom + distance / 2,
|
|
}
|
|
);
|
|
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
const count0 = await rects.count();
|
|
|
|
await page.mouse.wheel(viewport.right, -distance / 4);
|
|
await waitNextFrame(page);
|
|
|
|
const count1 = await rects.count();
|
|
|
|
expect(count0).toBeGreaterThan(0);
|
|
expect(count0).toBe(count1);
|
|
|
|
await page.mouse.wheel(viewport.right, distance / 4);
|
|
await waitNextFrame(page);
|
|
|
|
const count2 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const rects = viewport.querySelectorAll('affine-block-selection');
|
|
const visibleRects = Array.from(rects).filter(rect => {
|
|
const display = window.getComputedStyle(rect).display;
|
|
return display !== 'none';
|
|
});
|
|
return visibleRects.length;
|
|
});
|
|
|
|
expect(count0).toBe(count2);
|
|
});
|
|
|
|
test('should refresh selected rects when resizing the window/viewport', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
for (let i = 0; i < 6; i++) {
|
|
await pressEnter(page);
|
|
}
|
|
|
|
await type(page, '987');
|
|
await pressEnter(page);
|
|
await type(page, '654');
|
|
await pressEnter(page);
|
|
await type(page, '321');
|
|
|
|
const [viewport, first, distance] = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const distance = viewport.scrollHeight - viewport.clientHeight;
|
|
viewport.scrollTo(0, distance / 2);
|
|
const container = viewport.querySelector(
|
|
'affine-note .affine-block-children-container'
|
|
);
|
|
if (!container) {
|
|
throw new Error();
|
|
}
|
|
const first = container.firstElementChild;
|
|
if (!first) {
|
|
throw new Error();
|
|
}
|
|
return [
|
|
viewport.getBoundingClientRect(),
|
|
first.getBoundingClientRect(),
|
|
distance,
|
|
] as const;
|
|
});
|
|
|
|
await page.mouse.move(0, 0);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: first.left - 1,
|
|
y: first.top - 1,
|
|
},
|
|
{
|
|
x: first.left + 1,
|
|
y: first.top + distance / 2,
|
|
}
|
|
);
|
|
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
const count0 = await rects.count();
|
|
const scrollTop0 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
await page.mouse.click(viewport.right, first.top + distance / 2);
|
|
|
|
const size = page.viewportSize();
|
|
|
|
if (!size) {
|
|
throw new Error();
|
|
}
|
|
|
|
await page.setViewportSize({
|
|
width: size.width - 100,
|
|
height: size.height - 100,
|
|
});
|
|
await page.waitForTimeout(250);
|
|
|
|
const count1 = await rects.count();
|
|
const scrollTop1 = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
|
|
expect(count0).toBe(count1);
|
|
expect(scrollTop0).toBeCloseTo(scrollTop1, -0.01);
|
|
});
|
|
|
|
test('should clear block selection before native selection', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
// `123`
|
|
const first = await page.evaluate(() => {
|
|
const first = document.querySelector('[data-block-id="2"]');
|
|
if (!first) {
|
|
throw new Error();
|
|
}
|
|
return first.getBoundingClientRect();
|
|
});
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: first.left - 10,
|
|
y: first.top - 10,
|
|
},
|
|
{
|
|
x: first.left + 1,
|
|
y: first.top + 1,
|
|
}
|
|
);
|
|
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
const count0 = await rects.count();
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[1, 3],
|
|
[1, 0],
|
|
{ x: 0, y: 0 },
|
|
{ x: 0, y: 0 }
|
|
);
|
|
|
|
const count1 = await rects.count();
|
|
const textCount = await page.evaluate(() => {
|
|
return window.getSelection()?.rangeCount || 0;
|
|
});
|
|
|
|
expect(count0).toBe(1);
|
|
expect(count1).toBe(0);
|
|
expect(textCount).toBe(1);
|
|
});
|
|
|
|
test('should not be misaligned when the editor container has padding or margin', async ({
|
|
page,
|
|
}) => {
|
|
await page.locator('body').evaluate(element => {
|
|
element.style.margin = '50px';
|
|
element.style.padding = '50px';
|
|
});
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
// `123`, `789`
|
|
const [first, last] = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
const container = viewport.querySelector(
|
|
'affine-note .affine-block-children-container'
|
|
);
|
|
if (!container) {
|
|
throw new Error();
|
|
}
|
|
const first = container.firstElementChild;
|
|
if (!first) {
|
|
throw new Error();
|
|
}
|
|
const last = container.lastElementChild;
|
|
if (!last) {
|
|
throw new Error();
|
|
}
|
|
return [first.getBoundingClientRect(), last.getBoundingClientRect()];
|
|
});
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: first.left - 10,
|
|
y: first.top - 10,
|
|
},
|
|
{
|
|
x: last.left + 1,
|
|
y: last.top + 1,
|
|
}
|
|
);
|
|
|
|
const rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(3);
|
|
});
|
|
|
|
test('undo should clear block selection', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
|
|
await type(page, 'hello');
|
|
await pressEnter(page);
|
|
await type(page, 'world');
|
|
await pressEnter(page);
|
|
|
|
const rect = await getRichTextBoundingBox(page, '2');
|
|
await dragBetweenCoords(
|
|
page,
|
|
{ x: rect.x - 5, y: rect.y - 5 },
|
|
{ x: rect.x + 5, y: rect.y + rect.height }
|
|
);
|
|
|
|
await redoByKeyboard(page);
|
|
const selectedBlocks = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(selectedBlocks).toHaveCount(1);
|
|
|
|
await undoByKeyboard(page);
|
|
await expect(selectedBlocks).toHaveCount(0);
|
|
});
|
|
|
|
test('should not draw rect for sub selected blocks when entering tab key', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
const coord = await getIndexCoordinate(page, [1, 3]);
|
|
|
|
// blur
|
|
await page.mouse.click(20, 20);
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{ x: coord.x - 60, y: coord.y + 10 },
|
|
{ x: coord.x + 100, y: coord.y + 30 },
|
|
{ steps: 50 }
|
|
);
|
|
|
|
await pressTab(page);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}.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('*:focus')).toHaveCount(1);
|
|
|
|
const coord = await getIndexCoordinate(page, [1, 2]);
|
|
await dragBetweenCoords(
|
|
page,
|
|
{ x: coord.x - 30, y: coord.y - 10 },
|
|
{ x: coord.x + 20, y: coord.y + 50 }
|
|
);
|
|
|
|
await expect(page.locator('*:focus')).toHaveCount(0);
|
|
});
|
|
|
|
test('should not show option menu of image on block selection', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initImageState(page);
|
|
await activeEmbed(page);
|
|
|
|
await expect(page.locator('.affine-image-toolbar-container')).toHaveCount(1);
|
|
|
|
await pressEnter(page);
|
|
|
|
const imageRect = await page.locator('affine-image').boundingBox();
|
|
if (!imageRect) {
|
|
throw new Error();
|
|
}
|
|
|
|
await dragBetweenCoords(
|
|
page,
|
|
{
|
|
x: imageRect.x + imageRect.width + 60,
|
|
y: imageRect.y + imageRect.height / 2 + 10,
|
|
},
|
|
{
|
|
x: imageRect.x - 100,
|
|
y: imageRect.y + imageRect.height / 2,
|
|
}
|
|
);
|
|
|
|
await page.waitForTimeout(50);
|
|
|
|
await expect(page.locator('.affine-image-toolbar-container')).toHaveCount(0);
|
|
await expect(
|
|
page.locator('affine-block-selection').locator('visible=true')
|
|
).toHaveCount(1);
|
|
});
|
|
|
|
test('click bottom of page and if the last is embed block, editor should insert a new editable block', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initImageState(page);
|
|
await activeEmbed(page);
|
|
await dragEmbedResizeByTopLeft(page);
|
|
|
|
const hostRect = await page.evaluate(() => {
|
|
const host = document.querySelector('editor-host');
|
|
if (!host) {
|
|
throw new Error("Can't find doc viewport");
|
|
}
|
|
return host.getBoundingClientRect();
|
|
});
|
|
|
|
await page.mouse.click(hostRect.x + hostRect.width / 2, hostRect.bottom - 10);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}.json`
|
|
);
|
|
});
|
|
|
|
test('should select blocks when pressing escape', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await focusRichText(page, 2);
|
|
await page.keyboard.press('Escape');
|
|
await expect(
|
|
page.locator('affine-block-selection').locator('visible=true')
|
|
).toHaveCount(1);
|
|
await page.keyboard.press('Escape');
|
|
|
|
const cords = await getIndexCoordinate(page, [1, 2]);
|
|
await page.mouse.move(cords.x + 10, cords.y + 10, { steps: 20 });
|
|
await page.mouse.down();
|
|
await page.mouse.move(cords.x + 20, cords.y + 30, { steps: 20 });
|
|
await page.mouse.up();
|
|
|
|
await page.keyboard.press('Escape');
|
|
await expect(
|
|
page.locator('affine-block-selection').locator('visible=true')
|
|
).toHaveCount(1);
|
|
});
|
|
|
|
test('should un-select blocks when pressing escape', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
await focusRichText(page, 2);
|
|
await pressEscape(page);
|
|
await expect(
|
|
page.locator('affine-block-selection').locator('visible=true')
|
|
).toHaveCount(1);
|
|
|
|
await pressEscape(page);
|
|
await expect(
|
|
page.locator('affine-block-selection').locator('visible=true')
|
|
).toHaveCount(0);
|
|
|
|
await focusRichText(page, 2);
|
|
await pressEnter(page);
|
|
await type(page, '-');
|
|
await pressSpace(page);
|
|
await clickListIcon(page, 0);
|
|
await expect(
|
|
page.locator('affine-block-selection').locator('visible=true')
|
|
).toHaveCount(1);
|
|
|
|
await pressEscape(page);
|
|
await expect(
|
|
page.locator('affine-block-selection').locator('visible=true')
|
|
).toHaveCount(0);
|
|
});
|
|
|
|
test('verify cursor position after changing block type', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await type(page, 'hello');
|
|
const anchorOffset = await page.evaluate(() => {
|
|
return window.getSelection()?.anchorOffset || 0;
|
|
});
|
|
expect(anchorOffset).toBe(5);
|
|
|
|
await type(page, '/');
|
|
const slashMenu = page.locator(`.slash-menu`);
|
|
await expect(slashMenu).toBeVisible();
|
|
|
|
const todayBlock = page.getByTestId('Heading 1');
|
|
await todayBlock.click();
|
|
await expect(slashMenu).toBeHidden();
|
|
|
|
await type(page, 'w');
|
|
const anchorOffset2 = await page.evaluate(() => {
|
|
return window.getSelection()?.anchorOffset || 0;
|
|
});
|
|
expect(anchorOffset2).toBe(6);
|
|
});
|
|
|
|
// https://github.com/toeverything/blocksuite/issues/3613
|
|
test('should scroll page properly by wheel after inserting a new block and selecting it', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
|
|
await test.step('Insert enough blocks to make page scrollable', async () => {
|
|
await focusRichText(page);
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
await type(page, String(i));
|
|
await pressEnter(page);
|
|
await pressEnter(page);
|
|
}
|
|
});
|
|
|
|
await type(page, 'new block');
|
|
|
|
const lastBlockId = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport')!;
|
|
const container = viewport.querySelector(
|
|
'affine-note .affine-block-children-container'
|
|
);
|
|
const last = container!.lastElementChild as HTMLElement;
|
|
if (!last) {
|
|
throw new Error();
|
|
}
|
|
return last.dataset.blockId!;
|
|
});
|
|
|
|
// click drag handle to select block
|
|
await clickBlockDragHandle(page, lastBlockId);
|
|
|
|
async function getViewportScrollTop() {
|
|
return page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
}
|
|
await page.mouse.move(0, 0);
|
|
// scroll to top by wheel
|
|
await page.mouse.wheel(0, -(await getViewportScrollTop()) * 2);
|
|
await page.waitForTimeout(250);
|
|
expect(await getViewportScrollTop()).toBe(0);
|
|
|
|
// scroll to end by wheel
|
|
const distanceToEnd = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport')!;
|
|
return viewport.scrollHeight - viewport.clientHeight;
|
|
});
|
|
await page.mouse.wheel(0, distanceToEnd * 2);
|
|
await page.waitForTimeout(250);
|
|
expect(await getViewportScrollTop()).toBe(distanceToEnd);
|
|
});
|
|
|
|
test('should not select parent block when dragging area only intersects with child', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
|
|
const coord = await getIndexCoordinate(page, [1, 2]);
|
|
|
|
// blur
|
|
await page.mouse.click(0, 0);
|
|
await page.mouse.move(coord.x - 26 - 24, coord.y - 10, { steps: 20 });
|
|
await page.mouse.down();
|
|
// ←
|
|
await page.mouse.move(coord.x + 20, coord.y + 50, { steps: 20 });
|
|
await page.mouse.up();
|
|
|
|
let rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(2);
|
|
|
|
// indent children blocks
|
|
await pressTab(page);
|
|
|
|
const secondCoord = await getIndexCoordinate(page, [1, 2]);
|
|
await page.mouse.click(0, 0);
|
|
await page.mouse.move(secondCoord.x - 100, secondCoord.y - 10, {
|
|
steps: 20,
|
|
});
|
|
await page.mouse.down();
|
|
// ←
|
|
await page.mouse.move(secondCoord.x + 100, secondCoord.y + 10, { steps: 20 });
|
|
await page.mouse.up();
|
|
|
|
rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(1);
|
|
});
|
|
|
|
test('scroll should update dragging area and select blocks when dragging', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initParagraphsByCount(page, 20);
|
|
|
|
await page.mouse.click(0, 0);
|
|
// eslint-disable-next-line sonarjs/no-identical-functions
|
|
async function getViewportScrollTop() {
|
|
return page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport');
|
|
if (!viewport) {
|
|
throw new Error();
|
|
}
|
|
return viewport.scrollTop;
|
|
});
|
|
}
|
|
|
|
await page.mouse.wheel(0, -(await getViewportScrollTop()) * 2);
|
|
await page.waitForTimeout(250);
|
|
expect(await getViewportScrollTop()).toBe(0);
|
|
|
|
const coord = await getIndexCoordinate(page, [1, 1]);
|
|
|
|
await page.mouse.move(coord.x - 26 - 24, coord.y - 30, { steps: 40 });
|
|
await waitNextFrame(page, 300);
|
|
await page.mouse.down();
|
|
await waitNextFrame(page, 300);
|
|
await page.mouse.move(coord.x + 100, coord.y + 10, { steps: 40 });
|
|
await waitNextFrame(page, 300);
|
|
|
|
let rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(2);
|
|
|
|
// scroll to end by wheel
|
|
const distanceToEnd = await page.evaluate(() => {
|
|
const viewport = document.querySelector('.affine-page-viewport')!;
|
|
return viewport.scrollHeight - viewport.clientHeight;
|
|
});
|
|
await page.mouse.wheel(0, distanceToEnd * 2);
|
|
await page.waitForTimeout(250);
|
|
expect(await getViewportScrollTop()).toBe(distanceToEnd);
|
|
|
|
await page.mouse.up();
|
|
|
|
rects = page.locator('affine-block-selection').locator('visible=true');
|
|
await expect(rects).toHaveCount(3);
|
|
});
|