@featurevisor/core 2.3.3 → 2.6.0

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 (90) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/coverage/clover.xml +444 -78
  3. package/coverage/coverage-final.json +9 -3
  4. package/coverage/lcov-report/index.html +42 -42
  5. package/coverage/lcov-report/lib/builder/allocator.js.html +1 -1
  6. package/coverage/lcov-report/lib/builder/buildScopedConditions.js.html +373 -0
  7. package/coverage/lcov-report/lib/builder/buildScopedDatafile.js.html +403 -0
  8. package/coverage/lcov-report/lib/builder/buildScopedSegments.js.html +379 -0
  9. package/coverage/lcov-report/lib/builder/index.html +53 -8
  10. package/coverage/lcov-report/lib/builder/revision.js.html +1 -1
  11. package/coverage/lcov-report/lib/builder/traffic.js.html +1 -1
  12. package/coverage/lcov-report/lib/list/index.html +14 -14
  13. package/coverage/lcov-report/lib/list/matrix.js.html +23 -8
  14. package/coverage/lcov-report/lib/tester/helpers.js.html +1 -1
  15. package/coverage/lcov-report/lib/tester/index.html +1 -1
  16. package/coverage/lcov-report/src/builder/allocator.ts.html +1 -1
  17. package/coverage/lcov-report/src/builder/buildScopedConditions.ts.html +487 -0
  18. package/coverage/lcov-report/src/builder/buildScopedDatafile.ts.html +604 -0
  19. package/coverage/lcov-report/src/builder/buildScopedSegments.ts.html +544 -0
  20. package/coverage/lcov-report/src/builder/index.html +55 -10
  21. package/coverage/lcov-report/src/builder/revision.ts.html +1 -1
  22. package/coverage/lcov-report/src/builder/traffic.ts.html +3 -3
  23. package/coverage/lcov-report/src/list/index.html +14 -14
  24. package/coverage/lcov-report/src/list/matrix.ts.html +33 -12
  25. package/coverage/lcov-report/src/tester/helpers.ts.html +1 -1
  26. package/coverage/lcov-report/src/tester/index.html +1 -1
  27. package/coverage/lcov.info +810 -129
  28. package/jest.config.js +8 -0
  29. package/lib/builder/buildDatafile.d.ts +10 -0
  30. package/lib/builder/buildDatafile.js +27 -0
  31. package/lib/builder/buildDatafile.js.map +1 -1
  32. package/lib/builder/buildProject.d.ts +2 -0
  33. package/lib/builder/buildProject.js +38 -4
  34. package/lib/builder/buildProject.js.map +1 -1
  35. package/lib/builder/buildScopedConditions.d.ts +5 -0
  36. package/lib/builder/buildScopedConditions.js +97 -0
  37. package/lib/builder/buildScopedConditions.js.map +1 -0
  38. package/lib/builder/buildScopedConditions.spec.d.ts +1 -0
  39. package/lib/builder/buildScopedConditions.spec.js +2167 -0
  40. package/lib/builder/buildScopedConditions.spec.js.map +1 -0
  41. package/lib/builder/buildScopedDatafile.d.ts +2 -0
  42. package/lib/builder/buildScopedDatafile.js +107 -0
  43. package/lib/builder/buildScopedDatafile.js.map +1 -0
  44. package/lib/builder/buildScopedDatafile.spec.d.ts +1 -0
  45. package/lib/builder/buildScopedDatafile.spec.js +1988 -0
  46. package/lib/builder/buildScopedDatafile.spec.js.map +1 -0
  47. package/lib/builder/buildScopedSegments.d.ts +5 -0
  48. package/lib/builder/buildScopedSegments.js +99 -0
  49. package/lib/builder/buildScopedSegments.js.map +1 -0
  50. package/lib/builder/buildScopedSegments.spec.d.ts +1 -0
  51. package/lib/builder/buildScopedSegments.spec.js +1062 -0
  52. package/lib/builder/buildScopedSegments.spec.js.map +1 -0
  53. package/lib/builder/index.d.ts +1 -0
  54. package/lib/builder/index.js +1 -0
  55. package/lib/builder/index.js.map +1 -1
  56. package/lib/config/projectConfig.d.ts +9 -1
  57. package/lib/config/projectConfig.js +1 -0
  58. package/lib/config/projectConfig.js.map +1 -1
  59. package/lib/datasource/adapter.d.ts +3 -1
  60. package/lib/datasource/adapter.js.map +1 -1
  61. package/lib/datasource/filesystemAdapter.js +3 -1
  62. package/lib/datasource/filesystemAdapter.js.map +1 -1
  63. package/lib/linter/testSchema.d.ts +5 -0
  64. package/lib/linter/testSchema.js +8 -0
  65. package/lib/linter/testSchema.js.map +1 -1
  66. package/lib/list/matrix.js +5 -0
  67. package/lib/list/matrix.js.map +1 -1
  68. package/lib/tester/testFeature.d.ts +5 -3
  69. package/lib/tester/testFeature.js +34 -9
  70. package/lib/tester/testFeature.js.map +1 -1
  71. package/lib/tester/testProject.d.ts +3 -2
  72. package/lib/tester/testProject.js +40 -6
  73. package/lib/tester/testProject.js.map +1 -1
  74. package/package.json +5 -5
  75. package/src/builder/buildDatafile.ts +47 -0
  76. package/src/builder/buildProject.ts +58 -3
  77. package/src/builder/buildScopedConditions.spec.ts +2659 -0
  78. package/src/builder/buildScopedConditions.ts +134 -0
  79. package/src/builder/buildScopedDatafile.spec.ts +2236 -0
  80. package/src/builder/buildScopedDatafile.ts +173 -0
  81. package/src/builder/buildScopedSegments.spec.ts +1573 -0
  82. package/src/builder/buildScopedSegments.ts +153 -0
  83. package/src/builder/index.ts +1 -0
  84. package/src/config/projectConfig.ts +11 -1
  85. package/src/datasource/adapter.ts +4 -1
  86. package/src/datasource/filesystemAdapter.ts +4 -1
  87. package/src/linter/testSchema.ts +12 -0
  88. package/src/list/matrix.ts +7 -0
  89. package/src/tester/testFeature.ts +50 -16
  90. package/src/tester/testProject.ts +68 -8
