From 45d32b50846ad2013c2c3236a8f97c6efb019d04 Mon Sep 17 00:00:00 2001 From: Taran Vohra Date: Thu, 12 Mar 2026 20:27:59 +0530 Subject: [PATCH] Site Preview URLs (#4096) --- .github/workflows/deploy-preview.yaml | 5 - bun.lock | 4 +- packages/gitbook/e2e/internal.spec.ts | 135 +++++++++++++++++- packages/gitbook/next.config.mjs | 1 + .../components/TableOfContents/Trademark.tsx | 1 + packages/gitbook/src/lib/context.ts | 1 + packages/gitbook/src/lib/env/globals.ts | 6 + packages/gitbook/src/lib/preview.ts | 63 +++++++- packages/gitbook/src/middleware.ts | 70 ++++----- packages/gitbook/tests/utils.ts | 8 ++ 10 files changed, 253 insertions(+), 41 deletions(-) diff --git a/.github/workflows/deploy-preview.yaml b/.github/workflows/deploy-preview.yaml index 3d7db35ce..5745f6b2d 100644 --- a/.github/workflows/deploy-preview.yaml +++ b/.github/workflows/deploy-preview.yaml @@ -136,7 +136,6 @@ jobs: env: BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }} SITE_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/ - SITE_PREVIEW_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/preview/ ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }} ARGOS_BUILD_NAME: v2-vercel visual-testing-v2-cloudflare: @@ -161,7 +160,6 @@ jobs: env: BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }} SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/ - SITE_PREVIEW_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/preview/ ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }} ARGOS_BUILD_NAME: v2-cloudflare visual-testing-customers-v2: @@ -186,7 +184,6 @@ jobs: env: BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }} SITE_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/ - SITE_PREVIEW_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/preview/ ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }} ARGOS_BUILD_NAME: customers-v2-vercel visual-testing-customers-v2-cloudflare: @@ -211,7 +208,6 @@ jobs: env: BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }} SITE_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/url/ - SITE_PREVIEW_BASE_URL: ${{ needs.deploy-v2-cloudflare.outputs.deployment-url }}/preview/ ARGOS_TOKEN: ${{ secrets.ARGOS_TOKEN }} ARGOS_BUILD_NAME: customers-v2-cloudflare browserless-testing-v2-vercel: @@ -235,7 +231,6 @@ jobs: env: BASE_URL: ${{needs.deploy-v2-vercel.outputs.deployment-url}} SITE_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/url/ - SITE_PREVIEW_BASE_URL: ${{ needs.deploy-v2-vercel.outputs.deployment-url }}/preview/ # browserless-testing-v2-cloudflare: # runs-on: ubuntu-latest # name: Browserless Testing v2 (Cloudflare) diff --git a/bun.lock b/bun.lock index ffbfb180d..9817d3b39 100644 --- a/bun.lock +++ b/bun.lock @@ -7,8 +7,8 @@ "devDependencies": { "@biomejs/biome": "^1.9.4", "@changesets/cli": "^2.30.0", - "turbo": "^2.8.13", - "vercel": "^50.26.1", + "turbo": "^2.8.16", + "vercel": "^50.31.1", }, }, "packages/browser-types": { diff --git a/packages/gitbook/e2e/internal.spec.ts b/packages/gitbook/e2e/internal.spec.ts index e68b6ba2d..546ed3a5d 100644 --- a/packages/gitbook/e2e/internal.spec.ts +++ b/packages/gitbook/e2e/internal.spec.ts @@ -16,7 +16,7 @@ import jwt from 'jsonwebtoken'; import { VISITOR_TOKEN_COOKIE } from '@/lib/visitors'; -import { getSiteAPIToken } from '../tests/utils'; +import { getGitBookPreviewURL, getSiteAPIToken } from '../tests/utils'; import { type Test, type TestsCase, @@ -754,6 +754,139 @@ const testCases: TestsCase[] = [ } }, }, + { + name: 'With customization cookie', + url: async () => { + const data = await getSiteAPIToken( + 'https://gitbook.gitbook.io/test-gitbook-open/' + ); + + const searchParams = new URLSearchParams(); + searchParams.set('token', data.apiToken); + + return `url/preview/${data.site}/?${searchParams.toString()}`; + }, + screenshot: false, + run: async (page) => { + await expect(page.locator('[data-testid="table-of-contents"]')).toBeVisible(); + // Trademark exists by default + await expect(page.getByTestId('gb-trademark')).toHaveCount(1); + + // Go to another page with the customization query to disable the trademark + const pageBlocks = new URL(page.url()); + pageBlocks.pathname = `${pageBlocks.pathname.replace(/\/$/, '')}/blocks`; + pageBlocks.search = getCustomizationURL({ + trademark: { + enabled: false, + }, + }).slice(1); + await page.goto(pageBlocks.toString()); + // No trademark because customization is disabled + await expect(page.getByTestId('gb-trademark')).toHaveCount(0); + await expect( + page.getByRole('heading', { level: 1, name: 'Blocks' }) + ).toBeVisible(); + + const pageBlocksCode = new URL(page.url()); + pageBlocksCode.pathname = `${pageBlocksCode.pathname.replace(/\/$/, '')}/code`; + pageBlocksCode.search = ''; + await page.goto(pageBlocksCode.toString()); + // The trademark should not be visible because the cookie is still set, + await expect(page.getByTestId('gb-trademark')).toHaveCount(0); + await expect( + page.getByRole('heading', { level: 1, name: 'Code' }) + ).toBeVisible(); + }, + }, + ], + }, + { + name: 'Site Previews', + skip: process.env.ARGOS_BUILD_NAME !== 'v2-vercel', + tests: [ + { + name: 'Main content', + url: async () => { + const data = await getSiteAPIToken( + 'https://gitbook.gitbook.io/test-gitbook-open/' + ); + + const searchParams = new URLSearchParams(); + searchParams.set('token', data.apiToken); + + return `url/${getGitBookPreviewURL(`${data.site}/?${searchParams.toString()}`)}`; + }, + screenshot: false, + run: async (page) => { + await expect(page.locator('[data-testid="table-of-contents"]')).toBeVisible(); + }, + }, + { + name: 'With sections', + url: async () => { + const data = await getSiteAPIToken('https://gitbook.com/docs'); + + const searchParams = new URLSearchParams(); + searchParams.set('token', data.apiToken); + + return `url/${getGitBookPreviewURL(`${data.site}/?${searchParams.toString()}`)}`; + }, + screenshot: false, + run: async (page) => { + const sectionTabs = page.getByLabel('Sections'); + await expect(sectionTabs).toBeVisible(); + + const sectionTabLinks = sectionTabs.getByRole('link'); + for (const link of await sectionTabLinks.all()) { + const href = await link.getAttribute('href'); + expect(href?.includes('/preview/site_p4Xo4')).toBeTruthy(); + } + }, + }, + { + name: 'With customization cookie', + url: async () => { + const data = await getSiteAPIToken( + 'https://gitbook.gitbook.io/test-gitbook-open/' + ); + + const searchParams = new URLSearchParams(); + searchParams.set('token', data.apiToken); + + return `url/${getGitBookPreviewURL(`${data.site}/?${searchParams.toString()}`)}`; + }, + screenshot: false, + run: async (page) => { + await expect(page.locator('[data-testid="table-of-contents"]')).toBeVisible(); + // Trademark exists by default + await expect(page.getByTestId('gb-trademark')).toHaveCount(1); + + // Go to another page with the customization query to disable the trademark + const pageBlocks = new URL(page.url()); + pageBlocks.pathname = `${pageBlocks.pathname.replace(/\/$/, '')}/blocks`; + pageBlocks.search = getCustomizationURL({ + trademark: { + enabled: false, + }, + }).slice(1); + await page.goto(pageBlocks.toString()); + // No trademark because customization is disabled + await expect(page.getByTestId('gb-trademark')).toHaveCount(0); + await expect( + page.getByRole('heading', { level: 1, name: 'Blocks' }) + ).toBeVisible(); + + const pageBlocksCode = new URL(page.url()); + pageBlocksCode.pathname = `${pageBlocksCode.pathname.replace(/\/$/, '')}/code`; + pageBlocksCode.search = ''; + await page.goto(pageBlocksCode.toString()); + // The trademark should not be visible because the cookie is still set, + await expect(page.getByTestId('gb-trademark')).toHaveCount(0); + await expect( + page.getByRole('heading', { level: 1, name: 'Code' }) + ).toBeVisible(); + }, + }, ], }, { diff --git a/packages/gitbook/next.config.mjs b/packages/gitbook/next.config.mjs index e25a57fbf..bd9e2ac6c 100644 --- a/packages/gitbook/next.config.mjs +++ b/packages/gitbook/next.config.mjs @@ -27,6 +27,7 @@ const nextConfig = { GITBOOK_API_URL: process.env.GITBOOK_API_URL, GITBOOK_APP_URL: process.env.GITBOOK_APP_URL, GITBOOK_OAUTH_SERVER_URL: process.env.GITBOOK_OAUTH_SERVER_URL, + GITBOOK_PREVIEW_BASE_URL: process.env.GITBOOK_PREVIEW_BASE_URL, GITBOOK_INTEGRATIONS_HOST: process.env.GITBOOK_INTEGRATIONS_HOST, GITBOOK_IMAGE_RESIZE_URL: process.env.GITBOOK_IMAGE_RESIZE_URL, GITBOOK_ICONS_URL: process.env.GITBOOK_ICONS_URL, diff --git a/packages/gitbook/src/components/TableOfContents/Trademark.tsx b/packages/gitbook/src/components/TableOfContents/Trademark.tsx index 5eb048344..ba8ccf534 100644 --- a/packages/gitbook/src/components/TableOfContents/Trademark.tsx +++ b/packages/gitbook/src/components/TableOfContents/Trademark.tsx @@ -25,6 +25,7 @@ export function Trademark(props: { return (