Skip to content

Commit 0ba6c70

Browse files
committed
test(analytics): make posthog client tests order-independent
Readiness is module-level and settles once, so the capture and exception cases silently depended on an earlier case having settled the gate and failed when run in isolation. Walk the lifecycle in one case instead. Moves the disabled-analytics case to its own file, where vitest's per-file isolation supplies an unsettled gate — dropping the vi.resetModules() and dynamic import the repo's testing conventions rule out. Replaces the hand-rolled setTimeout flush with vi.waitFor.
1 parent 8419c6f commit 0ba6c70

2 files changed

Lines changed: 45 additions & 41 deletions

File tree

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/**
2+
* @vitest-environment node
3+
*/
4+
import { describe, expect, it } from 'vitest'
5+
import {
6+
captureClientEvent,
7+
captureClientException,
8+
settlePostHogClient,
9+
} from '@/lib/posthog/client'
10+
11+
/**
12+
* Lives in its own file because readiness is module-level and settles once.
13+
* Vitest isolates the module registry per file, so this gets an unsettled gate
14+
* to settle with `null` without `vi.resetModules()` and a dynamic import.
15+
*/
16+
describe('client capture when analytics is disabled', () => {
17+
it('drops events instead of sending them, without throwing', async () => {
18+
settlePostHogClient(null)
19+
20+
expect(() => captureClientEvent('login_page_viewed', {})).not.toThrow()
21+
expect(() => captureClientException(new Error('boom'))).not.toThrow()
22+
23+
// Lets the gate's continuations run; a missing catch would surface here as
24+
// an unhandled rejection and fail the test.
25+
await Promise.resolve()
26+
})
27+
})

apps/sim/lib/posthog/client.test.ts

Lines changed: 18 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
* @vitest-environment node
33
*/
44
import type { PostHog } from 'posthog-js'
5-
import { beforeEach, describe, expect, it, vi } from 'vitest'
5+
import { describe, expect, it, vi } from 'vitest'
66
import {
77
captureClientEvent,
88
captureClientException,
@@ -21,56 +21,33 @@ function createFakePostHog() {
2121
} as unknown as PostHog
2222
}
2323

24-
/** Lets the readiness promise and its continuations settle. */
25-
const flush = () => new Promise<void>((resolve) => setTimeout(resolve, 0))
24+
describe('client capture', () => {
25+
/**
26+
* Readiness is module-level and settles exactly once, so the whole lifecycle
27+
* runs as a single case. Split across separate cases, each one after the
28+
* first would silently depend on an earlier case having settled the gate, and
29+
* would fail when run in isolation or reordered.
30+
*/
31+
it('holds events until PostHog initializes, then sends them and everything after', async () => {
32+
const posthog = createFakePostHog()
2633

27-
describe('captureClientEvent', () => {
28-
const posthog = createFakePostHog()
29-
30-
beforeEach(() => {
31-
vi.clearAllMocks()
32-
})
33-
34-
it('holds an event captured before PostHog initializes, then sends it', async () => {
3534
captureClientEvent('login_page_viewed', {})
36-
await flush()
35+
await Promise.resolve()
3736

3837
expect(posthog.capture).not.toHaveBeenCalled()
3938

4039
settlePostHogClient(posthog)
41-
await flush()
40+
await vi.waitFor(() => expect(posthog.capture).toHaveBeenCalledWith('login_page_viewed', {}))
4241

43-
expect(posthog.capture).toHaveBeenCalledWith('login_page_viewed', {})
44-
})
45-
46-
it('sends events captured after initialization', async () => {
4742
captureClientEvent('signup_page_viewed', {})
48-
await flush()
49-
50-
expect(posthog.capture).toHaveBeenCalledWith('signup_page_viewed', {})
51-
})
43+
await vi.waitFor(() => expect(posthog.capture).toHaveBeenCalledWith('signup_page_viewed', {}))
5244

53-
it('reports a caught error through captureException so error tracking sees it', async () => {
5445
const error = new Error('canvas exploded')
55-
5646
captureClientException(error, { error_boundary: 'workflow_canvas' })
57-
await flush()
58-
59-
expect(posthog.captureException).toHaveBeenCalledWith(error, {
60-
error_boundary: 'workflow_canvas',
61-
})
62-
})
63-
})
64-
65-
describe('captureClientEvent when analytics is disabled', () => {
66-
it('drops events without throwing once the provider settles with null', async () => {
67-
vi.resetModules()
68-
const client = await import('@/lib/posthog/client')
69-
70-
client.captureClientEvent('login_page_viewed', {})
71-
client.captureClientException(new Error('boom'))
72-
client.settlePostHogClient(null)
73-
74-
await expect(flush()).resolves.toBeUndefined()
47+
await vi.waitFor(() =>
48+
expect(posthog.captureException).toHaveBeenCalledWith(error, {
49+
error_boundary: 'workflow_canvas',
50+
})
51+
)
7552
})
7653
})

0 commit comments

Comments
 (0)