@featurevisor/core 1.2.1 → 1.2.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 +8 -0
- package/coverage/clover.xml +2 -2
- package/coverage/coverage-final.json +1 -1
- 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/lib/tester/checkIfObjectsAreEqual.js.html +1 -1
- package/coverage/lcov-report/lib/tester/index.html +1 -1
- package/coverage/lcov-report/lib/tester/matrix.js.html +5 -5
- 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/coverage/lcov-report/src/tester/checkIfObjectsAreEqual.ts.html +1 -1
- package/coverage/lcov-report/src/tester/index.html +1 -1
- package/coverage/lcov-report/src/tester/matrix.ts.html +5 -5
- package/lib/tester/matrix.js +4 -4
- package/lib/tester/matrix.js.map +1 -1
- package/lib/tester/prettyDuration.d.ts +1 -0
- package/lib/tester/prettyDuration.js +31 -0
- package/lib/tester/prettyDuration.js.map +1 -0
- package/lib/tester/printTestResult.d.ts +2 -0
- package/lib/tester/printTestResult.js +41 -0
- package/lib/tester/printTestResult.js.map +1 -0
- package/lib/tester/testFeature.d.ts +2 -2
- package/lib/tester/testFeature.js +78 -31
- package/lib/tester/testFeature.js.map +1 -1
- package/lib/tester/testProject.js +37 -11
- package/lib/tester/testProject.js.map +1 -1
- package/lib/tester/testSegment.d.ts +2 -2
- package/lib/tester/testSegment.js +35 -12
- package/lib/tester/testSegment.js.map +1 -1
- package/package.json +5 -5
- package/src/tester/matrix.ts +4 -4
- package/src/tester/prettyDuration.ts +34 -0
- package/src/tester/printTestResult.ts +53 -0
- package/src/tester/testFeature.ts +87 -48
- package/src/tester/testProject.ts +44 -12
- package/src/tester/testSegment.ts +48 -20
|
@@ -41,10 +41,12 @@ var fs = require("fs");
|
|
|
41
41
|
var testSegment_1 = require("./testSegment");
|
|
42
42
|
var testFeature_1 = require("./testFeature");
|
|
43
43
|
var cliFormat_1 = require("./cliFormat");
|
|
44
|
+
var prettyDuration_1 = require("./prettyDuration");
|
|
45
|
+
var printTestResult_1 = require("./printTestResult");
|
|
44
46
|
function testProject(deps, options) {
|
|
45
47
|
if (options === void 0) { options = {}; }
|
|
46
48
|
return __awaiter(this, void 0, void 0, function () {
|
|
47
|
-
var rootDirectoryPath, projectConfig, datasource, hasError, testFiles, patterns, _i, testFiles_1, testFile, testFilePath, t, test_1,
|
|
49
|
+
var rootDirectoryPath, projectConfig, datasource, hasError, testFiles, startTime, patterns, passedTestsCount, failedTestsCount, passedAssertionsCount, failedAssertionsCount, _i, testFiles_1, testFile, testFilePath, t, test_1, testResult, test_2, testResult, diffInMs, testSpecsMessage, testAssertionsMessage;
|
|
48
50
|
return __generator(this, function (_a) {
|
|
49
51
|
switch (_a.label) {
|
|
50
52
|
case 0:
|
|
@@ -63,10 +65,15 @@ function testProject(deps, options) {
|
|
|
63
65
|
hasError = true;
|
|
64
66
|
return [2 /*return*/, hasError];
|
|
65
67
|
}
|
|
68
|
+
startTime = Date.now();
|
|
66
69
|
patterns = {
|
|
67
70
|
keyPattern: options.keyPattern ? new RegExp(options.keyPattern) : undefined,
|
|
68
71
|
assertionPattern: options.assertionPattern ? new RegExp(options.assertionPattern) : undefined,
|
|
69
72
|
};
|
|
73
|
+
passedTestsCount = 0;
|
|
74
|
+
failedTestsCount = 0;
|
|
75
|
+
passedAssertionsCount = 0;
|
|
76
|
+
failedAssertionsCount = 0;
|
|
70
77
|
_i = 0, testFiles_1 = testFiles;
|
|
71
78
|
_a.label = 2;
|
|
72
79
|
case 2:
|
|
@@ -81,12 +88,19 @@ function testProject(deps, options) {
|
|
|
81
88
|
if (patterns.keyPattern && !patterns.keyPattern.test(test_1.segment)) {
|
|
82
89
|
return [3 /*break*/, 8];
|
|
83
90
|
}
|
|
84
|
-
console.log(cliFormat_1.CLI_FORMAT_BOLD, "\nTesting: ".concat(testFilePath.replace(rootDirectoryPath, "")));
|
|
85
91
|
return [4 /*yield*/, (0, testSegment_1.testSegment)(datasource, test_1, patterns)];
|
|
86
92
|
case 4:
|
|
87
|
-
|
|
88
|
-
|
|
93
|
+
testResult = _a.sent();
|
|
94
|
+
(0, printTestResult_1.printTestResult)(testResult, testFilePath, rootDirectoryPath);
|
|
95
|
+
if (!testResult.passed) {
|
|
89
96
|
hasError = true;
|
|
97
|
+
failedTestsCount++;
|
|
98
|
+
failedAssertionsCount += testResult.assertions.filter(function (a) { return !a.passed; }).length;
|
|
99
|
+
passedAssertionsCount += testResult.assertions.length - failedAssertionsCount;
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
passedTestsCount++;
|
|
103
|
+
passedAssertionsCount += testResult.assertions.length;
|
|
90
104
|
}
|
|
91
105
|
return [3 /*break*/, 8];
|
|
92
106
|
case 5:
|
|
@@ -95,12 +109,19 @@ function testProject(deps, options) {
|
|
|
95
109
|
if (patterns.keyPattern && !patterns.keyPattern.test(test_2.feature)) {
|
|
96
110
|
return [3 /*break*/, 8];
|
|
97
111
|
}
|
|
98
|
-
console.log(cliFormat_1.CLI_FORMAT_BOLD, "\nTesting: ".concat(testFilePath.replace(rootDirectoryPath, "")));
|
|
99
112
|
return [4 /*yield*/, (0, testFeature_1.testFeature)(datasource, projectConfig, test_2, options, patterns)];
|
|
100
113
|
case 6:
|
|
101
|
-
|
|
102
|
-
|
|
114
|
+
testResult = _a.sent();
|
|
115
|
+
(0, printTestResult_1.printTestResult)(testResult, testFilePath, rootDirectoryPath);
|
|
116
|
+
if (!testResult.passed) {
|
|
103
117
|
hasError = true;
|
|
118
|
+
failedTestsCount++;
|
|
119
|
+
failedAssertionsCount += testResult.assertions.filter(function (a) { return !a.passed; }).length;
|
|
120
|
+
passedAssertionsCount += testResult.assertions.length - failedAssertionsCount;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
passedTestsCount++;
|
|
124
|
+
passedAssertionsCount += testResult.assertions.length;
|
|
104
125
|
}
|
|
105
126
|
return [3 /*break*/, 8];
|
|
106
127
|
case 7:
|
|
@@ -111,14 +132,19 @@ function testProject(deps, options) {
|
|
|
111
132
|
_i++;
|
|
112
133
|
return [3 /*break*/, 2];
|
|
113
134
|
case 9:
|
|
114
|
-
|
|
135
|
+
diffInMs = Date.now() - startTime;
|
|
136
|
+
console.log("\n---\n");
|
|
137
|
+
testSpecsMessage = "Test specs: ".concat(passedTestsCount, " passed, ").concat(failedTestsCount, " failed");
|
|
138
|
+
testAssertionsMessage = "Assertions: ".concat(passedAssertionsCount, " passed, ").concat(failedAssertionsCount, " failed");
|
|
115
139
|
if (hasError) {
|
|
116
|
-
console.log(cliFormat_1.CLI_FORMAT_RED,
|
|
140
|
+
console.log(cliFormat_1.CLI_FORMAT_RED, testSpecsMessage);
|
|
141
|
+
console.log(cliFormat_1.CLI_FORMAT_RED, testAssertionsMessage);
|
|
117
142
|
}
|
|
118
143
|
else {
|
|
119
|
-
console.log(cliFormat_1.CLI_FORMAT_GREEN,
|
|
144
|
+
console.log(cliFormat_1.CLI_FORMAT_GREEN, testSpecsMessage);
|
|
145
|
+
console.log(cliFormat_1.CLI_FORMAT_GREEN, testAssertionsMessage);
|
|
120
146
|
}
|
|
121
|
-
console.log("");
|
|
147
|
+
console.log(cliFormat_1.CLI_FORMAT_BOLD, "Time: ".concat((0, prettyDuration_1.prettyDuration)(diffInMs)));
|
|
122
148
|
return [2 /*return*/, hasError];
|
|
123
149
|
}
|
|
124
150
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testProject.js","sourceRoot":"","sources":["../../src/tester/testProject.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,uBAAyB;AAIzB,6CAA4C;AAC5C,6CAA4C;AAC5C,yCAAgF;
|
|
1
|
+
{"version":3,"file":"testProject.js","sourceRoot":"","sources":["../../src/tester/testProject.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { TestSegment } from "@featurevisor/types";
|
|
1
|
+
import { TestSegment, TestResult } from "@featurevisor/types";
|
|
2
2
|
import { Datasource } from "../datasource";
|
|
3
|
-
export declare function testSegment(datasource: Datasource, test: TestSegment, patterns: any): Promise<
|
|
3
|
+
export declare function testSegment(datasource: Datasource, test: TestSegment, patterns: any): Promise<TestResult>;
|
|
@@ -38,24 +38,31 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.testSegment = void 0;
|
|
40
40
|
var sdk_1 = require("@featurevisor/sdk");
|
|
41
|
-
var cliFormat_1 = require("./cliFormat");
|
|
42
41
|
var matrix_1 = require("./matrix");
|
|
43
42
|
function testSegment(datasource, test, patterns) {
|
|
44
43
|
return __awaiter(this, void 0, void 0, function () {
|
|
45
|
-
var
|
|
44
|
+
var testStartTime, segmentKey, testResult, segmentExists, parsedSegment, conditions;
|
|
46
45
|
return __generator(this, function (_a) {
|
|
47
46
|
switch (_a.label) {
|
|
48
47
|
case 0:
|
|
49
|
-
|
|
48
|
+
testStartTime = Date.now();
|
|
50
49
|
segmentKey = test.segment;
|
|
51
|
-
|
|
50
|
+
testResult = {
|
|
51
|
+
type: "segment",
|
|
52
|
+
key: segmentKey,
|
|
53
|
+
// to be updated later
|
|
54
|
+
notFound: false,
|
|
55
|
+
duration: 0,
|
|
56
|
+
passed: true,
|
|
57
|
+
assertions: [],
|
|
58
|
+
};
|
|
52
59
|
return [4 /*yield*/, datasource.segmentExists(segmentKey)];
|
|
53
60
|
case 1:
|
|
54
61
|
segmentExists = _a.sent();
|
|
55
62
|
if (!segmentExists) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
return [2 /*return*/,
|
|
63
|
+
testResult.notFound = true;
|
|
64
|
+
testResult.passed = false;
|
|
65
|
+
return [2 /*return*/, testResult];
|
|
59
66
|
}
|
|
60
67
|
return [4 /*yield*/, datasource.readSegment(segmentKey)];
|
|
61
68
|
case 2:
|
|
@@ -64,19 +71,35 @@ function testSegment(datasource, test, patterns) {
|
|
|
64
71
|
test.assertions.forEach(function (assertion, aIndex) {
|
|
65
72
|
var assertions = (0, matrix_1.getSegmentAssertionsFromMatrix)(aIndex, assertion);
|
|
66
73
|
assertions.forEach(function (assertion) {
|
|
74
|
+
var assertionStartTime = Date.now();
|
|
75
|
+
var testResultAssertion = {
|
|
76
|
+
description: assertion.description,
|
|
77
|
+
duration: 0,
|
|
78
|
+
passed: true,
|
|
79
|
+
errors: [],
|
|
80
|
+
};
|
|
67
81
|
if (patterns.assertionPattern && !patterns.assertionPattern.test(assertion.description)) {
|
|
68
82
|
return;
|
|
69
83
|
}
|
|
70
|
-
console.log(assertion.description);
|
|
71
84
|
var expected = assertion.expectedToMatch;
|
|
72
85
|
var actual = (0, sdk_1.allConditionsAreMatched)(conditions, assertion.context);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
86
|
+
var passed = actual === expected;
|
|
87
|
+
if (!passed) {
|
|
88
|
+
var testResultAssertionError = {
|
|
89
|
+
type: "segment",
|
|
90
|
+
expected: expected,
|
|
91
|
+
actual: actual,
|
|
92
|
+
};
|
|
93
|
+
testResultAssertion.errors.push(testResultAssertionError);
|
|
94
|
+
testResult.passed = false;
|
|
95
|
+
testResultAssertion.passed = passed;
|
|
76
96
|
}
|
|
97
|
+
testResult.assertions.push(testResultAssertion);
|
|
98
|
+
testResultAssertion.duration = Date.now() - assertionStartTime;
|
|
77
99
|
});
|
|
78
100
|
});
|
|
79
|
-
|
|
101
|
+
testResult.duration = Date.now() - testStartTime;
|
|
102
|
+
return [2 /*return*/, testResult];
|
|
80
103
|
}
|
|
81
104
|
});
|
|
82
105
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testSegment.js","sourceRoot":"","sources":["../../src/tester/testSegment.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"testSegment.js","sourceRoot":"","sources":["../../src/tester/testSegment.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOA,yCAA4D;AAI5D,mCAA0D;AAE1D,SAAsB,WAAW,CAC/B,UAAsB,EACtB,IAAiB,EACjB,QAAQ;;;;;;oBAEF,aAAa,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;oBAC3B,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC;oBAE1B,UAAU,GAAe;wBAC7B,IAAI,EAAE,SAAS;wBACf,GAAG,EAAE,UAAU;wBAEf,sBAAsB;wBACtB,QAAQ,EAAE,KAAK;wBACf,QAAQ,EAAE,CAAC;wBACX,MAAM,EAAE,IAAI;wBACZ,UAAU,EAAE,EAAE;qBACf,CAAC;oBAEoB,qBAAM,UAAU,CAAC,aAAa,CAAC,UAAU,CAAC,EAAA;;oBAA1D,aAAa,GAAG,SAA0C;oBAEhE,IAAI,CAAC,aAAa,EAAE;wBAClB,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC;wBAC3B,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC;wBAE1B,sBAAO,UAAU,EAAC;qBACnB;oBAEqB,qBAAM,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,EAAA;;oBAAxD,aAAa,GAAG,SAAwC;oBACxD,UAAU,GAAG,aAAa,CAAC,UAAqC,CAAC;oBAEvE,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,SAAS,EAAE,MAAM;wBACjD,IAAM,UAAU,GAAG,IAAA,uCAA8B,EAAC,MAAM,EAAE,SAAS,CAAC,CAAC;wBAErE,UAAU,CAAC,OAAO,CAAC,UAAU,SAAS;4BACpC,IAAM,kBAAkB,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;4BACtC,IAAM,mBAAmB,GAAwB;gCAC/C,WAAW,EAAE,SAAS,CAAC,WAAqB;gCAC5C,QAAQ,EAAE,CAAC;gCACX,MAAM,EAAE,IAAI;gCACZ,MAAM,EAAE,EAAE;6BACX,CAAC;4BAEF,IAAI,QAAQ,CAAC,gBAAgB,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,EAAE;gCACvF,OAAO;6BACR;4BAED,IAAM,QAAQ,GAAG,SAAS,CAAC,eAAe,CAAC;4BAC3C,IAAM,MAAM,GAAG,IAAA,6BAAuB,EAAC,UAAU,EAAE,SAAS,CAAC,OAAO,CAAC,CAAC;4BACtE,IAAM,MAAM,GAAG,MAAM,KAAK,QAAQ,CAAC;4BAEnC,IAAI,CAAC,MAAM,EAAE;gCACX,IAAM,wBAAwB,GAA6B;oCACzD,IAAI,EAAE,SAAS;oCACf,QAAQ,UAAA;oCACR,MAAM,QAAA;iCACP,CAAC;gCAED,mBAAmB,CAAC,MAAqC,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;gCAC1F,UAAU,CAAC,MAAM,GAAG,KAAK,CAAC;gCAC1B,mBAAmB,CAAC,MAAM,GAAG,MAAM,CAAC;6BACrC;4BAED,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;4BAChD,mBAAmB,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,kBAAkB,CAAC;wBACjE,CAAC,CAAC,CAAC;oBACL,CAAC,CAAC,CAAC;oBAEH,UAAU,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,aAAa,CAAC;oBAEjD,sBAAO,UAAU,EAAC;;;;CACnB;AAvED,kCAuEC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@featurevisor/core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Core package of Featurevisor for Node.js usage",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -44,9 +44,9 @@
|
|
|
44
44
|
},
|
|
45
45
|
"license": "MIT",
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@featurevisor/sdk": "^1.2.
|
|
48
|
-
"@featurevisor/site": "^1.2.
|
|
49
|
-
"@featurevisor/types": "^1.2.
|
|
47
|
+
"@featurevisor/sdk": "^1.2.2",
|
|
48
|
+
"@featurevisor/site": "^1.2.2",
|
|
49
|
+
"@featurevisor/types": "^1.2.2",
|
|
50
50
|
"axios": "^1.3.4",
|
|
51
51
|
"joi": "^17.8.3",
|
|
52
52
|
"js-yaml": "^4.1.0",
|
|
@@ -57,5 +57,5 @@
|
|
|
57
57
|
"@types/js-yaml": "^4.0.5",
|
|
58
58
|
"@types/tar": "^6.1.4"
|
|
59
59
|
},
|
|
60
|
-
"gitHead": "
|
|
60
|
+
"gitHead": "65f5422bceb755a6d6a2e5b0c025b41486612c73"
|
|
61
61
|
}
|
package/src/tester/matrix.ts
CHANGED
|
@@ -105,7 +105,7 @@ export function getFeatureAssertionsFromMatrix(
|
|
|
105
105
|
): FeatureAssertion[] {
|
|
106
106
|
if (!assertionWithMatrix.matrix) {
|
|
107
107
|
const assertion = { ...assertionWithMatrix };
|
|
108
|
-
assertion.description = `
|
|
108
|
+
assertion.description = `Assertion #${aIndex + 1}: (${assertion.environment}) ${
|
|
109
109
|
assertion.description || `at ${assertion.at}%`
|
|
110
110
|
}`;
|
|
111
111
|
|
|
@@ -118,7 +118,7 @@ export function getFeatureAssertionsFromMatrix(
|
|
|
118
118
|
for (let cIndex = 0; cIndex < combinations.length; cIndex++) {
|
|
119
119
|
const combination = combinations[cIndex];
|
|
120
120
|
const assertion = applyCombinationToFeatureAssertion(combination, assertionWithMatrix);
|
|
121
|
-
assertion.description = `
|
|
121
|
+
assertion.description = `Assertion #${aIndex + 1}: (${assertion.environment}) ${
|
|
122
122
|
assertion.description || `at ${assertion.at}%`
|
|
123
123
|
}`;
|
|
124
124
|
|
|
@@ -161,7 +161,7 @@ export function getSegmentAssertionsFromMatrix(
|
|
|
161
161
|
): SegmentAssertion[] {
|
|
162
162
|
if (!assertionWithMatrix.matrix) {
|
|
163
163
|
const assertion = { ...assertionWithMatrix };
|
|
164
|
-
assertion.description = `
|
|
164
|
+
assertion.description = `Assertion #${aIndex + 1}: ${
|
|
165
165
|
assertion.description || `#${aIndex + 1}`
|
|
166
166
|
}`;
|
|
167
167
|
|
|
@@ -174,7 +174,7 @@ export function getSegmentAssertionsFromMatrix(
|
|
|
174
174
|
for (let cIndex = 0; cIndex < combinations.length; cIndex++) {
|
|
175
175
|
const combination = combinations[cIndex];
|
|
176
176
|
const assertion = applyCombinationToSegmentAssertion(combination, assertionWithMatrix);
|
|
177
|
-
assertion.description = `
|
|
177
|
+
assertion.description = `Assertion #${aIndex + 1}: ${
|
|
178
178
|
assertion.description || `#${aIndex + 1}`
|
|
179
179
|
}`;
|
|
180
180
|
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function prettyDuration(diffInMs) {
|
|
2
|
+
let diff = Math.abs(diffInMs);
|
|
3
|
+
|
|
4
|
+
if (diff === 0) {
|
|
5
|
+
return `0ms`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const ms = diff % 1000;
|
|
9
|
+
diff = (diff - ms) / 1000;
|
|
10
|
+
const secs = diff % 60;
|
|
11
|
+
diff = (diff - secs) / 60;
|
|
12
|
+
const mins = diff % 60;
|
|
13
|
+
const hrs = (diff - mins) / 60;
|
|
14
|
+
|
|
15
|
+
let result = "";
|
|
16
|
+
|
|
17
|
+
if (hrs) {
|
|
18
|
+
result += ` ${hrs}h`;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (mins) {
|
|
22
|
+
result += ` ${mins}m`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (secs) {
|
|
26
|
+
result += ` ${secs}s`;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (ms) {
|
|
30
|
+
result += ` ${ms}ms`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return result.trim();
|
|
34
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { TestResult } from "@featurevisor/types";
|
|
2
|
+
|
|
3
|
+
import { CLI_FORMAT_BOLD, CLI_FORMAT_RED } from "./cliFormat";
|
|
4
|
+
import { prettyDuration } from "./prettyDuration";
|
|
5
|
+
|
|
6
|
+
export function printTestResult(testResult: TestResult, testFilePath, rootDirectoryPath) {
|
|
7
|
+
console.log("");
|
|
8
|
+
|
|
9
|
+
const title = `Testing: ${testFilePath.replace(rootDirectoryPath, "")} (${prettyDuration(
|
|
10
|
+
testResult.duration,
|
|
11
|
+
)})`;
|
|
12
|
+
console.log(title);
|
|
13
|
+
|
|
14
|
+
if (testResult.notFound) {
|
|
15
|
+
console.log(CLI_FORMAT_RED, ` => ${testResult.type} ${testResult.key} not found`);
|
|
16
|
+
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
console.log(CLI_FORMAT_BOLD, ` ${testResult.type} "${testResult.key}":`);
|
|
21
|
+
|
|
22
|
+
testResult.assertions.forEach(function (assertion) {
|
|
23
|
+
if (assertion.passed) {
|
|
24
|
+
console.log(` ✔ ${assertion.description} (${prettyDuration(assertion.duration)})`);
|
|
25
|
+
} else {
|
|
26
|
+
console.log(
|
|
27
|
+
CLI_FORMAT_RED,
|
|
28
|
+
` ✘ ${assertion.description} (${prettyDuration(assertion.duration)})`,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
assertion.errors?.forEach(function (error) {
|
|
32
|
+
if (error.message) {
|
|
33
|
+
console.log(CLI_FORMAT_RED, ` => ${error.message}`);
|
|
34
|
+
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (error.type === "variable") {
|
|
39
|
+
const variableKey = (error.details as any).variableKey;
|
|
40
|
+
|
|
41
|
+
console.log(CLI_FORMAT_RED, ` => variable key: ${variableKey}`);
|
|
42
|
+
console.log(CLI_FORMAT_RED, ` => expected: ${error.expected}`);
|
|
43
|
+
console.log(CLI_FORMAT_RED, ` => received: ${error.actual}`);
|
|
44
|
+
} else {
|
|
45
|
+
console.log(
|
|
46
|
+
CLI_FORMAT_RED,
|
|
47
|
+
` => ${error.type}: expected "${error.expected}", received "${error.actual}"`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
TestFeature,
|
|
3
|
+
TestResult,
|
|
4
|
+
TestResultAssertion,
|
|
5
|
+
TestResultAssertionError,
|
|
6
|
+
} from "@featurevisor/types";
|
|
2
7
|
import { createInstance, MAX_BUCKETED_NUMBER } from "@featurevisor/sdk";
|
|
3
8
|
|
|
4
9
|
import { Datasource } from "../datasource";
|
|
@@ -8,7 +13,6 @@ import { SCHEMA_VERSION } from "../config";
|
|
|
8
13
|
|
|
9
14
|
import { checkIfArraysAreEqual } from "./checkIfArraysAreEqual";
|
|
10
15
|
import { checkIfObjectsAreEqual } from "./checkIfObjectsAreEqual";
|
|
11
|
-
import { CLI_FORMAT_BOLD, CLI_FORMAT_RED } from "./cliFormat";
|
|
12
16
|
import { getFeatureAssertionsFromMatrix } from "./matrix";
|
|
13
17
|
|
|
14
18
|
export async function testFeature(
|
|
@@ -17,24 +21,39 @@ export async function testFeature(
|
|
|
17
21
|
test: TestFeature,
|
|
18
22
|
options: { verbose?: boolean; showDatafile?: boolean } = {},
|
|
19
23
|
patterns,
|
|
20
|
-
): Promise<
|
|
21
|
-
|
|
24
|
+
): Promise<TestResult> {
|
|
25
|
+
const testStartTime = Date.now();
|
|
22
26
|
const featureKey = test.feature;
|
|
23
27
|
|
|
24
|
-
|
|
28
|
+
const testResult: TestResult = {
|
|
29
|
+
type: "feature",
|
|
30
|
+
key: featureKey,
|
|
31
|
+
|
|
32
|
+
// to be updated later
|
|
33
|
+
notFound: false,
|
|
34
|
+
duration: 0,
|
|
35
|
+
passed: true,
|
|
36
|
+
assertions: [],
|
|
37
|
+
};
|
|
25
38
|
|
|
26
39
|
for (let aIndex = 0; aIndex < test.assertions.length; aIndex++) {
|
|
27
40
|
const assertions = getFeatureAssertionsFromMatrix(aIndex, test.assertions[aIndex]);
|
|
28
41
|
|
|
29
42
|
for (let bIndex = 0; bIndex < assertions.length; bIndex++) {
|
|
43
|
+
const assertionStartTime = Date.now();
|
|
30
44
|
const assertion = assertions[bIndex];
|
|
31
45
|
|
|
46
|
+
const testResultAssertion: TestResultAssertion = {
|
|
47
|
+
description: assertion.description as string,
|
|
48
|
+
duration: 0,
|
|
49
|
+
passed: true,
|
|
50
|
+
errors: [],
|
|
51
|
+
};
|
|
52
|
+
|
|
32
53
|
if (patterns.assertionPattern && !patterns.assertionPattern.test(assertion.description)) {
|
|
33
54
|
continue;
|
|
34
55
|
}
|
|
35
56
|
|
|
36
|
-
console.log(assertion.description);
|
|
37
|
-
|
|
38
57
|
const requiredChain = await datasource.getRequiredFeaturesChain(test.feature);
|
|
39
58
|
const featuresToInclude = Array.from(requiredChain);
|
|
40
59
|
|
|
@@ -68,17 +87,27 @@ export async function testFeature(
|
|
|
68
87
|
sdk.setLogLevels(["debug", "info", "warn", "error"]);
|
|
69
88
|
}
|
|
70
89
|
|
|
90
|
+
const feature = await datasource.readFeature(featureKey);
|
|
91
|
+
if (!feature) {
|
|
92
|
+
testResult.notFound = true;
|
|
93
|
+
testResult.passed = false;
|
|
94
|
+
|
|
95
|
+
return testResult;
|
|
96
|
+
}
|
|
97
|
+
|
|
71
98
|
// isEnabled
|
|
72
99
|
if ("expectedToBeEnabled" in assertion) {
|
|
73
100
|
const isEnabled = sdk.isEnabled(featureKey, assertion.context);
|
|
74
101
|
|
|
75
102
|
if (isEnabled !== assertion.expectedToBeEnabled) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
103
|
+
testResult.passed = false;
|
|
104
|
+
testResultAssertion.passed = false;
|
|
105
|
+
|
|
106
|
+
(testResultAssertion.errors as TestResultAssertionError[]).push({
|
|
107
|
+
type: "flag",
|
|
108
|
+
expected: assertion.expectedToBeEnabled,
|
|
109
|
+
actual: isEnabled,
|
|
110
|
+
});
|
|
82
111
|
}
|
|
83
112
|
}
|
|
84
113
|
|
|
@@ -87,26 +116,18 @@ export async function testFeature(
|
|
|
87
116
|
const variation = sdk.getVariation(featureKey, assertion.context);
|
|
88
117
|
|
|
89
118
|
if (variation !== assertion.expectedVariation) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
119
|
+
testResult.passed = false;
|
|
120
|
+
testResultAssertion.passed = false;
|
|
121
|
+
|
|
122
|
+
(testResultAssertion.errors as TestResultAssertionError[]).push({
|
|
123
|
+
type: "variation",
|
|
124
|
+
expected: assertion.expectedVariation,
|
|
125
|
+
actual: variation,
|
|
126
|
+
});
|
|
96
127
|
}
|
|
97
128
|
}
|
|
98
129
|
|
|
99
130
|
// variables
|
|
100
|
-
const feature = await datasource.readFeature(featureKey);
|
|
101
|
-
|
|
102
|
-
if (!feature) {
|
|
103
|
-
hasError = true;
|
|
104
|
-
|
|
105
|
-
console.error(CLI_FORMAT_RED, ` Feature "${featureKey}" failed: feature not found`);
|
|
106
|
-
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
131
|
if (typeof assertion.expectedVariables === "object") {
|
|
111
132
|
Object.keys(assertion.expectedVariables).forEach(function (variableKey) {
|
|
112
133
|
const expectedValue =
|
|
@@ -118,12 +139,15 @@ export async function testFeature(
|
|
|
118
139
|
const variableSchema = feature.variablesSchema?.find((v) => v.key === variableKey);
|
|
119
140
|
|
|
120
141
|
if (!variableSchema) {
|
|
121
|
-
|
|
142
|
+
testResult.passed = false;
|
|
143
|
+
testResultAssertion.passed = false;
|
|
122
144
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
145
|
+
(testResultAssertion.errors as TestResultAssertionError[]).push({
|
|
146
|
+
type: "variable",
|
|
147
|
+
expected: assertion.expectedVariation,
|
|
148
|
+
actual: undefined,
|
|
149
|
+
message: `schema for variable "${variableKey}" not found in feature`,
|
|
150
|
+
});
|
|
127
151
|
|
|
128
152
|
return;
|
|
129
153
|
}
|
|
@@ -144,12 +168,18 @@ export async function testFeature(
|
|
|
144
168
|
}
|
|
145
169
|
|
|
146
170
|
if (!passed) {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
171
|
+
testResult.passed = false;
|
|
172
|
+
testResultAssertion.passed = false;
|
|
173
|
+
|
|
174
|
+
(testResultAssertion.errors as TestResultAssertionError[]).push({
|
|
175
|
+
type: "variable",
|
|
176
|
+
expected:
|
|
177
|
+
typeof expectedValue !== "string" ? JSON.stringify(expectedValue) : expectedValue,
|
|
178
|
+
actual: typeof actualValue !== "string" ? JSON.stringify(actualValue) : actualValue,
|
|
179
|
+
details: {
|
|
180
|
+
variableKey,
|
|
181
|
+
},
|
|
182
|
+
});
|
|
153
183
|
}
|
|
154
184
|
} else {
|
|
155
185
|
// other types
|
|
@@ -162,19 +192,28 @@ export async function testFeature(
|
|
|
162
192
|
}
|
|
163
193
|
|
|
164
194
|
if (!passed) {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
195
|
+
testResult.passed = false;
|
|
196
|
+
testResultAssertion.passed = false;
|
|
197
|
+
|
|
198
|
+
(testResultAssertion.errors as TestResultAssertionError[]).push({
|
|
199
|
+
type: "variable",
|
|
200
|
+
expected: expectedValue as string,
|
|
201
|
+
actual: actualValue as string,
|
|
202
|
+
details: {
|
|
203
|
+
variableKey,
|
|
204
|
+
},
|
|
205
|
+
});
|
|
168
206
|
}
|
|
169
207
|
}
|
|
170
|
-
|
|
171
|
-
if (!passed) {
|
|
172
|
-
hasError = true;
|
|
173
|
-
}
|
|
174
208
|
});
|
|
175
209
|
}
|
|
210
|
+
|
|
211
|
+
testResultAssertion.duration = Date.now() - assertionStartTime;
|
|
212
|
+
testResult.assertions.push(testResultAssertion);
|
|
176
213
|
}
|
|
177
214
|
}
|
|
178
215
|
|
|
179
|
-
|
|
216
|
+
testResult.duration = Date.now() - testStartTime;
|
|
217
|
+
|
|
218
|
+
return testResult;
|
|
180
219
|
}
|