creevey 0.9.0-beta.1 → 0.9.0-beta.10.timeouts.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 (221) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +9 -1
  3. package/addon/README.md +3 -0
  4. package/addon/package.json +5 -0
  5. package/docs/config.md +29 -26
  6. package/jest.config.js +6 -0
  7. package/lib/cjs/cli.js +5 -0
  8. package/lib/cjs/client/addon/Manager.js +264 -0
  9. package/lib/cjs/client/addon/components/Addon.js +55 -0
  10. package/lib/cjs/client/addon/components/Icons.js +46 -0
  11. package/lib/cjs/client/addon/components/Panel.js +72 -0
  12. package/lib/cjs/client/addon/components/TestSelect.js +65 -0
  13. package/lib/cjs/client/addon/components/Tools.js +95 -0
  14. package/lib/cjs/client/addon/decorator.js +11 -0
  15. package/lib/cjs/client/addon/index.js +31 -0
  16. package/lib/cjs/client/addon/preset.ie11.js +74 -0
  17. package/lib/cjs/client/addon/preset.js +17 -0
  18. package/lib/cjs/client/addon/preset.sb7.js +19 -0
  19. package/lib/cjs/client/addon/preview.js +14 -0
  20. package/lib/cjs/client/addon/readyForCapture.js +12 -0
  21. package/lib/cjs/client/addon/register.js +72 -0
  22. package/lib/cjs/client/addon/utils.js +42 -0
  23. package/lib/cjs/client/addon/withCreevey.js +351 -0
  24. package/lib/cjs/client/shared/components/ImagesView/BlendView.js +87 -0
  25. package/lib/cjs/client/shared/components/ImagesView/ImagesView.js +92 -0
  26. package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +154 -0
  27. package/lib/cjs/client/shared/components/ImagesView/SlideView.js +166 -0
  28. package/lib/cjs/client/shared/components/ImagesView/SwapView.js +91 -0
  29. package/lib/cjs/client/shared/components/ImagesView/index.js +45 -0
  30. package/lib/cjs/client/shared/components/PageFooter/PageFooter.js +50 -0
  31. package/lib/cjs/client/shared/components/PageFooter/Paging.js +94 -0
  32. package/lib/cjs/client/shared/components/PageHeader/ImagePreview.js +82 -0
  33. package/lib/cjs/client/shared/components/PageHeader/PageHeader.js +119 -0
  34. package/lib/cjs/client/shared/components/ResultsPage.js +143 -0
  35. package/lib/cjs/client/shared/creeveyClientApi.js +76 -0
  36. package/lib/cjs/client/shared/helpers.js +411 -0
  37. package/lib/cjs/client/shared/viewMode.js +17 -0
  38. package/lib/cjs/client/web/142.js +2 -0
  39. package/lib/cjs/client/web/142.js.LICENSE.txt +12 -0
  40. package/lib/cjs/client/web/32.js +1 -0
  41. package/lib/cjs/client/web/551.js +1 -0
  42. package/lib/cjs/client/web/566.js +2 -0
  43. package/lib/cjs/client/web/566.js.LICENSE.txt +31 -0
  44. package/lib/cjs/client/web/691.js +2 -0
  45. package/lib/cjs/client/web/691.js.LICENSE.txt +8 -0
  46. package/lib/cjs/client/web/725.js +1 -0
  47. package/lib/cjs/client/web/index.html +19 -0
  48. package/lib/cjs/client/web/main.js +2 -38
  49. package/lib/cjs/client/web/main.js.LICENSE.txt +49 -0
  50. package/lib/cjs/creevey.js +69 -0
  51. package/lib/cjs/index.js +62 -0
  52. package/lib/cjs/server/config.js +94 -0
  53. package/lib/cjs/server/docker.js +146 -0
  54. package/lib/cjs/server/extract.js +46 -0
  55. package/lib/cjs/server/index.js +83 -0
  56. package/lib/cjs/server/loaders/babel/creevey-plugin.js +86 -0
  57. package/lib/cjs/server/loaders/babel/helpers.js +469 -0
  58. package/lib/cjs/server/loaders/babel/register.js +124 -0
  59. package/lib/cjs/server/loaders/hooks/mdx.js +30 -0
  60. package/lib/cjs/server/loaders/hooks/svelte.js +65 -0
  61. package/lib/cjs/server/loaders/webpack/compile.js +269 -0
  62. package/lib/cjs/server/loaders/webpack/creevey-loader.js +172 -0
  63. package/lib/cjs/server/loaders/webpack/dummy-hmr.js +39 -0
  64. package/lib/cjs/server/loaders/webpack/mdx-loader.js +72 -0
  65. package/lib/cjs/server/loaders/webpack/start.js +41 -0
  66. package/lib/cjs/server/logger.js +48 -0
  67. package/lib/cjs/server/master/api.js +71 -0
  68. package/lib/cjs/server/master/index.js +146 -0
  69. package/lib/cjs/server/master/master.js +57 -0
  70. package/lib/cjs/server/master/pool.js +197 -0
  71. package/lib/cjs/server/master/runner.js +281 -0
  72. package/lib/cjs/server/master/server.js +131 -0
  73. package/lib/cjs/server/messages.js +264 -0
  74. package/lib/cjs/server/selenium/browser.js +671 -0
  75. package/lib/cjs/server/selenium/index.js +31 -0
  76. package/lib/cjs/server/selenium/selenoid.js +172 -0
  77. package/lib/cjs/server/stories.js +153 -0
  78. package/lib/cjs/server/storybook/entry.js +53 -0
  79. package/lib/cjs/server/storybook/helpers.js +158 -0
  80. package/lib/cjs/server/storybook/providers/browser.js +74 -0
  81. package/lib/cjs/server/storybook/providers/hybrid.js +82 -0
  82. package/lib/cjs/server/storybook/providers/nodejs.js +239 -0
  83. package/lib/cjs/server/testsFiles/parser.js +72 -0
  84. package/lib/cjs/server/testsFiles/register.js +44 -0
  85. package/lib/cjs/server/update.js +79 -0
  86. package/lib/cjs/server/utils.js +176 -0
  87. package/lib/cjs/server/worker/chai-image.js +142 -0
  88. package/lib/cjs/server/worker/helpers.js +69 -0
  89. package/lib/cjs/server/worker/index.js +15 -0
  90. package/lib/cjs/server/worker/reporter.js +108 -0
  91. package/lib/cjs/server/worker/worker.js +268 -0
  92. package/lib/cjs/shared/index.js +101 -0
  93. package/lib/cjs/shared/serializeRegExp.js +42 -0
  94. package/lib/cjs/types.js +75 -0
  95. package/lib/esm/cli.js +4 -0
  96. package/lib/esm/client/addon/Manager.js +248 -0
  97. package/lib/esm/client/addon/components/Addon.js +39 -0
  98. package/lib/esm/client/addon/components/Icons.js +31 -0
  99. package/lib/esm/client/addon/components/Panel.js +53 -0
  100. package/lib/esm/client/addon/components/TestSelect.js +51 -0
  101. package/lib/esm/client/addon/components/Tools.js +74 -0
  102. package/lib/esm/client/addon/decorator.js +2 -0
  103. package/lib/esm/client/addon/index.js +2 -0
  104. package/lib/esm/client/addon/preset.ie11.js +59 -0
  105. package/lib/esm/client/addon/preset.js +8 -0
  106. package/lib/esm/client/addon/preset.sb7.js +8 -0
  107. package/lib/esm/client/addon/preview.js +5 -0
  108. package/lib/esm/client/addon/readyForCapture.js +5 -0
  109. package/lib/esm/client/addon/register.js +51 -0
  110. package/lib/esm/client/addon/utils.js +32 -0
  111. package/lib/esm/client/addon/withCreevey.js +325 -0
  112. package/lib/esm/client/shared/components/ImagesView/BlendView.js +67 -0
  113. package/lib/esm/client/shared/components/ImagesView/ImagesView.js +69 -0
  114. package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +131 -0
  115. package/lib/esm/client/shared/components/ImagesView/SlideView.js +143 -0
  116. package/lib/esm/client/shared/components/ImagesView/SwapView.js +71 -0
  117. package/lib/esm/client/shared/components/ImagesView/index.js +5 -0
  118. package/lib/esm/client/shared/components/PageFooter/PageFooter.js +36 -0
  119. package/lib/esm/client/shared/components/PageFooter/Paging.js +80 -0
  120. package/lib/esm/client/shared/components/PageHeader/ImagePreview.js +68 -0
  121. package/lib/esm/client/shared/components/PageHeader/PageHeader.js +97 -0
  122. package/lib/esm/client/shared/components/ResultsPage.js +115 -0
  123. package/lib/esm/client/shared/creeveyClientApi.js +67 -0
  124. package/lib/esm/client/shared/helpers.js +353 -0
  125. package/lib/esm/client/shared/viewMode.js +6 -0
  126. package/lib/esm/creevey.js +54 -0
  127. package/lib/esm/index.js +5 -0
  128. package/lib/esm/server/config.js +71 -0
  129. package/lib/esm/server/docker.js +123 -0
  130. package/lib/esm/server/extract.js +32 -0
  131. package/lib/esm/server/index.js +64 -0
  132. package/lib/esm/server/loaders/babel/creevey-plugin.js +72 -0
  133. package/lib/esm/server/loaders/babel/helpers.js +452 -0
  134. package/lib/esm/server/loaders/babel/register.js +103 -0
  135. package/lib/esm/server/loaders/hooks/mdx.js +15 -0
  136. package/lib/esm/server/loaders/hooks/svelte.js +49 -0
  137. package/lib/esm/server/loaders/webpack/compile.js +246 -0
  138. package/lib/esm/server/loaders/webpack/creevey-loader.js +152 -0
  139. package/lib/esm/server/loaders/webpack/dummy-hmr.js +32 -0
  140. package/lib/esm/server/loaders/webpack/mdx-loader.js +58 -0
  141. package/lib/esm/server/loaders/webpack/start.js +27 -0
  142. package/lib/esm/server/logger.js +20 -0
  143. package/lib/esm/server/master/api.js +60 -0
  144. package/lib/esm/server/master/index.js +125 -0
  145. package/lib/esm/server/master/master.js +38 -0
  146. package/lib/esm/server/master/pool.js +176 -0
  147. package/lib/esm/server/master/runner.js +259 -0
  148. package/lib/esm/server/master/server.js +107 -0
  149. package/lib/esm/server/messages.js +232 -0
  150. package/lib/esm/server/selenium/browser.js +638 -0
  151. package/lib/esm/server/selenium/index.js +2 -0
  152. package/lib/esm/server/selenium/selenoid.js +149 -0
  153. package/lib/esm/server/stories.js +135 -0
  154. package/lib/esm/server/storybook/entry.js +27 -0
  155. package/lib/esm/server/storybook/helpers.js +97 -0
  156. package/lib/esm/server/storybook/providers/browser.js +58 -0
  157. package/lib/esm/server/storybook/providers/hybrid.js +60 -0
  158. package/lib/esm/server/storybook/providers/nodejs.js +216 -0
  159. package/lib/esm/server/testsFiles/parser.js +50 -0
  160. package/lib/esm/server/testsFiles/register.js +31 -0
  161. package/lib/esm/server/update.js +61 -0
  162. package/lib/esm/server/utils.js +133 -0
  163. package/lib/esm/server/worker/chai-image.js +130 -0
  164. package/lib/esm/server/worker/helpers.js +60 -0
  165. package/lib/esm/server/worker/index.js +1 -0
  166. package/lib/esm/server/worker/reporter.js +86 -0
  167. package/lib/esm/server/worker/worker.js +238 -0
  168. package/lib/esm/shared/index.js +78 -0
  169. package/lib/esm/shared/serializeRegExp.js +24 -0
  170. package/lib/esm/types.js +44 -0
  171. package/lib/types/client/addon/Manager.d.ts +2 -2
  172. package/lib/types/client/addon/components/TestSelect.d.ts +0 -1
  173. package/lib/types/client/addon/index.d.ts +2 -0
  174. package/lib/types/client/addon/preset.d.ts +0 -22
  175. package/lib/types/client/addon/preset.ie11.d.ts +10 -0
  176. package/lib/types/client/addon/preset.sb7.d.ts +2 -0
  177. package/lib/types/client/addon/preview.d.ts +4 -0
  178. package/lib/types/client/addon/utils.d.ts +1 -0
  179. package/lib/types/client/addon/withCreevey.d.ts +1 -1
  180. package/lib/types/client/shared/components/ImagesView/BlendView.d.ts +1 -1
  181. package/lib/types/client/shared/components/ImagesView/ImagesView.d.ts +0 -1
  182. package/lib/types/client/shared/components/ImagesView/SideBySideView.d.ts +1 -1
  183. package/lib/types/client/shared/components/ImagesView/SlideView.d.ts +1 -1
  184. package/lib/types/client/shared/components/ImagesView/SwapView.d.ts +1 -1
  185. package/lib/types/client/shared/components/PageFooter/PageFooter.d.ts +0 -1
  186. package/lib/types/client/shared/components/PageFooter/Paging.d.ts +0 -1
  187. package/lib/types/client/shared/components/PageHeader/ImagePreview.d.ts +1 -1
  188. package/lib/types/client/shared/components/PageHeader/PageHeader.d.ts +0 -1
  189. package/lib/types/client/shared/components/ResultsPage.d.ts +1 -1
  190. package/lib/types/client/web/CreeveyApp.d.ts +0 -1
  191. package/lib/types/client/web/CreeveyLoader.d.ts +1 -2
  192. package/lib/types/client/web/CreeveyView/SideBar/Checkbox.d.ts +1 -1
  193. package/lib/types/client/web/CreeveyView/SideBar/SideBarHeader.d.ts +0 -1
  194. package/lib/types/client/web/CreeveyView/SideBar/SuiteLink.d.ts +4 -4
  195. package/lib/types/client/web/CreeveyView/SideBar/TestLink.d.ts +0 -1
  196. package/lib/types/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +1 -1
  197. package/lib/types/client/web/CreeveyView/SideBar/TestsStatus.d.ts +1 -1
  198. package/lib/types/index.d.ts +1 -0
  199. package/lib/types/server/loaders/babel/register.d.ts +1 -1
  200. package/lib/types/server/loaders/webpack/creevey-loader.d.ts +4 -2
  201. package/lib/types/server/logger.d.ts +6 -2
  202. package/lib/types/server/messages.d.ts +14 -12
  203. package/lib/types/server/selenium/browser.d.ts +5 -3
  204. package/lib/types/server/storybook/entry.d.ts +2 -3
  205. package/lib/types/server/storybook/helpers.d.ts +1 -1
  206. package/lib/types/server/storybook/providers/browser.d.ts +2 -4
  207. package/lib/types/server/storybook/providers/hybrid.d.ts +2 -4
  208. package/lib/types/server/storybook/providers/nodejs.d.ts +3 -3
  209. package/lib/types/server/utils.d.ts +5 -1
  210. package/lib/types/{shared.d.ts → shared/index.d.ts} +1 -10
  211. package/lib/types/shared/serializeRegExp.d.ts +9 -0
  212. package/lib/types/types.d.ts +4 -7
  213. package/package.json +117 -103
  214. package/preset/ie11.js +5 -0
  215. package/{preset.js → preset/index.js} +2 -2
  216. package/preset/sb7.js +5 -0
  217. package/types/global.d.ts +5 -0
  218. package/types/mdx.d.ts +3 -2
  219. package/lib/cjs/client/web/1.js +0 -13
  220. package/lib/cjs/client/web/2.js +0 -1
  221. package/storybook-static/stories.json +0 -21
