creevey 0.10.0-beta.4 → 0.10.0-beta.41
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.
- package/README.md +19 -41
- package/dist/client/addon/components/Addon.js +17 -7
- package/dist/client/addon/components/Addon.js.map +1 -1
- package/dist/client/addon/components/Panel.js +2 -2
- package/dist/client/addon/components/Panel.js.map +1 -1
- package/dist/client/addon/components/Tools.js +17 -7
- package/dist/client/addon/components/Tools.js.map +1 -1
- package/dist/client/addon/withCreevey.d.ts +2 -1
- package/dist/client/addon/withCreevey.js +11 -1
- package/dist/client/addon/withCreevey.js.map +1 -1
- package/dist/client/shared/components/ImagesView/BlendView.d.ts +1 -1
- package/dist/client/shared/components/ImagesView/BlendView.js +17 -7
- package/dist/client/shared/components/ImagesView/BlendView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SideBySideView.d.ts +1 -1
- package/dist/client/shared/components/ImagesView/SideBySideView.js +17 -7
- package/dist/client/shared/components/ImagesView/SideBySideView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SlideView.d.ts +1 -1
- package/dist/client/shared/components/ImagesView/SlideView.js +17 -7
- package/dist/client/shared/components/ImagesView/SlideView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SwapView.d.ts +1 -1
- package/dist/client/shared/components/ImagesView/SwapView.js +29 -7
- package/dist/client/shared/components/ImagesView/SwapView.js.map +1 -1
- package/dist/client/shared/components/PageHeader/ImagePreview.d.ts +1 -1
- package/dist/client/shared/components/PageHeader/ImagePreview.js +1 -0
- package/dist/client/shared/components/PageHeader/ImagePreview.js.map +1 -1
- package/dist/client/shared/components/PageHeader/PageHeader.js +20 -8
- package/dist/client/shared/components/PageHeader/PageHeader.js.map +1 -1
- package/dist/client/shared/components/ResultsPage.d.ts +1 -1
- package/dist/client/shared/components/ResultsPage.js +43 -13
- package/dist/client/shared/components/ResultsPage.js.map +1 -1
- package/dist/client/shared/creeveyClientApi.js +8 -1
- package/dist/client/shared/creeveyClientApi.js.map +1 -1
- package/dist/client/shared/helpers.d.ts +1 -3
- package/dist/client/shared/helpers.js +4 -19
- package/dist/client/shared/helpers.js.map +1 -1
- package/dist/client/web/CreeveyApp.js +42 -14
- package/dist/client/web/CreeveyApp.js.map +1 -1
- package/dist/client/web/CreeveyContext.d.ts +5 -0
- package/dist/client/web/CreeveyContext.js +20 -7
- package/dist/client/web/CreeveyContext.js.map +1 -1
- package/dist/client/web/CreeveyLoader.js +2 -2
- package/dist/client/web/CreeveyLoader.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/Search.js +19 -9
- package/dist/client/web/CreeveyView/SideBar/Search.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBar.js +18 -7
- package/dist/client/web/CreeveyView/SideBar/SideBar.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js +60 -7
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js +17 -7
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.d.ts +2 -2
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js +18 -10
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestLink.js +18 -10
- package/dist/client/web/CreeveyView/SideBar/TestLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.d.ts +1 -1
- package/dist/client/web/KeyboardEventsContext.d.ts +1 -8
- package/dist/client/web/KeyboardEventsContext.js +79 -64
- package/dist/client/web/KeyboardEventsContext.js.map +1 -1
- package/dist/client/web/assets/index-C47njyZV.js +802 -0
- package/dist/client/web/index.html +1 -1
- package/dist/client/web/index.js +17 -7
- package/dist/client/web/index.js.map +1 -1
- package/dist/client/web/themes.d.ts +2 -0
- package/dist/client/web/themes.js +22 -0
- package/dist/client/web/themes.js.map +1 -0
- package/dist/creevey.js +16 -9
- package/dist/creevey.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/server/config.d.ts +1 -1
- package/dist/server/config.js +30 -7
- package/dist/server/config.js.map +1 -1
- package/dist/server/connection.d.ts +3 -0
- package/dist/server/connection.js +28 -0
- package/dist/server/connection.js.map +1 -0
- package/dist/server/docker.d.ts +1 -1
- package/dist/server/docker.js +56 -32
- package/dist/server/docker.js.map +1 -1
- package/dist/server/index.js +64 -11
- package/dist/server/index.js.map +1 -1
- package/dist/server/logger.d.ts +2 -1
- package/dist/server/logger.js +7 -3
- package/dist/server/logger.js.map +1 -1
- package/dist/server/master/api.js +1 -1
- package/dist/server/master/api.js.map +1 -1
- package/dist/server/master/pool.d.ts +4 -3
- package/dist/server/master/pool.js +13 -66
- package/dist/server/master/pool.js.map +1 -1
- package/dist/server/master/queue.d.ts +13 -0
- package/dist/server/master/queue.js +71 -0
- package/dist/server/master/queue.js.map +1 -0
- package/dist/server/master/runner.d.ts +3 -0
- package/dist/server/master/runner.js +78 -10
- package/dist/server/master/runner.js.map +1 -1
- package/dist/server/master/server.js +1 -1
- package/dist/server/master/server.js.map +1 -1
- package/dist/server/master/start.js +13 -11
- package/dist/server/master/start.js.map +1 -1
- package/dist/server/playwright/docker-file.d.ts +1 -1
- package/dist/server/playwright/docker-file.js +15 -6
- package/dist/server/playwright/docker-file.js.map +1 -1
- package/dist/server/playwright/docker.d.ts +2 -1
- package/dist/server/playwright/docker.js +10 -2
- package/dist/server/playwright/docker.js.map +1 -1
- package/dist/server/playwright/index-source.mjs +16 -0
- package/dist/server/playwright/internal.d.ts +6 -6
- package/dist/server/playwright/internal.js +143 -91
- package/dist/server/playwright/internal.js.map +1 -1
- package/dist/server/playwright/webdriver.d.ts +1 -1
- package/dist/server/playwright/webdriver.js +5 -8
- package/dist/server/playwright/webdriver.js.map +1 -1
- package/dist/server/providers/browser.js +6 -4
- package/dist/server/providers/browser.js.map +1 -1
- package/dist/server/providers/hybrid.js +1 -1
- package/dist/server/providers/hybrid.js.map +1 -1
- package/dist/server/reporters/creevey.d.ts +7 -0
- package/dist/server/reporters/creevey.js +63 -0
- package/dist/server/reporters/creevey.js.map +1 -0
- package/dist/server/reporters/index.d.ts +2 -0
- package/dist/server/reporters/index.js +16 -0
- package/dist/server/reporters/index.js.map +1 -0
- package/dist/server/reporters/junit.d.ts +16 -0
- package/dist/server/reporters/junit.js +165 -0
- package/dist/server/reporters/junit.js.map +1 -0
- package/dist/server/reporters/teamcity.d.ts +7 -0
- package/dist/server/reporters/teamcity.js +60 -0
- package/dist/server/reporters/teamcity.js.map +1 -0
- package/dist/server/selenium/internal.d.ts +3 -4
- package/dist/server/selenium/internal.js +127 -108
- package/dist/server/selenium/internal.js.map +1 -1
- package/dist/server/selenium/selenoid.js +8 -6
- package/dist/server/selenium/selenoid.js.map +1 -1
- package/dist/server/selenium/webdriver.d.ts +1 -1
- package/dist/server/selenium/webdriver.js +5 -9
- package/dist/server/selenium/webdriver.js.map +1 -1
- package/dist/server/telemetry.js +2 -2
- package/dist/server/testsFiles/parser.js +45 -5
- package/dist/server/testsFiles/parser.js.map +1 -1
- package/dist/server/utils.d.ts +19 -1
- package/dist/server/utils.js +87 -8
- package/dist/server/utils.js.map +1 -1
- package/dist/server/webdriver.d.ts +5 -4
- package/dist/server/webdriver.js +23 -10
- package/dist/server/webdriver.js.map +1 -1
- package/dist/server/worker/chai-image.d.ts +1 -2
- package/dist/server/worker/chai-image.js +4 -3
- package/dist/server/worker/chai-image.js.map +1 -1
- package/dist/server/worker/context.d.ts +3 -0
- package/dist/server/worker/context.js +15 -0
- package/dist/server/worker/context.js.map +1 -0
- package/dist/server/worker/match-image.d.ts +4 -4
- package/dist/server/worker/match-image.js +7 -4
- package/dist/server/worker/match-image.js.map +1 -1
- package/dist/server/worker/start.js +47 -73
- package/dist/server/worker/start.js.map +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/types.d.ts +46 -10
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -1
- package/docs/cli.md +12 -0
- package/docs/config.md +179 -165
- package/docs/storybook.md +60 -0
- package/docs/tests.md +50 -45
- package/package.json +64 -63
- package/src/client/addon/components/Panel.tsx +2 -2
- package/src/client/addon/withCreevey.ts +10 -2
- package/src/client/shared/components/ImagesView/SwapView.tsx +18 -0
- package/src/client/shared/components/PageHeader/ImagePreview.tsx +1 -0
- package/src/client/shared/components/PageHeader/PageHeader.tsx +4 -2
- package/src/client/shared/components/ResultsPage.tsx +31 -8
- package/src/client/shared/creeveyClientApi.ts +9 -1
- package/src/client/shared/helpers.ts +4 -24
- package/src/client/web/CreeveyApp.tsx +27 -8
- package/src/client/web/CreeveyContext.tsx +9 -0
- package/src/client/web/CreeveyLoader.tsx +1 -1
- package/src/client/web/CreeveyView/SideBar/Search.tsx +3 -3
- package/src/client/web/CreeveyView/SideBar/SideBar.tsx +1 -0
- package/src/client/web/CreeveyView/SideBar/SideBarFooter.tsx +37 -6
- package/src/client/web/CreeveyView/SideBar/SuiteLink.tsx +3 -5
- package/src/client/web/CreeveyView/SideBar/TestLink.tsx +2 -4
- package/src/client/web/KeyboardEventsContext.tsx +61 -73
- package/src/client/web/themes.ts +24 -0
- package/src/creevey.ts +16 -10
- package/src/server/config.ts +30 -7
- package/src/server/connection.ts +26 -0
- package/src/server/docker.ts +63 -34
- package/src/server/index.ts +72 -14
- package/src/server/logger.ts +6 -2
- package/src/server/master/api.ts +1 -1
- package/src/server/master/pool.ts +23 -59
- package/src/server/master/queue.ts +77 -0
- package/src/server/master/runner.ts +96 -10
- package/src/server/master/server.ts +1 -1
- package/src/server/master/start.ts +16 -11
- package/src/server/playwright/docker-file.ts +18 -6
- package/src/server/playwright/docker.ts +16 -3
- package/src/server/playwright/index-source.mjs +16 -0
- package/src/server/playwright/internal.ts +182 -111
- package/src/server/playwright/webdriver.ts +6 -9
- package/src/server/providers/browser.ts +6 -4
- package/src/server/providers/hybrid.ts +1 -1
- package/src/server/reporters/creevey.ts +71 -0
- package/src/server/reporters/index.ts +11 -0
- package/src/server/reporters/junit.ts +205 -0
- package/src/server/reporters/teamcity.ts +74 -0
- package/src/server/selenium/internal.ts +131 -116
- package/src/server/selenium/selenoid.ts +8 -6
- package/src/server/selenium/webdriver.ts +6 -10
- package/src/server/telemetry.ts +2 -2
- package/src/server/testsFiles/parser.ts +52 -4
- package/src/server/utils.ts +97 -9
- package/src/server/webdriver.ts +24 -16
- package/src/server/worker/chai-image.ts +4 -4
- package/src/server/worker/context.ts +14 -0
- package/src/server/worker/match-image.ts +12 -8
- package/src/server/worker/start.ts +51 -86
- package/src/shared/index.ts +1 -1
- package/src/types.ts +50 -11
- package/types/global.d.ts +1 -0
- package/.yarnrc.yml +0 -1
- package/chromatic.config.json +0 -5
- package/dist/client/web/assets/index-DkmZfG9C.js +0 -591
- package/dist/server/reporter.d.ts +0 -26
- package/dist/server/reporter.js +0 -108
- package/dist/server/reporter.js.map +0 -1
- package/src/server/reporter.ts +0 -138
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Args } from '@storybook/
|
1
|
+
import type { Args } from '@storybook/types';
|
2
2
|
import chalk from 'chalk';
|
3
3
|
import http from 'http';
|
4
4
|
import https from 'https';
|
@@ -24,7 +24,7 @@ import {
|
|
24
24
|
StorybookEvents,
|
25
25
|
} from '../../types.js';
|
26
26
|
import { colors, logger } from '../logger.js';
|
27
|
-
import { subscribeOn } from '../messages.js';
|
27
|
+
import { emitWorkerMessage, subscribeOn } from '../messages.js';
|
28
28
|
import { getTestPath, isShuttingDown, runSequence } from '../utils.js';
|
29
29
|
import {
|
30
30
|
appendIframePath,
|
@@ -94,21 +94,25 @@ async function openUrlAndWaitForPageSource(
|
|
94
94
|
}
|
95
95
|
|
96
96
|
async function buildWebdriver(
|
97
|
-
|
97
|
+
browser: string,
|
98
98
|
gridUrl: string,
|
99
99
|
config: Config,
|
100
100
|
options: Options,
|
101
101
|
): Promise<WebDriver | null> {
|
102
|
-
const browserConfig = config.browsers[
|
103
|
-
const { /*customizeBuilder,*/ seleniumCapabilities } = browserConfig;
|
102
|
+
const browserConfig = config.browsers[browser] as BrowserConfigObject;
|
103
|
+
const { /*customizeBuilder,*/ seleniumCapabilities, browserName } = browserConfig;
|
104
104
|
|
105
105
|
const url = new URL(gridUrl);
|
106
106
|
url.username = url.username ? '********' : '';
|
107
107
|
url.password = url.password ? '********' : '';
|
108
|
-
logger.debug(`
|
108
|
+
logger().debug(`Connecting to Selenium ${chalk.magenta(url.toString())}`);
|
109
109
|
|
110
110
|
// TODO Define some capabilities explicitly and define typings
|
111
|
-
const capabilities = new Capabilities({
|
111
|
+
const capabilities = new Capabilities({
|
112
|
+
browserName,
|
113
|
+
...seleniumCapabilities,
|
114
|
+
pageLoadStrategy: PageLoadStrategy.EAGER,
|
115
|
+
});
|
112
116
|
const prefs = new logging.Preferences();
|
113
117
|
|
114
118
|
if (options.trace) {
|
@@ -121,37 +125,65 @@ async function buildWebdriver(
|
|
121
125
|
// TODO Validate browsers, versions, and platform
|
122
126
|
// TODO Use `customizeBuilder`
|
123
127
|
|
124
|
-
let
|
128
|
+
let webdriver: WebDriver | null;
|
125
129
|
|
126
130
|
try {
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
131
|
+
const maxRetries = 5;
|
132
|
+
let retries = 0;
|
133
|
+
do {
|
134
|
+
webdriver = await Promise.race([
|
135
|
+
new Promise<null>((resolve) => {
|
136
|
+
setTimeout(() => {
|
137
|
+
retries += 1;
|
138
|
+
resolve(null);
|
139
|
+
}, 120_000);
|
140
|
+
}),
|
141
|
+
(async () => {
|
142
|
+
if (retries > 0) {
|
143
|
+
logger().debug(`Trying to initialize session to Selenium Grid: retried ${retries} of ${maxRetries}`);
|
144
|
+
}
|
145
|
+
const retry = retries;
|
146
|
+
// const ie = new IeOptions();
|
147
|
+
// const edge = new EdgeOptions();
|
148
|
+
// const chrome = new ChromeOptions();
|
149
|
+
// const safari = new SafariOptions();
|
150
|
+
// const firefox = new FirefoxOptions();
|
151
|
+
// edge.enableBidi();
|
152
|
+
// chrome.enableBidi();
|
153
|
+
// firefox.enableBidi();
|
154
|
+
|
155
|
+
const driver = await new Builder()
|
156
|
+
// .setIeOptions(ie)
|
157
|
+
// .setEdgeOptions(edge)
|
158
|
+
// .setChromeOptions(chrome)
|
159
|
+
// .setSafariOptions(safari)
|
160
|
+
// .setFirefoxOptions(firefox)
|
161
|
+
.usingServer(gridUrl)
|
162
|
+
.withCapabilities(capabilities)
|
163
|
+
.setLoggingPrefs(prefs) // NOTE: Should go last
|
164
|
+
.build();
|
165
|
+
|
166
|
+
// const id = await driver.getWindowHandle();
|
167
|
+
// context = await BrowsingContext(driver, { browsingContextId: id });
|
168
|
+
|
169
|
+
if (retry != retries) {
|
170
|
+
void driver.quit();
|
171
|
+
return null;
|
172
|
+
}
|
173
|
+
|
174
|
+
return driver;
|
175
|
+
})(),
|
176
|
+
]);
|
177
|
+
if (webdriver) break;
|
178
|
+
} while (retries < maxRetries);
|
179
|
+
|
180
|
+
if (!webdriver) throw new Error('Failed to initialize session to Selenium Grid due to many retries');
|
149
181
|
} catch (error) {
|
150
|
-
logger.error(`
|
182
|
+
logger().error(`Failed to start browser:`, error);
|
151
183
|
return null;
|
152
184
|
}
|
153
185
|
|
154
|
-
return
|
186
|
+
return webdriver;
|
155
187
|
}
|
156
188
|
|
157
189
|
export class InternalBrowser {
|
@@ -159,13 +191,13 @@ export class InternalBrowser {
|
|
159
191
|
#browser: WebDriver;
|
160
192
|
#serverHost: string | null = null;
|
161
193
|
#serverPort: number;
|
162
|
-
#
|
194
|
+
#storybookGlobals?: StorybookGlobals;
|
163
195
|
#unsubscribe: () => void = noop;
|
164
196
|
#keepAliveInterval: NodeJS.Timeout | null = null;
|
165
|
-
constructor(browser: WebDriver, port: number,
|
197
|
+
constructor(browser: WebDriver, port: number, storybookGlobals?: StorybookGlobals) {
|
166
198
|
this.#browser = browser;
|
167
199
|
this.#serverPort = port;
|
168
|
-
this.#
|
200
|
+
this.#storybookGlobals = storybookGlobals;
|
169
201
|
this.#unsubscribe = subscribeOn('shutdown', () => {
|
170
202
|
void this.closeBrowser();
|
171
203
|
});
|
@@ -194,7 +226,7 @@ export class InternalBrowser {
|
|
194
226
|
|
195
227
|
const ignoreStyles = await this.insertIgnoreStyles(ignoreElements);
|
196
228
|
|
197
|
-
if (
|
229
|
+
if (logger().getLevel() <= Logger.levels.DEBUG) {
|
198
230
|
const { innerWidth, innerHeight } = await this.#browser.executeScript<{
|
199
231
|
innerWidth: number;
|
200
232
|
innerHeight: number;
|
@@ -204,20 +236,20 @@ export class InternalBrowser {
|
|
204
236
|
innerHeight: window.innerHeight,
|
205
237
|
};
|
206
238
|
});
|
207
|
-
|
239
|
+
logger().debug(`Viewport size is: ${innerWidth}x${innerHeight}`);
|
208
240
|
}
|
209
241
|
|
210
242
|
try {
|
211
243
|
if (!captureElement) {
|
212
|
-
|
244
|
+
logger().debug('Capturing viewport screenshot');
|
213
245
|
screenshot = await this.#browser.takeScreenshot();
|
214
|
-
|
246
|
+
logger().debug('Viewport screenshot is captured');
|
215
247
|
} else {
|
216
|
-
|
248
|
+
logger().debug(`Checking is element ${chalk.cyan(captureElement)} fit into viewport`);
|
217
249
|
const rects = await this.#browser.executeScript<
|
218
250
|
{ elementRect: ElementRect; windowRect: ElementRect } | undefined
|
219
251
|
>(function (selector: string): { elementRect: ElementRect; windowRect: ElementRect } | undefined {
|
220
|
-
window.scrollTo(0, 0);
|
252
|
+
window.scrollTo(0, 0);
|
221
253
|
// eslint-disable-next-line no-var
|
222
254
|
var element = document.querySelector(selector);
|
223
255
|
if (!element) return;
|
@@ -234,9 +266,7 @@ export class InternalBrowser {
|
|
234
266
|
},
|
235
267
|
// NOTE page_Offset is used only for IE9-11
|
236
268
|
windowRect: {
|
237
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
238
269
|
top: Math.round(window.scrollY || window.pageYOffset),
|
239
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
240
270
|
left: Math.round(window.scrollX || window.pageXOffset),
|
241
271
|
width: window.innerWidth,
|
242
272
|
height: window.innerHeight,
|
@@ -252,11 +282,11 @@ export class InternalBrowser {
|
|
252
282
|
elementRect.height + elementRect.top <= windowRect.height;
|
253
283
|
|
254
284
|
if (isFitIntoViewport) {
|
255
|
-
|
285
|
+
logger().debug(
|
256
286
|
`Capturing ${chalk.cyan(captureElement)} with size: ${elementRect.width}x${elementRect.height}`,
|
257
287
|
);
|
258
288
|
} else
|
259
|
-
|
289
|
+
logger().debug(
|
260
290
|
`Capturing composite screenshot image of ${chalk.cyan(captureElement)} with size: ${elementRect.width}x${elementRect.height}`,
|
261
291
|
);
|
262
292
|
|
@@ -265,14 +295,12 @@ export class InternalBrowser {
|
|
265
295
|
// ? context
|
266
296
|
// ? await context.captureElementScreenshot(await element.getId())
|
267
297
|
// : await browser.findElement(By.css(captureElement)).takeScreenshot()
|
268
|
-
// :
|
269
|
-
// await takeCompositeScreenshot(browser, windowRect, elementRect);
|
298
|
+
// : await takeCompositeScreenshot(browser, windowRect, elementRect);
|
270
299
|
screenshot = isFitIntoViewport
|
271
300
|
? await this.#browser.findElement(By.css(captureElement)).takeScreenshot()
|
272
|
-
:
|
273
|
-
await this.takeCompositeScreenshot(windowRect, elementRect);
|
301
|
+
: await this.takeCompositeScreenshot(windowRect, elementRect);
|
274
302
|
|
275
|
-
|
303
|
+
logger().debug(`${chalk.cyan(captureElement)} is captured`);
|
276
304
|
}
|
277
305
|
} finally {
|
278
306
|
await this.removeIgnoreStyles(ignoreStyles);
|
@@ -291,10 +319,11 @@ export class InternalBrowser {
|
|
291
319
|
|
292
320
|
async selectStory(id: string, waitForReady = false): Promise<boolean> {
|
293
321
|
// NOTE: Global variables might be reset after hot reload. I think it's workaround, maybe we need better solution
|
322
|
+
await this.updateStorybookGlobals();
|
294
323
|
await this.updateBrowserGlobalVariables();
|
295
324
|
await this.resetMousePosition();
|
296
325
|
|
297
|
-
|
326
|
+
logger().debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(id)}`);
|
298
327
|
|
299
328
|
const result = await this.#browser.executeAsyncScript<[error?: string | null, isCaptureCalled?: boolean] | null>(
|
300
329
|
function (
|
@@ -356,14 +385,14 @@ export class InternalBrowser {
|
|
356
385
|
}
|
357
386
|
|
358
387
|
async afterTest(test: ServerTest): Promise<void> {
|
359
|
-
if (
|
388
|
+
if (logger().getLevel() === Logger.levels.TRACE) {
|
360
389
|
const output: string[] = [];
|
361
390
|
const types = await this.#browser.manage().logs().getAvailableLogTypes();
|
362
391
|
for (const type of types) {
|
363
392
|
const logs = await this.#browser.manage().logs().get(type);
|
364
393
|
output.push(logs.map((log) => JSON.stringify(log.toJSON(), null, 2)).join('\n'));
|
365
394
|
}
|
366
|
-
|
395
|
+
logger().debug(
|
367
396
|
'----------',
|
368
397
|
getTestPath(test).join('/'),
|
369
398
|
'----------\n',
|
@@ -387,7 +416,7 @@ export class InternalBrowser {
|
|
387
416
|
|
388
417
|
if (!browser) return null;
|
389
418
|
|
390
|
-
const internalBrowser = new InternalBrowser(browser, options.port,
|
419
|
+
const internalBrowser = new InternalBrowser(browser, options.port, _storybookGlobals);
|
391
420
|
|
392
421
|
try {
|
393
422
|
if (isShuttingDown.current) return null;
|
@@ -397,19 +426,18 @@ export class InternalBrowser {
|
|
397
426
|
gridUrl,
|
398
427
|
viewport,
|
399
428
|
storybookUrl: address,
|
400
|
-
storybookGlobals: _storybookGlobals,
|
401
|
-
resolveStorybookUrl: config.resolveStorybookUrl,
|
402
429
|
});
|
403
430
|
|
404
431
|
return done ? internalBrowser : null;
|
405
432
|
} catch (originalError) {
|
406
433
|
void internalBrowser.closeBrowser();
|
407
434
|
|
408
|
-
const message =
|
409
|
-
|
435
|
+
const message =
|
436
|
+
originalError instanceof Error ? originalError.message : ((originalError ?? 'Unknown error') as string);
|
437
|
+
const error = new Error(`Can't load storybook root page: ${message}`);
|
410
438
|
if (originalError instanceof Error) error.stack = originalError.stack;
|
411
439
|
|
412
|
-
logger.error(error);
|
440
|
+
logger().error(error);
|
413
441
|
|
414
442
|
return null;
|
415
443
|
}
|
@@ -420,15 +448,11 @@ export class InternalBrowser {
|
|
420
448
|
gridUrl,
|
421
449
|
viewport,
|
422
450
|
storybookUrl,
|
423
|
-
storybookGlobals,
|
424
|
-
resolveStorybookUrl,
|
425
451
|
}: {
|
426
452
|
browserName: string;
|
427
453
|
gridUrl: string;
|
428
454
|
viewport?: { width: number; height: number };
|
429
455
|
storybookUrl: string;
|
430
|
-
storybookGlobals?: StorybookGlobals;
|
431
|
-
resolveStorybookUrl?: () => Promise<string>;
|
432
456
|
}): Promise<boolean> {
|
433
457
|
const sessionId = (await this.#browser.getSession()).getId();
|
434
458
|
let browserHost = '';
|
@@ -440,23 +464,21 @@ export class InternalBrowser {
|
|
440
464
|
/* noop */
|
441
465
|
}
|
442
466
|
|
443
|
-
|
444
|
-
|
445
|
-
prefix.apply(this.#logger, {
|
467
|
+
prefix.apply(logger(), {
|
446
468
|
format(level) {
|
447
469
|
const levelColor = colors[level.toUpperCase() as keyof typeof colors];
|
448
|
-
return `[${browserName}:${chalk.gray(
|
470
|
+
return `[${browserName}:${chalk.gray(process.pid)}] ${levelColor(level)} => ${chalk.gray(sessionId)}`;
|
449
471
|
},
|
450
472
|
});
|
451
473
|
|
452
|
-
|
474
|
+
logger().debug(`Connected successful with ${chalk.green(browserHost)}`);
|
453
475
|
|
454
476
|
return await runSequence(
|
455
477
|
[
|
456
|
-
() => this.#browser.manage().setTimeouts({ pageLoad:
|
457
|
-
() => this.openStorybookPage(storybookUrl
|
478
|
+
() => this.#browser.manage().setTimeouts({ pageLoad: 60000, script: 60000 }),
|
479
|
+
() => this.openStorybookPage(storybookUrl),
|
458
480
|
() => this.waitForStorybook(),
|
459
|
-
() => this.updateStorybookGlobals(
|
481
|
+
() => this.updateStorybookGlobals(),
|
460
482
|
() => this.resolveCreeveyHost(),
|
461
483
|
() => this.updateBrowserGlobalVariables(),
|
462
484
|
// NOTE: Selenium draws automation toolbar with some delay after webdriver initialization
|
@@ -471,26 +493,16 @@ export class InternalBrowser {
|
|
471
493
|
);
|
472
494
|
}
|
473
495
|
|
474
|
-
private async openStorybookPage(storybookUrl: string
|
496
|
+
private async openStorybookPage(storybookUrl: string): Promise<void> {
|
475
497
|
if (!LOCALHOST_REGEXP.test(storybookUrl)) {
|
476
498
|
return this.#browser.get(appendIframePath(storybookUrl));
|
477
499
|
}
|
478
500
|
|
479
501
|
try {
|
480
|
-
|
481
|
-
|
482
|
-
|
483
|
-
const resolvedUrl = await resolver();
|
484
|
-
|
485
|
-
this.#logger.debug(`Resolver storybook url ${resolvedUrl}`);
|
486
|
-
|
487
|
-
await this.#browser.get(appendIframePath(resolvedUrl));
|
488
|
-
} else {
|
489
|
-
// NOTE: getUrlChecker already calls `browser.get` so we don't need another one
|
490
|
-
await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url), this.#logger);
|
491
|
-
}
|
502
|
+
// NOTE: getUrlChecker already calls `browser.get` so we don't need another one
|
503
|
+
await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
|
492
504
|
} catch (error) {
|
493
|
-
|
505
|
+
logger().error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
|
494
506
|
throw error;
|
495
507
|
}
|
496
508
|
}
|
@@ -498,13 +510,13 @@ export class InternalBrowser {
|
|
498
510
|
private async checkUrl(url: string): Promise<boolean> {
|
499
511
|
try {
|
500
512
|
// NOTE: Before trying a new url, reset the current one
|
501
|
-
|
513
|
+
logger().debug(`Opening ${chalk.magenta('about:blank')} page`);
|
502
514
|
await openUrlAndWaitForPageSource(
|
503
515
|
this.#browser,
|
504
516
|
'about:blank',
|
505
517
|
(source: string) => !source.includes('<body></body>'),
|
506
518
|
);
|
507
|
-
|
519
|
+
logger().debug(`Opening ${chalk.magenta(url)} and checking the page source`);
|
508
520
|
const source = await openUrlAndWaitForPageSource(
|
509
521
|
this.#browser,
|
510
522
|
url,
|
@@ -516,7 +528,7 @@ export class InternalBrowser {
|
|
516
528
|
// because other add significant delay and some of them don't work in earlier chrome versions
|
517
529
|
// Browsers always load page successful even it's failed
|
518
530
|
// So we just check `root` element
|
519
|
-
|
531
|
+
logger().debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
|
520
532
|
return source.includes(`id="${storybookRootID}"`);
|
521
533
|
} catch {
|
522
534
|
return false;
|
@@ -524,7 +536,7 @@ export class InternalBrowser {
|
|
524
536
|
}
|
525
537
|
|
526
538
|
private async waitForStorybook(): Promise<void> {
|
527
|
-
|
539
|
+
logger().debug('Waiting for Storybook to initiate');
|
528
540
|
|
529
541
|
const isTimeout = await Promise.race([
|
530
542
|
new Promise<boolean>((resolve) => {
|
@@ -535,35 +547,27 @@ export class InternalBrowser {
|
|
535
547
|
(async () => {
|
536
548
|
let wait = true;
|
537
549
|
do {
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
if (typeof window.__STORYBOOK_ADDONS_CHANNEL__ == 'undefined') return true;
|
545
|
-
if (window.__STORYBOOK_ADDONS_CHANNEL__.last(SET_GLOBALS) == undefined) return true;
|
546
|
-
return false;
|
547
|
-
}, StorybookEvents.SET_GLOBALS);
|
548
|
-
} catch (e: unknown) {
|
549
|
-
this.#logger.debug('An error has been caught during the script:', e);
|
550
|
-
}
|
550
|
+
// TODO Research a different way to ensure storybook is initiated
|
551
|
+
wait = await this.#browser.executeScript<boolean>(function (SET_GLOBALS: string): boolean {
|
552
|
+
if (typeof window.__STORYBOOK_ADDONS_CHANNEL__ == 'undefined') return true;
|
553
|
+
if (window.__STORYBOOK_ADDONS_CHANNEL__.last(SET_GLOBALS) == undefined) return true;
|
554
|
+
return false;
|
555
|
+
}, StorybookEvents.SET_GLOBALS);
|
551
556
|
} while (wait);
|
552
557
|
return false;
|
553
558
|
})(),
|
554
559
|
]);
|
555
560
|
|
556
|
-
|
557
|
-
if (isTimeout) throw new Error('Failed to wait `setStories` event');
|
561
|
+
if (isTimeout) throw new Error('Failed to wait Storybook init');
|
558
562
|
}
|
559
563
|
|
560
|
-
private async updateStorybookGlobals(
|
561
|
-
if (!
|
564
|
+
private async updateStorybookGlobals(): Promise<void> {
|
565
|
+
if (!this.#storybookGlobals) return;
|
562
566
|
|
563
|
-
|
567
|
+
logger().debug('Applying storybook globals');
|
564
568
|
await this.#browser.executeScript(function (globals: StorybookGlobals) {
|
565
569
|
window.__CREEVEY_UPDATE_GLOBALS__(globals);
|
566
|
-
},
|
570
|
+
}, this.#storybookGlobals);
|
567
571
|
}
|
568
572
|
|
569
573
|
private async resolveCreeveyHost(): Promise<void> {
|
@@ -607,6 +611,7 @@ export class InternalBrowser {
|
|
607
611
|
private async updateBrowserGlobalVariables() {
|
608
612
|
await this.#browser.executeScript(
|
609
613
|
function (workerId: number, creeveyHost: string, creeveyPort: number) {
|
614
|
+
window.__CREEVEY_ENV__ = true;
|
610
615
|
window.__CREEVEY_WORKER_ID__ = workerId;
|
611
616
|
window.__CREEVEY_SERVER_HOST__ = creeveyHost;
|
612
617
|
window.__CREEVEY_SERVER_PORT__ = creeveyPort;
|
@@ -630,7 +635,7 @@ export class InternalBrowser {
|
|
630
635
|
},
|
631
636
|
);
|
632
637
|
|
633
|
-
|
638
|
+
logger().debug(`Resizing viewport from ${innerWidth}x${innerHeight} to ${viewport.width}x${viewport.height}`);
|
634
639
|
|
635
640
|
const dWidth = windowRect.width - innerWidth;
|
636
641
|
const dHeight = windowRect.height - innerHeight;
|
@@ -644,7 +649,7 @@ export class InternalBrowser {
|
|
644
649
|
}
|
645
650
|
|
646
651
|
private async resetMousePosition(): Promise<void> {
|
647
|
-
|
652
|
+
logger().debug('Resetting mouse position to the top-left corner');
|
648
653
|
const browserName = (await this.#browser.getCapabilities()).getBrowserName();
|
649
654
|
const [browserVersion] =
|
650
655
|
(await this.#browser.getCapabilities()).getBrowserVersion()?.split('.') ??
|
@@ -672,8 +677,9 @@ export class InternalBrowser {
|
|
672
677
|
y: Math.ceil((-1 * height) / 2) - top,
|
673
678
|
})
|
674
679
|
.perform();
|
675
|
-
} else if (browserName == 'firefox'
|
680
|
+
} else if (browserName == 'firefox') {
|
676
681
|
// NOTE Firefox for some reason moving by 0 x 0 move cursor in bottom left corner :sad:
|
682
|
+
// NOTE In recent versions (eg 128.0) moving by 0 x 0 doesn't work at all
|
677
683
|
await this.#browser.actions().move({ origin: Origin.VIEWPORT, x: 0, y: 1 }).perform();
|
678
684
|
} else {
|
679
685
|
// NOTE IE don't emit move events until force window focus or connect by RDP on virtual machine
|
@@ -685,7 +691,7 @@ export class InternalBrowser {
|
|
685
691
|
const ignoreSelectors = Array.prototype.concat(ignoreElements).filter(Boolean);
|
686
692
|
if (!ignoreSelectors.length) return null;
|
687
693
|
|
688
|
-
|
694
|
+
logger().debug('Hiding ignored elements before capturing');
|
689
695
|
|
690
696
|
return await this.#browser.executeScript(function (ignoreSelectors: string[]) {
|
691
697
|
return window.__CREEVEY_INSERT_IGNORE_STYLES__(ignoreSelectors);
|
@@ -766,7 +772,7 @@ export class InternalBrowser {
|
|
766
772
|
|
767
773
|
private async removeIgnoreStyles(ignoreStyles: WebElement | null): Promise<void> {
|
768
774
|
if (ignoreStyles) {
|
769
|
-
|
775
|
+
logger().debug('Revert hiding ignored elements');
|
770
776
|
await this.#browser.executeScript(function (ignoreStyles: HTMLStyleElement) {
|
771
777
|
window.__CREEVEY_REMOVE_IGNORE_STYLES__(ignoreStyles);
|
772
778
|
}, ignoreStyles);
|
@@ -804,9 +810,18 @@ export class InternalBrowser {
|
|
804
810
|
private keepAlive(): void {
|
805
811
|
this.#keepAliveInterval = setInterval(() => {
|
806
812
|
// NOTE Simple way to keep session alive
|
807
|
-
void this.#browser
|
808
|
-
|
809
|
-
|
813
|
+
void this.#browser
|
814
|
+
.getCurrentUrl()
|
815
|
+
.then((url) => {
|
816
|
+
logger().debug('current url', chalk.magenta(url));
|
817
|
+
})
|
818
|
+
.catch((error: unknown) => {
|
819
|
+
logger().error(error);
|
820
|
+
emitWorkerMessage({
|
821
|
+
type: 'error',
|
822
|
+
payload: { subtype: 'browser', error: 'Failed to ping browser' },
|
823
|
+
});
|
824
|
+
});
|
810
825
|
}, 10 * 1000);
|
811
826
|
}
|
812
827
|
}
|
@@ -2,9 +2,9 @@ import path from 'path';
|
|
2
2
|
import assert from 'assert';
|
3
3
|
import { lstatSync, existsSync } from 'fs';
|
4
4
|
import { mkdir, writeFile, copyFile } from 'fs/promises';
|
5
|
-
import
|
5
|
+
import { exec, chmod } from 'shelljs';
|
6
6
|
import { Config, BrowserConfigObject } from '../../types.js';
|
7
|
-
import { downloadBinary, getCreeveyCache } from '../utils.js';
|
7
|
+
import { downloadBinary, getCreeveyCache, killTree } from '../utils.js';
|
8
8
|
import { pullImages, runImage } from '../docker.js';
|
9
9
|
import { subscribeOn } from '../messages.js';
|
10
10
|
|
@@ -34,7 +34,7 @@ async function createSelenoidConfig(
|
|
34
34
|
dockerImage = `selenoid/${browserName}:${browserVersion}`,
|
35
35
|
webdriverCommand = [],
|
36
36
|
}) => {
|
37
|
-
|
37
|
+
selenoidConfig[browserName] ??= { default: browserVersion, versions: {} };
|
38
38
|
if (!useDocker && webdriverCommand.length == 0)
|
39
39
|
throw new Error('Please specify "webdriverCommand" browser option with path to browser webdriver');
|
40
40
|
selenoidConfig[browserName].versions[browserVersion] = {
|
@@ -91,12 +91,12 @@ export async function startSelenoidStandalone(config: Config, debug: boolean): P
|
|
91
91
|
|
92
92
|
// TODO Download browser webdrivers
|
93
93
|
try {
|
94
|
-
if (process.platform != 'win32')
|
94
|
+
if (process.platform != 'win32') chmod('+x', binaryPath);
|
95
95
|
} catch {
|
96
96
|
/* noop */
|
97
97
|
}
|
98
98
|
|
99
|
-
const selenoidProcess =
|
99
|
+
const selenoidProcess = exec(`${binaryPath} -conf ./browsers.json -disable-docker`, {
|
100
100
|
async: true,
|
101
101
|
cwd: selenoidConfigDir,
|
102
102
|
});
|
@@ -106,7 +106,9 @@ export async function startSelenoidStandalone(config: Config, debug: boolean): P
|
|
106
106
|
selenoidProcess.stderr?.pipe(process.stderr);
|
107
107
|
}
|
108
108
|
|
109
|
-
subscribeOn('shutdown', () =>
|
109
|
+
subscribeOn('shutdown', () => {
|
110
|
+
if (selenoidProcess.pid) void killTree(selenoidProcess.pid);
|
111
|
+
});
|
110
112
|
}
|
111
113
|
|
112
114
|
export async function startSelenoidContainer(config: Config, debug: boolean): Promise<string> {
|
@@ -1,10 +1,11 @@
|
|
1
1
|
/// <reference types="../../../types/selenium-context" />
|
2
|
-
import { Args } from '@storybook/
|
2
|
+
import type { Args } from '@storybook/types';
|
3
3
|
import { Config, StorybookGlobals, StoryInput, StoriesRaw, Options, ServerTest } from '../../types.js';
|
4
4
|
import { subscribeOn } from '../messages.js';
|
5
5
|
import { CreeveyWebdriverBase } from '../webdriver.js';
|
6
6
|
import type { InternalBrowser } from './internal.js';
|
7
7
|
import { logger } from '../logger.js';
|
8
|
+
import { removeWorkerContainer } from '../worker/context.js';
|
8
9
|
|
9
10
|
declare global {
|
10
11
|
interface Window {
|
@@ -31,7 +32,9 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
31
32
|
this.#options = options;
|
32
33
|
|
33
34
|
subscribeOn('shutdown', () => {
|
34
|
-
void this.#browser?.closeBrowser().finally(() =>
|
35
|
+
void this.#browser?.closeBrowser().finally(() => {
|
36
|
+
void removeWorkerContainer().finally(() => process.exit());
|
37
|
+
});
|
35
38
|
this.#browser = null;
|
36
39
|
});
|
37
40
|
}
|
@@ -42,7 +45,6 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
42
45
|
|
43
46
|
getSessionId(): Promise<string> {
|
44
47
|
if (!this.#browser) {
|
45
|
-
// TODO Describe the error
|
46
48
|
throw new Error('Browser is not initialized');
|
47
49
|
}
|
48
50
|
|
@@ -63,7 +65,7 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
63
65
|
try {
|
64
66
|
return await import('./internal.js');
|
65
67
|
} catch (error) {
|
66
|
-
logger.error(error);
|
68
|
+
logger().error(error);
|
67
69
|
return null;
|
68
70
|
}
|
69
71
|
})();
|
@@ -89,7 +91,6 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
89
91
|
|
90
92
|
async loadStoriesFromBrowser(): Promise<StoriesRaw> {
|
91
93
|
if (!this.#browser) {
|
92
|
-
// TODO Describe the error
|
93
94
|
throw new Error('Browser is not initialized');
|
94
95
|
}
|
95
96
|
|
@@ -98,7 +99,6 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
98
99
|
|
99
100
|
afterTest(test: ServerTest): Promise<void> {
|
100
101
|
if (!this.#browser) {
|
101
|
-
// TODO Describe the error
|
102
102
|
throw new Error('Browser is not initialized');
|
103
103
|
}
|
104
104
|
|
@@ -110,7 +110,6 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
110
110
|
ignoreElements?: string | string[] | null,
|
111
111
|
): Promise<Buffer> {
|
112
112
|
if (!this.#browser) {
|
113
|
-
// TODO Describe the error
|
114
113
|
throw new Error('Browser is not initialized');
|
115
114
|
}
|
116
115
|
|
@@ -119,7 +118,6 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
119
118
|
|
120
119
|
protected waitForComplete(callback: (isCompleted: boolean) => void): void {
|
121
120
|
if (!this.#browser) {
|
122
|
-
// TODO Describe the error
|
123
121
|
throw new Error('Browser is not initialized');
|
124
122
|
}
|
125
123
|
|
@@ -128,7 +126,6 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
128
126
|
|
129
127
|
protected async selectStory(id: string, waitForReady?: boolean): Promise<boolean> {
|
130
128
|
if (!this.#browser) {
|
131
|
-
// TODO Describe the error
|
132
129
|
throw new Error('Browser is not initialized');
|
133
130
|
}
|
134
131
|
|
@@ -137,7 +134,6 @@ export class SeleniumWebdriver extends CreeveyWebdriverBase {
|
|
137
134
|
|
138
135
|
protected async updateStoryArgs(story: StoryInput, updatedArgs: Args): Promise<void> {
|
139
136
|
if (!this.#browser) {
|
140
|
-
// TODO Describe the error
|
141
137
|
throw new Error('Browser is not initialized');
|
142
138
|
}
|
143
139
|
|
package/src/server/telemetry.ts
CHANGED
@@ -181,8 +181,8 @@ export async function sendScreenshotsCount(
|
|
181
181
|
const testsMeta = { runId: uuid, tests };
|
182
182
|
|
183
183
|
const fullPathname = buildPathname('tests', testsMeta);
|
184
|
-
// NOTE: Keep request path shorter than
|
185
|
-
const chunksCount = Math.ceil(fullPathname.length /
|
184
|
+
// NOTE: Keep request path shorter than 24k symbols
|
185
|
+
const chunksCount = Math.ceil(fullPathname.length / 24_000);
|
186
186
|
let chunks: string[] = [];
|
187
187
|
if (chunksCount > 1) {
|
188
188
|
const testsString = JSON.stringify(tests);
|