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.
Files changed (67) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/lib/edge/index.cjs +3919 -0
  3. package/lib/edge/index.cjs.map +1 -0
  4. package/lib/edge/index.mjs +3893 -0
  5. package/lib/edge/index.mjs.map +1 -0
  6. package/lib/index.d.ts +921 -859
  7. package/lib/{index.cjs.js → node/index.cjs} +3576 -3595
  8. package/lib/node/index.cjs.map +1 -0
  9. package/lib/{index.esm.js → node/index.mjs} +3577 -3593
  10. package/lib/node/index.mjs.map +1 -0
  11. package/package.json +31 -4
  12. package/index.ts +0 -3
  13. package/lib/index.cjs.js.map +0 -1
  14. package/lib/index.esm.js.map +0 -1
  15. package/lib/posthog-core/src/eventemitter.d.ts +0 -8
  16. package/lib/posthog-core/src/featureFlagUtils.d.ts +0 -34
  17. package/lib/posthog-core/src/index.d.ts +0 -259
  18. package/lib/posthog-core/src/lz-string.d.ts +0 -8
  19. package/lib/posthog-core/src/storage-memory.d.ts +0 -6
  20. package/lib/posthog-core/src/types.d.ts +0 -422
  21. package/lib/posthog-core/src/utils.d.ts +0 -20
  22. package/lib/posthog-core/src/vendor/uuidv7.d.ts +0 -179
  23. package/lib/posthog-node/index.d.ts +0 -3
  24. package/lib/posthog-node/src/crypto-helpers.d.ts +0 -3
  25. package/lib/posthog-node/src/crypto.d.ts +0 -2
  26. package/lib/posthog-node/src/error-tracking.d.ts +0 -12
  27. package/lib/posthog-node/src/extensions/error-tracking/autocapture.d.ts +0 -3
  28. package/lib/posthog-node/src/extensions/error-tracking/context-lines.d.ts +0 -6
  29. package/lib/posthog-node/src/extensions/error-tracking/error-conversion.d.ts +0 -2
  30. package/lib/posthog-node/src/extensions/error-tracking/reduceable-cache.d.ts +0 -12
  31. package/lib/posthog-node/src/extensions/error-tracking/stack-trace.d.ts +0 -15
  32. package/lib/posthog-node/src/extensions/error-tracking/type-checking.d.ts +0 -7
  33. package/lib/posthog-node/src/extensions/error-tracking/types.d.ts +0 -57
  34. package/lib/posthog-node/src/extensions/express.d.ts +0 -17
  35. package/lib/posthog-node/src/extensions/sentry-integration.d.ts +0 -51
  36. package/lib/posthog-node/src/feature-flags.d.ts +0 -84
  37. package/lib/posthog-node/src/fetch.d.ts +0 -11
  38. package/lib/posthog-node/src/lazy.d.ts +0 -23
  39. package/lib/posthog-node/src/posthog-node.d.ts +0 -98
  40. package/lib/posthog-node/src/types.d.ts +0 -229
  41. package/lib/posthog-node/test/test-utils.d.ts +0 -18
  42. package/src/crypto-helpers.ts +0 -36
  43. package/src/crypto.ts +0 -22
  44. package/src/error-tracking.ts +0 -67
  45. package/src/extensions/error-tracking/autocapture.ts +0 -65
  46. package/src/extensions/error-tracking/context-lines.ts +0 -425
  47. package/src/extensions/error-tracking/error-conversion.ts +0 -262
  48. package/src/extensions/error-tracking/reduceable-cache.ts +0 -39
  49. package/src/extensions/error-tracking/stack-trace.ts +0 -269
  50. package/src/extensions/error-tracking/type-checking.ts +0 -40
  51. package/src/extensions/error-tracking/types.ts +0 -65
  52. package/src/extensions/express.ts +0 -37
  53. package/src/extensions/sentry-integration.ts +0 -196
  54. package/src/feature-flags.ts +0 -864
  55. package/src/fetch.ts +0 -39
  56. package/src/lazy.ts +0 -55
  57. package/src/posthog-node.ts +0 -668
  58. package/src/types.ts +0 -257
  59. package/test/crypto.spec.ts +0 -36
  60. package/test/extensions/error-conversion.spec.ts +0 -44
  61. package/test/extensions/sentry-integration.spec.ts +0 -164
  62. package/test/feature-flags.decide.spec.ts +0 -380
  63. package/test/feature-flags.spec.ts +0 -4683
  64. package/test/lazy.spec.ts +0 -71
  65. package/test/posthog-node.spec.ts +0 -1341
  66. package/test/test-utils.ts +0 -111
  67. package/tsconfig.json +0 -7
