import { expect } from '@playwright/test'; import * as actions from '../../utils/actions/edgeless.js'; import { getNoteBoundBoxInEdgeless, setEdgelessTool, switchEditorMode, } from '../../utils/actions/edgeless.js'; import { addBasicBrushElement, addBasicRectShapeElement, click, clickInCenter, dragBetweenCoords, enterPlaygroundRoom, getBoundingRect, initEmptyEdgelessState, initThreeParagraphs, pressEnter, waitNextFrame, } from '../../utils/actions/index.js'; import { assertBlockCount, assertEdgelessRemoteSelectedModelRect, assertEdgelessRemoteSelectedRect, assertEdgelessSelectedModelRect, assertEdgelessSelectedRect, assertSelectionInNote, } from '../../utils/asserts.js'; import { test } from '../../utils/playwright.js'; test('should update rect of selection when resizing viewport', async ({ page, }) => { await enterPlaygroundRoom(page); await initEmptyEdgelessState(page); await actions.switchEditorMode(page); await actions.zoomResetByKeyboard(page); await addBasicRectShapeElement(page, { x: 100, y: 100 }, { x: 200, y: 200 }); await dragBetweenCoords(page, { x: 120, y: 90 }, { x: 220, y: 130 }); const selectedRectClass = '.affine-edgeless-selected-rect'; await actions.zoomResetByKeyboard(page); await assertEdgelessSelectedRect(page, [100, 100, 100, 100]); await actions.decreaseZoomLevel(page); await waitNextFrame(page); await actions.decreaseZoomLevel(page); await waitNextFrame(page); const selectedRectInZoom = await getBoundingRect(page, selectedRectClass); await assertEdgelessSelectedRect(page, [ selectedRectInZoom.x, selectedRectInZoom.y, 50, 50, ]); await actions.switchEditorEmbedMode(page); await waitNextFrame(page); const selectedRectInEmbed = await getBoundingRect(page, selectedRectClass); await assertEdgelessSelectedRect(page, [ selectedRectInEmbed.x, selectedRectInEmbed.y, 50, 50, ]); await actions.switchEditorEmbedMode(page); await actions.increaseZoomLevel(page); await waitNextFrame(page); await actions.increaseZoomLevel(page); await waitNextFrame(page); await assertEdgelessSelectedRect(page, [100, 100, 100, 100]); }); test('should update react of remote selection when resizing viewport', async ({ context, page: pageA, }) => { const room = await enterPlaygroundRoom(pageA); await initEmptyEdgelessState(pageA); await actions.switchEditorMode(pageA); await actions.zoomResetByKeyboard(pageA); const pageB = await context.newPage(); await enterPlaygroundRoom(pageB, { room, noInit: true, }); await actions.switchEditorMode(pageB); await actions.zoomResetByKeyboard(pageB); await actions.createShapeElement( pageA, [0, 0], [100, 100], actions.Shape.Square ); const point = await actions.toViewCoord(pageA, [50, 50]); await click(pageA, { x: point[0], y: point[1] }); await click(pageB, { x: point[0], y: point[1] }); await assertEdgelessSelectedModelRect(pageB, [0, 0, 100, 100]); await assertEdgelessRemoteSelectedModelRect(pageB, [0, 0, 100, 100]); // to 50% await actions.decreaseZoomLevel(pageB); await waitNextFrame(pageB); await actions.decreaseZoomLevel(pageB); await waitNextFrame(pageB); const selectedRectInZoom = await getBoundingRect( pageB, '.affine-edgeless-selected-rect' ); await assertEdgelessRemoteSelectedRect(pageB, [ selectedRectInZoom.x, selectedRectInZoom.y, 50, 50, ]); }); test('select multiple shapes and translate', async ({ page }) => { await enterPlaygroundRoom(page); await initEmptyEdgelessState(page); await switchEditorMode(page); await actions.zoomResetByKeyboard(page); await addBasicBrushElement(page, { x: 100, y: 100 }, { x: 200, y: 200 }); await page.mouse.click(110, 110); await assertEdgelessSelectedRect(page, [98, 98, 104, 104]); await addBasicRectShapeElement(page, { x: 210, y: 110 }, { x: 310, y: 210 }); await page.mouse.click(220, 120); await assertEdgelessSelectedRect(page, [210, 110, 100, 100]); await dragBetweenCoords(page, { x: 120, y: 90 }, { x: 220, y: 130 }); await assertEdgelessSelectedRect(page, [98, 98, 212, 112]); await dragBetweenCoords(page, { x: 120, y: 120 }, { x: 150, y: 150 }); await assertEdgelessSelectedRect(page, [125, 128, 212, 112]); await page.mouse.click(160, 160); await assertEdgelessSelectedRect(page, [125, 128, 104, 104]); await page.mouse.click(250, 150); await assertEdgelessSelectedRect(page, [237, 140, 100, 100]); }); test('selection box of shape element sync on fast dragging', async ({ page, }) => { await enterPlaygroundRoom(page); await initEmptyEdgelessState(page); await switchEditorMode(page); await actions.zoomResetByKeyboard(page); await setEdgelessTool(page, 'shape'); await dragBetweenCoords(page, { x: 100, y: 100 }, { x: 200, y: 200 }); await setEdgelessTool(page, 'default'); await dragBetweenCoords( page, { x: 110, y: 110 }, { x: 660, y: 460 }, { click: true } ); await assertEdgelessSelectedRect(page, [650, 450, 100, 100]); }); test('when the selection is always a note, it should remain in an active state', async ({ page, }) => { await enterPlaygroundRoom(page); const ids = await initEmptyEdgelessState(page); await initThreeParagraphs(page); await switchEditorMode(page); await actions.zoomResetByKeyboard(page); const bound = await getNoteBoundBoxInEdgeless(page, ids.noteId); await setEdgelessTool(page, 'note'); const newNoteX = bound.x; const newNoteY = bound.y + bound.height + 100; // add text await page.mouse.click(newNoteX, newNoteY); await waitNextFrame(page); await page.keyboard.type('hello'); await pressEnter(page); // should wait for inline editor update and resizeObserver callback await waitNextFrame(page); // assert add text success await assertBlockCount(page, 'edgeless-note', 2); await clickInCenter(page, bound); await clickInCenter(page, bound); await waitNextFrame(page); await assertSelectionInNote(page, ids.noteId, 'affine-edgeless-note'); }); test('should auto panning when selection rectangle reaches viewport edges', async ({ page, }) => { await enterPlaygroundRoom(page); await initEmptyEdgelessState(page); await switchEditorMode(page); await actions.zoomResetByKeyboard(page); await addBasicRectShapeElement(page, { x: 200, y: 100 }, { x: 300, y: 200 }); await page.mouse.click(210, 110); await assertEdgelessSelectedRect(page, [200, 100, 100, 100]); const selectedRectClass = '.affine-edgeless-selected-rect'; // Panning to the left await setEdgelessTool(page, 'pan'); await dragBetweenCoords( page, { x: 600, y: 200, }, { x: 200, y: 200, } ); await setEdgelessTool(page, 'default'); await page.mouse.click(210, 110); let selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(300); await expect(selectedRect).toBeHidden(); // Click to start selection and hold the mouse to trigger auto panning to the left await page.mouse.move(210, 110); await page.mouse.down(); await page.mouse.move(0, 210, { steps: 20 }); await page.waitForTimeout(500); await page.mouse.up(); // Expect to select the shape element selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(300); await expect(selectedRect).toBeVisible(); // Panning to the top await page.mouse.click(400, 600); await setEdgelessTool(page, 'pan'); await dragBetweenCoords( page, { x: 400, y: 600, }, { x: 400, y: 100, } ); await setEdgelessTool(page, 'default'); await page.mouse.click(600, 100); selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(300); await expect(selectedRect).toBeHidden(); // Click to start selection and hold the mouse to trigger auto panning to the top await page.mouse.move(600, 100); await page.mouse.down(); await page.mouse.move(400, 0, { steps: 20 }); await page.waitForTimeout(500); await page.mouse.up(); // Expect to select the empty note selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(300); await expect(selectedRect).toBeVisible(); // Panning to the right await page.mouse.click(100, 600); await setEdgelessTool(page, 'pan'); await dragBetweenCoords( page, { x: 20, y: 600, }, { x: 1000, y: 600, } ); await setEdgelessTool(page, 'default'); await page.mouse.click(800, 600); selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(100); await expect(selectedRect).toBeHidden(); // Click to start selection and hold the mouse to trigger auto panning to the right await dragBetweenCoords( page, { x: 800, y: 600, }, { x: 1000, y: 200, }, { beforeMouseUp: async () => { await page.waitForTimeout(600); }, } ); // Expect to select the empty note selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(300); await expect(selectedRect).toBeVisible(); // Panning to the bottom await page.mouse.click(400, 100); await setEdgelessTool(page, 'pan'); await dragBetweenCoords( page, { x: 400, y: 100, }, { x: 400, y: 850, }, { click: true, } ); await setEdgelessTool(page, 'default'); await waitNextFrame(page, 500); await page.mouse.click(400, 400); selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(100); await expect(selectedRect).toBeHidden(); // Click to start selection and hold the mouse to trigger auto panning to the right await dragBetweenCoords( page, { x: 800, y: 300, }, { x: 820, y: 1150, }, { click: true, beforeMouseUp: async () => { await page.waitForTimeout(500); }, } ); // Expect to select the empty note selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(300); await expect(selectedRect).toBeVisible(); }); test('should also update dragging area when viewport changes', async ({ page, }) => { await enterPlaygroundRoom(page); await initEmptyEdgelessState(page); await switchEditorMode(page); await actions.zoomResetByKeyboard(page); // Panning to the top await page.mouse.click(400, 600); await setEdgelessTool(page, 'pan'); await dragBetweenCoords( page, { x: 400, y: 600, }, { x: 400, y: 100, } ); await setEdgelessTool(page, 'default'); await page.mouse.click(200, 300); const selectedRectClass = '.affine-edgeless-selected-rect'; let selectedRect = page.locator(selectedRectClass); await expect(selectedRect).toBeHidden(); // set up initial dragging area await page.mouse.move(200, 300); await page.mouse.down(); await page.mouse.move(600, 200, { steps: 20 }); await page.waitForTimeout(300); // wheel the viewport to the top await page.mouse.wheel(0, -300); await page.waitForTimeout(300); await page.mouse.up(); // Expect to select the empty note selectedRect = page.locator(selectedRectClass); await page.waitForTimeout(300); await expect(selectedRect).toBeVisible(); await page.waitForTimeout(300); }); test('should select shapes while moving selection', async ({ page }) => { await enterPlaygroundRoom(page); await initEmptyEdgelessState(page); await switchEditorMode(page); await actions.zoomResetByKeyboard(page); await addBasicRectShapeElement(page, { x: 100, y: 100 }, { x: 200, y: 200 }); // Make the selection out side the rect and move the selection to the rect await dragBetweenCoords( page, // Make the selection not selecting the rect { x: 70, y: 70 }, { x: 90, y: 90 }, { beforeMouseUp: async () => { await page.keyboard.down('Space'); // Move the selection over to the rect await page.mouse.move(120, 120); await page.keyboard.up('Space'); }, } ); await assertEdgelessSelectedRect(page, [100, 100, 100, 100]); await addBasicBrushElement(page, { x: 210, y: 100 }, { x: 310, y: 300 }); await page.mouse.click(211, 101); // Make a wide selection and move it to select both of the shapes await dragBetweenCoords( page, // Make the selection above the spaces { x: 70, y: 70 }, { x: 400, y: 90 }, { beforeMouseUp: async () => { await page.keyboard.down('Space'); // Move the selection over both of the shapes await page.mouse.move(400, 120); await page.keyboard.up('Space'); }, } ); await assertEdgelessSelectedRect(page, [100, 98, 212, 204]); });