creevey 0.10.0-beta.8 → 0.10.0-rc.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 (350) hide show
  1. package/AUTHORS +2 -0
  2. package/CHANGELOG.md +281 -0
  3. package/README.md +19 -41
  4. package/dist/client/addon/components/Addon.js +18 -8
  5. package/dist/client/addon/components/Addon.js.map +1 -1
  6. package/dist/client/addon/components/Panel.js +4 -4
  7. package/dist/client/addon/components/Panel.js.map +1 -1
  8. package/dist/client/addon/components/TestSelect.js +2 -2
  9. package/dist/client/addon/components/TestSelect.js.map +1 -1
  10. package/dist/client/addon/components/Tools.js +19 -9
  11. package/dist/client/addon/components/Tools.js.map +1 -1
  12. package/dist/client/addon/controller.d.ts +1 -1
  13. package/dist/client/addon/controller.js +3 -3
  14. package/dist/client/addon/controller.js.map +1 -1
  15. package/dist/client/addon/decorator.d.ts +1 -1
  16. package/dist/client/addon/makeDecorator.d.ts +9 -0
  17. package/dist/client/addon/makeDecorator.js +48 -0
  18. package/dist/client/addon/makeDecorator.js.map +1 -0
  19. package/dist/client/addon/manager.js +38 -39
  20. package/dist/client/addon/manager.js.map +1 -1
  21. package/dist/client/addon/preset.d.ts +0 -1
  22. package/dist/client/addon/preset.js +3 -2
  23. package/dist/client/addon/preset.js.map +1 -1
  24. package/dist/client/addon/preview.d.ts +1 -1
  25. package/dist/client/addon/withCreevey.d.ts +5 -3
  26. package/dist/client/addon/withCreevey.js +14 -21
  27. package/dist/client/addon/withCreevey.js.map +1 -1
  28. package/dist/client/shared/components/ImagesView/BlendView.d.ts +2 -2
  29. package/dist/client/shared/components/ImagesView/BlendView.js +18 -8
  30. package/dist/client/shared/components/ImagesView/BlendView.js.map +1 -1
  31. package/dist/client/shared/components/ImagesView/ImagesView.js +1 -1
  32. package/dist/client/shared/components/ImagesView/ImagesView.js.map +1 -1
  33. package/dist/client/shared/components/ImagesView/SideBySideView.d.ts +2 -2
  34. package/dist/client/shared/components/ImagesView/SideBySideView.js +19 -9
  35. package/dist/client/shared/components/ImagesView/SideBySideView.js.map +1 -1
  36. package/dist/client/shared/components/ImagesView/SlideView.d.ts +2 -2
  37. package/dist/client/shared/components/ImagesView/SlideView.js +19 -9
  38. package/dist/client/shared/components/ImagesView/SlideView.js.map +1 -1
  39. package/dist/client/shared/components/ImagesView/SwapView.d.ts +2 -2
  40. package/dist/client/shared/components/ImagesView/SwapView.js +31 -9
  41. package/dist/client/shared/components/ImagesView/SwapView.js.map +1 -1
  42. package/dist/client/shared/components/ImagesView/common.d.ts +1 -1
  43. package/dist/client/shared/components/PageFooter/PageFooter.js +1 -1
  44. package/dist/client/shared/components/PageFooter/PageFooter.js.map +1 -1
  45. package/dist/client/shared/components/PageFooter/Paging.js +1 -1
  46. package/dist/client/shared/components/PageFooter/Paging.js.map +1 -1
  47. package/dist/client/shared/components/PageHeader/ImagePreview.d.ts +2 -2
  48. package/dist/client/shared/components/PageHeader/ImagePreview.js +2 -1
  49. package/dist/client/shared/components/PageHeader/ImagePreview.js.map +1 -1
  50. package/dist/client/shared/components/PageHeader/PageHeader.js +34 -13
  51. package/dist/client/shared/components/PageHeader/PageHeader.js.map +1 -1
  52. package/dist/client/shared/components/ResultsPage.d.ts +2 -2
  53. package/dist/client/shared/components/ResultsPage.js +45 -15
  54. package/dist/client/shared/components/ResultsPage.js.map +1 -1
  55. package/dist/client/shared/creeveyClientApi.js +18 -1
  56. package/dist/client/shared/creeveyClientApi.js.map +1 -1
  57. package/dist/client/shared/helpers.d.ts +1 -3
  58. package/dist/client/shared/helpers.js +4 -19
  59. package/dist/client/shared/helpers.js.map +1 -1
  60. package/dist/client/web/CreeveyApp.d.ts +1 -0
  61. package/dist/client/web/CreeveyApp.js +44 -15
  62. package/dist/client/web/CreeveyApp.js.map +1 -1
  63. package/dist/client/web/CreeveyContext.d.ts +6 -0
  64. package/dist/client/web/CreeveyContext.js +21 -7
  65. package/dist/client/web/CreeveyContext.js.map +1 -1
  66. package/dist/client/web/CreeveyLoader.d.ts +1 -1
  67. package/dist/client/web/CreeveyLoader.js +3 -3
  68. package/dist/client/web/CreeveyLoader.js.map +1 -1
  69. package/dist/client/web/CreeveyView/SideBar/Checkbox.d.ts +4 -4
  70. package/dist/client/web/CreeveyView/SideBar/Checkbox.js +36 -6
  71. package/dist/client/web/CreeveyView/SideBar/Checkbox.js.map +1 -1
  72. package/dist/client/web/CreeveyView/SideBar/Search.js +20 -10
  73. package/dist/client/web/CreeveyView/SideBar/Search.js.map +1 -1
  74. package/dist/client/web/CreeveyView/SideBar/SideBar.js +26 -12
  75. package/dist/client/web/CreeveyView/SideBar/SideBar.js.map +1 -1
  76. package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js +67 -13
  77. package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js.map +1 -1
  78. package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js +32 -12
  79. package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js.map +1 -1
  80. package/dist/client/web/CreeveyView/SideBar/SuiteLink.d.ts +6 -6
  81. package/dist/client/web/CreeveyView/SideBar/SuiteLink.js +20 -13
  82. package/dist/client/web/CreeveyView/SideBar/SuiteLink.js.map +1 -1
  83. package/dist/client/web/CreeveyView/SideBar/TestLink.js +20 -13
  84. package/dist/client/web/CreeveyView/SideBar/TestLink.js.map +1 -1
  85. package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +2 -2
  86. package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.js +2 -2
  87. package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.js.map +1 -1
  88. package/dist/client/web/CreeveyView/SideBar/TestsStatus.d.ts +2 -2
  89. package/dist/client/web/CreeveyView/SideBar/TestsStatus.js +3 -2
  90. package/dist/client/web/CreeveyView/SideBar/TestsStatus.js.map +1 -1
  91. package/dist/client/web/CreeveyView/SideBar/Toggle.js +1 -1
  92. package/dist/client/web/CreeveyView/SideBar/Toggle.js.map +1 -1
  93. package/dist/client/web/KeyboardEventsContext.d.ts +1 -8
  94. package/dist/client/web/KeyboardEventsContext.js +79 -64
  95. package/dist/client/web/KeyboardEventsContext.js.map +1 -1
  96. package/dist/client/web/assets/index-CtSq3IhG.js +518 -0
  97. package/dist/client/web/index.html +1 -1
  98. package/dist/client/web/index.js +26 -11
  99. package/dist/client/web/index.js.map +1 -1
  100. package/dist/client/web/themes.d.ts +2 -0
  101. package/dist/client/web/themes.js +22 -0
  102. package/dist/client/web/themes.js.map +1 -0
  103. package/dist/creevey.d.ts +1 -1
  104. package/dist/creevey.js +122 -41
  105. package/dist/creevey.js.map +1 -1
  106. package/dist/index.d.ts +1 -0
  107. package/dist/playwright/generator.d.ts +25 -0
  108. package/dist/playwright/generator.js +243 -0
  109. package/dist/playwright/generator.js.map +1 -0
  110. package/dist/playwright/helpers.d.ts +2 -0
  111. package/dist/playwright/helpers.js +29 -0
  112. package/dist/playwright/helpers.js.map +1 -0
  113. package/dist/playwright/reporter.d.ts +83 -0
  114. package/dist/playwright/reporter.js +334 -0
  115. package/dist/playwright/reporter.js.map +1 -0
  116. package/dist/playwright/setup.d.ts +3 -0
  117. package/dist/playwright/setup.js +72 -0
  118. package/dist/playwright/setup.js.map +1 -0
  119. package/dist/playwright.d.ts +1 -0
  120. package/dist/playwright.js +3 -1
  121. package/dist/playwright.js.map +1 -1
  122. package/dist/server/compare.d.ts +18 -0
  123. package/dist/server/compare.js +182 -0
  124. package/dist/server/compare.js.map +1 -0
  125. package/dist/server/config.d.ts +3 -3
  126. package/dist/server/config.js +75 -8
  127. package/dist/server/config.js.map +1 -1
  128. package/dist/server/connection.d.ts +3 -0
  129. package/dist/server/connection.js +28 -0
  130. package/dist/server/connection.js.map +1 -0
  131. package/dist/server/docker.d.ts +1 -1
  132. package/dist/server/docker.js +54 -32
  133. package/dist/server/docker.js.map +1 -1
  134. package/dist/server/index.d.ts +2 -2
  135. package/dist/server/index.js +165 -64
  136. package/dist/server/index.js.map +1 -1
  137. package/dist/server/master/api.d.ts +11 -6
  138. package/dist/server/master/api.js +88 -25
  139. package/dist/server/master/api.js.map +1 -1
  140. package/dist/server/master/handlers/capture-handler.d.ts +5 -0
  141. package/dist/server/master/handlers/capture-handler.js +25 -0
  142. package/dist/server/master/handlers/capture-handler.js.map +1 -0
  143. package/dist/server/master/handlers/index.d.ts +4 -0
  144. package/dist/server/master/handlers/index.js +21 -0
  145. package/dist/server/master/handlers/index.js.map +1 -0
  146. package/dist/server/master/handlers/ping-handler.d.ts +2 -0
  147. package/dist/server/master/handlers/ping-handler.js +8 -0
  148. package/dist/server/master/handlers/ping-handler.js.map +1 -0
  149. package/dist/server/master/handlers/static-handler.d.ts +1 -0
  150. package/dist/server/master/handlers/static-handler.js +20 -0
  151. package/dist/server/master/handlers/static-handler.js.map +1 -0
  152. package/dist/server/master/handlers/stories-handler.d.ts +4 -0
  153. package/dist/server/master/handlers/stories-handler.js +24 -0
  154. package/dist/server/master/handlers/stories-handler.js.map +1 -0
  155. package/dist/server/master/master.js +7 -24
  156. package/dist/server/master/master.js.map +1 -1
  157. package/dist/server/master/pool.d.ts +1 -0
  158. package/dist/server/master/pool.js +5 -3
  159. package/dist/server/master/pool.js.map +1 -1
  160. package/dist/server/master/queue.d.ts +1 -1
  161. package/dist/server/master/queue.js +14 -6
  162. package/dist/server/master/queue.js.map +1 -1
  163. package/dist/server/master/runner.d.ts +6 -6
  164. package/dist/server/master/runner.js +98 -130
  165. package/dist/server/master/runner.js.map +1 -1
  166. package/dist/server/master/server.d.ts +1 -1
  167. package/dist/server/master/server.js +193 -88
  168. package/dist/server/master/server.js.map +1 -1
  169. package/dist/server/master/start.d.ts +1 -2
  170. package/dist/server/master/start.js +13 -29
  171. package/dist/server/master/start.js.map +1 -1
  172. package/dist/server/master/testsManager.d.ts +81 -0
  173. package/dist/server/master/testsManager.js +282 -0
  174. package/dist/server/master/testsManager.js.map +1 -0
  175. package/dist/server/playwright/docker-file.d.ts +1 -1
  176. package/dist/server/playwright/docker-file.js +17 -8
  177. package/dist/server/playwright/docker-file.js.map +1 -1
  178. package/dist/server/playwright/docker.d.ts +2 -1
  179. package/dist/server/playwright/docker.js +10 -2
  180. package/dist/server/playwright/docker.js.map +1 -1
  181. package/dist/server/playwright/index-source.mjs +16 -0
  182. package/dist/server/playwright/internal.d.ts +7 -7
  183. package/dist/server/playwright/internal.js +144 -84
  184. package/dist/server/playwright/internal.js.map +1 -1
  185. package/dist/server/playwright/webdriver.d.ts +3 -3
  186. package/dist/server/playwright/webdriver.js +0 -6
  187. package/dist/server/playwright/webdriver.js.map +1 -1
  188. package/dist/server/providers/browser.js +4 -3
  189. package/dist/server/providers/browser.js.map +1 -1
  190. package/dist/server/providers/hybrid.js +2 -2
  191. package/dist/server/providers/hybrid.js.map +1 -1
  192. package/dist/server/report.d.ts +10 -0
  193. package/dist/server/report.js +45 -0
  194. package/dist/server/report.js.map +1 -0
  195. package/dist/server/reporters/creevey.d.ts +7 -0
  196. package/dist/server/reporters/creevey.js +63 -0
  197. package/dist/server/reporters/creevey.js.map +1 -0
  198. package/dist/server/reporters/index.d.ts +2 -0
  199. package/dist/server/reporters/index.js +16 -0
  200. package/dist/server/reporters/index.js.map +1 -0
  201. package/dist/server/reporters/junit.d.ts +16 -0
  202. package/dist/server/reporters/junit.js +167 -0
  203. package/dist/server/reporters/junit.js.map +1 -0
  204. package/dist/server/reporters/teamcity.d.ts +7 -0
  205. package/dist/server/reporters/teamcity.js +60 -0
  206. package/dist/server/reporters/teamcity.js.map +1 -0
  207. package/dist/server/selenium/internal.d.ts +4 -4
  208. package/dist/server/selenium/internal.js +56 -40
  209. package/dist/server/selenium/internal.js.map +1 -1
  210. package/dist/server/selenium/selenoid.js +12 -6
  211. package/dist/server/selenium/selenoid.js.map +1 -1
  212. package/dist/server/selenium/webdriver.d.ts +3 -3
  213. package/dist/server/selenium/webdriver.js +4 -8
  214. package/dist/server/selenium/webdriver.js.map +1 -1
  215. package/dist/server/shutdown.d.ts +1 -0
  216. package/dist/server/shutdown.js +23 -0
  217. package/dist/server/shutdown.js.map +1 -0
  218. package/dist/server/stories.d.ts +0 -1
  219. package/dist/server/stories.js +0 -12
  220. package/dist/server/stories.js.map +1 -1
  221. package/dist/server/telemetry.js +3 -3
  222. package/dist/server/telemetry.js.map +1 -1
  223. package/dist/server/testsFiles/parser.js +45 -5
  224. package/dist/server/testsFiles/parser.js.map +1 -1
  225. package/dist/server/utils.d.ts +23 -0
  226. package/dist/server/utils.js +114 -15
  227. package/dist/server/utils.js.map +1 -1
  228. package/dist/server/webdriver.d.ts +1 -1
  229. package/dist/server/worker/context.d.ts +3 -0
  230. package/dist/server/worker/context.js +15 -0
  231. package/dist/server/worker/context.js.map +1 -0
  232. package/dist/server/worker/match-image.d.ts +8 -12
  233. package/dist/server/worker/match-image.js +11 -178
  234. package/dist/server/worker/match-image.js.map +1 -1
  235. package/dist/server/worker/start.d.ts +2 -2
  236. package/dist/server/worker/start.js +41 -64
  237. package/dist/server/worker/start.js.map +1 -1
  238. package/dist/shared/index.d.ts +1 -1
  239. package/dist/shared/index.js +9 -7
  240. package/dist/shared/index.js.map +1 -1
  241. package/dist/types.d.ts +84 -43
  242. package/dist/types.js +65 -1
  243. package/dist/types.js.map +1 -1
  244. package/docs/cli.md +80 -0
  245. package/docs/config.md +179 -165
  246. package/docs/examples/playwright-reporer/playwright.config.ts +37 -0
  247. package/docs/migration-0.9-to-0.10.md +144 -0
  248. package/docs/playwright-reporter.md +357 -0
  249. package/docs/storybook.md +60 -0
  250. package/docs/tests.md +50 -45
  251. package/package.json +78 -83
  252. package/playwright.config.mts +46 -0
  253. package/src/client/addon/components/Addon.tsx +1 -1
  254. package/src/client/addon/components/Panel.tsx +4 -4
  255. package/src/client/addon/components/TestSelect.tsx +2 -2
  256. package/src/client/addon/components/Tools.tsx +2 -2
  257. package/src/client/addon/controller.ts +4 -4
  258. package/src/client/addon/makeDecorator.ts +69 -0
  259. package/src/client/addon/manager.ts +38 -37
  260. package/src/client/addon/preset.ts +2 -1
  261. package/src/client/addon/withCreevey.ts +16 -19
  262. package/src/client/shared/components/ImagesView/BlendView.tsx +1 -1
  263. package/src/client/shared/components/ImagesView/ImagesView.tsx +1 -1
  264. package/src/client/shared/components/ImagesView/SideBySideView.tsx +2 -2
  265. package/src/client/shared/components/ImagesView/SlideView.tsx +2 -2
  266. package/src/client/shared/components/ImagesView/SwapView.tsx +20 -2
  267. package/src/client/shared/components/ImagesView/common.ts +1 -1
  268. package/src/client/shared/components/PageFooter/PageFooter.tsx +1 -1
  269. package/src/client/shared/components/PageFooter/Paging.tsx +1 -1
  270. package/src/client/shared/components/PageHeader/ImagePreview.tsx +2 -1
  271. package/src/client/shared/components/PageHeader/PageHeader.tsx +23 -7
  272. package/src/client/shared/components/ResultsPage.tsx +33 -10
  273. package/src/client/shared/creeveyClientApi.ts +19 -1
  274. package/src/client/shared/helpers.ts +4 -24
  275. package/src/client/web/CreeveyApp.tsx +30 -9
  276. package/src/client/web/CreeveyContext.tsx +11 -0
  277. package/src/client/web/CreeveyLoader.tsx +2 -2
  278. package/src/client/web/CreeveyView/SideBar/Checkbox.tsx +3 -3
  279. package/src/client/web/CreeveyView/SideBar/Search.tsx +4 -4
  280. package/src/client/web/CreeveyView/SideBar/SideBar.tsx +11 -6
  281. package/src/client/web/CreeveyView/SideBar/SideBarFooter.tsx +48 -15
  282. package/src/client/web/CreeveyView/SideBar/SideBarHeader.tsx +20 -5
  283. package/src/client/web/CreeveyView/SideBar/SuiteLink.tsx +12 -12
  284. package/src/client/web/CreeveyView/SideBar/TestLink.tsx +10 -10
  285. package/src/client/web/CreeveyView/SideBar/TestStatusIcon.tsx +2 -2
  286. package/src/client/web/CreeveyView/SideBar/TestsStatus.tsx +3 -2
  287. package/src/client/web/CreeveyView/SideBar/Toggle.tsx +1 -1
  288. package/src/client/web/KeyboardEventsContext.tsx +61 -73
  289. package/src/client/web/index.tsx +10 -5
  290. package/src/client/web/themes.ts +24 -0
  291. package/src/creevey.ts +92 -38
  292. package/src/playwright/generator.ts +322 -0
  293. package/src/playwright/helpers.ts +31 -0
  294. package/src/playwright/reporter.ts +381 -0
  295. package/src/playwright/setup.ts +84 -0
  296. package/src/playwright.ts +1 -0
  297. package/src/server/compare.ts +260 -0
  298. package/src/server/config.ts +52 -9
  299. package/src/server/connection.ts +26 -0
  300. package/src/server/docker.ts +62 -34
  301. package/src/server/index.ts +166 -79
  302. package/src/server/master/api.ts +94 -28
  303. package/src/server/master/handlers/capture-handler.ts +20 -0
  304. package/src/server/master/handlers/index.ts +4 -0
  305. package/src/server/master/handlers/ping-handler.ts +6 -0
  306. package/src/server/master/handlers/static-handler.ts +16 -0
  307. package/src/server/master/handlers/stories-handler.ts +20 -0
  308. package/src/server/master/master.ts +10 -27
  309. package/src/server/master/pool.ts +7 -3
  310. package/src/server/master/queue.ts +21 -7
  311. package/src/server/master/runner.ts +123 -134
  312. package/src/server/master/server.ts +214 -101
  313. package/src/server/master/start.ts +19 -41
  314. package/src/server/master/testsManager.ts +316 -0
  315. package/src/server/playwright/docker-file.ts +20 -8
  316. package/src/server/playwright/docker.ts +16 -3
  317. package/src/server/playwright/index-source.mjs +16 -0
  318. package/src/server/playwright/internal.ts +176 -103
  319. package/src/server/playwright/webdriver.ts +4 -10
  320. package/src/server/providers/browser.ts +4 -3
  321. package/src/server/providers/hybrid.ts +2 -3
  322. package/src/server/report.ts +51 -0
  323. package/src/server/reporters/creevey.ts +71 -0
  324. package/src/server/reporters/index.ts +11 -0
  325. package/src/server/reporters/junit.ts +207 -0
  326. package/src/server/reporters/teamcity.ts +74 -0
  327. package/src/server/selenium/internal.ts +70 -53
  328. package/src/server/selenium/selenoid.ts +13 -6
  329. package/src/server/selenium/webdriver.ts +8 -12
  330. package/src/server/shutdown.ts +19 -0
  331. package/src/server/stories.ts +1 -12
  332. package/src/server/telemetry.ts +3 -3
  333. package/src/server/testsFiles/parser.ts +52 -4
  334. package/src/server/utils.ts +124 -16
  335. package/src/server/webdriver.ts +1 -1
  336. package/src/server/worker/context.ts +14 -0
  337. package/src/server/worker/match-image.ts +16 -248
  338. package/src/server/worker/start.ts +49 -79
  339. package/src/shared/index.ts +10 -8
  340. package/src/types.ts +91 -58
  341. package/types/global.d.ts +1 -0
  342. package/dist/client/web/assets/index-DB8lHlJw.js +0 -591
  343. package/dist/server/reporter.d.ts +0 -26
  344. package/dist/server/reporter.js +0 -108
  345. package/dist/server/reporter.js.map +0 -1
  346. package/dist/server/update.d.ts +0 -2
  347. package/dist/server/update.js +0 -53
  348. package/dist/server/update.js.map +0 -1
  349. package/src/server/reporter.ts +0 -139
  350. package/src/server/update.ts +0 -74
