@featurevisor/sdk 2.7.0 → 2.10.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.
- package/CHANGELOG.md +11 -0
- package/coverage/clover.xml +182 -182
- package/coverage/coverage-final.json +9 -9
- package/coverage/lcov-report/bucketer.ts.html +16 -16
- package/coverage/lcov-report/child.ts.html +4 -4
- package/coverage/lcov-report/conditions.ts.html +3 -3
- package/coverage/lcov-report/datafileReader.ts.html +30 -30
- package/coverage/lcov-report/emitter.ts.html +2 -2
- package/coverage/lcov-report/evaluate.ts.html +67 -67
- package/coverage/lcov-report/events.ts.html +1 -1
- package/coverage/lcov-report/helpers.ts.html +8 -8
- package/coverage/lcov-report/hooks.ts.html +6 -6
- package/coverage/lcov-report/index.html +5 -5
- package/coverage/lcov-report/instance.ts.html +43 -43
- package/coverage/lcov-report/logger.ts.html +17 -17
- package/coverage/lcov.info +306 -306
- package/dist/child.d.ts +1 -1
- package/dist/index.js.map +1 -1
- package/dist/index.mjs.gz +0 -0
- package/dist/index.mjs.map +1 -1
- package/dist/instance.d.ts +1 -1
- package/lib/child.d.ts +1 -1
- package/lib/instance.d.ts +1 -1
- package/package.json +3 -3
- package/src/child.ts +3 -3
- package/src/instance.spec.ts +324 -0
- package/src/instance.ts +3 -3
package/src/instance.spec.ts
CHANGED
|
@@ -1147,6 +1147,330 @@ describe("sdk: instance", function () {
|
|
|
1147
1147
|
).toEqual("orange");
|
|
1148
1148
|
});
|
|
1149
1149
|
|
|
1150
|
+
describe("array and object variables", function () {
|
|
1151
|
+
const arrayAndObjectDatafile: DatafileContent = {
|
|
1152
|
+
schemaVersion: "2",
|
|
1153
|
+
revision: "1.0",
|
|
1154
|
+
features: {
|
|
1155
|
+
withArray: {
|
|
1156
|
+
key: "withArray",
|
|
1157
|
+
bucketBy: "userId",
|
|
1158
|
+
variablesSchema: {
|
|
1159
|
+
simpleArray: {
|
|
1160
|
+
key: "simpleArray",
|
|
1161
|
+
type: "array",
|
|
1162
|
+
defaultValue: ["red", "blue", "green"],
|
|
1163
|
+
},
|
|
1164
|
+
simpleStringArray: {
|
|
1165
|
+
key: "simpleStringArray",
|
|
1166
|
+
type: "array",
|
|
1167
|
+
defaultValue: ["red", "blue", "green"],
|
|
1168
|
+
},
|
|
1169
|
+
objectArray: {
|
|
1170
|
+
key: "objectArray",
|
|
1171
|
+
type: "array",
|
|
1172
|
+
defaultValue: [
|
|
1173
|
+
{ color: "red", opacity: 100 },
|
|
1174
|
+
{ color: "blue", opacity: 90 },
|
|
1175
|
+
{ color: "green", opacity: 95 },
|
|
1176
|
+
],
|
|
1177
|
+
},
|
|
1178
|
+
},
|
|
1179
|
+
traffic: [
|
|
1180
|
+
{
|
|
1181
|
+
key: "1",
|
|
1182
|
+
segments: "*",
|
|
1183
|
+
percentage: 100000,
|
|
1184
|
+
allocation: [],
|
|
1185
|
+
},
|
|
1186
|
+
],
|
|
1187
|
+
},
|
|
1188
|
+
withObject: {
|
|
1189
|
+
key: "withObject",
|
|
1190
|
+
bucketBy: "userId",
|
|
1191
|
+
variablesSchema: {
|
|
1192
|
+
themeConfig: {
|
|
1193
|
+
key: "themeConfig",
|
|
1194
|
+
type: "object",
|
|
1195
|
+
defaultValue: {
|
|
1196
|
+
theme: "light",
|
|
1197
|
+
darkMode: false,
|
|
1198
|
+
},
|
|
1199
|
+
},
|
|
1200
|
+
layoutConfig: {
|
|
1201
|
+
key: "layoutConfig",
|
|
1202
|
+
type: "object",
|
|
1203
|
+
defaultValue: {
|
|
1204
|
+
width: 1200,
|
|
1205
|
+
align: "center",
|
|
1206
|
+
},
|
|
1207
|
+
},
|
|
1208
|
+
headerConfig: {
|
|
1209
|
+
key: "headerConfig",
|
|
1210
|
+
type: "object",
|
|
1211
|
+
defaultValue: {
|
|
1212
|
+
style: { fontSize: 18, bold: true },
|
|
1213
|
+
title: "Welcome",
|
|
1214
|
+
},
|
|
1215
|
+
},
|
|
1216
|
+
contentConfig: {
|
|
1217
|
+
key: "contentConfig",
|
|
1218
|
+
type: "object",
|
|
1219
|
+
defaultValue: {
|
|
1220
|
+
tags: ["news", "featured"],
|
|
1221
|
+
count: 2,
|
|
1222
|
+
},
|
|
1223
|
+
},
|
|
1224
|
+
panelConfig: {
|
|
1225
|
+
key: "panelConfig",
|
|
1226
|
+
type: "object",
|
|
1227
|
+
defaultValue: {
|
|
1228
|
+
sections: [
|
|
1229
|
+
{ id: "hero", visible: true },
|
|
1230
|
+
{ id: "sidebar", visible: false },
|
|
1231
|
+
],
|
|
1232
|
+
},
|
|
1233
|
+
},
|
|
1234
|
+
deepConfig: {
|
|
1235
|
+
key: "deepConfig",
|
|
1236
|
+
type: "object",
|
|
1237
|
+
defaultValue: {
|
|
1238
|
+
level1: {
|
|
1239
|
+
level2: {
|
|
1240
|
+
value: "deep",
|
|
1241
|
+
count: 1,
|
|
1242
|
+
},
|
|
1243
|
+
},
|
|
1244
|
+
},
|
|
1245
|
+
},
|
|
1246
|
+
mixedConfig: {
|
|
1247
|
+
key: "mixedConfig",
|
|
1248
|
+
type: "object",
|
|
1249
|
+
defaultValue: {
|
|
1250
|
+
name: "mixed",
|
|
1251
|
+
enabled: true,
|
|
1252
|
+
meta: {
|
|
1253
|
+
score: 0.95,
|
|
1254
|
+
items: ["a", "b"],
|
|
1255
|
+
},
|
|
1256
|
+
},
|
|
1257
|
+
},
|
|
1258
|
+
},
|
|
1259
|
+
traffic: [
|
|
1260
|
+
{
|
|
1261
|
+
key: "1",
|
|
1262
|
+
segments: "*",
|
|
1263
|
+
percentage: 100000,
|
|
1264
|
+
allocation: [],
|
|
1265
|
+
},
|
|
1266
|
+
],
|
|
1267
|
+
},
|
|
1268
|
+
},
|
|
1269
|
+
segments: {},
|
|
1270
|
+
};
|
|
1271
|
+
|
|
1272
|
+
const context = { userId: "user-1" };
|
|
1273
|
+
|
|
1274
|
+
it("should get array variables via getVariable and getVariableArray (no generics)", function () {
|
|
1275
|
+
const sdk = createInstance({ datafile: arrayAndObjectDatafile });
|
|
1276
|
+
|
|
1277
|
+
const simpleArray = sdk.getVariable("withArray", "simpleArray", context);
|
|
1278
|
+
expect(simpleArray).toEqual(["red", "blue", "green"]);
|
|
1279
|
+
|
|
1280
|
+
const simpleArrayTyped = sdk.getVariableArray("withArray", "simpleArray", context);
|
|
1281
|
+
expect(simpleArrayTyped).toEqual(["red", "blue", "green"]);
|
|
1282
|
+
|
|
1283
|
+
const simpleStringArray = sdk.getVariable("withArray", "simpleStringArray", context);
|
|
1284
|
+
expect(simpleStringArray).toEqual(["red", "blue", "green"]);
|
|
1285
|
+
|
|
1286
|
+
const simpleStringArrayTyped = sdk.getVariableArray(
|
|
1287
|
+
"withArray",
|
|
1288
|
+
"simpleStringArray",
|
|
1289
|
+
context,
|
|
1290
|
+
);
|
|
1291
|
+
expect(simpleStringArrayTyped).toEqual(["red", "blue", "green"]);
|
|
1292
|
+
|
|
1293
|
+
const objectArray = sdk.getVariable("withArray", "objectArray", context);
|
|
1294
|
+
expect(objectArray).toEqual([
|
|
1295
|
+
{ color: "red", opacity: 100 },
|
|
1296
|
+
{ color: "blue", opacity: 90 },
|
|
1297
|
+
{ color: "green", opacity: 95 },
|
|
1298
|
+
]);
|
|
1299
|
+
|
|
1300
|
+
const objectArrayTyped = sdk.getVariableArray("withArray", "objectArray", context);
|
|
1301
|
+
expect(objectArrayTyped).toEqual([
|
|
1302
|
+
{ color: "red", opacity: 100 },
|
|
1303
|
+
{ color: "blue", opacity: 90 },
|
|
1304
|
+
{ color: "green", opacity: 95 },
|
|
1305
|
+
]);
|
|
1306
|
+
});
|
|
1307
|
+
|
|
1308
|
+
it("should get array variables with generics for type safety", function () {
|
|
1309
|
+
const sdk = createInstance({ datafile: arrayAndObjectDatafile });
|
|
1310
|
+
|
|
1311
|
+
const stringArray = sdk.getVariableArray<string>("withArray", "simpleArray", context);
|
|
1312
|
+
expect(stringArray).toEqual(["red", "blue", "green"]);
|
|
1313
|
+
if (stringArray) {
|
|
1314
|
+
const first: string = stringArray[0];
|
|
1315
|
+
expect(first).toBe("red");
|
|
1316
|
+
}
|
|
1317
|
+
|
|
1318
|
+
interface ColorOpacity {
|
|
1319
|
+
color: string;
|
|
1320
|
+
opacity: number;
|
|
1321
|
+
}
|
|
1322
|
+
const objectArray = sdk.getVariableArray<ColorOpacity>("withArray", "objectArray", context);
|
|
1323
|
+
expect(objectArray).toEqual([
|
|
1324
|
+
{ color: "red", opacity: 100 },
|
|
1325
|
+
{ color: "blue", opacity: 90 },
|
|
1326
|
+
{ color: "green", opacity: 95 },
|
|
1327
|
+
]);
|
|
1328
|
+
if (objectArray && objectArray.length > 0) {
|
|
1329
|
+
const first: ColorOpacity = objectArray[0];
|
|
1330
|
+
expect(first.color).toBe("red");
|
|
1331
|
+
expect(first.opacity).toBe(100);
|
|
1332
|
+
}
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1335
|
+
it("should get object variables via getVariable and getVariableObject (no generics)", function () {
|
|
1336
|
+
const sdk = createInstance({ datafile: arrayAndObjectDatafile });
|
|
1337
|
+
|
|
1338
|
+
const themeConfig = sdk.getVariable("withObject", "themeConfig", context);
|
|
1339
|
+
expect(themeConfig).toEqual({ theme: "light", darkMode: false });
|
|
1340
|
+
|
|
1341
|
+
const themeConfigTyped = sdk.getVariableObject("withObject", "themeConfig", context);
|
|
1342
|
+
expect(themeConfigTyped).toEqual({ theme: "light", darkMode: false });
|
|
1343
|
+
|
|
1344
|
+
const layoutConfig = sdk.getVariable("withObject", "layoutConfig", context);
|
|
1345
|
+
expect(layoutConfig).toEqual({ width: 1200, align: "center" });
|
|
1346
|
+
|
|
1347
|
+
const headerConfig = sdk.getVariable("withObject", "headerConfig", context);
|
|
1348
|
+
expect(headerConfig).toEqual({
|
|
1349
|
+
style: { fontSize: 18, bold: true },
|
|
1350
|
+
title: "Welcome",
|
|
1351
|
+
});
|
|
1352
|
+
|
|
1353
|
+
const contentConfig = sdk.getVariable("withObject", "contentConfig", context);
|
|
1354
|
+
expect(contentConfig).toEqual({
|
|
1355
|
+
tags: ["news", "featured"],
|
|
1356
|
+
count: 2,
|
|
1357
|
+
});
|
|
1358
|
+
|
|
1359
|
+
const panelConfig = sdk.getVariableObject("withObject", "panelConfig", context);
|
|
1360
|
+
expect(panelConfig).toEqual({
|
|
1361
|
+
sections: [
|
|
1362
|
+
{ id: "hero", visible: true },
|
|
1363
|
+
{ id: "sidebar", visible: false },
|
|
1364
|
+
],
|
|
1365
|
+
});
|
|
1366
|
+
|
|
1367
|
+
const deepConfig = sdk.getVariableObject("withObject", "deepConfig", context);
|
|
1368
|
+
expect(deepConfig).toEqual({
|
|
1369
|
+
level1: {
|
|
1370
|
+
level2: {
|
|
1371
|
+
value: "deep",
|
|
1372
|
+
count: 1,
|
|
1373
|
+
},
|
|
1374
|
+
},
|
|
1375
|
+
});
|
|
1376
|
+
|
|
1377
|
+
const mixedConfig = sdk.getVariableObject("withObject", "mixedConfig", context);
|
|
1378
|
+
expect(mixedConfig).toEqual({
|
|
1379
|
+
name: "mixed",
|
|
1380
|
+
enabled: true,
|
|
1381
|
+
meta: {
|
|
1382
|
+
score: 0.95,
|
|
1383
|
+
items: ["a", "b"],
|
|
1384
|
+
},
|
|
1385
|
+
});
|
|
1386
|
+
});
|
|
1387
|
+
|
|
1388
|
+
it("should get object variables with generics for type safety", function () {
|
|
1389
|
+
const sdk = createInstance({ datafile: arrayAndObjectDatafile });
|
|
1390
|
+
|
|
1391
|
+
interface ThemeConfig {
|
|
1392
|
+
theme: string;
|
|
1393
|
+
darkMode: boolean;
|
|
1394
|
+
}
|
|
1395
|
+
const themeConfig = sdk.getVariableObject<ThemeConfig>("withObject", "themeConfig", context);
|
|
1396
|
+
expect(themeConfig).toEqual({ theme: "light", darkMode: false });
|
|
1397
|
+
if (themeConfig) {
|
|
1398
|
+
expect(themeConfig.theme).toBe("light");
|
|
1399
|
+
expect(themeConfig.darkMode).toBe(false);
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1402
|
+
interface HeaderConfig {
|
|
1403
|
+
style: { fontSize: number; bold: boolean };
|
|
1404
|
+
title: string;
|
|
1405
|
+
}
|
|
1406
|
+
const headerConfig = sdk.getVariableObject<HeaderConfig>(
|
|
1407
|
+
"withObject",
|
|
1408
|
+
"headerConfig",
|
|
1409
|
+
context,
|
|
1410
|
+
);
|
|
1411
|
+
expect(headerConfig).toEqual({
|
|
1412
|
+
style: { fontSize: 18, bold: true },
|
|
1413
|
+
title: "Welcome",
|
|
1414
|
+
});
|
|
1415
|
+
if (headerConfig) {
|
|
1416
|
+
expect(headerConfig.style.fontSize).toBe(18);
|
|
1417
|
+
expect(headerConfig.title).toBe("Welcome");
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
interface PanelSection {
|
|
1421
|
+
id: string;
|
|
1422
|
+
visible: boolean;
|
|
1423
|
+
}
|
|
1424
|
+
interface PanelConfig {
|
|
1425
|
+
sections: PanelSection[];
|
|
1426
|
+
}
|
|
1427
|
+
const panelConfig = sdk.getVariableObject<PanelConfig>("withObject", "panelConfig", context);
|
|
1428
|
+
expect(panelConfig?.sections).toHaveLength(2);
|
|
1429
|
+
expect(panelConfig?.sections[0]).toEqual({ id: "hero", visible: true });
|
|
1430
|
+
});
|
|
1431
|
+
|
|
1432
|
+
it("should return null for getVariableArray and getVariableObject when variable or feature missing", function () {
|
|
1433
|
+
const sdk = createInstance({ datafile: arrayAndObjectDatafile });
|
|
1434
|
+
|
|
1435
|
+
expect(sdk.getVariableArray("withArray", "nonExisting", context)).toBeNull();
|
|
1436
|
+
expect(sdk.getVariableObject("withObject", "nonExisting", context)).toBeNull();
|
|
1437
|
+
expect(sdk.getVariableArray("nonExistingFeature", "simpleArray", context)).toBeNull();
|
|
1438
|
+
expect(sdk.getVariableObject("nonExistingFeature", "themeConfig", context)).toBeNull();
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
it("should include array and object variables in getAllEvaluations", function () {
|
|
1442
|
+
const sdk = createInstance({ datafile: arrayAndObjectDatafile });
|
|
1443
|
+
|
|
1444
|
+
const all = sdk.getAllEvaluations(context);
|
|
1445
|
+
|
|
1446
|
+
expect(all.withArray).toBeDefined();
|
|
1447
|
+
expect(all.withArray?.enabled).toBe(true);
|
|
1448
|
+
expect(all.withArray?.variables?.simpleArray).toEqual(["red", "blue", "green"]);
|
|
1449
|
+
expect(all.withArray?.variables?.simpleStringArray).toEqual(["red", "blue", "green"]);
|
|
1450
|
+
expect(all.withArray?.variables?.objectArray).toEqual([
|
|
1451
|
+
{ color: "red", opacity: 100 },
|
|
1452
|
+
{ color: "blue", opacity: 90 },
|
|
1453
|
+
{ color: "green", opacity: 95 },
|
|
1454
|
+
]);
|
|
1455
|
+
|
|
1456
|
+
expect(all.withObject).toBeDefined();
|
|
1457
|
+
expect(all.withObject?.enabled).toBe(true);
|
|
1458
|
+
expect(all.withObject?.variables?.themeConfig).toEqual({
|
|
1459
|
+
theme: "light",
|
|
1460
|
+
darkMode: false,
|
|
1461
|
+
});
|
|
1462
|
+
expect(all.withObject?.variables?.headerConfig).toEqual({
|
|
1463
|
+
style: { fontSize: 18, bold: true },
|
|
1464
|
+
title: "Welcome",
|
|
1465
|
+
});
|
|
1466
|
+
expect(all.withObject?.variables?.mixedConfig).toEqual({
|
|
1467
|
+
name: "mixed",
|
|
1468
|
+
enabled: true,
|
|
1469
|
+
meta: { score: 0.95, items: ["a", "b"] },
|
|
1470
|
+
});
|
|
1471
|
+
});
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1150
1474
|
it("should check if enabled for individually named segments", function () {
|
|
1151
1475
|
const sdk = createInstance({
|
|
1152
1476
|
datafile: {
|
package/src/instance.ts
CHANGED
|
@@ -361,15 +361,15 @@ export class FeaturevisorInstance {
|
|
|
361
361
|
return getValueByType(variableValue, "double") as number | null;
|
|
362
362
|
}
|
|
363
363
|
|
|
364
|
-
getVariableArray(
|
|
364
|
+
getVariableArray<T = string>(
|
|
365
365
|
featureKey: FeatureKey,
|
|
366
366
|
variableKey: string,
|
|
367
367
|
context: Context = {},
|
|
368
368
|
options: OverrideOptions = {},
|
|
369
|
-
):
|
|
369
|
+
): T[] | null {
|
|
370
370
|
const variableValue = this.getVariable(featureKey, variableKey, context, options);
|
|
371
371
|
|
|
372
|
-
return getValueByType(variableValue, "array") as
|
|
372
|
+
return getValueByType(variableValue, "array") as T[] | null;
|
|
373
373
|
}
|
|
374
374
|
|
|
375
375
|
getVariableObject<T>(
|