creevey 0.9.0-beta.0 → 0.9.0-beta.1

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 (148) hide show
  1. package/lib/types/index.d.ts +4 -3
  2. package/lib/types/server/config.d.ts +1 -1
  3. package/lib/types/server/messages.d.ts +0 -1
  4. package/lib/types/server/{parser.d.ts → testsFiles/parser.d.ts} +1 -1
  5. package/lib/types/server/testsFiles/register.d.ts +2 -0
  6. package/lib/types/types.d.ts +3 -2
  7. package/package.json +5 -2
  8. package/lib/cjs/cli.js +0 -5
  9. package/lib/cjs/client/addon/Manager.js +0 -412
  10. package/lib/cjs/client/addon/components/Addon.js +0 -76
  11. package/lib/cjs/client/addon/components/Icons.js +0 -42
  12. package/lib/cjs/client/addon/components/Panel.js +0 -68
  13. package/lib/cjs/client/addon/components/TestSelect.js +0 -63
  14. package/lib/cjs/client/addon/components/Tools.js +0 -114
  15. package/lib/cjs/client/addon/decorator.js +0 -11
  16. package/lib/cjs/client/addon/preset.js +0 -81
  17. package/lib/cjs/client/addon/readyForCapture.js +0 -12
  18. package/lib/cjs/client/addon/register.js +0 -96
  19. package/lib/cjs/client/addon/utils.js +0 -38
  20. package/lib/cjs/client/addon/withCreevey.js +0 -556
  21. package/lib/cjs/client/shared/components/ImagesView/BlendView.js +0 -85
  22. package/lib/cjs/client/shared/components/ImagesView/ImagesView.js +0 -88
  23. package/lib/cjs/client/shared/components/ImagesView/SideBySideView.js +0 -176
  24. package/lib/cjs/client/shared/components/ImagesView/SlideView.js +0 -179
  25. package/lib/cjs/client/shared/components/ImagesView/SwapView.js +0 -110
  26. package/lib/cjs/client/shared/components/ImagesView/index.js +0 -45
  27. package/lib/cjs/client/shared/components/PageFooter/PageFooter.js +0 -46
  28. package/lib/cjs/client/shared/components/PageFooter/Paging.js +0 -98
  29. package/lib/cjs/client/shared/components/PageHeader/ImagePreview.js +0 -78
  30. package/lib/cjs/client/shared/components/PageHeader/PageHeader.js +0 -144
  31. package/lib/cjs/client/shared/components/ResultsPage.js +0 -173
  32. package/lib/cjs/client/shared/creeveyClientApi.js +0 -103
  33. package/lib/cjs/client/shared/helpers.js +0 -482
  34. package/lib/cjs/client/shared/viewMode.js +0 -17
  35. package/lib/cjs/client/web/index.html +0 -19
  36. package/lib/cjs/creevey.js +0 -71
  37. package/lib/cjs/index.js +0 -62
  38. package/lib/cjs/server/config.js +0 -96
  39. package/lib/cjs/server/docker.js +0 -150
  40. package/lib/cjs/server/extract.js +0 -50
  41. package/lib/cjs/server/index.js +0 -83
  42. package/lib/cjs/server/loaders/babel/creevey-plugin.js +0 -88
  43. package/lib/cjs/server/loaders/babel/helpers.js +0 -479
  44. package/lib/cjs/server/loaders/babel/register.js +0 -126
  45. package/lib/cjs/server/loaders/hooks/mdx.js +0 -30
  46. package/lib/cjs/server/loaders/hooks/svelte.js +0 -65
  47. package/lib/cjs/server/loaders/webpack/compile.js +0 -286
  48. package/lib/cjs/server/loaders/webpack/creevey-loader.js +0 -174
  49. package/lib/cjs/server/loaders/webpack/dummy-hmr.js +0 -44
  50. package/lib/cjs/server/loaders/webpack/mdx-loader.js +0 -72
  51. package/lib/cjs/server/loaders/webpack/start.js +0 -41
  52. package/lib/cjs/server/logger.js +0 -47
  53. package/lib/cjs/server/master/api.js +0 -71
  54. package/lib/cjs/server/master/index.js +0 -146
  55. package/lib/cjs/server/master/master.js +0 -57
  56. package/lib/cjs/server/master/pool.js +0 -206
  57. package/lib/cjs/server/master/runner.js +0 -294
  58. package/lib/cjs/server/master/server.js +0 -129
  59. package/lib/cjs/server/messages.js +0 -266
  60. package/lib/cjs/server/parser.js +0 -85
  61. package/lib/cjs/server/selenium/browser.js +0 -680
  62. package/lib/cjs/server/selenium/index.js +0 -31
  63. package/lib/cjs/server/selenium/selenoid.js +0 -174
  64. package/lib/cjs/server/stories.js +0 -170
  65. package/lib/cjs/server/storybook/entry.js +0 -68
  66. package/lib/cjs/server/storybook/helpers.js +0 -165
  67. package/lib/cjs/server/storybook/providers/browser.js +0 -78
  68. package/lib/cjs/server/storybook/providers/hybrid.js +0 -79
  69. package/lib/cjs/server/storybook/providers/nodejs.js +0 -239
  70. package/lib/cjs/server/update.js +0 -83
  71. package/lib/cjs/server/utils.js +0 -185
  72. package/lib/cjs/server/worker/chai-image.js +0 -142
  73. package/lib/cjs/server/worker/helpers.js +0 -69
  74. package/lib/cjs/server/worker/index.js +0 -15
  75. package/lib/cjs/server/worker/reporter.js +0 -120
  76. package/lib/cjs/server/worker/worker.js +0 -278
  77. package/lib/cjs/shared.js +0 -107
  78. package/lib/cjs/types.js +0 -74
  79. package/lib/esm/cli.js +0 -4
  80. package/lib/esm/client/addon/Manager.js +0 -396
  81. package/lib/esm/client/addon/components/Addon.js +0 -58
  82. package/lib/esm/client/addon/components/Icons.js +0 -27
  83. package/lib/esm/client/addon/components/Panel.js +0 -49
  84. package/lib/esm/client/addon/components/TestSelect.js +0 -49
  85. package/lib/esm/client/addon/components/Tools.js +0 -91
  86. package/lib/esm/client/addon/decorator.js +0 -2
  87. package/lib/esm/client/addon/preset.js +0 -56
  88. package/lib/esm/client/addon/readyForCapture.js +0 -5
  89. package/lib/esm/client/addon/register.js +0 -75
  90. package/lib/esm/client/addon/utils.js +0 -31
  91. package/lib/esm/client/addon/withCreevey.js +0 -532
  92. package/lib/esm/client/shared/components/ImagesView/BlendView.js +0 -63
  93. package/lib/esm/client/shared/components/ImagesView/ImagesView.js +0 -65
  94. package/lib/esm/client/shared/components/ImagesView/SideBySideView.js +0 -151
  95. package/lib/esm/client/shared/components/ImagesView/SlideView.js +0 -154
  96. package/lib/esm/client/shared/components/ImagesView/SwapView.js +0 -88
  97. package/lib/esm/client/shared/components/ImagesView/index.js +0 -5
  98. package/lib/esm/client/shared/components/PageFooter/PageFooter.js +0 -32
  99. package/lib/esm/client/shared/components/PageFooter/Paging.js +0 -84
  100. package/lib/esm/client/shared/components/PageHeader/ImagePreview.js +0 -64
  101. package/lib/esm/client/shared/components/PageHeader/PageHeader.js +0 -120
  102. package/lib/esm/client/shared/components/ResultsPage.js +0 -143
  103. package/lib/esm/client/shared/creeveyClientApi.js +0 -94
  104. package/lib/esm/client/shared/helpers.js +0 -424
  105. package/lib/esm/client/shared/viewMode.js +0 -6
  106. package/lib/esm/creevey.js +0 -56
  107. package/lib/esm/index.js +0 -7
  108. package/lib/esm/server/config.js +0 -73
  109. package/lib/esm/server/docker.js +0 -123
  110. package/lib/esm/server/extract.js +0 -34
  111. package/lib/esm/server/index.js +0 -64
  112. package/lib/esm/server/loaders/babel/creevey-plugin.js +0 -74
  113. package/lib/esm/server/loaders/babel/helpers.js +0 -462
  114. package/lib/esm/server/loaders/babel/register.js +0 -105
  115. package/lib/esm/server/loaders/hooks/mdx.js +0 -15
  116. package/lib/esm/server/loaders/hooks/svelte.js +0 -49
  117. package/lib/esm/server/loaders/webpack/compile.js +0 -263
  118. package/lib/esm/server/loaders/webpack/creevey-loader.js +0 -153
  119. package/lib/esm/server/loaders/webpack/dummy-hmr.js +0 -36
  120. package/lib/esm/server/loaders/webpack/mdx-loader.js +0 -58
  121. package/lib/esm/server/loaders/webpack/start.js +0 -27
  122. package/lib/esm/server/logger.js +0 -20
  123. package/lib/esm/server/master/api.js +0 -60
  124. package/lib/esm/server/master/index.js +0 -125
  125. package/lib/esm/server/master/master.js +0 -38
  126. package/lib/esm/server/master/pool.js +0 -187
  127. package/lib/esm/server/master/runner.js +0 -272
  128. package/lib/esm/server/master/server.js +0 -105
  129. package/lib/esm/server/messages.js +0 -234
  130. package/lib/esm/server/parser.js +0 -63
  131. package/lib/esm/server/selenium/browser.js +0 -647
  132. package/lib/esm/server/selenium/index.js +0 -2
  133. package/lib/esm/server/selenium/selenoid.js +0 -151
  134. package/lib/esm/server/stories.js +0 -151
  135. package/lib/esm/server/storybook/entry.js +0 -44
  136. package/lib/esm/server/storybook/helpers.js +0 -106
  137. package/lib/esm/server/storybook/providers/browser.js +0 -61
  138. package/lib/esm/server/storybook/providers/hybrid.js +0 -63
  139. package/lib/esm/server/storybook/providers/nodejs.js +0 -217
  140. package/lib/esm/server/update.js +0 -65
  141. package/lib/esm/server/utils.js +0 -146
  142. package/lib/esm/server/worker/chai-image.js +0 -130
  143. package/lib/esm/server/worker/helpers.js +0 -60
  144. package/lib/esm/server/worker/index.js +0 -1
  145. package/lib/esm/server/worker/reporter.js +0 -98
  146. package/lib/esm/server/worker/worker.js +0 -248
  147. package/lib/esm/shared.js +0 -76
  148. package/lib/esm/types.js +0 -43
