@remix-run/test 0.1.0 → 0.2.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 (143) hide show
  1. package/README.md +140 -35
  2. package/dist/app/client/entry.d.ts +2 -0
  3. package/dist/app/client/entry.d.ts.map +1 -0
  4. package/dist/app/client/entry.js +324 -0
  5. package/dist/app/client/iframe.d.ts +2 -0
  6. package/dist/app/client/iframe.d.ts.map +1 -0
  7. package/dist/app/client/iframe.js +22 -0
  8. package/dist/app/server.d.ts +6 -0
  9. package/dist/app/server.d.ts.map +1 -0
  10. package/dist/app/server.js +303 -0
  11. package/dist/cli-entry.d.ts +3 -0
  12. package/dist/cli-entry.d.ts.map +1 -0
  13. package/dist/cli-entry.js +14 -0
  14. package/dist/cli.d.ts +7 -2
  15. package/dist/cli.d.ts.map +1 -1
  16. package/dist/cli.js +273 -139
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/lib/colors.d.ts +2 -0
  20. package/dist/lib/colors.d.ts.map +1 -0
  21. package/dist/lib/colors.js +2 -0
  22. package/dist/lib/config.d.ts +32 -1
  23. package/dist/lib/config.d.ts.map +1 -1
  24. package/dist/lib/config.js +125 -22
  25. package/dist/lib/context.d.ts +37 -13
  26. package/dist/lib/context.d.ts.map +1 -1
  27. package/dist/lib/context.js +19 -3
  28. package/dist/lib/coverage-loader.d.ts +16 -0
  29. package/dist/lib/coverage-loader.d.ts.map +1 -0
  30. package/dist/lib/coverage-loader.js +20 -0
  31. package/dist/lib/coverage.d.ts +28 -0
  32. package/dist/lib/coverage.d.ts.map +1 -0
  33. package/dist/lib/coverage.js +212 -0
  34. package/dist/lib/executor.d.ts +3 -26
  35. package/dist/lib/executor.d.ts.map +1 -1
  36. package/dist/lib/executor.js +11 -6
  37. package/dist/lib/fake-timers.d.ts +6 -0
  38. package/dist/lib/fake-timers.d.ts.map +1 -0
  39. package/dist/lib/fake-timers.js +45 -0
  40. package/dist/lib/import-module.d.ts +2 -0
  41. package/dist/lib/import-module.d.ts.map +1 -0
  42. package/dist/lib/import-module.js +29 -0
  43. package/dist/lib/normalize.d.ts +2 -0
  44. package/dist/lib/normalize.d.ts.map +1 -0
  45. package/dist/lib/{utils.js → normalize.js} +0 -9
  46. package/dist/lib/playwright.d.ts +1 -1
  47. package/dist/lib/playwright.d.ts.map +1 -1
  48. package/dist/lib/playwright.js +5 -8
  49. package/dist/lib/reporters/dot.d.ts +1 -2
  50. package/dist/lib/reporters/dot.d.ts.map +1 -1
  51. package/dist/lib/reporters/dot.js +2 -1
  52. package/dist/lib/reporters/files.d.ts +1 -2
  53. package/dist/lib/reporters/files.d.ts.map +1 -1
  54. package/dist/lib/reporters/files.js +2 -1
  55. package/dist/lib/reporters/index.d.ts +4 -5
  56. package/dist/lib/reporters/index.d.ts.map +1 -1
  57. package/dist/lib/reporters/index.js +3 -3
  58. package/dist/lib/reporters/results.d.ts +30 -0
  59. package/dist/lib/reporters/results.d.ts.map +1 -0
  60. package/dist/lib/reporters/results.js +1 -0
  61. package/dist/lib/reporters/spec.d.ts +1 -2
  62. package/dist/lib/reporters/spec.d.ts.map +1 -1
  63. package/dist/lib/reporters/spec.js +2 -1
  64. package/dist/lib/reporters/tap.d.ts +1 -2
  65. package/dist/lib/reporters/tap.d.ts.map +1 -1
  66. package/dist/lib/reporters/tap.js +1 -1
  67. package/dist/lib/runner-browser.d.ts +21 -0
  68. package/dist/lib/runner-browser.d.ts.map +1 -0
  69. package/dist/lib/runner-browser.js +117 -0
  70. package/dist/lib/runner.d.ts +7 -2
  71. package/dist/lib/runner.d.ts.map +1 -1
  72. package/dist/lib/runner.js +33 -4
  73. package/dist/lib/runtime.d.ts +2 -0
  74. package/dist/lib/runtime.d.ts.map +1 -0
  75. package/dist/lib/runtime.js +2 -0
  76. package/dist/lib/ts-transform.d.ts +4 -0
  77. package/dist/lib/ts-transform.d.ts.map +1 -0
  78. package/dist/lib/ts-transform.js +29 -0
  79. package/dist/lib/worker-e2e.js +5 -4
  80. package/dist/lib/worker.js +31 -3
  81. package/dist/test/coverage/fixture.d.ts +5 -0
  82. package/dist/test/coverage/fixture.d.ts.map +1 -0
  83. package/dist/test/coverage/fixture.js +32 -0
  84. package/dist/test/coverage/test-browser.d.ts +2 -0
  85. package/dist/test/coverage/test-browser.d.ts.map +1 -0
  86. package/dist/test/coverage/test-browser.js +24 -0
  87. package/dist/test/coverage/test-e2e.d.ts +2 -0
  88. package/dist/test/coverage/test-e2e.d.ts.map +1 -0
  89. package/dist/test/coverage/test-e2e.js +60 -0
  90. package/dist/test/coverage/test-unit.d.ts +2 -0
  91. package/dist/test/coverage/test-unit.d.ts.map +1 -0
  92. package/dist/test/coverage/test-unit.js +27 -0
  93. package/dist/test/framework.test.browser.d.ts +2 -0
  94. package/dist/test/framework.test.browser.d.ts.map +1 -0
  95. package/dist/test/framework.test.browser.js +107 -0
  96. package/dist/test/framework.test.e2e.d.ts.map +1 -0
  97. package/dist/test/framework.test.e2e.js +34 -0
  98. package/package.json +30 -9
  99. package/src/app/client/entry.ts +353 -0
  100. package/src/app/client/iframe.ts +18 -0
  101. package/src/app/server.ts +336 -0
  102. package/src/cli-entry.ts +15 -0
  103. package/src/cli.ts +322 -148
  104. package/src/index.ts +1 -0
  105. package/src/lib/colors.ts +3 -0
  106. package/src/lib/config.ts +169 -23
  107. package/src/lib/context.ts +59 -17
  108. package/src/lib/coverage-loader.ts +31 -0
  109. package/src/lib/coverage.ts +320 -0
  110. package/src/lib/executor.ts +18 -35
  111. package/src/lib/fake-timers.ts +64 -0
  112. package/src/lib/import-module.ts +29 -0
  113. package/src/lib/{utils.ts → normalize.ts} +0 -18
  114. package/src/lib/playwright.ts +5 -7
  115. package/src/lib/reporters/dot.ts +3 -2
  116. package/src/lib/reporters/files.ts +3 -2
  117. package/src/lib/reporters/index.ts +4 -5
  118. package/src/lib/reporters/results.ts +29 -0
  119. package/src/lib/reporters/spec.ts +3 -2
  120. package/src/lib/reporters/tap.ts +2 -2
  121. package/src/lib/runner-browser.ts +165 -0
  122. package/src/lib/runner.ts +62 -10
  123. package/src/lib/runtime.ts +2 -0
  124. package/src/lib/ts-transform.ts +36 -0
  125. package/src/lib/worker-e2e.ts +7 -5
  126. package/src/lib/worker.ts +24 -4
  127. package/src/test/coverage/fixture.ts +34 -0
  128. package/src/test/coverage/test-browser.ts +29 -0
  129. package/src/test/coverage/test-e2e.ts +70 -0
  130. package/src/test/coverage/test-unit.ts +32 -0
  131. package/tsconfig.json +3 -1
  132. package/dist/lib/e2e-server.d.ts +0 -11
  133. package/dist/lib/e2e-server.d.ts.map +0 -1
  134. package/dist/lib/e2e-server.js +0 -15
  135. package/dist/lib/framework.test.d.ts +0 -2
  136. package/dist/lib/framework.test.d.ts.map +0 -1
  137. package/dist/lib/framework.test.e2e.d.ts.map +0 -1
  138. package/dist/lib/framework.test.e2e.js +0 -29
  139. package/dist/lib/framework.test.js +0 -283
  140. package/dist/lib/utils.d.ts +0 -16
  141. package/dist/lib/utils.d.ts.map +0 -1
  142. package/src/lib/e2e-server.ts +0 -28
  143. /package/dist/{lib → test}/framework.test.e2e.d.ts +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/lib/executor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEhE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAE3D,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;IAChD,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,UAAU,EAAE,CAAA;CACpB;AAED,wBAAsB,QAAQ,CAAC,OAAO,CAAC,EAAE;IACvC,YAAY,CAAC,EAAE,oBAAoB,CAAA;IACnC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,qBAAqB,CAAC,EAAE,qBAAqB,CAAA;CAC9C,GAAG,OAAO,CAAC,WAAW,CAAC,CAoIvB"}
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../../src/lib/executor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,KAAK,wBAAwB,EAAE,MAAM,cAAc,CAAA;AAE/E,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAErE,wBAAsB,QAAQ,CAC5B,OAAO,CAAC,EAAE,IAAI,CAAC,wBAAwB,EAAE,uBAAuB,CAAC,GAChE,OAAO,CAAC,WAAW,CAAC,CA0ItB"}
@@ -1,6 +1,7 @@
1
1
  import { createTestContext } from "./context.js";
