creevey 0.10.0-beta.9 → 0.10.0-rc.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.
- 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 +34 -0
- package/dist/playwright/generator.js +267 -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 +166 -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 +36 -0
- package/docs/migration-0.9-to-0.10.md +182 -0
- package/docs/playwright-reporter.md +170 -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 +360 -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 +165 -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 +74 -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
package/src/server/index.ts
CHANGED
@@ -1,103 +1,175 @@
|
|
1
1
|
import cluster from 'cluster';
|
2
2
|
import path from 'path';
|
3
|
+
import sh from 'shelljs';
|
4
|
+
import { getUserAgent } from 'package-manager-detector/detect';
|
5
|
+
import { resolveCommand } from 'package-manager-detector/commands';
|
3
6
|
import { readConfig, defaultBrowser } from './config.js';
|
4
|
-
import {
|
7
|
+
import {
|
8
|
+
Options,
|
9
|
+
Config,
|
10
|
+
BrowserConfigObject,
|
11
|
+
isWorkerMessage,
|
12
|
+
WorkerOptions,
|
13
|
+
OptionsSchema,
|
14
|
+
WorkerOptionsSchema,
|
15
|
+
} from '../types.js';
|
5
16
|
import { logger } from './logger.js';
|
17
|
+
import { getStorybookUrl, checkIsStorybookConnected } from './connection.js';
|
6
18
|
import { SeleniumWebdriver } from './selenium/webdriver.js';
|
7
19
|
import { LOCALHOST_REGEXP } from './webdriver.js';
|
8
|
-
import { isInsideDocker } from './utils.js';
|
9
|
-
import { sendWorkerMessage } from './messages.js';
|
10
|
-
import { playwrightDockerFile } from './playwright/docker-file.js';
|
20
|
+
import { isInsideDocker, killTree, resolvePlaywrightBrowserType, shutdownWithError } from './utils.js';
|
21
|
+
import { sendWorkerMessage, subscribeOn } from './messages.js';
|
11
22
|
import { buildImage } from './docker.js';
|
12
|
-
import { writeFile } from 'fs/promises';
|
23
|
+
import { mkdir, writeFile } from 'fs/promises';
|
24
|
+
import assert from 'assert';
|
25
|
+
import * as v from 'valibot';
|
26
|
+
import { PlaywrightWebdriver } from 'src/playwright.js';
|
27
|
+
|
28
|
+
async function getPlaywrightVersion(): Promise<string> {
|
29
|
+
const {
|
30
|
+
default: { version },
|
31
|
+
} = await import('playwright-core/package.json', { with: { type: 'json' } });
|
32
|
+
return version;
|
33
|
+
}
|
13
34
|
|
14
|
-
async function
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
return gridUrl;
|
25
|
-
}
|
26
|
-
// TODO Worker might want to use docker image of browser or start standalone selenium
|
27
|
-
} else {
|
28
|
-
if (config.gridUrl) return undefined;
|
35
|
+
async function startSelenoid(config: Config, debug = false): Promise<string | undefined> {
|
36
|
+
const { startSelenoidContainer, startSelenoidStandalone } = await import('./selenium/selenoid.js');
|
37
|
+
const gridUrl = 'http://localhost:4444/wd/hub';
|
38
|
+
if (config.useDocker) {
|
39
|
+
const host = await startSelenoidContainer(config, debug);
|
40
|
+
return isInsideDocker ? gridUrl.replace(LOCALHOST_REGEXP, host) : gridUrl;
|
41
|
+
}
|
42
|
+
await startSelenoidStandalone(config, debug);
|
43
|
+
return gridUrl;
|
44
|
+
}
|
29
45
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
46
|
+
async function startPlaywright(config: Config, browser: string, version: string, debug = false): Promise<string> {
|
47
|
+
// TODO Re-use dockerImage
|
48
|
+
const { startPlaywrightContainer } = await import('./playwright/docker.js');
|
49
|
+
const { browserName } = config.browsers[browser] as BrowserConfigObject;
|
34
50
|
|
35
|
-
|
36
|
-
|
51
|
+
const imageName = `creevey/${browserName}:v${version}`;
|
52
|
+
const host = await startPlaywrightContainer(imageName, browser, config, debug);
|
37
53
|
|
38
|
-
|
39
|
-
|
40
|
-
const { startPlaywrightContainer } = await import('./playwright/docker.js');
|
41
|
-
const { browserName } = config.browsers[browser] as BrowserConfigObject;
|
54
|
+
return host;
|
55
|
+
}
|
42
56
|
|
57
|
+
async function buildPlaywright(config: Config, version: string): Promise<void> {
|
58
|
+
const { playwrightDockerFile } = await import('./playwright/docker-file.js');
|
59
|
+
const {
|
60
|
+
default: { version: creeveyVersion },
|
61
|
+
} = await import('../../package.json', { with: { type: 'json' } });
|
62
|
+
const browsers = [...new Set(Object.values(config.browsers).map((c) => (c as BrowserConfigObject).browserName))];
|
63
|
+
await Promise.all(
|
64
|
+
browsers.map(async (browserName) => {
|
43
65
|
const imageName = `creevey/${browserName}:v${version}`;
|
44
|
-
const
|
66
|
+
const dockerfile = await playwrightDockerFile(browserName, version);
|
45
67
|
|
46
|
-
|
47
|
-
}
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
const workerMessage = message;
|
64
|
-
if (workerMessage.type != 'port') return;
|
65
|
-
|
66
|
-
void getPort().then((port) => {
|
67
|
-
sendWorkerMessage(worker, {
|
68
|
-
type: 'port',
|
69
|
-
payload: { port },
|
70
|
-
});
|
71
|
-
});
|
68
|
+
await buildImage(imageName, creeveyVersion, dockerfile);
|
69
|
+
}),
|
70
|
+
);
|
71
|
+
|
72
|
+
const { default: getPort } = await import('get-port');
|
73
|
+
|
74
|
+
cluster.on('message', (worker, message: unknown) => {
|
75
|
+
if (!isWorkerMessage(message)) return;
|
76
|
+
|
77
|
+
const workerMessage = message;
|
78
|
+
if (workerMessage.type != 'port') return;
|
79
|
+
|
80
|
+
void getPort().then((port) => {
|
81
|
+
sendWorkerMessage(worker, {
|
82
|
+
type: 'port',
|
83
|
+
payload: { port },
|
72
84
|
});
|
85
|
+
});
|
86
|
+
});
|
87
|
+
}
|
88
|
+
|
89
|
+
async function startWebdriverServer(config: Config, options: Options): Promise<string | undefined> {
|
90
|
+
if (config.webdriver === SeleniumWebdriver) {
|
91
|
+
return startSelenoid(config, options.debug);
|
92
|
+
// TODO Worker might want to use docker image of browser or start standalone selenium
|
93
|
+
} else {
|
94
|
+
if (config.gridUrl) return undefined;
|
95
|
+
|
96
|
+
if (config.useDocker) {
|
97
|
+
const version = await getPlaywrightVersion();
|
98
|
+
await buildPlaywright(config, version);
|
73
99
|
}
|
74
100
|
// TODO Support gridUrl for playwright
|
75
101
|
// NOTE: There is no grid for playwright right now
|
76
102
|
}
|
77
103
|
}
|
78
104
|
|
79
|
-
|
105
|
+
async function waitForStorybook(config: Config, options: Options): Promise<void> {
|
106
|
+
const [localUrl, remoteUrl] = getStorybookUrl(config, options);
|
107
|
+
|
108
|
+
if (options.storybookStart) {
|
109
|
+
const pm = getUserAgent();
|
110
|
+
assert(pm, new Error('Failed to detect current package manager'));
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
112
|
+
const { command, args } = resolveCommand(pm, 'run', ['storybook', 'dev'])!;
|
113
|
+
const storybookPort = new URL(localUrl).port;
|
114
|
+
const storybookCommand = `${config.storybookAutorunCmd ?? [command, ...args, '--ci'].join(' ')} -p ${storybookPort}`;
|
115
|
+
|
116
|
+
logger().info(`Start Storybook via \`${storybookCommand}\`, it should be accessible at:`);
|
117
|
+
logger().info(`Local - ${localUrl}`);
|
118
|
+
if (remoteUrl && localUrl != remoteUrl) logger().info(`On your network - ${remoteUrl}`);
|
119
|
+
logger().info('Waiting Storybook...');
|
120
|
+
|
121
|
+
const storybook = sh.exec(storybookCommand, { async: true });
|
122
|
+
subscribeOn('shutdown', () => {
|
123
|
+
if (storybook.pid) void killTree(storybook.pid);
|
124
|
+
});
|
125
|
+
} else {
|
126
|
+
logger().info('Storybook should be started and be accessible at:');
|
127
|
+
logger().info(`Local - ${localUrl}`);
|
128
|
+
if (remoteUrl && localUrl != remoteUrl) logger().info(`On your network - ${remoteUrl}`);
|
129
|
+
logger().info(
|
130
|
+
'Tip: Creevey can start Storybook automatically by using `-s` option at the command line. (e.g., yarn/npm run creevey -s)',
|
131
|
+
);
|
132
|
+
logger().info('Waiting Storybook...');
|
133
|
+
}
|
134
|
+
|
135
|
+
if (options.storybookStart || process.env.CI !== 'true') {
|
136
|
+
const isConnected = await checkIsStorybookConnected(localUrl);
|
137
|
+
if (isConnected) {
|
138
|
+
logger().info('Storybook connected!\n');
|
139
|
+
} else {
|
140
|
+
logger().error('Storybook is not responding. Please start Storybook and restart Creevey');
|
141
|
+
shutdownWithError();
|
142
|
+
}
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
// TODO Why docker containers are not deleting after stop?
|
147
|
+
export default async function (command: 'report' | 'test' | 'worker', options: Options | WorkerOptions): Promise<void> {
|
80
148
|
const config = await readConfig(options);
|
81
|
-
const { browser = defaultBrowser, update, ui, port } = options;
|
82
|
-
let gridUrl = cluster.isPrimary ? config.gridUrl : options.gridUrl;
|
83
149
|
|
84
150
|
// TODO Add package.json with `"type": "commonjs"` as workaround for esm packages to load `data.js`
|
151
|
+
await mkdir(config.reportDir, { recursive: true });
|
85
152
|
await writeFile(path.join(config.reportDir, 'package.json'), '{"type": "commonjs"}');
|
86
153
|
|
87
|
-
|
88
|
-
if (
|
89
|
-
!(gridUrl || (Object.values(config.browsers) as BrowserConfigObject[]).every(({ gridUrl }) => gridUrl)) &&
|
90
|
-
!update
|
91
|
-
) {
|
92
|
-
gridUrl = await startWebdriverServer(browser, config, options);
|
93
|
-
}
|
154
|
+
await import('./shutdown.js');
|
94
155
|
|
95
|
-
|
96
|
-
|
97
|
-
|
156
|
+
if (v.is(OptionsSchema, options)) {
|
157
|
+
const { port, reportDir = config.reportDir } = options;
|
158
|
+
|
159
|
+
if (command == 'report') {
|
160
|
+
const { report } = await import('./report.js');
|
161
|
+
report(config, reportDir, port);
|
98
162
|
return;
|
99
163
|
}
|
100
|
-
|
164
|
+
|
165
|
+
if (cluster.isPrimary) {
|
166
|
+
let gridUrl: string | undefined = config.gridUrl;
|
167
|
+
|
168
|
+
if (!(gridUrl || (Object.values(config.browsers) as BrowserConfigObject[]).every(({ gridUrl }) => gridUrl))) {
|
169
|
+
gridUrl = await startWebdriverServer(config, options);
|
170
|
+
}
|
171
|
+
await waitForStorybook(config, options);
|
172
|
+
|
101
173
|
if (config.webdriver === SeleniumWebdriver) {
|
102
174
|
try {
|
103
175
|
await import('selenium-webdriver');
|
@@ -115,16 +187,30 @@ export default async function (options: Options): Promise<void> {
|
|
115
187
|
}
|
116
188
|
logger().info('Starting Master Process');
|
117
189
|
|
118
|
-
|
119
|
-
|
120
|
-
return (await import('./master/start.js')).start(gridUrl, config, options, resolveApi);
|
190
|
+
return (await import('./master/start.js')).start(gridUrl, config, options);
|
121
191
|
}
|
122
|
-
|
123
|
-
logger().info(`Starting Worker for ${browser}`);
|
192
|
+
}
|
124
193
|
|
125
|
-
|
126
|
-
|
127
|
-
|
194
|
+
if (v.is(WorkerOptionsSchema, options) && cluster.isWorker) {
|
195
|
+
let gridUrl = options.gridUrl;
|
196
|
+
const { browser = defaultBrowser, debug } = options;
|
197
|
+
|
198
|
+
if (!gridUrl) {
|
199
|
+
if (config.webdriver === PlaywrightWebdriver) {
|
200
|
+
if (config.useDocker) {
|
201
|
+
const version = await getPlaywrightVersion();
|
202
|
+
gridUrl = await startPlaywright(config, browser, version, debug);
|
203
|
+
} else {
|
204
|
+
const { browserName } = config.browsers[browser] as BrowserConfigObject;
|
205
|
+
gridUrl = `creevey://${resolvePlaywrightBrowserType(browserName)}`;
|
206
|
+
}
|
207
|
+
} else {
|
208
|
+
assert(gridUrl, 'Grid URL is required for Selenium');
|
209
|
+
}
|
128
210
|
}
|
211
|
+
|
212
|
+
logger().info(`Starting Worker for ${browser}`);
|
213
|
+
|
214
|
+
return (await import('./worker/start.js')).start(browser, gridUrl, config, options);
|
129
215
|
}
|
130
216
|
}
|
package/src/server/master/api.ts
CHANGED
@@ -1,14 +1,10 @@
|
|
1
|
-
import WebSocket from 'ws';
|
2
|
-
import
|
3
|
-
import {
|
1
|
+
import { Data, WebSocket, WebSocketServer } from 'ws';
|
2
|
+
import type { Request, Response, CreeveyUpdate } from '../../types.js';
|
3
|
+
import type { TestsManager } from './testsManager.js';
|
4
|
+
import type Runner from './runner.js';
|
4
5
|
import { logger } from '../logger.js';
|
5
6
|
|
6
|
-
|
7
|
-
subscribe: (wss: WebSocket.Server) => void;
|
8
|
-
handleMessage: (ws: WebSocket, message: WebSocket.Data) => void;
|
9
|
-
}
|
10
|
-
|
11
|
-
function broadcast(wss: WebSocket.Server, message: Response): void {
|
7
|
+
function broadcast(wss: WebSocketServer, message: Response): void {
|
12
8
|
wss.clients.forEach((ws) => {
|
13
9
|
if (ws.readyState === WebSocket.OPEN) {
|
14
10
|
ws.send(JSON.stringify(message));
|
@@ -16,44 +12,114 @@ function broadcast(wss: WebSocket.Server, message: Response): void {
|
|
16
12
|
});
|
17
13
|
}
|
18
14
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
15
|
+
function send(ws: WebSocket, message: Response): void {
|
16
|
+
if (ws.readyState === WebSocket.OPEN) {
|
17
|
+
ws.send(JSON.stringify(message));
|
18
|
+
}
|
19
|
+
}
|
20
|
+
|
21
|
+
// The class-based implementation of CreeveyApi for native WebSockets
|
22
|
+
export class CreeveyApi {
|
23
|
+
private runner: Runner | null = null;
|
24
|
+
private testsManager: TestsManager;
|
25
|
+
private wss: WebSocketServer | null = null;
|
26
|
+
|
27
|
+
constructor(testsManager: TestsManager, runner?: Runner) {
|
28
|
+
this.testsManager = testsManager;
|
29
|
+
|
30
|
+
// Use the provided runner in normal mode, or keep as null in update mode
|
31
|
+
if (runner) {
|
32
|
+
this.runner = runner;
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
subscribe(wss: WebSocketServer): void {
|
37
|
+
this.wss = wss;
|
38
|
+
|
39
|
+
// If we have a runner, subscribe to its updates
|
40
|
+
if (this.runner) {
|
41
|
+
this.runner.on('update', (payload: CreeveyUpdate) => {
|
42
|
+
this.broadcastUpdate(payload);
|
43
|
+
});
|
44
|
+
} else {
|
45
|
+
// Subscribe to TestsManager updates
|
46
|
+
this.testsManager.on('update', (update: CreeveyUpdate) => {
|
47
|
+
this.broadcastUpdate(update);
|
24
48
|
});
|
25
|
-
}
|
49
|
+
}
|
50
|
+
}
|
26
51
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
52
|
+
handleMessage(ws: WebSocket, message: Data): void {
|
53
|
+
if (typeof message != 'string') {
|
54
|
+
logger().info('unhandled message', message);
|
55
|
+
return;
|
56
|
+
}
|
32
57
|
|
33
|
-
|
58
|
+
const command = JSON.parse(message) as Request;
|
34
59
|
|
60
|
+
if (this.runner) {
|
61
|
+
// Normal mode handling with runner
|
35
62
|
switch (command.type) {
|
36
63
|
case 'status': {
|
37
|
-
|
64
|
+
const status = this.runner.status;
|
65
|
+
send(ws, { type: 'status', payload: status });
|
38
66
|
return;
|
39
67
|
}
|
40
68
|
case 'start': {
|
41
|
-
runner.start(command.payload);
|
69
|
+
this.runner.start(command.payload);
|
42
70
|
return;
|
43
71
|
}
|
44
72
|
case 'stop': {
|
45
|
-
runner.stop();
|
73
|
+
this.runner.stop();
|
74
|
+
return;
|
75
|
+
}
|
76
|
+
case 'approve': {
|
77
|
+
void this.runner.approve(command.payload);
|
78
|
+
return;
|
79
|
+
}
|
80
|
+
case 'approveAll': {
|
81
|
+
void this.runner.approveAll();
|
46
82
|
return;
|
47
83
|
}
|
84
|
+
}
|
85
|
+
} else {
|
86
|
+
// In update mode, only approve and approveAll commands are allowed
|
87
|
+
switch (command.type) {
|
48
88
|
case 'approve': {
|
49
|
-
void
|
89
|
+
void this.testsManager.approve(command.payload);
|
50
90
|
return;
|
51
91
|
}
|
52
92
|
case 'approveAll': {
|
53
|
-
void
|
93
|
+
void this.testsManager.approveAll();
|
94
|
+
return;
|
95
|
+
}
|
96
|
+
case 'status': {
|
97
|
+
// In update mode, respond with static status including tests data
|
98
|
+
send(ws, {
|
99
|
+
type: 'status',
|
100
|
+
payload: {
|
101
|
+
isRunning: false,
|
102
|
+
tests: this.testsManager.getTestsData(),
|
103
|
+
browsers: [],
|
104
|
+
isUpdateMode: true,
|
105
|
+
},
|
106
|
+
});
|
107
|
+
return;
|
108
|
+
}
|
109
|
+
default: {
|
110
|
+
// Ignore other commands in update mode
|
111
|
+
logger().debug(`Command ${command.type} is not available in update mode`);
|
54
112
|
return;
|
55
113
|
}
|
56
114
|
}
|
57
|
-
}
|
58
|
-
}
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
private broadcastUpdate(payload: CreeveyUpdate): void {
|
119
|
+
if (!this.wss) return;
|
120
|
+
|
121
|
+
const message: Response = { type: 'update', payload };
|
122
|
+
|
123
|
+
broadcast(this.wss, message);
|
124
|
+
}
|
59
125
|
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import cluster from 'cluster';
|
2
|
+
import { subscribeOnWorker, sendStoriesMessage } from '../../messages.js';
|
3
|
+
import { CaptureOptions, isDefined } from '../../../types.js';
|
4
|
+
|
5
|
+
export function captureHandler({ workerId, options }: { workerId: number; options?: CaptureOptions }): void {
|
6
|
+
const worker = Object.values(cluster.workers ?? {})
|
7
|
+
.filter(isDefined)
|
8
|
+
.find((worker) => worker.process.pid == workerId);
|
9
|
+
|
10
|
+
// NOTE: Hypothetical case when someone send to us capture req and we don't have a worker with browser session for it
|
11
|
+
if (!worker) {
|
12
|
+
return;
|
13
|
+
}
|
14
|
+
|
15
|
+
const unsubscribe = subscribeOnWorker(worker, 'stories', (message) => {
|
16
|
+
if (message.type != 'capture') return;
|
17
|
+
unsubscribe();
|
18
|
+
});
|
19
|
+
sendStoriesMessage(worker, { type: 'capture', payload: options });
|
20
|
+
}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import path from 'path';
|
2
|
+
import fs from 'fs';
|
3
|
+
|
4
|
+
export function staticHandler(baseDir: string, pathPrefix?: string) {
|
5
|
+
return (requestedPath: string): string | undefined => {
|
6
|
+
const relativePath = pathPrefix ? requestedPath.replace(pathPrefix, '') : requestedPath;
|
7
|
+
let filePath = path.join(baseDir, relativePath || 'index.html');
|
8
|
+
|
9
|
+
// If the path points to a directory, append index.html
|
10
|
+
if (fs.existsSync(filePath) && fs.statSync(filePath).isDirectory()) {
|
11
|
+
filePath = path.join(filePath, 'index.html');
|
12
|
+
}
|
13
|
+
|
14
|
+
return fs.existsSync(filePath) ? filePath : undefined;
|
15
|
+
};
|
16
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
import cluster from 'cluster';
|
2
|
+
import { emitStoriesMessage, sendStoriesMessage } from '../../messages.js';
|
3
|
+
import { isDefined, StoryInput } from '../../../types.js';
|
4
|
+
import { deserializeStory } from '../../../shared/index.js';
|
5
|
+
|
6
|
+
export function storiesHandler({ stories }: { stories: [string, StoryInput[]][] }): void {
|
7
|
+
const deserializedStories = stories.map<[string, StoryInput[]]>(([file, stories]) => [
|
8
|
+
file,
|
9
|
+
stories.map(deserializeStory),
|
10
|
+
]);
|
11
|
+
|
12
|
+
emitStoriesMessage({ type: 'update', payload: deserializedStories });
|
13
|
+
|
14
|
+
Object.values(cluster.workers ?? {})
|
15
|
+
.filter(isDefined)
|
16
|
+
.filter((worker) => worker.isConnected())
|
17
|
+
.forEach((worker) => {
|
18
|
+
sendStoriesMessage(worker, { type: 'update', payload: deserializedStories });
|
19
|
+
});
|
20
|
+
}
|
@@ -1,44 +1,27 @@
|
|
1
|
-
import
|
2
|
-
import {
|
3
|
-
import {
|
1
|
+
import { Config } from '../../types.js';
|
2
|
+
import { loadTestsFromStories } from '../stories.js';
|
3
|
+
import { TestsManager } from './testsManager.js';
|
4
4
|
import Runner from './runner.js';
|
5
|
-
import { tryToLoadTestsData } from '../utils.js';
|
6
|
-
|
7
|
-
function mergeTests(
|
8
|
-
testsWithReports: Partial<Record<string, TestData>>,
|
9
|
-
testsFromStories: Partial<Record<string, ServerTest>>,
|
10
|
-
): Partial<Record<string, ServerTest>> {
|
11
|
-
Object.values(testsFromStories)
|
12
|
-
.filter(isDefined)
|
13
|
-
.forEach((test) => {
|
14
|
-
const testWithReport = testsWithReports[test.id];
|
15
|
-
if (!testWithReport) return;
|
16
|
-
test.retries = testWithReport.retries;
|
17
|
-
if (testWithReport.status == 'success' || testWithReport.status == 'failed') test.status = testWithReport.status;
|
18
|
-
test.results = testWithReport.results;
|
19
|
-
test.approved = testWithReport.approved;
|
20
|
-
});
|
21
|
-
return testsFromStories;
|
22
|
-
}
|
23
5
|
|
24
6
|
export default async function master(config: Config, gridUrl?: string): Promise<Runner> {
|
25
|
-
|
26
|
-
const
|
27
|
-
|
7
|
+
// Create TestsManager instance
|
8
|
+
const testsManager = new TestsManager(config.screenDir, config.reportDir);
|
9
|
+
|
10
|
+
// Create Runner with TestsManager
|
11
|
+
const runner = new Runner(config, testsManager, gridUrl);
|
28
12
|
|
29
13
|
await runner.init();
|
30
14
|
|
15
|
+
// Load tests from stories and update TestsManager
|
31
16
|
const tests = await loadTestsFromStories(
|
32
17
|
Object.keys(config.browsers),
|
33
18
|
(listener) => config.storiesProvider(config, listener),
|
34
19
|
(testsDiff) => {
|
35
20
|
runner.updateTests(testsDiff);
|
36
|
-
saveTestsJson(runner.tests, config.reportDir);
|
37
21
|
},
|
38
22
|
);
|
39
23
|
|
40
|
-
|
41
|
-
saveTestsJson(runner.tests, config.reportDir);
|
24
|
+
testsManager.updateTests(testsManager.loadAndMergeTests(tests));
|
42
25
|
|
43
26
|
return runner;
|
44
27
|
}
|
@@ -19,6 +19,7 @@ export default class Pool extends EventEmitter {
|
|
19
19
|
private forcedStop = false;
|
20
20
|
private failFast: boolean;
|
21
21
|
private gridUrl?: string;
|
22
|
+
private storybookUrl: string;
|
22
23
|
public get isRunning(): boolean {
|
23
24
|
return this.workers.length !== this.freeWorkers.length;
|
24
25
|
}
|
@@ -34,13 +35,16 @@ export default class Pool extends EventEmitter {
|
|
34
35
|
this.maxRetries = config.maxRetries;
|
35
36
|
this.config = config.browsers[browser] as BrowserConfigObject;
|
36
37
|
this.gridUrl = this.config.gridUrl ?? gridUrl;
|
38
|
+
this.storybookUrl = this.config.storybookUrl ?? config.storybookUrl;
|
37
39
|
}
|
38
40
|
|
39
41
|
async init(): Promise<void> {
|
40
42
|
const poolSize = Math.max(1, this.config.limit ?? 1);
|
41
43
|
this.workers = (
|
42
44
|
await Promise.all(
|
43
|
-
Array.from({ length: poolSize }).map(() =>
|
45
|
+
Array.from({ length: poolSize }).map(() =>
|
46
|
+
this.scheduler.forkWorker(this.browser, this.storybookUrl, this.gridUrl),
|
47
|
+
),
|
44
48
|
)
|
45
49
|
).filter((workerOrError): workerOrError is Worker => workerOrError instanceof ClusterWorker);
|
46
50
|
if (this.workers.length != poolSize)
|
@@ -120,7 +124,7 @@ export default class Pool extends EventEmitter {
|
|
120
124
|
worker.once('exit', async () => {
|
121
125
|
if (isShuttingDown.current) return;
|
122
126
|
|
123
|
-
const workerOrError = await this.scheduler.forkWorker(this.browser, this.gridUrl);
|
127
|
+
const workerOrError = await this.scheduler.forkWorker(this.browser, this.storybookUrl, this.gridUrl);
|
124
128
|
|
125
129
|
if (!(workerOrError instanceof ClusterWorker))
|
126
130
|
throw new Error(`Can't instantiate worker for ${this.browser} due many errors`);
|
@@ -165,7 +169,7 @@ export default class Pool extends EventEmitter {
|
|
165
169
|
gracefullyKill(worker);
|
166
170
|
}
|
167
171
|
|
168
|
-
this.handleTestResult(worker, test, { status: 'failed', error: message.payload.error });
|
172
|
+
this.handleTestResult(worker, test, { status: 'failed', error: message.payload.error, retries: test.retries });
|
169
173
|
}),
|
170
174
|
subscribeOnWorker(worker, 'test', (message) => {
|
171
175
|
if (message.type != 'end') return;
|