creevey 0.10.0-beta.31 → 0.10.0-beta.33
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/withCreevey.js +1 -0
- package/dist/client/addon/withCreevey.js.map +1 -1
- package/dist/client/shared/components/ResultsPage.d.ts +1 -1
- package/dist/client/web/CreeveyApp.js +1 -0
- package/dist/client/web/CreeveyApp.js.map +1 -1
- package/dist/server/docker.d.ts +1 -1
- package/dist/server/docker.js +21 -14
- package/dist/server/docker.js.map +1 -1
- package/dist/server/index.js +9 -10
- package/dist/server/index.js.map +1 -1
- package/dist/server/playwright/docker-file.d.ts +1 -2
- package/dist/server/playwright/docker-file.js +10 -4
- 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/internal.d.ts +3 -4
- package/dist/server/playwright/internal.js +48 -37
- package/dist/server/playwright/internal.js.map +1 -1
- package/dist/server/playwright/webdriver.js +4 -7
- package/dist/server/playwright/webdriver.js.map +1 -1
- package/dist/server/selenium/internal.js +5 -12
- package/dist/server/selenium/internal.js.map +1 -1
- package/dist/server/selenium/webdriver.js +4 -8
- package/dist/server/selenium/webdriver.js.map +1 -1
- package/dist/server/telemetry.js +2 -2
- package/dist/server/utils.d.ts +1 -2
- package/dist/server/utils.js +11 -8
- package/dist/server/utils.js.map +1 -1
- package/dist/server/webdriver.d.ts +2 -0
- package/dist/server/webdriver.js +13 -1
- package/dist/server/webdriver.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/types.d.ts +0 -2
- package/dist/types.js.map +1 -1
- package/docs/cli.md +12 -0
- package/docs/config.md +178 -167
- package/docs/storybook.md +60 -0
- package/docs/tests.md +50 -45
- package/package.json +1 -1
- package/src/client/addon/withCreevey.ts +1 -0
- package/src/client/web/CreeveyApp.tsx +1 -0
- package/src/server/docker.ts +24 -13
- package/src/server/index.ts +11 -14
- package/src/server/playwright/docker-file.ts +12 -5
- package/src/server/playwright/docker.ts +16 -3
- package/src/server/playwright/index-source.mjs +16 -0
- package/src/server/playwright/internal.ts +78 -52
- package/src/server/playwright/webdriver.ts +4 -7
- package/src/server/selenium/internal.ts +5 -12
- package/src/server/selenium/webdriver.ts +4 -8
- package/src/server/telemetry.ts +2 -2
- package/src/server/utils.ts +33 -25
- package/src/server/webdriver.ts +13 -0
- package/src/server/worker/context.ts +14 -0
- package/src/types.ts +0 -2
@@ -0,0 +1,60 @@
|
|
1
|
+
## Creevey Storybook Parameters
|
2
|
+
|
3
|
+
Creevey allows you to customize how stories will be captured. You could define parameters on `global`, `kind` or `story` levels. All these parameters are deeply merged by Storybook for each story.
|
4
|
+
|
5
|
+
```ts
|
6
|
+
// .storybook/preview.tsx
|
7
|
+
export const parameters = {
|
8
|
+
creevey: {
|
9
|
+
// Global parameters are applied to all stories
|
10
|
+
captureElement: '#storybook-root',
|
11
|
+
},
|
12
|
+
};
|
13
|
+
```
|
14
|
+
|
15
|
+
```ts
|
16
|
+
// stories/MyModal.stories.tsx
|
17
|
+
import React from 'react';
|
18
|
+
import { Meta, StoryObj } from '@storybook/react';
|
19
|
+
import { CreeveyMeta, CreeveyStory } from 'creevey';
|
20
|
+
import MyModal from './src/components/MyModal';
|
21
|
+
|
22
|
+
const Kind: Meta<typeof MyModal> = {
|
23
|
+
title: 'MyModal',
|
24
|
+
component: MyModal,
|
25
|
+
parameters: {
|
26
|
+
creevey: {
|
27
|
+
// It's possible to add additional delay before capturing screenshot
|
28
|
+
delay: 1000,
|
29
|
+
|
30
|
+
// For capturing the whole browser viewport, you can define `null` instead of css selector.
|
31
|
+
captureElement: null,
|
32
|
+
|
33
|
+
// You can skip some stories from capturing, by defining `skip` option:
|
34
|
+
// skip: { "The reason why story is skipped": { in: 'chrome', stories: 'Loading' } }
|
35
|
+
// - `in` - browser name, regex or array of browser names, which are defined in the Creevey config
|
36
|
+
// - `stories` - story name, regex or array of story names
|
37
|
+
// - `tests` - test name, regex or array of test names
|
38
|
+
// NOTE: If you try to skip stories by story name, the storybook name format will be used
|
39
|
+
// For more info see [storybook-export-vs-name-handling](https://storybook.js.org/docs/formats/component-story-format/#storybook-export-vs-name-handling))
|
40
|
+
skip: {
|
41
|
+
"`MyModal` doesn't support ie11": { in: 'ie11' },
|
42
|
+
'Loading stories are flaky in firefox': { in: 'firefox', stories: 'Loading' },
|
43
|
+
"`MyModal` hovering doesn't work correctly": {
|
44
|
+
in: ['firefox', 'chrome'],
|
45
|
+
tests: /.*hover$/,
|
46
|
+
},
|
47
|
+
},
|
48
|
+
},
|
49
|
+
},
|
50
|
+
};
|
51
|
+
|
52
|
+
export default Kind;
|
53
|
+
|
54
|
+
export const Basic: StoryObj<typeof MyModal> = {
|
55
|
+
creevey: {
|
56
|
+
// Lastly some elements can be ignored in capturing screenshot
|
57
|
+
ignoreElements: ['button', '.local-time'],
|
58
|
+
},
|
59
|
+
};
|
60
|
+
```
|
package/docs/tests.md
CHANGED
@@ -1,63 +1,68 @@
|
|
1
|
-
## Write tests
|
1
|
+
## Write interactive screenshot tests
|
2
2
|
|
3
|
-
|
3
|
+
In most cases following Storybook's ideology of [writing stories](https://storybook.js.org/docs/get-started/whats-a-story) is enough to test your UI components. Where each component has a separate stories file with its different states. But sometimes you might have pretty complicated components with a lot of interactions and internal states. In this case, you can write tests for your stories.
|
4
4
|
|
5
|
-
|
6
|
-
import React from 'react';
|
7
|
-
import { Story } from '@storybook/react';
|
8
|
-
import { CreeveyStory } from 'creevey';
|
9
|
-
import MyComponent from './src/components/MyComponent';
|
5
|
+
There are two different ways how to write interactive tests with Creevey:
|
10
6
|
|
11
|
-
|
7
|
+
### Write tests in `*.creevey.ts` files
|
12
8
|
|
13
|
-
|
14
|
-
Basic.parameters = {
|
15
|
-
creevey: {
|
16
|
-
captureElement: '#storybook-root',
|
17
|
-
tests: {
|
18
|
-
async click() {
|
19
|
-
await this.browser.actions().click(this.captureElement).perform();
|
9
|
+
It's the recommended way to write tests. It allows you to run these tests by Creevey itself and utilize webdriver benefits. The crucial part of it is webdriver action calls are more close to real user interactions and mitigate flakiness and false-negative results. Here is a simple example of how to write tests in `*.creevey.ts` files
|
20
10
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
11
|
+
```ts
|
12
|
+
// stories/MyComponent.creevey.ts
|
13
|
+
import { kind, story, test } from 'creevey';
|
14
|
+
|
15
|
+
kind('MyComponent', () => {
|
16
|
+
story('Story', ({ setStoryParameters }) => {
|
17
|
+
// It's possible to pass Creevey parameters to story
|
18
|
+
setStoryParameters({
|
19
|
+
captureElement: 'span[data-test-id~="x"]',
|
20
|
+
ignoreElements: [],
|
21
|
+
});
|
22
|
+
|
23
|
+
test('idle', async (context) => {
|
24
|
+
await context.matchImage(await context.takeScreenshot());
|
25
|
+
});
|
26
|
+
|
27
|
+
test('input', async (context) => {
|
28
|
+
await context.webdriver.keyboard.press('Tab');
|
29
|
+
const focus = await context.takeScreenshot();
|
30
|
+
await context.webdriver.keyboard.type('Hello Creevey');
|
31
|
+
const input = await context.takeScreenshot();
|
32
|
+
await context.matchImages({ focus, input });
|
33
|
+
});
|
34
|
+
});
|
35
|
+
});
|
26
36
|
```
|
27
37
|
|
28
|
-
|
38
|
+
In the example above, we used Playwright API to interact with the story. But Creevey also supports Selenium webdriver. And in that case `context.webdriver` will be an instance of Selenium webdriver. Obviously Selenium API is different from Playwright.
|
29
39
|
|
30
|
-
|
40
|
+
### Using Storybook's `play` function
|
31
41
|
|
32
|
-
|
33
|
-
import React from 'react';
|
34
|
-
import { CSFStory } from 'creevey';
|
35
|
-
import MyForm from './src/components/MyForm';
|
42
|
+
Storybook allows you to write tests in the story file itself by using [`play` function](https://storybook.js.org/docs/writing-tests/component-testing). It's a good way to write simple tests. But there are couple drawbacks of this approach:
|
36
43
|
|
37
|
-
|
44
|
+
- You can have only one test per story. Which is not a big deal, but sometimes you might not want to have multiple stories with the same markup.
|
45
|
+
- Tests are running in browser environment and use https://testing-library.com API under the hood. It's good for unit tests, but might not be suitable for visual regression tests, because testing-library relies on DOM API and not even close to real user interactions. For example, you might have a button that could be visible for user, but it's covered by some other transparent element. With testing-library the button easily accessible and clickable, but the user can't interact with it.
|
38
46
|
|
39
|
-
|
40
|
-
Basic.story = {
|
41
|
-
parameters: {
|
42
|
-
creevey: {
|
43
|
-
captureElement: '#storybook-root',
|
44
|
-
delay: 1000,
|
45
|
-
tests: {
|
46
|
-
async submit() {
|
47
|
-
const input = await this.browser.findElement({ css: '.my-input' });
|
47
|
+
Here is an example of how to write tests using Storybook's `play` function:
|
48
48
|
|
49
|
-
|
49
|
+
```tsx
|
50
|
+
// stories/MyComponent.stories.tsx
|
51
|
+
import React from 'react';
|
52
|
+
import { Meta, StoryObj } from '@storybook/react';
|
53
|
+
import { fireEvent, within } from '@storybook/test';
|
54
|
+
import MyComponent from './src/components/MyComponent';
|
50
55
|
|
51
|
-
|
56
|
+
export default {
|
57
|
+
title: 'MyComponent',
|
58
|
+
component: MyComponent,
|
59
|
+
};
|
52
60
|
|
53
|
-
|
61
|
+
export const Basic: StoryObj<typeof MyComponent> = {
|
62
|
+
play: async ({ canvasElement }) => {
|
63
|
+
const slider = await within(canvasElement).findByTestId('slider');
|
54
64
|
|
55
|
-
|
56
|
-
},
|
57
|
-
},
|
58
|
-
},
|
65
|
+
await fireEvent.change(slider, { target: { value: 50 } });
|
59
66
|
},
|
60
67
|
};
|
61
68
|
```
|
62
|
-
|
63
|
-
NOTE: In this example I fill some simple form and submit it. Also as you could see, I taking two different screenshots `empty` and `submitted` and assert these in the end.
|
package/package.json
CHANGED
@@ -322,6 +322,7 @@ export function withCreevey(): ReturnType<typeof makeDecorator> {
|
|
322
322
|
});
|
323
323
|
}
|
324
324
|
|
325
|
+
// TODO It's not accessible from the outside the package
|
325
326
|
export async function capture(options?: CaptureOptions): Promise<void> {
|
326
327
|
if (!isTestBrowser) return;
|
327
328
|
|
@@ -137,6 +137,7 @@ export function CreeveyApp({ api, initialState }: CreeveyAppProps): JSX.Element
|
|
137
137
|
}, [handleImageApproveNew, handleGoToNextFailedTest]);
|
138
138
|
|
139
139
|
const handleApproveAll = useCallback(() => {
|
140
|
+
// TODO Update handled incorrectly
|
140
141
|
api?.approveAll();
|
141
142
|
}, [api]);
|
142
143
|
|
package/src/server/docker.ts
CHANGED
@@ -3,8 +3,8 @@ import Logger from 'loglevel';
|
|
3
3
|
import { Writable } from 'stream';
|
4
4
|
import Dockerode, { Container } from 'dockerode';
|
5
5
|
import { DockerAuth } from '../types.js';
|
6
|
-
import { subscribeOn } from './messages.js';
|
7
6
|
import { logger } from './logger.js';
|
7
|
+
import { setWorkerContainer } from './worker/context.js';
|
8
8
|
|
9
9
|
const docker = new Dockerode();
|
10
10
|
|
@@ -58,12 +58,13 @@ export async function pullImages(
|
|
58
58
|
}
|
59
59
|
}
|
60
60
|
|
61
|
-
export async function buildImage(imageName: string, dockerfile: string): Promise<void> {
|
61
|
+
export async function buildImage(imageName: string, version: string, dockerfile: string): Promise<void> {
|
62
62
|
const images = await docker.listImages({ filters: { label: [`creevey=${imageName}`] } });
|
63
63
|
|
64
|
-
|
64
|
+
const containers = await docker.listContainers({ all: true, filters: { label: [`creevey=${imageName}`] } });
|
65
|
+
if (containers.length > 0) {
|
65
66
|
await Promise.all(
|
66
|
-
|
67
|
+
containers.map(async (info) => {
|
67
68
|
const container = docker.getContainer(info.Id);
|
68
69
|
try {
|
69
70
|
await container.remove({ force: true });
|
@@ -72,6 +73,23 @@ export async function buildImage(imageName: string, dockerfile: string): Promise
|
|
72
73
|
}
|
73
74
|
}),
|
74
75
|
);
|
76
|
+
}
|
77
|
+
|
78
|
+
const oldImages = images.filter((info) => info.Labels.version !== version);
|
79
|
+
if (oldImages.length > 0) {
|
80
|
+
await Promise.all(
|
81
|
+
oldImages.map(async (info) => {
|
82
|
+
const image = docker.getImage(info.Id);
|
83
|
+
try {
|
84
|
+
await image.remove({ force: true });
|
85
|
+
} catch {
|
86
|
+
/* noop */
|
87
|
+
}
|
88
|
+
}),
|
89
|
+
);
|
90
|
+
}
|
91
|
+
|
92
|
+
if (oldImages.length !== images.length) {
|
75
93
|
logger().info(`Image ${imageName} already exists`);
|
76
94
|
return;
|
77
95
|
}
|
@@ -91,7 +109,7 @@ export async function buildImage(imageName: string, dockerfile: string): Promise
|
|
91
109
|
// @ts-expect-error Type incompatibility AsyncIterator and AsyncIterableIterator
|
92
110
|
pack,
|
93
111
|
// TODO Support buildkit decode grpc (version: '2')
|
94
|
-
{ t: imageName, labels: { creevey: imageName }, version: '1' },
|
112
|
+
{ t: imageName, labels: { creevey: imageName, version }, version: '1' },
|
95
113
|
(buildError: Error | null, stream) => {
|
96
114
|
if (buildError || !stream) {
|
97
115
|
// spinner.error(buildError?.message);
|
@@ -148,14 +166,7 @@ export async function runImage(
|
|
148
166
|
|
149
167
|
return new Promise((resolve) => {
|
150
168
|
hub.once('container', (container: Container) => {
|
151
|
-
|
152
|
-
subscribeOn('shutdown', async () => {
|
153
|
-
try {
|
154
|
-
await container.remove({ force: true });
|
155
|
-
} catch {
|
156
|
-
/* noop */
|
157
|
-
}
|
158
|
-
});
|
169
|
+
setWorkerContainer(container);
|
159
170
|
});
|
160
171
|
hub.once(
|
161
172
|
'start',
|
package/src/server/index.ts
CHANGED
@@ -6,13 +6,13 @@ import { resolveCommand } from 'package-manager-detector/commands';
|
|
6
6
|
import { readConfig, defaultBrowser } from './config.js';
|
7
7
|
import { Options, Config, BrowserConfigObject, isWorkerMessage } from '../types.js';
|
8
8
|
import { logger } from './logger.js';
|
9
|
+
import { getStorybookUrl, checkIsStorybookConnected } from './connection.js';
|
9
10
|
import { SeleniumWebdriver } from './selenium/webdriver.js';
|
10
11
|
import { LOCALHOST_REGEXP } from './webdriver.js';
|
11
12
|
import { isInsideDocker, killTree, resolvePlaywrightBrowserType, shutdownWithError } from './utils.js';
|
12
13
|
import { sendWorkerMessage, subscribeOn } from './messages.js';
|
13
14
|
import { buildImage } from './docker.js';
|
14
15
|
import { mkdir, writeFile } from 'fs/promises';
|
15
|
-
import { getStorybookUrl, checkIsStorybookConnected } from './connection.js';
|
16
16
|
import assert from 'assert';
|
17
17
|
|
18
18
|
async function startWebdriverServer(browser: string, config: Config, options: Options): Promise<string | undefined> {
|
@@ -35,7 +35,7 @@ async function startWebdriverServer(browser: string, config: Config, options: Op
|
|
35
35
|
if (cluster.isPrimary) return undefined;
|
36
36
|
|
37
37
|
const { browserName } = config.browsers[browser] as BrowserConfigObject;
|
38
|
-
return `creevey://${resolvePlaywrightBrowserType(browserName)}
|
38
|
+
return `creevey://${resolvePlaywrightBrowserType(browserName)}`;
|
39
39
|
}
|
40
40
|
|
41
41
|
const {
|
@@ -48,24 +48,21 @@ async function startWebdriverServer(browser: string, config: Config, options: Op
|
|
48
48
|
const { browserName } = config.browsers[browser] as BrowserConfigObject;
|
49
49
|
|
50
50
|
const imageName = `creevey/${browserName}:v${version}`;
|
51
|
-
const host = await startPlaywrightContainer(imageName, options.debug);
|
51
|
+
const host = await startPlaywrightContainer(imageName, browser, config, options.debug);
|
52
52
|
|
53
53
|
return host;
|
54
54
|
} else {
|
55
55
|
const { playwrightDockerFile } = await import('./playwright/docker-file.js');
|
56
|
-
const
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
),
|
61
|
-
),
|
62
|
-
];
|
56
|
+
const {
|
57
|
+
default: { version: creeveyVersion },
|
58
|
+
} = await import('../../package.json', { with: { type: 'json' } });
|
59
|
+
const browsers = [...new Set(Object.values(config.browsers).map((c) => (c as BrowserConfigObject).browserName))];
|
63
60
|
await Promise.all(
|
64
|
-
browsers.map(async (
|
61
|
+
browsers.map(async (browserName) => {
|
65
62
|
const imageName = `creevey/${browserName}:v${version}`;
|
66
|
-
const dockerfile = playwrightDockerFile(browserName, version
|
63
|
+
const dockerfile = await playwrightDockerFile(browserName, version);
|
67
64
|
|
68
|
-
await buildImage(imageName, dockerfile);
|
65
|
+
await buildImage(imageName, creeveyVersion, dockerfile);
|
69
66
|
}),
|
70
67
|
);
|
71
68
|
|
@@ -107,7 +104,7 @@ export default async function (options: Options): Promise<void> {
|
|
107
104
|
gridUrl = await startWebdriverServer(browser, config, options);
|
108
105
|
}
|
109
106
|
|
110
|
-
if (cluster.isPrimary) {
|
107
|
+
if (cluster.isPrimary && process.env.CI !== 'true') {
|
111
108
|
const [localUrl, remoteUrl] = getStorybookUrl(config, options);
|
112
109
|
const pm = getUserAgent();
|
113
110
|
assert(pm, new Error('Failed to detect current package manager'));
|
@@ -1,10 +1,12 @@
|
|
1
|
+
import { readFile } from 'fs/promises';
|
2
|
+
import { pathToFileURL } from 'url';
|
1
3
|
import semver from 'semver';
|
2
4
|
import { exec } from 'shelljs';
|
3
|
-
|
4
|
-
|
5
|
+
|
6
|
+
const importMetaUrl = pathToFileURL(__filename).href;
|
5
7
|
|
6
8
|
// TODO Support custom docker images
|
7
|
-
export function playwrightDockerFile(browser: string, version: string
|
9
|
+
export async function playwrightDockerFile(browser: string, version: string): Promise<string> {
|
8
10
|
const sv = semver.coerce(version);
|
9
11
|
|
10
12
|
let npmRegistry;
|
@@ -14,14 +16,19 @@ export function playwrightDockerFile(browser: string, version: string, serverOpt
|
|
14
16
|
/* noop */
|
15
17
|
}
|
16
18
|
|
19
|
+
const indexJs = await readFile(new URL('./index-source.mjs', importMetaUrl), 'utf-8');
|
20
|
+
|
17
21
|
return `
|
18
22
|
FROM node:lts
|
19
23
|
|
20
24
|
WORKDIR /creevey
|
21
25
|
|
22
26
|
RUN echo "{ \\"type\\": \\"module\\" }" > package.json && \\
|
23
|
-
|
24
|
-
|
27
|
+
${indexJs
|
28
|
+
.split('\n')
|
29
|
+
.map((line) => `echo "${line.replace(/"/g, '\\"')}" >> index.js && \\`)
|
30
|
+
.join('\n')}
|
31
|
+
${
|
25
32
|
npmRegistry
|
26
33
|
? `
|
27
34
|
echo "registry=${npmRegistry}" > .npmrc && \\`
|
@@ -1,9 +1,17 @@
|
|
1
|
+
import assert from 'assert';
|
1
2
|
import { runImage } from '../docker';
|
2
3
|
import { emitWorkerMessage, subscribeOn } from '../messages';
|
3
|
-
import { isInsideDocker } from '../utils';
|
4
|
+
import { getCreeveyCache, isInsideDocker, resolvePlaywrightBrowserType } from '../utils';
|
4
5
|
import { LOCALHOST_REGEXP } from '../webdriver';
|
6
|
+
import type { BrowserConfigObject, Config } from '../../types';
|
5
7
|
|
6
|
-
export async function startPlaywrightContainer(
|
8
|
+
export async function startPlaywrightContainer(
|
9
|
+
imageName: string,
|
10
|
+
browser: string,
|
11
|
+
config: Config,
|
12
|
+
debug: boolean,
|
13
|
+
): Promise<string> {
|
14
|
+
const { browserName, playwrightOptions } = config.browsers[browser] as BrowserConfigObject;
|
7
15
|
const port = await new Promise<number>((resolve) => {
|
8
16
|
subscribeOn('worker', (message) => {
|
9
17
|
if (message.type == 'port') {
|
@@ -13,13 +21,18 @@ export async function startPlaywrightContainer(imageName: string, debug: boolean
|
|
13
21
|
emitWorkerMessage({ type: 'port', payload: { port: -1 } });
|
14
22
|
});
|
15
23
|
|
24
|
+
const cacheDir = await getCreeveyCache();
|
25
|
+
|
26
|
+
assert(cacheDir, "Couldn't get cache directory");
|
27
|
+
|
16
28
|
const host = await runImage(
|
17
29
|
imageName,
|
18
|
-
[],
|
30
|
+
[JSON.stringify({ ...playwrightOptions, browser: resolvePlaywrightBrowserType(browserName) })],
|
19
31
|
{
|
20
32
|
ExposedPorts: { [`${port}/tcp`]: {} },
|
21
33
|
HostConfig: {
|
22
34
|
PortBindings: { ['4444/tcp']: [{ HostPort: `${port}` }] },
|
35
|
+
Binds: [`${cacheDir}/${process.pid}:/creevey/traces`],
|
23
36
|
},
|
24
37
|
},
|
25
38
|
debug,
|
@@ -0,0 +1,16 @@
|
|
1
|
+
import { chromium, firefox, webkit } from 'playwright-core';
|
2
|
+
|
3
|
+
/** @type import("playwright-core").LaunchOptions & { browser: 'chromium' | 'firefox' | 'webkit' } */
|
4
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
5
|
+
const config = JSON.parse(process.argv.slice(2)[0]);
|
6
|
+
|
7
|
+
const browsers = { chromium, firefox, webkit };
|
8
|
+
|
9
|
+
const ws = await browsers[config.browser].launchServer({
|
10
|
+
...config,
|
11
|
+
port: 4444,
|
12
|
+
wsPath: 'creevey',
|
13
|
+
tracesDir: 'traces',
|
14
|
+
});
|
15
|
+
|
16
|
+
console.log(config.browser, 'browser server launched on:', ws.wsEndpoint());
|