Skip to content

Fix EventTracker silently dormant in real browsers#1327

Merged
BilalG1 merged 6 commits intodevfrom
event-tracker-fix
Apr 13, 2026
Merged

Fix EventTracker silently dormant in real browsers#1327
BilalG1 merged 6 commits intodevfrom
event-tracker-fix

Conversation

@BilalG1
Copy link
Copy Markdown
Collaborator

@BilalG1 BilalG1 commented Apr 10, 2026

window.screen and window.history are accessor properties on Window.prototype, so Object.getOwnPropertyDescriptor(window, X)?.value returned undefined in real browsers, causing start() to short-circuit and never capture or send any $page-view / $click events. Read the globals directly instead; the jsdom-based regression test pins the accessor-descriptor shape so this can't silently come back.

Summary by CodeRabbit

  • Tests

    • Added a new test suite verifying event batching, timing, page-view and click event capture, and client-side navigation behavior using simulated timers and DOM environment.
  • Bug Fixes

    • Improved event tracker reliability by changing how browser screen and history are read, yielding more consistent detection of screen dimensions and navigation for analytics capture.

`window.screen` and `window.history` are accessor properties on
`Window.prototype`, so `Object.getOwnPropertyDescriptor(window, X)?.value`
returned undefined in real browsers, causing `start()` to short-circuit
and never capture or send any $page-view / $click events. Read the
globals directly instead; the jsdom-based regression test pins the
accessor-descriptor shape so this can't silently come back.
@vercel
Copy link
Copy Markdown

vercel Bot commented Apr 10, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
stack-auth-hosted-components Ready Ready Preview, Comment Apr 11, 2026 1:29am
stack-backend Ready Ready Preview, Comment Apr 11, 2026 1:29am
stack-dashboard Ready Ready Preview, Comment Apr 11, 2026 1:29am
stack-demo Ready Ready Preview, Comment Apr 11, 2026 1:29am
stack-docs Ready Ready Preview, Comment Apr 11, 2026 1:29am
stack-preview-backend Ready Ready Preview, Comment Apr 11, 2026 1:29am
stack-preview-dashboard Ready Ready Preview, Comment Apr 11, 2026 1:29am

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 10, 2026

📝 Walkthrough

Walkthrough

Replaced descriptor-based reads of window.screen and window.history with direct window.screen / window.history access in EventTracker; added a Vitest+JSDOM test suite exercising page-view, click, and client-side navigation tracking using fake timers and proper cleanup.

Changes

Cohort / File(s) Summary
EventTracker Implementation
packages/template/src/lib/stack-app/apps/implementations/event-tracker.ts
Replaced Object.getOwnPropertyDescriptor(window, "...")?.value checks with direct window.screen and window.history usage for preflight validation, page-view capture, history method detection, and teardown/restoration.
EventTracker Tests
packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts
Added Vitest + JSDOM tests that use fake timers and microtask flushing; include helpers to advance time past refresh intervals, parse sent analytics batch payloads, and assert $page-view / $click events and navigation-triggered page-views; ensure timers and tracker are cleaned up.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through props and window panes,
Swapped descriptors for simpler lanes.
Timers tick, events take flight,
Clicks and pages checked at night —
A rabbit's tiny testing gains.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main bug being fixed: EventTracker being dormant/non-functional in real browsers.
Description check ✅ Passed The description provides a clear explanation of the bug (accessor properties returning undefined), the fix (reading globals directly), and the regression test added. All critical information is present and complete.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch event-tracker-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Apr 10, 2026

Greptile Summary

This PR fixes EventTracker being silently dormant in all real browsers by replacing Object.getOwnPropertyDescriptor(window, "screen")?.value / …"history"?.value with direct window.screen / window.history access. Because those globals are accessor properties on Window.prototype (not own data properties of the window object), the descriptor lookup always returned undefined, causing start() to short-circuit before ever attaching any listeners. The fix is applied consistently across all four call sites (start, _capturePageView, _setupPageViewCapture, _teardown) and is covered by a new jsdom regression test that pins the accessor-descriptor shape.

Confidence Score: 5/5

Safe to merge — targeted, well-tested fix for a clear root cause with no functional regressions.

All four changed lines apply the identical, correct substitution. The new test suite explicitly asserts the accessor-descriptor shape in jsdom and end-to-end verifies that events are captured and sent after start(). No new logic is introduced; no existing paths are altered. All remaining observations are P2 or lower.

No files require special attention.

Important Files Changed

Filename Overview
packages/template/src/lib/stack-app/apps/implementations/event-tracker.ts Replaces Object.getOwnPropertyDescriptor(window, X)?.value with direct window.screen / window.history access in four places, fixing silent no-op in real browsers where these are accessor (not data) descriptors on Window.prototype.
packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts New regression test suite (jsdom env) that asserts screen/history are exposed as accessor descriptors (.get, no .value) and verifies that $page-view + $click events are captured and batched after the fix.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["EventTracker.start()"] --> B{"isBrowserLike()?"}
    B -- No --> Z["return (no-op)"]
    B -- Yes --> C{"addEventListener present?\nremoveEventListener present?\nhasScreenDimensions(window.screen)?"}
    C -- "Any false\n(was always false in real browsers\nbefore this fix)" --> Z
    C -- All true --> D["_started = true"]
    D --> E["_setupPageViewCapture()"]
    E --> F["_capturePageView('initial')\nuses window.screen directly"]
    E --> G["historyObject = window.history\nmonkey-patch pushState / replaceState"]
    D --> H["_setupClickCapture()"]
    D --> I["_setupPageHideListeners()"]
    D --> J["setInterval → _tick() every 10s"]
    J --> K["fetch access token async"]
    J --> L{"has token & events?"}
    L -- Yes --> M["_flush() → sendBatch()"]
    L -- No --> J
