Skip to content

test(session-replay-browser): add TRC e2e harness (local + published modes)#1853

Open
bravecod wants to merge 2 commits into
mainfrom
trc-e2e-harness
Open

test(session-replay-browser): add TRC e2e harness (local + published modes)#1853
bravecod wants to merge 2 commits into
mainfrom
trc-e2e-harness

Conversation

@bravecod

@bravecod bravecod commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

What

A React Targeted Recording (TRC) e2e harness for reproducing and verifying URL-based Session Replay targeting end to end against real remote config. Lives under test-server/session-replay-browser/trc-e2e/ with its own vite config, so its @amplitude/* alias strategy is isolated from the other test pages.

Two build modes (SR_MODE env var)

Command Mode Resolves @amplitude/* from
pnpm dev:trc local (default) workspace builds in packages/* (paired with the watch task)
pnpm dev:trc:npm published the released packages pinned under the *-srnpm aliases in root package.json

In published mode the two app-facing imports (analytics-browser, plugin-session-replay-browser) redirect to the *-srnpm installs, and the plugin's transitive deps (session-replay-browser, targeting, analytics-core, ...) resolve from that published package's own node_modules — i.e. a true published-artifact test, not a workspace mix. The pinned versions are editable in root package.json.

Config & observability

  • In-page user-editable config: apiKey, serverZone, logLevel, and URL-change polling (enable + interval).
  • A mode badge shows whether local or published is live.
  • Intercepts and displays the real /v1/capture diagnostics POSTs (counters + events with srId / variant / trigger), so you can watch TRC evaluation without DataDog.
  • Routes include /settings/price-plan/details for the common "URL contains" rule, plus pushState/replaceState/back-forward controls to exercise SPA navigation re-eval.

Why

We had no committed, repeatable way to test TRC locally — the prior harness lived only on a debug branch / a throwaway folder. This makes both "does my workspace change behave?" (local) and "does the published package behave?" (npm) reproducible from one place.

Testing

  • Both modes vite build clean.
  • Published-mode bundle confirmed to carry the page-in-context targeting fix (@amplitude/targeting@0.3.0-…), local-mode bundles the workspace builds.

Notes / blast radius

  • Shared test-server vite config is untouched (dedicated config avoids breaking other pages' @amplitude resolution).
  • Adds dev-only root devDeps: react/react-dom/react-router-dom, @vitejs/plugin-react, and the two *-srnpm npm: aliases. lint-staged only touches *.ts, so the new .jsx/.mjs aren't linted.

🤖 Generated with Claude Code


Note

Low Risk
Changes are limited to a dev/test harness and root devDependencies/scripts; production packages and the shared test-server Vite config are not modified.

Overview
Adds a Targeted Recording (TRC) e2e harness under test-server/session-replay-browser/trc-e2e/ with its own Vite + React SPA so Session Replay URL targeting can be exercised against real remote config without touching the shared test-server Vite setup.

Root package.json gains dev:trc (watch + local workspace @amplitude/* aliases) and dev:trc:npm (SR_MODE=npm, bundles pinned *-srnpm published packages). Dev-only deps include React, @vitejs/plugin-react, workspace SR packages, and the npm alias pins.

The harness lets you configure apiKey/serverZone/logLevel and URL-change polling, shows local vs npm mode, intercepts real /v1/capture diagnostics POSTs, and provides SPA routes/navigation controls (including /settings/price-plan/details) to re-test targeting on navigation.

Reviewed by Cursor Bugbot for commit 4a309d1. Bugbot is set up for automated code reviews on this repo. Configure here.

…ished modes

A React harness for reproducing/verifying URL Targeted Recording (TRC) end to
end against real remote config. Self-contained under test-server with its own
vite config so its @amplitude alias strategy doesn't affect the other test pages.

Two build modes via the SR_MODE env var:
- pnpm dev:trc      -> local: bundles the workspace builds (packages/*).
- pnpm dev:trc:npm  -> published: bundles the released packages pinned under the
  *-srnpm aliases in root package.json, so the plugin's transitive deps
  (session-replay-browser, targeting, ...) come from the published artifact.

User-editable config in-page: apiKey, serverZone, logLevel, and URL-change
polling (enable + interval). Surfaces the live mode and the real /v1/capture
diagnostics POSTs (counters/events with srId/variant/trigger). Routes include a
price-plan path for the common URL-contains rule.

Both modes verified to build clean; published bundle confirmed to carry the
page-in-context targeting fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 2 potential issues.

Fix All in Cursor

Bugbot Autofix prepared fixes for both issues found in the latest run.

  • ✅ Fixed: Manual track uses unset global
    • The manual track button now calls the imported Amplitude module directly instead of relying on an unset window global.
  • ✅ Fixed: Fetch patch stacks on retry
    • The capture fetch interceptor is now idempotent and is restored when initialization fails so retries do not stack wrappers.

Create PR

Or push these changes by commenting:

@cursor push 3bf394f52a
Preview (3bf394f52a)
diff --git a/test-server/session-replay-browser/trc-e2e/main.jsx b/test-server/session-replay-browser/trc-e2e/main.jsx
--- a/test-server/session-replay-browser/trc-e2e/main.jsx
+++ b/test-server/session-replay-browser/trc-e2e/main.jsx
@@ -20,9 +20,15 @@
 // In plugin mode the diagnostics client is internal to the SDK, so we can't inject a spy. Instead we
 // intercept fetch and surface the real POSTs to .../v1/capture — that's the actual diagnostics
 // payload (counters/histograms/events) the SDK ships. Install before init().
+let restoreCaptureInterceptor;
+
 function installCaptureInterceptor(onCapture) {
-  const orig = window.fetch.bind(window);
-  window.fetch = async (input, init) => {
+  if (restoreCaptureInterceptor) {
+    return restoreCaptureInterceptor;
+  }
+
+  const orig = window.fetch;
+  const interceptedFetch = async (input, init) => {
     const url = typeof input === 'string' ? input : input?.url ?? '';
     if (url.includes('/v1/capture')) {
       try {
@@ -32,8 +38,17 @@
         onCapture({ url, body: undefined, at: new Date().toLocaleTimeString() });
       }
     }
-    return orig(input, init);
+    return orig.call(window, input, init);
   };
+
+  window.fetch = interceptedFetch;
+  restoreCaptureInterceptor = () => {
+    if (window.fetch === interceptedFetch) {
+      window.fetch = orig;
+    }
+    restoreCaptureInterceptor = undefined;
+  };
+  return restoreCaptureInterceptor;
 }
 
 // Routes to exercise URL targeting. Whether each records depends on YOUR project's TRC rules.
@@ -55,7 +70,7 @@
       </p>
       {id && <p>route param id = {id}</p>}
       <p>SPA navigation (no reload) fires URL-change targeting re-eval; tracked events also trigger eval.</p>
-      <button onClick={() => window.amplitude.track('manual_test_event', { from: title })}>
+      <button onClick={() => amplitude.track('manual_test_event', { from: title })}>
         amplitude.track(&apos;manual_test_event&apos;)
       </button>
     </div>
@@ -251,9 +266,10 @@
   const handleStart = async (opts) => {
     if (initedRef.current) return;
     initedRef.current = true;
+    let restoreCaptureInterceptorOnFailure;
     try {
       // Surface real /v1/capture POSTs (install before init so init-time flushes are caught).
-      installCaptureInterceptor((cap) => setCaptures((prev) => [...prev, cap]));
+      restoreCaptureInterceptorOnFailure = installCaptureInterceptor((cap) => setCaptures((prev) => [...prev, cap]));
 
       // The analytics SDK's own diagnostics default to sampleRate 0; sample them in if supported.
       // (The SR plugin creates its own Debug-gated diagnostics client regardless.)
@@ -282,6 +298,7 @@
       );
       setStarted(true);
     } catch (err) {
+      restoreCaptureInterceptorOnFailure?.();
       initedRef.current = false;
       window.srError = err.message;
       // eslint-disable-next-line no-alert

You can send follow-ups to the cloud agent here.

Reviewed by Cursor Bugbot for commit 6328106. Configure here.

Comment thread test-server/session-replay-browser/trc-e2e/main.jsx
Comment thread test-server/session-replay-browser/trc-e2e/main.jsx
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Session Replay Browser E2E Results

passed  150 passed
flaky  1 flaky

Details

stats  151 tests across 17 suites
duration  4 minutes, 17 seconds
commit  4a309d1

Flaky tests

chromium › e2e/trc-url-rule.spec.ts › TRC URL rule — happy path › starts recording after SPA navigation to a matching URL

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

size-limit report 📦

Path Size
packages/analytics-browser/lib/scripts/amplitude-min.js.gz 58.43 KB (0%)
packages/session-replay-browser/lib/scripts/session-replay-browser-min.js.gz 133.19 KB (0%)
packages/unified/lib/scripts/amplitude-min.umd.js.gz 210.94 KB (0%)
@amplitude/element-selector (gzipped esm) 2.67 KB (0%)

…track global

Address Bugbot review on the TRC e2e harness:
- track button used window.amplitude (never assigned) and threw; assign
  window.amplitude/sessionReplay for console use and call the imported
  amplitude.track directly from the button.
- installCaptureInterceptor re-wrapped window.fetch on a retried Start,
  duplicating captures; patch fetch once and route to the latest sink.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@bravecod bravecod requested a review from a team June 24, 2026 21:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants