@g_package/jest-cucumber-fusion 3.0.2 → 3.0.3
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/dist/index.cjs +39 -12
- package/package.json +9 -10
- package/src/feature-source.js +11 -4
- package/src/scenario-name.js +3 -6
- package/src/test-registration.js +16 -8
- package/src/value-description.js +29 -1
package/dist/index.cjs
CHANGED
|
@@ -58,6 +58,22 @@ var asText = (value) => {
|
|
|
58
58
|
}
|
|
59
59
|
};
|
|
60
60
|
var describeValue = (value) => `${typeof value} ${asText(value)}`;
|
|
61
|
+
var textOf = (value) => {
|
|
62
|
+
try {
|
|
63
|
+
return String(value);
|
|
64
|
+
} catch {
|
|
65
|
+
return describeValue(value);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
var describeThrown = (thrown) => {
|
|
69
|
+
let message;
|
|
70
|
+
try {
|
|
71
|
+
message = thrown == null ? void 0 : thrown.message;
|
|
72
|
+
} catch {
|
|
73
|
+
return describeValue(thrown);
|
|
74
|
+
}
|
|
75
|
+
return textOf(message ? message : thrown);
|
|
76
|
+
};
|
|
61
77
|
|
|
62
78
|
// src/shared-state.js
|
|
63
79
|
var STORE = /* @__PURE__ */ Symbol.for("@g_package/jest-cucumber-fusion@3");
|
|
@@ -9324,9 +9340,13 @@ var parseFeature = (featureText) => {
|
|
|
9324
9340
|
try {
|
|
9325
9341
|
return { document: parser.parse(featureText), ids };
|
|
9326
9342
|
} catch (parseFailure) {
|
|
9327
|
-
throw new Error(
|
|
9328
|
-
|
|
9329
|
-
|
|
9343
|
+
throw new Error(
|
|
9344
|
+
`Error parsing feature Gherkin: ${/** @type {Error} */
|
|
9345
|
+
parseFailure.message}`,
|
|
9346
|
+
{
|
|
9347
|
+
cause: parseFailure
|
|
9348
|
+
}
|
|
9349
|
+
);
|
|
9330
9350
|
}
|
|
9331
9351
|
};
|
|
9332
9352
|
var compilePickles = (document, absoluteFeatureFilePath, ids) => {
|
|
@@ -9334,7 +9354,8 @@ var compilePickles = (document, absoluteFeatureFilePath, ids) => {
|
|
|
9334
9354
|
return compile(document, absoluteFeatureFilePath, ids);
|
|
9335
9355
|
} catch (compileFailure) {
|
|
9336
9356
|
throw new Error(
|
|
9337
|
-
`Error parsing feature Gherkin: ${
|
|
9357
|
+
`Error parsing feature Gherkin: ${/** @type {Error} */
|
|
9358
|
+
compileFailure.message}`,
|
|
9338
9359
|
{
|
|
9339
9360
|
cause: compileFailure
|
|
9340
9361
|
}
|
|
@@ -9649,7 +9670,7 @@ var answerFromTemplate = (loadedFeature, scenario, scenarioNameTemplate) => {
|
|
|
9649
9670
|
} catch (templateFailure) {
|
|
9650
9671
|
throw refuseUnusableName(
|
|
9651
9672
|
scenario.title,
|
|
9652
|
-
`the scenarioNameTemplate threw: ${templateFailure
|
|
9673
|
+
`the scenarioNameTemplate threw: ${describeThrown(templateFailure)}`,
|
|
9653
9674
|
"make the template total over the four variables it is handed: featureTitle, featureTags, scenarioTitle and scenarioTags. A tag list may be empty, and a title may hold any character a feature file allows."
|
|
9654
9675
|
);
|
|
9655
9676
|
}
|
|
@@ -9706,7 +9727,13 @@ var namedScenarios = (loadedFeature, scenarios, scenarioNameTemplate) => scenari
|
|
|
9706
9727
|
)
|
|
9707
9728
|
})
|
|
9708
9729
|
);
|
|
9709
|
-
var isError = (value) =>
|
|
9730
|
+
var isError = (value) => {
|
|
9731
|
+
try {
|
|
9732
|
+
return Object.prototype.toString.call(value) === "[object Error]";
|
|
9733
|
+
} catch {
|
|
9734
|
+
return false;
|
|
9735
|
+
}
|
|
9736
|
+
};
|
|
9710
9737
|
var decorationOf = (stepText, stepArguments, originalMessage) => `Failing step: "${stepText}"
|
|
9711
9738
|
|
|
9712
9739
|
Step arguments: ${JSON.stringify(stepArguments)}
|
|
@@ -9723,13 +9750,13 @@ var decorateInPlace = (failure, decorated) => {
|
|
|
9723
9750
|
var decorate = (stepText, stepArguments, failure) => {
|
|
9724
9751
|
if (!isError(failure))
|
|
9725
9752
|
return new Error(
|
|
9726
|
-
decorationOf(
|
|
9727
|
-
stepText,
|
|
9728
|
-
stepArguments,
|
|
9729
|
-
failure && failure.message ? failure.message : failure
|
|
9730
|
-
)
|
|
9753
|
+
decorationOf(stepText, stepArguments, describeThrown(failure))
|
|
9731
9754
|
);
|
|
9732
|
-
const decorated = decorationOf(
|
|
9755
|
+
const decorated = decorationOf(
|
|
9756
|
+
stepText,
|
|
9757
|
+
stepArguments,
|
|
9758
|
+
textOf(failure.message)
|
|
9759
|
+
);
|
|
9733
9760
|
try {
|
|
9734
9761
|
return decorateInPlace(failure, decorated);
|
|
9735
9762
|
} catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@g_package/jest-cucumber-fusion",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.3",
|
|
4
4
|
"description": "Write cucumber test as part of a jest run (including coverage)",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
@@ -21,12 +21,13 @@
|
|
|
21
21
|
"coverage": "node --experimental-vm-modules node_modules/jest/bin/jest.js --coverage",
|
|
22
22
|
"lint": "eslint . --max-warnings 0",
|
|
23
23
|
"lint:secrets": "secretlint \"**/*\"",
|
|
24
|
-
"commit": "
|
|
24
|
+
"commit": "czg",
|
|
25
25
|
"semantic-release": "semantic-release",
|
|
26
26
|
"fmt": "npx prettier --write .",
|
|
27
27
|
"prepare": "node scripts/prepare-hooks.js",
|
|
28
28
|
"build": "node scripts/build-cjs.js",
|
|
29
|
-
"prepack": "npm run build"
|
|
29
|
+
"prepack": "npm run build",
|
|
30
|
+
"typecheck": "tsc -p jsconfig.json"
|
|
30
31
|
},
|
|
31
32
|
"repository": {
|
|
32
33
|
"type": "git",
|
|
@@ -50,7 +51,9 @@
|
|
|
50
51
|
"@eslint/js": "^10.0.1",
|
|
51
52
|
"@secretlint/secretlint-rule-pattern": "^13.0.7",
|
|
52
53
|
"@secretlint/secretlint-rule-preset-recommend": "^13.0.7",
|
|
53
|
-
"
|
|
54
|
+
"@types/jest": "^30.0.0",
|
|
55
|
+
"@types/node": "^18.19.130",
|
|
56
|
+
"czg": "1.14.0",
|
|
54
57
|
"esbuild": "0.28.2",
|
|
55
58
|
"eslint": "^10.12.0",
|
|
56
59
|
"eslint-plugin-jest": "^29.16.6",
|
|
@@ -62,16 +65,12 @@
|
|
|
62
65
|
"prettier": "3.9.9",
|
|
63
66
|
"secretlint": "^13.0.7",
|
|
64
67
|
"semantic-release": "^25.0.5",
|
|
65
|
-
"tsd": "^0.33.0"
|
|
68
|
+
"tsd": "^0.33.0",
|
|
69
|
+
"typescript": "^6.0.3"
|
|
66
70
|
},
|
|
67
71
|
"publishConfig": {
|
|
68
72
|
"access": "public"
|
|
69
73
|
},
|
|
70
|
-
"config": {
|
|
71
|
-
"commitizen": {
|
|
72
|
-
"path": "./node_modules/cz-conventional-changelog"
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
74
|
"release": {
|
|
76
75
|
"plugins": [
|
|
77
76
|
"@semantic-release/commit-analyzer",
|
package/src/feature-source.js
CHANGED
|
@@ -95,9 +95,15 @@ const parseFeature = (featureText) => {
|
|
|
95
95
|
try {
|
|
96
96
|
return { document: parser.parse(featureText), ids };
|
|
97
97
|
} catch (parseFailure) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
// @cucumber/gherkin throws Errors only: plain ones, or its own exceptions, which all
|
|
99
|
+
// extend Error (dist/Errors.js). The cast states that contract for the type check rather
|
|
100
|
+
// than adding a branch for a non-Error that Gherkin never throws.
|
|
101
|
+
throw new Error(
|
|
102
|
+
`Error parsing feature Gherkin: ${/** @type {Error} */ (parseFailure).message}`,
|
|
103
|
+
{
|
|
104
|
+
cause: parseFailure,
|
|
105
|
+
},
|
|
106
|
+
);
|
|
101
107
|
}
|
|
102
108
|
};
|
|
103
109
|
|
|
@@ -105,8 +111,9 @@ const compilePickles = (document, absoluteFeatureFilePath, ids) => {
|
|
|
105
111
|
try {
|
|
106
112
|
return gherkin.compile(document, absoluteFeatureFilePath, ids);
|
|
107
113
|
} catch (compileFailure) {
|
|
114
|
+
// Errors only, as for the parse above.
|
|
108
115
|
throw new Error(
|
|
109
|
-
`Error parsing feature Gherkin: ${compileFailure.message}`,
|
|
116
|
+
`Error parsing feature Gherkin: ${/** @type {Error} */ (compileFailure).message}`,
|
|
110
117
|
{
|
|
111
118
|
cause: compileFailure,
|
|
112
119
|
},
|
package/src/scenario-name.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
//
|
|
12
12
|
// Pure: no filesystem, no parser, no Jest global. The port calls it; it calls the template.
|
|
13
13
|
|
|
14
|
-
import { describeValue } from "./value-description.js";
|
|
14
|
+
import { describeThrown, describeValue } from "./value-description.js";
|
|
15
15
|
|
|
16
16
|
// Kept as the first line of the refusal because the previous engine used it, so anything a
|
|
17
17
|
// consumer greps for still matches.
|
|
@@ -73,13 +73,10 @@ const answerFromTemplate = (loadedFeature, scenario, scenarioNameTemplate) => {
|
|
|
73
73
|
try {
|
|
74
74
|
return scenarioNameTemplate(templateVariablesFor(loadedFeature, scenario));
|
|
75
75
|
} catch (templateFailure) {
|
|
76
|
+
// A consumer's template can throw anything; describeThrown quotes anything.
|
|
76
77
|
throw refuseUnusableName(
|
|
77
78
|
scenario.title,
|
|
78
|
-
`the scenarioNameTemplate threw: ${
|
|
79
|
-
templateFailure && templateFailure.message
|
|
80
|
-
? templateFailure.message
|
|
81
|
-
: templateFailure
|
|
82
|
-
}`,
|
|
79
|
+
`the scenarioNameTemplate threw: ${describeThrown(templateFailure)}`,
|
|
83
80
|
"make the template total over the four variables it is handed: featureTitle, " +
|
|
84
81
|
"featureTags, scenarioTitle and scenarioTags. A tag list may be empty, and a title " +
|
|
85
82
|
"may hold any character a feature file allows.",
|
package/src/test-registration.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { findMatchingStep } from "./step-matching.js";
|
|
11
11
|
import { unmatchedStepRefusal } from "./code-suggestion.js";
|
|
12
12
|
import { nameForScenario } from "./scenario-name.js";
|
|
13
|
+
import { describeThrown, textOf } from "./value-description.js";
|
|
13
14
|
|
|
14
15
|
// BIND BEFORE REGISTERING. Every step of every scenario is matched up front, so a refusal
|
|
15
16
|
// (an unmatched step, an ambiguous one, an unsupported keyword) leaves Fusion() on the way
|
|
@@ -70,8 +71,15 @@ const namedScenarios = (loadedFeature, scenarios, scenarioNameTemplate) =>
|
|
|
70
71
|
// match. Recognised by its tag rather than instanceof, which fails for an error made in another
|
|
71
72
|
// realm (Node's own, from inside Jest's test context). Anything that cannot be rewritten (a
|
|
72
73
|
// frozen error) is wrapped with the original as its cause; anything else thrown is wrapped.
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
// Reading the tag can itself throw, for a Proxy whose traps throw or one that was revoked; such
|
|
75
|
+
// a value is no Error, so it is wrapped like any other.
|
|
76
|
+
const isError = (value) => {
|
|
77
|
+
try {
|
|
78
|
+
return Object.prototype.toString.call(value) === "[object Error]";
|
|
79
|
+
} catch {
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
};
|
|
75
83
|
|
|
76
84
|
const decorationOf = (stepText, stepArguments, originalMessage) =>
|
|
77
85
|
`Failing step: "${stepText}"\n\n` +
|
|
@@ -90,13 +98,13 @@ const decorateInPlace = (failure, decorated) => {
|
|
|
90
98
|
const decorate = (stepText, stepArguments, failure) => {
|
|
91
99
|
if (!isError(failure))
|
|
92
100
|
return new Error(
|
|
93
|
-
decorationOf(
|
|
94
|
-
stepText,
|
|
95
|
-
stepArguments,
|
|
96
|
-
failure && failure.message ? failure.message : failure,
|
|
97
|
-
),
|
|
101
|
+
decorationOf(stepText, stepArguments, describeThrown(failure)),
|
|
98
102
|
);
|
|
99
|
-
const decorated = decorationOf(
|
|
103
|
+
const decorated = decorationOf(
|
|
104
|
+
stepText,
|
|
105
|
+
stepArguments,
|
|
106
|
+
textOf(failure.message),
|
|
107
|
+
);
|
|
100
108
|
try {
|
|
101
109
|
return decorateInPlace(failure, decorated);
|
|
102
110
|
} catch {
|
package/src/value-description.js
CHANGED
|
@@ -23,4 +23,32 @@ const asText = (value) => {
|
|
|
23
23
|
|
|
24
24
|
const describeValue = (value) => `${typeof value} ${asText(value)}`;
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
// How a report quotes what the consumer's code threw: its message when that is truthy,
|
|
27
|
+
// otherwise the value itself, as text. textOf is the text step alone, for a value already known
|
|
28
|
+
// to be the message.
|
|
29
|
+
//
|
|
30
|
+
// A template literal cannot make text of a Symbol, of an object with no prototype, or of an
|
|
31
|
+
// object whose message getter or toString throws, so quoting those crashed the report and the
|
|
32
|
+
// consumer saw a TypeError instead of their own failure (the PR #36 fuzz run). String() makes
|
|
33
|
+
// the same text as a template literal for everything else and accepts a Symbol; anything still
|
|
34
|
+
// unprintable is named as describeValue names it. The message is read once, so a getter that
|
|
35
|
+
// answers differently each time cannot be quoted as one thing and checked as another.
|
|
36
|
+
const textOf = (value) => {
|
|
37
|
+
try {
|
|
38
|
+
return String(value);
|
|
39
|
+
} catch {
|
|
40
|
+
return describeValue(value);
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const describeThrown = (thrown) => {
|
|
45
|
+
let message;
|
|
46
|
+
try {
|
|
47
|
+
message = thrown == null ? undefined : thrown.message;
|
|
48
|
+
} catch {
|
|
49
|
+
return describeValue(thrown);
|
|
50
|
+
}
|
|
51
|
+
return textOf(message ? message : thrown);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export { describeThrown, describeValue, textOf };
|