creevey 0.10.0-beta.9 → 0.10.0-rc.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.
- package/AUTHORS +2 -0
- package/CHANGELOG.md +281 -0
- package/README.md +19 -41
- package/dist/client/addon/components/Addon.js +18 -8
- 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/TestSelect.js +2 -2
- package/dist/client/addon/components/TestSelect.js.map +1 -1
- package/dist/client/addon/components/Tools.js +19 -9
- package/dist/client/addon/components/Tools.js.map +1 -1
- package/dist/client/addon/controller.d.ts +1 -1
- package/dist/client/addon/controller.js +3 -3
- package/dist/client/addon/controller.js.map +1 -1
- package/dist/client/addon/decorator.d.ts +1 -1
- package/dist/client/addon/makeDecorator.d.ts +9 -0
- package/dist/client/addon/makeDecorator.js +48 -0
- package/dist/client/addon/makeDecorator.js.map +1 -0
- package/dist/client/addon/manager.js +38 -39
- package/dist/client/addon/manager.js.map +1 -1
- package/dist/client/addon/preset.d.ts +0 -1
- package/dist/client/addon/preset.js +3 -2
- package/dist/client/addon/preset.js.map +1 -1
- package/dist/client/addon/preview.d.ts +1 -1
- package/dist/client/addon/withCreevey.d.ts +5 -3
- package/dist/client/addon/withCreevey.js +5 -20
- package/dist/client/addon/withCreevey.js.map +1 -1
- package/dist/client/shared/components/ImagesView/BlendView.d.ts +2 -2
- package/dist/client/shared/components/ImagesView/BlendView.js +18 -8
- package/dist/client/shared/components/ImagesView/BlendView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/ImagesView.js +1 -1
- package/dist/client/shared/components/ImagesView/ImagesView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SideBySideView.d.ts +2 -2
- package/dist/client/shared/components/ImagesView/SideBySideView.js +19 -9
- package/dist/client/shared/components/ImagesView/SideBySideView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SlideView.d.ts +2 -2
- package/dist/client/shared/components/ImagesView/SlideView.js +19 -9
- package/dist/client/shared/components/ImagesView/SlideView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SwapView.d.ts +2 -2
- package/dist/client/shared/components/ImagesView/SwapView.js +19 -9
- package/dist/client/shared/components/ImagesView/SwapView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/common.d.ts +1 -1
- package/dist/client/shared/components/PageFooter/PageFooter.js +1 -1
- package/dist/client/shared/components/PageFooter/PageFooter.js.map +1 -1
- package/dist/client/shared/components/PageFooter/Paging.js +1 -1
- package/dist/client/shared/components/PageFooter/Paging.js.map +1 -1
- package/dist/client/shared/components/PageHeader/ImagePreview.d.ts +2 -2
- package/dist/client/shared/components/PageHeader/ImagePreview.js +1 -1
- package/dist/client/shared/components/PageHeader/ImagePreview.js.map +1 -1
- package/dist/client/shared/components/PageHeader/PageHeader.js +34 -13
- package/dist/client/shared/components/PageHeader/PageHeader.js.map +1 -1
- package/dist/client/shared/components/ResultsPage.d.ts +2 -2
- package/dist/client/shared/components/ResultsPage.js +22 -10
- package/dist/client/shared/components/ResultsPage.js.map +1 -1
- package/dist/client/shared/creeveyClientApi.js +18 -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.d.ts +1 -0
- package/dist/client/web/CreeveyApp.js +22 -9
- package/dist/client/web/CreeveyApp.js.map +1 -1
- package/dist/client/web/CreeveyContext.d.ts +1 -0
- package/dist/client/web/CreeveyContext.js +18 -7
- package/dist/client/web/CreeveyContext.js.map +1 -1
- package/dist/client/web/CreeveyLoader.d.ts +1 -1
- package/dist/client/web/CreeveyLoader.js +3 -3
- package/dist/client/web/CreeveyLoader.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/Checkbox.d.ts +4 -4
- package/dist/client/web/CreeveyView/SideBar/Checkbox.js +36 -6
- package/dist/client/web/CreeveyView/SideBar/Checkbox.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/Search.js +18 -8
- package/dist/client/web/CreeveyView/SideBar/Search.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBar.js +26 -12
- package/dist/client/web/CreeveyView/SideBar/SideBar.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js +28 -17
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js +32 -12
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.d.ts +6 -6
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js +20 -11
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestLink.js +20 -11
- package/dist/client/web/CreeveyView/SideBar/TestLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.d.ts +2 -2
- package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.js +2 -2
- package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.d.ts +2 -2
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.js +3 -2
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/Toggle.js +1 -1
- package/dist/client/web/CreeveyView/SideBar/Toggle.js.map +1 -1
- package/dist/client/web/KeyboardEventsContext.js +17 -7
- package/dist/client/web/KeyboardEventsContext.js.map +1 -1
- package/dist/client/web/assets/index-CtSq3IhG.js +518 -0
- package/dist/client/web/index.html +1 -1
- package/dist/client/web/index.js +26 -11
- 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.d.ts +1 -1
- package/dist/creevey.js +122 -41
- package/dist/creevey.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/playwright/generator.d.ts +25 -0
- package/dist/playwright/generator.js +243 -0
- package/dist/playwright/generator.js.map +1 -0
- package/dist/playwright/helpers.d.ts +2 -0
- package/dist/playwright/helpers.js +29 -0
- package/dist/playwright/helpers.js.map +1 -0
- package/dist/playwright/reporter.d.ts +83 -0
- package/dist/playwright/reporter.js +334 -0
- package/dist/playwright/reporter.js.map +1 -0
- package/dist/playwright/setup.d.ts +3 -0
- package/dist/playwright/setup.js +72 -0
- package/dist/playwright/setup.js.map +1 -0
- package/dist/playwright.d.ts +1 -0
- package/dist/playwright.js +3 -1
- package/dist/playwright.js.map +1 -1
- package/dist/server/compare.d.ts +18 -0
- package/dist/server/compare.js +182 -0
- package/dist/server/compare.js.map +1 -0
- package/dist/server/config.d.ts +3 -3
- package/dist/server/config.js +75 -8
- 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 +54 -32
- package/dist/server/docker.js.map +1 -1
- package/dist/server/index.d.ts +2 -2
- package/dist/server/index.js +161 -64
- package/dist/server/index.js.map +1 -1
- package/dist/server/master/api.d.ts +11 -6
- package/dist/server/master/api.js +88 -25
- package/dist/server/master/api.js.map +1 -1
- package/dist/server/master/handlers/capture-handler.d.ts +5 -0
- package/dist/server/master/handlers/capture-handler.js +25 -0
- package/dist/server/master/handlers/capture-handler.js.map +1 -0
- package/dist/server/master/handlers/index.d.ts +4 -0
- package/dist/server/master/handlers/index.js +21 -0
- package/dist/server/master/handlers/index.js.map +1 -0
- package/dist/server/master/handlers/ping-handler.d.ts +2 -0
- package/dist/server/master/handlers/ping-handler.js +8 -0
- package/dist/server/master/handlers/ping-handler.js.map +1 -0
- package/dist/server/master/handlers/static-handler.d.ts +1 -0
- package/dist/server/master/handlers/static-handler.js +20 -0
- package/dist/server/master/handlers/static-handler.js.map +1 -0
- package/dist/server/master/handlers/stories-handler.d.ts +4 -0
- package/dist/server/master/handlers/stories-handler.js +24 -0
- package/dist/server/master/handlers/stories-handler.js.map +1 -0
- package/dist/server/master/master.js +7 -24
- package/dist/server/master/master.js.map +1 -1
- package/dist/server/master/pool.d.ts +1 -0
- package/dist/server/master/pool.js +5 -3
- package/dist/server/master/pool.js.map +1 -1
- package/dist/server/master/queue.d.ts +1 -1
- package/dist/server/master/queue.js +14 -6
- package/dist/server/master/queue.js.map +1 -1
- package/dist/server/master/runner.d.ts +6 -6
- package/dist/server/master/runner.js +98 -130
- package/dist/server/master/runner.js.map +1 -1
- package/dist/server/master/server.d.ts +1 -1
- package/dist/server/master/server.js +193 -88
- package/dist/server/master/server.js.map +1 -1
- package/dist/server/master/start.d.ts +1 -2
- package/dist/server/master/start.js +13 -29
- package/dist/server/master/start.js.map +1 -1
- package/dist/server/master/testsManager.d.ts +81 -0
- package/dist/server/master/testsManager.js +282 -0
- package/dist/server/master/testsManager.js.map +1 -0
- package/dist/server/playwright/docker-file.d.ts +1 -1
- package/dist/server/playwright/docker-file.js +17 -8
- 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 +7 -7
- package/dist/server/playwright/internal.js +137 -79
- package/dist/server/playwright/internal.js.map +1 -1
- package/dist/server/playwright/webdriver.d.ts +3 -3
- package/dist/server/playwright/webdriver.js +0 -6
- package/dist/server/playwright/webdriver.js.map +1 -1
- package/dist/server/providers/browser.js +4 -3
- package/dist/server/providers/browser.js.map +1 -1
- package/dist/server/providers/hybrid.js +2 -2
- package/dist/server/providers/hybrid.js.map +1 -1
- package/dist/server/report.d.ts +10 -0
- package/dist/server/report.js +45 -0
- package/dist/server/report.js.map +1 -0
- 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 +167 -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 -3
- package/dist/server/selenium/internal.js +48 -34
- package/dist/server/selenium/internal.js.map +1 -1
- package/dist/server/selenium/selenoid.js +12 -6
- package/dist/server/selenium/selenoid.js.map +1 -1
- package/dist/server/selenium/webdriver.d.ts +3 -3
- package/dist/server/selenium/webdriver.js +4 -8
- package/dist/server/selenium/webdriver.js.map +1 -1
- package/dist/server/shutdown.d.ts +1 -0
- package/dist/server/shutdown.js +23 -0
- package/dist/server/shutdown.js.map +1 -0
- package/dist/server/stories.d.ts +0 -1
- package/dist/server/stories.js +0 -12
- package/dist/server/stories.js.map +1 -1
- package/dist/server/telemetry.js +3 -3
- package/dist/server/telemetry.js.map +1 -1
- package/dist/server/testsFiles/parser.js +45 -5
- package/dist/server/testsFiles/parser.js.map +1 -1
- package/dist/server/utils.d.ts +23 -0
- package/dist/server/utils.js +113 -13
- package/dist/server/utils.js.map +1 -1
- package/dist/server/webdriver.d.ts +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 +8 -12
- package/dist/server/worker/match-image.js +11 -178
- package/dist/server/worker/match-image.js.map +1 -1
- package/dist/server/worker/start.d.ts +2 -2
- package/dist/server/worker/start.js +27 -63
- package/dist/server/worker/start.js.map +1 -1
- package/dist/shared/index.d.ts +1 -1
- package/dist/shared/index.js +9 -7
- package/dist/shared/index.js.map +1 -1
- package/dist/types.d.ts +84 -43
- package/dist/types.js +65 -1
- package/dist/types.js.map +1 -1
- package/docs/cli.md +80 -0
- package/docs/config.md +179 -165
- package/docs/examples/playwright-reporer/playwright.config.ts +37 -0
- package/docs/migration-0.9-to-0.10.md +144 -0
- package/docs/playwright-reporter.md +357 -0
- package/docs/storybook.md +60 -0
- package/docs/tests.md +50 -45
- package/package.json +78 -83
- package/playwright.config.mts +46 -0
- package/src/client/addon/components/Addon.tsx +1 -1
- package/src/client/addon/components/Panel.tsx +2 -2
- package/src/client/addon/components/TestSelect.tsx +2 -2
- package/src/client/addon/components/Tools.tsx +2 -2
- package/src/client/addon/controller.ts +4 -4
- package/src/client/addon/makeDecorator.ts +69 -0
- package/src/client/addon/manager.ts +38 -37
- package/src/client/addon/preset.ts +2 -1
- package/src/client/addon/withCreevey.ts +10 -18
- package/src/client/shared/components/ImagesView/BlendView.tsx +1 -1
- package/src/client/shared/components/ImagesView/ImagesView.tsx +1 -1
- package/src/client/shared/components/ImagesView/SideBySideView.tsx +2 -2
- package/src/client/shared/components/ImagesView/SlideView.tsx +2 -2
- package/src/client/shared/components/ImagesView/SwapView.tsx +2 -2
- package/src/client/shared/components/ImagesView/common.ts +1 -1
- package/src/client/shared/components/PageFooter/PageFooter.tsx +1 -1
- package/src/client/shared/components/PageFooter/Paging.tsx +1 -1
- package/src/client/shared/components/PageHeader/ImagePreview.tsx +1 -1
- package/src/client/shared/components/PageHeader/PageHeader.tsx +23 -7
- package/src/client/shared/components/ResultsPage.tsx +6 -4
- package/src/client/shared/creeveyClientApi.ts +19 -1
- package/src/client/shared/helpers.ts +4 -24
- package/src/client/web/CreeveyApp.tsx +5 -2
- package/src/client/web/CreeveyContext.tsx +2 -0
- package/src/client/web/CreeveyLoader.tsx +2 -2
- package/src/client/web/CreeveyView/SideBar/Checkbox.tsx +3 -3
- package/src/client/web/CreeveyView/SideBar/Search.tsx +1 -1
- package/src/client/web/CreeveyView/SideBar/SideBar.tsx +11 -6
- package/src/client/web/CreeveyView/SideBar/SideBarFooter.tsx +21 -19
- package/src/client/web/CreeveyView/SideBar/SideBarHeader.tsx +20 -5
- package/src/client/web/CreeveyView/SideBar/SuiteLink.tsx +10 -8
- package/src/client/web/CreeveyView/SideBar/TestLink.tsx +9 -7
- package/src/client/web/CreeveyView/SideBar/TestStatusIcon.tsx +2 -2
- package/src/client/web/CreeveyView/SideBar/TestsStatus.tsx +3 -2
- package/src/client/web/CreeveyView/SideBar/Toggle.tsx +1 -1
- package/src/client/web/index.tsx +10 -5
- package/src/client/web/themes.ts +24 -0
- package/src/creevey.ts +92 -38
- package/src/playwright/generator.ts +322 -0
- package/src/playwright/helpers.ts +31 -0
- package/src/playwright/reporter.ts +381 -0
- package/src/playwright/setup.ts +84 -0
- package/src/playwright.ts +1 -0
- package/src/server/compare.ts +260 -0
- package/src/server/config.ts +52 -9
- package/src/server/connection.ts +26 -0
- package/src/server/docker.ts +62 -34
- package/src/server/index.ts +161 -79
- package/src/server/master/api.ts +94 -28
- package/src/server/master/handlers/capture-handler.ts +20 -0
- package/src/server/master/handlers/index.ts +4 -0
- package/src/server/master/handlers/ping-handler.ts +6 -0
- package/src/server/master/handlers/static-handler.ts +16 -0
- package/src/server/master/handlers/stories-handler.ts +20 -0
- package/src/server/master/master.ts +10 -27
- package/src/server/master/pool.ts +7 -3
- package/src/server/master/queue.ts +21 -7
- package/src/server/master/runner.ts +123 -134
- package/src/server/master/server.ts +214 -101
- package/src/server/master/start.ts +19 -41
- package/src/server/master/testsManager.ts +316 -0
- package/src/server/playwright/docker-file.ts +20 -8
- package/src/server/playwright/docker.ts +16 -3
- package/src/server/playwright/index-source.mjs +16 -0
- package/src/server/playwright/internal.ts +169 -96
- package/src/server/playwright/webdriver.ts +4 -10
- package/src/server/providers/browser.ts +4 -3
- package/src/server/providers/hybrid.ts +2 -3
- package/src/server/report.ts +51 -0
- package/src/server/reporters/creevey.ts +71 -0
- package/src/server/reporters/index.ts +11 -0
- package/src/server/reporters/junit.ts +207 -0
- package/src/server/reporters/teamcity.ts +74 -0
- package/src/server/selenium/internal.ts +62 -45
- package/src/server/selenium/selenoid.ts +13 -6
- package/src/server/selenium/webdriver.ts +8 -12
- package/src/server/shutdown.ts +19 -0
- package/src/server/stories.ts +1 -12
- package/src/server/telemetry.ts +3 -3
- package/src/server/testsFiles/parser.ts +52 -4
- package/src/server/utils.ts +123 -14
- package/src/server/webdriver.ts +1 -1
- package/src/server/worker/context.ts +14 -0
- package/src/server/worker/match-image.ts +16 -248
- package/src/server/worker/start.ts +32 -75
- package/src/shared/index.ts +10 -8
- package/src/types.ts +91 -58
- package/types/global.d.ts +1 -0
- package/dist/client/web/assets/index-BE9CL5_G.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/dist/server/update.d.ts +0 -2
- package/dist/server/update.js +0 -53
- package/dist/server/update.js.map +0 -1
- package/src/server/reporter.ts +0 -139
- package/src/server/update.ts +0 -74
@@ -0,0 +1,71 @@
|
|
1
|
+
import chalk from 'chalk';
|
2
|
+
import Logger from 'loglevel';
|
3
|
+
import prefix from 'loglevel-plugin-prefix';
|
4
|
+
import { FakeTest, isImageError, TEST_EVENTS } from '../../types.js';
|
5
|
+
import EventEmitter from 'events';
|
6
|
+
|
7
|
+
const testLevels: Record<string, string> = {
|
8
|
+
INFO: chalk.green('PASS'),
|
9
|
+
WARN: chalk.yellow('START'),
|
10
|
+
ERROR: chalk.red('FAIL'),
|
11
|
+
};
|
12
|
+
|
13
|
+
export class CreeveyReporter {
|
14
|
+
private logger: Logger.Logger | null = null;
|
15
|
+
// TODO Output in better way, like vitest, maybe
|
16
|
+
constructor(runner: EventEmitter) {
|
17
|
+
runner.on(TEST_EVENTS.TEST_BEGIN, (test: FakeTest) => {
|
18
|
+
this.getLogger(test.creevey).warn(chalk.cyan(test.fullTitle()));
|
19
|
+
});
|
20
|
+
runner.on(TEST_EVENTS.TEST_PASS, (test: FakeTest) => {
|
21
|
+
this.getLogger(test.creevey).info(chalk.cyan(test.fullTitle()), chalk.gray(`(${test.duration} ms)`));
|
22
|
+
});
|
23
|
+
runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest, error) => {
|
24
|
+
this.getLogger(test.creevey).error(
|
25
|
+
chalk.cyan(test.fullTitle()),
|
26
|
+
chalk.gray(`(${test.duration} ms)`),
|
27
|
+
'\n ',
|
28
|
+
this.getErrors(
|
29
|
+
error,
|
30
|
+
(error, imageName) => `${chalk.bold(imageName ?? test.creevey.browserName)}:${error}`,
|
31
|
+
(error) => error.stack ?? error.message,
|
32
|
+
).join('\n '),
|
33
|
+
);
|
34
|
+
});
|
35
|
+
}
|
36
|
+
|
37
|
+
private getLogger(options: { sessionId: string; browserName: string }) {
|
38
|
+
if (this.logger) return this.logger;
|
39
|
+
const { sessionId, browserName } = options;
|
40
|
+
const testLogger = Logger.getLogger(sessionId);
|
41
|
+
|
42
|
+
this.logger = prefix.apply(testLogger, {
|
43
|
+
format(level) {
|
44
|
+
return `[${browserName}:${chalk.gray(process.pid)}] ${testLevels[level]} => ${chalk.gray(sessionId)}`;
|
45
|
+
},
|
46
|
+
});
|
47
|
+
|
48
|
+
return this.logger;
|
49
|
+
}
|
50
|
+
|
51
|
+
private getErrors(
|
52
|
+
error: unknown,
|
53
|
+
imageErrorToString: (error: string, imageName?: string) => string,
|
54
|
+
errorToString: (error: Error) => string,
|
55
|
+
): string[] {
|
56
|
+
const errors = [];
|
57
|
+
if (!(error instanceof Error)) {
|
58
|
+
errors.push(error as string);
|
59
|
+
} else if (!isImageError(error)) {
|
60
|
+
errors.push(errorToString(error));
|
61
|
+
} else if (typeof error.images == 'string') {
|
62
|
+
errors.push(imageErrorToString(error.images));
|
63
|
+
} else {
|
64
|
+
const imageErrors = error.images ?? {};
|
65
|
+
Object.keys(imageErrors).forEach((imageName) => {
|
66
|
+
errors.push(imageErrorToString(imageErrors[imageName] ?? '', imageName));
|
67
|
+
});
|
68
|
+
}
|
69
|
+
return errors;
|
70
|
+
}
|
71
|
+
}
|
@@ -0,0 +1,11 @@
|
|
1
|
+
import { BaseReporter } from '../../types.js';
|
2
|
+
import { CreeveyReporter } from './creevey.js';
|
3
|
+
import { JUnitReporter } from './junit.js';
|
4
|
+
import { TeamcityReporter } from './teamcity.js';
|
5
|
+
|
6
|
+
export function getReporter(reporter: BaseReporter | 'creevey' | 'teamcity' | 'junit'): BaseReporter {
|
7
|
+
if (reporter === 'creevey') return CreeveyReporter;
|
8
|
+
if (reporter === 'teamcity') return TeamcityReporter;
|
9
|
+
if (reporter === 'junit') return JUnitReporter;
|
10
|
+
return reporter;
|
11
|
+
}
|
@@ -0,0 +1,207 @@
|
|
1
|
+
import EventEmitter from 'events';
|
2
|
+
import { dirname, resolve } from 'path';
|
3
|
+
import { closeSync, existsSync, mkdirSync, openSync, writeFileSync } from 'fs';
|
4
|
+
import { TEST_EVENTS, FakeTest } from '../../types.js';
|
5
|
+
import { logger } from '../logger.js';
|
6
|
+
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
8
|
+
class IndentedLogger<T = any> {
|
9
|
+
private currentIndent = '';
|
10
|
+
|
11
|
+
constructor(private baseLog: (text: string) => T) {}
|
12
|
+
|
13
|
+
indent(): void {
|
14
|
+
this.currentIndent += ' ';
|
15
|
+
}
|
16
|
+
|
17
|
+
unindent(): void {
|
18
|
+
this.currentIndent = this.currentIndent.substring(0, this.currentIndent.length - 4);
|
19
|
+
}
|
20
|
+
|
21
|
+
log(text: string): T {
|
22
|
+
return this.baseLog(this.currentIndent + text);
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
// NOTE: This is a reworked copy of the JUnitReporter class from Vitest.
|
27
|
+
export class JUnitReporter {
|
28
|
+
private reportFile: string;
|
29
|
+
private fileFd?: number;
|
30
|
+
private logger: IndentedLogger<void>;
|
31
|
+
private suites: Record<string, FakeTest[]> = {};
|
32
|
+
// TODO classnameTemplate
|
33
|
+
// TODO Output console logs
|
34
|
+
// TODO Output attachments
|
35
|
+
constructor(runner: EventEmitter, options: { reportDir: string; reporterOptions: { outputFile?: string } }) {
|
36
|
+
const { reportDir, reporterOptions } = options;
|
37
|
+
|
38
|
+
this.reportFile = reporterOptions.outputFile ?? resolve(reportDir, 'junit.xml');
|
39
|
+
|
40
|
+
this.logger = new IndentedLogger((text) => {
|
41
|
+
this.fileFd ??= openSync(this.reportFile, 'w+');
|
42
|
+
|
43
|
+
writeFileSync(this.fileFd, `${text}\n`);
|
44
|
+
});
|
45
|
+
|
46
|
+
runner.on(TEST_EVENTS.RUN_BEGIN, () => {
|
47
|
+
this.suites = {};
|
48
|
+
|
49
|
+
const outputDirectory = dirname(this.reportFile);
|
50
|
+
if (!existsSync(outputDirectory)) {
|
51
|
+
mkdirSync(outputDirectory, { recursive: true });
|
52
|
+
}
|
53
|
+
|
54
|
+
this.fileFd = openSync(this.reportFile, 'w+');
|
55
|
+
});
|
56
|
+
runner.on(TEST_EVENTS.TEST_PASS, (test: FakeTest) => {
|
57
|
+
const suite = (this.suites[test.parent.title] ??= []);
|
58
|
+
suite.push(test);
|
59
|
+
});
|
60
|
+
runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest) => {
|
61
|
+
const suite = (this.suites[test.parent.title] ??= []);
|
62
|
+
suite.push(test);
|
63
|
+
});
|
64
|
+
runner.on(TEST_EVENTS.RUN_END, () => {
|
65
|
+
this.onFinished();
|
66
|
+
});
|
67
|
+
}
|
68
|
+
|
69
|
+
private writeElement(name: string, attrs: Record<string, string | number | undefined>, children?: () => void): void {
|
70
|
+
const pairs: string[] = [];
|
71
|
+
for (const key in attrs) {
|
72
|
+
const attr = attrs[key];
|
73
|
+
if (attr === undefined) {
|
74
|
+
continue;
|
75
|
+
}
|
76
|
+
|
77
|
+
pairs.push(`${key}="${escapeXML(attr)}"`);
|
78
|
+
}
|
79
|
+
|
80
|
+
this.logger.log(`<${name}${pairs.length ? ` ${pairs.join(' ')}` : ''}>`);
|
81
|
+
this.logger.indent();
|
82
|
+
children?.call(this);
|
83
|
+
this.logger.unindent();
|
84
|
+
|
85
|
+
this.logger.log(`</${name}>`);
|
86
|
+
}
|
87
|
+
|
88
|
+
private writeTasks(tests: FakeTest[]): void {
|
89
|
+
for (const test of tests) {
|
90
|
+
const classname = test.parent.title;
|
91
|
+
|
92
|
+
this.writeElement(
|
93
|
+
'testcase',
|
94
|
+
{
|
95
|
+
classname,
|
96
|
+
name: test.title,
|
97
|
+
time: getDuration(test),
|
98
|
+
},
|
99
|
+
() => {
|
100
|
+
if (test.state === 'failed') {
|
101
|
+
const error = test.err;
|
102
|
+
this.writeElement('failure', { message: error });
|
103
|
+
}
|
104
|
+
},
|
105
|
+
);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
private onFinished(): void {
|
110
|
+
this.logger.log('<?xml version="1.0" encoding="UTF-8" ?>');
|
111
|
+
|
112
|
+
const suites = Object.entries(this.suites).map(([name, tests]) => {
|
113
|
+
return {
|
114
|
+
name,
|
115
|
+
tests,
|
116
|
+
failures: tests.filter((test) => test.state === 'failed').length,
|
117
|
+
time: tests.reduce((acc, test) => acc + (test.duration ?? 0), 0),
|
118
|
+
};
|
119
|
+
});
|
120
|
+
const stats = suites.reduce(
|
121
|
+
(s, { tests, failures, time }) => {
|
122
|
+
s.tests += tests.length;
|
123
|
+
s.failures += failures;
|
124
|
+
s.time += time;
|
125
|
+
return s;
|
126
|
+
},
|
127
|
+
{ name: 'creevey tests', tests: 0, failures: 0, time: 0 },
|
128
|
+
);
|
129
|
+
|
130
|
+
this.writeElement('testsuites', { ...stats, time: executionTime(stats.time) }, () => {
|
131
|
+
suites.forEach(({ name, tests, failures, time }) => {
|
132
|
+
this.writeElement(
|
133
|
+
'testsuite',
|
134
|
+
{
|
135
|
+
name,
|
136
|
+
tests: tests.length,
|
137
|
+
failures,
|
138
|
+
time: executionTime(time),
|
139
|
+
},
|
140
|
+
() => {
|
141
|
+
this.writeTasks(tests);
|
142
|
+
},
|
143
|
+
);
|
144
|
+
});
|
145
|
+
});
|
146
|
+
|
147
|
+
if (this.reportFile) {
|
148
|
+
logger().info(`JUNIT report written to ${this.reportFile}`);
|
149
|
+
}
|
150
|
+
|
151
|
+
if (this.fileFd) {
|
152
|
+
closeSync(this.fileFd);
|
153
|
+
this.fileFd = undefined;
|
154
|
+
}
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
// https://gist.github.com/john-doherty/b9195065884cdbfd2017a4756e6409cc
|
159
|
+
function removeInvalidXMLCharacters(value: string, removeDiscouragedChars: boolean): string {
|
160
|
+
let regex =
|
161
|
+
// eslint-disable-next-line no-control-regex
|
162
|
+
/([\0-\x08\v\f\x0E-\x1F\uFFFD\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF])/g;
|
163
|
+
value = String(value).replace(regex, '');
|
164
|
+
|
165
|
+
if (removeDiscouragedChars) {
|
166
|
+
// remove everything discouraged by XML 1.0 specifications
|
167
|
+
regex = new RegExp(
|
168
|
+
'([\\x7F-\\x84]|[\\x86-\\x9F]|[\\uFDD0-\\uFDEF]|\\uD83F[\\uDFFE\\uDFFF]|(?:\\uD87F[\\uDF' +
|
169
|
+
'FE\\uDFFF])|\\uD8BF[\\uDFFE\\uDFFF]|\\uD8FF[\\uDFFE\\uDFFF]|(?:\\uD93F[\\uDFFE\\uD' +
|
170
|
+
'FFF])|\\uD97F[\\uDFFE\\uDFFF]|\\uD9BF[\\uDFFE\\uDFFF]|\\uD9FF[\\uDFFE\\uDFFF]' +
|
171
|
+
'|\\uDA3F[\\uDFFE\\uDFFF]|\\uDA7F[\\uDFFE\\uDFFF]|\\uDABF[\\uDFFE\\uDFFF]|(?:\\' +
|
172
|
+
'uDAFF[\\uDFFE\\uDFFF])|\\uDB3F[\\uDFFE\\uDFFF]|\\uDB7F[\\uDFFE\\uDFFF]|(?:\\uDBBF' +
|
173
|
+
'[\\uDFFE\\uDFFF])|\\uDBFF[\\uDFFE\\uDFFF](?:[\\0-\\t\\v\\f\\x0E-\\u2027\\u202A-\\uD7FF\\' +
|
174
|
+
'uE000-\\uFFFF]|[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]|[\\uD800-\\uDBFF](?![\\uDC00-\\uDFFF])|' +
|
175
|
+
'(?:[^\\uD800-\\uDBFF]|^)[\\uDC00-\\uDFFF]))',
|
176
|
+
'g',
|
177
|
+
);
|
178
|
+
|
179
|
+
value = value.replace(regex, '');
|
180
|
+
}
|
181
|
+
|
182
|
+
return value;
|
183
|
+
}
|
184
|
+
|
185
|
+
function escapeXML(value: string | number): string {
|
186
|
+
return removeInvalidXMLCharacters(
|
187
|
+
String(value)
|
188
|
+
.replace(/&/g, '&')
|
189
|
+
.replace(/"/g, '"')
|
190
|
+
.replace(/'/g, ''')
|
191
|
+
.replace(/</g, '<')
|
192
|
+
.replace(/>/g, '>'),
|
193
|
+
true,
|
194
|
+
);
|
195
|
+
}
|
196
|
+
|
197
|
+
function executionTime(durationMS: number) {
|
198
|
+
return (durationMS / 1000).toLocaleString('en-US', {
|
199
|
+
useGrouping: false,
|
200
|
+
maximumFractionDigits: 10,
|
201
|
+
});
|
202
|
+
}
|
203
|
+
|
204
|
+
function getDuration(task: FakeTest): string | undefined {
|
205
|
+
const duration = task.duration ?? 0;
|
206
|
+
return executionTime(duration);
|
207
|
+
}
|
@@ -0,0 +1,74 @@
|
|
1
|
+
import { FakeTest, Images, isDefined, TEST_EVENTS } from '../../types.js';
|
2
|
+
import EventEmitter from 'events';
|
3
|
+
|
4
|
+
export class TeamcityReporter {
|
5
|
+
constructor(runner: EventEmitter, options: { reportDir: string }) {
|
6
|
+
const { reportDir } = options;
|
7
|
+
|
8
|
+
runner.on(TEST_EVENTS.TEST_BEGIN, (test: FakeTest) => {
|
9
|
+
console.log(`##teamcity[testStarted name='${this.escape(test.fullTitle())}' flowId='${test.creevey.workerId}']`);
|
10
|
+
});
|
11
|
+
|
12
|
+
runner.on(TEST_EVENTS.TEST_PASS, (test: FakeTest) => {
|
13
|
+
console.log(`##teamcity[testFinished name='${this.escape(test.fullTitle())}' flowId='${test.creevey.workerId}']`);
|
14
|
+
});
|
15
|
+
|
16
|
+
runner.on(TEST_EVENTS.TEST_FAIL, (test: FakeTest, error: Error) => {
|
17
|
+
const browserName = this.escape(test.creevey.browserName);
|
18
|
+
Object.entries(test.creevey.images).forEach(([name, image]) => {
|
19
|
+
if (!image) return;
|
20
|
+
const filePath = test
|
21
|
+
.titlePath()
|
22
|
+
.slice(0, -1)
|
23
|
+
.concat(name == browserName ? [] : [browserName])
|
24
|
+
.map(this.escape)
|
25
|
+
.join('/');
|
26
|
+
|
27
|
+
const { error: _, ...rest } = image;
|
28
|
+
Object.values(rest as Partial<Images>)
|
29
|
+
.filter(isDefined)
|
30
|
+
.forEach((fileName) => {
|
31
|
+
console.log(`##teamcity[publishArtifacts '${reportDir}/${filePath}/${fileName} => report/${filePath}']`);
|
32
|
+
console.log(
|
33
|
+
`##teamcity[testMetadata testName='${this.escape(
|
34
|
+
test.fullTitle(),
|
35
|
+
)}' type='image' value='report/${filePath}/${fileName}' flowId='${test.creevey.workerId}']`,
|
36
|
+
);
|
37
|
+
});
|
38
|
+
});
|
39
|
+
|
40
|
+
// Output failed test as passed due TC don't support retry mechanic
|
41
|
+
// https://teamcity-support.jetbrains.com/hc/en-us/community/posts/207216829-Count-test-as-successful-if-at-least-one-try-is-successful?page=1#community_comment_207394125
|
42
|
+
|
43
|
+
if (test.creevey.willRetry)
|
44
|
+
console.log(
|
45
|
+
`##teamcity[testFinished name='${this.escape(test.fullTitle())}' flowId='${test.creevey.workerId}']`,
|
46
|
+
);
|
47
|
+
else
|
48
|
+
console.log(
|
49
|
+
`##teamcity[testFailed name='${this.escape(test.fullTitle())}' message='${this.escape(
|
50
|
+
error.message,
|
51
|
+
)}' details='${this.escape(error.stack ?? '')}' flowId='${test.creevey.workerId}']`,
|
52
|
+
);
|
53
|
+
});
|
54
|
+
}
|
55
|
+
|
56
|
+
private escape = (str: string): string => {
|
57
|
+
if (!str) return '';
|
58
|
+
return (
|
59
|
+
str
|
60
|
+
.toString()
|
61
|
+
// eslint-disable-next-line no-control-regex
|
62
|
+
.replace(/\x1B.*?m/g, '')
|
63
|
+
.replace(/\|/g, '||')
|
64
|
+
.replace(/\n/g, '|n')
|
65
|
+
.replace(/\r/g, '|r')
|
66
|
+
.replace(/\[/g, '|[')
|
67
|
+
.replace(/\]/g, '|]')
|
68
|
+
.replace(/\u0085/g, '|x')
|
69
|
+
.replace(/\u2028/g, '|l')
|
70
|
+
.replace(/\u2029/g, '|p')
|
71
|
+
.replace(/'/g, "|'")
|
72
|
+
);
|
73
|
+
};
|
74
|
+
}
|
@@ -1,4 +1,4 @@
|
|
1
|
-
import { Args } from '
|
1
|
+
import type { Args } from 'storybook/internal/types';
|
2
2
|
import chalk from 'chalk';
|
3
3
|
import http from 'http';
|
4
4
|
import https from 'https';
|
@@ -19,12 +19,12 @@ import {
|
|
19
19
|
StorybookGlobals,
|
20
20
|
StoryInput,
|
21
21
|
StoriesRaw,
|
22
|
-
|
22
|
+
WorkerOptions,
|
23
23
|
ServerTest,
|
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,
|
@@ -97,7 +97,7 @@ async function buildWebdriver(
|
|
97
97
|
browser: string,
|
98
98
|
gridUrl: string,
|
99
99
|
config: Config,
|
100
|
-
options:
|
100
|
+
options: WorkerOptions,
|
101
101
|
): Promise<WebDriver | null> {
|
102
102
|
const browserConfig = config.browsers[browser] as BrowserConfigObject;
|
103
103
|
const { /*customizeBuilder,*/ seleniumCapabilities, browserName } = browserConfig;
|
@@ -249,7 +249,7 @@ export class InternalBrowser {
|
|
249
249
|
const rects = await this.#browser.executeScript<
|
250
250
|
{ elementRect: ElementRect; windowRect: ElementRect } | undefined
|
251
251
|
>(function (selector: string): { elementRect: ElementRect; windowRect: ElementRect } | undefined {
|
252
|
-
window.scrollTo(0, 0);
|
252
|
+
window.scrollTo(0, 0);
|
253
253
|
// eslint-disable-next-line no-var
|
254
254
|
var element = document.querySelector(selector);
|
255
255
|
if (!element) return;
|
@@ -266,9 +266,7 @@ export class InternalBrowser {
|
|
266
266
|
},
|
267
267
|
// NOTE page_Offset is used only for IE9-11
|
268
268
|
windowRect: {
|
269
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
270
269
|
top: Math.round(window.scrollY || window.pageYOffset),
|
271
|
-
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
272
270
|
left: Math.round(window.scrollX || window.pageXOffset),
|
273
271
|
width: window.innerWidth,
|
274
272
|
height: window.innerHeight,
|
@@ -297,12 +295,10 @@ export class InternalBrowser {
|
|
297
295
|
// ? context
|
298
296
|
// ? await context.captureElementScreenshot(await element.getId())
|
299
297
|
// : await browser.findElement(By.css(captureElement)).takeScreenshot()
|
300
|
-
// :
|
301
|
-
// await takeCompositeScreenshot(browser, windowRect, elementRect);
|
298
|
+
// : await takeCompositeScreenshot(browser, windowRect, elementRect);
|
302
299
|
screenshot = isFitIntoViewport
|
303
300
|
? await this.#browser.findElement(By.css(captureElement)).takeScreenshot()
|
304
|
-
:
|
305
|
-
await this.takeCompositeScreenshot(windowRect, elementRect);
|
301
|
+
: await this.takeCompositeScreenshot(windowRect, elementRect);
|
306
302
|
|
307
303
|
logger().debug(`${chalk.cyan(captureElement)} is captured`);
|
308
304
|
}
|
@@ -377,12 +373,32 @@ export class InternalBrowser {
|
|
377
373
|
}
|
378
374
|
|
379
375
|
async loadStoriesFromBrowser(): Promise<StoriesRaw> {
|
380
|
-
const
|
381
|
-
|
376
|
+
const result = await this.#browser.executeAsyncScript<
|
377
|
+
[error?: { message: string; stack?: string } | null, stories?: StoriesRaw]
|
378
|
+
>(function (
|
379
|
+
callback: (response: [error?: { message: string; stack?: string } | null, stories?: StoriesRaw]) => void,
|
382
380
|
) {
|
383
|
-
|
381
|
+
window
|
382
|
+
.__CREEVEY_GET_STORIES__()
|
383
|
+
.then((stories) => {
|
384
|
+
callback([null, stories]);
|
385
|
+
})
|
386
|
+
.catch((error: unknown) => {
|
387
|
+
const errorInfo = {
|
388
|
+
message: error instanceof Error ? error.message : String(error),
|
389
|
+
stack: error instanceof Error ? error.stack : undefined,
|
390
|
+
};
|
391
|
+
callback([errorInfo]);
|
392
|
+
});
|
384
393
|
});
|
385
394
|
|
395
|
+
const [error, stories] = result;
|
396
|
+
|
397
|
+
if (error) {
|
398
|
+
const errorObj = new Error(error.message);
|
399
|
+
if (error.stack) errorObj.stack = error.stack;
|
400
|
+
throw errorObj;
|
401
|
+
}
|
386
402
|
if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
|
387
403
|
|
388
404
|
return stories;
|
@@ -410,17 +426,24 @@ export class InternalBrowser {
|
|
410
426
|
browserName: string,
|
411
427
|
gridUrl: string,
|
412
428
|
config: Config,
|
413
|
-
options:
|
429
|
+
options: WorkerOptions,
|
414
430
|
): Promise<InternalBrowser | null> {
|
415
431
|
const browserConfig = config.browsers[browserName] as BrowserConfigObject;
|
416
|
-
const {
|
432
|
+
const {
|
433
|
+
storybookUrl: address = config.storybookUrl,
|
434
|
+
limit,
|
435
|
+
viewport,
|
436
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated
|
437
|
+
_storybookGlobals,
|
438
|
+
storybookGlobals = _storybookGlobals,
|
439
|
+
} = browserConfig;
|
417
440
|
void limit;
|
418
441
|
|
419
442
|
const browser = await buildWebdriver(browserName, gridUrl, config, options);
|
420
443
|
|
421
444
|
if (!browser) return null;
|
422
445
|
|
423
|
-
const internalBrowser = new InternalBrowser(browser, options.port,
|
446
|
+
const internalBrowser = new InternalBrowser(browser, options.port, storybookGlobals);
|
424
447
|
|
425
448
|
try {
|
426
449
|
if (isShuttingDown.current) return null;
|
@@ -430,7 +453,6 @@ export class InternalBrowser {
|
|
430
453
|
gridUrl,
|
431
454
|
viewport,
|
432
455
|
storybookUrl: address,
|
433
|
-
resolveStorybookUrl: config.resolveStorybookUrl,
|
434
456
|
});
|
435
457
|
|
436
458
|
return done ? internalBrowser : null;
|
@@ -453,13 +475,11 @@ export class InternalBrowser {
|
|
453
475
|
gridUrl,
|
454
476
|
viewport,
|
455
477
|
storybookUrl,
|
456
|
-
resolveStorybookUrl,
|
457
478
|
}: {
|
458
479
|
browserName: string;
|
459
480
|
gridUrl: string;
|
460
481
|
viewport?: { width: number; height: number };
|
461
482
|
storybookUrl: string;
|
462
|
-
resolveStorybookUrl?: () => Promise<string>;
|
463
483
|
}): Promise<boolean> {
|
464
484
|
const sessionId = (await this.#browser.getSession()).getId();
|
465
485
|
let browserHost = '';
|
@@ -483,7 +503,7 @@ export class InternalBrowser {
|
|
483
503
|
return await runSequence(
|
484
504
|
[
|
485
505
|
() => this.#browser.manage().setTimeouts({ pageLoad: 60000, script: 60000 }),
|
486
|
-
() => this.openStorybookPage(storybookUrl
|
506
|
+
() => this.openStorybookPage(storybookUrl),
|
487
507
|
() => this.waitForStorybook(),
|
488
508
|
() => this.updateStorybookGlobals(),
|
489
509
|
() => this.resolveCreeveyHost(),
|
@@ -500,25 +520,14 @@ export class InternalBrowser {
|
|
500
520
|
);
|
501
521
|
}
|
502
522
|
|
503
|
-
private async openStorybookPage(storybookUrl: string
|
523
|
+
private async openStorybookPage(storybookUrl: string): Promise<void> {
|
504
524
|
if (!LOCALHOST_REGEXP.test(storybookUrl)) {
|
505
525
|
return this.#browser.get(appendIframePath(storybookUrl));
|
506
526
|
}
|
507
527
|
|
508
528
|
try {
|
509
|
-
|
510
|
-
|
511
|
-
|
512
|
-
const resolvedUrl = await resolver();
|
513
|
-
|
514
|
-
logger().debug(`Resolver storybook url ${resolvedUrl}`);
|
515
|
-
|
516
|
-
await this.#browser.get(appendIframePath(resolvedUrl));
|
517
|
-
} else {
|
518
|
-
// TODO Pageload timeout 10s
|
519
|
-
// NOTE: getUrlChecker already calls `browser.get` so we don't need another one
|
520
|
-
await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
|
521
|
-
}
|
529
|
+
// NOTE: getUrlChecker already calls `browser.get` so we don't need another one
|
530
|
+
await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url));
|
522
531
|
} catch (error) {
|
523
532
|
logger().error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
|
524
533
|
throw error;
|
@@ -554,7 +563,7 @@ export class InternalBrowser {
|
|
554
563
|
}
|
555
564
|
|
556
565
|
private async waitForStorybook(): Promise<void> {
|
557
|
-
logger().debug('Waiting for
|
566
|
+
logger().debug('Waiting for Storybook to initiate');
|
558
567
|
|
559
568
|
const isTimeout = await Promise.race([
|
560
569
|
new Promise<boolean>((resolve) => {
|
@@ -567,9 +576,6 @@ export class InternalBrowser {
|
|
567
576
|
do {
|
568
577
|
// TODO Research a different way to ensure storybook is initiated
|
569
578
|
wait = await this.#browser.executeScript<boolean>(function (SET_GLOBALS: string): boolean {
|
570
|
-
// TODO Maybe use
|
571
|
-
// import { global } from '@storybook/global';
|
572
|
-
// global.IS_STORYBOOK
|
573
579
|
if (typeof window.__STORYBOOK_ADDONS_CHANNEL__ == 'undefined') return true;
|
574
580
|
if (window.__STORYBOOK_ADDONS_CHANNEL__.last(SET_GLOBALS) == undefined) return true;
|
575
581
|
return false;
|
@@ -579,8 +585,7 @@ export class InternalBrowser {
|
|
579
585
|
})(),
|
580
586
|
]);
|
581
587
|
|
582
|
-
|
583
|
-
if (isTimeout) throw new Error('Failed to wait `setStories` event');
|
588
|
+
if (isTimeout) throw new Error('Failed to wait Storybook init');
|
584
589
|
}
|
585
590
|
|
586
591
|
private async updateStorybookGlobals(): Promise<void> {
|
@@ -593,7 +598,9 @@ export class InternalBrowser {
|
|
593
598
|
}
|
594
599
|
|
595
600
|
private async resolveCreeveyHost(): Promise<void> {
|
596
|
-
const
|
601
|
+
const storybookUrl = await this.#browser.getCurrentUrl();
|
602
|
+
const storybookHost = new URL(storybookUrl).hostname;
|
603
|
+
const addresses = [storybookHost, ...getAddresses()];
|
597
604
|
|
598
605
|
this.#serverHost = await this.#browser.executeAsyncScript(
|
599
606
|
function (hosts: string[], port: number, callback: (host?: string | null) => void) {
|
@@ -633,6 +640,7 @@ export class InternalBrowser {
|
|
633
640
|
private async updateBrowserGlobalVariables() {
|
634
641
|
await this.#browser.executeScript(
|
635
642
|
function (workerId: number, creeveyHost: string, creeveyPort: number) {
|
643
|
+
window.__CREEVEY_ENV__ = true;
|
636
644
|
window.__CREEVEY_WORKER_ID__ = workerId;
|
637
645
|
window.__CREEVEY_SERVER_HOST__ = creeveyHost;
|
638
646
|
window.__CREEVEY_SERVER_PORT__ = creeveyPort;
|
@@ -831,9 +839,18 @@ export class InternalBrowser {
|
|
831
839
|
private keepAlive(): void {
|
832
840
|
this.#keepAliveInterval = setInterval(() => {
|
833
841
|
// NOTE Simple way to keep session alive
|
834
|
-
void this.#browser
|
835
|
-
|
836
|
-
|
842
|
+
void this.#browser
|
843
|
+
.getCurrentUrl()
|
844
|
+
.then((url) => {
|
845
|
+
logger().debug('current url', chalk.magenta(url));
|
846
|
+
})
|
847
|
+
.catch((error: unknown) => {
|
848
|
+
logger().error(error);
|
849
|
+
emitWorkerMessage({
|
850
|
+
type: 'error',
|
851
|
+
payload: { subtype: 'browser', error: 'Failed to ping browser' },
|
852
|
+
});
|
853
|
+
});
|
837
854
|
}, 10 * 1000);
|
838
855
|
}
|
839
856
|
}
|
@@ -2,11 +2,12 @@ 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
|
+
import { removeWorkerContainer } from '../worker/context.js';
|
10
11
|
|
11
12
|
async function createSelenoidConfig(
|
12
13
|
browsers: BrowserConfigObject[],
|
@@ -34,7 +35,7 @@ async function createSelenoidConfig(
|
|
34
35
|
dockerImage = `selenoid/${browserName}:${browserVersion}`,
|
35
36
|
webdriverCommand = [],
|
36
37
|
}) => {
|
37
|
-
|
38
|
+
selenoidConfig[browserName] ??= { default: browserVersion, versions: {} };
|
38
39
|
if (!useDocker && webdriverCommand.length == 0)
|
39
40
|
throw new Error('Please specify "webdriverCommand" browser option with path to browser webdriver');
|
40
41
|
selenoidConfig[browserName].versions[browserVersion] = {
|
@@ -91,12 +92,12 @@ export async function startSelenoidStandalone(config: Config, debug: boolean): P
|
|
91
92
|
|
92
93
|
// TODO Download browser webdrivers
|
93
94
|
try {
|
94
|
-
if (process.platform != 'win32')
|
95
|
+
if (process.platform != 'win32') chmod('+x', binaryPath);
|
95
96
|
} catch {
|
96
97
|
/* noop */
|
97
98
|
}
|
98
99
|
|
99
|
-
const selenoidProcess =
|
100
|
+
const selenoidProcess = exec(`${binaryPath} -conf ./browsers.json -disable-docker`, {
|
100
101
|
async: true,
|
101
102
|
cwd: selenoidConfigDir,
|
102
103
|
});
|
@@ -106,7 +107,9 @@ export async function startSelenoidStandalone(config: Config, debug: boolean): P
|
|
106
107
|
selenoidProcess.stderr?.pipe(process.stderr);
|
107
108
|
}
|
108
109
|
|
109
|
-
subscribeOn('shutdown', () =>
|
110
|
+
subscribeOn('shutdown', () => {
|
111
|
+
if (selenoidProcess.pid) void killTree(selenoidProcess.pid);
|
112
|
+
});
|
110
113
|
}
|
111
114
|
|
112
115
|
export async function startSelenoidContainer(config: Config, debug: boolean): Promise<string> {
|
@@ -145,5 +148,9 @@ export async function startSelenoidContainer(config: Config, debug: boolean): Pr
|
|
145
148
|
},
|
146
149
|
};
|
147
150
|
|
151
|
+
subscribeOn('shutdown', () => {
|
152
|
+
void removeWorkerContainer();
|
153
|
+
});
|
154
|
+
|
148
155
|
return runImage(selenoidImage, ['-limit', String(limit)], selenoidOptions, debug);
|
149
156
|
}
|