@@ -1,4683 +0,0 @@
1
- // import { PostHog, PostHogOptions } from '../'
2
- // Uncomment below line while developing to not compile code everytime
3
- import { PostHog as PostHog, PostHogOptions } from '../src/posthog-node'
4
- import { matchProperty, InconclusiveMatchError, relativeDateParseForFeatureFlagMatching } from '../src/feature-flags'
5
- import fetch from '../src/fetch'
6
- import { anyDecideCall, anyLocalEvalCall, apiImplementation } from './test-utils'
7
- import { waitForPromises } from 'posthog-core/test/test-utils/test-utils'
8
- jest.mock('../src/fetch')
9
-
10
- jest.spyOn(console, 'debug').mockImplementation()
11
-
12
- const mockedFetch = jest.mocked(fetch, true)
13
-
14
- const posthogImmediateResolveOptions: PostHogOptions = {
15
- fetchRetryCount: 0,
16
- }
17
-
18
- describe('local evaluation', () => {
19
- let posthog: PostHog
20
-
21
- jest.useFakeTimers()
22
-
23
- afterEach(async () => {
24
- // ensure clean shutdown & no test interdependencies
25
- await posthog.shutdown()
26
- })
27
-
28
- it('evaluates person properties with undefined property values', async () => {
29
- const flags = {
30
- flags: [
31
- {
32
- id: 1,
33
- name: 'Beta Feature',
34
- key: 'person-flag',
35
- active: true,
36
- filters: {
37
- groups: [
38
- {
39
- variant: null,
40
- properties: [
41
- {
42
- key: 'latestBuildVersion',
43
- type: 'person',
44
- value: '.+',
45
- operator: 'regex',
46
- },
47
- {
48
- key: 'latestBuildVersionMajor',
49
- type: 'person',
50
- value: '23',
51
- operator: 'gt',
52
- },
53
- {
54
- key: 'latestBuildVersionMinor',
55
- type: 'person',
56
- value: '31',
57
- operator: 'gt',
58
- },
59
- {
60
- key: 'latestBuildVersionPatch',
61
- type: 'person',
62
- value: '0',
63
- operator: 'gt',
64
- },
65
- ],
66
- rollout_percentage: 100,
67
- },
68
- ],
69
- },
70
- },
71
- ],
72
- }
73
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
74
-
75
- posthog = new PostHog('TEST_API_KEY', {
76
- host: 'http://example.com',
77
- personalApiKey: 'TEST_PERSONAL_API_KEY',
78
- ...posthogImmediateResolveOptions,
79
- })
80
-
81
- expect(
82
- await posthog.getFeatureFlag('person-flag', 'some-distinct-id', {
83
- personProperties: {
84
- latestBuildVersion: undefined,
85
- latestBuildVersionMajor: undefined,
86
- latestBuildVersionMinor: undefined,
87
- latestBuildVersionPatch: undefined,
88
- } as unknown as Record<string, string>,
89
- })
90
- ).toEqual(false)
91
-
92
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
93
- })
94
-
95
- it('evaluates person properties', async () => {
96
- const flags = {
97
- flags: [
98
- {
99
- id: 1,
100
- name: 'Beta Feature',
101
- key: 'person-flag',
102
- active: true,
103
- filters: {
104
- groups: [
105
- {
106
- properties: [
107
- {
108
- key: 'region',
109
- operator: 'exact',
110
- value: ['USA'],
111
- type: 'person',
112
- },
113
- ],
114
- rollout_percentage: null,
115
- },
116
- ],
117
- },
118
- },
119
- ],
120
- }
121
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
122
-
123
- posthog = new PostHog('TEST_API_KEY', {
124
- host: 'http://example.com',
125
- personalApiKey: 'TEST_PERSONAL_API_KEY',
126
- ...posthogImmediateResolveOptions,
127
- })
128
-
129
- expect(
130
- await posthog.getFeatureFlag('person-flag', 'some-distinct-id', { personProperties: { region: 'USA' } })
131
- ).toEqual(true)
132
- expect(
133
- await posthog.getFeatureFlag('person-flag', 'some-distinct-id', { personProperties: { region: 'Canada' } })
134
- ).toEqual(false)
135
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
136
- })
137
-
138
- it('evaluates group properties', async () => {
139
- const flags = {
140
- flags: [
141
- {
142
- id: 1,
143
- name: 'Beta Feature',
144
- key: 'group-flag',
145
- active: true,
146
- filters: {
147
- aggregation_group_type_index: 0,
148
- groups: [
149
- {
150
- properties: [
151
- {
152
- group_type_index: 0,
153
- key: 'name',
154
- operator: 'exact',
155
- value: ['Project Name 1'],
156
- type: 'group',
157
- },
158
- ],
159
- rollout_percentage: 35,
160
- },
161
- ],
162
- },
163
- },
164
- ],
165
- group_type_mapping: { '0': 'company', '1': 'project' },
166
- }
167
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
168
-
169
- posthog = new PostHog('TEST_API_KEY', {
170
- host: 'http://example.com',
171
- personalApiKey: 'TEST_PERSONAL_API_KEY',
172
- ...posthogImmediateResolveOptions,
173
- })
174
-
175
- // # groups not passed in, hence false
176
- expect(
177
- await posthog.getFeatureFlag('group-flag', 'some-distinct-id', {
178
- groupProperties: { company: { name: 'Project Name 1' } },
179
- })
180
- ).toEqual(false)
181
- expect(
182
- await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
183
- groupProperties: { company: { name: 'Project Name 2' } },
184
- })
185
- ).toEqual(false)
186
-
187
- // # this is good
188
- expect(
189
- await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
190
- groups: { company: 'amazon_without_rollout' },
191
- groupProperties: { company: { name: 'Project Name 1' } },
192
- })
193
- ).toEqual(true)
194
-
195
- // # rollout % not met
196
- expect(
197
- await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
198
- groups: { company: 'amazon' },
199
- groupProperties: { company: { name: 'Project Name 1' } },
200
- })
201
- ).toEqual(false)
202
-
203
- // # property mismatch
204
- expect(
205
- await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
206
- groups: { company: 'amazon_without_rollout' },
207
- groupProperties: { company: { name: 'Project Name 2' } },
208
- })
209
- ).toEqual(false)
210
-
211
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
212
- // decide not called
213
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
214
- })
215
-
216
- it('evaluates group properties and falls back to decide when group_type_mappings not present', async () => {
217
- const flags = {
218
- flags: [
219
- {
220
- id: 1,
221
- name: 'Beta Feature',
222
- key: 'group-flag',
223
- active: true,
224
- filters: {
225
- aggregation_group_type_index: 0,
226
- groups: [
227
- {
228
- properties: [
229
- {
230
- group_type_index: 0,
231
- key: 'name',
232
- operator: 'exact',
233
- value: ['Project Name 1'],
234
- type: 'group',
235
- },
236
- ],
237
- rollout_percentage: 35,
238
- },
239
- ],
240
- },
241
- },
242
- ],
243
- // "group_type_mapping": {"0": "company", "1": "project"}
244
- }
245
- mockedFetch.mockImplementation(
246
- apiImplementation({ localFlags: flags, decideFlags: { 'group-flag': 'decide-fallback-value' } })
247
- )
248
-
249
- posthog = new PostHog('TEST_API_KEY', {
250
- host: 'http://example.com',
251
- personalApiKey: 'TEST_PERSONAL_API_KEY',
252
- ...posthogImmediateResolveOptions,
253
- })
254
- // # group_type_mappings not present, so fallback to `/flags`
255
- expect(
256
- await posthog.getFeatureFlag('group-flag', 'some-distinct-2', {
257
- groupProperties: {
258
- company: { name: 'Project Name 1' },
259
- },
260
- })
261
- ).toEqual('decide-fallback-value')
262
- })
263
-
264
- it('evaluates flag with complex definition', async () => {
265
- const flags = {
266
- flags: [
267
- {
268
- id: 1,
269
- name: 'Beta Feature',
270
- key: 'complex-flag',
271
- active: true,
272
- filters: {
273
- groups: [
274
- {
275
- properties: [
276
- {
277
- key: 'region',
278
- operator: 'exact',
279
- value: ['USA'],
280
- type: 'person',
281
- },
282
- {
283
- key: 'name',
284
- operator: 'exact',
285
- value: ['Aloha'],
286
- type: 'person',
287
- },
288
- ],
289
- rollout_percentage: undefined,
290
- },
291
- {
292
- properties: [
293
- {
294
- key: 'email',
295
- operator: 'exact',
296
- value: ['a@b.com', 'b@c.com'],
297
- type: 'person',
298
- },
299
- ],
300
- rollout_percentage: 30,
301
- },
302
- {
303
- properties: [
304
- {
305
- key: 'doesnt_matter',
306
- operator: 'exact',
307
- value: ['1', '2'],
308
- type: 'person',
309
- },
310
- ],
311
- rollout_percentage: 0,
312
- },
313
- ],
314
- },
315
- },
316
- ],
317
- }
318
- mockedFetch.mockImplementation(
319
- apiImplementation({ localFlags: flags, decideFlags: { 'complex-flag': 'decide-fallback-value' } })
320
- )
321
-
322
- posthog = new PostHog('TEST_API_KEY', {
323
- host: 'http://example.com',
324
- personalApiKey: 'TEST_PERSONAL_API_KEY',
325
- ...posthogImmediateResolveOptions,
326
- })
327
-
328
- expect(
329
- await posthog.getFeatureFlag('complex-flag', 'some-distinct-id', {
330
- personProperties: { region: 'USA', name: 'Aloha' },
331
- })
332
- ).toEqual(true)
333
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
334
-
335
- // # this distinctIDs hash is < rollout %
336
- expect(
337
- await posthog.getFeatureFlag('complex-flag', 'some-distinct-id_within_rollout?', {
338
- personProperties: { region: 'USA', email: 'a@b.com' },
339
- })
340
- ).toEqual(true)
341
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
342
-
343
- // # will fall back on `/flags`, as all properties present for second group, but that group resolves to false
344
- expect(
345
- await posthog.getFeatureFlag('complex-flag', 'some-distinct-id_outside_rollout?', {
346
- personProperties: { region: 'USA', email: 'a@b.com' },
347
- })
348
- ).toEqual('decide-fallback-value')
349
- expect(mockedFetch).toHaveBeenCalledWith(
350
- 'http://example.com/flags/?v=2',
351
- expect.objectContaining({
352
- body: JSON.stringify({
353
- token: 'TEST_API_KEY',
354
- distinct_id: 'some-distinct-id_outside_rollout?',
355
- groups: {},
356
- person_properties: {
357
- distinct_id: 'some-distinct-id_outside_rollout?',
358
- region: 'USA',
359
- email: 'a@b.com',
360
- },
361
- group_properties: {},
362
- geoip_disable: true,
363
- flag_keys_to_evaluate: ['complex-flag'],
364
- }),
365
- })
366
- )
367
- mockedFetch.mockClear()
368
-
369
- // # same as above
370
- expect(
371
- await posthog.getFeatureFlag('complex-flag', 'some-distinct-id', { personProperties: { doesnt_matter: '1' } })
372
- ).toEqual('decide-fallback-value')
373
- expect(mockedFetch).toHaveBeenCalledWith(
374
- 'http://example.com/flags/?v=2',
375
- expect.objectContaining({
376
- body: JSON.stringify({
377
- token: 'TEST_API_KEY',
378
- distinct_id: 'some-distinct-id',
379
- groups: {},
380
- person_properties: { distinct_id: 'some-distinct-id', doesnt_matter: '1' },
381
- group_properties: {},
382
- geoip_disable: true,
383
- flag_keys_to_evaluate: ['complex-flag'],
384
- }),
385
- })
386
- )
387
- mockedFetch.mockClear()
388
-
389
- expect(
390
- await posthog.getFeatureFlag('complex-flag', 'some-distinct-id', { personProperties: { region: 'USA' } })
391
- ).toEqual('decide-fallback-value')
392
- expect(mockedFetch).toHaveBeenCalledTimes(1) // TODO: Check this
393
- mockedFetch.mockClear()
394
-
395
- // # won't need to fallback when all values are present, and resolves to False
396
- expect(
397
- await posthog.getFeatureFlag('complex-flag', 'some-distinct-id_outside_rollout?', {
398
- personProperties: { region: 'USA', email: 'a@b.com', name: 'X', doesnt_matter: '1' },
399
- })
400
- ).toEqual(false)
401
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
402
- })
403
-
404
- it('falls back to decide', async () => {
405
- const flags = {
406
- flags: [
407
- {
408
- id: 1,
409
- name: 'Beta Feature',
410
- key: 'beta-feature',
411
- active: true,
412
- filters: {
413
- groups: [
414
- {
415
- properties: [{ key: 'id', value: 98, operator: undefined, type: 'cohort' }],
416
- rollout_percentage: 100,
417
- },
418
- ],
419
- },
420
- },
421
- {
422
- id: 2,
423
- name: 'Beta Feature',
424
- key: 'beta-feature2',
425
- active: true,
426
- filters: {
427
- groups: [
428
- {
429
- properties: [
430
- {
431
- key: 'region',
432
- operator: 'exact',
433
- value: ['USA'],
434
- type: 'person',
435
- },
436
- ],
437
- rollout_percentage: 100,
438
- },
439
- ],
440
- },
441
- },
442
- ],
443
- }
444
- mockedFetch.mockImplementation(
445
- apiImplementation({
446
- localFlags: flags,
447
- decideFlags: { 'beta-feature': 'alakazam', 'beta-feature2': 'alakazam2' },
448
- })
449
- )
450
-
451
- posthog = new PostHog('TEST_API_KEY', {
452
- host: 'http://example.com',
453
- personalApiKey: 'TEST_PERSONAL_API_KEY',
454
- ...posthogImmediateResolveOptions,
455
- })
456
-
457
- // # beta-feature fallbacks to decide because property type is unknown
458
- expect(await posthog.getFeatureFlag('beta-feature', 'some-distinct-id')).toEqual('alakazam')
459
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
460
- mockedFetch.mockClear()
461
-
462
- // # beta-feature2 fallbacks to decide because region property not given with call
463
- expect(await posthog.getFeatureFlag('beta-feature2', 'some-distinct-id')).toEqual('alakazam2')
464
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
465
- })
466
-
467
- it('dont fall back to decide when local evaluation is set', async () => {
468
- const flags = {
469
- flags: [
470
- {
471
- id: 1,
472
- name: 'Beta Feature',
473
- key: 'beta-feature',
474
- active: true,
475
- filters: {
476
- groups: [
477
- {
478
- properties: [{ key: 'id', value: 98, operator: undefined, type: 'cohort' }],
479
- rollout_percentage: 100,
480
- },
481
- ],
482
- },
483
- },
484
- {
485
- id: 2,
486
- name: 'Beta Feature',
487
- key: 'beta-feature2',
488
- active: true,
489
- filters: {
490
- groups: [
491
- {
492
- properties: [
493
- {
494
- key: 'region',
495
- operator: 'exact',
496
- value: ['USA'],
497
- type: 'person',
498
- },
499
- ],
500
- rollout_percentage: 100,
501
- },
502
- ],
503
- },
504
- },
505
- ],
506
- }
507
- mockedFetch.mockImplementation(
508
- apiImplementation({
509
- localFlags: flags,
510
- decideFlags: { 'beta-feature': 'alakazam', 'beta-feature2': 'alakazam2' },
511
- })
512
- )
513
-
514
- posthog = new PostHog('TEST_API_KEY', {
515
- host: 'http://example.com',
516
- personalApiKey: 'TEST_PERSONAL_API_KEY',
517
- ...posthogImmediateResolveOptions,
518
- })
519
-
520
- // # beta-feature should fallback to decide because property type is unknown
521
- // # but doesn't because only_evaluate_locally is true
522
- expect(await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', { onlyEvaluateLocally: true })).toEqual(
523
- undefined
524
- )
525
- expect(await posthog.isFeatureEnabled('beta-feature', 'some-distinct-id', { onlyEvaluateLocally: true })).toEqual(
526
- undefined
527
- )
528
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
529
-
530
- // # beta-feature2 should fallback to decide because region property not given with call
531
- // # but doesn't because only_evaluate_locally is true
532
- expect(await posthog.getFeatureFlag('beta-feature2', 'some-distinct-id', { onlyEvaluateLocally: true })).toEqual(
533
- undefined
534
- )
535
- expect(await posthog.isFeatureEnabled('beta-feature2', 'some-distinct-id', { onlyEvaluateLocally: true })).toEqual(
536
- undefined
537
- )
538
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
539
- })
540
-
541
- it("doesn't return undefined when flag is evaluated successfully", async () => {
542
- const flags = {
543
- flags: [
544
- {
545
- id: 1,
546
- name: 'Beta Feature',
547
- key: 'beta-feature',
548
- active: true,
549
- filters: {
550
- groups: [
551
- {
552
- properties: [],
553
- rollout_percentage: 0,
554
- },
555
- ],
556
- },
557
- },
558
- ],
559
- }
560
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags, decideFlags: {} }))
561
-
562
- posthog = new PostHog('TEST_API_KEY', {
563
- host: 'http://example.com',
564
- personalApiKey: 'TEST_PERSONAL_API_KEY',
565
- ...posthogImmediateResolveOptions,
566
- })
567
-
568
- // # beta-feature resolves to False
569
- expect(await posthog.getFeatureFlag('beta-feature', 'some-distinct-id')).toEqual(false)
570
- expect(await posthog.isFeatureEnabled('beta-feature', 'some-distinct-id')).toEqual(false)
571
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
572
-
573
- // # beta-feature2 falls back to decide, and whatever decide returns is the value
574
- expect(await posthog.getFeatureFlag('beta-feature2', 'some-distinct-id')).toEqual(undefined)
575
- expect(await posthog.isFeatureEnabled('beta-feature2', 'some-distinct-id')).toEqual(undefined)
576
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
577
- })
578
-
579
- it('experience continuity flags are not evaluated locally', async () => {
580
- const flags = {
581
- flags: [
582
- {
583
- id: 1,
584
- name: 'Beta Feature',
585
- key: 'beta-feature',
586
- active: true,
587
- ensure_experience_continuity: true,
588
- filters: {
589
- groups: [
590
- {
591
- properties: [],
592
- rollout_percentage: 0,
593
- },
594
- ],
595
- },
596
- },
597
- ],
598
- }
599
- mockedFetch.mockImplementation(
600
- apiImplementation({ localFlags: flags, decideFlags: { 'beta-feature': 'decide-fallback-value' } })
601
- )
602
-
603
- posthog = new PostHog('TEST_API_KEY', {
604
- host: 'http://example.com',
605
- personalApiKey: 'TEST_PERSONAL_API_KEY',
606
- ...posthogImmediateResolveOptions,
607
- })
608
-
609
- // # beta-feature2 falls back to decide, which on error returns default
610
- expect(await posthog.getFeatureFlag('beta-feature', 'some-distinct-id')).toEqual('decide-fallback-value')
611
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
612
- })
613
-
614
- it('get all flags with fallback', async () => {
615
- const flags = {
616
- flags: [
617
- {
618
- id: 1,
619
- name: 'Beta Feature',
620
- key: 'beta-feature',
621
- active: true,
622
- rollout_percentage: 100,
623
- filters: {
624
- groups: [
625
- {
626
- properties: [],
627
- rollout_percentage: 100,
628
- },
629
- ],
630
- },
631
- },
632
- {
633
- id: 2,
634
- name: 'Beta Feature',
635
- key: 'disabled-feature',
636
- active: true,
637
- filters: {
638
- groups: [
639
- {
640
- properties: [],
641
- rollout_percentage: 0,
642
- },
643
- ],
644
- },
645
- },
646
- {
647
- id: 3,
648
- name: 'Beta Feature',
649
- key: 'beta-feature2',
650
- active: true,
651
- filters: {
652
- groups: [
653
- {
654
- properties: [{ key: 'country', value: 'US' }],
655
- rollout_percentage: 0,
656
- },
657
- ],
658
- },
659
- },
660
- ],
661
- }
662
- mockedFetch.mockImplementation(
663
- apiImplementation({
664
- localFlags: flags,
665
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
666
- })
667
- )
668
-
669
- posthog = new PostHog('TEST_API_KEY', {
670
- host: 'http://example.com',
671
- personalApiKey: 'TEST_PERSONAL_API_KEY',
672
- ...posthogImmediateResolveOptions,
673
- })
674
-
675
- // # beta-feature value overridden by /flags
676
- expect(await posthog.getAllFlags('distinct-id')).toEqual({
677
- 'beta-feature': 'variant-1',
678
- 'beta-feature2': 'variant-2',
679
- 'disabled-feature': false,
680
- })
681
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
682
- mockedFetch.mockClear()
683
- })
684
-
685
- it('get all payloads with fallback', async () => {
686
- const flags = {
687
- flags: [
688
- {
689
- id: 1,
690
- name: 'Beta Feature',
691
- key: 'beta-feature',
692
- active: true,
693
- rollout_percentage: 100,
694
- filters: {
695
- groups: [
696
- {
697
- properties: [],
698
- rollout_percentage: 100,
699
- },
700
- ],
701
- payloads: {
702
- true: 'some-payload',
703
- },
704
- },
705
- },
706
- {
707
- id: 2,
708
- name: 'Beta Feature',
709
- key: 'disabled-feature',
710
- active: true,
711
- filters: {
712
- groups: [
713
- {
714
- properties: [],
715
- rollout_percentage: 0,
716
- },
717
- ],
718
- payloads: {
719
- true: 'another-payload',
720
- },
721
- },
722
- },
723
- {
724
- id: 3,
725
- name: 'Beta Feature',
726
- key: 'beta-feature2',
727
- active: true,
728
- filters: {
729
- groups: [
730
- {
731
- properties: [{ key: 'country', value: 'US' }],
732
- rollout_percentage: 0,
733
- },
734
- ],
735
- payloads: {
736
- true: 'payload-3',
737
- },
738
- },
739
- },
740
- ],
741
- }
742
- mockedFetch.mockImplementation(
743
- apiImplementation({
744
- localFlags: flags,
745
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
746
- decideFlagPayloads: { 'beta-feature': 100, 'beta-feature2': 300 },
747
- })
748
- )
749
-
750
- posthog = new PostHog('TEST_API_KEY', {
751
- host: 'http://example.com',
752
- personalApiKey: 'TEST_PERSONAL_API_KEY',
753
- ...posthogImmediateResolveOptions,
754
- })
755
-
756
- // # beta-feature value overridden by /flags
757
- expect((await posthog.getAllFlagsAndPayloads('distinct-id')).featureFlagPayloads).toEqual({
758
- 'beta-feature': 100,
759
- 'beta-feature2': 300,
760
- })
761
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
762
- mockedFetch.mockClear()
763
- })
764
-
765
- it('get all flags with fallback but only_locally_evaluated set', async () => {
766
- const flags = {
767
- flags: [
768
- {
769
- id: 1,
770
- name: 'Beta Feature',
771
- key: 'beta-feature',
772
- active: true,
773
- rollout_percentage: 100,
774
- filters: {
775
- groups: [
776
- {
777
- properties: [],
778
- rollout_percentage: 100,
779
- },
780
- ],
781
- },
782
- },
783
- {
784
- id: 2,
785
- name: 'Beta Feature',
786
- key: 'disabled-feature',
787
- active: true,
788
- filters: {
789
- groups: [
790
- {
791
- properties: [],
792
- rollout_percentage: 0,
793
- },
794
- ],
795
- },
796
- },
797
- {
798
- id: 3,
799
- name: 'Beta Feature',
800
- key: 'beta-feature2',
801
- active: true,
802
- filters: {
803
- groups: [
804
- {
805
- properties: [{ key: 'country', value: 'US' }],
806
- rollout_percentage: 0,
807
- },
808
- ],
809
- },
810
- },
811
- ],
812
- }
813
- mockedFetch.mockImplementation(
814
- apiImplementation({
815
- localFlags: flags,
816
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
817
- })
818
- )
819
-
820
- posthog = new PostHog('TEST_API_KEY', {
821
- host: 'http://example.com',
822
- personalApiKey: 'TEST_PERSONAL_API_KEY',
823
- ...posthogImmediateResolveOptions,
824
- })
825
-
826
- // # beta-feature2 has no value
827
- expect(await posthog.getAllFlags('distinct-id', { onlyEvaluateLocally: true })).toEqual({
828
- 'beta-feature': true,
829
- 'disabled-feature': false,
830
- })
831
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
832
- })
833
-
834
- it('get all payloads with fallback but only_evaluate_locally set', async () => {
835
- const flags = {
836
- flags: [
837
- {
838
- id: 1,
839
- name: 'Beta Feature',
840
- key: 'beta-feature',
841
- active: true,
842
- rollout_percentage: 100,
843
- filters: {
844
- groups: [
845
- {
846
- properties: [],
847
- rollout_percentage: 100,
848
- },
849
- ],
850
- payloads: {
851
- true: 'some-payload',
852
- },
853
- },
854
- },
855
- {
856
- id: 2,
857
- name: 'Beta Feature',
858
- key: 'disabled-feature',
859
- active: true,
860
- filters: {
861
- groups: [
862
- {
863
- properties: [],
864
- rollout_percentage: 0,
865
- },
866
- ],
867
- payloads: {
868
- true: 'another-payload',
869
- },
870
- },
871
- },
872
- {
873
- id: 3,
874
- name: 'Beta Feature',
875
- key: 'beta-feature2',
876
- active: true,
877
- filters: {
878
- groups: [
879
- {
880
- properties: [{ key: 'country', value: 'US' }],
881
- rollout_percentage: 0,
882
- },
883
- ],
884
- payloads: {
885
- true: 'payload-3',
886
- },
887
- },
888
- },
889
- ],
890
- }
891
- mockedFetch.mockImplementation(
892
- apiImplementation({
893
- localFlags: flags,
894
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
895
- decideFlagPayloads: { 'beta-feature': 100, 'beta-feature2': 300 },
896
- })
897
- )
898
-
899
- posthog = new PostHog('TEST_API_KEY', {
900
- host: 'http://example.com',
901
- personalApiKey: 'TEST_PERSONAL_API_KEY',
902
- ...posthogImmediateResolveOptions,
903
- })
904
-
905
- expect(
906
- (await posthog.getAllFlagsAndPayloads('distinct-id', { onlyEvaluateLocally: true })).featureFlagPayloads
907
- ).toEqual({
908
- 'beta-feature': 'some-payload',
909
- })
910
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
911
- })
912
-
913
- it('get all flags with fallback, with no local flags', async () => {
914
- const flags = {
915
- flags: [],
916
- }
917
- mockedFetch.mockImplementation(
918
- apiImplementation({
919
- localFlags: flags,
920
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
921
- })
922
- )
923
-
924
- posthog = new PostHog('TEST_API_KEY', {
925
- host: 'http://example.com',
926
- personalApiKey: 'TEST_PERSONAL_API_KEY',
927
- ...posthogImmediateResolveOptions,
928
- })
929
-
930
- expect(await posthog.getAllFlags('distinct-id')).toEqual({
931
- 'beta-feature': 'variant-1',
932
- 'beta-feature2': 'variant-2',
933
- })
934
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
935
- mockedFetch.mockClear()
936
- })
937
-
938
- it('get all payloads with fallback, with no local payloads', async () => {
939
- const flags = {
940
- flags: [],
941
- }
942
- mockedFetch.mockImplementation(
943
- apiImplementation({
944
- localFlags: flags,
945
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
946
- decideFlagPayloads: { 'beta-feature': 100, 'beta-feature2': 300 },
947
- })
948
- )
949
-
950
- posthog = new PostHog('TEST_API_KEY', {
951
- host: 'http://example.com',
952
- personalApiKey: 'TEST_PERSONAL_API_KEY',
953
- ...posthogImmediateResolveOptions,
954
- })
955
-
956
- expect((await posthog.getAllFlagsAndPayloads('distinct-id')).featureFlagPayloads).toEqual({
957
- 'beta-feature': 100,
958
- 'beta-feature2': 300,
959
- })
960
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
961
- mockedFetch.mockClear()
962
- })
963
-
964
- it('get all flags with no fallback', async () => {
965
- const flags = {
966
- flags: [
967
- {
968
- id: 1,
969
- name: 'Beta Feature',
970
- key: 'beta-feature',
971
- active: true,
972
- rollout_percentage: 100,
973
- filters: {
974
- groups: [
975
- {
976
- properties: [],
977
- rollout_percentage: 100,
978
- },
979
- ],
980
- },
981
- },
982
- {
983
- id: 2,
984
- name: 'Beta Feature',
985
- key: 'disabled-feature',
986
- active: true,
987
- filters: {
988
- groups: [
989
- {
990
- properties: [],
991
- rollout_percentage: 0,
992
- },
993
- ],
994
- },
995
- },
996
- ],
997
- }
998
- mockedFetch.mockImplementation(
999
- apiImplementation({
1000
- localFlags: flags,
1001
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
1002
- })
1003
- )
1004
-
1005
- posthog = new PostHog('TEST_API_KEY', {
1006
- host: 'http://example.com',
1007
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1008
- ...posthogImmediateResolveOptions,
1009
- })
1010
-
1011
- expect(await posthog.getAllFlags('distinct-id')).toEqual({ 'beta-feature': true, 'disabled-feature': false })
1012
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1013
- })
1014
-
1015
- it('get all payloads with no fallback', async () => {
1016
- const flags = {
1017
- flags: [
1018
- {
1019
- id: 1,
1020
- name: 'Beta Feature',
1021
- key: 'beta-feature',
1022
- active: true,
1023
- rollout_percentage: 100,
1024
- filters: {
1025
- groups: [
1026
- {
1027
- properties: [],
1028
- rollout_percentage: 100,
1029
- },
1030
- ],
1031
- payloads: {
1032
- true: 'new',
1033
- },
1034
- },
1035
- },
1036
- {
1037
- id: 2,
1038
- name: 'Beta Feature',
1039
- key: 'disabled-feature',
1040
- active: true,
1041
- filters: {
1042
- groups: [
1043
- {
1044
- properties: [],
1045
- rollout_percentage: 0,
1046
- },
1047
- ],
1048
- payloads: {
1049
- true: 'some-payload',
1050
- },
1051
- },
1052
- },
1053
- ],
1054
- }
1055
- mockedFetch.mockImplementation(
1056
- apiImplementation({
1057
- localFlags: flags,
1058
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
1059
- })
1060
- )
1061
-
1062
- posthog = new PostHog('TEST_API_KEY', {
1063
- host: 'http://example.com',
1064
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1065
- ...posthogImmediateResolveOptions,
1066
- })
1067
-
1068
- expect((await posthog.getAllFlagsAndPayloads('distinct-id')).featureFlagPayloads).toEqual({ 'beta-feature': 'new' })
1069
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1070
- })
1071
-
1072
- it('computes inactive flags locally as well', async () => {
1073
- const flags = {
1074
- flags: [
1075
- {
1076
- id: 1,
1077
- name: 'Beta Feature',
1078
- key: 'beta-feature',
1079
- active: true,
1080
- rollout_percentage: 100,
1081
- filters: {
1082
- groups: [
1083
- {
1084
- properties: [],
1085
- rollout_percentage: 100,
1086
- },
1087
- ],
1088
- },
1089
- },
1090
- {
1091
- id: 2,
1092
- name: 'Beta Feature',
1093
- key: 'disabled-feature',
1094
- active: true,
1095
- filters: {
1096
- groups: [
1097
- {
1098
- properties: [],
1099
- rollout_percentage: 0,
1100
- },
1101
- ],
1102
- },
1103
- },
1104
- ],
1105
- }
1106
- mockedFetch.mockImplementation(
1107
- apiImplementation({
1108
- localFlags: flags,
1109
- decideFlags: { 'beta-feature': 'variant-1', 'beta-feature2': 'variant-2' },
1110
- })
1111
- )
1112
-
1113
- posthog = new PostHog('TEST_API_KEY', {
1114
- host: 'http://example.com',
1115
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1116
- ...posthogImmediateResolveOptions,
1117
- })
1118
-
1119
- expect(await posthog.getAllFlags('distinct-id')).toEqual({ 'beta-feature': true, 'disabled-feature': false })
1120
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1121
-
1122
- // # Now, after a poll interval, flag 1 is inactive, and flag 2 rollout is set to 100%.
1123
- const flags2 = {
1124
- flags: [
1125
- {
1126
- id: 1,
1127
- name: 'Beta Feature',
1128
- key: 'beta-feature',
1129
- active: false,
1130
- rollout_percentage: 100,
1131
- filters: {
1132
- groups: [
1133
- {
1134
- properties: [],
1135
- rollout_percentage: 100,
1136
- },
1137
- ],
1138
- },
1139
- },
1140
- {
1141
- id: 2,
1142
- name: 'Beta Feature',
1143
- key: 'disabled-feature',
1144
- active: true,
1145
- filters: {
1146
- groups: [
1147
- {
1148
- properties: [],
1149
- rollout_percentage: 100,
1150
- },
1151
- ],
1152
- },
1153
- },
1154
- ],
1155
- }
1156
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags2 }))
1157
-
1158
- // # force reload to simulate poll interval
1159
- await posthog.reloadFeatureFlags()
1160
-
1161
- expect(await posthog.getAllFlags('distinct-id')).toEqual({ 'beta-feature': false, 'disabled-feature': true })
1162
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1163
- })
1164
-
1165
- it('computes complex cohorts locally', async () => {
1166
- const flags = {
1167
- flags: [
1168
- {
1169
- id: 1,
1170
- name: 'Beta Feature',
1171
- key: 'beta-feature',
1172
- active: true,
1173
- rollout_percentage: 100,
1174
- filters: {
1175
- groups: [
1176
- {
1177
- properties: [
1178
- {
1179
- key: 'region',
1180
- operator: 'exact',
1181
- value: ['USA'],
1182
- type: 'person',
1183
- },
1184
- { key: 'id', value: 98, type: 'cohort' },
1185
- ],
1186
- rollout_percentage: 100,
1187
- },
1188
- ],
1189
- },
1190
- },
1191
- ],
1192
- cohorts: {
1193
- '98': {
1194
- type: 'OR',
1195
- values: [
1196
- { key: 'id', value: 1, type: 'cohort' },
1197
- {
1198
- key: 'nation',
1199
- operator: 'exact',
1200
- value: ['UK'],
1201
- type: 'person',
1202
- },
1203
- ],
1204
- },
1205
- '1': {
1206
- type: 'AND',
1207
- values: [{ key: 'other', operator: 'exact', value: ['thing'], type: 'person' }],
1208
- },
1209
- },
1210
- }
1211
- mockedFetch.mockImplementation(
1212
- apiImplementation({
1213
- localFlags: flags,
1214
- decideFlags: {},
1215
- })
1216
- )
1217
-
1218
- posthog = new PostHog('TEST_API_KEY', {
1219
- host: 'http://example.com',
1220
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1221
- ...posthogImmediateResolveOptions,
1222
- })
1223
-
1224
- expect(
1225
- await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', { personProperties: { region: 'UK' } })
1226
- ).toEqual(false)
1227
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1228
-
1229
- // # even though 'other' property is not present, the cohort should still match since it's an OR condition
1230
- expect(
1231
- await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
1232
- personProperties: { region: 'USA', nation: 'UK' },
1233
- })
1234
- ).toEqual(true)
1235
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1236
-
1237
- // # even though 'other' property is not present, the cohort should still match since it's an OR condition
1238
- expect(
1239
- await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
1240
- personProperties: { region: 'USA', other: 'thing' },
1241
- })
1242
- ).toEqual(true)
1243
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1244
- })
1245
-
1246
- it('computes complex cohorts with negation locally', async () => {
1247
- const flags = {
1248
- flags: [
1249
- {
1250
- id: 1,
1251
- name: 'Beta Feature',
1252
- key: 'beta-feature',
1253
- active: true,
1254
- rollout_percentage: 100,
1255
- filters: {
1256
- groups: [
1257
- {
1258
- properties: [
1259
- {
1260
- key: 'region',
1261
- operator: 'exact',
1262
- value: ['USA'],
1263
- type: 'person',
1264
- },
1265
- { key: 'id', value: 98, type: 'cohort' },
1266
- ],
1267
- rollout_percentage: 100,
1268
- },
1269
- ],
1270
- },
1271
- },
1272
- ],
1273
- cohorts: {
1274
- '98': {
1275
- type: 'OR',
1276
- values: [
1277
- { key: 'id', value: 1, type: 'cohort' },
1278
- {
1279
- key: 'nation',
1280
- operator: 'exact',
1281
- value: ['UK'],
1282
- type: 'person',
1283
- },
1284
- ],
1285
- },
1286
- '1': {
1287
- type: 'AND',
1288
- values: [{ key: 'other', operator: 'exact', value: ['thing'], type: 'person', negation: true }],
1289
- },
1290
- },
1291
- }
1292
- mockedFetch.mockImplementation(
1293
- apiImplementation({
1294
- localFlags: flags,
1295
- decideFlags: {},
1296
- })
1297
- )
1298
-
1299
- posthog = new PostHog('TEST_API_KEY', {
1300
- host: 'http://example.com',
1301
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1302
- ...posthogImmediateResolveOptions,
1303
- })
1304
-
1305
- expect(
1306
- await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', { personProperties: { region: 'UK' } })
1307
- ).toEqual(false)
1308
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1309
-
1310
- // # even though 'other' property is not present, the cohort should still match since it's an OR condition
1311
- expect(
1312
- await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
1313
- personProperties: { region: 'USA', nation: 'UK' },
1314
- })
1315
- ).toEqual(true)
1316
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1317
-
1318
- // # since 'other' is negated, we return False. Since 'nation' is not present, we can't tell whether the flag should be true or false, so go to decide
1319
- expect(
1320
- await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
1321
- personProperties: { region: 'USA', other: 'thing' },
1322
- })
1323
- ).toEqual(undefined)
1324
- expect(mockedFetch).toHaveBeenCalledWith(...anyDecideCall)
1325
-
1326
- mockedFetch.mockClear()
1327
-
1328
- expect(
1329
- await posthog.getFeatureFlag('beta-feature', 'some-distinct-id', {
1330
- personProperties: { region: 'USA', other: 'thing2' },
1331
- })
1332
- ).toEqual(true)
1333
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1334
- })
1335
-
1336
- it('gets feature flag with variant overrides', async () => {
1337
- const flags = {
1338
- flags: [
1339
- {
1340
- id: 1,
1341
- name: 'Beta Feature',
1342
- key: 'beta-feature',
1343
- active: true,
1344
- filters: {
1345
- groups: [
1346
- {
1347
- properties: [
1348
- {
1349
- key: 'email',
1350
- operator: 'exact',
1351
- value: 'test@posthog.com',
1352
- type: 'person',
1353
- },
1354
- ],
1355
- rollout_percentage: 100,
1356
- variant: 'second-variant',
1357
- },
1358
- {
1359
- rollout_percentage: 50,
1360
- variant: 'first-variant',
1361
- },
1362
- ],
1363
- multivariate: {
1364
- variants: [
1365
- {
1366
- key: 'first-variant',
1367
- name: 'First Variant',
1368
- rollout_percentage: 50,
1369
- },
1370
- {
1371
- key: 'second-variant',
1372
- name: 'Second Variant',
1373
- rollout_percentage: 25,
1374
- },
1375
- {
1376
- key: 'third-variant',
1377
- name: 'Third Variant',
1378
- rollout_percentage: 25,
1379
- },
1380
- ],
1381
- },
1382
- },
1383
- },
1384
- ],
1385
- }
1386
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1387
-
1388
- posthog = new PostHog('TEST_API_KEY', {
1389
- host: 'http://example.com',
1390
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1391
- ...posthogImmediateResolveOptions,
1392
- })
1393
-
1394
- expect(
1395
- await posthog.getFeatureFlag('beta-feature', 'test_id', { personProperties: { email: 'test@posthog.com' } })
1396
- ).toEqual('second-variant')
1397
- expect(await posthog.getFeatureFlag('beta-feature', 'example_id')).toEqual('first-variant')
1398
-
1399
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
1400
- // decide not called
1401
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1402
- })
1403
-
1404
- it('gets feature flag with clashing variant overrides', async () => {
1405
- const flags = {
1406
- flags: [
1407
- {
1408
- id: 1,
1409
- name: 'Beta Feature',
1410
- key: 'beta-feature',
1411
- active: true,
1412
- filters: {
1413
- groups: [
1414
- {
1415
- properties: [
1416
- {
1417
- key: 'email',
1418
- operator: 'exact',
1419
- value: 'test@posthog.com',
1420
- type: 'person',
1421
- },
1422
- ],
1423
- rollout_percentage: 100,
1424
- variant: 'second-variant',
1425
- },
1426
- // # since second-variant comes first in the list, it will be the one that gets picked
1427
- {
1428
- properties: [
1429
- {
1430
- key: 'email',
1431
- operator: 'exact',
1432
- value: 'test@posthog.com',
1433
- type: 'person',
1434
- },
1435
- ],
1436
- rollout_percentage: 100,
1437
- variant: 'first-variant',
1438
- },
1439
- {
1440
- rollout_percentage: 50,
1441
- variant: 'first-variant',
1442
- },
1443
- ],
1444
- multivariate: {
1445
- variants: [
1446
- {
1447
- key: 'first-variant',
1448
- name: 'First Variant',
1449
- rollout_percentage: 50,
1450
- },
1451
- {
1452
- key: 'second-variant',
1453
- name: 'Second Variant',
1454
- rollout_percentage: 25,
1455
- },
1456
- {
1457
- key: 'third-variant',
1458
- name: 'Third Variant',
1459
- rollout_percentage: 25,
1460
- },
1461
- ],
1462
- },
1463
- },
1464
- },
1465
- ],
1466
- }
1467
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1468
-
1469
- posthog = new PostHog('TEST_API_KEY', {
1470
- host: 'http://example.com',
1471
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1472
- ...posthogImmediateResolveOptions,
1473
- })
1474
-
1475
- expect(
1476
- await posthog.getFeatureFlag('beta-feature', 'test_id', { personProperties: { email: 'test@posthog.com' } })
1477
- ).toEqual('second-variant')
1478
- expect(
1479
- await posthog.getFeatureFlag('beta-feature', 'example_id', { personProperties: { email: 'test@posthog.com' } })
1480
- ).toEqual('second-variant')
1481
- expect(await posthog.getFeatureFlag('beta-feature', 'example_id')).toEqual('first-variant')
1482
-
1483
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
1484
- // decide not called
1485
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1486
- })
1487
-
1488
- it('gets feature flag with invalid variant overrides', async () => {
1489
- const flags = {
1490
- flags: [
1491
- {
1492
- id: 1,
1493
- name: 'Beta Feature',
1494
- key: 'beta-feature',
1495
- active: true,
1496
- filters: {
1497
- groups: [
1498
- {
1499
- properties: [
1500
- {
1501
- key: 'email',
1502
- operator: 'exact',
1503
- value: 'test@posthog.com',
1504
- type: 'person',
1505
- },
1506
- ],
1507
- rollout_percentage: 100,
1508
- variant: 'second???',
1509
- },
1510
- {
1511
- rollout_percentage: 50,
1512
- variant: 'first???',
1513
- },
1514
- ],
1515
- multivariate: {
1516
- variants: [
1517
- {
1518
- key: 'first-variant',
1519
- name: 'First Variant',
1520
- rollout_percentage: 50,
1521
- },
1522
- {
1523
- key: 'second-variant',
1524
- name: 'Second Variant',
1525
- rollout_percentage: 25,
1526
- },
1527
- {
1528
- key: 'third-variant',
1529
- name: 'Third Variant',
1530
- rollout_percentage: 25,
1531
- },
1532
- ],
1533
- },
1534
- },
1535
- },
1536
- ],
1537
- }
1538
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1539
-
1540
- posthog = new PostHog('TEST_API_KEY', {
1541
- host: 'http://example.com',
1542
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1543
- ...posthogImmediateResolveOptions,
1544
- })
1545
-
1546
- expect(
1547
- await posthog.getFeatureFlag('beta-feature', 'test_id', { personProperties: { email: 'test@posthog.com' } })
1548
- ).toEqual('third-variant')
1549
- expect(await posthog.getFeatureFlag('beta-feature', 'example_id')).toEqual('second-variant')
1550
-
1551
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
1552
- // decide not called
1553
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1554
- })
1555
-
1556
- it('gets feature flag with multiple variant overrides', async () => {
1557
- const flags = {
1558
- flags: [
1559
- {
1560
- id: 1,
1561
- name: 'Beta Feature',
1562
- key: 'beta-feature',
1563
- active: true,
1564
- filters: {
1565
- groups: [
1566
- {
1567
- rollout_percentage: 100,
1568
- // # The override applies even if the first condition matches all and gives everyone their default group
1569
- },
1570
- {
1571
- properties: [
1572
- {
1573
- key: 'email',
1574
- operator: 'exact',
1575
- value: 'test@posthog.com',
1576
- type: 'person',
1577
- },
1578
- ],
1579
- rollout_percentage: 100,
1580
- variant: 'second-variant',
1581
- },
1582
- {
1583
- rollout_percentage: 50,
1584
- variant: 'third-variant',
1585
- },
1586
- ],
1587
- multivariate: {
1588
- variants: [
1589
- {
1590
- key: 'first-variant',
1591
- name: 'First Variant',
1592
- rollout_percentage: 50,
1593
- },
1594
- {
1595
- key: 'second-variant',
1596
- name: 'Second Variant',
1597
- rollout_percentage: 25,
1598
- },
1599
- {
1600
- key: 'third-variant',
1601
- name: 'Third Variant',
1602
- rollout_percentage: 25,
1603
- },
1604
- ],
1605
- },
1606
- },
1607
- },
1608
- ],
1609
- }
1610
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1611
-
1612
- posthog = new PostHog('TEST_API_KEY', {
1613
- host: 'http://example.com',
1614
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1615
- ...posthogImmediateResolveOptions,
1616
- })
1617
-
1618
- expect(
1619
- await posthog.getFeatureFlag('beta-feature', 'test_id', { personProperties: { email: 'test@posthog.com' } })
1620
- ).toEqual('second-variant')
1621
- expect(await posthog.getFeatureFlag('beta-feature', 'example_id')).toEqual('third-variant')
1622
- expect(await posthog.getFeatureFlag('beta-feature', 'another_id')).toEqual('second-variant')
1623
-
1624
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
1625
- // decide not called
1626
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1627
- })
1628
-
1629
- it('get feature flag payload based on boolean flag', async () => {
1630
- const flags = {
1631
- flags: [
1632
- {
1633
- id: 1,
1634
- name: 'Beta Feature',
1635
- key: 'person-flag',
1636
- active: true,
1637
- filters: {
1638
- groups: [
1639
- {
1640
- properties: [
1641
- {
1642
- key: 'region',
1643
- operator: 'exact',
1644
- value: ['USA'],
1645
- type: 'person',
1646
- },
1647
- ],
1648
- rollout_percentage: null,
1649
- },
1650
- ],
1651
- payloads: {
1652
- true: {
1653
- log: 'all',
1654
- },
1655
- },
1656
- },
1657
- },
1658
- ],
1659
- }
1660
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1661
-
1662
- posthog = new PostHog('TEST_API_KEY', {
1663
- host: 'http://example.com',
1664
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1665
- ...posthogImmediateResolveOptions,
1666
- })
1667
-
1668
- expect(
1669
- await posthog.getFeatureFlagPayload('person-flag', 'some-distinct-id', true, {
1670
- personProperties: { region: 'USA' },
1671
- })
1672
- ).toEqual({
1673
- log: 'all',
1674
- })
1675
- expect(
1676
- await posthog.getFeatureFlagPayload('person-flag', 'some-distinct-id', undefined, {
1677
- personProperties: { region: 'USA' },
1678
- })
1679
- ).toEqual({
1680
- log: 'all',
1681
- })
1682
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
1683
- // decide not called
1684
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1685
- })
1686
-
1687
- it('get feature flag payload on a multivariate', async () => {
1688
- const flags = {
1689
- flags: [
1690
- {
1691
- id: 1,
1692
- name: 'Beta Feature',
1693
- key: 'beta-feature',
1694
- active: true,
1695
- filters: {
1696
- groups: [
1697
- {
1698
- properties: [
1699
- {
1700
- key: 'email',
1701
- operator: 'exact',
1702
- value: 'test@posthog.com',
1703
- type: 'person',
1704
- },
1705
- ],
1706
- rollout_percentage: 100,
1707
- variant: 'second-variant',
1708
- },
1709
- {
1710
- rollout_percentage: 50,
1711
- variant: 'first-variant',
1712
- },
1713
- ],
1714
- multivariate: {
1715
- variants: [
1716
- {
1717
- key: 'first-variant',
1718
- name: 'First Variant',
1719
- rollout_percentage: 50,
1720
- },
1721
- {
1722
- key: 'second-variant',
1723
- name: 'Second Variant',
1724
- rollout_percentage: 25,
1725
- },
1726
- {
1727
- key: 'third-variant',
1728
- name: 'Third Variant',
1729
- rollout_percentage: 25,
1730
- },
1731
- ],
1732
- },
1733
- payloads: {
1734
- 'second-variant': 2500,
1735
- },
1736
- },
1737
- },
1738
- ],
1739
- }
1740
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1741
-
1742
- posthog = new PostHog('TEST_API_KEY', {
1743
- host: 'http://example.com',
1744
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1745
- ...posthogImmediateResolveOptions,
1746
- })
1747
-
1748
- expect(
1749
- await posthog.getFeatureFlagPayload('beta-feature', 'test_id', 'second-variant', {
1750
- personProperties: { email: 'test@posthog.com' },
1751
- })
1752
- ).toEqual(2500)
1753
-
1754
- expect(mockedFetch).toHaveBeenCalledWith(...anyLocalEvalCall)
1755
- // decide not called
1756
- expect(mockedFetch).not.toHaveBeenCalledWith(...anyDecideCall)
1757
- })
1758
-
1759
- describe('isLocalEvaluationReady', () => {
1760
- it('returns false when featureFlagsPoller is undefined', () => {
1761
- posthog = new PostHog('TEST_API_KEY', {
1762
- host: 'http://example.com',
1763
- ...posthogImmediateResolveOptions,
1764
- })
1765
- expect(posthog.isLocalEvaluationReady()).toBe(false)
1766
- })
1767
-
1768
- it('returns false when featureFlagsPoller has not loaded successfully', () => {
1769
- posthog = new PostHog('TEST_API_KEY', {
1770
- host: 'http://example.com',
1771
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1772
- ...posthogImmediateResolveOptions,
1773
- })
1774
- expect(posthog.isLocalEvaluationReady()).toBe(false)
1775
- })
1776
-
1777
- it('returns false when featureFlagsPoller has no flags', async () => {
1778
- const flags = { flags: [] }
1779
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1780
- posthog = new PostHog('TEST_API_KEY', {
1781
- host: 'http://example.com',
1782
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1783
- ...posthogImmediateResolveOptions,
1784
- })
1785
- await posthog.reloadFeatureFlags()
1786
- expect(posthog.isLocalEvaluationReady()).toBe(false)
1787
- })
1788
-
1789
- it('returns true when featureFlagsPoller has loaded flags successfully', async () => {
1790
- const flags = {
1791
- flags: [
1792
- {
1793
- id: 1,
1794
- name: 'Beta Feature',
1795
- key: 'beta-feature',
1796
- active: true,
1797
- filters: {
1798
- groups: [
1799
- {
1800
- properties: [],
1801
- rollout_percentage: 100,
1802
- },
1803
- ],
1804
- },
1805
- },
1806
- ],
1807
- }
1808
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1809
- posthog = new PostHog('TEST_API_KEY', {
1810
- host: 'http://example.com',
1811
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1812
- ...posthogImmediateResolveOptions,
1813
- })
1814
- await posthog.reloadFeatureFlags()
1815
- expect(posthog.isLocalEvaluationReady()).toBe(true)
1816
- })
1817
- })
1818
-
1819
- describe('waitForLocalEvaluationReady', () => {
1820
- it('returns true when local evaluation is ready', async () => {
1821
- const flags = {
1822
- flags: [
1823
- {
1824
- id: 1,
1825
- name: 'Beta Feature',
1826
- key: 'beta-feature',
1827
- active: true,
1828
- filters: {
1829
- groups: [{ properties: [], rollout_percentage: 100 }],
1830
- },
1831
- },
1832
- ],
1833
- }
1834
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1835
- posthog = new PostHog('TEST_API_KEY', {
1836
- host: 'http://example.com',
1837
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1838
- ...posthogImmediateResolveOptions,
1839
- })
1840
-
1841
- expect(await posthog.waitForLocalEvaluationReady()).toBe(true)
1842
- })
1843
-
1844
- it('returns false when local evaluation endpoint returns empty flags', async () => {
1845
- const flags = { flags: [] }
1846
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1847
- posthog = new PostHog('TEST_API_KEY', {
1848
- host: 'http://example.com',
1849
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1850
- ...posthogImmediateResolveOptions,
1851
- })
1852
- expect(await posthog.waitForLocalEvaluationReady()).toBe(false)
1853
- })
1854
-
1855
- it('returns false when local evaluation is not enabled', async () => {
1856
- const flags = { flags: [] }
1857
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1858
- posthog = new PostHog('TEST_API_KEY', {
1859
- host: 'http://example.com',
1860
- personalApiKey: undefined,
1861
- ...posthogImmediateResolveOptions,
1862
- })
1863
- expect(await posthog.waitForLocalEvaluationReady()).toBe(false)
1864
- })
1865
- })
1866
-
1867
- it('emits localEvaluationFlagsLoaded event when flags are loaded', async () => {
1868
- const flags = {
1869
- flags: [
1870
- {
1871
- id: 1,
1872
- name: 'Beta Feature',
1873
- key: 'beta-feature',
1874
- active: true,
1875
- filters: {
1876
- groups: [
1877
- {
1878
- properties: [],
1879
- rollout_percentage: 100,
1880
- },
1881
- ],
1882
- },
1883
- },
1884
- {
1885
- id: 2,
1886
- name: 'Alpha Feature',
1887
- key: 'alpha-feature',
1888
- active: true,
1889
- filters: {
1890
- groups: [
1891
- {
1892
- properties: [],
1893
- rollout_percentage: 50,
1894
- },
1895
- ],
1896
- },
1897
- },
1898
- ],
1899
- }
1900
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1901
-
1902
- posthog = new PostHog('TEST_API_KEY', {
1903
- host: 'http://example.com',
1904
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1905
- ...posthogImmediateResolveOptions,
1906
- })
1907
-
1908
- const eventHandler = jest.fn()
1909
- posthog.on('localEvaluationFlagsLoaded', eventHandler)
1910
-
1911
- // Wait for initial load
1912
- await waitForPromises()
1913
-
1914
- expect(eventHandler).toHaveBeenCalledWith(2) // Should be called with number of flags loaded
1915
- })
1916
-
1917
- it('does not emit localEvaluationFlagsLoaded event when loading fails', async () => {
1918
- mockedFetch.mockImplementation(() => {
1919
- throw new Error('Failed to load flags')
1920
- })
1921
-
1922
- posthog = new PostHog('TEST_API_KEY', {
1923
- host: 'http://example.com',
1924
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1925
- ...posthogImmediateResolveOptions,
1926
- })
1927
-
1928
- const eventHandler = jest.fn()
1929
- posthog.on('localEvaluationFlagsLoaded', eventHandler)
1930
-
1931
- // Wait for initial load
1932
- await waitForPromises()
1933
-
1934
- expect(eventHandler).not.toHaveBeenCalled()
1935
- })
1936
-
1937
- it('emits localEvaluationFlagsLoaded event on reload', async () => {
1938
- const flags = {
1939
- flags: [
1940
- {
1941
- id: 1,
1942
- name: 'Beta Feature',
1943
- key: 'beta-feature',
1944
- active: true,
1945
- filters: {
1946
- groups: [
1947
- {
1948
- properties: [],
1949
- rollout_percentage: 100,
1950
- },
1951
- ],
1952
- },
1953
- },
1954
- ],
1955
- }
1956
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
1957
-
1958
- posthog = new PostHog('TEST_API_KEY', {
1959
- host: 'http://example.com',
1960
- personalApiKey: 'TEST_PERSONAL_API_KEY',
1961
- ...posthogImmediateResolveOptions,
1962
- })
1963
-
1964
- const eventHandler = jest.fn()
1965
- posthog.on('localEvaluationFlagsLoaded', eventHandler)
1966
-
1967
- // Wait for initial load
1968
- await waitForPromises()
1969
- eventHandler.mockClear() // Clear initial call
1970
-
1971
- // Reload flags
1972
- await posthog.reloadFeatureFlags()
1973
-
1974
- expect(eventHandler).toHaveBeenCalledWith(1) // Should be called with number of flags loaded
1975
- })
1976
- })
1977
-
1978
- describe('getFeatureFlag', () => {
1979
- it('should capture $feature_flag_called when called, but not add all cached flags', async () => {
1980
- const flags = {
1981
- flags: [
1982
- {
1983
- id: 1,
1984
- name: 'Beta Feature',
1985
- key: 'complex-flag',
1986
- active: true,
1987
- filters: {
1988
- groups: [
1989
- {
1990
- variant: null,
1991
- properties: [{ key: 'region', type: 'person', value: 'USA', operator: 'exact' }],
1992
- rollout_percentage: 100,
1993
- },
1994
- ],
1995
- },
1996
- },
1997
- {
1998
- id: 2,
1999
- name: 'Gamma Feature',
2000
- key: 'simple-flag',
2001
- active: true,
2002
- filters: {
2003
- groups: [
2004
- {
2005
- variant: null,
2006
- properties: [],
2007
- rollout_percentage: 100,
2008
- },
2009
- ],
2010
- },
2011
- },
2012
- ],
2013
- }
2014
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags }))
2015
- const posthog = new PostHog('TEST_API_KEY', {
2016
- host: 'http://example.com',
2017
- personalApiKey: 'TEST_PERSONAL_API_KEY',
2018
- ...posthogImmediateResolveOptions,
2019
- })
2020
- let capturedMessage: any
2021
- posthog.on('capture', (message) => {
2022
- capturedMessage = message
2023
- })
2024
-
2025
- expect(
2026
- await posthog.getFeatureFlag('complex-flag', 'some-distinct-id', {
2027
- personProperties: {
2028
- region: 'USA',
2029
- } as unknown as Record<string, string>,
2030
- })
2031
- ).toEqual(true)
2032
-
2033
- await waitForPromises()
2034
-
2035
- expect(capturedMessage).toMatchObject({
2036
- distinct_id: 'some-distinct-id',
2037
- event: '$feature_flag_called',
2038
- library: posthog.getLibraryId(),
2039
- library_version: posthog.getLibraryVersion(),
2040
- properties: {
2041
- '$feature/complex-flag': true,
2042
- $feature_flag: 'complex-flag',
2043
- $feature_flag_response: true,
2044
- $groups: undefined,
2045
- $lib: posthog.getLibraryId(),
2046
- $lib_version: posthog.getLibraryVersion(),
2047
- locally_evaluated: true,
2048
- },
2049
- })
2050
-
2051
- expect(capturedMessage.properties).not.toHaveProperty('$active_feature_flags')
2052
- expect(capturedMessage.properties).not.toHaveProperty('$feature/simple-flag')
2053
- })
2054
- })
2055
-
2056
- describe('match properties', () => {
2057
- jest.useFakeTimers()
2058
-
2059
- it('with operator exact', () => {
2060
- const property_a = { key: 'key', value: 'value' }
2061
-
2062
- expect(matchProperty(property_a, { key: 'value' })).toBe(true)
2063
-
2064
- expect(matchProperty(property_a, { key: 'value2' })).toBe(false)
2065
- expect(matchProperty(property_a, { key: '' })).toBe(false)
2066
- expect(matchProperty(property_a, { key: undefined })).toBe(false)
2067
-
2068
- expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
2069
- expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
2070
-
2071
- const property_b = { key: 'key', value: 'value', operator: 'exact' }
2072
-
2073
- expect(matchProperty(property_b, { key: 'value' })).toBe(true)
2074
- expect(matchProperty(property_b, { key: 'value2' })).toBe(false)
2075
-
2076
- const property_c = { key: 'key', value: ['value1', 'value2', 'value3'], operator: 'exact' }
2077
- expect(matchProperty(property_c, { key: 'value1' })).toBe(true)
2078
- expect(matchProperty(property_c, { key: 'value2' })).toBe(true)
2079
- expect(matchProperty(property_c, { key: 'value3' })).toBe(true)
2080
-
2081
- expect(matchProperty(property_c, { key: 'value4' })).toBe(false)
2082
-
2083
- expect(() => matchProperty(property_c, { key2: 'value' })).toThrow(InconclusiveMatchError)
2084
- })
2085
-
2086
- it('with operator is_not', () => {
2087
- const property_a = { key: 'key', value: 'value', operator: 'is_not' }
2088
-
2089
- expect(matchProperty(property_a, { key: 'value' })).toBe(false)
2090
- expect(matchProperty(property_a, { key: 'value2' })).toBe(true)
2091
- expect(matchProperty(property_a, { key: '' })).toBe(true)
2092
- expect(matchProperty(property_a, { key: undefined })).toBe(true)
2093
-
2094
- expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
2095
- expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
2096
-
2097
- const property_c = { key: 'key', value: ['value1', 'value2', 'value3'], operator: 'is_not' }
2098
- expect(matchProperty(property_c, { key: 'value1' })).toBe(false)
2099
- expect(matchProperty(property_c, { key: 'value2' })).toBe(false)
2100
- expect(matchProperty(property_c, { key: 'value3' })).toBe(false)
2101
-
2102
- expect(matchProperty(property_c, { key: 'value4' })).toBe(true)
2103
- expect(matchProperty(property_c, { key: 'value5' })).toBe(true)
2104
- expect(matchProperty(property_c, { key: '' })).toBe(true)
2105
- expect(matchProperty(property_c, { key: undefined })).toBe(true)
2106
-
2107
- expect(() => matchProperty(property_c, { key2: 'value' })).toThrow(InconclusiveMatchError)
2108
- })
2109
-
2110
- it('with operator is_set', () => {
2111
- const property_a = { key: 'key', value: 'is_set', operator: 'is_set' }
2112
-
2113
- expect(matchProperty(property_a, { key: 'value' })).toBe(true)
2114
- expect(matchProperty(property_a, { key: 'value2' })).toBe(true)
2115
- expect(matchProperty(property_a, { key: '' })).toBe(true)
2116
- expect(matchProperty(property_a, { key: undefined })).toBe(false)
2117
-
2118
- expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
2119
- expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
2120
- })
2121
-
2122
- it('with operator icontains', () => {
2123
- const property_a = { key: 'key', value: 'vaLuE', operator: 'icontains' }
2124
-
2125
- expect(matchProperty(property_a, { key: 'value' })).toBe(true)
2126
- expect(matchProperty(property_a, { key: 'value2' })).toBe(true)
2127
- expect(matchProperty(property_a, { key: 'vaLue3' })).toBe(true)
2128
- expect(matchProperty(property_a, { key: '343tfvalUe5' })).toBe(true)
2129
-
2130
- expect(matchProperty(property_a, { key: '' })).toBe(false)
2131
- expect(matchProperty(property_a, { key: undefined })).toBe(false)
2132
- expect(matchProperty(property_a, { key: 1234 })).toBe(false)
2133
- expect(matchProperty(property_a, { key: '1234' })).toBe(false)
2134
-
2135
- expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
2136
- expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
2137
-
2138
- const property_b = { key: 'key', value: '3', operator: 'icontains' }
2139
-
2140
- expect(matchProperty(property_b, { key: '3' })).toBe(true)
2141
- expect(matchProperty(property_b, { key: 323 })).toBe(true)
2142
- expect(matchProperty(property_b, { key: 'val3' })).toBe(true)
2143
-
2144
- expect(matchProperty(property_b, { key: 'three' })).toBe(false)
2145
- })
2146
-
2147
- it('with operator regex', () => {
2148
- const property_a = { key: 'key', value: '\\.com$', operator: 'regex' }
2149
-
2150
- expect(matchProperty(property_a, { key: 'value.com' })).toBe(true)
2151
- expect(matchProperty(property_a, { key: 'value2.com' })).toBe(true)
2152
-
2153
- expect(matchProperty(property_a, { key: 'valuecom' })).toBe(false)
2154
- expect(matchProperty(property_a, { key: 'valuecom' })).toBe(false)
2155
- expect(matchProperty(property_a, { key: '.com343tfvalue5' })).toBe(false)
2156
- expect(matchProperty(property_a, { key: undefined })).toBe(false)
2157
- expect(matchProperty(property_a, { key: '' })).toBe(false)
2158
-
2159
- expect(() => matchProperty(property_a, { key2: 'value' })).toThrow(InconclusiveMatchError)
2160
- expect(() => matchProperty(property_a, {})).toThrow(InconclusiveMatchError)
2161
-
2162
- const property_b = { key: 'key', value: '3', operator: 'regex' }
2163
-
2164
- expect(matchProperty(property_b, { key: '3' })).toBe(true)
2165
- expect(matchProperty(property_b, { key: 323 })).toBe(true)
2166
- expect(matchProperty(property_b, { key: 'val3' })).toBe(true)
2167
-
2168
- expect(matchProperty(property_b, { key: 'three' })).toBe(false)
2169
-
2170
- // # invalid regex
2171
- const property_c = { key: 'key', value: '?*', operator: 'regex' }
2172
- expect(matchProperty(property_c, { key: 'value.com' })).toBe(false)
2173
- expect(matchProperty(property_c, { key: 'value2' })).toBe(false)
2174
-
2175
- // # non string value
2176
- const property_d = { key: 'key', value: 4, operator: 'regex' }
2177
- expect(matchProperty(property_d, { key: '4' })).toBe(true)
2178
- expect(matchProperty(property_d, { key: 4 })).toBe(true)
2179
-
2180
- expect(matchProperty(property_d, { key: 'value' })).toBe(false)
2181
-
2182
- // # non string value - not_regex
2183
- const property_e = { key: 'key', value: 4, operator: 'not_regex' }
2184
- expect(matchProperty(property_e, { key: '4' })).toBe(false)
2185
- expect(matchProperty(property_e, { key: 4 })).toBe(false)
2186
-
2187
- expect(matchProperty(property_e, { key: 'value' })).toBe(true)
2188
- })
2189
-
2190
- it('with math operators', () => {
2191
- const property_a = { key: 'key', value: 1, operator: 'gt' }
2192
-
2193
- expect(matchProperty(property_a, { key: 2 })).toBe(true)
2194
- expect(matchProperty(property_a, { key: 3 })).toBe(true)
2195
-
2196
- expect(matchProperty(property_a, { key: 0 })).toBe(false)
2197
- expect(matchProperty(property_a, { key: -1 })).toBe(false)
2198
- // # now we handle type mismatches so this should be true
2199
- expect(matchProperty(property_a, { key: '23' })).toBe(true)
2200
-
2201
- const property_b = { key: 'key', value: 1, operator: 'lt' }
2202
- expect(matchProperty(property_b, { key: 0 })).toBe(true)
2203
- expect(matchProperty(property_b, { key: -1 })).toBe(true)
2204
- expect(matchProperty(property_b, { key: -3 })).toBe(true)
2205
-
2206
- expect(matchProperty(property_b, { key: '3' })).toBe(false)
2207
- expect(matchProperty(property_b, { key: '1' })).toBe(false)
2208
- expect(matchProperty(property_b, { key: 1 })).toBe(false)
2209
-
2210
- const property_c = { key: 'key', value: 1, operator: 'gte' }
2211
- expect(matchProperty(property_c, { key: 2 })).toBe(true)
2212
- expect(matchProperty(property_c, { key: 1 })).toBe(true)
2213
-
2214
- expect(matchProperty(property_c, { key: 0 })).toBe(false)
2215
- expect(matchProperty(property_c, { key: -1 })).toBe(false)
2216
- expect(matchProperty(property_c, { key: -3 })).toBe(false)
2217
- // # now we handle type mismatches so this should be true
2218
- expect(matchProperty(property_c, { key: '3' })).toBe(true)
2219
-
2220
- const property_d = { key: 'key', value: '43', operator: 'lte' }
2221
- expect(matchProperty(property_d, { key: '43' })).toBe(true)
2222
- expect(matchProperty(property_d, { key: '42' })).toBe(true)
2223
-
2224
- expect(matchProperty(property_d, { key: '44' })).toBe(false)
2225
- expect(matchProperty(property_d, { key: 44 })).toBe(false)
2226
- expect(matchProperty(property_d, { key: 42 })).toBe(true)
2227
-
2228
- const property_e = { key: 'key', value: '30', operator: 'lt' }
2229
- expect(matchProperty(property_e, { key: '29' })).toBe(true)
2230
-
2231
- // # depending on the type of override, we adjust type comparison
2232
- expect(matchProperty(property_e, { key: '100' })).toBe(true)
2233
- expect(matchProperty(property_e, { key: 100 })).toBe(false)
2234
-
2235
- const property_f = { key: 'key', value: '123aloha', operator: 'gt' }
2236
- expect(matchProperty(property_f, { key: '123' })).toBe(false)
2237
- expect(matchProperty(property_f, { key: 122 })).toBe(false)
2238
-
2239
- // # this turns into a string comparison
2240
- expect(matchProperty(property_f, { key: 129 })).toBe(true)
2241
- })
2242
-
2243
- it('with date operators', () => {
2244
- // is date before
2245
- const property_a = { key: 'key', value: '2022-05-01', operator: 'is_date_before' }
2246
- expect(matchProperty(property_a, { key: '2022-03-01' })).toBe(true)
2247
- expect(matchProperty(property_a, { key: '2022-04-30' })).toBe(true)
2248
- expect(matchProperty(property_a, { key: new Date(2022, 3, 30) })).toBe(true)
2249
- expect(matchProperty(property_a, { key: new Date(2022, 3, 30, 1, 2, 3) })).toBe(true)
2250
- expect(matchProperty(property_a, { key: new Date('2022-04-30T00:00:00+02:00') })).toBe(true) // europe/madrid
2251
- expect(matchProperty(property_a, { key: new Date('2022-04-30') })).toBe(true)
2252
- expect(matchProperty(property_a, { key: '2022-05-30' })).toBe(false)
2253
-
2254
- // is date after
2255
- const property_b = { key: 'key', value: '2022-05-01', operator: 'is_date_after' }
2256
- expect(matchProperty(property_b, { key: '2022-05-02' })).toBe(true)
2257
- expect(matchProperty(property_b, { key: '2022-05-30' })).toBe(true)
2258
- expect(matchProperty(property_b, { key: new Date(2022, 4, 30) })).toBe(true)
2259
- expect(matchProperty(property_b, { key: new Date('2022-05-30') })).toBe(true)
2260
- expect(matchProperty(property_b, { key: '2022-04-30' })).toBe(false)
2261
-
2262
- // can't be an invalid number or invalid string
2263
- expect(() => matchProperty(property_a, { key: parseInt('62802180000012345') })).toThrow(InconclusiveMatchError)
2264
- expect(() => matchProperty(property_a, { key: 'abcdef' })).toThrow(InconclusiveMatchError)
2265
- // invalid flag property
2266
- const property_c = { key: 'key', value: 'abcd123', operator: 'is_date_before' }
2267
- expect(() => matchProperty(property_c, { key: '2022-05-30' })).toThrow(InconclusiveMatchError)
2268
-
2269
- // Timezone
2270
- const property_d = { key: 'key', value: '2022-04-05 12:34:12 +01:00', operator: 'is_date_before' }
2271
- expect(matchProperty(property_d, { key: '2022-05-30' })).toBe(false)
2272
-
2273
- expect(matchProperty(property_d, { key: '2022-03-30' })).toBe(true)
2274
- expect(matchProperty(property_d, { key: '2022-04-05 12:34:11+01:00' })).toBe(true)
2275
- expect(matchProperty(property_d, { key: '2022-04-05 11:34:11 +00:00' })).toBe(true)
2276
- expect(matchProperty(property_d, { key: '2022-04-05 11:34:13 +00:00' })).toBe(false)
2277
- })
2278
-
2279
- it.each([
2280
- ['is_date_before', '-6h', '2022-03-01', true],
2281
- ['is_date_before', '-6h', '2022-04-30', true],
2282
- // :TRICKY: MonthIndex is 0 indexed, so 3 is actually the 4th month, April.
2283
- ['is_date_before', '-6h', new Date(Date.UTC(2022, 3, 30, 1, 2, 3)), true],
2284
- // false because date comparison, instead of datetime, so reduces to same date
2285
- ['is_date_before', '-6h', new Date('2022-04-30T01:02:03+02:00'), true], // europe/madrid
2286
- ['is_date_before', '-6h', new Date('2022-04-30T20:02:03+02:00'), false], // europe/madrid
2287
- ['is_date_before', '-6h', new Date('2022-04-30T19:59:03+02:00'), true], // europe/madrid
2288
- ['is_date_before', '-6h', new Date('2022-04-30'), true],
2289
- ['is_date_before', '-6h', '2022-05-30', false],
2290
- // is date after
2291
- ['is_date_after', '1h', '2022-05-02', true],
2292
- ['is_date_after', '1h', '2022-05-30', true],
2293
- ['is_date_after', '1h', new Date(2022, 4, 30), true],
2294
- ['is_date_after', '1h', new Date('2022-05-30'), true],
2295
- ['is_date_after', '1h', '2022-04-30', false],
2296
- // # Try all possible relative dates
2297
- ['is_date_before', '1h', '2022-05-01 00:00:00 GMT', false],
2298
- ['is_date_before', '1h', '2022-04-30 22:00:00 GMT', true],
2299
- ['is_date_before', '-1d', '2022-04-29 23:59:00 GMT', true],
2300
- ['is_date_before', '-1d', '2022-04-30 00:00:01 GMT', false],
2301
- ['is_date_before', '1w', '2022-04-23 00:00:00 GMT', true],
2302
- ['is_date_before', '1w', '2022-04-24 00:00:00 GMT', false],
2303
- ['is_date_before', '1w', '2022-04-24 00:00:01 GMT', false],
2304
- ['is_date_before', '1m', '2022-03-01 00:00:00 GMT', true],
2305
- ['is_date_before', '1m', '2022-04-01 00:00:00 GMT', false],
2306
- ['is_date_before', '1m', '2022-04-05 00:00:01 GMT', false],
2307
-
2308
- ['is_date_before', '-1y', '2021-04-28 00:00:00 GMT', true],
2309
- ['is_date_before', '-1y', '2021-05-01 00:00:01 GMT', false],
2310
-
2311
- ['is_date_after', '122h', '2022-05-01 00:00:00 GMT', true],
2312
- ['is_date_after', '122h', '2022-04-23 01:00:00 GMT', false],
2313
-
2314
- ['is_date_after', '2d', '2022-05-01 00:00:00 GMT', true],
2315
- ['is_date_after', '2d', '2022-04-29 00:00:01 GMT', true],
2316
- ['is_date_after', '2d', '2022-04-29 00:00:00 GMT', false],
2317
-
2318
- ['is_date_after', '02w', '2022-05-01 00:00:00 GMT', true],
2319
- ['is_date_after', '02w', '2022-04-16 00:00:00 GMT', false],
2320
-
2321
- ['is_date_after', '-1m', '2022-04-01 00:00:01 GMT', true],
2322
- ['is_date_after', '-1m', '2022-04-01 00:00:00 GMT', false],
2323
-
2324
- ['is_date_after', '1y', '2022-05-01 00:00:00 GMT', true],
2325
- ['is_date_after', '1y', '2021-05-01 00:00:01 GMT', true],
2326
- ['is_date_after', '1y', '2021-05-01 00:00:00 GMT', false],
2327
- ['is_date_after', '1y', '2021-04-30 00:00:00 GMT', false],
2328
- ['is_date_after', '1y', '2021-03-01 12:13:00 GMT', false],
2329
- ])('with relative date operators: %s, %s, %s', (operator, value, date, expectation) => {
2330
- jest.setSystemTime(new Date('2022-05-01'))
2331
- expect(matchProperty({ key: 'key', value, operator }, { key: date })).toBe(expectation)
2332
-
2333
- return
2334
- })
2335
-
2336
- it('with relative date operators handles invalid keys', () => {
2337
- jest.setSystemTime(new Date('2022-05-01'))
2338
-
2339
- // # can't be an invalid string
2340
- expect(() => matchProperty({ key: 'key', value: '1d', operator: 'is_date_before' }, { key: 'abcdef' })).toThrow(
2341
- InconclusiveMatchError
2342
- )
2343
- // however js understands numbers as date offsets from utc epoch
2344
- expect(() => matchProperty({ key: 'key', value: '1d', operator: 'is_date_before' }, { key: 1 })).not.toThrow(
2345
- InconclusiveMatchError
2346
- )
2347
- })
2348
-
2349
- it('null or undefined property value', () => {
2350
- const property_a = { key: 'key', value: 'null', operator: 'is_not' }
2351
- expect(matchProperty(property_a, { key: null })).toBe(false)
2352
- expect(matchProperty(property_a, { key: undefined })).toBe(true)
2353
- expect(matchProperty(property_a, { key: 'null' })).toBe(false)
2354
- expect(matchProperty(property_a, { key: 'nul' })).toBe(true)
2355
-
2356
- const property_b = { key: 'key', value: 'null', operator: 'is_set' }
2357
- expect(matchProperty(property_b, { key: null })).toBe(false)
2358
- expect(matchProperty(property_b, { key: undefined })).toBe(false)
2359
- expect(matchProperty(property_b, { key: 'null' })).toBe(true)
2360
-
2361
- const property_c = { key: 'key', value: 'undefined', operator: 'icontains' }
2362
- expect(matchProperty(property_c, { key: null })).toBe(false)
2363
- expect(matchProperty(property_c, { key: undefined })).toBe(false)
2364
- expect(matchProperty(property_c, { key: 'lol' })).toBe(false)
2365
-
2366
- const property_d = { key: 'key', value: 'undefined', operator: 'regex' }
2367
- expect(matchProperty(property_d, { key: null })).toBe(false)
2368
- expect(matchProperty(property_d, { key: undefined })).toBe(false)
2369
-
2370
- const property_e = { key: 'key', value: 1, operator: 'gt' }
2371
- expect(matchProperty(property_e, { key: null })).toBe(false)
2372
- expect(matchProperty(property_e, { key: undefined })).toBe(false)
2373
-
2374
- const property_f = { key: 'key', value: 1, operator: 'lt' }
2375
- expect(matchProperty(property_f, { key: null })).toBe(false)
2376
- expect(matchProperty(property_f, { key: undefined })).toBe(false)
2377
-
2378
- const property_g = { key: 'key', value: 'xyz', operator: 'gte' }
2379
- expect(matchProperty(property_g, { key: null })).toBe(false)
2380
- expect(matchProperty(property_g, { key: undefined })).toBe(false)
2381
-
2382
- const property_h = { key: 'key', value: 'Oo', operator: 'lte' }
2383
- expect(matchProperty(property_h, { key: null })).toBe(false)
2384
- expect(matchProperty(property_h, { key: undefined })).toBe(false)
2385
-
2386
- const property_h_lower = { key: 'key', value: 'oo', operator: 'lte' }
2387
- expect(matchProperty(property_h_lower, { key: null })).toBe(false)
2388
- expect(matchProperty(property_h_lower, { key: undefined })).toBe(false)
2389
-
2390
- const property_i = { key: 'key', value: '2022-05-01', operator: 'is_date_before' }
2391
-
2392
- expect(matchProperty(property_i, { key: null })).toBe(false)
2393
- expect(matchProperty(property_i, { key: undefined })).toBe(false)
2394
-
2395
- const property_j = { key: 'key', value: '2022-05-01', operator: 'is_date_after' }
2396
- expect(matchProperty(property_j, { key: null })).toBe(false)
2397
-
2398
- const property_k = { key: 'key', value: '2022-05-01', operator: 'is_date_before' }
2399
- expect(matchProperty(property_k, { key: null })).toBe(false)
2400
- })
2401
-
2402
- it('null or undefined override value', () => {
2403
- const property_a = { key: 'key', value: 'ab', operator: 'is_not' }
2404
- expect(matchProperty(property_a, { key: null })).toBe(true)
2405
- expect(matchProperty(property_a, { key: undefined })).toBe(true)
2406
- expect(matchProperty(property_a, { key: 'null' })).toBe(true)
2407
- expect(matchProperty(property_a, { key: 'nul' })).toBe(true)
2408
-
2409
- const property_b = { key: 'key', value: 'null', operator: 'is_set' }
2410
- expect(matchProperty(property_b, { key: null })).toBe(false)
2411
- expect(matchProperty(property_b, { key: undefined })).toBe(false)
2412
- expect(matchProperty(property_b, { key: 'null' })).toBe(true)
2413
-
2414
- const property_c = { key: 'key', value: 'app.posthog.com', operator: 'icontains' }
2415
- expect(matchProperty(property_c, { key: null })).toBe(false)
2416
- expect(matchProperty(property_c, { key: undefined })).toBe(false)
2417
- expect(matchProperty(property_c, { key: 'lol' })).toBe(false)
2418
- expect(matchProperty(property_c, { key: 'https://app.posthog.com' })).toBe(true)
2419
-
2420
- const property_d = { key: 'key', value: '.+', operator: 'regex' }
2421
- expect(matchProperty(property_d, { key: null })).toBe(false)
2422
- expect(matchProperty(property_d, { key: undefined })).toBe(false)
2423
- expect(matchProperty(property_d, { key: 'i_am_a_value' })).toBe(true)
2424
-
2425
- const property_e = { key: 'key', value: 1, operator: 'gt' }
2426
- expect(matchProperty(property_e, { key: null })).toBe(false)
2427
- expect(matchProperty(property_e, { key: undefined })).toBe(false)
2428
- expect(matchProperty(property_e, { key: 1 })).toBe(false)
2429
- expect(matchProperty(property_e, { key: 2 })).toBe(true)
2430
-
2431
- const property_f = { key: 'key', value: 1, operator: 'lt' }
2432
- expect(matchProperty(property_f, { key: null })).toBe(false)
2433
- expect(matchProperty(property_f, { key: undefined })).toBe(false)
2434
- expect(matchProperty(property_f, { key: 0 })).toBe(true)
2435
-
2436
- const property_g = { key: 'key', value: 'xyz', operator: 'gte' }
2437
- expect(matchProperty(property_g, { key: null })).toBe(false)
2438
- expect(matchProperty(property_g, { key: undefined })).toBe(false)
2439
- expect(matchProperty(property_g, { key: 'xyz' })).toBe(true)
2440
-
2441
- const property_h = { key: 'key', value: 'Oo', operator: 'lte' }
2442
- expect(matchProperty(property_h, { key: null })).toBe(false)
2443
- expect(matchProperty(property_h, { key: undefined })).toBe(false)
2444
- expect(matchProperty(property_h, { key: 'Oo' })).toBe(true)
2445
-
2446
- const property_h_lower = { key: 'key', value: 'oo', operator: 'lte' }
2447
- expect(matchProperty(property_h_lower, { key: null })).toBe(false)
2448
- expect(matchProperty(property_h_lower, { key: undefined })).toBe(false)
2449
- expect(matchProperty(property_h_lower, { key: 'oo' })).toBe(true)
2450
-
2451
- const property_i = { key: 'key', value: '2022-05-01', operator: 'is_date_before' }
2452
- expect(matchProperty(property_i, { key: null })).toBe(false)
2453
- expect(matchProperty(property_i, { key: undefined })).toBe(false)
2454
-
2455
- const property_j = { key: 'key', value: '2022-05-01', operator: 'is_date_after' }
2456
- expect(matchProperty(property_j, { key: null })).toBe(false)
2457
-
2458
- const property_k = { key: 'key', value: '2022-05-01', operator: 'is_date_before' }
2459
- expect(matchProperty(property_k, { key: null })).toBe(false)
2460
- })
2461
-
2462
- it('with invalid operator', () => {
2463
- const property_a = { key: 'key', value: '2022-05-01', operator: 'is_unknown' }
2464
-
2465
- expect(() => matchProperty(property_a, { key: 'random' })).toThrow(
2466
- new InconclusiveMatchError('Unknown operator: is_unknown')
2467
- )
2468
- })
2469
- })
2470
-
2471
- describe('relative date parsing', () => {
2472
- jest.useFakeTimers()
2473
- beforeEach(() => {
2474
- jest.setSystemTime(new Date('2020-01-01T12:01:20.134Z'))
2475
- })
2476
-
2477
- it('invalid input', () => {
2478
- expect(relativeDateParseForFeatureFlagMatching('1')).toBe(null)
2479
- expect(relativeDateParseForFeatureFlagMatching('1x')).toBe(null)
2480
- expect(relativeDateParseForFeatureFlagMatching('1.2y')).toBe(null)
2481
- expect(relativeDateParseForFeatureFlagMatching('1z')).toBe(null)
2482
- expect(relativeDateParseForFeatureFlagMatching('1s')).toBe(null)
2483
- expect(relativeDateParseForFeatureFlagMatching('123344000.134m')).toBe(null)
2484
- expect(relativeDateParseForFeatureFlagMatching('bazinga')).toBe(null)
2485
- expect(relativeDateParseForFeatureFlagMatching('000bello')).toBe(null)
2486
- expect(relativeDateParseForFeatureFlagMatching('000hello')).toBe(null)
2487
-
2488
- expect(relativeDateParseForFeatureFlagMatching('000h')).not.toBe(null)
2489
- expect(relativeDateParseForFeatureFlagMatching('1000h')).not.toBe(null)
2490
- })
2491
-
2492
- it('overflow', () => {
2493
- expect(relativeDateParseForFeatureFlagMatching('1000000h')).toBe(null)
2494
- expect(relativeDateParseForFeatureFlagMatching('100000000000000000y')).toBe(null)
2495
- })
2496
-
2497
- it('hour parsing', () => {
2498
- expect(relativeDateParseForFeatureFlagMatching('1h')).toEqual(new Date('2020-01-01T11:01:20.134Z'))
2499
- expect(relativeDateParseForFeatureFlagMatching('2h')).toEqual(new Date('2020-01-01T10:01:20.134Z'))
2500
- expect(relativeDateParseForFeatureFlagMatching('24h')).toEqual(new Date('2019-12-31T12:01:20.134Z'))
2501
- expect(relativeDateParseForFeatureFlagMatching('30h')).toEqual(new Date('2019-12-31T06:01:20.134Z'))
2502
- expect(relativeDateParseForFeatureFlagMatching('48h')).toEqual(new Date('2019-12-30T12:01:20.134Z'))
2503
-
2504
- expect(relativeDateParseForFeatureFlagMatching('24h')).toEqual(relativeDateParseForFeatureFlagMatching('1d'))
2505
- expect(relativeDateParseForFeatureFlagMatching('48h')).toEqual(relativeDateParseForFeatureFlagMatching('2d'))
2506
- })
2507
-
2508
- it('day parsing', () => {
2509
- expect(relativeDateParseForFeatureFlagMatching('1d')).toEqual(new Date('2019-12-31T12:01:20.134Z'))
2510
- expect(relativeDateParseForFeatureFlagMatching('2d')).toEqual(new Date('2019-12-30T12:01:20.134Z'))
2511
- expect(relativeDateParseForFeatureFlagMatching('7d')).toEqual(new Date('2019-12-25T12:01:20.134Z'))
2512
- expect(relativeDateParseForFeatureFlagMatching('14d')).toEqual(new Date('2019-12-18T12:01:20.134Z'))
2513
- expect(relativeDateParseForFeatureFlagMatching('30d')).toEqual(new Date('2019-12-02T12:01:20.134Z'))
2514
-
2515
- expect(relativeDateParseForFeatureFlagMatching('7d')).toEqual(relativeDateParseForFeatureFlagMatching('1w'))
2516
- })
2517
-
2518
- it('week parsing', () => {
2519
- expect(relativeDateParseForFeatureFlagMatching('1w')).toEqual(new Date('2019-12-25T12:01:20.134Z'))
2520
- expect(relativeDateParseForFeatureFlagMatching('2w')).toEqual(new Date('2019-12-18T12:01:20.134Z'))
2521
- expect(relativeDateParseForFeatureFlagMatching('4w')).toEqual(new Date('2019-12-04T12:01:20.134Z'))
2522
- expect(relativeDateParseForFeatureFlagMatching('8w')).toEqual(new Date('2019-11-06T12:01:20.134Z'))
2523
-
2524
- expect(relativeDateParseForFeatureFlagMatching('1m')).toEqual(new Date('2019-12-01T12:01:20.134Z'))
2525
- expect(relativeDateParseForFeatureFlagMatching('4w')).not.toEqual(relativeDateParseForFeatureFlagMatching('1m'))
2526
- })
2527
-
2528
- it('month parsing', () => {
2529
- expect(relativeDateParseForFeatureFlagMatching('1m')).toEqual(new Date('2019-12-01T12:01:20.134Z'))
2530
- expect(relativeDateParseForFeatureFlagMatching('2m')).toEqual(new Date('2019-11-01T12:01:20.134Z'))
2531
-
2532
- expect(relativeDateParseForFeatureFlagMatching('4m')).toEqual(new Date('2019-09-01T12:01:20.134Z'))
2533
- expect(relativeDateParseForFeatureFlagMatching('5m')).toEqual(new Date('2019-08-01T12:01:20.134Z'))
2534
- expect(relativeDateParseForFeatureFlagMatching('6m')).toEqual(new Date('2019-07-01T12:01:20.134Z'))
2535
- expect(relativeDateParseForFeatureFlagMatching('8m')).toEqual(new Date('2019-05-01T12:01:20.134Z'))
2536
- expect(relativeDateParseForFeatureFlagMatching('10m')).toEqual(new Date('2019-03-01T12:01:20.134Z'))
2537
-
2538
- expect(relativeDateParseForFeatureFlagMatching('24m')).toEqual(new Date('2018-01-01T12:01:20.134Z'))
2539
-
2540
- expect(relativeDateParseForFeatureFlagMatching('1y')).toEqual(new Date('2019-01-01T12:01:20.134Z'))
2541
- expect(relativeDateParseForFeatureFlagMatching('12m')).toEqual(relativeDateParseForFeatureFlagMatching('1y'))
2542
-
2543
- jest.setSystemTime(new Date('2020-04-03T00:00:00Z'))
2544
- expect(relativeDateParseForFeatureFlagMatching('1m')).toEqual(new Date('2020-03-03T00:00:00Z'))
2545
- expect(relativeDateParseForFeatureFlagMatching('2m')).toEqual(new Date('2020-02-03T00:00:00Z'))
2546
- expect(relativeDateParseForFeatureFlagMatching('4m')).toEqual(new Date('2019-12-03T00:00:00Z'))
2547
- expect(relativeDateParseForFeatureFlagMatching('8m')).toEqual(new Date('2019-08-03T00:00:00Z'))
2548
-
2549
- expect(relativeDateParseForFeatureFlagMatching('1y')).toEqual(new Date('2019-04-03T00:00:00Z'))
2550
- expect(relativeDateParseForFeatureFlagMatching('12m')).toEqual(relativeDateParseForFeatureFlagMatching('1y'))
2551
- })
2552
-
2553
- it('year parsing', () => {
2554
- expect(relativeDateParseForFeatureFlagMatching('1y')).toEqual(new Date('2019-01-01T12:01:20.134Z'))
2555
- expect(relativeDateParseForFeatureFlagMatching('2y')).toEqual(new Date('2018-01-01T12:01:20.134Z'))
2556
- expect(relativeDateParseForFeatureFlagMatching('4y')).toEqual(new Date('2016-01-01T12:01:20.134Z'))
2557
- expect(relativeDateParseForFeatureFlagMatching('8y')).toEqual(new Date('2012-01-01T12:01:20.134Z'))
2558
-
2559
- expect(relativeDateParseForFeatureFlagMatching('1y')).toEqual(new Date('2019-01-01T12:01:20.134Z'))
2560
- expect(relativeDateParseForFeatureFlagMatching('12m')).toEqual(relativeDateParseForFeatureFlagMatching('1y'))
2561
- })
2562
- })
2563
-
2564
- describe('consistency tests', () => {
2565
- // # These tests are the same across all libraries
2566
- // # See https://github.com/PostHog/posthog/blob/master/posthog/test/test_feature_flag.py#L627
2567
- // # where this test has directly been copied from.
2568
- // # They ensure that the server and library hash calculations are in sync.
2569
-
2570
- let posthog: PostHog
2571
- jest.useFakeTimers()
2572
-
2573
- it('is consistent for simple flags', () => {
2574
- const flags = {
2575
- flags: [
2576
- {
2577
- id: 1,
2578
- name: '',
2579
- key: 'simple-flag',
2580
- active: true,
2581
- filters: {
2582
- groups: [{ properties: [], rollout_percentage: 45 }],
2583
- },
2584
- },
2585
- ],
2586
- }
2587
-
2588
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags, decideFlags: {}, decideStatus: 400 }))
2589
-
2590
- posthog = new PostHog('TEST_API_KEY', {
2591
- host: 'http://example.com',
2592
- personalApiKey: 'TEST_PERSONAL_API_KEY',
2593
- ...posthogImmediateResolveOptions,
2594
- })
2595
-
2596
- const results = [
2597
- false,
2598
- true,
2599
- true,
2600
- false,
2601
- true,
2602
- false,
2603
- false,
2604
- true,
2605
- false,
2606
- true,
2607
- false,
2608
- true,
2609
- true,
2610
- false,
2611
- true,
2612
- false,
2613
- false,
2614
- false,
2615
- true,
2616
- true,
2617
- false,
2618
- true,
2619
- false,
2620
- false,
2621
- true,
2622
- false,
2623
- true,
2624
- true,
2625
- false,
2626
- false,
2627
- false,
2628
- true,
2629
- true,
2630
- true,
2631
- true,
2632
- false,
2633
- false,
2634
- false,
2635
- false,
2636
- false,
2637
- false,
2638
- true,
2639
- true,
2640
- false,
2641
- true,
2642
- true,
2643
- false,
2644
- false,
2645
- false,
2646
- true,
2647
- true,
2648
- false,
2649
- false,
2650
- false,
2651
- false,
2652
- true,
2653
- false,
2654
- true,
2655
- false,
2656
- true,
2657
- false,
2658
- true,
2659
- true,
2660
- false,
2661
- true,
2662
- false,
2663
- true,
2664
- false,
2665
- true,
2666
- true,
2667
- false,
2668
- false,
2669
- true,
2670
- false,
2671
- false,
2672
- true,
2673
- false,
2674
- true,
2675
- false,
2676
- false,
2677
- true,
2678
- false,
2679
- false,
2680
- false,
2681
- true,
2682
- true,
2683
- false,
2684
- true,
2685
- true,
2686
- false,
2687
- true,
2688
- true,
2689
- true,
2690
- true,
2691
- true,
2692
- false,
2693
- true,
2694
- true,
2695
- false,
2696
- false,
2697
- true,
2698
- true,
2699
- true,
2700
- true,
2701
- false,
2702
- false,
2703
- true,
2704
- false,
2705
- true,
2706
- true,
2707
- true,
2708
- false,
2709
- false,
2710
- false,
2711
- false,
2712
- false,
2713
- true,
2714
- false,
2715
- false,
2716
- true,
2717
- true,
2718
- true,
2719
- false,
2720
- false,
2721
- true,
2722
- false,
2723
- true,
2724
- false,
2725
- false,
2726
- true,
2727
- false,
2728
- false,
2729
- false,
2730
- false,
2731
- false,
2732
- false,
2733
- false,
2734
- false,
2735
- true,
2736
- true,
2737
- false,
2738
- false,
2739
- true,
2740
- false,
2741
- false,
2742
- true,
2743
- true,
2744
- false,
2745
- false,
2746
- true,
2747
- false,
2748
- true,
2749
- false,
2750
- true,
2751
- true,
2752
- true,
2753
- false,
2754
- false,
2755
- false,
2756
- true,
2757
- false,
2758
- false,
2759
- false,
2760
- false,
2761
- true,
2762
- true,
2763
- false,
2764
- true,
2765
- true,
2766
- false,
2767
- true,
2768
- false,
2769
- true,
2770
- true,
2771
- false,
2772
- true,
2773
- false,
2774
- true,
2775
- true,
2776
- true,
2777
- false,
2778
- true,
2779
- false,
2780
- false,
2781
- true,
2782
- true,
2783
- false,
2784
- true,
2785
- false,
2786
- true,
2787
- true,
2788
- false,
2789
- false,
2790
- true,
2791
- true,
2792
- true,
2793
- true,
2794
- false,
2795
- true,
2796
- true,
2797
- false,
2798
- false,
2799
- true,
2800
- false,
2801
- true,
2802
- false,
2803
- false,
2804
- true,
2805
- true,
2806
- false,
2807
- true,
2808
- false,
2809
- true,
2810
- false,
2811
- false,
2812
- false,
2813
- false,
2814
- false,
2815
- false,
2816
- false,
2817
- true,
2818
- false,
2819
- true,
2820
- true,
2821
- false,
2822
- false,
2823
- true,
2824
- false,
2825
- true,
2826
- false,
2827
- false,
2828
- false,
2829
- true,
2830
- false,
2831
- true,
2832
- false,
2833
- false,
2834
- false,
2835
- true,
2836
- false,
2837
- false,
2838
- true,
2839
- false,
2840
- true,
2841
- true,
2842
- false,
2843
- false,
2844
- false,
2845
- false,
2846
- true,
2847
- false,
2848
- false,
2849
- false,
2850
- false,
2851
- false,
2852
- false,
2853
- false,
2854
- false,
2855
- false,
2856
- false,
2857
- false,
2858
- false,
2859
- false,
2860
- true,
2861
- true,
2862
- false,
2863
- true,
2864
- false,
2865
- true,
2866
- true,
2867
- false,
2868
- true,
2869
- false,
2870
- true,
2871
- false,
2872
- false,
2873
- false,
2874
- true,
2875
- true,
2876
- true,
2877
- true,
2878
- false,
2879
- false,
2880
- false,
2881
- false,
2882
- false,
2883
- true,
2884
- true,
2885
- true,
2886
- false,
2887
- false,
2888
- true,
2889
- true,
2890
- false,
2891
- false,
2892
- false,
2893
- false,
2894
- false,
2895
- true,
2896
- false,
2897
- true,
2898
- true,
2899
- true,
2900
- true,
2901
- false,
2902
- true,
2903
- true,
2904
- true,
2905
- false,
2906
- false,
2907
- true,
2908
- false,
2909
- true,
2910
- false,
2911
- false,
2912
- true,
2913
- true,
2914
- true,
2915
- false,
2916
- true,
2917
- false,
2918
- false,
2919
- false,
2920
- true,
2921
- true,
2922
- false,
2923
- true,
2924
- false,
2925
- true,
2926
- false,
2927
- true,
2928
- true,
2929
- true,
2930
- true,
2931
- true,
2932
- false,
2933
- false,
2934
- true,
2935
- false,
2936
- true,
2937
- false,
2938
- true,
2939
- true,
2940
- true,
2941
- false,
2942
- true,
2943
- false,
2944
- true,
2945
- true,
2946
- false,
2947
- true,
2948
- true,
2949
- true,
2950
- true,
2951
- true,
2952
- false,
2953
- false,
2954
- false,
2955
- false,
2956
- false,
2957
- true,
2958
- false,
2959
- true,
2960
- false,
2961
- false,
2962
- true,
2963
- true,
2964
- false,
2965
- false,
2966
- false,
2967
- true,
2968
- false,
2969
- true,
2970
- true,
2971
- true,
2972
- true,
2973
- false,
2974
- false,
2975
- false,
2976
- false,
2977
- true,
2978
- true,
2979
- false,
2980
- false,
2981
- true,
2982
- true,
2983
- false,
2984
- true,
2985
- true,
2986
- true,
2987
- true,
2988
- false,
2989
- true,
2990
- true,
2991
- true,
2992
- false,
2993
- false,
2994
- true,
2995
- true,
2996
- false,
2997
- false,
2998
- true,
2999
- false,
3000
- false,
3001
- true,
3002
- false,
3003
- false,
3004
- false,
3005
- false,
3006
- false,
3007
- false,
3008
- false,
3009
- false,
3010
- false,
3011
- false,
3012
- true,
3013
- true,
3014
- false,
3015
- false,
3016
- true,
3017
- false,
3018
- false,
3019
- true,
3020
- false,
3021
- true,
3022
- false,
3023
- false,
3024
- true,
3025
- false,
3026
- false,
3027
- false,
3028
- false,
3029
- false,
3030
- false,
3031
- true,
3032
- false,
3033
- false,
3034
- false,
3035
- false,
3036
- false,
3037
- false,
3038
- false,
3039
- false,
3040
- false,
3041
- true,
3042
- true,
3043
- true,
3044
- false,
3045
- false,
3046
- false,
3047
- true,
3048
- false,
3049
- true,
3050
- false,
3051
- false,
3052
- false,
3053
- true,
3054
- false,
3055
- false,
3056
- false,
3057
- false,
3058
- false,
3059
- false,
3060
- false,
3061
- true,
3062
- false,
3063
- false,
3064
- false,
3065
- false,
3066
- false,
3067
- false,
3068
- false,
3069
- false,
3070
- true,
3071
- false,
3072
- true,
3073
- false,
3074
- true,
3075
- true,
3076
- true,
3077
- false,
3078
- false,
3079
- false,
3080
- true,
3081
- true,
3082
- true,
3083
- false,
3084
- true,
3085
- false,
3086
- true,
3087
- true,
3088
- false,
3089
- false,
3090
- false,
3091
- true,
3092
- false,
3093
- false,
3094
- false,
3095
- false,
3096
- true,
3097
- false,
3098
- true,
3099
- false,
3100
- true,
3101
- true,
3102
- false,
3103
- true,
3104
- false,
3105
- false,
3106
- false,
3107
- true,
3108
- false,
3109
- false,
3110
- true,
3111
- true,
3112
- false,
3113
- true,
3114
- false,
3115
- false,
3116
- false,
3117
- false,
3118
- false,
3119
- false,
3120
- true,
3121
- true,
3122
- false,
3123
- false,
3124
- true,
3125
- false,
3126
- false,
3127
- true,
3128
- true,
3129
- true,
3130
- false,
3131
- false,
3132
- false,
3133
- true,
3134
- false,
3135
- false,
3136
- false,
3137
- false,
3138
- true,
3139
- false,
3140
- true,
3141
- false,
3142
- false,
3143
- false,
3144
- true,
3145
- false,
3146
- true,
3147
- true,
3148
- false,
3149
- true,
3150
- false,
3151
- true,
3152
- false,
3153
- true,
3154
- false,
3155
- false,
3156
- true,
3157
- false,
3158
- false,
3159
- true,
3160
- false,
3161
- true,
3162
- false,
3163
- true,
3164
- false,
3165
- true,
3166
- false,
3167
- false,
3168
- true,
3169
- true,
3170
- true,
3171
- true,
3172
- false,
3173
- true,
3174
- false,
3175
- false,
3176
- false,
3177
- false,
3178
- false,
3179
- true,
3180
- false,
3181
- false,
3182
- true,
3183
- false,
3184
- false,
3185
- true,
3186
- true,
3187
- false,
3188
- false,
3189
- false,
3190
- false,
3191
- true,
3192
- true,
3193
- true,
3194
- false,
3195
- false,
3196
- true,
3197
- false,
3198
- false,
3199
- true,
3200
- true,
3201
- true,
3202
- true,
3203
- false,
3204
- false,
3205
- false,
3206
- true,
3207
- false,
3208
- false,
3209
- false,
3210
- true,
3211
- false,
3212
- false,
3213
- true,
3214
- true,
3215
- true,
3216
- true,
3217
- false,
3218
- false,
3219
- true,
3220
- true,
3221
- false,
3222
- true,
3223
- false,
3224
- true,
3225
- false,
3226
- false,
3227
- true,
3228
- true,
3229
- false,
3230
- true,
3231
- true,
3232
- true,
3233
- true,
3234
- false,
3235
- false,
3236
- true,
3237
- false,
3238
- false,
3239
- true,
3240
- true,
3241
- false,
3242
- true,
3243
- false,
3244
- true,
3245
- false,
3246
- false,
3247
- true,
3248
- false,
3249
- false,
3250
- false,
3251
- false,
3252
- true,
3253
- true,
3254
- true,
3255
- false,
3256
- true,
3257
- false,
3258
- false,
3259
- true,
3260
- false,
3261
- false,
3262
- true,
3263
- false,
3264
- false,
3265
- false,
3266
- false,
3267
- true,
3268
- false,
3269
- true,
3270
- false,
3271
- true,
3272
- true,
3273
- false,
3274
- false,
3275
- true,
3276
- false,
3277
- true,
3278
- true,
3279
- true,
3280
- false,
3281
- false,
3282
- false,
3283
- false,
3284
- true,
3285
- true,
3286
- false,
3287
- true,
3288
- false,
3289
- false,
3290
- false,
3291
- true,
3292
- false,
3293
- false,
3294
- false,
3295
- false,
3296
- true,
3297
- true,
3298
- true,
3299
- false,
3300
- false,
3301
- false,
3302
- true,
3303
- true,
3304
- true,
3305
- true,
3306
- false,
3307
- true,
3308
- true,
3309
- false,
3310
- true,
3311
- true,
3312
- true,
3313
- false,
3314
- true,
3315
- false,
3316
- false,
3317
- true,
3318
- false,
3319
- true,
3320
- true,
3321
- true,
3322
- true,
3323
- false,
3324
- true,
3325
- false,
3326
- true,
3327
- false,
3328
- true,
3329
- false,
3330
- false,
3331
- true,
3332
- true,
3333
- false,
3334
- false,
3335
- true,
3336
- false,
3337
- true,
3338
- false,
3339
- false,
3340
- false,
3341
- false,
3342
- true,
3343
- false,
3344
- true,
3345
- false,
3346
- false,
3347
- false,
3348
- true,
3349
- true,
3350
- true,
3351
- false,
3352
- false,
3353
- false,
3354
- true,
3355
- false,
3356
- true,
3357
- true,
3358
- false,
3359
- false,
3360
- false,
3361
- false,
3362
- false,
3363
- true,
3364
- false,
3365
- true,
3366
- false,
3367
- false,
3368
- true,
3369
- true,
3370
- false,
3371
- true,
3372
- true,
3373
- true,
3374
- true,
3375
- false,
3376
- false,
3377
- true,
3378
- false,
3379
- false,
3380
- true,
3381
- false,
3382
- true,
3383
- false,
3384
- true,
3385
- true,
3386
- false,
3387
- false,
3388
- false,
3389
- true,
3390
- false,
3391
- true,
3392
- true,
3393
- false,
3394
- false,
3395
- false,
3396
- true,
3397
- false,
3398
- true,
3399
- false,
3400
- true,
3401
- true,
3402
- false,
3403
- true,
3404
- false,
3405
- false,
3406
- true,
3407
- false,
3408
- false,
3409
- false,
3410
- true,
3411
- true,
3412
- true,
3413
- false,
3414
- false,
3415
- false,
3416
- false,
3417
- false,
3418
- true,
3419
- false,
3420
- false,
3421
- true,
3422
- true,
3423
- true,
3424
- true,
3425
- true,
3426
- false,
3427
- false,
3428
- false,
3429
- false,
3430
- false,
3431
- false,
3432
- false,
3433
- false,
3434
- true,
3435
- true,
3436
- true,
3437
- false,
3438
- false,
3439
- true,
3440
- true,
3441
- false,
3442
- true,
3443
- true,
3444
- false,
3445
- true,
3446
- false,
3447
- true,
3448
- false,
3449
- false,
3450
- false,
3451
- true,
3452
- false,
3453
- false,
3454
- true,
3455
- false,
3456
- false,
3457
- true,
3458
- true,
3459
- true,
3460
- true,
3461
- false,
3462
- false,
3463
- true,
3464
- false,
3465
- true,
3466
- true,
3467
- false,
3468
- false,
3469
- true,
3470
- false,
3471
- false,
3472
- true,
3473
- true,
3474
- false,
3475
- true,
3476
- false,
3477
- false,
3478
- true,
3479
- true,
3480
- true,
3481
- false,
3482
- false,
3483
- false,
3484
- false,
3485
- false,
3486
- true,
3487
- false,
3488
- true,
3489
- false,
3490
- false,
3491
- false,
3492
- false,
3493
- false,
3494
- true,
3495
- true,
3496
- false,
3497
- true,
3498
- true,
3499
- true,
3500
- false,
3501
- false,
3502
- false,
3503
- false,
3504
- true,
3505
- true,
3506
- true,
3507
- true,
3508
- false,
3509
- true,
3510
- true,
3511
- false,
3512
- true,
3513
- false,
3514
- true,
3515
- false,
3516
- true,
3517
- false,
3518
- false,
3519
- false,
3520
- false,
3521
- true,
3522
- true,
3523
- true,
3524
- true,
3525
- false,
3526
- false,
3527
- true,
3528
- false,
3529
- true,
3530
- true,
3531
- false,
3532
- false,
3533
- false,
3534
- false,
3535
- false,
3536
- false,
3537
- true,
3538
- false,
3539
- true,
3540
- false,
3541
- true,
3542
- true,
3543
- false,
3544
- false,
3545
- true,
3546
- true,
3547
- true,
3548
- true,
3549
- false,
3550
- false,
3551
- true,
3552
- false,
3553
- true,
3554
- true,
3555
- false,
3556
- false,
3557
- true,
3558
- true,
3559
- true,
3560
- false,
3561
- true,
3562
- false,
3563
- false,
3564
- true,
3565
- true,
3566
- false,
3567
- false,
3568
- false,
3569
- true,
3570
- false,
3571
- false,
3572
- true,
3573
- false,
3574
- false,
3575
- false,
3576
- true,
3577
- true,
3578
- true,
3579
- true,
3580
- false,
3581
- true,
3582
- false,
3583
- true,
3584
- false,
3585
- true,
3586
- false,
3587
- true,
3588
- false,
3589
- false,
3590
- true,
3591
- false,
3592
- false,
3593
- true,
3594
- false,
3595
- true,
3596
- true,
3597
- ]
3598
-
3599
- results.forEach(async (result, index) => {
3600
- const distinctId = `distinct_id_${index}`
3601
- const value = await posthog.isFeatureEnabled('simple-flag', distinctId)
3602
- expect(value).toBe(result)
3603
- })
3604
- })
3605
-
3606
- it('is consistent for multivariate flags', async () => {
3607
- const flags = {
3608
- flags: [
3609
- {
3610
- id: 1,
3611
- name: 'Beta Feature',
3612
- key: 'multivariate-flag',
3613
- active: true,
3614
- filters: {
3615
- groups: [{ properties: [], rollout_percentage: 55 }],
3616
- multivariate: {
3617
- variants: [
3618
- { key: 'first-variant', name: 'First Variant', rollout_percentage: 50 },
3619
- { key: 'second-variant', name: 'Second Variant', rollout_percentage: 20 },
3620
- { key: 'third-variant', name: 'Third Variant', rollout_percentage: 20 },
3621
- { key: 'fourth-variant', name: 'Fourth Variant', rollout_percentage: 5 },
3622
- { key: 'fifth-variant', name: 'Fifth Variant', rollout_percentage: 5 },
3623
- ],
3624
- },
3625
- },
3626
- },
3627
- ],
3628
- }
3629
-
3630
- mockedFetch.mockImplementation(apiImplementation({ localFlags: flags, decideFlags: {}, decideStatus: 400 }))
3631
-
3632
- posthog = new PostHog('TEST_API_KEY', {
3633
- host: 'http://example.com',
3634
- personalApiKey: 'TEST_PERSONAL_API_KEY',
3635
- ...posthogImmediateResolveOptions,
3636
- })
3637
-
3638
- const results = [
3639
- 'second-variant',
3640
- 'second-variant',
3641
- 'first-variant',
3642
- false,
3643
- false,
3644
- 'second-variant',
3645
- 'first-variant',
3646
- false,
3647
- false,
3648
- false,
3649
- 'first-variant',
3650
- 'third-variant',
3651
- false,
3652
- 'first-variant',
3653
- 'second-variant',
3654
- 'first-variant',
3655
- false,
3656
- false,
3657
- 'fourth-variant',
3658
- 'first-variant',
3659
- false,
3660
- 'third-variant',
3661
- false,
3662
- false,
3663
- false,
3664
- 'first-variant',
3665
- 'first-variant',
3666
- 'first-variant',
3667
- 'first-variant',
3668
- 'first-variant',
3669
- 'first-variant',
3670
- 'third-variant',
3671
- false,
3672
- 'third-variant',
3673
- 'second-variant',
3674
- 'first-variant',
3675
- false,
3676
- 'third-variant',
3677
- false,
3678
- false,
3679
- 'first-variant',
3680
- 'second-variant',
3681
- false,
3682
- 'first-variant',
3683
- 'first-variant',
3684
- 'second-variant',
3685
- false,
3686
- 'first-variant',
3687
- false,
3688
- false,
3689
- 'first-variant',
3690
- 'first-variant',
3691
- 'first-variant',
3692
- 'second-variant',
3693
- 'first-variant',
3694
- false,
3695
- 'second-variant',
3696
- 'second-variant',
3697
- 'third-variant',
3698
- 'second-variant',
3699
- 'first-variant',
3700
- false,
3701
- 'first-variant',
3702
- 'second-variant',
3703
- 'fourth-variant',
3704
- false,
3705
- 'first-variant',
3706
- 'first-variant',
3707
- 'first-variant',
3708
- false,
3709
- 'first-variant',
3710
- 'second-variant',
3711
- false,
3712
- 'third-variant',
3713
- false,
3714
- false,
3715
- false,
3716
- false,
3717
- false,
3718
- false,
3719
- 'first-variant',
3720
- 'fifth-variant',
3721
- false,
3722
- 'second-variant',
3723
- 'first-variant',
3724
- 'second-variant',
3725
- false,
3726
- 'third-variant',
3727
- 'third-variant',
3728
- false,
3729
- false,
3730
- false,
3731
- false,
3732
- 'third-variant',
3733
- false,
3734
- false,
3735
- 'first-variant',
3736
- 'first-variant',
3737
- false,
3738
- 'third-variant',
3739
- 'third-variant',
3740
- false,
3741
- 'third-variant',
3742
- 'second-variant',
3743
- 'third-variant',
3744
- false,
3745
- false,
3746
- 'second-variant',
3747
- 'first-variant',
3748
- false,
3749
- false,
3750
- 'first-variant',
3751
- false,
3752
- false,
3753
- false,
3754
- false,
3755
- 'first-variant',
3756
- 'first-variant',
3757
- 'first-variant',
3758
- false,
3759
- false,
3760
- false,
3761
- 'first-variant',
3762
- 'first-variant',
3763
- false,
3764
- 'first-variant',
3765
- 'first-variant',
3766
- false,
3767
- false,
3768
- false,
3769
- false,
3770
- false,
3771
- false,
3772
- false,
3773
- false,
3774
- false,
3775
- 'first-variant',
3776
- 'first-variant',
3777
- 'first-variant',
3778
- 'first-variant',
3779
- 'second-variant',
3780
- 'first-variant',
3781
- 'first-variant',
3782
- 'first-variant',
3783
- 'second-variant',
3784
- false,
3785
- 'second-variant',
3786
- 'first-variant',
3787
- 'second-variant',
3788
- 'first-variant',
3789
- false,
3790
- 'second-variant',
3791
- 'second-variant',
3792
- false,
3793
- 'first-variant',
3794
- false,
3795
- false,
3796
- false,
3797
- 'third-variant',
3798
- 'first-variant',
3799
- false,
3800
- false,
3801
- 'first-variant',
3802
- false,
3803
- false,
3804
- false,
3805
- false,
3806
- 'first-variant',
3807
- false,
3808
- false,
3809
- false,
3810
- false,
3811
- false,
3812
- false,
3813
- false,
3814
- 'first-variant',
3815
- 'first-variant',
3816
- 'third-variant',
3817
- 'first-variant',
3818
- 'first-variant',
3819
- false,
3820
- false,
3821
- 'first-variant',
3822
- false,
3823
- false,
3824
- 'fifth-variant',
3825
- 'second-variant',
3826
- false,
3827
- 'second-variant',
3828
- false,
3829
- 'first-variant',
3830
- 'third-variant',
3831
- 'first-variant',
3832
- 'fifth-variant',
3833
- 'third-variant',
3834
- false,
3835
- false,
3836
- 'fourth-variant',
3837
- false,
3838
- false,
3839
- false,
3840
- false,
3841
- 'third-variant',
3842
- false,
3843
- false,
3844
- 'third-variant',
3845
- false,
3846
- 'first-variant',
3847
- 'second-variant',
3848
- 'second-variant',
3849
- 'second-variant',
3850
- false,
3851
- 'first-variant',
3852
- 'third-variant',
3853
- 'first-variant',
3854
- 'first-variant',
3855
- false,
3856
- false,
3857
- false,
3858
- false,
3859
- false,
3860
- 'first-variant',
3861
- 'first-variant',
3862
- 'first-variant',
3863
- 'second-variant',
3864
- false,
3865
- false,
3866
- false,
3867
- 'second-variant',
3868
- false,
3869
- false,
3870
- 'first-variant',
3871
- false,
3872
- 'first-variant',
3873
- false,
3874
- false,
3875
- 'first-variant',
3876
- 'first-variant',
3877
- 'first-variant',
3878
- 'first-variant',
3879
- 'third-variant',
3880
- 'first-variant',
3881
- 'third-variant',
3882
- 'first-variant',
3883
- 'first-variant',
3884
- 'second-variant',
3885
- 'third-variant',
3886
- 'third-variant',
3887
- false,
3888
- 'second-variant',
3889
- 'first-variant',
3890
- false,
3891
- 'second-variant',
3892
- 'first-variant',
3893
- false,
3894
- 'first-variant',
3895
- false,
3896
- false,
3897
- 'first-variant',
3898
- 'fifth-variant',
3899
- 'first-variant',
3900
- false,
3901
- false,
3902
- false,
3903
- false,
3904
- 'first-variant',
3905
- 'first-variant',
3906
- 'second-variant',
3907
- false,
3908
- 'second-variant',
3909
- 'third-variant',
3910
- 'third-variant',
3911
- false,
3912
- 'first-variant',
3913
- 'third-variant',
3914
- false,
3915
- false,
3916
- 'first-variant',
3917
- false,
3918
- 'third-variant',
3919
- 'first-variant',
3920
- false,
3921
- 'third-variant',
3922
- 'first-variant',
3923
- 'first-variant',
3924
- false,
3925
- 'first-variant',
3926
- 'second-variant',
3927
- 'second-variant',
3928
- 'first-variant',
3929
- false,
3930
- false,
3931
- false,
3932
- 'second-variant',
3933
- false,
3934
- false,
3935
- 'first-variant',
3936
- 'first-variant',
3937
- false,
3938
- 'third-variant',
3939
- false,
3940
- 'first-variant',
3941
- false,
3942
- 'third-variant',
3943
- false,
3944
- 'third-variant',
3945
- 'second-variant',
3946
- 'first-variant',
3947
- false,
3948
- false,
3949
- 'first-variant',
3950
- 'third-variant',
3951
- 'first-variant',
3952
- 'second-variant',
3953
- 'fifth-variant',
3954
- false,
3955
- false,
3956
- 'first-variant',
3957
- false,
3958
- false,
3959
- false,
3960
- 'third-variant',
3961
- false,
3962
- 'second-variant',
3963
- 'first-variant',
3964
- false,
3965
- false,
3966
- false,
3967
- false,
3968
- 'third-variant',
3969
- false,
3970
- false,
3971
- 'third-variant',
3972
- false,
3973
- false,
3974
- 'first-variant',
3975
- 'third-variant',
3976
- false,
3977
- false,
3978
- 'first-variant',
3979
- false,
3980
- false,
3981
- 'fourth-variant',
3982
- 'fourth-variant',
3983
- 'third-variant',
3984
- 'second-variant',
3985
- 'first-variant',
3986
- 'third-variant',
3987
- 'fifth-variant',
3988
- false,
3989
- 'first-variant',
3990
- 'fifth-variant',
3991
- false,
3992
- 'first-variant',
3993
- 'first-variant',
3994
- 'first-variant',
3995
- false,
3996
- false,
3997
- false,
3998
- 'second-variant',
3999
- 'fifth-variant',
4000
- 'second-variant',
4001
- 'first-variant',
4002
- 'first-variant',
4003
- 'second-variant',
4004
- false,
4005
- false,
4006
- 'third-variant',
4007
- false,
4008
- 'second-variant',
4009
- 'fifth-variant',
4010
- false,
4011
- 'third-variant',
4012
- 'first-variant',
4013
- false,
4014
- false,
4015
- 'fourth-variant',
4016
- false,
4017
- false,
4018
- 'second-variant',
4019
- false,
4020
- false,
4021
- 'first-variant',
4022
- 'fourth-variant',
4023
- 'first-variant',
4024
- 'second-variant',
4025
- false,
4026
- false,
4027
- false,
4028
- 'first-variant',
4029
- 'third-variant',
4030
- 'third-variant',
4031
- false,
4032
- 'first-variant',
4033
- 'first-variant',
4034
- 'first-variant',
4035
- false,
4036
- 'first-variant',
4037
- false,
4038
- 'first-variant',
4039
- 'third-variant',
4040
- 'third-variant',
4041
- false,
4042
- false,
4043
- 'first-variant',
4044
- false,
4045
- false,
4046
- 'second-variant',
4047
- 'second-variant',
4048
- 'first-variant',
4049
- 'first-variant',
4050
- 'first-variant',
4051
- false,
4052
- 'fifth-variant',
4053
- 'first-variant',
4054
- false,
4055
- false,
4056
- false,
4057
- 'second-variant',
4058
- 'third-variant',
4059
- 'first-variant',
4060
- 'fourth-variant',
4061
- 'first-variant',
4062
- 'third-variant',
4063
- false,
4064
- 'first-variant',
4065
- 'first-variant',
4066
- false,
4067
- 'third-variant',
4068
- 'first-variant',
4069
- 'first-variant',
4070
- 'third-variant',
4071
- false,
4072
- 'fourth-variant',
4073
- 'fifth-variant',
4074
- 'first-variant',
4075
- 'first-variant',
4076
- false,
4077
- false,
4078
- false,
4079
- 'first-variant',
4080
- 'first-variant',
4081
- 'first-variant',
4082
- false,
4083
- 'first-variant',
4084
- 'first-variant',
4085
- 'second-variant',
4086
- 'first-variant',
4087
- false,
4088
- 'first-variant',
4089
- 'second-variant',
4090
- 'first-variant',
4091
- false,
4092
- 'first-variant',
4093
- 'second-variant',
4094
- false,
4095
- 'first-variant',
4096
- 'first-variant',
4097
- false,
4098
- 'first-variant',
4099
- false,
4100
- 'first-variant',
4101
- false,
4102
- 'first-variant',
4103
- false,
4104
- false,
4105
- false,
4106
- 'third-variant',
4107
- 'third-variant',
4108
- 'first-variant',
4109
- false,
4110
- false,
4111
- 'second-variant',
4112
- 'third-variant',
4113
- 'first-variant',
4114
- 'first-variant',
4115
- false,
4116
- false,
4117
- false,
4118
- 'second-variant',
4119
- 'first-variant',
4120
- false,
4121
- 'first-variant',
4122
- 'third-variant',
4123
- false,
4124
- 'first-variant',
4125
- false,
4126
- false,
4127
- false,
4128
- 'first-variant',
4129
- 'third-variant',
4130
- 'third-variant',
4131
- false,
4132
- false,
4133
- false,
4134
- false,
4135
- 'third-variant',
4136
- 'fourth-variant',
4137
- 'fourth-variant',
4138
- 'first-variant',
4139
- 'second-variant',
4140
- false,
4141
- 'first-variant',
4142
- false,
4143
- 'second-variant',
4144
- 'first-variant',
4145
- 'third-variant',
4146
- false,
4147
- 'third-variant',
4148
- false,
4149
- 'first-variant',
4150
- 'first-variant',
4151
- 'third-variant',
4152
- false,
4153
- false,
4154
- false,
4155
- 'fourth-variant',
4156
- 'second-variant',
4157
- 'first-variant',
4158
- false,
4159
- false,
4160
- 'first-variant',
4161
- 'fourth-variant',
4162
- false,
4163
- 'first-variant',
4164
- 'third-variant',
4165
- 'first-variant',
4166
- false,
4167
- false,
4168
- 'third-variant',
4169
- false,
4170
- 'first-variant',
4171
- false,
4172
- 'first-variant',
4173
- 'first-variant',
4174
- 'third-variant',
4175
- 'second-variant',
4176
- 'fourth-variant',
4177
- false,
4178
- 'first-variant',
4179
- false,
4180
- false,
4181
- false,
4182
- false,
4183
- 'second-variant',
4184
- 'first-variant',
4185
- 'second-variant',
4186
- false,
4187
- 'first-variant',
4188
- false,
4189
- 'first-variant',
4190
- 'first-variant',
4191
- false,
4192
- 'first-variant',
4193
- 'first-variant',
4194
- 'second-variant',
4195
- 'third-variant',
4196
- 'first-variant',
4197
- 'first-variant',
4198
- 'first-variant',
4199
- false,
4200
- false,
4201
- false,
4202
- 'third-variant',
4203
- false,
4204
- 'first-variant',
4205
- 'first-variant',
4206
- 'first-variant',
4207
- 'third-variant',
4208
- 'first-variant',
4209
- 'first-variant',
4210
- 'second-variant',
4211
- 'first-variant',
4212
- 'fifth-variant',
4213
- 'fourth-variant',
4214
- 'first-variant',
4215
- 'second-variant',
4216
- false,
4217
- 'fourth-variant',
4218
- false,
4219
- false,
4220
- false,
4221
- 'fourth-variant',
4222
- false,
4223
- false,
4224
- 'third-variant',
4225
- false,
4226
- false,
4227
- false,
4228
- 'first-variant',
4229
- 'third-variant',
4230
- 'third-variant',
4231
- 'second-variant',
4232
- 'first-variant',
4233
- 'second-variant',
4234
- 'first-variant',
4235
- false,
4236
- 'first-variant',
4237
- false,
4238
- false,
4239
- false,
4240
- false,
4241
- false,
4242
- 'first-variant',
4243
- 'first-variant',
4244
- false,
4245
- 'second-variant',
4246
- false,
4247
- false,
4248
- 'first-variant',
4249
- false,
4250
- 'second-variant',
4251
- 'first-variant',
4252
- 'first-variant',
4253
- 'first-variant',
4254
- 'third-variant',
4255
- 'second-variant',
4256
- false,
4257
- false,
4258
- 'fifth-variant',
4259
- 'third-variant',
4260
- false,
4261
- false,
4262
- 'first-variant',
4263
- false,
4264
- false,
4265
- false,
4266
- 'first-variant',
4267
- 'second-variant',
4268
- 'third-variant',
4269
- 'third-variant',
4270
- false,
4271
- false,
4272
- 'first-variant',
4273
- false,
4274
- 'third-variant',
4275
- 'first-variant',
4276
- false,
4277
- false,
4278
- false,
4279
- false,
4280
- 'fourth-variant',
4281
- 'first-variant',
4282
- false,
4283
- false,
4284
- false,
4285
- 'third-variant',
4286
- false,
4287
- false,
4288
- 'second-variant',
4289
- 'first-variant',
4290
- false,
4291
- false,
4292
- 'second-variant',
4293
- 'third-variant',
4294
- 'first-variant',
4295
- 'first-variant',
4296
- false,
4297
- 'first-variant',
4298
- 'first-variant',
4299
- false,
4300
- false,
4301
- 'second-variant',
4302
- 'third-variant',
4303
- 'second-variant',
4304
- 'third-variant',
4305
- false,
4306
- false,
4307
- 'first-variant',
4308
- false,
4309
- false,
4310
- 'first-variant',
4311
- false,
4312
- 'second-variant',
4313
- false,
4314
- false,
4315
- false,
4316
- false,
4317
- 'first-variant',
4318
- false,
4319
- 'third-variant',
4320
- false,
4321
- 'first-variant',
4322
- false,
4323
- false,
4324
- 'second-variant',
4325
- 'third-variant',
4326
- 'second-variant',
4327
- 'fourth-variant',
4328
- 'first-variant',
4329
- 'first-variant',
4330
- 'first-variant',
4331
- false,
4332
- 'first-variant',
4333
- false,
4334
- 'second-variant',
4335
- false,
4336
- false,
4337
- false,
4338
- false,
4339
- false,
4340
- 'first-variant',
4341
- false,
4342
- false,
4343
- false,
4344
- false,
4345
- false,
4346
- 'first-variant',
4347
- false,
4348
- 'second-variant',
4349
- false,
4350
- false,
4351
- false,
4352
- false,
4353
- 'second-variant',
4354
- false,
4355
- 'first-variant',
4356
- false,
4357
- 'third-variant',
4358
- false,
4359
- false,
4360
- 'first-variant',
4361
- 'third-variant',
4362
- false,
4363
- 'third-variant',
4364
- false,
4365
- false,
4366
- 'second-variant',
4367
- false,
4368
- 'first-variant',
4369
- 'second-variant',
4370
- 'first-variant',
4371
- false,
4372
- false,
4373
- false,
4374
- false,
4375
- false,
4376
- 'second-variant',
4377
- false,
4378
- false,
4379
- 'first-variant',
4380
- 'third-variant',
4381
- false,
4382
- 'first-variant',
4383
- false,
4384
- false,
4385
- false,
4386
- false,
4387
- false,
4388
- 'first-variant',
4389
- 'second-variant',
4390
- false,
4391
- false,
4392
- false,
4393
- 'first-variant',
4394
- 'first-variant',
4395
- 'fifth-variant',
4396
- false,
4397
- false,
4398
- false,
4399
- 'first-variant',
4400
- false,
4401
- 'third-variant',
4402
- false,
4403
- false,
4404
- 'second-variant',
4405
- false,
4406
- false,
4407
- false,
4408
- false,
4409
- false,
4410
- 'fourth-variant',
4411
- 'second-variant',
4412
- 'first-variant',
4413
- 'second-variant',
4414
- false,
4415
- 'second-variant',
4416
- false,
4417
- 'second-variant',
4418
- false,
4419
- 'first-variant',
4420
- false,
4421
- 'first-variant',
4422
- 'first-variant',
4423
- false,
4424
- 'second-variant',
4425
- false,
4426
- 'first-variant',
4427
- false,
4428
- 'fifth-variant',
4429
- false,
4430
- 'first-variant',
4431
- 'first-variant',
4432
- false,
4433
- false,
4434
- false,
4435
- 'first-variant',
4436
- false,
4437
- 'first-variant',
4438
- 'third-variant',
4439
- false,
4440
- false,
4441
- 'first-variant',
4442
- 'first-variant',
4443
- false,
4444
- false,
4445
- 'fifth-variant',
4446
- false,
4447
- false,
4448
- 'third-variant',
4449
- false,
4450
- 'third-variant',
4451
- 'first-variant',
4452
- 'first-variant',
4453
- 'third-variant',
4454
- 'third-variant',
4455
- false,
4456
- 'first-variant',
4457
- false,
4458
- false,
4459
- false,
4460
- false,
4461
- false,
4462
- 'first-variant',
4463
- false,
4464
- false,
4465
- false,
4466
- false,
4467
- 'second-variant',
4468
- 'first-variant',
4469
- 'second-variant',
4470
- 'first-variant',
4471
- false,
4472
- 'fifth-variant',
4473
- 'first-variant',
4474
- false,
4475
- false,
4476
- 'fourth-variant',
4477
- 'first-variant',
4478
- 'first-variant',
4479
- false,
4480
- false,
4481
- 'fourth-variant',
4482
- 'first-variant',
4483
- false,
4484
- 'second-variant',
4485
- 'third-variant',
4486
- 'third-variant',
4487
- 'first-variant',
4488
- 'first-variant',
4489
- false,
4490
- false,
4491
- false,
4492
- 'first-variant',
4493
- 'first-variant',
4494
- 'first-variant',
4495
- false,
4496
- 'third-variant',
4497
- 'third-variant',
4498
- 'third-variant',
4499
- false,
4500
- false,
4501
- 'first-variant',
4502
- 'first-variant',
4503
- false,
4504
- 'second-variant',
4505
- false,
4506
- false,
4507
- 'second-variant',
4508
- false,
4509
- 'third-variant',
4510
- 'first-variant',
4511
- 'second-variant',
4512
- 'fifth-variant',
4513
- 'first-variant',
4514
- 'first-variant',
4515
- false,
4516
- 'first-variant',
4517
- 'fifth-variant',
4518
- false,
4519
- false,
4520
- false,
4521
- 'third-variant',
4522
- 'first-variant',
4523
- 'first-variant',
4524
- 'second-variant',
4525
- 'fourth-variant',
4526
- 'first-variant',
4527
- 'second-variant',
4528
- 'first-variant',
4529
- false,
4530
- false,
4531
- false,
4532
- 'second-variant',
4533
- 'third-variant',
4534
- false,
4535
- false,
4536
- 'first-variant',
4537
- false,
4538
- false,
4539
- false,
4540
- false,
4541
- false,
4542
- false,
4543
- 'first-variant',
4544
- 'first-variant',
4545
- false,
4546
- 'third-variant',
4547
- false,
4548
- 'first-variant',
4549
- false,
4550
- 'third-variant',
4551
- 'third-variant',
4552
- 'first-variant',
4553
- 'first-variant',
4554
- false,
4555
- 'second-variant',
4556
- false,
4557
- 'second-variant',
4558
- 'first-variant',
4559
- false,
4560
- false,
4561
- false,
4562
- 'second-variant',
4563
- false,
4564
- 'third-variant',
4565
- false,
4566
- 'first-variant',
4567
- 'fifth-variant',
4568
- 'first-variant',
4569
- 'first-variant',
4570
- false,
4571
- false,
4572
- 'first-variant',
4573
- false,
4574
- false,
4575
- false,
4576
- 'first-variant',
4577
- 'fourth-variant',
4578
- 'first-variant',
4579
- 'first-variant',
4580
- 'first-variant',
4581
- 'fifth-variant',
4582
- false,
4583
- false,
4584
- false,
4585
- 'second-variant',
4586
- false,
4587
- false,
4588
- false,
4589
- 'first-variant',
4590
- 'first-variant',
4591
- false,
4592
- false,
4593
- 'first-variant',
4594
- 'first-variant',
4595
- 'second-variant',
4596
- 'first-variant',
4597
- 'first-variant',
4598
- 'first-variant',
4599
- 'first-variant',
4600
- 'first-variant',
4601
- 'third-variant',
4602
- 'first-variant',
4603
- false,
4604
- 'second-variant',
4605
- false,
4606
- false,
4607
- 'third-variant',
4608
- 'second-variant',
4609
- 'third-variant',
4610
- false,
4611
- 'first-variant',
4612
- 'third-variant',
4613
- 'second-variant',
4614
- 'first-variant',
4615
- 'third-variant',
4616
- false,
4617
- false,
4618
- 'first-variant',
4619
- 'first-variant',
4620
- false,
4621
- false,
4622
- false,
4623
- 'first-variant',
4624
- 'third-variant',
4625
- 'second-variant',
4626
- 'first-variant',
4627
- 'first-variant',
4628
- 'first-variant',
4629
- false,
4630
- 'third-variant',
4631
- 'second-variant',
4632
- 'third-variant',
4633
- false,
4634
- false,
4635
- 'third-variant',
4636
- 'first-variant',
4637
- false,
4638
- 'first-variant',
4639
- ]
4640
-
4641
- results.forEach(async (result, index) => {
4642
- const distinctId = `distinct_id_${index}`
4643
- const value = await posthog.getFeatureFlag('multivariate-flag', distinctId)
4644
- expect(value).toBe(result)
4645
- })
4646
- })
4647
- })
4648
-
4649
- describe('quota limiting', () => {
4650
- it('should clear local flags when quota limited', async () => {
4651
- const consoleSpy = jest.spyOn(console, 'warn')
4652
-
4653
- mockedFetch.mockImplementation(
4654
- apiImplementation({
4655
- localFlagsStatus: 402,
4656
- })
4657
- )
4658
-
4659
- const posthog = new PostHog('TEST_API_KEY', {
4660
- host: 'http://example.com',
4661
- personalApiKey: 'TEST_PERSONAL_API_KEY',
4662
- ...posthogImmediateResolveOptions,
4663
- })
4664
-
4665
- // Enable debug mode to see the messages
4666
- posthog.debug(true)
4667
-
4668
- // Force a reload and wait for it to complete
4669
- await posthog.reloadFeatureFlags()
4670
-
4671
- // locally evaluate the flags
4672
- const res = await posthog.getAllFlagsAndPayloads('distinct-id', { onlyEvaluateLocally: true })
4673
-
4674
- // expect the flags to be cleared and for the debug message to be logged
4675
- expect(res.featureFlags).toEqual({})
4676
- expect(res.featureFlagPayloads).toEqual({})
4677
- expect(consoleSpy).toHaveBeenCalledWith(
4678
- '[FEATURE FLAGS] Feature flags quota limit exceeded - unsetting all local flags. Learn more about billing limits at https://posthog.com/docs/billing/limits-alerts'
4679
- )
4680
-
4681
- consoleSpy.mockRestore()
4682
- })
4683
- })