2
2
  export async function runTests(options) {
3
3
  let suites = globalThis.__testSuites || [];
4
+ let e2eCoverageEntries = [];
4
5
  let results = {
5
6
  passed: 0,
6
7
  failed: 0,
@@ -71,12 +72,13 @@ export async function runTests(options) {
71
72
  status: 'passed',
72
73
  duration: 0,
73
74
  };
74
- let { testContext, cleanup } = createTestContext({
75
- createServer: options?.createServer,
76
- browser: options?.browser,
77
- open: options?.open,
78
- playwrightPageOptions: options?.playwrightPageOptions,
79
- });
75
+ let contextOpts = options
76
+ ? {
77
+ ...options,
78
+ addE2ECoverageEntries: (e) => e2eCoverageEntries.push(e),
79
+ }
80
+ : undefined;
81
+ let { testContext, cleanup } = createTestContext(contextOpts);
80
82
  try {
81
83
  if (suite.beforeEach) {
82
84
  await suite.beforeEach();
@@ -119,5 +121,8 @@ export async function runTests(options) {
119
121
  // Clear suites in-place so the shared framework module is reset
120
122
  // for the next test file (which reuses the same cached module instance)
121
123
  suites.length = 0;
124
+ if (e2eCoverageEntries.length > 0) {
125
+ results.e2eBrowserCoverageEntries = e2eCoverageEntries;
126
+ }
122
127
  return results;
123
128
  }
@@ -0,0 +1,6 @@
1
+ export interface FakeTimers {
2
+ advance(ms: number): void;
3
+ restore(): void;
4
+ }
5
+ export declare function createFakeTimers(): FakeTimers;
6
+ //# sourceMappingURL=fake-timers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fake-timers.d.ts","sourceRoot":"","sources":["../../src/lib/fake-timers.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,OAAO,IAAI,IAAI,CAAA;CAChB;AAED,wBAAgB,gBAAgB,IAAI,UAAU,CAwD7C"}
@@ -0,0 +1,45 @@
1
+ import { mock } from "./mock.js";
2
+ export function createFakeTimers() {
3
+ let currentTime = 0;
4
+ let nextId = 1;
5
+ let pending = [];
6
+ function schedule(fn, delay, repeatMs) {
7
+ let id = nextId++;
8
+ pending.push({ id, fn, time: currentTime + Math.max(0, delay), repeatMs });
9
+ return id;
10
+ }
11
+ function cancel(id) {
12
+ pending = pending.filter((t) => t.id !== id);
13
+ }
14
+ let setTimeoutMock = mock.method(globalThis, 'setTimeout', ((fn, delay = 0) => schedule(fn, delay)));
15
+ let clearTimeoutMock = mock.method(globalThis, 'clearTimeout', cancel);
16
+ let setIntervalMock = mock.method(globalThis, 'setInterval', ((fn, delay = 0) => schedule(fn, delay, Math.max(0, delay))));
17
+ let clearIntervalMock = mock.method(globalThis, 'clearInterval', cancel);
18
+ return {
19
+ advance(ms) {
20
+ let targetTime = currentTime + ms;
21
+ while (true) {
22
+ let next = pending.filter((t) => t.time <= targetTime).sort((a, b) => a.time - b.time)[0];
23
+ if (!next)
24
+ break;
25
+ currentTime = next.time;
26
+ pending = pending.filter((t) => t.id !== next.id);
27
+ // Requeue intervals before running the callback so that calling
28
+ // clearInterval(id) from inside the callback can cancel the next firing.
29
+ if (next.repeatMs !== undefined) {
30
+ pending.push({ ...next, time: next.time + Math.max(1, next.repeatMs) });
31
+ }
32
+ next.fn();
33
+ }
34
+ currentTime = targetTime;
35
+ },
36
+ restore() {
37
+ setTimeoutMock.mock.restore?.();
38
+ clearTimeoutMock.mock.restore?.();
39
+ setIntervalMock.mock.restore?.();
40
+ clearIntervalMock.mock.restore?.();
41
+ pending = [];
42
+ currentTime = 0;
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,2 @@
1
+ export declare function importModule(specifier: string, meta: ImportMeta): Promise<any>;
2
+ //# sourceMappingURL=import-module.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"import-module.d.ts","sourceRoot":"","sources":["../../src/lib/import-module.ts"],"names":[],"mappings":"AAkBA,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAUpF"}
@@ -0,0 +1,29 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { tsImport } from 'tsx/esm/api';
10
+ import { IS_BUN } from "./runtime.js";
11
+ function hasImportMetaResolve(meta) {
12
+ return 'resolve' in meta && typeof meta.resolve === 'function';
13
+ }
14
+ /*
15
+ * Loads a module specifier relative to the caller's module context.
16
+ *
17
+ * @param specifier The module specifier or file path to load.
18
+ * @param meta The caller's `import.meta`, used as the context for resolution.
19
+ * @returns The imported module namespace.
20
+ */
21
+ export async function importModule(specifier, meta) {
22
+ if (IS_BUN) {
23
+ if (!hasImportMetaResolve(meta)) {
24
+ throw new Error('importModule() requires import.meta.resolve() in Bun');
25
+ }
26
+ return import(__rewriteRelativeImportExtension(meta.resolve(specifier, meta.url)));
27
+ }
28
+ return tsImport(specifier, meta.url);
29
+ }
@@ -0,0 +1,2 @@
1
+ export declare function normalizeLine(line: string): string;
2
+ //# sourceMappingURL=normalize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"normalize.d.ts","sourceRoot":"","sources":["../../src/lib/normalize.ts"],"names":[],"mappings":"AAYA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CASlD"}
@@ -1,12 +1,3 @@
1
- const noColor = process.env.CI === 'true' || !!process.env.NO_COLOR;
2
- export const colors = {
3
- reset: noColor ? '' : '\x1b[0m',
4
- dim: noColor ? (s) => s : (s) => `\x1b[2m${s}\x1b[0m`,
5
- green: noColor ? (s) => s : (s) => `\x1b[32m${s}\x1b[0m`,
6
- red: noColor ? (s) => s : (s) => `\x1b[31m${s}\x1b[0m`,
7
- cyan: noColor ? (s) => s : (s) => `\x1b[36m${s}\x1b[0m`,
8
- yellow: noColor ? (s) => s : (s) => `\x1b[2m\x1b[33m${s}\x1b[0m`,
9
- };
10
1
  function normalizeFilePath(path) {
11
2
  let locSuffix = path.match(/(:\d+:\d+)$/)?.[0] || '';
12
3
  let normalized = path
@@ -1,7 +1,7 @@
1
1
  import type { BrowserContextOptions, LaunchOptions } from 'playwright';
2
2
  import type { PlaywrightTestConfig } from 'playwright/test';
3
3
  export type PlaywrightUseOpts = PlaywrightTestConfig['use'];
4
- export declare function loadPlaywrightConfig(input: string | undefined): Promise<PlaywrightTestConfig | undefined>;
4
+ export declare function loadPlaywrightConfig(input: string | undefined, cwd?: string): Promise<PlaywrightTestConfig | undefined>;
5
5
  export declare function getBrowserLauncher(playwrightUseOpts?: PlaywrightUseOpts): import("playwright").BrowserType<{}>;
6
6
  export declare function resolveProjects(config?: PlaywrightTestConfig): Array<{
7
7
  name?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/lib/playwright.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAG3D,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAA;AAE3D,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GAAG,SAAS,GACxB,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAiB3C;AAQD,wBAAgB,kBAAkB,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,wCAavE;AAED,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAahE;AAED,wBAAgB,0BAA0B,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAK/F;AAED,wBAAgB,wBAAwB,CACtC,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,qBAAqB,GAAG;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAwBhF"}
1
+ {"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/lib/playwright.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,qBAAqB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AACtE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAA;AAG3D,MAAM,MAAM,iBAAiB,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAA;AAE3D,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,GAAG,SAAgB,GAClB,OAAO,CAAC,oBAAoB,GAAG,SAAS,CAAC,CAc3C;AAQD,wBAAgB,kBAAkB,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,wCAavE;AAED,wBAAgB,eAAe,CAC7B,MAAM,CAAC,EAAE,oBAAoB,GAC5B,KAAK,CAAC;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,iBAAiB,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAahE;AAED,wBAAgB,0BAA0B,CAAC,iBAAiB,CAAC,EAAE,iBAAiB,GAAG,aAAa,CAK/F;AAED,wBAAgB,wBAAwB,CACtC,iBAAiB,CAAC,EAAE,iBAAiB,GACpC,qBAAqB,GAAG;IAAE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAwBhF"}
@@ -1,18 +1,15 @@
1
1
  import * as path from 'node:path';
2
2
  import * as fs from 'node:fs/promises';
3
3
  import { chromium, firefox, webkit } from 'playwright';
4
- import { tsImport } from 'tsx/esm/api';
5
- export async function loadPlaywrightConfig(input) {
4
+ import { importModule } from "./import-module.js";
5
+ export async function loadPlaywrightConfig(input, cwd = process.cwd()) {
6
6
  let candidates = input
7
- ? [path.resolve(process.cwd(), input)]
8
- : [
9
- path.join(process.cwd(), 'playwright.config.ts'),
10
- path.join(process.cwd(), 'playwright.config.js'),
11
- ];
7
+ ? [path.resolve(cwd, input)]
8
+ : [path.join(cwd, 'playwright.config.ts'), path.join(cwd, 'playwright.config.js')];
12
9
  for (let configPath of candidates) {
13
10
  try {
14
11
  await fs.access(configPath);
15
- let mod = await tsImport(configPath, { parentURL: import.meta.url });
12
+ let mod = await importModule(configPath, import.meta);
16
13
  return mod.default ?? mod;
17
14
  }
18
15
  catch {
@@ -1,6 +1,5 @@
1
- import { type Counts } from '../utils.ts';
2
- import type { TestResults } from '../executor.ts';
3
1
  import type { Reporter } from './index.ts';
2
+ import type { Counts, TestResults } from './results.ts';
4
3
  export declare class DotReporter implements Reporter {
5
4
  #private;
6
5
  onSectionStart(_label: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"dot.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/dot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AAChE,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,qBAAa,WAAY,YAAW,QAAQ;;IAI1C,cAAc,CAAC,MAAM,EAAE,MAAM,QAAI;IAEjC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,QAc3C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QA6B3C;CACF"}
1
+ {"version":3,"file":"dot.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/dot.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAc,WAAW,EAAE,MAAM,cAAc,CAAA;AAEnE,qBAAa,WAAY,YAAW,QAAQ;;IAI1C,cAAc,CAAC,MAAM,EAAE,MAAM,QAAI;IAEjC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,CAAC,EAAE,MAAM,QAc3C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QA6B3C;CACF"}
@@ -1,4 +1,5 @@
1
- import { colors, normalizeLine } from "../utils.js";
1
+ import { colors } from "../colors.js";
2
+ import { normalizeLine } from "../normalize.js";
2
3
  export class DotReporter {
3
4
  #failures = [];
4
5
  #dotCount = 0;
@@ -1,6 +1,5 @@
1
- import { type Counts } from '../utils.ts';
2
- import type { TestResults } from '../executor.ts';
3
1
  import type { Reporter } from './index.ts';
2
+ import type { Counts, TestResults } from './results.ts';
4
3
  export declare class FilesReporter implements Reporter {
5
4
  #private;
6
5
  onSectionStart(_label: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/files.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AAChE,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,qBAAa,aAAc,YAAW,QAAQ;;IAG5C,cAAc,CAAC,MAAM,EAAE,MAAM,QAAI;IAEjC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,QA8B1C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAgC3C;CACF"}
1
+ {"version":3,"file":"files.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/files.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAc,WAAW,EAAE,MAAM,cAAc,CAAA;AAEnE,qBAAa,aAAc,YAAW,QAAQ;;IAG5C,cAAc,CAAC,MAAM,EAAE,MAAM,QAAI;IAEjC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,QA8B1C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAgC3C;CACF"}
@@ -1,5 +1,6 @@
1
1
  import * as path from 'node:path';
2
- import { colors, normalizeLine } from "../utils.js";
2
+ import { colors } from "../colors.js";
3
+ import { normalizeLine } from "../normalize.js";
3
4
  export class FilesReporter {
4
5
  #failures = [];
5
6
  onSectionStart(_label) { }
@@ -1,14 +1,13 @@
1
- import type { Counts } from '../utils.ts';
2
- import type { TestResults } from '../executor.ts';
3
- import { SpecReporter } from './spec.ts';
4
- import { TapReporter } from './tap.ts';
5
1
  import { DotReporter } from './dot.ts';
6
2
  import { FilesReporter } from './files.ts';
3
+ import type { Counts, TestResults } from './results.ts';
4
+ import { SpecReporter } from './spec.ts';
5
+ import { TapReporter } from './tap.ts';
7
6
  export interface Reporter {
8
7
  onResult(results: TestResults, env?: string): void;
9
8
  onSummary(counts: Counts, durationMs: number): void;
10
9
  onSectionStart(label: string): void;
11
10
  }
12
- export { SpecReporter, TapReporter, DotReporter, FilesReporter };
11
+ export { DotReporter, FilesReporter, SpecReporter, TapReporter };
13
12
  export declare function createReporter(type: string): Reporter;
14
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAA;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE1C,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACnD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAED,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,CAAA;AAEhE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAYrD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAEtC,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAClD,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACnD,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACpC;AAED,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,CAAA;AAEhE,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,CAYrD"}
@@ -1,8 +1,8 @@
1
- import { SpecReporter } from "./spec.js";
2
- import { TapReporter } from "./tap.js";
3
1
  import { DotReporter } from "./dot.js";
4
2
  import { FilesReporter } from "./files.js";
5
- export { SpecReporter, TapReporter, DotReporter, FilesReporter };
3
+ import { SpecReporter } from "./spec.js";
4
+ import { TapReporter } from "./tap.js";
5
+ export { DotReporter, FilesReporter, SpecReporter, TapReporter };
6
6
  export function createReporter(type) {
7
7
  switch (type) {
8
8
  case 'tap':
@@ -0,0 +1,30 @@
1
+ import type { V8CoverageEntry } from '../coverage';
2
+ export interface TestResult {
3
+ name: string;
4
+ suiteName: string;
5
+ filePath?: string;
6
+ status: 'passed' | 'failed' | 'skipped' | 'todo';
7
+ error?: {
8
+ message: string;
9
+ stack?: string;
10
+ };
11
+ duration: number;
12
+ }
13
+ export interface TestResults {
14
+ passed: number;
15
+ failed: number;
16
+ skipped: number;
17
+ todo: number;
18
+ tests: TestResult[];
19
+ e2eBrowserCoverageEntries?: Array<{
20
+ entries: V8CoverageEntry[];
21
+ baseUrl: string;
22
+ }>;
23
+ }
24
+ export type Counts = {
25
+ passed: number;
26
+ failed: number;
27
+ skipped: number;
28
+ todo: number;
29
+ };
30
+ //# sourceMappingURL=results.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"results.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/results.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAA;AAElD,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,GAAG,MAAM,CAAA;IAChD,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,MAAM,CAAA;QACf,KAAK,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,UAAU,EAAE,CAAA;IACnB,yBAAyB,CAAC,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,eAAe,EAAE,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;CACnF;AAED,MAAM,MAAM,MAAM,GAAG;IACnB,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;CACb,CAAA"}
@@ -0,0 +1 @@
1
+ export {};
@@ -1,6 +1,5 @@
1
- import { type Counts } from '../utils.ts';
2
- import type { TestResults } from '../executor.ts';
3
1
  import type { Reporter } from './index.ts';
2
+ import type { Counts, TestResults } from './results.ts';
4
3
  export declare class SpecReporter implements Reporter {
5
4
  #private;
6
5
  onSectionStart(label: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/spec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAyB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AAChE,OAAO,KAAK,EAAc,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAC7D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,qBAAa,YAAa,YAAW,QAAQ;;IAG3C,cAAc,CAAC,KAAK,EAAE,MAAM,QAE3B;IAED,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,QA8H1C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAgC3C;CACF"}
1
+ {"version":3,"file":"spec.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/spec.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAc,WAAW,EAAE,MAAM,cAAc,CAAA;AAEnE,qBAAa,YAAa,YAAW,QAAQ;;IAG3C,cAAc,CAAC,KAAK,EAAE,MAAM,QAE3B;IAED,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,QA8H1C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAgC3C;CACF"}
@@ -1,4 +1,5 @@
1
- import { colors, normalizeLine } from "../utils.js";
1
+ import { colors } from "../colors.js";
2
+ import { normalizeLine } from "../normalize.js";
2
3
  export class SpecReporter {
3
4
  #failures = [];
4
5
  onSectionStart(label) {
@@ -1,6 +1,5 @@
1
- import { type Counts } from '../utils.ts';
2
- import type { TestResults } from '../executor.ts';
3
1
  import type { Reporter } from './index.ts';
2
+ import type { Counts, TestResults } from './results.ts';
4
3
  export declare class TapReporter implements Reporter {
5
4
  #private;
6
5
  onSectionStart(_label: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"tap.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/tap.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C,qBAAa,WAAY,YAAW,QAAQ;;IAI1C,cAAc,CAAC,MAAM,EAAE,MAAM,QAAI;IAEjC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,QAmC1C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAS3C;CACF"}
1
+ {"version":3,"file":"tap.d.ts","sourceRoot":"","sources":["../../../src/lib/reporters/tap.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAC1C,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAEvD,qBAAa,WAAY,YAAW,QAAQ;;IAI1C,cAAc,CAAC,MAAM,EAAE,MAAM,QAAI;IAEjC,QAAQ,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,QAmC1C;IAED,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAS3C;CACF"}
@@ -1,4 +1,4 @@
1
- import { normalizeLine } from "../utils.js";
1
+ import { normalizeLine } from "../normalize.js";
2
2
  export class TapReporter {
3
3
  #counter = 0;
4
4
  #total = 0;
@@ -0,0 +1,21 @@
1
+ import { type CoverageMap } from './coverage.ts';
2
+ import { type PlaywrightUseOpts } from './playwright.ts';
3
+ import type { Reporter } from './reporters/index.ts';
4
+ import type { TestResults } from './reporters/results.ts';
5
+ export interface TestRunOptions {
6
+ baseUrl: string;
7
+ console?: boolean;
8
+ coverage?: boolean;
9
+ open?: boolean;
10
+ playwrightUseOpts?: PlaywrightUseOpts;
11
+ projectName?: string;
12
+ reporter: Reporter;
13
+ testFiles?: string[];
14
+ }
15
+ export declare function runBrowserTests(options: TestRunOptions): Promise<{
16
+ results: TestResults;
17
+ coverageMap: CoverageMap | null;
18
+ close: () => Promise<void>;
19
+ disconnected: Promise<void>;
20
+ }>;
21
+ //# sourceMappingURL=runner-browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runner-browser.d.ts","sourceRoot":"","sources":["../../src/lib/runner-browser.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAA;AACtB,OAAO,EAIL,KAAK,iBAAiB,EACvB,MAAM,iBAAiB,CAAA;AACxB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAA;AAWzD,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,QAAQ,CAAA;IAGlB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAA;CACrB;AAED,wBAAsB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC;IACtE,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;IAC/B,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,YAAY,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B,CAAC,CAuHD"}
@@ -0,0 +1,117 @@
1
+ import * as path from 'node:path';
2
+ import { colors } from "./colors.js";
3
+ import { getBrowserTestRootDir } from "./config.js";
4
+ import { collectCoverageMapFromPlaywright, } from "./coverage.js";
5
+ import { getBrowserLauncher, getPlaywrightLaunchOptions, getPlaywrightPageOptions, } from "./playwright.js";
6
+ // The harness reports each test result with `filePath` set to the
7
+ // `/scripts/<rel>` URL the iframe loaded. Reporters expect a real filesystem
8
+ // path so they can compute `path.relative(cwd, ...)` cleanly; otherwise they
9
+ // produce noisy `../../../scripts/...` strings.
10
+ function urlPathToFilePath(urlPath, rootDir) {
11
+ if (!urlPath.startsWith('/scripts/'))
12
+ return urlPath;
13
+ return path.resolve(rootDir, urlPath.slice('/scripts/'.length));
14
+ }
15
+ export async function runBrowserTests(options) {
16
+ let envLabel = options.projectName ? `browser:${options.projectName}` : 'browser';
17
+ let browser;
18
+ let page;
19
+ let close = async () => {
20
+ await page?.close();
21
+ await browser?.close();
22
+ browser = undefined;
23
+ page = undefined;
24
+ };
25
+ let results;
26
+ let coverageMap = null;
27
+ try {
28
+ browser = await getBrowserLauncher(options.playwrightUseOpts).launch(getPlaywrightLaunchOptions(options.playwrightUseOpts));
29
+ page = await browser.newPage(getPlaywrightPageOptions(options.playwrightUseOpts));
30
+ if (options.console) {
31
+ page.on('console', (msg) => console.log(`${colors.dim('[browser console]')} ${msg.text()}`));
32
+ }
33
+ // Playwright's JS coverage is Chromium-only. Start before navigation so
34
+ // the harness scripts and test modules are instrumented from first parse.
35
+ let coverageEnabled = options.coverage && browser.browserType().name() === 'chromium';
36
+ if (coverageEnabled) {
37
+ await page.coverage.startJSCoverage({ resetOnNavigation: false });
38
+ }
39
+ let totalPassed = 0;
40
+ let totalFailed = 0;
41
+ let totalSkipped = 0;
42
+ let totalTodo = 0;
43
+ let rootDir = getBrowserTestRootDir();
44
+ await page.route('**/file-results', async (route) => {
45
+ let results = route.request().postDataJSON();
46
+ for (let test of results.tests) {
47
+ if (test.filePath)
48
+ test.filePath = urlPathToFilePath(test.filePath, rootDir);
49
+ }
50
+ options.reporter.onResult(results, envLabel);
51
+ totalPassed += results.passed;
52
+ totalFailed += results.failed;
53
+ totalSkipped += results.skipped;
54
+ totalTodo += results.todo;
55
+ await route.fulfill({ status: 200 });
56
+ });
57
+ // Fail the tests if any /scripts/ request fails (harness scripts, test
58
+ // modules, or their transitive imports — all served via the same prefix).
59
+ let errorPromise = new Promise((_, reject) => {
60
+ let isScriptRequest = (request) => new URL(request.url()).pathname.startsWith('/scripts/');
61
+ page.on('response', (response) => {
62
+ if (!response.ok() && isScriptRequest(response.request())) {
63
+ reject(new Error(`Failed to load script: ${response.request().url()}`));
64
+ }
65
+ });
66
+ page.on('requestfailed', (request) => {
67
+ if (isScriptRequest(request)) {
68
+ reject(new Error(`Failed to load script: ${request.url()}`));
69
+ }
70
+ });
71
+ });
72
+ // Prevent unhandled rejection if we fail before setting up the listener
73
+ errorPromise.catch(() => { });
74
+ await page.goto(options.baseUrl);
75
+ await Promise.race([page.waitForFunction('window.__testsDone'), errorPromise]);
76
+ if (coverageEnabled) {
77
+ let entries = (await page.coverage.stopJSCoverage());
78
+ if (entries.length > 0) {
79
+ coverageMap = await collectCoverageMapFromPlaywright(entries, getBrowserTestRootDir(), new Set(options.testFiles ?? []), async (urlPath) => urlPath.startsWith('/scripts/') ? urlPath.slice('/scripts/'.length) : null);
80
+ }
81
+ }
82
+ results = {
83
+ passed: totalPassed,
84
+ failed: totalFailed,
85
+ skipped: totalSkipped,
86
+ todo: totalTodo,
87
+ tests: [],
88
+ };
89
+ }
90
+ catch (error) {
91
+ console.error('Browser tests failed to run:', error);
92
+ results = {
93
+ passed: 0,
94
+ failed: 1,
95
+ skipped: 0,
96
+ todo: 0,
97
+ tests: [],
98
+ };
99
+ }
100
+ if (options.open) {
101
+ return {
102
+ results,
103
+ coverageMap,
104
+ close,
105
+ disconnected: new Promise((r) => browser.on('disconnected', () => r())),
106
+ };
107
+ }
108
+ else {
109
+ await close();
110
+ return {
111
+ results,
112
+ coverageMap,
113
+ close,
114
+ disconnected: Promise.resolve(),
115
+ };
116
+ }
117
+ }
@@ -1,9 +1,14 @@
1
+ import { type CoverageConfig, type CoverageMap } from './coverage.ts';
1
2
  import { type PlaywrightUseOpts } from './playwright.ts';
2
3
  import type { Reporter } from './reporters/index.ts';
3
- import type { Counts } from './utils.ts';
4
+ import type { Counts } from './reporters/results.ts';
4
5
  export declare function runServerTests(files: string[], reporter: Reporter, concurrency: number, type: 'server' | 'e2e', options?: {
6
+ cwd?: string;
5
7
  open?: boolean;
6
8
  playwrightUseOpts?: PlaywrightUseOpts;
7
9
  projectName?: string;
8
- }): Promise<Counts>;
10
+ coverage?: CoverageConfig;
11
+ }): Promise<Counts & {
12
+ coverageMap: CoverageMap | null;
13
+ }>;
9
14
  //# sourceMappingURL=runner.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAMxC,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,QAAQ,GAAG,KAAK,EACtB,OAAO,GAAE;IACP,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,WAAW,CAAC,EAAE,MAAM,CAAA;CAChB,GACL,OAAO,CAAC,MAAM,CAAC,CAoCjB"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,cAAc,EACnB,KAAK,WAAW,EAEjB,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,KAAK,iBAAiB,EAAE,MAAM,iBAAiB,CAAA;AACxD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,KAAK,EAAE,MAAM,EAAe,MAAM,wBAAwB,CAAA;AAQjE,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EAAE,EACf,QAAQ,EAAE,QAAQ,EAClB,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,QAAQ,GAAG,KAAK,EACtB,OAAO,GAAE;IACP,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;IACrC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,cAAc,CAAA;CACrB,GACL,OAAO,CAAC,MAAM,GAAG;IAAE,WAAW,EAAE,WAAW,GAAG,IAAI,CAAA;CAAE,CAAC,CAwEvD"}
@@ -1,12 +1,19 @@
1
+ import * as fsp from 'node:fs/promises';
1
2
  import * as path from 'node:path';
2
3
  import { pathToFileURL } from 'node:url';
3
4
  import { Worker } from 'node:worker_threads';
5
+ import { IS_RUNNING_FROM_SRC } from "./config.js";
6
+ import { collectCoverageMapFromPlaywright, collectServerCoverageMap, } from "./coverage.js";
4
7
  import {} from "./playwright.js";
5
- const ext = path.extname(import.meta.url);
8
+ // Ensure we load the right file whether we're running in the monorepo (TS) or
9
+ // from a published package (JS)
10
+ const ext = IS_RUNNING_FROM_SRC ? '.ts' : '.js';
6
11
  const workerUrl = new URL(`./worker${ext}`, import.meta.url);
7
12
  const workerE2EUrl = new URL(`./worker-e2e${ext}`, import.meta.url);
8
13
  export async function runServerTests(files, reporter, concurrency, type, options = {}) {
9
14
  let counts = { passed: 0, failed: 0, skipped: 0, todo: 0 };
15
+ let coverageMap = null;
16
+ let cwd = options.cwd ?? process.cwd();
10
17
  let envLabel = options.projectName ? `${type}:${options.projectName}` : type;
11
18
  function accumulate(results, file) {
12
19
  reporter.onResult({ ...results, tests: results.tests.map((t) => ({ ...t, filePath: file })) }, envLabel);
@@ -16,15 +23,35 @@ export async function runServerTests(files, reporter, concurrency, type, options
16
23
  counts.todo += results.todo;
17
24
  }
18
25
  if (type === 'e2e') {
19
- await runInConcurrentWorkers(files, concurrency, (file) => runFileInWorker(file, type, (results) => accumulate(results, file), {
26
+ let allBrowserCoverageEntries = [];
27
+ await runInConcurrentWorkers(files, concurrency, (file) => runFileInWorker(file, type, (results) => {
28
+ accumulate(results, file);
29
+ if (results.e2eBrowserCoverageEntries) {
30
+ allBrowserCoverageEntries.push(...results.e2eBrowserCoverageEntries);
31
+ }
32
+ }, {
20
33
  ...options,
21
34
  playwrightUseOpts: options.playwrightUseOpts,
22
35
  }), () => counts.failed++);
36
+ if (options.coverage && allBrowserCoverageEntries.length > 0) {
37
+ coverageMap = await collectCoverageMapFromPlaywright(allBrowserCoverageEntries.flatMap((e) => e.entries), cwd, new Set(files), async (urlPath) => (urlPath.startsWith('/') ? urlPath.slice(1) : urlPath));
38
+ }
23
39
  }
24
40
  else {
25
- await runInConcurrentWorkers(files, concurrency, (file) => runFileInWorker(file, type, (results) => accumulate(results, file)), () => counts.failed++);
41
+ let coverageDataDir;
42
+ if (options.coverage) {
43
+ coverageDataDir = path.resolve(cwd, options.coverage.dir);
44
+ await fsp.mkdir(coverageDataDir, { recursive: true });
45
+ process.env.NODE_V8_COVERAGE = coverageDataDir;
46
+ }
47
+ await runInConcurrentWorkers(files, concurrency, (file) => runFileInWorker(file, type, (results) => accumulate(results, file), options), () => counts.failed++);
48
+ if (coverageDataDir) {
49
+ delete process.env.NODE_V8_COVERAGE;
50
+ let serverMap = await collectServerCoverageMap(coverageDataDir, cwd, new Set(files));
51
+ coverageMap = serverMap;
52
+ }
26
53
  }
27
- return { ...counts };
54
+ return { ...counts, coverageMap };
28
55
  }
29
56
  async function runInConcurrentWorkers(files, concurrency, runFile, onError) {
30
57
  let index = 0;
@@ -67,6 +94,7 @@ function runFileInWorker(file, type, onResults, options = {}) {
67
94
  workerData: {
68
95
  file: pathToFileURL(file).href,
69
96
  type,
97
+ coverage: options.coverage,
70
98
  open: options.open,
71
99
  playwrightUseOpts: options.playwrightUseOpts,
72
100
  },
@@ -75,6 +103,7 @@ function runFileInWorker(file, type, onResults, options = {}) {
75
103
  workerData: {
76
104
  file: pathToFileURL(file).href,
77
105
  type,
106
+ coverage: options.coverage,
78
107
  },
79
108
  });
80
109
  worker.once('message', (msg) => onResults(msg));
@@ -0,0 +1,2 @@
1
+ export declare const IS_BUN: boolean;
2
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../src/lib/runtime.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,MAAM,SAA2C,CAAA"}