@@ -1,22 +1,39 @@
1
- import { Browser, BrowserType, Page, chromium, firefox, webkit } from 'playwright-core';
1
+ import path from 'path';
2
+ import assert from 'assert';
3
+ import {
4
+ Browser,
5
+ BrowserContext,
6
+ BrowserContextOptions,
7
+ BrowserType,
8
+ Page,
9
+ chromium,
10
+ firefox,
11
+ webkit,
12
+ } from 'playwright-core';
2
13
  import chalk from 'chalk';
3
14
  import { v4 } from 'uuid';
15
+ import Logger from 'loglevel';
4
16
  import prefix from 'loglevel-plugin-prefix';
17
+ import type { Args } from 'storybook/internal/types';
5
18
  import {
6
19
  BrowserConfigObject,
7
20
  Config,
8
- Options,
9
21
  StoriesRaw,
10
22
  StoryInput,
11
23
  StorybookEvents,
12
24
  StorybookGlobals,
13
- noop,
25
+ WorkerOptions,
14
26
  } from '../../types';
15
- import { subscribeOn } from '../messages';
16
27
  import { appendIframePath, getAddresses, LOCALHOST_REGEXP, resolveStorybookUrl, storybookRootID } from '../webdriver';
17
- import { isShuttingDown, runSequence } from '../utils';
28
+ import { getCreeveyCache, isShuttingDown, resolvePlaywrightBrowserType, runSequence } from '../utils';
18
29
  import { colors, logger } from '../logger';
