diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 6daf82a5..a75827b0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -13,8 +13,6 @@ jobs: steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - with: - version: 8 - uses: volta-cli/action@v4 with: node-version: 20.12.0 diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 3affb930..78751f0d 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -2,36 +2,26 @@ name: Playwright Tests on: [pull_request] jobs: - wait-for-vercel: - name: Wait for vercel - runs-on: ubuntu-latest - steps: - - name: Wait for Vercel preview deployment to be ready - uses: patrickedqvist/wait-for-vercel-preview@v1.3.1 - id: wait - with: - token: ${{ secrets.GITHUB_TOKEN }} - max_timeout: 120 - outputs: - preview_url: ${{ steps.wait.outputs.url }} test: timeout-minutes: 60 runs-on: ubuntu-latest - needs: [wait-for-vercel] steps: - uses: actions/checkout@v4 - uses: pnpm/action-setup@v4 - with: - version: 8 - uses: actions/setup-node@v4 with: - node-version: lts/* + node-version: 20.12.0 + cache: pnpm - name: Install dependencies run: pnpm i + - name: Build site + run: pnpm run build + env: + NEXT_PUBLIC_SITE_URL: 'https://docs.replay.io' - name: Install Replay Chromium run: pnpx replayio install - name: Run Playwright tests run: pnpm test env: - BASE_URL: ${{ needs.wait-for-vercel.outputs.preview_url }} + BASE_URL: http://127.0.0.1:3000 REPLAY_API_KEY: ${{ secrets.REPLAY_API_KEY }} diff --git a/mdx-components.tsx b/mdx-components.tsx index 369061b6..18690ea5 100644 --- a/mdx-components.tsx +++ b/mdx-components.tsx @@ -45,7 +45,9 @@ export function useMDXComponents(components: MDXComponents): MDXComponents { const fileName = codeProps.fileName as string | undefined const lineNumbers = codeProps.lineNumbers as boolean | undefined const rawHighlight = codeProps.highlight as - string | string[] | undefined + | string + | string[] + | undefined const highlight = typeof rawHighlight === 'string' ? (JSON.parse(rawHighlight) as string[]) diff --git a/package.json b/package.json index ac3c9a46..cae4a4aa 100644 --- a/package.json +++ b/package.json @@ -79,5 +79,6 @@ "volta": { "node": "20.12.0", "bun": "1.1.1" - } + }, + "packageManager": "pnpm@9.15.4" } diff --git a/playwright.config.ts b/playwright.config.ts index 7eab4970..fa85eab1 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -3,10 +3,22 @@ import { devices as replayDevices, replayReporter } from '@replayio/playwright' import dotenv from 'dotenv' dotenv.config() +const baseURL = + process.env.BASE_URL || + (process.env.CI ? 'http://127.0.0.1:3000' : 'http://localhost:3000') + const config: PlaywrightTestConfig = { use: { - baseURL: process.env.CI ? process.env.BASE_URL : 'http://localhost:3000', + baseURL, }, + webServer: process.env.CI + ? { + command: 'pnpm start', + url: baseURL, + reuseExistingServer: false, + timeout: 120_000, + } + : undefined, reporter: [ replayReporter({ apiKey: process.env.REPLAY_API_KEY, diff --git a/public/agent/basics.md b/public/agent/basics.md index 22440413..73e2ae28 100644 --- a/public/agent/basics.md +++ b/public/agent/basics.md @@ -2,44 +2,13 @@ Canonical URL: https://docs.replay.io/basics -The Basics section covers everything you need to start recording and -debugging with Replay. +The Basics section currently highlights **Replay QA** — an autonomous app +testing tool built on Replay's time-travel debugging engine. -## Getting Started +## Replay QA -- [How to record](https://docs.replay.io/basics/getting-started/record-your-app) -- [Record your Playwright tests](https://docs.replay.io/basics/getting-started/record-your-playwright-tests) +- [Overview](https://docs.replay.io/basics/replay-qa/overview) +- [CI integration with FRPC](https://docs.replay.io/basics/replay-qa/frpc-ci) +- [Publishing with source maps](https://docs.replay.io/basics/replay-qa/source-maps) -## Time Travel - -- [Why time travel?](https://docs.replay.io/basics/time-travel/why-time-travel) -- [How does time travel work?](https://docs.replay.io/basics/time-travel/how-does-time-travel-work) - -## Replay DevTools - -- [Overview](https://docs.replay.io/basics/replay-devtools/overview) -- Time Travel DevTools -- Framework DevTools -- Browser DevTools - -## Replay Chrome Extension - -- [Getting started](https://docs.replay.io/basics/replay-chrome-extension/getting-started) - -## Replay MCP - -The Replay MCP server lets AI agents inspect the contents of recordings on -https://replay.io. - -- [Overview](https://docs.replay.io/basics/replay-mcp/overview) -- [Quickstart](https://docs.replay.io/basics/replay-mcp/quickstart) -- [Tools reference](https://docs.replay.io/basics/replay-mcp/tools-reference) -- Public MCP endpoint: https://mcp.replay.io -- Server card: https://docs.replay.io/.well-known/mcp/server-card.json - -## Test Suite Dashboard - -- [Overview](https://docs.replay.io/basics/test-suites/overview) -- [Recent runs](https://docs.replay.io/basics/test-suites/recent-runs) -- [Top failing and flaky tests](https://docs.replay.io/basics/test-suites/top-failing-and-flaky-tests) -- [PR comments](https://docs.replay.io/basics/test-suites/pr-comments) +Launch Replay QA: https://qa.replay.io diff --git a/public/agent/index.md b/public/agent/index.md index 2b80facd..d2a5fb8a 100644 --- a/public/agent/index.md +++ b/public/agent/index.md @@ -2,25 +2,18 @@ Canonical URL: https://docs.replay.io/ -Replay is the first deterministic browser. Once a bug or flaky test is captured, -anyone can inspect it with browser DevTools without having to reproduce it -locally. - -The docs homepage prioritizes **CI/CD** (Playwright in GitHub Actions and other -providers), **Replay MCP** (agents inspecting recordings), and the **Chrome -extension**, in that order. Time-travel **DevTools** remain the inspection layer -for any recording—they are not the primary narrative. - -This documentation site is the canonical reference for using Replay. The -top-level sections are: - -- [Basics](https://docs.replay.io/basics) — getting started, recording your - app, recording Playwright test suites, time-travel debugging, Replay - DevTools, and the Test Suite Dashboard. -- [Learn](https://docs.replay.io/learn) — guided tutorials, framework-specific - examples, and conceptual deep-dives on how Replay works. -- [Reference](https://docs.replay.io/reference) — exhaustive reference for the - Replay APIs, the Replay Protocol, the Replay CLI, and supported test runners. +Replay QA is an autonomous app testing tool built on Replay's time-travel +debugging engine. Point it at a web app and it explores the application, +discovers user journeys, writes Playwright tests, executes them while capturing +full runtime recordings, and delivers root cause analysis and suggested fixes +for every bug it finds. + +This documentation site is the canonical reference for Replay QA. Start here: + +- [Replay QA overview](https://docs.replay.io/basics/replay-qa/overview) +- [CI integration with FRPC](https://docs.replay.io/basics/replay-qa/frpc-ci) +- [Publishing with source maps](https://docs.replay.io/basics/replay-qa/source-maps) +- [Basics section index](https://docs.replay.io/basics) ## Machine-readable surfaces @@ -30,7 +23,7 @@ top-level sections are: Playwright, MCP) - `/.well-known/openid-configuration` — OAuth/OIDC discovery for the Replay authentication tenant -- `/sitemap.xml` — full URL inventory +- `/sitemap.xml` — URL inventory for public Replay QA documentation - `/robots.txt` — crawl + AI-bot policy with `Content-Signal` ## Content usage @@ -42,6 +35,7 @@ not be ingested into model training corpora. ## Contact - Product site: https://www.replay.io +- Replay QA: https://qa.replay.io - App: https://app.replay.io - Discord: https://replay.io/discord - GitHub: https://github.com/replayio diff --git a/public/agent/learn.md b/public/agent/learn.md deleted file mode 100644 index f7da94db..00000000 --- a/public/agent/learn.md +++ /dev/null @@ -1,27 +0,0 @@ -# Replay Docs — Learn - -Canonical URL: https://docs.replay.io/learn - -The Learn section contains guided tutorials and framework-specific examples -for working with Replay. - -## Replay Course - -- [Replay Course](https://docs.replay.io/learn/replay-course) - -## Examples - -Framework-specific recipes for adding Replay to a real codebase: - -- [Next.js](https://docs.replay.io/learn/examples/nextjs) -- [Vite](https://docs.replay.io/learn/examples/vite) -- [Create React App](https://docs.replay.io/learn/examples/cra) -- [Storybook](https://docs.replay.io/learn/examples/storybook) - -## Comparisons - -How Replay relates to existing browser/debugger tooling: - -- [Chrome Recorder](https://docs.replay.io/learn/comparisons/chrome) -- [Loom](https://docs.replay.io/learn/comparisons/loom) -- [Browser DevTools](https://docs.replay.io/learn/comparisons/devtools) diff --git a/public/agent/reference.md b/public/agent/reference.md deleted file mode 100644 index cd5b98e0..00000000 --- a/public/agent/reference.md +++ /dev/null @@ -1,37 +0,0 @@ -# Replay Docs — Reference - -Canonical URL: https://docs.replay.io/reference - -The Reference section is the complete, exhaustive reference for the Replay -platform. - -## Test Runners - -Replay's first-party test-runner integrations: - -- [Overview](https://docs.replay.io/reference/test-runners/overview) -- [Playwright](https://docs.replay.io/basics/getting-started/record-your-playwright-tests) -- [GitHub Actions setup (Playwright)](https://docs.replay.io/reference/test-runners/playwright/github-actions) - -## Integrations / APIs - -- [Replay Protocol](https://docs.replay.io/reference/integrations/replay-apis/replay-protocol) -- Protocol docs: https://static.replay.io/protocol/tot/ -- GraphQL API: https://api.replay.io/v1/graphql - -## Replay CLI - -The `replayio` CLI installs the Replay browser, records sessions, and uploads -them. See https://github.com/replayio/replay-cli/tree/main/packages/replayio. - -## Replay MCP server - -- Public endpoint: https://mcp.replay.io -- Server card: https://docs.replay.io/.well-known/mcp/server-card.json - -## Authentication - -Replay uses OAuth 2.0 / OIDC for API access. Discovery metadata: - -- OIDC: https://docs.replay.io/.well-known/openid-configuration -- OAuth Protected Resource: https://docs.replay.io/.well-known/oauth-protected-resource diff --git a/src/app/basics/replay-qa/source-maps/page.mdx b/src/app/basics/replay-qa/source-maps/page.mdx index 0e69bbdd..0f56a0ff 100644 --- a/src/app/basics/replay-qa/source-maps/page.mdx +++ b/src/app/basics/replay-qa/source-maps/page.mdx @@ -66,22 +66,6 @@ module.exports = { Deploy with these settings and Replay QA will pick up the source maps on its own. -### Private source maps — for apps that don't expose source to end users - -If you want source maps available to Replay but don't want to expose them publicly (a common production concern), upload them to Replay ahead of time: - -1. Configure your build to emit source maps (same settings above) -2. Upload them to Replay using the CLI after the build: - -```sh -npm run build -npx replayio upload-source-maps --group -``` - -3. Strip or omit the `sourceMappingURL` comment from your build output so the maps aren't publicly reachable - -Set `REPLAY_API_KEY` in your environment so the CLI can authenticate. API keys are available in [Team Settings](/reference/ci-workflows/generate-api-key). For Webpack, the [`@replayio/sourcemap-upload-webpack-plugin`](https://www.npmjs.com/package/@replayio/sourcemap-upload-webpack-plugin) handles upload automatically as part of the build. See the [Uploading source maps reference](/reference/replay-cli/source-maps) for full details. - ### React 18.3 — extra step required React 19 ships unminified production artifacts, so standard source maps work. React 18 shipped pre-minified builds with no source maps, which means Replay cannot read the internal function names it needs for React analysis. @@ -100,7 +84,7 @@ Check which bundler or framework is in use (Next.js, Vite, Webpack, etc.) and ap - Vite: set `build.sourcemap: true` in vite.config.js - Webpack: set `devtool: 'source-map'` in webpack.config.js -Replay QA discovers publicly served source maps automatically — no upload step is needed unless we specifically want to keep source maps private. +Replay QA discovers publicly served source maps automatically — no upload step is needed. If the project uses React 18.3 (not React 19), also install @acemarke/react-prod-sourcemaps and configure it per the package README, so Replay can instrument React internals for component analysis. diff --git a/src/app/page.tsx b/src/app/page.tsx index 97fa4c5b..30db4e5a 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -3,77 +3,51 @@ import MiniCard from '@/components/MiniCard' import { Hero } from '@/components/Hero' import Link from 'next/link' -/** Primary paths: CI/CD (Playwright, GitHub, dashboards), then MCP, then Chrome. */ const cards: CardAttributes[] = [ { - icon: 'playwrightsimple', - title: 'Playwright in CI', - content: - 'Run tests in Replay Browser and upload recordings from GitHub Actions—or wire up other CI providers.', - href: '/basics/getting-started/record-your-playwright-tests/github-actions', - }, - { - icon: 'analytics', - title: 'Test suite dashboard', - content: - 'Recent runs, top failing and flaky tests, and PR comments—visibility across your pipeline.', - href: '/basics/test-suites/overview', - }, - { - icon: 'link', - title: 'Replay MCP', + icon: 'replay', + title: 'Replay QA overview', content: - 'Connect recordings to Cursor, Claude Code, and other MCP clients so agents can inspect real runtime.', - href: '/basics/replay-mcp/overview', + 'Autonomous app testing that explores your web app, writes Playwright tests, captures recordings, and delivers root cause analysis.', + href: '/basics/replay-qa/overview', }, { - icon: 'replay', - title: 'Replay Chrome extension', + icon: 'github', + title: 'CI integration with FRPC', content: - 'One-click recording from the browser when you are not driving everything from CI.', - href: '/basics/replay-chrome-extension/getting-started', + 'Run Replay QA against every change in your pipeline with the FRPC CI integration.', + href: '/basics/replay-qa/frpc-ci', }, { icon: 'uploadicon', - title: 'Other CI providers', + title: 'Publishing with source maps', content: - 'Not on GitHub Actions? Adapt the same install, env, and upload flow to your stack.', - href: '/basics/getting-started/record-your-playwright-tests/other-ci-providers', + 'Publish your app with source maps so Replay QA traces failures back to your original source code.', + href: '/basics/replay-qa/source-maps', }, ] const miniCards: CardAttributes[] = [ { - icon: 'playwrightsimple', - title: 'Playwright guide', - content: 'Full setup: Replay browser, reporter, debugging, and FAQs.', - href: '/basics/getting-started/record-your-playwright-tests', - }, - { - icon: 'pullrequest', - title: 'PR comments', - content: 'Root cause and context on the pull request when tests fail.', - href: '/basics/test-suites/pr-comments', - }, - { - icon: 'analytics', - title: 'Top failing & flaky tests', + icon: 'replay', + title: 'Launch Replay QA', content: - 'Prioritize what breaks most often so agents and humans fix the right things.', - href: '/basics/test-suites/top-failing-and-flaky-tests', + 'Give Replay QA a URL and let it autonomously navigate, test, and report bugs.', + href: '/basics/replay-qa/overview', }, { - icon: 'link', - title: 'MCP quickstart', - content: 'Wire the server into your editor and start querying recordings.', - href: '/basics/replay-mcp/quickstart', + icon: 'github', + title: 'Connect a GitHub repo', + content: + 'Install the GitHub App and test every change as it lands—no CI configuration required.', + href: '/basics/replay-qa/overview', }, { - icon: 'inspect', - title: 'Replay DevTools overview', + icon: 'uploadicon', + title: 'Source maps guide', content: - 'Time-travel panels for console, network, sources—still the foundation under CI and MCP.', - href: '/basics/replay-devtools/overview', + 'Get root cause analyses that reference the right files and line numbers.', + href: '/basics/replay-qa/source-maps', }, ] @@ -85,7 +59,7 @@ export default function Page() { >

- Tool setup guides + Replay QA guides

{cards.map(({ href, content, icon, title }) => ( @@ -98,11 +72,11 @@ export default function Page() { /> ))}
-

Learn the basics

+

Get started

{miniCards.map(({ href, content, icon, title }) => (

- Curious how time travel fits in?{' '} + Ready to try Replay QA?{' '} - Why time travel? + Launch Replay QA

diff --git a/src/app/robots.txt/route.ts b/src/app/robots.txt/route.ts index a8d9bd76..40df3121 100644 --- a/src/app/robots.txt/route.ts +++ b/src/app/robots.txt/route.ts @@ -60,6 +60,10 @@ export function GET() { // Default rules apply to every UA that doesn't match a more specific block. lines.push('User-agent: *') lines.push('Allow: /') + lines.push('Allow: /basics/replay-qa/') + lines.push('Disallow: /basics/') + lines.push('Disallow: /learn/') + lines.push('Disallow: /reference/') lines.push('Disallow: /api/') lines.push('Disallow: /_next/') lines.push('Disallow: /agent/') // markdown mirrors are reached via Accept-negotiation @@ -69,6 +73,10 @@ export function GET() { for (const bot of allowedBots) { lines.push(`User-agent: ${bot}`) lines.push('Allow: /') + lines.push('Allow: /basics/replay-qa/') + lines.push('Disallow: /basics/') + lines.push('Disallow: /learn/') + lines.push('Disallow: /reference/') lines.push('Disallow: /api/') lines.push('Disallow: /_next/') lines.push('') diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts index 9b3cfaa2..74c026fa 100644 --- a/src/app/sitemap.ts +++ b/src/app/sitemap.ts @@ -2,6 +2,7 @@ import type { MetadataRoute } from 'next' import { flatNavigation, NavigationItem } from '@/lib/navigation' import { getSiteOrigin } from '@/lib/agentReadiness' +import { isVisibleNavItem } from '@/lib/visibility' /** * /sitemap.xml @@ -16,6 +17,7 @@ export default function sitemap(): MetadataRoute.Sitemap { const urls: MetadataRoute.Sitemap = [] function visit(item: NavigationItem) { + if (!isVisibleNavItem(item)) return if (item.href && !seen.has(item.href)) { seen.add(item.href) urls.push({ diff --git a/src/components/Card.tsx b/src/components/Card.tsx index 59270781..fb85c4d9 100644 --- a/src/components/Card.tsx +++ b/src/components/Card.tsx @@ -11,7 +11,7 @@ export interface CardAttributes { /** Resource card — matches Replay app dashboard (icon tile + title + muted description). */ export default function Card({ title, content, icon, href }: CardAttributes) { return ( - +
)}

- Replay documentation + Replay QA documentation

- CI & agents first + Autonomous app testing

- Record Playwright runs in CI, close the loop on failures from - the PR, and give your coding agent full runtime context through - Replay MCP. Everything here is built on the same deterministic, - time-travel recordings that power Replay DevTools—we are - doubling down on agentic workflows and less manual digging. + Replay QA explores your web app, discovers user journeys, writes + Playwright tests, and delivers root cause analysis for every bug + it finds — all powered by Replay's time-travel debugging + engine.

- Quickstart: record your app in the Replay browser + CI integration with FRPC

diff --git a/src/components/HiddenPagesToggle.tsx b/src/components/HiddenPagesToggle.tsx new file mode 100644 index 00000000..20691801 --- /dev/null +++ b/src/components/HiddenPagesToggle.tsx @@ -0,0 +1,129 @@ +'use client' + +import { useSyncExternalStore } from 'react' +import clsx from 'clsx' + +const STORAGE_KEY = 'replay-docs:showHidden' + +function readShowHidden(): boolean { + if (typeof window === 'undefined') return false + return window.localStorage.getItem(STORAGE_KEY) === '1' +} + +function subscribe(onStoreChange: () => void) { + const handler = (event: StorageEvent) => { + if (event.key === STORAGE_KEY || event.key === null) { + onStoreChange() + } + } + window.addEventListener('storage', handler) + window.addEventListener('replay-docs:showHidden', onStoreChange) + return () => { + window.removeEventListener('storage', handler) + window.removeEventListener('replay-docs:showHidden', onStoreChange) + } +} + +export function useShowHidden(): [boolean, (next: boolean) => void] { + const showHidden = useSyncExternalStore( + subscribe, + readShowHidden, + () => false, + ) + + const setShowHidden = (next: boolean) => { + if (typeof window === 'undefined') return + if (next) { + window.localStorage.setItem(STORAGE_KEY, '1') + } else { + window.localStorage.removeItem(STORAGE_KEY) + } + window.dispatchEvent(new Event('replay-docs:showHidden')) + } + + return [showHidden, setShowHidden] +} + +function EyeIcon({ className }: { className?: string }) { + return ( + + ) +} + +function EyeOffIcon({ className }: { className?: string }) { + return ( + + ) +} + +export function HiddenPagesToggle({ className }: { className?: string }) { + const [showHidden, setShowHidden] = useShowHidden() + + return ( +
+ +
+ ) +} diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index 5548b199..7c7423e4 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -6,7 +6,9 @@ import { usePathname } from 'next/navigation' import clsx from 'clsx' import { Logo, Logomark } from '@/components/Logo' +import { useShowHidden } from '@/components/HiddenPagesToggle' import { MobileNavigation } from '@/components/MobileNavigation' +import { HiddenPagesToggle } from '@/components/HiddenPagesToggle' import { Navigation } from '@/components/Navigation' import { Search } from '@/components/Search' import { ThemeSelector } from '@/components/ThemeSelector' @@ -67,7 +69,7 @@ function Header() { @@ -115,6 +117,8 @@ function SubheaderNavigationLink({ } function SubheaderNavigation() { + const [showHidden] = useShowHidden() + return (
@@ -123,18 +127,15 @@ function SubheaderNavigation() { name={'Basics'} isDefault={true} baseHref={'/basics'} - href={'/basics/time-travel/why-time-travel'} - /> - {/* */} - + {showHidden && ( + + )}
@@ -198,9 +199,17 @@ export function Layout({ children }: { children: React.ReactNode }) {
{/* Sidebar — flush against the left edge, wide enough to keep nav items on a single line without wrapping. */} -