creevey 0.8.1-beta.0 → 0.8.1-sb7.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 (242) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/package.json +22 -19
  3. package/lib/cjs/cli.js +0 -5
  4. package/lib/cjs/client/addon/Manager.js +0 -264
  5. package/lib/cjs/client/addon/components/Addon.js +0 -55
  6. package/lib/cjs/client/addon/components/Icons.js +0 -46
  7. package/lib/cjs/client/addon/components/Panel.js +0 -72
  8. package/lib/cjs/client/addon/components/TestSelect.js +0 -65
  9. package/lib/cjs/client/addon/components/Tools.js +0 -95
  10. package/lib/cjs/client/addon/decorator.js +0 -11
  11. package/lib/cjs/client/addon/index.js +0 -31
  12. package/lib/cjs/client/addon/preset.ie11.js +0 -74
  13. package/lib/cjs/client/addon/preset.js +0 -62
  14. package/lib/cjs/client/addon/readyForCapture.js +0 -12
  15. package/lib/cjs/client/addon/register.js +0 -72
  16. package/lib/cjs/client/addon/utils.js +0 -42
  17. package/lib/cjs/client/addon/withCreevey.js +0 -350
  18. package/lib/cjs/client/shared/components/ImagesView/BlendView.js +0 -87
  19. package/lib/cjs/client/shared/components/ImagesView/ImagesView.js +0 -92
  20. package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +0 -154
  21. package/lib/cjs/client/shared/components/ImagesView/SlideView.js +0 -166
  22. package/lib/cjs/client/shared/components/ImagesView/SwapView.js +0 -91
  23. package/lib/cjs/client/shared/components/ImagesView/index.js +0 -45
  24. package/lib/cjs/client/shared/components/PageFooter/PageFooter.js +0 -50
  25. package/lib/cjs/client/shared/components/PageFooter/Paging.js +0 -94
  26. package/lib/cjs/client/shared/components/PageHeader/ImagePreview.js +0 -82
  27. package/lib/cjs/client/shared/components/PageHeader/PageHeader.js +0 -119
  28. package/lib/cjs/client/shared/components/ResultsPage.js +0 -143
  29. package/lib/cjs/client/shared/creeveyClientApi.js +0 -76
  30. package/lib/cjs/client/shared/helpers.js +0 -411
  31. package/lib/cjs/client/shared/viewMode.js +0 -17
  32. package/lib/cjs/client/web/142.js +0 -2
  33. package/lib/cjs/client/web/142.js.LICENSE.txt +0 -12
  34. package/lib/cjs/client/web/32.js +0 -1
  35. package/lib/cjs/client/web/551.js +0 -1
  36. package/lib/cjs/client/web/566.js +0 -2
  37. package/lib/cjs/client/web/566.js.LICENSE.txt +0 -31
  38. package/lib/cjs/client/web/691.js +0 -2
  39. package/lib/cjs/client/web/691.js.LICENSE.txt +0 -8
  40. package/lib/cjs/client/web/725.js +0 -1
  41. package/lib/cjs/client/web/index.html +0 -19
  42. package/lib/cjs/client/web/main.js +0 -2
  43. package/lib/cjs/client/web/main.js.LICENSE.txt +0 -49
  44. package/lib/cjs/creevey.js +0 -69
  45. package/lib/cjs/index.js +0 -39
  46. package/lib/cjs/server/config.js +0 -93
  47. package/lib/cjs/server/docker.js +0 -146
  48. package/lib/cjs/server/extract.js +0 -46
  49. package/lib/cjs/server/index.js +0 -83
  50. package/lib/cjs/server/loaders/babel/creevey-plugin.js +0 -86
  51. package/lib/cjs/server/loaders/babel/helpers.js +0 -469
  52. package/lib/cjs/server/loaders/babel/register.js +0 -124
  53. package/lib/cjs/server/loaders/hooks/mdx.js +0 -30
  54. package/lib/cjs/server/loaders/hooks/svelte.js +0 -65
  55. package/lib/cjs/server/loaders/webpack/compile.js +0 -269
  56. package/lib/cjs/server/loaders/webpack/creevey-loader.js +0 -172
  57. package/lib/cjs/server/loaders/webpack/dummy-hmr.js +0 -39
  58. package/lib/cjs/server/loaders/webpack/mdx-loader.js +0 -72
  59. package/lib/cjs/server/loaders/webpack/start.js +0 -41
  60. package/lib/cjs/server/logger.js +0 -48
  61. package/lib/cjs/server/master/api.js +0 -71
  62. package/lib/cjs/server/master/index.js +0 -152
  63. package/lib/cjs/server/master/master.js +0 -57
  64. package/lib/cjs/server/master/pool.js +0 -197
  65. package/lib/cjs/server/master/runner.js +0 -281
  66. package/lib/cjs/server/master/server.js +0 -131
  67. package/lib/cjs/server/messages.js +0 -264
  68. package/lib/cjs/server/selenium/browser.js +0 -656
  69. package/lib/cjs/server/selenium/index.js +0 -31
  70. package/lib/cjs/server/selenium/selenoid.js +0 -172
  71. package/lib/cjs/server/stories.js +0 -153
  72. package/lib/cjs/server/storybook/entry.js +0 -53
  73. package/lib/cjs/server/storybook/helpers.js +0 -158
  74. package/lib/cjs/server/storybook/providers/browser.js +0 -74
  75. package/lib/cjs/server/storybook/providers/nodejs.js +0 -237
  76. package/lib/cjs/server/update.js +0 -79
  77. package/lib/cjs/server/utils.js +0 -169
  78. package/lib/cjs/server/worker/chai-image.js +0 -142
  79. package/lib/cjs/server/worker/helpers.js +0 -69
  80. package/lib/cjs/server/worker/index.js +0 -15
  81. package/lib/cjs/server/worker/reporter.js +0 -108
  82. package/lib/cjs/server/worker/worker.js +0 -268
  83. package/lib/cjs/shared/index.js +0 -101
  84. package/lib/cjs/shared/serializeRegExp.js +0 -42
  85. package/lib/cjs/types.js +0 -74
  86. package/lib/esm/cli.js +0 -4
  87. package/lib/esm/client/addon/Manager.js +0 -248
  88. package/lib/esm/client/addon/components/Addon.js +0 -39
  89. package/lib/esm/client/addon/components/Icons.js +0 -31
  90. package/lib/esm/client/addon/components/Panel.js +0 -53
  91. package/lib/esm/client/addon/components/TestSelect.js +0 -51
  92. package/lib/esm/client/addon/components/Tools.js +0 -74
  93. package/lib/esm/client/addon/decorator.js +0 -2
  94. package/lib/esm/client/addon/index.js +0 -2
  95. package/lib/esm/client/addon/preset.ie11.js +0 -59
  96. package/lib/esm/client/addon/preset.js +0 -41
  97. package/lib/esm/client/addon/readyForCapture.js +0 -5
  98. package/lib/esm/client/addon/register.js +0 -51
  99. package/lib/esm/client/addon/utils.js +0 -32
  100. package/lib/esm/client/addon/withCreevey.js +0 -323
  101. package/lib/esm/client/shared/components/ImagesView/BlendView.js +0 -67
  102. package/lib/esm/client/shared/components/ImagesView/ImagesView.js +0 -69
  103. package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +0 -131
  104. package/lib/esm/client/shared/components/ImagesView/SlideView.js +0 -143
  105. package/lib/esm/client/shared/components/ImagesView/SwapView.js +0 -71
  106. package/lib/esm/client/shared/components/ImagesView/index.js +0 -5
  107. package/lib/esm/client/shared/components/PageFooter/PageFooter.js +0 -36
  108. package/lib/esm/client/shared/components/PageFooter/Paging.js +0 -80
  109. package/lib/esm/client/shared/components/PageHeader/ImagePreview.js +0 -68
  110. package/lib/esm/client/shared/components/PageHeader/PageHeader.js +0 -97
  111. package/lib/esm/client/shared/components/ResultsPage.js +0 -115
  112. package/lib/esm/client/shared/creeveyClientApi.js +0 -67
  113. package/lib/esm/client/shared/helpers.js +0 -353
  114. package/lib/esm/client/shared/viewMode.js +0 -6
  115. package/lib/esm/creevey.js +0 -54
  116. package/lib/esm/index.js +0 -3
  117. package/lib/esm/server/config.js +0 -70
  118. package/lib/esm/server/docker.js +0 -123
  119. package/lib/esm/server/extract.js +0 -32
  120. package/lib/esm/server/index.js +0 -64
  121. package/lib/esm/server/loaders/babel/creevey-plugin.js +0 -72
  122. package/lib/esm/server/loaders/babel/helpers.js +0 -452
  123. package/lib/esm/server/loaders/babel/register.js +0 -103
  124. package/lib/esm/server/loaders/hooks/mdx.js +0 -15
  125. package/lib/esm/server/loaders/hooks/svelte.js +0 -49
  126. package/lib/esm/server/loaders/webpack/compile.js +0 -246
  127. package/lib/esm/server/loaders/webpack/creevey-loader.js +0 -152
  128. package/lib/esm/server/loaders/webpack/dummy-hmr.js +0 -32
  129. package/lib/esm/server/loaders/webpack/mdx-loader.js +0 -58
  130. package/lib/esm/server/loaders/webpack/start.js +0 -27
  131. package/lib/esm/server/logger.js +0 -20
  132. package/lib/esm/server/master/api.js +0 -60
  133. package/lib/esm/server/master/index.js +0 -131
  134. package/lib/esm/server/master/master.js +0 -38
  135. package/lib/esm/server/master/pool.js +0 -176
  136. package/lib/esm/server/master/runner.js +0 -259
  137. package/lib/esm/server/master/server.js +0 -107
  138. package/lib/esm/server/messages.js +0 -232
  139. package/lib/esm/server/selenium/browser.js +0 -623
  140. package/lib/esm/server/selenium/index.js +0 -2
  141. package/lib/esm/server/selenium/selenoid.js +0 -149
  142. package/lib/esm/server/stories.js +0 -135
  143. package/lib/esm/server/storybook/entry.js +0 -27
  144. package/lib/esm/server/storybook/helpers.js +0 -97
  145. package/lib/esm/server/storybook/providers/browser.js +0 -60
  146. package/lib/esm/server/storybook/providers/nodejs.js +0 -216
  147. package/lib/esm/server/update.js +0 -61
  148. package/lib/esm/server/utils.js +0 -128
  149. package/lib/esm/server/worker/chai-image.js +0 -130
  150. package/lib/esm/server/worker/helpers.js +0 -60
  151. package/lib/esm/server/worker/index.js +0 -1
  152. package/lib/esm/server/worker/reporter.js +0 -86
  153. package/lib/esm/server/worker/worker.js +0 -238
  154. package/lib/esm/shared/index.js +0 -78
  155. package/lib/esm/shared/serializeRegExp.js +0 -24
  156. package/lib/esm/types.js +0 -43
  157. package/lib/types/cli.d.ts +0 -1
  158. package/lib/types/client/addon/Manager.d.ts +0 -37
  159. package/lib/types/client/addon/components/Addon.d.ts +0 -8
  160. package/lib/types/client/addon/components/Icons.d.ts +0 -7
  161. package/lib/types/client/addon/components/Panel.d.ts +0 -9
  162. package/lib/types/client/addon/components/TestSelect.d.ts +0 -8
  163. package/lib/types/client/addon/components/Tools.d.ts +0 -6
  164. package/lib/types/client/addon/decorator.d.ts +0 -1
  165. package/lib/types/client/addon/index.d.ts +0 -2
  166. package/lib/types/client/addon/preset.d.ts +0 -23
  167. package/lib/types/client/addon/preset.ie11.d.ts +0 -10
  168. package/lib/types/client/addon/readyForCapture.d.ts +0 -6
  169. package/lib/types/client/addon/register.d.ts +0 -3
  170. package/lib/types/client/addon/utils.d.ts +0 -3
  171. package/lib/types/client/addon/withCreevey.d.ts +0 -24
  172. package/lib/types/client/shared/components/ImagesView/BlendView.d.ts +0 -3
  173. package/lib/types/client/shared/components/ImagesView/ImagesView.d.ts +0 -24
  174. package/lib/types/client/shared/components/ImagesView/SideBySideView.d.ts +0 -3
  175. package/lib/types/client/shared/components/ImagesView/SlideView.d.ts +0 -3
  176. package/lib/types/client/shared/components/ImagesView/SwapView.d.ts +0 -3
  177. package/lib/types/client/shared/components/ImagesView/index.d.ts +0 -5
  178. package/lib/types/client/shared/components/PageFooter/PageFooter.d.ts +0 -8
  179. package/lib/types/client/shared/components/PageFooter/Paging.d.ts +0 -7
  180. package/lib/types/client/shared/components/PageHeader/ImagePreview.d.ts +0 -12
  181. package/lib/types/client/shared/components/PageHeader/PageHeader.d.ts +0 -16
  182. package/lib/types/client/shared/components/ResultsPage.d.ts +0 -18
  183. package/lib/types/client/shared/creeveyClientApi.d.ts +0 -9
  184. package/lib/types/client/shared/helpers.d.ts +0 -46
  185. package/lib/types/client/shared/viewMode.d.ts +0 -4
  186. package/lib/types/client/web/CreeveyApp.d.ts +0 -11
  187. package/lib/types/client/web/CreeveyContext.d.ts +0 -11
  188. package/lib/types/client/web/CreeveyLoader.d.ts +0 -2
  189. package/lib/types/client/web/CreeveyView/SideBar/Checkbox.d.ts +0 -19
  190. package/lib/types/client/web/CreeveyView/SideBar/Search.d.ts +0 -6
  191. package/lib/types/client/web/CreeveyView/SideBar/SideBar.d.ts +0 -14
  192. package/lib/types/client/web/CreeveyView/SideBar/SideBarHeader.d.ts +0 -12
  193. package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +0 -33
  194. package/lib/types/client/web/CreeveyView/SideBar/TestLink.d.ts +0 -7
  195. package/lib/types/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +0 -10
  196. package/lib/types/client/web/CreeveyView/SideBar/TestsStatus.d.ts +0 -9
  197. package/lib/types/client/web/CreeveyView/SideBar/Toggle.d.ts +0 -6
  198. package/lib/types/client/web/CreeveyView/SideBar/index.d.ts +0 -1
  199. package/lib/types/client/web/KeyboardEventsContext.d.ts +0 -13
  200. package/lib/types/client/web/index.d.ts +0 -4
  201. package/lib/types/creevey.d.ts +0 -1
  202. package/lib/types/index.d.ts +0 -2
  203. package/lib/types/server/config.d.ts +0 -4
  204. package/lib/types/server/docker.d.ts +0 -7
  205. package/lib/types/server/extract.d.ts +0 -2
  206. package/lib/types/server/index.d.ts +0 -2
  207. package/lib/types/server/loaders/babel/creevey-plugin.d.ts +0 -1
  208. package/lib/types/server/loaders/babel/helpers.d.ts +0 -19
  209. package/lib/types/server/loaders/babel/register.d.ts +0 -5
  210. package/lib/types/server/loaders/hooks/mdx.d.ts +0 -1
  211. package/lib/types/server/loaders/hooks/svelte.d.ts +0 -1
  212. package/lib/types/server/loaders/webpack/compile.d.ts +0 -2
  213. package/lib/types/server/loaders/webpack/creevey-loader.d.ts +0 -4
  214. package/lib/types/server/loaders/webpack/dummy-hmr.d.ts +0 -10
  215. package/lib/types/server/loaders/webpack/mdx-loader.d.ts +0 -6
  216. package/lib/types/server/loaders/webpack/start.d.ts +0 -1
  217. package/lib/types/server/logger.d.ts +0 -10
  218. package/lib/types/server/master/api.d.ts +0 -7
  219. package/lib/types/server/master/index.d.ts +0 -3
  220. package/lib/types/server/master/master.d.ts +0 -7
  221. package/lib/types/server/master/pool.d.ts +0 -31
  222. package/lib/types/server/master/runner.d.ts +0 -26
  223. package/lib/types/server/master/server.d.ts +0 -2
  224. package/lib/types/server/messages.d.ts +0 -29
  225. package/lib/types/server/selenium/browser.d.ts +0 -17
  226. package/lib/types/server/selenium/index.d.ts +0 -2
  227. package/lib/types/server/selenium/selenoid.d.ts +0 -3
  228. package/lib/types/server/stories.d.ts +0 -8
  229. package/lib/types/server/storybook/entry.d.ts +0 -17
  230. package/lib/types/server/storybook/helpers.d.ts +0 -24
  231. package/lib/types/server/storybook/providers/browser.d.ts +0 -4
  232. package/lib/types/server/storybook/providers/nodejs.d.ts +0 -9
  233. package/lib/types/server/update.d.ts +0 -2
  234. package/lib/types/server/utils.d.ts +0 -23
  235. package/lib/types/server/worker/chai-image.d.ts +0 -6
  236. package/lib/types/server/worker/helpers.d.ts +0 -8
  237. package/lib/types/server/worker/index.d.ts +0 -1
  238. package/lib/types/server/worker/reporter.d.ts +0 -8
  239. package/lib/types/server/worker/worker.d.ts +0 -4
  240. package/lib/types/shared/index.d.ts +0 -7
  241. package/lib/types/shared/serializeRegExp.d.ts +0 -9
  242. package/lib/types/types.d.ts +0 -486
