creevey 0.9.1 → 0.10.0-beta.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/chromatic.config.json +5 -0
- package/dist/client/addon/components/Addon.d.ts +1 -0
- package/dist/client/addon/components/Addon.js.map +1 -1
- package/dist/client/addon/components/Icons.d.ts +1 -0
- package/dist/client/addon/components/Icons.js.map +1 -1
- package/dist/client/addon/components/Panel.d.ts +1 -0
- package/dist/client/addon/components/Panel.js.map +1 -1
- package/dist/client/addon/components/TestSelect.d.ts +1 -0
- package/dist/client/addon/components/TestSelect.js +4 -3
- package/dist/client/addon/components/TestSelect.js.map +1 -1
- package/dist/client/addon/components/Tools.d.ts +1 -0
- package/dist/client/addon/components/Tools.js +7 -8
- 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.map +1 -1
- package/dist/client/addon/decorator.d.ts +1 -1
- package/dist/client/addon/manager.js +3 -2
- package/dist/client/addon/manager.js.map +1 -1
- package/dist/client/addon/preview.d.ts +1 -1
- package/dist/client/addon/withCreevey.d.ts +6 -8
- package/dist/client/addon/withCreevey.js +21 -19
- package/dist/client/addon/withCreevey.js.map +1 -1
- package/dist/client/shared/components/ImagesView/BlendView.d.ts +1 -1
- package/dist/client/shared/components/ImagesView/BlendView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/ImagesView.d.ts +1 -0
- package/dist/client/shared/components/ImagesView/ImagesView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SideBySideView.d.ts +1 -1
- package/dist/client/shared/components/ImagesView/SideBySideView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SlideView.d.ts +1 -1
- package/dist/client/shared/components/ImagesView/SlideView.js.map +1 -1
- package/dist/client/shared/components/ImagesView/SwapView.d.ts +1 -1
- package/dist/client/shared/components/ImagesView/SwapView.js.map +1 -1
- package/dist/client/shared/components/PageFooter/PageFooter.d.ts +1 -0
- 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.d.ts +2 -2
- package/dist/client/shared/components/PageFooter/Paging.js +8 -6
- package/dist/client/shared/components/PageFooter/Paging.js.map +1 -1
- package/dist/client/shared/components/PageHeader/ImagePreview.js.map +1 -1
- package/dist/client/shared/components/PageHeader/PageHeader.d.ts +1 -0
- package/dist/client/shared/components/PageHeader/PageHeader.js +2 -1
- 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.map +1 -1
- package/dist/client/web/CreeveyApp.d.ts +1 -0
- package/dist/client/web/CreeveyApp.js.map +1 -1
- package/dist/client/web/CreeveyLoader.d.ts +1 -0
- package/dist/client/web/CreeveyLoader.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/Checkbox.d.ts +1 -1
- package/dist/client/web/CreeveyView/SideBar/Checkbox.js +4 -4
- package/dist/client/web/CreeveyView/SideBar/Checkbox.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/Search.d.ts +1 -0
- package/dist/client/web/CreeveyView/SideBar/Search.js +4 -4
- package/dist/client/web/CreeveyView/SideBar/Search.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBar.d.ts +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBar.js +1 -7
- package/dist/client/web/CreeveyView/SideBar/SideBar.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.d.ts +1 -0
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js +5 -4
- package/dist/client/web/CreeveyView/SideBar/SideBarFooter.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.d.ts +1 -0
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js +4 -3
- package/dist/client/web/CreeveyView/SideBar/SideBarHeader.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.d.ts +3 -7
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js +6 -5
- package/dist/client/web/CreeveyView/SideBar/SuiteLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestLink.d.ts +1 -0
- package/dist/client/web/CreeveyView/SideBar/TestLink.js +5 -1
- package/dist/client/web/CreeveyView/SideBar/TestLink.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.js +15 -8
- package/dist/client/web/CreeveyView/SideBar/TestStatusIcon.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.js +5 -4
- package/dist/client/web/CreeveyView/SideBar/TestsStatus.js.map +1 -1
- package/dist/client/web/CreeveyView/SideBar/Toggle.d.ts +1 -0
- package/dist/client/web/CreeveyView/SideBar/Toggle.js.map +1 -1
- package/dist/client/web/KeyboardEventsContext.d.ts +3 -4
- package/dist/client/web/KeyboardEventsContext.js.map +1 -1
- package/dist/client/web/assets/index-DkmZfG9C.js +591 -0
- package/dist/client/web/index.html +1 -1
- package/dist/client/web/index.js +5 -6
- package/dist/client/web/index.js.map +1 -1
- package/dist/creevey.js +21 -9
- package/dist/creevey.js.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/server/config.d.ts +1 -1
- package/dist/server/config.js +9 -5
- package/dist/server/config.js.map +1 -1
- package/dist/server/docker.d.ts +2 -2
- package/dist/server/docker.js +46 -40
- package/dist/server/docker.js.map +1 -1
- package/dist/server/index.js +54 -15
- package/dist/server/index.js.map +1 -1
- package/dist/server/master/master.d.ts +1 -5
- package/dist/server/master/master.js +3 -3
- package/dist/server/master/master.js.map +1 -1
- package/dist/server/master/pool.d.ts +2 -1
- package/dist/server/master/pool.js +13 -7
- package/dist/server/master/pool.js.map +1 -1
- package/dist/server/master/runner.d.ts +1 -1
- package/dist/server/master/runner.js +4 -2
- package/dist/server/master/runner.js.map +1 -1
- package/dist/server/master/server.js +1 -0
- package/dist/server/master/server.js.map +1 -1
- package/dist/server/master/start.d.ts +3 -0
- package/dist/server/master/{index.js → start.js} +6 -9
- package/dist/server/master/start.js.map +1 -0
- package/dist/server/messages.d.ts +4 -10
- package/dist/server/messages.js +4 -58
- package/dist/server/messages.js.map +1 -1
- package/dist/server/playwright/docker-file.d.ts +1 -0
- package/dist/server/playwright/docker-file.js +26 -0
- package/dist/server/playwright/docker-file.js.map +1 -0
- package/dist/server/playwright/docker.d.ts +1 -0
- package/dist/server/playwright/docker.js +31 -0
- package/dist/server/playwright/docker.js.map +1 -0
- package/dist/server/playwright/internal.d.ts +25 -0
- package/dist/server/playwright/internal.js +319 -0
- package/dist/server/playwright/internal.js.map +1 -0
- package/dist/server/playwright/webdriver.d.ts +16 -0
- package/dist/server/playwright/webdriver.js +105 -0
- package/dist/server/playwright/webdriver.js.map +1 -0
- package/dist/server/providers/browser.d.ts +2 -0
- package/dist/server/{storybook/providers → providers}/browser.js +6 -7
- package/dist/server/providers/browser.js.map +1 -0
- package/dist/server/providers/hybrid.d.ts +2 -0
- package/dist/server/{storybook/providers → providers}/hybrid.js +8 -8
- package/dist/server/providers/hybrid.js.map +1 -0
- package/dist/server/reporter.d.ts +26 -0
- package/dist/server/{worker/reporter.js → reporter.js} +34 -56
- package/dist/server/reporter.js.map +1 -0
- package/dist/server/selenium/internal.d.ts +31 -0
- package/dist/server/selenium/internal.js +606 -0
- package/dist/server/selenium/internal.js.map +1 -0
- package/dist/server/selenium/selenoid.js +6 -13
- package/dist/server/selenium/selenoid.js.map +1 -1
- package/dist/server/selenium/webdriver.d.ts +24 -0
- package/dist/server/selenium/webdriver.js +106 -0
- package/dist/server/selenium/webdriver.js.map +1 -0
- package/dist/server/stories.js +16 -9
- package/dist/server/stories.js.map +1 -1
- package/dist/server/telemetry.d.ts +1 -1
- package/dist/server/telemetry.js +4 -4
- package/dist/server/telemetry.js.map +1 -1
- package/dist/server/utils.d.ts +3 -4
- package/dist/server/utils.js +10 -9
- package/dist/server/utils.js.map +1 -1
- package/dist/server/webdriver.d.ts +19 -0
- package/dist/server/webdriver.js +79 -0
- package/dist/server/webdriver.js.map +1 -0
- package/dist/server/worker/chai-image.d.ts +2 -5
- package/dist/server/worker/chai-image.js +14 -102
- package/dist/server/worker/chai-image.js.map +1 -1
- package/dist/server/worker/match-image.d.ts +14 -0
- package/dist/server/worker/match-image.js +231 -0
- package/dist/server/worker/match-image.js.map +1 -0
- package/dist/server/worker/start.d.ts +2 -0
- package/dist/server/worker/start.js +258 -0
- package/dist/server/worker/start.js.map +1 -0
- package/dist/types.d.ts +127 -64
- package/dist/types.js +15 -9
- package/dist/types.js.map +1 -1
- package/package.json +108 -110
- package/src/client/addon/components/Addon.tsx +1 -1
- package/src/client/addon/components/Icons.tsx +1 -1
- package/src/client/addon/components/Panel.tsx +1 -1
- package/src/client/addon/components/TestSelect.tsx +5 -5
- package/src/client/addon/components/Tools.tsx +9 -9
- package/src/client/addon/controller.ts +1 -1
- package/src/client/addon/manager.ts +4 -4
- package/src/client/addon/withCreevey.ts +26 -28
- package/src/client/shared/components/ImagesView/BlendView.tsx +1 -1
- package/src/client/shared/components/ImagesView/ImagesView.tsx +2 -2
- package/src/client/shared/components/ImagesView/SideBySideView.tsx +1 -1
- package/src/client/shared/components/ImagesView/SlideView.tsx +1 -1
- package/src/client/shared/components/ImagesView/SwapView.tsx +1 -1
- package/src/client/shared/components/PageFooter/PageFooter.tsx +2 -2
- package/src/client/shared/components/PageFooter/Paging.tsx +13 -13
- package/src/client/shared/components/PageHeader/ImagePreview.tsx +1 -1
- package/src/client/shared/components/PageHeader/PageHeader.tsx +4 -3
- package/src/client/shared/components/ResultsPage.tsx +1 -1
- package/src/client/web/CreeveyApp.tsx +1 -1
- package/src/client/web/CreeveyLoader.tsx +1 -1
- package/src/client/web/CreeveyView/SideBar/Checkbox.tsx +6 -7
- package/src/client/web/CreeveyView/SideBar/Search.tsx +4 -4
- package/src/client/web/CreeveyView/SideBar/SideBar.tsx +3 -10
- package/src/client/web/CreeveyView/SideBar/SideBarFooter.tsx +7 -6
- package/src/client/web/CreeveyView/SideBar/SideBarHeader.tsx +7 -6
- package/src/client/web/CreeveyView/SideBar/SuiteLink.tsx +8 -6
- package/src/client/web/CreeveyView/SideBar/TestLink.tsx +8 -3
- package/src/client/web/CreeveyView/SideBar/TestStatusIcon.tsx +18 -10
- package/src/client/web/CreeveyView/SideBar/TestsStatus.tsx +7 -10
- package/src/client/web/CreeveyView/SideBar/Toggle.tsx +1 -2
- package/src/client/web/KeyboardEventsContext.tsx +3 -4
- package/src/client/web/index.html +1 -1
- package/src/client/web/index.tsx +4 -3
- package/src/creevey.ts +25 -8
- package/src/index.ts +4 -2
- package/src/server/config.ts +12 -8
- package/src/server/docker.ts +58 -44
- package/src/server/index.ts +57 -18
- package/src/server/master/master.ts +3 -6
- package/src/server/master/pool.ts +25 -9
- package/src/server/master/runner.ts +4 -2
- package/src/server/master/server.ts +1 -0
- package/src/server/master/{index.ts → start.ts} +13 -11
- package/src/server/messages.ts +11 -75
- package/src/server/playwright/docker-file.ts +21 -0
- package/src/server/playwright/docker.ts +41 -0
- package/src/server/playwright/internal.ts +387 -0
- package/src/server/playwright/webdriver.ts +126 -0
- package/src/server/{storybook/providers → providers}/browser.ts +7 -8
- package/src/server/{storybook/providers → providers}/hybrid.ts +19 -19
- package/src/server/{worker/reporter.ts → reporter.ts} +40 -72
- package/src/server/selenium/internal.ts +785 -0
- package/src/server/selenium/selenoid.ts +12 -17
- package/src/server/selenium/webdriver.ts +136 -0
- package/src/server/stories.ts +18 -11
- package/src/server/telemetry.ts +2 -2
- package/src/server/utils.ts +9 -9
- package/src/server/webdriver.ts +127 -0
- package/src/server/worker/chai-image.ts +21 -133
- package/src/server/worker/match-image.ts +303 -0
- package/src/server/worker/start.ts +303 -0
- package/src/types.ts +162 -60
- package/dist/client/web/202.js +0 -1
- package/dist/client/web/270.js +0 -43
- package/dist/client/web/752.js +0 -1
- package/dist/client/web/main.js +0 -79
- package/dist/client/web/main.js.LICENSE.txt +0 -34
- package/dist/server/master/index.d.ts +0 -3
- package/dist/server/master/index.js.map +0 -1
- package/dist/server/selenium/browser.d.ts +0 -19
- package/dist/server/selenium/browser.js +0 -640
- package/dist/server/selenium/browser.js.map +0 -1
- package/dist/server/selenium/index.d.ts +0 -2
- package/dist/server/selenium/index.js +0 -19
- package/dist/server/selenium/index.js.map +0 -1
- package/dist/server/storybook/providers/browser.d.ts +0 -2
- package/dist/server/storybook/providers/browser.js.map +0 -1
- package/dist/server/storybook/providers/hybrid.d.ts +0 -2
- package/dist/server/storybook/providers/hybrid.js.map +0 -1
- package/dist/server/worker/helpers.d.ts +0 -8
- package/dist/server/worker/helpers.js +0 -57
- package/dist/server/worker/helpers.js.map +0 -1
- package/dist/server/worker/index.d.ts +0 -1
- package/dist/server/worker/index.js +0 -6
- package/dist/server/worker/index.js.map +0 -1
- package/dist/server/worker/reporter.d.ts +0 -8
- package/dist/server/worker/reporter.js.map +0 -1
- package/dist/server/worker/worker.d.ts +0 -4
- package/dist/server/worker/worker.js +0 -212
- package/dist/server/worker/worker.js.map +0 -1
- package/src/server/selenium/browser.ts +0 -840
- package/src/server/selenium/index.ts +0 -2
- package/src/server/worker/helpers.ts +0 -61
- package/src/server/worker/index.ts +0 -1
- package/src/server/worker/worker.ts +0 -240
- package/types/mocha.d.ts +0 -20
package/src/server/messages.ts
CHANGED
@@ -3,14 +3,10 @@ import {
|
|
3
3
|
WorkerMessage,
|
4
4
|
StoriesMessage,
|
5
5
|
TestMessage,
|
6
|
-
WebpackMessage,
|
7
|
-
DockerMessage,
|
8
6
|
ProcessMessage,
|
9
7
|
WorkerHandler,
|
10
8
|
StoriesHandler,
|
11
9
|
TestHandler,
|
12
|
-
WebpackHandler,
|
13
|
-
DockerHandler,
|
14
10
|
ShutdownHandler,
|
15
11
|
} from '../types.js';
|
16
12
|
|
@@ -35,14 +31,6 @@ export function emitTestMessage(message: TestMessage): boolean {
|
|
35
31
|
return emitMessage({ scope: 'test', ...message });
|
36
32
|
}
|
37
33
|
|
38
|
-
export function emitWebpackMessage(message: WebpackMessage): boolean {
|
39
|
-
return emitMessage({ scope: 'webpack', ...message });
|
40
|
-
}
|
41
|
-
|
42
|
-
export function emitDockerMessage(message: DockerMessage): boolean {
|
43
|
-
return emitMessage({ scope: 'docker', ...message });
|
44
|
-
}
|
45
|
-
|
46
34
|
export function emitShutdownMessage(): boolean {
|
47
35
|
return emitMessage({ scope: 'shutdown' });
|
48
36
|
}
|
@@ -51,8 +39,6 @@ interface Handlers {
|
|
51
39
|
worker: Set<WorkerHandler>;
|
52
40
|
stories: Set<StoriesHandler>;
|
53
41
|
test: Set<TestHandler>;
|
54
|
-
webpack: Set<WebpackHandler>;
|
55
|
-
docker: Set<DockerHandler>;
|
56
42
|
shutdown: Set<ShutdownHandler>;
|
57
43
|
}
|
58
44
|
|
@@ -61,8 +47,6 @@ function createHandlers(): Handlers {
|
|
61
47
|
worker: new Set<WorkerHandler>(),
|
62
48
|
stories: new Set<StoriesHandler>(),
|
63
49
|
test: new Set<TestHandler>(),
|
64
|
-
webpack: new Set<WebpackHandler>(),
|
65
|
-
docker: new Set<DockerHandler>(),
|
66
50
|
shutdown: new Set<ShutdownHandler>(),
|
67
51
|
});
|
68
52
|
}
|
@@ -89,18 +73,6 @@ const handler = (message: ProcessMessage): void => {
|
|
89
73
|
});
|
90
74
|
return;
|
91
75
|
}
|
92
|
-
case 'webpack': {
|
93
|
-
handlers.webpack.forEach((h) => {
|
94
|
-
h(message);
|
95
|
-
});
|
96
|
-
return;
|
97
|
-
}
|
98
|
-
case 'docker': {
|
99
|
-
handlers.docker.forEach((h) => {
|
100
|
-
h(message);
|
101
|
-
});
|
102
|
-
return;
|
103
|
-
}
|
104
76
|
case 'shutdown': {
|
105
77
|
handlers.shutdown.forEach((h) => {
|
106
78
|
h(message);
|
@@ -117,27 +89,25 @@ export function sendStoriesMessage(target: NodeJS.Process | Worker, message: Sto
|
|
117
89
|
export function sendTestMessage(target: NodeJS.Process | Worker, message: TestMessage): void {
|
118
90
|
target.send?.({ scope: 'test', ...message });
|
119
91
|
}
|
120
|
-
export function sendDockerMessage(target: NodeJS.Process | Worker, message: DockerMessage): void {
|
121
|
-
target.send?.({ scope: 'docker', ...message });
|
122
|
-
}
|
123
92
|
export function sendShutdownMessage(target: NodeJS.Process | Worker): void {
|
124
93
|
target.send?.({ scope: 'shutdown' });
|
125
94
|
}
|
95
|
+
export function sendWorkerMessage(target: NodeJS.Process | Worker, message: WorkerMessage): void {
|
96
|
+
target.send?.({ scope: 'worker', ...message });
|
97
|
+
}
|
126
98
|
|
127
99
|
export function subscribeOn(scope: 'worker', handler: WorkerHandler): () => void;
|
128
100
|
export function subscribeOn(scope: 'stories', handler: StoriesHandler): () => void;
|
129
101
|
export function subscribeOn(scope: 'test', handler: TestHandler): () => void;
|
130
|
-
export function subscribeOn(scope: 'webpack', handler: WebpackHandler): () => void;
|
131
|
-
export function subscribeOn(scope: 'docker', handler: DockerHandler): () => void;
|
132
102
|
export function subscribeOn(scope: 'shutdown', handler: ShutdownHandler): () => void;
|
133
103
|
export function subscribeOn(
|
134
|
-
scope: 'worker' | 'stories' | 'test' | '
|
135
|
-
handler: WorkerHandler | StoriesHandler | TestHandler |
|
104
|
+
scope: 'worker' | 'stories' | 'test' | 'shutdown',
|
105
|
+
handler: WorkerHandler | StoriesHandler | TestHandler | ShutdownHandler,
|
136
106
|
): () => void;
|
137
107
|
|
138
108
|
export function subscribeOn(
|
139
|
-
scope: 'worker' | 'stories' | 'test' | '
|
140
|
-
handler: WorkerHandler | StoriesHandler | TestHandler |
|
109
|
+
scope: 'worker' | 'stories' | 'test' | 'shutdown',
|
110
|
+
handler: WorkerHandler | StoriesHandler | TestHandler | ShutdownHandler,
|
141
111
|
): () => void {
|
142
112
|
switch (scope) {
|
143
113
|
case 'worker': {
|
@@ -155,16 +125,6 @@ export function subscribeOn(
|
|
155
125
|
handlers.test.add(testHandler);
|
156
126
|
return () => handlers.test.delete(testHandler);
|
157
127
|
}
|
158
|
-
case 'webpack': {
|
159
|
-
const webpackHandler = handler as WebpackHandler;
|
160
|
-
handlers.webpack.add(webpackHandler);
|
161
|
-
return () => handlers.webpack.delete(webpackHandler);
|
162
|
-
}
|
163
|
-
case 'docker': {
|
164
|
-
const dockerHandler = handler as DockerHandler;
|
165
|
-
handlers.docker.add(dockerHandler);
|
166
|
-
return () => handlers.docker.delete(dockerHandler);
|
167
|
-
}
|
168
128
|
case 'shutdown': {
|
169
129
|
const shutdownHandler = handler as ShutdownHandler;
|
170
130
|
handlers.shutdown.add(shutdownHandler);
|
@@ -178,19 +138,17 @@ const workers = new Map<Worker, Handlers>();
|
|
178
138
|
export function subscribeOnWorker(worker: Worker, scope: 'worker', handler: WorkerHandler): () => void;
|
179
139
|
export function subscribeOnWorker(worker: Worker, scope: 'stories', handler: StoriesHandler): () => void;
|
180
140
|
export function subscribeOnWorker(worker: Worker, scope: 'test', handler: TestHandler): () => void;
|
181
|
-
export function subscribeOnWorker(worker: Worker, scope: 'webpack', handler: WebpackHandler): () => void;
|
182
|
-
export function subscribeOnWorker(worker: Worker, scope: 'docker', handler: DockerHandler): () => void;
|
183
141
|
export function subscribeOnWorker(worker: Worker, scope: 'shutdown', handler: ShutdownHandler): () => void;
|
184
142
|
export function subscribeOnWorker(
|
185
143
|
worker: Worker,
|
186
|
-
scope: 'worker' | 'stories' | 'test' | '
|
187
|
-
handler: WorkerHandler | StoriesHandler | TestHandler |
|
144
|
+
scope: 'worker' | 'stories' | 'test' | 'shutdown',
|
145
|
+
handler: WorkerHandler | StoriesHandler | TestHandler | ShutdownHandler,
|
188
146
|
): () => void;
|
189
147
|
|
190
148
|
export function subscribeOnWorker(
|
191
149
|
worker: Worker,
|
192
|
-
scope: 'worker' | 'stories' | 'test' | '
|
193
|
-
handler: WorkerHandler | StoriesHandler | TestHandler |
|
150
|
+
scope: 'worker' | 'stories' | 'test' | 'shutdown',
|
151
|
+
handler: WorkerHandler | StoriesHandler | TestHandler | ShutdownHandler,
|
194
152
|
): () => void {
|
195
153
|
const workerHandlers = workers.get(worker) ?? createHandlers();
|
196
154
|
if (!workers.has(worker)) {
|
@@ -216,18 +174,6 @@ export function subscribeOnWorker(
|
|
216
174
|
});
|
217
175
|
return;
|
218
176
|
}
|
219
|
-
case 'webpack': {
|
220
|
-
workerHandlers.webpack.forEach((h) => {
|
221
|
-
h(message);
|
222
|
-
});
|
223
|
-
return;
|
224
|
-
}
|
225
|
-
case 'docker': {
|
226
|
-
workerHandlers.docker.forEach((h) => {
|
227
|
-
h(message);
|
228
|
-
});
|
229
|
-
return;
|
230
|
-
}
|
231
177
|
case 'shutdown': {
|
232
178
|
workerHandlers.shutdown.forEach((h) => {
|
233
179
|
h(message);
|
@@ -254,16 +200,6 @@ export function subscribeOnWorker(
|
|
254
200
|
workerHandlers.test.add(testHandler);
|
255
201
|
return () => workerHandlers.test.delete(testHandler);
|
256
202
|
}
|
257
|
-
case 'webpack': {
|
258
|
-
const webpackHandler = handler as WebpackHandler;
|
259
|
-
workerHandlers.webpack.add(webpackHandler);
|
260
|
-
return () => workerHandlers.webpack.delete(webpackHandler);
|
261
|
-
}
|
262
|
-
case 'docker': {
|
263
|
-
const dockerHandler = handler as DockerHandler;
|
264
|
-
workerHandlers.docker.add(dockerHandler);
|
265
|
-
return () => workerHandlers.docker.delete(dockerHandler);
|
266
|
-
}
|
267
203
|
case 'shutdown': {
|
268
204
|
const shutdownHandler = handler as ShutdownHandler;
|
269
205
|
workerHandlers.shutdown.add(shutdownHandler);
|
@@ -0,0 +1,21 @@
|
|
1
|
+
import semver from 'semver';
|
2
|
+
|
3
|
+
// TODO Support custom docker images
|
4
|
+
export function playwrightDockerFile(browser: string, version: string): string {
|
5
|
+
const sv = semver.coerce(version);
|
6
|
+
|
7
|
+
return `
|
8
|
+
FROM mcr.microsoft.com/playwright:v${sv?.format() ?? version}
|
9
|
+
|
10
|
+
WORKDIR /creevey
|
11
|
+
|
12
|
+
RUN echo "{ \\"type\\": \\"module\\" }" > package.json && \\
|
13
|
+
echo "import { ${browser} as browser } from 'playwright-core';" >> index.js && \\
|
14
|
+
echo "const ws = await browser.launchServer({ port: 4444, wsPath: 'creevey' })" >> index.js && \\
|
15
|
+
npm i playwright-core${sv ? `@${sv.format()}` : ''}
|
16
|
+
|
17
|
+
EXPOSE 4444
|
18
|
+
|
19
|
+
ENTRYPOINT [ "node", "./index.js" ]
|
20
|
+
`;
|
21
|
+
}
|
@@ -0,0 +1,41 @@
|
|
1
|
+
import { buildImage, runImage } from '../docker';
|
2
|
+
import { emitWorkerMessage, subscribeOn } from '../messages';
|
3
|
+
import { isInsideDocker } from '../utils';
|
4
|
+
import { LOCALHOST_REGEXP } from '../webdriver';
|
5
|
+
import { playwrightDockerFile } from './docker-file';
|
6
|
+
|
7
|
+
export async function startPlaywrightContainer(browserName: string, debug: boolean): Promise<string> {
|
8
|
+
const {
|
9
|
+
default: { version },
|
10
|
+
} = await import('playwright-core/package.json', { with: { type: 'json' } });
|
11
|
+
|
12
|
+
const imageName = `creevey/${browserName}:v${version}`;
|
13
|
+
const dockerfile = playwrightDockerFile(browserName, version);
|
14
|
+
|
15
|
+
await buildImage(imageName, dockerfile);
|
16
|
+
|
17
|
+
const port = await new Promise<number>((resolve) => {
|
18
|
+
subscribeOn('worker', (message) => {
|
19
|
+
if (message.type == 'port') {
|
20
|
+
resolve(message.payload.port);
|
21
|
+
}
|
22
|
+
});
|
23
|
+
emitWorkerMessage({ type: 'port', payload: { port: -1 } });
|
24
|
+
});
|
25
|
+
|
26
|
+
const host = await runImage(
|
27
|
+
imageName,
|
28
|
+
[],
|
29
|
+
{
|
30
|
+
ExposedPorts: { [`${port}/tcp`]: {} },
|
31
|
+
HostConfig: {
|
32
|
+
PortBindings: { ['4444/tcp']: [{ HostPort: `${port}` }] },
|
33
|
+
},
|
34
|
+
},
|
35
|
+
debug,
|
36
|
+
);
|
37
|
+
|
38
|
+
const gridUrl = `ws://localhost:${port}/creevey`;
|
39
|
+
|
40
|
+
return isInsideDocker ? gridUrl.replace(LOCALHOST_REGEXP, host) : gridUrl;
|
41
|
+
}
|
@@ -0,0 +1,387 @@
|
|
1
|
+
import { Browser, BrowserType, Page, chromium, firefox, webkit } from 'playwright-core';
|
2
|
+
import Logger from 'loglevel';
|
3
|
+
import chalk from 'chalk';
|
4
|
+
import { v4 } from 'uuid';
|
5
|
+
import prefix from 'loglevel-plugin-prefix';
|
6
|
+
import { SET_GLOBALS, STORY_RENDERED, UPDATE_STORY_ARGS } from '@storybook/core-events';
|
7
|
+
import { BrowserConfigObject, Config, Options, StoriesRaw, StoryInput, StorybookGlobals, noop } from '../../types';
|
8
|
+
import { subscribeOn } from '../messages';
|
9
|
+
import { appendIframePath, getAddresses, LOCALHOST_REGEXP, resolveStorybookUrl, storybookRootID } from '../webdriver';
|
10
|
+
import { isShuttingDown, runSequence } from '../utils';
|
11
|
+
import { colors, logger } from '../logger';
|
12
|
+
import { Args } from '@storybook/csf';
|
13
|
+
|
14
|
+
async function tryConnect(type: BrowserType, gridUrl: string): Promise<Browser | null> {
|
15
|
+
let timeout: NodeJS.Timeout | null = null;
|
16
|
+
let isTimeout = false;
|
17
|
+
let error: unknown = null;
|
18
|
+
return Promise.race([
|
19
|
+
new Promise<null>(
|
20
|
+
(resolve) =>
|
21
|
+
(timeout = setTimeout(() => {
|
22
|
+
isTimeout = true;
|
23
|
+
logger.error(`Can't connect to ${type.name()} playwright browser`, error);
|
24
|
+
resolve(null);
|
25
|
+
}, 10000)),
|
26
|
+
),
|
27
|
+
(async () => {
|
28
|
+
let browser: Browser | null = null;
|
29
|
+
do {
|
30
|
+
try {
|
31
|
+
browser = await type.connect(gridUrl);
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
33
|
+
if (timeout) clearTimeout(timeout);
|
34
|
+
break;
|
35
|
+
} catch (e: unknown) {
|
36
|
+
error = e;
|
37
|
+
}
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
39
|
+
} while (!isTimeout);
|
40
|
+
return browser;
|
41
|
+
})(),
|
42
|
+
]);
|
43
|
+
}
|
44
|
+
|
45
|
+
export class InternalBrowser {
|
46
|
+
#isShuttingDown = false;
|
47
|
+
#browser: Browser;
|
48
|
+
#page: Page;
|
49
|
+
#sessionId: string = v4();
|
50
|
+
#serverHost: string | null = null;
|
51
|
+
#serverPort: number;
|
52
|
+
#logger: Logger.Logger;
|
53
|
+
#unsubscribe: () => void = noop;
|
54
|
+
constructor(browser: Browser, page: Page, port: number) {
|
55
|
+
this.#browser = browser;
|
56
|
+
this.#page = page;
|
57
|
+
this.#serverPort = port;
|
58
|
+
this.#logger = Logger.getLogger(this.#sessionId);
|
59
|
+
this.#unsubscribe = subscribeOn('shutdown', () => {
|
60
|
+
void this.closeBrowser();
|
61
|
+
});
|
62
|
+
}
|
63
|
+
|
64
|
+
get browser() {
|
65
|
+
return this.#page;
|
66
|
+
}
|
67
|
+
|
68
|
+
get sessionId() {
|
69
|
+
return this.#sessionId;
|
70
|
+
}
|
71
|
+
|
72
|
+
async closeBrowser(): Promise<void> {
|
73
|
+
if (this.#isShuttingDown) return;
|
74
|
+
|
75
|
+
this.#isShuttingDown = true;
|
76
|
+
this.#unsubscribe();
|
77
|
+
|
78
|
+
try {
|
79
|
+
await this.#page.close();
|
80
|
+
await this.#browser.close();
|
81
|
+
} catch (_) {
|
82
|
+
/* noop */
|
83
|
+
}
|
84
|
+
}
|
85
|
+
|
86
|
+
async takeScreenshot(captureElement?: string | null, ignoreElements?: string | string[] | null): Promise<Buffer> {
|
87
|
+
// TODO Implement features from selenium `takeScreenshot`
|
88
|
+
// TODO Do we need scroll bar hack from selenium?
|
89
|
+
const ignore = Array.isArray(ignoreElements) ? ignoreElements : ignoreElements ? [ignoreElements] : [];
|
90
|
+
const mask = ignore.map((el) => this.#page.locator(el));
|
91
|
+
if (captureElement) {
|
92
|
+
const element = await this.#page.$(captureElement);
|
93
|
+
if (!element) throw new Error(`Element with selector ${captureElement} not found`);
|
94
|
+
return element.screenshot({ animations: 'disabled', mask });
|
95
|
+
}
|
96
|
+
return this.#page.screenshot({ animations: 'disabled', mask, fullPage: true });
|
97
|
+
}
|
98
|
+
|
99
|
+
waitForComplete(callback: (isCompleted: boolean) => void): void {
|
100
|
+
void this.#page.evaluate<boolean>(() => window.__CREEVEY_HAS_PLAY_COMPLETED_YET__()).then(callback);
|
101
|
+
}
|
102
|
+
|
103
|
+
async selectStory(id: string, waitForReady = false): Promise<boolean> {
|
104
|
+
// NOTE: Global variables might be reset after hot reload. I think it's workaround, maybe we need better solution
|
105
|
+
await this.updateBrowserGlobalVariables();
|
106
|
+
await this.resetMousePosition();
|
107
|
+
|
108
|
+
this.#logger.debug(`Triggering 'SetCurrentStory' event with storyId ${chalk.magenta(id)}`);
|
109
|
+
|
110
|
+
const result = await this.#page.evaluate<
|
111
|
+
[error?: string | null, isCaptureCalled?: boolean] | null,
|
112
|
+
[id: string, shouldWaitForReady: boolean]
|
113
|
+
>(
|
114
|
+
([id, shouldWaitForReady]) => {
|
115
|
+
if (typeof window.__CREEVEY_SELECT_STORY__ == 'undefined') {
|
116
|
+
return [
|
117
|
+
"Creevey can't switch story. This may happened if forget to add `creevey` addon to your storybook config, or storybook not loaded in browser due syntax error.",
|
118
|
+
];
|
119
|
+
}
|
120
|
+
return window.__CREEVEY_SELECT_STORY__(id, shouldWaitForReady);
|
121
|
+
},
|
122
|
+
[id, waitForReady],
|
123
|
+
);
|
124
|
+
|
125
|
+
const [errorMessage, isCaptureCalled = false] = result ?? [];
|
126
|
+
|
127
|
+
if (errorMessage) throw new Error(errorMessage);
|
128
|
+
|
129
|
+
return isCaptureCalled;
|
130
|
+
}
|
131
|
+
|
132
|
+
async updateStoryArgs(story: StoryInput, updatedArgs: Args): Promise<void> {
|
133
|
+
await this.#page.evaluate(
|
134
|
+
([storyId, updatedArgs, UPDATE_STORY_ARGS, STORY_RENDERED]) => {
|
135
|
+
return new Promise((resolve) => {
|
136
|
+
window.__STORYBOOK_ADDONS_CHANNEL__.once(STORY_RENDERED, resolve);
|
137
|
+
window.__STORYBOOK_ADDONS_CHANNEL__.emit(UPDATE_STORY_ARGS, {
|
138
|
+
storyId,
|
139
|
+
updatedArgs,
|
140
|
+
});
|
141
|
+
});
|
142
|
+
},
|
143
|
+
[story.id, updatedArgs, UPDATE_STORY_ARGS, STORY_RENDERED] as const,
|
144
|
+
);
|
145
|
+
}
|
146
|
+
|
147
|
+
async loadStoriesFromBrowser(retry = false): Promise<StoriesRaw> {
|
148
|
+
try {
|
149
|
+
const stories = await this.#page.evaluate<StoriesRaw | undefined>(() => window.__CREEVEY_GET_STORIES__());
|
150
|
+
|
151
|
+
if (!stories) throw new Error("Can't get stories, it seems creevey or storybook API isn't available");
|
152
|
+
|
153
|
+
return stories;
|
154
|
+
} catch (error) {
|
155
|
+
// TODO Check how other solutions with playwright get stories from storybook
|
156
|
+
if (retry) throw error;
|
157
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
158
|
+
// NOTE: Try one more time because of dynamic nature of vite and storybook
|
159
|
+
return this.loadStoriesFromBrowser(true);
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
static async getBrowser(
|
164
|
+
browserName: string,
|
165
|
+
gridUrl: string,
|
166
|
+
config: Config,
|
167
|
+
options: Options,
|
168
|
+
): Promise<InternalBrowser | null> {
|
169
|
+
const browserConfig = config.browsers[browserName] as BrowserConfigObject;
|
170
|
+
const { storybookUrl: address = config.storybookUrl, viewport, _storybookGlobals } = browserConfig;
|
171
|
+
|
172
|
+
let browser: Browser | null = null;
|
173
|
+
|
174
|
+
// TODO Support Selenium Grid 4
|
175
|
+
switch (browserConfig.browserName) {
|
176
|
+
case 'chromium':
|
177
|
+
browser = await tryConnect(chromium, gridUrl);
|
178
|
+
break;
|
179
|
+
case 'firefox':
|
180
|
+
browser = await tryConnect(firefox, gridUrl);
|
181
|
+
break;
|
182
|
+
case 'webkit':
|
183
|
+
browser = await tryConnect(webkit, gridUrl);
|
184
|
+
break;
|
185
|
+
|
186
|
+
default:
|
187
|
+
throw new Error(
|
188
|
+
`Unknown browser ${browserConfig.browserName}. Playwright supports browsers: chromium, firefox, webkit`,
|
189
|
+
);
|
190
|
+
}
|
191
|
+
|
192
|
+
if (!browser) {
|
193
|
+
return null;
|
194
|
+
}
|
195
|
+
|
196
|
+
const page = await browser.newPage();
|
197
|
+
|
198
|
+
// TODO Add debug output
|
199
|
+
|
200
|
+
const internalBrowser = new InternalBrowser(browser, page, options.port);
|
201
|
+
|
202
|
+
try {
|
203
|
+
if (isShuttingDown.current) return null;
|
204
|
+
const done = await internalBrowser.init({
|
205
|
+
browserName,
|
206
|
+
viewport,
|
207
|
+
storybookUrl: address,
|
208
|
+
storybookGlobals: _storybookGlobals,
|
209
|
+
resolveStorybookUrl: config.resolveStorybookUrl,
|
210
|
+
});
|
211
|
+
|
212
|
+
return done ? internalBrowser : null;
|
213
|
+
} catch (originalError) {
|
214
|
+
void internalBrowser.closeBrowser();
|
215
|
+
|
216
|
+
const message = originalError instanceof Error ? originalError.message : (originalError as string);
|
217
|
+
const error = new Error(`Can't load storybook root page: ${message}`);
|
218
|
+
if (originalError instanceof Error) error.stack = originalError.stack;
|
219
|
+
|
220
|
+
logger.error(error);
|
221
|
+
|
222
|
+
return null;
|
223
|
+
}
|
224
|
+
}
|
225
|
+
|
226
|
+
private async init({
|
227
|
+
browserName,
|
228
|
+
viewport,
|
229
|
+
storybookUrl,
|
230
|
+
storybookGlobals,
|
231
|
+
resolveStorybookUrl,
|
232
|
+
}: {
|
233
|
+
browserName: string;
|
234
|
+
viewport?: { width: number; height: number };
|
235
|
+
storybookUrl: string;
|
236
|
+
storybookGlobals?: StorybookGlobals;
|
237
|
+
resolveStorybookUrl?: () => Promise<string>;
|
238
|
+
}) {
|
239
|
+
const sessionId = this.#sessionId;
|
240
|
+
|
241
|
+
prefix.apply(this.#logger, {
|
242
|
+
format(level) {
|
243
|
+
const levelColor = colors[level.toUpperCase() as keyof typeof colors];
|
244
|
+
return `[${browserName}:${chalk.gray(sessionId)}] ${levelColor(level)} =>`;
|
245
|
+
},
|
246
|
+
});
|
247
|
+
|
248
|
+
this.#page.setDefaultNavigationTimeout(10000);
|
249
|
+
this.#page.setDefaultTimeout(60000);
|
250
|
+
|
251
|
+
return await runSequence(
|
252
|
+
[
|
253
|
+
() => this.openStorybookPage(storybookUrl, resolveStorybookUrl),
|
254
|
+
() => this.waitForStorybook(),
|
255
|
+
() => this.updateStorybookGlobals(storybookGlobals),
|
256
|
+
() => this.resolveCreeveyHost(),
|
257
|
+
() => this.updateBrowserGlobalVariables(),
|
258
|
+
() => this.resizeViewport(viewport),
|
259
|
+
],
|
260
|
+
() => !this.#isShuttingDown,
|
261
|
+
);
|
262
|
+
}
|
263
|
+
|
264
|
+
private async openStorybookPage(storybookUrl: string, resolver?: () => Promise<string>): Promise<void> {
|
265
|
+
if (!LOCALHOST_REGEXP.test(storybookUrl)) {
|
266
|
+
await this.#page.goto(appendIframePath(storybookUrl));
|
267
|
+
return;
|
268
|
+
}
|
269
|
+
|
270
|
+
try {
|
271
|
+
if (resolver) {
|
272
|
+
this.#logger.debug('Resolving storybook url with custom resolver');
|
273
|
+
|
274
|
+
const resolvedUrl = await resolver();
|
275
|
+
|
276
|
+
this.#logger.debug(`Resolver storybook url ${resolvedUrl}`);
|
277
|
+
|
278
|
+
await this.#page.goto(appendIframePath(resolvedUrl));
|
279
|
+
} else {
|
280
|
+
await resolveStorybookUrl(appendIframePath(storybookUrl), (url) => this.checkUrl(url), this.#logger);
|
281
|
+
}
|
282
|
+
} catch (error) {
|
283
|
+
this.#logger.error('Failed to resolve storybook URL', error instanceof Error ? error.message : '');
|
284
|
+
throw error;
|
285
|
+
}
|
286
|
+
}
|
287
|
+
|
288
|
+
private async checkUrl(url: string): Promise<boolean> {
|
289
|
+
try {
|
290
|
+
this.#logger.debug(`Opening ${chalk.magenta(url)} and checking the page source`);
|
291
|
+
const response = await this.#page.goto(url, { waitUntil: 'commit' });
|
292
|
+
const source = await response?.text();
|
293
|
+
|
294
|
+
this.#logger.debug(`Checking ${chalk.cyan(`#${storybookRootID}`)} existence on ${chalk.magenta(url)}`);
|
295
|
+
return source?.includes(`id="${storybookRootID}"`) ?? false;
|
296
|
+
} catch {
|
297
|
+
return false;
|
298
|
+
}
|
299
|
+
}
|
300
|
+
|
301
|
+
private async waitForStorybook(): Promise<void> {
|
302
|
+
// TODO Duplicated code with selenium
|
303
|
+
this.#logger.debug('Waiting for `setStories` event to make sure that storybook is initiated');
|
304
|
+
|
305
|
+
const isTimeout = await Promise.race([
|
306
|
+
new Promise<boolean>((resolve) => {
|
307
|
+
setTimeout(() => {
|
308
|
+
resolve(true);
|
309
|
+
}, 60000);
|
310
|
+
}),
|
311
|
+
(async () => {
|
312
|
+
let wait = true;
|
313
|
+
do {
|
314
|
+
try {
|
315
|
+
// TODO Research a different way to ensure storybook is initiated
|
316
|
+
wait = await this.#page.evaluate((SET_GLOBALS: string) => {
|
317
|
+
if (typeof window.__STORYBOOK_ADDONS_CHANNEL__ == 'undefined') return true;
|
318
|
+
if (window.__STORYBOOK_ADDONS_CHANNEL__.last(SET_GLOBALS) == undefined) return true;
|
319
|
+
return false;
|
320
|
+
}, SET_GLOBALS);
|
321
|
+
} catch (e: unknown) {
|
322
|
+
this.#logger.debug('An error has been caught during the script:', e);
|
323
|
+
}
|
324
|
+
} while (wait);
|
325
|
+
return false;
|
326
|
+
})(),
|
327
|
+
]);
|
328
|
+
|
329
|
+
// TODO Change the message to describe a reason why it might happen
|
330
|
+
if (isTimeout) throw new Error('Failed to wait `setStories` event');
|
331
|
+
}
|
332
|
+
|
333
|
+
private async updateStorybookGlobals(globals?: StorybookGlobals): Promise<void> {
|
334
|
+
if (!globals) return;
|
335
|
+
|
336
|
+
this.#logger.debug('Applying storybook globals');
|
337
|
+
await this.#page.evaluate((globals: StorybookGlobals) => {
|
338
|
+
window.__CREEVEY_UPDATE_GLOBALS__(globals);
|
339
|
+
}, globals);
|
340
|
+
}
|
341
|
+
|
342
|
+
private async resolveCreeveyHost(): Promise<void> {
|
343
|
+
const addresses = getAddresses();
|
344
|
+
|
345
|
+
this.#serverHost = await this.#page.evaluate(
|
346
|
+
([hosts, port]) => {
|
347
|
+
return Promise.all(
|
348
|
+
hosts.map((host) => {
|
349
|
+
return Promise.race([
|
350
|
+
// eslint-disable-next-line @typescript-eslint/restrict-plus-operands
|
351
|
+
fetch('http://' + host + ':' + port + '/ping').then((response) => response.text()),
|
352
|
+
new Promise((_resolve, reject) => {
|
353
|
+
setTimeout(reject, 5000);
|
354
|
+
}),
|
355
|
+
])
|
356
|
+
.then((pong) => (pong == 'pong' ? host : null))
|
357
|
+
.catch(() => null);
|
358
|
+
}),
|
359
|
+
).then((hosts) => hosts.find((host) => host != null) ?? null);
|
360
|
+
},
|
361
|
+
[addresses, this.#serverPort] as const,
|
362
|
+
);
|
363
|
+
|
364
|
+
if (this.#serverHost == null) throw new Error("Can't reach creevey server from a browser");
|
365
|
+
}
|
366
|
+
|
367
|
+
private async updateBrowserGlobalVariables() {
|
368
|
+
await this.#page.evaluate(
|
369
|
+
([workerId, creeveyHost, creeveyPort]) => {
|
370
|
+
window.__CREEVEY_WORKER_ID__ = workerId;
|
371
|
+
window.__CREEVEY_SERVER_HOST__ = creeveyHost ?? 'localhost';
|
372
|
+
window.__CREEVEY_SERVER_PORT__ = creeveyPort;
|
373
|
+
},
|
374
|
+
[process.pid, this.#serverHost, this.#serverPort] as const,
|
375
|
+
);
|
376
|
+
}
|
377
|
+
|
378
|
+
private async resizeViewport(viewport?: { width: number; height: number }): Promise<void> {
|
379
|
+
if (!viewport) return;
|
380
|
+
|
381
|
+
await this.#page.setViewportSize(viewport);
|
382
|
+
}
|
383
|
+
|
384
|
+
private async resetMousePosition(): Promise<void> {
|
385
|
+
await this.#page.mouse.move(0, 0);
|
386
|
+
}
|
387
|
+
}
|