From a5bcfb0b1489517917a8a8b0b6ac7ad704f5b17f Mon Sep 17 00:00:00 2001 From: fundon Date: Tue, 12 Nov 2024 09:23:57 +0000 Subject: [PATCH] fix: reference parameters and add test cases (#8740) Upstreams: https://github.com/toeverything/blocksuite/pull/8689 Closes: AF-1650 --- packages/frontend/apps/android/package.json | 2 +- packages/frontend/apps/ios/package.json | 2 +- packages/frontend/apps/mobile/package.json | 2 +- .../modules/peek-view/entities/peek-view.ts | 25 +- tests/affine-local/e2e/links.spec.ts | 333 ++++++++++++++++++ tests/affine-local/e2e/quick-search.spec.ts | 25 +- tests/kit/utils/keyboard.ts | 23 ++ yarn.lock | 8 +- 8 files changed, 376 insertions(+), 44 deletions(-) create mode 100644 tests/affine-local/e2e/links.spec.ts diff --git a/packages/frontend/apps/android/package.json b/packages/frontend/apps/android/package.json index 5537bf49aa..5d2b1db1b0 100644 --- a/packages/frontend/apps/android/package.json +++ b/packages/frontend/apps/android/package.json @@ -14,7 +14,7 @@ "@affine/core": "workspace:*", "@affine/i18n": "workspace:*", "@blocksuite/affine": "0.17.28", - "@blocksuite/icons": "^2.1.67", + "@blocksuite/icons": "^2.1.69", "@capacitor/android": "^6.1.2", "@capacitor/core": "^6.1.2", "@sentry/react": "^8.0.0", diff --git a/packages/frontend/apps/ios/package.json b/packages/frontend/apps/ios/package.json index 35cf19d634..126d5eb90f 100644 --- a/packages/frontend/apps/ios/package.json +++ b/packages/frontend/apps/ios/package.json @@ -14,7 +14,7 @@ "@affine/core": "workspace:*", "@affine/i18n": "workspace:*", "@blocksuite/affine": "0.17.28", - "@blocksuite/icons": "^2.1.67", + "@blocksuite/icons": "^2.1.69", "@capacitor/app": "^6.0.1", "@capacitor/browser": "^6.0.3", "@capacitor/core": "^6.1.2", diff --git a/packages/frontend/apps/mobile/package.json b/packages/frontend/apps/mobile/package.json index eed477555d..262c33586b 100644 --- a/packages/frontend/apps/mobile/package.json +++ b/packages/frontend/apps/mobile/package.json @@ -14,7 +14,7 @@ "@affine/core": "workspace:*", "@affine/i18n": "workspace:*", "@blocksuite/affine": "0.17.28", - "@blocksuite/icons": "^2.1.67", + "@blocksuite/icons": "^2.1.69", "@sentry/react": "^8.0.0", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/packages/frontend/core/src/modules/peek-view/entities/peek-view.ts b/packages/frontend/core/src/modules/peek-view/entities/peek-view.ts index 9dfe4935d6..2ce82f61d8 100644 --- a/packages/frontend/core/src/modules/peek-view/entities/peek-view.ts +++ b/packages/frontend/core/src/modules/peek-view/entities/peek-view.ts @@ -119,34 +119,25 @@ function resolvePeekInfoFromPeekTarget( if (element instanceof AffineReference) { const referenceInfo = element.referenceInfo; if (referenceInfo) { - const { pageId: docId } = referenceInfo; + const { pageId: docId, params } = referenceInfo; const info: DocPeekViewInfo = { type: 'doc', - docRef: { - docId, - }, + docRef: { docId, ...params }, }; - Object.assign(info, referenceInfo.params); return info; } } else if ('model' in element) { const blockModel = element.model; - if (isEmbedLinkedDocModel(blockModel)) { + if ( + isEmbedLinkedDocModel(blockModel) || + isEmbedSyncedDocModel(blockModel) + ) { + const { pageId: docId, params } = blockModel; const info: DocPeekViewInfo = { type: 'doc', - docRef: { - docId: blockModel.pageId, - }, + docRef: { docId, ...params }, }; - Object.assign(info, blockModel.params); return info; - } else if (isEmbedSyncedDocModel(blockModel)) { - return { - type: 'doc', - docRef: { - docId: blockModel.pageId, - }, - }; } else if (isSurfaceRefModel(blockModel)) { const refModel = (element as SurfaceRefBlockComponent).referenceModel; // refModel can be null if the reference is invalid diff --git a/tests/affine-local/e2e/links.spec.ts b/tests/affine-local/e2e/links.spec.ts new file mode 100644 index 0000000000..bd1a8c6959 --- /dev/null +++ b/tests/affine-local/e2e/links.spec.ts @@ -0,0 +1,333 @@ +import { test } from '@affine-test/kit/playwright'; +import { + pasteByKeyboard, + writeTextToClipboard, +} from '@affine-test/kit/utils/keyboard'; +import { coreUrl, openHomePage } from '@affine-test/kit/utils/load-page'; +import { + clickNewPageButton, + createLinkedPage, + waitForEmptyEditor, +} from '@affine-test/kit/utils/page-logic'; +import { expect, type Locator } from '@playwright/test'; + +test.beforeEach(async ({ page }) => { + await openHomePage(page); + await clickNewPageButton(page); + await waitForEmptyEditor(page); +}); + +async function notClickable(locator: Locator) { + await expect(locator).toHaveAttribute('disabled', ''); +} + +async function clickable(locator: Locator) { + await expect(locator).not.toHaveAttribute('disabled', ''); +} + +test('not allowed to switch to embed view when linking to the same document', async ({ + page, +}) => { + await page.keyboard.press('Enter'); + await page.waitForTimeout(500); + + const url0 = new URL(page.url()); + + await writeTextToClipboard(page, url0.toString()); + await pasteByKeyboard(page); + + // Inline + await page.locator('affine-reference').hover(); + await page.getByLabel('Switch view').click(); + + const linkToInlineBtn = page.getByTestId('link-to-inline'); + const linkToCardBtn = page.getByTestId('link-to-card'); + const linkToEmbedBtn = page.getByTestId('link-to-embed'); + + await notClickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await notClickable(linkToEmbedBtn); + + // Switches to card view + await linkToCardBtn.click(); + + // Card + await page.locator('affine-embed-linked-doc-block').dblclick(); + + const peekViewModel = page.getByTestId('peek-view-modal'); + await expect(peekViewModel).toBeVisible(); + await expect(peekViewModel.locator('page-editor')).toBeVisible(); + await page.keyboard.press('Escape'); + await expect(peekViewModel).not.toBeVisible(); + + await page.locator('affine-embed-linked-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await notClickable(linkToCardBtn); + await notClickable(linkToEmbedBtn); +}); + +test('not allowed to switch to embed view when linking to block', async ({ + page, +}) => { + await page.keyboard.press('Enter'); + await createLinkedPage(page, 'Test Page'); + + await page.locator('affine-reference').hover(); + await page.getByLabel('Switch view').click(); + + const linkToInlineBtn = page.getByTestId('link-to-inline'); + const linkToCardBtn = page.getByTestId('link-to-card'); + const linkToEmbedBtn = page.getByTestId('link-to-embed'); + + await notClickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await clickable(linkToEmbedBtn); + + // Switches to card view + await linkToCardBtn.click(); + + // Card + await page.locator('affine-embed-linked-doc-block').dblclick(); + + const peekViewModel = page.getByTestId('peek-view-modal'); + await expect(peekViewModel).toBeVisible(); + await expect(peekViewModel.locator('page-editor')).toBeVisible(); + await page.keyboard.press('Escape'); + await expect(peekViewModel).not.toBeVisible(); + + await page.locator('affine-embed-linked-doc-block').click(); + + await page.locator('affine-embed-card-toolbar').getByLabel('More').click(); + await page.getByLabel('Copy link to block').click(); + + await page.keyboard.press('Enter'); + await pasteByKeyboard(page); + + const href0 = await page + .locator('affine-reference') + .locator('a') + .getAttribute('href'); + + await page.locator('affine-reference').hover(); + await page.getByLabel('Switch view').click(); + + await notClickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await notClickable(linkToEmbedBtn); + + // Switches to card view + await linkToCardBtn.click(); + + await page.locator('affine-embed-linked-doc-block').nth(1).dblclick(); + + await expect(peekViewModel).toBeVisible(); + await expect(peekViewModel.locator('page-editor')).toBeVisible(); + await page.keyboard.press('Escape'); + await expect(peekViewModel).not.toBeVisible(); + + await page.locator('affine-embed-linked-doc-block').nth(1).click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await notClickable(linkToCardBtn); + await notClickable(linkToEmbedBtn); + + // Switches to inline view + await linkToInlineBtn.click(); + + const href1 = await page + .locator('affine-reference') + .locator('a') + .getAttribute('href'); + + expect(href0).not.toBeNull(); + expect(href1).not.toBeNull(); + + const url0 = new URL(href0!, coreUrl); + const url1 = new URL(href1!, coreUrl); + + url0.searchParams.delete('refreshKey'); + url1.searchParams.delete('refreshKey'); + expect(url0.toJSON()).toStrictEqual(url1.toJSON()); +}); + +test('allow switching to embed view when linking to the other document without mode', async ({ + page, +}) => { + await page.keyboard.press('Enter'); + await createLinkedPage(page, 'Test Page'); + + // Inline + await page.locator('affine-reference').hover(); + await page.getByLabel('Switch view').click(); + + const linkToInlineBtn = page.getByTestId('link-to-inline'); + const linkToCardBtn = page.getByTestId('link-to-card'); + const linkToEmbedBtn = page.getByTestId('link-to-embed'); + + await notClickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await clickable(linkToEmbedBtn); + + // Switches to card view + await linkToCardBtn.click(); + + // Card + await page.locator('affine-embed-linked-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await notClickable(linkToCardBtn); + await clickable(linkToEmbedBtn); + + // Switches to embed view + await linkToEmbedBtn.click(); + + // Embed + await page.locator('affine-embed-synced-doc-block').click(); + await page.waitForTimeout(300); + await page.locator('affine-embed-synced-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await notClickable(linkToEmbedBtn); + + // Closes + await page.getByLabel('Switch view').click(); + await expect( + page.locator('.affine-embed-synced-doc-container.page') + ).toBeVisible(); + + // Opens in peek view + await page.locator('affine-embed-synced-doc-block').dblclick(); + + const peekViewModel = page.getByTestId('peek-view-modal'); + await expect(peekViewModel).toBeVisible(); + await expect(peekViewModel.locator('page-editor')).toBeVisible(); + await page.keyboard.press('Escape'); + await expect(peekViewModel).not.toBeVisible(); + await page.waitForTimeout(300); + + await page.locator('affine-embed-synced-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await notClickable(linkToEmbedBtn); + + // Switches to card view + await linkToCardBtn.click(); + + await page.locator('affine-embed-linked-doc-block').click(); + await page.waitForTimeout(300); + await page.locator('affine-embed-linked-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await notClickable(linkToCardBtn); + await clickable(linkToEmbedBtn); + + // Switches to inline view + await linkToInlineBtn.click(); + + await expect(page.locator('affine-reference')).toBeVisible(); +}); + +test('allow switching to embed view when linking to the other document with mode', async ({ + page, +}) => { + await page.keyboard.press('Enter'); + await createLinkedPage(page, 'Test Page'); + + const url = new URL(page.url()); + url.searchParams.append('mode', 'edgeless'); + + await page.locator('affine-reference').click(); + await page.waitForTimeout(300); + await page.keyboard.press('Enter'); + + await writeTextToClipboard(page, url.toString()); + await pasteByKeyboard(page); + + // Inline + await page.locator('affine-reference').hover(); + await page.getByLabel('Switch view').click(); + + const linkToInlineBtn = page.getByTestId('link-to-inline'); + const linkToCardBtn = page.getByTestId('link-to-card'); + const linkToEmbedBtn = page.getByTestId('link-to-embed'); + + await notClickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await clickable(linkToEmbedBtn); + + // Switches to card view + await linkToCardBtn.click(); + + // Card + await page.locator('affine-embed-linked-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await notClickable(linkToCardBtn); + await clickable(linkToEmbedBtn); + + // Switches to embed view + await linkToEmbedBtn.click(); + + // Embed + await page.locator('affine-embed-synced-doc-block').click(); + await page.waitForTimeout(300); + await page.locator('affine-embed-synced-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await notClickable(linkToEmbedBtn); + + // Closes + await page.getByLabel('Switch view').click(); + await expect( + page.locator('.affine-embed-synced-doc-container.edgeless') + ).toBeVisible(); + + // Opens in peek view + await page.locator('affine-embed-synced-doc-block').dblclick(); + + const peekViewModel = page.getByTestId('peek-view-modal'); + await expect(peekViewModel).toBeVisible(); + await expect(peekViewModel.locator('edgeless-editor')).toBeVisible(); + await page.keyboard.press('Escape'); + await expect(peekViewModel).not.toBeVisible(); + await page.waitForTimeout(300); + + await page.locator('affine-embed-synced-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await clickable(linkToCardBtn); + await notClickable(linkToEmbedBtn); + + // Switches to card view + await linkToCardBtn.click(); + + await page.locator('affine-embed-linked-doc-block').click(); + await page.getByLabel('Switch view').click(); + + await clickable(linkToInlineBtn); + await notClickable(linkToCardBtn); + await clickable(linkToEmbedBtn); + + // Switches to inline view + await linkToInlineBtn.click(); + + await page.locator('affine-reference').click(); + + // Checks the url + const url2 = new URL(page.url()); + url2.searchParams.delete('refreshKey'); + expect(url.toJSON()).toStrictEqual(url2.toJSON()); +}); diff --git a/tests/affine-local/e2e/quick-search.spec.ts b/tests/affine-local/e2e/quick-search.spec.ts index c506c17f5c..44240a199e 100644 --- a/tests/affine-local/e2e/quick-search.spec.ts +++ b/tests/affine-local/e2e/quick-search.spec.ts @@ -1,6 +1,9 @@ import { test } from '@affine-test/kit/playwright'; import { clickEdgelessModeButton } from '@affine-test/kit/utils/editor'; -import { withCtrlOrMeta } from '@affine-test/kit/utils/keyboard'; +import { + withCtrlOrMeta, + writeTextToClipboard, +} from '@affine-test/kit/utils/keyboard'; import { openHomePage } from '@affine-test/kit/utils/load-page'; import { clickNewPageButton, @@ -510,25 +513,7 @@ test('can paste a doc link to create link reference', async ({ page }) => { await page.keyboard.press('Enter'); // paste the url - await page.evaluate( - async ([url]) => { - const clipData = { - 'text/plain': url, - }; - const e = new ClipboardEvent('paste', { - clipboardData: new DataTransfer(), - }); - Object.defineProperty(e, 'target', { - writable: false, - value: document, - }); - Object.entries(clipData).forEach(([key, value]) => { - e.clipboardData?.setData(key, value); - }); - document.dispatchEvent(e); - }, - [url] - ); + await writeTextToClipboard(page, url); // check the link reference await page.waitForTimeout(500); diff --git a/tests/kit/utils/keyboard.ts b/tests/kit/utils/keyboard.ts index 8da69c9818..a51dd81664 100644 --- a/tests/kit/utils/keyboard.ts +++ b/tests/kit/utils/keyboard.ts @@ -63,3 +63,26 @@ export async function pasteByKeyboard(page: Page) { await page.keyboard.press('v', { delay: 50 }); await keyUpCtrlOrMeta(page); } + +export async function writeTextToClipboard(page: Page, text: string) { + // paste the url + await page.evaluate( + async ([text]) => { + const clipData = { + 'text/plain': text, + }; + const e = new ClipboardEvent('paste', { + clipboardData: new DataTransfer(), + }); + Object.defineProperty(e, 'target', { + writable: false, + value: document, + }); + Object.entries(clipData).forEach(([key, value]) => { + e.clipboardData?.setData(key, value); + }); + document.dispatchEvent(e); + }, + [text] + ); +} diff --git a/yarn.lock b/yarn.lock index 5106b3f516..c5449088c7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -223,7 +223,7 @@ __metadata: "@affine/core": "workspace:*" "@affine/i18n": "workspace:*" "@blocksuite/affine": "npm:0.17.28" - "@blocksuite/icons": "npm:^2.1.67" + "@blocksuite/icons": "npm:^2.1.69" "@capacitor/android": "npm:^6.1.2" "@capacitor/cli": "npm:^6.1.2" "@capacitor/core": "npm:^6.1.2" @@ -620,7 +620,7 @@ __metadata: "@affine/core": "workspace:*" "@affine/i18n": "workspace:*" "@blocksuite/affine": "npm:0.17.28" - "@blocksuite/icons": "npm:^2.1.67" + "@blocksuite/icons": "npm:^2.1.69" "@capacitor/app": "npm:^6.0.1" "@capacitor/browser": "npm:^6.0.3" "@capacitor/cli": "npm:^6.1.2" @@ -646,7 +646,7 @@ __metadata: "@affine/core": "workspace:*" "@affine/i18n": "workspace:*" "@blocksuite/affine": "npm:0.17.28" - "@blocksuite/icons": "npm:^2.1.67" + "@blocksuite/icons": "npm:^2.1.69" "@sentry/react": "npm:^8.0.0" "@types/react": "npm:^18.2.75" "@types/react-dom": "npm:^18.2.24" @@ -2812,7 +2812,7 @@ __metadata: languageName: node linkType: hard -"@blocksuite/icons@npm:2.1.69, @blocksuite/icons@npm:^2.1.67, @blocksuite/icons@npm:^2.1.68": +"@blocksuite/icons@npm:2.1.69, @blocksuite/icons@npm:^2.1.68, @blocksuite/icons@npm:^2.1.69": version: 2.1.69 resolution: "@blocksuite/icons@npm:2.1.69" peerDependencies: