creevey 0.9.0-beta.2 → 0.9.0-beta.20

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 (226) hide show
  1. package/.yarn/install-state.gz +0 -0
  2. package/.yarnrc.yml +1 -0
  3. package/CHANGELOG.md +51 -0
  4. package/README.md +9 -1
  5. package/addon/README.md +3 -0
  6. package/addon/package.json +5 -0
  7. package/docs/config.md +29 -26
  8. package/jest.config.js +6 -0
  9. package/lib/cjs/cli.js +1 -0
  10. package/lib/cjs/client/addon/Manager.js +170 -390
  11. package/lib/cjs/client/addon/components/Addon.js +17 -45
  12. package/lib/cjs/client/addon/components/Icons.js +12 -14
  13. package/lib/cjs/client/addon/components/Panel.js +21 -30
  14. package/lib/cjs/client/addon/components/TestSelect.js +20 -31
  15. package/lib/cjs/client/addon/components/Tools.js +35 -65
  16. package/lib/cjs/client/addon/decorator.js +1 -4
  17. package/lib/cjs/client/addon/index.js +27 -0
  18. package/lib/cjs/client/addon/preset.js +3 -76
  19. package/lib/cjs/client/addon/preview.js +11 -0
  20. package/lib/cjs/client/addon/readyForCapture.js +1 -4
  21. package/lib/cjs/client/addon/register.js +43 -82
  22. package/lib/cjs/client/addon/utils.js +4 -8
  23. package/lib/cjs/client/addon/withCreevey.js +145 -404
  24. package/lib/cjs/client/shared/components/ImagesView/BlendView.js +25 -35
  25. package/lib/cjs/client/shared/components/ImagesView/ImagesView.js +29 -41
  26. package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +46 -83
  27. package/lib/cjs/client/shared/components/ImagesView/SlideView.js +39 -67
  28. package/lib/cjs/client/shared/components/ImagesView/SwapView.js +26 -57
  29. package/lib/cjs/client/shared/components/ImagesView/index.js +9 -14
  30. package/lib/cjs/client/shared/components/PageFooter/PageFooter.js +13 -16
  31. package/lib/cjs/client/shared/components/PageFooter/Paging.js +16 -37
  32. package/lib/cjs/client/shared/components/PageHeader/ImagePreview.js +42 -34
  33. package/lib/cjs/client/shared/components/PageHeader/PageHeader.js +40 -84
  34. package/lib/cjs/client/shared/components/ResultsPage.js +42 -99
  35. package/lib/cjs/client/shared/creeveyClientApi.js +56 -93
  36. package/lib/cjs/client/shared/helpers.js +149 -274
  37. package/lib/cjs/client/shared/viewMode.js +5 -9
  38. package/lib/cjs/client/web/192.js +1 -0
  39. package/lib/cjs/client/web/632.js +43 -0
  40. package/lib/cjs/client/web/794.js +1 -0
  41. package/lib/cjs/client/web/main.js +79 -38
  42. package/lib/cjs/client/web/main.js.LICENSE.txt +34 -0
  43. package/lib/cjs/creevey.js +15 -30
  44. package/lib/cjs/index.js +0 -15
  45. package/lib/cjs/server/config.js +16 -36
  46. package/lib/cjs/server/docker.js +8 -34
  47. package/lib/cjs/server/index.js +9 -34
  48. package/lib/cjs/server/logger.js +7 -20
  49. package/lib/cjs/server/master/api.js +1 -14
  50. package/lib/cjs/server/master/index.js +25 -49
  51. package/lib/cjs/server/master/master.js +6 -21
  52. package/lib/cjs/server/master/pool.js +10 -53
  53. package/lib/cjs/server/master/runner.js +65 -105
  54. package/lib/cjs/server/master/server.js +10 -29
  55. package/lib/cjs/server/messages.js +14 -62
  56. package/lib/cjs/server/selenium/browser.js +149 -185
  57. package/lib/cjs/server/selenium/index.js +0 -4
  58. package/lib/cjs/server/selenium/selenoid.js +18 -44
  59. package/lib/cjs/server/stories.js +35 -57
  60. package/lib/cjs/server/storybook/providers/browser.js +15 -29
  61. package/lib/cjs/server/storybook/providers/hybrid.js +16 -37
  62. package/lib/cjs/server/telemetry.js +167 -0
  63. package/lib/cjs/server/testsFiles/parser.js +3 -19
  64. package/lib/cjs/server/testsFiles/register.js +8 -14
  65. package/lib/cjs/server/update.js +4 -25
  66. package/lib/cjs/server/utils.js +35 -76
  67. package/lib/cjs/server/worker/chai-image.js +1 -27
  68. package/lib/cjs/server/worker/helpers.js +2 -12
  69. package/lib/cjs/server/worker/index.js +1 -3
  70. package/lib/cjs/server/worker/reporter.js +16 -43
  71. package/lib/cjs/server/worker/worker.js +32 -72
  72. package/lib/cjs/shared/index.js +87 -0
  73. package/lib/cjs/shared/serializeRegExp.js +34 -0
  74. package/lib/cjs/types.js +11 -20
  75. package/lib/esm/cli.js +1 -1
  76. package/lib/esm/client/addon/Manager.js +170 -381
  77. package/lib/esm/client/addon/components/Addon.js +15 -34
  78. package/lib/esm/client/addon/components/Icons.js +10 -6
  79. package/lib/esm/client/addon/components/Panel.js +20 -18
  80. package/lib/esm/client/addon/components/TestSelect.js +19 -23
  81. package/lib/esm/client/addon/components/Tools.js +33 -49
  82. package/lib/esm/client/addon/decorator.js +1 -1
  83. package/lib/esm/client/addon/index.js +2 -0
  84. package/lib/esm/client/addon/preset.js +2 -56
  85. package/lib/esm/client/addon/preview.js +5 -0
  86. package/lib/esm/client/addon/readyForCapture.js +1 -3
  87. package/lib/esm/client/addon/register.js +41 -67
  88. package/lib/esm/client/addon/utils.js +3 -7
  89. package/lib/esm/client/addon/withCreevey.js +142 -388
  90. package/lib/esm/client/shared/components/ImagesView/BlendView.js +22 -18
  91. package/lib/esm/client/shared/components/ImagesView/ImagesView.js +27 -25
  92. package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +43 -63
  93. package/lib/esm/client/shared/components/ImagesView/SlideView.js +36 -47
  94. package/lib/esm/client/shared/components/ImagesView/SwapView.js +23 -40
  95. package/lib/esm/client/shared/components/PageFooter/PageFooter.js +12 -8
  96. package/lib/esm/client/shared/components/PageFooter/Paging.js +15 -29
  97. package/lib/esm/client/shared/components/PageHeader/ImagePreview.js +40 -25
  98. package/lib/esm/client/shared/components/PageHeader/PageHeader.js +38 -66
  99. package/lib/esm/client/shared/components/ResultsPage.js +39 -75
  100. package/lib/esm/client/shared/creeveyClientApi.js +56 -90
  101. package/lib/esm/client/shared/helpers.js +133 -230
  102. package/lib/esm/client/shared/viewMode.js +4 -4
  103. package/lib/esm/client/web/192.js +1 -0
  104. package/lib/esm/client/web/632.js +43 -0
  105. package/lib/esm/client/web/794.js +1 -0
  106. package/lib/esm/client/web/index.html +19 -0
  107. package/lib/esm/client/web/main.js +79 -0
  108. package/lib/esm/client/web/main.js.LICENSE.txt +34 -0
  109. package/lib/esm/creevey.js +13 -16
  110. package/lib/esm/index.js +1 -4
  111. package/lib/esm/server/config.js +9 -16
  112. package/lib/esm/server/docker.js +6 -14
  113. package/lib/esm/server/index.js +8 -22
  114. package/lib/esm/server/logger.js +0 -1
  115. package/lib/esm/server/master/api.js +0 -9
  116. package/lib/esm/server/master/index.js +25 -35
  117. package/lib/esm/server/master/master.js +2 -7
  118. package/lib/esm/server/master/pool.js +8 -41
  119. package/lib/esm/server/master/runner.js +64 -90
  120. package/lib/esm/server/master/server.js +9 -11
  121. package/lib/esm/server/messages.js +8 -42
  122. package/lib/esm/server/selenium/browser.js +147 -163
  123. package/lib/esm/server/selenium/selenoid.js +16 -27
  124. package/lib/esm/server/stories.js +34 -46
  125. package/lib/esm/server/storybook/providers/browser.js +12 -17
  126. package/lib/esm/server/storybook/providers/hybrid.js +11 -22
  127. package/lib/esm/server/telemetry.js +160 -0
  128. package/lib/esm/server/testsFiles/parser.js +0 -6
  129. package/lib/esm/server/testsFiles/register.js +6 -7
  130. package/lib/esm/server/update.js +1 -13
  131. package/lib/esm/server/utils.js +20 -41
  132. package/lib/esm/server/worker/chai-image.js +0 -21
  133. package/lib/esm/server/worker/helpers.js +2 -9
  134. package/lib/esm/server/worker/reporter.js +15 -29
  135. package/lib/esm/server/worker/worker.js +31 -48
  136. package/lib/esm/shared/index.js +77 -0
  137. package/lib/esm/shared/serializeRegExp.js +24 -0
  138. package/lib/esm/types.js +5 -1
  139. package/lib/types/client/addon/Manager.d.ts +3 -3
  140. package/lib/types/client/addon/components/Addon.d.ts +1 -0
  141. package/lib/types/client/addon/components/Icons.d.ts +1 -0
  142. package/lib/types/client/addon/components/Panel.d.ts +1 -0
  143. package/lib/types/client/addon/components/Tools.d.ts +1 -0
  144. package/lib/types/client/addon/decorator.d.ts +1 -1
  145. package/lib/types/client/addon/index.d.ts +2 -0
  146. package/lib/types/client/addon/preset.d.ts +2 -24
  147. package/lib/types/client/addon/preview.d.ts +4 -0
  148. package/lib/types/client/addon/utils.d.ts +1 -0
  149. package/lib/types/client/addon/withCreevey.d.ts +4 -3
  150. package/lib/types/client/shared/components/ImagesView/BlendView.d.ts +3 -1
  151. package/lib/types/client/shared/components/ImagesView/SideBySideView.d.ts +3 -1
  152. package/lib/types/client/shared/components/ImagesView/SlideView.d.ts +3 -1
  153. package/lib/types/client/shared/components/ImagesView/SwapView.d.ts +3 -1
  154. package/lib/types/client/shared/components/PageHeader/ImagePreview.d.ts +3 -1
  155. package/lib/types/client/shared/components/ResultsPage.d.ts +3 -1
  156. package/lib/types/client/web/CreeveyLoader.d.ts +1 -1
  157. package/lib/types/client/web/CreeveyView/SideBar/Checkbox.d.ts +6 -3
  158. package/lib/types/client/web/CreeveyView/SideBar/Search.d.ts +1 -0
  159. package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +19 -14
  160. package/lib/types/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +3 -1
  161. package/lib/types/client/web/CreeveyView/SideBar/TestsStatus.d.ts +3 -1
  162. package/lib/types/client/web/CreeveyView/SideBar/Toggle.d.ts +1 -0
  163. package/lib/types/client/web/KeyboardEventsContext.d.ts +4 -2
  164. package/lib/types/index.d.ts +4 -1
  165. package/lib/types/server/logger.d.ts +6 -2
  166. package/lib/types/server/messages.d.ts +14 -12
  167. package/lib/types/server/selenium/browser.d.ts +5 -3
  168. package/lib/types/server/storybook/providers/browser.d.ts +2 -4
  169. package/lib/types/server/storybook/providers/hybrid.d.ts +2 -4
  170. package/lib/types/server/telemetry.d.ts +2 -0
  171. package/lib/types/server/utils.d.ts +5 -1
  172. package/lib/types/shared/index.d.ts +7 -0
  173. package/lib/types/shared/serializeRegExp.d.ts +9 -0
  174. package/lib/types/types.d.ts +29 -36
  175. package/package.json +132 -133
  176. package/types/global.d.ts +5 -0
  177. package/lib/cjs/client/web/1.js +0 -13
  178. package/lib/cjs/client/web/2.js +0 -1
  179. package/lib/cjs/server/extract.js +0 -50
  180. package/lib/cjs/server/loaders/babel/creevey-plugin.js +0 -88
  181. package/lib/cjs/server/loaders/babel/helpers.js +0 -479
  182. package/lib/cjs/server/loaders/babel/register.js +0 -126
  183. package/lib/cjs/server/loaders/hooks/mdx.js +0 -30
  184. package/lib/cjs/server/loaders/hooks/svelte.js +0 -65
  185. package/lib/cjs/server/loaders/webpack/compile.js +0 -286
  186. package/lib/cjs/server/loaders/webpack/creevey-loader.js +0 -174
  187. package/lib/cjs/server/loaders/webpack/dummy-hmr.js +0 -44
  188. package/lib/cjs/server/loaders/webpack/mdx-loader.js +0 -72
  189. package/lib/cjs/server/loaders/webpack/start.js +0 -41
  190. package/lib/cjs/server/storybook/entry.js +0 -68
  191. package/lib/cjs/server/storybook/helpers.js +0 -165
  192. package/lib/cjs/server/storybook/providers/nodejs.js +0 -239
  193. package/lib/cjs/shared.js +0 -124
  194. package/lib/esm/server/extract.js +0 -34
  195. package/lib/esm/server/loaders/babel/creevey-plugin.js +0 -74
  196. package/lib/esm/server/loaders/babel/helpers.js +0 -462
  197. package/lib/esm/server/loaders/babel/register.js +0 -105
  198. package/lib/esm/server/loaders/hooks/mdx.js +0 -15
  199. package/lib/esm/server/loaders/hooks/svelte.js +0 -49
  200. package/lib/esm/server/loaders/webpack/compile.js +0 -263
  201. package/lib/esm/server/loaders/webpack/creevey-loader.js +0 -153
  202. package/lib/esm/server/loaders/webpack/dummy-hmr.js +0 -36
  203. package/lib/esm/server/loaders/webpack/mdx-loader.js +0 -58
  204. package/lib/esm/server/loaders/webpack/start.js +0 -27
  205. package/lib/esm/server/storybook/entry.js +0 -44
  206. package/lib/esm/server/storybook/helpers.js +0 -106
  207. package/lib/esm/server/storybook/providers/nodejs.js +0 -217
  208. package/lib/esm/shared.js +0 -93
  209. package/lib/types/server/extract.d.ts +0 -2
  210. package/lib/types/server/loaders/babel/creevey-plugin.d.ts +0 -1
  211. package/lib/types/server/loaders/babel/helpers.d.ts +0 -19
  212. package/lib/types/server/loaders/babel/register.d.ts +0 -5
  213. package/lib/types/server/loaders/hooks/mdx.d.ts +0 -1
  214. package/lib/types/server/loaders/hooks/svelte.d.ts +0 -1
  215. package/lib/types/server/loaders/webpack/compile.d.ts +0 -2
  216. package/lib/types/server/loaders/webpack/creevey-loader.d.ts +0 -2
  217. package/lib/types/server/loaders/webpack/dummy-hmr.d.ts +0 -10
  218. package/lib/types/server/loaders/webpack/mdx-loader.d.ts +0 -6
  219. package/lib/types/server/loaders/webpack/start.d.ts +0 -1
  220. package/lib/types/server/storybook/entry.d.ts +0 -18
  221. package/lib/types/server/storybook/helpers.d.ts +0 -24
  222. package/lib/types/server/storybook/providers/nodejs.d.ts +0 -9
  223. package/lib/types/shared.d.ts +0 -16
  224. package/preset.js +0 -9
  225. package/storybook-static/stories.json +0 -21
  226. package/types/mdx.d.ts +0 -6
