From 73e0cbb2d659a4236114cd550bbb6cc5efcb61e9 Mon Sep 17 00:00:00 2001 From: Steven H Date: Wed, 18 Jun 2025 13:26:20 +0100 Subject: [PATCH] Fix an issue where PDF export URLs were not bringing their query params. (#3351) --- .changeset/rich-buses-hunt.md | 5 + packages/gitbook-v2/src/middleware.ts | 2 + packages/gitbook/e2e/pdf.spec.ts | 170 ++++++++++++++++++ packages/gitbook/e2e/util.ts | 2 +- packages/gitbook/package.json | 2 +- .../gitbook/src/components/PDF/PDFPage.tsx | 2 +- 6 files changed, 180 insertions(+), 3 deletions(-) create mode 100644 .changeset/rich-buses-hunt.md create mode 100644 packages/gitbook/e2e/pdf.spec.ts diff --git a/.changeset/rich-buses-hunt.md b/.changeset/rich-buses-hunt.md new file mode 100644 index 000000000..fbc59d232 --- /dev/null +++ b/.changeset/rich-buses-hunt.md @@ -0,0 +1,5 @@ +--- +"gitbook-v2": patch +--- + +Fix an issue where PDF export URLs were not keeping their query params. diff --git a/packages/gitbook-v2/src/middleware.ts b/packages/gitbook-v2/src/middleware.ts index d413d6a43..4bcbf77ae 100644 --- a/packages/gitbook-v2/src/middleware.ts +++ b/packages/gitbook-v2/src/middleware.ts @@ -259,6 +259,8 @@ async function serveSiteRoutes(requestURL: URL, request: NextRequest) { console.log(`rewriting ${request.nextUrl.toString()} to ${route}`); const rewrittenURL = new URL(`/${route}`, request.nextUrl.toString()); + rewrittenURL.search = request.nextUrl.search; // Preserve the original search params + const response = NextResponse.rewrite(rewrittenURL, { request: { headers: requestHeaders, diff --git a/packages/gitbook/e2e/pdf.spec.ts b/packages/gitbook/e2e/pdf.spec.ts new file mode 100644 index 000000000..4798a2a04 --- /dev/null +++ b/packages/gitbook/e2e/pdf.spec.ts @@ -0,0 +1,170 @@ +import { argosScreenshot } from '@argos-ci/playwright'; +import { expect, test } from '@playwright/test'; +import { getContentTestURL } from '../tests/utils'; +import { waitForIcons } from './util'; + +test.describe('PDF export', () => { + test('export all pages as PDF (e2e)', async ({ page }) => { + // Set the header to disable the Vercel toolbar + // But only on the main document as it'd cause CORS issues on other resources + await page.route('**/*', async (route, request) => { + if (request.resourceType() === 'document') { + await route.continue({ + headers: { + ...request.headers(), + 'x-vercel-skip-toolbar': '1', + }, + }); + } else { + await route.continue(); + } + }); + + await page.goto( + getContentTestURL( + 'https://gitbook-open-e2e-sites.gitbook.io/gitbook-doc/~gitbook/pdf?limit=10' + ) + ); + + const printBtn = page.getByTestId('print-button'); + await expect(printBtn).toBeVisible(); + + await argosScreenshot(page, 'pdf - all pages', { + viewports: ['macbook-13'], + argosCSS: ` + /* Hide Intercom */ + .intercom-lightweight-app { + display: none !important; + } + `, + threshold: undefined, + fullPage: true, + beforeScreenshot: async ({ runStabilization }) => { + await runStabilization(); + await waitForIcons(page); + }, + }); + }); + + test('export all pages as PDF (GitBook docs)', async ({ page }) => { + // Set the header to disable the Vercel toolbar + // But only on the main document as it'd cause CORS issues on other resources + await page.route('**/*', async (route, request) => { + if (request.resourceType() === 'document') { + await route.continue({ + headers: { + ...request.headers(), + 'x-vercel-skip-toolbar': '1', + }, + }); + } else { + await route.continue(); + } + }); + + await page.goto(getContentTestURL('https://gitbook.com/docs/~gitbook/pdf?limit=10')); + + const printBtn = page.getByTestId('print-button'); + await expect(printBtn).toBeVisible(); + + await argosScreenshot(page, 'pdf - all pages', { + viewports: ['macbook-13'], + argosCSS: ` + /* Hide Intercom */ + .intercom-lightweight-app { + display: none !important; + } + `, + threshold: undefined, + fullPage: true, + beforeScreenshot: async ({ runStabilization }) => { + await runStabilization(); + await waitForIcons(page); + }, + }); + }); + + test('export a single page as PDF (e2e)', async ({ page }) => { + // Set the header to disable the Vercel toolbar + // But only on the main document as it'd cause CORS issues on other resources + await page.route('**/*', async (route, request) => { + if (request.resourceType() === 'document') { + await route.continue({ + headers: { + ...request.headers(), + 'x-vercel-skip-toolbar': '1', + }, + }); + } else { + await route.continue(); + } + }); + + await page.goto( + getContentTestURL( + 'https://gitbook-open-e2e-sites.gitbook.io/gitbook-doc/~gitbook/pdf?page=Bw7LjWwgTjV8nIV4s7rs&only=yes&limit=2' + ) + ); + + const printBtn = page.getByTestId('print-button'); + await expect(printBtn).toBeVisible(); + + await argosScreenshot(page, 'pdf - all pages', { + viewports: ['macbook-13'], + argosCSS: ` + /* Hide Intercom */ + .intercom-lightweight-app { + display: none !important; + } + `, + threshold: undefined, + fullPage: true, + beforeScreenshot: async ({ runStabilization }) => { + await runStabilization(); + await waitForIcons(page); + }, + }); + }); + + test('export a single page as PDF (GitBook docs)', async ({ page }) => { + // Set the header to disable the Vercel toolbar + // But only on the main document as it'd cause CORS issues on other resources + await page.route('**/*', async (route, request) => { + if (request.resourceType() === 'document') { + await route.continue({ + headers: { + ...request.headers(), + 'x-vercel-skip-toolbar': '1', + }, + }); + } else { + await route.continue(); + } + }); + + await page.goto( + getContentTestURL( + 'https://gitbook.com/docs/~gitbook/pdf?page=DfnNkU49mvLe2ythHAyx&only=yes&limit=2' + ) + ); + + const printBtn = page.getByTestId('print-button'); + await expect(printBtn).toBeVisible(); + + await argosScreenshot(page, 'pdf - all pages', { + viewports: ['macbook-13'], + argosCSS: ` + /* Hide Intercom */ + .intercom-lightweight-app { + display: none !important; + } + `, + threshold: undefined, + fullPage: true, + beforeScreenshot: async ({ runStabilization }) => { + await runStabilization(); + await waitForIcons(page); + }, + }); + }); +}); diff --git a/packages/gitbook/e2e/util.ts b/packages/gitbook/e2e/util.ts index f54b70fbe..937b776d7 100644 --- a/packages/gitbook/e2e/util.ts +++ b/packages/gitbook/e2e/util.ts @@ -346,7 +346,7 @@ export function getCustomizationURL(partial: DeepPartial { const urlStates: Record< string, diff --git a/packages/gitbook/package.json b/packages/gitbook/package.json index e54901316..a430218cf 100644 --- a/packages/gitbook/package.json +++ b/packages/gitbook/package.json @@ -8,7 +8,7 @@ "build:cloudflare": "next-on-pages --custom-entrypoint=./src/cloudflare-entrypoint.ts", "start": "next start", "typecheck": "tsc --noEmit", - "e2e": "playwright test e2e/internal.spec.ts", + "e2e": "playwright test e2e/internal.spec.ts e2e/pdf.spec.ts", "e2e-customers": "playwright test e2e/customers.spec.ts", "unit": "bun test {src,packages} --preload ./tests/preload-bun.ts", "generate": "gitbook-icons ./public/~gitbook/static/icons custom-icons && gitbook-math ./public/~gitbook/static/math", diff --git a/packages/gitbook/src/components/PDF/PDFPage.tsx b/packages/gitbook/src/components/PDF/PDFPage.tsx index cc9bf2807..d2bc5c91a 100644 --- a/packages/gitbook/src/components/PDF/PDFPage.tsx +++ b/packages/gitbook/src/components/PDF/PDFPage.tsx @@ -53,7 +53,7 @@ export async function PDFPage(props: { }) { const baseContext = props.context; const searchParams = new URLSearchParams(props.searchParams); - const pdfParams = getPDFSearchParams(new URLSearchParams(searchParams)); + const pdfParams = getPDFSearchParams(searchParams); const customization = 'customization' in baseContext ? baseContext.customization : defaultCustomization();