@interop/was-conformance-suite 0.1.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.
Files changed (91) hide show
  1. package/LICENSE.md +20 -0
  2. package/README.md +264 -0
  3. package/dist/cli/index.d.ts +3 -0
  4. package/dist/cli/index.d.ts.map +1 -0
  5. package/dist/cli/index.js +69 -0
  6. package/dist/cli/index.js.map +1 -0
  7. package/dist/cli/main.d.ts +64 -0
  8. package/dist/cli/main.d.ts.map +1 -0
  9. package/dist/cli/main.js +294 -0
  10. package/dist/cli/main.js.map +1 -0
  11. package/dist/cli/reporters/json.d.ts +32 -0
  12. package/dist/cli/reporters/json.d.ts.map +1 -0
  13. package/dist/cli/reporters/json.js +30 -0
  14. package/dist/cli/reporters/json.js.map +1 -0
  15. package/dist/cli/reporters/pretty.d.ts +21 -0
  16. package/dist/cli/reporters/pretty.d.ts.map +1 -0
  17. package/dist/cli/reporters/pretty.js +152 -0
  18. package/dist/cli/reporters/pretty.js.map +1 -0
  19. package/dist/harness/assert.d.ts +13 -0
  20. package/dist/harness/assert.d.ts.map +1 -0
  21. package/dist/harness/assert.js +13 -0
  22. package/dist/harness/assert.js.map +1 -0
  23. package/dist/harness/runner.d.ts +25 -0
  24. package/dist/harness/runner.d.ts.map +1 -0
  25. package/dist/harness/runner.js +240 -0
  26. package/dist/harness/runner.js.map +1 -0
  27. package/dist/harness/types.d.ts +199 -0
  28. package/dist/harness/types.d.ts.map +1 -0
  29. package/dist/harness/types.js +2 -0
  30. package/dist/harness/types.js.map +1 -0
  31. package/dist/helpers.d.ts +103 -0
  32. package/dist/helpers.d.ts.map +1 -0
  33. package/dist/helpers.js +195 -0
  34. package/dist/helpers.js.map +1 -0
  35. package/dist/index.d.ts +28 -0
  36. package/dist/index.d.ts.map +1 -0
  37. package/dist/index.js +35 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/suites/changes-query-api.d.ts +9 -0
  40. package/dist/suites/changes-query-api.d.ts.map +1 -0
  41. package/dist/suites/changes-query-api.js +115 -0
  42. package/dist/suites/changes-query-api.js.map +1 -0
  43. package/dist/suites/client-backends.d.ts +11 -0
  44. package/dist/suites/client-backends.d.ts.map +1 -0
  45. package/dist/suites/client-backends.js +198 -0
  46. package/dist/suites/client-backends.js.map +1 -0
  47. package/dist/suites/client-delegation.d.ts +21 -0
  48. package/dist/suites/client-delegation.d.ts.map +1 -0
  49. package/dist/suites/client-delegation.js +94 -0
  50. package/dist/suites/client-delegation.js.map +1 -0
  51. package/dist/suites/client-export-import.d.ts +19 -0
  52. package/dist/suites/client-export-import.d.ts.map +1 -0
  53. package/dist/suites/client-export-import.js +74 -0
  54. package/dist/suites/client-export-import.js.map +1 -0
  55. package/dist/suites/client-resources.d.ts +11 -0
  56. package/dist/suites/client-resources.d.ts.map +1 -0
  57. package/dist/suites/client-resources.js +193 -0
  58. package/dist/suites/client-resources.js.map +1 -0
  59. package/dist/suites/client-spaces.d.ts +11 -0
  60. package/dist/suites/client-spaces.d.ts.map +1 -0
  61. package/dist/suites/client-spaces.js +347 -0
  62. package/dist/suites/client-spaces.js.map +1 -0
  63. package/dist/suites/collection-api.d.ts +7 -0
  64. package/dist/suites/collection-api.d.ts.map +1 -0
  65. package/dist/suites/collection-api.js +266 -0
  66. package/dist/suites/collection-api.js.map +1 -0
  67. package/dist/suites/encryption-marker-api.d.ts +9 -0
  68. package/dist/suites/encryption-marker-api.d.ts.map +1 -0
  69. package/dist/suites/encryption-marker-api.js +266 -0
  70. package/dist/suites/encryption-marker-api.js.map +1 -0
  71. package/dist/suites/index.d.ts +23 -0
  72. package/dist/suites/index.d.ts.map +1 -0
  73. package/dist/suites/index.js +32 -0
  74. package/dist/suites/index.js.map +1 -0
  75. package/dist/suites/policy-api.d.ts +12 -0
  76. package/dist/suites/policy-api.d.ts.map +1 -0
  77. package/dist/suites/policy-api.js +187 -0
  78. package/dist/suites/policy-api.js.map +1 -0
  79. package/dist/suites/resource-api.d.ts +8 -0
  80. package/dist/suites/resource-api.d.ts.map +1 -0
  81. package/dist/suites/resource-api.js +393 -0
  82. package/dist/suites/resource-api.js.map +1 -0
  83. package/dist/suites/server.d.ts +3 -0
  84. package/dist/suites/server.d.ts.map +1 -0
  85. package/dist/suites/server.js +25 -0
  86. package/dist/suites/server.js.map +1 -0
  87. package/dist/suites/spaces-api.d.ts +11 -0
  88. package/dist/suites/spaces-api.d.ts.map +1 -0
  89. package/dist/suites/spaces-api.js +411 -0
  90. package/dist/suites/spaces-api.js.map +1 -0
  91. package/package.json +93 -0
