as-test 0.4.4 → 0.5.1

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 (67) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +196 -82
  3. package/as-test.config.schema.json +137 -0
  4. package/assembly/coverage.ts +19 -0
  5. package/assembly/index.ts +172 -85
  6. package/assembly/src/expectation.ts +263 -199
  7. package/assembly/src/log.ts +1 -9
  8. package/assembly/src/suite.ts +61 -25
  9. package/assembly/src/tests.ts +2 -0
  10. package/assembly/util/wipc.ts +286 -0
  11. package/bin/build.js +86 -41
  12. package/bin/index.js +337 -68
  13. package/bin/init.js +441 -183
  14. package/bin/reporter.js +1 -1
  15. package/bin/reporters/default.js +379 -0
  16. package/bin/reporters/types.js +1 -0
  17. package/bin/run.js +882 -194
  18. package/bin/types.js +14 -7
  19. package/bin/util.js +54 -3
  20. package/package.json +34 -16
  21. package/transform/lib/builder.js +169 -169
  22. package/transform/lib/builder.js.map +1 -1
  23. package/transform/lib/coverage.js +47 -1
  24. package/transform/lib/coverage.js.map +1 -1
  25. package/transform/lib/index.js +70 -0
  26. package/transform/lib/index.js.map +1 -1
  27. package/transform/lib/location.js +20 -0
  28. package/transform/lib/location.js.map +1 -0
  29. package/transform/lib/log.js +118 -0
  30. package/transform/lib/log.js.map +1 -0
  31. package/transform/lib/mock.js +2 -2
  32. package/transform/lib/mock.js.map +1 -1
  33. package/transform/lib/util.js +3 -3
  34. package/transform/lib/util.js.map +1 -1
  35. package/.github/workflows/as-test.yml +0 -26
  36. package/.prettierrc +0 -3
  37. package/as-test.config.json +0 -19
  38. package/assembly/__tests__/array.spec.ts +0 -25
  39. package/assembly/__tests__/math.spec.ts +0 -16
  40. package/assembly/__tests__/mock.spec.ts +0 -22
  41. package/assembly/__tests__/mock.ts +0 -7
  42. package/assembly/__tests__/sleep.spec.ts +0 -28
  43. package/assembly/tsconfig.json +0 -97
  44. package/assets/img/screenshot.png +0 -0
  45. package/cli/build.ts +0 -117
  46. package/cli/index.ts +0 -190
  47. package/cli/init.ts +0 -247
  48. package/cli/reporter.ts +0 -1
  49. package/cli/run.ts +0 -286
  50. package/cli/tsconfig.json +0 -9
  51. package/cli/types.ts +0 -29
  52. package/cli/util.ts +0 -65
  53. package/run/package.json +0 -27
  54. package/tests/array.run.js +0 -7
  55. package/tests/math.run.js +0 -7
  56. package/tests/mock.run.js +0 -14
  57. package/tests/sleep.run.js +0 -7
  58. package/transform/src/builder.ts +0 -1474
  59. package/transform/src/coverage.ts +0 -580
  60. package/transform/src/index.ts +0 -73
  61. package/transform/src/linker.ts +0 -41
  62. package/transform/src/mock.ts +0 -163
  63. package/transform/src/range.ts +0 -12
  64. package/transform/src/types.ts +0 -35
  65. package/transform/src/util.ts +0 -81
  66. package/transform/src/visitor.ts +0 -744
  67. package/transform/tsconfig.json +0 -10
package/bin/index.js CHANGED
@@ -1,13 +1,17 @@
1
1
  #!/usr/bin/env node
2
2
  import chalk from "chalk";
3
3
  import { build } from "./build.js";
4
- import { run } from "./run.js";
4
+ import { createRunReporter, run } from "./run.js";
5
5
  import { init } from "./init.js";
6
+ import { getCliVersion, loadConfig } from "./util.js";
7
+ import * as path from "path";
8
+ import { glob } from "glob";
6
9
  const _args = process.argv.slice(2);
7
10
  const flags = [];
8
11
  const args = [];
9
12
  const COMMANDS = ["run", "build", "test", "init"];