Loading

Reviews (1): Last reviewed commit: "fix" | Re-trigger Greptile

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts (2)

53-57: Avoid silently skipping the click dispatch in the test flow.

Line 53 uses optional chaining and can no-op if the button lookup fails. Make this an explicit error so the test fails at the real setup fault.

♻️ Proposed fail-loud diff
-      document.querySelector("button")?.dispatchEvent(new MouseEvent("click", {
+      const button = document.querySelector("button");
+      if (button == null) {
+        throw new Error("Expected test button to exist before dispatching click.");
+      }
+      button.dispatchEvent(new MouseEvent("click", {
         bubbles: true,
         clientX: 12,
         clientY: 34,
       }));

As per coding guidelines: "Use ?? throwErr(...) instead of non-null assertions or silent fallback values; include explicit error messages" and "Fail early, fail loud; fail fast with an error instead of silently continuing".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts`
around lines 53 - 57, The test currently uses optional chaining on
document.querySelector("button") which can silently skip dispatching the click
if the button is missing; change this to explicitly fail when the button isn't
found by grabbing the element into a variable (e.g., const btn =
document.querySelector("button")), assert or throw a clear Error if btn is null
(use your throwErr helper if available), then call btn.dispatchEvent(...) so the
test fails loudly on setup errors instead of silently no-oping.

14-23: Harden payload parsing to fail fast with explicit test errors.

If no batch is sent, JSON.parse currently fails with a generic parse error. Also, each event’s event_type is assumed but not asserted before mapping. Add explicit guards so failures point to the exact contract break.

♻️ Proposed test-hardening diff
 function getSentEventTypes(sentBodies: string[]) {
   const [body] = sentBodies;
+  if (body == null) {
+    throw new Error("Expected at least one analytics batch body to be sent.");
+  }

-  const payload = JSON.parse(body);
+  const payload: unknown = JSON.parse(body);
   if (typeof payload !== "object" || payload === null || !("events" in payload) || !Array.isArray(payload.events)) {
     throw new Error("Expected analytics batch payload to include an events array.");
   }

-  return payload.events.map((event) => event.event_type);
+  return payload.events.map((event) => {
+    if (typeof event !== "object" || event === null || !("event_type" in event) || typeof event.event_type !== "string") {
+      throw new Error("Expected each analytics event to include a string event_type.");
+    }
+    return event.event_type;
+  });
 }

As per coding guidelines: "Validate all assumptions through type system (preferred), assertions, or tests; ideally two out of three" and "Fail early, fail loud; fail fast with an error instead of silently continuing".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts`
around lines 14 - 23, The helper getSentEventTypes should fail fast with clear
errors: check that sentBodies is non-empty and throw a descriptive error if no
batch was sent, wrap JSON.parse in a try/catch to rethrow a clear "invalid JSON
payload" error, assert that the parsed payload is a non-null object with an
"events" array, and validate each event in payload.events has an event_type of
type string (throwing an explicit error identifying the bad event/index) before
returning the array of event_type values; update the function getSentEventTypes
to perform these guards and throw meaningful messages when any assumption is
violated.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts`:
- Around line 53-57: The test currently uses optional chaining on
document.querySelector("button") which can silently skip dispatching the click
if the button is missing; change this to explicitly fail when the button isn't
found by grabbing the element into a variable (e.g., const btn =
document.querySelector("button")), assert or throw a clear Error if btn is null
(use your throwErr helper if available), then call btn.dispatchEvent(...) so the
test fails loudly on setup errors instead of silently no-oping.
- Around line 14-23: The helper getSentEventTypes should fail fast with clear
errors: check that sentBodies is non-empty and throw a descriptive error if no
batch was sent, wrap JSON.parse in a try/catch to rethrow a clear "invalid JSON
payload" error, assert that the parsed payload is a non-null object with an
"events" array, and validate each event in payload.events has an event_type of
type string (throwing an explicit error identifying the bad event/index) before
returning the array of event_type values; update the function getSentEventTypes
to perform these guards and throw meaningful messages when any assumption is
violated.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b0c7bf8a-ab08-46bb-8626-edb13fd6c519

📥 Commits

Reviewing files that changed from the base of the PR and between 839bee3 and d36484a.

📒 Files selected for processing (1)
  • packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts

@BilalG1 BilalG1 requested a review from N2D4 April 11, 2026 00:20
@BilalG1 BilalG1 assigned N2D4 and unassigned BilalG1 Apr 11, 2026
`payload.events` comes from `JSON.parse`, so after the narrow it's
`any[]` and the `.map` callback parameter trips noImplicitAny in the
generated SDK packages. Cast to a concrete element type before mapping.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts`:
- Around line 14-23: The helper getSentEventTypes should guard against an empty
sentBodies array and remove the unsafe type cast: first check sentBodies and
throw a clear error if no body is present, then JSON.parse the body into payload
and validate payload.events is an array; iterate payload.events and at runtime
verify each item has a string event_type (throwing a descriptive error if not)
before collecting event_type values—update references to sentBodies, payload,
events, and event_type accordingly and eliminate the `as` cast.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7b43b4c0-1b31-4808-99e1-5b2567f1dc48

📥 Commits

Reviewing files that changed from the base of the PR and between d36484a and fc923a9.

📒 Files selected for processing (1)
  • packages/template/src/lib/stack-app/apps/implementations/event-tracker.test.ts

@github-actions github-actions Bot assigned BilalG1 and unassigned N2D4 Apr 12, 2026
@BilalG1 BilalG1 merged commit 3102787 into dev Apr 13, 2026
34 checks passed
@BilalG1 BilalG1 deleted the event-tracker-fix branch April 13, 2026 16: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