testeranto 0.200.0 → 0.202.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 (376) hide show
  1. package/README.md +26 -1
  2. package/bundle.js +3 -2
  3. package/design-editor/DesignEditor.tsx +40 -241
  4. package/dist/common/design-editor/DesignEditor.js +33 -198
  5. package/dist/common/src/App.js +158 -16
  6. package/dist/common/src/PM/PM_WithBuild.js +135 -0
  7. package/dist/common/src/PM/PM_WithEslintAndTsc.js +79 -67
  8. package/dist/common/src/PM/PM_WithGit.js +517 -0
  9. package/dist/common/src/PM/PM_WithProcesses.js +519 -0
  10. package/dist/common/src/PM/PM_WithWebSocket.js +535 -0
  11. package/dist/common/src/PM/base.js +62 -0
  12. package/dist/common/src/PM/main.js +533 -1676
  13. package/dist/common/src/PM/metafileOutputs.js +78 -0
  14. package/dist/common/src/PM/node.js +0 -6
  15. package/dist/common/src/PM/pure.js +0 -8
  16. package/dist/common/src/PM/types.js +1 -0
  17. package/dist/common/src/PM/utils.js +210 -0
  18. package/dist/common/src/PM/web.js +0 -6
  19. package/dist/common/src/ReportServer.js +0 -10
  20. package/dist/common/src/ReportServerLib.js +0 -140
  21. package/dist/common/src/components/pure/AppFrame.js +68 -56
  22. package/dist/common/src/components/pure/ArtifactTree.js +80 -0
  23. package/dist/common/src/components/pure/BuildLogViewer.js +106 -0
  24. package/dist/common/src/components/pure/DebugEnv.js +30 -0
  25. package/dist/common/src/components/pure/FileTree.js +34 -0
  26. package/dist/common/src/components/pure/FileTreeItem.js +29 -0
  27. package/dist/common/src/components/pure/GitHubLoginButton.js +18 -0
  28. package/dist/common/src/components/pure/GitIntegrationView.js +342 -0
  29. package/dist/common/src/components/pure/ProcessManager.js +1 -0
  30. package/dist/common/src/components/pure/ProcessManagerView.js +73 -74
  31. package/dist/common/src/components/pure/ProjectPageView.js +4 -117
  32. package/dist/common/src/components/pure/Settings.js +121 -0
  33. package/dist/common/src/components/pure/Settings.test.js +34 -0
  34. package/dist/common/src/components/pure/SignIn.js +22 -0
  35. package/dist/common/src/components/pure/SingleProcessView.js +166 -213
  36. package/dist/common/src/components/pure/TestPageView.js +113 -368
  37. package/dist/common/src/components/pure/TestPageView_utils.js +117 -0
  38. package/dist/common/src/components/pure/TestTable.js +33 -0
  39. package/dist/common/src/components/pure/ToastNotification.js +14 -0
  40. package/dist/common/src/components/pure/UserProfile.js +27 -0
  41. package/dist/common/src/components/stateful/AuthCallbackPage.js +51 -0
  42. package/dist/common/src/components/stateful/FeaturesReporter.js +2 -1
  43. package/dist/common/src/components/stateful/FileTree.js +58 -39
  44. package/dist/common/src/components/stateful/GitIntegrationPage.js +12 -0
  45. package/dist/common/src/components/stateful/ProcessManagerPage.js +13 -15
  46. package/dist/common/src/components/stateful/ProjectPage.js +6 -5
  47. package/dist/common/src/components/stateful/ProjectsPage.js +17 -19
  48. package/dist/common/src/components/stateful/SingleProcessPage.js +16 -26
  49. package/dist/common/src/components/stateful/TestPage.js +7 -5
  50. package/dist/common/src/hooks/useGitMode.js +21 -0
  51. package/dist/common/src/lib/BaseSuite.test/mock.js +15 -8
  52. package/dist/common/src/lib/BaseSuite.test/test.js +56 -80
  53. package/dist/common/src/lib/Tiposkripto.js +24 -0
  54. package/dist/common/src/lib/Tiposkripto.test/MockTiposkripto.js +154 -10
  55. package/dist/common/src/lib/Tiposkripto.test/Tiposkripto.adapter.js +6 -12
  56. package/dist/common/src/lib/Tiposkripto.test/Tiposkripto.implementation.js +63 -23
  57. package/dist/common/src/lib/Tiposkripto.test/Tiposkripto.specification.js +14 -6
  58. package/dist/common/src/lib/pmProxy.test/specification.js +167 -52
  59. package/dist/common/src/services/FileService.js +505 -0
  60. package/dist/common/src/services/GitHubAuthService.js +184 -0
  61. package/dist/common/src/testeranto.js +38 -97
  62. package/dist/common/src/utils/api.js +12 -8
  63. package/dist/common/src/utils/gitTest.js +27 -0
  64. package/dist/common/src/utils.js +23 -13
  65. package/dist/common/testeranto.config.js +21 -17
  66. package/dist/common/tsconfig.common.tsbuildinfo +1 -1
  67. package/dist/module/design-editor/DesignEditor.js +33 -199
  68. package/dist/module/src/App.js +121 -15
  69. package/dist/module/src/PM/PM_WithBuild.js +128 -0
  70. package/dist/module/src/PM/PM_WithEslintAndTsc.js +79 -67
  71. package/dist/module/src/PM/PM_WithGit.js +477 -0
  72. package/dist/module/src/PM/PM_WithProcesses.js +479 -0
  73. package/dist/module/src/PM/PM_WithWebSocket.js +528 -0
  74. package/dist/module/src/PM/base.js +62 -0
  75. package/dist/module/src/PM/main.js +533 -1676
  76. package/dist/module/src/PM/metafileOutputs.js +78 -0
  77. package/dist/module/src/PM/node.js +0 -6
  78. package/dist/module/src/PM/pure.js +0 -8
  79. package/dist/module/src/PM/types.js +1 -1
  80. package/dist/module/src/PM/utils.js +196 -0
  81. package/dist/module/src/PM/web.js +0 -6
  82. package/dist/module/src/ReportServer.js +1 -9
  83. package/dist/module/src/ReportServerLib.js +1 -134
  84. package/dist/module/src/components/pure/AppFrame.js +66 -24
  85. package/dist/module/src/components/pure/ArtifactTree.js +80 -0
  86. package/dist/module/src/components/pure/BuildLogViewer.js +99 -0
  87. package/dist/module/src/components/pure/DebugEnv.js +23 -0
  88. package/dist/module/src/components/pure/FileTree.js +27 -0
  89. package/dist/module/src/components/pure/FileTreeItem.js +22 -0
  90. package/dist/module/src/components/pure/GitHubLoginButton.js +11 -0
  91. package/dist/module/src/components/pure/GitIntegrationView.js +305 -0
  92. package/dist/module/src/components/pure/ProcessManager.js +1 -0
  93. package/dist/module/src/components/pure/ProcessManagerView.js +74 -75
  94. package/dist/module/src/components/pure/ProjectPageView.js +5 -118
  95. package/dist/module/src/components/pure/Settings.js +84 -0
  96. package/dist/module/src/components/pure/Settings.test.js +29 -0
  97. package/dist/module/src/components/pure/SignIn.js +15 -0
  98. package/dist/module/src/components/pure/SingleProcessView.js +130 -214
  99. package/dist/module/src/components/pure/TestPageView.js +97 -352
  100. package/dist/module/src/components/pure/TestPageView_utils.js +109 -0
  101. package/dist/module/src/components/pure/TestTable.js +26 -0
  102. package/dist/module/src/components/pure/ToastNotification.js +7 -0
  103. package/dist/module/src/components/pure/UserProfile.js +20 -0
  104. package/dist/module/src/components/stateful/AuthCallbackPage.js +14 -0
  105. package/dist/module/src/components/stateful/FeaturesReporter.js +2 -1
  106. package/dist/module/src/components/stateful/FileTree.js +59 -33
  107. package/dist/module/src/components/stateful/GitIntegrationPage.js +5 -0
  108. package/dist/module/src/components/stateful/ProcessManagerPage.js +13 -15
  109. package/dist/module/src/components/stateful/ProjectPage.js +6 -5
  110. package/dist/module/src/components/stateful/ProjectsPage.js +16 -18
  111. package/dist/module/src/components/stateful/SingleProcessPage.js +16 -26
  112. package/dist/module/src/components/stateful/TestPage.js +8 -6
  113. package/dist/module/src/hooks/useGitMode.js +17 -0
  114. package/dist/module/src/lib/BaseSuite.test/mock.js +15 -8
  115. package/dist/module/src/lib/BaseSuite.test/test.js +56 -80
  116. package/dist/module/src/lib/Tiposkripto.js +24 -0
  117. package/dist/module/src/lib/Tiposkripto.test/MockTiposkripto.js +154 -10
  118. package/dist/module/src/lib/Tiposkripto.test/Tiposkripto.adapter.js +6 -12
  119. package/dist/module/src/lib/Tiposkripto.test/Tiposkripto.implementation.js +63 -23
  120. package/dist/module/src/lib/Tiposkripto.test/Tiposkripto.specification.js +14 -6
  121. package/dist/module/src/lib/pmProxy.test/specification.js +167 -52
  122. package/dist/module/src/services/FileService.js +468 -0
  123. package/dist/module/src/services/GitHubAuthService.js +180 -0
  124. package/dist/module/src/testeranto.js +38 -97
  125. package/dist/module/src/utils/api.js +10 -7
  126. package/dist/module/src/utils/gitTest.js +23 -0
  127. package/dist/module/src/utils.js +21 -12
  128. package/dist/module/testeranto.config.js +21 -17
  129. package/dist/module/tsconfig.module.tsbuildinfo +1 -1
  130. package/dist/prebuild/App.css +94 -121
  131. package/dist/prebuild/App.js +44601 -11225
  132. package/dist/prebuild/testeranto.mjs +4106 -0
  133. package/dist/types/design-editor/DesignEditor.d.ts +1 -18
  134. package/dist/types/src/App.d.ts +18 -0
  135. package/dist/types/src/PM/PM_WithBuild.d.ts +13 -0
  136. package/dist/types/src/PM/PM_WithEslintAndTsc.d.ts +2 -4
  137. package/dist/types/src/PM/PM_WithGit.d.ts +27 -0
  138. package/dist/types/src/PM/PM_WithProcesses.d.ts +29 -0
  139. package/dist/types/src/PM/PM_WithWebSocket.d.ts +108 -0
  140. package/dist/types/src/PM/base.d.ts +1 -1
  141. package/dist/types/src/PM/index.d.ts +0 -2
  142. package/dist/types/src/PM/main.d.ts +6 -77
  143. package/dist/types/src/PM/metafileOutputs.d.ts +0 -0
  144. package/dist/types/src/PM/node.d.ts +0 -2
  145. package/dist/types/src/PM/pure.d.ts +0 -2
  146. package/dist/types/src/PM/types.d.ts +118 -0
  147. package/dist/types/src/PM/utils.d.ts +35 -0
  148. package/dist/types/src/PM/web.d.ts +0 -2
  149. package/dist/types/src/Pure.d.ts +6 -1
  150. package/dist/types/src/ReportServer.d.ts +0 -1
  151. package/dist/types/src/ReportServerLib.d.ts +0 -1
  152. package/dist/types/src/Types.d.ts +1 -0
  153. package/dist/types/src/components/pure/ArtifactTree.d.ts +0 -0
  154. package/dist/types/src/components/pure/BuildLogViewer.d.ts +7 -0
  155. package/dist/types/src/components/pure/DebugEnv.d.ts +2 -0
  156. package/dist/types/src/components/pure/FileTree.d.ts +6 -0
  157. package/dist/types/src/components/pure/FileTreeItem.d.ts +8 -0
  158. package/dist/types/src/components/pure/GitHubLoginButton.d.ts +8 -0
  159. package/dist/types/src/components/pure/GitIntegrationView.d.ts +1 -0
  160. package/dist/types/src/components/pure/Settings.d.ts +1 -0
  161. package/dist/types/src/components/pure/Settings.test.d.ts +1 -0
  162. package/dist/types/src/components/pure/SignIn.d.ts +1 -0
  163. package/dist/types/src/components/pure/SingleProcessView.d.ts +10 -0
  164. package/dist/types/src/components/pure/TestPageView.d.ts +2 -1
  165. package/dist/types/src/components/pure/TestPageView_utils.d.ts +23 -0
  166. package/dist/types/src/components/pure/TestTable.d.ts +16 -0
  167. package/dist/types/src/components/pure/ToastNotification.d.ts +6 -0
  168. package/dist/types/src/components/pure/UserProfile.d.ts +2 -0
  169. package/dist/types/src/components/stateful/AuthCallbackPage.d.ts +2 -0
  170. package/dist/types/src/components/stateful/FileTree.d.ts +0 -8
  171. package/dist/types/src/components/stateful/GitIntegrationPage.d.ts +1 -0
  172. package/dist/types/src/hooks/useGitMode.d.ts +7 -0
  173. package/dist/types/src/lib/BaseSuite.test/mock.d.ts +2 -2
  174. package/dist/types/src/lib/BaseSuite.test/test.d.ts +4 -3
  175. package/dist/types/src/lib/Tiposkripto.d.ts +2 -0
  176. package/dist/types/src/lib/Tiposkripto.test/MockTiposkripto.d.ts +3 -0
  177. package/dist/types/src/lib/Tiposkripto.test/Tiposkripto.types.d.ts +5 -0
  178. package/dist/types/src/lib/index.d.ts +6 -0
  179. package/dist/types/src/services/FileService.d.ts +34 -0
  180. package/dist/types/src/services/GitHubAuthService.d.ts +32 -0
  181. package/dist/types/src/utils/api.d.ts +1 -0
  182. package/dist/types/src/utils/gitTest.d.ts +11 -0
  183. package/dist/types/src/utils.d.ts +3 -3
  184. package/dist/types/tsconfig.types.tsbuildinfo +1 -1
  185. package/example/__pycache__/Calculator.cpython-313.pyc +0 -0
  186. package/example/vendor/github.com/adamwong246/testeranto/src/golingvu/README.md +187 -0
  187. package/example/vendor/github.com/adamwong246/testeranto/src/golingvu/base_given.go +163 -0
  188. package/example/vendor/github.com/adamwong246/testeranto/src/golingvu/base_suite.go +85 -0
  189. package/example/vendor/github.com/adamwong246/testeranto/src/golingvu/base_then.go +21 -0
  190. package/example/vendor/github.com/adamwong246/testeranto/src/golingvu/base_when.go +21 -0
  191. package/example/vendor/github.com/adamwong246/testeranto/src/golingvu/golingvu.go +554 -0
  192. package/example/vendor/github.com/adamwong246/testeranto/src/golingvu/test_adapter.go +33 -0
  193. package/example/vendor/github.com/adamwong246/testeranto/src/golingvu/types.go +72 -0
  194. package/example/vendor/modules.txt +5 -0
  195. package/git-integration-plan.md +395 -0
  196. package/package.json +26 -14
  197. package/src/App.tsx +169 -18
  198. package/src/PM/PM_WithBuild.ts +171 -0
  199. package/src/PM/PM_WithEslintAndTsc.ts +109 -86
  200. package/src/PM/PM_WithGit.ts +585 -0
  201. package/src/PM/PM_WithProcesses.ts +639 -0
  202. package/src/PM/PM_WithWebSocket.ts +631 -0
  203. package/src/PM/base.ts +63 -1
  204. package/src/PM/index.ts +8 -5
  205. package/src/PM/main.ts +672 -2050
  206. package/src/PM/metafileOutputs.ts +90 -0
  207. package/src/PM/node.ts +18 -18
  208. package/src/PM/pure.ts +5 -13
  209. package/src/PM/types.ts +145 -0
  210. package/src/PM/utils.ts +256 -0
  211. package/src/PM/web.ts +8 -8
  212. package/src/README.md +122 -0
  213. package/src/ReportServer.ts +0 -12
  214. package/src/ReportServerLib.ts +0 -147
  215. package/src/Types.ts +1 -0
  216. package/src/app.scss +14 -164
  217. package/src/components/pure/AppFrame.tsx +237 -71
  218. package/src/components/pure/ArtifactTree.tsx +82 -0
  219. package/src/components/pure/BuildLogViewer.tsx +168 -0
  220. package/src/components/pure/DebugEnv.tsx +30 -0
  221. package/src/components/pure/FileTree.tsx +58 -0
  222. package/src/components/pure/FileTreeItem.tsx +49 -0
  223. package/src/components/pure/GitHubLoginButton.tsx +31 -0
  224. package/src/components/pure/GitIntegrationView.tsx +443 -0
  225. package/src/components/pure/ProcessManager.tsx +6 -5
  226. package/src/components/pure/ProcessManagerView.tsx +162 -166
  227. package/src/components/pure/ProjectPageView.tsx +6 -224
  228. package/src/components/pure/Settings.test.tsx +34 -0
  229. package/src/components/pure/Settings.tsx +163 -0
  230. package/src/components/pure/SignIn.tsx +33 -0
  231. package/src/components/pure/SingleProcessView.tsx +231 -235
  232. package/src/components/pure/TestPageView.test/specification.ts +1 -0
  233. package/src/components/pure/TestPageView.tsx +317 -826
  234. package/src/components/pure/TestPageView_utils.tsx +285 -0
  235. package/src/components/pure/TestTable.tsx +88 -0
  236. package/src/components/pure/ToastNotification.tsx +19 -0
  237. package/src/components/pure/UserProfile.tsx +44 -0
  238. package/src/components/stateful/AuthCallbackPage.tsx +21 -0
  239. package/src/components/stateful/FeaturesReporter.tsx +3 -1
  240. package/src/components/stateful/FileTree.tsx +58 -58
  241. package/src/components/stateful/GitIntegrationPage.tsx +8 -0
  242. package/src/components/stateful/ProcessManagerPage.tsx +13 -17
  243. package/src/components/stateful/ProjectPage.tsx +6 -5
  244. package/src/components/stateful/ProjectsPage.tsx +17 -19
  245. package/src/components/stateful/SVGEditor/CircleForm.tsx +68 -0
  246. package/src/components/stateful/SVGEditor/GroupForm.tsx +56 -0
  247. package/src/components/stateful/SVGEditor/RectForm.tsx +74 -0
  248. package/src/components/stateful/SVGEditor/SVGAttributeField.tsx +29 -0
  249. package/src/components/stateful/SVGEditor/SVGAttributesEditor.tsx +73 -0
  250. package/src/components/stateful/SVGEditor/SVGEditorControls.tsx +45 -0
  251. package/src/components/stateful/SVGEditor/SVGElementForm.tsx +45 -0
  252. package/src/components/stateful/SVGEditor/SVGPreview.tsx +225 -0
  253. package/src/components/stateful/SVGEditor/SVGTextEditor.tsx +166 -0
  254. package/src/components/stateful/SVGEditor/SVGTree.tsx +159 -0
  255. package/src/components/stateful/SVGEditor/SVGTypes.ts +36 -0
  256. package/src/components/stateful/SVGEditor/svg.xsd.xml +3038 -0
  257. package/src/components/stateful/SVGEditorPage.tsx +503 -0
  258. package/src/components/stateful/SingleProcessPage.tsx +18 -31
  259. package/src/components/stateful/TestPage.tsx +25 -22
  260. package/src/golingvu/golingvu.go +38 -2
  261. package/src/hooks/useGitMode.ts +20 -0
  262. package/src/lib/BaseSuite.test/mock.ts +16 -10
  263. package/src/lib/BaseSuite.test/test.ts +144 -103
  264. package/src/lib/Tiposkripto.test/MockTiposkripto.ts +178 -14
  265. package/src/lib/Tiposkripto.test/Tiposkripto.adapter.ts +9 -14
  266. package/src/lib/Tiposkripto.test/Tiposkripto.implementation.ts +78 -38
  267. package/src/lib/Tiposkripto.test/Tiposkripto.specification.ts +51 -9
  268. package/src/lib/Tiposkripto.test/Tiposkripto.types.ts +5 -0
  269. package/src/lib/Tiposkripto.ts +27 -0
  270. package/src/lib/index.ts +7 -0
  271. package/src/lib/pmProxy.test/specification.ts +168 -166
  272. package/src/pitono/PM/__pycache__/python.cpython-313.pyc +0 -0
  273. package/src/pitono/__pycache__/Pitono.cpython-313.pyc +0 -0
  274. package/src/pitono/__pycache__/__init__.cpython-313.pyc +0 -0
  275. package/src/pitono/__pycache__/base_given.cpython-313.pyc +0 -0
  276. package/src/pitono/__pycache__/base_suite.cpython-313.pyc +0 -0
  277. package/src/pitono/__pycache__/base_then.cpython-313.pyc +0 -0
  278. package/src/pitono/__pycache__/base_when.cpython-313.pyc +0 -0
  279. package/src/pitono/__pycache__/core_generator.cpython-313.pyc +0 -0
  280. package/src/pitono/__pycache__/simple_adapter.cpython-313.pyc +0 -0
  281. package/src/pitono/__pycache__/types.cpython-313.pyc +0 -0
  282. package/src/services/FileService.ts +542 -0
  283. package/src/services/GitHubAuthService.ts +240 -0
  284. package/src/testeranto.ts +62 -140
  285. package/src/utils/api.ts +15 -13
  286. package/src/utils/gitTest.ts +29 -0
  287. package/src/utils.ts +21 -12
  288. package/testeranto/App.css +94 -121
  289. package/testeranto/App.js +44601 -11225
  290. package/testeranto/bundles/golang/core/Calculator.golingvu.go +53 -0
  291. package/testeranto/bundles/golang/core/Calculator.golingvu.golingvu.go +53 -0
  292. package/testeranto/bundles/node/core/chunk-RIM6RECA.mjs +1170 -0
  293. package/testeranto/bundles/node/core/chunk-VXVF7WFO.mjs +4321 -0
  294. package/testeranto/bundles/node/core/example/Calculator.test.mjs +503 -0
  295. package/testeranto/bundles/node/core/src/lib/BaseSuite.test/node.test.mjs +94 -1231
  296. package/testeranto/bundles/node/core/src/lib/TipoSkripto.test/TipoSkripto.mjs +574 -0
  297. package/testeranto/bundles/node/core/src/lib/pmProxy.test/index.mjs +482 -0
  298. package/testeranto/bundles/pure/core/chunk-XYOCRDEQ.mjs +1080 -0
  299. package/testeranto/bundles/pure/core/src/Pure.test.mjs +410 -0
  300. package/testeranto/bundles/pure/core/src/lib/BaseSuite.test/pure.test.mjs +93 -1146
  301. package/testeranto/bundles/python/core/Calculator.pitono.test.py +24 -0
  302. package/testeranto/bundles/python/core/test_example.py +24 -0
  303. package/testeranto/bundles/web/core/MPLUSRounded1c-Black-O75GP5JI.ttf +0 -0
  304. package/testeranto/bundles/web/core/MPLUSRounded1c-Bold-R524Q5BH.ttf +0 -0
  305. package/testeranto/bundles/web/core/MPLUSRounded1c-ExtraBold-C6GRMYVT.ttf +0 -0
  306. package/testeranto/bundles/web/core/MPLUSRounded1c-Light-WKN65Y2C.ttf +0 -0
  307. package/testeranto/bundles/web/core/MPLUSRounded1c-Medium-ZC4DWL7C.ttf +0 -0
  308. package/testeranto/bundles/web/core/MPLUSRounded1c-Regular-DT6EKZ3S.ttf +0 -0
  309. package/testeranto/bundles/web/core/MPLUSRounded1c-Thin-YWDNVG6M.ttf +0 -0
  310. package/testeranto/bundles/web/core/chunk-DFRN4SYZ.mjs +2297 -0
  311. package/testeranto/bundles/web/core/chunk-JMDLMADH.mjs +27996 -0
  312. package/testeranto/bundles/web/core/chunk-LQMU5NCG.mjs +3082 -0
  313. package/testeranto/bundles/web/core/chunk-Q5TONB2Z.mjs +6874 -0
  314. package/testeranto/bundles/web/core/src/components/pure/FeaturesReporterView.test/index.mjs +164 -0
  315. package/testeranto/bundles/web/core/src/components/pure/ModalContent.test/index.css +11697 -0
  316. package/testeranto/bundles/web/core/src/components/pure/ModalContent.test/index.mjs +336 -0
  317. package/testeranto/bundles/web/core/src/components/pure/ProjectPageView.test/index.css +11697 -0
  318. package/testeranto/bundles/web/core/src/components/pure/ProjectPageView.test/index.mjs +517 -0
  319. package/testeranto/bundles/web/core/src/lib/BaseSuite.test/web.test.mjs +107 -1134
  320. package/testeranto/metafiles/golang/core.json +3 -3
  321. package/testeranto/metafiles/node/core.json +474 -31
  322. package/testeranto/metafiles/pure/core.json +144 -28
  323. package/testeranto/metafiles/python/core.json +11 -0
  324. package/testeranto/metafiles/web/core.json +15829 -45
  325. package/testeranto/reports/core/config.json +40 -0
  326. package/testeranto/reports/core/src/Pure.test/pure/exit.log +0 -0
  327. package/testeranto/reports/core/src/Pure.test/pure/lint_errors.txt +0 -0
  328. package/testeranto/reports/core/src/Pure.test/pure/prompt.txt +14 -0
  329. package/testeranto/reports/core/src/Pure.test/pure/type_errors.txt +73 -0
  330. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/debug.log +0 -0
  331. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/error.log +91 -0
  332. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/exit.log +1 -0
  333. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/info.log +2 -0
  334. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/tests.json +68 -0
  335. package/testeranto/reports/core/src/components/pure/FeaturesReporterView.test/index/web/warn.log +0 -0
  336. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/debug.log +0 -0
  337. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/error.log +30 -0
  338. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/exit.log +1 -0
  339. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/info.log +2 -0
  340. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/tests.json +88 -0
  341. package/testeranto/reports/core/src/components/pure/ProjectPageView.test/index/web/warn.log +0 -0
  342. package/testeranto/reports/core/src/lib/BaseSuite.test/node.test/node/lint_errors.txt +0 -6
  343. package/testeranto/reports/core/src/lib/BaseSuite.test/node.test/node/prompt.txt +0 -11
  344. package/testeranto/reports/core/src/lib/BaseSuite.test/node.test/node/stdout.log +1 -0
  345. package/testeranto/reports/core/src/lib/BaseSuite.test/node.test/node/tests.json +1 -1
  346. package/testeranto/reports/core/src/lib/BaseSuite.test/node.test/node/type_errors.txt +35 -38
  347. package/testeranto/reports/core/src/lib/BaseSuite.test/pure.test/pure/lint_errors.txt +0 -2
  348. package/testeranto/reports/core/src/lib/BaseSuite.test/pure.test/pure/prompt.txt +0 -10
  349. package/testeranto/reports/core/src/lib/BaseSuite.test/pure.test/pure/type_errors.txt +40 -38
  350. package/testeranto/reports/core/src/lib/BaseSuite.test/web.test/web/tests.json +1 -1
  351. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/exit.log +1 -0
  352. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/lint_errors.txt +0 -0
  353. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/message.txt +17 -0
  354. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/prompt.txt +17 -0
  355. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/stderr.log +55 -0
  356. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/stdout.log +191 -0
  357. package/testeranto/reports/core/src/lib/TipoSkripto.test/TipoSkripto/node/type_errors.txt +71 -0
  358. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/exit.log +1 -0
  359. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/lint_errors.txt +15 -0
  360. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/message.txt +17 -0
  361. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/prompt.txt +17 -0
  362. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/stderr.log +20 -0
  363. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/stdout.log +4 -0
  364. package/testeranto/reports/core/src/lib/pmProxy.test/index/node/type_errors.txt +49 -0
  365. package/testeranto/reports/core/summary.json +34 -6
  366. package/testeranto.config.ts +26 -20
  367. package/tsc.log +141 -91
  368. package/tsconfig.json +6 -2
  369. package/dist/prebuild/ReportServer.mjs +0 -227
  370. package/dist/prebuild/mothership/index.mjs +0 -22
  371. package/dist/types/design-editor/server.d.ts +0 -1
  372. package/testeranto/bundles/web/core/src/lib/BaseSuite.test/web.test.html +0 -15
  373. package/testeranto/reports/core/src/lib/BaseSuite.test/web.test/web/lint_errors.txt +0 -2
  374. package/testeranto/reports/core/src/lib/BaseSuite.test/web.test/web/prompt.txt +0 -25
  375. package/testeranto/reports/core/src/lib/BaseSuite.test/web.test/web/type_errors.txt +0 -56
  376. /package/testeranto/reports/core/src/{lib/BaseSuite.test/web.test/web → Pure.test/pure}/message.txt +0 -0
