@solidxai/core 0.1.15 → 0.1.16-beta.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 (171) hide show
  1. package/CHANGELOG.md +642 -0
  2. package/dist/commands/run-tests.command.d.ts +3 -1
  3. package/dist/commands/run-tests.command.d.ts.map +1 -1
  4. package/dist/commands/run-tests.command.js +6 -2
  5. package/dist/commands/run-tests.command.js.map +1 -1
  6. package/dist/controllers/test-run.controller.d.ts +12 -0
  7. package/dist/controllers/test-run.controller.d.ts.map +1 -0
  8. package/dist/controllers/test-run.controller.js +54 -0
  9. package/dist/controllers/test-run.controller.js.map +1 -0
  10. package/dist/dtos/test-run-request.dto.d.ts +35 -0
  11. package/dist/dtos/test-run-request.dto.d.ts.map +1 -0
  12. package/dist/dtos/test-run-request.dto.js +120 -0
  13. package/dist/dtos/test-run-request.dto.js.map +1 -0
  14. package/dist/guards/access-token.guard.d.ts +5 -1
  15. package/dist/guards/access-token.guard.d.ts.map +1 -1
  16. package/dist/guards/access-token.guard.js +20 -1
  17. package/dist/guards/access-token.guard.js.map +1 -1
  18. package/dist/index.d.ts +12 -0
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +17 -1
  21. package/dist/index.js.map +1 -1
  22. package/dist/interfaces/active-user-data.interface.d.ts +1 -0
  23. package/dist/interfaces/active-user-data.interface.d.ts.map +1 -1
  24. package/dist/interfaces/active-user-data.interface.js.map +1 -1
  25. package/dist/jobs/database/test-run-queue-options-database.d.ts +8 -0
  26. package/dist/jobs/database/test-run-queue-options-database.d.ts.map +1 -0
  27. package/dist/jobs/database/test-run-queue-options-database.js +10 -0
  28. package/dist/jobs/database/test-run-queue-options-database.js.map +1 -0
  29. package/dist/jobs/database/test-run-queue-publisher-database.service.d.ts +12 -0
  30. package/dist/jobs/database/test-run-queue-publisher-database.service.d.ts.map +1 -0
  31. package/dist/jobs/database/test-run-queue-publisher-database.service.js +39 -0
  32. package/dist/jobs/database/test-run-queue-publisher-database.service.js.map +1 -0
  33. package/dist/jobs/database/test-run-queue-subscriber-database.service.d.ts +26 -0
  34. package/dist/jobs/database/test-run-queue-subscriber-database.service.d.ts.map +1 -0
  35. package/dist/jobs/database/test-run-queue-subscriber-database.service.js +120 -0
  36. package/dist/jobs/database/test-run-queue-subscriber-database.service.js.map +1 -0
  37. package/dist/seeders/seed-data/solid-core-metadata.json +3 -1
  38. package/dist/services/access-token-denylist.service.d.ts +16 -0
  39. package/dist/services/access-token-denylist.service.d.ts.map +1 -0
  40. package/dist/services/access-token-denylist.service.js +74 -0
  41. package/dist/services/access-token-denylist.service.js.map +1 -0
  42. package/dist/services/authentication.service.d.ts +9 -1
  43. package/dist/services/authentication.service.d.ts.map +1 -1
  44. package/dist/services/authentication.service.js +39 -4
  45. package/dist/services/authentication.service.js.map +1 -1
  46. package/dist/services/import-transaction.service.d.ts +1 -1
  47. package/dist/services/import-transaction.service.d.ts.map +1 -1
  48. package/dist/services/import-transaction.service.js +13 -23
  49. package/dist/services/import-transaction.service.js.map +1 -1
  50. package/dist/services/settings/default-settings-provider.service.d.ts +20 -0
  51. package/dist/services/settings/default-settings-provider.service.d.ts.map +1 -1
  52. package/dist/services/settings/default-settings-provider.service.js +11 -0
  53. package/dist/services/settings/default-settings-provider.service.js.map +1 -1
  54. package/dist/services/workflow-secret.service.d.ts +3 -0
  55. package/dist/services/workflow-secret.service.d.ts.map +1 -1
  56. package/dist/services/workflow-secret.service.js +25 -0
  57. package/dist/services/workflow-secret.service.js.map +1 -1
  58. package/dist/solid-core.module.d.ts.map +1 -1
  59. package/dist/solid-core.module.js +9 -0
  60. package/dist/solid-core.module.js.map +1 -1
  61. package/dist/testing/adapters/ui/browser-provisioner.d.ts +5 -0
  62. package/dist/testing/adapters/ui/browser-provisioner.d.ts.map +1 -0
  63. package/dist/testing/adapters/ui/browser-provisioner.js +102 -0
  64. package/dist/testing/adapters/ui/browser-provisioner.js.map +1 -0
  65. package/dist/testing/adapters/ui/playwright-adapter.d.ts +40 -1
  66. package/dist/testing/adapters/ui/playwright-adapter.d.ts.map +1 -1
  67. package/dist/testing/adapters/ui/playwright-adapter.js +185 -3
  68. package/dist/testing/adapters/ui/playwright-adapter.js.map +1 -1
  69. package/dist/testing/adapters/ui/ui.types.d.ts +6 -0
  70. package/dist/testing/adapters/ui/ui.types.d.ts.map +1 -1
  71. package/dist/testing/adapters/ui/ui.types.js.map +1 -1
  72. package/dist/testing/contracts/runtime-context.types.d.ts +2 -0
  73. package/dist/testing/contracts/runtime-context.types.d.ts.map +1 -1
  74. package/dist/testing/contracts/runtime-context.types.js.map +1 -1
  75. package/dist/testing/core/interpolation.d.ts +1 -0
  76. package/dist/testing/core/interpolation.d.ts.map +1 -1
  77. package/dist/testing/core/interpolation.js +28 -68
  78. package/dist/testing/core/interpolation.js.map +1 -1
  79. package/dist/testing/core/path-resolution.spec.d.ts +2 -0
  80. package/dist/testing/core/path-resolution.spec.d.ts.map +1 -0
  81. package/dist/testing/core/path-resolution.spec.js +106 -0
  82. package/dist/testing/core/path-resolution.spec.js.map +1 -0
  83. package/dist/testing/core/path-segments.d.ts +2 -0
  84. package/dist/testing/core/path-segments.d.ts.map +1 -0
  85. package/dist/testing/core/path-segments.js +70 -0
  86. package/dist/testing/core/path-segments.js.map +1 -0
  87. package/dist/testing/core/resource-store.d.ts.map +1 -1
  88. package/dist/testing/core/resource-store.js +5 -2
  89. package/dist/testing/core/resource-store.js.map +1 -1
  90. package/dist/testing/core/testing-engine.d.ts.map +1 -1
  91. package/dist/testing/core/testing-engine.js +16 -0
  92. package/dist/testing/core/testing-engine.js.map +1 -1
  93. package/dist/testing/reporter/artifact-sink.d.ts +14 -0
  94. package/dist/testing/reporter/artifact-sink.d.ts.map +1 -0
  95. package/dist/testing/reporter/artifact-sink.js +3 -0
  96. package/dist/testing/reporter/artifact-sink.js.map +1 -0
  97. package/dist/testing/reporter/console-reporter.d.ts.map +1 -1
  98. package/dist/testing/reporter/console-reporter.js +7 -0
  99. package/dist/testing/reporter/console-reporter.js.map +1 -1
  100. package/dist/testing/reporter/file-service-artifact-sink.d.ts +22 -0
  101. package/dist/testing/reporter/file-service-artifact-sink.d.ts.map +1 -0
  102. package/dist/testing/reporter/file-service-artifact-sink.js +31 -0
  103. package/dist/testing/reporter/file-service-artifact-sink.js.map +1 -0
  104. package/dist/testing/reporter/lifecycle-events.types.d.ts +67 -0
  105. package/dist/testing/reporter/lifecycle-events.types.d.ts.map +1 -0
  106. package/dist/testing/reporter/lifecycle-events.types.js +3 -0
  107. package/dist/testing/reporter/lifecycle-events.types.js.map +1 -0
  108. package/dist/testing/reporter/lifecycle-webhook-reporter.d.ts +77 -0
  109. package/dist/testing/reporter/lifecycle-webhook-reporter.d.ts.map +1 -0
  110. package/dist/testing/reporter/lifecycle-webhook-reporter.js +275 -0
  111. package/dist/testing/reporter/lifecycle-webhook-reporter.js.map +1 -0
  112. package/dist/testing/reporter/progress-reporter.d.ts +3 -3
  113. package/dist/testing/reporter/progress-reporter.d.ts.map +1 -1
  114. package/dist/testing/reporter/progress-reporter.js.map +1 -1
  115. package/dist/testing/reporter/reporter.types.d.ts +13 -1
  116. package/dist/testing/reporter/reporter.types.d.ts.map +1 -1
  117. package/dist/testing/reporter/reporter.types.js.map +1 -1
  118. package/dist/testing/runner/read-scenarios-file.d.ts +9 -0
  119. package/dist/testing/runner/read-scenarios-file.d.ts.map +1 -0
  120. package/dist/testing/runner/read-scenarios-file.js +56 -0
  121. package/dist/testing/runner/read-scenarios-file.js.map +1 -0
  122. package/dist/testing/runner/run-from-metadata.d.ts +7 -2
  123. package/dist/testing/runner/run-from-metadata.d.ts.map +1 -1
  124. package/dist/testing/runner/run-from-metadata.js +57 -6
  125. package/dist/testing/runner/run-from-metadata.js.map +1 -1
  126. package/dist/testing/steps/ui/navigation.step.d.ts.map +1 -1
  127. package/dist/testing/steps/ui/navigation.step.js +12 -12
  128. package/dist/testing/steps/ui/navigation.step.js.map +1 -1
  129. package/package.json +1 -1
  130. package/postman/instant-logout.postman_collection.json +1493 -0
  131. package/src/commands/run-tests.command.ts +3 -0
  132. package/src/controllers/test-run.controller.ts +44 -0
  133. package/src/dtos/test-run-request.dto.ts +130 -0
  134. package/src/guards/access-token.guard.ts +48 -0
  135. package/src/index.ts +21 -1
  136. package/src/interfaces/active-user-data.interface.ts +8 -0
  137. package/src/jobs/database/test-run-queue-options-database.ts +9 -0
  138. package/src/jobs/database/test-run-queue-publisher-database.service.ts +24 -0
  139. package/src/jobs/database/test-run-queue-subscriber-database.service.ts +122 -0
  140. package/src/seeders/seed-data/solid-core-metadata.json +3 -1
  141. package/src/services/access-token-denylist.service.ts +145 -0
  142. package/src/services/authentication.service.ts +118 -2
  143. package/src/services/import-transaction.service.ts +19 -36
  144. package/src/services/settings/default-settings-provider.service.ts +12 -0
  145. package/src/services/workflow-secret.service.ts +61 -2
  146. package/src/solid-core.module.ts +11 -0
  147. package/src/testing/README.md +44 -1
  148. package/src/testing/adapters/ui/browser-provisioner.ts +115 -0
  149. package/src/testing/adapters/ui/playwright-adapter.ts +255 -5
  150. package/src/testing/adapters/ui/ui.types.ts +14 -0
  151. package/src/testing/contracts/runtime-context.types.ts +6 -0
  152. package/src/testing/core/interpolation.ts +39 -69
  153. package/src/testing/core/path-resolution.spec.ts +133 -0
  154. package/src/testing/core/path-segments.ts +81 -0
  155. package/src/testing/core/resource-store.ts +8 -2
  156. package/src/testing/core/testing-engine.ts +22 -0
  157. package/src/testing/reporter/artifact-sink.ts +16 -0
  158. package/src/testing/reporter/console-reporter.ts +9 -0
  159. package/src/testing/reporter/file-service-artifact-sink.ts +46 -0
  160. package/src/testing/reporter/lifecycle-events.types.ts +103 -0
  161. package/src/testing/reporter/lifecycle-webhook-reporter.ts +385 -0
  162. package/src/testing/reporter/progress-reporter.ts +3 -1
  163. package/src/testing/reporter/reporter.types.ts +10 -1
  164. package/src/testing/runner/read-scenarios-file.ts +44 -0
  165. package/src/testing/runner/run-from-metadata.ts +102 -9
  166. package/src/testing/steps/ui/navigation.step.ts +24 -20
  167. package/dist/services/access-token-storage.service.d.ts +0 -1
  168. package/dist/services/access-token-storage.service.d.ts.map +0 -1
  169. package/dist/services/access-token-storage.service.js +0 -1
  170. package/dist/services/access-token-storage.service.js.map +0 -1
  171. package/src/services/access-token-storage.service.ts +0 -0
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.resolvePlaywrightCli = resolvePlaywrightCli;
37
+ exports.isChromiumInstalled = isChromiumInstalled;
38
+ exports.ensureChromiumInstalled = ensureChromiumInstalled;
39
+ exports.cancelChromiumInstall = cancelChromiumInstall;
40
+ const child_process_1 = require("child_process");
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ let inFlight = null;
44
+ let activeInstall = null;
45
+ function resolvePlaywrightCli() {
46
+ try {
47
+ const packageJsonPath = require.resolve("playwright/package.json");
48
+ const cliPath = path.join(path.dirname(packageJsonPath), "cli.js");
49
+ return fs.existsSync(cliPath) ? cliPath : null;
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ async function isChromiumInstalled() {
56
+ try {
57
+ const { chromium } = await Promise.resolve().then(() => __importStar(require("playwright")));
58
+ return fs.existsSync(chromium.executablePath());
59
+ }
60
+ catch {
61
+ return false;
62
+ }
63
+ }
64
+ function ensureChromiumInstalled() {
65
+ if (!inFlight) {
66
+ inFlight = installChromium().finally(() => {
67
+ inFlight = null;
68
+ activeInstall = null;
69
+ });
70
+ }
71
+ return inFlight;
72
+ }
73
+ function cancelChromiumInstall() {
74
+ activeInstall?.kill("SIGTERM");
75
+ }
76
+ async function installChromium() {
77
+ if (await isChromiumInstalled())
78
+ return;
79
+ const cliPath = resolvePlaywrightCli();
80
+ if (!cliPath) {
81
+ throw new Error("Chromium is required for UI scenarios but the playwright package could not be resolved. " +
82
+ "Install it in the API project with: npm install playwright");
83
+ }
84
+ console.log("Chromium is required for UI scenarios and is not installed yet. Downloading it now (~150MB, one time)...");
85
+ await new Promise((resolve, reject) => {
86
+ const child = (0, child_process_1.spawn)(process.execPath, [cliPath, "install", "chromium"], {
87
+ stdio: "inherit",
88
+ });
89
+ activeInstall = child;
90
+ child.on("error", (error) => reject(new Error(`Failed to download Chromium: ${error.message}. ` +
91
+ "Install it manually with: npx playwright install chromium")));
92
+ child.on("close", (code, signal) => {
93
+ if (code === 0) {
94
+ resolve();
95
+ return;
96
+ }
97
+ reject(new Error(`Chromium download ${signal ? `was stopped by ${signal}` : `exited with code ${code}`}. ` +
98
+ "Install it manually with: npx playwright install chromium"));
99
+ });
100
+ });
101
+ }
102
+ //# sourceMappingURL=browser-provisioner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-provisioner.js","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/browser-provisioner.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmBA,oDAQC;AAMD,kDAOC;AASD,0DAUC;AAMD,sDAEC;AAnED,iDAAoD;AACpD,uCAAyB;AACzB,2CAA6B;AAO7B,IAAI,QAAQ,GAAyB,IAAI,CAAC;AAC1C,IAAI,aAAa,GAAwB,IAAI,CAAC;AAS9C,SAAgB,oBAAoB;IAClC,IAAI,CAAC;QACH,MAAM,eAAe,GAAG,OAAO,CAAC,OAAO,CAAC,yBAAyB,CAAC,CAAC;QACnE,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,eAAe,CAAC,EAAE,QAAQ,CAAC,CAAC;QACnE,OAAO,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC;IACjD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAMM,KAAK,UAAU,mBAAmB;IACvC,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,wDAAa,YAAY,GAAC,CAAC;QAChD,OAAO,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,cAAc,EAAE,CAAC,CAAC;IAClD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AASD,SAAgB,uBAAuB;IACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;QAGd,QAAQ,GAAG,eAAe,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE;YACxC,QAAQ,GAAG,IAAI,CAAC;YAChB,aAAa,GAAG,IAAI,CAAC;QACvB,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAMD,SAAgB,qBAAqB;IACnC,aAAa,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;AACjC,CAAC;AAED,KAAK,UAAU,eAAe;IAC5B,IAAI,MAAM,mBAAmB,EAAE;QAAE,OAAO;IAExC,MAAM,OAAO,GAAG,oBAAoB,EAAE,CAAC;IACvC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,0FAA0F;YACxF,4DAA4D,CAC/D,CAAC;IACJ,CAAC;IAED,OAAO,CAAC,GAAG,CACT,0GAA0G,CAC3G,CAAC;IAEF,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QAG1C,MAAM,KAAK,GAAG,IAAA,qBAAK,EAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,SAAS,EAAE,UAAU,CAAC,EAAE;YACtE,KAAK,EAAE,SAAS;SACjB,CAAC,CAAC;QACH,aAAa,GAAG,KAAK,CAAC;QAEtB,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAC1B,MAAM,CACJ,IAAI,KAAK,CACP,gCAAgC,KAAK,CAAC,OAAO,IAAI;YAC/C,2DAA2D,CAC9D,CACF,CACF,CAAC;QAEF,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE;YACjC,IAAI,IAAI,KAAK,CAAC,EAAE,CAAC;gBACf,OAAO,EAAE,CAAC;gBACV,OAAO;YACT,CAAC;YACD,MAAM,CACJ,IAAI,KAAK,CACP,qBAAqB,MAAM,CAAC,CAAC,CAAC,kBAAkB,MAAM,EAAE,CAAC,CAAC,CAAC,oBAAoB,IAAI,EAAE,IAAI;gBACvF,2DAA2D,CAC9D,CACF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import { ChildProcess, spawn } from \"child_process\";\nimport * as fs from \"fs\";\nimport * as path from \"path\";\n\n/**\n * The npm `playwright` package ships no install script, so `npm install` places the\n * JS module but never downloads the browser binary. These helpers fetch it on demand.\n */\n\nlet inFlight: Promise<void> | null = null;\nlet activeInstall: ChildProcess | null = null;\n\n/**\n * Resolves Playwright's own CLI entry point.\n *\n * `require.resolve('playwright/cli.js')` fails with ERR_PACKAGE_PATH_NOT_EXPORTED because\n * `cli.js` is absent from Playwright's `exports` map, so we resolve `package.json` (which\n * is exported) and walk to its sibling.\n */\nexport function resolvePlaywrightCli(): string | null {\n try {\n const packageJsonPath = require.resolve(\"playwright/package.json\");\n const cliPath = path.join(path.dirname(packageJsonPath), \"cli.js\");\n return fs.existsSync(cliPath) ? cliPath : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Asking Playwright for the executable path keeps us in step with PLAYWRIGHT_BROWSERS_PATH\n * and its per-version cache layout instead of second-guessing either.\n */\nexport async function isChromiumInstalled(): Promise<boolean> {\n try {\n const { chromium } = await import(\"playwright\");\n return fs.existsSync(chromium.executablePath());\n } catch {\n return false;\n }\n}\n\n/**\n * Downloads Chromium unless it is already present. Only Chromium is fetched: it is the\n * sole browser PlaywrightAdapter launches.\n *\n * Concurrent callers share one download. This matters once a server warms the browser up\n * at boot while a test run may start before that finishes.\n */\nexport function ensureChromiumInstalled(): Promise<void> {\n if (!inFlight) {\n // Clearing on settle rather than caching the result lets a failed download be retried\n // by the next caller instead of poisoning the whole process lifetime.\n inFlight = installChromium().finally(() => {\n inFlight = null;\n activeInstall = null;\n });\n }\n return inFlight;\n}\n\n/**\n * Stops a download that is still running, so shutting down mid-install does not orphan the\n * child process against a half-written browser cache.\n */\nexport function cancelChromiumInstall(): void {\n activeInstall?.kill(\"SIGTERM\");\n}\n\nasync function installChromium(): Promise<void> {\n if (await isChromiumInstalled()) return;\n\n const cliPath = resolvePlaywrightCli();\n if (!cliPath) {\n throw new Error(\n \"Chromium is required for UI scenarios but the playwright package could not be resolved. \" +\n \"Install it in the API project with: npm install playwright\",\n );\n }\n\n console.log(\n \"Chromium is required for UI scenarios and is not installed yet. Downloading it now (~150MB, one time)...\",\n );\n\n await new Promise<void>((resolve, reject) => {\n // Async spawn, never spawnSync: this also runs inside a live server during module init,\n // where a synchronous child would block the event loop for the whole download.\n const child = spawn(process.execPath, [cliPath, \"install\", \"chromium\"], {\n stdio: \"inherit\",\n });\n activeInstall = child;\n\n child.on(\"error\", (error) =>\n reject(\n new Error(\n `Failed to download Chromium: ${error.message}. ` +\n \"Install it manually with: npx playwright install chromium\",\n ),\n ),\n );\n\n child.on(\"close\", (code, signal) => {\n if (code === 0) {\n resolve();\n return;\n }\n reject(\n new Error(\n `Chromium download ${signal ? `was stopped by ${signal}` : `exited with code ${code}`}. ` +\n \"Install it manually with: npx playwright install chromium\",\n ),\n );\n });\n });\n}\n"]}
@@ -1,20 +1,59 @@
1
1
  import type { Page } from "playwright";
2
2
  import type { PlaywrightAdapterOptions } from "./ui.types";
3
3
  export declare const DEFAULT_UI_TIMEOUT_MS = 30000;
4
+ export interface ConsoleLogEntry {
5
+ type: string;
6
+ text: string;
7
+ location?: string;
8
+ }
9
+ export interface NetworkLogEntry {
10
+ method: string;
11
+ url: string;
12
+ resourceType?: string;
13
+ requestHeaders?: Record<string, string>;
14
+ requestBody?: string;
15
+ status?: number;
16
+ ok?: boolean;
17
+ responseHeaders?: Record<string, string>;
18
+ responseBody?: string;
19
+ durationMs?: number;
20
+ failure?: string;
21
+ }
22
+ export interface FailureArtifact {
23
+ name: string;
24
+ contentType: string;
25
+ data: Buffer | string;
26
+ }
4
27
  export declare class PlaywrightAdapter {
5
28
  private readonly baseUrl?;
6
29
  private readonly headless;
7
30
  private readonly defaultTimeoutMs;
8
31
  private readonly navigationTimeoutMs;
32
+ private readonly capture;
33
+ private readonly recordVideo;
9
34
  private browser?;
10
35
  private context?;
11
36
  page?: Page;
37
+ private consoleBuf;
38
+ private networkBuf;
39
+ private videoDir?;
40
+ private runVideo?;
41
+ private readonly requestStartedAt;
12
42
  constructor(opts?: PlaywrightAdapterOptions);
13
43
  isHeadless(): boolean;
14
44
  resolveTimeout(stepTimeoutMs?: number): number;
15
45
  resolveNavigationTimeout(stepTimeoutMs?: number): number;
16
46
  start(): Promise<void>;
17
- stop(): Promise<void>;
47
+ getRunVideo(): FailureArtifact | undefined;
48
+ private attachCaptureListeners;
49
+ resetCapture(): void;
50
+ getConsoleLog(): ConsoleLogEntry[];
51
+ getNetworkLog(): NetworkLogEntry[];
52
+ private readResponseBody;
53
+ collectFailureArtifacts(): Promise<FailureArtifact[]>;
54
+ stop(opts?: {
55
+ keepVideo?: boolean;
56
+ }): Promise<void>;
18
57
  resolveUrl(url: string): string;
19
58
  }
20
59
  //# sourceMappingURL=playwright-adapter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"playwright-adapter.d.ts","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/playwright-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,IAAI,EAAE,MAAM,YAAY,CAAC;AAEhE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAM3D,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAE5C,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAC,CAAiB;IAC1B,IAAI,CAAC,EAAE,IAAI,CAAC;gBAEP,IAAI,CAAC,EAAE,wBAAwB;IAQ3C,UAAU,IAAI,OAAO;IAQrB,cAAc,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM;IAM9C,wBAAwB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM;IAMlD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAStB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAmB3B,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;CAOhC"}
1
+ {"version":3,"file":"playwright-adapter.d.ts","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/playwright-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA2B,IAAI,EAAqC,MAAM,YAAY,CAAC;AAEnG,OAAO,KAAK,EAAkB,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAM3E,eAAO,MAAM,qBAAqB,QAAS,CAAC;AAO5C,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAGD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;CACvB;AAED,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAU;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAAS;IAC1C,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAS;IAC7C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAiB;IACzC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,OAAO,CAAC,CAAU;IAC1B,OAAO,CAAC,OAAO,CAAC,CAAiB;IAC1B,IAAI,CAAC,EAAE,IAAI,CAAC;IAGnB,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,UAAU,CAAyB;IAG3C,OAAO,CAAC,QAAQ,CAAC,CAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,CAAkB;IACnC,OAAO,CAAC,QAAQ,CAAC,gBAAgB,CAA8B;gBAEnD,IAAI,CAAC,EAAE,wBAAwB;IAU3C,UAAU,IAAI,OAAO;IAQrB,cAAc,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM;IAM9C,wBAAwB,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,MAAM;IAMlD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAsB5B,WAAW,IAAI,eAAe,GAAG,SAAS;IAS1C,OAAO,CAAC,sBAAsB;IA8D9B,YAAY,IAAI,IAAI;IAMpB,aAAa,IAAI,eAAe,EAAE;IAIlC,aAAa,IAAI,eAAe,EAAE;YAIpB,gBAAgB;IAoBxB,uBAAuB,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC;IAuBrD,IAAI,CAAC,IAAI,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IA0CzD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;CAYhC"}
@@ -38,13 +38,22 @@ function isAbsoluteUrl(url) {
38
38
  return /^https?:\/\//i.test(url);
39
39
  }
40
40
  exports.DEFAULT_UI_TIMEOUT_MS = 30_000;
41
+ const MAX_CONSOLE_ENTRIES = 500;
42
+ const MAX_NETWORK_ENTRIES = 500;
43
+ const MAX_NETWORK_BODY_CHARS = 16_000;
44
+ const STATIC_EXT_RE = /\.(?:css|js|mjs|cjs|map|png|jpe?g|gif|svg|ico|webp|avif|woff2?|ttf|eot|otf|mp4|webm|mp3|wav|pdf|zip)$/i;
41
45
  class PlaywrightAdapter {
42
46
  constructor(opts) {
47
+ this.consoleBuf = [];
48
+ this.networkBuf = [];
49
+ this.requestStartedAt = new Map();
43
50
  this.baseUrl = opts?.baseUrl;
44
51
  this.headless = opts?.headless ?? true;
45
52
  this.defaultTimeoutMs = opts?.defaultTimeoutMs ?? exports.DEFAULT_UI_TIMEOUT_MS;
46
53
  this.navigationTimeoutMs =
47
54
  opts?.navigationTimeoutMs ?? this.defaultTimeoutMs;
55
+ this.capture = opts?.capture ?? {};
56
+ this.recordVideo = opts?.recordVideo ?? true;
48
57
  }
49
58
  isHeadless() {
50
59
  return this.headless;
@@ -62,12 +71,133 @@ class PlaywrightAdapter {
62
71
  async start() {
63
72
  const { chromium } = await Promise.resolve().then(() => __importStar(require('playwright')));
64
73
  this.browser = await chromium.launch({ headless: this.headless });
65
- this.context = await this.browser.newContext();
74
+ if (this.recordVideo) {
75
+ const os = await Promise.resolve().then(() => __importStar(require('os')));
76
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
77
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
78
+ this.videoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'solid-run-video-'));
79
+ }
80
+ this.context = await this.browser.newContext(this.videoDir ? { recordVideo: { dir: this.videoDir } } : {});
66
81
  this.context.setDefaultTimeout(this.defaultTimeoutMs);
67
82
  this.context.setDefaultNavigationTimeout(this.navigationTimeoutMs);
68
83
  this.page = await this.context.newPage();
84
+ this.attachCaptureListeners(this.page);
85
+ }
86
+ getRunVideo() {
87
+ return this.runVideo;
88
+ }
89
+ attachCaptureListeners(page) {
90
+ if (this.capture.console) {
91
+ page.on("console", (msg) => {
92
+ if (this.consoleBuf.length >= MAX_CONSOLE_ENTRIES)
93
+ return;
94
+ const loc = msg.location();
95
+ this.consoleBuf.push({
96
+ type: msg.type(),
97
+ text: msg.text(),
98
+ location: loc?.url ? `${loc.url}:${loc.lineNumber ?? 0}` : undefined,
99
+ });
100
+ });
101
+ page.on("pageerror", (err) => {
102
+ if (this.consoleBuf.length >= MAX_CONSOLE_ENTRIES)
103
+ return;
104
+ this.consoleBuf.push({ type: "pageerror", text: err?.stack || err?.message || String(err) });
105
+ });
106
+ }
107
+ if (this.capture.network) {
108
+ page.on("request", (req) => {
109
+ this.requestStartedAt.set(req, Date.now());
110
+ });
111
+ page.on("response", async (res) => {
112
+ if (this.networkBuf.length >= MAX_NETWORK_ENTRIES)
113
+ return;
114
+ const req = res.request();
115
+ const captureDetail = shouldCaptureDetailedNetworkEntry(req);
116
+ const requestHeaders = captureDetail ? await req.allHeaders().catch(() => undefined) : undefined;
117
+ const responseHeaders = captureDetail ? await res.allHeaders().catch(() => undefined) : undefined;
118
+ const responseBody = captureDetail ? await this.readResponseBody(res, responseHeaders) : undefined;
119
+ const startedAt = this.requestStartedAt.get(req);
120
+ this.requestStartedAt.delete(req);
121
+ this.networkBuf.push({
122
+ method: req.method(),
123
+ url: res.url(),
124
+ resourceType: req.resourceType(),
125
+ requestHeaders,
126
+ requestBody: captureDetail ? truncateText(req.postData() ?? undefined) : undefined,
127
+ status: res.status(),
128
+ ok: res.ok(),
129
+ responseHeaders,
130
+ responseBody,
131
+ durationMs: startedAt != null ? Date.now() - startedAt : undefined,
132
+ });
133
+ });
134
+ page.on("requestfailed", async (req) => {
135
+ if (this.networkBuf.length >= MAX_NETWORK_ENTRIES)
136
+ return;
137
+ const startedAt = this.requestStartedAt.get(req);
138
+ this.requestStartedAt.delete(req);
139
+ const captureDetail = shouldCaptureDetailedNetworkEntry(req);
140
+ this.networkBuf.push({
141
+ method: req.method(),
142
+ url: req.url(),
143
+ resourceType: req.resourceType(),
144
+ requestHeaders: captureDetail ? await req.allHeaders().catch(() => undefined) : undefined,
145
+ requestBody: captureDetail ? truncateText(req.postData() ?? undefined) : undefined,
146
+ failure: req.failure()?.errorText ?? "request failed",
147
+ durationMs: startedAt != null ? Date.now() - startedAt : undefined,
148
+ });
149
+ });
150
+ }
151
+ }
152
+ resetCapture() {
153
+ this.consoleBuf = [];
154
+ this.networkBuf = [];
155
+ this.requestStartedAt.clear();
156
+ }
157
+ getConsoleLog() {
158
+ return this.consoleBuf;
69
159
  }
70
- async stop() {
160
+ getNetworkLog() {
161
+ return this.networkBuf;
162
+ }
163
+ async readResponseBody(res, headers) {
164
+ const contentType = String(headers?.["content-type"] ?? headers?.["Content-Type"] ?? "").toLowerCase();
165
+ const contentLength = Number(headers?.["content-length"] ?? headers?.["Content-Length"] ?? 0);
166
+ if (contentLength && contentLength > MAX_NETWORK_BODY_CHARS) {
167
+ return `[body omitted: ${contentLength} bytes]`;
168
+ }
169
+ if (!isTextLikeContentType(contentType))
170
+ return undefined;
171
+ try {
172
+ return truncateText(await res.text());
173
+ }
174
+ catch {
175
+ return undefined;
176
+ }
177
+ }
178
+ async collectFailureArtifacts() {
179
+ if (!this.page)
180
+ return [];
181
+ const artifacts = [];
182
+ if (this.capture.console && this.consoleBuf.length) {
183
+ artifacts.push({
184
+ name: "console.json",
185
+ contentType: "application/json",
186
+ data: JSON.stringify(this.consoleBuf),
187
+ });
188
+ }
189
+ if (this.capture.network && this.networkBuf.length) {
190
+ artifacts.push({
191
+ name: "network.json",
192
+ contentType: "application/json",
193
+ data: JSON.stringify(this.networkBuf),
194
+ });
195
+ }
196
+ return artifacts;
197
+ }
198
+ async stop(opts) {
199
+ const keepVideo = opts?.keepVideo ?? false;
200
+ const video = this.videoDir ? this.page?.video() : undefined;
71
201
  try {
72
202
  if (this.context) {
73
203
  await this.context.close();
@@ -77,6 +207,22 @@ class PlaywrightAdapter {
77
207
  this.context = undefined;
78
208
  this.page = undefined;
79
209
  }
210
+ if (video && this.videoDir) {
211
+ try {
212
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
213
+ if (keepVideo) {
214
+ const videoPath = await video.path();
215
+ const data = await fs.promises.readFile(videoPath);
216
+ this.runVideo = { name: 'run.webm', contentType: 'video/webm', data };
217
+ }
218
+ await fs.promises.rm(this.videoDir, { recursive: true, force: true }).catch(() => { });
219
+ }
220
+ catch {
221
+ }
222
+ finally {
223
+ this.videoDir = undefined;
224
+ }
225
+ }
80
226
  try {
81
227
  if (this.browser) {
82
228
  await this.browser.close();
@@ -90,10 +236,46 @@ class PlaywrightAdapter {
90
236
  if (isAbsoluteUrl(url))
91
237
  return url;
92
238
  if (this.baseUrl) {
93
- return new URL(url, this.baseUrl).toString();
239
+ return `${this.baseUrl.replace(/\/+$/, "")}/${url.replace(/^\/+/, "")}`;
94
240
  }
95
241
  return url;
96
242
  }
97
243
  }
98
244
  exports.PlaywrightAdapter = PlaywrightAdapter;
245
+ function truncateText(value) {
246
+ if (!value)
247
+ return undefined;
248
+ if (value.length <= MAX_NETWORK_BODY_CHARS)
249
+ return value;
250
+ return `${value.slice(0, MAX_NETWORK_BODY_CHARS)}\n… [truncated ${value.length - MAX_NETWORK_BODY_CHARS} chars]`;
251
+ }
252
+ function isTextLikeContentType(contentType) {
253
+ if (!contentType)
254
+ return true;
255
+ return (contentType.includes("json") ||
256
+ contentType.includes("text/") ||
257
+ contentType.includes("javascript") ||
258
+ contentType.includes("xml") ||
259
+ contentType.includes("html") ||
260
+ contentType.includes("x-www-form-urlencoded"));
261
+ }
262
+ function shouldCaptureDetailedNetworkEntry(req) {
263
+ const resourceType = req.resourceType().toLowerCase();
264
+ if (resourceType === "xhr" || resourceType === "fetch")
265
+ return true;
266
+ const method = req.method().toUpperCase();
267
+ if (method !== "GET" && method !== "HEAD")
268
+ return true;
269
+ const url = req.url().toLowerCase();
270
+ try {
271
+ const pathname = new URL(url).pathname.toLowerCase();
272
+ if (STATIC_EXT_RE.test(pathname))
273
+ return false;
274
+ }
275
+ catch {
276
+ if (STATIC_EXT_RE.test(url))
277
+ return false;
278
+ }
279
+ return resourceType === "document";
280
+ }
99
281
  //# sourceMappingURL=playwright-adapter.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"playwright-adapter.js","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/playwright-adapter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAIA,SAAS,aAAa,CAAC,GAAW;IAChC,OAAO,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AACnC,CAAC;AAEY,QAAA,qBAAqB,GAAG,MAAM,CAAC;AAE5C,MAAa,iBAAiB;IAS5B,YAAY,IAA+B;QACzC,IAAI,CAAC,OAAO,GAAG,IAAI,EAAE,OAAO,CAAC;QAC7B,IAAI,CAAC,QAAQ,GAAG,IAAI,EAAE,QAAQ,IAAI,IAAI,CAAC;QACvC,IAAI,CAAC,gBAAgB,GAAG,IAAI,EAAE,gBAAgB,IAAI,6BAAqB,CAAC;QACxE,IAAI,CAAC,mBAAmB;YACtB,IAAI,EAAE,mBAAmB,IAAI,IAAI,CAAC,gBAAgB,CAAC;IACvD,CAAC;IAED,UAAU;QACR,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAMD,cAAc,CAAC,aAAsB;QACnC,OAAO,OAAO,aAAa,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;YACxE,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC;IAC5B,CAAC;IAED,wBAAwB,CAAC,aAAsB;QAC7C,OAAO,OAAO,aAAa,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAC;YACxE,CAAC,CAAC,aAAa;YACf,CAAC,CAAC,IAAI,CAAC,mBAAmB,CAAC;IAC/B,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,EAAE,QAAQ,EAAE,GAAG,wDAAa,YAAY,GAAC,CAAC;QAChD,IAAI,CAAC,OAAO,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAClE,IAAI,CAAC,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;QAC/C,IAAI,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;QACtD,IAAI,CAAC,OAAO,CAAC,2BAA2B,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;QACnE,IAAI,CAAC,IAAI,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,IAAI;QACR,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YACzB,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;QACxB,CAAC;QAED,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;gBACjB,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;YAC7B,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,UAAU,CAAC,GAAW;QACpB,IAAI,aAAa,CAAC,GAAG,CAAC;YAAE,OAAO,GAAG,CAAC;QACnC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,OAAO,IAAI,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,CAAC;QAC/C,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;CACF;AAxED,8CAwEC","sourcesContent":["import type { Browser, BrowserContext, Page } from \"playwright\";\n\nimport type { PlaywrightAdapterOptions } from \"./ui.types\";\n\nfunction isAbsoluteUrl(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n}\n\nexport const DEFAULT_UI_TIMEOUT_MS = 30_000;\n\nexport class PlaywrightAdapter {\n private readonly baseUrl?: string;\n private readonly headless: boolean;\n private readonly defaultTimeoutMs: number;\n private readonly navigationTimeoutMs: number;\n private browser?: Browser;\n private context?: BrowserContext;\n public page?: Page;\n\n constructor(opts?: PlaywrightAdapterOptions) {\n this.baseUrl = opts?.baseUrl;\n this.headless = opts?.headless ?? true;\n this.defaultTimeoutMs = opts?.defaultTimeoutMs ?? DEFAULT_UI_TIMEOUT_MS;\n this.navigationTimeoutMs =\n opts?.navigationTimeoutMs ?? this.defaultTimeoutMs;\n }\n\n isHeadless(): boolean {\n return this.headless;\n }\n\n /**\n * Resolves the timeout for a single Playwright call: an explicit per-step\n * value wins, otherwise the run-wide default applies.\n */\n resolveTimeout(stepTimeoutMs?: number): number {\n return typeof stepTimeoutMs === 'number' && Number.isFinite(stepTimeoutMs)\n ? stepTimeoutMs\n : this.defaultTimeoutMs;\n }\n\n resolveNavigationTimeout(stepTimeoutMs?: number): number {\n return typeof stepTimeoutMs === 'number' && Number.isFinite(stepTimeoutMs)\n ? stepTimeoutMs\n : this.navigationTimeoutMs;\n }\n\n async start(): Promise<void> {\n const { chromium } = await import('playwright');\n this.browser = await chromium.launch({ headless: this.headless });\n this.context = await this.browser.newContext();\n this.context.setDefaultTimeout(this.defaultTimeoutMs);\n this.context.setDefaultNavigationTimeout(this.navigationTimeoutMs);\n this.page = await this.context.newPage();\n }\n\n async stop(): Promise<void> {\n try {\n if (this.context) {\n await this.context.close();\n }\n } finally {\n this.context = undefined;\n this.page = undefined;\n }\n\n try {\n if (this.browser) {\n await this.browser.close();\n }\n } finally {\n this.browser = undefined;\n }\n }\n\n resolveUrl(url: string): string {\n if (isAbsoluteUrl(url)) return url;\n if (this.baseUrl) {\n return new URL(url, this.baseUrl).toString();\n }\n return url;\n }\n}\n"]}
1
+ {"version":3,"file":"playwright-adapter.js","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/playwright-adapter.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Browser, BrowserContext, Page, ConsoleMessage, Response, Request } from \"playwright\";\n\nimport type { CaptureOptions, PlaywrightAdapterOptions } from \"./ui.types\";\n\nfunction isAbsoluteUrl(url: string): boolean {\n return /^https?:\\/\\//i.test(url);\n}\n\nexport const DEFAULT_UI_TIMEOUT_MS = 30_000;\n/** Keep the buffers bounded so a chatty page can't grow memory without limit. */\nconst MAX_CONSOLE_ENTRIES = 500;\nconst MAX_NETWORK_ENTRIES = 500;\nconst MAX_NETWORK_BODY_CHARS = 16_000;\nconst STATIC_EXT_RE = /\\.(?:css|js|mjs|cjs|map|png|jpe?g|gif|svg|ico|webp|avif|woff2?|ttf|eot|otf|mp4|webm|mp3|wav|pdf|zip)$/i;\n\nexport interface ConsoleLogEntry {\n type: string;\n text: string;\n location?: string;\n}\n\nexport interface NetworkLogEntry {\n method: string;\n url: string;\n resourceType?: string;\n requestHeaders?: Record<string, string>;\n requestBody?: string;\n status?: number;\n ok?: boolean;\n responseHeaders?: Record<string, string>;\n responseBody?: string;\n durationMs?: number;\n failure?: string;\n}\n\n/** One artifact to hand to the reporter's `attach()` on scenario failure. */\nexport interface FailureArtifact {\n name: string;\n contentType: string;\n data: Buffer | string;\n}\n\nexport class PlaywrightAdapter {\n private readonly baseUrl?: string;\n private readonly headless: boolean;\n private readonly defaultTimeoutMs: number;\n private readonly navigationTimeoutMs: number;\n private readonly capture: CaptureOptions;\n private readonly recordVideo: boolean;\n private browser?: Browser;\n private context?: BrowserContext;\n public page?: Page;\n\n /** Per-scenario ring buffers; reset via {@link resetCapture} at each scenario start. */\n private consoleBuf: ConsoleLogEntry[] = [];\n private networkBuf: NetworkLogEntry[] = [];\n\n /** Whole-run video: temp dir Playwright writes the webm into, and the finalized bytes. */\n private videoDir?: string;\n private runVideo?: FailureArtifact;\n private readonly requestStartedAt = new Map<Request, number>();\n\n constructor(opts?: PlaywrightAdapterOptions) {\n this.baseUrl = opts?.baseUrl;\n this.headless = opts?.headless ?? true;\n this.defaultTimeoutMs = opts?.defaultTimeoutMs ?? DEFAULT_UI_TIMEOUT_MS;\n this.navigationTimeoutMs =\n opts?.navigationTimeoutMs ?? this.defaultTimeoutMs;\n this.capture = opts?.capture ?? {};\n this.recordVideo = opts?.recordVideo ?? true;\n }\n\n isHeadless(): boolean {\n return this.headless;\n }\n\n /**\n * Resolves the timeout for a single Playwright call: an explicit per-step\n * value wins, otherwise the run-wide default applies.\n */\n resolveTimeout(stepTimeoutMs?: number): number {\n return typeof stepTimeoutMs === 'number' && Number.isFinite(stepTimeoutMs)\n ? stepTimeoutMs\n : this.defaultTimeoutMs;\n }\n\n resolveNavigationTimeout(stepTimeoutMs?: number): number {\n return typeof stepTimeoutMs === 'number' && Number.isFinite(stepTimeoutMs)\n ? stepTimeoutMs\n : this.navigationTimeoutMs;\n }\n\n async start(): Promise<void> {\n const { chromium } = await import('playwright');\n this.browser = await chromium.launch({ headless: this.headless });\n // Record a whole-run video only when this run enables it; it is still only KEPT when\n // the run fails (see stop({ keepVideo }) / the runner). The temp dir has to exist\n // before the context is created, since recordVideo is a context-creation option.\n if (this.recordVideo) {\n const os = await import('os');\n const path = await import('path');\n const fs = await import('fs');\n this.videoDir = fs.mkdtempSync(path.join(os.tmpdir(), 'solid-run-video-'));\n }\n this.context = await this.browser.newContext(\n this.videoDir ? { recordVideo: { dir: this.videoDir } } : {},\n );\n this.context.setDefaultTimeout(this.defaultTimeoutMs);\n this.context.setDefaultNavigationTimeout(this.navigationTimeoutMs);\n this.page = await this.context.newPage();\n this.attachCaptureListeners(this.page);\n }\n\n /** Whole-run video, available after {@link stop} has finalized the recording. */\n getRunVideo(): FailureArtifact | undefined {\n return this.runVideo;\n }\n\n /**\n * Wire console/network listeners once per page. Events accumulate in the per-scenario\n * buffers and are only surfaced (via {@link collectFailureArtifacts}) when a scenario\n * fails — passing scenarios reset and discard them.\n */\n private attachCaptureListeners(page: Page): void {\n if (this.capture.console) {\n page.on(\"console\", (msg: ConsoleMessage) => {\n if (this.consoleBuf.length >= MAX_CONSOLE_ENTRIES) return;\n const loc = msg.location();\n this.consoleBuf.push({\n type: msg.type(),\n text: msg.text(),\n location: loc?.url ? `${loc.url}:${loc.lineNumber ?? 0}` : undefined,\n });\n });\n page.on(\"pageerror\", (err: Error) => {\n if (this.consoleBuf.length >= MAX_CONSOLE_ENTRIES) return;\n this.consoleBuf.push({ type: \"pageerror\", text: err?.stack || err?.message || String(err) });\n });\n }\n\n if (this.capture.network) {\n page.on(\"request\", (req: Request) => {\n this.requestStartedAt.set(req, Date.now());\n });\n page.on(\"response\", async (res: Response) => {\n if (this.networkBuf.length >= MAX_NETWORK_ENTRIES) return;\n const req = res.request();\n const captureDetail = shouldCaptureDetailedNetworkEntry(req);\n const requestHeaders = captureDetail ? await req.allHeaders().catch(() => undefined) : undefined;\n const responseHeaders = captureDetail ? await res.allHeaders().catch(() => undefined) : undefined;\n const responseBody = captureDetail ? await this.readResponseBody(res, responseHeaders) : undefined;\n const startedAt = this.requestStartedAt.get(req);\n this.requestStartedAt.delete(req);\n this.networkBuf.push({\n method: req.method(),\n url: res.url(),\n resourceType: req.resourceType(),\n requestHeaders,\n requestBody: captureDetail ? truncateText(req.postData() ?? undefined) : undefined,\n status: res.status(),\n ok: res.ok(),\n responseHeaders,\n responseBody,\n durationMs: startedAt != null ? Date.now() - startedAt : undefined,\n });\n });\n page.on(\"requestfailed\", async (req: Request) => {\n if (this.networkBuf.length >= MAX_NETWORK_ENTRIES) return;\n const startedAt = this.requestStartedAt.get(req);\n this.requestStartedAt.delete(req);\n const captureDetail = shouldCaptureDetailedNetworkEntry(req);\n this.networkBuf.push({\n method: req.method(),\n url: req.url(),\n resourceType: req.resourceType(),\n requestHeaders: captureDetail ? await req.allHeaders().catch(() => undefined) : undefined,\n requestBody: captureDetail ? truncateText(req.postData() ?? undefined) : undefined,\n failure: req.failure()?.errorText ?? \"request failed\",\n durationMs: startedAt != null ? Date.now() - startedAt : undefined,\n });\n });\n }\n }\n\n /** Clear the per-scenario buffers. Called by the engine at each scenario start. */\n resetCapture(): void {\n this.consoleBuf = [];\n this.networkBuf = [];\n this.requestStartedAt.clear();\n }\n\n getConsoleLog(): ConsoleLogEntry[] {\n return this.consoleBuf;\n }\n\n getNetworkLog(): NetworkLogEntry[] {\n return this.networkBuf;\n }\n\n private async readResponseBody(res: Response, headers?: Record<string, string>): Promise<string | undefined> {\n const contentType = String(headers?.[\"content-type\"] ?? headers?.[\"Content-Type\"] ?? \"\").toLowerCase();\n const contentLength = Number(headers?.[\"content-length\"] ?? headers?.[\"Content-Length\"] ?? 0);\n if (contentLength && contentLength > MAX_NETWORK_BODY_CHARS) {\n return `[body omitted: ${contentLength} bytes]`;\n }\n if (!isTextLikeContentType(contentType)) return undefined;\n try {\n return truncateText(await res.text());\n } catch {\n return undefined;\n }\n }\n\n /**\n * Build the text artifacts to attach when the current scenario has FAILED (console +\n * network logs). Honors this adapter's capture flags. Returns an empty list when no page\n * is open (e.g. an API scenario). Failure screenshots were removed — the whole-run video\n * (kept only on failure) is the visual failure artifact now.\n */\n async collectFailureArtifacts(): Promise<FailureArtifact[]> {\n if (!this.page) return [];\n const artifacts: FailureArtifact[] = [];\n\n if (this.capture.console && this.consoleBuf.length) {\n artifacts.push({\n name: \"console.json\",\n contentType: \"application/json\",\n data: JSON.stringify(this.consoleBuf),\n });\n }\n\n if (this.capture.network && this.networkBuf.length) {\n artifacts.push({\n name: \"network.json\",\n contentType: \"application/json\",\n data: JSON.stringify(this.networkBuf),\n });\n }\n\n return artifacts;\n }\n\n async stop(opts?: { keepVideo?: boolean }): Promise<void> {\n const keepVideo = opts?.keepVideo ?? false;\n // Grab the video handle before the context closes; its file is only finalized on close().\n const video = this.videoDir ? this.page?.video() : undefined;\n\n try {\n if (this.context) {\n await this.context.close();\n }\n } finally {\n this.context = undefined;\n this.page = undefined;\n }\n\n if (video && this.videoDir) {\n try {\n const fs = await import('fs');\n // Only read the recording into memory when the caller wants to keep it (i.e. the\n // run FAILED). Passing runs discard the webm unread — no wasted IO/memory.\n if (keepVideo) {\n const videoPath = await video.path();\n const data = await fs.promises.readFile(videoPath);\n this.runVideo = { name: 'run.webm', contentType: 'video/webm', data };\n }\n // Clean up the temp recording directory either way.\n await fs.promises.rm(this.videoDir, { recursive: true, force: true }).catch(() => {});\n } catch {\n // A video read failure must never mask the real run result.\n } finally {\n this.videoDir = undefined;\n }\n }\n\n try {\n if (this.browser) {\n await this.browser.close();\n }\n } finally {\n this.browser = undefined;\n }\n }\n\n resolveUrl(url: string): string {\n if (isAbsoluteUrl(url)) return url;\n if (this.baseUrl) {\n // Treat the configured base as a prefix and keep it whole, matching how\n // the API adapter combines axios `baseURL` with a relative url. Resolving\n // through `new URL()` follows RFC 3986 and discards any path on the base,\n // so an application registered at https://host/myapp would send \"/login\"\n // to https://host/login instead of https://host/myapp/login.\n return `${this.baseUrl.replace(/\\/+$/, \"\")}/${url.replace(/^\\/+/, \"\")}`;\n }\n return url;\n }\n}\n\nfunction truncateText(value?: string): string | undefined {\n if (!value) return undefined;\n if (value.length <= MAX_NETWORK_BODY_CHARS) return value;\n return `${value.slice(0, MAX_NETWORK_BODY_CHARS)}\\n… [truncated ${value.length - MAX_NETWORK_BODY_CHARS} chars]`;\n}\n\nfunction isTextLikeContentType(contentType: string): boolean {\n if (!contentType) return true;\n return (\n contentType.includes(\"json\") ||\n contentType.includes(\"text/\") ||\n contentType.includes(\"javascript\") ||\n contentType.includes(\"xml\") ||\n contentType.includes(\"html\") ||\n contentType.includes(\"x-www-form-urlencoded\")\n );\n}\n\nfunction shouldCaptureDetailedNetworkEntry(req: Request): boolean {\n const resourceType = req.resourceType().toLowerCase();\n if (resourceType === \"xhr\" || resourceType === \"fetch\") return true;\n\n const method = req.method().toUpperCase();\n if (method !== \"GET\" && method !== \"HEAD\") return true;\n\n const url = req.url().toLowerCase();\n try {\n const pathname = new URL(url).pathname.toLowerCase();\n if (STATIC_EXT_RE.test(pathname)) return false;\n } catch {\n if (STATIC_EXT_RE.test(url)) return false;\n }\n\n return resourceType === \"document\";\n}\n"]}
@@ -1,7 +1,13 @@
1
+ export interface CaptureOptions {
2
+ console?: boolean;
3
+ network?: boolean;
4
+ }
1
5
  export interface PlaywrightAdapterOptions {
2
6
  headless?: boolean;
3
7
  baseUrl?: string;
4
8
  defaultTimeoutMs?: number;
5
9
  navigationTimeoutMs?: number;
10
+ capture?: CaptureOptions;
11
+ recordVideo?: boolean;
6
12
  }
7
13
  //# sourceMappingURL=ui.types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ui.types.d.ts","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/ui.types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;CAC9B"}
1
+ {"version":3,"file":"ui.types.d.ts","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/ui.types.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,cAAc;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ui.types.js","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/ui.types.ts"],"names":[],"mappings":"","sourcesContent":["export interface PlaywrightAdapterOptions {\n headless?: boolean;\n baseUrl?: string;\n defaultTimeoutMs?: number;\n navigationTimeoutMs?: number;\n}\n"]}
1
+ {"version":3,"file":"ui.types.js","sourceRoot":"","sources":["../../../../src/testing/adapters/ui/ui.types.ts"],"names":[],"mappings":"","sourcesContent":["/**\n * Per-run capture toggles for the Playwright adapter. Console/network events are\n * buffered while a scenario runs and only flushed (attached to the reporter) when the\n * scenario FAILS; passing scenarios discard them. API scenarios never open a page, so\n * nothing is captured. The whole-run video may be enabled per run and is kept only when\n * the run fails.\n */\nexport interface CaptureOptions {\n console?: boolean;\n network?: boolean;\n}\n\nexport interface PlaywrightAdapterOptions {\n headless?: boolean;\n baseUrl?: string;\n defaultTimeoutMs?: number;\n navigationTimeoutMs?: number;\n capture?: CaptureOptions;\n recordVideo?: boolean;\n}\n"]}
@@ -28,6 +28,8 @@ export interface TestContext {
28
28
  reporter: Reporter;
29
29
  specRegistry?: SpecRegistry;
30
30
  testData?: Record<string, Record<string, any>>;
31
+ env?: Record<string, string>;
32
+ secrets?: Record<string, any>;
31
33
  options?: {
32
34
  printApiLogs?: boolean;
33
35
  };
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-context.types.d.ts","sourceRoot":"","sources":["../../../src/testing/contracts/runtime-context.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,SAAS,EAAE,aAAa,CAAC;IACzB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,WAAW,CAAA;KAAE,CAAC;IACrC,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/C,OAAO,CAAC,EAAE;QACR,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;CACH"}
1
+ {"version":3,"file":"runtime-context.types.d.ts","sourceRoot":"","sources":["../../../src/testing/contracts/runtime-context.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAC;AAEnE,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;IAC3B,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAC;IACxC,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,YAAY,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC5B,SAAS,EAAE,aAAa,CAAC;IACzB,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,EAAE,CAAC,EAAE,iBAAiB,CAAC;IACvB,IAAI,CAAC,EAAE;QAAE,WAAW,CAAC,EAAE,WAAW,CAAA;KAAE,CAAC;IACrC,QAAQ,EAAE,QAAQ,CAAC;IACnB,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;IAC/C,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAK7B,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC9B,OAAO,CAAC,EAAE;QACR,YAAY,CAAC,EAAE,OAAO,CAAC;KACxB,CAAC;CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-context.types.js","sourceRoot":"","sources":["../../../src/testing/contracts/runtime-context.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ApiAdapter } from \"../adapters/api/api-adapter\";\nimport type { PlaywrightAdapter } from \"../adapters/ui/playwright-adapter\";\nimport type { Reporter } from \"../reporter/reporter.types\";\nimport type { SpecRegistry } from \"../core/spec-registry\";\nimport type { ScenarioType } from \"./testing-metadata.types\";\n\nexport type StepPhase = \"given\" | \"when\" | \"then\" | \"and\" | \"step\";\n\nexport interface ResourceStore {\n get(path: string): unknown;\n set(path: string, value: unknown): void;\n has(path: string): boolean;\n}\n\nexport interface ApiResponse {\n status: number;\n headers: Record<string, string>;\n bodyText: string;\n bodyJson?: unknown;\n}\n\nexport interface TestContext {\n scenarioId: string;\n scenarioType: ScenarioType;\n params: Record<string, any>;\n resources: ResourceStore;\n api?: ApiAdapter;\n ui?: PlaywrightAdapter;\n last?: { apiResponse?: ApiResponse };\n reporter: Reporter;\n specRegistry?: SpecRegistry;\n testData?: Record<string, Record<string, any>>;\n options?: {\n printApiLogs?: boolean;\n };\n}\n"]}
1
+ {"version":3,"file":"runtime-context.types.js","sourceRoot":"","sources":["../../../src/testing/contracts/runtime-context.types.ts"],"names":[],"mappings":"","sourcesContent":["import type { ApiAdapter } from \"../adapters/api/api-adapter\";\nimport type { PlaywrightAdapter } from \"../adapters/ui/playwright-adapter\";\nimport type { Reporter } from \"../reporter/reporter.types\";\nimport type { SpecRegistry } from \"../core/spec-registry\";\nimport type { ScenarioType } from \"./testing-metadata.types\";\n\nexport type StepPhase = \"given\" | \"when\" | \"then\" | \"and\" | \"step\";\n\nexport interface ResourceStore {\n get(path: string): unknown;\n set(path: string, value: unknown): void;\n has(path: string): boolean;\n}\n\nexport interface ApiResponse {\n status: number;\n headers: Record<string, string>;\n bodyText: string;\n bodyJson?: unknown;\n}\n\nexport interface TestContext {\n scenarioId: string;\n scenarioType: ScenarioType;\n params: Record<string, any>;\n resources: ResourceStore;\n api?: ApiAdapter;\n ui?: PlaywrightAdapter;\n last?: { apiResponse?: ApiResponse };\n reporter: Reporter;\n specRegistry?: SpecRegistry;\n testData?: Record<string, Record<string, any>>;\n env?: Record<string, string>;\n /**\n * Secrets referenced by this scenario, resolved server-side before the run.\n * Scoped per scenario — a scenario never sees another scenario's secrets.\n */\n secrets?: Record<string, any>;\n options?: {\n printApiLogs?: boolean;\n };\n}\n"]}
@@ -1,4 +1,5 @@
1
1
  import type { TestContext } from "../contracts/runtime-context.types";
2
+ export declare function collectSecretKeys(value: unknown): string[];
2
3
  export declare function interpolateString(input: string, ctx: TestContext): string;
3
4
  export declare function interpolateDeep<T>(input: T, ctx: TestContext): T;
4
5
  //# sourceMappingURL=interpolation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"interpolation.d.ts","sourceRoot":"","sources":["../../../src/testing/core/interpolation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AA0JtE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CAOzE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,CAAC,CAyBhE"}
1
+ {"version":3,"file":"interpolation.d.ts","sourceRoot":"","sources":["../../../src/testing/core/interpolation.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAuCtE,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,EAAE,CAU1D;AA2ED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,MAAM,CAOzE;AAED,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,EAAE,WAAW,GAAG,CAAC,CAyBhE"}