@elench/testkit 0.1.65 → 0.1.66

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 (305) hide show
  1. package/lib/app/browser-bridge.mjs +66 -0
  2. package/lib/app/configs.mjs +81 -0
  3. package/lib/app/configs.test.mjs +34 -0
  4. package/lib/cli/command-helpers.mjs +2 -10
  5. package/lib/cli/commands/browser/serve.mjs +3 -62
  6. package/lib/cli/db.mjs +3 -68
  7. package/lib/config/binaries.mjs +34 -0
  8. package/lib/config/database.mjs +9 -6
  9. package/lib/config/index.mjs +2 -31
  10. package/lib/config/runtime.mjs +24 -95
  11. package/lib/config/validation.mjs +18 -62
  12. package/lib/coverage/backend-discovery.mjs +68 -85
  13. package/lib/coverage/backend-discovery.test.mjs +55 -46
  14. package/lib/coverage/graph-builder.mjs +5 -5
  15. package/lib/coverage/next-ir-to-graph.mjs +0 -1
  16. package/lib/coverage/routing.mjs +2 -29
  17. package/lib/coverage/routing.test.mjs +0 -16
  18. package/lib/coverage/shared.mjs +22 -82
  19. package/lib/database/fingerprint.mjs +1 -1
  20. package/lib/known-failures/github-cache.mjs +159 -0
  21. package/lib/known-failures/github-transport.mjs +174 -0
  22. package/lib/known-failures/github.mjs +17 -325
  23. package/lib/runner/default-runtime-runner.mjs +4 -10
  24. package/lib/runner/execution-config.mjs +12 -83
  25. package/lib/runner/live-run.mjs +45 -0
  26. package/lib/runner/managed-processes.mjs +29 -0
  27. package/lib/runner/orchestrator.mjs +57 -188
  28. package/lib/runner/playwright-runner.mjs +4 -11
  29. package/lib/runner/run-finalization.mjs +132 -0
  30. package/lib/runner/run-guards.mjs +45 -0
  31. package/lib/runner/runtime-preparation.mjs +1 -1
  32. package/lib/runner/services.mjs +3 -4
  33. package/lib/runner/template-steps.mjs +8 -45
  34. package/lib/runner/template.mjs +7 -28
  35. package/lib/shared/configured-steps.mjs +178 -0
  36. package/lib/shared/configured-steps.test.mjs +73 -0
  37. package/lib/shared/execution-schema.mjs +74 -0
  38. package/lib/shared/execution-schema.test.mjs +26 -0
  39. package/node_modules/@elench/next-analysis/dist/api-routes.d.ts +7 -0
  40. package/node_modules/@elench/next-analysis/dist/api-routes.d.ts.map +1 -0
  41. package/node_modules/@elench/next-analysis/dist/api-routes.js +66 -0
  42. package/node_modules/@elench/next-analysis/dist/api-routes.js.map +1 -0
  43. package/node_modules/@elench/next-analysis/dist/app-root.d.ts +2 -0
  44. package/node_modules/@elench/next-analysis/dist/app-root.d.ts.map +1 -0
  45. package/node_modules/@elench/next-analysis/dist/app-root.js +7 -0
  46. package/node_modules/@elench/next-analysis/dist/app-root.js.map +1 -0
  47. package/node_modules/@elench/next-analysis/dist/backend-links.d.ts +8 -0
  48. package/node_modules/@elench/next-analysis/dist/backend-links.d.ts.map +1 -0
  49. package/node_modules/@elench/next-analysis/dist/backend-links.js +30 -0
  50. package/node_modules/@elench/next-analysis/dist/backend-links.js.map +1 -0
  51. package/node_modules/@elench/next-analysis/dist/index.d.ts +11 -0
  52. package/node_modules/@elench/next-analysis/dist/index.d.ts.map +1 -0
  53. package/node_modules/@elench/next-analysis/dist/index.js +10 -0
  54. package/node_modules/@elench/next-analysis/dist/index.js.map +1 -0
  55. package/node_modules/@elench/next-analysis/dist/pages.d.ts +7 -0
  56. package/node_modules/@elench/next-analysis/dist/pages.d.ts.map +1 -0
  57. package/node_modules/@elench/next-analysis/dist/pages.js +47 -0
  58. package/node_modules/@elench/next-analysis/dist/pages.js.map +1 -0
  59. package/node_modules/@elench/next-analysis/dist/project.d.ts +3 -0
  60. package/node_modules/@elench/next-analysis/dist/project.d.ts.map +1 -0
  61. package/node_modules/@elench/next-analysis/dist/project.js +102 -0
  62. package/node_modules/@elench/next-analysis/dist/project.js.map +1 -0
  63. package/node_modules/@elench/next-analysis/dist/route-tree.d.ts +7 -0
  64. package/node_modules/@elench/next-analysis/dist/route-tree.d.ts.map +1 -0
  65. package/node_modules/@elench/next-analysis/dist/route-tree.js +575 -0
  66. package/node_modules/@elench/next-analysis/dist/route-tree.js.map +1 -0
  67. package/node_modules/@elench/next-analysis/dist/routes.d.ts +6 -0
  68. package/node_modules/@elench/next-analysis/dist/routes.d.ts.map +1 -0
  69. package/node_modules/@elench/next-analysis/dist/routes.js +41 -0
  70. package/node_modules/@elench/next-analysis/dist/routes.js.map +1 -0
  71. package/node_modules/@elench/next-analysis/dist/server-actions.d.ts +7 -0
  72. package/node_modules/@elench/next-analysis/dist/server-actions.d.ts.map +1 -0
  73. package/node_modules/@elench/next-analysis/dist/server-actions.js +37 -0
  74. package/node_modules/@elench/next-analysis/dist/server-actions.js.map +1 -0
  75. package/node_modules/@elench/next-analysis/dist/shared.d.ts +57 -0
  76. package/node_modules/@elench/next-analysis/dist/shared.d.ts.map +1 -0
  77. package/node_modules/@elench/next-analysis/dist/shared.js +229 -0
  78. package/node_modules/@elench/next-analysis/dist/shared.js.map +1 -0
  79. package/node_modules/@elench/next-analysis/dist/swc.d.ts +53 -0
  80. package/node_modules/@elench/next-analysis/dist/swc.d.ts.map +1 -0
  81. package/node_modules/@elench/next-analysis/dist/swc.js +387 -0
  82. package/node_modules/@elench/next-analysis/dist/swc.js.map +1 -0
  83. package/node_modules/@elench/next-analysis/dist/types.d.ts +125 -0
  84. package/node_modules/@elench/next-analysis/dist/types.d.ts.map +1 -0
  85. package/node_modules/@elench/next-analysis/dist/types.js +2 -0
  86. package/node_modules/@elench/next-analysis/dist/types.js.map +1 -0
  87. package/node_modules/@elench/next-analysis/package.json +15 -2
  88. package/node_modules/@elench/testkit-bridge/dist/index.d.ts +36 -0
  89. package/node_modules/@elench/testkit-bridge/dist/index.d.ts.map +1 -0
  90. package/node_modules/@elench/testkit-bridge/dist/index.js +538 -0
  91. package/node_modules/@elench/testkit-bridge/dist/index.js.map +1 -0
  92. package/node_modules/@elench/testkit-bridge/package.json +16 -5
  93. package/node_modules/@elench/testkit-protocol/dist/index.d.ts +190 -0
  94. package/node_modules/@elench/testkit-protocol/dist/index.d.ts.map +1 -0
  95. package/node_modules/@elench/testkit-protocol/dist/index.js +296 -0
  96. package/node_modules/@elench/testkit-protocol/dist/index.js.map +1 -0
  97. package/node_modules/@elench/testkit-protocol/package.json +14 -7
  98. package/node_modules/@elench/ts-analysis/dist/callables.d.ts +8 -0
  99. package/node_modules/@elench/ts-analysis/dist/callables.d.ts.map +1 -0
  100. package/node_modules/@elench/ts-analysis/dist/callables.js +126 -0
  101. package/node_modules/@elench/ts-analysis/dist/callables.js.map +1 -0
  102. package/node_modules/@elench/ts-analysis/dist/exports.d.ts +6 -0
  103. package/node_modules/@elench/ts-analysis/dist/exports.d.ts.map +1 -0
  104. package/node_modules/@elench/ts-analysis/dist/exports.js +70 -0
  105. package/node_modules/@elench/ts-analysis/dist/exports.js.map +1 -0
  106. package/node_modules/@elench/ts-analysis/dist/index.d.ts +10 -0
  107. package/node_modules/@elench/ts-analysis/dist/index.d.ts.map +1 -0
  108. package/node_modules/@elench/ts-analysis/{src/index.mjs → dist/index.js} +9 -14
  109. package/node_modules/@elench/ts-analysis/dist/index.js.map +1 -0
  110. package/node_modules/@elench/ts-analysis/dist/jsx.d.ts +9 -0
  111. package/node_modules/@elench/ts-analysis/dist/jsx.d.ts.map +1 -0
  112. package/node_modules/@elench/ts-analysis/dist/jsx.js +68 -0
  113. package/node_modules/@elench/ts-analysis/dist/jsx.js.map +1 -0
  114. package/node_modules/@elench/ts-analysis/dist/project.d.ts +5 -0
  115. package/node_modules/@elench/ts-analysis/dist/project.d.ts.map +1 -0
  116. package/node_modules/@elench/ts-analysis/dist/project.js +90 -0
  117. package/node_modules/@elench/ts-analysis/dist/project.js.map +1 -0
  118. package/node_modules/@elench/ts-analysis/dist/requests.d.ts +6 -0
  119. package/node_modules/@elench/ts-analysis/dist/requests.d.ts.map +1 -0
  120. package/node_modules/@elench/ts-analysis/dist/requests.js +140 -0
  121. package/node_modules/@elench/ts-analysis/dist/requests.js.map +1 -0
  122. package/node_modules/@elench/ts-analysis/dist/resolution.d.ts +4 -0
  123. package/node_modules/@elench/ts-analysis/dist/resolution.d.ts.map +1 -0
  124. package/node_modules/@elench/ts-analysis/dist/resolution.js +53 -0
  125. package/node_modules/@elench/ts-analysis/dist/resolution.js.map +1 -0
  126. package/node_modules/@elench/ts-analysis/dist/shared.d.ts +6 -0
  127. package/node_modules/@elench/ts-analysis/dist/shared.d.ts.map +1 -0
  128. package/node_modules/@elench/ts-analysis/dist/shared.js +31 -0
  129. package/node_modules/@elench/ts-analysis/dist/shared.js.map +1 -0
  130. package/node_modules/@elench/ts-analysis/dist/syntax.d.ts +7 -0
  131. package/node_modules/@elench/ts-analysis/dist/syntax.d.ts.map +1 -0
  132. package/node_modules/@elench/ts-analysis/dist/syntax.js +27 -0
  133. package/node_modules/@elench/ts-analysis/dist/syntax.js.map +1 -0
  134. package/node_modules/@elench/ts-analysis/dist/types.d.ts +58 -0
  135. package/node_modules/@elench/ts-analysis/dist/types.d.ts.map +1 -0
  136. package/node_modules/@elench/ts-analysis/dist/types.js +2 -0
  137. package/node_modules/@elench/ts-analysis/dist/types.js.map +1 -0
  138. package/node_modules/@elench/ts-analysis/package.json +18 -2
  139. package/node_modules/typescript/LICENSE.txt +55 -0
  140. package/node_modules/typescript/README.md +50 -0
  141. package/node_modules/typescript/SECURITY.md +41 -0
  142. package/node_modules/typescript/ThirdPartyNoticeText.txt +193 -0
  143. package/node_modules/typescript/bin/tsc +2 -0
  144. package/node_modules/typescript/bin/tsserver +2 -0
  145. package/node_modules/typescript/lib/_tsc.js +133818 -0
  146. package/node_modules/typescript/lib/_tsserver.js +659 -0
  147. package/node_modules/typescript/lib/_typingsInstaller.js +222 -0
  148. package/node_modules/typescript/lib/cs/diagnosticMessages.generated.json +2122 -0
  149. package/node_modules/typescript/lib/de/diagnosticMessages.generated.json +2122 -0
  150. package/node_modules/typescript/lib/es/diagnosticMessages.generated.json +2122 -0
  151. package/node_modules/typescript/lib/fr/diagnosticMessages.generated.json +2122 -0
  152. package/node_modules/typescript/lib/it/diagnosticMessages.generated.json +2122 -0
  153. package/node_modules/typescript/lib/ja/diagnosticMessages.generated.json +2122 -0
  154. package/node_modules/typescript/lib/ko/diagnosticMessages.generated.json +2122 -0
  155. package/node_modules/typescript/lib/lib.d.ts +22 -0
  156. package/node_modules/typescript/lib/lib.decorators.d.ts +384 -0
  157. package/node_modules/typescript/lib/lib.decorators.legacy.d.ts +22 -0
  158. package/node_modules/typescript/lib/lib.dom.asynciterable.d.ts +41 -0
  159. package/node_modules/typescript/lib/lib.dom.d.ts +39429 -0
  160. package/node_modules/typescript/lib/lib.dom.iterable.d.ts +571 -0
  161. package/node_modules/typescript/lib/lib.es2015.collection.d.ts +147 -0
  162. package/node_modules/typescript/lib/lib.es2015.core.d.ts +597 -0
  163. package/node_modules/typescript/lib/lib.es2015.d.ts +28 -0
  164. package/node_modules/typescript/lib/lib.es2015.generator.d.ts +77 -0
  165. package/node_modules/typescript/lib/lib.es2015.iterable.d.ts +605 -0
  166. package/node_modules/typescript/lib/lib.es2015.promise.d.ts +81 -0
  167. package/node_modules/typescript/lib/lib.es2015.proxy.d.ts +128 -0
  168. package/node_modules/typescript/lib/lib.es2015.reflect.d.ts +144 -0
  169. package/node_modules/typescript/lib/lib.es2015.symbol.d.ts +46 -0
  170. package/node_modules/typescript/lib/lib.es2015.symbol.wellknown.d.ts +326 -0
  171. package/node_modules/typescript/lib/lib.es2016.array.include.d.ts +116 -0
  172. package/node_modules/typescript/lib/lib.es2016.d.ts +21 -0
  173. package/node_modules/typescript/lib/lib.es2016.full.d.ts +23 -0
  174. package/node_modules/typescript/lib/lib.es2016.intl.d.ts +31 -0
  175. package/node_modules/typescript/lib/lib.es2017.arraybuffer.d.ts +21 -0
  176. package/node_modules/typescript/lib/lib.es2017.d.ts +26 -0
  177. package/node_modules/typescript/lib/lib.es2017.date.d.ts +31 -0
  178. package/node_modules/typescript/lib/lib.es2017.full.d.ts +23 -0
  179. package/node_modules/typescript/lib/lib.es2017.intl.d.ts +44 -0
  180. package/node_modules/typescript/lib/lib.es2017.object.d.ts +49 -0
  181. package/node_modules/typescript/lib/lib.es2017.sharedmemory.d.ts +135 -0
  182. package/node_modules/typescript/lib/lib.es2017.string.d.ts +45 -0
  183. package/node_modules/typescript/lib/lib.es2017.typedarrays.d.ts +53 -0
  184. package/node_modules/typescript/lib/lib.es2018.asyncgenerator.d.ts +77 -0
  185. package/node_modules/typescript/lib/lib.es2018.asynciterable.d.ts +53 -0
  186. package/node_modules/typescript/lib/lib.es2018.d.ts +24 -0
  187. package/node_modules/typescript/lib/lib.es2018.full.d.ts +24 -0
  188. package/node_modules/typescript/lib/lib.es2018.intl.d.ts +83 -0
  189. package/node_modules/typescript/lib/lib.es2018.promise.d.ts +30 -0
  190. package/node_modules/typescript/lib/lib.es2018.regexp.d.ts +37 -0
  191. package/node_modules/typescript/lib/lib.es2019.array.d.ts +79 -0
  192. package/node_modules/typescript/lib/lib.es2019.d.ts +24 -0
  193. package/node_modules/typescript/lib/lib.es2019.full.d.ts +24 -0
  194. package/node_modules/typescript/lib/lib.es2019.intl.d.ts +23 -0
  195. package/node_modules/typescript/lib/lib.es2019.object.d.ts +33 -0
  196. package/node_modules/typescript/lib/lib.es2019.string.d.ts +37 -0
  197. package/node_modules/typescript/lib/lib.es2019.symbol.d.ts +24 -0
  198. package/node_modules/typescript/lib/lib.es2020.bigint.d.ts +765 -0
  199. package/node_modules/typescript/lib/lib.es2020.d.ts +27 -0
  200. package/node_modules/typescript/lib/lib.es2020.date.d.ts +42 -0
  201. package/node_modules/typescript/lib/lib.es2020.full.d.ts +24 -0
  202. package/node_modules/typescript/lib/lib.es2020.intl.d.ts +474 -0
  203. package/node_modules/typescript/lib/lib.es2020.number.d.ts +28 -0
  204. package/node_modules/typescript/lib/lib.es2020.promise.d.ts +47 -0
  205. package/node_modules/typescript/lib/lib.es2020.sharedmemory.d.ts +99 -0
  206. package/node_modules/typescript/lib/lib.es2020.string.d.ts +44 -0
  207. package/node_modules/typescript/lib/lib.es2020.symbol.wellknown.d.ts +41 -0
  208. package/node_modules/typescript/lib/lib.es2021.d.ts +23 -0
  209. package/node_modules/typescript/lib/lib.es2021.full.d.ts +24 -0
  210. package/node_modules/typescript/lib/lib.es2021.intl.d.ts +166 -0
  211. package/node_modules/typescript/lib/lib.es2021.promise.d.ts +48 -0
  212. package/node_modules/typescript/lib/lib.es2021.string.d.ts +33 -0
  213. package/node_modules/typescript/lib/lib.es2021.weakref.d.ts +78 -0
  214. package/node_modules/typescript/lib/lib.es2022.array.d.ts +121 -0
  215. package/node_modules/typescript/lib/lib.es2022.d.ts +25 -0
  216. package/node_modules/typescript/lib/lib.es2022.error.d.ts +75 -0
  217. package/node_modules/typescript/lib/lib.es2022.full.d.ts +24 -0
  218. package/node_modules/typescript/lib/lib.es2022.intl.d.ts +145 -0
  219. package/node_modules/typescript/lib/lib.es2022.object.d.ts +26 -0
  220. package/node_modules/typescript/lib/lib.es2022.regexp.d.ts +39 -0
  221. package/node_modules/typescript/lib/lib.es2022.string.d.ts +25 -0
  222. package/node_modules/typescript/lib/lib.es2023.array.d.ts +924 -0
  223. package/node_modules/typescript/lib/lib.es2023.collection.d.ts +21 -0
  224. package/node_modules/typescript/lib/lib.es2023.d.ts +22 -0
  225. package/node_modules/typescript/lib/lib.es2023.full.d.ts +24 -0
  226. package/node_modules/typescript/lib/lib.es2023.intl.d.ts +56 -0
  227. package/node_modules/typescript/lib/lib.es2024.arraybuffer.d.ts +65 -0
  228. package/node_modules/typescript/lib/lib.es2024.collection.d.ts +29 -0
  229. package/node_modules/typescript/lib/lib.es2024.d.ts +26 -0
  230. package/node_modules/typescript/lib/lib.es2024.full.d.ts +24 -0
  231. package/node_modules/typescript/lib/lib.es2024.object.d.ts +29 -0
  232. package/node_modules/typescript/lib/lib.es2024.promise.d.ts +35 -0
  233. package/node_modules/typescript/lib/lib.es2024.regexp.d.ts +25 -0
  234. package/node_modules/typescript/lib/lib.es2024.sharedmemory.d.ts +68 -0
  235. package/node_modules/typescript/lib/lib.es2024.string.d.ts +29 -0
  236. package/node_modules/typescript/lib/lib.es5.d.ts +4601 -0
  237. package/node_modules/typescript/lib/lib.es6.d.ts +23 -0
  238. package/node_modules/typescript/lib/lib.esnext.array.d.ts +35 -0
  239. package/node_modules/typescript/lib/lib.esnext.collection.d.ts +96 -0
  240. package/node_modules/typescript/lib/lib.esnext.d.ts +29 -0
  241. package/node_modules/typescript/lib/lib.esnext.decorators.d.ts +28 -0
  242. package/node_modules/typescript/lib/lib.esnext.disposable.d.ts +193 -0
  243. package/node_modules/typescript/lib/lib.esnext.error.d.ts +24 -0
  244. package/node_modules/typescript/lib/lib.esnext.float16.d.ts +445 -0
  245. package/node_modules/typescript/lib/lib.esnext.full.d.ts +24 -0
  246. package/node_modules/typescript/lib/lib.esnext.intl.d.ts +21 -0
  247. package/node_modules/typescript/lib/lib.esnext.iterator.d.ts +148 -0
  248. package/node_modules/typescript/lib/lib.esnext.promise.d.ts +34 -0
  249. package/node_modules/typescript/lib/lib.esnext.sharedmemory.d.ts +25 -0
  250. package/node_modules/typescript/lib/lib.scripthost.d.ts +322 -0
  251. package/node_modules/typescript/lib/lib.webworker.asynciterable.d.ts +41 -0
  252. package/node_modules/typescript/lib/lib.webworker.d.ts +13150 -0
  253. package/node_modules/typescript/lib/lib.webworker.importscripts.d.ts +23 -0
  254. package/node_modules/typescript/lib/lib.webworker.iterable.d.ts +340 -0
  255. package/node_modules/typescript/lib/pl/diagnosticMessages.generated.json +2122 -0
  256. package/node_modules/typescript/lib/pt-br/diagnosticMessages.generated.json +2122 -0
  257. package/node_modules/typescript/lib/ru/diagnosticMessages.generated.json +2122 -0
  258. package/node_modules/typescript/lib/tr/diagnosticMessages.generated.json +2122 -0
  259. package/node_modules/typescript/lib/tsc.js +8 -0
  260. package/node_modules/typescript/lib/tsserver.js +8 -0
  261. package/node_modules/typescript/lib/tsserverlibrary.d.ts +17 -0
  262. package/node_modules/typescript/lib/tsserverlibrary.js +21 -0
  263. package/node_modules/typescript/lib/typesMap.json +497 -0
  264. package/node_modules/typescript/lib/typescript.d.ts +11437 -0
  265. package/node_modules/typescript/lib/typescript.js +200276 -0
  266. package/node_modules/typescript/lib/typingsInstaller.js +8 -0
  267. package/node_modules/typescript/lib/watchGuard.js +53 -0
  268. package/node_modules/typescript/lib/zh-cn/diagnosticMessages.generated.json +2122 -0
  269. package/node_modules/typescript/lib/zh-tw/diagnosticMessages.generated.json +2122 -0
  270. package/node_modules/typescript/package.json +120 -0
  271. package/package.json +12 -9
  272. package/lib/coverage/fs-walk.mjs +0 -64
  273. package/node_modules/@elench/next-analysis/src/api-routes.mjs +0 -81
  274. package/node_modules/@elench/next-analysis/src/api-routes.test.mjs +0 -22
  275. package/node_modules/@elench/next-analysis/src/app-root.mjs +0 -7
  276. package/node_modules/@elench/next-analysis/src/backend-links.mjs +0 -31
  277. package/node_modules/@elench/next-analysis/src/index.mjs +0 -21
  278. package/node_modules/@elench/next-analysis/src/pages.mjs +0 -68
  279. package/node_modules/@elench/next-analysis/src/project.mjs +0 -94
  280. package/node_modules/@elench/next-analysis/src/project.test.mjs +0 -35
  281. package/node_modules/@elench/next-analysis/src/route-tree.mjs +0 -621
  282. package/node_modules/@elench/next-analysis/src/routes.mjs +0 -41
  283. package/node_modules/@elench/next-analysis/src/routes.test.mjs +0 -25
  284. package/node_modules/@elench/next-analysis/src/server-actions.mjs +0 -53
  285. package/node_modules/@elench/next-analysis/src/server-actions.test.mjs +0 -37
  286. package/node_modules/@elench/next-analysis/src/shared.mjs +0 -209
  287. package/node_modules/@elench/next-analysis/src/swc.mjs +0 -388
  288. package/node_modules/@elench/testkit-bridge/src/index.mjs +0 -583
  289. package/node_modules/@elench/testkit-bridge/src/index.test.mjs +0 -409
  290. package/node_modules/@elench/testkit-protocol/src/index.d.ts +0 -231
  291. package/node_modules/@elench/testkit-protocol/src/index.mjs +0 -265
  292. package/node_modules/@elench/testkit-protocol/src/index.test.mjs +0 -242
  293. package/node_modules/@elench/ts-analysis/src/callables.mjs +0 -135
  294. package/node_modules/@elench/ts-analysis/src/callables.test.mjs +0 -55
  295. package/node_modules/@elench/ts-analysis/src/exports.mjs +0 -69
  296. package/node_modules/@elench/ts-analysis/src/exports.test.mjs +0 -50
  297. package/node_modules/@elench/ts-analysis/src/jsx.mjs +0 -69
  298. package/node_modules/@elench/ts-analysis/src/jsx.test.mjs +0 -43
  299. package/node_modules/@elench/ts-analysis/src/project.mjs +0 -100
  300. package/node_modules/@elench/ts-analysis/src/project.test.mjs +0 -54
  301. package/node_modules/@elench/ts-analysis/src/requests.mjs +0 -141
  302. package/node_modules/@elench/ts-analysis/src/requests.test.mjs +0 -35
  303. package/node_modules/@elench/ts-analysis/src/resolution.mjs +0 -53
  304. package/node_modules/@elench/ts-analysis/src/shared.mjs +0 -32
  305. package/node_modules/@elench/ts-analysis/src/syntax.mjs +0 -27