package/src/PM/main.ts CHANGED
@@ -3,2225 +3,847 @@
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
4
  /* eslint-disable @typescript-eslint/no-unused-vars */
5
5
 
6
- import { ChildProcess, spawn } from "node:child_process";
6
+ import { spawn } from "node:child_process";
7
7
  import ansiColors from "ansi-colors";
8
8
  import net from "net";
9
- import { Page } from "puppeteer-core/lib/esm/puppeteer";
10
- import fs, { watch } from "fs";
11
- import path from "path";
12
- import puppeteer, { ConsoleMessage } from "puppeteer-core";
9
+ import fs from "fs";
10
+ import { ConsoleMessage } from "puppeteer-core";
13
11
  import ansiC from "ansi-colors";
14
- import crypto from "node:crypto";
15
- import { WebSocketServer } from "ws";
16
- import http from "http";
17
- import url from "url";
18
- import mime from "mime-types";
19
-
20
- import {
21
- IFinalResults,
22
- IRunnables,
23
- ITTestResourceConfiguration,
24
- } from "../lib/index.js";
25
- import { getRunnables } from "../utils";
26
- import { IBuiltConfig, IRunTime, ITestTypes } from "../Types.js";
27
- import { Sidecar } from "../lib/Sidecar.js";
28
- import { Queue } from "../utils/queue.js";
29
12
 
30
- import { PM_WithEslintAndTsc } from "./PM_WithEslintAndTsc.js";
13
+ import { IFinalResults, ITTestResourceConfiguration } from "../lib/index.js";
14
+ import { webEvaluator } from "../utils";
15
+ import { Queue } from "../utils/queue.js";
31
16
 
32
- type IOutputs = Record<
33
- string,
34
- {
35
- entryPoint: string;
36
- inputs: Record<string, string>;
37
- }
38
- >;
17
+ import { createLogStreams, statusMessagePretty } from "./utils.js";
18
+ import { PM_WithProcesses } from "./PM_WithProcesses.js";
39
19
 
40
- const changes: Record<string, string> = {};
41
- const fileHashes = {};
42
20
  const files: Record<string, Set<string>> = {};
43
21
  const screenshots: Record<string, Promise<Uint8Array>[]> = {};
44
22
 