19
- import { Args } from '@storybook/csf';
30
+ import { removeWorkerContainer } from '../worker/context';
31
+
32
+ const browsers = {
33
+ chromium,
34
+ firefox,
35
+ webkit,
36
+ };
20
37
 
21
38
  async function tryConnect(type: BrowserType, gridUrl: string): Promise<Browser | null> {
22
39
  let timeout: NodeJS.Timeout | null = null;
@@ -49,23 +66,59 @@ async function tryConnect(type: BrowserType, gridUrl: string): Promise<Browser |
49
66
  ]);
50
67
  }
51
68
 
69
+ async function tryCreateBrowserContext(
70
+ browser: Browser,
71
+ options: BrowserContextOptions,
72
+ ): Promise<{ context: BrowserContext; page: Page }> {
73
+ try {
74
+ const context = await browser.newContext(options);
75
+ const page = await context.newPage();
76
+ return { context, page };
77
+ } catch (error) {
78
+ if (error instanceof Error && error.message.includes('ffmpeg')) {
79
+ logger().warn('Failed to create browser context with video recording. Video recording will be disabled.');
80
+ logger().warn(error);
81
+ const context = await browser.newContext({
82
+ ...options,
83
+ recordVideo: undefined,
84
+ });
85
+ const page = await context.newPage();
86
+ return { context, page };
87
+ }
88
+ throw error;
89
+ }
90
+ }
91
+
52
92
  export class InternalBrowser {
53
93
  #isShuttingDown = false;
54
94
  #browser: Browser;
95
+ #context: BrowserContext;
55
96
  #page: Page;
97
+ #traceDir: string;
56
98
  #sessionId: string = v4();
57
99
  #serverHost: string | null = null;
58
100
  #serverPort: number;
59
- #unsubscribe: () => void = noop;
60
- constructor(browser: Browser, page: Page, port: number) {
101
+ #debug: boolean;
102
+ #storybookGlobals?: StorybookGlobals;
103
+ constructor(
104
+ browser: Browser,
105
+ context: BrowserContext,
106
+ page: Page,
107
+ traceDir: string,
108
+ port: number,
109
+ debug = false,
110
+ storybookGlobals?: StorybookGlobals,
111
+ ) {
61
112
  this.#browser = browser;
113
+ this.#context = context;
62
114
  this.#page = page;
115
+ this.#traceDir = traceDir;
63
116
  this.#serverPort = port;
64
- this.#unsubscribe = subscribeOn('shutdown', () => {
65
- void this.closeBrowser();
66
- });
117
+ this.#debug = debug;
118
+ this.#storybookGlobals = storybookGlobals;
67
119
  }