@@ -0,0 +1,353 @@
1
+ import { themes } from '@storybook/theming';
2
+ import { parse, stringify } from 'qs';
3
+ import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
4
+ import { isTest, isDefined } from '../../types';
5
+ const statusUpdatesMap = new Map([[undefined, /(unknown|success|failed|pending|running)/], ['unknown', /(success|failed|pending|running)/], ['success', /(failed|pending|running)/], ['failed', /(pending|running)/], ['pending', /running/]]);
6
+
7
+ function makeEmptySuiteNode() {
8
+ let path = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : [];
9
+ return {
10
+ path,
11
+ skip: true,
12
+ opened: false,
13
+ checked: true,
14
+ indeterminate: false,
15
+ children: {}
16
+ };
17
+ }
18
+
19
+ export function calcStatus(oldStatus, newStatus) {
20
+ var _statusUpdatesMap$get;
21
+
22
+ return newStatus && (_statusUpdatesMap$get = statusUpdatesMap.get(oldStatus)) !== null && _statusUpdatesMap$get !== void 0 && _statusUpdatesMap$get.test(newStatus) ? newStatus : oldStatus;
23
+ }
24
+ export function getTestPath(test) {
25
+ const {
26
+ browser,
27
+ testName,
28
+ storyPath
29
+ } = test;
30
+ return [...storyPath, testName, browser].filter(isDefined);
31
+ }
32
+ export function getSuiteByPath(suite, path) {
33
+ return path.reduce((suiteOrTest, pathToken) => isTest(suiteOrTest) ? suiteOrTest : suiteOrTest === null || suiteOrTest === void 0 ? void 0 : suiteOrTest.children[pathToken], suite);
34
+ }
35
+ export function getTestByPath(suite, path) {
36
+ var _getSuiteByPath;
37
+
38
+ const test = (_getSuiteByPath = getSuiteByPath(suite, path)) !== null && _getSuiteByPath !== void 0 ? _getSuiteByPath : suite;
39
+ return isTest(test) ? test : null;
40
+ }
41
+ export function getTestsByStoryId(suite, storyId) {
42
+ return Object.values(suite.children).filter(isDefined).flatMap(suiteOrTest => {
43
+ if (isTest(suiteOrTest)) return suiteOrTest.storyId === storyId ? suiteOrTest : [];
44
+ return getTestsByStoryId(suiteOrTest, storyId);
45
+ }).filter(isDefined);
46
+ }
47
+
48
+ function checkTests(suiteOrTest, checked) {
49
+ suiteOrTest.checked = checked;
50
+
51
+ if (!isTest(suiteOrTest)) {
52
+ suiteOrTest.indeterminate = false;
53
+ Object.values(suiteOrTest.children).filter(isDefined).forEach(child => checkTests(child, checked));
54
+ }
55
+ }
56
+
57
+ function updateChecked(suite) {
58
+ const children = Object.values(suite.children).filter(isDefined).filter(child => !child.skip);
59
+ const checkedEvery = children.every(test => test.checked);
60
+ const checkedSome = children.some(test => test.checked);
61
+ const indeterminate = children.some(test => isTest(test) ? false : test.indeterminate) || !checkedEvery && checkedSome;
62
+ const checked = indeterminate || suite.checked == checkedEvery ? suite.checked : checkedEvery;
63
+ suite.checked = checked;
64
+ suite.indeterminate = indeterminate;
65
+ }
66
+
67
+ export function checkSuite(suite, path, checked) {
68
+ const subSuite = getSuiteByPath(suite, path);
69
+ if (subSuite) checkTests(subSuite, checked);
70
+ path.slice(0, -1).map((_, index, tokens) => tokens.slice(0, tokens.length - index)).forEach(parentPath => {
71
+ const parentSuite = getSuiteByPath(suite, parentPath);
72
+ if (isTest(parentSuite)) return;
73
+ if (parentSuite) updateChecked(parentSuite);
74
+ });
75
+ updateChecked(suite);
76
+ }
77
+ export function treeifyTests(testsById) {
78
+ const rootSuite = makeEmptySuiteNode();
79
+ rootSuite.opened = true;
80
+ Object.values(testsById).forEach(test => {
81
+ if (!test) return;
82
+ const [browser, ...testPath] = getTestPath(test).reverse();
83
+ const lastSuite = testPath.reverse().reduce((suite, token) => {
84
+ const subSuite = suite.children[token] || makeEmptySuiteNode([...suite.path, token]);
85
+ subSuite.status = calcStatus(subSuite.status, test.status);
86
+ if (!test.skip) subSuite.skip = false;
87
+ if (!subSuite.skip) suite.skip = false;
88
+ suite.children[token] = subSuite;
89
+ suite.status = calcStatus(suite.status, subSuite.status);
90
+
91
+ if (isTest(subSuite)) {
92
+ throw new Error("Suite and Test should not have same path '".concat(JSON.stringify(getTestPath(subSuite)), "'"));
93
+ }
94
+
95
+ return subSuite;
96
+ }, rootSuite);
97
+ lastSuite.children[browser] = { ...test,
98
+ checked: true
99
+ };
100
+ });
101
+ return rootSuite;
102
+ }
103
+ export function getCheckedTests(suite) {
104
+ return Object.values(suite.children).filter(isDefined).flatMap(suiteOrTest => {
105
+ if (isTest(suiteOrTest)) return suiteOrTest.checked ? suiteOrTest : [];
106
+ if (!suiteOrTest.checked && !suiteOrTest.indeterminate) return [];
107
+ return getCheckedTests(suiteOrTest);
108
+ });
109
+ }
110
+ export function updateTestStatus(suite, path, update) {
111
+ var _suite$children$title;
112
+
113
+ const title = path.shift();
114
+ if (!title) return;
115
+ const suiteOrTest = (_suite$children$title = suite.children[title]) !== null && _suite$children$title !== void 0 ? _suite$children$title : suite.children[title] = { ...(path.length == 0 ? update : makeEmptySuiteNode([...suite.path, title])),
116
+ checked: suite.checked
117
+ };
118
+
119
+ if (isTest(suiteOrTest)) {
120
+ const test = suiteOrTest;
121
+ const {
122
+ skip,
123
+ status,
124
+ results,
125
+ approved
126
+ } = update;
127
+ if (isDefined(skip)) test.skip = skip;
128
+ if (isDefined(status)) test.status = status;
129
+ if (isDefined(results)) test.results ? test.results.push(...results) : test.results = results;
130
+ if (isDefined(approved)) Object.entries(approved).forEach(_ref => {
131
+ let [image, retry] = _ref;
132
+ return retry !== undefined && ((test.approved = test.approved || {})[image] = retry);
133
+ });
134
+ } else {
135
+ const subSuite = suiteOrTest;
136
+ updateTestStatus(subSuite, path, update);
137
+ }
138
+
139
+ suite.skip = Object.values(suite.children).filter(isDefined).map(_ref2 => {
140
+ let {
141
+ skip
142
+ } = _ref2;
143
+ return skip;
144
+ }).every(Boolean);
145
+ suite.status = Object.values(suite.children).filter(isDefined).map(_ref3 => {
146
+ let {
147
+ status
148
+ } = _ref3;
149
+ return status;
150
+ }).reduce(calcStatus);
151
+ }
152
+ export function removeTests(suite, path) {
153
+ var _suiteOrTest$children;
154
+
155
+ const title = path.shift();
156
+ if (!title) return;
157
+ const suiteOrTest = suite.children[title];
158
+ if (suiteOrTest && !isTest(suiteOrTest)) removeTests(suiteOrTest, path);
159
+ if (isTest(suiteOrTest) || Object.keys((_suiteOrTest$children = suiteOrTest === null || suiteOrTest === void 0 ? void 0 : suiteOrTest.children) !== null && _suiteOrTest$children !== void 0 ? _suiteOrTest$children : {}).length == 0) delete suite.children[title];
160
+ if (Object.keys(suite.children).length == 0) return;
161
+ updateChecked(suite);
162
+ suite.skip = Object.values(suite.children).filter(isDefined).map(_ref4 => {
163
+ let {
164
+ skip
165
+ } = _ref4;
166
+ return skip;
167
+ }).every(Boolean);
168
+ suite.status = Object.values(suite.children).filter(isDefined).map(_ref5 => {
169
+ let {
170
+ status
171
+ } = _ref5;
172
+ return status;
173
+ }).reduce(calcStatus);
174
+ }
175
+ export function filterTests(suite, filter) {
176
+ const {
177
+ status,
178
+ subStrings
179
+ } = filter;
180
+ if (!status && !subStrings.length) return suite;
181
+ const filteredSuite = { ...suite,
182
+ children: {}
183
+ };
184
+ Object.entries(suite.children).forEach(_ref6 => {
185
+ let [title, suiteOrTest] = _ref6;
186
+ if (!suiteOrTest || suiteOrTest.skip) return;
187
+
188
+ if (!status && subStrings.some(subString => title.toLowerCase().includes(subString))) {
189
+ filteredSuite.children[title] = suiteOrTest;
190
+ } else if (isTest(suiteOrTest)) {
191
+ if (status && suiteOrTest.status && ['pending', 'running', status].includes(suiteOrTest.status)) filteredSuite.children[title] = suiteOrTest;
192
+ } else {
193
+ const filteredSubSuite = filterTests(suiteOrTest, filter);
194
+ if (Object.keys(filteredSubSuite.children).length == 0) return;
195
+ filteredSuite.children[title] = filteredSubSuite;
196
+ }
197
+ });
198
+ return filteredSuite;
199
+ }
200
+ export function openSuite(suite, path, opened) {
201
+ const subSuite = path.reduce((suiteOrTest, pathToken) => {
202
+ if (suiteOrTest && !isTest(suiteOrTest)) {
203
+ if (opened) suiteOrTest.opened = opened;
204
+ return suiteOrTest.children[pathToken];
205
+ }
206
+ }, suite);
207
+ if (subSuite && !isTest(subSuite)) subSuite.opened = opened;
208
+ }
209
+ export function flattenSuite(suite) {
210
+ if (!suite.opened) return [];
211
+ return Object.entries(suite.children).flatMap(_ref7 => {
212
+ let [title, subSuite] = _ref7;
213
+ return subSuite ? [{
214
+ title,
215
+ suite: subSuite
216
+ }, ...(isTest(subSuite) ? [] : flattenSuite(subSuite))] : [];
217
+ });
218
+ }
219
+ export function countTestsStatus(suite) {
220
+ let successCount = 0;
221
+ let failedCount = 0;
222
+ let skippedCount = 0;
223
+ let pendingCount = 0;
224
+ const cases = Object.values(suite.children).filter(isDefined);
225
+ let suiteOrTest;
226
+
227
+ while (suiteOrTest = cases.pop()) {
228
+ if (isTest(suiteOrTest)) {
229
+ if (suiteOrTest.skip) skippedCount++;
230
+ if (suiteOrTest.status === 'success') successCount++;
231
+ if (suiteOrTest.status === 'failed') failedCount++;
232
+ if (suiteOrTest.status === 'pending') pendingCount++;
233
+ } else {
234
+ cases.push(...Object.values(suiteOrTest.children).filter(isDefined));
235
+ }
236
+ }
237
+
238
+ return {
239
+ successCount,
240
+ failedCount,
241
+ skippedCount,
242
+ pendingCount
243
+ };
244
+ }
245
+ export function getConnectionUrl() {
246
+ return [typeof __CREEVEY_SERVER_HOST__ == 'undefined' ? window.location.hostname : __CREEVEY_SERVER_HOST__, typeof __CREEVEY_SERVER_PORT__ == 'undefined' ? window.location.port : __CREEVEY_SERVER_PORT__].filter(Boolean).join(':');
247
+ }
248
+ export function getImageUrl(path, imageName) {
249
+ // path => [kind, story, test, browser]
250
+ const browser = path.slice(-1)[0];
251
+ const imagesUrl = window.location.host ? "".concat(window.location.protocol, "//").concat(getConnectionUrl()).concat(window.location.pathname == '/' ? '/report' : window.location.pathname.split('/').slice(0, -1).join('/'), "/").concat(encodeURI(path.slice(0, -1).join('/'))) : encodeURI(path.slice(0, -1).join('/'));
252
+ return imageName == browser ? imagesUrl : "".concat(imagesUrl, "/").concat(encodeURI(browser));
253
+ }
254
+ export function getBorderSize(element) {
255
+ // NOTE Firefox returns empty string for `borderWidth` prop
256
+ const borderSize = parseFloat(getComputedStyle(element).borderTopWidth);
257
+ return Number.isNaN(borderSize) ? 0 : borderSize;
258
+ }
259
+ export function useLoadImages(s1, s2, s3) {
260
+ const [loaded, setLoaded] = useState(false);
261
+ useEffect(() => {
262
+ setLoaded(false);
263
+ void Promise.all([s1, s2, s3].map(url => new Promise(resolve => {
264
+ const image = document.createElement('img');
265
+ image.src = url;
266
+ image.onload = resolve;
267
+ image.onerror = resolve;
268
+ }))).then(() => setLoaded(true));
269
+ }, [s1, s2, s3]);
270
+ return loaded;
271
+ }
272
+ /**
273
+ * Uses the ResizeObserver API to observe changes within the given HTML Element DOM Rect.
274
+ *
275
+ * @returns dimensions of element's content box (which means without paddings and border width)
276
+ */
277
+
278
+ export function useResizeObserver(elementRef, onResize) {
279
+ let debounceTimeout = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 16;
280
+ const observerRef = useRef(null);
281
+ useEffect(() => {
282
+ if (!elementRef.current) return;
283
+ observerRef.current = new ResizeObserver(onResize);
284
+ observerRef.current.observe(elementRef.current);
285
+ return () => {
286
+ var _observerRef$current;
287
+
288
+ return (_observerRef$current = observerRef.current) === null || _observerRef$current === void 0 ? void 0 : _observerRef$current.disconnect();
289
+ };
290
+ }, [debounceTimeout, elementRef, onResize]);
291
+ }
292
+ export function useApplyScale(imageRef, scale, dependency) {
293
+ useLayoutEffect(() => {
294
+ if (!imageRef.current) return;
295
+ const image = imageRef.current;
296
+ const borderSize = getBorderSize(image);
297
+ image.style.height = "".concat(image.naturalHeight * scale + borderSize * 2, "px");
298
+ }, [imageRef, scale, dependency]);
299
+ }
300
+ export function useCalcScale(diffImageRef, loaded) {
301
+ const [scale, setScale] = useState(1);
302
+ const calcScale = useCallback(() => {
303
+ const diffImage = diffImageRef.current;
304
+ if (!diffImage || !loaded) return setScale(1);
305
+ const borderSize = getBorderSize(diffImage);
306
+ const ratio = (diffImage.getBoundingClientRect().width - borderSize * 2) / diffImage.naturalWidth;
307
+ setScale(Math.min(1, ratio));
308
+ }, [diffImageRef, loaded]);
309
+ useResizeObserver(diffImageRef, calcScale);
310
+ useLayoutEffect(calcScale, [calcScale]);
311
+ return scale;
312
+ }
313
+ const CREEVEY_THEME = 'Creevey_theme';
314
+
315
+ function isTheme(theme) {
316
+ return isDefined(theme) && Object.prototype.hasOwnProperty.call(themes, theme);
317
+ }
318
+
319
+ function initialTheme() {
320
+ const theme = localStorage.getItem(CREEVEY_THEME);
321
+ return isTheme(theme) ? theme : 'light';
322
+ }
323
+
324
+ export function useTheme() {
325
+ const [theme, setTheme] = useState(initialTheme());
326
+ useEffect(() => {
327
+ localStorage.setItem(CREEVEY_THEME, theme);
328
+ }, [theme]);
329
+ return [theme, setTheme];
330
+ }
331
+ export function setSearchParams(testPath) {
332
+ const pageUrl = "?".concat(stringify({
333
+ testPath
334
+ }));
335
+ window.history.pushState({
336
+ testPath
337
+ }, '', pageUrl);
338
+ }
339
+ export function getTestPathFromSearch() {
340
+ const {
341
+ testPath
342
+ } = parse(window.location.search.slice(1)); //@ts-expect-error: This expression is not callable.
343
+
344
+ if (Array.isArray(testPath) && testPath.every(token => typeof token == 'string')) {
345
+ return testPath;
346
+ }
347
+
348
+ return [];
349
+ }
350
+ export function useForceUpdate() {
351
+ const [, update] = useState({});
352
+ return useCallback(() => update({}), []);
353
+ }
@@ -0,0 +1,6 @@
1
+ export const VIEW_MODE_KEY = 'Creevey_view_mode';
2
+ export const viewModes = ['side-by-side', 'swap', 'slide', 'blend'];
3
+ export const getViewMode = () => {
4
+ const item = localStorage.getItem(VIEW_MODE_KEY);
5
+ return item && viewModes.includes(item) ? item : 'side-by-side';
6
+ };
@@ -0,0 +1,54 @@
1
+ import cluster from 'cluster';
2
+ import minimist from 'minimist';
3
+ import creevey from './server';
4
+ import { noop } from './types';
5
+ import { emitWorkerMessage } from './server/messages';
6
+ import { isShuttingDown, shutdown, shutdownWorkers } from './server/utils';
7
+ import { setDefaultLevel, levels } from 'loglevel';
8
+ import { logger } from './server/logger';
9
+
10
+ function shutdownOnException(reason) {
11
+ if (isShuttingDown.current) return;
12
+ const error = reason instanceof Error ? reason.stack ?? reason.message : reason;
13
+ logger.error(error);
14
+ process.exitCode = -1;
15
+ if (cluster.isWorker) emitWorkerMessage({
16
+ type: 'error',
17
+ payload: {
18
+ error
19
+ }
20
+ });
21
+ if (cluster.isPrimary && !isShuttingDown.current) void shutdownWorkers();
22
+ }
23
+
24
+ process.on('uncaughtException', shutdownOnException);
25
+ process.on('unhandledRejection', shutdownOnException);
26
+ if (cluster.isWorker) process.on('SIGINT', noop);
27
+ if (cluster.isPrimary) process.on('SIGINT', shutdown);
28
+ const argv = minimist(process.argv.slice(2), {
29
+ string: ['browser', 'config', 'reporter', 'reportDir', 'screenDir'],
30
+ boolean: ['debug', 'ui', 'saveReport', 'webpack', 'tests'],
31
+ default: {
32
+ port: 3000,
33
+ saveReport: true
34
+ },
35
+ alias: {
36
+ port: 'p',
37
+ config: 'c',
38
+ debug: 'd',
39
+ update: 'u',
40
+ extract: 'e'
41
+ }
42
+ }); // @ts-expect-error: define log level for storybook
43
+
44
+ global.LOGLEVEL = argv.debug ? 'debug' : 'warn';
45
+
46
+ if (argv.debug) {
47
+ logger.setDefaultLevel(levels.DEBUG);
48
+ setDefaultLevel(levels.DEBUG);
49
+ } else {
50
+ logger.setDefaultLevel(levels.INFO);
51
+ setDefaultLevel(levels.INFO);
52
+ }
53
+
54
+ void creevey(argv);
@@ -0,0 +1,5 @@
1
+ export * from './types';
2
+ export { loadStories as browserStoriesProvider } from './server/storybook/providers/browser';
3
+ export { loadStories as nodejsStoriesProvider } from './server/storybook/providers/nodejs';
4
+ export { loadStories as hybridStoriesProvider } from './server/storybook/providers/hybrid';
5
+ export * from './server/testsFiles/parser';
@@ -0,0 +1,71 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { isCSFv3Enabled, storybookDirRef } from './storybook/helpers';
4
+ import { loadStories as nodejsStoriesProvider } from './storybook/providers/nodejs';
5
+ import { loadStories as browserStoriesProvider } from './storybook/providers/browser';
6
+ import { isDefined } from '../types';
7
+ export const defaultBrowser = 'chrome';
8
+ export const defaultConfig = {
9
+ useDocker: true,
10
+ useWebpackToExtractTests: false,
11
+ dockerImage: 'aerokube/selenoid:latest-release',
12
+ dockerImagePlatform: '',
13
+ pullImages: true,
14
+ failFast: false,
15
+ storybookUrl: 'http://localhost:6006',
16
+ screenDir: path.resolve('images'),
17
+ reportDir: path.resolve('report'),
18
+ storybookDir: path.resolve('.storybook'),
19
+ maxRetries: 0,
20
+ diffOptions: {
21
+ threshold: 0,
22
+ includeAA: true
23
+ },
24
+ browsers: {
25
+ [defaultBrowser]: true
26
+ },
27
+ hooks: {},
28
+ babelOptions: _ => _,
29
+ testsRegex: /\.creevey\.(t|j)s$/
30
+ };
31
+
32
+ function normalizeBrowserConfig(name, config) {
33
+ if (typeof config == 'boolean') return {
34
+ browserName: name
35
+ };
36
+ if (typeof config == 'string') return {
37
+ browserName: config
38
+ };
39
+ return config;
40
+ }
41
+
42
+ function resolveConfigPath(configPath) {
43
+ const rootDir = process.cwd();
44
+ const configDir = path.resolve('.creevey');
45
+
46
+ if (isDefined(configPath)) {
47
+ configPath = path.resolve(configPath);
48
+ } else if (fs.existsSync(configDir)) {
49
+ configPath = path.join(configDir, 'config'); // TODO We already find file with extension, why not use it?
50
+ } else if (fs.readdirSync(rootDir).find(filename => filename.startsWith('creevey.config'))) {
51
+ configPath = path.join(rootDir, 'creevey.config');
52
+ }
53
+
54
+ return configPath;
55
+ }
56
+
57
+ export async function readConfig(options) {
58
+ const configPath = resolveConfigPath(options.config);
59
+ const userConfig = { ...defaultConfig
60
+ };
61
+ if (isDefined(configPath)) Object.assign(userConfig, (await import(configPath)).default);
62
+ storybookDirRef.current = userConfig.storybookDir;
63
+ if (!userConfig.storiesProvider) userConfig.storiesProvider = (await isCSFv3Enabled()) ? browserStoriesProvider : nodejsStoriesProvider;
64
+ if (options.failFast != undefined) userConfig.failFast = Boolean(options.failFast);
65
+ if (options.reportDir) userConfig.reportDir = path.resolve(options.reportDir);
66
+ if (options.screenDir) userConfig.screenDir = path.resolve(options.screenDir); // NOTE: Hack to pass typescript checking
67
+
68
+ const config = userConfig;
69
+ Object.entries(config.browsers).forEach(([browser, browserConfig]) => config.browsers[browser] = normalizeBrowserConfig(browser, browserConfig));
70
+ return config;
71
+ }
@@ -0,0 +1,123 @@
1
+ import cluster from 'cluster';
2
+ import { isDockerMessage } from '../types';
3
+ import { subscribeOn, sendDockerMessage, emitDockerMessage } from './messages';
4
+ import { isInsideDocker, LOCALHOST_REGEXP } from './utils';
5
+ import Dockerode from 'dockerode';
6
+ import { Writable } from 'stream';
7
+ import ora from 'ora';
8
+ import { logger } from './logger';
9
+ const docker = new Dockerode();
10
+
11
+ class DevNull extends Writable {
12
+ _write(_chunk, _encoding, callback) {
13
+ setImmediate(callback);
14
+ }
15
+
16
+ }
17
+
18
+ export async function pullImages(images, {
19
+ auth,
20
+ platform
21
+ } = {}) {
22
+ const args = {};
23
+ if (auth) args.authconfig = auth;
24
+ if (platform) args.platform = platform;
25
+ logger.info('Pull docker images');
26
+
27
+ for (const image of images) {
28
+ await new Promise((resolve, reject) => {
29
+ const spinner = ora(`${image}: Pull start`).start(); // eslint-disable-next-line @typescript-eslint/no-floating-promises
30
+
31
+ docker.pull(image, args, function (pullError, stream) {
32
+ if (pullError) {
33
+ spinner.fail();
34
+ return reject(pullError);
35
+ } // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
36
+
37
+
38
+ docker.modem.followProgress(stream, onFinished, onProgress);
39
+
40
+ function onFinished(error) {
41
+ if (error) {
42
+ spinner.fail();
43
+ return reject(error);
44
+ }
45
+
46
+ spinner.succeed(`${image}: Pull complete`);
47
+ resolve();
48
+ }
49
+
50
+ function onProgress(event) {
51
+ if (!/^[a-z0-9]{12}$/i.test(event.id)) return;
52
+ spinner.text = `${image}: [${event.id}] ${event.status} ${event.progress ? `${event.progress}` : ''}`;
53
+ }
54
+ });
55
+ });
56
+ }
57
+ }
58
+ export async function runImage(image, args, options, debug) {
59
+ await Promise.all((await docker.listContainers({
60
+ all: true,
61
+ filters: {
62
+ ancestor: [image]
63
+ }
64
+ })).map(async info => {
65
+ const container = docker.getContainer(info.Id);
66
+
67
+ try {
68
+ await container.stop();
69
+ } catch (_) {
70
+ /* noop */
71
+ }
72
+
73
+ await container.remove();
74
+ }));
75
+ const hub = docker.run(image, args, debug ? process.stdout : new DevNull(), options, error => {
76
+ if (error) throw error;
77
+ });
78
+ return new Promise(resolve => {
79
+ hub.once('container', container => {
80
+ // eslint-disable-next-line @typescript-eslint/no-misused-promises
81
+ subscribeOn('shutdown', async () => {
82
+ try {
83
+ await container.stop();
84
+ await container.remove();
85
+ } catch (error) {
86
+ /* noop */
87
+ }
88
+ });
89
+ });
90
+ hub.once('start', container => void container.inspect().then(info => resolve(info.NetworkSettings.Networks.bridge.IPAddress)));
91
+ });
92
+ }
93
+ export default async function (config, browser, startContainer) {
94
+ if (cluster.isPrimary) {
95
+ const host = await startContainer();
96
+ let gridUrl = 'http://localhost:4444/wd/hub';
97
+ gridUrl = isInsideDocker ? gridUrl.replace(LOCALHOST_REGEXP, host) : gridUrl;
98
+ cluster.on('message', (worker, message) => {
99
+ if (!isDockerMessage(message)) return;
100
+ const dockerMessage = message;
101
+ if (dockerMessage.type != 'start') return;
102
+ sendDockerMessage(worker, {
103
+ type: 'success',
104
+ payload: {
105
+ gridUrl
106
+ }
107
+ });
108
+ });
109
+ } else {
110
+ if (browser && config.browsers[browser].gridUrl) return Promise.resolve();
111
+ return new Promise(resolve => {
112
+ subscribeOn('docker', message => {
113
+ if (message.type == 'success') {
114
+ config.gridUrl = message.payload.gridUrl;
115
+ resolve();
116
+ }
117
+ });
118
+ emitDockerMessage({
119
+ type: 'start'
120
+ });
121
+ });
122
+ }
123
+ }
@@ -0,0 +1,32 @@
1
+ import { subscribeOn } from './messages';
2
+ import { loadTestsFromStories, saveStoriesJson, saveTestsJson } from './stories';
3
+ import { extractStoriesData } from './storybook/providers/nodejs';
4
+ export default async function extract(config, options) {
5
+ if (config.useWebpackToExtractTests && process.env.__CREEVEY_ENV__ != 'test') {
6
+ await new Promise((resolve, reject) => {
7
+ subscribeOn('webpack', message => {
8
+ switch (message.type) {
9
+ case 'success':
10
+ return resolve();
11
+
12
+ case 'fail':
13
+ return reject();
14
+ }
15
+ });
16
+ void (async () => (await import('./loaders/webpack/compile')).default(config, options))();
17
+ });
18
+ }
19
+
20
+ const tests = await loadTestsFromStories(Object.keys(config.browsers), async () => {
21
+ const data = await extractStoriesData(config, {
22
+ watch: false,
23
+ debug: options.debug
24
+ });
25
+ const stories = data.stories;
26
+ if (options.extract) saveStoriesJson(data, options.extract);
27
+ return stories;
28
+ });
29
+ if (options.tests) saveTestsJson(tests); // eslint-disable-next-line no-process-exit
30
+
31
+ process.exit(0);
32
+ }