@reventlessdev/reventless-gwt 1.0.0-alpha.76 → 1.0.0-alpha.77
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 +8 -0
- package/package.json +6 -6
- package/src/Behavior_GWT.res +125 -159
- package/src/Behavior_GWT.res.mjs +156 -59
- package/src/BuildClassifier.res +15 -5
- package/src/BuildClassifier.res.mjs +3 -3
- package/src/Cli.res +157 -29
- package/src/Cli.res.mjs +138 -24
- package/src/Collector.res +2 -2
- package/src/Collector.res.mjs +2 -2
- package/src/ComponentMeta.res +16 -43
- package/src/ComponentMeta.res.mjs +12 -41
- package/src/Discovery.res +19 -19
- package/src/Discovery.res.mjs +5 -9
- package/src/DomainGraph.res +0 -0
- package/src/DomainGraph.res.mjs +0 -0
- package/src/FormatterHuman.res +14 -43
- package/src/FormatterHuman.res.mjs +21 -28
- package/src/FormatterJson.res +12 -5
- package/src/FormatterJson.res.mjs +8 -6
- package/src/FormatterTap.res +13 -52
- package/src/FormatterTap.res.mjs +25 -43
- package/src/FormatterVsCode.res +4 -19
- package/src/FormatterVsCode.res.mjs +4 -23
- package/src/LocalHost.res +11 -27
- package/src/LocalHost.res.mjs +6 -34
- package/src/MismatchRender.res +72 -0
- package/src/MismatchRender.res.mjs +127 -0
- package/src/Outcome.res +0 -64
- package/src/Outcome.res.mjs +0 -47
- package/src/PlatformScan.res +14 -10
- package/src/PlatformScan.res.mjs +14 -13
- package/tests/BuildClassifierTest.res +39 -0
- package/tests/BuildClassifierTest.res.mjs +44 -0
- package/tests/CliWatchScopeTest.res +62 -0
- package/tests/CliWatchScopeTest.res.mjs +95 -0
- package/tests/ComponentMetaTest.res +15 -0
- package/tests/ComponentMetaTest.res.mjs +14 -0
- package/tests/DomainGraphTest.res +2 -2
- package/tests/FlowAggregateGwtTest.res +2 -0
- package/tests/FlowAggregateGwtTest.res.mjs +4 -0
- package/tests/FormatterGoldenTest.res +125 -0
- package/tests/FormatterGoldenTest.res.mjs +162 -0
- package/tests/MappingGwtTest.res +2 -0
- package/tests/MappingGwtTest.res.mjs +4 -0
package/src/ComponentMeta.res
CHANGED
|
@@ -5,42 +5,16 @@
|
|
|
5
5
|
|
|
6
6
|
type component = {kind: string, name: string}
|
|
7
7
|
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
"StateViewSlice",
|
|
14
|
-
"StateViewSliceStream",
|
|
15
|
-
"ReadModel",
|
|
16
|
-
"ReadModelStream",
|
|
17
|
-
"AutomationSlice",
|
|
18
|
-
"InboundTranslationSlice",
|
|
19
|
-
"OutboundTranslationSlice",
|
|
20
|
-
"Extension",
|
|
21
|
-
"ExtensionPoint",
|
|
22
|
-
"Task",
|
|
23
|
-
]
|
|
24
|
-
|
|
25
|
-
let isKindFolder = (segment: string) => kindFolders->Array.includes(segment)
|
|
26
|
-
|
|
27
|
-
// Body-file suffixes stripped to recover the spec stem (longest first so
|
|
28
|
-
// `_ExtensionPointMapping` is removed before `_ExtensionPoint`). Spec and body
|
|
29
|
-
// files in one folder collapse to the same component name.
|
|
30
|
-
let bodySuffixes = [
|
|
31
|
-
"_ExtensionPointMapping",
|
|
32
|
-
"_ExtensionPoint",
|
|
33
|
-
"_Extension",
|
|
34
|
-
"_Projections",
|
|
35
|
-
"_Projection",
|
|
36
|
-
"_Mappings",
|
|
37
|
-
"_Behavior",
|
|
38
|
-
"_Automation",
|
|
39
|
-
"_Translation",
|
|
40
|
-
]
|
|
8
|
+
// The folder→kind vocabulary and body-file suffixes are the single source in
|
|
9
|
+
// `Reventless.ComponentKind` (shared with the plugin generator). Deriving from it
|
|
10
|
+
// means the gwt discovery recognises exactly the folder spellings the generator
|
|
11
|
+
// classifies — including the plural / short forms this file used to miss.
|
|
12
|
+
module Kind = Reventless.ComponentKind
|
|
41
13
|
|
|
14
|
+
// Strip a body-file suffix (longest first) to recover the spec stem, so spec and
|
|
15
|
+
// body files in one folder collapse to the same component name.
|
|
42
16
|
let stripBody = (stem: string) =>
|
|
43
|
-
switch bodySuffixes->Array.find(suf => String.endsWith(stem, suf)) {
|
|
17
|
+
switch Kind.bodySuffixes->Array.find(suf => String.endsWith(stem, suf)) {
|
|
44
18
|
| Some(suf) => String.slice(stem, ~start=0, ~end=String.length(stem) - String.length(suf))
|
|
45
19
|
| None => stem
|
|
46
20
|
}
|
|
@@ -87,18 +61,17 @@ let stem = (filename: string) =>
|
|
|
87
61
|
}
|
|
88
62
|
|
|
89
63
|
// {kind, name} for a discovered GWT test file, or None if it isn't inside a
|
|
90
|
-
// recognised kind folder.
|
|
64
|
+
// recognised kind folder. `kind` is the canonical folder name, so a plural
|
|
65
|
+
// folder (`Aggregates/`) reports the same kind as its singular form.
|
|
91
66
|
let componentOfTestFile = (path: string): option<component> =>
|
|
92
|
-
switch parentFolder(path) {
|
|
93
|
-
| Some(
|
|
94
|
-
|
|
95
|
-
| _ => None
|
|
67
|
+
switch parentFolder(path)->Option.flatMap(Kind.folderToKind) {
|
|
68
|
+
| Some(kind) => Some({kind: Kind.folderName(kind), name: path->basename->stem->stripGwt->stripBody})
|
|
69
|
+
| None => None
|
|
96
70
|
}
|
|
97
71
|
|
|
98
72
|
// {kind, name} for a src/ component file (spec or body), or None.
|
|
99
73
|
let componentOfSrcFile = (path: string): option<component> =>
|
|
100
|
-
switch parentFolder(path) {
|
|
101
|
-
| Some(
|
|
102
|
-
|
|
103
|
-
| _ => None
|
|
74
|
+
switch parentFolder(path)->Option.flatMap(Kind.folderToKind) {
|
|
75
|
+
| Some(kind) => Some({kind: Kind.folderName(kind), name: path->basename->stem->stripBody})
|
|
76
|
+
| None => None
|
|
104
77
|
}
|
|
@@ -1,40 +1,11 @@
|
|
|
1
1
|
// Generated by ReScript, PLEASE EDIT WITH CARE
|
|
2
2
|
|
|
3
3
|
import * as Stdlib_Array from "@rescript/runtime/lib/es6/Stdlib_Array.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
"Aggregate",
|
|
7
|
-
"StateChangeSlice",
|
|
8
|
-
"StateViewSlice",
|
|
9
|
-
"StateViewSliceStream",
|
|
10
|
-
"ReadModel",
|
|
11
|
-
"ReadModelStream",
|
|
12
|
-
"AutomationSlice",
|
|
13
|
-
"InboundTranslationSlice",
|
|
14
|
-
"OutboundTranslationSlice",
|
|
15
|
-
"Extension",
|
|
16
|
-
"ExtensionPoint",
|
|
17
|
-
"Task"
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
function isKindFolder(segment) {
|
|
21
|
-
return kindFolders.includes(segment);
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
let bodySuffixes = [
|
|
25
|
-
"_ExtensionPointMapping",
|
|
26
|
-
"_ExtensionPoint",
|
|
27
|
-
"_Extension",
|
|
28
|
-
"_Projections",
|
|
29
|
-
"_Projection",
|
|
30
|
-
"_Mappings",
|
|
31
|
-
"_Behavior",
|
|
32
|
-
"_Automation",
|
|
33
|
-
"_Translation"
|
|
34
|
-
];
|
|
4
|
+
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
5
|
+
import * as ComponentKind$Reventless from "@reventlessdev/reventless-spec/src/components/ComponentKind.res.mjs";
|
|
35
6
|
|
|
36
7
|
function stripBody(stem) {
|
|
37
|
-
let suf = bodySuffixes.find(suf => stem.endsWith(suf));
|
|
8
|
+
let suf = ComponentKind$Reventless.bodySuffixes.find(suf => stem.endsWith(suf));
|
|
38
9
|
if (suf !== undefined) {
|
|
39
10
|
return stem.slice(0, stem.length - suf.length | 0);
|
|
40
11
|
} else {
|
|
@@ -86,29 +57,29 @@ function stem(filename) {
|
|
|
86
57
|
}
|
|
87
58
|
|
|
88
59
|
function componentOfTestFile(path) {
|
|
89
|
-
let
|
|
90
|
-
if (
|
|
60
|
+
let kind = Stdlib_Option.flatMap(parentFolder(path), ComponentKind$Reventless.folderToKind);
|
|
61
|
+
if (kind !== undefined) {
|
|
91
62
|
return {
|
|
92
|
-
kind:
|
|
63
|
+
kind: ComponentKind$Reventless.folderName(kind),
|
|
93
64
|
name: stripBody(stripGwt(stem(basename(path))))
|
|
94
65
|
};
|
|
95
66
|
}
|
|
96
67
|
}
|
|
97
68
|
|
|
98
69
|
function componentOfSrcFile(path) {
|
|
99
|
-
let
|
|
100
|
-
if (
|
|
70
|
+
let kind = Stdlib_Option.flatMap(parentFolder(path), ComponentKind$Reventless.folderToKind);
|
|
71
|
+
if (kind !== undefined) {
|
|
101
72
|
return {
|
|
102
|
-
kind:
|
|
73
|
+
kind: ComponentKind$Reventless.folderName(kind),
|
|
103
74
|
name: stripBody(stem(basename(path)))
|
|
104
75
|
};
|
|
105
76
|
}
|
|
106
77
|
}
|
|
107
78
|
|
|
79
|
+
let Kind;
|
|
80
|
+
|
|
108
81
|
export {
|
|
109
|
-
|
|
110
|
-
isKindFolder,
|
|
111
|
-
bodySuffixes,
|
|
82
|
+
Kind,
|
|
112
83
|
stripBody,
|
|
113
84
|
stripGwt,
|
|
114
85
|
basename,
|
package/src/Discovery.res
CHANGED
|
@@ -43,36 +43,37 @@ let isGwtTestFile = (name: string) =>
|
|
|
43
43
|
String.endsWith(name, "GwtTest.res.mjs") ||
|
|
44
44
|
String.endsWith(name, "Gwt.res.mjs")
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
// Depth-first, pushing into a shared accumulator instead of rebuilding it with
|
|
47
|
+
// `Array.concat` per entry (which was O(n²) over a large tree). The traversal
|
|
48
|
+
// order is unchanged: files and nested subtree results still land in on-disk
|
|
49
|
+
// entry order.
|
|
50
|
+
let rec walk = async (dir: string, acc: array<string>): unit => {
|
|
47
51
|
let entries = try {
|
|
48
52
|
await _readdir(dir, {withFileTypes: true})
|
|
49
53
|
} catch {
|
|
50
54
|
| _ => []
|
|
51
55
|
}
|
|
52
56
|
if isPruned(entries) {
|
|
53
|
-
|
|
57
|
+
()
|
|
54
58
|
} else {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
found := Array.concat(found.contents, [full])
|
|
59
|
+
for i in 0 to entries->Array.length - 1 {
|
|
60
|
+
let entry = entries->Array.getUnsafe(i)
|
|
61
|
+
if !shouldIgnore(entry.name) {
|
|
62
|
+
let full = join(dir, entry.name)
|
|
63
|
+
if entry._isDirectory() {
|
|
64
|
+
await walk(full, acc)
|
|
65
|
+
} else if entry._isFile() && isGwtTestFile(entry.name) {
|
|
66
|
+
acc->Array.push(full)
|
|
67
|
+
}
|
|
65
68
|
}
|
|
66
69
|
}
|
|
67
70
|
}
|
|
68
|
-
found.contents
|
|
69
|
-
}
|
|
70
71
|
}
|
|
71
72
|
|
|
72
73
|
// Returns absolute paths of `*GWT*.res.mjs` test files reachable from the
|
|
73
74
|
// supplied roots. A root may be a directory or a single file.
|
|
74
75
|
let discover = async (roots: array<string>): array<string> => {
|
|
75
|
-
let found =
|
|
76
|
+
let found = []
|
|
76
77
|
for i in 0 to roots->Array.length - 1 {
|
|
77
78
|
let root = roots->Array.getUnsafe(i)
|
|
78
79
|
let absolute = isAbsolute(root) ? root : resolve(root)
|
|
@@ -83,16 +84,15 @@ let discover = async (roots: array<string>): array<string> => {
|
|
|
83
84
|
| _ => false
|
|
84
85
|
}
|
|
85
86
|
if isDir {
|
|
86
|
-
|
|
87
|
-
found := Array.concat(found.contents, collected)
|
|
87
|
+
await walk(absolute, found)
|
|
88
88
|
} else if isGwtTestFile(absolute) {
|
|
89
|
-
found
|
|
89
|
+
found->Array.push(absolute)
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
// Deduplicate (if roots overlap).
|
|
93
93
|
let seen = Dict.make()
|
|
94
94
|
let unique = []
|
|
95
|
-
found
|
|
95
|
+
found->Array.forEach(path =>
|
|
96
96
|
switch seen->Dict.get(path) {
|
|
97
97
|
| Some(_) => ()
|
|
98
98
|
| None => {
|
package/src/Discovery.res.mjs
CHANGED
|
@@ -39,22 +39,19 @@ async function walk(dir, acc) {
|
|
|
39
39
|
entries = [];
|
|
40
40
|
}
|
|
41
41
|
if (isPruned(entries)) {
|
|
42
|
-
return
|
|
42
|
+
return;
|
|
43
43
|
}
|
|
44
|
-
let found = acc;
|
|
45
44
|
for (let i = 0, i_finish = entries.length; i < i_finish; ++i) {
|
|
46
45
|
let entry = entries[i];
|
|
47
46
|
if (!ignoreNames.includes(entry.name)) {
|
|
48
47
|
let full = Nodepath.join(dir, entry.name);
|
|
49
48
|
if (entry.isDirectory()) {
|
|
50
|
-
|
|
51
|
-
found = found.concat(nested);
|
|
49
|
+
await walk(full, acc);
|
|
52
50
|
} else if (entry.isFile() && isGwtTestFile(entry.name)) {
|
|
53
|
-
|
|
51
|
+
acc.push(full);
|
|
54
52
|
}
|
|
55
53
|
}
|
|
56
54
|
}
|
|
57
|
-
return found;
|
|
58
55
|
}
|
|
59
56
|
|
|
60
57
|
async function discover(roots) {
|
|
@@ -70,10 +67,9 @@ async function discover(roots) {
|
|
|
70
67
|
isDir = false;
|
|
71
68
|
}
|
|
72
69
|
if (isDir) {
|
|
73
|
-
|
|
74
|
-
found = found.concat(collected);
|
|
70
|
+
await walk(absolute, found);
|
|
75
71
|
} else if (isGwtTestFile(absolute)) {
|
|
76
|
-
found
|
|
72
|
+
found.push(absolute);
|
|
77
73
|
}
|
|
78
74
|
}
|
|
79
75
|
let seen = {};
|
package/src/DomainGraph.res
CHANGED
|
Binary file
|
package/src/DomainGraph.res.mjs
CHANGED
|
Binary file
|
package/src/FormatterHuman.res
CHANGED
|
@@ -41,51 +41,22 @@ let formatLocation = (loc: option<Collector.location>) =>
|
|
|
41
41
|
| Some(l) => `${l.file}:${Int.toString(l.line)}`
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
| None => `Ok(${RenderRescript.renderMany(actualEvents)})`
|
|
56
|
-
}
|
|
57
|
-
` expected: ${exp}\n actual: ${act}`
|
|
58
|
-
}
|
|
59
|
-
| StateMismatch({key, expected, actual}) =>
|
|
60
|
-
` key: "${key}"\n expected: ${RenderRescript.renderOption(
|
|
61
|
-
expected,
|
|
62
|
-
)}\n actual: ${RenderRescript.renderOption(actual)}`
|
|
63
|
-
| NoEventExpected({actual}) =>
|
|
64
|
-
` expected no events\n actual: ${RenderRescript.renderMany(actual)}`
|
|
65
|
-
| TodoMismatch({expected, actual}) => {
|
|
66
|
-
let fmt = (arr: array<(string, JSON.t)>) =>
|
|
67
|
-
arr
|
|
68
|
-
->Array.map(((id, v)) => `(${id}, ${RenderRescript.render(v)})`)
|
|
69
|
-
->Array.join(", ")
|
|
70
|
-
` expected: [${fmt(expected)}]\n actual: [${fmt(actual)}]`
|
|
71
|
-
}
|
|
72
|
-
| AppendConditionMismatch({expected, actual}) =>
|
|
73
|
-
` expected: ${RenderRescript.render(expected)}\n actual: ${RenderRescript.render(
|
|
74
|
-
actual,
|
|
75
|
-
)}`
|
|
76
|
-
| TranslateError({expected, actual}) =>
|
|
77
|
-
` expected: ${expected}\n actual: ${actual->Option.getOr("(none)")}`
|
|
78
|
-
| QueryRowsMismatch({expected, actual}) =>
|
|
79
|
-
` expected: ${RenderRescript.renderMany(expected)}\n actual: ${RenderRescript.renderMany(
|
|
80
|
-
actual,
|
|
81
|
-
)}`
|
|
82
|
-
| PublishedActionsMismatch({expected, actual}) =>
|
|
83
|
-
` expected: ${RenderRescript.renderMany(expected)}\n actual: ${RenderRescript.renderMany(
|
|
84
|
-
actual,
|
|
85
|
-
)}`
|
|
86
|
-
| Throw({error, stack}) => ` error: ${error}\n${stack}`
|
|
44
|
+
// Frames one normalized field as a terminal line. Labels are padded so values
|
|
45
|
+
// align at column 12 (`key:`, `expected:`, `actual:`); `error:` is its own
|
|
46
|
+
// shorter form and the `Throw` stack is dumped raw beneath it.
|
|
47
|
+
let renderField = (f: MismatchRender.field) =>
|
|
48
|
+
switch f {
|
|
49
|
+
| Expected(v) => ` expected: ${v}`
|
|
50
|
+
| Actual(v) => ` actual: ${v}`
|
|
51
|
+
| Key(k) => ` key: "${k}"`
|
|
52
|
+
| ExpectedNoEvents => " expected no events"
|
|
53
|
+
| Error(e) => ` error: ${e}`
|
|
54
|
+
| Stack(s) => s
|
|
87
55
|
}
|
|
88
56
|
|
|
57
|
+
let renderMismatch = (m: Outcome.mismatch) =>
|
|
58
|
+
MismatchRender.normalize(m).fields->Array.map(renderField)->Array.join("\n")
|
|
59
|
+
|
|
89
60
|
let emitTest = (t: RunnerTypes.testResult) => {
|
|
90
61
|
let path = t.describePath->Array.join(" > ")
|
|
91
62
|
let full = path == "" ? t.name : `${path} > ${t.name}`
|
|
@@ -4,7 +4,7 @@ import * as Nodepath from "node:path";
|
|
|
4
4
|
import Picocolors from "picocolors";
|
|
5
5
|
import * as Stdlib_Option from "@rescript/runtime/lib/es6/Stdlib_Option.js";
|
|
6
6
|
import * as Hint$ReventlessGwt from "./Hint.res.mjs";
|
|
7
|
-
import * as
|
|
7
|
+
import * as MismatchRender$ReventlessGwt from "./MismatchRender.res.mjs";
|
|
8
8
|
|
|
9
9
|
function write(s) {
|
|
10
10
|
process.stdout.write(s);
|
|
@@ -29,36 +29,28 @@ function formatLocation(loc) {
|
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
-
function
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
return `
|
|
43
|
-
case "
|
|
44
|
-
return `
|
|
45
|
-
case "
|
|
46
|
-
return
|
|
47
|
-
case "TodoMismatch" :
|
|
48
|
-
let fmt = arr => arr.map(param => `(` + param[0] + `, ` + RenderRescript$ReventlessGwt.render(undefined, param[1]) + `)`).join(", ");
|
|
49
|
-
return ` expected: [` + fmt(m.expected) + `]\n actual: [` + fmt(m.actual) + `]`;
|
|
50
|
-
case "AppendConditionMismatch" :
|
|
51
|
-
return ` expected: ` + RenderRescript$ReventlessGwt.render(undefined, m.expected) + `\n actual: ` + RenderRescript$ReventlessGwt.render(undefined, m.actual);
|
|
52
|
-
case "TranslateError" :
|
|
53
|
-
return ` expected: ` + m.expected + `\n actual: ` + Stdlib_Option.getOr(m.actual, "(none)");
|
|
54
|
-
case "QueryRowsMismatch" :
|
|
55
|
-
case "PublishedActionsMismatch" :
|
|
56
|
-
return ` expected: ` + RenderRescript$ReventlessGwt.renderMany(m.expected) + `\n actual: ` + RenderRescript$ReventlessGwt.renderMany(m.actual);
|
|
57
|
-
case "Throw" :
|
|
58
|
-
return ` error: ` + m.error + `\n` + m.stack;
|
|
32
|
+
function renderField(f) {
|
|
33
|
+
if (typeof f !== "object") {
|
|
34
|
+
return " expected no events";
|
|
35
|
+
}
|
|
36
|
+
switch (f.TAG) {
|
|
37
|
+
case "Expected" :
|
|
38
|
+
return ` expected: ` + f._0;
|
|
39
|
+
case "Actual" :
|
|
40
|
+
return ` actual: ` + f._0;
|
|
41
|
+
case "Key" :
|
|
42
|
+
return ` key: "` + f._0 + `"`;
|
|
43
|
+
case "Error" :
|
|
44
|
+
return ` error: ` + f._0;
|
|
45
|
+
case "Stack" :
|
|
46
|
+
return f._0;
|
|
59
47
|
}
|
|
60
48
|
}
|
|
61
49
|
|
|
50
|
+
function renderMismatch(m) {
|
|
51
|
+
return MismatchRender$ReventlessGwt.normalize(m).fields.map(renderField).join("\n");
|
|
52
|
+
}
|
|
53
|
+
|
|
62
54
|
function emitTest(t) {
|
|
63
55
|
let path = t.describePath.join(" > ");
|
|
64
56
|
let full = path === "" ? t.name : path + ` > ` + t.name;
|
|
@@ -113,6 +105,7 @@ export {
|
|
|
113
105
|
writeLine,
|
|
114
106
|
formatFilePath,
|
|
115
107
|
formatLocation,
|
|
108
|
+
renderField,
|
|
116
109
|
renderMismatch,
|
|
117
110
|
emitTest,
|
|
118
111
|
emitFile,
|
package/src/FormatterJson.res
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
|
|
6
6
|
// 1.1.0 — additive: `PublishedActionsMismatch` mismatch kind for the
|
|
7
7
|
// Delegate_GWT / cross-plugin Flow_GWT boundary steps.
|
|
8
|
-
|
|
8
|
+
// The default the emitted envelope carries; the `--schema-version` CLI flag
|
|
9
|
+
// overrides it (threaded through `emit` / `streamRunStart`) so a consumer can
|
|
10
|
+
// pin the schema version its AI prompt was built against.
|
|
11
|
+
let defaultSchemaVersion = "1.1.0"
|
|
9
12
|
|
|
10
13
|
@val external processStdout: {"write": string => unit} = "process.stdout"
|
|
11
14
|
let write = (s: string) => processStdout["write"](s)
|
|
@@ -184,7 +187,11 @@ let summaryJson = (s: RunnerTypes.summary): JSON.t => {
|
|
|
184
187
|
JSON.Encode.object(d)
|
|
185
188
|
}
|
|
186
189
|
|
|
187
|
-
let envelope = (
|
|
190
|
+
let envelope = (
|
|
191
|
+
r: RunnerTypes.runResult,
|
|
192
|
+
~toolVersion: string,
|
|
193
|
+
~schemaVersion: string,
|
|
194
|
+
): JSON.t => {
|
|
188
195
|
let d = Dict.make()
|
|
189
196
|
d->Dict.set("schemaVersion", JSON.Encode.string(schemaVersion))
|
|
190
197
|
d->Dict.set("tool", JSON.Encode.string("reventless-gwt"))
|
|
@@ -196,11 +203,11 @@ let envelope = (r: RunnerTypes.runResult, ~toolVersion: string): JSON.t => {
|
|
|
196
203
|
JSON.Encode.object(d)
|
|
197
204
|
}
|
|
198
205
|
|
|
199
|
-
let emit = (r: RunnerTypes.runResult, ~toolVersion: string) =>
|
|
200
|
-
write(JSON.stringify(envelope(r, ~toolVersion), ~space=2) ++ "\n")
|
|
206
|
+
let emit = (r: RunnerTypes.runResult, ~toolVersion: string, ~schemaVersion=defaultSchemaVersion) =>
|
|
207
|
+
write(JSON.stringify(envelope(r, ~toolVersion, ~schemaVersion), ~space=2) ++ "\n")
|
|
201
208
|
|
|
202
209
|
// Streaming variant — NDJSON events.
|
|
203
|
-
let streamRunStart = (~toolVersion: string, ~startedAt: string) => {
|
|
210
|
+
let streamRunStart = (~toolVersion: string, ~startedAt: string, ~schemaVersion=defaultSchemaVersion) => {
|
|
204
211
|
let d = Dict.make()
|
|
205
212
|
d->Dict.set("type", JSON.Encode.string("runStarted"))
|
|
206
213
|
d->Dict.set("schemaVersion", JSON.Encode.string(schemaVersion))
|
|
@@ -7,7 +7,7 @@ import * as Outcome$ReventlessGwt from "./Outcome.res.mjs";
|
|
|
7
7
|
import * as RunnerTypes$ReventlessGwt from "./RunnerTypes.res.mjs";
|
|
8
8
|
import * as RenderRescript$ReventlessGwt from "./RenderRescript.res.mjs";
|
|
9
9
|
|
|
10
|
-
let
|
|
10
|
+
let defaultSchemaVersion = "1.1.0";
|
|
11
11
|
|
|
12
12
|
function write(s) {
|
|
13
13
|
process.stdout.write(s);
|
|
@@ -214,7 +214,7 @@ function summaryJson(s) {
|
|
|
214
214
|
return d;
|
|
215
215
|
}
|
|
216
216
|
|
|
217
|
-
function envelope(r, toolVersion) {
|
|
217
|
+
function envelope(r, toolVersion, schemaVersion) {
|
|
218
218
|
let d = {};
|
|
219
219
|
d["schemaVersion"] = schemaVersion;
|
|
220
220
|
d["tool"] = "reventless-gwt";
|
|
@@ -226,11 +226,13 @@ function envelope(r, toolVersion) {
|
|
|
226
226
|
return d;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
function emit(r, toolVersion) {
|
|
230
|
-
|
|
229
|
+
function emit(r, toolVersion, schemaVersionOpt) {
|
|
230
|
+
let schemaVersion = schemaVersionOpt !== undefined ? schemaVersionOpt : defaultSchemaVersion;
|
|
231
|
+
process.stdout.write(JSON.stringify(envelope(r, toolVersion, schemaVersion), undefined, 2) + "\n");
|
|
231
232
|
}
|
|
232
233
|
|
|
233
|
-
function streamRunStart(toolVersion, startedAt) {
|
|
234
|
+
function streamRunStart(toolVersion, startedAt, schemaVersionOpt) {
|
|
235
|
+
let schemaVersion = schemaVersionOpt !== undefined ? schemaVersionOpt : defaultSchemaVersion;
|
|
234
236
|
let d = {};
|
|
235
237
|
d["type"] = "runStarted";
|
|
236
238
|
d["schemaVersion"] = schemaVersion;
|
|
@@ -299,7 +301,7 @@ function streamRunEnd(r) {
|
|
|
299
301
|
}
|
|
300
302
|
|
|
301
303
|
export {
|
|
302
|
-
|
|
304
|
+
defaultSchemaVersion,
|
|
303
305
|
write,
|
|
304
306
|
writeLine,
|
|
305
307
|
renderValue,
|
package/src/FormatterTap.res
CHANGED
|
@@ -18,59 +18,20 @@ let yamlLine = (~indent=2, key: string, value: string) =>
|
|
|
18
18
|
String.repeat(" ", indent) ++ key ++ ": " ++ value
|
|
19
19
|
|
|
20
20
|
let renderMismatchYaml = (m: Outcome.mismatch) => {
|
|
21
|
-
let
|
|
22
|
-
lines
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
21
|
+
let n = MismatchRender.normalize(m)
|
|
22
|
+
let lines = [yamlLine("kind", yamlString(n.kind))]
|
|
23
|
+
// `ExpectedNoEvents` is a Human-only literal (TAP simply omits `expected`);
|
|
24
|
+
// every other field maps to one yaml `key: "escaped-value"` line.
|
|
25
|
+
n.fields->Array.forEach(f =>
|
|
26
|
+
switch f {
|
|
27
|
+
| Expected(v) => lines->Array.push(yamlLine("expected", yamlString(v)))
|
|
28
|
+
| Actual(v) => lines->Array.push(yamlLine("actual", yamlString(v)))
|
|
29
|
+
| Key(k) => lines->Array.push(yamlLine("key", yamlString(k)))
|
|
30
|
+
| Error(e) => lines->Array.push(yamlLine("error", yamlString(e)))
|
|
31
|
+
| Stack(s) => lines->Array.push(yamlLine("stack", yamlString(s)))
|
|
32
|
+
| ExpectedNoEvents => ()
|
|
27
33
|
}
|
|
28
|
-
|
|
29
|
-
lines->Array.push(
|
|
30
|
-
yamlLine("expected", yamlString("Error(" ++ RenderRescript.render(expected) ++ ")")),
|
|
31
|
-
)
|
|
32
|
-
let actualStr = switch actual {
|
|
33
|
-
| Some(v) => "Error(" ++ RenderRescript.render(v) ++ ")"
|
|
34
|
-
| None => "Ok(" ++ RenderRescript.renderMany(actualEvents) ++ ")"
|
|
35
|
-
}
|
|
36
|
-
lines->Array.push(yamlLine("actual", yamlString(actualStr)))
|
|
37
|
-
}
|
|
38
|
-
| StateMismatch({key, expected, actual}) => {
|
|
39
|
-
lines->Array.push(yamlLine("key", yamlString(key)))
|
|
40
|
-
lines->Array.push(yamlLine("expected", yamlString(RenderRescript.renderOption(expected))))
|
|
41
|
-
lines->Array.push(yamlLine("actual", yamlString(RenderRescript.renderOption(actual))))
|
|
42
|
-
}
|
|
43
|
-
| NoEventExpected({actual}) =>
|
|
44
|
-
lines->Array.push(yamlLine("actual", yamlString(RenderRescript.renderMany(actual))))
|
|
45
|
-
| TodoMismatch({expected, actual}) => {
|
|
46
|
-
let fmt = arr =>
|
|
47
|
-
arr
|
|
48
|
-
->Array.map(((id, v)) => "(" ++ id ++ ", " ++ RenderRescript.render(v) ++ ")")
|
|
49
|
-
->Array.join(", ")
|
|
50
|
-
lines->Array.push(yamlLine("expected", yamlString("[" ++ fmt(expected) ++ "]")))
|
|
51
|
-
lines->Array.push(yamlLine("actual", yamlString("[" ++ fmt(actual) ++ "]")))
|
|
52
|
-
}
|
|
53
|
-
| AppendConditionMismatch({expected, actual}) => {
|
|
54
|
-
lines->Array.push(yamlLine("expected", yamlString(RenderRescript.render(expected))))
|
|
55
|
-
lines->Array.push(yamlLine("actual", yamlString(RenderRescript.render(actual))))
|
|
56
|
-
}
|
|
57
|
-
| TranslateError({expected, actual}) => {
|
|
58
|
-
lines->Array.push(yamlLine("expected", yamlString(expected)))
|
|
59
|
-
lines->Array.push(yamlLine("actual", yamlString(actual->Option.getOr("(none)"))))
|
|
60
|
-
}
|
|
61
|
-
| QueryRowsMismatch({expected, actual}) => {
|
|
62
|
-
lines->Array.push(yamlLine("expected", yamlString(RenderRescript.renderMany(expected))))
|
|
63
|
-
lines->Array.push(yamlLine("actual", yamlString(RenderRescript.renderMany(actual))))
|
|
64
|
-
}
|
|
65
|
-
| PublishedActionsMismatch({expected, actual}) => {
|
|
66
|
-
lines->Array.push(yamlLine("expected", yamlString(RenderRescript.renderMany(expected))))
|
|
67
|
-
lines->Array.push(yamlLine("actual", yamlString(RenderRescript.renderMany(actual))))
|
|
68
|
-
}
|
|
69
|
-
| Throw({error, stack}) => {
|
|
70
|
-
lines->Array.push(yamlLine("error", yamlString(error)))
|
|
71
|
-
lines->Array.push(yamlLine("stack", yamlString(stack)))
|
|
72
|
-
}
|
|
73
|
-
}
|
|
34
|
+
)
|
|
74
35
|
lines->Array.join("\n")
|
|
75
36
|
}
|
|
76
37
|
|