68
120
 
121
+ // TODO Expose #browser and #context in tests
69
122
  get browser() {
70
123
  return this.#page;
71
124
  }
@@ -78,32 +131,41 @@ export class InternalBrowser {
78
131
  if (this.#isShuttingDown) return;
79
132
 
80
133
  this.#isShuttingDown = true;
81
- this.#unsubscribe();
82
134
 
83
- try {
84
- await this.#page.close();
85
- await this.#browser.close();
86
- } catch (_) {
87
- /* noop */
135
+ const teardown = [
136
+ this.#debug ? () => this.#context.tracing.stop({ path: path.join(this.#traceDir, 'trace.zip') }) : null,
137
+ () => this.#page.close(),
138
+ this.#debug ? () => this.#page.video()?.saveAs(path.join(this.#traceDir, 'video.webm')) : null,
139
+ () => this.#context.close(),
140
+ () => this.#browser.close(),
141
+ () => removeWorkerContainer(),
142
+ ];
143
+
144
+ for (const fn of teardown) {
145
+ try {
146
+ if (fn) await fn();
147
+ } catch {
148
+ /* noop */
149
+ }
88
150
  }
89
151
  }
90
152
 
91
153
  async takeScreenshot(captureElement?: string | null, ignoreElements?: string | string[] | null): Promise<Buffer> {
92
- // TODO Implement features from selenium `takeScreenshot`
93
- // TODO Do we need scroll bar hack from selenium?
94
154
  const ignore = Array.isArray(ignoreElements) ? ignoreElements : ignoreElements ? [ignoreElements] : [];
95
155
  const mask = ignore.map((el) => this.#page.locator(el));
96
156
  if (captureElement) {
97
157
  const element = await this.#page.$(captureElement);
98
158
  if (!element) throw new Error(`Element with selector ${captureElement} not found`);
99
159
 
160
+ logger().debug(`Capturing ${chalk.cyan(captureElement)} element`);
100
161
  return element.screenshot({
162
+ style: ':root { overflow: hidden !important; }',
101
163
  animations: 'disabled',
102
164
  mask,
103
- style: ':root { overflow: hidden !important; }',
104
165
  });
105
166
  }
106
- return this.#page.screenshot({ animations: 'disabled', mask, fullPage: true });
167
+ logger().debug('Capturing viewport screenshot');
168
+ return this.#page.screenshot({ animations: 'disabled', mask });
107
169
  }
108
170
 
109
171
  waitForComplete(callback: (isCompleted: boolean) => void): void {
@@ -112,6 +174,7 @@ export class InternalBrowser {
112
174
 
113
175
  async selectStory(id: string, waitForReady = false): Promise<boolean> {
114
176
  // NOTE: Global variables might be reset after hot reload. I think it's workaround, maybe we need better solution
177
+ await this.updateStorybookGlobals();
115
178
  await this.updateBrowserGlobalVariables();
116
179
  await this.resetMousePosition();
117
180
 
@@ -122,6 +185,7 @@ export class InternalBrowser {
122
185
  [id: string, shouldWaitForReady: boolean]
123
186
  >(
124
187
  ([id, shouldWaitForReady]) => {
188
+ // TODO: Don't use creevey related global variables, inline this function to simplify support
125
189
  if (typeof window.__CREEVEY_SELECT_STORY__ == 'undefined') {
126
190
  return [
127
191
  "Creevey can't switch story. This may happened if forget to add `creevey` addon to your storybook config, or storybook not loaded in browser due syntax error.",
@@ -154,58 +218,50 @@ export class InternalBrowser {
154
218
  );
155
219
  }
156
220
 
157
- async loadStoriesFromBrowser(retry = false): Promise<StoriesRaw> {
158
- try {
159
- const stories = await this.#page.evaluate<StoriesRaw | undefined>(() => window.__CREEVEY_GET_STORIES__());
221
+ async loadStoriesFromBrowser(): Promise<StoriesRaw> {
222
+ const stories = await this.#page.evaluate<StoriesRaw | undefined>(() => window.__CREEVEY_GET_STORIES__());
160
223
 
161
- if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
224
+ if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
162
225
 
163
- return stories;
164
- } catch (error) {
165
- // TODO Check how other solutions with playwright get stories from storybook
166
- if (retry) throw error;
167
- await new Promise((resolve) => setTimeout(resolve, 1000));
168
- // NOTE: Try one more time because of dynamic nature of vite and storybook
169
- return this.loadStoriesFromBrowser(true);
170
- }
226
+ return stories;
171
227
  }
172
228
 
173
229
  static async getBrowser(
174
230
  browserName: string,
175
231
  gridUrl: string,
176
232
  config: Config,
177
- options: Options,
233
+ options: WorkerOptions,
178
234
  ): Promise<InternalBrowser | null> {
179
235
  const browserConfig = config.browsers[browserName] as BrowserConfigObject;
180
236
  const {
181
237
  storybookUrl: address = config.storybookUrl,
182
238
  viewport,
239
+ // eslint-disable-next-line @typescript-eslint/no-deprecated
183
240
  _storybookGlobals,
241
+ storybookGlobals = _storybookGlobals,
184
242
  seleniumCapabilities,
185
243
  playwrightOptions,
186
244
  } = browserConfig;
245
+ const parsedUrl = new URL(gridUrl);
246
+ const tracesDir = path.join(
247
+ playwrightOptions?.tracesDir ?? path.join(config.reportDir, 'traces'),
248
+ process.pid.toString(),
249
+ );
250
+ const cacheDir = await getCreeveyCache();
187
251
 
188
- let browser: Browser | null = null;
252
+ assert(cacheDir, "Couldn't get cache directory");
189
253
 
190
- if (new URL(gridUrl).protocol === 'ws:') {
191
- switch (browserConfig.browserName) {
192
- case 'chromium':
193
- browser = await tryConnect(chromium, gridUrl);
194
- break;
195
- case 'firefox':
196
- browser = await tryConnect(firefox, gridUrl);
197
- break;
198
- case 'webkit':
199
- browser = await tryConnect(webkit, gridUrl);
200
- break;
254
+ let browser: Browser | null = null;
201
255
 
202
- default:
203
- logger().error(
204
- `Unknown browser ${browserConfig.browserName}. Playwright supports browsers: chromium, firefox, webkit`,
205
- );
206
- }
256
+ if (parsedUrl.protocol === 'ws:') {
257
+ browser = await tryConnect(browsers[resolvePlaywrightBrowserType(browserConfig.browserName)], gridUrl);
258
+ } else if (parsedUrl.protocol === 'creevey:') {
259
+ browser = await browsers[resolvePlaywrightBrowserType(browserConfig.browserName)].launch({
260
+ ...playwrightOptions,
261
+ tracesDir: path.join(cacheDir, `${process.pid}`),
262
+ });
207
263
  } else {
208
- if (browserConfig.browserName != 'chrome') {
264
+ if (browserConfig.browserName !== 'chrome') {
209
265
  logger().error("Playwright's Selenium Grid feature supports only chrome browser");
210
266
  return null;
211
267
  }
@@ -213,27 +269,50 @@ export class InternalBrowser {
213
269
  process.env.SELENIUM_REMOTE_URL = gridUrl;
214
270
  process.env.SELENIUM_REMOTE_CAPABILITIES = JSON.stringify(seleniumCapabilities);
215
271
 
216
- browser = await chromium.launch(playwrightOptions);
272
+ browser = await chromium.launch({ ...playwrightOptions, tracesDir: path.join(cacheDir, `${process.pid}`) });
217
273
  }
218
274
 
219
275
  if (!browser) {
220
276
  return null;
221
277
  }
222
278
 
223
- const page = await browser.newPage();
279
+ const { context, page } = await tryCreateBrowserContext(browser, {
280
+ recordVideo: options.debug
281
+ ? {
282
+ dir: path.join(cacheDir, `${process.pid}`),
283
+ size: viewport,
284
+ }
285
+ : undefined,
286
+ screen: viewport,
287
+ viewport,
288
+ });
289
+ if (options.debug) {
290
+ await context.tracing.start(
291
+ Object.assign({ screenshots: true, snapshots: true, sources: true }, playwrightOptions?.trace),
292
+ );
293
+ }
224
294
 
225
- // TODO Add debug output
295
+ if (logger().getLevel() <= Logger.levels.DEBUG) {
296
+ page.on('console', (msg) => {
297
+ logger().debug(`Console message: ${msg.text()}`);
298
+ });
299
+ }
226
300
 
227
- const internalBrowser = new InternalBrowser(browser, page, options.port);
301
+ const internalBrowser = new InternalBrowser(
302
+ browser,
303
+ context,
304
+ page,
305
+ tracesDir,
306
+ options.port,
307
+ options.debug,
308
+ storybookGlobals,
309
+ );
228
310
 
229
311
  try {
230
312
  if (isShuttingDown.current) return null;
231
313
  const done = await internalBrowser.init({
232
314
  browserName,
233
- viewport,
234
315
  storybookUrl: address,
235
- storybookGlobals: _storybookGlobals,
236
- resolveStorybookUrl: config.resolveStorybookUrl,
237
316
  });
238
317
 
239
318
  return done ? internalBrowser : null;
@@ -250,19 +329,7 @@ export class InternalBrowser {
250
329
  }
251
330
  }
252
331
 
253
- private async init({
254
- browserName,
255
- viewport,
256
- storybookUrl,
257
- storybookGlobals,
258
- resolveStorybookUrl,
259
- }: {
260
- browserName: string;
261
- viewport?: { width: number; height: number };
262
- storybookUrl: string;
263
- storybookGlobals?: StorybookGlobals;
264
- resolveStorybookUrl?: () => Promise<string>;
265
- }) {
332
+ private async init({ browserName, storybookUrl }: { browserName: string; storybookUrl: string }) {
266
333
  const sessionId = this.#sessionId;
267
334
 
268
335
  prefix.apply(logger(), {
@@ -276,36 +343,26 @@ export class InternalBrowser {
276
343
 
277
344
  return await runSequence(
278
345
  [
279
- () => this.openStorybookPage(storybookUrl, resolveStorybookUrl),
346
+ () => this.openStorybookPage(storybookUrl),
280
347
  () => this.waitForStorybook(),
281
- () => this.updateStorybookGlobals(storybookGlobals),
348
+ () => this.triggerViteReload(),
349
+ () => this.updateStorybookGlobals(),
282
350
  () => this.resolveCreeveyHost(),
283
351
  () => this.updateBrowserGlobalVariables(),
284
- () => this.resizeViewport(viewport),
285
352
  ],
286
353
  () => !this.#isShuttingDown,
287
354
  );
288
355
  }
289
356
 
290
- private async openStorybookPage(storybookUrl: string, resolver?: () => Promise<string>): Promise<void> {
357
+ private async openStorybookPage(storybookUrl: string): Promise<void> {
291
358
  if (!LOCALHOST_REGEXP.test(storybookUrl)) {
292
359
  await this.#page.goto(appendIframePath(storybookUrl));
293
360
  return;
294
361
  }
295
362
 
296
363
  try {
297
- if (resolver) {
298
- logger().debug('Resolving storybook url with custom resolver');
299
-
300
- const resolvedUrl = await resolver();
301
-
302
- logger().debug(`Resolver storybook url ${resolvedUrl}`);
303
-
304
- await this.#page.goto(appendIframePath(resolvedUrl));
305
- } else {
306
- // TODO this.#page.setDefaultNavigationTimeout(10000);
307
- await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
308
- }
364
+ const resolvedUrl = await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
365
+ await this.#page.goto(resolvedUrl);
309
366
  } catch (error) {
310
367
  logger().error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
311
368
  throw error;
@@ -313,21 +370,23 @@ export class InternalBrowser {
313
370
  }
314
371
 
315
372
  private async checkUrl(url: string): Promise<boolean> {
373
+ const page = await this.#browser.newPage();
316
374
  try {
317
375
  logger().debug(`Opening ${chalk.magenta(url)} and checking the page source`);
318
- const response = await this.#page.goto(url, { waitUntil: 'commit' });
376
+ const response = await page.goto(url, { waitUntil: 'commit' });
319
377
  const source = await response?.text();
320
378
 
321
379
  logger().debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
322
380
  return source?.includes(`id="${storybookRootID}"`) ?? false;
323
381
  } catch {
324
382
  return false;
383
+ } finally {
384
+ await page.close();
325
385
  }
326
386
  }
327
387
 
328
388
  private async waitForStorybook(): Promise<void> {
329
- // TODO Duplicated code with selenium
330
- logger().debug('Waiting for `setStories` event to make sure that storybook is initiated');
389
+ logger().debug('Waiting for Storybook to initiate');
331
390
 
332
391
  const isTimeout = await Promise.race([
333
392
  new Promise<boolean>((resolve) => {
@@ -338,6 +397,7 @@ export class InternalBrowser {
338
397
  (async () => {
339
398
  let wait = true;
340
399
  do {
400
+ if (this.#page.isClosed()) return false;
341
401
  try {
342
402
  // TODO Research a different way to ensure storybook is initiated
343
403
  wait = await this.#page.evaluate((SET_GLOBALS: string) => {
@@ -347,27 +407,43 @@ export class InternalBrowser {
347
407
  }, StorybookEvents.SET_GLOBALS);
348
408
  } catch (e: unknown) {
349
409
  logger().debug('An error has been caught during the script:', e);
410
+ if (this.#page.isClosed()) throw e;
350
411
  }
412
+ if (wait) await new Promise((resolve) => setTimeout(resolve, 1000));
351
413
  } while (wait);
352
414
  return false;
353
415
  })(),
354
416
  ]);
355
417
 
356
- // TODO Change the message to describe a reason why it might happen
357
- if (isTimeout) throw new Error('Failed to wait `setStories` event');
418
+ if (isTimeout) throw new Error('Failed to wait Storybook init');
419
+ }
420
+
421
+ // TODO Doesn't work for some reason, maybe because of race-condition
422
+ private async triggerViteReload(): Promise<void> {
423
+ // NOTE: On the first load, Vite might try to optimize some dependencies and reload the page
424
+ // We need to trigger reload earlier to avoid unnecessary reloads further
425
+ try {
426
+ await this.#page.evaluate(async () => {
427
+ await window.__STORYBOOK_PREVIEW__.extract();
428
+ });
429
+ } catch {
430
+ await this.waitForStorybook();
431
+ }
358
432
  }
359
433
 
360
- private async updateStorybookGlobals(globals?: StorybookGlobals): Promise<void> {
361
- if (!globals) return;
434
+ private async updateStorybookGlobals(): Promise<void> {
435
+ if (!this.#storybookGlobals) return;
362
436
 
363
437
  logger().debug('Applying storybook globals');
364
438
  await this.#page.evaluate((globals: StorybookGlobals) => {
365
439
  window.__CREEVEY_UPDATE_GLOBALS__(globals);
366
- }, globals);
440
+ }, this.#storybookGlobals);
367
441
  }
368
442
 
369
443
  private async resolveCreeveyHost(): Promise<void> {
370
- const addresses = getAddresses();
444
+ const storybookUrl = this.#page.url();
445
+ const storybookHost = new URL(storybookUrl).hostname;
446
+ const addresses = [storybookHost, ...getAddresses()];
371
447
 
372
448
  this.#serverHost = await this.#page.evaluate(
373
449
  ([hosts, port]) => {
@@ -392,8 +468,10 @@ export class InternalBrowser {
392
468
  }
393
469
 
394
470
  private async updateBrowserGlobalVariables() {
471
+ logger().debug('Updating browser global variables');
395
472
  await this.#page.evaluate(
396
473
  ([workerId, creeveyHost, creeveyPort]) => {
474
+ window.__CREEVEY_ENV__ = true;
397
475
  window.__CREEVEY_WORKER_ID__ = workerId;
398
476
  window.__CREEVEY_SERVER_HOST__ = creeveyHost ?? 'localhost';
399
477
  window.__CREEVEY_SERVER_PORT__ = creeveyPort;
@@ -402,13 +480,8 @@ export class InternalBrowser {
402
480
  );
403
481
  }
404
482
 
405
- private async resizeViewport(viewport?: { width: number; height: number }): Promise<void> {
406
- if (!viewport) return;
407
-
408
- await this.#page.setViewportSize(viewport);
409
- }
410
-
411
483
  private async resetMousePosition(): Promise<void> {
484
+ logger().debug('Resetting mouse position to (0, 0)');
412
485
  await this.#page.mouse.move(0, 0);
413
486
  }
414
487
  }
@@ -1,6 +1,6 @@
1
1
  /// <reference types="../../../types/playwright-context" />
2
- import { Args } from '@storybook/csf';
3
- import { Config, Options, ServerTest, StoriesRaw, StoryInput } from '../../types';
2
+ import type { Args } from 'storybook/internal/types';
3
+ import { Config, ServerTest, StoriesRaw, StoryInput, WorkerOptions } from '../../types';
4
4
  import { logger } from '../logger';
5
5
  import { subscribeOn } from '../messages';
6
6
  import { CreeveyWebdriverBase } from '../webdriver';
@@ -11,8 +11,8 @@ export class PlaywrightWebdriver extends CreeveyWebdriverBase {
11
11
  #browserName: string;
12
12
  #gridUrl: string;
13
13
  #config: Config;
14
- #options: Options;
15
- constructor(browser: string, gridUrl: string, config: Config, options: Options) {
14
+ #options: WorkerOptions;
15
+ constructor(browser: string, gridUrl: string, config: Config, options: WorkerOptions) {
16
16
  super();
17
17
 
18
18
  this.#browserName = browser;
@@ -32,7 +32,6 @@ export class PlaywrightWebdriver extends CreeveyWebdriverBase {
32
32
 
33
33
  getSessionId(): Promise<string> {
34
34
  if (!this.#browser) {
35
- // TODO Describe the error
36
35
  throw new Error('Browser is not initialized');
37
36
  }
38
37
 
@@ -79,7 +78,6 @@ export class PlaywrightWebdriver extends CreeveyWebdriverBase {
79
78
 
80
79
  async loadStoriesFromBrowser(): Promise<StoriesRaw> {
81
80
  if (!this.#browser) {
82
- // TODO Describe the error
83
81
  throw new Error('Browser is not initialized');
84
82
  }
85
83
 
@@ -95,7 +93,6 @@ export class PlaywrightWebdriver extends CreeveyWebdriverBase {
95
93
  ignoreElements?: string | string[] | null,
96
94
  ): Promise<Buffer> {
97
95
  if (!this.#browser) {
98
- // TODO Describe the error
99
96
  throw new Error('Browser is not initialized');
100
97
  }
101
98
 
@@ -104,7 +101,6 @@ export class PlaywrightWebdriver extends CreeveyWebdriverBase {
104
101
 
105
102
  protected waitForComplete(callback: (isCompleted: boolean) => void): void {
106
103
  if (!this.#browser) {
107
- // TODO Describe the error
108
104
  throw new Error('Browser is not initialized');
109
105
  }
110
106
 
@@ -113,7 +109,6 @@ export class PlaywrightWebdriver extends CreeveyWebdriverBase {
113
109
 
114
110
  protected async selectStory(id: string, waitForReady?: boolean): Promise<boolean> {
115
111
  if (!this.#browser) {
116
- // TODO Describe the error
117
112
  throw new Error('Browser is not initialized');
118
113
  }
119
114
 
@@ -122,7 +117,6 @@ export class PlaywrightWebdriver extends CreeveyWebdriverBase {
122
117
 
123
118
  protected async updateStoryArgs(story: StoryInput, updatedArgs: Args): Promise<void> {
124
119
  if (!this.#browser) {
125
- // TODO Describe the error
126
120
  throw new Error('Browser is not initialized');
127
121
  }
128
122
 
@@ -24,7 +24,7 @@ export const loadStories: StoriesProvider = async (_config, storiesListener, web
24
24
  oldTests.join('\n'),
25
25
  );
26
26
  unsubscribe();
27
- resolve(stories);
27
+ resolve(deserializeRawStories(stories));
28
28
  }
29
29
  });
30
30
  sendStoriesMessage(worker, { type: 'get' });
@@ -37,10 +37,11 @@ export const loadStories: StoriesProvider = async (_config, storiesListener, web
37
37
  } else {
38
38
  subscribeOn('stories', (message) => {
39
39
  if (message.type == 'get')
40
- emitStoriesMessage({ type: 'set', payload: { stories, oldTests: storiesWithOldTests } });
40
+ emitStoriesMessage({ type: 'set', payload: { stories: rawStories, oldTests: storiesWithOldTests } });
41
41
  if (message.type == 'update') storiesListener(new Map(message.payload));
42
42
  });
43
- const stories = deserializeRawStories((await webdriver?.loadStoriesFromBrowser()) ?? {});
43
+ const rawStories = (await webdriver?.loadStoriesFromBrowser()) ?? {};
44
+ const stories = deserializeRawStories(rawStories);
44
45
 
45
46
  const storiesWithOldTests: string[] = [];
46
47
 
@@ -1,5 +1,4 @@
1
- import chokidar from 'chokidar';
2
-
1
+ import { watch } from 'chokidar';
3
2
  import { loadStories as browserProvider } from './browser.js';
4
3
  import type { Config, CreeveyStoryParams, CreeveyStory, StoriesProvider } from '../../types.js';
5
4
  import { logger } from '../logger.js';
@@ -53,7 +52,7 @@ async function parseParams(
53
52
  const testFiles = readDirRecursive(config.testsDir).filter((file) => config.testsRegex?.test(file));
54
53
 
55
54
  if (listener) {
56
- chokidar.watch(testFiles).on('change', (filePath) => {
55
+ watch(testFiles).on('change', (filePath) => {
57
56
  logger().debug(`changed: ${filePath}`);
58
57
 
59
58
  // doesn't work, always returns {} due modules caching
@@ -0,0 +1,51 @@
1
+ import open from 'open';
2
+ import { Config } from '../types.js';
3
+ import { logger } from './logger.js';
4
+ import { TestsManager } from './master/testsManager.js';
5
+ import { start as startServer } from './master/server.js';
6
+ import { CreeveyApi } from './master/api.js';
7
+ import { shutdownWorkers } from './utils.js';
8
+
9
+ /**
10
+ * UI Update Mode implementation.
11
+ * This mode allows users to review and approve screenshots from the browser interface.
12
+ * It combines the functionality of both --ui and --update flags.
13
+ *
14
+ * @param config Creevey configuration
15
+ * @param port Port to run the server on
16
+ */
17
+ export function report(config: Config, reportDir: string, port: number): void {
18
+ logger().info('Starting UI Update Mode');
19
+
20
+ process.on('SIGINT', () => void shutdownWorkers());
21
+
22
+ const url = `http://localhost:${port}`;
23
+
24
+ // Initialize TestsManager with the configured directories
25
+ const testsManager = new TestsManager(config.screenDir, reportDir);
26
+
27
+ // Load tests from the report
28
+ const testsFromReport = testsManager.loadTestsFromReport();
29
+
30
+ if (Object.keys(testsFromReport).length === 0) {
31
+ logger().warn('No tests found in report. Run tests first to generate report data.');
32
+ return;
33
+ }
34
+
35
+ // Set tests in the manager
36
+ testsManager.updateTests(testsFromReport);
37
+
38
+ // Start API server with UI enabled
39
+ const resolveApi = startServer(reportDir, port, true);
40
+
41
+ // Initialize API
42
+ const api = new CreeveyApi(testsManager);
43
+
44
+ // Resolve the API for the server
45
+ resolveApi(api);
46
+
47
+ logger().info(`UI Update Mode started on ${url}`);
48
+ logger().info('You can now review and approve screenshots from the browser.');
49
+
50
+ void open(url);
51
+ }