From 49d18ccc496180f0b0f2b8b97b79220da3ac732a Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 21 Oct 2026 15:20:01 -0700 Subject: [PATCH 1/3] fix(docs): cover clipboard activation and exported content --- apps/docs/scripts/test-navigation-e2e.ts | 106 +++++++++++++++++++---- 1 file changed, 87 insertions(+), 19 deletions(-) diff --git a/apps/docs/scripts/test-navigation-e2e.ts b/apps/docs/scripts/test-navigation-e2e.ts index dd9f4bfe93b..564705cb7e9 100644 --- a/apps/docs/scripts/test-navigation-e2e.ts +++ b/apps/docs/scripts/test-navigation-e2e.ts @@ -16,11 +16,15 @@ if (!['localhost', '127.0.0.1', '[::1]'].includes(target.hostname)) { } const pages = [ - '/introduction', - '/getting-started', - '/files', - '/integrations/apollo', - '/api-reference/workflows/listWorkflows', + { path: '/introduction', title: 'Introduction', content: '## Models and integrations' }, + { path: '/getting-started', title: 'Getting started', content: '## Start in Chat' }, + { path: '/files', title: 'Files', content: '## Upload or create a file' }, + { path: '/integrations/apollo', title: 'Apollo', content: '### Apollo People Search' }, + { + path: '/api-reference/workflows/listWorkflows', + title: 'List workflows', + content: '`GET /api/v2/workflows`', + }, ] as const const viewports = [ { name: 'desktop', width: 1440, height: 1000 }, @@ -33,7 +37,7 @@ let browser: Browser | undefined try { browser = await chromium.launch() for (const viewport of viewports) { - for (const path of pages) { + for (const { path, title, content } of pages) { const started = performance.now() const context = await browser.newContext({ viewport, reducedMotion: 'reduce' }) const page = await context.newPage() @@ -85,6 +89,9 @@ try { const markdown = await page.request.get(markdownUrl) expect(markdown.ok()).toBe(true) const expected = await markdown.text() + expect(expected).toContain(`# ${title} (${path})`) + expect(expected).toContain(content) + if (path.startsWith('/api-reference/')) expect(expected).toContain('## Responses') const copy = page.getByRole('button', { name: 'Copy page content', exact: true }) await context.grantPermissions(['clipboard-read', 'clipboard-write']) expect(markdownRequests).toEqual([]) @@ -94,18 +101,21 @@ try { await expect .poll(() => page.evaluate(() => navigator.clipboard.readText())) .toBe(expected) - await page.route(markdownUrl, (route) => - route.fulfill({ status: 503, body: 'Unavailable' }) - ) - await page.getByRole('button', { name: 'Copied to clipboard' }).click() - await expect(page.getByRole('status')).toHaveText('Could not copy. Try again.') - await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toHaveCount(0) - await expect - .poll(() => page.evaluate(() => navigator.clipboard.readText())) - .toBe(expected) - await page.unroute(markdownUrl) - await copy.click() - await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toBeVisible() + for (const response of [ + { status: 503, body: 'Unavailable' }, + { status: 200, body: ' \n ' }, + ]) { + await page.route(markdownUrl, (route) => route.fulfill(response)) + await page.getByRole('button', { name: 'Copied to clipboard' }).click() + await expect(page.getByRole('status')).toHaveText('Could not copy. Try again.') + await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toHaveCount(0) + await expect + .poll(() => page.evaluate(() => navigator.clipboard.readText())) + .toBe(expected) + await page.unroute(markdownUrl) + await copy.click() + await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toBeVisible() + } } await page.screenshot({ path: join(dirname(reportPath), `${viewport.name}${path.replaceAll('/', '-')}.png`), @@ -146,11 +156,14 @@ try { new URL('/llms.mdx/platform/costs', origin).toString() ) expect(markdown.ok()).toBe(true) + const expected = await markdown.text() + expect(expected).toContain('# Credits, billing and limits (/platform/costs)') + expect(expected).toContain('## What a workflow run costs') await page.getByRole('button', { name: 'Copy page content', exact: true }).click() await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toBeVisible() await expect .poll(() => page.evaluate(() => navigator.clipboard.readText())) - .toBe(await markdown.text()) + .toBe(expected) } } results.push({ @@ -176,6 +189,61 @@ try { } } } + const started = performance.now() + const context = await browser.newContext() + try { + await context.addInitScript(() => { + Object.defineProperty(window, 'ClipboardItem', { value: undefined }) + const writeText = navigator.clipboard.writeText.bind(navigator.clipboard) + navigator.clipboard.writeText = (text) => { + document.documentElement.dataset.clipboardActivation = String( + navigator.userActivation.isActive + ) + return writeText(text) + } + }) + const page = await context.newPage() + const markdownUrl = new URL('/llms.mdx/introduction', origin).toString() + let requests = 0 + await page.route(markdownUrl, async (route) => { + requests += 1 + await page.waitForFunction(() => !navigator.userActivation.isActive) + await route.continue() + }) + await page.goto(new URL('/introduction', origin).toString(), { waitUntil: 'networkidle' }) + const copy = page.getByRole('button', { name: 'Copy page content', exact: true }) + const loaded = page.waitForResponse(markdownUrl) + await copy.click() + await (await loaded).finished() + await expect(copy).toBeEnabled() + await expect(page.locator('html')).not.toHaveAttribute('data-clipboard-activation') + await copy.click() + await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toBeVisible() + await expect(page.locator('html')).toHaveAttribute('data-clipboard-activation', 'true') + expect(requests).toBe(1) + await context.grantPermissions(['clipboard-read']) + const copied = await page.evaluate(() => navigator.clipboard.readText()) + expect(copied).toContain('# Introduction (/introduction)') + expect(copied).toContain('## Models and integrations') + await page.screenshot({ path: join(dirname(reportPath), 'legacy-clipboard.png') }) + results.push({ + path: '/introduction', + viewport: 'legacy-clipboard', + status: 'passed', + durationMs: Math.round(performance.now() - started), + }) + } catch (error) { + failed = true + results.push({ + path: '/introduction', + viewport: 'legacy-clipboard', + status: 'failed', + durationMs: Math.round(performance.now() - started), + error: getErrorMessage(error), + }) + } finally { + await context.close() + } } catch (error) { failed = true results.push({ path: 'browser', status: 'failed', error: getErrorMessage(error) }) From 63fcaf7d5f9c10a92d4d8b59e9d71d83b065e898 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 21 Oct 2026 15:25:07 -0700 Subject: [PATCH 2/3] fix(docs): preserve clipboard activation and reject blank exports --- apps/docs/components/page-actions.tsx | 43 +++++++++++++++--------- apps/docs/scripts/test-navigation-e2e.ts | 34 ++++++++++++++++--- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/apps/docs/components/page-actions.tsx b/apps/docs/components/page-actions.tsx index 7be1cd3ac9d..c55fa06e7e9 100644 --- a/apps/docs/components/page-actions.tsx +++ b/apps/docs/components/page-actions.tsx @@ -1,6 +1,6 @@ 'use client' -import { useState } from 'react' +import { useRef, useState } from 'react' import { Chip, useCopyToClipboard } from '@sim/emcn' import { Check, Duplicate } from '@sim/emcn/icons' @@ -10,31 +10,38 @@ interface LLMCopyButtonProps { export function LLMCopyButton({ markdownUrl }: LLMCopyButtonProps) { const { copied, copy } = useCopyToClipboard() - const [pending, setPending] = useState(false) - const [failed, setFailed] = useState(false) - const checked = copied && !pending && !failed + const [status, setStatus] = useState<'idle' | 'pending' | 'ready' | 'failed'>('idle') + const preparedMarkdown = useRef(null) + const pending = status === 'pending' + const checked = copied && status === 'idle' async function onClick() { - setPending(true) - setFailed(false) + setStatus('pending') const prepare = async () => { const response = await fetch(markdownUrl, { signal: typeof AbortSignal.timeout === 'function' ? AbortSignal.timeout(10000) : undefined, }) if (!response.ok) throw new Error('Could not load page Markdown') - return response.text() + const markdown = await response.text() + if (!markdown.trim()) throw new Error('Page Markdown is empty') + return markdown } try { // Promise-backed writes preserve Safari's transient user activation during the fetch. - const success = - typeof ClipboardItem !== 'undefined' && typeof navigator.clipboard.write === 'function' - ? await copy({ prepare, fallback: '' }) - : await copy(await prepare()) - setFailed(!success) + if (typeof ClipboardItem !== 'undefined' && typeof navigator.clipboard.write === 'function') { + const success = await copy({ prepare, fallback: '' }) + setStatus(success ? 'idle' : 'failed') + return + } + if (preparedMarkdown.current === null) { + preparedMarkdown.current = await prepare() + setStatus('ready') + return + } + const success = await copy(preparedMarkdown.current) + setStatus(success ? 'idle' : 'failed') } catch { - setFailed(true) - } finally { - setPending(false) + setStatus('failed') } } @@ -50,7 +57,11 @@ export function LLMCopyButton({ markdownUrl }: LLMCopyButtonProps) { {checked ? 'Copied' : pending ? 'Copying…' : 'Copy page'} - {failed ? 'Could not copy. Try again.' : ''} + {status === 'failed' + ? 'Could not copy. Try again.' + : status === 'ready' + ? 'Click again to copy.' + : ''} ) diff --git a/apps/docs/scripts/test-navigation-e2e.ts b/apps/docs/scripts/test-navigation-e2e.ts index 564705cb7e9..33ca0a1e255 100644 --- a/apps/docs/scripts/test-navigation-e2e.ts +++ b/apps/docs/scripts/test-navigation-e2e.ts @@ -204,14 +204,14 @@ try { }) const page = await context.newPage() const markdownUrl = new URL('/llms.mdx/introduction', origin).toString() - let requests = 0 - await page.route(markdownUrl, async (route) => { - requests += 1 + const requests: string[] = [] + await page.route('**/llms.mdx/**', async (route) => { + requests.push(route.request().url()) await page.waitForFunction(() => !navigator.userActivation.isActive) await route.continue() }) await page.goto(new URL('/introduction', origin).toString(), { waitUntil: 'networkidle' }) - const copy = page.getByRole('button', { name: 'Copy page content', exact: true }) + const copy = page.getByRole('button', { name: /^(Copy page content|Copied to clipboard)$/ }) const loaded = page.waitForResponse(markdownUrl) await copy.click() await (await loaded).finished() @@ -220,12 +220,36 @@ try { await copy.click() await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toBeVisible() await expect(page.locator('html')).toHaveAttribute('data-clipboard-activation', 'true') - expect(requests).toBe(1) + expect(requests).toEqual([markdownUrl]) await context.grantPermissions(['clipboard-read']) const copied = await page.evaluate(() => navigator.clipboard.readText()) expect(copied).toContain('# Introduction (/introduction)') expect(copied).toContain('## Models and integrations') await page.screenshot({ path: join(dirname(reportPath), 'legacy-clipboard.png') }) + await context.clearPermissions() + const navigation = page.getByRole('complementary', { name: 'Documentation navigation' }) + await navigation.getByRole('button', { name: 'Platform', exact: true }).click() + await navigation.getByRole('link', { name: 'Credits, billing and limits', exact: true }).click() + await expect(page).toHaveURL(new URL('/platform/costs', origin).toString()) + await expect( + page.getByRole('heading', { level: 1, name: 'Credits, billing and limits', exact: true }) + ).toBeVisible() + const nextMarkdownUrl = new URL('/llms.mdx/platform/costs', origin).toString() + const nextLoaded = page.waitForResponse(nextMarkdownUrl) + await copy.click() + await (await nextLoaded).finished() + await expect(copy).toBeEnabled() + await context.grantPermissions(['clipboard-read']) + expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(copied) + await context.clearPermissions() + await copy.click() + await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toBeVisible() + await expect(page.locator('html')).toHaveAttribute('data-clipboard-activation', 'true') + expect(requests).toEqual([markdownUrl, nextMarkdownUrl]) + await context.grantPermissions(['clipboard-read']) + const nextCopied = await page.evaluate(() => navigator.clipboard.readText()) + expect(nextCopied).toContain('# Credits, billing and limits (/platform/costs)') + expect(nextCopied).toContain('## What a workflow run costs') results.push({ path: '/introduction', viewport: 'legacy-clipboard', From 8b5b98a25a23f14cc5b1987336930a81d75791ba Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 21 Oct 2026 15:35:47 -0700 Subject: [PATCH 3/3] fix(docs): isolate clipboard permissions in browser checks --- apps/docs/scripts/test-navigation-e2e.ts | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/apps/docs/scripts/test-navigation-e2e.ts b/apps/docs/scripts/test-navigation-e2e.ts index 33ca0a1e255..bec6466ef44 100644 --- a/apps/docs/scripts/test-navigation-e2e.ts +++ b/apps/docs/scripts/test-navigation-e2e.ts @@ -190,7 +190,7 @@ try { } } const started = performance.now() - const context = await browser.newContext() + const context = await browser.newContext({ permissions: ['clipboard-read', 'clipboard-write'] }) try { await context.addInitScript(() => { Object.defineProperty(window, 'ClipboardItem', { value: undefined }) @@ -217,16 +217,15 @@ try { await (await loaded).finished() await expect(copy).toBeEnabled() await expect(page.locator('html')).not.toHaveAttribute('data-clipboard-activation') + await page.screenshot({ path: join(dirname(reportPath), 'legacy-clipboard-ready.png') }) await copy.click() await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toBeVisible() await expect(page.locator('html')).toHaveAttribute('data-clipboard-activation', 'true') expect(requests).toEqual([markdownUrl]) - await context.grantPermissions(['clipboard-read']) const copied = await page.evaluate(() => navigator.clipboard.readText()) expect(copied).toContain('# Introduction (/introduction)') expect(copied).toContain('## Models and integrations') await page.screenshot({ path: join(dirname(reportPath), 'legacy-clipboard.png') }) - await context.clearPermissions() const navigation = page.getByRole('complementary', { name: 'Documentation navigation' }) await navigation.getByRole('button', { name: 'Platform', exact: true }).click() await navigation.getByRole('link', { name: 'Credits, billing and limits', exact: true }).click() @@ -239,14 +238,11 @@ try { await copy.click() await (await nextLoaded).finished() await expect(copy).toBeEnabled() - await context.grantPermissions(['clipboard-read']) expect(await page.evaluate(() => navigator.clipboard.readText())).toBe(copied) - await context.clearPermissions() await copy.click() await expect(page.getByRole('button', { name: 'Copied to clipboard' })).toBeVisible() await expect(page.locator('html')).toHaveAttribute('data-clipboard-activation', 'true') expect(requests).toEqual([markdownUrl, nextMarkdownUrl]) - await context.grantPermissions(['clipboard-read']) const nextCopied = await page.evaluate(() => navigator.clipboard.readText()) expect(nextCopied).toContain('# Credits, billing and limits (/platform/costs)') expect(nextCopied).toContain('## What a workflow run costs')