@neurodevs/node-tdd 0.2.5 → 1.0.1

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 (326) hide show
  1. package/.vscode/launch.json +52 -57
  2. package/.vscode/settings.json +61 -66
  3. package/.vscode/tasks.json +98 -129
  4. package/README.md +1 -0
  5. package/build/.spruce/settings.json +3 -10
  6. package/build/__tests__/MockFetch.d.ts +18 -0
  7. package/build/__tests__/MockFetch.js +55 -0
  8. package/build/__tests__/MockFetch.js.map +1 -0
  9. package/build/__tests__/{impl → behavioral}/AbstractModuleTest.test.d.ts +2 -3
  10. package/build/__tests__/{impl → behavioral}/AbstractModuleTest.test.js +3 -12
  11. package/build/__tests__/behavioral/AbstractModuleTest.test.js.map +1 -0
  12. package/build/__tests__/behavioral/utilities/AllHooksCalledEvenIfNotDefined.test.d.ts +5 -0
  13. package/build/__tests__/behavioral/utilities/AllHooksCalledEvenIfNotDefined.test.js +34 -0
  14. package/build/__tests__/behavioral/utilities/AllHooksCalledEvenIfNotDefined.test.js.map +1 -0
  15. package/build/__tests__/behavioral/utilities/Assert.test.d.ts +45 -0
  16. package/build/__tests__/behavioral/utilities/Assert.test.js +645 -0
  17. package/build/__tests__/behavioral/utilities/Assert.test.js.map +1 -0
  18. package/build/__tests__/behavioral/utilities/InstanceParentTestCanAccessParentMethods.test.d.ts +4 -0
  19. package/build/__tests__/behavioral/utilities/InstanceParentTestCanAccessParentMethods.test.js +23 -0
  20. package/build/__tests__/behavioral/utilities/InstanceParentTestCanAccessParentMethods.test.js.map +1 -0
  21. package/build/__tests__/behavioral/utilities/MockFetch.test.d.ts +28 -0
  22. package/build/__tests__/behavioral/utilities/MockFetch.test.js +211 -0
  23. package/build/__tests__/behavioral/utilities/MockFetch.test.js.map +1 -0
  24. package/build/__tests__/behavioral/utilities/StackCleaner.test.d.ts +4 -0
  25. package/build/__tests__/behavioral/utilities/StackCleaner.test.js +46 -0
  26. package/build/__tests__/behavioral/utilities/StackCleaner.test.js.map +1 -0
  27. package/build/__tests__/behavioral/utilities/StaticParentTestCanAccessParentMethods.test.d.ts +7 -0
  28. package/build/__tests__/behavioral/utilities/StaticParentTestCanAccessParentMethods.test.js +32 -0
  29. package/build/__tests__/behavioral/utilities/StaticParentTestCanAccessParentMethods.test.js.map +1 -0
  30. package/build/__tests__/behavioral/utilities/StaticTestInheritsAbstractSpruceTestProperly.test.d.ts +4 -0
  31. package/build/__tests__/behavioral/utilities/StaticTestInheritsAbstractSpruceTestProperly.test.js +19 -0
  32. package/build/__tests__/behavioral/utilities/StaticTestInheritsAbstractSpruceTestProperly.test.js.map +1 -0
  33. package/build/__tests__/behavioral/utilities/Stringify.test.d.ts +5 -0
  34. package/build/__tests__/behavioral/utilities/Stringify.test.js +127 -0
  35. package/build/__tests__/behavioral/utilities/Stringify.test.js.map +1 -0
  36. package/build/__tests__/behavioral/utilities/TestDecoratorResolver.test.d.ts +7 -0
  37. package/build/__tests__/behavioral/utilities/TestDecoratorResolver.test.js +38 -0
  38. package/build/__tests__/behavioral/utilities/TestDecoratorResolver.test.js.map +1 -0
  39. package/build/__tests__/behavioral/utilities/TestOnBasic.test.d.ts +12 -0
  40. package/build/__tests__/behavioral/utilities/TestOnBasic.test.js +68 -0
  41. package/build/__tests__/behavioral/utilities/TestOnBasic.test.js.map +1 -0
  42. package/build/__tests__/behavioral/utilities/TestOnInstance.test.d.ts +28 -0
  43. package/build/__tests__/behavioral/utilities/TestOnInstance.test.js +289 -0
  44. package/build/__tests__/behavioral/utilities/TestOnInstance.test.js.map +1 -0
  45. package/build/__tests__/behavioral/utilities/TestOnInstanceExtendsTest.test.d.ts +9 -0
  46. package/build/__tests__/behavioral/utilities/TestOnInstanceExtendsTest.test.js +118 -0
  47. package/build/__tests__/behavioral/utilities/TestOnInstanceExtendsTest.test.js.map +1 -0
  48. package/build/__tests__/behavioral/utilities/TestOnInstanceExtendsTestWithHooks.test.d.ts +5 -0
  49. package/build/__tests__/behavioral/utilities/TestOnInstanceExtendsTestWithHooks.test.js +78 -0
  50. package/build/__tests__/behavioral/utilities/TestOnInstanceExtendsTestWithHooks.test.js.map +1 -0
  51. package/build/__tests__/behavioral/utilities/TestOnInstanceWithParentBeforeAll.test.d.ts +4 -0
  52. package/build/__tests__/behavioral/utilities/TestOnInstanceWithParentBeforeAll.test.js +23 -0
  53. package/build/__tests__/behavioral/utilities/TestOnInstanceWithParentBeforeAll.test.js.map +1 -0
  54. package/build/__tests__/behavioral/utilities/TestOnInstanceWithTwoLevelsOfInheritence.test.d.ts +4 -0
  55. package/build/__tests__/behavioral/utilities/TestOnInstanceWithTwoLevelsOfInheritence.test.js +24 -0
  56. package/build/__tests__/behavioral/utilities/TestOnInstanceWithTwoLevelsOfInheritence.test.js.map +1 -0
  57. package/build/__tests__/behavioral/utilities/TestResolver.test.d.ts +7 -0
  58. package/build/__tests__/behavioral/utilities/TestResolver.test.js +38 -0
  59. package/build/__tests__/behavioral/utilities/TestResolver.test.js.map +1 -0
  60. package/build/__tests__/behavioral/utilities/TestResolverOnStatic.test.d.ts +4 -0
  61. package/build/__tests__/behavioral/utilities/TestResolverOnStatic.test.js +20 -0
  62. package/build/__tests__/behavioral/utilities/TestResolverOnStatic.test.js.map +1 -0
  63. package/build/__tests__/behavioral/workspace/JestJsonParser.test.d.ts +18 -0
  64. package/build/__tests__/behavioral/workspace/JestJsonParser.test.js +267 -0
  65. package/build/__tests__/behavioral/workspace/JestJsonParser.test.js.map +1 -0
  66. package/build/__tests__/behavioral/workspace/TestReporter.test.d.ts +16 -0
  67. package/build/__tests__/behavioral/workspace/TestReporter.test.js +227 -0
  68. package/build/__tests__/behavioral/workspace/TestReporter.test.js.map +1 -0
  69. package/build/__tests__/behavioral/workspace/TestRunner.test.d.ts +15 -0
  70. package/build/__tests__/behavioral/workspace/TestRunner.test.js +99 -0
  71. package/build/__tests__/behavioral/workspace/TestRunner.test.js.map +1 -0
  72. package/build/__tests__/behavioral/workspace/Widgets.test.d.ts +13 -0
  73. package/build/__tests__/behavioral/workspace/Widgets.test.js +99 -0
  74. package/build/__tests__/behavioral/workspace/Widgets.test.js.map +1 -0
  75. package/build/__tests__/mock-terminal-kit.d.ts +79 -0
  76. package/build/__tests__/mock-terminal-kit.js +103 -0
  77. package/build/__tests__/mock-terminal-kit.js.map +1 -0
  78. package/build/__tests__/support/AbstractForInstanceTest.d.ts +4 -0
  79. package/build/__tests__/support/AbstractForInstanceTest.js +9 -0
  80. package/build/__tests__/support/AbstractForInstanceTest.js.map +1 -0
  81. package/build/__tests__/support/AbstractLevelOneTest.d.ts +3 -0
  82. package/build/__tests__/support/AbstractLevelOneTest.js +4 -0
  83. package/build/__tests__/support/AbstractLevelOneTest.js.map +1 -0
  84. package/build/__tests__/support/AbstractLevelTwoTest.d.ts +7 -0
  85. package/build/__tests__/support/AbstractLevelTwoTest.js +14 -0
  86. package/build/__tests__/support/AbstractLevelTwoTest.js.map +1 -0
  87. package/build/__tests__/support/AbstractStaticTest.d.ts +7 -0
  88. package/build/__tests__/support/AbstractStaticTest.js +14 -0
  89. package/build/__tests__/support/AbstractStaticTest.js.map +1 -0
  90. package/build/__tests__/support/AbstractTestOnInstanceTest.d.ts +22 -0
  91. package/build/__tests__/support/AbstractTestOnInstanceTest.js +23 -0
  92. package/build/__tests__/support/AbstractTestOnInstanceTest.js.map +1 -0
  93. package/build/__tests__/support/AbstractTestOnInstanceWithHooks.d.ts +26 -0
  94. package/build/__tests__/support/AbstractTestOnInstanceWithHooks.js +59 -0
  95. package/build/__tests__/support/AbstractTestOnInstanceWithHooks.js.map +1 -0
  96. package/build/__tests__/support/jest.setup.d.ts +1 -0
  97. package/build/__tests__/support/jest.setup.js +4 -0
  98. package/build/__tests__/support/jest.setup.js.map +1 -0
  99. package/build/__tests__/support/onTestFileResult.d.ts +244 -0
  100. package/build/__tests__/support/onTestFileResult.js +980 -0
  101. package/build/__tests__/support/onTestFileResult.js.map +1 -0
  102. package/build/impl/AbstractModuleTest.d.ts +18 -4
  103. package/build/impl/AbstractModuleTest.js +47 -9
  104. package/build/impl/AbstractModuleTest.js.map +1 -1
  105. package/build/index.d.ts +4 -4
  106. package/build/index.js +4 -4
  107. package/build/index.js.map +1 -1
  108. package/build/utilities/AssertionError.d.ts +3 -0
  109. package/build/utilities/AssertionError.js +12 -0
  110. package/build/utilities/AssertionError.js.map +1 -0
  111. package/build/utilities/StackCleaner.d.ts +4 -0
  112. package/build/utilities/StackCleaner.js +10 -0
  113. package/build/utilities/StackCleaner.js.map +1 -0
  114. package/build/utilities/TestDecoratorResolver.d.ts +35 -0
  115. package/build/utilities/TestDecoratorResolver.js +93 -0
  116. package/build/utilities/TestDecoratorResolver.js.map +1 -0
  117. package/build/utilities/assert.d.ts +49 -0
  118. package/build/utilities/assert.js +255 -0
  119. package/build/utilities/assert.js.map +1 -0
  120. package/build/utilities/assert.utility.d.ts +28 -0
  121. package/build/utilities/assert.utility.js +185 -0
  122. package/build/utilities/assert.utility.js.map +1 -0
  123. package/build/utilities/decorators.d.ts +9 -0
  124. package/build/utilities/decorators.js +133 -0
  125. package/build/utilities/decorators.js.map +1 -0
  126. package/build/workspace/CommandService.d.ts +49 -0
  127. package/build/workspace/CommandService.js +179 -0
  128. package/build/workspace/CommandService.js.map +1 -0
  129. package/build/workspace/JestJsonParser.d.ts +15 -0
  130. package/build/workspace/JestJsonParser.js +149 -0
  131. package/build/workspace/JestJsonParser.js.map +1 -0
  132. package/build/workspace/TKButtonWidget.d.ts +13 -0
  133. package/build/workspace/TKButtonWidget.js +37 -0
  134. package/build/workspace/TKButtonWidget.js.map +1 -0
  135. package/build/workspace/TestLogItemGenerator.d.ts +15 -0
  136. package/build/workspace/TestLogItemGenerator.js +142 -0
  137. package/build/workspace/TestLogItemGenerator.js.map +1 -0
  138. package/build/workspace/TestReporter.d.ts +103 -0
  139. package/build/workspace/TestReporter.js +677 -0
  140. package/build/workspace/TestReporter.js.map +1 -0
  141. package/build/workspace/TestRunner.d.ts +24 -0
  142. package/build/workspace/TestRunner.js +95 -0
  143. package/build/workspace/TestRunner.js.map +1 -0
  144. package/build/workspace/TkBaseWidget.d.ts +37 -0
  145. package/build/workspace/TkBaseWidget.js +185 -0
  146. package/build/workspace/TkBaseWidget.js.map +1 -0
  147. package/build/workspace/TkInputWidget.d.ts +16 -0
  148. package/build/workspace/TkInputWidget.js +64 -0
  149. package/build/workspace/TkInputWidget.js.map +1 -0
  150. package/build/workspace/TkLayoutCellWidget.d.ts +18 -0
  151. package/build/workspace/TkLayoutCellWidget.js +32 -0
  152. package/build/workspace/TkLayoutCellWidget.js.map +1 -0
  153. package/build/workspace/TkLayoutWidget.d.ts +34 -0
  154. package/build/workspace/TkLayoutWidget.js +130 -0
  155. package/build/workspace/TkLayoutWidget.js.map +1 -0
  156. package/build/workspace/TkMenuBarWidget.d.ts +13 -0
  157. package/build/workspace/TkMenuBarWidget.js +71 -0
  158. package/build/workspace/TkMenuBarWidget.js.map +1 -0
  159. package/build/workspace/TkPopupWidget.d.ts +16 -0
  160. package/build/workspace/TkPopupWidget.js +36 -0
  161. package/build/workspace/TkPopupWidget.js.map +1 -0
  162. package/build/workspace/TkProgressBarWidget.d.ts +10 -0
  163. package/build/workspace/TkProgressBarWidget.js +39 -0
  164. package/build/workspace/TkProgressBarWidget.js.map +1 -0
  165. package/build/workspace/TkTextWidget.d.ts +20 -0
  166. package/build/workspace/TkTextWidget.js +115 -0
  167. package/build/workspace/TkTextWidget.js.map +1 -0
  168. package/build/workspace/TkWindowWidget.d.ts +22 -0
  169. package/build/workspace/TkWindowWidget.js +88 -0
  170. package/build/workspace/TkWindowWidget.js.map +1 -0
  171. package/build/workspace/WidgetFactory.d.ts +6 -0
  172. package/build/workspace/WidgetFactory.js +21 -0
  173. package/build/workspace/WidgetFactory.js.map +1 -0
  174. package/build/workspace/button.types.d.ts +15 -0
  175. package/build/workspace/button.types.js +6 -0
  176. package/build/workspace/button.types.js.map +1 -0
  177. package/build/workspace/duration.utility.d.ts +4 -0
  178. package/build/workspace/duration.utility.js +26 -0
  179. package/build/workspace/duration.utility.js.map +1 -0
  180. package/build/workspace/factory.types.d.ts +89 -0
  181. package/build/workspace/factory.types.js +37 -0
  182. package/build/workspace/factory.types.js.map +1 -0
  183. package/build/workspace/input.types.d.ts +18 -0
  184. package/build/workspace/input.types.js +7 -0
  185. package/build/workspace/input.types.js.map +1 -0
  186. package/build/workspace/keySelectChoices.d.ts +517 -0
  187. package/build/workspace/keySelectChoices.js +2 -0
  188. package/build/workspace/keySelectChoices.js.map +1 -0
  189. package/build/workspace/layout.types.d.ts +33 -0
  190. package/build/workspace/layout.types.js +2 -0
  191. package/build/workspace/layout.types.js.map +1 -0
  192. package/build/workspace/menuBar.types.d.ts +19 -0
  193. package/build/workspace/menuBar.types.js +6 -0
  194. package/build/workspace/menuBar.types.js.map +1 -0
  195. package/build/workspace/popup.types.d.ts +13 -0
  196. package/build/workspace/popup.types.js +6 -0
  197. package/build/workspace/popup.types.js.map +1 -0
  198. package/build/workspace/progressBar.types.d.ts +10 -0
  199. package/build/workspace/progressBar.types.js +2 -0
  200. package/build/workspace/progressBar.types.js.map +1 -0
  201. package/build/workspace/table.types.d.ts +6 -0
  202. package/build/workspace/table.types.js +2 -0
  203. package/build/workspace/table.types.js.map +1 -0
  204. package/build/workspace/termKit.utility.d.ts +12 -0
  205. package/build/workspace/termKit.utility.js +128 -0
  206. package/build/workspace/termKit.utility.js.map +1 -0
  207. package/build/workspace/test.types.d.ts +24 -0
  208. package/build/workspace/test.types.js +2 -0
  209. package/build/workspace/test.types.js.map +1 -0
  210. package/build/workspace/testRunner.cli.d.ts +1 -0
  211. package/build/workspace/testRunner.cli.js +149 -0
  212. package/build/workspace/testRunner.cli.js.map +1 -0
  213. package/build/workspace/text.types.d.ts +20 -0
  214. package/build/workspace/text.types.js +7 -0
  215. package/build/workspace/text.types.js.map +1 -0
  216. package/build/workspace/widget.utilities.d.ts +5 -0
  217. package/build/workspace/widget.utilities.js +20 -0
  218. package/build/workspace/widget.utilities.js.map +1 -0
  219. package/build/workspace/widgets.types.d.ts +66 -0
  220. package/build/workspace/widgets.types.js +2 -0
  221. package/build/workspace/widgets.types.js.map +1 -0
  222. package/build/workspace/window.types.d.ts +18 -0
  223. package/build/workspace/window.types.js +9 -0
  224. package/build/workspace/window.types.js.map +1 -0
  225. package/eslint.config.js +3 -0
  226. package/package.json +31 -37
  227. package/prettier.config.js +3 -0
  228. package/src/.spruce/settings.json +3 -10
  229. package/src/__tests__/MockFetch.ts +100 -0
  230. package/src/__tests__/behavioral/AbstractModuleTest.test.ts +15 -0
  231. package/src/__tests__/behavioral/utilities/AllHooksCalledEvenIfNotDefined.test.ts +31 -0
  232. package/src/__tests__/behavioral/utilities/Assert.test.ts +826 -0
  233. package/src/__tests__/behavioral/utilities/InstanceParentTestCanAccessParentMethods.test.ts +14 -0
  234. package/src/__tests__/behavioral/utilities/MockFetch.test.ts +240 -0
  235. package/src/__tests__/behavioral/utilities/StackCleaner.test.ts +46 -0
  236. package/src/__tests__/behavioral/utilities/StaticParentTestCanAccessParentMethods.test.ts +34 -0
  237. package/src/__tests__/behavioral/utilities/StaticTestInheritsAbstractSpruceTestProperly.test.ts +11 -0
  238. package/src/__tests__/behavioral/utilities/Stringify.test.ts +169 -0
  239. package/src/__tests__/behavioral/utilities/TestDecoratorResolver.test.ts +51 -0
  240. package/src/__tests__/behavioral/utilities/TestOnBasic.test.ts +62 -0
  241. package/src/__tests__/behavioral/utilities/TestOnInstance.test.ts +439 -0
  242. package/src/__tests__/behavioral/utilities/TestOnInstanceExtendsTest.test.ts +237 -0
  243. package/src/__tests__/behavioral/utilities/TestOnInstanceExtendsTestWithHooks.test.ts +85 -0
  244. package/src/__tests__/behavioral/utilities/TestOnInstanceWithParentBeforeAll.test.ts +14 -0
  245. package/src/__tests__/behavioral/utilities/TestOnInstanceWithTwoLevelsOfInheritence.test.ts +16 -0
  246. package/src/__tests__/behavioral/utilities/TestResolver.test.ts +48 -0
  247. package/src/__tests__/behavioral/utilities/TestResolverOnStatic.test.ts +12 -0
  248. package/src/__tests__/behavioral/workspace/JestJsonParser.test.ts +357 -0
  249. package/src/__tests__/behavioral/workspace/TestReporter.test.ts +257 -0
  250. package/src/__tests__/behavioral/workspace/TestRunner.test.ts +87 -0
  251. package/src/__tests__/behavioral/workspace/Widgets.test.ts +90 -0
  252. package/src/__tests__/mock-terminal-kit.ts +115 -0
  253. package/src/__tests__/support/AbstractForInstanceTest.ts +12 -0
  254. package/src/__tests__/support/AbstractLevelOneTest.ts +3 -0
  255. package/src/__tests__/support/AbstractLevelTwoTest.ts +16 -0
  256. package/src/__tests__/support/AbstractStaticTest.ts +16 -0
  257. package/src/__tests__/support/AbstractTestOnInstanceTest.ts +30 -0
  258. package/src/__tests__/support/AbstractTestOnInstanceWithHooks.ts +138 -0
  259. package/src/__tests__/support/jest.setup.ts +2 -0
  260. package/src/__tests__/support/onTestFileResult.ts +1008 -0
  261. package/src/impl/AbstractModuleTest.ts +59 -10
  262. package/src/index.ts +4 -4
  263. package/src/utilities/AssertionError.ts +14 -0
  264. package/src/utilities/StackCleaner.ts +14 -0
  265. package/src/utilities/TestDecoratorResolver.ts +118 -0
  266. package/src/utilities/assert.ts +583 -0
  267. package/src/utilities/assert.utility.ts +295 -0
  268. package/src/utilities/decorators.ts +167 -0
  269. package/src/workspace/CommandService.ts +256 -0
  270. package/src/workspace/JestJsonParser.ts +255 -0
  271. package/src/workspace/TKButtonWidget.ts +54 -0
  272. package/src/workspace/TestLogItemGenerator.ts +184 -0
  273. package/src/workspace/TestReporter.ts +865 -0
  274. package/src/workspace/TestRunner.ts +128 -0
  275. package/src/workspace/TkBaseWidget.ts +249 -0
  276. package/src/workspace/TkInputWidget.ts +83 -0
  277. package/src/workspace/TkLayoutCellWidget.ts +46 -0
  278. package/src/workspace/TkLayoutWidget.ts +181 -0
  279. package/src/workspace/TkMenuBarWidget.ts +95 -0
  280. package/src/workspace/TkPopupWidget.ts +47 -0
  281. package/src/workspace/TkProgressBarWidget.ts +54 -0
  282. package/src/workspace/TkTextWidget.ts +158 -0
  283. package/src/workspace/TkWindowWidget.ts +122 -0
  284. package/src/workspace/WidgetFactory.ts +33 -0
  285. package/src/workspace/button.types.ts +19 -0
  286. package/src/workspace/duration.utility.ts +34 -0
  287. package/src/workspace/factory.types.ts +101 -0
  288. package/src/workspace/input.types.ts +22 -0
  289. package/src/workspace/keySelectChoices.ts +134 -0
  290. package/src/workspace/layout.types.ts +40 -0
  291. package/src/workspace/menuBar.types.ts +24 -0
  292. package/src/workspace/popup.types.ts +17 -0
  293. package/src/workspace/progressBar.types.ts +13 -0
  294. package/src/workspace/table.types.ts +9 -0
  295. package/src/workspace/termKit.utility.ts +130 -0
  296. package/src/workspace/terminal-kit.d.ts +28 -0
  297. package/src/workspace/test.types.ts +34 -0
  298. package/src/workspace/testRunner.cli.ts +159 -0
  299. package/src/workspace/text.types.ts +26 -0
  300. package/src/workspace/widget.utilities.ts +35 -0
  301. package/src/workspace/widgets.types.ts +102 -0
  302. package/src/workspace/window.types.ts +22 -0
  303. package/tsconfig.json +23 -27
  304. package/build/__tests__/AbstractPackageTest.d.ts +0 -4
  305. package/build/__tests__/AbstractPackageTest.js +0 -7
  306. package/build/__tests__/AbstractPackageTest.js.map +0 -1
  307. package/build/__tests__/functions/assertFunction.test.d.ts +0 -5
  308. package/build/__tests__/functions/assertFunction.test.js +0 -23
  309. package/build/__tests__/functions/assertFunction.test.js.map +0 -1
  310. package/build/__tests__/functions/testFunction.test.d.ts +0 -5
  311. package/build/__tests__/functions/testFunction.test.js +0 -23
  312. package/build/__tests__/functions/testFunction.test.js.map +0 -1
  313. package/build/__tests__/impl/AbstractModuleTest.test.js.map +0 -1
  314. package/build/functions/assert.d.ts +0 -3
  315. package/build/functions/assert.js +0 -4
  316. package/build/functions/assert.js.map +0 -1
  317. package/build/functions/test.d.ts +0 -3
  318. package/build/functions/test.js +0 -4
  319. package/build/functions/test.js.map +0 -1
  320. package/eslint.config.mjs +0 -3
  321. package/src/__tests__/AbstractPackageTest.ts +0 -7
  322. package/src/__tests__/functions/assertFunction.test.ts +0 -22
  323. package/src/__tests__/functions/testFunction.test.ts +0 -22
  324. package/src/__tests__/impl/AbstractModuleTest.test.ts +0 -34
  325. package/src/functions/assert.ts +0 -4
  326. package/src/functions/test.ts +0 -4
