mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-12 12:28:42 +00:00
Closes: [BS-2186](https://linear.app/affine-design/issue/BS-2186/created-linked-doc-%E6%97%B6%EF%BC%8C%E4%BC%9A%E6%B8%85%E9%99%A4list%E7%9A%84-children)
1031 lines
30 KiB
TypeScript
1031 lines
30 KiB
TypeScript
import type { DeltaInsert } from '@inline/types.js';
|
|
import { expect } from '@playwright/test';
|
|
|
|
import {
|
|
activeEmbed,
|
|
captureHistory,
|
|
dragBetweenCoords,
|
|
dragBetweenIndices,
|
|
enterPlaygroundRoom,
|
|
focusRichText,
|
|
focusTitle,
|
|
getBoundingBox,
|
|
getEditorHostLocator,
|
|
getPageSnapshot,
|
|
getSelectionRect,
|
|
initEmptyParagraphState,
|
|
initImageState,
|
|
initThreeParagraphs,
|
|
pasteByKeyboard,
|
|
pressArrowDown,
|
|
pressArrowUp,
|
|
pressEnter,
|
|
pressEscape,
|
|
pressTab,
|
|
scrollToBottom,
|
|
scrollToTop,
|
|
selectAllBlocksByKeyboard,
|
|
selectAllByKeyboard,
|
|
setInlineRangeInInlineEditor,
|
|
setSelection,
|
|
switchReadonly,
|
|
type,
|
|
undoByKeyboard,
|
|
updateBlockType,
|
|
waitNextFrame,
|
|
withPressKey,
|
|
} from './utils/actions/index.js';
|
|
import {
|
|
assertAlmostEqual,
|
|
assertBlockChildrenIds,
|
|
assertExists,
|
|
assertLocatorVisible,
|
|
assertRichImage,
|
|
assertRichTextInlineRange,
|
|
assertRichTexts,
|
|
} from './utils/asserts.js';
|
|
import { test } from './utils/playwright.js';
|
|
import { getFormatBar } from './utils/query.js';
|
|
|
|
test('should format quick bar show when select text', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await dragBetweenIndices(page, [0, 0], [2, 3]);
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
const box = await formatBar.boundingBox();
|
|
if (!box) {
|
|
throw new Error("formatBar doesn't exist");
|
|
}
|
|
const rect = await getSelectionRect(page);
|
|
assertAlmostEqual(box.x - rect.left, -98, 5);
|
|
assertAlmostEqual(box.y - rect.bottom, 10, 5);
|
|
|
|
// Click the edge of the format quick bar
|
|
await page.mouse.click(box.x + 4, box.y + box.height / 2);
|
|
// Even not any button is clicked, the format quick bar should't be hidden
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
const noteEl = page.locator('affine-note');
|
|
const { x, y } = await getBoundingBox(noteEl);
|
|
await page.mouse.click(x + 100, y + 20);
|
|
await expect(formatBar).not.toBeVisible();
|
|
});
|
|
|
|
test('should format quick bar show when clicking drag handle', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
const locator = page.locator('affine-paragraph').first();
|
|
await locator.hover();
|
|
const dragHandle = page.locator('.affine-drag-handle-grabber');
|
|
const dragHandleRect = await dragHandle.boundingBox();
|
|
assertExists(dragHandleRect);
|
|
await dragHandle.click();
|
|
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
const box = await formatBar.boundingBox();
|
|
if (!box) {
|
|
throw new Error("formatBar doesn't exist");
|
|
}
|
|
assertAlmostEqual(box.x, 251, 5);
|
|
assertAlmostEqual(box.y - dragHandleRect.y, -55.5, 5);
|
|
});
|
|
|
|
test('should format quick bar show when select text by keyboard', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await type(page, 'hello world');
|
|
await withPressKey(page, 'Shift', async () => {
|
|
let i = 10;
|
|
while (i--) {
|
|
await page.keyboard.press('ArrowLeft');
|
|
await waitNextFrame(page);
|
|
}
|
|
});
|
|
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
const formatBarBox = await formatBar.boundingBox();
|
|
if (!formatBarBox) {
|
|
throw new Error("formatBar doesn't exist");
|
|
}
|
|
let selectionRect = await getSelectionRect(page);
|
|
assertAlmostEqual(formatBarBox.x - selectionRect.x, -107, 3);
|
|
assertAlmostEqual(
|
|
formatBarBox.y + formatBarBox.height - selectionRect.top,
|
|
-10,
|
|
3
|
|
);
|
|
|
|
await page.keyboard.press('ArrowLeft');
|
|
await expect(formatBar).not.toBeVisible();
|
|
|
|
await withPressKey(page, 'Shift', async () => {
|
|
let i = 10;
|
|
while (i--) {
|
|
await page.keyboard.press('ArrowRight');
|
|
await waitNextFrame(page);
|
|
}
|
|
});
|
|
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
const rightBox = await formatBar.boundingBox();
|
|
if (!rightBox) {
|
|
throw new Error("formatBar doesn't exist");
|
|
}
|
|
// The x position of the format quick bar depends on the font size
|
|
// so there are slight differences in different environments
|
|
selectionRect = await getSelectionRect(page);
|
|
assertAlmostEqual(formatBarBox.x - selectionRect.x, -107, 3);
|
|
assertAlmostEqual(
|
|
formatBarBox.y + formatBarBox.height - selectionRect.top,
|
|
-10,
|
|
3
|
|
);
|
|
});
|
|
|
|
test('should format quick bar can only display one at a time', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await dragBetweenIndices(page, [0, 3], [0, 0]);
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
await dragBetweenIndices(page, [2, 0], [2, 3]);
|
|
await expect(formatBar).toHaveCount(1);
|
|
});
|
|
|
|
test('should format quick bar hide when type text', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await dragBetweenIndices(page, [0, 0], [2, 3]);
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
await type(page, '1');
|
|
await expect(formatBar).not.toBeVisible();
|
|
});
|
|
|
|
test('should format quick bar be able to format text', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
// drag only the `456` paragraph
|
|
await dragBetweenIndices(page, [1, 0], [1, 3]);
|
|
|
|
const { boldBtn, italicBtn, underlineBtn, strikeBtn, codeBtn } =
|
|
getFormatBar(page);
|
|
|
|
await expect(boldBtn).not.toHaveAttribute('active', '');
|
|
await expect(italicBtn).not.toHaveAttribute('active', '');
|
|
await expect(underlineBtn).not.toHaveAttribute('active', '');
|
|
await expect(strikeBtn).not.toHaveAttribute('active', '');
|
|
await expect(codeBtn).not.toHaveAttribute('active', '');
|
|
|
|
await boldBtn.click();
|
|
await italicBtn.click();
|
|
await underlineBtn.click();
|
|
await strikeBtn.click();
|
|
await codeBtn.click();
|
|
|
|
// The button should be active after click
|
|
await expect(boldBtn).toHaveAttribute('active', '');
|
|
await expect(italicBtn).toHaveAttribute('active', '');
|
|
await expect(underlineBtn).toHaveAttribute('active', '');
|
|
await expect(strikeBtn).toHaveAttribute('active', '');
|
|
await expect(codeBtn).toHaveAttribute('active', '');
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
await boldBtn.click();
|
|
await underlineBtn.click();
|
|
await codeBtn.click();
|
|
|
|
await waitNextFrame(page);
|
|
|
|
// The bold button should be inactive after click again
|
|
await expect(boldBtn).not.toHaveAttribute('active', '');
|
|
await expect(italicBtn).toHaveAttribute('active', '');
|
|
await expect(underlineBtn).not.toHaveAttribute('active', '');
|
|
await expect(strikeBtn).toHaveAttribute('active', '');
|
|
await expect(codeBtn).not.toHaveAttribute('active', '');
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_finial.json`
|
|
);
|
|
});
|
|
|
|
test('should format quick bar be able to change background color', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
// select `456` paragraph by dragging
|
|
await dragBetweenIndices(page, [1, 0], [1, 3]);
|
|
|
|
const { highlight } = getFormatBar(page);
|
|
|
|
await highlight.highlightBtn.hover();
|
|
await expect(highlight.redForegroundBtn).toBeVisible();
|
|
await expect(highlight.highlightBtn).toHaveAttribute(
|
|
'data-last-used',
|
|
'unset'
|
|
);
|
|
await highlight.redForegroundBtn.click();
|
|
await expect(highlight.highlightBtn).toHaveAttribute(
|
|
'data-last-used',
|
|
'var(--affine-text-highlight-foreground-red)'
|
|
);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
// select `123` paragraph by ctrl + a
|
|
await focusRichText(page);
|
|
await selectAllByKeyboard(page);
|
|
// use last used color
|
|
await highlight.highlightBtn.click();
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_select_all.json`
|
|
);
|
|
|
|
await expect(highlight.defaultColorBtn).toBeVisible();
|
|
await highlight.defaultColorBtn.click();
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_default_color.json`
|
|
);
|
|
});
|
|
|
|
test('should format quick bar be able to format text when select multiple line', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await dragBetweenIndices(page, [0, 0], [2, 3]);
|
|
|
|
const { boldBtn } = getFormatBar(page);
|
|
await expect(boldBtn).not.toHaveAttribute('active', '');
|
|
await boldBtn.click();
|
|
|
|
// The bold button should be active after click
|
|
await expect(boldBtn).toHaveAttribute('active', '');
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
await boldBtn.click();
|
|
await expect(boldBtn).not.toHaveAttribute('active', '');
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_finial.json`
|
|
);
|
|
});
|
|
|
|
test('should format quick bar be able to link text', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
// drag only the `456` paragraph
|
|
await dragBetweenIndices(page, [1, 0], [1, 3]);
|
|
|
|
const { linkBtn } = getFormatBar(page);
|
|
await expect(linkBtn).not.toHaveAttribute('active', '');
|
|
await linkBtn.click();
|
|
|
|
const linkPopoverInput = page.locator('.affine-link-popover-input');
|
|
await expect(linkPopoverInput).toBeVisible();
|
|
|
|
await type(page, 'https://www.example.com');
|
|
await pressEnter(page);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
// FIXME: remove this
|
|
await focusRichText(page);
|
|
await setSelection(page, 3, 0, 3, 3);
|
|
// The link button should be active after click
|
|
await expect(linkBtn).toHaveAttribute('active', '');
|
|
await linkBtn.click();
|
|
await waitNextFrame(page);
|
|
await expect(linkBtn).not.toHaveAttribute('active', '');
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_finial.json`
|
|
);
|
|
});
|
|
|
|
test('should format quick bar be able to change to heading paragraph type', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
// drag only the `456` paragraph
|
|
await dragBetweenIndices(page, [0, 0], [0, 3]);
|
|
|
|
const { openParagraphMenu, h1Btn, bulletedBtn } = getFormatBar(page);
|
|
await openParagraphMenu();
|
|
|
|
await expect(h1Btn).toBeVisible();
|
|
await h1Btn.click();
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
await bulletedBtn.click();
|
|
await openParagraphMenu();
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_bulleted.json`
|
|
);
|
|
|
|
const { textBtn } = getFormatBar(page);
|
|
await textBtn.click();
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_finial.json`
|
|
);
|
|
await page.waitForTimeout(10);
|
|
// The paragraph button should prevent selection after click
|
|
await assertRichTextInlineRange(page, 0, 0, 3);
|
|
});
|
|
|
|
test('should format quick bar show when double click text', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
const editorHost = getEditorHostLocator(page);
|
|
const richText = editorHost.locator('rich-text').nth(0);
|
|
await richText.dblclick({
|
|
position: { x: 10, y: 10 },
|
|
});
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
});
|
|
|
|
test('should format quick bar not show at readonly mode', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await switchReadonly(page);
|
|
|
|
await dragBetweenIndices(page, [0, 0], [2, 3]);
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).not.toBeVisible();
|
|
|
|
const editorHost = getEditorHostLocator(page);
|
|
const richText = editorHost.locator('rich-text').nth(0);
|
|
await richText.dblclick({
|
|
position: { x: 10, y: 10 },
|
|
});
|
|
await expect(formatBar).not.toBeVisible();
|
|
});
|
|
|
|
test('should format bar follow scroll', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
for (let i = 0; i < 30; i++) {
|
|
await pressEnter(page);
|
|
}
|
|
|
|
await scrollToTop(page);
|
|
|
|
await dragBetweenIndices(page, [0, 0], [2, 3]);
|
|
const { formatBar, boldBtn } = getFormatBar(page);
|
|
await assertLocatorVisible(page, formatBar);
|
|
|
|
await scrollToBottom(page);
|
|
|
|
await assertLocatorVisible(page, formatBar, false);
|
|
|
|
// should format bar follow scroll after click bold button
|
|
await scrollToTop(page);
|
|
await assertLocatorVisible(page, formatBar);
|
|
await boldBtn.click();
|
|
await scrollToBottom(page);
|
|
await assertLocatorVisible(page, formatBar, false);
|
|
|
|
// should format bar follow scroll after transform text type
|
|
await scrollToTop(page);
|
|
await assertLocatorVisible(page, formatBar);
|
|
await updateBlockType(page, 'affine:list', 'bulleted');
|
|
await scrollToBottom(page);
|
|
await assertLocatorVisible(page, formatBar, false);
|
|
});
|
|
|
|
test('should format quick bar position correct at the start of second line', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await page.evaluate(() => {
|
|
const { doc } = window;
|
|
const rootId = doc.addBlock('affine:page', {
|
|
title: new doc.Text(),
|
|
});
|
|
const note = doc.addBlock('affine:note', {}, rootId);
|
|
const text = new doc.Text('a'.repeat(100));
|
|
const paragraphId = doc.addBlock('affine:paragraph', { text }, note);
|
|
return paragraphId;
|
|
});
|
|
// await focusRichText(page);
|
|
const editorHost = getEditorHostLocator(page);
|
|
const locator = editorHost.locator('.inline-editor').nth(0);
|
|
const textBox = await locator.boundingBox();
|
|
if (!textBox) {
|
|
throw new Error("Can't get bounding box");
|
|
}
|
|
// Drag to the start of the second line
|
|
await dragBetweenCoords(
|
|
page,
|
|
{ x: textBox.x + textBox.width - 1, y: textBox.y + textBox.height - 1 },
|
|
{ x: textBox.x, y: textBox.y + textBox.height - 1 }
|
|
);
|
|
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
await waitNextFrame(page);
|
|
|
|
const formatBox = await formatBar.boundingBox();
|
|
if (!formatBox) {
|
|
throw new Error("formatBar doesn't exist");
|
|
}
|
|
const selectionRect = await getSelectionRect(page);
|
|
assertAlmostEqual(formatBox.x - selectionRect.x, -99, 5);
|
|
assertAlmostEqual(formatBox.y + formatBox.height - selectionRect.top, 68, 5);
|
|
});
|
|
|
|
test('should format quick bar action status updated while undo', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await type(page, 'helloworld');
|
|
await captureHistory(page);
|
|
await dragBetweenIndices(page, [0, 1], [0, 6]);
|
|
|
|
const { formatBar, boldBtn } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
await expect(boldBtn).toBeVisible();
|
|
|
|
await expect(boldBtn).not.toHaveAttribute('active', '');
|
|
await boldBtn.click();
|
|
await expect(boldBtn).toHaveAttribute('active', '');
|
|
|
|
await undoByKeyboard(page);
|
|
await expect(formatBar).toBeVisible();
|
|
await expect(boldBtn).not.toHaveAttribute('active', '');
|
|
});
|
|
|
|
test('should format quick bar work in single block selection', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[1, 0],
|
|
[1, 3],
|
|
{ x: -26 - 24, y: -10 },
|
|
{ x: 0, y: 0 }
|
|
);
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(1);
|
|
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
const formatRect = await formatBar.boundingBox();
|
|
const selectionRect = await blockSelections.boundingBox();
|
|
assertExists(formatRect);
|
|
assertExists(selectionRect);
|
|
assertAlmostEqual(formatRect.x - selectionRect.x, 147.5, 10);
|
|
assertAlmostEqual(formatRect.y - selectionRect.y, 33, 10);
|
|
|
|
const boldBtn = formatBar.getByTestId('bold');
|
|
await boldBtn.click();
|
|
const italicBtn = formatBar.getByTestId('italic');
|
|
await italicBtn.click();
|
|
const underlineBtn = formatBar.getByTestId('underline');
|
|
await underlineBtn.click();
|
|
//FIXME: trt to cancel italic
|
|
// Cancel italic
|
|
// await italicBtn.click();
|
|
|
|
await expect(blockSelections).toHaveCount(1);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}.json`
|
|
);
|
|
|
|
const noteEl = page.locator('affine-note');
|
|
const { x, y, width, height } = await getBoundingBox(noteEl);
|
|
await page.mouse.click(x + width / 2, y + height / 2);
|
|
await expect(formatBar).not.toBeVisible();
|
|
});
|
|
|
|
test('should format quick bar work in multiple block selection', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[2, 3],
|
|
[0, 0],
|
|
{ x: 20, y: 20 },
|
|
{ x: 0, y: 0 }
|
|
);
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(3);
|
|
|
|
const formatBarController = getFormatBar(page);
|
|
await expect(formatBarController.formatBar).toBeVisible();
|
|
|
|
const box = await formatBarController.formatBar.boundingBox();
|
|
if (!box) {
|
|
throw new Error("formatBar doesn't exist");
|
|
}
|
|
const rect = await blockSelections.first().boundingBox();
|
|
assertExists(rect);
|
|
assertAlmostEqual(box.x - rect.x, 147.5, 10);
|
|
assertAlmostEqual(box.y - rect.y, 99, 10);
|
|
|
|
await formatBarController.boldBtn.click();
|
|
await formatBarController.italicBtn.click();
|
|
await formatBarController.underlineBtn.click();
|
|
// Cancel italic
|
|
await formatBarController.italicBtn.click();
|
|
|
|
await expect(blockSelections).toHaveCount(3);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}.json`
|
|
);
|
|
|
|
const noteEl = page.locator('affine-note');
|
|
const { x, y, width, height } = await getBoundingBox(noteEl);
|
|
await page.mouse.click(x + width / 2, y + height / 2);
|
|
await expect(formatBarController.formatBar).not.toBeVisible();
|
|
});
|
|
|
|
test('should format quick bar with block selection works when update block type', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[2, 3],
|
|
[0, 0],
|
|
{ x: 20, y: 20 },
|
|
{ x: 0, y: 0 }
|
|
);
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(3);
|
|
|
|
const formatBarController = getFormatBar(page);
|
|
await expect(formatBarController.formatBar).toBeVisible();
|
|
|
|
await formatBarController.openParagraphMenu();
|
|
await formatBarController.bulletedBtn.click();
|
|
await expect(blockSelections).toHaveCount(3);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_init.json`
|
|
);
|
|
|
|
await expect(formatBarController.formatBar).toBeVisible();
|
|
await formatBarController.h1Btn.click();
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}_final.json`
|
|
);
|
|
await expect(formatBarController.formatBar).toBeVisible();
|
|
await expect(blockSelections).toHaveCount(3);
|
|
|
|
const noteEl = page.locator('affine-note');
|
|
const { x, y, width, height } = await getBoundingBox(noteEl);
|
|
await page.mouse.click(x + width / 2, y + height / 2);
|
|
await expect(formatBarController.formatBar).not.toBeVisible();
|
|
});
|
|
|
|
test('should format quick bar show after convert to code block', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
const formatBarController = getFormatBar(page);
|
|
await dragBetweenIndices(
|
|
page,
|
|
[2, 3],
|
|
[0, 0],
|
|
{ x: 20, y: 20 },
|
|
{ x: 0, y: 0 }
|
|
);
|
|
await expect(formatBarController.formatBar).toBeVisible();
|
|
await expect(formatBarController.formatBar).toBeInViewport();
|
|
|
|
await formatBarController.openParagraphMenu();
|
|
await formatBarController.codeBlockBtn.click();
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}.json`
|
|
);
|
|
});
|
|
|
|
test('buttons in format quick bar should have correct active styles', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
// `45`
|
|
await setInlineRangeInInlineEditor(
|
|
page,
|
|
{
|
|
index: 0,
|
|
length: 2,
|
|
},
|
|
2
|
|
);
|
|
const { codeBtn } = getFormatBar(page);
|
|
await codeBtn.click();
|
|
await expect(codeBtn).toHaveAttribute('active', '');
|
|
|
|
// `456`
|
|
await setInlineRangeInInlineEditor(
|
|
page,
|
|
{
|
|
index: 0,
|
|
length: 3,
|
|
},
|
|
2
|
|
);
|
|
await expect(codeBtn).not.toHaveAttribute('active', '');
|
|
});
|
|
|
|
test('should format bar style active correctly', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await page.evaluate(() => {
|
|
const { doc } = window;
|
|
const rootId = doc.addBlock('affine:page', {
|
|
title: new doc.Text(),
|
|
});
|
|
const note = doc.addBlock('affine:note', {}, rootId);
|
|
const delta = [
|
|
{ insert: '1', attributes: { bold: true, italic: true } },
|
|
{ insert: '2', attributes: { bold: true, underline: true } },
|
|
{ insert: '3', attributes: { bold: true, code: true } },
|
|
];
|
|
const text = new doc.Text(delta as DeltaInsert[]);
|
|
doc.addBlock('affine:paragraph', { text }, note);
|
|
});
|
|
|
|
const { boldBtn, codeBtn, underlineBtn } = getFormatBar(page);
|
|
await dragBetweenIndices(page, [0, 0], [0, 3]);
|
|
await expect(boldBtn).toHaveAttribute('active', '');
|
|
await expect(underlineBtn).not.toHaveAttribute('active', '');
|
|
await expect(codeBtn).not.toHaveAttribute('active', '');
|
|
|
|
await underlineBtn.click();
|
|
await expect(underlineBtn).toHaveAttribute('active', '');
|
|
await expect(boldBtn).toHaveAttribute('active', '');
|
|
await expect(codeBtn).not.toHaveAttribute('active', '');
|
|
});
|
|
|
|
test('should format quick bar show when double click button', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await dragBetweenIndices(page, [0, 0], [2, 3]);
|
|
const { formatBar, boldBtn } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
await boldBtn.dblclick({
|
|
delay: 100,
|
|
});
|
|
await expect(formatBar).toBeVisible();
|
|
});
|
|
|
|
test('should the database action icon show correctly', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
const databaseAction = page.getByTestId('convert-to-database');
|
|
|
|
await focusRichText(page);
|
|
await dragBetweenIndices(
|
|
page,
|
|
[2, 3],
|
|
[0, 0],
|
|
{ x: 20, y: 20 },
|
|
{ x: 0, y: 0 }
|
|
);
|
|
await expect(databaseAction).toBeVisible();
|
|
|
|
await focusRichText(page, 2);
|
|
await pressEnter(page);
|
|
await updateBlockType(page, 'affine:code');
|
|
const codeBlock = page.locator('affine-code');
|
|
const codeBox = await codeBlock.boundingBox();
|
|
if (!codeBox) throw new Error('Missing code block box');
|
|
|
|
await page.keyboard.type('hello world');
|
|
const position = {
|
|
startX: codeBox.x,
|
|
startY: codeBox.y + codeBox.height / 2,
|
|
endX: codeBox.x + codeBox.width,
|
|
endY: codeBox.y + codeBox.height / 2,
|
|
};
|
|
await page.mouse.click(position.endX + 150, position.endY + 150);
|
|
await dragBetweenCoords(
|
|
page,
|
|
{ x: position.startX + 10, y: position.startY - 10 },
|
|
{ x: position.endX, y: position.endY },
|
|
{ steps: 20 }
|
|
);
|
|
await expect(databaseAction).not.toBeVisible();
|
|
});
|
|
|
|
test('should convert to database work', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
await dragBetweenIndices(
|
|
page,
|
|
[2, 3],
|
|
[0, 0],
|
|
{ x: 20, y: 20 },
|
|
{ x: 0, y: 0 }
|
|
);
|
|
const databaseAction = page.getByTestId('convert-to-database');
|
|
await databaseAction.click();
|
|
const database = page.locator('affine-database');
|
|
await expect(database).toBeVisible();
|
|
const rows = page.locator('.affine-database-block-row');
|
|
expect(await rows.count()).toBe(3);
|
|
});
|
|
|
|
test('should show format-quick-bar and select all text of the block when triple clicking on text', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await focusRichText(page);
|
|
await type(page, 'hello world');
|
|
|
|
const editorHost = getEditorHostLocator(page);
|
|
const locator = editorHost.locator('.inline-editor').nth(0);
|
|
const textBox = await locator.boundingBox();
|
|
if (!textBox) {
|
|
throw new Error("Can't get bounding box");
|
|
}
|
|
|
|
await page.mouse.dblclick(textBox.x + 10, textBox.y + textBox.height / 2);
|
|
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
await assertRichTextInlineRange(page, 0, 0, 5);
|
|
|
|
const noteEl = page.locator('affine-note');
|
|
const { x, y, width, height } = await getBoundingBox(noteEl);
|
|
await page.mouse.click(x + width / 2, y + height / 2);
|
|
|
|
await expect(formatBar).toBeHidden();
|
|
|
|
await page.mouse.move(textBox.x + 10, textBox.y + textBox.height / 2);
|
|
|
|
const options = {
|
|
clickCount: 1,
|
|
};
|
|
await page.mouse.down(options);
|
|
await page.mouse.up(options);
|
|
|
|
options.clickCount++;
|
|
await page.mouse.down(options);
|
|
await page.mouse.up(options);
|
|
|
|
options.clickCount++;
|
|
await page.mouse.down(options);
|
|
await page.mouse.up(options);
|
|
|
|
await assertRichTextInlineRange(page, 0, 0, 'hello world'.length);
|
|
});
|
|
|
|
test('should update the format quick bar state when there is a change in keyboard selection', async ({
|
|
page,
|
|
}) => {
|
|
await enterPlaygroundRoom(page);
|
|
await page.evaluate(() => {
|
|
const { doc } = window;
|
|
const rootId = doc.addBlock('affine:page', {
|
|
title: new doc.Text(),
|
|
});
|
|
const note = doc.addBlock('affine:note', {}, rootId);
|
|
const delta = [
|
|
{ insert: '1', attributes: { bold: true } },
|
|
{ insert: '2', attributes: { bold: true } },
|
|
{ insert: '3', attributes: { bold: false } },
|
|
];
|
|
const text = new doc.Text(delta as DeltaInsert[]);
|
|
doc.addBlock('affine:paragraph', { text }, note);
|
|
});
|
|
await focusTitle(page);
|
|
await pressArrowDown(page);
|
|
|
|
const formatBar = getFormatBar(page);
|
|
await withPressKey(page, 'Shift', async () => {
|
|
await page.keyboard.press('ArrowRight');
|
|
await page.keyboard.press('ArrowRight');
|
|
await expect(formatBar.boldBtn).toHaveAttribute('active', '');
|
|
await page.keyboard.press('ArrowRight');
|
|
await expect(formatBar.boldBtn).not.toHaveAttribute('active', '');
|
|
});
|
|
});
|
|
|
|
test('format quick bar should not break cursor jumping', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
await dragBetweenIndices(page, [1, 3], [1, 2]);
|
|
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).toBeVisible();
|
|
|
|
await pressArrowUp(page);
|
|
await type(page, '0');
|
|
await assertRichTexts(page, ['1203', '456', '789']);
|
|
|
|
await dragBetweenIndices(page, [1, 3], [1, 2]);
|
|
await pressArrowDown(page);
|
|
await type(page, '0');
|
|
await assertRichTexts(page, ['1203', '456', '7809']);
|
|
});
|
|
|
|
test('selecting image should not show format bar', async ({ page }) => {
|
|
test.info().annotations.push({
|
|
type: 'issue',
|
|
description: 'https://github.com/toeverything/blocksuite/issues/4535',
|
|
});
|
|
await enterPlaygroundRoom(page);
|
|
await initImageState(page);
|
|
await assertRichImage(page, 1);
|
|
await activeEmbed(page);
|
|
await waitNextFrame(page);
|
|
const { formatBar } = getFormatBar(page);
|
|
await expect(formatBar).not.toBeVisible();
|
|
});
|
|
|
|
test('create linked doc from block selection with format bar', async ({
|
|
page,
|
|
}, testInfo) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
await focusRichText(page, 1);
|
|
await pressTab(page);
|
|
await assertRichTexts(page, ['123', '456', '789']);
|
|
await assertBlockChildrenIds(page, '1', ['2', '4']);
|
|
await assertBlockChildrenIds(page, '2', ['3']);
|
|
|
|
await selectAllBlocksByKeyboard(page);
|
|
await waitNextFrame(page, 200);
|
|
|
|
const blockSelections = page
|
|
.locator('affine-block-selection')
|
|
.locator('visible=true');
|
|
await expect(blockSelections).toHaveCount(2);
|
|
|
|
const { createLinkedDocBtn } = getFormatBar(page);
|
|
expect(await createLinkedDocBtn.isVisible()).toBe(true);
|
|
await createLinkedDocBtn.click();
|
|
|
|
const linkedDocBlock = page.locator('affine-embed-linked-doc-block');
|
|
await expect(linkedDocBlock).toHaveCount(1);
|
|
|
|
const linkedDocBox = await linkedDocBlock.boundingBox();
|
|
assertExists(linkedDocBox);
|
|
await page.mouse.dblclick(
|
|
linkedDocBox.x + linkedDocBox.width / 2,
|
|
linkedDocBox.y + linkedDocBox.height / 2
|
|
);
|
|
await waitNextFrame(page, 200);
|
|
|
|
expect(await getPageSnapshot(page, true)).toMatchSnapshot(
|
|
`${testInfo.title}.json`
|
|
);
|
|
|
|
const paragraph = page.locator('affine-paragraph');
|
|
await expect(paragraph).toHaveCount(3);
|
|
});
|
|
|
|
test.describe('more menu button', () => {
|
|
test('should be able to perform the copy action', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
// drag only the `456` paragraph
|
|
await dragBetweenIndices(page, [1, 0], [1, 3]);
|
|
|
|
const { openMoreMenu, copyBtn } = getFormatBar(page);
|
|
await openMoreMenu();
|
|
await expect(copyBtn).toBeVisible();
|
|
await assertRichTextInlineRange(page, 1, 0, 3);
|
|
await copyBtn.click();
|
|
await assertRichTextInlineRange(page, 1, 0, 3);
|
|
|
|
await focusRichText(page, 1);
|
|
await pasteByKeyboard(page);
|
|
await waitNextFrame(page);
|
|
|
|
await assertRichTexts(page, ['123', '456456', '789']);
|
|
});
|
|
|
|
test('should be able to perform the duplicate action', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
await focusRichText(page, 1);
|
|
await pressEscape(page);
|
|
|
|
const { openMoreMenu, duplicateBtn } = getFormatBar(page);
|
|
await openMoreMenu();
|
|
await expect(duplicateBtn).toBeVisible();
|
|
await duplicateBtn.click();
|
|
|
|
await waitNextFrame(page);
|
|
|
|
await assertRichTexts(page, ['123', '456', '456', '789']);
|
|
});
|
|
|
|
test('should be able to perform the delete action', async ({ page }) => {
|
|
await enterPlaygroundRoom(page);
|
|
await initEmptyParagraphState(page);
|
|
await initThreeParagraphs(page);
|
|
|
|
await focusRichText(page, 1);
|
|
await pressEscape(page);
|
|
|
|
const { openMoreMenu, deleteBtn } = getFormatBar(page);
|
|
await openMoreMenu();
|
|
await expect(deleteBtn).toBeVisible();
|
|
await deleteBtn.click();
|
|
|
|
await waitNextFrame(page);
|
|
|
|
await assertRichTexts(page, ['123', '789']);
|
|
});
|
|
});
|