45
- type LogStreams = {
46
- closeAll: () => void;
47
- writeExitCode: (code: number, error?: Error) => void;
48
- stdout?: fs.WriteStream;
49
- stderr?: fs.WriteStream;
50
- info?: fs.WriteStream;
51
- warn?: fs.WriteStream;
52
- error?: fs.WriteStream;
53
- debug?: fs.WriteStream;
54
- exit: fs.WriteStream;
55
- };
56
-
57
- function runtimeLogs(
58
- runtime: IRunTime,
59
- reportDest: string
60
- ): Record<string, fs.WriteStream> {
61
- const safeDest = reportDest || `testeranto/reports/default_${Date.now()}`;
62
-
63
- try {
64
- if (!fs.existsSync(safeDest)) {
65
- fs.mkdirSync(safeDest, { recursive: true });
66
- }
67
-
68
- if (runtime === "node") {
69
- return {
70
- stdout: fs.createWriteStream(`${safeDest}/stdout.log`),
71
- stderr: fs.createWriteStream(`${safeDest}/stderr.log`),
72
- exit: fs.createWriteStream(`${safeDest}/exit.log`),
73
- };
74
- } else if (runtime === "web") {
75
- return {
76
- info: fs.createWriteStream(`${safeDest}/info.log`),
77
- warn: fs.createWriteStream(`${safeDest}/warn.log`),
78
- error: fs.createWriteStream(`${safeDest}/error.log`),
79
- debug: fs.createWriteStream(`${safeDest}/debug.log`),
80
- exit: fs.createWriteStream(`${safeDest}/exit.log`),
81
- };
82
- } else if (runtime === "pure") {
83
- return {
84
- exit: fs.createWriteStream(`${safeDest}/exit.log`),
85
- };
86
- } else if (runtime === "pitono") {
87
- return {
88
- stdout: fs.createWriteStream(`${safeDest}/stdout.log`),
89
- stderr: fs.createWriteStream(`${safeDest}/stderr.log`),
90
- exit: fs.createWriteStream(`${safeDest}/exit.log`),
91
- };
92
- } else {
93
- throw `unknown runtime: ${runtime}`;
94
- }
95
- } catch (e) {
96
- console.error(`Failed to create log streams in ${safeDest}:`, e);
97
- throw e;
98
- }
99
- }
100
-
101
- function createLogStreams(reportDest: string, runtime: IRunTime): LogStreams {
102
- // Create directory if it doesn't exist
103
- if (!fs.existsSync(reportDest)) {
104
- fs.mkdirSync(reportDest, { recursive: true });
105
- }
106
-
107
- const streams = runtimeLogs(runtime, reportDest);
108
-
109
- // const streams = {
110
- // exit: fs.createWriteStream(`${reportDest}/exit.log`),
111
- const safeDest = reportDest || `testeranto/reports/default_${Date.now()}`;
112
-
113
- try {
114
- if (!fs.existsSync(safeDest)) {
115
- fs.mkdirSync(safeDest, { recursive: true });
116
- }
117
-
118
- const streams = runtimeLogs(runtime, safeDest);
119
- // const streams = {
120
- // exit: fs.createWriteStream(`${safeDest}/exit.log`),
121
- // ...(runtime === "node" || runtime === "pure"
122
- // ? {
123
- // stdout: fs.createWriteStream(`${safeDest}/stdout.log`),
124
- // stderr: fs.createWriteStream(`${safeDest}/stderr.log`),
125
- // }
126
- // : {
127
- // info: fs.createWriteStream(`${safeDest}/info.log`),
128
- // warn: fs.createWriteStream(`${safeDest}/warn.log`),
129
- // error: fs.createWriteStream(`${safeDest}/error.log`),
130
- // debug: fs.createWriteStream(`${safeDest}/debug.log`),
131
- // }),
132
- // };
133
-
134
- return {
135
- ...streams,
136
- closeAll: () => {
137
- Object.values(streams).forEach(
138
- (stream) => !stream.closed && stream.close()
139
- );
140
- },
141
- writeExitCode: (code: number, error?: Error) => {
142
- if (error) {
143
- streams.exit.write(`Error: ${error.message}\n`);
144
- if (error.stack) {
145
- streams.exit.write(`Stack Trace:\n${error.stack}\n`);
146
- }
147
- }
148
- streams.exit.write(`${code}\n`);
149
- },
150
- exit: streams.exit,
151
- };
152
- } catch (e) {
153
- console.error(`Failed to create log streams in ${safeDest}:`, e);
154
- throw e;
155
- }
156
- }
157
-
158
- async function fileHash(filePath, algorithm = "md5") {
159
- return new Promise<string>((resolve, reject) => {
160
- const hash = crypto.createHash(algorithm);
161
- const fileStream = fs.createReadStream(filePath);
23
+ export class PM_Main extends PM_WithProcesses {
24
+ launchPure = async (src: string, dest: string) => {
25
+ const processId = `pure-${src}-${Date.now()}`;
26
+ const command = `pure test: ${src}`;
162
27
 
163
- fileStream.on("data", (data) => {
164
- hash.update(data);
165
- });
28
+ // Create the promise
29
+ const purePromise = (async () => {
30
+ this.bddTestIsRunning(src);
166
31
 
167
- fileStream.on("end", () => {
168
- const fileHash = hash.digest("hex");
169
- resolve(fileHash);
170
- });
32
+ const reportDest = `testeranto/reports/${this.name}/${src
33
+ .split(".")
34
+ .slice(0, -1)
35
+ .join(".")}/pure`;
171
36
 
172
- fileStream.on("error", (error) => {
173
- reject(`Error reading file: ${error.message}`);
174
- });
175
- });
176
- }
37
+ if (!fs.existsSync(reportDest)) {
38
+ fs.mkdirSync(reportDest, { recursive: true });
39
+ }
177
40
 
178
- const statusMessagePretty = (
179
- failures: number,
180
- test: string,
181
- runtime: IRunTime
182
- ) => {
183
- if (failures === 0) {
184
- console.log(ansiC.green(ansiC.inverse(`${runtime} > ${test}`)));
185
- } else if (failures > 0) {
186
- console.log(
187
- ansiC.red(
188
- ansiC.inverse(
189
- `${runtime} > ${test} failed ${failures} times (exit code: ${failures})`
190
- )
191
- )
192
- );
193
- } else {
194
- console.log(
195
- ansiC.red(ansiC.inverse(`${runtime} > ${test} crashed (exit code: -1)`))
196
- );
197
- }
198
- };
199
-
200
- async function writeFileAndCreateDir(filePath, data) {
201
- const dirPath = path.dirname(filePath);
202
-
203
- try {
204
- await fs.promises.mkdir(dirPath, { recursive: true });
205
- await fs.writeFileSync(filePath, data);
206
- } catch (error) {
207
- console.error(`Error writing file: ${error}`);
208
- }
209
- }
41
+ const destFolder = dest.replace(".mjs", "");
210
42
 
211
- const filesHash = async (files: string[], algorithm = "md5") => {
212
- return new Promise<string>((resolve, reject) => {
213
- resolve(
214
- files.reduce(async (mm: Promise<string>, f) => {
215
- return (await mm) + (await fileHash(f));
216
- }, Promise.resolve(""))
217
- );
218
- });
219
- };
220
-
221
- function isValidUrl(string) {
222
- try {
223
- new URL(string);
224
- return true;
225
- } catch (err) {
226
- return false;
227
- }
228
- }
43
+ let argz = "";
229
44
 
230
- // Wait for file to exist, checks every 2 seconds by default
231
- async function pollForFile(path, timeout = 2000) {
232
- const intervalObj = setInterval(function () {
233
- const file = path;
234
- const fileExists = fs.existsSync(file);
235
- if (fileExists) {
236
- clearInterval(intervalObj);
237
- }
238
- }, timeout);
239
- }
45
+ const testConfig = this.configs.tests.find((t) => {
46
+ return t[0] === src;
47
+ });
240
48
 
241
- export class PM_Main extends PM_WithEslintAndTsc {
242
- ports: Record<number, string>;
243
- queue: string[];
244
- logStreams: Record<string, ReturnType<typeof createLogStreams>> = {};
245
- webMetafileWatcher: fs.FSWatcher;
246
- nodeMetafileWatcher: fs.FSWatcher;
247
- importMetafileWatcher: fs.FSWatcher;
248
- pitonoMetafileWatcher: fs.FSWatcher;
249
- pureSidecars: Record<number, Sidecar>;
250
- nodeSidecars: Record<number, ChildProcess>;
251
- webSidecars: Record<number, Page>;
252
- sidecars: Record<number, any> = {};
253
- launchers: Record<string, () => void>;
254
-
255
- wss: WebSocketServer;
256
- clients: Set<any> = new Set();
257
- httpServer: http.Server;
258
- runningProcesses: Map<string, ChildProcess> = new Map();
259
- allProcesses: Map<
260
- string,
261
- {
262
- child?: ChildProcess;
263
- status: "running" | "exited" | "error";
264
- exitCode?: number;
265
- error?: string;
266
- command: string;
267
- pid?: number;
268
- timestamp: string;
269
- }
270
- > = new Map();
271
- processLogs: Map<string, string[]> = new Map();
272
-
273
- constructor(configs: IBuiltConfig, name: string, mode: "once" | "dev") {
274
- super(configs, name, mode);
275
-
276
- this.launchers = {};
277
- this.ports = {};
278
- this.queue = [];
279
-
280
- this.nodeSidecars = {};
281
- this.webSidecars = {};
282
- this.pureSidecars = {};
283
-
284
- this.configs.ports.forEach((element) => {
285
- this.ports[element] = ""; // set ports as open
286
- });
287
-
288
- // Create HTTP server
289
- this.httpServer = http.createServer(this.requestHandler.bind(this));
290
-
291
- // Start WebSocket server attached to the HTTP server
292
- this.wss = new WebSocketServer({ server: this.httpServer });
293
-
294
- this.wss.on("connection", (ws) => {
295
- this.clients.add(ws);
296
- console.log("Client connected");
297
-
298
- ws.on("message", (data) => {
299
- try {
300
- const message = JSON.parse(data.toString());
301
- if (message.type === "executeCommand") {
302
- // Validate the command starts with 'aider'
303
- if (message.command && message.command.trim().startsWith("aider")) {
304
- console.log(`Executing command: ${message.command}`);
305
- // Execute the command
306
- const processId = Date.now().toString();
307
- const child = spawn(message.command, {
308
- shell: true,
309
- cwd: process.cwd(),
310
- });
49
+ if (!testConfig) {
50
+ console.log(
51
+ ansiC.inverse("missing test config! Exiting ungracefully!")
52
+ );
53
+ process.exit(-1);
54
+ }
55
+ const testConfigResource = testConfig[2];
311
56
 
312
- // Track the process in both maps
313
- this.runningProcesses.set(processId, child);
314
- this.allProcesses.set(processId, {
315
- child,
316
- status: "running",
317
- command: message.command,
318
- pid: child.pid,
319
- timestamp: new Date().toISOString(),
320
- });
57
+ const portsToUse: string[] = [];
58
+ if (testConfigResource.ports === 0) {
59
+ argz = JSON.stringify({
60
+ scheduled: true,
61
+ name: src,
62
+ ports: portsToUse,
63
+ fs: reportDest,
64
+ browserWSEndpoint: this.browser.wsEndpoint(),
65
+ });
66
+ } else if (testConfigResource.ports > 0) {
67
+ const openPorts = Object.entries(this.ports).filter(
68
+ ([portnumber, status]) => status === ""
69
+ );
321
70
 
322
- // Initialize logs for this process
323
- this.processLogs.set(processId, []);
71
+ if (openPorts.length >= testConfigResource.ports) {
72
+ for (let i = 0; i < testConfigResource.ports; i++) {
73
+ portsToUse.push(openPorts[i][0]);
324
74
 
325
- // Broadcast process started
326
- this.broadcast({
327
- type: "processStarted",
328
- processId,
329
- command: message.command,
330
- timestamp: new Date().toISOString(),
331
- logs: [],
332
- });
75
+ this.ports[openPorts[i][0]] = src; // port is now claimed
76
+ }
333
77
 
334
- // Capture stdout and stderr
335
- child.stdout?.on("data", (data) => {
336
- const logData = data.toString();
337
- // Add to stored logs
338
- const logs = this.processLogs.get(processId) || [];
339
- logs.push(logData);
340
- this.processLogs.set(processId, logs);
341
-
342
- this.broadcast({
343
- type: "processStdout",
344
- processId,
345
- data: logData,
346
- timestamp: new Date().toISOString(),
347
- });
348
- });
78
+ argz = JSON.stringify({
79
+ scheduled: true,
80
+ name: src,
81
+ ports: portsToUse,
82
+ fs: destFolder,
83
+ browserWSEndpoint: this.browser.wsEndpoint(),
84
+ });
85
+ } else {
86
+ this.queue.push(src);
87
+ return [Math.random(), argz];
88
+ }
89
+ } else {
90
+ console.error("negative port makes no sense", src);
91
+ process.exit(-1);
92
+ }
349
93
 
350
- child.stderr?.on("data", (data) => {
351
- const logData = data.toString();
352
- // Add to stored logs
353
- const logs = this.processLogs.get(processId) || [];
354
- logs.push(logData);
355
- this.processLogs.set(processId, logs);
356
-
357
- this.broadcast({
358
- type: "processStderr",
359
- processId,
360
- data: logData,
361
- timestamp: new Date().toISOString(),
362
- });
363
- });
94
+ const builtfile = dest;
364
95
 
365
- child.on("error", (error) => {
366
- console.error(`Failed to execute command: ${error}`);
367
- this.runningProcesses.delete(processId);
368
- // Update the process status to error
369
- const processInfo = this.allProcesses.get(processId);
370
- if (processInfo) {
371
- this.allProcesses.set(processId, {
372
- ...processInfo,
373
- status: "error",
374
- error: error.message,
375
- });
376
- }
377
- this.broadcast({
378
- type: "processError",
379
- processId,
380
- error: error.message,
381
- timestamp: new Date().toISOString(),
382
- });
383
- });
96
+ const logs = createLogStreams(reportDest, "pure");
384
97
 
385
- child.on("exit", (code) => {
386
- console.log(`Command exited with code ${code}`);
387
- // Remove from running processes but keep in allProcesses
388
- this.runningProcesses.delete(processId);
389
- // Update the process status to exited
390
- const processInfo = this.allProcesses.get(processId);
391
- if (processInfo) {
392
- this.allProcesses.set(processId, {
393
- ...processInfo,
394
- status: "exited",
395
- exitCode: code,
396
- });
397
- }
398
- this.broadcast({
399
- type: "processExited",
400
- processId,
401
- exitCode: code,
402
- timestamp: new Date().toISOString(),
98
+ try {
99
+ await import(`${builtfile}?cacheBust=${Date.now()}`).then((module) => {
100
+ return module.default
101
+ .then((defaultModule) => {
102
+ return defaultModule
103
+ .receiveTestResourceConfig(argz)
104
+ .then(async (results: IFinalResults) => {
105
+ statusMessagePretty(results.fails, src, "pure");
106
+ this.bddTestIsNowDone(src, results.fails);
107
+ return results.fails;
403
108
  });
404
- });
405
- } else {
406
- console.error('Invalid command: must start with "aider"');
407
- }
408
- } else if (message.type === "getRunningProcesses") {
409
- // Send list of all processes (both running and completed) with their full logs
410
- const processes = Array.from(this.allProcesses.entries()).map(
411
- ([id, procInfo]) => ({
412
- processId: id,
413
- command: procInfo.command,
414
- pid: procInfo.pid,
415
- status: procInfo.status,
416
- exitCode: procInfo.exitCode,
417
- error: procInfo.error,
418
- timestamp: procInfo.timestamp,
419
- logs: this.processLogs.get(id) || [],
420
- })
421
- );
422
- ws.send(
423
- JSON.stringify({
424
- type: "runningProcesses",
425
- processes,
426
- })
427
- );
428
- } else if (message.type === "getProcess") {
429
- // Send specific process with full logs
430
- const processId = message.processId;
431
- const procInfo = this.allProcesses.get(processId);
432
- if (procInfo) {
433
- ws.send(
434
- JSON.stringify({
435
- type: "processData",
436
- processId,
437
- command: procInfo.command,
438
- pid: procInfo.pid,
439
- status: procInfo.status,
440
- exitCode: procInfo.exitCode,
441
- error: procInfo.error,
442
- timestamp: procInfo.timestamp,
443
- logs: this.processLogs.get(processId) || [],
444
- })
445
- );
446
- }
447
- } else if (message.type === "stdin") {
448
- // Handle stdin input for a process
449
- const processId = message.processId;
450
- const data = message.data;
451
- console.log("Received stdin for process", processId, ":", data);
452
- const childProcess = this.runningProcesses.get(processId);
453
-
454
- if (childProcess && childProcess.stdin) {
455
- console.log("Writing to process stdin");
456
- childProcess.stdin.write(data);
457
- } else {
109
+ })
110
+ .catch((e2) => {
458
111
  console.log(
459
- "Cannot write to stdin - process not found or no stdin:",
460
- {
461
- processExists: !!childProcess,
462
- stdinExists: childProcess?.stdin ? true : false,
463
- }
112
+ ansiColors.red(
113
+ `pure ! ${src} failed to execute. No "tests.json" file was generated. Check the logs for more info`
114
+ )
464
115
  );
465
- }
466
- } else if (message.type === "killProcess") {
467
- // Handle killing a process
468
- const processId = message.processId;
469
- console.log("Received killProcess for process", processId);
470
- const childProcess = this.runningProcesses.get(processId);
471
-
472
- if (childProcess) {
473
- console.log("Killing process");
474
- childProcess.kill("SIGTERM");
475
- // The process exit handler will update the status and broadcast the change
476
- } else {
477
- console.log("Cannot kill process - process not found:", {
478
- processExists: !!childProcess,
479
- });
480
- }
481
- }
482
- } catch (error) {
483
- console.error("Error handling WebSocket message:", error);
484
- }
485
- });
486
-
487
- ws.on("close", () => {
488
- this.clients.delete(ws);
489
- console.log("Client disconnected");
490
- });
491
116
 
492
- ws.on("error", (error) => {
493
- console.error("WebSocket error:", error);
494
- this.clients.delete(ws);
495
- });
496
- });
497
-
498
- // Start HTTP server
499
- const httpPort = Number(process.env.HTTP_PORT) || 3000;
500
- this.httpServer.listen(httpPort, () => {
501
- console.log(`HTTP server running on http://localhost:${httpPort}`);
502
- });
503
- }
504
-
505
- async stopSideCar(uid: number): Promise<any> {
506
- console.log(ansiC.green(ansiC.inverse(`stopSideCar ${uid}`)));
507
-
508
- Object.entries(this.pureSidecars).forEach(async ([k, v]) => {
509
- if (Number(k) === uid) {
510
- await this.pureSidecars[Number(k)].stop();
511
- delete this.pureSidecars[Number(k)];
512
- }
513
- });
514
-
515
- Object.entries(this.nodeSidecars).forEach(async ([k, v]) => {
516
- if (Number(k) === uid) {
517
- await this.nodeSidecars[Number(k)].send("stop");
518
- delete this.nodeSidecars[Number(k)];
519
- }
520
- });
521
-
522
- Object.entries(this.webSidecars).forEach(async ([k, v]) => {
523
- if (Number(k) === uid) {
524
- (await this.browser.pages()).forEach(async (p) => {
525
- if (p.mainFrame()._id === k) {
526
- await this.webSidecars[Number(k)].close();
527
- delete this.webSidecars[Number(k)];
528
- }
117
+ logs.exit.write(e2.stack);
118
+ logs.exit.write(-1);
119
+ this.bddTestIsNowDone(src, -1);
120
+ statusMessagePretty(-1, src, "pure");
121
+ throw e2;
122
+ });
529
123
  });
530
- }
531
- });
532
-
533
- return;
534
- }
535
-
536
- async launchSideCar(
537
- n: number,
538
- name: string
539
- ): Promise<[number, ITTestResourceConfiguration]> {
540
- const c = this.configs.tests.find(([v, r]) => {
541
- return v === name;
542
- }) as ITestTypes;
543
-
544
- const s = c[3][n];
545
-
546
- const r = s[1];
547
- if (r === "node") {
548
- return this.launchNodeSideCar(s);
549
- } else if (r === "web") {
550
- return this.launchWebSideCar(s);
551
- } else if (r === "pure") {
552
- return this.launchPureSideCar(s);
553
- } else {
554
- throw `unknown runtime ${r}`;
555
- }
556
- }
557
-
558
- mapping(): [string, (...a) => any][] {
559
- return [
560
- ["$", this.$],
561
- ["click", this.click],
562
- ["closePage", this.closePage],
563
- ["createWriteStream", this.createWriteStream],
564
- ["customclose", this.customclose],
565
- ["customScreenShot", this.customScreenShot.bind(this)],
566
- ["end", this.end],
567
- ["existsSync", this.existsSync],
568
- ["focusOn", this.focusOn],
569
- ["getAttribute", this.getAttribute],
570
- ["getInnerHtml", this.getInnerHtml],
571
- // ["setValue", this.setValue],
572
- ["goto", this.goto.bind(this)],
573
- ["isDisabled", this.isDisabled],
574
- ["launchSideCar", this.launchSideCar.bind(this)],
575
- ["mkdirSync", this.mkdirSync],
576
- ["newPage", this.newPage],
577
- ["page", this.page],
578
- ["pages", this.pages],
579
- ["screencast", this.screencast],
580
- ["screencastStop", this.screencastStop],
581
- ["stopSideCar", this.stopSideCar.bind(this)],
582
- ["typeInto", this.typeInto],
583
- ["waitForSelector", this.waitForSelector],
584
- ["write", this.write],
585
- ["writeFileSync", this.writeFileSync],
586
- ];
587
- }
588
-
589
- async start() {
590
- // set up the "pure" listeners
591
- this.mapping().forEach(async ([command, func]) => {
592
- globalThis[command] = func;
593
- });
594
-
595
- if (!fs.existsSync(`testeranto/reports/${this.name}`)) {
596
- fs.mkdirSync(`testeranto/reports/${this.name}`);
597
- }
598
-
599
- const executablePath = "/opt/homebrew/bin/chromium";
600
-
601
- try {
602
- this.browser = await puppeteer.launch({
603
- slowMo: 1,
604
- waitForInitialPage: false,
605
- executablePath,
606
- headless: true,
607
- defaultViewport: null, // Disable default 800x600 viewport
608
- dumpio: false,
609
- devtools: false,
610
-
611
- args: [
612
- "--allow-file-access-from-files",
613
- "--allow-insecure-localhost",
614
- "--allow-running-insecure-content",
615
- "--auto-open-devtools-for-tabs",
616
- "--disable-dev-shm-usage",
617
- "--disable-extensions",
618
- "--disable-features=site-per-process",
619
- "--disable-gpu",
620
- "--disable-setuid-sandbox",
621
- "--disable-site-isolation-trials",
622
- "--disable-web-security",
623
- "--no-first-run",
624
- "--no-sandbox",
625
- "--no-startup-window",
626
- "--reduce-security-for-testing",
627
- "--remote-allow-origins=*",
628
- "--start-maximized",
629
- "--unsafely-treat-insecure-origin-as-secure=*",
630
- `--remote-debugging-port=3234`,
631
- // "--disable-features=IsolateOrigins,site-per-process",
632
- // "--disable-features=IsolateOrigins",
633
- // "--disk-cache-dir=/dev/null",
634
- // "--disk-cache-size=1",
635
- // "--no-zygote",
636
- // "--remote-allow-origins=ws://localhost:3234",
637
- // "--single-process",
638
- // "--start-maximized",
639
- // "--unsafely-treat-insecure-origin-as-secure",
640
- // "--unsafely-treat-insecure-origin-as-secure=ws://192.168.0.101:3234",
641
- ],
642
- });
643
- } catch (e) {
644
- console.error(e);
645
- console.error(
646
- "could not start chrome via puppeter. Check this path: ",
647
- executablePath
648
- );
649
- }
650
-
651
- const { nodeEntryPoints, webEntryPoints, pureEntryPoints, pitonoEntryPoints } =
652
- this.getRunnables(this.configs.tests, this.name);
653
-
654
- [
655
- [
656
- nodeEntryPoints,
657
- this.launchNode,
658
- "node",
659
- (w) => {
660
- this.nodeMetafileWatcher = w;
661
- },
662
- ],
663
- [
664
- webEntryPoints,
665
- this.launchWeb,
666
- "web",
667
- (w) => {
668
- this.webMetafileWatcher = w;
669
- },
670
- ],
671
- [
672
- pureEntryPoints,
673
- this.launchPure,
674
- "pure",
675
- (w) => {
676
- this.importMetafileWatcher = w;
677
- },
678
- ],
679
- [
680
- pitonoEntryPoints,
681
- this.launchPitono,
682
- "pitono",
683
- (w) => {
684
- this.pitonoMetafileWatcher = w;
685
- },
686
- ],
687
- ].forEach(
688
- async ([eps, launcher, runtime, watcher]: [
689
- Record<string, string>,
690
- (src: string, dest: string) => Promise<void>,
691
- IRunTime,
692
- (f: fs.FSWatcher) => void
693
- ]) => {
694
- let metafile: string;
695
- if (runtime === "pitono") {
696
- metafile = `./testeranto/metafiles/python/core.json`;
697
- // Ensure the directory exists before trying to watch
698
- const metafileDir = path.dirname(metafile);
699
- if (!fs.existsSync(metafileDir)) {
700
- fs.mkdirSync(metafileDir, { recursive: true });
701
- }
702
- } else {
703
- metafile = `./testeranto/metafiles/${runtime}/${this.name}.json`;
704
- }
705
-
706
- // Only poll for file if it's not a pitono runtime
707
- if (runtime !== "pitono") {
708
- await pollForFile(metafile);
709
- }
710
-
711
- Object.entries(eps).forEach(
712
- async ([inputFile, outputFile]: [string, string]) => {
713
- // await pollForFile(outputFile);\
714
-
715
- this.launchers[inputFile] = () => launcher(inputFile, outputFile);
716
- this.launchers[inputFile]();
717
-
718
- try {
719
- watch(outputFile, async (e, filename) => {
720
- const hash = await fileHash(outputFile);
721
- if (fileHashes[inputFile] !== hash) {
722
- fileHashes[inputFile] = hash;
723
- console.log(
724
- ansiC.yellow(ansiC.inverse(`< ${e} ${filename}`))
725
- );
726
- // launcher(inputFile, outputFile);
727
- this.launchers[inputFile]();
728
- }
729
- });
730
- } catch (e) {
731
- console.error(e);
732
- }
733
- }
124
+ } catch (e3) {
125
+ logs.writeExitCode(-1, e3);
126
+ console.log(
127
+ ansiC.red(
128
+ ansiC.inverse(
129
+ `${src} 1 errored with: ${e3}. Check logs for more info`
130
+ )
131
+ )
734
132
  );
735
133
 
736
- this.metafileOutputs(runtime);
737
-
738
- // For pitono, we need to wait for the file to be created
739
- if (runtime === "pitono") {
740
- // Use polling to wait for the file to exist
741
- const checkFileExists = () => {
742
- if (fs.existsSync(metafile)) {
743
- console.log(
744
- ansiC.green(ansiC.inverse(`Pitono metafile found: ${metafile}`))
745
- );
746
- // Set up the watcher once the file exists
747
- watcher(
748
- watch(metafile, async (e, filename) => {
749
- console.log(
750
- ansiC.yellow(ansiC.inverse(`< ${e} ${filename} (${runtime})`))
751
- );
752
- this.metafileOutputs(runtime);
753
- })
754
- );
755
- // Read the metafile immediately
756
- this.metafileOutputs(runtime);
757
- } else {
758
- // Check again after a delay
759
- setTimeout(checkFileExists, 1000);
760
- }
761
- };
762
- // Start checking for the file
763
- checkFileExists();
764
- } else {
765
- // For other runtimes, only set up watcher if the file exists
766
- if (fs.existsSync(metafile)) {
767
- watcher(
768
- watch(metafile, async (e, filename) => {
769
- console.log(
770
- ansiC.yellow(ansiC.inverse(`< ${e} ${filename} (${runtime})`))
771
- );
772
- this.metafileOutputs(runtime);
773
- })
774
- );
134
+ logs.exit.write(e3.stack);
135
+ logs.exit.write("-1");
136
+ this.bddTestIsNowDone(src, -1);
137
+ statusMessagePretty(-1, src, "pure");
138
+ throw e3;
139
+ } finally {
140
+ for (let i = 0; i <= portsToUse.length; i++) {
141
+ if (portsToUse[i]) {
142
+ this.ports[portsToUse[i]] = ""; // port is open again
775
143
  }
776
144
  }
777
145
  }
146
+ })();
147
+
148
+ // Add to process manager
149
+ this.addPromiseProcess(
150
+ processId,
151
+ purePromise,
152
+ command,
153
+ "bdd-test",
154
+ src,
155
+ "pure"
778
156
  );
779
-
780
- // Object.keys(this.configs.externalTests).forEach((et) => {
781
- // this.launchExternalTest(et, this.configs.externalTests[et]);
782
- // });
783
- }
784
-
785
- // async launchExternalTest(
786
- // externalTestName: string,
787
- // externalTest: {
788
- // watch: string[];
789
- // exec: string;
790
- // }
791
- // ) {
792
- // // fs.mkdirSync(`testeranto/externalTests/${externalTestName}`);
793
- // // exec(externalTest.exec, (error, stdout, stderr) => {
794
- // // if (error) {
795
- // // fs.writeFileSync(
796
- // // `testeranto/externalTests/${externalTestName}/exitcode.txt`,
797
- // // `${error.name}\n${error.message}\n${error.code}\n`
798
- // // );
799
- // // } else {
800
- // // fs.writeFileSync(
801
- // // `testeranto/externalTests/${externalTestName}/exitcode.txt`,
802
- // // `0`
803
- // // );
804
- // // }
805
- // // fs.writeFileSync(
806
- // // `testeranto/externalTests/${externalTestName}/stdout.txt`,
807
- // // stdout
808
- // // );
809
- // // fs.writeFileSync(
810
- // // `testeranto/externalTests/${externalTestName}/stderr.txt`,
811
- // // stderr
812
- // // );
813
- // // });
814
- // }
815
-
816
- async stop() {
817
- console.log(ansiC.inverse("Testeranto-Run is shutting down gracefully..."));
818
- this.mode = "once";
819
- this.nodeMetafileWatcher.close();
820
- this.webMetafileWatcher.close();
821
- this.importMetafileWatcher.close();
822
- if (this.pitonoMetafileWatcher) {
823
- this.pitonoMetafileWatcher.close();
824
- }
825
-
826
- // Close any remaining log streams
827
- Object.values(this.logStreams || {}).forEach((logs) => logs.closeAll());
828
-
829
- // Close WebSocket server
830
- this.wss.close(() => {
831
- console.log("WebSocket server closed");
832
- });
833
-
834
- // Close all client connections
835
- this.clients.forEach((client) => {
836
- client.terminate();
837
- });
838
- this.clients.clear();
839
-
840
- // Close HTTP server
841
- this.httpServer.close(() => {
842
- console.log("HTTP server closed");
843
- });
844
-
845
- this.checkForShutdown();
846
- }
847
-
848
- getRunnables = (
849
- tests: ITestTypes[],
850
- testName: string,
851
- payload = {
852
- nodeEntryPoints: {},
853
- nodeEntryPointSidecars: {},
854
- webEntryPoints: {},
855
- webEntryPointSidecars: {},
856
- pureEntryPoints: {},
857
- pureEntryPointSidecars: {},
858
- }
859
- ): IRunnables => {
860
- return getRunnables(tests, testName, payload);
861
157
  };
862
158
 
863
- async metafileOutputs(platform: IRunTime) {
864
- let metafilePath: string;
865
- if (platform === "pitono") {
866
- metafilePath = `./testeranto/metafiles/python/core.json`;
867
- } else {
868
- metafilePath = `./testeranto/metafiles/${platform}/${this.name}.json`;
869
- }
870
-
871
- // Check if the file exists
872
- if (!fs.existsSync(metafilePath)) {
873
- if (platform === "pitono") {
874
- console.log(
875
- ansiC.yellow(ansiC.inverse(`Pitono metafile not found yet: ${metafilePath}`))
876
- );
877
- }
878
- return;
879
- }
880
-
881
- let metafile;
882
- try {
883
- const fileContent = fs.readFileSync(metafilePath).toString();
884
- const parsedData = JSON.parse(fileContent);
885
- // Handle different metafile structures
886
- if (platform === "pitono") {
887
- // Pitono metafile might be the entire content or have a different structure
888
- metafile = parsedData.metafile || parsedData;
889
- } else {
890
- metafile = parsedData.metafile;
891
- }
892
- if (!metafile) {
893
- console.log(
894
- ansiC.yellow(ansiC.inverse(`No metafile found in ${metafilePath}`))
895
- );
896
- return;
897
- }
898
- } catch (error) {
899
- console.error(`Error reading metafile at ${metafilePath}:`, error);
900
- return;
901
- }
902
-
903
- const outputs: IOutputs = metafile.outputs;
904
-
905
- Object.keys(outputs).forEach(async (k) => {
906
- const pattern = `testeranto/bundles/${platform}/${this.name}/${this.configs.src}`;
907
- if (!k.startsWith(pattern)) {
908
- return false;
909
- }
910
-
911
- const addableFiles = Object.keys(outputs[k].inputs).filter((i) => {
912
- if (!fs.existsSync(i)) return false;
913
- if (i.startsWith("node_modules")) return false;
914
- if (i.startsWith("./node_modules")) return false;
915
-
916
- return true;
917
- });
918
-
919
- const f = `${k.split(".").slice(0, -1).join(".")}/`;
920
-
921
- if (!fs.existsSync(f)) {
922
- fs.mkdirSync(f);
923
- }
159
+ launchNode = async (src: string, dest: string) => {
160
+ const processId = `node-${src}-${Date.now()}`;
161
+ const command = `node test: ${src}`;
924
162
 
925
- const entrypoint = outputs[k].entryPoint;
163
+ // Create the promise
164
+ const nodePromise = (async () => {
165
+ this.bddTestIsRunning(src);
926
166
 
927
- if (entrypoint) {
928
- const changeDigest = await filesHash(addableFiles);
167
+ const reportDest = `testeranto/reports/${this.name}/${src
168
+ .split(".")
169
+ .slice(0, -1)
170
+ .join(".")}/node`;
929
171
 
930
- if (changeDigest === changes[entrypoint]) {
931
- // skip
932
- } else {
933
- changes[entrypoint] = changeDigest;
934
- this.tscCheck({
935
- platform,
936
- addableFiles,
937
- entrypoint: entrypoint,
938
- });
939
- this.eslintCheck(entrypoint, platform, addableFiles);
940
- this.makePrompt(entrypoint, addableFiles, platform);
941
- }
172
+ if (!fs.existsSync(reportDest)) {
173
+ fs.mkdirSync(reportDest, { recursive: true });
942
174
  }
943
- });
944
- }
945
-
946
- launchPure = async (src: string, dest: string) => {
947
- console.log(ansiC.green(ansiC.inverse(`pure < ${src}`)));
948
- this.bddTestIsRunning(src);
949
-
950
- const reportDest = `testeranto/reports/${this.name}/${src
951
- .split(".")
952
- .slice(0, -1)
953
- .join(".")}/pure`;
954
-
955
- if (!fs.existsSync(reportDest)) {
956
- fs.mkdirSync(reportDest, { recursive: true });
957
- }
958
-
959
- const destFolder = dest.replace(".mjs", "");
960
175
 
961
- let argz = "";
176
+ let testResources = "";
962
177
 
963
- const testConfig = this.configs.tests.find((t) => {
964
- return t[0] === src;
965
- });
966
-
967
- if (!testConfig) {
968
- console.log(ansiC.inverse("missing test config! Exiting ungracefully!"));
969
- process.exit(-1);
970
- }
971
- const testConfigResource = testConfig[2];
972
-
973
- const portsToUse: string[] = [];
974
- if (testConfigResource.ports === 0) {
975
- argz = JSON.stringify({
976
- scheduled: true,
977
- name: src,
978
- ports: portsToUse,
979
- fs: reportDest,
980
- browserWSEndpoint: this.browser.wsEndpoint(),
178
+ const testConfig = this.configs.tests.find((t) => {
179
+ return t[0] === src;
981
180
  });
982
- } else if (testConfigResource.ports > 0) {
983
- const openPorts = Object.entries(this.ports).filter(
984
- ([portnumber, status]) => status === ""
985
- );
986
181
 
987
- if (openPorts.length >= testConfigResource.ports) {
988
- for (let i = 0; i < testConfigResource.ports; i++) {
989
- portsToUse.push(openPorts[i][0]);
990
-
991
- this.ports[openPorts[i][0]] = src; // port is now claimed
992
- }
993
-
994
- argz = JSON.stringify({
995
- scheduled: true,
996
- name: src,
997
- ports: portsToUse,
998
- fs: destFolder,
999
- browserWSEndpoint: this.browser.wsEndpoint(),
1000
- });
1001
- } else {
1002
- this.queue.push(src);
1003
- return [Math.random(), argz];
1004
- }
1005
- } else {
1006
- console.error("negative port makes no sense", src);
1007
- process.exit(-1);
1008
- }
1009
-
1010
- const builtfile = dest;
1011
-
1012
- // const webSideCares: Page[] = [];
1013
-
1014
- // fs.writeFileSync(
1015
- // `${reportDest}/stdlog.txt`,
1016
- // "THIS FILE IS AUTO GENERATED. IT IS PURPOSEFULLY LEFT BLANK."
1017
- // );
1018
-
1019
- // await Promise.all(
1020
- // testConfig[3].map(async (sidecar) => {
1021
- // if (sidecar[1] === "web") {
1022
- // const s = await this.launchWebSideCar(
1023
- // sidecar[0],
1024
- // destinationOfRuntime(sidecar[0], "web", this.configs),
1025
- // sidecar
1026
- // );
1027
- // webSideCares.push(s);
1028
- // return s;
1029
- // }
1030
-
1031
- // if (sidecar[1] === "node") {
1032
- // return this.launchNodeSideCar(
1033
- // sidecar[0],
1034
- // destinationOfRuntime(sidecar[0], "node", this.configs),
1035
- // sidecar
1036
- // );
1037
- // }
1038
- // })
1039
- // );
1040
-
1041
- const logs = createLogStreams(reportDest, "pure");
1042
-
1043
- try {
1044
- await import(`${builtfile}?cacheBust=${Date.now()}`).then((module) => {
1045
- // Override console methods to redirect logs
1046
- // Only override stdout/stderr methods for pure runtime
1047
- const originalConsole = { ...console };
1048
-
1049
- // console.log = (...args) => {
1050
- // logs.stdout.write(args.join(" ") + "\n");
1051
- // originalConsole.log(...args);
1052
- // };
1053
-
1054
- // console.error = (...args) => {
1055
- // logs.stderr.write(args.join(" ") + "\n");
1056
- // originalConsole.error(...args);
1057
- // };
1058
-
1059
- return module.default
1060
- .then((defaultModule) => {
1061
- defaultModule
1062
- .receiveTestResourceConfig(argz)
1063
- .then(async (results: IFinalResults) => {
1064
- // this.receiveFeatures(results.features, destFolder, src, "pure");
1065
- // this.receiveFeaturesV2(reportDest, src, "pure");
1066
-
1067
- statusMessagePretty(results.fails, src, "pure");
1068
- this.bddTestIsNowDone(src, results.fails);
1069
- })
1070
- .catch((e1) => {
1071
- console.log(
1072
- ansiC.red(`launchPure - ${src} errored with: ${e1.stack}`)
1073
- );
1074
- this.bddTestIsNowDone(src, -1);
1075
- statusMessagePretty(-1, src, "pure");
1076
- });
1077
- // .finally(() => {
1078
- // // webSideCares.forEach((webSideCar) => webSideCar.close());
1079
- // });
1080
- })
1081
- .catch((e2) => {
1082
- console.log(
1083
- ansiColors.red(
1084
- `pure ! ${src} failed to execute. No "tests.json" file was generated. Check the logs for more info`
1085
- )
1086
- );
1087
-
1088
- logs.exit.write(e2.stack);
1089
- logs.exit.write(-1);
1090
- this.bddTestIsNowDone(src, -1);
1091
- statusMessagePretty(-1, src, "pure");
1092
- // console.error(e);
1093
- })
1094
- .finally((x) => {
1095
- // const fileSet = files[src] || new Set();
1096
- // fs.writeFileSync(
1097
- // reportDest + "/manifest.json",
1098
- // JSON.stringify(Array.from(fileSet))
1099
- // );
1100
- });
1101
- });
1102
- } catch (e3) {
1103
- logs.writeExitCode(-1, e3);
1104
- console.log(
1105
- ansiC.red(
182
+ if (!testConfig) {
183
+ console.log(
1106
184
  ansiC.inverse(
1107
- `${src} 1 errored with: ${e3}. Check logs for more info`
185
+ `missing test config! Exiting ungracefully for '${src}'`
1108
186
  )
1109
- )
1110
- );
1111
-
1112
- logs.exit.write(e3.stack);
1113
- logs.exit.write("-1");
1114
- this.bddTestIsNowDone(src, -1);
1115
- statusMessagePretty(-1, src, "pure");
1116
- }
1117
-
1118
- for (let i = 0; i <= portsToUse.length; i++) {
1119
- if (portsToUse[i]) {
1120
- this.ports[portsToUse[i]] = ""; //port is open again
187
+ );
188
+ process.exit(-1);
1121
189
  }
1122
- }
1123
- };
1124
-
1125
- launchNode = async (src: string, dest: string) => {
1126
- console.log(ansiC.green(ansiC.inverse(`node < ${src}`)));
1127
- this.bddTestIsRunning(src);
1128
-
1129
- const reportDest = `testeranto/reports/${this.name}/${src
1130
- .split(".")
1131
- .slice(0, -1)
1132
- .join(".")}/node`;
1133
- if (!fs.existsSync(reportDest)) {
1134
- fs.mkdirSync(reportDest, { recursive: true });
1135
- }
1136
-
1137
- // const destFolder = dest.replace(".mjs", "");
1138
-
1139
- let testResources = "";
1140
-
1141
- const testConfig = this.configs.tests.find((t) => {
1142
- return t[0] === src;
1143
- });
1144
-
1145
- if (!testConfig) {
1146
- console.log(
1147
- ansiC.inverse(`missing test config! Exiting ungracefully for '${src}'`)
1148
- );
1149
- process.exit(-1);
1150
- }
1151
- const testConfigResource = testConfig[2];
1152
-
1153
- const portsToUse: string[] = [];
1154
- if (testConfigResource.ports === 0) {
1155
- const t: ITTestResourceConfiguration = {
1156
- name: src,
1157
- // ports: portsToUse.map((v) => Number(v)),
1158
- ports: [],
1159
- fs: reportDest,
1160
- browserWSEndpoint: this.browser.wsEndpoint(),
1161
- };
1162
190
 
1163
- testResources = JSON.stringify(t);
1164
- } else if (testConfigResource.ports > 0) {
1165
- const openPorts: [string, string][] = Object.entries(this.ports).filter(
1166
- ([portnumber, portopen]) => portopen === ""
1167
- );
191
+ const testConfigResource = testConfig[2];
1168
192
 
1169
- if (openPorts.length >= testConfigResource.ports) {
1170
- for (let i = 0; i < testConfigResource.ports; i++) {
1171
- portsToUse.push(openPorts[i][0]); // Convert string port to number
193
+ const portsToUse: string[] = [];
1172
194
 
1173
- this.ports[openPorts[i][0]] = src; // port is now claimed
1174
- }
1175
-
1176
- testResources = JSON.stringify({
1177
- scheduled: true,
195
+ if (testConfigResource.ports === 0) {
196
+ const t: ITTestResourceConfiguration = {
1178
197
  name: src,
1179
- ports: portsToUse,
198
+ ports: [],
1180
199
  fs: reportDest,
1181
200
  browserWSEndpoint: this.browser.wsEndpoint(),
1182
- });
1183
- } else {
1184
- console.log(
1185
- ansiC.red(
1186
- `node: cannot run ${src} because there are no open ports ATM. This job will be enqueued and run again run a port is available`
1187
- )
1188
- );
1189
- this.queue.push(src);
1190
- return [Math.random(), argz]; // Add this return
1191
- }
1192
- } else {
1193
- console.error("negative port makes no sense", src);
1194
- process.exit(-1);
1195
- }
1196
-
1197
- const builtfile = dest;
1198
-
1199
- let haltReturns = false;
1200
-
1201
- const ipcfile = "/tmp/tpipe_" + Math.random();
1202
- const child = spawn(
1203
- "node",
1204
- // "node",
1205
- [
1206
- // "--inspect-brk",
1207
- builtfile,
1208
- testResources,
1209
- ipcfile,
1210
- ],
1211
- {
1212
- stdio: ["pipe", "pipe", "pipe", "ipc"],
1213
- }
1214
- );
1215
-
1216
- let buffer: Buffer<ArrayBufferLike> = new Buffer("");
1217
-
1218
- const server = net.createServer((socket) => {
1219
- const queue = new Queue<string[]>();
201
+ };
1220
202
 
1221
- socket.on("data", (data) => {
1222
- buffer = Buffer.concat([buffer, data]);
203
+ testResources = JSON.stringify(t);
204
+ } else if (testConfigResource.ports > 0) {
205
+ const openPorts: [string, string][] = Object.entries(this.ports).filter(
206
+ ([portnumber, portopen]) => portopen === ""
207
+ );
1223
208
 
1224
- for (let b = 0; b < buffer.length + 1; b++) {
1225
- const c = buffer.slice(0, b);
1226
- let d;
1227
- try {
1228
- d = JSON.parse(c.toString());
209
+ if (openPorts.length >= testConfigResource.ports) {
210
+ for (let i = 0; i < testConfigResource.ports; i++) {
211
+ portsToUse.push(openPorts[i][0]);
1229
212
 
1230
- queue.enqueue(d);
1231
- buffer = buffer.slice(b, buffer.length + 1);
1232
- b = 0;
1233
- } catch (e) {
1234
- // b++;
213
+ this.ports[openPorts[i][0]] = src; // port is now claimed
1235
214
  }
1236
- }
1237
215
 
1238
- while (queue.size() > 0) {
1239
- const message = queue.dequeue();
1240
-
1241
- if (message) {
1242
- // set up the "node" listeners
1243
- this.mapping().forEach(async ([command, func]) => {
1244
- if (message[0] === command) {
1245
- const x = message.slice(1, -1);
1246
- const r = await this[command](...x);
1247
-
1248
- if (!haltReturns) {
1249
- child.send(
1250
- JSON.stringify({
1251
- payload: r,
1252
- key: message[message.length - 1],
1253
- })
1254
- );
1255
- }
1256
- }
1257
- });
1258
- }
1259
- }
1260
- });
1261
- });
1262
-
1263
- const logs = createLogStreams(reportDest, "node");
1264
-
1265
- server.listen(ipcfile, () => {
1266
- // Only handle stdout/stderr for node runtime
1267
- child.stdout?.on("data", (data) => {
1268
- logs.stdout?.write(data); // Add null check
1269
- });
1270
-
1271
- child.stderr?.on("data", (data) => {
1272
- logs.stderr?.write(data); // Add null check
1273
- });
1274
- child.on("error", (err) => {});
1275
- child.on("close", (code) => {
1276
- const exitCode = code === null ? -1 : code;
1277
- if (exitCode < 0) {
1278
- logs.writeExitCode(
1279
- exitCode,
1280
- new Error("Process crashed or was terminated")
1281
- );
216
+ testResources = JSON.stringify({
217
+ scheduled: true,
218
+ name: src,
219
+ ports: portsToUse,
220
+ fs: reportDest,
221
+ browserWSEndpoint: this.browser.wsEndpoint(),
222
+ });
1282
223
  } else {
1283
- logs.writeExitCode(exitCode);
1284
- }
1285
- logs.closeAll();
1286
- server.close();
1287
-
1288
- if (!files[src]) {
1289
- files[src] = new Set();
1290
- }
1291
-
1292
- if (exitCode === 255) {
1293
224
  console.log(
1294
- ansiColors.red(
1295
- `node ! ${src} failed to execute. No "tests.json" file was generated. Check ${reportDest}/stderr.log for more info`
225
+ ansiC.red(
226
+ `node: cannot run ${src} because there are no open ports ATM. This job will be enqueued and run again run a port is available`
1296
227
  )
1297
228
  );
1298
- this.bddTestIsNowDone(src, -1);
1299
- statusMessagePretty(-1, src, "node");
1300
- return;
1301
- } else if (exitCode === 0) {
1302
- this.bddTestIsNowDone(src, 0);
1303
- statusMessagePretty(0, src, "node");
1304
- } else {
1305
- this.bddTestIsNowDone(src, exitCode);
1306
- statusMessagePretty(exitCode, src, "node");
229
+ this.queue.push(src);
230
+ return [Math.random(), testResources];
1307
231
  }
232
+ } else {
233
+ console.error("negative port makes no sense", src);
234
+ process.exit(-1);
235
+ }
1308
236
 
1309
- haltReturns = true;
1310
- });
1311
- child.on("exit", (code) => {
1312
- haltReturns = true;
1313
-
1314
- for (let i = 0; i <= portsToUse.length; i++) {
1315
- if (portsToUse[i]) {
1316
- this.ports[portsToUse[i]] = ""; //port is open again
1317
- }
1318
- }
1319
- });
1320
- child.on("error", (e) => {
1321
- console.log("error");
237
+ const builtfile = dest;
1322
238
 
1323
- haltReturns = true;
239
+ let haltReturns = false;
1324
240
 
1325
- console.log(
1326
- ansiC.red(
1327
- ansiC.inverse(
1328
- `${src} errored with: ${e.name}. Check error logs for more info`
1329
- )
1330
- )
1331
- );
1332
- this.bddTestIsNowDone(src, -1);
1333
- statusMessagePretty(-1, src, "node");
241
+ const ipcfile = "/tmp/tpipe_" + Math.random();
242
+ const child = spawn("node", [builtfile, testResources, ipcfile], {
243
+ stdio: ["pipe", "pipe", "pipe", "ipc"],
1334
244
  });
1335
- });
1336
- };
1337
245
 
1338
- launchWebSideCar = async (
1339
- testConfig: ITestTypes
1340
- ): Promise<[number, Page]> => {
1341
- const src = testConfig[0];
1342
- const dest = src.split(".").slice(0, -1).join(".");
1343
- // const d = dest + ".mjs";
246
+ let buffer: Buffer<ArrayBufferLike> = new Buffer("");
1344
247
 
1345
- const destFolder = dest.replace(".mjs", "");
248
+ const server = net.createServer((socket) => {
249
+ const queue = new Queue<string[]>();
1346
250
 
1347
- console.log(ansiC.green(ansiC.inverse(`launchWebSideCar ${src}`)));
251
+ socket.on("data", (data) => {
252
+ buffer = Buffer.concat([buffer, data]);
1348
253
 
1349
- // const fileStreams2: fs.WriteStream[] = [];
1350
- // const doneFileStream2: Promise<any>[] = [];
1351
-
1352
- const logs = createLogStreams(dest, "web");
1353
-
1354
- return new Promise((res, rej) => {
1355
- this.browser
1356
- .newPage()
1357
- .then(async (page) => {
1358
- this.mapping().forEach(async ([command, func]) => {
1359
- page.exposeFunction(command, func);
1360
- });
1361
-
1362
- const close = () => {
1363
- if (!files[src]) {
1364
- files[src] = new Set();
1365
- }
1366
- // files[src].add(filepath);
1367
-
1368
- // fs.writeFileSync(
1369
- // destFolder + "/manifest.json",
1370
- // JSON.stringify(Array.from(files[src]))
1371
- // );
1372
-
1373
- delete files[src];
1374
-
1375
- Promise.all(screenshots[src] || []).then(() => {
1376
- delete screenshots[src];
1377
- page.close();
1378
- });
1379
- };
1380
-
1381
- page.on("pageerror", (err: Error) => {
1382
- console.debug(`Error from ${src}: [${err.name}] `);
1383
- console.debug(`Error from ${src}: [${err.name}] `);
1384
- if (err.cause) {
1385
- console.debug(`Error from ${src} cause: [${err.cause}] `);
1386
- }
1387
- if (err.stack) {
1388
- console.debug(`Error from stack ${src}: [${err.stack}] `);
1389
- }
1390
- console.debug(`Error from message ${src}: [${err.message}] `);
1391
- this.bddTestIsNowDone(src, -1);
1392
- close();
1393
- });
254
+ for (let b = 0; b < buffer.length + 1; b++) {
255
+ const c = buffer.slice(0, b);
256
+ let d;
257
+ try {
258
+ d = JSON.parse(c.toString());
1394
259
 
1395
- page.on("console", (log: ConsoleMessage) => {
1396
- const msg = `${log.text()}\n${JSON.stringify(
1397
- log.location()
1398
- )}\n${JSON.stringify(log.stackTrace())}\n`;
1399
- switch (log.type()) {
1400
- case "info":
1401
- logs.info?.write(msg);
1402
- break;
1403
- case "warn":
1404
- logs.warn?.write(msg);
1405
- break;
1406
- case "error":
1407
- logs.error?.write(msg);
1408
- break;
1409
- case "debug":
1410
- logs.debug?.write(msg);
1411
- break;
1412
- default:
1413
- break;
260
+ queue.enqueue(d);
261
+ buffer = buffer.slice(b, buffer.length + 1);
262
+ b = 0;
263
+ } catch (e) {
264
+ // b++;
1414
265
  }
1415
- });
1416
-
1417
- await page.goto(`file://${`${destFolder}.html`}`, {});
1418
-
1419
- const webArgz = JSON.stringify({
1420
- name: dest,
1421
- ports: [].toString(),
1422
- fs: dest,
1423
- browserWSEndpoint: this.browser.wsEndpoint(),
1424
- });
1425
-
1426
- const d = `${dest}?cacheBust=${Date.now()}`;
1427
-
1428
- const evaluation = `
1429
- import('${d}').then(async (x) => {
1430
-
1431
- try {
1432
- return await (await x.default).receiveTestResourceConfig(${webArgz})
1433
- } catch (e) {
1434
- console.log("fail", e.toString())
1435
- }
1436
- })`;
1437
-
1438
- await page
1439
- .evaluate(evaluation)
1440
- .then(async ({ fails, failed, features }: IFinalResults) => {
1441
- // this.receiveFeatures(features, destFolder, src, "web");
1442
- // this.receiveFeaturesV2(reportDest, src, "web");
1443
-
1444
- statusMessagePretty(fails, src, "web");
1445
- this.bddTestIsNowDone(src, fails);
1446
- })
1447
- .catch((e) => {
1448
- console.log(
1449
- ansiC.red(
1450
- ansiC.inverse(`launchWebSidecar - ${src} errored with: ${e}`)
1451
- )
1452
- );
1453
- })
1454
- .finally(() => {
1455
- this.bddTestIsNowDone(src, -1);
1456
- close();
1457
- });
1458
-
1459
- return page;
1460
-
1461
- // return page;
1462
- })
1463
- .then(async (page) => {
1464
- await page.goto(`file://${`${dest}.html`}`, {});
1465
-
1466
- res([Math.random(), page]);
1467
- });
1468
- });
1469
- };
1470
-
1471
- launchNodeSideCar = async (
1472
- sidecar: ITestTypes
1473
- ): Promise<[number, ITTestResourceConfiguration]> => {
1474
- const src = sidecar[0];
1475
- const dest =
1476
- process.cwd() + `/testeranto/bundles/node/${this.name}/${sidecar[0]}`;
1477
- const d = dest + ".mjs";
1478
- console.log(ansiC.green(ansiC.inverse(`launchNodeSideCar ${sidecar[0]}`)));
1479
-
1480
- const destFolder = dest.replace(".ts", "");
1481
-
1482
- const reportDest = `testeranto/reports/${this.name}/${src
1483
- .split(".")
1484
- .slice(0, -1)
1485
- .join(".")}/node`;
1486
-
1487
- const argz: ITTestResourceConfiguration = {
1488
- name: sidecar[0],
1489
- ports: [],
1490
- fs: destFolder,
1491
- browserWSEndpoint: this.browser.wsEndpoint(),
1492
- };
1493
-
1494
- const testReq: { ports: number } = sidecar[2];
1495
-
1496
- const logs = createLogStreams(dest, "node");
1497
-
1498
- const portsToUse: number[] = [];
1499
- if (testReq.ports === 0) {
1500
- // argz = {
1501
- // name: sidecar[0],
1502
- // ports: portsToUse,
1503
- // fs: destFolder,
1504
- // browserWSEndpoint: this.browser.wsEndpoint(),
1505
- // };
1506
- } else if (testReq.ports > 0) {
1507
- const openPorts = Object.entries(this.ports).filter(
1508
- ([portnumber, portopen]) => portopen === ""
1509
- );
1510
-
1511
- if (openPorts.length >= testReq.ports) {
1512
- for (let i = 0; i < testReq.ports; i++) {
1513
- portsToUse.push(Number(openPorts[i][0])); // Convert string port to number
1514
-
1515
- this.ports[openPorts[i][0]] = src; // port is now closed
1516
- }
1517
-
1518
- argz.ports = portsToUse;
1519
-
1520
- const builtfile = destFolder + ".mjs";
1521
-
1522
- let haltReturns = false;
1523
-
1524
- let buffer: Buffer<ArrayBufferLike> = new Buffer("");
1525
-
1526
- const server = net.createServer((socket) => {
1527
- socket.on("data", (data) => {
1528
- buffer = Buffer.concat([buffer, data]);
266
+ }
1529
267
 
1530
- const messages: string[][] = [];
1531
- for (let b = 0; b < buffer.length + 1; b++) {
1532
- const c = buffer.slice(0, b);
1533
- let d;
1534
- try {
1535
- d = JSON.parse(c.toString());
268
+ while (queue.size() > 0) {
269
+ const message = queue.dequeue();
1536
270
 
1537
- messages.push(d);
1538
- buffer = buffer.slice(b, buffer.length + 1);
1539
- b = 0;
1540
- } catch (e) {
1541
- // b++;
1542
- }
1543
- }
1544
-
1545
- messages.forEach(async (payload) => {
271
+ if (message) {
272
+ // set up the "node" listeners
1546
273
  this.mapping().forEach(async ([command, func]) => {
1547
- if (payload[0] === command) {
1548
- const x = payload.slice(1, -1);
274
+ if (message[0] === command) {
275
+ const x = message.slice(1, -1);
1549
276
  const r = await this[command](...x);
1550
277
 
1551
278
  if (!haltReturns) {
1552
279
  child.send(
1553
280
  JSON.stringify({
1554
281
  payload: r,
1555
- key: payload[payload.length - 1],
282
+ key: message[message.length - 1],
1556
283
  })
1557
284
  );
1558
285
  }
1559
286
  }
1560
- });
1561
- });
1562
- });
287
+ });
288
+ }
289
+ }
1563
290
  });
291
+ });
1564
292
 
1565
- const child = spawn("node", [builtfile, JSON.stringify(argz)], {
1566
- stdio: ["pipe", "pipe", "pipe", "ipc"],
1567
- // silent: true
1568
- });
293
+ const logs = createLogStreams(reportDest, "node");
1569
294
 
1570
- const p = "/tmp/tpipe" + Math.random();
295
+ return new Promise<void>((resolve, reject) => {
296
+ server.listen(ipcfile, () => {
297
+ child.stdout?.on("data", (data) => {
298
+ logs.stdout?.write(data);
299
+ });
1571
300
 
1572
- server.listen(p, () => {
1573
- child.on("close", (code) => {
1574
- server.close();
1575
- haltReturns = true;
301
+ child.stderr?.on("data", (data) => {
302
+ logs.stderr?.write(data);
1576
303
  });
1577
- child.on("exit", (code) => {
1578
- haltReturns = true;
1579
304
 
1580
- for (let i = 0; i <= portsToUse.length; i++) {
1581
- if (portsToUse[i]) {
1582
- this.ports[portsToUse[i]] = ""; //port is open again
1583
- }
305
+ child.on("close", (code) => {
306
+ const exitCode = code === null ? -1 : code;
307
+ if (exitCode < 0) {
308
+ logs.writeExitCode(
309
+ exitCode,
310
+ new Error("Process crashed or was terminated")
311
+ );
312
+ } else {
313
+ logs.writeExitCode(exitCode);
1584
314
  }
1585
- });
1586
- child.on("error", (e) => {
1587
- if (fs.existsSync(p)) {
1588
- fs.rmSync(p);
315
+ logs.closeAll();
316
+ server.close();
317
+
318
+ if (exitCode === 255) {
319
+ console.log(
320
+ ansiColors.red(
321
+ `node ! ${src} failed to execute. No "tests.json" file was generated. Check ${reportDest}/stderr.log for more info`
322
+ )
323
+ );
324
+ this.bddTestIsNowDone(src, -1);
325
+ statusMessagePretty(-1, src, "node");
326
+ reject(new Error(`Process exited with code ${exitCode}`));
327
+ } else if (exitCode === 0) {
328
+ this.bddTestIsNowDone(src, 0);
329
+ statusMessagePretty(0, src, "node");
330
+ resolve();
331
+ } else {
332
+ this.bddTestIsNowDone(src, exitCode);
333
+ statusMessagePretty(exitCode, src, "node");
334
+ reject(new Error(`Process exited with code ${exitCode}`));
1589
335
  }
1590
336
 
1591
337
  haltReturns = true;
338
+ });
1592
339
 
340
+ child.on("error", (e) => {
341
+ console.log("error");
342
+ haltReturns = true;
1593
343
  console.log(
1594
344
  ansiC.red(
1595
345
  ansiC.inverse(
1596
- `launchNodeSideCar - ${src} errored with: ${e.name}. Check logs for more info`
346
+ `${src} errored with: ${e.name}. Check error logs for more info`
1597
347
  )
1598
348
  )
1599
349
  );
1600
- logs.error?.write(e.toString() + "\n");
1601
- // this.bddTestIsNowDone(src, -1);
1602
- // statusMessagePretty(-1, src);
350
+ this.bddTestIsNowDone(src, -1);
351
+ statusMessagePretty(-1, src, "node");
352
+ reject(e);
1603
353
  });
1604
354
  });
355
+ }).finally(() => {
356
+ for (let i = 0; i <= portsToUse.length; i++) {
357
+ if (portsToUse[i]) {
358
+ this.ports[portsToUse[i]] = ""; //port is open again
359
+ }
360
+ }
361
+ });
362
+ })();
363
+
364
+ // Add to process manager
365
+ this.addPromiseProcess(
366
+ processId,
367
+ nodePromise,
368
+ command,
369
+ "bdd-test",
370
+ src,
371
+ "node"
372
+ );
373
+ };
1605
374
 
1606
- child.send({ path: p });
375
+ launchWeb = async (src: string, dest: string) => {
376
+ const processId = `web-${src}-${Date.now()}`;
377
+ const command = `web test: ${src}`;
378
+
379
+ // Create the promise
380
+ const webPromise = (async () => {
381
+ this.bddTestIsRunning(src);
382
+
383
+ const reportDest = `testeranto/reports/${this.name}/${src
384
+ .split(".")
385
+ .slice(0, -1)
386
+ .join(".")}/web`;
387
+ if (!fs.existsSync(reportDest)) {
388
+ fs.mkdirSync(reportDest, { recursive: true });
389
+ }
1607
390
 
1608
- const r = Math.random();
1609
- this.nodeSidecars[r] = child;
1610
- return [r, argz];
1611
- } else {
1612
- console.log(
1613
- ansiC.red(
1614
- `cannot ${src} because there are no open ports. the job will be unqueued`
1615
- )
1616
- );
391
+ const destFolder = dest.replace(".mjs", "");
1617
392
 
1618
- this.queue.push(sidecar[0]);
1619
- return [Math.random(), argz];
1620
- }
1621
- } else {
1622
- console.error("negative port makes no sense", sidecar[0]);
1623
- process.exit(-1);
1624
- }
1625
- };
393
+ const webArgz = JSON.stringify({
394
+ name: src,
395
+ ports: [].toString(),
396
+ fs: reportDest,
397
+ browserWSEndpoint: this.browser.wsEndpoint(),
398
+ });
399
+
400
+ const d = `${dest}?cacheBust=${Date.now()}`;
401
+
402
+ const logs = createLogStreams(reportDest, "web");
403
+
404
+ return new Promise<void>((resolve, reject) => {
405
+ this.browser
406
+ .newPage()
407
+ .then((page) => {
408
+ page.on("console", (log: ConsoleMessage) => {
409
+ const msg = `${log.text()}\n`;
410
+
411
+ switch (log.type()) {
412
+ case "info":
413
+ logs.info?.write(msg);
414
+ break;
415
+ case "warn":
416
+ logs.warn?.write(msg);
417
+ break;
418
+ case "error":
419
+ logs.error?.write(msg);
420
+ break;
421
+ case "debug":
422
+ logs.debug?.write(msg);
423
+ break;
424
+ default:
425
+ break;
426
+ }
427
+ });
428
+
429
+ page.on("close", () => {
430
+ logs.writeExitCode(0);
431
+ logs.closeAll();
432
+ });
433
+
434
+ this.mapping().forEach(async ([command, func]) => {
435
+ if (command === "page") {
436
+ page.exposeFunction(command, (x?) => {
437
+ if (x) {
438
+ return func(x);
439
+ } else {
440
+ return func(page.mainFrame()._id);
441
+ }
442
+ });
443
+ } else {
444
+ return page.exposeFunction(command, func);
445
+ }
446
+ });
447
+
448
+ return page;
449
+ })
450
+ .then(async (page) => {
451
+ const close = () => {
452
+ if (!files[src]) {
453
+ files[src] = new Set();
454
+ }
455
+
456
+ delete files[src];
1626
457
 
1627
- stopPureSideCar = async (uid: number) => {
1628
- console.log(ansiC.green(ansiC.inverse(`stopPureSideCar ${uid}`)));
1629
- await this.sidecars[uid].shutdown();
1630
- return;
458
+ Promise.all(screenshots[src] || []).then(() => {
459
+ delete screenshots[src];
460
+ page.close();
461
+ });
462
+ };
463
+
464
+ page.on("pageerror", (err: Error) => {
465
+ logs.writeExitCode(-1, err);
466
+ console.log(
467
+ ansiColors.red(
468
+ `web ! ${src} failed to execute No "tests.json" file was generated. Check ${reportDest}/error.log for more info`
469
+ )
470
+ );
471
+ this.bddTestIsNowDone(src, -1);
472
+ close();
473
+ reject(err);
474
+ });
475
+
476
+ await page.goto(`file://${`${destFolder}.html`}`, {});
477
+
478
+ await page
479
+ .evaluate(webEvaluator(d, webArgz))
480
+ .then(async ({ fails, failed, features }: IFinalResults) => {
481
+ statusMessagePretty(fails, src, "web");
482
+ this.bddTestIsNowDone(src, fails);
483
+ resolve();
484
+ })
485
+ .catch((e) => {
486
+ console.log(ansiC.red(ansiC.inverse(e.stack)));
487
+ console.log(
488
+ ansiC.red(
489
+ ansiC.inverse(
490
+ `web ! ${src} failed to execute. No "tests.json" file was generated. Check logs for more info`
491
+ )
492
+ )
493
+ );
494
+ this.bddTestIsNowDone(src, -1);
495
+ reject(e);
496
+ })
497
+ .finally(() => {
498
+ close();
499
+ });
500
+ })
501
+ .catch((error) => {
502
+ reject(error);
503
+ });
504
+ });
505
+ })();
506
+
507
+ // Add to process manager
508
+ this.addPromiseProcess(
509
+ processId,
510
+ webPromise,
511
+ command,
512
+ "bdd-test",
513
+ src,
514
+ "web"
515
+ );
1631
516
  };
1632
517
 
1633
- launchPureSideCar = async (
1634
- sidecar: ITestTypes
1635
- ): Promise<[number, ITTestResourceConfiguration]> => {
1636
- console.log(ansiC.green(ansiC.inverse(`launchPureSideCar ${sidecar[0]}`)));
518
+ launchPython = async (src: string, dest: string) => {
519
+ const processId = `python-${src}-${Date.now()}`;
520
+ const command = `python test: ${src}`;
1637
521
 
1638
- const r = Math.random();
522
+ const pythonPromise = (async () => {
523
+ this.bddTestIsRunning(src);
1639
524
 
1640
- const dest =
1641
- process.cwd() + `/testeranto/bundles/pure/${this.name}/${sidecar[0]}`;
1642
- const builtfile = dest.split(".").slice(0, -1).concat("mjs").join(".");
525
+ const reportDest = `testeranto/reports/${this.name}/${src
526
+ .split(".")
527
+ .slice(0, -1)
528
+ .join(".")}/python`;
1643
529
 
1644
- const destFolder = dest.replace(".mjs", "");
530
+ if (!fs.existsSync(reportDest)) {
531
+ fs.mkdirSync(reportDest, { recursive: true });
532
+ }
1645
533
 
1646
- let argz: ITTestResourceConfiguration;
534
+ let testResources = "";
1647
535
 
1648
- const z = sidecar[2];
1649
- const testConfigResource: { ports: number } = sidecar[2];
1650
- const src = sidecar[0];
536
+ const testConfig = this.configs.tests.find((t) => t[0] === src);
537
+ if (!testConfig) {
538
+ console.log(
539
+ ansiColors.inverse(
540
+ `missing test config! Exiting ungracefully for '${src}'`
541
+ )
542
+ );
543
+ process.exit(-1);
544
+ }
1651
545
 
1652
- const portsToUse: number[] = [];
1653
- if (testConfigResource.ports === 0) {
1654
- argz = {
1655
- // scheduled: true,
1656
- name: src,
1657
- ports: portsToUse,
1658
- fs: destFolder,
1659
- browserWSEndpoint: this.browser.wsEndpoint(),
1660
- };
1661
- } else if (testConfigResource.ports > 0) {
1662
- const openPorts = Object.entries(this.ports).filter(
1663
- ([portnumber, portopen]) => portopen === ""
1664
- );
1665
- if (openPorts.length >= testConfigResource.ports) {
1666
- for (let i = 0; i < testConfigResource.ports; i++) {
1667
- portsToUse.push(Number(openPorts[i][0]));
1668
-
1669
- this.ports[openPorts[i][0]] = src; // port is now claimed
1670
- }
546
+ const testConfigResource = testConfig[2];
547
+ const portsToUse: string[] = [];
1671
548
 
1672
- argz = {
1673
- // scheduled: true,
549
+ if (testConfigResource.ports === 0) {
550
+ testResources = JSON.stringify({
551
+ scheduled: true,
1674
552
  name: src,
1675
- // ports: [3333],
1676
553
  ports: portsToUse,
1677
- fs: ".",
554
+ fs: reportDest,
1678
555
  browserWSEndpoint: this.browser.wsEndpoint(),
1679
- };
556
+ });
557
+ } else if (testConfigResource.ports > 0) {
558
+ const openPorts = Object.entries(this.ports).filter(
559
+ ([, status]) => status === ""
560
+ );
561
+
562
+ if (openPorts.length >= testConfigResource.ports) {
563
+ for (let i = 0; i < testConfigResource.ports; i++) {
564
+ portsToUse.push(openPorts[i][0]);
565
+ this.ports[openPorts[i][0]] = src;
566
+ }
567
+
568
+ testResources = JSON.stringify({
569
+ scheduled: true,
570
+ name: src,
571
+ ports: portsToUse,
572
+ fs: reportDest,
573
+ browserWSEndpoint: this.browser.wsEndpoint(),
574
+ });
575
+ } else {
576
+ console.log(
577
+ ansiColors.red(
578
+ `python: cannot run ${src} because there are no open ports ATM. This job will be enqueued and run again when a port is available`
579
+ )
580
+ );
581
+ this.queue.push(src);
582
+ return;
583
+ }
1680
584
  } else {
1681
- this.queue.push(src);
1682
- // return;
585
+ console.error("negative port makes no sense", src);
586
+ process.exit(-1);
1683
587
  }
1684
- } else {
1685
- console.error("negative port makes no sense", src);
1686
- process.exit(-1);
1687
- }
1688
-
1689
- // const builtfile = dest + ".mjs";
1690
-
1691
- await import(`${builtfile}?cacheBust=${Date.now()}`).then((module) => {
1692
- if (!this.pureSidecars) this.pureSidecars = {};
1693
- this.pureSidecars[r] = module.default;
1694
- this.pureSidecars[r].start(argz);
1695
- });
1696
-
1697
- return [r, argz];
1698
- // for (let i = 0; i <= portsToUse.length; i++) {
1699
- // if (portsToUse[i]) {
1700
- // this.ports[portsToUse[i]] = "true"; //port is open again
1701
- // }
1702
- // }
1703
- };
1704
588
 
1705
- launchPitono = async (src: string, dest: string) => {
1706
- console.log(ansiC.green(ansiC.inverse(`pitono < ${src}`)));
1707
- this.bddTestIsRunning(src);
1708
-
1709
- const reportDest = `testeranto/reports/${this.name}/${src
1710
- .split(".")
1711
- .slice(0, -1)
1712
- .join(".")}/pitono`;
1713
- if (!fs.existsSync(reportDest)) {
1714
- fs.mkdirSync(reportDest, { recursive: true });
1715
- }
1716
-
1717
- const logs = createLogStreams(reportDest, "node"); // Use node-style logs for pitono
1718
-
1719
- try {
1720
- // Execute the Python test using the pitono runner
1721
- const { PitonoRunner } = await import('./pitonoRunner');
1722
- const runner = new PitonoRunner(this.configs, this.name);
1723
- await runner.run();
1724
-
1725
- this.bddTestIsNowDone(src, 0);
1726
- statusMessagePretty(0, src, "pitono");
1727
- } catch (error) {
1728
- logs.writeExitCode(-1, error);
1729
- console.log(
1730
- ansiC.red(
1731
- ansiC.inverse(
1732
- `${src} errored with: ${error}. Check logs for more info`
1733
- )
1734
- )
1735
- );
1736
- this.bddTestIsNowDone(src, -1);
1737
- statusMessagePretty(-1, src, "pitono");
1738
- }
1739
- };
589
+ const logs = createLogStreams(reportDest, "python");
1740
590
 
1741
- launchWeb = async (src: string, dest: string) => {
1742
- console.log(ansiC.green(ansiC.inverse(`web < ${src}`)));
1743
- this.bddTestIsRunning(src);
1744
-
1745
- const reportDest = `testeranto/reports/${this.name}/${src
1746
- .split(".")
1747
- .slice(0, -1)
1748
- .join(".")}/web`;
1749
- if (!fs.existsSync(reportDest)) {
1750
- fs.mkdirSync(reportDest, { recursive: true });
1751
- }
1752
-
1753
- const destFolder = dest.replace(".mjs", "");
1754
-
1755
- const webArgz = JSON.stringify({
1756
- name: src,
1757
- ports: [].toString(),
1758
- fs: reportDest,
1759
- browserWSEndpoint: this.browser.wsEndpoint(),
1760
- });
1761
-
1762
- const d = `${dest}?cacheBust=${Date.now()}`;
1763
-
1764
- const logs = createLogStreams(reportDest, "web");
1765
-
1766
- this.browser
1767
- .newPage()
1768
- .then((page) => {
1769
- page.on("console", (log: ConsoleMessage) => {
1770
- const msg = `${log.text()}\n`;
1771
-
1772
- switch (log.type()) {
1773
- case "info":
1774
- logs.info?.write(msg);
1775
- break;
1776
- case "warn":
1777
- logs.warn?.write(msg);
1778
- break;
1779
- case "error":
1780
- logs.error?.write(msg);
1781
- break;
1782
- case "debug":
1783
- logs.debug?.write(msg);
1784
- break;
1785
- default:
1786
- break;
1787
- }
591
+ // For Python, we'll just run the script directly and pass test resources as an argument
592
+ // Python tests need to handle their own IPC if needed
593
+ const child = spawn("python3", [src, testResources], {
594
+ stdio: ["pipe", "pipe", "pipe"],
595
+ });
596
+
597
+ return new Promise<void>((resolve, reject) => {
598
+ child.stdout?.on("data", (data) => {
599
+ logs.stdout?.write(data);
1788
600
  });
1789
601
 
1790
- page.on("close", () => {
1791
- logs.writeExitCode(0); // Web tests exit with 0 unless there's an error
1792
- logs.closeAll();
1793
- logs.closeAll();
602
+ child.stderr?.on("data", (data) => {
603
+ logs.stderr?.write(data);
1794
604
  });
1795
- this.mapping().forEach(async ([command, func]) => {
1796
- if (command === "page") {
1797
- page.exposeFunction(command, (x?) => {
1798
- if (x) {
1799
- return func(x);
1800
- } else {
1801
- return func(page.mainFrame()._id);
1802
- }
1803
- });
605
+
606
+ child.on("close", (code) => {
607
+ const exitCode = code === null ? -1 : code;
608
+ if (exitCode < 0) {
609
+ logs.writeExitCode(
610
+ exitCode,
611
+ new Error("Process crashed or was terminated")
612
+ );
1804
613
  } else {
1805
- return page.exposeFunction(command, func);
614
+ logs.writeExitCode(exitCode);
1806
615
  }
1807
- });
616
+ logs.closeAll();
1808
617
 
1809
- return page;
1810
- })
1811
- .then(async (page) => {
1812
- const close = () => {
1813
- if (!files[src]) {
1814
- files[src] = new Set();
618
+ if (exitCode === 0) {
619
+ this.bddTestIsNowDone(src, 0);
620
+ statusMessagePretty(0, src, "python");
621
+ resolve();
622
+ } else {
623
+ console.log(
624
+ ansiColors.red(
625
+ `python ! ${src} failed to execute. Check ${reportDest}/stderr.log for more info`
626
+ )
627
+ );
628
+ this.bddTestIsNowDone(src, exitCode);
629
+ statusMessagePretty(exitCode, src, "python");
630
+ reject(new Error(`Process exited with code ${exitCode}`));
1815
631
  }
1816
- // files[t].add(filepath);
1817
-
1818
- // fs.writeFileSync(
1819
- // destFolder + "/manifest.json",
1820
- // JSON.stringify(Array.from(files[src]))
1821
- // );
1822
- delete files[src];
1823
-
1824
- Promise.all(screenshots[src] || []).then(() => {
1825
- delete screenshots[src];
1826
- page.close();
1827
- });
1828
-
1829
- return;
1830
- };
632
+ });
1831
633
 
1832
- page.on("pageerror", (err: Error) => {
1833
- logs.writeExitCode(-1, err);
634
+ child.on("error", (e) => {
1834
635
  console.log(
1835
636
  ansiColors.red(
1836
- `web ! ${src} failed to execute No "tests.json" file was generated. Check ${reportDest}/error.log for more info`
637
+ ansiColors.inverse(
638
+ `python: ${src} errored with: ${e.name}. Check error logs for more info`
639
+ )
1837
640
  )
1838
641
  );
1839
642
  this.bddTestIsNowDone(src, -1);
1840
- close();
643
+ statusMessagePretty(-1, src, "python");
644
+ reject(e);
645
+ });
646
+ }).finally(() => {
647
+ portsToUse.forEach(port => {
648
+ this.ports[port] = "";
1841
649
  });
1842
-
1843
- // page.on("console", (log: ConsoleMessage) => {});
1844
-
1845
- await page.goto(`file://${`${destFolder}.html`}`, {});
1846
-
1847
- await page
1848
- .evaluate(
1849
- `
1850
- import('${d}').then(async (x) => {
1851
- try {
1852
- return await (await x.default).receiveTestResourceConfig(${webArgz})
1853
- } catch (e) {
1854
- console.log("web run failure", e.toString())
1855
- }
1856
- })
1857
- `
1858
- )
1859
- .then(async ({ fails, failed, features }: IFinalResults) => {
1860
- statusMessagePretty(fails, src, "web");
1861
- this.bddTestIsNowDone(src, fails);
1862
- // close();
1863
- })
1864
- .catch((e) => {
1865
- console.log(ansiC.red(ansiC.inverse(e.stack)));
1866
-
1867
- console.log(
1868
- ansiC.red(
1869
- ansiC.inverse(
1870
- `web ! ${src} failed to execute. No "tests.json" file was generated. Check logs for more info`
1871
- )
1872
- )
1873
- );
1874
- this.bddTestIsNowDone(src, -1);
1875
- })
1876
- .finally(() => {
1877
- // process.exit(-1);
1878
- close();
1879
- });
1880
-
1881
- return page;
1882
650
  });
1883
- };
1884
-
1885
- receiveFeaturesV2 = (
1886
- reportDest: string,
1887
- srcTest: string,
1888
- platform: IRunTime
1889
- ) => {
1890
- const featureDestination = path.resolve(
1891
- process.cwd(),
1892
- "reports",
1893
- "features",
1894
- "strings",
1895
- srcTest.split(".").slice(0, -1).join(".") + ".features.txt"
651
+ })();
652
+
653
+ this.addPromiseProcess(
654
+ processId,
655
+ pythonPromise,
656
+ command,
657
+ "bdd-test",
658
+ src,
659
+ "python"
1896
660
  );
661
+ };
1897
662
 
1898
- // Read and parse the test report
1899
- const testReportPath = `${reportDest}/tests.json`;
1900
- if (!fs.existsSync(testReportPath)) {
1901
- console.error(`tests.json not found at: ${testReportPath}`);
1902
- return;
1903
- }
1904
-
1905
- const testReport = JSON.parse(fs.readFileSync(testReportPath, "utf8"));
663
+ launchGolang = async (src: string, dest: string) => {
664
+ const processId = `golang-${src}-${Date.now()}`;
665
+ const command = `golang test: ${src}`;
1906
666
 
1907
- // Add full path information to each test
1908
- if (testReport.tests) {
1909
- testReport.tests.forEach((test) => {
1910
- // Add the full path to each test
1911
- test.fullPath = path.resolve(process.cwd(), srcTest);
1912
- });
1913
- }
1914
-
1915
- // Add full path to the report itself
1916
- testReport.fullPath = path.resolve(process.cwd(), srcTest);
1917
-
1918
- // Write the modified report back
1919
- fs.writeFileSync(testReportPath, JSON.stringify(testReport, null, 2));
1920
-
1921
- testReport.features
1922
- .reduce(async (mm, featureStringKey) => {
1923
- const accum = await mm;
1924
-
1925
- const isUrl = isValidUrl(featureStringKey);
1926
-
1927
- if (isUrl) {
1928
- const u = new URL(featureStringKey);
1929
-
1930
- if (u.protocol === "file:") {
1931
- const newPath = `${process.cwd()}/testeranto/features/internal/${path.relative(
1932
- process.cwd(),
1933
- u.pathname
1934
- )}`;
1935
-
1936
- // await fs.promises.mkdir(path.dirname(newPath), { recursive: true });
1937
-
1938
- // try {
1939
- // await fs.unlinkSync(newPath);
1940
- // // console.log(`Removed existing link at ${newPath}`);
1941
- // } catch (error) {
1942
- // if (error.code !== "ENOENT") {
1943
- // // throw error;
1944
- // }
1945
- // }
1946
-
1947
- // fs.symlink(u.pathname, newPath, (err) => {
1948
- // if (err) {
1949
- // // console.error("Error creating symlink:", err);
1950
- // } else {
1951
- // // console.log("Symlink created successfully");
1952
- // }
1953
- // });
1954
- accum.files.push(u.pathname);
1955
- } else if (u.protocol === "http:" || u.protocol === "https:") {
1956
- const newPath = `${process.cwd()}/testeranto/features/external/${
1957
- u.hostname
1958
- }${u.pathname}`;
1959
-
1960
- const body = await this.configs.featureIngestor(featureStringKey);
1961
-
1962
- writeFileAndCreateDir(newPath, body);
1963
- accum.files.push(newPath);
1964
- }
1965
- } else {
1966
- await fs.promises.mkdir(path.dirname(featureDestination), {
1967
- recursive: true,
1968
- });
667
+ const golangPromise = (async () => {
668
+ this.bddTestIsRunning(src);
1969
669
 
1970
- accum.strings.push(featureStringKey);
1971
- }
670
+ const reportDest = `testeranto/reports/${this.name}/${src
671
+ .split(".")
672
+ .slice(0, -1)
673
+ .join(".")}/golang`;
1972
674
 
1973
- return accum;
1974
- }, Promise.resolve({ files: [] as string[], strings: [] as string[] }))
675
+ if (!fs.existsSync(reportDest)) {
676
+ fs.mkdirSync(reportDest, { recursive: true });
677
+ }
1975
678
 
1976
- .then(({ files, strings }: { files: string[]; strings: string[] }) => {
1977
- // Markdown files must be referenced in the prompt but string style features are already present in the tests.json file
679
+ let testResources = "";
1978
680
 
1979
- fs.writeFileSync(
1980
- `testeranto/reports/${this.name}/${srcTest
1981
- .split(".")
1982
- .slice(0, -1)
1983
- .join(".")}/${platform}/featurePrompt.txt`,
1984
- files
1985
- .map((f) => {
1986
- return `/read ${f}`;
1987
- })
1988
- .join("\n")
681
+ const testConfig = this.configs.tests.find((t) => t[0] === src);
682
+ if (!testConfig) {
683
+ console.log(
684
+ ansiColors.inverse(
685
+ `golang: missing test config! Exiting ungracefully for '${src}'`
686
+ )
1989
687
  );
1990
- });
1991
-
1992
- // const f: Record<string, string> = {};
1993
-
1994
- testReport.givens.forEach((g) => {
1995
- if (g.failed === true) {
1996
- this.summary[srcTest].failingFeatures[g.key] = g.features;
688
+ process.exit(-1);
1997
689
  }
1998
- });
1999
690
 
2000
- // this.summary[srcTest].failingFeatures = f;
2001
- this.writeBigBoard();
2002
- };
691
+ const testConfigResource = testConfig[2];
692
+ const portsToUse: string[] = [];
2003
693
 
2004
- requestHandler(req: http.IncomingMessage, res: http.ServerResponse) {
2005
- // Parse the URL
2006
- const parsedUrl = url.parse(req.url || "/");
2007
- let pathname = parsedUrl.pathname || "/";
2008
-
2009
- // Handle root path
2010
- if (pathname === "/") {
2011
- pathname = "/index.html";
2012
- }
2013
-
2014
- // Remove leading slash
2015
- let filePath = pathname.substring(1);
2016
-
2017
- // Determine which directory to serve from
2018
- if (filePath.startsWith("reports/")) {
2019
- // Serve from reports directory
2020
- filePath = `testeranto/${filePath}`;
2021
- } else if (filePath.startsWith("metafiles/")) {
2022
- // Serve from metafiles directory
2023
- filePath = `testeranto/${filePath}`;
2024
- } else if (filePath === "projects.json") {
2025
- // Serve projects.json
2026
- filePath = `testeranto/${filePath}`;
2027
- } else {
2028
- // For frontend assets, try multiple possible locations
2029
- // First, try the dist directory
2030
- const possiblePaths = [
2031
- `dist/${filePath}`,
2032
- `testeranto/dist/${filePath}`,
2033
- `../dist/${filePath}`,
2034
- `./${filePath}`,
2035
- ];
2036
-
2037
- // Find the first existing file
2038
- let foundPath = null;
2039
- for (const possiblePath of possiblePaths) {
2040
- if (fs.existsSync(possiblePath)) {
2041
- foundPath = possiblePath;
2042
- break;
2043
- }
2044
- }
694
+ if (testConfigResource.ports === 0) {
695
+ testResources = JSON.stringify({
696
+ scheduled: true,
697
+ name: src,
698
+ ports: portsToUse,
699
+ fs: reportDest,
700
+ browserWSEndpoint: this.browser.wsEndpoint(),
701
+ });
702
+ } else if (testConfigResource.ports > 0) {
703
+ const openPorts = Object.entries(this.ports).filter(
704
+ ([, status]) => status === ""
705
+ );
2045
706
 
2046
- if (foundPath) {
2047
- filePath = foundPath;
2048
- } else {
2049
- // If no file found, serve index.html for SPA routing
2050
- const indexPath = this.findIndexHtml();
2051
- if (indexPath) {
2052
- fs.readFile(indexPath, (err, data) => {
2053
- if (err) {
2054
- res.writeHead(404, { "Content-Type": "text/plain" });
2055
- res.end("404 Not Found");
2056
- return;
2057
- }
2058
- res.writeHead(200, { "Content-Type": "text/html" });
2059
- res.end(data);
707
+ if (openPorts.length >= testConfigResource.ports) {
708
+ for (let i = 0; i < testConfigResource.ports; i++) {
709
+ portsToUse.push(openPorts[i][0]);
710
+ this.ports[openPorts[i][0]] = src;
711
+ }
712
+
713
+ testResources = JSON.stringify({
714
+ scheduled: true,
715
+ name: src,
716
+ ports: portsToUse,
717
+ fs: reportDest,
718
+ browserWSEndpoint: this.browser.wsEndpoint(),
2060
719
  });
2061
- return;
2062
720
  } else {
2063
- res.writeHead(404, { "Content-Type": "text/plain" });
2064
- res.end("404 Not Found");
721
+ console.log(
722
+ ansiColors.red(
723
+ `golang: cannot run ${src} because there are no open ports ATM. This job will be enqueued and run again when a port is available`
724
+ )
725
+ );
726
+ this.queue.push(src);
2065
727
  return;
2066
728
  }
729
+ } else {
730
+ console.error("negative port makes no sense", src);
731
+ process.exit(-1);
2067
732
  }
2068
- }
2069
-
2070
- // Check if file exists
2071
- fs.exists(filePath, (exists) => {
2072
- if (!exists) {
2073
- // For SPA routing, serve index.html if the path looks like a route
2074
- if (!pathname.includes(".") && pathname !== "/") {
2075
- const indexPath = this.findIndexHtml();
2076
- if (indexPath) {
2077
- fs.readFile(indexPath, (err, data) => {
2078
- if (err) {
2079
- res.writeHead(404, { "Content-Type": "text/plain" });
2080
- res.end("404 Not Found");
2081
- return;
2082
- }
2083
- res.writeHead(200, { "Content-Type": "text/html" });
2084
- res.end(data);
2085
- });
2086
- return;
2087
- } else {
2088
- // Serve a simple message if index.html is not found
2089
- res.writeHead(200, { "Content-Type": "text/html" });
2090
- res.end(`
2091
- <html>
2092
- <body>
2093
- <h1>Testeranto is running</h1>
2094
- <p>Frontend files are not built yet. Run 'npm run build' to build the frontend.</p>
2095
- </body>
2096
- </html>
2097
- `);
2098
- return;
2099
- }
2100
- }
2101
- res.writeHead(404, { "Content-Type": "text/plain" });
2102
- res.end("404 Not Found");
2103
- return;
2104
- }
2105
-
2106
- // Read and serve the file
2107
- fs.readFile(filePath, (err, data) => {
2108
- if (err) {
2109
- res.writeHead(500, { "Content-Type": "text/plain" });
2110
- res.end("500 Internal Server Error");
2111
- return;
2112
- }
2113
733
 
2114
- // Get MIME type
2115
- const mimeType = mime.lookup(filePath) || "application/octet-stream";
2116
- res.writeHead(200, { "Content-Type": mimeType });
2117
- res.end(data);
2118
- });
2119
- });
2120
- }
2121
-
2122
- findIndexHtml(): string | null {
2123
- const possiblePaths = [
2124
- "dist/index.html",
2125
- "testeranto/dist/index.html",
2126
- "../dist/index.html",
2127
- "./index.html",
2128
- ];
2129
-
2130
- for (const path of possiblePaths) {
2131
- if (fs.existsSync(path)) {
2132
- return path;
2133
- }
2134
- }
2135
- return null;
2136
- }
2137
-
2138
- broadcast(message: any) {
2139
- const data =
2140
- typeof message === "string" ? message : JSON.stringify(message);
2141
- this.clients.forEach((client) => {
2142
- if (client.readyState === 1) {
2143
- // WebSocket.OPEN
2144
- client.send(data);
2145
- }
2146
- });
2147
- }
2148
-
2149
- checkQueue() {
2150
- const x = this.queue.pop();
2151
- if (!x) {
2152
- ansiC.inverse(`The following queue is empty`);
2153
- return;
2154
- }
2155
- const test = this.configs.tests.find((t) => t[0] === x);
2156
- if (!test) throw `test is undefined ${x}`;
2157
-
2158
- // const [src, runtime, ...xx]: [string, IRunTime, ...any] = test;
2159
- this.launchers[test[0]]();
2160
- }
2161
-
2162
- checkForShutdown = () => {
2163
- // console.log(ansiC.inverse(JSON.stringify(this.summary, null, 2)));
2164
-
2165
- this.checkQueue();
2166
-
2167
- console.log(
2168
- ansiC.inverse(
2169
- `The following jobs are awaiting resources: ${JSON.stringify(
2170
- this.queue
2171
- )}`
2172
- )
2173
- );
2174
- console.log(
2175
- ansiC.inverse(`The status of ports: ${JSON.stringify(this.ports)}`)
2176
- );
734
+ // Compile the Go test first
735
+ const buildDir = path.dirname(dest);
736
+ const binaryName = path.basename(dest, '.go');
737
+ const binaryPath = path.join(buildDir, binaryName);
738
+
739
+ const logs = createLogStreams(reportDest, "golang");
2177
740
 
2178
- this.writeBigBoard();
741
+ // First, compile the Go program
742
+ const compileProcess = spawn("go", ["build", "-o", binaryPath, dest]);
743
+
744
+ return new Promise<void>((resolve, reject) => {
745
+ compileProcess.stdout?.on("data", (data) => {
746
+ logs.stdout?.write(data);
747
+ });
2179
748
 
2180
- if (this.mode === "dev") return;
749
+ compileProcess.stderr?.on("data", (data) => {
750
+ logs.stderr?.write(data);
751
+ });
2181
752
 
2182
- let inflight = false;
753
+ compileProcess.on("close", (compileCode) => {
754
+ if (compileCode !== 0) {
755
+ console.log(
756
+ ansiColors.red(
757
+ `golang ! ${src} failed to compile. Check ${reportDest}/stderr.log for more info`
758
+ )
759
+ );
760
+ this.bddTestIsNowDone(src, compileCode || -1);
761
+ statusMessagePretty(compileCode || -1, src, "golang");
762
+ reject(new Error(`Compilation failed with code ${compileCode}`));
763
+ return;
764
+ }
2183
765
 
2184
- Object.keys(this.summary).forEach((k) => {
2185
- if (this.summary[k].prompt === "?") {
2186
- console.log(ansiC.blue(ansiC.inverse(`🕕 prompt ${k}`)));
2187
- inflight = true;
2188
- }
2189
- });
766
+ // Now run the compiled binary
767
+ const child = spawn(binaryPath, [testResources], {
768
+ stdio: ["pipe", "pipe", "pipe"],
769
+ });
2190
770
 
2191
- Object.keys(this.summary).forEach((k) => {
2192
- if (this.summary[k].runTimeErrors === "?") {
2193
- console.log(ansiC.blue(ansiC.inverse(`🕕 runTimeError ${k}`)));
2194
- inflight = true;
2195
- }
2196
- });
771
+ child.stdout?.on("data", (data) => {
772
+ logs.stdout?.write(data);
773
+ });
2197
774
 
2198
- Object.keys(this.summary).forEach((k) => {
2199
- if (this.summary[k].staticErrors === "?") {
2200
- console.log(ansiC.blue(ansiC.inverse(`🕕 staticErrors ${k}`)));
2201
- inflight = true;
2202
- }
2203
- });
775
+ child.stderr?.on("data", (data) => {
776
+ logs.stderr?.write(data);
777
+ });
2204
778
 
2205
- Object.keys(this.summary).forEach((k) => {
2206
- if (this.summary[k].typeErrors === "?") {
2207
- console.log(ansiC.blue(ansiC.inverse(`🕕 typeErrors ${k}`)));
2208
- inflight = true;
2209
- }
2210
- });
779
+ child.on("close", (code) => {
780
+ const exitCode = code === null ? -1 : code;
781
+ if (exitCode < 0) {
782
+ logs.writeExitCode(
783
+ exitCode,
784
+ new Error("Process crashed or was terminated")
785
+ );
786
+ } else {
787
+ logs.writeExitCode(exitCode);
788
+ }
789
+ logs.closeAll();
2211
790
 
2212
- this.writeBigBoard();
791
+ if (exitCode === 0) {
792
+ this.bddTestIsNowDone(src, 0);
793
+ statusMessagePretty(0, src, "golang");
794
+ resolve();
795
+ } else {
796
+ console.log(
797
+ ansiColors.red(
798
+ `golang ! ${src} failed to execute. Check ${reportDest}/stderr.log for more info`
799
+ )
800
+ );
801
+ this.bddTestIsNowDone(src, exitCode);
802
+ statusMessagePretty(exitCode, src, "golang");
803
+ reject(new Error(`Process exited with code ${exitCode}`));
804
+ }
805
+ });
2213
806
 
2214
- if (!inflight) {
2215
- if (this.browser) {
2216
- if (this.browser) {
2217
- this.browser.disconnect().then(() => {
807
+ child.on("error", (e) => {
2218
808
  console.log(
2219
- ansiC.inverse(`${this.name} has been tested. Goodbye.`)
809
+ ansiColors.red(
810
+ ansiColors.inverse(
811
+ `golang: ${src} errored with: ${e.name}. Check error logs for more info`
812
+ )
813
+ )
2220
814
  );
2221
- process.exit();
815
+ this.bddTestIsNowDone(src, -1);
816
+ statusMessagePretty(-1, src, "golang");
817
+ reject(e);
2222
818
  });
2223
- }
2224
- }
2225
- }
819
+ });
820
+
821
+ compileProcess.on("error", (e) => {
822
+ console.log(
823
+ ansiColors.red(
824
+ ansiColors.inverse(
825
+ `golang: ${src} compilation errored with: ${e.name}. Check error logs for more info`
826
+ )
827
+ )
828
+ );
829
+ this.bddTestIsNowDone(src, -1);
830
+ statusMessagePretty(-1, src, "golang");
831
+ reject(e);
832
+ });
833
+ }).finally(() => {
834
+ portsToUse.forEach(port => {
835
+ this.ports[port] = "";
836
+ });
837
+ });
838
+ })();
839
+
840
+ this.addPromiseProcess(
841
+ processId,
842
+ golangPromise,
843
+ command,
844
+ "bdd-test",
845
+ src,
846
+ "golang"
847
+ );
2226
848
  };
2227
849
  }