@@ -1,647 +0,0 @@
1
- import chalk from 'chalk';
2
- import http from 'http';
3
- import https from 'https';
4
- import { getLogger } from 'loglevel';
5
- import prefix from 'loglevel-plugin-prefix';
6
- import { networkInterfaces } from 'os';
7
- import { PNG } from 'pngjs';
8
- import { Builder, By, Capabilities, Origin } from 'selenium-webdriver';
9
- import { PageLoadStrategy } from 'selenium-webdriver/lib/capabilities';
10
- import { isDefined, noop } from '../../types';
11
- import { colors, logger } from '../logger';
12
- import { emitStoriesMessage, subscribeOn } from '../messages';
13
- import { importStorybookCoreEvents, isStorybookVersionLessThan } from '../storybook/helpers';
14
- import { isShuttingDown, LOCALHOST_REGEXP, runSequence } from '../utils';
15
- const DOCKER_INTERNAL = 'host.docker.internal';
16
- let browserLogger = logger;
17
- let browserName = '';
18
- let browser = null;
19
- let creeveyServerHost = null;
20
-
21
- function getSessionData(grid, sessionId = '') {
22
- const gridUrl = new URL(grid);
23
- gridUrl.pathname = `/host/${sessionId}`;
24
- return new Promise((resolve, reject) => (gridUrl.protocol == 'https:' ? https : http).get(gridUrl.toString(), res => {
25
- if (res.statusCode !== 200) {
26
- var _res$statusCode;
27
-
28
- return reject(new Error(`Couldn't get session data for ${sessionId}. Status code: ${(_res$statusCode = res.statusCode) !== null && _res$statusCode !== void 0 ? _res$statusCode : 'Unknown'}`));
29
- }
30
-
31
- let data = '';
32
- res.setEncoding('utf8');
33
- res.on('data', chunk => data += chunk);
34
- res.on('end', () => {
35
- try {
36
- resolve(JSON.parse(data));
37
- } catch (error) {
38
- var _error$stack;
39
-
40
- reject(new Error(`Couldn't get session data for ${sessionId}. ${error instanceof Error ? (_error$stack = error.stack) !== null && _error$stack !== void 0 ? _error$stack : error.message : error}`));
41
- }
42
- });
43
- }));
44
- }
45
-
46
- function getAddresses() {
47
- return [DOCKER_INTERNAL].concat(...Object.values(networkInterfaces()).filter(isDefined).map(network => network.filter(info => info.family == 'IPv4').map(info => info.address)));
48
- }
49
-
50
- async function resolveStorybookUrl(storybookUrl, checkUrl) {
51
- browserLogger.debug('Resolving storybook url');
52
- const addresses = getAddresses();
53
-
54
- for (const ip of addresses) {
55
- const resolvedUrl = storybookUrl.replace(LOCALHOST_REGEXP, ip);
56
- browserLogger.debug(`Checking storybook availability on ${chalk.magenta(resolvedUrl)}`);
57
-
58
- if (await checkUrl(resolvedUrl)) {
59
- browserLogger.debug(`Resolved storybook url ${chalk.magenta(resolvedUrl)}`);
60
- return resolvedUrl;
61
- }
62
- }
63
-
64
- const error = new Error('Please specify `storybookUrl` with IP address that accessible from remote browser');
65
- error.name = 'ResolveUrlError';
66
- throw error;
67
- }
68
-
69
- async function openUrlAndWaitForPageSource(browser, url, predicate) {
70
- let source = '';
71
- await browser.get(url);
72
-
73
- do {
74
- try {
75
- source = await browser.getPageSource();
76
- } catch (_) {// NOTE: Firefox can raise exception "curContainer.frame.document.documentElement is null"
77
- }
78
- } while (predicate(source));
79
-
80
- return source;
81
- }
82
-
83
- function getUrlChecker(browser) {
84
- return async url => {
85
- try {
86
- // NOTE: Before trying a new url, reset the current one
87
- browserLogger.debug(`Opening ${chalk.magenta('about:blank')} page`);
88
- await openUrlAndWaitForPageSource(browser, 'about:blank', source => !source.includes('<body></body>'));
89
- browserLogger.debug(`Opening ${chalk.magenta(url)} and checking the page source`);
90
- const source = await openUrlAndWaitForPageSource(browser, url, // NOTE: IE11 can return only `head` without body
91
- source => source.length == 0 || !/<body([^>]*>).+<\/body>/s.test(source)); // NOTE: This is the most optimal way to check if we in storybook or not
92
- // We don't use any page load strategies except `NONE`
93
- // because other add significant delay and some of them don't work in earlier chrome versions
94
- // Browsers always load page successful even it's failed
95
- // So we just check `#root` element
96
-
97
- browserLogger.debug(`Checking ${chalk.cyan('#root')} existence on ${chalk.magenta(url)}`);
98
- return source.includes('<div id="root"></div>');
99
- } catch (error) {
100
- return false;
101
- }
102
- };
103
- }
104
-
105
- async function waitForStorybook(browser) {
106
- // NOTE: Storybook 5.x doesn't have the `last` method
107
- if (isStorybookVersionLessThan(6)) {
108
- browserLogger.debug('Waiting for `load` event to make sure that storybook is initiated');
109
- return browser.executeAsyncScript(function (callback) {
110
- if (document.readyState == 'complete') return callback();
111
- window.addEventListener('load', function () {
112
- callback();
113
- });
114
- });
115
- }
116
-
117
- browserLogger.debug('Waiting for `setStories` event to make sure that storybook is initiated');
118
- let wait = true;
119
- let isTimeout = false;
120
- const Events = await importStorybookCoreEvents();
121
- const initiateTimeout = setTimeout(() => {
122
- wait = false;
123
- isTimeout = true;
124
- }, 60000);
125
-
126
- while (wait) {
127
- wait = await browser.executeAsyncScript(function (SET_STORIES, callback) {
128
- if (typeof window.__STORYBOOK_ADDONS_CHANNEL__ == 'undefined') return callback(true);
129
- if (window.__STORYBOOK_ADDONS_CHANNEL__.last(SET_STORIES) == undefined) return callback(true);
130
- return callback(false);
131
- }, Events.SET_STORIES);
132
- if (!wait) clearTimeout(initiateTimeout);
133
- }
134
-
135
- if (isTimeout) throw new Error('Failed to wait `setStories` event');
136
- }
137
-
138
- async function resetMousePosition(browser) {
139
- var _ref, _await$browser$getCap, _await$browser$getCap2, _await$browser$getCap3;
140
-
141
- browserLogger.debug('Resetting mouse position to the top-left corner');
142
- const browserName = (await browser.getCapabilities()).getBrowserName();
143
- const [browserVersion] = (_ref = (_await$browser$getCap = (_await$browser$getCap2 = (await browser.getCapabilities()).getBrowserVersion()) === null || _await$browser$getCap2 === void 0 ? void 0 : _await$browser$getCap2.split('.')) !== null && _await$browser$getCap !== void 0 ? _await$browser$getCap : (_await$browser$getCap3 = (await browser.getCapabilities()).get('version')) === null || _await$browser$getCap3 === void 0 ? void 0 : _await$browser$getCap3.split('.')) !== null && _ref !== void 0 ? _ref : []; // NOTE Reset mouse position to support keweb selenium grid browser versions
144
-
145
- if (browserName == 'chrome' && browserVersion == '70') {
146
- const {
147
- top,
148
- left,
149
- width,
150
- height
151
- } = await browser.executeScript(function () {
152
- const bodyRect = document.body.getBoundingClientRect();
153
- return {
154
- top: bodyRect.top,
155
- left: bodyRect.left,
156
- width: bodyRect.width,
157
- height: bodyRect.height
158
- };
159
- }); // NOTE Bridge mode doesn't support `Origin.VIEWPORT`, move mouse relative
160
-
161
- await browser.actions({
162
- bridge: true
163
- }).move({
164
- origin: browser.findElement(By.css('body')),
165
- x: Math.ceil(-1 * width / 2) - left,
166
- y: Math.ceil(-1 * height / 2) - top
167
- }).perform();
168
- } else if (browserName == 'firefox' && browserVersion == '61') {
169
- // NOTE Firefox for some reason moving by 0 x 0 move cursor in bottom left corner :sad:
170
- await browser.actions().move({
171
- origin: Origin.VIEWPORT,
172
- x: 0,
173
- y: 1
174
- }).perform();
175
- } else {
176
- // NOTE IE don't emit move events until force window focus or connect by RDP on virtual machine
177
- await browser.actions().move({
178
- origin: Origin.VIEWPORT,
179
- x: 0,
180
- y: 0
181
- }).perform();
182
- }
183
- }
184
-
185
- async function resizeViewport(browser, viewport) {
186
- const windowRect = await browser.manage().window().getRect();
187
- const {
188
- innerWidth,
189
- innerHeight
190
- } = await browser.executeScript(function () {
191
- return {
192
- innerWidth: window.innerWidth,
193
- innerHeight: window.innerHeight
194
- };
195
- });
196
- browserLogger.debug(`Resizing viewport from ${innerWidth}x${innerHeight} to ${viewport.width}x${viewport.height}`);
197
- const dWidth = windowRect.width - innerWidth;
198
- const dHeight = windowRect.height - innerHeight;
199
- await browser.manage().window().setRect({
200
- width: viewport.width + dWidth,
201
- height: viewport.height + dHeight
202
- });
203
- }
204
-
205
- const getScrollBarWidth = (() => {
206
- let scrollBarWidth = null;
207
- return async browser => {
208
- if (scrollBarWidth != null) return Promise.resolve(scrollBarWidth);
209
- scrollBarWidth = await browser.executeScript(function () {
210
- // eslint-disable-next-line no-var
211
- var div = document.createElement('div');
212
- div.innerHTML = 'a'; // NOTE: In IE clientWidth is 0 if this div is empty.
213
-
214
- div.style.overflowY = 'scroll';
215
- document.body.appendChild(div); // eslint-disable-next-line no-var
216
-
217
- var widthDiff = div.offsetWidth - div.clientWidth;
218
- document.body.removeChild(div);
219
- return widthDiff;
220
- });
221
- return scrollBarWidth;
222
- };
223
- })(); // NOTE Firefox and Safari take viewport screenshot without scrollbars
224
-
225
-
226
- async function hasScrollBar(browser) {
227
- var _await$browser$getCap4, _await$browser$getCap5;
228
-
229
- const browserName = (await browser.getCapabilities()).getBrowserName();
230
- const [browserVersion] = (_await$browser$getCap4 = (_await$browser$getCap5 = (await browser.getCapabilities()).getBrowserVersion()) === null || _await$browser$getCap5 === void 0 ? void 0 : _await$browser$getCap5.split('.')) !== null && _await$browser$getCap4 !== void 0 ? _await$browser$getCap4 : [];
231
- return browserName != 'Safari' && // NOTE This need to work with keweb selenium grid
232
- !(browserName == 'firefox' && browserVersion == '61');
233
- }
234
-
235
- async function takeCompositeScreenshot(browser, windowRect, elementRect) {
236
- const screens = [];
237
- const isScreenshotWithoutScrollBar = !(await hasScrollBar(browser));
238
- const scrollBarWidth = await getScrollBarWidth(browser); // NOTE Sometimes viewport has been scrolled somewhere
239
-
240
- const normalizedElementRect = {
241
- left: elementRect.left - windowRect.left,
242
- right: elementRect.left + elementRect.width - windowRect.left,
243
- top: elementRect.top - windowRect.top,
244
- bottom: elementRect.top + elementRect.height - windowRect.top
245
- };
246
- const isFitHorizontally = windowRect.width >= elementRect.width + normalizedElementRect.left;
247
- const isFitVertically = windowRect.height >= elementRect.height + normalizedElementRect.top;
248
- const viewportWidth = windowRect.width - (isFitVertically ? 0 : scrollBarWidth);
249
- const viewportHeight = windowRect.height - (isFitHorizontally ? 0 : scrollBarWidth);
250
- const cols = Math.ceil(elementRect.width / viewportWidth);
251
- const rows = Math.ceil(elementRect.height / viewportHeight);
252
- const xOffset = Math.round(isFitHorizontally ? normalizedElementRect.left : Math.max(0, cols * viewportWidth - elementRect.width));
253
- const yOffset = Math.round(isFitVertically ? normalizedElementRect.top : Math.max(0, rows * viewportHeight - elementRect.height));
254
-
255
- for (let row = 0; row < rows; row += 1) {
256
- for (let col = 0; col < cols; col += 1) {
257
- const dx = Math.min(viewportWidth * col + normalizedElementRect.left, Math.max(0, normalizedElementRect.right - viewportWidth));
258
- const dy = Math.min(viewportHeight * row + normalizedElementRect.top, Math.max(0, normalizedElementRect.bottom - viewportHeight));
259
- await browser.executeScript(function (x, y) {
260
- window.scrollTo(x, y);
261
- }, dx, dy);
262
- screens.push(await browser.takeScreenshot());
263
- }
264
- }
265
-
266
- const images = screens.map(s => Buffer.from(s, 'base64')).map(b => PNG.sync.read(b));
267
- const compositeImage = new PNG({
268
- width: Math.round(elementRect.width),
269
- height: Math.round(elementRect.height)
270
- });
271
-
272
- for (let y = 0; y < compositeImage.height; y += 1) {
273
- for (let x = 0; x < compositeImage.width; x += 1) {
274
- const col = Math.floor(x / viewportWidth);
275
- const row = Math.floor(y / viewportHeight);
276
- const isLastCol = cols - col == 1;
277
- const isLastRow = rows - row == 1;
278
- const scrollOffset = isFitVertically || isScreenshotWithoutScrollBar ? 0 : scrollBarWidth;
279
- const i = (y * compositeImage.width + x) * 4;
280
- const j = // NOTE compositeImage(x, y) => image(x, y)
281
- (y % viewportHeight * (viewportWidth + scrollOffset) + x % viewportWidth) * 4 + (isLastRow ? yOffset * (viewportWidth + scrollOffset) * 4 : 0) + (isLastCol ? xOffset * 4 : 0);
282
- const image = images[row * cols + col];
283
- compositeImage.data[i + 0] = image.data[j + 0];
284
- compositeImage.data[i + 1] = image.data[j + 1];
285
- compositeImage.data[i + 2] = image.data[j + 2];
286
- compositeImage.data[i + 3] = image.data[j + 3];
287
- }
288
- }
289
-
290
- return PNG.sync.write(compositeImage).toString('base64');
291
- }
292
-
293
- export async function takeScreenshot(browser, captureElement, ignoreElements) {
294
- let screenshot;
295
- const ignoreStyles = await insertIgnoreStyles(browser, ignoreElements);
296
-
297
- try {
298
- if (!captureElement) {
299
- browserLogger.debug('Capturing viewport screenshot');
300
- screenshot = await browser.takeScreenshot();
301
- browserLogger.debug('Viewport screenshot is captured');
302
- } else {
303
- browserLogger.debug(`Checking is element ${chalk.cyan(captureElement)} fit into viewport`);
304
- const rects = await browser.executeScript(function (selector) {
305
- window.scrollTo(0, 0); // TODO Maybe we should remove same code from `resetMousePosition`
306
- // eslint-disable-next-line no-var
307
-
308
- var element = document.querySelector(selector);
309
- if (!element) return; // eslint-disable-next-line no-var
310
-
311
- var elementRect = element.getBoundingClientRect();
312
- return {
313
- elementRect: {
314
- top: elementRect.top,
315
- left: elementRect.left,
316
- width: elementRect.width,
317
- height: elementRect.height
318
- },
319
- windowRect: {
320
- top: Math.round(window.scrollY || window.pageYOffset),
321
- left: Math.round(window.scrollX || window.pageXOffset),
322
- width: window.innerWidth,
323
- height: window.innerHeight
324
- }
325
- };
326
- }, captureElement);
327
- const {
328
- elementRect,
329
- windowRect
330
- } = rects !== null && rects !== void 0 ? rects : {};
331
- if (!elementRect || !windowRect) throw new Error(`Couldn't find element with selector: '${captureElement}'`);
332
- const isFitIntoViewport = elementRect.width + elementRect.left <= windowRect.width && elementRect.height + elementRect.top <= windowRect.height;
333
- if (isFitIntoViewport) browserLogger.debug(`Capturing ${chalk.cyan(captureElement)}`);else browserLogger.debug(`Capturing composite screenshot image of ${chalk.cyan(captureElement)}`);
334
- screenshot = isFitIntoViewport ? await browser.findElement(By.css(captureElement)).takeScreenshot() : // TODO pointer-events: none, need to research
335
- await takeCompositeScreenshot(browser, windowRect, elementRect);
336
- browserLogger.debug(`${chalk.cyan(captureElement)} is captured`);
337
- }
338
- } finally {
339
- await removeIgnoreStyles(browser, ignoreStyles);
340
- }
341
-
342
- return screenshot;
343
- }
344
-
345
- async function selectStory(browser, {
346
- id,
347
- kind,
348
- name
349
- }, waitForReady = false) {
350
- browserLogger.debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(id)}`);
351
- const result = await browser.executeAsyncScript(function (id, kind, name, shouldWaitForReady, callback) {
352
- if (typeof window.__CREEVEY_SELECT_STORY__ == 'undefined') {
353
- return callback(["Creevey can't switch story. This may happened if forget to add `creevey` addon to your storybook config, or storybook not loaded in browser due syntax error."]);
354
- }
355
-
356
- window.__CREEVEY_SELECT_STORY__(id, kind, name, shouldWaitForReady, callback);
357
- }, id, kind, name, waitForReady);
358
- const [errorMessage, isCaptureCalled = false] = result || [];
359
- if (errorMessage) throw new Error(errorMessage);
360
- return isCaptureCalled;
361
- }
362
-
363
- export async function updateStorybookGlobals(browser, globals) {
364
- if (isStorybookVersionLessThan(6)) {
365
- browserLogger.warn('Globals are not supported by Storybook versions less than 6');
366
- return;
367
- }
368
-
369
- browserLogger.debug('Applying storybook globals');
370
- await browser.executeScript(function (globals) {
371
- window.__CREEVEY_UPDATE_GLOBALS__(globals);
372
- }, globals);
373
- }
374
-
375
- function appendIframePath(url) {
376
- return `${url.replace(/\/$/, '')}/iframe.html`;
377
- }
378
-
379
- async function openStorybookPage(browser, storybookUrl, resolver) {
380
- if (!LOCALHOST_REGEXP.test(storybookUrl)) {
381
- return browser === null || browser === void 0 ? void 0 : browser.get(appendIframePath(storybookUrl));
382
- }
383
-
384
- try {
385
- if (resolver) {
386
- browserLogger.debug('Resolving storybook url with custom resolver');
387
- const resolvedUrl = await resolver();
388
- browserLogger.debug(`Resolver storybook url ${resolvedUrl}`);
389
- return browser.get(appendIframePath(resolvedUrl));
390
- } // NOTE: getUrlChecker already calls `browser.get` so we don't need another one
391
-
392
-
393
- return void (await resolveStorybookUrl(appendIframePath(storybookUrl), getUrlChecker(browser)));
394
- } catch (error) {
395
- browserLogger.error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
396
- throw error;
397
- }
398
- }
399
-
400
- async function resolveCreeveyHost(browser, port) {
401
- if (creeveyServerHost != null) return creeveyServerHost;
402
- const addresses = getAddresses();
403
- creeveyServerHost = await browser.executeAsyncScript(function (hosts, port, callback) {
404
- void Promise.all(hosts.map(function (host) {
405
- return new Promise(function (resolve, reject) {
406
- setTimeout(reject, 10000); // eslint-disable-next-line @typescript-eslint/restrict-plus-operands
407
-
408
- fetch('http://' + host + ':' + port + '/ping').then(resolve).catch(reject);
409
- }).then(function (response) {
410
- return response.text();
411
- }).then(function (pong) {
412
- return pong == 'pong' ? host : null;
413
- }).catch(function () {
414
- return null;
415
- });
416
- })).then(function (hosts) {
417
- callback(hosts.find(function (host) {
418
- return host != null;
419
- }));
420
- });
421
- }, addresses, port);
422
- if (creeveyServerHost == null) throw new Error("Can't reach creevey server from a browser");
423
- return creeveyServerHost;
424
- }
425
-
426
- export async function loadStoriesFromBrowser(port) {
427
- if (!browser) throw new Error("Can't get stories from browser if webdriver isn't connected");
428
- const host = await resolveCreeveyHost(browser, port);
429
- const stories = await browser.executeAsyncScript(function (creeveyHost, creeveyPort, callback) {
430
- window.__CREEVEY_SERVER_HOST__ = creeveyHost;
431
- window.__CREEVEY_SERVER_PORT__ = creeveyPort;
432
- void window.__CREEVEY_GET_STORIES__().then(callback);
433
- }, host, port);
434
- if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
435
- return stories;
436
- }
437
- export async function getBrowser(config, name) {
438
- if (browser) return browser;
439
- browserName = name;
440
- const browserConfig = config.browsers[browserName];
441
- const {
442
- gridUrl = config.gridUrl,
443
- storybookUrl: address = config.storybookUrl,
444
- limit,
445
- viewport,
446
- _storybookGlobals,
447
- ...userCapabilities
448
- } = browserConfig;
449
- void limit;
450
- const realAddress = address; // TODO Define some capabilities explicitly and define typings
451
-
452
- const capabilities = new Capabilities({ ...userCapabilities,
453
- pageLoadStrategy: PageLoadStrategy.NONE
454
- });
455
- subscribeOn('shutdown', () => {
456
- var _browser;
457
-
458
- (_browser = browser) === null || _browser === void 0 ? void 0 : _browser.quit().finally(() => // eslint-disable-next-line no-process-exit
459
- process.exit());
460
- browser = null;
461
- });
462
-
463
- try {
464
- var _await$browser$getSes;
465
-
466
- const url = new URL(gridUrl);
467
- url.username = url.username ? '********' : '';
468
- url.password = url.password ? '********' : '';
469
- browserLogger.debug(`(${name}) Connecting to Selenium ${chalk.magenta(url.toString())}`);
470
- browser = await new Builder().usingServer(gridUrl).withCapabilities(capabilities).build();
471
- const sessionId = (_await$browser$getSes = await browser.getSession()) === null || _await$browser$getSes === void 0 ? void 0 : _await$browser$getSes.getId();
472
- let browserHost = '';
473
-
474
- try {
475
- const {
476
- Name
477
- } = await getSessionData(gridUrl, sessionId);
478
- if (typeof Name == 'string') browserHost = Name;
479
- } catch (_) {
480
- /* noop */
481
- }
482
-
483
- browserLogger.debug(`(${name}) Connected successful with ${[chalk.green(browserHost), chalk.magenta(sessionId)].filter(Boolean).join(':')}`);
484
- browserLogger = getLogger(sessionId);
485
- prefix.apply(browserLogger, {
486
- format(level) {
487
- const levelColor = colors[level.toUpperCase()];
488
- return `[${name}:${chalk.gray(sessionId)}] ${levelColor(level)} =>`;
489
- }
490
-
491
- });
492
- await runSequence([() => {
493
- var _browser2;
494
-
495
- return (_browser2 = browser) === null || _browser2 === void 0 ? void 0 : _browser2.manage().setTimeouts({
496
- pageLoad: 5000,
497
- script: 60000
498
- });
499
- }, () => viewport && browser && resizeViewport(browser, viewport), () => browser && openStorybookPage(browser, realAddress, config.resolveStorybookUrl), () => browser && waitForStorybook(browser)], () => !isShuttingDown.current);
500
- } catch (originalError) {
501
- var _await$browser$getCur, _browser4;
502
-
503
- if (isShuttingDown.current) {
504
- var _browser3;
505
-
506
- (_browser3 = browser) === null || _browser3 === void 0 ? void 0 : _browser3.quit().catch(noop);
507
- browser = null;
508
- return null;
509
- }
510
-
511
- if (originalError instanceof Error && originalError.name == 'ResolveUrlError') throw originalError;
512
- const error = new Error(`Can't load storybook root page by URL ${(_await$browser$getCur = await ((_browser4 = browser) === null || _browser4 === void 0 ? void 0 : _browser4.getCurrentUrl())) !== null && _await$browser$getCur !== void 0 ? _await$browser$getCur : realAddress}`);
513
- if (originalError instanceof Error) error.stack = originalError.stack;
514
- throw error;
515
- }
516
-
517
- if (_storybookGlobals) {
518
- await updateStorybookGlobals(browser, _storybookGlobals);
519
- }
520
-
521
- await browser.executeScript(function (workerId) {
522
- window.__CREEVEY_WORKER_ID__ = workerId;
523
- }, process.pid);
524
- return browser;
525
- }
526
-
527
- async function updateStoryArgs(browser, story, updatedArgs) {
528
- const Events = await importStorybookCoreEvents();
529
- await browser.executeAsyncScript(function (storyId, updatedArgs, UPDATE_STORY_ARGS, STORY_RENDERED, callback) {
530
- window.__STORYBOOK_ADDONS_CHANNEL__.once(STORY_RENDERED, callback);
531
-
532
- window.__STORYBOOK_ADDONS_CHANNEL__.emit(UPDATE_STORY_ARGS, {
533
- storyId,
534
- updatedArgs
535
- });
536
- }, story.id, updatedArgs, Events.UPDATE_STORY_ARGS, Events.STORY_RENDERED);
537
- }
538
-
539
- export async function closeBrowser() {
540
- if (!browser) return;
541
-
542
- try {
543
- await browser.quit();
544
- } finally {
545
- browser = null;
546
- }
547
- }
548
- export async function switchStory() {
549
- var _this$currentTest, _this$currentTest$ctx, _parameters$creevey;
550
-
551
- let testOrSuite = this.currentTest;
552
- if (!testOrSuite) throw new Error("Can't switch story, because test context doesn't have 'currentTest' field");
553
- this.testScope.length = 0;
554
- this.screenshots.length = 0;
555
- this.testScope.push(this.browserName);
556
-
557
- while ((_testOrSuite = testOrSuite) !== null && _testOrSuite !== void 0 && _testOrSuite.title) {
558
- var _testOrSuite;
559
-
560
- this.testScope.push(testOrSuite.title);
561
- testOrSuite = testOrSuite.parent;
562
- }
563
-
564
- const story = (_this$currentTest = this.currentTest) === null || _this$currentTest === void 0 ? void 0 : (_this$currentTest$ctx = _this$currentTest.ctx) === null || _this$currentTest$ctx === void 0 ? void 0 : _this$currentTest$ctx.story;
565
- if (!story) throw new Error(`Current test '${this.testScope.join('/')}' context doesn't have 'story' field`);
566
- const {
567
- id,
568
- kind,
569
- name,
570
- parameters
571
- } = story;
572
- const {
573
- captureElement = '#root',
574
- waitForReady,
575
- ignoreElements
576
- } = (_parameters$creevey = parameters.creevey) !== null && _parameters$creevey !== void 0 ? _parameters$creevey : {};
577
- browserLogger.debug(`Switching to story ${chalk.cyan(kind)}/${chalk.cyan(name)} by id ${chalk.magenta(id)}`);
578
- if (captureElement) Object.defineProperty(this, 'captureElement', {
579
- enumerable: true,
580
- configurable: true,
581
- get: () => this.browser.findElement(By.css(captureElement))
582
- });else Reflect.deleteProperty(this, 'captureElement');
583
-
584
- this.takeScreenshot = () => takeScreenshot(this.browser, captureElement, ignoreElements);
585
-
586
- this.updateStoryArgs = updatedArgs => updateStoryArgs(this.browser, story, updatedArgs);
587
-
588
- this.testScope.reverse();
589
- let storyPlayResolver;
590
- let waitForComplete = new Promise(resolve => storyPlayResolver = resolve);
591
- const unsubscribe = subscribeOn('stories', message => {
592
- var _payload$captureEleme, _payload$ignoreElemen;
593
-
594
- if (message.type != 'capture') return;
595
- const {
596
- payload = {},
597
- payload: {
598
- imageName
599
- } = {}
600
- } = message;
601
- void takeScreenshot(this.browser, (_payload$captureEleme = payload.captureElement) !== null && _payload$captureEleme !== void 0 ? _payload$captureEleme : captureElement, (_payload$ignoreElemen = payload.ignoreElements) !== null && _payload$ignoreElemen !== void 0 ? _payload$ignoreElemen : ignoreElements).then(screenshot => {
602
- this.screenshots.push({
603
- imageName,
604
- screenshot
605
- });
606
- void this.browser.executeAsyncScript(function (callback) {
607
- window.__CREEVEY_HAS_PLAY_COMPLETED_YET__(callback);
608
- }).then(isCompleted => storyPlayResolver(isCompleted));
609
- emitStoriesMessage({
610
- type: 'capture'
611
- });
612
- });
613
- });
614
- await resetMousePosition(this.browser);
615
- const isCaptureCalled = await selectStory(this.browser, {
616
- id,
617
- kind,
618
- name
619
- }, waitForReady);
620
-
621
- if (isCaptureCalled) {
622
- while (!(await waitForComplete)) {
623
- waitForComplete = new Promise(resolve => storyPlayResolver = resolve);
624
- }
625
- }
626
-
627
- unsubscribe();
628
- browserLogger.debug(`Story ${chalk.magenta(id)} ready for capturing`);
629
- }
630
-
631
- async function insertIgnoreStyles(browser, ignoreElements) {
632
- const ignoreSelectors = Array.prototype.concat(ignoreElements).filter(Boolean);
633
- if (!ignoreSelectors.length) return null;
634
- browserLogger.debug('Hiding ignored elements before capturing');
635
- return await browser.executeScript(function (ignoreSelectors) {
636
- return window.__CREEVEY_INSERT_IGNORE_STYLES__(ignoreSelectors);
637
- }, ignoreSelectors);
638
- }
639
-
640
- async function removeIgnoreStyles(browser, ignoreStyles) {
641
- if (ignoreStyles) {
642
- browserLogger.debug('Revert hiding ignored elements');
643
- await browser.executeScript(function (ignoreStyles) {
644
- window.__CREEVEY_REMOVE_IGNORE_STYLES__(ignoreStyles);
645
- }, ignoreStyles);
646
- }
647
- }
@@ -1,2 +0,0 @@
1
- export * from './browser';
2
- export * from './selenoid';