posthog-node 4.16.0 → 4.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/lib/edge/index.cjs +3919 -0
- package/lib/edge/index.cjs.map +1 -0
- package/lib/edge/index.mjs +3893 -0
- package/lib/edge/index.mjs.map +1 -0
- package/lib/index.d.ts +921 -859
- package/lib/{index.cjs.js → node/index.cjs} +3576 -3595
- package/lib/node/index.cjs.map +1 -0
- package/lib/{index.esm.js → node/index.mjs} +3577 -3593
- package/lib/node/index.mjs.map +1 -0
- package/package.json +31 -4
- package/index.ts +0 -3
- package/lib/index.cjs.js.map +0 -1
- package/lib/index.esm.js.map +0 -1
- package/lib/posthog-core/src/eventemitter.d.ts +0 -8
- package/lib/posthog-core/src/featureFlagUtils.d.ts +0 -34
- package/lib/posthog-core/src/index.d.ts +0 -259
- package/lib/posthog-core/src/lz-string.d.ts +0 -8
- package/lib/posthog-core/src/storage-memory.d.ts +0 -6
- package/lib/posthog-core/src/types.d.ts +0 -422
- package/lib/posthog-core/src/utils.d.ts +0 -20
- package/lib/posthog-core/src/vendor/uuidv7.d.ts +0 -179
- package/lib/posthog-node/index.d.ts +0 -3
- package/lib/posthog-node/src/crypto-helpers.d.ts +0 -3
- package/lib/posthog-node/src/crypto.d.ts +0 -2
- package/lib/posthog-node/src/error-tracking.d.ts +0 -12
- package/lib/posthog-node/src/extensions/error-tracking/autocapture.d.ts +0 -3
- package/lib/posthog-node/src/extensions/error-tracking/context-lines.d.ts +0 -6
- package/lib/posthog-node/src/extensions/error-tracking/error-conversion.d.ts +0 -2
- package/lib/posthog-node/src/extensions/error-tracking/reduceable-cache.d.ts +0 -12
- package/lib/posthog-node/src/extensions/error-tracking/stack-trace.d.ts +0 -15
- package/lib/posthog-node/src/extensions/error-tracking/type-checking.d.ts +0 -7
- package/lib/posthog-node/src/extensions/error-tracking/types.d.ts +0 -57
- package/lib/posthog-node/src/extensions/express.d.ts +0 -17
- package/lib/posthog-node/src/extensions/sentry-integration.d.ts +0 -51
- package/lib/posthog-node/src/feature-flags.d.ts +0 -84
- package/lib/posthog-node/src/fetch.d.ts +0 -11
- package/lib/posthog-node/src/lazy.d.ts +0 -23
- package/lib/posthog-node/src/posthog-node.d.ts +0 -98
- package/lib/posthog-node/src/types.d.ts +0 -229
- package/lib/posthog-node/test/test-utils.d.ts +0 -18
- package/src/crypto-helpers.ts +0 -36
- package/src/crypto.ts +0 -22
- package/src/error-tracking.ts +0 -67
- package/src/extensions/error-tracking/autocapture.ts +0 -65
- package/src/extensions/error-tracking/context-lines.ts +0 -425
- package/src/extensions/error-tracking/error-conversion.ts +0 -262
- package/src/extensions/error-tracking/reduceable-cache.ts +0 -39
- package/src/extensions/error-tracking/stack-trace.ts +0 -269
- package/src/extensions/error-tracking/type-checking.ts +0 -40
- package/src/extensions/error-tracking/types.ts +0 -65
- package/src/extensions/express.ts +0 -37
- package/src/extensions/sentry-integration.ts +0 -196
- package/src/feature-flags.ts +0 -864
- package/src/fetch.ts +0 -39
- package/src/lazy.ts +0 -55
- package/src/posthog-node.ts +0 -668
- package/src/types.ts +0 -257
- package/test/crypto.spec.ts +0 -36
- package/test/extensions/error-conversion.spec.ts +0 -44
- package/test/extensions/sentry-integration.spec.ts +0 -164
- package/test/feature-flags.decide.spec.ts +0 -380
- package/test/feature-flags.spec.ts +0 -4683
- package/test/lazy.spec.ts +0 -71
- package/test/posthog-node.spec.ts +0 -1341
- package/test/test-utils.ts +0 -111
- package/tsconfig.json +0 -7
|
@@ -1,1341 +0,0 @@
|
|
|
1
|
-
import { MINIMUM_POLLING_INTERVAL, PostHog as PostHog, THIRTY_SECONDS } from '../src/posthog-node'
|
|
2
|
-
import fetch from '../src/fetch'
|
|
3
|
-
import { anyDecideCall, anyLocalEvalCall, apiImplementation, isPending } from './test-utils'
|
|
4
|
-
import { waitForPromises, wait } from '../../posthog-core/test/test-utils/test-utils'
|
|
5
|
-
import { randomUUID } from 'crypto'
|
|
6
|
-
jest.mock('../src/fetch')
|
|
7
|
-
|
|
8
|
-
jest.mock('../package.json', () => ({ version: '1.2.3' }))
|
|
9
|
-
|
|
10
|
-
const mockedFetch = jest.mocked(fetch, true)
|
|
11
|
-
|
|
12
|
-
const waitForFlushTimer = async (): Promise<void> => {
|
|
13
|
-
await waitForPromises()
|
|
14
|
-
// To trigger the flush via the timer
|
|
15
|
-
jest.runOnlyPendingTimers()
|
|
16
|
-
// Then wait for the flush promise
|
|
17
|
-
await waitForPromises()
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const getLastBatchEvents = (): any[] | undefined => {
|
|
21
|
-
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.objectContaining({ method: 'POST' }))
|
|
22
|
-
|
|
23
|
-
// reverse mock calls array to get the last call
|
|
24
|
-
const call = mockedFetch.mock.calls.reverse().find((x) => (x[0] as string).includes('/batch/'))
|
|
25
|
-
if (!call) {
|
|
26
|
-
return undefined
|
|
27
|
-
}
|
|
28
|
-
return JSON.parse((call[1] as any).body as any).batch
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
describe('PostHog Node.js', () => {
|
|
32
|
-
let posthog: PostHog
|
|
33
|
-
|
|
34
|
-
jest.useFakeTimers()
|
|
35
|
-
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
38
|
-
host: 'http://example.com',
|
|
39
|
-
fetchRetryCount: 0,
|
|
40
|
-
})
|
|
41
|
-
|
|
42
|
-
mockedFetch.mockResolvedValue({
|
|
43
|
-
status: 200,
|
|
44
|
-
text: () => Promise.resolve('ok'),
|
|
45
|
-
json: () =>
|
|
46
|
-
Promise.resolve({
|
|
47
|
-
status: 'ok',
|
|
48
|
-
}),
|
|
49
|
-
} as any)
|
|
50
|
-
})
|
|
51
|
-
|
|
52
|
-
afterEach(async () => {
|
|
53
|
-
mockedFetch.mockResolvedValue({
|
|
54
|
-
status: 200,
|
|
55
|
-
text: () => Promise.resolve('ok'),
|
|
56
|
-
json: () =>
|
|
57
|
-
Promise.resolve({
|
|
58
|
-
status: 'ok',
|
|
59
|
-
}),
|
|
60
|
-
} as any)
|
|
61
|
-
|
|
62
|
-
// ensure clean shutdown & no test interdependencies
|
|
63
|
-
await posthog.shutdown()
|
|
64
|
-
})
|
|
65
|
-
|
|
66
|
-
describe('core methods', () => {
|
|
67
|
-
it('should capture an event to shared queue', async () => {
|
|
68
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
69
|
-
posthog.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
|
|
70
|
-
|
|
71
|
-
await waitForFlushTimer()
|
|
72
|
-
|
|
73
|
-
const batchEvents = getLastBatchEvents()
|
|
74
|
-
expect(batchEvents).toEqual([
|
|
75
|
-
{
|
|
76
|
-
distinct_id: '123',
|
|
77
|
-
event: 'test-event',
|
|
78
|
-
properties: {
|
|
79
|
-
$groups: { org: 123 },
|
|
80
|
-
foo: 'bar',
|
|
81
|
-
$geoip_disable: true,
|
|
82
|
-
$lib: 'posthog-node',
|
|
83
|
-
$lib_version: '1.2.3',
|
|
84
|
-
},
|
|
85
|
-
uuid: expect.any(String),
|
|
86
|
-
timestamp: expect.any(String),
|
|
87
|
-
type: 'capture',
|
|
88
|
-
library: 'posthog-node',
|
|
89
|
-
library_version: '1.2.3',
|
|
90
|
-
},
|
|
91
|
-
])
|
|
92
|
-
})
|
|
93
|
-
|
|
94
|
-
it('shouldnt muddy subsequent capture calls', async () => {
|
|
95
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
96
|
-
posthog.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
|
|
97
|
-
|
|
98
|
-
await waitForFlushTimer()
|
|
99
|
-
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
100
|
-
expect.objectContaining({
|
|
101
|
-
distinct_id: '123',
|
|
102
|
-
event: 'test-event',
|
|
103
|
-
properties: expect.objectContaining({
|
|
104
|
-
$groups: { org: 123 },
|
|
105
|
-
foo: 'bar',
|
|
106
|
-
}),
|
|
107
|
-
library: 'posthog-node',
|
|
108
|
-
library_version: '1.2.3',
|
|
109
|
-
})
|
|
110
|
-
)
|
|
111
|
-
mockedFetch.mockClear()
|
|
112
|
-
|
|
113
|
-
posthog.capture({
|
|
114
|
-
distinctId: '123',
|
|
115
|
-
event: 'test-event',
|
|
116
|
-
properties: { foo: 'bar' },
|
|
117
|
-
groups: { other_group: 'x' },
|
|
118
|
-
})
|
|
119
|
-
|
|
120
|
-
await waitForFlushTimer()
|
|
121
|
-
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
122
|
-
expect.objectContaining({
|
|
123
|
-
distinct_id: '123',
|
|
124
|
-
event: 'test-event',
|
|
125
|
-
properties: expect.objectContaining({
|
|
126
|
-
$groups: { other_group: 'x' },
|
|
127
|
-
foo: 'bar',
|
|
128
|
-
$geoip_disable: true,
|
|
129
|
-
}),
|
|
130
|
-
library: 'posthog-node',
|
|
131
|
-
library_version: '1.2.3',
|
|
132
|
-
})
|
|
133
|
-
)
|
|
134
|
-
})
|
|
135
|
-
|
|
136
|
-
it('should capture identify events on shared queue', async () => {
|
|
137
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
138
|
-
posthog.identify({ distinctId: '123', properties: { foo: 'bar' } })
|
|
139
|
-
jest.runOnlyPendingTimers()
|
|
140
|
-
await waitForPromises()
|
|
141
|
-
|
|
142
|
-
const batchEvents = getLastBatchEvents()
|
|
143
|
-
expect(batchEvents).toMatchObject([
|
|
144
|
-
{
|
|
145
|
-
distinct_id: '123',
|
|
146
|
-
event: '$identify',
|
|
147
|
-
properties: {
|
|
148
|
-
$set: {
|
|
149
|
-
foo: 'bar',
|
|
150
|
-
},
|
|
151
|
-
$geoip_disable: true,
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
])
|
|
155
|
-
})
|
|
156
|
-
|
|
157
|
-
it('should handle identify using $set and $set_once', async () => {
|
|
158
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
159
|
-
posthog.identify({ distinctId: '123', properties: { $set: { foo: 'bar' }, $set_once: { vip: true } } })
|
|
160
|
-
jest.runOnlyPendingTimers()
|
|
161
|
-
await waitForPromises()
|
|
162
|
-
const batchEvents = getLastBatchEvents()
|
|
163
|
-
expect(batchEvents).toMatchObject([
|
|
164
|
-
{
|
|
165
|
-
distinct_id: '123',
|
|
166
|
-
event: '$identify',
|
|
167
|
-
properties: {
|
|
168
|
-
$set: {
|
|
169
|
-
foo: 'bar',
|
|
170
|
-
},
|
|
171
|
-
$set_once: {
|
|
172
|
-
vip: true,
|
|
173
|
-
},
|
|
174
|
-
$geoip_disable: true,
|
|
175
|
-
},
|
|
176
|
-
},
|
|
177
|
-
])
|
|
178
|
-
})
|
|
179
|
-
|
|
180
|
-
it('should handle identify using $set_once', async () => {
|
|
181
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
182
|
-
posthog.identify({ distinctId: '123', properties: { foo: 'bar', $set_once: { vip: true } } })
|
|
183
|
-
jest.runOnlyPendingTimers()
|
|
184
|
-
await waitForPromises()
|
|
185
|
-
const batchEvents = getLastBatchEvents()
|
|
186
|
-
expect(batchEvents).toMatchObject([
|
|
187
|
-
{
|
|
188
|
-
distinct_id: '123',
|
|
189
|
-
event: '$identify',
|
|
190
|
-
properties: {
|
|
191
|
-
$set: {
|
|
192
|
-
foo: 'bar',
|
|
193
|
-
},
|
|
194
|
-
$set_once: {
|
|
195
|
-
vip: true,
|
|
196
|
-
},
|
|
197
|
-
$geoip_disable: true,
|
|
198
|
-
},
|
|
199
|
-
},
|
|
200
|
-
])
|
|
201
|
-
})
|
|
202
|
-
|
|
203
|
-
it('should capture alias events on shared queue', async () => {
|
|
204
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
205
|
-
posthog.alias({ distinctId: '123', alias: '1234' })
|
|
206
|
-
jest.runOnlyPendingTimers()
|
|
207
|
-
await waitForPromises()
|
|
208
|
-
const batchEvents = getLastBatchEvents()
|
|
209
|
-
expect(batchEvents).toMatchObject([
|
|
210
|
-
{
|
|
211
|
-
distinct_id: '123',
|
|
212
|
-
event: '$create_alias',
|
|
213
|
-
properties: {
|
|
214
|
-
distinct_id: '123',
|
|
215
|
-
alias: '1234',
|
|
216
|
-
$geoip_disable: true,
|
|
217
|
-
},
|
|
218
|
-
},
|
|
219
|
-
])
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
it('should allow overriding timestamp', async () => {
|
|
223
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
224
|
-
posthog.capture({ event: 'custom-time', distinctId: '123', timestamp: new Date('2021-02-03') })
|
|
225
|
-
await waitForFlushTimer()
|
|
226
|
-
const batchEvents = getLastBatchEvents()
|
|
227
|
-
expect(batchEvents).toMatchObject([
|
|
228
|
-
{
|
|
229
|
-
distinct_id: '123',
|
|
230
|
-
timestamp: '2021-02-03T00:00:00.000Z',
|
|
231
|
-
event: 'custom-time',
|
|
232
|
-
uuid: expect.any(String),
|
|
233
|
-
},
|
|
234
|
-
])
|
|
235
|
-
})
|
|
236
|
-
|
|
237
|
-
it('should allow overriding uuid', async () => {
|
|
238
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
239
|
-
const uuid = randomUUID()
|
|
240
|
-
posthog.capture({ event: 'custom-time', distinctId: '123', uuid })
|
|
241
|
-
await waitForFlushTimer()
|
|
242
|
-
const batchEvents = getLastBatchEvents()
|
|
243
|
-
expect(batchEvents).toMatchObject([
|
|
244
|
-
{
|
|
245
|
-
distinct_id: '123',
|
|
246
|
-
timestamp: expect.any(String),
|
|
247
|
-
event: 'custom-time',
|
|
248
|
-
uuid: uuid,
|
|
249
|
-
},
|
|
250
|
-
])
|
|
251
|
-
})
|
|
252
|
-
|
|
253
|
-
it('should respect disableGeoip setting if passed in', async () => {
|
|
254
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
255
|
-
posthog.capture({
|
|
256
|
-
distinctId: '123',
|
|
257
|
-
event: 'test-event',
|
|
258
|
-
properties: { foo: 'bar' },
|
|
259
|
-
groups: { org: 123 },
|
|
260
|
-
disableGeoip: false,
|
|
261
|
-
})
|
|
262
|
-
|
|
263
|
-
await waitForFlushTimer()
|
|
264
|
-
const batchEvents = getLastBatchEvents()
|
|
265
|
-
expect(batchEvents?.[0].properties).toEqual({
|
|
266
|
-
$groups: { org: 123 },
|
|
267
|
-
foo: 'bar',
|
|
268
|
-
$lib: 'posthog-node',
|
|
269
|
-
$lib_version: '1.2.3',
|
|
270
|
-
})
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
it('should use default is set, and override on specific disableGeoip calls', async () => {
|
|
274
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
275
|
-
const client = new PostHog('TEST_API_KEY', {
|
|
276
|
-
host: 'http://example.com',
|
|
277
|
-
disableGeoip: false,
|
|
278
|
-
})
|
|
279
|
-
client.capture({ distinctId: '123', event: 'test-event', properties: { foo: 'bar' }, groups: { org: 123 } })
|
|
280
|
-
|
|
281
|
-
await waitForFlushTimer()
|
|
282
|
-
|
|
283
|
-
let batchEvents = getLastBatchEvents()
|
|
284
|
-
expect(batchEvents?.[0].properties).toEqual({
|
|
285
|
-
$groups: { org: 123 },
|
|
286
|
-
foo: 'bar',
|
|
287
|
-
$lib: 'posthog-node',
|
|
288
|
-
$lib_version: '1.2.3',
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
client.capture({
|
|
292
|
-
distinctId: '123',
|
|
293
|
-
event: 'test-event',
|
|
294
|
-
properties: { foo: 'bar' },
|
|
295
|
-
groups: { org: 123 },
|
|
296
|
-
disableGeoip: true,
|
|
297
|
-
})
|
|
298
|
-
|
|
299
|
-
await waitForFlushTimer()
|
|
300
|
-
|
|
301
|
-
batchEvents = getLastBatchEvents()
|
|
302
|
-
expect(batchEvents?.[0].properties).toEqual({
|
|
303
|
-
$groups: { org: 123 },
|
|
304
|
-
foo: 'bar',
|
|
305
|
-
$lib: 'posthog-node',
|
|
306
|
-
$lib_version: '1.2.3',
|
|
307
|
-
$geoip_disable: true,
|
|
308
|
-
})
|
|
309
|
-
|
|
310
|
-
client.capture({
|
|
311
|
-
distinctId: '123',
|
|
312
|
-
event: 'test-event',
|
|
313
|
-
properties: { foo: 'bar' },
|
|
314
|
-
groups: { org: 123 },
|
|
315
|
-
disableGeoip: false,
|
|
316
|
-
})
|
|
317
|
-
|
|
318
|
-
await waitForFlushTimer()
|
|
319
|
-
await waitForPromises()
|
|
320
|
-
|
|
321
|
-
batchEvents = getLastBatchEvents()
|
|
322
|
-
expect(batchEvents?.[0].properties).toEqual({
|
|
323
|
-
$groups: { org: 123 },
|
|
324
|
-
foo: 'bar',
|
|
325
|
-
$lib: 'posthog-node',
|
|
326
|
-
$lib_version: '1.2.3',
|
|
327
|
-
})
|
|
328
|
-
|
|
329
|
-
await client.shutdown()
|
|
330
|
-
})
|
|
331
|
-
|
|
332
|
-
it('should warn if capture is called with a string', () => {
|
|
333
|
-
const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {})
|
|
334
|
-
posthog.debug(true)
|
|
335
|
-
// @ts-expect-error - Testing the warning when passing a string instead of an object
|
|
336
|
-
posthog.capture('test-event')
|
|
337
|
-
expect(warnSpy).toHaveBeenCalledWith(
|
|
338
|
-
'Called capture() with a string as the first argument when an object was expected.'
|
|
339
|
-
)
|
|
340
|
-
warnSpy.mockRestore()
|
|
341
|
-
})
|
|
342
|
-
})
|
|
343
|
-
|
|
344
|
-
describe('shutdown', () => {
|
|
345
|
-
let warnSpy: jest.SpyInstance, logSpy: jest.SpyInstance
|
|
346
|
-
beforeEach(() => {
|
|
347
|
-
const actualLog = console.log
|
|
348
|
-
warnSpy = jest.spyOn(console, 'warn').mockImplementation((...args) => {
|
|
349
|
-
actualLog('spied warn:', ...args)
|
|
350
|
-
})
|
|
351
|
-
logSpy = jest.spyOn(console, 'log').mockImplementation((...args) => {
|
|
352
|
-
actualLog('spied log:', ...args)
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
mockedFetch.mockImplementation(async () => {
|
|
356
|
-
// simulate network delay
|
|
357
|
-
await wait(500)
|
|
358
|
-
|
|
359
|
-
return Promise.resolve({
|
|
360
|
-
status: 200,
|
|
361
|
-
text: () => Promise.resolve('ok'),
|
|
362
|
-
json: () =>
|
|
363
|
-
Promise.resolve({
|
|
364
|
-
status: 'ok',
|
|
365
|
-
}),
|
|
366
|
-
} as any)
|
|
367
|
-
})
|
|
368
|
-
|
|
369
|
-
jest.useRealTimers()
|
|
370
|
-
})
|
|
371
|
-
|
|
372
|
-
afterEach(() => {
|
|
373
|
-
jest.useFakeTimers()
|
|
374
|
-
})
|
|
375
|
-
|
|
376
|
-
it('should shutdown cleanly', async () => {
|
|
377
|
-
const ph = new PostHog('TEST_API_KEY', {
|
|
378
|
-
host: 'http://example.com',
|
|
379
|
-
fetchRetryCount: 0,
|
|
380
|
-
flushAt: 1,
|
|
381
|
-
})
|
|
382
|
-
ph.debug(true)
|
|
383
|
-
|
|
384
|
-
ph.capture({ event: 'test-event-1', distinctId: '123' })
|
|
385
|
-
|
|
386
|
-
// start flushing, but don't wait for promise to resolve before resuming events
|
|
387
|
-
const flushPromise = ph.flush()
|
|
388
|
-
expect(isPending(flushPromise)).toEqual(true)
|
|
389
|
-
|
|
390
|
-
ph.capture({ event: 'test-event-2', distinctId: '123' })
|
|
391
|
-
|
|
392
|
-
// start shutdown, but don't wait for promise to resolve before resuming events
|
|
393
|
-
const shutdownPromise = ph.shutdown()
|
|
394
|
-
|
|
395
|
-
ph.capture({ event: 'test-event-3', distinctId: '123' })
|
|
396
|
-
|
|
397
|
-
// wait for shutdown to finish
|
|
398
|
-
await shutdownPromise
|
|
399
|
-
expect(isPending(flushPromise)).toEqual(false)
|
|
400
|
-
|
|
401
|
-
expect(3).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('capture')).length)
|
|
402
|
-
const flushedEvents = logSpy.mock.calls.filter((call) => call[1].includes('flush')).flatMap((flush) => flush[2])
|
|
403
|
-
expect(flushedEvents).toMatchObject([
|
|
404
|
-
{ event: 'test-event-1' },
|
|
405
|
-
{ event: 'test-event-2' },
|
|
406
|
-
{ event: 'test-event-3' },
|
|
407
|
-
])
|
|
408
|
-
|
|
409
|
-
logSpy.mockRestore()
|
|
410
|
-
warnSpy.mockRestore()
|
|
411
|
-
})
|
|
412
|
-
|
|
413
|
-
it('should shutdown cleanly with pending capture flag promises', async () => {
|
|
414
|
-
const ph = new PostHog('TEST_API_KEY', {
|
|
415
|
-
host: 'http://example.com',
|
|
416
|
-
fetchRetryCount: 0,
|
|
417
|
-
flushAt: 4,
|
|
418
|
-
})
|
|
419
|
-
ph.debug(true)
|
|
420
|
-
|
|
421
|
-
for (let i = 0; i < 10; i++) {
|
|
422
|
-
ph.capture({ event: 'test-event', distinctId: `${i}`, sendFeatureFlags: true })
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
await ph.shutdown()
|
|
426
|
-
// all capture calls happen during shutdown
|
|
427
|
-
const batchEvents = getLastBatchEvents()
|
|
428
|
-
expect(batchEvents?.length).toEqual(6)
|
|
429
|
-
expect(batchEvents?.[batchEvents?.length - 1]).toMatchObject({
|
|
430
|
-
// last event in batch
|
|
431
|
-
distinct_id: '9',
|
|
432
|
-
event: 'test-event',
|
|
433
|
-
library: 'posthog-node',
|
|
434
|
-
library_version: '1.2.3',
|
|
435
|
-
properties: {
|
|
436
|
-
$lib: 'posthog-node',
|
|
437
|
-
$lib_version: '1.2.3',
|
|
438
|
-
$geoip_disable: true,
|
|
439
|
-
},
|
|
440
|
-
timestamp: expect.any(String),
|
|
441
|
-
type: 'capture',
|
|
442
|
-
})
|
|
443
|
-
expect(10).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('capture')).length)
|
|
444
|
-
// 1 for the captured events, 1 for the final flush of feature flag called events
|
|
445
|
-
expect(2).toEqual(logSpy.mock.calls.filter((call) => call[1].includes('flush')).length)
|
|
446
|
-
logSpy.mockRestore()
|
|
447
|
-
})
|
|
448
|
-
})
|
|
449
|
-
|
|
450
|
-
describe('groupIdentify', () => {
|
|
451
|
-
it('should identify group with unique id', async () => {
|
|
452
|
-
posthog.groupIdentify({ groupType: 'posthog', groupKey: 'team-1', properties: { analytics: true } })
|
|
453
|
-
jest.runOnlyPendingTimers()
|
|
454
|
-
await posthog.flush()
|
|
455
|
-
const batchEvents = getLastBatchEvents()
|
|
456
|
-
expect(batchEvents).toMatchObject([
|
|
457
|
-
{
|
|
458
|
-
distinct_id: '$posthog_team-1',
|
|
459
|
-
event: '$groupidentify',
|
|
460
|
-
properties: {
|
|
461
|
-
$group_type: 'posthog',
|
|
462
|
-
$group_key: 'team-1',
|
|
463
|
-
$group_set: { analytics: true },
|
|
464
|
-
$lib: 'posthog-node',
|
|
465
|
-
$geoip_disable: true,
|
|
466
|
-
},
|
|
467
|
-
},
|
|
468
|
-
])
|
|
469
|
-
})
|
|
470
|
-
|
|
471
|
-
it('should allow passing optional distinctID to identify group', async () => {
|
|
472
|
-
posthog.groupIdentify({
|
|
473
|
-
groupType: 'posthog',
|
|
474
|
-
groupKey: 'team-1',
|
|
475
|
-
properties: { analytics: true },
|
|
476
|
-
distinctId: '123',
|
|
477
|
-
})
|
|
478
|
-
jest.runOnlyPendingTimers()
|
|
479
|
-
await posthog.flush()
|
|
480
|
-
const batchEvents = getLastBatchEvents()
|
|
481
|
-
expect(batchEvents).toMatchObject([
|
|
482
|
-
{
|
|
483
|
-
distinct_id: '123',
|
|
484
|
-
event: '$groupidentify',
|
|
485
|
-
properties: {
|
|
486
|
-
$group_type: 'posthog',
|
|
487
|
-
$group_key: 'team-1',
|
|
488
|
-
$group_set: { analytics: true },
|
|
489
|
-
$lib: 'posthog-node',
|
|
490
|
-
$geoip_disable: true,
|
|
491
|
-
},
|
|
492
|
-
},
|
|
493
|
-
])
|
|
494
|
-
})
|
|
495
|
-
})
|
|
496
|
-
|
|
497
|
-
describe('feature flags', () => {
|
|
498
|
-
beforeEach(() => {
|
|
499
|
-
const mockFeatureFlags = {
|
|
500
|
-
'feature-1': true,
|
|
501
|
-
'feature-2': true,
|
|
502
|
-
'feature-variant': 'variant',
|
|
503
|
-
'disabled-flag': false,
|
|
504
|
-
'feature-array': true,
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// these are stringified in apiImplementation
|
|
508
|
-
const mockFeatureFlagPayloads = {
|
|
509
|
-
'feature-1': { color: 'blue' },
|
|
510
|
-
'feature-variant': 2,
|
|
511
|
-
'feature-array': [1],
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
const multivariateFlag = {
|
|
515
|
-
id: 1,
|
|
516
|
-
name: 'Beta Feature',
|
|
517
|
-
key: 'beta-feature-local',
|
|
518
|
-
active: true,
|
|
519
|
-
rollout_percentage: 100,
|
|
520
|
-
filters: {
|
|
521
|
-
groups: [
|
|
522
|
-
{
|
|
523
|
-
properties: [{ key: 'email', type: 'person', value: 'test@posthog.com', operator: 'exact' }],
|
|
524
|
-
rollout_percentage: 100,
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
rollout_percentage: 50,
|
|
528
|
-
},
|
|
529
|
-
],
|
|
530
|
-
multivariate: {
|
|
531
|
-
variants: [
|
|
532
|
-
{ key: 'first-variant', name: 'First Variant', rollout_percentage: 50 },
|
|
533
|
-
{ key: 'second-variant', name: 'Second Variant', rollout_percentage: 25 },
|
|
534
|
-
{ key: 'third-variant', name: 'Third Variant', rollout_percentage: 25 },
|
|
535
|
-
],
|
|
536
|
-
},
|
|
537
|
-
payloads: { 'first-variant': 'some-payload', 'third-variant': JSON.stringify({ a: 'json' }) },
|
|
538
|
-
},
|
|
539
|
-
}
|
|
540
|
-
const basicFlag = {
|
|
541
|
-
id: 1,
|
|
542
|
-
name: 'Beta Feature',
|
|
543
|
-
key: 'person-flag',
|
|
544
|
-
active: true,
|
|
545
|
-
filters: {
|
|
546
|
-
groups: [
|
|
547
|
-
{
|
|
548
|
-
properties: [
|
|
549
|
-
{
|
|
550
|
-
key: 'region',
|
|
551
|
-
operator: 'exact',
|
|
552
|
-
value: ['USA'],
|
|
553
|
-
type: 'person',
|
|
554
|
-
},
|
|
555
|
-
],
|
|
556
|
-
rollout_percentage: 100,
|
|
557
|
-
},
|
|
558
|
-
],
|
|
559
|
-
payloads: { true: '300' },
|
|
560
|
-
},
|
|
561
|
-
}
|
|
562
|
-
const falseFlag = {
|
|
563
|
-
id: 1,
|
|
564
|
-
name: 'Beta Feature',
|
|
565
|
-
key: 'false-flag',
|
|
566
|
-
active: true,
|
|
567
|
-
filters: {
|
|
568
|
-
groups: [
|
|
569
|
-
{
|
|
570
|
-
properties: [],
|
|
571
|
-
rollout_percentage: 0,
|
|
572
|
-
},
|
|
573
|
-
],
|
|
574
|
-
payloads: { true: '300' },
|
|
575
|
-
},
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
const arrayFlag = {
|
|
579
|
-
id: 5,
|
|
580
|
-
name: 'Beta Feature',
|
|
581
|
-
key: 'feature-array',
|
|
582
|
-
active: true,
|
|
583
|
-
filters: {
|
|
584
|
-
groups: [
|
|
585
|
-
{
|
|
586
|
-
properties: [],
|
|
587
|
-
rollout_percentage: 100,
|
|
588
|
-
},
|
|
589
|
-
],
|
|
590
|
-
payloads: { true: JSON.stringify([1]) },
|
|
591
|
-
},
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
mockedFetch.mockImplementation(
|
|
595
|
-
apiImplementation({
|
|
596
|
-
decideFlags: mockFeatureFlags,
|
|
597
|
-
decideFlagPayloads: mockFeatureFlagPayloads,
|
|
598
|
-
localFlags: { flags: [multivariateFlag, basicFlag, falseFlag, arrayFlag] },
|
|
599
|
-
})
|
|
600
|
-
)
|
|
601
|
-
|
|
602
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
603
|
-
host: 'http://example.com',
|
|
604
|
-
fetchRetryCount: 0,
|
|
605
|
-
})
|
|
606
|
-
})
|
|
607
|
-
|
|
608
|
-
it('should do getFeatureFlag', async () => {
|
|
609
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
610
|
-
await expect(posthog.getFeatureFlag('feature-variant', '123', { groups: { org: '123' } })).resolves.toEqual(
|
|
611
|
-
'variant'
|
|
612
|
-
)
|
|
613
|
-
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
614
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
615
|
-
'http://example.com/flags/?v=2',
|
|
616
|
-
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
617
|
-
)
|
|
618
|
-
})
|
|
619
|
-
|
|
620
|
-
it('should do isFeatureEnabled', async () => {
|
|
621
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
622
|
-
await expect(posthog.isFeatureEnabled('feature-1', '123', { groups: { org: '123' } })).resolves.toEqual(true)
|
|
623
|
-
await expect(posthog.isFeatureEnabled('feature-4', '123', { groups: { org: '123' } })).resolves.toEqual(undefined)
|
|
624
|
-
expect(mockedFetch).toHaveBeenCalledTimes(2)
|
|
625
|
-
})
|
|
626
|
-
|
|
627
|
-
it('captures feature flags when no personal API key is present', async () => {
|
|
628
|
-
mockedFetch.mockClear()
|
|
629
|
-
mockedFetch.mockClear()
|
|
630
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
631
|
-
|
|
632
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
633
|
-
host: 'http://example.com',
|
|
634
|
-
flushAt: 1,
|
|
635
|
-
fetchRetryCount: 0,
|
|
636
|
-
})
|
|
637
|
-
|
|
638
|
-
posthog.capture({
|
|
639
|
-
distinctId: 'distinct_id',
|
|
640
|
-
event: 'node test event',
|
|
641
|
-
sendFeatureFlags: true,
|
|
642
|
-
})
|
|
643
|
-
|
|
644
|
-
jest.runOnlyPendingTimers()
|
|
645
|
-
await waitForPromises()
|
|
646
|
-
|
|
647
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
648
|
-
'http://example.com/flags/?v=2',
|
|
649
|
-
expect.objectContaining({ method: 'POST' })
|
|
650
|
-
)
|
|
651
|
-
|
|
652
|
-
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
653
|
-
expect.objectContaining({
|
|
654
|
-
distinct_id: 'distinct_id',
|
|
655
|
-
event: 'node test event',
|
|
656
|
-
properties: expect.objectContaining({
|
|
657
|
-
$active_feature_flags: ['feature-1', 'feature-2', 'feature-array', 'feature-variant'],
|
|
658
|
-
'$feature/feature-1': true,
|
|
659
|
-
'$feature/feature-2': true,
|
|
660
|
-
'$feature/feature-array': true,
|
|
661
|
-
'$feature/feature-variant': 'variant',
|
|
662
|
-
$lib: 'posthog-node',
|
|
663
|
-
$lib_version: '1.2.3',
|
|
664
|
-
$geoip_disable: true,
|
|
665
|
-
}),
|
|
666
|
-
})
|
|
667
|
-
)
|
|
668
|
-
|
|
669
|
-
// no calls to `/local_evaluation`
|
|
670
|
-
|
|
671
|
-
expect(mockedFetch).not.toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
672
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
673
|
-
'http://example.com/flags/?v=2',
|
|
674
|
-
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
675
|
-
)
|
|
676
|
-
})
|
|
677
|
-
|
|
678
|
-
it('should use minimum featureFlagsPollingInterval of 100ms if set less to less than 100', async () => {
|
|
679
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
680
|
-
host: 'http://example.com',
|
|
681
|
-
fetchRetryCount: 0,
|
|
682
|
-
personalApiKey: 'TEST_PERSONAL_API_KEY',
|
|
683
|
-
featureFlagsPollingInterval: 98,
|
|
684
|
-
})
|
|
685
|
-
|
|
686
|
-
expect(posthog.options.featureFlagsPollingInterval).toEqual(MINIMUM_POLLING_INTERVAL)
|
|
687
|
-
})
|
|
688
|
-
|
|
689
|
-
it('should use default featureFlagsPollingInterval of 30000ms if none provided', async () => {
|
|
690
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
691
|
-
host: 'http://example.com',
|
|
692
|
-
fetchRetryCount: 0,
|
|
693
|
-
personalApiKey: 'TEST_PERSONAL_API_KEY',
|
|
694
|
-
})
|
|
695
|
-
|
|
696
|
-
expect(posthog.options.featureFlagsPollingInterval).toEqual(THIRTY_SECONDS)
|
|
697
|
-
})
|
|
698
|
-
|
|
699
|
-
it('should throw an error when creating SDK if a project key is passed in as personalApiKey', async () => {
|
|
700
|
-
expect(() => {
|
|
701
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
702
|
-
host: 'http://example.com',
|
|
703
|
-
fetchRetryCount: 0,
|
|
704
|
-
personalApiKey: 'phc_abc123',
|
|
705
|
-
featureFlagsPollingInterval: 100,
|
|
706
|
-
})
|
|
707
|
-
}).toThrow(Error)
|
|
708
|
-
})
|
|
709
|
-
|
|
710
|
-
it('captures feature flags with locally evaluated flags', async () => {
|
|
711
|
-
mockedFetch.mockClear()
|
|
712
|
-
mockedFetch.mockClear()
|
|
713
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
714
|
-
|
|
715
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
716
|
-
host: 'http://example.com',
|
|
717
|
-
flushAt: 1,
|
|
718
|
-
fetchRetryCount: 0,
|
|
719
|
-
personalApiKey: 'TEST_PERSONAL_API_KEY',
|
|
720
|
-
})
|
|
721
|
-
|
|
722
|
-
jest.runOnlyPendingTimers()
|
|
723
|
-
await waitForPromises()
|
|
724
|
-
|
|
725
|
-
posthog.capture({
|
|
726
|
-
distinctId: 'distinct_id',
|
|
727
|
-
event: 'node test event',
|
|
728
|
-
})
|
|
729
|
-
|
|
730
|
-
expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
731
|
-
// no decide call
|
|
732
|
-
expect(mockedFetch).not.toHaveBeenCalledWith(
|
|
733
|
-
'http://example.com/flags/?v=2',
|
|
734
|
-
expect.objectContaining({ method: 'POST' })
|
|
735
|
-
)
|
|
736
|
-
|
|
737
|
-
jest.runOnlyPendingTimers()
|
|
738
|
-
|
|
739
|
-
await waitForPromises()
|
|
740
|
-
|
|
741
|
-
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
742
|
-
expect.objectContaining({
|
|
743
|
-
distinct_id: 'distinct_id',
|
|
744
|
-
event: 'node test event',
|
|
745
|
-
properties: expect.objectContaining({
|
|
746
|
-
$active_feature_flags: ['beta-feature-local', 'feature-array'],
|
|
747
|
-
'$feature/beta-feature-local': 'third-variant',
|
|
748
|
-
'$feature/feature-array': true,
|
|
749
|
-
'$feature/false-flag': false,
|
|
750
|
-
$lib: 'posthog-node',
|
|
751
|
-
$lib_version: '1.2.3',
|
|
752
|
-
$geoip_disable: true,
|
|
753
|
-
}),
|
|
754
|
-
})
|
|
755
|
-
)
|
|
756
|
-
expect(
|
|
757
|
-
Object.prototype.hasOwnProperty.call(getLastBatchEvents()?.[0].properties, '$feature/beta-feature-local')
|
|
758
|
-
).toBe(true)
|
|
759
|
-
expect(Object.prototype.hasOwnProperty.call(getLastBatchEvents()?.[0].properties, '$feature/beta-feature')).toBe(
|
|
760
|
-
false
|
|
761
|
-
)
|
|
762
|
-
|
|
763
|
-
await posthog.shutdown()
|
|
764
|
-
})
|
|
765
|
-
|
|
766
|
-
it('doesnt add flag properties when locally evaluated flags are empty', async () => {
|
|
767
|
-
mockedFetch.mockClear()
|
|
768
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
769
|
-
mockedFetch.mockImplementation(
|
|
770
|
-
apiImplementation({ decideFlags: { a: false, b: 'true' }, decideFlagPayloads: {}, localFlags: { flags: [] } })
|
|
771
|
-
)
|
|
772
|
-
|
|
773
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
774
|
-
host: 'http://example.com',
|
|
775
|
-
flushAt: 1,
|
|
776
|
-
fetchRetryCount: 0,
|
|
777
|
-
personalApiKey: 'TEST_PERSONAL_API_KEY',
|
|
778
|
-
})
|
|
779
|
-
|
|
780
|
-
posthog.capture({
|
|
781
|
-
distinctId: 'distinct_id',
|
|
782
|
-
event: 'node test event',
|
|
783
|
-
})
|
|
784
|
-
|
|
785
|
-
jest.runOnlyPendingTimers()
|
|
786
|
-
await waitForPromises()
|
|
787
|
-
|
|
788
|
-
expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
789
|
-
// no decide call
|
|
790
|
-
expect(mockedFetch).not.toHaveBeenCalledWith(
|
|
791
|
-
'http://example.com/flags/?v=2',
|
|
792
|
-
expect.objectContaining({ method: 'POST' })
|
|
793
|
-
)
|
|
794
|
-
|
|
795
|
-
jest.runOnlyPendingTimers()
|
|
796
|
-
|
|
797
|
-
await waitForPromises()
|
|
798
|
-
|
|
799
|
-
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
800
|
-
expect.objectContaining({
|
|
801
|
-
distinct_id: 'distinct_id',
|
|
802
|
-
event: 'node test event',
|
|
803
|
-
properties: expect.objectContaining({
|
|
804
|
-
$lib: 'posthog-node',
|
|
805
|
-
$lib_version: '1.2.3',
|
|
806
|
-
$geoip_disable: true,
|
|
807
|
-
}),
|
|
808
|
-
})
|
|
809
|
-
)
|
|
810
|
-
expect(
|
|
811
|
-
Object.prototype.hasOwnProperty.call(getLastBatchEvents()?.[0].properties, '$feature/beta-feature-local')
|
|
812
|
-
).toBe(false)
|
|
813
|
-
expect(Object.prototype.hasOwnProperty.call(getLastBatchEvents()?.[0].properties, '$feature/beta-feature')).toBe(
|
|
814
|
-
false
|
|
815
|
-
)
|
|
816
|
-
})
|
|
817
|
-
|
|
818
|
-
it('captures feature flags with same geoip setting as capture', async () => {
|
|
819
|
-
mockedFetch.mockClear()
|
|
820
|
-
mockedFetch.mockClear()
|
|
821
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
822
|
-
|
|
823
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
824
|
-
host: 'http://example.com',
|
|
825
|
-
flushAt: 1,
|
|
826
|
-
fetchRetryCount: 0,
|
|
827
|
-
})
|
|
828
|
-
|
|
829
|
-
posthog.capture({
|
|
830
|
-
distinctId: 'distinct_id',
|
|
831
|
-
event: 'node test event',
|
|
832
|
-
sendFeatureFlags: true,
|
|
833
|
-
disableGeoip: false,
|
|
834
|
-
})
|
|
835
|
-
|
|
836
|
-
await waitForFlushTimer()
|
|
837
|
-
|
|
838
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
839
|
-
'http://example.com/flags/?v=2',
|
|
840
|
-
expect.objectContaining({ method: 'POST', body: expect.not.stringContaining('geoip_disable') })
|
|
841
|
-
)
|
|
842
|
-
|
|
843
|
-
expect(getLastBatchEvents()?.[0].properties).toEqual({
|
|
844
|
-
$active_feature_flags: ['feature-1', 'feature-2', 'feature-array', 'feature-variant'],
|
|
845
|
-
'$feature/feature-1': true,
|
|
846
|
-
'$feature/feature-2': true,
|
|
847
|
-
'$feature/feature-array': true,
|
|
848
|
-
'$feature/disabled-flag': false,
|
|
849
|
-
'$feature/feature-variant': 'variant',
|
|
850
|
-
$lib: 'posthog-node',
|
|
851
|
-
$lib_version: '1.2.3',
|
|
852
|
-
})
|
|
853
|
-
|
|
854
|
-
// no calls to `/local_evaluation`
|
|
855
|
-
|
|
856
|
-
expect(mockedFetch).not.toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
857
|
-
})
|
|
858
|
-
|
|
859
|
-
it('manages memory well when sending feature flags', async () => {
|
|
860
|
-
const flags = {
|
|
861
|
-
flags: [
|
|
862
|
-
{
|
|
863
|
-
id: 1,
|
|
864
|
-
name: 'Beta Feature',
|
|
865
|
-
key: 'beta-feature',
|
|
866
|
-
active: true,
|
|
867
|
-
filters: {
|
|
868
|
-
groups: [
|
|
869
|
-
{
|
|
870
|
-
properties: [],
|
|
871
|
-
rollout_percentage: 100,
|
|
872
|
-
},
|
|
873
|
-
],
|
|
874
|
-
},
|
|
875
|
-
},
|
|
876
|
-
],
|
|
877
|
-
}
|
|
878
|
-
|
|
879
|
-
mockedFetch.mockImplementation(
|
|
880
|
-
apiImplementation({ localFlags: flags, decideFlags: { 'beta-feature': 'decide-fallback-value' } })
|
|
881
|
-
)
|
|
882
|
-
|
|
883
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
884
|
-
host: 'http://example.com',
|
|
885
|
-
personalApiKey: 'TEST_PERSONAL_API_KEY',
|
|
886
|
-
maxCacheSize: 10,
|
|
887
|
-
fetchRetryCount: 0,
|
|
888
|
-
flushAt: 1,
|
|
889
|
-
})
|
|
890
|
-
|
|
891
|
-
expect(Object.keys(posthog.distinctIdHasSentFlagCalls).length).toEqual(0)
|
|
892
|
-
|
|
893
|
-
for (let i = 0; i < 100; i++) {
|
|
894
|
-
const distinctId = `some-distinct-id${i}`
|
|
895
|
-
await posthog.getFeatureFlag('beta-feature', distinctId)
|
|
896
|
-
|
|
897
|
-
await waitForPromises()
|
|
898
|
-
jest.runOnlyPendingTimers()
|
|
899
|
-
|
|
900
|
-
const batchEvents = getLastBatchEvents()
|
|
901
|
-
expect(batchEvents).toMatchObject([
|
|
902
|
-
{
|
|
903
|
-
distinct_id: distinctId,
|
|
904
|
-
event: '$feature_flag_called',
|
|
905
|
-
properties: expect.objectContaining({
|
|
906
|
-
$feature_flag: 'beta-feature',
|
|
907
|
-
$feature_flag_response: true,
|
|
908
|
-
$lib: 'posthog-node',
|
|
909
|
-
$lib_version: '1.2.3',
|
|
910
|
-
locally_evaluated: true,
|
|
911
|
-
'$feature/beta-feature': true,
|
|
912
|
-
}),
|
|
913
|
-
},
|
|
914
|
-
])
|
|
915
|
-
mockedFetch.mockClear()
|
|
916
|
-
|
|
917
|
-
expect(Object.keys(posthog.distinctIdHasSentFlagCalls).length <= 10).toEqual(true)
|
|
918
|
-
}
|
|
919
|
-
})
|
|
920
|
-
|
|
921
|
-
it('$feature_flag_called is called appropriately when querying flags', async () => {
|
|
922
|
-
mockedFetch.mockClear()
|
|
923
|
-
|
|
924
|
-
const flags = {
|
|
925
|
-
flags: [
|
|
926
|
-
{
|
|
927
|
-
id: 1,
|
|
928
|
-
name: 'Beta Feature',
|
|
929
|
-
key: 'beta-feature',
|
|
930
|
-
active: true,
|
|
931
|
-
filters: {
|
|
932
|
-
groups: [
|
|
933
|
-
{
|
|
934
|
-
properties: [{ key: 'region', value: 'USA' }],
|
|
935
|
-
rollout_percentage: 100,
|
|
936
|
-
},
|
|
937
|
-
],
|
|
938
|
-
},
|
|
939
|
-
},
|
|
940
|
-
],
|
|
941
|
-
}
|
|
942
|
-
|
|
943
|
-
mockedFetch.mockImplementation(
|
|
944
|
-
apiImplementation({ localFlags: flags, decideFlags: { 'decide-flag': 'decide-value' } })
|
|
945
|
-
)
|
|
946
|
-
|
|
947
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
948
|
-
host: 'http://example.com',
|
|
949
|
-
personalApiKey: 'TEST_PERSONAL_API_KEY',
|
|
950
|
-
maxCacheSize: 10,
|
|
951
|
-
fetchRetryCount: 0,
|
|
952
|
-
})
|
|
953
|
-
|
|
954
|
-
jest.runOnlyPendingTimers()
|
|
955
|
-
|
|
956
|
-
expect(
|
|
957
|
-
await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
|
|
958
|
-
personProperties: { region: 'USA', name: 'Aloha' },
|
|
959
|
-
})
|
|
960
|
-
).toEqual(true)
|
|
961
|
-
|
|
962
|
-
// TRICKY: There's now an extra step before events are queued, so need to wait for that to resolve
|
|
963
|
-
jest.runOnlyPendingTimers()
|
|
964
|
-
await waitForPromises()
|
|
965
|
-
await posthog.flush()
|
|
966
|
-
|
|
967
|
-
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
968
|
-
|
|
969
|
-
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
970
|
-
expect.objectContaining({
|
|
971
|
-
distinct_id: 'some-distinct-id',
|
|
972
|
-
event: '$feature_flag_called',
|
|
973
|
-
properties: expect.objectContaining({
|
|
974
|
-
$feature_flag: 'beta-feature',
|
|
975
|
-
$feature_flag_response: true,
|
|
976
|
-
'$feature/beta-feature': true,
|
|
977
|
-
$lib: 'posthog-node',
|
|
978
|
-
$lib_version: '1.2.3',
|
|
979
|
-
locally_evaluated: true,
|
|
980
|
-
$geoip_disable: true,
|
|
981
|
-
}),
|
|
982
|
-
})
|
|
983
|
-
)
|
|
984
|
-
mockedFetch.mockClear()
|
|
985
|
-
|
|
986
|
-
// # called again for same user, shouldn't call capture again
|
|
987
|
-
expect(
|
|
988
|
-
await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
|
|
989
|
-
personProperties: { region: 'USA', name: 'Aloha' },
|
|
990
|
-
})
|
|
991
|
-
).toEqual(true)
|
|
992
|
-
jest.runOnlyPendingTimers()
|
|
993
|
-
await waitForPromises()
|
|
994
|
-
await posthog.flush()
|
|
995
|
-
|
|
996
|
-
expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
997
|
-
|
|
998
|
-
// # called for different user, should call capture again
|
|
999
|
-
expect(
|
|
1000
|
-
await posthog.getFeatureFlag('beta-feature', 'some-distinct-id2', {
|
|
1001
|
-
groups: { x: 'y' },
|
|
1002
|
-
personProperties: { region: 'USA', name: 'Aloha' },
|
|
1003
|
-
disableGeoip: false,
|
|
1004
|
-
})
|
|
1005
|
-
).toEqual(true)
|
|
1006
|
-
jest.runOnlyPendingTimers()
|
|
1007
|
-
await waitForPromises()
|
|
1008
|
-
await posthog.flush()
|
|
1009
|
-
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
1010
|
-
|
|
1011
|
-
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
1012
|
-
expect.objectContaining({
|
|
1013
|
-
distinct_id: 'some-distinct-id2',
|
|
1014
|
-
event: '$feature_flag_called',
|
|
1015
|
-
})
|
|
1016
|
-
)
|
|
1017
|
-
expect(getLastBatchEvents()?.[0].properties).toEqual({
|
|
1018
|
-
$feature_flag: 'beta-feature',
|
|
1019
|
-
$feature_flag_response: true,
|
|
1020
|
-
$lib: 'posthog-node',
|
|
1021
|
-
$lib_version: '1.2.3',
|
|
1022
|
-
locally_evaluated: true,
|
|
1023
|
-
'$feature/beta-feature': true,
|
|
1024
|
-
$groups: { x: 'y' },
|
|
1025
|
-
})
|
|
1026
|
-
mockedFetch.mockClear()
|
|
1027
|
-
|
|
1028
|
-
// # called for different user, but send configuration is false, so should NOT call capture again
|
|
1029
|
-
expect(
|
|
1030
|
-
await posthog.getFeatureFlag('beta-feature', 'some-distinct-id23', {
|
|
1031
|
-
personProperties: { region: 'USA', name: 'Aloha' },
|
|
1032
|
-
sendFeatureFlagEvents: false,
|
|
1033
|
-
})
|
|
1034
|
-
).toEqual(true)
|
|
1035
|
-
jest.runOnlyPendingTimers()
|
|
1036
|
-
await waitForPromises()
|
|
1037
|
-
await posthog.flush()
|
|
1038
|
-
expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
1039
|
-
|
|
1040
|
-
// # called for different flag, falls back to decide, should call capture again
|
|
1041
|
-
expect(
|
|
1042
|
-
await posthog.getFeatureFlag('decide-flag', 'some-distinct-id2345', {
|
|
1043
|
-
groups: { organization: 'org1' },
|
|
1044
|
-
personProperties: { region: 'USA', name: 'Aloha' },
|
|
1045
|
-
})
|
|
1046
|
-
).toEqual('decide-value')
|
|
1047
|
-
jest.runOnlyPendingTimers()
|
|
1048
|
-
await waitForPromises()
|
|
1049
|
-
await posthog.flush()
|
|
1050
|
-
// one to decide, one to batch
|
|
1051
|
-
expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
|
|
1052
|
-
expect(mockedFetch).toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
1053
|
-
|
|
1054
|
-
expect(getLastBatchEvents()?.[0]).toEqual(
|
|
1055
|
-
expect.objectContaining({
|
|
1056
|
-
distinct_id: 'some-distinct-id2345',
|
|
1057
|
-
event: '$feature_flag_called',
|
|
1058
|
-
properties: expect.objectContaining({
|
|
1059
|
-
$feature_flag: 'decide-flag',
|
|
1060
|
-
$feature_flag_response: 'decide-value',
|
|
1061
|
-
$lib: 'posthog-node',
|
|
1062
|
-
$lib_version: '1.2.3',
|
|
1063
|
-
locally_evaluated: false,
|
|
1064
|
-
'$feature/decide-flag': 'decide-value',
|
|
1065
|
-
$groups: { organization: 'org1' },
|
|
1066
|
-
}),
|
|
1067
|
-
})
|
|
1068
|
-
)
|
|
1069
|
-
mockedFetch.mockClear()
|
|
1070
|
-
|
|
1071
|
-
expect(
|
|
1072
|
-
await posthog.isFeatureEnabled('decide-flag', 'some-distinct-id2345', {
|
|
1073
|
-
groups: { organization: 'org1' },
|
|
1074
|
-
personProperties: { region: 'USA', name: 'Aloha' },
|
|
1075
|
-
})
|
|
1076
|
-
).toEqual(true)
|
|
1077
|
-
jest.runOnlyPendingTimers()
|
|
1078
|
-
await waitForPromises()
|
|
1079
|
-
await posthog.flush()
|
|
1080
|
-
// call decide, but not batch
|
|
1081
|
-
expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
|
|
1082
|
-
expect(mockedFetch).not.toHaveBeenCalledWith('http://example.com/batch/', expect.any(Object))
|
|
1083
|
-
})
|
|
1084
|
-
|
|
1085
|
-
it('should do getFeatureFlagPayloads', async () => {
|
|
1086
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1087
|
-
await expect(
|
|
1088
|
-
posthog.getFeatureFlagPayload('feature-variant', '123', 'variant', { groups: { org: '123' } })
|
|
1089
|
-
).resolves.toEqual(2)
|
|
1090
|
-
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1091
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1092
|
-
'http://example.com/flags/?v=2',
|
|
1093
|
-
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
1094
|
-
)
|
|
1095
|
-
})
|
|
1096
|
-
|
|
1097
|
-
it('should not double parse json with getFeatureFlagPayloads and local eval', async () => {
|
|
1098
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1099
|
-
|
|
1100
|
-
posthog = new PostHog('TEST_API_KEY', {
|
|
1101
|
-
host: 'http://example.com',
|
|
1102
|
-
flushAt: 1,
|
|
1103
|
-
fetchRetryCount: 0,
|
|
1104
|
-
personalApiKey: 'TEST_PERSONAL_API_KEY',
|
|
1105
|
-
})
|
|
1106
|
-
|
|
1107
|
-
mockedFetch.mockClear()
|
|
1108
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1109
|
-
|
|
1110
|
-
await expect(
|
|
1111
|
-
posthog.getFeatureFlagPayload('feature-array', '123', true, { onlyEvaluateLocally: true })
|
|
1112
|
-
).resolves.toEqual([1])
|
|
1113
|
-
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1114
|
-
expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
|
|
1115
|
-
|
|
1116
|
-
mockedFetch.mockClear()
|
|
1117
|
-
|
|
1118
|
-
await expect(posthog.getFeatureFlagPayload('feature-array', '123')).resolves.toEqual([1])
|
|
1119
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1120
|
-
|
|
1121
|
-
await expect(posthog.getFeatureFlagPayload('false-flag', '123', true)).resolves.toEqual(300)
|
|
1122
|
-
// Check no non-batch API calls were made
|
|
1123
|
-
const additionalNonBatchCalls = mockedFetch.mock.calls.filter((call) => !call[0].includes('/batch'))
|
|
1124
|
-
expect(additionalNonBatchCalls.length).toBe(0)
|
|
1125
|
-
})
|
|
1126
|
-
|
|
1127
|
-
it('should not double parse json with getFeatureFlagPayloads and server eval', async () => {
|
|
1128
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1129
|
-
await expect(
|
|
1130
|
-
posthog.getFeatureFlagPayload('feature-array', '123', undefined, { groups: { org: '123' } })
|
|
1131
|
-
).resolves.toEqual([1])
|
|
1132
|
-
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1133
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1134
|
-
'http://example.com/flags/?v=2',
|
|
1135
|
-
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
1136
|
-
)
|
|
1137
|
-
})
|
|
1138
|
-
|
|
1139
|
-
it('should do getFeatureFlagPayloads without matchValue', async () => {
|
|
1140
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1141
|
-
await expect(
|
|
1142
|
-
posthog.getFeatureFlagPayload('feature-variant', '123', undefined, { groups: { org: '123' } })
|
|
1143
|
-
).resolves.toEqual(2)
|
|
1144
|
-
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1145
|
-
})
|
|
1146
|
-
|
|
1147
|
-
it('should do getFeatureFlags with geoip disabled and enabled', async () => {
|
|
1148
|
-
expect(mockedFetch).toHaveBeenCalledTimes(0)
|
|
1149
|
-
await expect(
|
|
1150
|
-
posthog.getFeatureFlagPayload('feature-variant', '123', 'variant', { groups: { org: '123' } })
|
|
1151
|
-
).resolves.toEqual(2)
|
|
1152
|
-
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1153
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1154
|
-
'http://example.com/flags/?v=2',
|
|
1155
|
-
expect.objectContaining({ method: 'POST', body: expect.stringContaining('"geoip_disable":true') })
|
|
1156
|
-
)
|
|
1157
|
-
|
|
1158
|
-
mockedFetch.mockClear()
|
|
1159
|
-
|
|
1160
|
-
await expect(posthog.isFeatureEnabled('feature-variant', '123', { disableGeoip: false })).resolves.toEqual(true)
|
|
1161
|
-
expect(mockedFetch).toHaveBeenCalledTimes(1)
|
|
1162
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1163
|
-
'http://example.com/flags/?v=2',
|
|
1164
|
-
expect.objectContaining({ method: 'POST', body: expect.not.stringContaining('geoip_disable') })
|
|
1165
|
-
)
|
|
1166
|
-
})
|
|
1167
|
-
|
|
1168
|
-
it('should add default person & group properties for feature flags', async () => {
|
|
1169
|
-
await posthog.getFeatureFlag('random_key', 'some_id', {
|
|
1170
|
-
groups: { company: 'id:5', instance: 'app.posthog.com' },
|
|
1171
|
-
personProperties: { x1: 'y1' },
|
|
1172
|
-
groupProperties: { company: { x: 'y' } },
|
|
1173
|
-
})
|
|
1174
|
-
jest.runOnlyPendingTimers()
|
|
1175
|
-
|
|
1176
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1177
|
-
'http://example.com/flags/?v=2',
|
|
1178
|
-
expect.objectContaining({
|
|
1179
|
-
body: JSON.stringify({
|
|
1180
|
-
token: 'TEST_API_KEY',
|
|
1181
|
-
distinct_id: 'some_id',
|
|
1182
|
-
groups: { company: 'id:5', instance: 'app.posthog.com' },
|
|
1183
|
-
person_properties: {
|
|
1184
|
-
distinct_id: 'some_id',
|
|
1185
|
-
x1: 'y1',
|
|
1186
|
-
},
|
|
1187
|
-
group_properties: {
|
|
1188
|
-
company: { $group_key: 'id:5', x: 'y' },
|
|
1189
|
-
instance: { $group_key: 'app.posthog.com' },
|
|
1190
|
-
},
|
|
1191
|
-
geoip_disable: true,
|
|
1192
|
-
flag_keys_to_evaluate: ['random_key'],
|
|
1193
|
-
}),
|
|
1194
|
-
})
|
|
1195
|
-
)
|
|
1196
|
-
|
|
1197
|
-
mockedFetch.mockClear()
|
|
1198
|
-
|
|
1199
|
-
await posthog.getFeatureFlag('random_key', 'some_id', {
|
|
1200
|
-
groups: { company: 'id:5', instance: 'app.posthog.com' },
|
|
1201
|
-
personProperties: { distinct_id: 'override' },
|
|
1202
|
-
groupProperties: { company: { $group_key: 'group_override' } },
|
|
1203
|
-
})
|
|
1204
|
-
jest.runOnlyPendingTimers()
|
|
1205
|
-
|
|
1206
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1207
|
-
'http://example.com/flags/?v=2',
|
|
1208
|
-
expect.objectContaining({
|
|
1209
|
-
body: JSON.stringify({
|
|
1210
|
-
token: 'TEST_API_KEY',
|
|
1211
|
-
distinct_id: 'some_id',
|
|
1212
|
-
groups: { company: 'id:5', instance: 'app.posthog.com' },
|
|
1213
|
-
person_properties: {
|
|
1214
|
-
distinct_id: 'override',
|
|
1215
|
-
},
|
|
1216
|
-
group_properties: {
|
|
1217
|
-
company: { $group_key: 'group_override' },
|
|
1218
|
-
instance: { $group_key: 'app.posthog.com' },
|
|
1219
|
-
},
|
|
1220
|
-
geoip_disable: true,
|
|
1221
|
-
flag_keys_to_evaluate: ['random_key'],
|
|
1222
|
-
}),
|
|
1223
|
-
})
|
|
1224
|
-
)
|
|
1225
|
-
|
|
1226
|
-
mockedFetch.mockClear()
|
|
1227
|
-
|
|
1228
|
-
// test nones
|
|
1229
|
-
await posthog.getAllFlagsAndPayloads('some_id', {
|
|
1230
|
-
groups: undefined,
|
|
1231
|
-
personProperties: undefined,
|
|
1232
|
-
groupProperties: undefined,
|
|
1233
|
-
})
|
|
1234
|
-
|
|
1235
|
-
jest.runOnlyPendingTimers()
|
|
1236
|
-
|
|
1237
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1238
|
-
'http://example.com/flags/?v=2',
|
|
1239
|
-
expect.objectContaining({
|
|
1240
|
-
body: JSON.stringify({
|
|
1241
|
-
token: 'TEST_API_KEY',
|
|
1242
|
-
distinct_id: 'some_id',
|
|
1243
|
-
groups: {},
|
|
1244
|
-
person_properties: {
|
|
1245
|
-
distinct_id: 'some_id',
|
|
1246
|
-
},
|
|
1247
|
-
group_properties: {},
|
|
1248
|
-
geoip_disable: true,
|
|
1249
|
-
}),
|
|
1250
|
-
})
|
|
1251
|
-
)
|
|
1252
|
-
|
|
1253
|
-
mockedFetch.mockClear()
|
|
1254
|
-
await posthog.getAllFlags('some_id', {
|
|
1255
|
-
groups: { company: 'id:5' },
|
|
1256
|
-
personProperties: undefined,
|
|
1257
|
-
groupProperties: undefined,
|
|
1258
|
-
})
|
|
1259
|
-
jest.runOnlyPendingTimers()
|
|
1260
|
-
|
|
1261
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1262
|
-
'http://example.com/flags/?v=2',
|
|
1263
|
-
expect.objectContaining({
|
|
1264
|
-
body: JSON.stringify({
|
|
1265
|
-
token: 'TEST_API_KEY',
|
|
1266
|
-
distinct_id: 'some_id',
|
|
1267
|
-
groups: { company: 'id:5' },
|
|
1268
|
-
person_properties: {
|
|
1269
|
-
distinct_id: 'some_id',
|
|
1270
|
-
},
|
|
1271
|
-
group_properties: { company: { $group_key: 'id:5' } },
|
|
1272
|
-
geoip_disable: true,
|
|
1273
|
-
}),
|
|
1274
|
-
})
|
|
1275
|
-
)
|
|
1276
|
-
|
|
1277
|
-
mockedFetch.mockClear()
|
|
1278
|
-
await posthog.getFeatureFlagPayload('random_key', 'some_id', undefined)
|
|
1279
|
-
jest.runOnlyPendingTimers()
|
|
1280
|
-
|
|
1281
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1282
|
-
'http://example.com/flags/?v=2',
|
|
1283
|
-
expect.objectContaining({
|
|
1284
|
-
body: JSON.stringify({
|
|
1285
|
-
token: 'TEST_API_KEY',
|
|
1286
|
-
distinct_id: 'some_id',
|
|
1287
|
-
groups: {},
|
|
1288
|
-
person_properties: {
|
|
1289
|
-
distinct_id: 'some_id',
|
|
1290
|
-
},
|
|
1291
|
-
group_properties: {},
|
|
1292
|
-
geoip_disable: true,
|
|
1293
|
-
flag_keys_to_evaluate: ['random_key'],
|
|
1294
|
-
}),
|
|
1295
|
-
})
|
|
1296
|
-
)
|
|
1297
|
-
|
|
1298
|
-
mockedFetch.mockClear()
|
|
1299
|
-
|
|
1300
|
-
await posthog.isFeatureEnabled('random_key', 'some_id')
|
|
1301
|
-
jest.runOnlyPendingTimers()
|
|
1302
|
-
|
|
1303
|
-
expect(mockedFetch).toHaveBeenCalledWith(
|
|
1304
|
-
'http://example.com/flags/?v=2',
|
|
1305
|
-
expect.objectContaining({
|
|
1306
|
-
body: JSON.stringify({
|
|
1307
|
-
token: 'TEST_API_KEY',
|
|
1308
|
-
distinct_id: 'some_id',
|
|
1309
|
-
groups: {},
|
|
1310
|
-
person_properties: {
|
|
1311
|
-
distinct_id: 'some_id',
|
|
1312
|
-
},
|
|
1313
|
-
group_properties: {},
|
|
1314
|
-
geoip_disable: true,
|
|
1315
|
-
flag_keys_to_evaluate: ['random_key'],
|
|
1316
|
-
}),
|
|
1317
|
-
})
|
|
1318
|
-
)
|
|
1319
|
-
})
|
|
1320
|
-
|
|
1321
|
-
it('should log error when decide response has errors', async () => {
|
|
1322
|
-
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
1323
|
-
|
|
1324
|
-
mockedFetch.mockImplementation(
|
|
1325
|
-
apiImplementation({
|
|
1326
|
-
decideFlags: { 'feature-1': true },
|
|
1327
|
-
decideFlagPayloads: {},
|
|
1328
|
-
errorsWhileComputingFlags: true,
|
|
1329
|
-
})
|
|
1330
|
-
)
|
|
1331
|
-
|
|
1332
|
-
await posthog.getFeatureFlag('feature-1', '123')
|
|
1333
|
-
|
|
1334
|
-
expect(errorSpy).toHaveBeenCalledWith(
|
|
1335
|
-
'[FEATURE FLAGS] Error while computing feature flags, some flags may be missing or incorrect. Learn more at https://posthog.com/docs/feature-flags/best-practices'
|
|
1336
|
-
)
|
|
1337
|
-
|
|
1338
|
-
errorSpy.mockRestore()
|
|
1339
|
-
})
|
|
1340
|
-
})
|
|
1341
|
-
})
|