@@ -0,0 +1,865 @@
1
+ import chalk from 'chalk'
2
+ import durationUtil from './duration.utility.js'
3
+ import { ButtonWidget } from './button.types.js'
4
+ import { InputWidget } from './input.types.js'
5
+ import { LayoutWidget } from './layout.types.js'
6
+ import { MenuBarWidget } from './menuBar.types.js'
7
+ import { PopupWidget } from './popup.types.js'
8
+ import { ProgressBarWidget } from './progressBar.types.js'
9
+ import { TextWidget } from './text.types.js'
10
+ import { WindowWidget } from './window.types.js'
11
+ import WidgetFactory from './WidgetFactory.js'
12
+ import { TestResults, TestRunnerStatus } from './test.types.js'
13
+ import TestLogItemGenerator from './TestLogItemGenerator.js'
14
+
15
+ export default class TestReporter {
16
+ private started = false
17
+ private table?: any
18
+ private bar!: ProgressBarWidget
19
+ private bottomLayout!: LayoutWidget
20
+ private testLog!: TextWidget
21
+ private errorLog?: TextWidget
22
+ private errorLogItemGenerator: TestLogItemGenerator
23
+ private lastResults: TestReporterResults = {
24
+ totalTestFiles: 0,
25
+ customErrors: [],
26
+ }
27
+ private updateInterval?: any
28
+ private menu!: MenuBarWidget
29
+ private statusBar!: TextWidget
30
+ private window!: WindowWidget
31
+ private widgets: WidgetFactory
32
+ private selectTestPopup?: PopupWidget
33
+ private topLayout!: LayoutWidget
34
+ private filterInput!: InputWidget
35
+ private filterPattern?: string
36
+ private clearFilterPatternButton!: ButtonWidget
37
+ private isDebugging = false
38
+ private watchMode: WatchMode = 'off'
39
+ private status: TestRunnerStatus = 'ready'
40
+ private countDownTimeInterval?: any
41
+ private cwd: string | undefined
42
+ private orientation: TestReporterOrientation = 'landscape'
43
+
44
+ private handleStartStop?: () => void
45
+ private handleRestart?: () => void
46
+ private handleQuit?: () => void
47
+ private handleRerunTestFile?: (fileName: string) => void
48
+ private handleFilterChange?: (pattern?: string) => void
49
+ private handleOpenTestFile?: (testFile: string) => void
50
+ private handleToggleDebug?: () => void
51
+ private handletoggleStandardWatch?: () => void
52
+ private handleToggleSmartWatch?: () => any
53
+ private minWidth = 50
54
+
55
+ public constructor(options?: TestReporterOptions) {
56
+ this.cwd = options?.cwd
57
+ this.filterPattern = options?.filterPattern
58
+ this.handleRestart = options?.handleRestart
59
+ this.handleStartStop = options?.handleStartStop
60
+ this.handleQuit = options?.handleQuit
61
+ this.handleRerunTestFile = options?.handleRerunTestFile
62
+ this.handleOpenTestFile = options?.handleOpenTestFile
63
+ this.handleFilterChange = options?.handleFilterPatternChange
64
+ this.status = options?.status ?? 'ready'
65
+ this.handleToggleDebug = options?.handleToggleDebug
66
+ this.handletoggleStandardWatch = options?.handletoggleStandardWatch
67
+ this.isDebugging = options?.isDebugging ?? false
68
+ this.watchMode = options?.watchMode ?? 'off'
69
+ this.handleToggleSmartWatch = options?.handleToggleSmartWatch
70
+
71
+ this.errorLogItemGenerator = new TestLogItemGenerator()
72
+ this.widgets = new WidgetFactory()
73
+ }
74
+
75
+ public setFilterPattern(pattern: string | undefined) {
76
+ this.filterPattern = pattern
77
+ this.filterInput.setValue(pattern ?? '')
78
+ this.clearFilterPatternButton.setText(buildPatternButtonText(pattern))
79
+ }
80
+
81
+ public setIsDebugging(isDebugging: boolean) {
82
+ this.setLabelStatus('toggleDebug', 'Debug', isDebugging)
83
+ this.isDebugging = isDebugging
84
+ }
85
+
86
+ public setWatchMode(watchMode: WatchMode) {
87
+ this.watchMode = watchMode
88
+ if (!this.countDownTimeInterval) {
89
+ let label = watchMode === 'smart' ? 'Smart Watch' : 'Standard Watch'
90
+ if (watchMode === 'off') {
91
+ label = 'Not Watching'
92
+ }
93
+ this.setWatchLabel(label)
94
+ }
95
+ }
96
+
97
+ private setWatchLabel(label: string) {
98
+ const isEnabled = this.watchMode !== 'off'
99
+ this.setLabelStatus('watchDropdown', label, isEnabled)
100
+
101
+ this.menu.setTextForItem(
102
+ 'toggleStandardWatch',
103
+ this.watchMode === 'standard' ? '√ Standard' : 'Standard'
104
+ )
105
+
106
+ this.menu.setTextForItem(
107
+ 'toggleSmartWatch',
108
+ this.watchMode === 'smart' ? '√ Smart' : 'Smart'
109
+ )
110
+ }
111
+
112
+ private setLabelStatus(menuKey: string, label: string, isEnabled: boolean) {
113
+ this.menu.setTextForItem(
114
+ menuKey,
115
+ `${label} ^${isEnabled ? 'k' : 'w'}^#^${isEnabled ? 'g' : 'r'}${isEnabled ? ' • ' : ' • '}^`
116
+ )
117
+ }
118
+
119
+ public startCountdownTimer(durationSec: number) {
120
+ clearInterval(this.countDownTimeInterval)
121
+ this.countDownTimeInterval = undefined
122
+
123
+ let remaining = durationSec
124
+
125
+ function renderCountdownTime(time: number) {
126
+ return `Starting ${time} `
127
+ }
128
+
129
+ this.setWatchLabel(renderCountdownTime(remaining))
130
+
131
+ this.countDownTimeInterval = setInterval(() => {
132
+ remaining--
133
+
134
+ if (remaining < 0) {
135
+ this.stopCountdownTimer()
136
+ } else {
137
+ this.setWatchLabel(renderCountdownTime(remaining))
138
+ }
139
+ }, 1000) as any
140
+ }
141
+
142
+ public stopCountdownTimer() {
143
+ clearInterval(this.countDownTimeInterval)
144
+ this.countDownTimeInterval = undefined
145
+ this.setWatchMode(this.watchMode)
146
+ }
147
+
148
+ public async start() {
149
+ this.started = true
150
+
151
+ this.window = this.widgets.Widget('window', {})
152
+ this.window.hideCursor()
153
+
154
+ const { width } = this.window.getFrame()
155
+ if (width < this.minWidth) {
156
+ throw new Error(
157
+ `Your screen must be at least ${this.minWidth} characters wide.`
158
+ )
159
+ }
160
+
161
+ void this.window.on('key', this.handleGlobalKeypress.bind(this))
162
+ void this.window.on('kill', (payload: { code: any }) => {
163
+ if (payload.code instanceof Error) {
164
+ const term = (this.window as any).term as any
165
+ const doc = (this.window as any).getTermKitElement?.() as any
166
+ term?.grabInput?.({ mouse: 'button' })
167
+ doc?.draw?.()
168
+ } else {
169
+ void this.destroy()
170
+ }
171
+ })
172
+ void this.window.on('resize', this.handleWindowResize.bind(this))
173
+
174
+ this.dropInTopLayout()
175
+ this.dropInProgressBar()
176
+ this.dropInMenu()
177
+ this.dropInBottomLayout()
178
+ this.dropInStatusBar()
179
+ this.dropInTestLog()
180
+ this.dropInFilterControls()
181
+
182
+ this.updateOrientation()
183
+
184
+ this.setIsDebugging(this.isDebugging)
185
+ this.setWatchMode(this.watchMode)
186
+ this.setStatus(this.status)
187
+
188
+ this.updateInterval = setInterval(
189
+ this.handleUpdateInterval.bind(this),
190
+ 1000
191
+ )
192
+ }
193
+
194
+ private handleWindowResize() {
195
+ this.updateOrientation()
196
+ }
197
+
198
+ private updateOrientation() {
199
+ const frame = this.window.getFrame()
200
+
201
+ if (frame.width * 0.4 > frame.height) {
202
+ this.orientation = 'landscape'
203
+ } else {
204
+ this.orientation = 'portrait'
205
+ }
206
+ }
207
+
208
+ private dropInMenu() {
209
+ this.menu = this.widgets.Widget('menuBar', {
210
+ parent: this.window,
211
+ left: 0,
212
+ top: 0,
213
+ shouldLockWidthWithParent: true,
214
+ items: [
215
+ {
216
+ label: 'Restart ',
217
+ value: 'restart',
218
+ },
219
+ {
220
+ label: 'Debug ',
221
+ value: 'toggleDebug',
222
+ },
223
+ {
224
+ label: 'Not Watching ',
225
+ value: 'watchDropdown',
226
+ items: [
227
+ {
228
+ label: 'Watch all',
229
+ value: 'toggleStandardWatch',
230
+ },
231
+ {
232
+ label: 'Smart watch',
233
+ value: 'toggleSmartWatch',
234
+ },
235
+ ],
236
+ },
237
+ {
238
+ label: 'Quit',
239
+ value: 'quit',
240
+ },
241
+ ],
242
+ })
243
+
244
+ void this.menu.on('select', this.handleMenuSelect.bind(this))
245
+ }
246
+
247
+ public setStatus(status: TestRunnerStatus) {
248
+ this.status = status
249
+
250
+ this.updateMenuLabels()
251
+ this.closeSelectTestPopup()
252
+ this.bottomLayout.updateLayout()
253
+
254
+ if (status === 'ready') {
255
+ this.setStatusLabel('Starting...')
256
+ } else if (this.status === 'stopped') {
257
+ this.refreshResults()
258
+ this.setStatusLabel('')
259
+ } else if (this.status === 'running') {
260
+ this.setStatusLabel('Running tests...')
261
+ }
262
+ }
263
+
264
+ private updateMenuLabels() {
265
+ let restartLabel = 'Start ^#^r › ^'
266
+ switch (this.status) {
267
+ case 'running':
268
+ restartLabel = 'Stop ^k^#^g › ^'
269
+ break
270
+ case 'stopped':
271
+ restartLabel = `Start ^w^#^r › ^`
272
+ break
273
+ case 'ready':
274
+ restartLabel = 'Booting ^#^K › ^'
275
+ break
276
+ }
277
+
278
+ this.menu.setTextForItem('restart', restartLabel)
279
+ }
280
+
281
+ private handleMenuSelect(payload: { value: string }) {
282
+ switch (payload.value) {
283
+ case 'quit':
284
+ this.handleQuit?.()
285
+ break
286
+ case 'restart':
287
+ this.handleStartStop?.()
288
+ break
289
+ case 'toggleDebug':
290
+ this.handleToggleDebug?.()
291
+ break
292
+ case 'toggleStandardWatch':
293
+ this.handletoggleStandardWatch?.()
294
+ break
295
+ case 'toggleSmartWatch':
296
+ this.handleToggleSmartWatch?.()
297
+ break
298
+ }
299
+ }
300
+
301
+ private handleUpdateInterval() {
302
+ try {
303
+ if (this.status !== 'stopped') {
304
+ this.refreshResults()
305
+ }
306
+ } catch {
307
+ // prevent uncaughtException from crashing the TUI
308
+ }
309
+ }
310
+
311
+ private refreshResults() {
312
+ if (this.lastResults) {
313
+ this.updateLogs()
314
+ }
315
+ }
316
+
317
+ private async handleGlobalKeypress(payload: { key: string }) {
318
+ if (this.window.getFocusedWidget() === this.filterInput) {
319
+ return
320
+ }
321
+
322
+ switch (payload.key) {
323
+ case 'ENTER':
324
+ this.handleRestart?.()
325
+ break
326
+ case 'CTRL_C':
327
+ this.handleQuit?.()
328
+ process.exit()
329
+ break
330
+ }
331
+ }
332
+
333
+ private dropInTestLog() {
334
+ const parent = this.bottomLayout.getChildById('results')
335
+
336
+ if (parent) {
337
+ this.testLog = this.widgets.Widget('text', {
338
+ parent,
339
+ isScrollEnabled: true,
340
+ left: 0,
341
+ top: 0,
342
+ height: '100%',
343
+ width: '100%',
344
+ shouldLockHeightWithParent: true,
345
+ shouldLockWidthWithParent: true,
346
+ })
347
+
348
+ void this.testLog.on('click', this.handleClickTestLog.bind(this))
349
+ }
350
+ }
351
+
352
+ private async handleClickTestLog(payload: { row: number; column: number }) {
353
+ const testFile = this.getFileForLine(payload.row)
354
+ const { row, column } = payload
355
+
356
+ this.closeSelectTestPopup()
357
+
358
+ if (testFile) {
359
+ this.dropInSelectTestPopup({ testFile, column, row })
360
+ }
361
+ }
362
+
363
+ public async showAlert(options: { title: string; message: string }) {
364
+ const { title, message } = options
365
+
366
+ const windowFrame = this.window.getFrame()
367
+ const popupHeight = Math.min(windowFrame.height - 4, 25)
368
+
369
+ return new Promise<void>((resolve) => {
370
+ const popup = this.widgets.Widget('popup', {
371
+ parent: this.window,
372
+ top: 2,
373
+ left: 4,
374
+ width: Math.min(windowFrame.width - 8, 80),
375
+ height: popupHeight,
376
+ })
377
+
378
+ this.widgets.Widget('text', {
379
+ parent: popup,
380
+ left: 2,
381
+ top: 1,
382
+ height: 1,
383
+ width: popup.getFrame().width - 4,
384
+ text: title,
385
+ })
386
+
387
+ this.widgets.Widget('text', {
388
+ parent: popup,
389
+ left: 2,
390
+ top: 3,
391
+ height: popupHeight - 7,
392
+ width: popup.getFrame().width - 4,
393
+ text: message,
394
+ isScrollEnabled: true,
395
+ wordWrap: true,
396
+ })
397
+
398
+ const okButton = this.widgets.Widget('button', {
399
+ parent: popup,
400
+ left: Math.floor(popup.getFrame().width / 2) - 4,
401
+ top: popupHeight - 3,
402
+ text: ' OK ',
403
+ })
404
+
405
+ void okButton.on('click', () => {
406
+ void popup.destroy()
407
+ resolve()
408
+ })
409
+ })
410
+ }
411
+
412
+ private closeSelectTestPopup() {
413
+ if (this.selectTestPopup) {
414
+ void this.selectTestPopup.destroy()
415
+ this.selectTestPopup = undefined
416
+ }
417
+ }
418
+
419
+ private dropInSelectTestPopup(options: {
420
+ testFile: string
421
+ column: number
422
+ row: number
423
+ }) {
424
+ const { testFile, row, column } = options
425
+
426
+ this.selectTestPopup = this.widgets.Widget('popup', {
427
+ parent: this.window,
428
+ left: Math.max(1, column - 25),
429
+ top: Math.max(4, row - 2),
430
+ width: 50,
431
+ height: 10,
432
+ })
433
+
434
+ this.widgets.Widget('text', {
435
+ parent: this.selectTestPopup,
436
+ left: 1,
437
+ top: 1,
438
+ height: 4,
439
+ width: this.selectTestPopup.getFrame().width - 2,
440
+ text: `What do you wanna do with:\n\n${testFile}`,
441
+ })
442
+
443
+ const open = this.widgets.Widget('button', {
444
+ parent: this.selectTestPopup,
445
+ left: 1,
446
+ top: 6,
447
+ text: 'Open',
448
+ })
449
+
450
+ const rerun = this.widgets.Widget('button', {
451
+ parent: this.selectTestPopup,
452
+ left: 20,
453
+ top: 6,
454
+ text: 'Test',
455
+ })
456
+
457
+ const cancel = this.widgets.Widget('button', {
458
+ parent: this.selectTestPopup,
459
+ left: 37,
460
+ top: 6,
461
+ text: 'Nevermind',
462
+ })
463
+
464
+ void rerun.on('click', () => {
465
+ this.handleRerunTestFile?.(testFile)
466
+ this.closeSelectTestPopup()
467
+ })
468
+ void cancel.on('click', this.closeSelectTestPopup.bind(this))
469
+ void open.on('click', () => {
470
+ this.openTestFile(testFile)
471
+ })
472
+ }
473
+
474
+ private openTestFile(testFile: string) {
475
+ this.handleOpenTestFile?.(testFile)
476
+ this.closeSelectTestPopup()
477
+ }
478
+
479
+ public getFileForLine(row: number): string | undefined {
480
+ let line = this.testLog.getScrollY()
481
+
482
+ for (const file of this.lastResults.testFiles ?? []) {
483
+ const minRow = line
484
+ const maxRow = line + (file.tests ?? []).length
485
+
486
+ if (row >= minRow && row <= maxRow) {
487
+ return file.path
488
+ }
489
+
490
+ line = maxRow
491
+ }
492
+
493
+ return undefined
494
+ }
495
+
496
+ private dropInProgressBar() {
497
+ const parent = this.topLayout.getChildById('progress') ?? this.window
498
+ this.bar = this.widgets.Widget('progressBar', {
499
+ parent,
500
+ left: 0,
501
+ top: 0,
502
+ width: parent.getFrame().width,
503
+ shouldLockWidthWithParent: true,
504
+ label: 'Ready and waiting...',
505
+ progress: 0,
506
+ })
507
+ }
508
+
509
+ private dropInFilterControls() {
510
+ const parent = this.topLayout.getChildById('filter') ?? this.window
511
+
512
+ const buttonWidth = 3
513
+ this.filterInput = this.widgets.Widget('input', {
514
+ parent,
515
+ left: 0,
516
+ label: 'Pattern',
517
+ width: parent.getFrame().width - buttonWidth,
518
+ height: 1,
519
+ shouldLockWidthWithParent: true,
520
+ value: this.filterPattern,
521
+ })
522
+
523
+ void this.filterInput.on('cancel', () => {
524
+ this.filterInput.setValue(this.filterPattern ?? '')
525
+ })
526
+
527
+ void this.filterInput.on('submit', (payload) => {
528
+ this.handleFilterChange?.(payload?.value ?? undefined)
529
+ })
530
+
531
+ this.clearFilterPatternButton = this.widgets.Widget('button', {
532
+ parent,
533
+ left: this.filterInput.getFrame().width,
534
+ width: buttonWidth,
535
+ top: 0,
536
+ text: buildPatternButtonText(this.filterPattern),
537
+ shouldLockRightWithParent: true,
538
+ })
539
+
540
+ void this.clearFilterPatternButton.on('click', () => {
541
+ if (this.filterPattern || this.filterPattern?.length === 0) {
542
+ this.handleFilterChange?.(undefined)
543
+ } else {
544
+ this.filterInput.setValue('')
545
+ }
546
+ })
547
+ }
548
+
549
+ private dropInBottomLayout() {
550
+ this.bottomLayout = this.widgets.Widget('layout', {
551
+ parent: this.window,
552
+ width: '100%',
553
+ top: 4,
554
+ height: this.window.getFrame().height - 5,
555
+ shouldLockWidthWithParent: true,
556
+ shouldLockHeightWithParent: true,
557
+ rows: [
558
+ {
559
+ height: '100%',
560
+ columns: [
561
+ {
562
+ id: 'results',
563
+ width: '100%',
564
+ },
565
+ ],
566
+ },
567
+ ],
568
+ })
569
+ }
570
+
571
+ private dropInStatusBar() {
572
+ this.statusBar = this.widgets.Widget('text', {
573
+ parent: this.window,
574
+ top: this.window.getFrame().height - 1,
575
+ width: '100%',
576
+ shouldLockWidthWithParent: true,
577
+ shouldLockBottomWithParent: true,
578
+ backgroundColor: 'yellow',
579
+ foregroundColor: 'black',
580
+ text: '...',
581
+ })
582
+ }
583
+
584
+ private dropInTopLayout() {
585
+ this.topLayout = this.widgets.Widget('layout', {
586
+ parent: this.window,
587
+ width: '100%',
588
+ top: 1,
589
+ height: 3,
590
+ shouldLockWidthWithParent: true,
591
+ shouldLockHeightWithParent: false,
592
+ rows: [
593
+ {
594
+ height: '100%',
595
+ columns: [
596
+ {
597
+ id: 'progress',
598
+ width: 50,
599
+ },
600
+ {
601
+ id: 'filter',
602
+ },
603
+ ],
604
+ },
605
+ ],
606
+ })
607
+ }
608
+
609
+ public updateResults(results: TestResults) {
610
+ if (!this.started) {
611
+ throw new Error('You must call start() before anything else.')
612
+ }
613
+
614
+ this.lastResults = {
615
+ ...this.lastResults,
616
+ ...results,
617
+ }
618
+
619
+ this.updateProgressBar(results)
620
+
621
+ const percentPassing = this.generatePercentPassing(results)
622
+ const percentComplete = this.generatePercentComplete(results)
623
+
624
+ this.window.setTitle(
625
+ `Testing: ${percentComplete}% complete.${
626
+ percentComplete > 0 ? ` ${percentPassing}% passing.` : ''
627
+ }`
628
+ )
629
+
630
+ this.updateLogs()
631
+ }
632
+
633
+ private updateLogs() {
634
+ if (this.selectTestPopup) {
635
+ return
636
+ }
637
+
638
+ const { logContent, errorContent } = this.resultsToLogContents(
639
+ this.lastResults
640
+ )
641
+
642
+ this.testLog.setText(logContent)
643
+
644
+ if (!errorContent) {
645
+ this.errorLog?.setText(' Nothing to report...')
646
+ } else {
647
+ !this.errorLog && this.dropInErrorLog()
648
+ const cleanedLog = this.cwd
649
+ ? errorContent.replace(new RegExp(this.cwd + '/', 'gim'), '')
650
+ : errorContent
651
+
652
+ this.errorLog?.setText(cleanedLog)
653
+ }
654
+ }
655
+
656
+ private resultsToLogContents(results: TestResults) {
657
+ let logContent = ''
658
+ let errorContent = ''
659
+
660
+ results.testFiles?.forEach((file) => {
661
+ logContent += this.errorLogItemGenerator.generateLogItemForFile(
662
+ file,
663
+ this.status
664
+ )
665
+ errorContent +=
666
+ this.errorLogItemGenerator.generateErrorLogItemForFile(file)
667
+ })
668
+
669
+ if (this.lastResults.customErrors.length > 0) {
670
+ errorContent =
671
+ this.lastResults.customErrors
672
+ .map((err) => chalk.red(err))
673
+ .join(`\n`) + `\n${errorContent}`
674
+ }
675
+
676
+ return { logContent, errorContent }
677
+ }
678
+
679
+ private dropInErrorLog() {
680
+ if (this.bottomLayout.getRows().length === 1) {
681
+ if (this.orientation === 'portrait') {
682
+ this.bottomLayout.addRow({
683
+ id: 'row_2',
684
+ columns: [{ id: 'errors', width: '100%' }],
685
+ })
686
+
687
+ this.bottomLayout.setRowHeight(0, '60%')
688
+ this.bottomLayout.setRowHeight(1, '40%')
689
+ } else {
690
+ this.bottomLayout.addColumn(0, { id: 'errors', width: '40%' })
691
+ this.bottomLayout.setColumnWidth({
692
+ rowIdx: 0,
693
+ columnIdx: 0,
694
+ width: '60%',
695
+ })
696
+ }
697
+
698
+ this.bottomLayout.updateLayout()
699
+
700
+ const cell = this.bottomLayout.getChildById('errors')
701
+
702
+ if (!cell) {
703
+ throw new Error('Pulling child error')
704
+ }
705
+
706
+ this.errorLog = this.widgets.Widget('text', {
707
+ parent: cell,
708
+ width: '100%',
709
+ height: '100%',
710
+ isScrollEnabled: true,
711
+ shouldAutoScrollWhenAppendingContent: false,
712
+ shouldLockHeightWithParent: true,
713
+ shouldLockWidthWithParent: true,
714
+ padding: { left: 1 },
715
+ focusable: false,
716
+ })
717
+ }
718
+ }
719
+
720
+ private updateProgressBar(results: TestResults) {
721
+ if ((results.totalTestFilesComplete ?? 0) > 0) {
722
+ const testsRemaining =
723
+ results.totalTestFiles - (results.totalTestFilesComplete ?? 0)
724
+
725
+ if (testsRemaining === 0) {
726
+ const { percent, totalTests, totalPassedTests, totalTime } =
727
+ this.generateProgressStats(results)
728
+
729
+ this.bar.setLabel(
730
+ `Finished! ${totalPassedTests} of ${totalTests} (${percent}%) passed in ${durationUtil.msToFriendly(
731
+ totalTime
732
+ )}.${percent < 100 ? ` Don't give up!` : ''}`
733
+ )
734
+ } else {
735
+ this.bar.setLabel(
736
+ `${results.totalTestFilesComplete} of ${
737
+ results.totalTestFiles
738
+ } (${this.generatePercentComplete(
739
+ results
740
+ )}%) complete. ${testsRemaining} remaining...`
741
+ )
742
+ }
743
+ } else {
744
+ this.bar.setLabel('0%')
745
+ }
746
+
747
+ this.bar.setProgress(this.generatePercentComplete(results) / 100)
748
+ }
749
+
750
+ private generateProgressStats(results: TestResults): {
751
+ percent: number
752
+ totalTests: number
753
+ totalPassedTests: number
754
+ totalTime: number
755
+ } {
756
+ let totalTests = 0
757
+ let totalPassedTests = 0
758
+ let totalTime = 0
759
+
760
+ results.testFiles?.forEach((file) => {
761
+ file.tests?.forEach((test) => {
762
+ totalTime += test.duration
763
+ if (test.status === 'passed') {
764
+ totalPassedTests++
765
+ }
766
+
767
+ if (test.status === 'passed' || test.status === 'failed') {
768
+ totalTests++
769
+ }
770
+ })
771
+ })
772
+
773
+ const percent = Math.floor((totalPassedTests / totalTests) * 100)
774
+ return {
775
+ percent: percent > 0 ? percent : 0,
776
+ totalTests,
777
+ totalPassedTests,
778
+ totalTime,
779
+ }
780
+ }
781
+
782
+ private generatePercentComplete(results: TestResults): number {
783
+ const percent =
784
+ (results.totalTestFilesComplete ?? 0) / results.totalTestFiles
785
+ if (isNaN(percent)) {
786
+ return 0
787
+ }
788
+ return Math.round(percent * 100)
789
+ }
790
+
791
+ private generatePercentPassing(results: TestResults): number {
792
+ const percent =
793
+ (results.totalPassed ?? 0) / this.getTotalTestFilesRun(results)
794
+
795
+ if (isNaN(percent)) {
796
+ return 0
797
+ }
798
+
799
+ return Math.floor(percent * 100)
800
+ }
801
+
802
+ private getTotalTestFilesRun(results: TestResults) {
803
+ return (
804
+ (results.totalTests ?? 0) -
805
+ (results.totalSkipped ?? 0) -
806
+ (results.totalTodo ?? 0)
807
+ )
808
+ }
809
+
810
+ public render() {
811
+ this.table?.computeCells()
812
+ this.table?.draw()
813
+ }
814
+
815
+ public async destroy() {
816
+ clearInterval(this.updateInterval)
817
+ await this.window.destroy()
818
+ }
819
+
820
+ public reset() {
821
+ this.testLog.setText('')
822
+ this.lastResults = {
823
+ totalTestFiles: 0,
824
+ customErrors: [],
825
+ }
826
+ this.errorLogItemGenerator.resetStartTimes()
827
+ }
828
+
829
+ public setStatusLabel(text: string) {
830
+ this.statusBar.setText(text)
831
+ }
832
+
833
+ public appendError(message: string) {
834
+ this.lastResults.customErrors.push(message)
835
+ }
836
+ }
837
+
838
+ function buildPatternButtonText(pattern: string | undefined): string {
839
+ return pattern ? ' x ' : ' - '
840
+ }
841
+
842
+ export interface TestReporterOptions {
843
+ handleStartStop?: () => void
844
+ handleRestart?: () => void
845
+ handleQuit?: () => void
846
+ onRequestOpenTestFile?: () => void
847
+ handleRerunTestFile?: (fileName: string) => void
848
+ handleOpenTestFile?: (fileName: string) => void
849
+ handleFilterPatternChange?: (pattern?: string) => void
850
+ handleToggleDebug?: () => void
851
+ handletoggleStandardWatch?: () => void
852
+ handleToggleSmartWatch?: () => void
853
+ filterPattern?: string
854
+ isDebugging?: boolean
855
+ watchMode?: WatchMode
856
+ status?: TestRunnerStatus
857
+ cwd?: string
858
+ }
859
+
860
+ type TestReporterResults = TestResults & {
861
+ customErrors: string[]
862
+ }
863
+
864
+ export type TestReporterOrientation = 'landscape' | 'portrait'
865
+ export type WatchMode = 'off' | 'standard' | 'smart'