@@ -1,131 +0,0 @@
1
- import path from 'path';
2
- import { writeFileSync, copyFile, readdir, mkdir, readdirSync, existsSync } from 'fs';
3
- import { promisify } from 'util';
4
- import master from './master';
5
- import creeveyApi from './api';
6
- import { isDefined } from '../../types';
7
- import { shutdown, shutdownWorkers, testsToImages } from '../utils';
8
- import { subscribeOn } from '../messages';
9
- import { logger } from '../logger';
10
- const copyFileAsync = promisify(copyFile);
11
- const readdirAsync = promisify(readdir);
12
- const mkdirAsync = promisify(mkdir);
13
-
14
- async function copyStatics(reportDir) {
15
- const clientDir = path.join(__dirname, '../../client/web');
16
- const files = (await readdirAsync(clientDir, {
17
- withFileTypes: true
18
- })).filter(dirent => dirent.isFile() && !dirent.name.endsWith('.d.ts') && !dirent.name.endsWith('.tsx')).map(dirent => dirent.name);
19
- await mkdirAsync(reportDir, {
20
- recursive: true
21
- });
22
-
23
- for (const file of files) {
24
- await copyFileAsync(path.join(clientDir, file), path.join(reportDir, file));
25
- }
26
- }
27
-
28
- function reportDataModule(data) {
29
- return `
30
- (function (root, factory) {
31
- if (typeof module === 'object' && module.exports) {
32
- module.exports = factory();
33
- } else {
34
- root.__CREEVEY_DATA__ = factory();
35
- }
36
- }(this, function () { return ${JSON.stringify(data)} }));
37
- `;
38
- }
39
-
40
- function readDirRecursive(dirPath) {
41
- return [].concat(...readdirSync(dirPath, {
42
- withFileTypes: true
43
- }).map(dirent => dirent.isDirectory() ? readDirRecursive(`${dirPath}/${dirent.name}`) : [`${dirPath}/${dirent.name}`]));
44
- }
45
-
46
- function outputUnnecessaryImages(imagesDir, images) {
47
- if (!existsSync(imagesDir)) return;
48
- const unnecessaryImages = readDirRecursive(imagesDir).map(imagePath => path.posix.relative(imagesDir, imagePath)).filter(imagePath => !images.has(imagePath));
49
-
50
- if (unnecessaryImages.length > 0) {
51
- logger.warn('We found unnecessary screenshot images, those can be safely removed:\n', unnecessaryImages.join('\n'));
52
- }
53
- }
54
-
55
- export default async function (config, options, resolveApi) {
56
- let runner = null;
57
-
58
- if (config.hooks.before) {
59
- await config.hooks.before();
60
- }
61
-
62
- subscribeOn('shutdown', () => {
63
- var _config$hooks$after, _config$hooks;
64
-
65
- return (_config$hooks$after = (_config$hooks = config.hooks).after) === null || _config$hooks$after === void 0 ? void 0 : _config$hooks$after.call(_config$hooks);
66
- });
67
- process.removeListener('SIGINT', shutdown);
68
- process.on('SIGINT', () => {
69
- var _runner, _runner2;
70
-
71
- (_runner = runner) === null || _runner === void 0 ? void 0 : _runner.removeAllListeners('stop');
72
-
73
- if ((_runner2 = runner) !== null && _runner2 !== void 0 && _runner2.isRunning) {
74
- var _runner4;
75
-
76
- // TODO Better handle stop
77
- void Promise.race([new Promise(resolve => setTimeout(resolve, 10000)), new Promise(resolve => {
78
- var _runner3;
79
-
80
- return (_runner3 = runner) === null || _runner3 === void 0 ? void 0 : _runner3.once('stop', resolve);
81
- })]).then(() => shutdownWorkers());
82
- (_runner4 = runner) === null || _runner4 === void 0 ? void 0 : _runner4.stop();
83
- } else {
84
- void shutdownWorkers();
85
- }
86
- });
87
- runner = await master(config, {
88
- watch: options.ui,
89
- debug: options.debug,
90
- port: options.port
91
- });
92
-
93
- if (options.saveReport) {
94
- await copyStatics(config.reportDir);
95
- runner.on('stop', () => {
96
- var _runner5;
97
-
98
- return writeFileSync(path.join(config.reportDir, 'data.js'), reportDataModule((_runner5 = runner) === null || _runner5 === void 0 ? void 0 : _runner5.status.tests));
99
- });
100
- }
101
-
102
- if (options.ui) {
103
- resolveApi(creeveyApi(runner));
104
- logger.info(`Started on http://localhost:${options.port}`);
105
- } else {
106
- if (Object.values(runner.status.tests).filter(test => test && !test.skip).length == 0) {
107
- logger.warn("Don't have any tests to run"); // eslint-disable-next-line no-process-exit
108
-
109
- void shutdownWorkers().then(() => process.exit());
110
- return;
111
- }
112
-
113
- runner.once('stop', () => {
114
- var _runner6;
115
-
116
- const tests = Object.values(((_runner6 = runner) === null || _runner6 === void 0 ? void 0 : _runner6.status.tests) ?? {});
117
- const isSuccess = tests.filter(isDefined).filter(({
118
- skip
119
- }) => !skip).every(({
120
- status
121
- }) => status == 'success'); // TODO output summary
122
-
123
- process.exitCode = isSuccess ? 0 : -1;
124
- if (!config.failFast) outputUnnecessaryImages(config.screenDir, testsToImages(tests)); // eslint-disable-next-line no-process-exit
125
-
126
- void shutdownWorkers().then(() => process.exit());
127
- }); // TODO grep
128
-
129
- runner.start(Object.keys(runner.status.tests));
130
- }
131
- }
@@ -1,38 +0,0 @@
1
- import path from 'path';
2
- import { isDefined } from '../../types';
3
- import { loadTestsFromStories, saveTestsJson } from '../stories';
4
- import Runner from './runner';
5
- import { startWebpackCompiler } from '../loaders/webpack/start';
6
-
7
- function mergeTests(testsWithReports, testsFromStories) {
8
- Object.values(testsFromStories).filter(isDefined).forEach(test => {
9
- const testWithReport = testsWithReports[test.id];
10
- if (!testWithReport) return;
11
- test.retries = testWithReport.retries;
12
- if (testWithReport.status == 'success' || testWithReport.status == 'failed') test.status = testWithReport.status;
13
- test.results = testWithReport.results;
14
- test.approved = testWithReport.approved;
15
- });
16
- return testsFromStories;
17
- }
18
-
19
- export default async function master(config, options) {
20
- if (config.useWebpackToExtractTests) await startWebpackCompiler();
21
- const runner = new Runner(config);
22
- const reportDataPath = path.join(config.reportDir, 'data.js');
23
- let testsFromReport = {};
24
-
25
- try {
26
- testsFromReport = await import(reportDataPath);
27
- } catch (error) {// Ignore error
28
- }
29
-
30
- await runner.init();
31
- const tests = await loadTestsFromStories(Object.keys(config.browsers), listener => config.storiesProvider(config, options, listener), testsDiff => {
32
- runner.updateTests(testsDiff);
33
- saveTestsJson(runner.tests, config.reportDir);
34
- });
35
- runner.tests = mergeTests(testsFromReport, tests);
36
- saveTestsJson(runner.tests, config.reportDir);
37
- return runner;
38
- }
@@ -1,176 +0,0 @@
1
- import cluster, { Worker as ClusterWorker } from 'cluster';
2
- import { EventEmitter } from 'events';
3
- import { isWorkerMessage } from '../../types';
4
- import { sendTestMessage, sendShutdownMessage, subscribeOnWorker } from '../messages';
5
- import { isShuttingDown } from '../utils';
6
- const FORK_RETRIES = 5;
7
- export default class Pool extends EventEmitter {
8
- workers = [];
9
- queue = [];
10
- forcedStop = false;
11
-
12
- get isRunning() {
13
- return this.workers.length !== this.freeWorkers.length;
14
- }
15
-
16
- constructor(config, browser) {
17
- super();
18
- this.browser = browser;
19
- this.failFast = config.failFast;
20
- this.maxRetries = config.maxRetries;
21
- this.config = config.browsers[browser];
22
- }
23
-
24
- async init() {
25
- const poolSize = this.config.limit || 1;
26
- this.workers = (await Promise.all(Array.from({
27
- length: poolSize
28
- }).map(() => this.forkWorker()))).filter(workerOrError => workerOrError instanceof ClusterWorker);
29
- if (this.workers.length != poolSize) throw new Error(`Can't instantiate workers for ${this.browser} due many errors`);
30
- this.workers.forEach(worker => this.exitHandler(worker));
31
- }
32
-
33
- start(tests) {
34
- if (this.isRunning) return false;
35
- this.queue = tests.map(({
36
- id,
37
- path
38
- }) => ({
39
- id,
40
- path,
41
- retries: 0
42
- }));
43
- this.process();
44
- return true;
45
- }
46
-
47
- stop() {
48
- if (!this.isRunning) {
49
- this.emit('stop');
50
- return;
51
- }
52
-
53
- this.forcedStop = true;
54
- this.queue = [];
55
- }
56
-
57
- process() {
58
- const worker = this.getFreeWorker();
59
- const [test] = this.queue;
60
-
61
- if (this.queue.length == 0 && this.workers.length === this.freeWorkers.length) {
62
- this.forcedStop = false;
63
- this.emit('stop');
64
- return;
65
- }
66
-
67
- if (!worker || !test) return;
68
- worker.isRunning = true;
69
- const {
70
- id
71
- } = test;
72
- this.queue.shift();
73
- this.sendStatus({
74
- id,
75
- status: 'running'
76
- });
77
- this.subscribe(worker, test);
78
- sendTestMessage(worker, {
79
- type: 'start',
80
- payload: test
81
- });
82
- this.process();
83
- }
84
-
85
- sendStatus(message) {
86
- this.emit('test', message);
87
- }
88
-
89
- getFreeWorker() {
90
- return this.freeWorkers[Math.floor(Math.random() * this.freeWorkers.length)];
91
- }
92
-
93
- get aliveWorkers() {
94
- return this.workers.filter(worker => !worker.exitedAfterDisconnect);
95
- }
96
-
97
- get freeWorkers() {
98
- return this.aliveWorkers.filter(worker => !worker.isRunning);
99
- }
100
-
101
- async forkWorker(retry = 0) {
102
- cluster.setupMaster({
103
- args: ['--browser', this.browser, ...process.argv.slice(2)]
104
- });
105
- const worker = cluster.fork();
106
- const message = await new Promise(resolve => {
107
- const readyHandler = message => {
108
- if (!isWorkerMessage(message)) return;
109
- worker.off('message', readyHandler);
110
- resolve(message);
111
- };
112
-
113
- worker.on('message', readyHandler);
114
- });
115
- if (message.type != 'error') return worker;
116
- this.gracefullyKill(worker);
117
- if (retry == FORK_RETRIES) return message.payload;
118
- return this.forkWorker(retry + 1);
119
- }
120
-
121
- exitHandler(worker) {
122
- // eslint-disable-next-line @typescript-eslint/no-misused-promises
123
- worker.once('exit', async () => {
124
- if (isShuttingDown.current) return;
125
- const workerOrError = await this.forkWorker();
126
- if (!(workerOrError instanceof ClusterWorker)) throw new Error(`Can't instantiate worker for ${this.browser} due many errors`);
127
- this.exitHandler(workerOrError);
128
- this.workers[this.workers.indexOf(worker)] = workerOrError;
129
- this.process();
130
- });
131
- }
132
-
133
- gracefullyKill(worker) {
134
- const timeout = setTimeout(() => worker.kill(), 10000);
135
- worker.on('exit', () => clearTimeout(timeout));
136
- sendShutdownMessage(worker);
137
- }
138
-
139
- shouldRetry(test) {
140
- return test.retries < this.maxRetries && !this.forcedStop;
141
- }
142
-
143
- handleTestResult(worker, test, result) {
144
- const shouldRetry = result.status == 'failed' && this.shouldRetry(test);
145
-
146
- if (shouldRetry) {
147
- test.retries += 1;
148
- this.queue[this.failFast ? 'unshift' : 'push'](test);
149
- }
150
-
151
- this.sendStatus({
152
- id: test.id,
153
- status: shouldRetry ? 'retrying' : result.status,
154
- result
155
- });
156
- worker.isRunning = false;
157
- setImmediate(() => this.process());
158
- }
159
-
160
- subscribe(worker, test) {
161
- const subscriptions = [subscribeOnWorker(worker, 'worker', message => {
162
- if (message.type != 'error') return;
163
- subscriptions.forEach(unsubscribe => unsubscribe());
164
- this.gracefullyKill(worker);
165
- this.handleTestResult(worker, test, {
166
- status: 'failed',
167
- ...message.payload
168
- });
169
- }), subscribeOnWorker(worker, 'test', message => {
170
- if (message.type != 'end') return;
171
- subscriptions.forEach(unsubscribe => unsubscribe());
172
- this.handleTestResult(worker, test, message.payload);
173
- })];
174
- }
175
-
176
- }
@@ -1,259 +0,0 @@
1
- import path from 'path';
2
- import { copyFile, mkdir } from 'fs';
3
- import { promisify } from 'util';
4
- import { EventEmitter } from 'events';
5
- import { isDefined } from '../../types';
6
- import Pool from './pool';
7
- const copyFileAsync = promisify(copyFile);
8
- const mkdirAsync = promisify(mkdir);
9
- export default class Runner extends EventEmitter {
10
- pools = {};
11
- tests = {};
12
-
13
- get isRunning() {
14
- return Object.values(this.pools).some(pool => pool.isRunning);
15
- }
16
-
17
- constructor(config) {
18
- super();
19
- this.failFast = config.failFast;
20
- this.screenDir = config.screenDir;
21
- this.reportDir = config.reportDir;
22
- this.browsers = Object.keys(config.browsers);
23
- this.browsers.map(browser => this.pools[browser] = new Pool(config, browser)).map(pool => pool.on('test', this.handlePoolMessage));
24
- }
25
-
26
- handlePoolMessage = message => {
27
- const {
28
- id,
29
- status,
30
- result
31
- } = message;
32
- const test = this.tests[id];
33
- if (!test) return;
34
- const {
35
- browser,
36
- testName,
37
- storyPath,
38
- storyId
39
- } = test; // TODO Handle 'retrying' status
40
-
41
- test.status = status == 'retrying' ? 'failed' : status;
42
-
43
- if (!result) {
44
- this.sendUpdate({
45
- tests: {
46
- [id]: {
47
- id,
48
- browser,
49
- testName,
50
- storyPath,
51
- status: test.status,
52
- storyId
53
- }
54
- }
55
- });
56
- return;
57
- }
58
-
59
- if (!test.results) {
60
- test.results = [];
61
- }
62
-
63
- test.results.push(result);
64
- this.sendUpdate({
65
- tests: {
66
- [id]: {
67
- id,
68
- browser,
69
- testName,
70
- storyPath,
71
- status: test.status,
72
- results: [result],
73
- storyId
74
- }
75
- }
76
- });
77
- if (this.failFast && status == 'failed') this.stop();
78
- };
79
- handlePoolStop = () => {
80
- if (!this.isRunning) {
81
- this.sendUpdate({
82
- isRunning: false
83
- });
84
- this.emit('stop');
85
- }
86
- };
87
-
88
- async init() {
89
- await Promise.all(Object.values(this.pools).map(pool => pool.init()));
90
- }
91
-
92
- updateTests(testsDiff) {
93
- const tests = {};
94
- const removedTests = [];
95
- Object.entries(testsDiff).forEach(([id, newTest]) => {
96
- const oldTest = this.tests[id];
97
-
98
- if (newTest) {
99
- if (oldTest) {
100
- this.tests[id] = { ...newTest,
101
- retries: oldTest.retries,
102
- results: oldTest.results,
103
- approved: oldTest.approved
104
- };
105
- } else this.tests[id] = newTest; // eslint-disable-next-line @typescript-eslint/no-unused-vars
106
-
107
-
108
- const {
109
- story,
110
- fn,
111
- ...restTest
112
- } = newTest;
113
- tests[id] = { ...restTest,
114
- status: 'unknown'
115
- };
116
- } else if (oldTest) {
117
- const {
118
- id,
119
- browser,
120
- testName,
121
- storyPath,
122
- storyId
123
- } = oldTest;
124
- removedTests.push({
125
- id,
126
- browser,
127
- testName,
128
- storyPath,
129
- storyId
130
- });
131
- delete this.tests[id];
132
- }
133
- });
134
- this.sendUpdate({
135
- tests,
136
- removedTests
137
- });
138
- }
139
-
140
- start(ids) {
141
- if (this.isRunning) return;
142
- const testsToStart = ids.map(id => this.tests[id]).filter(isDefined).filter(test => !test.skip);
143
- if (testsToStart.length == 0) return;
144
- this.sendUpdate({
145
- isRunning: true,
146
- tests: testsToStart.reduce((update, {
147
- id,
148
- storyId,
149
- browser,
150
- testName,
151
- storyPath
152
- }) => ({ ...update,
153
- [id]: {
154
- id,
155
- browser,
156
- testName,
157
- storyPath,
158
- status: 'pending',
159
- storyId
160
- }
161
- }), {})
162
- });
163
- const testsByBrowser = testsToStart.reduce((tests, test) => {
164
- const {
165
- id,
166
- browser,
167
- testName,
168
- storyPath
169
- } = test;
170
- const restPath = [...storyPath, testName].filter(isDefined);
171
- test.status = 'pending';
172
- return { ...tests,
173
- [browser]: [...(tests[browser] || []), {
174
- id,
175
- path: restPath
176
- }]
177
- };
178
- }, {});
179
- this.browsers.forEach(browser => {
180
- const pool = this.pools[browser];
181
- const tests = testsByBrowser[browser];
182
-
183
- if (tests && tests.length > 0 && pool.start(tests)) {
184
- pool.once('stop', this.handlePoolStop);
185
- }
186
- });
187
- }
188
-
189
- stop() {
190
- if (!this.isRunning) return;
191
- this.browsers.forEach(browser => this.pools[browser].stop());
192
- }
193
-
194
- get status() {
195
- const tests = {};
196
- Object.values(this.tests).filter(isDefined) // eslint-disable-next-line @typescript-eslint/no-unused-vars
197
- .forEach(({
198
- story,
199
- fn,
200
- ...test
201
- }) => tests[test.id] = test);
202
- return {
203
- isRunning: this.isRunning,
204
- tests,
205
- browsers: this.browsers
206
- };
207
- }
208
-
209
- async approve({
210
- id,
211
- retry,
212
- image
213
- }) {
214
- const test = this.tests[id];
215
- if (!test || !test.results) return;
216
- const result = test.results[retry];
217
- if (!result || !result.images) return;
218
- const images = result.images[image];
219
- if (!images) return;
220
-
221
- if (!test.approved) {
222
- test.approved = {};
223
- }
224
-
225
- const {
226
- browser,
227
- testName,
228
- storyPath
229
- } = test;
230
- const restPath = [...storyPath, testName].filter(isDefined);
231
- const testPath = path.join(...restPath, image == browser ? '' : browser);
232
- const srcImagePath = path.join(this.reportDir, testPath, images.actual);
233
- const dstImagePath = path.join(this.screenDir, testPath, `${image}.png`);
234
- await mkdirAsync(path.join(this.screenDir, testPath), {
235
- recursive: true
236
- });
237
- await copyFileAsync(srcImagePath, dstImagePath);
238
- test.approved[image] = retry;
239
- this.sendUpdate({
240
- tests: {
241
- [id]: {
242
- id,
243
- browser,
244
- testName,
245
- storyPath,
246
- approved: {
247
- [image]: retry
248
- },
249
- storyId: test.storyId
250
- }
251
- }
252
- });
253
- }
254
-
255
- sendUpdate(data) {
256
- this.emit('update', data);
257
- }
258
-
259
- }
@@ -1,107 +0,0 @@
1
- import path from 'path';
2
- import http from 'http';
3
- import cluster from 'cluster';
4
- import Koa from 'koa';
5
- import cors from '@koa/cors';
6
- import serve from 'koa-static';
7
- import mount from 'koa-mount';
8
- import body from 'koa-bodyparser';
9
- import WebSocket from 'ws';
10
- import { emitStoriesMessage, sendStoriesMessage, subscribeOn, subscribeOnWorker } from '../messages';
11
- import { isDefined, noop } from '../../types';
12
- import { logger } from '../logger';
13
- import { deserializeStory } from '../../shared';
14
- export default function server(reportDir, port, ui) {
15
- let resolveApi = noop;
16
- let setStoriesCounter = 0;
17
- const creeveyApi = new Promise(resolve => resolveApi = resolve);
18
- const app = new Koa();
19
- const server = http.createServer(app.callback());
20
- const wss = new WebSocket.Server({
21
- server
22
- });
23
- app.use(cors());
24
- app.use(body());
25
- app.use(async (ctx, next) => {
26
- if (ctx.method == 'GET' && ctx.path == '/ping') {
27
- ctx.body = 'pong';
28
- return;
29
- }
30
-
31
- await next();
32
- });
33
-
34
- if (ui) {
35
- app.use(async (_, next) => {
36
- await creeveyApi;
37
- await next();
38
- });
39
- }
40
-
41
- app.use(async (ctx, next) => {
42
- if (ctx.method == 'POST' && ctx.path == '/stories') {
43
- const {
44
- setStoriesCounter: counter,
45
- stories
46
- } = ctx.request.body;
47
- if (setStoriesCounter >= counter) return;
48
- const deserializedStories = stories.map(([file, stories]) => [file, stories.map(deserializeStory)]);
49
- setStoriesCounter = counter;
50
- emitStoriesMessage({
51
- type: 'update',
52
- payload: deserializedStories
53
- });
54
- Object.values(cluster.workers ?? {}).filter(isDefined).filter(worker => worker.isConnected()).forEach(worker => sendStoriesMessage(worker, {
55
- type: 'update',
56
- payload: deserializedStories
57
- }));
58
- return;
59
- }
60
-
61
- await next();
62
- });
63
- app.use(async (ctx, next) => {
64
- if (ctx.method == 'POST' && ctx.path == '/capture') {
65
- const {
66
- workerId,
67
- options
68
- } = ctx.request.body;
69
- const worker = Object.values(cluster.workers ?? {}).filter(isDefined).find(worker => worker.process.pid == workerId); // NOTE: Hypothetical case when someone send to us capture req and we don't have a worker with browser session for it
70
-
71
- if (!worker) return;
72
- await new Promise(resolve => {
73
- const unsubscribe = subscribeOnWorker(worker, 'stories', message => {
74
- if (message.type != 'capture') return;
75
- unsubscribe();
76
- resolve();
77
- });
78
- sendStoriesMessage(worker, {
79
- type: 'capture',
80
- payload: options
81
- });
82
- }); // TODO Pass screenshot result to show it in inspector
83
-
84
- ctx.body = 'Ok';
85
- return;
86
- }
87
-
88
- await next();
89
- });
90
- app.use(serve(path.join(__dirname, '../../client/web')));
91
- app.use(mount('/report', serve(reportDir)));
92
- wss.on('error', error => logger.error(error));
93
- server.listen(port);
94
- subscribeOn('shutdown', () => {
95
- server.close();
96
- wss.close();
97
- });
98
- void creeveyApi.then(api => {
99
- api.subscribe(wss);
100
- wss.on('connection', ws => {
101
- ws.on('message', (message, isBinary) => {
102
- api.handleMessage(ws, isBinary ? message : message.toString());
103
- });
104
- });
105
- });
106
- return resolveApi;
107
- }