@@ -0,0 +1,294 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The testable core of the `was-conformance` CLI: parse argv into a config,
6
+ * run the suite through the injected `runConformance`, stream events to the
7
+ * chosen reporter, and map the result to an exit code. The bin entry
8
+ * (`index.ts`) is a thin wrapper that binds the real process I/O to this.
9
+ */
10
+ import { parseArgs } from 'node:util';
11
+ import { runConformance } from '../index.js';
12
+ import { createPrettyReporter } from './reporters/pretty.js';
13
+ import { createJsonReporter } from './reporters/json.js';
14
+ const HELP = `Usage: was-conformance <server-url> [options]
15
+
16
+ Runs the WAS conformance suite against a running server.
17
+ <server-url> falls back to the TEST_SERVER_URL environment variable.
18
+
19
+ Options:
20
+ -t, --token <token> onboarding token (or TEST_ONBOARDING_TOKEN)
21
+ -s, --suite <id> run only the named suite(s), repeatable
22
+ -g, --grep <pattern> filter tests by name
23
+ --include-optional treat optional tests as required (default: run
24
+ them but report as warnings)
25
+ --skip-optional do not run optional tests at all
26
+ -r, --reporter <name> pretty (default) | json
27
+ --timeout <ms> per-test timeout
28
+ --fail-fast stop on first failure
29
+ -h, --help show this help and exit
30
+ -V, --version print the version and exit`;
31
+ /**
32
+ * Parses argv (the tokens after `node script`) into a `ParseResult`, resolving
33
+ * env fallbacks. Pure: it never touches the network or the suite registry, so
34
+ * arg handling is unit-testable on its own.
35
+ *
36
+ * @param argv {string[]} the raw argument tokens
37
+ * @param env {object} the environment (for TEST_SERVER_URL / token fallbacks)
38
+ * @returns {ParseResult}
39
+ */
40
+ export function parseArgv(argv, env) {
41
+ let parsed;
42
+ try {
43
+ parsed = parseArgs({
44
+ args: argv,
45
+ allowPositionals: true,
46
+ options: {
47
+ token: { type: 'string', short: 't' },
48
+ suite: { type: 'string', short: 's', multiple: true },
49
+ grep: { type: 'string', short: 'g' },
50
+ 'include-optional': { type: 'boolean' },
51
+ 'skip-optional': { type: 'boolean' },
52
+ reporter: { type: 'string', short: 'r' },
53
+ timeout: { type: 'string' },
54
+ 'fail-fast': { type: 'boolean' },
55
+ help: { type: 'boolean', short: 'h' },
56
+ version: { type: 'boolean', short: 'V' }
57
+ }
58
+ });
59
+ }
60
+ catch (err) {
61
+ return {
62
+ kind: 'usage',
63
+ message: err instanceof Error ? err.message : String(err)
64
+ };
65
+ }
66
+ const { values, positionals } = parsed;
67
+ if (values.help) {
68
+ return { kind: 'help' };
69
+ }
70
+ if (values.version) {
71
+ return { kind: 'version' };
72
+ }
73
+ if (positionals.length > 1) {
74
+ return {
75
+ kind: 'usage',
76
+ message: `unexpected argument: ${positionals[1]}`
77
+ };
78
+ }
79
+ const serverUrl = positionals[0] ?? env.TEST_SERVER_URL;
80
+ if (!serverUrl) {
81
+ return {
82
+ kind: 'usage',
83
+ message: 'missing <server-url> (or set TEST_SERVER_URL)'
84
+ };
85
+ }
86
+ try {
87
+ new URL(serverUrl);
88
+ }
89
+ catch {
90
+ return { kind: 'usage', message: `invalid server URL: ${serverUrl}` };
91
+ }
92
+ const reporter = values.reporter ?? 'pretty';
93
+ if (reporter !== 'pretty' && reporter !== 'json') {
94
+ return {
95
+ kind: 'usage',
96
+ message: `unknown reporter: ${reporter} (expected 'pretty' or 'json')`
97
+ };
98
+ }
99
+ if (values['include-optional'] && values['skip-optional']) {
100
+ return {
101
+ kind: 'usage',
102
+ message: '--include-optional and --skip-optional are mutually exclusive'
103
+ };
104
+ }
105
+ let timeoutMs = null;
106
+ if (values.timeout !== undefined) {
107
+ const n = Number(values.timeout);
108
+ if (!Number.isInteger(n) || n <= 0) {
109
+ return {
110
+ kind: 'usage',
111
+ message: `invalid --timeout: ${values.timeout} (expected a positive integer of milliseconds)`
112
+ };
113
+ }
114
+ timeoutMs = n;
115
+ }
116
+ if (values.grep !== undefined) {
117
+ try {
118
+ new RegExp(values.grep);
119
+ }
120
+ catch {
121
+ return {
122
+ kind: 'usage',
123
+ message: `invalid --grep pattern: ${values.grep}`
124
+ };
125
+ }
126
+ }
127
+ return {
128
+ kind: 'run',
129
+ config: {
130
+ serverUrl,
131
+ onboardingToken: values.token ?? env.TEST_ONBOARDING_TOKEN ?? null,
132
+ suiteIds: values.suite ?? [],
133
+ grep: values.grep ?? null,
134
+ includeOptional: values['include-optional'] ?? false,
135
+ skipOptional: values['skip-optional'] ?? false,
136
+ reporter,
137
+ timeoutMs,
138
+ failFast: values['fail-fast'] ?? false
139
+ }
140
+ };
141
+ }
142
+ /**
143
+ * Selects and shapes the suites to run from the registry per the config:
144
+ * restrict to `--suite` ids, drop optional tests under `--skip-optional`, and
145
+ * keep only `--grep`-matching tests. Suites left with no tests are dropped.
146
+ * Returns the filtered suites, or a usage error string on a bad selector.
147
+ *
148
+ * @param registry {Suite[]} the full available registry
149
+ * @param config {RunConfig}
150
+ * @returns {Suite[] | {error: string}}
151
+ */
152
+ function selectSuites(registry, config) {
153
+ let selected = registry;
154
+ if (config.suiteIds.length > 0) {
155
+ const known = new Set(registry.map(s => s.id));
156
+ const unknown = config.suiteIds.filter(id => !known.has(id));
157
+ if (unknown.length > 0) {
158
+ return {
159
+ error: `unknown suite(s): ${unknown.join(', ')}. Known suites: ${registry
160
+ .map(s => s.id)
161
+ .join(', ')}`
162
+ };
163
+ }
164
+ const wanted = new Set(config.suiteIds);
165
+ selected = registry.filter(s => wanted.has(s.id));
166
+ }
167
+ const grep = config.grep !== null ? new RegExp(config.grep, 'i') : null;
168
+ const shaped = selected
169
+ .map(suite => {
170
+ const suiteOptional = suite.optional ?? false;
171
+ const tests = suite.tests.filter(test => {
172
+ if (config.skipOptional &&
173
+ (suiteOptional || (test.optional ?? false))) {
174
+ return false;
175
+ }
176
+ if (grep && !grep.test(test.name)) {
177
+ return false;
178
+ }
179
+ return true;
180
+ });
181
+ return { ...suite, tests };
182
+ })
183
+ .filter(suite => suite.tests.length > 0);
184
+ if (shaped.length === 0) {
185
+ return { error: 'no tests matched the given filters' };
186
+ }
187
+ return shaped;
188
+ }
189
+ /** Removes any userinfo/query/hash from a URL before it is reported/logged. */
190
+ function redactUrl(url) {
191
+ try {
192
+ const u = new URL(url);
193
+ u.username = '';
194
+ u.password = '';
195
+ u.search = '';
196
+ u.hash = '';
197
+ return u.toString();
198
+ }
199
+ catch {
200
+ return url;
201
+ }
202
+ }
203
+ /** Best-effort connectivity probe with a bounded timeout. */
204
+ async function isReachable(fetchImpl, serverUrl) {
205
+ try {
206
+ await fetchImpl(new URL(serverUrl), {
207
+ method: 'GET',
208
+ signal: AbortSignal.timeout(10_000)
209
+ });
210
+ // Any HTTP response -- even a 404 -- means the server is reachable.
211
+ return true;
212
+ }
213
+ catch {
214
+ return false;
215
+ }
216
+ }
217
+ /**
218
+ * The CLI entry point, minus process wiring. Returns the intended exit code:
219
+ * 0 = all required tests passed, 1 = conformance failures, 2 = usage error or
220
+ * unreachable server.
221
+ *
222
+ * @param argv {string[]} argument tokens (after `node script`)
223
+ * @param deps {CliDeps} injected I/O and collaborators
224
+ * @returns {Promise<number>} the exit code
225
+ */
226
+ export async function main(argv, deps) {
227
+ const parseResult = parseArgv(argv, deps.env);
228
+ if (parseResult.kind === 'help') {
229
+ deps.stdout(HELP + '\n');
230
+ return 0;
231
+ }
232
+ if (parseResult.kind === 'version') {
233
+ deps.stdout(deps.version + '\n');
234
+ return 0;
235
+ }
236
+ if (parseResult.kind === 'usage') {
237
+ deps.stderr(`error: ${parseResult.message}\n\n${HELP}\n`);
238
+ return 2;
239
+ }
240
+ const { config } = parseResult;
241
+ const selected = selectSuites(deps.suites, config);
242
+ if ('error' in selected) {
243
+ deps.stderr(`error: ${selected.error}\n`);
244
+ return 2;
245
+ }
246
+ const safeUrl = redactUrl(config.serverUrl);
247
+ // Preflight: fail cleanly if the server cannot be reached, rather than
248
+ // letting every suite cascade into connection failures.
249
+ if (!(await isReachable(deps.fetch, config.serverUrl))) {
250
+ deps.stderr(`error: cannot reach ${safeUrl}\n`);
251
+ return 2;
252
+ }
253
+ const reporter = config.reporter === 'json'
254
+ ? createJsonReporter({
255
+ write: deps.stdout,
256
+ writeErr: deps.stderr,
257
+ tool: {
258
+ name: 'was-conformance',
259
+ version: deps.version,
260
+ ...(deps.clientVersion !== undefined && {
261
+ clientVersion: deps.clientVersion
262
+ })
263
+ },
264
+ serverUrl: safeUrl
265
+ })
266
+ : createPrettyReporter({
267
+ write: deps.stdout,
268
+ color: deps.isTTY && !deps.env.NO_COLOR,
269
+ includeOptional: config.includeOptional,
270
+ serverUrl: safeUrl
271
+ });
272
+ let report;
273
+ try {
274
+ report = await deps.runConformance({
275
+ serverUrl: config.serverUrl,
276
+ onboardingToken: config.onboardingToken,
277
+ suites: selected,
278
+ onEvent: (event) => reporter(event),
279
+ ...(config.timeoutMs !== null && { testTimeoutMs: config.timeoutMs }),
280
+ failFast: config.failFast
281
+ });
282
+ }
283
+ catch (err) {
284
+ deps.stderr(`error: ${err instanceof Error ? err.message : String(err)}\n`);
285
+ return 2;
286
+ }
287
+ // Exit code: optional failures never fail the run unless --include-optional
288
+ // promotes them. `conformant` already means fail === optionalFail.
289
+ const passed = config.includeOptional
290
+ ? report.counts.fail === 0
291
+ : report.conformant;
292
+ return passed ? 0 : 1;
293
+ }
294
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,32 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The CI reporter: it buffers events and, at run-end, writes one structured
6
+ * JSON document to stdout (and nothing else -- any human chatter goes to
7
+ * stderr). The document wraps the full `RunReport` with tool metadata and the
8
+ * redacted server URL.
9
+ */
10
+ import type { RunEvent } from '../../harness/types.js';
11
+ /**
12
+ * Creates a JSON reporter. Only the final `run-end` event produces stdout
13
+ * output, keeping stdout a single valid JSON document for machine consumers.
14
+ *
15
+ * @param options {object}
16
+ * @param options.write {Function} stdout sink
17
+ * @param options.writeErr {Function} stderr sink (reserved for diagnostics)
18
+ * @param options.tool {object} tool name/version metadata
19
+ * @param options.serverUrl {string} redacted target URL
20
+ * @returns {(event: RunEvent) => void}
21
+ */
22
+ export declare function createJsonReporter({ write, writeErr: _writeErr, tool, serverUrl }: {
23
+ write: (text: string) => void;
24
+ writeErr: (text: string) => void;
25
+ tool: {
26
+ name: string;
27
+ version: string;
28
+ clientVersion?: string;
29
+ };
30
+ serverUrl: string;
31
+ }): (event: RunEvent) => void;
32
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../../src/cli/reporters/json.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;GAKG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AAEtD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,EAAE,SAAS,EACnB,IAAI,EACJ,SAAS,EACV,EAAE;IACD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,IAAI,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,aAAa,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/D,SAAS,EAAE,MAAM,CAAA;CAClB,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAiB5B"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Creates a JSON reporter. Only the final `run-end` event produces stdout
3
+ * output, keeping stdout a single valid JSON document for machine consumers.
4
+ *
5
+ * @param options {object}
6
+ * @param options.write {Function} stdout sink
7
+ * @param options.writeErr {Function} stderr sink (reserved for diagnostics)
8
+ * @param options.tool {object} tool name/version metadata
9
+ * @param options.serverUrl {string} redacted target URL
10
+ * @returns {(event: RunEvent) => void}
11
+ */
12
+ export function createJsonReporter({ write, writeErr: _writeErr, tool, serverUrl }) {
13
+ return (event) => {
14
+ if (event.type !== 'run-end') {
15
+ return;
16
+ }
17
+ const { report } = event;
18
+ const document = {
19
+ tool,
20
+ serverUrl,
21
+ startedAt: report.startedAt,
22
+ durationMs: report.durationMs,
23
+ conformant: report.conformant,
24
+ counts: report.counts,
25
+ suites: report.suites
26
+ };
27
+ write(JSON.stringify(document, null, 2) + '\n');
28
+ };
29
+ }
30
+ //# sourceMappingURL=json.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.js","sourceRoot":"","sources":["../../../src/cli/reporters/json.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;GAUG;AACH,MAAM,UAAU,kBAAkB,CAAC,EACjC,KAAK,EACL,QAAQ,EAAE,SAAS,EACnB,IAAI,EACJ,SAAS,EAMV;IACC,OAAO,CAAC,KAAe,EAAQ,EAAE;QAC/B,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC7B,OAAM;QACR,CAAC;QACD,MAAM,EAAE,MAAM,EAAE,GAAG,KAAK,CAAA;QACxB,MAAM,QAAQ,GAAG;YACf,IAAI;YACJ,SAAS;YACT,SAAS,EAAE,MAAM,CAAC,SAAS;YAC3B,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,UAAU,EAAE,MAAM,CAAC,UAAU;YAC7B,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,MAAM,EAAE,MAAM,CAAC,MAAM;SACtB,CAAA;QACD,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACjD,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { RunEvent } from '../../harness/types.js';
2
+ /**
3
+ * Creates a pretty reporter: an `onEvent` handler that writes progress and,
4
+ * at run-end, a summary. `includeOptional` promotes optional-test failures
5
+ * from warnings to hard failures in the rendering, matching the exit-code
6
+ * treatment.
7
+ *
8
+ * @param options {object}
9
+ * @param options.write {Function} sink for a chunk of output (no trailing \n)
10
+ * @param options.color {boolean} whether to emit ANSI color
11
+ * @param options.includeOptional {boolean} render optional failures as failures
12
+ * @param options.serverUrl {string} the (redacted) target URL, for the header
13
+ * @returns {(event: RunEvent) => void}
14
+ */
15
+ export declare function createPrettyReporter({ write, color, includeOptional, serverUrl }: {
16
+ write: (text: string) => void;
17
+ color: boolean;
18
+ includeOptional: boolean;
19
+ serverUrl: string;
20
+ }): (event: RunEvent) => void;
21
+ //# sourceMappingURL=pretty.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pretty.d.ts","sourceRoot":"","sources":["../../../src/cli/reporters/pretty.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,QAAQ,EAIT,MAAM,wBAAwB,CAAA;AAuB/B;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,KAAK,EACL,eAAe,EACf,SAAS,EACV,EAAE;IACD,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,KAAK,EAAE,OAAO,CAAA;IACd,eAAe,EAAE,OAAO,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;CAClB,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CA8H5B"}
@@ -0,0 +1,152 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The default human reporter: live per-test lines, failure detail with an
6
+ * expected/actual diff, and an end-of-run per-suite summary table. Colors are
7
+ * plain ANSI escapes, gated by the caller (TTY and NO_COLOR), so there is no
8
+ * color dependency.
9
+ */
10
+ import { inspect } from 'node:util';
11
+ /** Builds ANSI wrappers that pass text through untouched when disabled. */
12
+ function makeStyle(enabled) {
13
+ const wrap = (code) => (s) => enabled ? `\x1b[${code}m${s}\x1b[0m` : s;
14
+ return {
15
+ green: wrap('32'),
16
+ red: wrap('31'),
17
+ yellow: wrap('33'),
18
+ gray: wrap('90'),
19
+ bold: wrap('1'),
20
+ cyan: wrap('36')
21
+ };
22
+ }
23
+ /** Renders a value for the expected/actual diff, one line where it fits. */
24
+ function show(value) {
25
+ return inspect(value, { depth: 4, breakLength: 72, sorted: true });
26
+ }
27
+ /**
28
+ * Creates a pretty reporter: an `onEvent` handler that writes progress and,
29
+ * at run-end, a summary. `includeOptional` promotes optional-test failures
30
+ * from warnings to hard failures in the rendering, matching the exit-code
31
+ * treatment.
32
+ *
33
+ * @param options {object}
34
+ * @param options.write {Function} sink for a chunk of output (no trailing \n)
35
+ * @param options.color {boolean} whether to emit ANSI color
36
+ * @param options.includeOptional {boolean} render optional failures as failures
37
+ * @param options.serverUrl {string} the (redacted) target URL, for the header
38
+ * @returns {(event: RunEvent) => void}
39
+ */
40
+ export function createPrettyReporter({ write, color, includeOptional, serverUrl }) {
41
+ const s = makeStyle(color);
42
+ const line = (text = '') => write(text + '\n');
43
+ const glyphFor = (result) => {
44
+ if (result.status === 'pass') {
45
+ return s.green('✓');
46
+ }
47
+ if (result.status === 'skip') {
48
+ return s.gray('○');
49
+ }
50
+ // A failing optional test is a warning unless --include-optional.
51
+ if (result.optional && !includeOptional) {
52
+ return s.yellow('⚠');
53
+ }
54
+ return s.red('✗');
55
+ };
56
+ const printError = (error, warn) => {
57
+ const label = warn ? s.yellow : s.red;
58
+ line(` ${label(error.message)}`);
59
+ if (error.expected !== undefined || error.actual !== undefined) {
60
+ line(` ${s.gray('expected:')} ${s.green(show(error.expected))}`);
61
+ line(` ${s.gray('actual: ')} ${s.red(show(error.actual))}`);
62
+ }
63
+ };
64
+ return (event) => {
65
+ switch (event.type) {
66
+ case 'run-start': {
67
+ line();
68
+ line(s.bold('WAS Conformance Suite'));
69
+ line(`${s.gray('server:')} ${serverUrl}`);
70
+ line(`${s.gray('running')} ${event.testCount} ${s.gray(`test(s) across ${event.suiteCount} suite(s)`)}`);
71
+ break;
72
+ }
73
+ case 'suite-start': {
74
+ line();
75
+ const tag = event.optional ? s.gray(' (optional)') : '';
76
+ line(s.bold(event.name) + tag);
77
+ break;
78
+ }
79
+ case 'test-end': {
80
+ const r = event.result;
81
+ const label = r.status === 'skip' ? s.gray(r.name) : r.name;
82
+ let extra = '';
83
+ if (r.status === 'skip' && r.skipReason) {
84
+ extra = ` ${s.gray(`(${r.skipReason})`)}`;
85
+ }
86
+ line(` ${glyphFor(r)} ${label}${extra}`);
87
+ if (r.status === 'fail' && r.error) {
88
+ printError(r.error, r.optional && !includeOptional);
89
+ }
90
+ break;
91
+ }
92
+ case 'suite-end': {
93
+ if (event.result.teardownError) {
94
+ line(` ${s.yellow('⚠')} ${s.gray(`teardown: ${event.result.teardownError}`)}`);
95
+ }
96
+ break;
97
+ }
98
+ case 'run-end': {
99
+ printSummary(event.report.suites);
100
+ const c = event.report.counts;
101
+ const realFail = includeOptional ? c.fail : c.fail - c.optionalFail;
102
+ line();
103
+ const parts = [
104
+ s.green(`${c.pass} passed`),
105
+ realFail > 0 ? s.red(`${realFail} failed`) : `${realFail} failed`,
106
+ c.skip > 0 ? s.gray(`${c.skip} skipped`) : `${c.skip} skipped`
107
+ ];
108
+ if (!includeOptional && c.optionalFail > 0) {
109
+ parts.push(s.yellow(`${c.optionalFail} optional failing`));
110
+ }
111
+ line(parts.join(s.gray(' | ')));
112
+ line(`${s.gray('total')} ${c.total} ${s.gray('in')} ${formatMs(event.report.durationMs)}`);
113
+ const conformant = includeOptional
114
+ ? c.fail === 0
115
+ : event.report.conformant;
116
+ line();
117
+ line(conformant
118
+ ? s.green(s.bold('CONFORMANT'))
119
+ : s.red(s.bold('NON-CONFORMANT')));
120
+ break;
121
+ }
122
+ default:
123
+ break;
124
+ }
125
+ };
126
+ /** Prints the aligned per-suite counts table. */
127
+ function printSummary(suiteResults) {
128
+ line();
129
+ line(s.bold('Summary'));
130
+ const nameWidth = Math.max(5, ...suiteResults.map(r => r.name.length));
131
+ const pad = (text, width) => text + ' '.repeat(Math.max(0, width - text.length));
132
+ for (const r of suiteResults) {
133
+ const c = r.counts;
134
+ const optFail = c.optionalFail;
135
+ const hardFail = c.fail - optFail;
136
+ const cells = [
137
+ s.green(`${c.pass} pass`),
138
+ hardFail > 0 ? s.red(`${hardFail} fail`) : `${hardFail} fail`,
139
+ c.skip > 0 ? s.gray(`${c.skip} skip`) : `${c.skip} skip`
140
+ ];
141
+ if (optFail > 0) {
142
+ cells.push(s.yellow(`${optFail} opt-fail`));
143
+ }
144
+ line(` ${pad(r.name, nameWidth)} ${cells.join(' ')}`);
145
+ }
146
+ }
147
+ }
148
+ /** Formats a duration in ms as `1.2s` past a second, else `850ms`. */
149
+ function formatMs(ms) {
150
+ return ms >= 1000 ? `${(ms / 1000).toFixed(1)}s` : `${Math.round(ms)}ms`;
151
+ }
152
+ //# sourceMappingURL=pretty.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pretty.js","sourceRoot":"","sources":["../../../src/cli/reporters/pretty.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The suite's single assertion module: the npm `assert` package, an
6
+ * isomorphic implementation of `node:assert`, so Node (CLI) and browser (web
7
+ * app) runs share one assertion behavior. The deep import matters: a bare
8
+ * `import 'assert'` resolves to the Node builtin in Node but the npm package
9
+ * in bundlers, which would split behavior between the two frontends.
10
+ */
11
+ import assert from 'assert/build/assert.js';
12
+ export default assert;
13
+ //# sourceMappingURL=assert.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.d.ts","sourceRoot":"","sources":["../../src/harness/assert.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,MAAM,MAAM,wBAAwB,CAAA;AAE3C,eAAe,MAAM,CAAA"}
@@ -0,0 +1,13 @@
1
+ /*!
2
+ * Copyright (c) 2026 Interop Alliance. All rights reserved.
3
+ */
4
+ /**
5
+ * The suite's single assertion module: the npm `assert` package, an
6
+ * isomorphic implementation of `node:assert`, so Node (CLI) and browser (web
7
+ * app) runs share one assertion behavior. The deep import matters: a bare
8
+ * `import 'assert'` resolves to the Node builtin in Node but the npm package
9
+ * in bundlers, which would split behavior between the two frontends.
10
+ */
11
+ import assert from 'assert/build/assert.js';
12
+ export default assert;
13
+ //# sourceMappingURL=assert.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assert.js","sourceRoot":"","sources":["../../src/harness/assert.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH;;;;;;GAMG;AACH,OAAO,MAAM,MAAM,wBAAwB,CAAA;AAE3C,eAAe,MAAM,CAAA"}
@@ -0,0 +1,25 @@
1
+ import type { ConformanceContext, RunEvent, RunReport, Suite } from './types.js';
2
+ /** Generous default: remote servers over a WAN can be slow. */
3
+ export declare const DEFAULT_TEST_TIMEOUT_MS = 60000;
4
+ /**
5
+ * Runs the given suites sequentially against a prepared context, emitting
6
+ * progress events along the way. Never throws for test failures -- those are
7
+ * captured in the returned report.
8
+ *
9
+ * @param options {object}
10
+ * @param options.suites {Suite[]} suites to run, in order
11
+ * @param options.ctx {ConformanceContext} context from `createContext()`
12
+ * @param [options.onEvent] {Function} progress-event callback
13
+ * @param [options.testTimeoutMs] {number} per-test (and per-hook) timeout
14
+ * @param [options.failFast] {boolean} stop after the first non-optional
15
+ * failure; remaining tests are reported as skipped
16
+ * @returns {Promise<RunReport>}
17
+ */
18
+ export declare function runSuites({ suites, ctx, onEvent, testTimeoutMs, failFast }: {
19
+ suites: Array<Suite<any>>;
20
+ ctx: ConformanceContext;
21
+ onEvent?: (event: RunEvent) => void;
22
+ testTimeoutMs?: number;
23
+ failFast?: boolean;
24
+ }): Promise<RunReport>;
25
+ //# sourceMappingURL=runner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/harness/runner.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,kBAAkB,EAElB,QAAQ,EACR,SAAS,EACT,KAAK,EAKN,MAAM,YAAY,CAAA;AAEnB,+DAA+D;AAC/D,eAAO,MAAM,uBAAuB,QAAS,CAAA;AA+D7C;;;;;;;;;;;;;GAaG;AACH,wBAAsB,SAAS,CAAC,EAC9B,MAAM,EACN,GAAG,EACH,OAAO,EACP,aAAuC,EACvC,QAAgB,EACjB,EAAE;IACD,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAA;IACzB,GAAG,EAAE,kBAAkB,CAAA;IACvB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAA;IACnC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,GAAG,OAAO,CAAC,SAAS,CAAC,CAwKrB"}