@@ -0,0 +1,2236 @@
1
+ import type { DatafileContent } from "@featurevisor/types";
2
+ import { buildScopedDatafile } from "./buildScopedDatafile";
3
+
4
+ describe("core: buildScopedDatafile", function () {
5
+ test("buildScopedDatafile is a function", function () {
6
+ expect(buildScopedDatafile).toBeInstanceOf(Function);
7
+ });
8
+
9
+ describe("basic functionality", function () {
10
+ test("empty datafile", function () {
11
+ const datafile: DatafileContent = {
12
+ schemaVersion: "2",
13
+ revision: "unknown",
14
+ segments: {},
15
+ features: {},
16
+ };
17
+
18
+ const result = buildScopedDatafile(datafile, {});
19
+
20
+ expect(result).toEqual(datafile);
21
+ });
22
+
23
+ test("datafile with no segments", function () {
24
+ const datafile: DatafileContent = {
25
+ schemaVersion: "2",
26
+ revision: "unknown",
27
+ segments: {},
28
+ features: {
29
+ feature1: {
30
+ bucketBy: ["userId"],
31
+ traffic: [
32
+ {
33
+ key: "rule1",
34
+ segments: "*",
35
+ percentage: 100000,
36
+ },
37
+ ],
38
+ },
39
+ },
40
+ };
41
+
42
+ const result = buildScopedDatafile(datafile, {});
43
+
44
+ expect(result.segments).toEqual({});
45
+ expect(result.features.feature1).toBeDefined();
46
+ });
47
+
48
+ test("datafile with no features", function () {
49
+ const datafile: DatafileContent = {
50
+ schemaVersion: "2",
51
+ revision: "unknown",
52
+ segments: {
53
+ web: {
54
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
55
+ },
56
+ },
57
+ features: {},
58
+ };
59
+
60
+ const result = buildScopedDatafile(datafile, { platform: "web" });
61
+
62
+ expect(result.segments).toEqual({});
63
+ expect(result.features).toEqual({});
64
+ });
65
+ });
66
+
67
+ describe("segments processing", function () {
68
+ test("segment with matching condition becomes * and is removed", function () {
69
+ const datafile: DatafileContent = {
70
+ schemaVersion: "2",
71
+ revision: "unknown",
72
+ segments: {
73
+ web: {
74
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
75
+ },
76
+ },
77
+ features: {},
78
+ };
79
+
80
+ const result = buildScopedDatafile(datafile, { platform: "web" });
81
+
82
+ expect(result.segments).toEqual({});
83
+ });
84
+
85
+ test("segment with non-matching condition remains", function () {
86
+ const datafile: DatafileContent = {
87
+ schemaVersion: "2",
88
+ revision: "unknown",
89
+ segments: {
90
+ web: {
91
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
92
+ },
93
+ },
94
+ features: {},
95
+ };
96
+
97
+ const result = buildScopedDatafile(datafile, { platform: "mobile" });
98
+
99
+ expect(result.segments.web).toBeDefined();
100
+ expect(result.segments.web.conditions).toEqual([
101
+ { attribute: "platform", operator: "equals", value: "web" },
102
+ ]);
103
+ });
104
+
105
+ test("segment with * condition is removed", function () {
106
+ const datafile: DatafileContent = {
107
+ schemaVersion: "2",
108
+ revision: "unknown",
109
+ segments: {
110
+ all: {
111
+ conditions: "*",
112
+ },
113
+ },
114
+ features: {},
115
+ };
116
+
117
+ const result = buildScopedDatafile(datafile, {});
118
+
119
+ expect(result.segments).toEqual({});
120
+ });
121
+
122
+ test("multiple segments - some match, some don't", function () {
123
+ const datafile: DatafileContent = {
124
+ schemaVersion: "2",
125
+ revision: "unknown",
126
+ segments: {
127
+ web: {
128
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
129
+ },
130
+ mobile: {
131
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
132
+ },
133
+ chrome: {
134
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
135
+ },
136
+ },
137
+ features: {},
138
+ };
139
+
140
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
141
+
142
+ expect(result.segments.web).toBeUndefined();
143
+ expect(result.segments.mobile).toBeDefined();
144
+ expect(result.segments.chrome).toBeUndefined();
145
+ });
146
+
147
+ test("segment with AND conditions - all match", function () {
148
+ const datafile: DatafileContent = {
149
+ schemaVersion: "2",
150
+ revision: "unknown",
151
+ segments: {
152
+ premium: {
153
+ conditions: {
154
+ and: [
155
+ { attribute: "tier", operator: "equals", value: "premium" },
156
+ { attribute: "status", operator: "equals", value: "active" },
157
+ ],
158
+ },
159
+ },
160
+ },
161
+ features: {},
162
+ };
163
+
164
+ const result = buildScopedDatafile(datafile, { tier: "premium", status: "active" });
165
+
166
+ expect(result.segments).toEqual({});
167
+ });
168
+
169
+ test("segment with AND conditions - partial match", function () {
170
+ const datafile: DatafileContent = {
171
+ schemaVersion: "2",
172
+ revision: "unknown",
173
+ segments: {
174
+ premium: {
175
+ conditions: {
176
+ and: [
177
+ { attribute: "tier", operator: "equals", value: "premium" },
178
+ { attribute: "status", operator: "equals", value: "active" },
179
+ ],
180
+ },
181
+ },
182
+ },
183
+ features: {},
184
+ };
185
+
186
+ const result = buildScopedDatafile(datafile, { tier: "premium" });
187
+
188
+ expect(result.segments.premium).toBeDefined();
189
+ expect(result.segments.premium.conditions).toEqual({
190
+ and: [{ attribute: "status", operator: "equals", value: "active" }],
191
+ });
192
+ });
193
+
194
+ test("segment with OR conditions - one matches", function () {
195
+ const datafile: DatafileContent = {
196
+ schemaVersion: "2",
197
+ revision: "unknown",
198
+ segments: {
199
+ browser: {
200
+ conditions: {
201
+ or: [
202
+ { attribute: "browser", operator: "equals", value: "chrome" },
203
+ { attribute: "browser", operator: "equals", value: "firefox" },
204
+ ],
205
+ },
206
+ },
207
+ },
208
+ features: {},
209
+ };
210
+
211
+ const result = buildScopedDatafile(datafile, { browser: "chrome" });
212
+
213
+ expect(result.segments).toEqual({
214
+ browser: {
215
+ conditions: {
216
+ // @TODO: this OR (with only one child) can become a single (AND) condition
217
+ or: [
218
+ {
219
+ attribute: "browser",
220
+ operator: "equals",
221
+ value: "firefox",
222
+ },
223
+ ],
224
+ },
225
+ },
226
+ });
227
+ });
228
+
229
+ test("segment with array of conditions - all match", function () {
230
+ const datafile: DatafileContent = {
231
+ schemaVersion: "2",
232
+ revision: "unknown",
233
+ segments: {
234
+ combined: {
235
+ conditions: [
236
+ { attribute: "platform", operator: "equals", value: "web" },
237
+ { attribute: "browser", operator: "equals", value: "chrome" },
238
+ ],
239
+ },
240
+ },
241
+ features: {},
242
+ };
243
+
244
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
245
+
246
+ expect(result.segments).toEqual({});
247
+ });
248
+
249
+ test("segment with array of conditions - partial match", function () {
250
+ const datafile: DatafileContent = {
251
+ schemaVersion: "2",
252
+ revision: "unknown",
253
+ segments: {
254
+ combined: {
255
+ conditions: [
256
+ { attribute: "platform", operator: "equals", value: "web" },
257
+ { attribute: "browser", operator: "equals", value: "chrome" },
258
+ ],
259
+ },
260
+ },
261
+ features: {},
262
+ };
263
+
264
+ const result = buildScopedDatafile(datafile, { platform: "web" });
265
+
266
+ expect(result.segments.combined).toBeDefined();
267
+ expect(result.segments.combined.conditions).toEqual([
268
+ { attribute: "browser", operator: "equals", value: "chrome" },
269
+ ]);
270
+ });
271
+ });
272
+
273
+ describe("features with force entries", function () {
274
+ test("force with matching segments becomes *", function () {
275
+ const datafile: DatafileContent = {
276
+ schemaVersion: "2",
277
+ revision: "unknown",
278
+ segments: {
279
+ web: {
280
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
281
+ },
282
+ },
283
+ features: {
284
+ feature1: {
285
+ bucketBy: ["userId"],
286
+ traffic: [],
287
+ force: [
288
+ {
289
+ segments: "web",
290
+ variation: "treatment",
291
+ },
292
+ ],
293
+ },
294
+ },
295
+ };
296
+
297
+ const result = buildScopedDatafile(datafile, { platform: "web" });
298
+
299
+ expect(result.features.feature1.force?.[0].segments).toEqual("*");
300
+ });
301
+
302
+ test("force with non-matching segments remains", function () {
303
+ const datafile: DatafileContent = {
304
+ schemaVersion: "2",
305
+ revision: "unknown",
306
+ segments: {
307
+ web: {
308
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
309
+ },
310
+ },
311
+ features: {
312
+ feature1: {
313
+ bucketBy: ["userId"],
314
+ traffic: [],
315
+ force: [
316
+ {
317
+ segments: "web",
318
+ variation: "treatment",
319
+ },
320
+ ],
321
+ },
322
+ },
323
+ };
324
+
325
+ const result = buildScopedDatafile(datafile, { platform: "mobile" });
326
+
327
+ expect(result.features.feature1.force?.[0].segments).toEqual("web");
328
+ });
329
+
330
+ test("force with matching conditions becomes *", function () {
331
+ const datafile: DatafileContent = {
332
+ schemaVersion: "2",
333
+ revision: "unknown",
334
+ segments: {},
335
+ features: {
336
+ feature1: {
337
+ bucketBy: ["userId"],
338
+ traffic: [],
339
+ force: [
340
+ {
341
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
342
+ variation: "treatment",
343
+ },
344
+ ],
345
+ },
346
+ },
347
+ };
348
+
349
+ const result = buildScopedDatafile(datafile, { platform: "web" });
350
+
351
+ expect(result.features.feature1.force?.[0].conditions).toEqual("*");
352
+ });
353
+
354
+ test("force with non-matching conditions remains", function () {
355
+ const datafile: DatafileContent = {
356
+ schemaVersion: "2",
357
+ revision: "unknown",
358
+ segments: {},
359
+ features: {
360
+ feature1: {
361
+ bucketBy: ["userId"],
362
+ traffic: [],
363
+ force: [
364
+ {
365
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
366
+ variation: "treatment",
367
+ },
368
+ ],
369
+ },
370
+ },
371
+ };
372
+
373
+ const result = buildScopedDatafile(datafile, { platform: "mobile" });
374
+
375
+ expect(result.features.feature1.force?.[0].conditions).toEqual([
376
+ { attribute: "platform", operator: "equals", value: "web" },
377
+ ]);
378
+ });
379
+
380
+ test("force with array of segments - all match", function () {
381
+ const datafile: DatafileContent = {
382
+ schemaVersion: "2",
383
+ revision: "unknown",
384
+ segments: {
385
+ web: {
386
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
387
+ },
388
+ chrome: {
389
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
390
+ },
391
+ },
392
+ features: {
393
+ feature1: {
394
+ bucketBy: ["userId"],
395
+ traffic: [],
396
+ force: [
397
+ {
398
+ segments: ["web", "chrome"],
399
+ variation: "treatment",
400
+ },
401
+ ],
402
+ },
403
+ },
404
+ };
405
+
406
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
407
+
408
+ expect(result.features.feature1.force?.[0].segments).toEqual("*");
409
+ });
410
+
411
+ test("force with array of segments - partial match", function () {
412
+ const datafile: DatafileContent = {
413
+ schemaVersion: "2",
414
+ revision: "unknown",
415
+ segments: {
416
+ web: {
417
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
418
+ },
419
+ chrome: {
420
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
421
+ },
422
+ },
423
+ features: {
424
+ feature1: {
425
+ bucketBy: ["userId"],
426
+ traffic: [],
427
+ force: [
428
+ {
429
+ segments: ["web", "chrome"],
430
+ variation: "treatment",
431
+ },
432
+ ],
433
+ },
434
+ },
435
+ };
436
+
437
+ const result = buildScopedDatafile(datafile, { platform: "web" });
438
+
439
+ expect(result.features.feature1.force?.[0].segments).toEqual(["chrome"]);
440
+ });
441
+
442
+ test("force with AND group segments", function () {
443
+ const datafile: DatafileContent = {
444
+ schemaVersion: "2",
445
+ revision: "unknown",
446
+ segments: {
447
+ web: {
448
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
449
+ },
450
+ chrome: {
451
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
452
+ },
453
+ },
454
+ features: {
455
+ feature1: {
456
+ bucketBy: ["userId"],
457
+ traffic: [],
458
+ force: [
459
+ {
460
+ segments: { and: ["web", "chrome"] },
461
+ variation: "treatment",
462
+ },
463
+ ],
464
+ },
465
+ },
466
+ };
467
+
468
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
469
+
470
+ expect(result.features.feature1.force?.[0].segments).toEqual("*");
471
+ });
472
+
473
+ test("force with OR group segments", function () {
474
+ const datafile: DatafileContent = {
475
+ schemaVersion: "2",
476
+ revision: "unknown",
477
+ segments: {
478
+ web: {
479
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
480
+ },
481
+ mobile: {
482
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
483
+ },
484
+ },
485
+ features: {
486
+ feature1: {
487
+ bucketBy: ["userId"],
488
+ traffic: [],
489
+ force: [
490
+ {
491
+ segments: { or: ["web", "mobile"] },
492
+ variation: "treatment",
493
+ },
494
+ ],
495
+ },
496
+ },
497
+ };
498
+
499
+ const result = buildScopedDatafile(datafile, { platform: "web" });
500
+
501
+ expect(result.features.feature1.force?.[0].segments).toEqual({ or: ["mobile"] });
502
+ });
503
+
504
+ test("force with AND conditions", function () {
505
+ const datafile: DatafileContent = {
506
+ schemaVersion: "2",
507
+ revision: "unknown",
508
+ segments: {},
509
+ features: {
510
+ feature1: {
511
+ bucketBy: ["userId"],
512
+ traffic: [],
513
+ force: [
514
+ {
515
+ conditions: {
516
+ and: [
517
+ { attribute: "platform", operator: "equals", value: "web" },
518
+ { attribute: "browser", operator: "equals", value: "chrome" },
519
+ ],
520
+ },
521
+ variation: "treatment",
522
+ },
523
+ ],
524
+ },
525
+ },
526
+ };
527
+
528
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
529
+
530
+ expect(result.features.feature1.force?.[0].conditions).toEqual("*");
531
+ });
532
+
533
+ test("force with OR conditions", function () {
534
+ const datafile: DatafileContent = {
535
+ schemaVersion: "2",
536
+ revision: "unknown",
537
+ segments: {},
538
+ features: {
539
+ feature1: {
540
+ bucketBy: ["userId"],
541
+ traffic: [],
542
+ force: [
543
+ {
544
+ conditions: {
545
+ or: [
546
+ { attribute: "platform", operator: "equals", value: "web" },
547
+ { attribute: "platform", operator: "equals", value: "mobile" },
548
+ ],
549
+ },
550
+ variation: "treatment",
551
+ },
552
+ ],
553
+ },
554
+ },
555
+ };
556
+
557
+ const result = buildScopedDatafile(datafile, { platform: "web" });
558
+
559
+ expect(result.features.feature1.force?.[0].conditions).toEqual({
560
+ or: [{ attribute: "platform", operator: "equals", value: "mobile" }],
561
+ });
562
+ });
563
+
564
+ test("multiple force entries", function () {
565
+ const datafile: DatafileContent = {
566
+ schemaVersion: "2",
567
+ revision: "unknown",
568
+ segments: {
569
+ web: {
570
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
571
+ },
572
+ mobile: {
573
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
574
+ },
575
+ },
576
+ features: {
577
+ feature1: {
578
+ bucketBy: ["userId"],
579
+ traffic: [],
580
+ force: [
581
+ {
582
+ segments: "web",
583
+ variation: "treatment",
584
+ },
585
+ {
586
+ segments: "mobile",
587
+ variation: "control",
588
+ },
589
+ ],
590
+ },
591
+ },
592
+ };
593
+
594
+ const result = buildScopedDatafile(datafile, { platform: "web" });
595
+
596
+ expect(result.features.feature1.force?.[0].segments).toEqual("*");
597
+ expect(result.features.feature1.force?.[1].segments).toEqual("mobile");
598
+ });
599
+ });
600
+
601
+ describe("features with traffic entries", function () {
602
+ test("traffic with matching segments becomes *", function () {
603
+ const datafile: DatafileContent = {
604
+ schemaVersion: "2",
605
+ revision: "unknown",
606
+ segments: {
607
+ web: {
608
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
609
+ },
610
+ },
611
+ features: {
612
+ feature1: {
613
+ bucketBy: ["userId"],
614
+ traffic: [
615
+ {
616
+ key: "rule1",
617
+ segments: "web",
618
+ percentage: 100000,
619
+ },
620
+ ],
621
+ },
622
+ },
623
+ };
624
+
625
+ const result = buildScopedDatafile(datafile, { platform: "web" });
626
+
627
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
628
+ });
629
+
630
+ test("traffic with non-matching segments remains", function () {
631
+ const datafile: DatafileContent = {
632
+ schemaVersion: "2",
633
+ revision: "unknown",
634
+ segments: {
635
+ web: {
636
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
637
+ },
638
+ },
639
+ features: {
640
+ feature1: {
641
+ bucketBy: ["userId"],
642
+ traffic: [
643
+ {
644
+ key: "rule1",
645
+ segments: "web",
646
+ percentage: 100000,
647
+ },
648
+ ],
649
+ },
650
+ },
651
+ };
652
+
653
+ const result = buildScopedDatafile(datafile, { platform: "mobile" });
654
+
655
+ expect(result.features.feature1.traffic[0].segments).toEqual("web");
656
+ });
657
+
658
+ test("traffic with array of segments - all match", function () {
659
+ const datafile: DatafileContent = {
660
+ schemaVersion: "2",
661
+ revision: "unknown",
662
+ segments: {
663
+ web: {
664
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
665
+ },
666
+ chrome: {
667
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
668
+ },
669
+ },
670
+ features: {
671
+ feature1: {
672
+ bucketBy: ["userId"],
673
+ traffic: [
674
+ {
675
+ key: "rule1",
676
+ segments: ["web", "chrome"],
677
+ percentage: 100000,
678
+ },
679
+ ],
680
+ },
681
+ },
682
+ };
683
+
684
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
685
+
686
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
687
+ });
688
+
689
+ test("traffic with array of segments - partial match", function () {
690
+ const datafile: DatafileContent = {
691
+ schemaVersion: "2",
692
+ revision: "unknown",
693
+ segments: {
694
+ web: {
695
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
696
+ },
697
+ chrome: {
698
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
699
+ },
700
+ },
701
+ features: {
702
+ feature1: {
703
+ bucketBy: ["userId"],
704
+ traffic: [
705
+ {
706
+ key: "rule1",
707
+ segments: ["web", "chrome"],
708
+ percentage: 100000,
709
+ },
710
+ ],
711
+ },
712
+ },
713
+ };
714
+
715
+ const result = buildScopedDatafile(datafile, { platform: "web" });
716
+
717
+ expect(result.features.feature1.traffic[0].segments).toEqual(["chrome"]);
718
+ });
719
+
720
+ test("traffic with AND group segments", function () {
721
+ const datafile: DatafileContent = {
722
+ schemaVersion: "2",
723
+ revision: "unknown",
724
+ segments: {
725
+ web: {
726
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
727
+ },
728
+ chrome: {
729
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
730
+ },
731
+ },
732
+ features: {
733
+ feature1: {
734
+ bucketBy: ["userId"],
735
+ traffic: [
736
+ {
737
+ key: "rule1",
738
+ segments: { and: ["web", "chrome"] },
739
+ percentage: 100000,
740
+ },
741
+ ],
742
+ },
743
+ },
744
+ };
745
+
746
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
747
+
748
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
749
+ });
750
+
751
+ test("traffic with OR group segments", function () {
752
+ const datafile: DatafileContent = {
753
+ schemaVersion: "2",
754
+ revision: "unknown",
755
+ segments: {
756
+ web: {
757
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
758
+ },
759
+ mobile: {
760
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
761
+ },
762
+ },
763
+ features: {
764
+ feature1: {
765
+ bucketBy: ["userId"],
766
+ traffic: [
767
+ {
768
+ key: "rule1",
769
+ segments: { or: ["web", "mobile"] },
770
+ percentage: 100000,
771
+ },
772
+ ],
773
+ },
774
+ },
775
+ };
776
+
777
+ const result = buildScopedDatafile(datafile, { platform: "web" });
778
+
779
+ expect(result.features.feature1.traffic[0].segments).toEqual({ or: ["mobile"] });
780
+ });
781
+
782
+ test("multiple traffic entries", function () {
783
+ const datafile: DatafileContent = {
784
+ schemaVersion: "2",
785
+ revision: "unknown",
786
+ segments: {
787
+ web: {
788
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
789
+ },
790
+ mobile: {
791
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
792
+ },
793
+ },
794
+ features: {
795
+ feature1: {
796
+ bucketBy: ["userId"],
797
+ traffic: [
798
+ {
799
+ key: "rule1",
800
+ segments: "web",
801
+ percentage: 50000,
802
+ },
803
+ {
804
+ key: "rule2",
805
+ segments: "mobile",
806
+ percentage: 50000,
807
+ },
808
+ ],
809
+ },
810
+ },
811
+ };
812
+
813
+ const result = buildScopedDatafile(datafile, { platform: "web" });
814
+
815
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
816
+ expect(result.features.feature1.traffic[1].segments).toEqual("mobile");
817
+ });
818
+
819
+ test("consecutive traffic entries with * segments - second is removed", function () {
820
+ const datafile: DatafileContent = {
821
+ schemaVersion: "2",
822
+ revision: "unknown",
823
+ segments: {
824
+ web: {
825
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
826
+ },
827
+ },
828
+ features: {
829
+ feature1: {
830
+ bucketBy: ["userId"],
831
+ traffic: [
832
+ {
833
+ key: "rule1",
834
+ segments: "*",
835
+ percentage: 50000,
836
+ },
837
+ {
838
+ key: "rule2",
839
+ segments: "*",
840
+ percentage: 50000,
841
+ },
842
+ ],
843
+ },
844
+ },
845
+ };
846
+
847
+ const result = buildScopedDatafile(datafile, {});
848
+
849
+ expect(result.features.feature1.traffic).toHaveLength(1);
850
+ expect(result.features.feature1.traffic[0].key).toEqual("rule1");
851
+ });
852
+
853
+ test("consecutive traffic entries with * segments - multiple consecutive removed", function () {
854
+ const datafile: DatafileContent = {
855
+ schemaVersion: "2",
856
+ revision: "unknown",
857
+ segments: {
858
+ web: {
859
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
860
+ },
861
+ },
862
+ features: {
863
+ feature1: {
864
+ bucketBy: ["userId"],
865
+ traffic: [
866
+ {
867
+ key: "rule1",
868
+ segments: "*",
869
+ percentage: 25000,
870
+ },
871
+ ],
872
+ },
873
+ },
874
+ };
875
+
876
+ const result = buildScopedDatafile(datafile, { platform: "web" });
877
+
878
+ expect(result.features.feature1.traffic).toHaveLength(1);
879
+ expect(result.features.feature1.traffic[0].key).toEqual("rule1");
880
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
881
+ });
882
+
883
+ test("non-consecutive traffic entries with * segments - both remain", function () {
884
+ const datafile: DatafileContent = {
885
+ schemaVersion: "2",
886
+ revision: "unknown",
887
+ segments: {
888
+ web: {
889
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
890
+ },
891
+ mobile: {
892
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
893
+ },
894
+ },
895
+ features: {
896
+ feature1: {
897
+ bucketBy: ["userId"],
898
+ traffic: [
899
+ {
900
+ key: "rule1",
901
+ segments: "*",
902
+ percentage: 33333,
903
+ },
904
+ {
905
+ key: "rule2",
906
+ segments: "mobile",
907
+ percentage: 33333,
908
+ },
909
+ {
910
+ key: "rule3",
911
+ segments: "*",
912
+ percentage: 33334,
913
+ },
914
+ ],
915
+ },
916
+ },
917
+ };
918
+
919
+ const result = buildScopedDatafile(datafile, {});
920
+
921
+ expect(result.features.feature1.traffic).toHaveLength(3);
922
+ });
923
+
924
+ test("traffic entry that becomes * after scoping - consecutive removal works", function () {
925
+ const datafile: DatafileContent = {
926
+ schemaVersion: "2",
927
+ revision: "unknown",
928
+ segments: {
929
+ web: {
930
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
931
+ },
932
+ },
933
+ features: {
934
+ feature1: {
935
+ bucketBy: ["userId"],
936
+ traffic: [
937
+ {
938
+ key: "rule1",
939
+ segments: "*",
940
+ percentage: 50000,
941
+ },
942
+ {
943
+ key: "rule2",
944
+ segments: "web",
945
+ percentage: 50000,
946
+ },
947
+ ],
948
+ },
949
+ },
950
+ };
951
+
952
+ const result = buildScopedDatafile(datafile, { platform: "web" });
953
+
954
+ expect(result.features.feature1.traffic).toHaveLength(1);
955
+ expect(result.features.feature1.traffic[0].key).toEqual("rule1");
956
+ });
957
+ });
958
+
959
+ describe("features with variations and variableOverrides", function () {
960
+ test("variableOverride with matching segments becomes *", function () {
961
+ const datafile: DatafileContent = {
962
+ schemaVersion: "2",
963
+ revision: "unknown",
964
+ segments: {
965
+ web: {
966
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
967
+ },
968
+ },
969
+ features: {
970
+ feature1: {
971
+ bucketBy: ["userId"],
972
+ traffic: [],
973
+ variations: [
974
+ {
975
+ value: "control",
976
+ variableOverrides: {
977
+ color: [
978
+ {
979
+ value: "red",
980
+ segments: "web",
981
+ },
982
+ ],
983
+ },
984
+ },
985
+ ],
986
+ },
987
+ },
988
+ };
989
+
990
+ const result = buildScopedDatafile(datafile, { platform: "web" });
991
+
992
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
993
+ "*",
994
+ );
995
+ });
996
+
997
+ test("variableOverride with non-matching segments remains", function () {
998
+ const datafile: DatafileContent = {
999
+ schemaVersion: "2",
1000
+ revision: "unknown",
1001
+ segments: {
1002
+ web: {
1003
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1004
+ },
1005
+ },
1006
+ features: {
1007
+ feature1: {
1008
+ bucketBy: ["userId"],
1009
+ traffic: [],
1010
+ variations: [
1011
+ {
1012
+ value: "control",
1013
+ variableOverrides: {
1014
+ color: [
1015
+ {
1016
+ value: "red",
1017
+ segments: "web",
1018
+ },
1019
+ ],
1020
+ },
1021
+ },
1022
+ ],
1023
+ },
1024
+ },
1025
+ };
1026
+
1027
+ const result = buildScopedDatafile(datafile, { platform: "mobile" });
1028
+
1029
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1030
+ "web",
1031
+ );
1032
+ });
1033
+
1034
+ test("variableOverride with matching conditions becomes *", function () {
1035
+ const datafile: DatafileContent = {
1036
+ schemaVersion: "2",
1037
+ revision: "unknown",
1038
+ segments: {},
1039
+ features: {
1040
+ feature1: {
1041
+ bucketBy: ["userId"],
1042
+ traffic: [],
1043
+ variations: [
1044
+ {
1045
+ value: "control",
1046
+ variableOverrides: {
1047
+ color: [
1048
+ {
1049
+ value: "red",
1050
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1051
+ },
1052
+ ],
1053
+ },
1054
+ },
1055
+ ],
1056
+ },
1057
+ },
1058
+ };
1059
+
1060
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1061
+
1062
+ expect(
1063
+ result.features.feature1.variations?.[0].variableOverrides?.color[0].conditions,
1064
+ ).toEqual("*");
1065
+ });
1066
+
1067
+ test("variableOverride with non-matching conditions remains", function () {
1068
+ const datafile: DatafileContent = {
1069
+ schemaVersion: "2",
1070
+ revision: "unknown",
1071
+ segments: {},
1072
+ features: {
1073
+ feature1: {
1074
+ bucketBy: ["userId"],
1075
+ traffic: [],
1076
+ variations: [
1077
+ {
1078
+ value: "control",
1079
+ variableOverrides: {
1080
+ color: [
1081
+ {
1082
+ value: "red",
1083
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1084
+ },
1085
+ ],
1086
+ },
1087
+ },
1088
+ ],
1089
+ },
1090
+ },
1091
+ };
1092
+
1093
+ const result = buildScopedDatafile(datafile, { platform: "mobile" });
1094
+
1095
+ expect(
1096
+ result.features.feature1.variations?.[0].variableOverrides?.color[0].conditions,
1097
+ ).toEqual([{ attribute: "platform", operator: "equals", value: "web" }]);
1098
+ });
1099
+
1100
+ test("variableOverride with array of segments", function () {
1101
+ const datafile: DatafileContent = {
1102
+ schemaVersion: "2",
1103
+ revision: "unknown",
1104
+ segments: {
1105
+ web: {
1106
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1107
+ },
1108
+ chrome: {
1109
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
1110
+ },
1111
+ },
1112
+ features: {
1113
+ feature1: {
1114
+ bucketBy: ["userId"],
1115
+ traffic: [],
1116
+ variations: [
1117
+ {
1118
+ value: "control",
1119
+ variableOverrides: {
1120
+ color: [
1121
+ {
1122
+ value: "red",
1123
+ segments: ["web", "chrome"],
1124
+ },
1125
+ ],
1126
+ },
1127
+ },
1128
+ ],
1129
+ },
1130
+ },
1131
+ };
1132
+
1133
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
1134
+
1135
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1136
+ "*",
1137
+ );
1138
+ });
1139
+
1140
+ test("variableOverride with AND group segments", function () {
1141
+ const datafile: DatafileContent = {
1142
+ schemaVersion: "2",
1143
+ revision: "unknown",
1144
+ segments: {
1145
+ web: {
1146
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1147
+ },
1148
+ chrome: {
1149
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
1150
+ },
1151
+ },
1152
+ features: {
1153
+ feature1: {
1154
+ bucketBy: ["userId"],
1155
+ traffic: [],
1156
+ variations: [
1157
+ {
1158
+ value: "control",
1159
+ variableOverrides: {
1160
+ color: [
1161
+ {
1162
+ value: "red",
1163
+ segments: { and: ["web", "chrome"] },
1164
+ },
1165
+ ],
1166
+ },
1167
+ },
1168
+ ],
1169
+ },
1170
+ },
1171
+ };
1172
+
1173
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
1174
+
1175
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1176
+ "*",
1177
+ );
1178
+ });
1179
+
1180
+ test("variableOverride with OR group segments", function () {
1181
+ const datafile: DatafileContent = {
1182
+ schemaVersion: "2",
1183
+ revision: "unknown",
1184
+ segments: {
1185
+ web: {
1186
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1187
+ },
1188
+ mobile: {
1189
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
1190
+ },
1191
+ },
1192
+ features: {
1193
+ feature1: {
1194
+ bucketBy: ["userId"],
1195
+ traffic: [],
1196
+ variations: [
1197
+ {
1198
+ value: "control",
1199
+ variableOverrides: {
1200
+ color: [
1201
+ {
1202
+ value: "red",
1203
+ segments: { or: ["web", "mobile"] },
1204
+ },
1205
+ ],
1206
+ },
1207
+ },
1208
+ ],
1209
+ },
1210
+ },
1211
+ };
1212
+
1213
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1214
+
1215
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1216
+ { or: ["mobile"] },
1217
+ );
1218
+ });
1219
+
1220
+ test("variableOverride with AND conditions", function () {
1221
+ const datafile: DatafileContent = {
1222
+ schemaVersion: "2",
1223
+ revision: "unknown",
1224
+ segments: {},
1225
+ features: {
1226
+ feature1: {
1227
+ bucketBy: ["userId"],
1228
+ traffic: [],
1229
+ variations: [
1230
+ {
1231
+ value: "control",
1232
+ variableOverrides: {
1233
+ color: [
1234
+ {
1235
+ value: "red",
1236
+ conditions: {
1237
+ and: [
1238
+ { attribute: "platform", operator: "equals", value: "web" },
1239
+ { attribute: "browser", operator: "equals", value: "chrome" },
1240
+ ],
1241
+ },
1242
+ },
1243
+ ],
1244
+ },
1245
+ },
1246
+ ],
1247
+ },
1248
+ },
1249
+ };
1250
+
1251
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
1252
+
1253
+ expect(
1254
+ result.features.feature1.variations?.[0].variableOverrides?.color[0].conditions,
1255
+ ).toEqual("*");
1256
+ });
1257
+
1258
+ test("variableOverride with OR conditions", function () {
1259
+ const datafile: DatafileContent = {
1260
+ schemaVersion: "2",
1261
+ revision: "unknown",
1262
+ segments: {},
1263
+ features: {
1264
+ feature1: {
1265
+ bucketBy: ["userId"],
1266
+ traffic: [],
1267
+ variations: [
1268
+ {
1269
+ value: "control",
1270
+ variableOverrides: {
1271
+ color: [
1272
+ {
1273
+ value: "red",
1274
+ conditions: {
1275
+ or: [
1276
+ { attribute: "platform", operator: "equals", value: "web" },
1277
+ { attribute: "platform", operator: "equals", value: "mobile" },
1278
+ ],
1279
+ },
1280
+ },
1281
+ ],
1282
+ },
1283
+ },
1284
+ ],
1285
+ },
1286
+ },
1287
+ };
1288
+
1289
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1290
+
1291
+ expect(
1292
+ result.features.feature1.variations?.[0].variableOverrides?.color[0].conditions,
1293
+ ).toEqual({
1294
+ or: [{ attribute: "platform", operator: "equals", value: "mobile" }],
1295
+ });
1296
+ });
1297
+
1298
+ test("multiple variableOverrides for same variable", function () {
1299
+ const datafile: DatafileContent = {
1300
+ schemaVersion: "2",
1301
+ revision: "unknown",
1302
+ segments: {
1303
+ web: {
1304
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1305
+ },
1306
+ mobile: {
1307
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
1308
+ },
1309
+ },
1310
+ features: {
1311
+ feature1: {
1312
+ bucketBy: ["userId"],
1313
+ traffic: [],
1314
+ variations: [
1315
+ {
1316
+ value: "control",
1317
+ variableOverrides: {
1318
+ color: [
1319
+ {
1320
+ value: "red",
1321
+ segments: "web",
1322
+ },
1323
+ {
1324
+ value: "blue",
1325
+ segments: "mobile",
1326
+ },
1327
+ ],
1328
+ },
1329
+ },
1330
+ ],
1331
+ },
1332
+ },
1333
+ };
1334
+
1335
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1336
+
1337
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1338
+ "*",
1339
+ );
1340
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[1].segments).toEqual(
1341
+ "mobile",
1342
+ );
1343
+ });
1344
+
1345
+ test("multiple variables with variableOverrides", function () {
1346
+ const datafile: DatafileContent = {
1347
+ schemaVersion: "2",
1348
+ revision: "unknown",
1349
+ segments: {
1350
+ web: {
1351
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1352
+ },
1353
+ },
1354
+ features: {
1355
+ feature1: {
1356
+ bucketBy: ["userId"],
1357
+ traffic: [],
1358
+ variations: [
1359
+ {
1360
+ value: "control",
1361
+ variableOverrides: {
1362
+ color: [
1363
+ {
1364
+ value: "red",
1365
+ segments: "web",
1366
+ },
1367
+ ],
1368
+ size: [
1369
+ {
1370
+ value: "large",
1371
+ segments: "web",
1372
+ },
1373
+ ],
1374
+ },
1375
+ },
1376
+ ],
1377
+ },
1378
+ },
1379
+ };
1380
+
1381
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1382
+
1383
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1384
+ "*",
1385
+ );
1386
+ expect(result.features.feature1.variations?.[0].variableOverrides?.size[0].segments).toEqual(
1387
+ "*",
1388
+ );
1389
+ });
1390
+
1391
+ test("multiple variations with variableOverrides", function () {
1392
+ const datafile: DatafileContent = {
1393
+ schemaVersion: "2",
1394
+ revision: "unknown",
1395
+ segments: {
1396
+ web: {
1397
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1398
+ },
1399
+ mobile: {
1400
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
1401
+ },
1402
+ },
1403
+ features: {
1404
+ feature1: {
1405
+ bucketBy: ["userId"],
1406
+ traffic: [],
1407
+ variations: [
1408
+ {
1409
+ value: "control",
1410
+ variableOverrides: {
1411
+ color: [
1412
+ {
1413
+ value: "red",
1414
+ segments: "web",
1415
+ },
1416
+ ],
1417
+ },
1418
+ },
1419
+ {
1420
+ value: "treatment",
1421
+ variableOverrides: {
1422
+ color: [
1423
+ {
1424
+ value: "blue",
1425
+ segments: "mobile",
1426
+ },
1427
+ ],
1428
+ },
1429
+ },
1430
+ ],
1431
+ },
1432
+ },
1433
+ };
1434
+
1435
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1436
+
1437
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1438
+ "*",
1439
+ );
1440
+ expect(result.features.feature1.variations?.[1].variableOverrides?.color[0].segments).toEqual(
1441
+ "mobile",
1442
+ );
1443
+ });
1444
+ });
1445
+
1446
+ describe("complex nested scenarios", function () {
1447
+ test("feature with force, traffic, and variations", function () {
1448
+ const datafile: DatafileContent = {
1449
+ schemaVersion: "2",
1450
+ revision: "unknown",
1451
+ segments: {
1452
+ web: {
1453
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1454
+ },
1455
+ chrome: {
1456
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
1457
+ },
1458
+ },
1459
+ features: {
1460
+ feature1: {
1461
+ bucketBy: ["userId"],
1462
+ traffic: [
1463
+ {
1464
+ key: "rule1",
1465
+ segments: "web",
1466
+ percentage: 100000,
1467
+ },
1468
+ ],
1469
+ force: [
1470
+ {
1471
+ segments: "chrome",
1472
+ variation: "treatment",
1473
+ },
1474
+ ],
1475
+ variations: [
1476
+ {
1477
+ value: "control",
1478
+ variableOverrides: {
1479
+ color: [
1480
+ {
1481
+ value: "red",
1482
+ segments: "web",
1483
+ },
1484
+ ],
1485
+ },
1486
+ },
1487
+ ],
1488
+ },
1489
+ },
1490
+ };
1491
+
1492
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
1493
+
1494
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
1495
+ expect(result.features.feature1.force?.[0].segments).toEqual("*");
1496
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1497
+ "*",
1498
+ );
1499
+ });
1500
+
1501
+ test("multiple features with different scoping results", function () {
1502
+ const datafile: DatafileContent = {
1503
+ schemaVersion: "2",
1504
+ revision: "unknown",
1505
+ segments: {
1506
+ web: {
1507
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1508
+ },
1509
+ mobile: {
1510
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
1511
+ },
1512
+ },
1513
+ features: {
1514
+ feature1: {
1515
+ bucketBy: ["userId"],
1516
+ traffic: [
1517
+ {
1518
+ key: "rule1",
1519
+ segments: "web",
1520
+ percentage: 100000,
1521
+ },
1522
+ ],
1523
+ },
1524
+ feature2: {
1525
+ bucketBy: ["userId"],
1526
+ traffic: [
1527
+ {
1528
+ key: "rule1",
1529
+ segments: "mobile",
1530
+ percentage: 100000,
1531
+ },
1532
+ ],
1533
+ },
1534
+ },
1535
+ };
1536
+
1537
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1538
+
1539
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
1540
+ expect(result.features.feature2.traffic[0].segments).toEqual("mobile");
1541
+ });
1542
+
1543
+ test("segment used in multiple places - all scoped correctly", function () {
1544
+ const datafile: DatafileContent = {
1545
+ schemaVersion: "2",
1546
+ revision: "unknown",
1547
+ segments: {
1548
+ web: {
1549
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1550
+ },
1551
+ },
1552
+ features: {
1553
+ feature1: {
1554
+ bucketBy: ["userId"],
1555
+ traffic: [
1556
+ {
1557
+ key: "rule1",
1558
+ segments: "web",
1559
+ percentage: 100000,
1560
+ },
1561
+ ],
1562
+ force: [
1563
+ {
1564
+ segments: "web",
1565
+ variation: "treatment",
1566
+ },
1567
+ ],
1568
+ variations: [
1569
+ {
1570
+ value: "control",
1571
+ variableOverrides: {
1572
+ color: [
1573
+ {
1574
+ value: "red",
1575
+ segments: "web",
1576
+ },
1577
+ ],
1578
+ },
1579
+ },
1580
+ ],
1581
+ },
1582
+ },
1583
+ };
1584
+
1585
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1586
+
1587
+ expect(result.segments).toEqual({});
1588
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
1589
+ expect(result.features.feature1.force?.[0].segments).toEqual("*");
1590
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1591
+ "*",
1592
+ );
1593
+ });
1594
+
1595
+ test("nested AND/OR segments in force and traffic", function () {
1596
+ const datafile: DatafileContent = {
1597
+ schemaVersion: "2",
1598
+ revision: "unknown",
1599
+ segments: {
1600
+ web: {
1601
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1602
+ },
1603
+ chrome: {
1604
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
1605
+ },
1606
+ mobile: {
1607
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
1608
+ },
1609
+ },
1610
+ features: {
1611
+ feature1: {
1612
+ bucketBy: ["userId"],
1613
+ traffic: [
1614
+ {
1615
+ key: "rule1",
1616
+ segments: {
1617
+ and: ["web", "chrome"],
1618
+ },
1619
+ percentage: 100000,
1620
+ },
1621
+ ],
1622
+ force: [
1623
+ {
1624
+ segments: {
1625
+ or: ["web", "mobile"],
1626
+ },
1627
+ variation: "treatment",
1628
+ },
1629
+ ],
1630
+ },
1631
+ },
1632
+ };
1633
+
1634
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
1635
+
1636
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
1637
+ expect(result.features.feature1.force?.[0].segments).toEqual({ or: ["mobile"] });
1638
+ });
1639
+
1640
+ test("nested AND/OR conditions in force and variableOverrides", function () {
1641
+ const datafile: DatafileContent = {
1642
+ schemaVersion: "2",
1643
+ revision: "unknown",
1644
+ segments: {},
1645
+ features: {
1646
+ feature1: {
1647
+ bucketBy: ["userId"],
1648
+ traffic: [],
1649
+ force: [
1650
+ {
1651
+ conditions: {
1652
+ and: [
1653
+ { attribute: "platform", operator: "equals", value: "web" },
1654
+ { attribute: "browser", operator: "equals", value: "chrome" },
1655
+ ],
1656
+ },
1657
+ variation: "treatment",
1658
+ },
1659
+ ],
1660
+ variations: [
1661
+ {
1662
+ value: "control",
1663
+ variableOverrides: {
1664
+ color: [
1665
+ {
1666
+ value: "red",
1667
+ conditions: {
1668
+ or: [
1669
+ { attribute: "platform", operator: "equals", value: "web" },
1670
+ { attribute: "platform", operator: "equals", value: "mobile" },
1671
+ ],
1672
+ },
1673
+ },
1674
+ ],
1675
+ },
1676
+ },
1677
+ ],
1678
+ },
1679
+ },
1680
+ };
1681
+
1682
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
1683
+
1684
+ expect(result.features.feature1.force?.[0].conditions).toEqual("*");
1685
+ expect(
1686
+ result.features.feature1.variations?.[0].variableOverrides?.color[0].conditions,
1687
+ ).toEqual({
1688
+ or: [{ attribute: "platform", operator: "equals", value: "mobile" }],
1689
+ });
1690
+ });
1691
+ });
1692
+
1693
+ describe("edge cases", function () {
1694
+ test("feature without force, traffic, or variations", function () {
1695
+ const datafile: DatafileContent = {
1696
+ schemaVersion: "2",
1697
+ revision: "unknown",
1698
+ segments: {},
1699
+ features: {
1700
+ feature1: {
1701
+ bucketBy: ["userId"],
1702
+ traffic: [],
1703
+ },
1704
+ },
1705
+ };
1706
+
1707
+ const result = buildScopedDatafile(datafile, {});
1708
+
1709
+ expect(result.features.feature1).toBeDefined();
1710
+ expect(result.features.feature1.traffic).toEqual([]);
1711
+ });
1712
+
1713
+ test("feature with empty force array", function () {
1714
+ const datafile: DatafileContent = {
1715
+ schemaVersion: "2",
1716
+ revision: "unknown",
1717
+ segments: {},
1718
+ features: {
1719
+ feature1: {
1720
+ bucketBy: ["userId"],
1721
+ traffic: [],
1722
+ force: [],
1723
+ },
1724
+ },
1725
+ };
1726
+
1727
+ const result = buildScopedDatafile(datafile, {});
1728
+
1729
+ expect(result.features.feature1.force).toEqual([]);
1730
+ });
1731
+
1732
+ test("feature with empty variations array", function () {
1733
+ const datafile: DatafileContent = {
1734
+ schemaVersion: "2",
1735
+ revision: "unknown",
1736
+ segments: {},
1737
+ features: {
1738
+ feature1: {
1739
+ bucketBy: ["userId"],
1740
+ traffic: [],
1741
+ variations: [],
1742
+ },
1743
+ },
1744
+ };
1745
+
1746
+ const result = buildScopedDatafile(datafile, {});
1747
+
1748
+ expect(result.features.feature1.variations).toEqual([]);
1749
+ });
1750
+
1751
+ test("variation without variableOverrides", function () {
1752
+ const datafile: DatafileContent = {
1753
+ schemaVersion: "2",
1754
+ revision: "unknown",
1755
+ segments: {},
1756
+ features: {
1757
+ feature1: {
1758
+ bucketBy: ["userId"],
1759
+ traffic: [],
1760
+ variations: [
1761
+ {
1762
+ value: "control",
1763
+ },
1764
+ ],
1765
+ },
1766
+ },
1767
+ };
1768
+
1769
+ const result = buildScopedDatafile(datafile, {});
1770
+
1771
+ expect(result.features.feature1.variations?.[0].variableOverrides).toBeUndefined();
1772
+ });
1773
+
1774
+ test("variation with empty variableOverrides object", function () {
1775
+ const datafile: DatafileContent = {
1776
+ schemaVersion: "2",
1777
+ revision: "unknown",
1778
+ segments: {},
1779
+ features: {
1780
+ feature1: {
1781
+ bucketBy: ["userId"],
1782
+ traffic: [],
1783
+ variations: [
1784
+ {
1785
+ value: "control",
1786
+ variableOverrides: {},
1787
+ },
1788
+ ],
1789
+ },
1790
+ },
1791
+ };
1792
+
1793
+ const result = buildScopedDatafile(datafile, {});
1794
+
1795
+ expect(result.features.feature1.variations?.[0].variableOverrides).toEqual({});
1796
+ });
1797
+
1798
+ test("variableOverride with empty segments array", function () {
1799
+ const datafile: DatafileContent = {
1800
+ schemaVersion: "2",
1801
+ revision: "unknown",
1802
+ segments: {},
1803
+ features: {
1804
+ feature1: {
1805
+ bucketBy: ["userId"],
1806
+ traffic: [],
1807
+ variations: [
1808
+ {
1809
+ value: "control",
1810
+ variableOverrides: {
1811
+ color: [
1812
+ {
1813
+ value: "red",
1814
+ segments: [],
1815
+ },
1816
+ ],
1817
+ },
1818
+ },
1819
+ ],
1820
+ },
1821
+ },
1822
+ };
1823
+
1824
+ const result = buildScopedDatafile(datafile, {});
1825
+
1826
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
1827
+ "*",
1828
+ );
1829
+ });
1830
+
1831
+ test("force with both segments and conditions - segments take precedence", function () {
1832
+ const datafile: DatafileContent = {
1833
+ schemaVersion: "2",
1834
+ revision: "unknown",
1835
+ segments: {
1836
+ web: {
1837
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1838
+ },
1839
+ },
1840
+ features: {
1841
+ feature1: {
1842
+ bucketBy: ["userId"],
1843
+ traffic: [],
1844
+ force: [
1845
+ {
1846
+ segments: "web",
1847
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
1848
+ variation: "treatment",
1849
+ },
1850
+ ],
1851
+ },
1852
+ },
1853
+ };
1854
+
1855
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1856
+
1857
+ expect(result.features.feature1.force?.[0].segments).toEqual("*");
1858
+ expect(result.features.feature1.force?.[0].conditions).toBeDefined();
1859
+ });
1860
+
1861
+ test("traffic with * segments initially - consecutive removal still works", function () {
1862
+ const datafile: DatafileContent = {
1863
+ schemaVersion: "2",
1864
+ revision: "unknown",
1865
+ segments: {},
1866
+ features: {
1867
+ feature1: {
1868
+ bucketBy: ["userId"],
1869
+ traffic: [
1870
+ {
1871
+ key: "rule1",
1872
+ segments: "*",
1873
+ percentage: 50000,
1874
+ },
1875
+ {
1876
+ key: "rule2",
1877
+ segments: "*",
1878
+ percentage: 50000,
1879
+ },
1880
+ ],
1881
+ },
1882
+ },
1883
+ };
1884
+
1885
+ const result = buildScopedDatafile(datafile, {});
1886
+
1887
+ expect(result.features.feature1.traffic).toHaveLength(1);
1888
+ });
1889
+
1890
+ test("segment that becomes * is removed even if referenced elsewhere", function () {
1891
+ const datafile: DatafileContent = {
1892
+ schemaVersion: "2",
1893
+ revision: "unknown",
1894
+ segments: {
1895
+ web: {
1896
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1897
+ },
1898
+ },
1899
+ features: {
1900
+ feature1: {
1901
+ bucketBy: ["userId"],
1902
+ traffic: [
1903
+ {
1904
+ key: "rule1",
1905
+ segments: "web",
1906
+ percentage: 100000,
1907
+ },
1908
+ ],
1909
+ },
1910
+ },
1911
+ };
1912
+
1913
+ const result = buildScopedDatafile(datafile, { platform: "web" });
1914
+
1915
+ expect(result.segments).toEqual({});
1916
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
1917
+ });
1918
+
1919
+ test("context with extra attributes not in segments", function () {
1920
+ const datafile: DatafileContent = {
1921
+ schemaVersion: "2",
1922
+ revision: "unknown",
1923
+ segments: {
1924
+ web: {
1925
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1926
+ },
1927
+ },
1928
+ features: {},
1929
+ };
1930
+
1931
+ const result = buildScopedDatafile(datafile, {
1932
+ platform: "web",
1933
+ extraAttribute: "extraValue",
1934
+ });
1935
+
1936
+ expect(result.segments).toEqual({});
1937
+ });
1938
+
1939
+ test("datafile with all segments matching context", function () {
1940
+ const datafile: DatafileContent = {
1941
+ schemaVersion: "2",
1942
+ revision: "unknown",
1943
+ segments: {
1944
+ web: {
1945
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1946
+ },
1947
+ chrome: {
1948
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
1949
+ },
1950
+ },
1951
+ features: {},
1952
+ };
1953
+
1954
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
1955
+
1956
+ expect(result.segments).toEqual({});
1957
+ });
1958
+
1959
+ test("datafile with no segments matching context", function () {
1960
+ const datafile: DatafileContent = {
1961
+ schemaVersion: "2",
1962
+ revision: "unknown",
1963
+ segments: {
1964
+ web: {
1965
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1966
+ },
1967
+ chrome: {
1968
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
1969
+ },
1970
+ },
1971
+ features: {},
1972
+ };
1973
+
1974
+ const result = buildScopedDatafile(datafile, { platform: "mobile", browser: "safari" });
1975
+
1976
+ expect(result.segments.web).toBeDefined();
1977
+ expect(result.segments.chrome).toBeDefined();
1978
+ });
1979
+
1980
+ test("feature with multiple traffic entries becoming * consecutively", function () {
1981
+ const datafile: DatafileContent = {
1982
+ schemaVersion: "2",
1983
+ revision: "unknown",
1984
+ segments: {
1985
+ web: {
1986
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
1987
+ },
1988
+ },
1989
+ features: {
1990
+ feature1: {
1991
+ bucketBy: ["userId"],
1992
+ traffic: [
1993
+ {
1994
+ key: "rule1",
1995
+ segments: "web",
1996
+ percentage: 25000,
1997
+ },
1998
+ {
1999
+ key: "rule2",
2000
+ segments: "web",
2001
+ percentage: 25000,
2002
+ },
2003
+ {
2004
+ key: "rule3",
2005
+ segments: "web",
2006
+ percentage: 25000,
2007
+ },
2008
+ {
2009
+ key: "rule4",
2010
+ segments: "web",
2011
+ percentage: 25000,
2012
+ },
2013
+ ],
2014
+ },
2015
+ },
2016
+ };
2017
+
2018
+ const result = buildScopedDatafile(datafile, { platform: "web" });
2019
+
2020
+ expect(result.features.feature1.traffic).toHaveLength(1);
2021
+ expect(result.features.feature1.traffic[0].key).toEqual("rule1");
2022
+ });
2023
+
2024
+ test("variableOverride with segments that become * after segment removal", function () {
2025
+ const datafile: DatafileContent = {
2026
+ schemaVersion: "2",
2027
+ revision: "unknown",
2028
+ segments: {
2029
+ web: {
2030
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
2031
+ },
2032
+ },
2033
+ features: {
2034
+ feature1: {
2035
+ bucketBy: ["userId"],
2036
+ traffic: [],
2037
+ variations: [
2038
+ {
2039
+ value: "control",
2040
+ variableOverrides: {
2041
+ color: [
2042
+ {
2043
+ value: "red",
2044
+ segments: "web",
2045
+ },
2046
+ ],
2047
+ },
2048
+ },
2049
+ ],
2050
+ },
2051
+ },
2052
+ };
2053
+
2054
+ const result = buildScopedDatafile(datafile, { platform: "web" });
2055
+
2056
+ expect(result.segments).toEqual({});
2057
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
2058
+ "*",
2059
+ );
2060
+ });
2061
+ });
2062
+
2063
+ describe("integration scenarios", function () {
2064
+ test("complete datafile with all feature types", function () {
2065
+ const datafile: DatafileContent = {
2066
+ schemaVersion: "2",
2067
+ revision: "unknown",
2068
+ segments: {
2069
+ web: {
2070
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
2071
+ },
2072
+ chrome: {
2073
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
2074
+ },
2075
+ mobile: {
2076
+ conditions: [{ attribute: "platform", operator: "equals", value: "mobile" }],
2077
+ },
2078
+ },
2079
+ features: {
2080
+ feature1: {
2081
+ bucketBy: ["userId"],
2082
+ traffic: [
2083
+ {
2084
+ key: "rule1",
2085
+ segments: "web",
2086
+ percentage: 100000,
2087
+ },
2088
+ ],
2089
+ force: [
2090
+ {
2091
+ segments: "chrome",
2092
+ variation: "treatment",
2093
+ },
2094
+ ],
2095
+ variations: [
2096
+ {
2097
+ value: "control",
2098
+ variableOverrides: {
2099
+ color: [
2100
+ {
2101
+ value: "red",
2102
+ segments: "web",
2103
+ },
2104
+ ],
2105
+ },
2106
+ },
2107
+ ],
2108
+ },
2109
+ feature2: {
2110
+ bucketBy: ["userId"],
2111
+ traffic: [
2112
+ {
2113
+ key: "rule1",
2114
+ segments: "mobile",
2115
+ percentage: 100000,
2116
+ },
2117
+ ],
2118
+ },
2119
+ },
2120
+ };
2121
+
2122
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
2123
+
2124
+ expect(result.segments.web).toBeUndefined();
2125
+ expect(result.segments.chrome).toBeUndefined();
2126
+ expect(result.segments.mobile).toBeDefined();
2127
+
2128
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
2129
+ expect(result.features.feature1.force?.[0].segments).toEqual("*");
2130
+ expect(result.features.feature1.variations?.[0].variableOverrides?.color[0].segments).toEqual(
2131
+ "*",
2132
+ );
2133
+
2134
+ expect(result.features.feature2.traffic[0].segments).toEqual("mobile");
2135
+ });
2136
+
2137
+ test("datafile with deeply nested conditions and segments", function () {
2138
+ const datafile: DatafileContent = {
2139
+ schemaVersion: "2",
2140
+ revision: "unknown",
2141
+ segments: {
2142
+ web: {
2143
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
2144
+ },
2145
+ chrome: {
2146
+ conditions: [{ attribute: "browser", operator: "equals", value: "chrome" }],
2147
+ },
2148
+ },
2149
+ features: {
2150
+ feature1: {
2151
+ bucketBy: ["userId"],
2152
+ traffic: [
2153
+ {
2154
+ key: "rule1",
2155
+ segments: {
2156
+ and: [
2157
+ "web",
2158
+ {
2159
+ or: ["chrome", "web"],
2160
+ },
2161
+ ],
2162
+ },
2163
+ percentage: 100000,
2164
+ },
2165
+ ],
2166
+ force: [
2167
+ {
2168
+ conditions: {
2169
+ and: [
2170
+ { attribute: "platform", operator: "equals", value: "web" },
2171
+ {
2172
+ or: [
2173
+ { attribute: "browser", operator: "equals", value: "chrome" },
2174
+ { attribute: "browser", operator: "equals", value: "firefox" },
2175
+ ],
2176
+ },
2177
+ ],
2178
+ },
2179
+ variation: "treatment",
2180
+ },
2181
+ ],
2182
+ },
2183
+ },
2184
+ };
2185
+
2186
+ const result = buildScopedDatafile(datafile, { platform: "web", browser: "chrome" });
2187
+
2188
+ expect(result.features.feature1.traffic[0].segments).toEqual("*");
2189
+ expect(result.features.feature1.force?.[0].conditions).toEqual({
2190
+ and: [
2191
+ {
2192
+ or: [
2193
+ {
2194
+ attribute: "browser",
2195
+ operator: "equals",
2196
+ value: "firefox",
2197
+ },
2198
+ ],
2199
+ },
2200
+ ],
2201
+ });
2202
+ });
2203
+
2204
+ test("datafile immutability - original not modified", function () {
2205
+ const datafile: DatafileContent = {
2206
+ schemaVersion: "2",
2207
+ revision: "unknown",
2208
+ segments: {
2209
+ web: {
2210
+ conditions: [{ attribute: "platform", operator: "equals", value: "web" }],
2211
+ },
2212
+ },
2213
+ features: {
2214
+ feature1: {
2215
+ bucketBy: ["userId"],
2216
+ traffic: [
2217
+ {
2218
+ key: "rule1",
2219
+ segments: "web",
2220
+ percentage: 100000,
2221
+ },
2222
+ ],
2223
+ },
2224
+ },
2225
+ };
2226
+
2227
+ const originalSegments = JSON.stringify(datafile.segments);
2228
+ const originalTraffic = JSON.stringify(datafile.features.feature1.traffic);
2229
+
2230
+ buildScopedDatafile(datafile, { platform: "web" });
2231
+
2232
+ expect(JSON.stringify(datafile.segments)).toEqual(originalSegments);
2233
+ expect(JSON.stringify(datafile.features.feature1.traffic)).toEqual(originalTraffic);
2234
+ });
2235
+ });
2236
+ });