10
- const version = "0.3.5";
13
+ const version = getCliVersion();
14
+ const configPath = resolveConfigPath(_args);
11
15
  for (const arg of _args) {
12
16
  if (arg.startsWith("-"))
13
17
  flags.push(arg);
@@ -24,19 +28,35 @@ if (!args.length) {
24
28
  }
25
29
  else if (COMMANDS.includes(args[0])) {
26
30
  const command = args.shift();
31
+ const commandArgs = resolveCommandArgs(_args, command ?? "");
32
+ const runFlags = {
33
+ snapshot: !flags.includes("--no-snapshot"),
34
+ updateSnapshots: flags.includes("--update-snapshots"),
35
+ clean: flags.includes("--clean"),
36
+ showCoverage: flags.includes("--show-coverage"),
37
+ verbose: flags.includes("--verbose"),
38
+ };
27
39
  if (command === "build") {
28
- build();
40
+ build(configPath).catch((error) => {
41
+ printCliError(error);
42
+ process.exit(1);
43
+ });
29
44
  }
30
45
  else if (command === "run") {
31
- run();
46
+ run(runFlags, configPath);
32
47
  }
33
48
  else if (command === "test") {
34
- build().then(() => {
35
- run();
49
+ runTestSequential(runFlags, configPath, commandArgs).catch((error) => {
50
+ printCliError(error);
51
+ process.exit(1);
36
52
  });
37
53
  }
38
54
  else if (command === "init") {
39
- init(args);
55
+ const commandTokens = resolveCommandTokens(_args, command ?? "");
56
+ init(commandTokens).catch((error) => {
57
+ printCliError(error);
58
+ process.exit(1);
59
+ });
40
60
  }
41
61
  }
42
62
  else {
@@ -64,83 +84,332 @@ function info() {
64
84
  console.log(chalk.bold("Commands:"));
65
85
  console.log(" " +
66
86
  chalk.bold.blueBright("run") +
67
- " " +
68
- chalk.dim("<my-test.spec.ts>") +
87
+ " " +
88
+ chalk.dim("<./**/*.spec.ts>") +
69
89
  " " +
70
90
  "Run unit tests with selected runtime");
71
91
  console.log(" " +
72
92
  chalk.bold.blueBright("build") +
73
- " " +
74
- chalk.dim("<my-test.spec.ts>") +
93
+ " " +
94
+ chalk.dim("<./**/*.spec.ts>") +
75
95
  " " +
76
96
  "Build unit tests and compile");
77
97
  console.log(" " +
78
98
  chalk.bold.blueBright("test") +
79
- " " +
80
- chalk.dim("<my-test.spec.ts>") +
81
- " " +
99
+ " " +
100
+ chalk.dim("<name>|<path-or-glob>") +
101
+ " " +
82
102
  "Build and run unit tests with selected runtime" +
83
103
  "\n");
84
104
  console.log(" " +
85
105
  chalk.bold.magentaBright("init") +
86
- " " +
87
- chalk.strikethrough.dim("") +
88
- " " +
89
- "Initialize an empty testing template");
90
- console.log(" " +
91
- chalk.strikethrough.bold.magentaBright("config") +
92
- " " +
93
- chalk.strikethrough.dim("as-test.config.json") +
94
106
  " " +
95
- "Specify the configuration file");
96
- console.log(" " +
97
- chalk.strikethrough.bold.magentaBright("reporter") +
98
- " " +
99
- chalk.strikethrough.dim("<tap>") +
100
- " " +
101
- "Specify the test reporter to use");
102
- console.log(" " +
103
- chalk.strikethrough.bold.magentaBright("use") +
104
- " " +
105
- chalk.strikethrough.dim("wasmtime") +
106
- " " +
107
- "Specify the runtime to use" +
108
- "\n");
107
+ chalk.dim("<./dir>") +
108
+ " " +
109
+ "Initialize an empty testing template");
110
+ console.log("");
109
111
  console.log(chalk.bold("Flags:"));
110
- console.log(" " +
111
- chalk.strikethrough.dim("run") +
112
- " " +
113
- chalk.strikethrough.bold.blue("--coverage") +
114
- " " +
115
- "Use code coverage");
116
- console.log(" " +
117
- chalk.strikethrough.dim("run") +
118
- " " +
119
- chalk.strikethrough.bold.blue("--snapshot") +
120
- " " +
121
- "Take a snapshot of the tests");
122
- console.log(" " +
123
- chalk.strikethrough.dim("use") +
124
- " " +
125
- chalk.strikethrough.bold.blue("--list") +
126
- " " +
127
- "List supported runtimes");
128
- console.log(" " +
129
- chalk.strikethrough.dim("reporter") +
130
- " " +
131
- chalk.strikethrough.bold.blue("--list") +
132
- " " +
133
- "List supported reporters");
134
- console.log(" " +
135
- chalk.strikethrough.dim("<command>") +
136
- " " +
137
- chalk.strikethrough.bold.blue("--help") +
112
+ console.log(" " +
113
+ chalk.bold.blue("--config <path>") +
114
+ " " +
115
+ "Use a specific config file");
116
+ console.log(" " +
117
+ chalk.bold.blue("--snapshot") +
118
+ " " +
119
+ "Snapshot assertions (enabled by default)");
120
+ console.log(" " +
121
+ chalk.bold.blue("--update-snapshots") +
122
+ " " +
123
+ "Create/update snapshot files on mismatch");
124
+ console.log(" " +
125
+ chalk.bold.blue("--no-snapshot") +
138
126
  " " +
139
- "Print info about command" +
140
- "\n");
127
+ "Disable snapshot assertions for this run");
128
+ console.log(" " +
129
+ chalk.bold.blue("--show-coverage") +
130
+ " " +
131
+ "Print all coverage points with line:column refs");
132
+ console.log(" " +
133
+ chalk.bold.blue("--verbose") +
134
+ " " +
135
+ "Print each suite start/end line");
136
+ console.log("");
141
137
  console.log(chalk.dim("If your using this, consider dropping a star, it would help a lot!") + "\n");
142
138
  console.log("View the repo: " +
143
139
  chalk.magenta("https://github.com/JairusSW/as-test"));
144
- console.log("View the docs: " +
145
- chalk.strikethrough.blue("https://docs.jairus.dev/as-test"));
140
+ // console.log(
141
+ // "View the docs: " +
142
+ // chalk.blue("https://docs.jairus.dev/as-test"),
143
+ // );
144
+ }
145
+ function resolveConfigPath(rawArgs) {
146
+ for (let i = 0; i < rawArgs.length; i++) {
147
+ const arg = rawArgs[i];
148
+ if (arg == "--config") {
149
+ const next = rawArgs[i + 1];
150
+ if (next && !next.startsWith("-")) {
151
+ return path.resolve(process.cwd(), next);
152
+ }
153
+ return undefined;
154
+ }
155
+ if (arg.startsWith("--config=")) {
156
+ const value = arg.slice("--config=".length);
157
+ if (value.length) {
158
+ return path.resolve(process.cwd(), value);
159
+ }
160
+ return undefined;
161
+ }
162
+ }
163
+ return undefined;
164
+ }
165
+ function resolveCommandArgs(rawArgs, command) {
166
+ const values = [];
167
+ let seenCommand = false;
168
+ for (let i = 0; i < rawArgs.length; i++) {
169
+ const arg = rawArgs[i];
170
+ if (!seenCommand) {
171
+ if (arg == command)
172
+ seenCommand = true;
173
+ continue;
174
+ }
175
+ if (arg == "--config") {
176
+ i++;
177
+ continue;
178
+ }
179
+ if (arg.startsWith("--config=")) {
180
+ continue;
181
+ }
182
+ if (arg.startsWith("-")) {
183
+ continue;
184
+ }
185
+ values.push(arg);
186
+ }
187
+ return values;
188
+ }
189
+ function resolveCommandTokens(rawArgs, command) {
190
+ const values = [];
191
+ let seenCommand = false;
192
+ for (let i = 0; i < rawArgs.length; i++) {
193
+ const arg = rawArgs[i];
194
+ if (!seenCommand) {
195
+ if (arg == command)
196
+ seenCommand = true;
197
+ continue;
198
+ }
199
+ values.push(arg);
200
+ }
201
+ return values;
202
+ }
203
+ async function runTestSequential(runFlags, configPath, selectors) {
204
+ const files = await resolveSelectedFiles(configPath, selectors);
205
+ if (!files.length) {
206
+ const scope = selectors.length > 0
207
+ ? selectors.join(", ")
208
+ : "configured input patterns";
209
+ throw new Error(`No test files matched: ${scope}`);
210
+ }
211
+ const reporterSession = await createRunReporter(configPath);
212
+ const reporter = reporterSession.reporter;
213
+ const snapshotEnabled = runFlags.snapshot !== false;
214
+ reporter.onRunStart?.({
215
+ runtimeName: reporterSession.runtimeName,
216
+ clean: runFlags.clean,
217
+ verbose: runFlags.verbose,
218
+ snapshotEnabled,
219
+ updateSnapshots: runFlags.updateSnapshots,
220
+ });
221
+ const results = [];
222
+ let failed = false;
223
+ for (const file of files) {
224
+ await build(configPath, [file]);
225
+ const artifactKey = path.basename(file).replace(/[^a-zA-Z0-9._-]/g, "_");
226
+ const result = await run(runFlags, configPath, [file], false, {
227
+ reporter,
228
+ emitRunStart: false,
229
+ emitRunComplete: false,
230
+ logFileName: `test.${artifactKey}.log.json`,
231
+ coverageFileName: `coverage.${artifactKey}.log.json`,
232
+ });
233
+ results.push(result);
234
+ if (result?.failed)
235
+ failed = true;
236
+ }
237
+ const summary = aggregateRunResults(results);
238
+ reporter.onRunComplete?.({
239
+ clean: runFlags.clean,
240
+ snapshotEnabled,
241
+ showCoverage: runFlags.showCoverage,
242
+ snapshotSummary: summary.snapshotSummary,
243
+ coverageSummary: summary.coverageSummary,
244
+ stats: summary.stats,
245
+ reports: summary.reports,
246
+ });
247
+ process.exit(failed ? 1 : 0);
248
+ }
249
+ async function resolveSelectedFiles(configPath, selectors) {
250
+ const resolvedConfigPath = configPath ?? path.join(process.cwd(), "./as-test.config.json");
251
+ const config = loadConfig(resolvedConfigPath, true);
252
+ const patterns = resolveInputPatterns(config.input, selectors);
253
+ const matches = await glob(patterns);
254
+ const specs = matches.filter((file) => file.endsWith(".spec.ts"));
255
+ return [...new Set(specs)];
256
+ }
257
+ function resolveInputPatterns(configured, selectors) {
258
+ const configuredInputs = Array.isArray(configured) ? configured : [configured];
259
+ if (!selectors.length)
260
+ return configuredInputs;
261
+ const patterns = new Set();
262
+ for (const selector of selectors) {
263
+ if (!selector)
264
+ continue;
265
+ if (isBareSuiteSelector(selector)) {
266
+ const base = stripSuiteSuffix(selector);
267
+ for (const configuredInput of configuredInputs) {
268
+ patterns.add(path.join(path.dirname(configuredInput), `${base}.spec.ts`));
269
+ }
270
+ continue;
271
+ }
272
+ patterns.add(selector);
273
+ }
274
+ return [...patterns];
275
+ }
276
+ function isBareSuiteSelector(selector) {
277
+ return (!selector.includes("/") &&
278
+ !selector.includes("\\") &&
279
+ !/[*?[\]{}]/.test(selector));
280
+ }
281
+ function stripSuiteSuffix(selector) {
282
+ return selector.replace(/\.spec\.ts$/, "").replace(/\.ts$/, "");
283
+ }
284
+ function aggregateRunResults(results) {
285
+ const stats = {
286
+ passedFiles: 0,
287
+ failedFiles: 0,
288
+ skippedFiles: 0,
289
+ passedSuites: 0,
290
+ failedSuites: 0,
291
+ skippedSuites: 0,
292
+ passedTests: 0,
293
+ failedTests: 0,
294
+ skippedTests: 0,
295
+ time: 0,
296
+ failedEntries: [],
297
+ };
298
+ const snapshotSummary = {
299
+ matched: 0,
300
+ created: 0,
301
+ updated: 0,
302
+ failed: 0,
303
+ };
304
+ const coverageSummary = {
305
+ enabled: false,
306
+ showPoints: false,
307
+ total: 0,
308
+ covered: 0,
309
+ uncovered: 0,
310
+ percent: 100,
311
+ files: [],
312
+ };
313
+ const uniqueCoveragePoints = new Map();
314
+ let fallbackCoverageTotal = 0;
315
+ let fallbackCoverageCovered = 0;
316
+ let fallbackCoverageUncovered = 0;
317
+ const fallbackCoverageFiles = [];
318
+ const reports = [];
319
+ for (const result of results) {
320
+ stats.passedFiles += result.stats.passedFiles;
321
+ stats.failedFiles += result.stats.failedFiles;
322
+ stats.skippedFiles += result.stats.skippedFiles;
323
+ stats.passedSuites += result.stats.passedSuites;
324
+ stats.failedSuites += result.stats.failedSuites;
325
+ stats.skippedSuites += result.stats.skippedSuites;
326
+ stats.passedTests += result.stats.passedTests;
327
+ stats.failedTests += result.stats.failedTests;
328
+ stats.skippedTests += result.stats.skippedTests;
329
+ stats.time += result.stats.time;
330
+ stats.failedEntries.push(...result.stats.failedEntries);
331
+ snapshotSummary.matched += result.snapshotSummary.matched;
332
+ snapshotSummary.created += result.snapshotSummary.created;
333
+ snapshotSummary.updated += result.snapshotSummary.updated;
334
+ snapshotSummary.failed += result.snapshotSummary.failed;
335
+ coverageSummary.enabled = coverageSummary.enabled || result.coverageSummary.enabled;
336
+ coverageSummary.showPoints =
337
+ coverageSummary.showPoints || result.coverageSummary.showPoints;
338
+ for (const fileCoverage of result.coverageSummary.files) {
339
+ if (fileCoverage.points.length > 0) {
340
+ for (const point of fileCoverage.points) {
341
+ const key = `${point.file}::${point.hash}`;
342
+ const existing = uniqueCoveragePoints.get(key);
343
+ if (!existing) {
344
+ uniqueCoveragePoints.set(key, { ...point });
345
+ }
346
+ else if (point.executed) {
347
+ existing.executed = true;
348
+ }
349
+ }
350
+ }
351
+ else {
352
+ fallbackCoverageTotal += fileCoverage.total;
353
+ fallbackCoverageCovered += fileCoverage.covered;
354
+ fallbackCoverageUncovered += fileCoverage.uncovered;
355
+ fallbackCoverageFiles.push(fileCoverage);
356
+ }
357
+ }
358
+ reports.push(...result.reports);
359
+ }
360
+ if (uniqueCoveragePoints.size > 0) {
361
+ const byFile = new Map();
362
+ for (const point of uniqueCoveragePoints.values()) {
363
+ if (!byFile.has(point.file))
364
+ byFile.set(point.file, []);
365
+ byFile.get(point.file).push(point);
366
+ }
367
+ const sortedFiles = [...byFile.keys()].sort((a, b) => a.localeCompare(b));
368
+ for (const file of sortedFiles) {
369
+ const points = byFile.get(file);
370
+ points.sort((a, b) => {
371
+ if (a.line !== b.line)
372
+ return a.line - b.line;
373
+ if (a.column !== b.column)
374
+ return a.column - b.column;
375
+ if (a.type !== b.type)
376
+ return a.type.localeCompare(b.type);
377
+ return a.hash.localeCompare(b.hash);
378
+ });
379
+ let covered = 0;
380
+ for (const point of points) {
381
+ coverageSummary.total++;
382
+ if (point.executed) {
383
+ coverageSummary.covered++;
384
+ covered++;
385
+ }
386
+ else {
387
+ coverageSummary.uncovered++;
388
+ }
389
+ }
390
+ const total = points.length;
391
+ coverageSummary.files.push({
392
+ file,
393
+ total,
394
+ covered,
395
+ uncovered: total - covered,
396
+ percent: total ? (covered * 100) / total : 100,
397
+ points,
398
+ });
399
+ }
400
+ }
401
+ else {
402
+ coverageSummary.total = fallbackCoverageTotal;
403
+ coverageSummary.covered = fallbackCoverageCovered;
404
+ coverageSummary.uncovered = fallbackCoverageUncovered;
405
+ coverageSummary.files = fallbackCoverageFiles;
406
+ }
407
+ coverageSummary.percent = coverageSummary.total
408
+ ? (coverageSummary.covered * 100) / coverageSummary.total
409
+ : 100;
410
+ return { stats, snapshotSummary, coverageSummary, reports };
411
+ }
412
+ function printCliError(error) {
413
+ const message = error instanceof Error ? error.message : String(error);
414
+ process.stderr.write(message + "\n");
146
415
  }