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
@@ -0,0 +1,381 @@
1
+ import fs from 'fs/promises';
2
+ import { createHash } from 'crypto';
3
+ import type { Reporter, FullConfig, Suite, TestCase, TestResult, TestStep } from '@playwright/test/reporter';
4
+ import {
5
+ type ServerTest,
6
+ type TestMeta,
7
+ type TestStatus,
8
+ type TestResult as CreeveyTestResult,
9
+ isDefined,
10
+ Images,
11
+ } from '../types.js';
12
+ import { TestsManager } from '../server/master/testsManager.js';
13
+ import { CreeveyApi } from '../server/master/api.js';
14
+ import { copyStatics } from '../server/utils.js';
15
+ import { start } from '../server/master/server.js';
16
+ import assert from 'assert';
17
+
18
+ /**
19
+ * Simple async queue to handle operations in sequence without returning promises
20
+ * from reporter methods that should be synchronous
21
+ */
22
+ class AsyncQueue {
23
+ private queue: Promise<void>;
24
+
25
+ constructor() {
26
+ this.queue = Promise.resolve();
27
+ }
28
+
29
+ /**
30
+ * Add an async operation to the queue
31
+ * @param operation Async operation to execute
32
+ */
33
+ enqueue(operation: () => Promise<void>): void {
34
+ this.queue = this.queue.then(operation).catch((error: unknown) => {
35
+ console.error(`Error in async queue: ${error instanceof Error ? error.message : String(error)}`);
36
+ });
37
+ }
38
+
39
+ /**
40
+ * Wait for all operations in the queue to complete
41
+ */
42
+ async waitForCompletion(): Promise<void> {
43
+ await this.queue;
44
+ }
45
+ }
46
+
47
+ /**
48
+ * CreeveyPlaywrightReporter is a Playwright reporter that integrates with Creevey
49
+ * to provide visual testing capabilities and use Creevey's UI for reviewing and approving screenshots.
50
+ */
51
+ class CreeveyPlaywrightReporter implements Reporter {
52
+ private testsManager: TestsManager | null = null;
53
+ private api: CreeveyApi | null = null;
54
+ private port: number;
55
+ private debug: boolean;
56
+ private testIdMap = new Map<string, string>(); // Maps Playwright test IDs to Creevey test IDs
57
+ private asyncQueue = new AsyncQueue();
58
+
59
+ /**
60
+ * Creates a new instance of the CreeveyPlaywrightReporter
61
+ * @param options Configuration options for the reporter
62
+ */
63
+ constructor(options?: { port?: number; debug?: boolean }) {
64
+ this.port = options?.port ?? 3000;
65
+ this.debug = options?.debug ?? false;
66
+ }
67
+
68
+ /**
69
+ * Called when the test run starts
70
+ * @param config Playwright configuration
71
+ * @param suite Test suite information
72
+ */
73
+ onBegin(config: FullConfig, suite: Suite): void {
74
+ this.logDebug('CreeveyPlaywrightReporter started');
75
+
76
+ const [snapshotDir, ...restSnapshotDirs] = [...new Set(config.projects.map((project) => project.snapshotDir))];
77
+ const [outputDir, ...restOutputDirs] = [...new Set(config.projects.map((project) => project.outputDir))];
78
+
79
+ assert(restSnapshotDirs.length === 0, 'Currently multiple snapshot directories are not supported');
80
+ assert(restOutputDirs.length === 0, 'Currently multiple output directories are not supported');
81
+
82
+ // Initialize TestsManager
83
+ this.testsManager = new TestsManager(snapshotDir, outputDir);
84
+
85
+ // Use the async queue to handle initialization without returning a promise
86
+ this.asyncQueue.enqueue(async () => {
87
+ assert(this.testsManager, 'TestsManager is not initialized');
88
+
89
+ try {
90
+ await fs.mkdir(outputDir, { recursive: true });
91
+ await copyStatics(outputDir);
92
+ } catch (error) {
93
+ this.logError(
94
+ `Failed to initialize report directory: ${error instanceof Error ? error.message : String(error)}`,
95
+ );
96
+ }
97
+
98
+ // Start server API
99
+ try {
100
+ const resolveApi = start(outputDir, this.port, true);
101
+
102
+ // Create and connect the API
103
+ this.api = new CreeveyApi(this.testsManager);
104
+ resolveApi(this.api);
105
+
106
+ const testsList = suite
107
+ .allTests()
108
+ .map((test) => {
109
+ const creeveyTest = this.mapToCreeveyTest(test);
110
+ if (!creeveyTest) return;
111
+
112
+ this.testIdMap.set(test.id, creeveyTest.id);
113
+
114
+ return creeveyTest;
115
+ })
116
+ .filter(isDefined);
117
+
118
+ const tests: Record<string, ServerTest> = {};
119
+ for (const test of testsList) {
120
+ tests[test.id] = test;
121
+ }
122
+
123
+ this.testsManager.updateTests(tests);
124
+
125
+ console.log(`Creevey report server started at http://localhost:${this.port}`);
126
+ } catch (error) {
127
+ this.logError(`Could not start Creevey server: ${error instanceof Error ? error.message : String(error)}`);
128
+ console.log('Screenshots will still be captured but UI will not be available');
129
+ }
130
+ });
131
+ }
132
+
133
+ /**
134
+ * Called when a test begins
135
+ * @param test Test case information
136
+ * @param result Test result (initially empty)
137
+ */
138
+ onTestBegin(test: TestCase, _result: TestResult): void {
139
+ try {
140
+ assert(this.testsManager, 'TestsManager is not initialized');
141
+
142
+ const creeveyTestId = this.testIdMap.get(test.id);
143
+
144
+ if (creeveyTestId) {
145
+ // Update test status to running
146
+ this.testsManager.updateTestStatus(creeveyTestId, 'running');
147
+
148
+ this.logDebug(`Test started: ${test.title} (${creeveyTestId})`);
149
+ }
150
+ } catch (error) {
151
+ this.logError(`Error in onTestBegin: ${error instanceof Error ? error.message : String(error)}`);
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Called when a test step begins
157
+ * @param test Test case information
158
+ * @param result Test result
159
+ * @param step Test step information
160
+ */
161
+ onStepBegin(test: TestCase, _result: TestResult, step: TestStep): void {
162
+ /*
163
+ [Creevey Reporter] Step started: browserType.launch in test: 100 X 100 Vs 2000 X 100
164
+ [Creevey Reporter] Step started: browserType.launch in test: Side By Side
165
+ [Creevey Reporter] Step started: browserType.launch in test: Full
166
+ */
167
+ if (this.debug) {
168
+ this.logDebug(`Step started: ${step.title} in test: ${test.title}`);
169
+ }
170
+ }
171
+
172
+ /**
173
+ * Called when a test step ends
174
+ * @param test Test case information
175
+ * @param result Test result
176
+ * @param step Test step information
177
+ */
178
+ onStepEnd(_test: TestCase, _result: TestResult, step: TestStep): void {
179
+ try {
180
+ // If step has attachments, process them
181
+ if (step.attachments.length > 0) {
182
+ this.logDebug(`Processing ${step.attachments.length} attachments from step: ${step.title}`);
183
+
184
+ // We'll process attachments in onTestEnd for simplicity in this initial implementation
185
+ }
186
+ } catch (error) {
187
+ this.logError(`Error in onStepEnd: ${error instanceof Error ? error.message : String(error)}`);
188
+ }
189
+ }
190
+
191
+ /**
192
+ * Called when a test ends
193
+ * @param test Test case information
194
+ * @param result Test result
195
+ */
196
+ onTestEnd(test: TestCase, result: TestResult): void {
197
+ const creeveyTestId = this.testIdMap.get(test.id);
198
+
199
+ // Use the async queue to handle result processing without returning a promise
200
+ this.asyncQueue.enqueue(async () => {
201
+ try {
202
+ // Process test results and screenshots
203
+ await this.processTestResult(test, result);
204
+
205
+ if (creeveyTestId) {
206
+ this.logDebug(`Test ended: ${test.title} (${creeveyTestId}) with status: ${result.status}`);
207
+ }
208
+ } catch (error) {
209
+ this.logError(`Error in onTestEnd: ${error instanceof Error ? error.message : String(error)}`);
210
+ }
211
+ });
212
+ }
213
+
214
+ /**
215
+ * Called when the test run ends
216
+ * @param result The overall test run result
217
+ */
218
+ async onEnd(result: { status: 'passed' | 'failed' | 'timedout' | 'interrupted' }): Promise<void> {
219
+ // Use the async queue to handle final operations without returning a promise
220
+ this.asyncQueue.enqueue(async () => {
221
+ try {
222
+ assert(this.testsManager, 'TestsManager is not initialized');
223
+
224
+ // Save test data
225
+ await this.testsManager.saveTestData();
226
+
227
+ this.logDebug(`Test run ended with status: ${result.status}`);
228
+ // TODO: Tell how to run reporter `yarn creevey update ./report --ui`
229
+ } catch (error) {
230
+ this.logError(`Error during reporter cleanup: ${error instanceof Error ? error.message : String(error)}`);
231
+ }
232
+ });
233
+
234
+ // Wait for all previous operations to complete
235
+ await this.asyncQueue.waitForCompletion();
236
+ }
237
+
238
+ /**
239
+ * Maps a Playwright test to a Creevey test format
240
+ * @param test Playwright test case
241
+ * @returns Creevey test object or null if mapping fails
242
+ */
243
+ private mapToCreeveyTest(test: TestCase): ServerTest | null {
244
+ try {
245
+ const storyName = test.title;
246
+ const storyTitle = test.parent.title;
247
+ const projectName = test.parent.project()?.name ?? 'chromium';
248
+ const testPath = [storyTitle, storyName, projectName];
249
+ const { description: storyId } = test.annotations.find((annotation) => annotation.type === 'storyId') ?? {};
250
+
251
+ // Generate a unique test ID
252
+ const testId = createHash('sha1').update(testPath.join('/')).digest('hex');
253
+
254
+ // Create the test metadata
255
+ const testMeta: TestMeta = {
256
+ id: testId,
257
+ storyPath: [...storyTitle.split('/').map((x) => x.trim()), storyName],
258
+ browser: projectName,
259
+ storyId: storyId ?? '',
260
+ };
261
+
262
+ // Create a stub ServerTest object
263
+ // This is missing the story and fn properties which would be used in a real Creevey test
264
+ // However, for our reporter purposes, we just need the metadata
265
+ const serverTest: ServerTest = {
266
+ ...testMeta,
267
+ story: {
268
+ parameters: {},
269
+ initialArgs: {},
270
+ argTypes: {},
271
+ component: '',
272
+ componentId: '',
273
+ name: storyName,
274
+ tags: [],
275
+ title: storyTitle,
276
+ kind: storyTitle,
277
+ id: storyId ?? '',
278
+ story: storyName,
279
+ }, // Placeholder
280
+ fn: async () => {
281
+ /* Empty function as placeholder */
282
+ }, // Placeholder
283
+ };
284
+
285
+ return serverTest;
286
+ } catch (error) {
287
+ this.logError(`Error mapping test to Creevey format: ${error instanceof Error ? error.message : String(error)}`);
288
+ return null;
289
+ }
290
+ }
291
+
292
+ /**
293
+ * Process a test result and any attachments
294
+ * @param test Playwright test case
295
+ * @param result Playwright test result
296
+ */
297
+ private async processTestResult(test: TestCase, result: TestResult): Promise<void> {
298
+ const creeveyTestId = this.testIdMap.get(test.id);
299
+ if (!creeveyTestId) {
300
+ this.logError(`No Creevey test ID found for test: ${test.title}`);
301
+ return Promise.resolve();
302
+ }
303
+
304
+ assert(this.testsManager, 'TestsManager is not initialized');
305
+
306
+ // Determine test status
307
+ let status: TestStatus;
308
+ switch (result.status) {
309
+ case 'passed':
310
+ status = 'success';
311
+ break;
312
+ case 'failed':
313
+ case 'timedOut':
314
+ status = 'failed';
315
+ break;
316
+ default:
317
+ status = 'unknown';
318
+ }
319
+
320
+ // Process attachments
321
+ const images: Record<string, Images> = {};
322
+ const attachmentPaths: string[] = [];
323
+ const projectName = test.parent.project()?.name ?? 'chromium';
324
+
325
+ for (const attachment of result.attachments) {
326
+ const { name, path: attachmentPath } = attachment;
327
+
328
+ if (!attachmentPath) continue;
329
+
330
+ attachmentPaths.push(attachmentPath);
331
+
332
+ switch (true) {
333
+ case name.includes('actual'): {
334
+ images[projectName] = { ...images[projectName], actual: name };
335
+ break;
336
+ }
337
+ case name.includes('expect'): {
338
+ images[projectName] = { ...images[projectName], expect: name };
339
+ break;
340
+ }
341
+ case name.includes('diff'): {
342
+ images[projectName] = { ...images[projectName], diff: name };
343
+ break;
344
+ }
345
+ }
346
+ }
347
+
348
+ // Update test status and result
349
+ const testResult: CreeveyTestResult = {
350
+ status: status === 'success' ? 'success' : 'failed',
351
+ retries: result.retry,
352
+ images,
353
+ error: result.error?.message ?? undefined,
354
+ duration: result.duration,
355
+ attachments: attachmentPaths,
356
+ browserName: projectName,
357
+ };
358
+
359
+ this.testsManager.updateTestStatus(creeveyTestId, status, testResult);
360
+ }
361
+
362
+ /**
363
+ * Logs a debug message if debug mode is enabled
364
+ * @param message Message to log
365
+ */
366
+ private logDebug(message: string): void {
367
+ if (this.debug) {
368
+ console.log(`[Creevey Reporter] ${message}`);
369
+ }
370
+ }
371
+
372
+ /**
373
+ * Logs an error message
374
+ * @param message Error message to log
375
+ */
376
+ private logError(message: string): void {
377
+ console.error(`[Creevey Reporter] ERROR: ${message}`);
378
+ }
379
+ }
380
+
381
+ export default CreeveyPlaywrightReporter;
@@ -0,0 +1,84 @@
1
+ import path from 'path';
2
+ import assert from 'assert';
3
+ import { mkdir, writeFile } from 'fs/promises';
4
+ import { chromium, firefox, webkit, Page, FullConfig } from '@playwright/test';
5
+ import { StoriesRaw } from '../types';
6
+ import { getCreeveyCache } from '../server/utils';
7
+ import { appendIframePath } from '../server/webdriver';
8
+ import { waitForStorybookReady } from './helpers';
9
+
10
+ // This function will fetch stories and cache them or an error if fetching fails.
11
+ // It's intended to be called once before tests that depend on stories are defined.
12
+ async function ensureStoriesFetched(page: Page, storybookUrl: string): Promise<StoriesRaw> {
13
+ try {
14
+ console.log(`Fetching stories from: ${storybookUrl}`);
15
+
16
+ await page.goto(appendIframePath(storybookUrl), { waitUntil: 'networkidle', timeout: 60000 });
17
+ await waitForStorybookReady(page);
18
+
19
+ // TODO: Inline `serializeRawStories` to serialize creevey skip parameters
20
+ const fetchedStories = await page.evaluate<StoriesRaw | undefined>(() => window.__STORYBOOK_PREVIEW__.extract());
21
+
22
+ if (!fetchedStories || Object.keys(fetchedStories).length === 0) {
23
+ throw new Error('No stories were found or cached from Storybook.');
24
+ }
25
+ console.log(`Successfully fetched and cached ${Object.keys(fetchedStories).length} stories.`);
26
+
27
+ return fetchedStories;
28
+ } catch (error: unknown) {
29
+ console.error('Error fetching stories');
30
+ throw error;
31
+ }
32
+ }
33
+
34
+ const browsers = {
35
+ chromium,
36
+ firefox,
37
+ webkit,
38
+ };
39
+
40
+ // TODO: Setup should generate test files for each story file (component)
41
+ // TODO: Add support for multiple storybook urls
42
+ async function globalSetup(config: FullConfig) {
43
+ const storybookUrl = config.webServer?.url;
44
+ const { defaultBrowserType = 'chromium', browserName = defaultBrowserType } = config.projects[0].use;
45
+
46
+ assert(storybookUrl, 'Storybook URL is required');
47
+
48
+ const cacheDir = await getCreeveyCache();
49
+ assert(cacheDir, 'Cache directory not found');
50
+
51
+ await mkdir(cacheDir, { recursive: true });
52
+
53
+ process.env.CREEVEY_CACHE_DIR = cacheDir;
54
+
55
+ const browser = await browsers[browserName].launch();
56
+ const context = await browser.newContext();
57
+ const page = await context.newPage();
58
+
59
+ if (process.env.PWDEBUG) {
60
+ await context.tracing.start({ name: 'storybook-setup' });
61
+ }
62
+
63
+ try {
64
+ const stories = await ensureStoriesFetched(page, storybookUrl);
65
+
66
+ await writeFile(path.join(cacheDir, 'stories.json'), JSON.stringify(stories, null, 2));
67
+ } catch (error) {
68
+ console.error('Error in globalSetup:', error);
69
+
70
+ if (process.env.PWDEBUG) {
71
+ const tracePath = path.join(cacheDir, 'storybook-setup-trace.zip');
72
+
73
+ console.log('Trace is saved to:', tracePath);
74
+
75
+ await context.tracing.stop({ path: tracePath });
76
+ }
77
+
78
+ throw error;
79
+ } finally {
80
+ await browser.close();
81
+ }
82
+ }
83
+
84
+ export default globalSetup;
package/src/playwright.ts CHANGED
@@ -1 +1,2 @@
1
1
  export { PlaywrightWebdriver } from './server/playwright/webdriver.js';
2
+ export { definePlaywrightTests } from './playwright/generator.js';