as-test 1.0.11 → 1.0.13

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.
@@ -327,6 +327,9 @@ function formatReadableLog(file, suites, modeName, buildCommand, runCommand, sna
327
327
  lines.push("", "Failures:");
328
328
  for (const failure of failures) {
329
329
  lines.push(`FAIL ${failure.title}${failure.where.length ? ` (${failure.where})` : ""}`);
330
+ if (failure.suitePath.length) {
331
+ lines.push(`Repro: ${buildSuiteReproCommand(file, failure.suitePath, modeName)}`);
332
+ }
330
333
  if (failure.message.length)
331
334
  lines.push(`Message: ${failure.message}`);
332
335
  if (failure.left.length)
@@ -352,6 +355,139 @@ function formatInvocation(invocation) {
352
355
  .map((part) => (/[\s"'\\]/.test(part) ? JSON.stringify(part) : part))
353
356
  .join(" ");
354
357
  }
358
+ function filterSelectedSuites(suites, selectors, file, modeName) {
359
+ const annotated = annotateSuitePaths(suites, []);
360
+ const matches = resolveSuiteSelectionMatches(annotated, selectors, file);
361
+ const selected = new Set(matches.map((match) => match.resolvedPath));
362
+ return cloneSelectedSuites(annotated, selected, file, modeName);
363
+ }
364
+ function annotateSuitePaths(suites, pathParts) {
365
+ return suites.map((suite) => annotateSuiteNode(suite, pathParts));
366
+ }
367
+ function annotateSuiteNode(suite, pathParts) {
368
+ const description = String(suite?.description ?? "unknown");
369
+ const slug = slugifySelectorSegment(description);
370
+ const nextParts = [...pathParts, slug];
371
+ const nextSuites = Array.isArray(suite?.suites)
372
+ ? suite.suites
373
+ : [];
374
+ const annotatedSuites = annotateSuitePaths(nextSuites, nextParts);
375
+ return {
376
+ ...suite,
377
+ path: nextParts.join("/"),
378
+ suites: annotatedSuites,
379
+ };
380
+ }
381
+ function resolveSuiteSelectionMatches(suites, selectors, file) {
382
+ const matches = [];
383
+ for (const selector of selectors) {
384
+ const normalized = selector.trim();
385
+ if (!normalized.length)
386
+ continue;
387
+ if (normalized.includes("/")) {
388
+ const resolved = resolveExplicitSuitePath(suites, normalized);
389
+ if (!resolved) {
390
+ throw new Error(`No suites matched "${selector}" in ${path.basename(file)}.`);
391
+ }
392
+ matches.push({
393
+ kind: "path",
394
+ raw: selector,
395
+ resolvedPath: resolved.path,
396
+ depth: resolved.depth,
397
+ });
398
+ continue;
399
+ }
400
+ const resolved = resolveBareSuiteSelector(suites, normalized);
401
+ if (!resolved) {
402
+ throw new Error(`No suites matched "${selector}" in ${path.basename(file)}.`);
403
+ }
404
+ matches.push({
405
+ kind: "bare",
406
+ raw: selector,
407
+ resolvedPath: resolved.path,
408
+ depth: resolved.depth,
409
+ });
410
+ }
411
+ return matches;
412
+ }
413
+ function resolveExplicitSuitePath(suites, selector) {
414
+ const normalized = selector
415
+ .split("/")
416
+ .map((part) => slugifySelectorSegment(part))
417
+ .filter((part) => part.length)
418
+ .join("/");
419
+ if (!normalized.length)
420
+ return null;
421
+ let match = null;
422
+ walkSuites(suites, (suite, depth) => {
423
+ if (suite.path == normalized) {
424
+ match = { path: suite.path, depth };
425
+ return true;
426
+ }
427
+ return false;
428
+ });
429
+ return match;
430
+ }
431
+ function resolveBareSuiteSelector(suites, selector) {
432
+ const slug = slugifySelectorSegment(selector);
433
+ if (!slug.length)
434
+ return null;
435
+ const matches = [];
436
+ walkSuites(suites, (suite, depth) => {
437
+ const leaf = String(suite.path ?? "").split("/").pop() ?? "";
438
+ if (leaf == slug) {
439
+ matches.push({ path: String(suite.path), depth });
440
+ }
441
+ return false;
442
+ });
443
+ if (!matches.length)
444
+ return null;
445
+ matches.sort((a, b) => a.depth - b.depth || a.path.localeCompare(b.path));
446
+ const shallowest = matches[0];
447
+ const ambiguous = matches.filter((match) => match.depth == shallowest.depth);
448
+ if (ambiguous.length > 1) {
449
+ throw new Error(`Suite selector "${selector}" is ambiguous. Matches: ${ambiguous.map((match) => match.path).join(", ")}`);
450
+ }
451
+ return shallowest;
452
+ }
453
+ function walkSuites(suites, visitor, depth = 0) {
454
+ for (const suite of suites) {
455
+ if (visitor(suite, depth))
456
+ return true;
457
+ const childSuites = Array.isArray(suite?.suites) ? suite.suites : [];
458
+ if (walkSuites(childSuites, visitor, depth + 1))
459
+ return true;
460
+ }
461
+ return false;
462
+ }
463
+ function cloneSelectedSuites(suites, selected, file, modeName) {
464
+ const out = [];
465
+ for (const suite of suites) {
466
+ const childSuites = Array.isArray(suite.suites) ? suite.suites : [];
467
+ const selectedChildren = cloneSelectedSuites(childSuites, selected, file, modeName);
468
+ const keep = selected.has(String(suite.path ?? "")) || selectedChildren.length > 0;
469
+ if (!keep)
470
+ continue;
471
+ out.push({
472
+ ...suite,
473
+ file,
474
+ modeName,
475
+ suites: selectedChildren,
476
+ });
477
+ }
478
+ return out;
479
+ }
480
+ function slugifySelectorSegment(value) {
481
+ return value
482
+ .trim()
483
+ .toLowerCase()
484
+ .replace(/[^a-z0-9]+/g, "-")
485
+ .replace(/^-+|-+$/g, "");
486
+ }
487
+ function buildSuiteReproCommand(file, suitePath, modeName) {
488
+ const modeArg = modeName && modeName != "default" ? ` --mode ${modeName}` : "";
489
+ return `ast run ${file}${modeArg} --suite ${suitePath}`;
490
+ }
355
491
  function collectReadableFailures(suites, file, pathParts) {
356
492
  const out = [];
357
493
  for (const suite of suites) {
@@ -372,6 +508,7 @@ function collectReadableFailures(suites, file, pathParts) {
372
508
  message: String(test.message ?? ""),
373
509
  left: JSON.stringify(test.left ?? ""),
374
510
  right: JSON.stringify(test.right ?? ""),
511
+ suitePath: String(suiteAny.path ?? ""),
375
512
  });
376
513
  }
377
514
  const childSuites = Array.isArray(suiteAny.suites)
@@ -492,6 +629,9 @@ export async function run(flags = {}, configPath = DEFAULT_CONFIG_PATH, selector
492
629
  throw new Error(`Failed to run ${path.basename(file)} in mode ${modeLabel} with ${details}`);
493
630
  }
494
631
  const normalized = normalizeReport(report);
632
+ const selectedSuites = options.suiteSelectors?.length
633
+ ? filterSelectedSuites(normalized.suites, options.suiteSelectors, file, options.modeName ?? "default")
634
+ : normalized.suites;
495
635
  snapshotStore.flush();
496
636
  snapshotSummary.matched += snapshotStore.matched;
497
637
  snapshotSummary.created += snapshotStore.created;
@@ -500,7 +640,7 @@ export async function run(flags = {}, configPath = DEFAULT_CONFIG_PATH, selector
500
640
  reports.push({
501
641
  file,
502
642
  modeName: options.modeName ?? "default",
503
- suites: normalized.suites,
643
+ suites: selectedSuites,
504
644
  coverage: normalized.coverage,
505
645
  runCommand: runCommandForLog,
506
646
  snapshotSummary: {
@@ -1483,6 +1623,31 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1483
1623
  let stderrPendingLine = "";
1484
1624
  let stdoutBuffer = "";
1485
1625
  let spawnError = null;
1626
+ let sawChannelClose = false;
1627
+ const runtimeEvents = {
1628
+ sawFileStart: false,
1629
+ sawFileEnd: false,
1630
+ fileName: path.basename(specFile),
1631
+ fileVerdict: "none",
1632
+ fileTime: "",
1633
+ suiteStarts: 0,
1634
+ suiteEnds: 0,
1635
+ assertionFails: 0,
1636
+ warnings: 0,
1637
+ logs: 0,
1638
+ };
1639
+ const reportStream = {
1640
+ dataFrames: 0,
1641
+ dataBytes: 0,
1642
+ sawChunkStart: false,
1643
+ sawChunkEnd: false,
1644
+ chunkCountExpected: 0,
1645
+ chunkBytesExpected: 0,
1646
+ chunkTotalBytesExpected: 0,
1647
+ chunkFramesReceived: 0,
1648
+ chunkBytesReceived: 0,
1649
+ chunks: [],
1650
+ };
1486
1651
  child.on("error", (error) => {
1487
1652
  spawnError = error;
1488
1653
  });
@@ -1512,6 +1677,7 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1512
1677
  const event = msg;
1513
1678
  const kind = String(event.kind ?? "");
1514
1679
  if (kind === "event:assert-fail") {
1680
+ runtimeEvents.assertionFails++;
1515
1681
  reporter.onAssertionFail?.({
1516
1682
  key: String(event.key ?? ""),
1517
1683
  instr: String(event.instr ?? ""),
@@ -1522,6 +1688,8 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1522
1688
  return;
1523
1689
  }
1524
1690
  if (kind === "event:file-start") {
1691
+ runtimeEvents.sawFileStart = true;
1692
+ runtimeEvents.fileName = String(event.file ?? runtimeEvents.fileName);
1525
1693
  reporter.onFileStart?.({
1526
1694
  file: String(event.file ?? "unknown"),
1527
1695
  depth: 0,
@@ -1531,6 +1699,10 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1531
1699
  return;
1532
1700
  }
1533
1701
  if (kind === "event:file-end") {
1702
+ runtimeEvents.sawFileEnd = true;
1703
+ runtimeEvents.fileName = String(event.file ?? runtimeEvents.fileName);
1704
+ runtimeEvents.fileVerdict = String(event.verdict ?? "none");
1705
+ runtimeEvents.fileTime = String(event.time ?? "");
1534
1706
  reporter.onFileEnd?.({
1535
1707
  file: String(event.file ?? "unknown"),
1536
1708
  depth: 0,
@@ -1542,6 +1714,7 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1542
1714
  return;
1543
1715
  }
1544
1716
  if (kind === "event:suite-start") {
1717
+ runtimeEvents.suiteStarts++;
1545
1718
  reporter.onSuiteStart?.({
1546
1719
  file: String(event.file ?? "unknown"),
1547
1720
  depth: Number(event.depth ?? 0),
@@ -1551,6 +1724,7 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1551
1724
  return;
1552
1725
  }
1553
1726
  if (kind === "event:suite-end") {
1727
+ runtimeEvents.suiteEnds++;
1554
1728
  reporter.onSuiteEnd?.({
1555
1729
  file: String(event.file ?? "unknown"),
1556
1730
  depth: Number(event.depth ?? 0),
@@ -1561,12 +1735,14 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1561
1735
  return;
1562
1736
  }
1563
1737
  if (kind === "event:warn") {
1738
+ runtimeEvents.warnings++;
1564
1739
  reporter.onWarning?.({
1565
1740
  message: String(event.message ?? ""),
1566
1741
  });
1567
1742
  return;
1568
1743
  }
1569
1744
  if (kind === "event:log") {
1745
+ runtimeEvents.logs++;
1570
1746
  reporter.onLog?.({
1571
1747
  file: String(event.file ?? "unknown"),
1572
1748
  depth: Number(event.depth ?? 0),
@@ -1584,16 +1760,43 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1584
1760
  this.send(MessageType.CALL, Buffer.from(`${result.ok ? "1" : "0"}\n${result.expected}`, "utf8"));
1585
1761
  return;
1586
1762
  }
1763
+ if (kind === "report:start") {
1764
+ reportStream.sawChunkStart = true;
1765
+ reportStream.sawChunkEnd = false;
1766
+ reportStream.chunkCountExpected = Number(event.chunkCount ?? 0);
1767
+ reportStream.chunkBytesExpected = Number(event.chunkBytes ?? 0);
1768
+ reportStream.chunkTotalBytesExpected = Number(event.totalBytes ?? 0);
1769
+ reportStream.chunkFramesReceived = 0;
1770
+ reportStream.chunkBytesReceived = 0;
1771
+ reportStream.chunks = [];
1772
+ return;
1773
+ }
1774
+ if (kind === "report:end") {
1775
+ reportStream.sawChunkEnd = true;
1776
+ return;
1777
+ }
1587
1778
  this.sendJSON(MessageType.CALL, { ok: true, expected: "" });
1588
1779
  }
1589
1780
  onDataMessage(data) {
1781
+ reportStream.dataFrames++;
1782
+ reportStream.dataBytes += data.length;
1783
+ if (reportStream.sawChunkStart && !reportStream.sawChunkEnd) {
1784
+ reportStream.chunkFramesReceived++;
1785
+ reportStream.chunkBytesReceived += data.length;
1786
+ reportStream.chunks.push(data.toString("utf8"));
1787
+ return;
1788
+ }
1590
1789
  try {
1591
1790
  report = JSON.parse(data.toString("utf8"));
1791
+ parseError = null;
1592
1792
  }
1593
1793
  catch (error) {
1594
1794
  parseError = String(error);
1595
1795
  }
1596
1796
  }
1797
+ onClose() {
1798
+ sawChannelClose = true;
1799
+ }
1597
1800
  }
1598
1801
  const _channel = new TestChannel(child.stdout, child.stdin);
1599
1802
  const code = await new Promise((resolve) => {
@@ -1602,8 +1805,9 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1602
1805
  if (stderrPendingLine.length && !shouldSuppressWasiWarningLine(stderrPendingLine)) {
1603
1806
  stderrBuffer += stderrPendingLine;
1604
1807
  }
1605
- if (spawnError) {
1606
- const errorText = spawnError.stack ?? spawnError.message;
1808
+ const processSpawnError = spawnError;
1809
+ if (processSpawnError) {
1810
+ const errorText = processSpawnError.stack ?? processSpawnError.message;
1607
1811
  persistCrashRecord(crashDir, {
1608
1812
  kind: "test",
1609
1813
  file: specFile,
@@ -1614,29 +1818,93 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1614
1818
  });
1615
1819
  return createRuntimeFailureReport(specFile, modeName, "failed to start test runtime", errorText, stdoutBuffer, stderrBuffer);
1616
1820
  }
1821
+ if (reportStream.sawChunkStart) {
1822
+ if (!reportStream.sawChunkEnd) {
1823
+ parseError =
1824
+ parseError ??
1825
+ "missing report:end marker for chunked report payload";
1826
+ }
1827
+ else {
1828
+ const chunkedPayload = reportStream.chunks.join("");
1829
+ try {
1830
+ report = JSON.parse(chunkedPayload);
1831
+ parseError = null;
1832
+ }
1833
+ catch (error) {
1834
+ parseError = `could not parse chunked report payload: ${String(error)}`;
1835
+ }
1836
+ if (reportStream.chunkCountExpected > 0 &&
1837
+ reportStream.chunkFramesReceived !== reportStream.chunkCountExpected) {
1838
+ parseError =
1839
+ parseError ??
1840
+ `chunk count mismatch: expected ${reportStream.chunkCountExpected}, received ${reportStream.chunkFramesReceived}`;
1841
+ }
1842
+ if (reportStream.chunkTotalBytesExpected > 0 &&
1843
+ reportStream.chunkBytesReceived !== reportStream.chunkTotalBytesExpected) {
1844
+ parseError =
1845
+ parseError ??
1846
+ `chunk size mismatch: expected ${reportStream.chunkTotalBytesExpected} bytes, received ${reportStream.chunkBytesReceived}`;
1847
+ }
1848
+ }
1849
+ }
1617
1850
  if (parseError) {
1618
1851
  const errorText = `could not parse report payload: ${parseError}`;
1852
+ const diagnostics = buildRuntimeReportDiagnostics(code, sawChannelClose, reportStream, runtimeEvents);
1853
+ const fullError = `${errorText}\n${diagnostics}`;
1619
1854
  persistCrashRecord(crashDir, {
1620
1855
  kind: "test",
1621
1856
  file: specFile,
1622
1857
  mode: modeName ?? "default",
1623
- error: errorText,
1858
+ error: fullError,
1624
1859
  stdout: stdoutBuffer,
1625
1860
  stderr: stderrBuffer,
1626
1861
  });
1627
- return createRuntimeFailureReport(specFile, modeName, "runtime returned an invalid report payload", errorText, stdoutBuffer, stderrBuffer);
1862
+ return createRuntimeFailureReport(specFile, modeName, "runtime returned an invalid report payload", fullError, stdoutBuffer, stderrBuffer);
1628
1863
  }
1629
1864
  if (!report) {
1865
+ const synthesized = synthesizeReportFromRuntimeEvents(specFile, runtimeEvents);
1866
+ if (synthesized) {
1867
+ reporter.onWarning?.({
1868
+ message: "runtime report payload missing; reconstructed result from streamed lifecycle events",
1869
+ });
1870
+ if (code !== 0 || hasMeaningfulRuntimeOutput(stderrBuffer)) {
1871
+ const errorParts = [];
1872
+ if (code !== 0) {
1873
+ errorParts.push(`child process exited with code ${code}`);
1874
+ }
1875
+ const stderrText = normalizeRuntimeOutput(stderrBuffer);
1876
+ if (stderrText.length) {
1877
+ errorParts.push(stderrText);
1878
+ }
1879
+ const diagnostics = buildRuntimeReportDiagnostics(code, sawChannelClose, reportStream, runtimeEvents);
1880
+ errorParts.push(diagnostics);
1881
+ const errorText = errorParts.join("\n\n");
1882
+ persistCrashRecord(crashDir, {
1883
+ kind: "test",
1884
+ file: specFile,
1885
+ mode: modeName ?? "default",
1886
+ error: errorText || "runtime reported an unknown error",
1887
+ stdout: stdoutBuffer,
1888
+ stderr: stderrBuffer,
1889
+ });
1890
+ return appendRuntimeFailureReport(synthesized, specFile, modeName, code !== 0
1891
+ ? `test runtime failed with exit code ${code}`
1892
+ : "test runtime wrote to stderr", errorText, stdoutBuffer, stderrBuffer);
1893
+ }
1894
+ return synthesized;
1895
+ }
1630
1896
  const errorText = "missing report payload from test runtime";
1897
+ const diagnostics = buildRuntimeReportDiagnostics(code, sawChannelClose, reportStream, runtimeEvents);
1898
+ const fullError = `${errorText}\n${diagnostics}`;
1631
1899
  persistCrashRecord(crashDir, {
1632
1900
  kind: "test",
1633
1901
  file: specFile,
1634
1902
  mode: modeName ?? "default",
1635
- error: errorText,
1903
+ error: fullError,
1636
1904
  stdout: stdoutBuffer,
1637
1905
  stderr: stderrBuffer,
1638
1906
  });
1639
- return createRuntimeFailureReport(specFile, modeName, "test runtime exited without sending a report", errorText, stdoutBuffer, stderrBuffer);
1907
+ return createRuntimeFailureReport(specFile, modeName, "test runtime exited without sending a report", fullError, stdoutBuffer, stderrBuffer);
1640
1908
  }
1641
1909
  if (code !== 0 || hasMeaningfulRuntimeOutput(stderrBuffer)) {
1642
1910
  const errorParts = [];
@@ -1662,6 +1930,52 @@ async function runProcess(invocation, specFile, crashDir, modeName, snapshots, s
1662
1930
  }
1663
1931
  return report;
1664
1932
  }
1933
+ function synthesizeReportFromRuntimeEvents(specFile, runtimeEvents) {
1934
+ if (!runtimeEvents.sawFileEnd &&
1935
+ runtimeEvents.suiteStarts <= 0 &&
1936
+ runtimeEvents.suiteEnds <= 0) {
1937
+ return null;
1938
+ }
1939
+ let verdict = runtimeEvents.fileVerdict;
1940
+ if (verdict == "none" && runtimeEvents.assertionFails > 0) {
1941
+ verdict = "fail";
1942
+ }
1943
+ else if (verdict == "none" && runtimeEvents.sawFileEnd) {
1944
+ verdict = "ok";
1945
+ }
1946
+ return {
1947
+ suites: [
1948
+ {
1949
+ file: specFile,
1950
+ description: runtimeEvents.fileName || path.basename(specFile),
1951
+ depth: 0,
1952
+ kind: "file",
1953
+ verdict,
1954
+ time: {
1955
+ start: 0,
1956
+ end: 0,
1957
+ },
1958
+ suites: [],
1959
+ logs: [],
1960
+ tests: [],
1961
+ },
1962
+ ],
1963
+ coverage: {
1964
+ total: 0,
1965
+ covered: 0,
1966
+ uncovered: 0,
1967
+ percent: 100,
1968
+ points: [],
1969
+ },
1970
+ };
1971
+ }
1972
+ function buildRuntimeReportDiagnostics(code, sawChannelClose, reportStream, runtimeEvents) {
1973
+ return [
1974
+ `runtime diagnostics: exitCode=${code}, channelClose=${sawChannelClose ? "yes" : "no"}`,
1975
+ `report stream: dataFrames=${reportStream.dataFrames}, dataBytes=${reportStream.dataBytes}, chunked=${reportStream.sawChunkStart ? "yes" : "no"}, chunkStart=${reportStream.sawChunkStart ? "yes" : "no"}, chunkEnd=${reportStream.sawChunkEnd ? "yes" : "no"}, chunkFrames=${reportStream.chunkFramesReceived}, expectedChunkFrames=${reportStream.chunkCountExpected}, chunkBytes=${reportStream.chunkBytesReceived}, expectedChunkBytes=${reportStream.chunkTotalBytesExpected}`,
1976
+ `runtime events: fileStart=${runtimeEvents.sawFileStart ? "yes" : "no"}, fileEnd=${runtimeEvents.sawFileEnd ? "yes" : "no"}, fileVerdict=${runtimeEvents.fileVerdict}, suiteStarts=${runtimeEvents.suiteStarts}, suiteEnds=${runtimeEvents.suiteEnds}, assertionFails=${runtimeEvents.assertionFails}, warnings=${runtimeEvents.warnings}, logs=${runtimeEvents.logs}`,
1977
+ ].join("\n");
1978
+ }
1665
1979
  function createRuntimeFailureReport(specFile, modeName, title, details, stdout, stderr) {
1666
1980
  return appendRuntimeFailureReport({
1667
1981
  suites: [],
@@ -1,6 +1,7 @@
1
1
  export { createRunReporter, run } from "./run-core.js";
2
2
  export async function executeRunCommand(rawArgs, flags, configPath, selectedModes, deps) {
3
3
  const commandArgs = deps.resolveCommandArgs(rawArgs, "run");
4
+ const suiteSelectors = deps.resolveSuiteSelectors(rawArgs, "run");
4
5
  const listFlags = deps.resolveListFlags(rawArgs, "run");
5
6
  const featureToggles = deps.resolveFeatureToggles(rawArgs, "run");
6
7
  const runFlags = {
@@ -20,5 +21,5 @@ export async function executeRunCommand(rawArgs, flags, configPath, selectedMode
20
21
  await deps.listExecutionPlan("run", configPath, commandArgs, modeTargets, listFlags);
21
22
  return;
22
23
  }
23
- await deps.runRuntimeModes(runFlags, configPath, commandArgs, modeTargets);
24
+ await deps.runRuntimeModes(runFlags, configPath, commandArgs, suiteSelectors, modeTargets);
24
25
  }
@@ -1,5 +1,7 @@
1
1
  export async function executeTestCommand(rawArgs, flags, configPath, selectedModes, deps) {
2
2
  const commandArgs = deps.resolveCommandArgs(rawArgs, "test");
3
+ const suiteSelectors = deps.resolveSuiteSelectors(rawArgs, "test");
4
+ const fuzzerSelectors = deps.resolveFuzzerSelectors(rawArgs, "test");
3
5
  const listFlags = deps.resolveListFlags(rawArgs, "test");
4
6
  const featureToggles = deps.resolveFeatureToggles(rawArgs, "test");
5
7
  const buildFeatureToggles = {
@@ -25,5 +27,5 @@ export async function executeTestCommand(rawArgs, flags, configPath, selectedMod
25
27
  await deps.listExecutionPlan("test", configPath, commandArgs, modeTargets, listFlags, fuzzEnabled);
26
28
  return;
27
29
  }
28
- await deps.runTestModes(runFlags, configPath, commandArgs, modeTargets, buildFeatureToggles, fuzzEnabled, fuzzOverrides);
30
+ await deps.runTestModes(runFlags, configPath, commandArgs, suiteSelectors, fuzzerSelectors, modeTargets, buildFeatureToggles, fuzzEnabled, fuzzOverrides);
29
31
  }