@@ -5,12 +5,11 @@ import { downloadBinary, getCreeveyCache } from '../utils';
5
5
  import { pullImages, runImage } from '../docker';
6
6
  import { Octokit } from '@octokit/core';
7
7
  import { subscribeOn } from '../messages';
8
- import { isWorker } from 'cluster';
8
+ import cluster from 'cluster';
9
9
  import { chmod, exec } from 'shelljs';
10
10
  const mkdirAsync = promisify(mkdir);
11
11
  const writeFileAsync = promisify(writeFile);
12
12
  const copyFileAsync = promisify(copyFile);
13
-
14
13
  async function createSelenoidConfig(browsers, {
15
14
  useDocker
16
15
  }) {
@@ -19,15 +18,16 @@ async function createSelenoidConfig(browsers, {
19
18
  browsers.forEach(({
20
19
  browserName,
21
20
  version = 'latest',
22
- dockerImage = `selenoid/${browserName}:${version}`,
21
+ browserVersion = version,
22
+ dockerImage = `selenoid/${browserName}:${browserVersion}`,
23
23
  webdriverCommand = []
24
24
  }) => {
25
25
  if (!selenoidConfig[browserName]) selenoidConfig[browserName] = {
26
- default: version,
26
+ default: browserVersion,
27
27
  versions: {}
28
28
  };
29
29
  if (!useDocker && webdriverCommand.length == 0) throw new Error('Please specify "webdriverCommand" browser option with path to browser webdriver');
30
- selenoidConfig[browserName].versions[version] = {
30
+ selenoidConfig[browserName].versions[browserVersion] = {
31
31
  image: useDocker ? dockerImage : webdriverCommand,
32
32
  port: '4444',
33
33
  path: !useDocker || ['chrome', 'opera', 'webkit', 'MicrosoftEdge'].includes(browserName) ? '/' : '/wd/hub'
@@ -39,10 +39,7 @@ async function createSelenoidConfig(browsers, {
39
39
  await writeFileAsync(path.join(selenoidConfigDir, 'browsers.json'), JSON.stringify(selenoidConfig));
40
40
  return selenoidConfigDir;
41
41
  }
42
-
43
42
  async function downloadSelenoidBinary(destination) {
44
- var _assets$find;
45
-
46
43
  const platformNameMapping = {
47
44
  darwin: 'selenoid_darwin_amd64',
48
45
  linux: 'selenoid_linux_amd64',
@@ -59,52 +56,44 @@ async function downloadSelenoidBinary(destination) {
59
56
  const {
60
57
  browser_download_url: downloadUrl,
61
58
  size: binarySize
62
- } = (_assets$find = assets.find(({
59
+ } = assets.find(({
63
60
  name
64
- }) => platformNameMapping[process.platform] == name)) !== null && _assets$find !== void 0 ? _assets$find : {};
61
+ }) => platformNameMapping[process.platform] == name) ?? {};
65
62
  if (existsSync(destination) && lstatSync(destination).size == binarySize) return;
66
-
67
63
  if (!downloadUrl) {
68
64
  throw new Error(`Couldn't get download url for selenoid binary. Please download it manually from "https://github.com/aerokube/selenoid/releases/latest" and define "selenoidPath" option in the Creevey config`);
69
65
  }
70
-
71
66
  return downloadBinary(downloadUrl, destination);
72
67
  }
73
-
74
68
  export async function startSelenoidStandalone(config, debug) {
75
69
  config.gridUrl = 'http://localhost:4444/wd/hub';
76
- if (isWorker) return;
70
+ if (cluster.isWorker) return;
77
71
  const browsers = Object.values(config.browsers).filter(browser => !browser.gridUrl);
78
72
  const selenoidConfigDir = await createSelenoidConfig(browsers, {
79
73
  useDocker: false
80
74
  });
81
75
  const binaryPath = path.join(selenoidConfigDir, process.platform == 'win32' ? 'selenoid.exe' : 'selenoid');
82
-
83
76
  if (config.selenoidPath) {
84
77
  await copyFileAsync(path.resolve(config.selenoidPath), binaryPath);
85
78
  } else {
86
79
  await downloadSelenoidBinary(binaryPath);
87
- } // TODO Download browser webdrivers
88
-
80
+ }
89
81
 
82
+ // TODO Download browser webdrivers
90
83
  try {
91
84
  if (process.platform != 'win32') chmod('+x', binaryPath);
92
85
  } catch (_) {
93
86
  /* noop */
94
87
  }
95
-
96
88
  const selenoidProcess = exec(`${binaryPath} -conf ./browsers.json -disable-docker`, {
97
89
  async: true,
98
90
  cwd: selenoidConfigDir
99
91
  });
100
-
101
92
  if (debug) {
102
93
  var _selenoidProcess$stdo, _selenoidProcess$stde;
103
-
104
- (_selenoidProcess$stdo = selenoidProcess.stdout) === null || _selenoidProcess$stdo === void 0 ? void 0 : _selenoidProcess$stdo.pipe(process.stdout);
105
- (_selenoidProcess$stde = selenoidProcess.stderr) === null || _selenoidProcess$stde === void 0 ? void 0 : _selenoidProcess$stde.pipe(process.stderr);
94
+ (_selenoidProcess$stdo = selenoidProcess.stdout) === null || _selenoidProcess$stdo === void 0 || _selenoidProcess$stdo.pipe(process.stdout);
95
+ (_selenoidProcess$stde = selenoidProcess.stderr) === null || _selenoidProcess$stde === void 0 || _selenoidProcess$stde.pipe(process.stderr);
106
96
  }
107
-
108
97
  subscribeOn('shutdown', () => selenoidProcess.kill());
109
98
  }
110
99
  export async function startSelenoidContainer(config, debug) {
@@ -114,8 +103,9 @@ export async function startSelenoidContainer(config, debug) {
114
103
  browsers.forEach(({
115
104
  browserName,
116
105
  version = 'latest',
106
+ browserVersion = version,
117
107
  limit: browserLimit = 1,
118
- dockerImage = `selenoid/${browserName}:${version}`
108
+ dockerImage = `selenoid/${browserName}:${browserVersion}`
119
109
  }) => {
120
110
  limit += browserLimit;
121
111
  images.push(dockerImage);
@@ -125,13 +115,12 @@ export async function startSelenoidContainer(config, debug) {
125
115
  auth: config.dockerAuth,
126
116
  platform: config.dockerImagePlatform
127
117
  };
128
-
129
118
  if (config.pullImages) {
130
119
  await pullImages([selenoidImage], pullOptions);
131
120
  await pullImages(images, pullOptions);
132
- } // TODO Allow pass custom options
133
-
121
+ }
134
122
 
123
+ // TODO Allow pass custom options
135
124
  const selenoidOptions = {
136
125
  ExposedPorts: {
137
126
  '4444/tcp': {}
@@ -4,22 +4,18 @@ import { createHash } from 'crypto';
4
4
  import { mapValues, pick } from 'lodash';
5
5
  import { isDefined, isFunction, isObject } from '../types';
6
6
  import { shouldSkip, removeProps } from './utils';
7
- import { isStorybookVersionLessThan } from './storybook/helpers';
8
-
9
7
  function storyTestFabric(delay, testFn) {
10
8
  return async function storyTest() {
11
- var _testFn$call;
12
-
13
9
  delay ? await new Promise(resolve => setTimeout(resolve, delay)) : void 0;
14
- await ((_testFn$call = testFn === null || testFn === void 0 ? void 0 : testFn.call(this)) !== null && _testFn$call !== void 0 ? _testFn$call : this.screenshots.length > 0 ? this.expect(this.screenshots.reduce((screenshots, {
10
+ await (testFn ? testFn.call(this) : this.screenshots.length > 0 ? this.expect(this.screenshots.reduce((screenshots, {
15
11
  imageName,
16
12
  screenshot
17
- }, index) => ({ ...screenshots,
18
- [imageName !== null && imageName !== void 0 ? imageName : `screenshot_${index}`]: screenshot
13
+ }, index) => ({
14
+ ...screenshots,
15
+ [imageName ?? `screenshot_${index}`]: screenshot
19
16
  }), {})).to.matchImages() : this.expect(await this.takeScreenshot()).to.matchImage());
20
17
  };
21
18
  }
22
-
23
19
  function createCreeveyTest(browser, storyMeta, skipOptions, testName) {
24
20
  const {
25
21
  kind,
@@ -41,37 +37,37 @@ function createCreeveyTest(browser, storyMeta, skipOptions, testName) {
41
37
  storyId
42
38
  };
43
39
  }
44
-
45
40
  function convertStories(browserName, stories) {
46
41
  const tests = {};
47
42
  (Array.isArray(stories) ? stories : Object.values(stories)).forEach(storyMeta => {
48
- var _storyMeta$parameters;
49
-
50
43
  // TODO Skip docsOnly stories for now
51
44
  if (storyMeta.parameters.docsOnly) return;
52
45
  const {
53
46
  delay: delayParam,
54
47
  tests: storyTests,
55
48
  skip
56
- } = (_storyMeta$parameters = storyMeta.parameters.creevey) !== null && _storyMeta$parameters !== void 0 ? _storyMeta$parameters : {};
57
- const delay = typeof delayParam == 'number' ? delayParam : delayParam !== null && delayParam !== void 0 && delayParam.for.includes(browserName) ? delayParam.ms : 0; // typeof tests === "undefined" => rootSuite -> kindSuite -> storyTest -> [browsers.png]
49
+ } = storyMeta.parameters.creevey ?? {};
50
+ const delay = typeof delayParam == 'number' ? delayParam : delayParam !== null && delayParam !== void 0 && delayParam.for.includes(browserName) ? delayParam.ms : 0;
51
+
52
+ // typeof tests === "undefined" => rootSuite -> kindSuite -> storyTest -> [browsers.png]
58
53
  // typeof tests === "function" => rootSuite -> kindSuite -> storyTest -> browser -> [images.png]
59
54
  // typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> [browsers.png]
60
55
  // typeof tests === "object" => rootSuite -> kindSuite -> storySuite -> test -> browser -> [images.png]
61
56
 
62
57
  if (!storyTests) {
63
58
  const test = createCreeveyTest(browserName, storyMeta, skip);
64
- tests[test.id] = { ...test,
59
+ tests[test.id] = {
60
+ ...test,
65
61
  storyId: storyMeta.id,
66
62
  story: storyMeta,
67
63
  fn: storyTestFabric(delay)
68
64
  };
69
65
  return;
70
66
  }
71
-
72
67
  Object.entries(storyTests).forEach(([testName, testFn]) => {
73
68
  const test = createCreeveyTest(browserName, storyMeta, skip, testName);
74
- tests[test.id] = { ...test,
69
+ tests[test.id] = {
70
+ ...test,
75
71
  storyId: storyMeta.id,
76
72
  story: storyMeta,
77
73
  fn: storyTestFabric(delay, testFn)
@@ -80,7 +76,6 @@ function convertStories(browserName, stories) {
80
76
  });
81
77
  return tests;
82
78
  }
83
-
84
79
  export async function loadTestsFromStories(browsers, provider, update) {
85
80
  const testIdsByFiles = new Map();
86
81
  const stories = await provider(storiesByFiles => {
@@ -88,17 +83,16 @@ export async function loadTestsFromStories(browsers, provider, update) {
88
83
  const tests = {};
89
84
  browsers.forEach(browser => {
90
85
  Array.from(storiesByFiles.entries()).forEach(([filename, stories]) => {
91
- var _testIdsByFiles$get$f, _testIdsByFiles$get;
92
-
86
+ var _testIdsByFiles$get;
93
87
  Object.assign(tests, convertStories(browser, stories));
94
88
  const changed = Object.keys(tests);
95
- const removed = (_testIdsByFiles$get$f = (_testIdsByFiles$get = testIdsByFiles.get(filename)) === null || _testIdsByFiles$get === void 0 ? void 0 : _testIdsByFiles$get.filter(testId => !tests[testId])) !== null && _testIdsByFiles$get$f !== void 0 ? _testIdsByFiles$get$f : [];
89
+ const removed = ((_testIdsByFiles$get = testIdsByFiles.get(filename)) === null || _testIdsByFiles$get === void 0 ? void 0 : _testIdsByFiles$get.filter(testId => !tests[testId])) ?? [];
96
90
  if (changed.length == 0) testIdsByFiles.delete(filename);else testIdsByFiles.set(filename, changed);
97
91
  Object.assign(testsDiff, tests);
98
92
  removed.forEach(testId => testsDiff[testId] = undefined);
99
93
  });
100
94
  });
101
- update === null || update === void 0 ? void 0 : update(testsDiff);
95
+ update === null || update === void 0 || update(testsDiff);
102
96
  });
103
97
  const tests = browsers.reduce((tests, browser) => Object.assign(tests, convertStories(browser, stories)), {});
104
98
  Object.values(tests).filter(isDefined).forEach(({
@@ -108,35 +102,28 @@ export async function loadTestsFromStories(browsers, provider, update) {
108
102
  fileName
109
103
  }
110
104
  }
111
- }) => {
112
- var _testIdsByFiles$get2;
113
-
114
- return (// TODO Don't use filename as a key, due possible collisions if two require.context with same structure of modules are defined
115
- testIdsByFiles.set(fileName, [...((_testIdsByFiles$get2 = testIdsByFiles.get(fileName)) !== null && _testIdsByFiles$get2 !== void 0 ? _testIdsByFiles$get2 : []), id])
116
- );
117
- });
105
+ }) =>
106
+ // TODO Don't use filename as a key, due possible collisions if two require.context with same structure of modules are defined
107
+ testIdsByFiles.set(fileName, [...(testIdsByFiles.get(fileName) ?? []), id]));
118
108
  return tests;
119
109
  }
120
110
  export function saveStoriesJson(storiesData, extract) {
121
- var _storiesData$stories;
122
-
123
111
  const outputDir = typeof extract == 'boolean' ? 'storybook-static' : extract;
124
112
 
125
- if (!isStorybookVersionLessThan(6)) {
126
- // NOTE Copy-pasted from Storybook's `getStoriesJsonData` method
127
- const allowed = ['fileName', 'docsOnly', 'framework', '__id', '__isArgsStory'];
128
- storiesData.globalParameters = pick(storiesData.globalParameters, allowed); // @ts-expect-error ignore error
129
-
130
- storiesData.kindParameters = mapValues(storiesData.kindParameters, v => pick(v, allowed)); // @ts-expect-error ignore error
131
-
132
- storiesData.stories = mapValues(storiesData.stories, v => ({ ...pick(v, ['id', 'name', 'kind', 'story']),
133
- parameters: pick(v.parameters, allowed)
134
- }));
135
- } // TODO Fix args stories
136
-
137
-
138
- removeProps(storiesData !== null && storiesData !== void 0 ? storiesData : {}, ['stories', () => true, 'parameters', '__isArgsStory']);
139
- Object.values((_storiesData$stories = storiesData === null || storiesData === void 0 ? void 0 : storiesData.stories) !== null && _storiesData$stories !== void 0 ? _storiesData$stories : {}).forEach(story => isObject(story) && 'parameters' in story && isObject(story.parameters) && delete story.parameters.__isArgsStory);
113
+ // NOTE Copy-pasted from Storybook's `getStoriesJsonData` method
114
+ const allowed = ['fileName', 'docsOnly', 'framework', '__id', '__isArgsStory'];
115
+ storiesData.globalParameters = pick(storiesData.globalParameters, allowed);
116
+ // @ts-expect-error ignore error
117
+ storiesData.kindParameters = mapValues(storiesData.kindParameters, v => pick(v, allowed));
118
+ // @ts-expect-error ignore error
119
+ storiesData.stories = mapValues(storiesData.stories, v => ({
120
+ ...pick(v, ['id', 'name', 'kind', 'story']),
121
+ parameters: pick(v.parameters, allowed)
122
+ }));
123
+
124
+ // TODO Fix args stories
125
+ removeProps(storiesData ?? {}, ['stories', () => true, 'parameters', '__isArgsStory']);
126
+ Object.values((storiesData === null || storiesData === void 0 ? void 0 : storiesData.stories) ?? {}).forEach(story => isObject(story) && 'parameters' in story && isObject(story.parameters) && delete story.parameters.__isArgsStory);
140
127
  mkdirSync(outputDir, {
141
128
  recursive: true
142
129
  });
@@ -146,6 +133,7 @@ export function saveTestsJson(tests, dstPath = process.cwd()) {
146
133
  mkdirSync(dstPath, {
147
134
  recursive: true
148
135
  });
149
- writeFileSync(path.join(dstPath, 'tests.json'), // eslint-disable-next-line @typescript-eslint/no-unsafe-return
136
+ writeFileSync(path.join(dstPath, 'tests.json'),
137
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-return
150
138
  JSON.stringify(tests, (_, value) => isFunction(value) ? value.toString() : value, 2));
151
139
  }
@@ -1,16 +1,13 @@
1
- import cluster, { isMaster } from 'cluster';
1
+ import cluster from 'cluster';
2
2
  import { loadStoriesFromBrowser } from '../../selenium';
3
3
  import { emitStoriesMessage, sendStoriesMessage, subscribeOn, subscribeOnWorker } from '../../messages';
4
- import { deserializeRawStories } from '../../../shared';
5
4
  import { isDefined } from '../../../types';
6
5
  import { logger } from '../../logger';
7
- export async function loadStories(_config, {
8
- port
9
- }, storiesListener) {
10
- if (isMaster) {
6
+ import { deserializeRawStories } from '../../../shared';
7
+ export const loadStories = async (_config, _options, storiesListener) => {
8
+ if (cluster.isPrimary) {
11
9
  return new Promise(resolve => {
12
- const worker = Object.values(cluster.workers).filter(isDefined).find(worker => worker.isConnected());
13
-
10
+ const worker = Object.values(cluster.workers ?? {}).filter(isDefined).find(worker => worker.isConnected());
14
11
  if (worker) {
15
12
  const unsubscribe = subscribeOnWorker(worker, 'stories', message => {
16
13
  if (message.type == 'set') {
@@ -27,7 +24,6 @@ export async function loadStories(_config, {
27
24
  type: 'get'
28
25
  });
29
26
  }
30
-
31
27
  subscribeOn('stories', message => {
32
28
  // TODO updates only one browser :(
33
29
  if (message.type == 'update') storiesListener(new Map(message.payload));
@@ -44,18 +40,17 @@ export async function loadStories(_config, {
44
40
  });
45
41
  if (message.type == 'update') storiesListener(new Map(message.payload));
46
42
  });
47
- const stories = deserializeRawStories(await loadStoriesFromBrowser(port));
43
+ const stories = deserializeRawStories(await loadStoriesFromBrowser());
48
44
  const storiesWithOldTests = [];
49
45
  Object.values(stories).forEach(story => {
50
- var _parameters, _parameters$creevey;
51
-
52
- if ((_parameters = story.parameters) !== null && _parameters !== void 0 && (_parameters$creevey = _parameters.creevey) !== null && _parameters$creevey !== void 0 && _parameters$creevey.tests) {
53
- var _parameters2, _parameters2$creevey;
54
-
55
- (_parameters2 = story.parameters) === null || _parameters2 === void 0 ? true : (_parameters2$creevey = _parameters2.creevey) === null || _parameters2$creevey === void 0 ? true : delete _parameters2$creevey.tests;
46
+ var _parameters;
47
+ if ((_parameters = story.parameters) !== null && _parameters !== void 0 && (_parameters = _parameters.creevey) !== null && _parameters !== void 0 && _parameters.tests) {
48
+ var _parameters2;
49
+ (_parameters2 = story.parameters) === null || _parameters2 === void 0 || (_parameters2 = _parameters2.creevey) === null || _parameters2 === void 0 || delete _parameters2.tests;
56
50
  storiesWithOldTests.push(`${story.kind}/${story.name}`);
57
51
  }
58
52
  });
59
53
  return stories;
60
54
  }
61
- }
55
+ };
56
+ loadStories.providerName = 'browser';
@@ -4,61 +4,50 @@ import { logger } from '../../logger';
4
4
  import parse from '../../testsFiles/parser';
5
5
  import { readDirRecursive } from '../../../server/utils';
6
6
  import { combineParameters } from '../../../shared';
7
- export async function loadStories(_config, {
8
- port
9
- }, storiesListener) {
7
+ export const loadStories = async (_config, _options, storiesListener) => {
10
8
  let creeveyParamsByStoryId = {};
11
-
12
9
  const mergeParamsFromTestsToStory = (story, creeveyParams) => {
13
10
  if (story.parameters) {
14
11
  story.parameters.creevey = combineParameters(story.parameters.creevey || {}, creeveyParams);
15
12
  }
16
13
  };
17
-
18
- const stories = await browserProvider(_config, {
19
- port
20
- }, updatedStoriesByFiles => {
14
+ const stories = await browserProvider(_config, {}, updatedStoriesByFiles => {
21
15
  Array.from(updatedStoriesByFiles.entries()).forEach(([, storiesArray]) => {
22
16
  storiesArray.forEach(story => {
23
17
  const creeveyParams = creeveyParamsByStoryId[story.id];
24
-
25
18
  if (creeveyParams) {
26
19
  mergeParamsFromTestsToStory(story, creeveyParams);
27
20
  }
28
21
  });
29
22
  });
30
23
  storiesListener(updatedStoriesByFiles);
31
- }); // TODO fix test files hot reloading
24
+ });
32
25
 
33
- creeveyParamsByStoryId = await parseParams(_config
34
- /*, (data) => console.log(data) */
35
- );
26
+ // TODO fix test files hot reloading
27
+ creeveyParamsByStoryId = await parseParams(_config /*, (data) => console.log(data) */);
36
28
  Object.entries(stories).forEach(([storyId, story]) => {
37
29
  mergeParamsFromTestsToStory(story, creeveyParamsByStoryId[storyId]);
38
30
  });
39
31
  return stories;
40
- }
41
-
32
+ };
42
33
  async function parseParams(config, listener) {
43
34
  if (!config.testsDir) {
44
35
  return Promise.resolve({});
45
36
  }
46
-
47
37
  const testFiles = readDirRecursive(config.testsDir).filter(file => {
48
38
  var _config$testsRegex;
49
-
50
39
  return (_config$testsRegex = config.testsRegex) === null || _config$testsRegex === void 0 ? void 0 : _config$testsRegex.test(file);
51
40
  });
52
41
  await (await import('../../testsFiles/register')).default(config);
53
-
54
42
  if (listener) {
55
43
  chokidar.watch(testFiles).on('change', filePath => {
56
- logger.debug(`changed: ${filePath}`); // doesn't work, always returns {} due modules caching
57
- // see https://github.com/nodejs/modules/issues/307
44
+ logger.debug(`changed: ${filePath}`);
58
45
 
46
+ // doesn't work, always returns {} due modules caching
47
+ // see https://github.com/nodejs/modules/issues/307
59
48
  void parse(testFiles).then(data => listener(data));
60
49
  });
61
50
  }
62
-
63
51
  return parse(testFiles);
64
- }
52
+ }
53
+ loadStories.providerName = 'hybrid';
@@ -0,0 +1,160 @@
1
+ import path from 'path';
2
+ import https from 'https';
3
+ import { exec } from 'shelljs';
4
+ import { stringify } from 'qs';
5
+ import { set } from 'lodash';
6
+ import { v4 } from 'uuid';
7
+ import { isDefined } from '../types';
8
+ const konturGitHost = 'git.skbkontur.ru';
9
+ const trackId = 232; // front_infra
10
+ const origin = 'http://localhost/';
11
+ const category = 'tests_run';
12
+ const action = 'done';
13
+ function buildPathname(label, info) {
14
+ return `/track-event?${stringify({
15
+ id: trackId,
16
+ c: category,
17
+ a: action,
18
+ l: label,
19
+ cv: typeof info == 'string' ? info : JSON.stringify(info),
20
+ ts: new Date().toISOString(),
21
+ url: origin
22
+ })}`;
23
+ }
24
+ function sanitizeGridUrl(gridUrl) {
25
+ const url = new URL(gridUrl);
26
+ url.username = url.username ? '********' : '';
27
+ url.password = url.password ? '********' : '';
28
+ return url.toString();
29
+ }
30
+ function tryGetRepoUrl() {
31
+ try {
32
+ const gitRemoteOutput = exec('git remote -v', {
33
+ silent: true
34
+ });
35
+ const [, repoUrl] = gitRemoteOutput.stdout.match(/origin\s+(.*)\s+\(fetch\)/) ?? [];
36
+ return [repoUrl, null];
37
+ } catch (error) {
38
+ return [undefined, error];
39
+ }
40
+ }
41
+ function tryGetStorybookVersion() {
42
+ try {
43
+ const storybookPackageOutput = exec(`node -e "console.log(JSON.stringify(require('storybook/package.json')))"`, {
44
+ silent: true
45
+ });
46
+ const storybookPackage = JSON.parse(storybookPackageOutput);
47
+ return [storybookPackage.version, null];
48
+ } catch (error) {
49
+ return [undefined, error];
50
+ }
51
+ }
52
+ function tryGetCreeveyVersion() {
53
+ try {
54
+ // eslint-disable-next-line @typescript-eslint/no-var-requires, node/no-missing-require
55
+ const creeveyPackage = require('creevey/package.json');
56
+ return [creeveyPackage.version, null];
57
+ } catch (error) {
58
+ return [undefined, error];
59
+ }
60
+ }
61
+ function sendRequest(options) {
62
+ return new Promise((resolve, reject) => {
63
+ const req = https.request(options, res => {
64
+ if (res.statusCode) {
65
+ if (res.statusCode >= 200 && res.statusCode < 300) {
66
+ resolve();
67
+ } else if (res.statusCode >= 300 && res.statusCode < 400) {
68
+ reject(new Error(`Redirection error: ${res.statusCode}`));
69
+ } else if (res.statusCode >= 400 && res.statusCode < 500) {
70
+ reject(new Error(`Client error: ${res.statusCode}`));
71
+ } else if (res.statusCode >= 500 && res.statusCode < 600) {
72
+ reject(new Error(`Server error: ${res.statusCode}`));
73
+ } else {
74
+ reject(new Error(`Unexpected status code: ${res.statusCode}`));
75
+ }
76
+ } else {
77
+ reject(new Error('No status code received'));
78
+ }
79
+ });
80
+ req.on('error', reject);
81
+ req.end();
82
+ });
83
+ }
84
+ export async function sendScreenshotsCount(config, options, status) {
85
+ var _config$storiesProvid;
86
+ const [repoUrl] = tryGetRepoUrl();
87
+ const isKonturRepo = repoUrl === null || repoUrl === void 0 ? void 0 : repoUrl.includes(konturGitHost);
88
+ if (!isKonturRepo || config.disableTelemetry) return;
89
+ const uuid = v4();
90
+ const [creeveyVersion, creeveyVersionError] = tryGetCreeveyVersion();
91
+ const [storybookVersion, storybookVersionError] = tryGetStorybookVersion();
92
+ const gridUrl = config.gridUrl ? sanitizeGridUrl(config.gridUrl) : undefined;
93
+ const configMeta = {
94
+ runId: uuid,
95
+ repoUrl: repoUrl ?? 'unknown',
96
+ creeveyVersion: creeveyVersion ?? 'unknown',
97
+ storybookVersion: storybookVersion ?? 'unknown',
98
+ options: options._,
99
+ gridUrl,
100
+ screenDir: config.screenDir ? path.relative(process.cwd(), config.screenDir) : undefined,
101
+ useDocker: config.useDocker,
102
+ dockerImage: config.dockerImage,
103
+ maxRetries: config.maxRetries,
104
+ diffOptions: config.diffOptions,
105
+ storiesProvider: ((_config$storiesProvid = config.storiesProvider) === null || _config$storiesProvid === void 0 ? void 0 : _config$storiesProvid.providerName) ?? 'unknown',
106
+ errors: [creeveyVersionError, storybookVersionError].some(Boolean) ? [creeveyVersionError ? `Error while getting creevey version: ${creeveyVersionError.message}` : undefined, storybookVersionError ? `Error while getting storybook version: ${storybookVersionError.message}` : undefined].filter(Boolean) : undefined
107
+ };
108
+ const browsersMeta = {
109
+ runId: uuid,
110
+ browsers: Object.fromEntries(Object.entries(config.browsers ?? {}).map(([name, browser]) => [name, typeof browser === 'object' ? {
111
+ name: browser.name,
112
+ gridUrl: browser.gridUrl ? sanitizeGridUrl(browser.gridUrl) : undefined,
113
+ browserName: browser.browserName,
114
+ browserVersion: browser.browserVersion,
115
+ platformName: browser.platformName,
116
+ viewport: browser.viewport,
117
+ limit: browser.limit,
118
+ dockerImage: browser.dockerImage,
119
+ 'se:teamname': browser['se:teamname']
120
+ } : browser]))
121
+ };
122
+ const tests = {};
123
+ Object.values((status === null || status === void 0 ? void 0 : status.tests) ?? {}).filter(isDefined).forEach(test => {
124
+ set(tests, [...test.storyPath, test.testName, test.browser].filter(isDefined), test.id);
125
+ });
126
+ const testsMeta = {
127
+ runId: uuid,
128
+ tests
129
+ };
130
+ const fullPathname = buildPathname('tests', testsMeta);
131
+ // NOTE: Keep request path shorter than 32k symbols
132
+ const chunksCount = Math.ceil(fullPathname.length / 32_000);
133
+ let chunks = [];
134
+ if (chunksCount > 1) {
135
+ const testsString = JSON.stringify(tests);
136
+ const chunkSize = Math.ceil(testsString.length / chunksCount);
137
+ chunks = Array.from({
138
+ length: chunksCount
139
+ }).map((_, chunkIndex) => testsString.slice(chunkIndex * chunkSize, (chunkIndex + 1) * chunkSize)).map((testsPart, seq) => buildPathname('tests', {
140
+ runId: uuid,
141
+ tests: testsPart,
142
+ seq
143
+ }));
144
+ } else {
145
+ chunks = [fullPathname];
146
+ }
147
+ await Promise.all([sendRequest({
148
+ host: 'metrika.kontur.ru',
149
+ path: buildPathname('config', configMeta),
150
+ protocol: 'https:'
151
+ }), sendRequest({
152
+ host: 'metrika.kontur.ru',
153
+ path: buildPathname('browsers', browsersMeta),
154
+ protocol: 'https:'
155
+ }), ...chunks.map(chunk => sendRequest({
156
+ host: 'metrika.kontur.ru',
157
+ path: chunk,
158
+ protocol: 'https:'
159
+ }))]);
160
+ }
@@ -8,15 +8,12 @@ let result = {};
8
8
  let kindTitle = '';
9
9
  let storyTitle = '';
10
10
  let storyParams = null;
11
-
12
11
  const setStoryParameters = params => {
13
12
  storyParams = params;
14
13
  };
15
-
16
14
  const getStoryId = (kindTitle, storyTitle) => {
17
15
  return toId(kindTitle, storyNameFromExport(storyTitle));
18
16
  };
19
-
20
17
  export const kind = (title, kindFn) => {
21
18
  kindTitle = title;
22
19
  kindFn();
@@ -24,7 +21,6 @@ export const kind = (title, kindFn) => {
24
21
  };
25
22
  export const story = (title, storyFn) => {
26
23
  var _result$storyId;
27
-
28
24
  storyTitle = title;
29
25
  storyParams = null;
30
26
  storyFn({
@@ -39,11 +35,9 @@ export const story = (title, storyFn) => {
39
35
  };
40
36
  export const test = (title, testFn) => {
41
37
  const storyId = getStoryId(kindTitle, storyTitle);
42
-
43
38
  if (!result[storyId]) {
44
39
  result[storyId] = {};
45
40
  }
46
-
47
41
  result[storyId].tests = Object.assign({}, result[storyId].tests, {
48
42
  [title]: testFn
49
43
  });
@@ -7,8 +7,9 @@ export default async function register(config) {
7
7
  });
8
8
  const {
9
9
  path: tsConfigPath
10
- } = getTsconfig(config.tsConfig) || {}; // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
10
+ } = getTsconfig(config.tsConfig) || {};
11
11
 
12
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
12
13
  (await import('@babel/register')).default(config.babelOptions({
13
14
  babelrc: false,
14
15
  rootMode: 'upward-optional',
@@ -24,12 +25,10 @@ export default async function register(config) {
24
25
  },
25
26
  modules: 'commonjs'
26
27
  }]],
27
- plugins: [['@babel/plugin-transform-runtime'], ['babel-plugin-tsconfig-paths', {
28
+ plugins: [['@babel/plugin-transform-runtime'], ...(tsConfigPath ? [['babel-plugin-tsconfig-paths', {
28
29
  tsconfig: tsConfigPath
29
- }]]
30
+ }]] : [])]
30
31
  }));
31
- (await import('ts-node')).register({
32
- project: tsConfigPath,
33
- transpileOnly: true
34
- });
32
+
33
+ // (await import('ts-node')).register({ project: tsConfigPath, transpileOnly: true });
35
34
  }