@@ -1,583 +0,0 @@
1
- import { createRequire } from "module";
2
- import http from "http";
3
- import {
4
- TESTKIT_BROWSER_PROTOCOL_VERSION,
5
- createBridgeErrorResponse,
6
- } from "@elench/testkit-protocol";
7
-
8
- const require = createRequire(import.meta.url);
9
- const BRIDGE_PACKAGE_VERSION = require("../package.json").version;
10
-
11
- export function createBrowserBridge(adapter) {
12
- if (!adapter || typeof adapter.loadProductContext !== "function") {
13
- throw new Error("createBrowserBridge(adapter) requires a loadProductContext function");
14
- }
15
-
16
- return {
17
- async handleRequest(request, response) {
18
- try {
19
- const url = new URL(request.url || "/", "http://127.0.0.1");
20
- const context = await adapter.loadProductContext();
21
-
22
- if (request.method === "OPTIONS") {
23
- response.statusCode = 204;
24
- response.setHeader("access-control-allow-origin", "*");
25
- response.setHeader("access-control-allow-methods", "GET, OPTIONS");
26
- response.setHeader("access-control-allow-headers", "content-type");
27
- response.end();
28
- return;
29
- }
30
-
31
- if (request.method !== "GET") {
32
- return writeJson(response, 405, createBridgeErrorResponse("method_not_allowed", "Only GET is supported"));
33
- }
34
-
35
- if (url.pathname === "/health") {
36
- return writeJson(response, 200, buildHealthResponse(context));
37
- }
38
-
39
- if (url.pathname === "/match") {
40
- const pageUrl = url.searchParams.get("url");
41
- if (!pageUrl) {
42
- return writeJson(response, 400, createBridgeErrorResponse("missing_url", 'Expected query parameter "url"'));
43
- }
44
- return writeJson(response, 200, buildMatchResponse(context, pageUrl));
45
- }
46
-
47
- if (url.pathname === "/page" || url.pathname === "/failures" || url.pathname === "/coverage") {
48
- const pageUrl = url.searchParams.get("url");
49
- if (!pageUrl) {
50
- return writeJson(response, 400, createBridgeErrorResponse("missing_url", 'Expected query parameter "url"'));
51
- }
52
- const payload = buildPageOverlayResponse(context, pageUrl);
53
- if (url.pathname === "/failures") {
54
- return writeJson(response, 200, {
55
- protocolVersion: payload.protocolVersion,
56
- page: payload.page,
57
- match: payload.match,
58
- run: payload.run,
59
- summary: payload.summary,
60
- failures: payload.failures,
61
- });
62
- }
63
- if (url.pathname === "/coverage") {
64
- return writeJson(response, 200, {
65
- protocolVersion: payload.protocolVersion,
66
- page: payload.page,
67
- match: payload.match,
68
- run: payload.run,
69
- summary: payload.summary,
70
- coverage: payload.coverage,
71
- });
72
- }
73
- return writeJson(response, 200, payload);
74
- }
75
-
76
- return writeJson(response, 404, createBridgeErrorResponse("not_found", `No bridge endpoint for ${url.pathname}`));
77
- } catch (error) {
78
- return writeJson(
79
- response,
80
- 500,
81
- createBridgeErrorResponse("internal_error", error instanceof Error ? error.message : String(error))
82
- );
83
- }
84
- },
85
- };
86
- }
87
-
88
- export function startBrowserBridgeServer(adapter, options = {}) {
89
- const bridge = createBrowserBridge(adapter);
90
- const host = options.host || "127.0.0.1";
91
- const port = Number(options.port || 3847);
92
- const server = http.createServer((request, response) => {
93
- bridge.handleRequest(request, response);
94
- });
95
-
96
- return new Promise((resolve, reject) => {
97
- server.once("error", reject);
98
- server.listen(port, host, () => {
99
- server.removeListener("error", reject);
100
- resolve({
101
- server,
102
- host,
103
- port,
104
- url: `http://${host}:${port}`,
105
- });
106
- });
107
- });
108
- }
109
-
110
- export function buildHealthResponse(context) {
111
- return {
112
- protocolVersion: TESTKIT_BROWSER_PROTOCOL_VERSION,
113
- bridgeVersion: BRIDGE_PACKAGE_VERSION,
114
- status: "ok",
115
- product: context.product,
116
- };
117
- }
118
-
119
- export function buildMatchResponse(context, pageUrl) {
120
- const page = normalizePage(pageUrl);
121
- const service = matchServiceForPage(context.services || [], page);
122
-
123
- return {
124
- protocolVersion: TESTKIT_BROWSER_PROTOCOL_VERSION,
125
- url: page.url,
126
- origin: page.origin,
127
- route: page.route,
128
- matched: Boolean(service),
129
- product: context.product,
130
- service: service
131
- ? {
132
- name: service.name,
133
- baseUrl: service.baseUrl,
134
- origin: service.origin,
135
- }
136
- : null,
137
- };
138
- }
139
-
140
- export function buildPageOverlayResponse(context, pageUrl) {
141
- const page = normalizePage(pageUrl);
142
- const match = buildMatchResponse(context, page.url);
143
- const projection = buildGraphProjection(context, page, match.service?.name || null);
144
- const relatedCoverageCount = projection.coverage.length;
145
- const relatedFailureCount = projection.failures.length;
146
- const coverageBreakdown = buildCoverageBreakdown(projection.coverage);
147
-
148
- return {
149
- protocolVersion: TESTKIT_BROWSER_PROTOCOL_VERSION,
150
- page,
151
- match,
152
- run: {
153
- artifactAvailable: Boolean(context.runArtifact),
154
- generatedAt: context.runArtifact?.generatedAt || null,
155
- status: context.runArtifact?.run?.status || null,
156
- },
157
- summary: {
158
- failureState: context.runArtifact
159
- ? relatedFailureCount > 0
160
- ? "failing"
161
- : "healthy"
162
- : "unavailable",
163
- coverageState: relatedCoverageCount > 0 ? "covered" : "missing",
164
- relatedFailureCount,
165
- relatedCoverageCount,
166
- coverageBreakdown,
167
- },
168
- failures: projection.failures,
169
- coverage: projection.coverage,
170
- };
171
- }
172
-
173
- function collectFailedFiles(runArtifact) {
174
- if (!runArtifact || !Array.isArray(runArtifact.services)) return [];
175
- return runArtifact.services.flatMap((service) =>
176
- (service.suites || []).flatMap((suite) =>
177
- (suite.files || [])
178
- .filter((file) => file.status === "failed")
179
- .map((file) => ({
180
- service: service.name,
181
- suite: suite.name,
182
- type: suite.type,
183
- framework: suite.framework,
184
- filePath: file.path,
185
- error: file.error || null,
186
- failureDetails: Array.isArray(file.failureDetails) ? file.failureDetails : [],
187
- }))
188
- )
189
- );
190
- }
191
-
192
- function matchServiceForPage(services, page) {
193
- return [...services]
194
- .filter((service) => matchesServiceBaseUrl(service.baseUrl, page))
195
- .sort((left, right) => normalizeBaseUrl(right.baseUrl).length - normalizeBaseUrl(left.baseUrl).length)[0] || null;
196
- }
197
-
198
- function matchesServiceBaseUrl(baseUrl, page) {
199
- const normalized = normalizeBaseUrl(baseUrl);
200
- if (!normalized) return false;
201
- if (!page.url.startsWith(normalized)) return false;
202
- const parsed = new URL(normalized);
203
- const basePath = normalizeRoute(parsed.pathname || "/");
204
- return basePath === "/" || page.route === basePath || page.route.startsWith(`${basePath}/`);
205
- }
206
-
207
- function normalizePage(value) {
208
- const parsed = new URL(String(value));
209
- return {
210
- url: parsed.href,
211
- origin: parsed.origin,
212
- route: normalizeRoute(parsed.pathname || "/"),
213
- };
214
- }
215
-
216
- function normalizeBaseUrl(value) {
217
- if (typeof value !== "string" || value.trim().length === 0) return null;
218
- try {
219
- const parsed = new URL(value);
220
- const normalizedPath = normalizeRoute(parsed.pathname || "/");
221
- return `${parsed.origin}${normalizedPath === "/" ? "" : normalizedPath}`;
222
- } catch {
223
- return null;
224
- }
225
- }
226
-
227
- function normalizeRoute(value) {
228
- const trimmed = String(value || "/").trim();
229
- if (!trimmed || trimmed === "/") return "/";
230
- const withLeadingSlash = trimmed.startsWith("/") ? trimmed : `/${trimmed}`;
231
- return withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/u, "") : withLeadingSlash;
232
- }
233
-
234
- function writeJson(response, statusCode, payload) {
235
- response.statusCode = statusCode;
236
- response.setHeader("content-type", "application/json; charset=utf-8");
237
- response.setHeader("access-control-allow-origin", "*");
238
- response.setHeader("access-control-allow-methods", "GET, OPTIONS");
239
- response.setHeader("access-control-allow-headers", "content-type");
240
- response.end(`${JSON.stringify(payload, null, 2)}\n`);
241
- }
242
-
243
- function buildGraphProjection(context, page, matchedServiceName) {
244
- const graph = context.discovery?.coverageGraph || null;
245
- if (!graph || !matchedServiceName) {
246
- return { coverage: [], failures: [] };
247
- }
248
-
249
- const nodes = Array.isArray(graph.nodes) ? graph.nodes : [];
250
- const edges = Array.isArray(graph.edges) ? graph.edges : [];
251
- const evidence = Array.isArray(graph.evidence) ? graph.evidence : [];
252
- const discoveryFiles = Array.isArray(context.discovery?.files) ? context.discovery.files : [];
253
- const discoveryByFile = new Map(discoveryFiles.map((entry) => [entry.path, entry]));
254
- const nodeById = new Map(nodes.map((node) => [node.id, node]));
255
- const outgoing = buildOutgoingEdgeMap(edges);
256
- const pageNode = nodes.find(
257
- (node) =>
258
- node.kind === "page_view" && node.service === matchedServiceName && normalizeRoute(node.route || "/") === page.route
259
- );
260
-
261
- if (!pageNode) {
262
- return { coverage: [], failures: [] };
263
- }
264
-
265
- const failedFiles = collectFailedFiles(context.runArtifact);
266
- const failureByFile = new Map(failedFiles.map((entry) => [entry.filePath, entry]));
267
- const surfaceNodes = (outgoing.get(pageNode.id) || [])
268
- .map((nodeId) => nodeById.get(nodeId))
269
- .filter((node) => node?.kind === "ui_surface");
270
- const pageReachableNodeIds = collectReachableNodeIds(pageNode.id, outgoing);
271
-
272
- const surfaceReachableEntries = surfaceNodes.map((surfaceNode) => ({
273
- surfaceNode,
274
- reachableNodeIds: collectReachableNodeIds(surfaceNode.id, outgoing),
275
- }));
276
-
277
- if (surfaceNodes.length === 0) {
278
- const relevantEvidence = evidence.filter((entry) => intersects(entry.coveredNodeIds || [], pageReachableNodeIds));
279
- return buildPageLevelProjection({
280
- pageNode,
281
- nodeById,
282
- relevantEvidence,
283
- pageReachableNodeIds,
284
- discoveryByFile,
285
- failureByFile,
286
- runArtifact: context.runArtifact,
287
- });
288
- }
289
-
290
- const coverage = [];
291
- const failures = [];
292
-
293
- for (const { surfaceNode, reachableNodeIds: surfaceReachableNodeIds } of surfaceReachableEntries.sort((left, right) =>
294
- left.surfaceNode.id.localeCompare(right.surfaceNode.id)
295
- )) {
296
- const relevantEvidence = evidence.filter((entry) => intersects(entry.coveredNodeIds || [], surfaceReachableNodeIds));
297
- const supportingTests = relevantEvidence
298
- .map((entry) => buildSupportingTestRef(entry, discoveryByFile, context.runArtifact))
299
- .filter(Boolean);
300
-
301
- if (supportingTests.length > 0) {
302
- const supportKind = inferCoverageSupportKind(supportingTests);
303
- coverage.push({
304
- id: surfaceNode.id,
305
- kind: surfaceNode.kind,
306
- label: surfaceNode.label,
307
- service: surfaceNode.service,
308
- route: pageNode.route || null,
309
- targets: collectTargetsForEvidence(relevantEvidence, surfaceNode.target),
310
- supportingTests,
311
- viaNodes: collectViaNodes(relevantEvidence, surfaceReachableNodeIds, nodeById, new Set([pageNode.id, surfaceNode.id])),
312
- confidence: supportingTests.some((entry) => entry.type === "pw") ? "high" : "medium",
313
- importance: inferSurfaceImportance(surfaceNode),
314
- surfaceKind: surfaceNode.metadata?.surfaceKind ? String(surfaceNode.metadata.surfaceKind) : null,
315
- supportKind,
316
- reason: buildCoverageReason(surfaceNode, supportKind, supportingTests, relevantEvidence),
317
- });
318
- }
319
-
320
- const failedEvidence = relevantEvidence.filter((entry) => failureByFile.has(entry.testFilePath));
321
- if (failedEvidence.length === 0) continue;
322
-
323
- const failedTests = failedEvidence
324
- .map((entry) => {
325
- const supporting = buildSupportingTestRef(entry, discoveryByFile, context.runArtifact);
326
- const failed = failureByFile.get(entry.testFilePath);
327
- if (!supporting) return null;
328
- return {
329
- ...supporting,
330
- error: failed?.error || supporting.error || null,
331
- status: "failed",
332
- };
333
- })
334
- .filter(Boolean);
335
-
336
- failures.push({
337
- id: `failure:${surfaceNode.id}`,
338
- kind: surfaceNode.kind,
339
- label: surfaceNode.label,
340
- service: surfaceNode.service,
341
- route: pageNode.route || null,
342
- targets: collectTargetsForEvidence(failedEvidence, surfaceNode.target),
343
- failedTests,
344
- viaNodes: collectViaNodes(failedEvidence, surfaceReachableNodeIds, nodeById, new Set([pageNode.id, surfaceNode.id])),
345
- importance: inferSurfaceImportance(surfaceNode),
346
- surfaceKind: surfaceNode.metadata?.surfaceKind ? String(surfaceNode.metadata.surfaceKind) : null,
347
- reason: buildFailureReason(surfaceNode, failedTests),
348
- });
349
- }
350
-
351
- const routeLevelNodeIds = collectRouteLevelNodeIds(pageReachableNodeIds, surfaceReachableEntries);
352
- if (routeLevelNodeIds.size > 0) {
353
- const routeRelevantEvidence = evidence.filter((entry) => intersects(entry.coveredNodeIds || [], routeLevelNodeIds));
354
- const pageProjection = buildPageLevelProjection({
355
- pageNode,
356
- nodeById,
357
- relevantEvidence: routeRelevantEvidence,
358
- pageReachableNodeIds: routeLevelNodeIds,
359
- discoveryByFile,
360
- failureByFile,
361
- runArtifact: context.runArtifact,
362
- });
363
- coverage.unshift(...pageProjection.coverage);
364
- failures.unshift(...pageProjection.failures);
365
- }
366
-
367
- return { coverage, failures };
368
- }
369
-
370
- function buildPageLevelProjection({ pageNode, nodeById, relevantEvidence, pageReachableNodeIds, discoveryByFile, failureByFile, runArtifact }) {
371
- const supportingTests = relevantEvidence
372
- .map((entry) => buildSupportingTestRef(entry, discoveryByFile, runArtifact))
373
- .filter(Boolean);
374
-
375
- const coverage = supportingTests.length > 0
376
- ? [
377
- {
378
- id: pageNode.id,
379
- kind: pageNode.kind,
380
- label: pageNode.label,
381
- service: pageNode.service,
382
- route: pageNode.route || null,
383
- targets: collectTargetsForEvidence(relevantEvidence, pageNode.target),
384
- supportingTests,
385
- viaNodes: collectViaNodes(relevantEvidence, pageReachableNodeIds, nodeById, new Set([pageNode.id])),
386
- confidence: supportingTests.some((entry) => entry.type === "pw") ? "high" : "medium",
387
- importance: inferSurfaceImportance(pageNode),
388
- surfaceKind: pageNode.kind,
389
- supportKind: inferCoverageSupportKind(supportingTests),
390
- reason: buildCoverageReason(pageNode, inferCoverageSupportKind(supportingTests), supportingTests, relevantEvidence),
391
- },
392
- ]
393
- : [];
394
-
395
- const failures = relevantEvidence
396
- .filter((entry) => failureByFile.has(entry.testFilePath))
397
- .map((entry) => {
398
- const failed = failureByFile.get(entry.testFilePath);
399
- const supporting = buildSupportingTestRef(entry, discoveryByFile, runArtifact);
400
- return {
401
- id: `failure:${entry.testFilePath}`,
402
- kind: pageNode.kind,
403
- label: supporting?.label || pageNode.label,
404
- service: pageNode.service,
405
- route: pageNode.route || null,
406
- targets: collectTargetsForEvidence([entry], pageNode.target),
407
- failedTests: supporting ? [{ ...supporting, error: failed?.error || supporting.error || null, status: "failed" }] : [],
408
- viaNodes: collectViaNodes([entry], pageReachableNodeIds, nodeById, new Set([pageNode.id])),
409
- importance: inferSurfaceImportance(pageNode),
410
- surfaceKind: pageNode.kind,
411
- reason: buildFailureReason(pageNode, supporting ? [{ ...supporting, error: failed?.error || supporting.error || null, status: "failed" }] : []),
412
- };
413
- });
414
-
415
- return { coverage, failures };
416
- }
417
-
418
- function buildOutgoingEdgeMap(edges) {
419
- const map = new Map();
420
- for (const edge of edges || []) {
421
- const entries = map.get(edge.from) || [];
422
- entries.push(edge.to);
423
- map.set(edge.from, entries);
424
- }
425
- return map;
426
- }
427
-
428
- function collectReachableNodeIds(startNodeId, outgoing) {
429
- const visited = new Set([startNodeId]);
430
- const queue = [startNodeId];
431
- while (queue.length > 0) {
432
- const current = queue.shift();
433
- for (const next of outgoing.get(current) || []) {
434
- if (visited.has(next)) continue;
435
- visited.add(next);
436
- queue.push(next);
437
- }
438
- }
439
- return visited;
440
- }
441
-
442
- function collectRouteLevelNodeIds(pageReachableNodeIds, surfaceReachableEntries) {
443
- const routeLevelNodeIds = new Set(pageReachableNodeIds);
444
- for (const { reachableNodeIds } of surfaceReachableEntries) {
445
- for (const nodeId of reachableNodeIds) {
446
- routeLevelNodeIds.delete(nodeId);
447
- }
448
- }
449
- return routeLevelNodeIds;
450
- }
451
-
452
- function collectViaNodes(evidenceEntries, reachableNodeIds, nodeById, excludedNodeIds = new Set()) {
453
- const nodes = new Map();
454
- for (const entry of evidenceEntries) {
455
- for (const nodeId of entry.coveredNodeIds || []) {
456
- if (!reachableNodeIds.has(nodeId) || excludedNodeIds.has(nodeId)) continue;
457
- const node = nodeById.get(nodeId);
458
- if (!node) continue;
459
- nodes.set(nodeId, {
460
- id: node.id,
461
- kind: node.kind,
462
- label: node.label,
463
- ...(node.route ? { route: node.route } : {}),
464
- ...(node.method ? { method: node.method } : {}),
465
- ...(node.path ? { path: node.path } : {}),
466
- });
467
- }
468
- }
469
- return [...nodes.values()].sort((left, right) => left.id.localeCompare(right.id));
470
- }
471
-
472
- function collectTargetsForEvidence(evidenceEntries, pageTarget) {
473
- const targets = [];
474
- if (pageTarget) targets.push(pageTarget);
475
- for (const entry of evidenceEntries || []) {
476
- for (const target of entry?.details?.targets || []) {
477
- targets.push(target);
478
- }
479
- }
480
- const seen = new Set();
481
- return targets.filter((target) => {
482
- const key = `${target.kind}:${target.value}`;
483
- if (seen.has(key)) return false;
484
- seen.add(key);
485
- return true;
486
- });
487
- }
488
-
489
- function buildSupportingTestRef(evidence, discoveryByFile, runArtifact) {
490
- const discovery = discoveryByFile.get(evidence.testFilePath);
491
- const runFile = findRunFileResult(runArtifact, evidence.testFilePath);
492
- return {
493
- service: evidence.service,
494
- suite: evidence.suiteName,
495
- type: evidence.selectionType,
496
- framework: evidence.framework,
497
- filePath: evidence.testFilePath,
498
- label: discovery?.displayName || pathBaseName(evidence.testFilePath),
499
- ...(runFile?.status ? { status: runFile.status } : {}),
500
- ...(runFile?.error ? { error: runFile.error } : {}),
501
- };
502
- }
503
-
504
- function findRunFileResult(runArtifact, targetPath) {
505
- if (!runArtifact || !Array.isArray(runArtifact.services)) return null;
506
- for (const service of runArtifact.services) {
507
- for (const suite of service.suites || []) {
508
- for (const file of suite.files || []) {
509
- if (file.path === targetPath) return file;
510
- }
511
- }
512
- }
513
- return null;
514
- }
515
-
516
- function intersects(values, setLike) {
517
- for (const value of values || []) {
518
- if (setLike.has(value)) return true;
519
- }
520
- return false;
521
- }
522
-
523
- function pathBaseName(filePath) {
524
- const parts = String(filePath || "").split("/");
525
- return parts[parts.length - 1] || filePath;
526
- }
527
-
528
- function buildCoverageBreakdown(entries) {
529
- const breakdown = { direct: 0, indirect: 0, mixed: 0 };
530
- for (const entry of entries || []) {
531
- if (entry.supportKind === "direct") breakdown.direct += 1;
532
- else if (entry.supportKind === "indirect") breakdown.indirect += 1;
533
- else breakdown.mixed += 1;
534
- }
535
- return breakdown;
536
- }
537
-
538
- function inferCoverageSupportKind(supportingTests) {
539
- const hasPw = (supportingTests || []).some((entry) => entry.type === "pw");
540
- const hasBackend = (supportingTests || []).some((entry) => entry.type !== "pw");
541
- if (hasPw && hasBackend) return "mixed";
542
- if (hasPw) return "direct";
543
- return "indirect";
544
- }
545
-
546
- function inferSurfaceImportance(node) {
547
- const label = String(node?.label || "").toLowerCase();
548
- const surfaceKind = String(node?.metadata?.surfaceKind || node?.kind || "").toLowerCase();
549
-
550
- if (/\b(pay|purchase|checkout|publish|send|submit|confirm|delete|remove)\b/u.test(label)) {
551
- return "critical";
552
- }
553
- if (/\b(save|create|update|refresh|retry|login|sign in|continue)\b/u.test(label)) {
554
- return "high";
555
- }
556
- if (surfaceKind === "form" || surfaceKind === "button" || surfaceKind === "input") {
557
- return "medium";
558
- }
559
- return "low";
560
- }
561
-
562
- function buildCoverageReason(node, supportKind, supportingTests, evidenceEntries) {
563
- const directCount = supportingTests.filter((entry) => entry.type === "pw").length;
564
- const backendCount = supportingTests.filter((entry) => entry.type !== "pw").length;
565
- const requestPaths = new Set();
566
- for (const entry of evidenceEntries || []) {
567
- for (const path of entry?.details?.requestPaths || []) requestPaths.add(path);
568
- }
569
- const requestSummary = requestPaths.size > 0 ? ` via ${[...requestPaths].join(", ")}` : "";
570
- if (supportKind === "mixed") {
571
- return `${node.label} is covered directly by ${directCount} UI test${directCount === 1 ? "" : "s"} and indirectly by ${backendCount} backend test${backendCount === 1 ? "" : "s"}${requestSummary}.`;
572
- }
573
- if (supportKind === "direct") {
574
- return `${node.label} is covered directly by ${directCount} UI test${directCount === 1 ? "" : "s"}${requestSummary}.`;
575
- }
576
- return `${node.label} is covered indirectly by ${backendCount} backend test${backendCount === 1 ? "" : "s"}${requestSummary}.`;
577
- }
578
-
579
- function buildFailureReason(node, failedTests) {
580
- const count = (failedTests || []).length;
581
- if (count === 0) return `${node.label} has a related failing test.`;
582
- return `${node.label} is implicated by ${count} failing test${count === 1 ? "" : "s"}.`;
583
- }