@featurevisor/core 0.53.0 → 0.53.2
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/.eslintcache +1 -1
- package/CHANGELOG.md +16 -0
- package/coverage/clover.xml +2 -2
- package/coverage/lcov-report/index.html +1 -1
- package/coverage/lcov-report/lib/builder/allocator.js.html +1 -1
- package/coverage/lcov-report/lib/builder/index.html +1 -1
- package/coverage/lcov-report/lib/builder/traffic.js.html +1 -1
- package/coverage/lcov-report/src/builder/allocator.ts.html +1 -1
- package/coverage/lcov-report/src/builder/index.html +1 -1
- package/coverage/lcov-report/src/builder/traffic.ts.html +1 -1
- package/lib/tester/checkIfArraysAreEqual.d.ts +1 -0
- package/lib/tester/checkIfArraysAreEqual.js +19 -0
- package/lib/tester/checkIfArraysAreEqual.js.map +1 -0
- package/lib/tester/checkIfObjectsAreEqual.d.ts +1 -0
- package/lib/tester/checkIfObjectsAreEqual.js +22 -0
- package/lib/tester/checkIfObjectsAreEqual.js.map +1 -0
- package/lib/tester/cliFormat.d.ts +3 -0
- package/lib/tester/cliFormat.js +7 -0
- package/lib/tester/cliFormat.js.map +1 -0
- package/lib/tester/index.d.ts +1 -0
- package/lib/tester/index.js +18 -0
- package/lib/tester/index.js.map +1 -0
- package/lib/tester/testFeature.d.ts +4 -0
- package/lib/tester/testFeature.js +75 -0
- package/lib/tester/testFeature.js.map +1 -0
- package/lib/tester/testProject.d.ts +2 -0
- package/lib/tester/testProject.js +61 -0
- package/lib/tester/testProject.js.map +1 -0
- package/lib/tester/testSegment.d.ts +3 -0
- package/lib/tester/testSegment.js +31 -0
- package/lib/tester/testSegment.js.map +1 -0
- package/package.json +2 -2
- package/src/tester/checkIfArraysAreEqual.ts +16 -0
- package/src/tester/checkIfObjectsAreEqual.ts +21 -0
- package/src/tester/cliFormat.ts +4 -0
- package/src/tester/index.ts +1 -0
- package/src/tester/testFeature.ts +116 -0
- package/src/tester/testProject.ts +73 -0
- package/src/tester/testSegment.ts +43 -0
- package/lib/tester.d.ts +0 -4
- package/lib/tester.js +0 -159
- package/lib/tester.js.map +0 -1
- package/src/tester.ts +0 -213
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from "./testProject";
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
|
|
3
|
+
import { TestFeature, ExistingState } from "@featurevisor/types";
|
|
4
|
+
import { createInstance, MAX_BUCKETED_NUMBER } from "@featurevisor/sdk";
|
|
5
|
+
|
|
6
|
+
import { Datasource } from "../datasource";
|
|
7
|
+
import { ProjectConfig } from "../config";
|
|
8
|
+
import { buildDatafile, getExistingStateFilePath } from "../builder";
|
|
9
|
+
import { SCHEMA_VERSION } from "../config";
|
|
10
|
+
|
|
11
|
+
import { checkIfArraysAreEqual } from "./checkIfArraysAreEqual";
|
|
12
|
+
import { checkIfObjectsAreEqual } from "./checkIfObjectsAreEqual";
|
|
13
|
+
import { CLI_FORMAT_BOLD, CLI_FORMAT_RED } from "./cliFormat";
|
|
14
|
+
|
|
15
|
+
export function testFeature(
|
|
16
|
+
datasource: Datasource,
|
|
17
|
+
projectConfig: ProjectConfig,
|
|
18
|
+
test: TestFeature,
|
|
19
|
+
): boolean {
|
|
20
|
+
let hasError = false;
|
|
21
|
+
const featureKey = test.feature;
|
|
22
|
+
|
|
23
|
+
console.log(CLI_FORMAT_BOLD, ` Feature "${featureKey}":`);
|
|
24
|
+
|
|
25
|
+
test.assertions.forEach(function (assertion, aIndex) {
|
|
26
|
+
const description = assertion.description || `at ${assertion.at}%`;
|
|
27
|
+
|
|
28
|
+
console.log(` Assertion #${aIndex + 1}: (${assertion.environment}) ${description}`);
|
|
29
|
+
|
|
30
|
+
const featuresToInclude = Array.from(datasource.getRequiredFeaturesChain(test.feature));
|
|
31
|
+
|
|
32
|
+
const datafileContent = buildDatafile(
|
|
33
|
+
projectConfig,
|
|
34
|
+
datasource,
|
|
35
|
+
{
|
|
36
|
+
schemaVersion: SCHEMA_VERSION,
|
|
37
|
+
revision: "testing",
|
|
38
|
+
environment: assertion.environment,
|
|
39
|
+
features: featuresToInclude,
|
|
40
|
+
},
|
|
41
|
+
JSON.parse(
|
|
42
|
+
fs.readFileSync(getExistingStateFilePath(projectConfig, assertion.environment), "utf8"),
|
|
43
|
+
) as ExistingState,
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
const sdk = createInstance({
|
|
47
|
+
datafile: datafileContent,
|
|
48
|
+
configureBucketValue: () => {
|
|
49
|
+
return assertion.at * (MAX_BUCKETED_NUMBER / 100);
|
|
50
|
+
},
|
|
51
|
+
// logger: createLogger({
|
|
52
|
+
// levels: ["debug", "info", "warn", "error"],
|
|
53
|
+
// }),
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
// isEnabled
|
|
57
|
+
if ("expectedToBeEnabled" in assertion) {
|
|
58
|
+
const isEnabled = sdk.isEnabled(featureKey, assertion.context);
|
|
59
|
+
|
|
60
|
+
if (isEnabled !== assertion.expectedToBeEnabled) {
|
|
61
|
+
hasError = true;
|
|
62
|
+
|
|
63
|
+
console.error(
|
|
64
|
+
CLI_FORMAT_RED,
|
|
65
|
+
` isEnabled failed: expected "${assertion.expectedToBeEnabled}", got "${isEnabled}"`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// variation
|
|
71
|
+
if ("expectedVariation" in assertion) {
|
|
72
|
+
const variation = sdk.getVariation(featureKey, assertion.context);
|
|
73
|
+
|
|
74
|
+
if (variation !== assertion.expectedVariation) {
|
|
75
|
+
hasError = true;
|
|
76
|
+
|
|
77
|
+
console.error(
|
|
78
|
+
CLI_FORMAT_RED,
|
|
79
|
+
` Variation failed: expected "${assertion.expectedVariation}", got "${variation}"`,
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// variables
|
|
85
|
+
if (typeof assertion.expectedVariables === "object") {
|
|
86
|
+
Object.keys(assertion.expectedVariables).forEach(function (variableKey) {
|
|
87
|
+
const expectedValue =
|
|
88
|
+
assertion.expectedVariables && assertion.expectedVariables[variableKey];
|
|
89
|
+
const actualValue = sdk.getVariable(featureKey, variableKey, assertion.context);
|
|
90
|
+
|
|
91
|
+
let passed;
|
|
92
|
+
|
|
93
|
+
if (typeof expectedValue === "object") {
|
|
94
|
+
passed = checkIfObjectsAreEqual(expectedValue, actualValue);
|
|
95
|
+
} else if (Array.isArray(expectedValue)) {
|
|
96
|
+
passed = checkIfArraysAreEqual(expectedValue, actualValue);
|
|
97
|
+
} else {
|
|
98
|
+
passed = expectedValue === actualValue;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!passed) {
|
|
102
|
+
hasError = true;
|
|
103
|
+
|
|
104
|
+
console.error(
|
|
105
|
+
CLI_FORMAT_RED,
|
|
106
|
+
` Variable "${variableKey}" failed: expected ${JSON.stringify(
|
|
107
|
+
expectedValue,
|
|
108
|
+
)}, got "${JSON.stringify(actualValue)}"`,
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
return hasError;
|
|
116
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
|
|
3
|
+
import { TestSegment, TestFeature } from "@featurevisor/types";
|
|
4
|
+
|
|
5
|
+
import { ProjectConfig } from "../config";
|
|
6
|
+
import { Datasource } from "../datasource";
|
|
7
|
+
|
|
8
|
+
import { testSegment } from "./testSegment";
|
|
9
|
+
import { testFeature } from "./testFeature";
|
|
10
|
+
import { CLI_FORMAT_BOLD, CLI_FORMAT_GREEN, CLI_FORMAT_RED } from "./cliFormat";
|
|
11
|
+
|
|
12
|
+
export function testProject(rootDirectoryPath: string, projectConfig: ProjectConfig): boolean {
|
|
13
|
+
let hasError = false;
|
|
14
|
+
const datasource = new Datasource(projectConfig);
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(projectConfig.testsDirectoryPath)) {
|
|
17
|
+
console.error(`Tests directory does not exist: ${projectConfig.testsDirectoryPath}`);
|
|
18
|
+
hasError = true;
|
|
19
|
+
|
|
20
|
+
return hasError;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const testFiles = datasource.listTests();
|
|
24
|
+
|
|
25
|
+
if (testFiles.length === 0) {
|
|
26
|
+
console.error(`No tests found in: ${projectConfig.testsDirectoryPath}`);
|
|
27
|
+
hasError = true;
|
|
28
|
+
|
|
29
|
+
return hasError;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const testFile of testFiles) {
|
|
33
|
+
const testFilePath = datasource.getEntityPath("test", testFile);
|
|
34
|
+
|
|
35
|
+
console.log("");
|
|
36
|
+
console.log(CLI_FORMAT_BOLD, `Testing: ${testFilePath.replace(rootDirectoryPath, "")}`);
|
|
37
|
+
|
|
38
|
+
const t = datasource.readTest(testFile);
|
|
39
|
+
|
|
40
|
+
if ((t as TestSegment).segment) {
|
|
41
|
+
// segment testing
|
|
42
|
+
const test = t as TestSegment;
|
|
43
|
+
|
|
44
|
+
const segmentHasError = testSegment(datasource, test);
|
|
45
|
+
|
|
46
|
+
if (segmentHasError) {
|
|
47
|
+
hasError = true;
|
|
48
|
+
}
|
|
49
|
+
} else if ((t as TestFeature).feature) {
|
|
50
|
+
// feature testing
|
|
51
|
+
const test = t as TestFeature;
|
|
52
|
+
|
|
53
|
+
const featureHasError = testFeature(datasource, projectConfig, test);
|
|
54
|
+
|
|
55
|
+
if (featureHasError) {
|
|
56
|
+
hasError = true;
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
console.error(` => Invalid test: ${JSON.stringify(test)}`);
|
|
60
|
+
hasError = true;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
console.log("");
|
|
65
|
+
if (hasError) {
|
|
66
|
+
console.log(CLI_FORMAT_RED, `Some tests failed`);
|
|
67
|
+
} else {
|
|
68
|
+
console.log(CLI_FORMAT_GREEN, `All tests passed`);
|
|
69
|
+
}
|
|
70
|
+
console.log("");
|
|
71
|
+
|
|
72
|
+
return hasError;
|
|
73
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { TestSegment, Condition } from "@featurevisor/types";
|
|
2
|
+
import { allConditionsAreMatched } from "@featurevisor/sdk";
|
|
3
|
+
|
|
4
|
+
import { Datasource } from "../datasource";
|
|
5
|
+
|
|
6
|
+
import { CLI_FORMAT_BOLD, CLI_FORMAT_RED } from "./cliFormat";
|
|
7
|
+
|
|
8
|
+
export function testSegment(datasource: Datasource, test: TestSegment): boolean {
|
|
9
|
+
let hasError = false;
|
|
10
|
+
|
|
11
|
+
const segmentKey = test.segment;
|
|
12
|
+
|
|
13
|
+
console.log(CLI_FORMAT_BOLD, ` Segment "${segmentKey}":`);
|
|
14
|
+
|
|
15
|
+
const segmentExists = datasource.entityExists("segment", segmentKey);
|
|
16
|
+
|
|
17
|
+
if (!segmentExists) {
|
|
18
|
+
console.error(CLI_FORMAT_RED, ` Segment does not exist: ${segmentKey}`);
|
|
19
|
+
hasError = true;
|
|
20
|
+
|
|
21
|
+
return hasError;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const parsedSegment = datasource.readSegment(segmentKey);
|
|
25
|
+
const conditions = parsedSegment.conditions as Condition | Condition[];
|
|
26
|
+
|
|
27
|
+
test.assertions.forEach(function (assertion, aIndex) {
|
|
28
|
+
const description = assertion.description || `#${aIndex + 1}`;
|
|
29
|
+
|
|
30
|
+
console.log(` Assertion #${aIndex + 1}: ${description}`);
|
|
31
|
+
|
|
32
|
+
const expected = assertion.expectedToMatch;
|
|
33
|
+
const actual = allConditionsAreMatched(conditions, assertion.context);
|
|
34
|
+
|
|
35
|
+
if (actual !== expected) {
|
|
36
|
+
hasError = true;
|
|
37
|
+
|
|
38
|
+
console.error(CLI_FORMAT_RED, ` Segment failed: expected "${expected}", got "${actual}"`);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return hasError;
|
|
43
|
+
}
|
package/lib/tester.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import { ProjectConfig } from "./config";
|
|
2
|
-
export declare function checkIfArraysAreEqual(a: any, b: any): boolean;
|
|
3
|
-
export declare function checkIfObjectsAreEqual(a: any, b: any): boolean;
|
|
4
|
-
export declare function testProject(rootDirectoryPath: string, projectConfig: ProjectConfig): boolean;
|
package/lib/tester.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.testProject = exports.checkIfObjectsAreEqual = exports.checkIfArraysAreEqual = void 0;
|
|
4
|
-
var fs = require("fs");
|
|
5
|
-
var sdk_1 = require("@featurevisor/sdk");
|
|
6
|
-
var config_1 = require("./config");
|
|
7
|
-
var builder_1 = require("./builder");
|
|
8
|
-
var datasource_1 = require("./datasource");
|
|
9
|
-
// @TODO: make it better
|
|
10
|
-
function checkIfArraysAreEqual(a, b) {
|
|
11
|
-
if (!Array.isArray(a) || !Array.isArray(b)) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
if (a.length !== b.length)
|
|
15
|
-
return false;
|
|
16
|
-
for (var i = 0; i < a.length; ++i) {
|
|
17
|
-
if (a[i] !== b[i]) {
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return true;
|
|
22
|
-
}
|
|
23
|
-
exports.checkIfArraysAreEqual = checkIfArraysAreEqual;
|
|
24
|
-
function checkIfObjectsAreEqual(a, b) {
|
|
25
|
-
if (typeof a !== "object" || typeof b !== "object") {
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
if (a === null || b === null) {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
if (Object.keys(a).length !== Object.keys(b).length) {
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
for (var key in a) {
|
|
35
|
-
if (a[key] !== b[key]) {
|
|
36
|
-
return false;
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
return true;
|
|
40
|
-
}
|
|
41
|
-
exports.checkIfObjectsAreEqual = checkIfObjectsAreEqual;
|
|
42
|
-
function testProject(rootDirectoryPath, projectConfig) {
|
|
43
|
-
var hasError = false;
|
|
44
|
-
var datasource = new datasource_1.Datasource(projectConfig);
|
|
45
|
-
if (!fs.existsSync(projectConfig.testsDirectoryPath)) {
|
|
46
|
-
console.error("Tests directory does not exist: ".concat(projectConfig.testsDirectoryPath));
|
|
47
|
-
hasError = true;
|
|
48
|
-
return hasError;
|
|
49
|
-
}
|
|
50
|
-
var testFiles = datasource.listTests();
|
|
51
|
-
if (testFiles.length === 0) {
|
|
52
|
-
console.error("No tests found in: ".concat(projectConfig.testsDirectoryPath));
|
|
53
|
-
hasError = true;
|
|
54
|
-
return hasError;
|
|
55
|
-
}
|
|
56
|
-
var _loop_1 = function (testFile) {
|
|
57
|
-
var testFilePath = datasource.getEntityPath("test", testFile);
|
|
58
|
-
console.log(" => Testing: ".concat(testFilePath.replace(rootDirectoryPath, "")));
|
|
59
|
-
var test_1 = datasource.readTest(testFile);
|
|
60
|
-
if (test_1.segment) {
|
|
61
|
-
// segment testing
|
|
62
|
-
var testSegment = test_1;
|
|
63
|
-
var segmentKey = testSegment.segment;
|
|
64
|
-
console.log(" => Segment \"".concat(segmentKey, "\":"));
|
|
65
|
-
var segmentExists = datasource.entityExists("segment", segmentKey);
|
|
66
|
-
if (!segmentExists) {
|
|
67
|
-
console.error(" => Segment does not exist: ".concat(segmentKey));
|
|
68
|
-
hasError = true;
|
|
69
|
-
return "continue";
|
|
70
|
-
}
|
|
71
|
-
var parsedSegment = datasource.readSegment(segmentKey);
|
|
72
|
-
var conditions_1 = parsedSegment.conditions;
|
|
73
|
-
testSegment.assertions.forEach(function (assertion, aIndex) {
|
|
74
|
-
var description = assertion.description || "#".concat(aIndex + 1);
|
|
75
|
-
console.log(" => Assertion #".concat(aIndex + 1, ": ").concat(description));
|
|
76
|
-
var expected = assertion.expectedToMatch;
|
|
77
|
-
var actual = (0, sdk_1.allConditionsAreMatched)(conditions_1, assertion.context);
|
|
78
|
-
if (actual !== expected) {
|
|
79
|
-
hasError = true;
|
|
80
|
-
console.error(" Segment failed: expected \"".concat(expected, "\", got \"").concat(actual, "\""));
|
|
81
|
-
}
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
else if (test_1.feature) {
|
|
85
|
-
// feature testing
|
|
86
|
-
var testFeature_1 = test_1;
|
|
87
|
-
var featureKey_1 = testFeature_1.feature;
|
|
88
|
-
console.log(" => Feature \"".concat(featureKey_1, "\":"));
|
|
89
|
-
testFeature_1.assertions.forEach(function (assertion, aIndex) {
|
|
90
|
-
var description = assertion.description || "at ".concat(assertion.at, "%");
|
|
91
|
-
console.log(" => Assertion #".concat(aIndex + 1, ": (").concat(assertion.environment, ") ").concat(description));
|
|
92
|
-
var featuresToInclude = Array.from(datasource.getRequiredFeaturesChain(testFeature_1.feature));
|
|
93
|
-
var datafileContent = (0, builder_1.buildDatafile)(projectConfig, datasource, {
|
|
94
|
-
schemaVersion: config_1.SCHEMA_VERSION,
|
|
95
|
-
revision: "testing",
|
|
96
|
-
environment: assertion.environment,
|
|
97
|
-
features: featuresToInclude,
|
|
98
|
-
}, JSON.parse(fs.readFileSync((0, builder_1.getExistingStateFilePath)(projectConfig, assertion.environment), "utf8")));
|
|
99
|
-
var sdk = (0, sdk_1.createInstance)({
|
|
100
|
-
datafile: datafileContent,
|
|
101
|
-
configureBucketValue: function () {
|
|
102
|
-
return assertion.at * (sdk_1.MAX_BUCKETED_NUMBER / 100);
|
|
103
|
-
},
|
|
104
|
-
// logger: createLogger({
|
|
105
|
-
// levels: ["debug", "info", "warn", "error"],
|
|
106
|
-
// }),
|
|
107
|
-
});
|
|
108
|
-
// isEnabled
|
|
109
|
-
if ("expectedToBeEnabled" in assertion) {
|
|
110
|
-
var isEnabled = sdk.isEnabled(featureKey_1, assertion.context);
|
|
111
|
-
if (isEnabled !== assertion.expectedToBeEnabled) {
|
|
112
|
-
hasError = true;
|
|
113
|
-
console.error(" isEnabled failed: expected \"".concat(assertion.expectedToBeEnabled, "\", got \"").concat(isEnabled, "\""));
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
// variation
|
|
117
|
-
if ("expectedVariation" in assertion) {
|
|
118
|
-
var variation = sdk.getVariation(featureKey_1, assertion.context);
|
|
119
|
-
if (variation !== assertion.expectedVariation) {
|
|
120
|
-
hasError = true;
|
|
121
|
-
console.error(" Variation failed: expected \"".concat(assertion.expectedVariation, "\", got \"").concat(variation, "\""));
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
// variables
|
|
125
|
-
if (typeof assertion.expectedVariables === "object") {
|
|
126
|
-
Object.keys(assertion.expectedVariables).forEach(function (variableKey) {
|
|
127
|
-
var expectedValue = assertion.expectedVariables && assertion.expectedVariables[variableKey];
|
|
128
|
-
var actualValue = sdk.getVariable(featureKey_1, variableKey, assertion.context);
|
|
129
|
-
var passed;
|
|
130
|
-
if (typeof expectedValue === "object") {
|
|
131
|
-
passed = checkIfObjectsAreEqual(expectedValue, actualValue);
|
|
132
|
-
}
|
|
133
|
-
else if (Array.isArray(expectedValue)) {
|
|
134
|
-
passed = checkIfArraysAreEqual(expectedValue, actualValue);
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
passed = expectedValue === actualValue;
|
|
138
|
-
}
|
|
139
|
-
if (!passed) {
|
|
140
|
-
hasError = true;
|
|
141
|
-
console.error(" Variable \"".concat(variableKey, "\" failed: expected ").concat(JSON.stringify(expectedValue), ", got \"").concat(JSON.stringify(actualValue), "\""));
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
console.error(" => Invalid test: ".concat(JSON.stringify(test_1)));
|
|
149
|
-
hasError = true;
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
for (var _i = 0, testFiles_1 = testFiles; _i < testFiles_1.length; _i++) {
|
|
153
|
-
var testFile = testFiles_1[_i];
|
|
154
|
-
_loop_1(testFile);
|
|
155
|
-
}
|
|
156
|
-
return hasError;
|
|
157
|
-
}
|
|
158
|
-
exports.testProject = testProject;
|
|
159
|
-
//# sourceMappingURL=tester.js.map
|
package/lib/tester.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tester.js","sourceRoot":"","sources":["../src/tester.ts"],"names":[],"mappings":"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"}
|
package/src/tester.ts
DELETED
|
@@ -1,213 +0,0 @@
|
|
|
1
|
-
import * as fs from "fs";
|
|
2
|
-
|
|
3
|
-
import { Condition, ExistingState, TestSegment, TestFeature } from "@featurevisor/types";
|
|
4
|
-
import { createInstance, allConditionsAreMatched, MAX_BUCKETED_NUMBER } from "@featurevisor/sdk";
|
|
5
|
-
|
|
6
|
-
import { ProjectConfig, SCHEMA_VERSION } from "./config";
|
|
7
|
-
import { getExistingStateFilePath, buildDatafile } from "./builder";
|
|
8
|
-
import { Datasource } from "./datasource";
|
|
9
|
-
|
|
10
|
-
// @TODO: make it better
|
|
11
|
-
export function checkIfArraysAreEqual(a, b) {
|
|
12
|
-
if (!Array.isArray(a) || !Array.isArray(b)) {
|
|
13
|
-
return false;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
if (a.length !== b.length) return false;
|
|
17
|
-
|
|
18
|
-
for (let i = 0; i < a.length; ++i) {
|
|
19
|
-
if (a[i] !== b[i]) {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export function checkIfObjectsAreEqual(a, b) {
|
|
28
|
-
if (typeof a !== "object" || typeof b !== "object") {
|
|
29
|
-
return false;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
if (a === null || b === null) {
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (Object.keys(a).length !== Object.keys(b).length) {
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
for (const key in a) {
|
|
41
|
-
if (a[key] !== b[key]) {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
return true;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function testProject(rootDirectoryPath: string, projectConfig: ProjectConfig): boolean {
|
|
50
|
-
let hasError = false;
|
|
51
|
-
const datasource = new Datasource(projectConfig);
|
|
52
|
-
|
|
53
|
-
if (!fs.existsSync(projectConfig.testsDirectoryPath)) {
|
|
54
|
-
console.error(`Tests directory does not exist: ${projectConfig.testsDirectoryPath}`);
|
|
55
|
-
hasError = true;
|
|
56
|
-
|
|
57
|
-
return hasError;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const testFiles = datasource.listTests();
|
|
61
|
-
|
|
62
|
-
if (testFiles.length === 0) {
|
|
63
|
-
console.error(`No tests found in: ${projectConfig.testsDirectoryPath}`);
|
|
64
|
-
hasError = true;
|
|
65
|
-
|
|
66
|
-
return hasError;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
for (const testFile of testFiles) {
|
|
70
|
-
const testFilePath = datasource.getEntityPath("test", testFile);
|
|
71
|
-
|
|
72
|
-
console.log(` => Testing: ${testFilePath.replace(rootDirectoryPath, "")}`);
|
|
73
|
-
|
|
74
|
-
const test = datasource.readTest(testFile);
|
|
75
|
-
|
|
76
|
-
if ((test as TestSegment).segment) {
|
|
77
|
-
// segment testing
|
|
78
|
-
const testSegment = test as TestSegment;
|
|
79
|
-
const segmentKey = testSegment.segment;
|
|
80
|
-
|
|
81
|
-
console.log(` => Segment "${segmentKey}":`);
|
|
82
|
-
|
|
83
|
-
const segmentExists = datasource.entityExists("segment", segmentKey);
|
|
84
|
-
|
|
85
|
-
if (!segmentExists) {
|
|
86
|
-
console.error(` => Segment does not exist: ${segmentKey}`);
|
|
87
|
-
hasError = true;
|
|
88
|
-
|
|
89
|
-
continue;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const parsedSegment = datasource.readSegment(segmentKey);
|
|
93
|
-
const conditions = parsedSegment.conditions as Condition | Condition[];
|
|
94
|
-
|
|
95
|
-
testSegment.assertions.forEach(function (assertion, aIndex) {
|
|
96
|
-
const description = assertion.description || `#${aIndex + 1}`;
|
|
97
|
-
|
|
98
|
-
console.log(` => Assertion #${aIndex + 1}: ${description}`);
|
|
99
|
-
|
|
100
|
-
const expected = assertion.expectedToMatch;
|
|
101
|
-
const actual = allConditionsAreMatched(conditions, assertion.context);
|
|
102
|
-
|
|
103
|
-
if (actual !== expected) {
|
|
104
|
-
hasError = true;
|
|
105
|
-
|
|
106
|
-
console.error(` Segment failed: expected "${expected}", got "${actual}"`);
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
} else if ((test as TestFeature).feature) {
|
|
110
|
-
// feature testing
|
|
111
|
-
const testFeature = test as TestFeature;
|
|
112
|
-
const featureKey = testFeature.feature;
|
|
113
|
-
|
|
114
|
-
console.log(` => Feature "${featureKey}":`);
|
|
115
|
-
|
|
116
|
-
testFeature.assertions.forEach(function (assertion, aIndex) {
|
|
117
|
-
const description = assertion.description || `at ${assertion.at}%`;
|
|
118
|
-
|
|
119
|
-
console.log(
|
|
120
|
-
` => Assertion #${aIndex + 1}: (${assertion.environment}) ${description}`,
|
|
121
|
-
);
|
|
122
|
-
|
|
123
|
-
const featuresToInclude = Array.from(
|
|
124
|
-
datasource.getRequiredFeaturesChain(testFeature.feature),
|
|
125
|
-
);
|
|
126
|
-
|
|
127
|
-
const datafileContent = buildDatafile(
|
|
128
|
-
projectConfig,
|
|
129
|
-
datasource,
|
|
130
|
-
{
|
|
131
|
-
schemaVersion: SCHEMA_VERSION,
|
|
132
|
-
revision: "testing",
|
|
133
|
-
environment: assertion.environment,
|
|
134
|
-
features: featuresToInclude,
|
|
135
|
-
},
|
|
136
|
-
JSON.parse(
|
|
137
|
-
fs.readFileSync(getExistingStateFilePath(projectConfig, assertion.environment), "utf8"),
|
|
138
|
-
) as ExistingState,
|
|
139
|
-
);
|
|
140
|
-
|
|
141
|
-
const sdk = createInstance({
|
|
142
|
-
datafile: datafileContent,
|
|
143
|
-
configureBucketValue: () => {
|
|
144
|
-
return assertion.at * (MAX_BUCKETED_NUMBER / 100);
|
|
145
|
-
},
|
|
146
|
-
// logger: createLogger({
|
|
147
|
-
// levels: ["debug", "info", "warn", "error"],
|
|
148
|
-
// }),
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// isEnabled
|
|
152
|
-
if ("expectedToBeEnabled" in assertion) {
|
|
153
|
-
const isEnabled = sdk.isEnabled(featureKey, assertion.context);
|
|
154
|
-
|
|
155
|
-
if (isEnabled !== assertion.expectedToBeEnabled) {
|
|
156
|
-
hasError = true;
|
|
157
|
-
|
|
158
|
-
console.error(
|
|
159
|
-
` isEnabled failed: expected "${assertion.expectedToBeEnabled}", got "${isEnabled}"`,
|
|
160
|
-
);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// variation
|
|
165
|
-
if ("expectedVariation" in assertion) {
|
|
166
|
-
const variation = sdk.getVariation(featureKey, assertion.context);
|
|
167
|
-
|
|
168
|
-
if (variation !== assertion.expectedVariation) {
|
|
169
|
-
hasError = true;
|
|
170
|
-
|
|
171
|
-
console.error(
|
|
172
|
-
` Variation failed: expected "${assertion.expectedVariation}", got "${variation}"`,
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// variables
|
|
178
|
-
if (typeof assertion.expectedVariables === "object") {
|
|
179
|
-
Object.keys(assertion.expectedVariables).forEach(function (variableKey) {
|
|
180
|
-
const expectedValue =
|
|
181
|
-
assertion.expectedVariables && assertion.expectedVariables[variableKey];
|
|
182
|
-
const actualValue = sdk.getVariable(featureKey, variableKey, assertion.context);
|
|
183
|
-
|
|
184
|
-
let passed;
|
|
185
|
-
|
|
186
|
-
if (typeof expectedValue === "object") {
|
|
187
|
-
passed = checkIfObjectsAreEqual(expectedValue, actualValue);
|
|
188
|
-
} else if (Array.isArray(expectedValue)) {
|
|
189
|
-
passed = checkIfArraysAreEqual(expectedValue, actualValue);
|
|
190
|
-
} else {
|
|
191
|
-
passed = expectedValue === actualValue;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (!passed) {
|
|
195
|
-
hasError = true;
|
|
196
|
-
|
|
197
|
-
console.error(
|
|
198
|
-
` Variable "${variableKey}" failed: expected ${JSON.stringify(
|
|
199
|
-
expectedValue,
|
|
200
|
-
)}, got "${JSON.stringify(actualValue)}"`,
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
}
|
|
205
|
-
});
|
|
206
|
-
} else {
|
|
207
|
-
console.error(` => Invalid test: ${JSON.stringify(test)}`);
|
|
208
|
-
hasError = true;
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return hasError;
|
|
213
|
-
}
|