@w-lfpup/jackrabbit 0.1.0 → 0.3.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/.github/workflows/browsers.json +45 -0
- package/.github/workflows/browsers.macos.json +51 -0
- package/.github/workflows/browsers.windows.json +19 -0
- package/.github/workflows/tests.yml +42 -0
- package/README.md +151 -8
- package/browser/dist/logger.js +43 -0
- package/browser/dist/mod.js +26 -0
- package/browser/dist/queue.js +27 -0
- package/browser/dist/runner.js +20 -0
- package/{cli → browser}/package.json +1 -1
- package/browser/src/logger.ts +57 -0
- package/browser/src/mod.ts +30 -0
- package/browser/src/runner.ts +22 -0
- package/browser/tsconfig.json +11 -0
- package/browser/tsconfig.tsbuildinfo +1 -0
- package/browsers.json +38 -0
- package/core/dist/jackrabbit_types.d.ts +62 -28
- package/core/dist/mod.d.ts +2 -2
- package/core/dist/mod.js +1 -1
- package/core/dist/run_steps.d.ts +2 -2
- package/core/dist/run_steps.js +83 -67
- package/core/src/jackrabbit_types.ts +73 -29
- package/core/src/mod.ts +2 -8
- package/core/src/run_steps.ts +111 -80
- package/examples/hello_world/goodbye_world.ts +1 -1
- package/examples/hello_world/hello_world.ts +1 -1
- package/nodejs/dist/logger.js +161 -0
- package/nodejs/dist/mod.js +31 -0
- package/nodejs/dist/results.js +139 -0
- package/nodejs/dist/results_str.js +147 -0
- package/nodejs/dist/runner.js +17 -0
- package/nodejs/src/logger.ts +193 -0
- package/nodejs/src/mod.ts +37 -0
- package/nodejs/src/results_str.ts +234 -0
- package/{cli → nodejs}/tsconfig.json +2 -1
- package/nodejs/tsconfig.tsbuildinfo +1 -0
- package/package.json +9 -6
- package/tests/dist/mod.d.ts +14 -3
- package/tests/dist/mod.js +33 -13
- package/tests/dist/test_error.test.d.ts +9 -0
- package/tests/dist/test_error.test.js +27 -0
- package/tests/dist/test_errors.test.d.ts +9 -0
- package/tests/dist/test_errors.test.js +27 -0
- package/tests/dist/test_logger.d.ts +3 -2
- package/tests/dist/test_logger.js +5 -1
- package/tests/src/mod.ts +31 -15
- package/tests/src/test_error.test.ts +32 -0
- package/tests/src/test_logger.ts +6 -1
- package/tests/tsconfig.tsbuildinfo +1 -1
- package/tsconfig.json +1 -1
- package/webdriver/dist/config.js +57 -0
- package/webdriver/dist/eventbus.js +18 -0
- package/webdriver/dist/listeners.js +21 -0
- package/webdriver/dist/logger.js +203 -0
- package/webdriver/dist/mod.js +36 -0
- package/webdriver/dist/results_str.js +167 -0
- package/webdriver/dist/routes.js +172 -0
- package/webdriver/dist/routes2.js +163 -0
- package/webdriver/dist/test_hangar.js +20 -0
- package/webdriver/dist/webdriver.js +273 -0
- package/webdriver/package.json +8 -0
- package/webdriver/src/config.ts +89 -0
- package/webdriver/src/eventbus.ts +104 -0
- package/webdriver/src/logger.ts +247 -0
- package/webdriver/src/mod.ts +43 -0
- package/webdriver/src/results.ts +56 -0
- package/webdriver/src/results_str.ts +222 -0
- package/webdriver/src/routes.ts +211 -0
- package/webdriver/src/test_hangar.ts +25 -0
- package/webdriver/src/webdriver.ts +372 -0
- package/{nodejs_cli → webdriver}/tsconfig.json +1 -0
- package/webdriver/tsconfig.tsbuildinfo +1 -0
- package/.github/workflows/build_and_test.yml +0 -18
- package/cli/dist/cli.d.ts +0 -3
- package/cli/dist/cli.js +0 -8
- package/cli/dist/cli_types.d.ts +0 -7
- package/cli/dist/config.d.ts +0 -5
- package/cli/dist/config.js +0 -27
- package/cli/dist/importer.d.ts +0 -7
- package/cli/dist/importer.js +0 -16
- package/cli/dist/logger.d.ts +0 -7
- package/cli/dist/logger.js +0 -88
- package/cli/dist/mod.d.ts +0 -6
- package/cli/dist/mod.js +0 -4
- package/cli/src/cli.ts +0 -17
- package/cli/src/cli_types.ts +0 -9
- package/cli/src/config.ts +0 -36
- package/cli/src/importer.ts +0 -25
- package/cli/src/logger.ts +0 -126
- package/cli/src/mod.ts +0 -7
- package/cli/tsconfig.tsbuildinfo +0 -1
- package/nodejs_cli/dist/mod.d.ts +0 -2
- package/nodejs_cli/dist/mod.js +0 -20
- package/nodejs_cli/src/mod.ts +0 -25
- package/nodejs_cli/tsconfig.tsbuildinfo +0 -1
- package/test_guide.md +0 -114
- /package/{nodejs_cli → nodejs}/package.json +0 -0
- /package/{cli/dist/cli_types.js → webdriver/dist/results.js} +0 -0
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
import { exec } from "child_process";
|
|
2
|
+
let headers = new Headers([["Content-Type", "application/json"]]);
|
|
3
|
+
export class WebDrivers {
|
|
4
|
+
#config;
|
|
5
|
+
#eventbus;
|
|
6
|
+
#webdrivers = [];
|
|
7
|
+
#currentIndex = 0;
|
|
8
|
+
constructor(config, eventbus) {
|
|
9
|
+
this.#eventbus = eventbus;
|
|
10
|
+
this.#config = config;
|
|
11
|
+
for (const params of config.webdrivers) {
|
|
12
|
+
this.#webdrivers.push(new WebdriverSession(params, config.hostAndPort, eventbus));
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
run() {
|
|
16
|
+
this.#eventbus.addListener("session_closed", (action) => {
|
|
17
|
+
if (action.id !== this.#config.webdrivers[this.#currentIndex]?.jrId)
|
|
18
|
+
return;
|
|
19
|
+
this.#currentIndex += 1;
|
|
20
|
+
let webdriver = this.#webdrivers[this.#currentIndex];
|
|
21
|
+
webdriver
|
|
22
|
+
? webdriver.run()
|
|
23
|
+
: this.#eventbus.dispatchAction({
|
|
24
|
+
type: "end",
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
let webdriver = this.#webdrivers[this.#currentIndex];
|
|
28
|
+
webdriver
|
|
29
|
+
? webdriver.run()
|
|
30
|
+
: this.#eventbus.dispatchAction({
|
|
31
|
+
type: "end",
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
runAll() {
|
|
35
|
+
this.#eventbus.addListener("session_closed", (action) => {
|
|
36
|
+
let { id } = action;
|
|
37
|
+
let [indexStr] = id.split(":");
|
|
38
|
+
let index = parseInt(indexStr);
|
|
39
|
+
if (this.#webdrivers[index]) {
|
|
40
|
+
if (id === this.#config.webdrivers[index]?.jrId)
|
|
41
|
+
this.#currentIndex += 1;
|
|
42
|
+
}
|
|
43
|
+
if (this.#currentIndex === this.#webdrivers.length) {
|
|
44
|
+
this.#eventbus.dispatchAction({
|
|
45
|
+
type: "end",
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
for (let webdriver of this.#webdrivers) {
|
|
50
|
+
webdriver.run();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
class WebdriverSession {
|
|
55
|
+
#params;
|
|
56
|
+
#hostAndPort;
|
|
57
|
+
#eventbus;
|
|
58
|
+
#process;
|
|
59
|
+
#signal;
|
|
60
|
+
#abortController;
|
|
61
|
+
#sessionId;
|
|
62
|
+
constructor(params, hostAndPort, eventbus) {
|
|
63
|
+
this.#params = params;
|
|
64
|
+
this.#hostAndPort = hostAndPort;
|
|
65
|
+
this.#eventbus = eventbus;
|
|
66
|
+
this.#abortController = new AbortController();
|
|
67
|
+
this.#eventbus.addListener("run_complete", (action) => {
|
|
68
|
+
if (action.id === this.#params.jrId)
|
|
69
|
+
this.#down();
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
async run() {
|
|
73
|
+
if (this.#process)
|
|
74
|
+
return;
|
|
75
|
+
let { jrId } = this.#params;
|
|
76
|
+
this.#eventbus.dispatchAction({
|
|
77
|
+
id: jrId,
|
|
78
|
+
type: "session_start",
|
|
79
|
+
});
|
|
80
|
+
this.#signal = setupSignal(this.#params, this.#eventbus, this.#abortController.signal);
|
|
81
|
+
this.#process = setupProcess(this.#params, this.#eventbus, this.#abortController.signal);
|
|
82
|
+
try {
|
|
83
|
+
await untilWebdriverReady(this.#params, this.#signal);
|
|
84
|
+
this.#sessionId = await getSession(this.#params, this.#signal);
|
|
85
|
+
await goToPing(this.#params, this.#signal, this.#sessionId, this.#hostAndPort);
|
|
86
|
+
await setCookie(this.#params, this.#signal, this.#sessionId);
|
|
87
|
+
await goToTestPage(this.#params, this.#signal, this.#sessionId, this.#hostAndPort);
|
|
88
|
+
}
|
|
89
|
+
catch (e) {
|
|
90
|
+
let errOutput;
|
|
91
|
+
if (e instanceof Error) {
|
|
92
|
+
errOutput = e.name + "\n" + e.message + (e.cause ? "\n" + e.cause : "");
|
|
93
|
+
}
|
|
94
|
+
if (!errOutput)
|
|
95
|
+
errOutput = e?.toString();
|
|
96
|
+
this.#eventbus.dispatchAction({
|
|
97
|
+
type: "session_error",
|
|
98
|
+
id: this.#params.jrId,
|
|
99
|
+
error: errOutput ?? "Unknown error creating browser session",
|
|
100
|
+
});
|
|
101
|
+
this.#abortController.abort();
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async #down() {
|
|
105
|
+
if (!this.#process)
|
|
106
|
+
return;
|
|
107
|
+
await deleteSession(this.#params, this.#signal, this.#eventbus, this.#sessionId);
|
|
108
|
+
this.#process.kill();
|
|
109
|
+
this.#process = undefined;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function setupSignal(params, eventbus, externalSignal) {
|
|
113
|
+
let { jrId, timeoutMs } = params;
|
|
114
|
+
let signal = AbortSignal.any([
|
|
115
|
+
externalSignal,
|
|
116
|
+
AbortSignal.timeout(timeoutMs),
|
|
117
|
+
]);
|
|
118
|
+
signal.addEventListener("abort", function () {
|
|
119
|
+
eventbus.dispatchAction({
|
|
120
|
+
type: "session_closed",
|
|
121
|
+
id: jrId,
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
return signal;
|
|
125
|
+
}
|
|
126
|
+
function setupProcess(params, eventbus, externalSignal) {
|
|
127
|
+
let { command, jrId } = params;
|
|
128
|
+
let process = exec(command, { signal: externalSignal }, (error, _stdout, stderr) => {
|
|
129
|
+
if (stderr) {
|
|
130
|
+
eventbus.dispatchAction({
|
|
131
|
+
id: jrId,
|
|
132
|
+
type: "stderr",
|
|
133
|
+
output: stderr,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
process.addListener("error", (error) => {
|
|
138
|
+
eventbus.dispatchAction({
|
|
139
|
+
id: jrId,
|
|
140
|
+
type: "session_error",
|
|
141
|
+
error: error.toString(),
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
|
+
process.addListener("exit", (statusCode) => {
|
|
145
|
+
if (statusCode) {
|
|
146
|
+
eventbus.dispatchAction({
|
|
147
|
+
type: "session_error",
|
|
148
|
+
id: jrId,
|
|
149
|
+
error: `Process returned status code: ${statusCode}`,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
eventbus.dispatchAction({
|
|
153
|
+
type: "session_closed",
|
|
154
|
+
id: jrId,
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
return process;
|
|
158
|
+
}
|
|
159
|
+
async function untilWebdriverReady(params, signal) {
|
|
160
|
+
let { url } = params;
|
|
161
|
+
while (signal && !signal.aborted) {
|
|
162
|
+
try {
|
|
163
|
+
let res = await fetch(new URL("/status", url), {
|
|
164
|
+
method: "GET",
|
|
165
|
+
headers,
|
|
166
|
+
body: null,
|
|
167
|
+
signal,
|
|
168
|
+
});
|
|
169
|
+
if (200 === res.status) {
|
|
170
|
+
let json = await res.json();
|
|
171
|
+
let { ready } = json?.value;
|
|
172
|
+
if (typeof ready === "boolean" && ready)
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch { }
|
|
177
|
+
await sleep(30);
|
|
178
|
+
}
|
|
179
|
+
throw new Error("Webdriver was never ready.");
|
|
180
|
+
}
|
|
181
|
+
async function getSession(params, signal) {
|
|
182
|
+
let { url, capabilities } = params;
|
|
183
|
+
let res = await fetch(new URL("/session", url), {
|
|
184
|
+
method: "POST",
|
|
185
|
+
headers,
|
|
186
|
+
body: JSON.stringify({ capabilities: capabilities ?? {} }),
|
|
187
|
+
signal,
|
|
188
|
+
});
|
|
189
|
+
if (200 !== res.status) {
|
|
190
|
+
let cause = await res.text();
|
|
191
|
+
throw new Error("Failed to create a session", { cause });
|
|
192
|
+
}
|
|
193
|
+
let json = await res.json();
|
|
194
|
+
let { sessionId } = json?.value;
|
|
195
|
+
if (typeof sessionId !== "string")
|
|
196
|
+
throw new Error("session is not a string");
|
|
197
|
+
return sessionId;
|
|
198
|
+
}
|
|
199
|
+
async function goToPing(params, signal, sessionId, hostAndPort) {
|
|
200
|
+
let { url } = params;
|
|
201
|
+
let pingUrl = new URL("/ping", hostAndPort);
|
|
202
|
+
let getCookie = await fetch(new URL(`/session/${sessionId}/url`, url), {
|
|
203
|
+
method: "POST",
|
|
204
|
+
headers,
|
|
205
|
+
body: JSON.stringify({ url: pingUrl }),
|
|
206
|
+
signal,
|
|
207
|
+
});
|
|
208
|
+
if (200 !== getCookie.status) {
|
|
209
|
+
let cause = await getCookie.json();
|
|
210
|
+
throw new Error("go-to-cookie request failed", { cause });
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
async function setCookie(params, signal, sessionId) {
|
|
214
|
+
let { url, jrId } = params;
|
|
215
|
+
let cookieReq = await fetch(new URL(`/session/${sessionId}/cookie`, url), {
|
|
216
|
+
method: "POST",
|
|
217
|
+
headers,
|
|
218
|
+
body: JSON.stringify({
|
|
219
|
+
cookie: {
|
|
220
|
+
name: "jackrabbit",
|
|
221
|
+
value: jrId,
|
|
222
|
+
// domain: this.#hostAndPort (issues in firefox)
|
|
223
|
+
path: "/",
|
|
224
|
+
httpOnly: true,
|
|
225
|
+
},
|
|
226
|
+
}),
|
|
227
|
+
signal,
|
|
228
|
+
});
|
|
229
|
+
if (200 !== cookieReq.status) {
|
|
230
|
+
let cause = await cookieReq.json();
|
|
231
|
+
throw new Error("set-cookie request failed", { cause });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
async function goToTestPage(params, signal, sessionId, hostAndPort) {
|
|
235
|
+
let { url } = params;
|
|
236
|
+
let goToUrlRes = await fetch(new URL(`/session/${sessionId}/url`, url), {
|
|
237
|
+
method: "POST",
|
|
238
|
+
headers,
|
|
239
|
+
body: JSON.stringify({ url: hostAndPort }),
|
|
240
|
+
signal,
|
|
241
|
+
});
|
|
242
|
+
if (200 !== goToUrlRes.status)
|
|
243
|
+
throw new Error("go-to-url request failed");
|
|
244
|
+
}
|
|
245
|
+
async function deleteSession(params, signal, eventbus, sessionId) {
|
|
246
|
+
let { url } = params;
|
|
247
|
+
try {
|
|
248
|
+
let delReqest = await fetch(new URL(`/session/${sessionId}`, url), {
|
|
249
|
+
method: "DELETE",
|
|
250
|
+
headers,
|
|
251
|
+
body: null,
|
|
252
|
+
signal: signal,
|
|
253
|
+
});
|
|
254
|
+
if (200 !== delReqest.status) {
|
|
255
|
+
let cause = await delReqest.json();
|
|
256
|
+
throw new Error("delete-cookie request failed", { cause });
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch (e) {
|
|
260
|
+
eventbus.dispatchAction({
|
|
261
|
+
type: "session_error",
|
|
262
|
+
id: params.jrId,
|
|
263
|
+
error: e?.toString() ?? "failed to delete browser session error",
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
function sleep(timeMs) {
|
|
268
|
+
return new Promise(function (resolve) {
|
|
269
|
+
setTimeout(function () {
|
|
270
|
+
resolve();
|
|
271
|
+
}, timeMs);
|
|
272
|
+
});
|
|
273
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
|
|
3
|
+
interface WebdriverConfig {
|
|
4
|
+
command: string;
|
|
5
|
+
url: URL;
|
|
6
|
+
title: string;
|
|
7
|
+
timeoutMs: number;
|
|
8
|
+
capabilities?: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface WebdriverParams extends WebdriverConfig {
|
|
12
|
+
jrId: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ConfigInterface {
|
|
16
|
+
hostAndPort: URL;
|
|
17
|
+
runAsynchronously?: boolean;
|
|
18
|
+
webdrivers: WebdriverParams[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function createConfig(
|
|
22
|
+
args: string[],
|
|
23
|
+
): Promise<ConfigInterface | Error> {
|
|
24
|
+
let configFilepath = args[0];
|
|
25
|
+
let relPath = path.resolve(process.cwd(), configFilepath);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
// windows might need a "file://<relPath>" situation
|
|
29
|
+
let { default: json } = await import(`file://${relPath}`, {
|
|
30
|
+
with: { type: "json" },
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
let hostAndPort: URL | null = URL.parse(json.host_and_port);
|
|
34
|
+
if (!hostAndPort)
|
|
35
|
+
throw new Error(`Config: invalid host_and_port json property`);
|
|
36
|
+
|
|
37
|
+
let { run_asynchronously: runAsynchronously } = json;
|
|
38
|
+
if (
|
|
39
|
+
typeof runAsynchronously !== "boolean" &&
|
|
40
|
+
undefined !== runAsynchronously
|
|
41
|
+
)
|
|
42
|
+
throw new Error(
|
|
43
|
+
"Config: the property runAsynchronously is not a boolean or undefined",
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
let webdrivers: WebdriverParams[] = [];
|
|
47
|
+
if (Array.isArray(json.webdrivers))
|
|
48
|
+
for (const [index, webdriverParams] of json.webdrivers.entries()) {
|
|
49
|
+
let params = createWebdriverParams(webdriverParams);
|
|
50
|
+
if (params instanceof Error) return params;
|
|
51
|
+
|
|
52
|
+
let session = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER);
|
|
53
|
+
let jrId = `${index}:${session.toString(32)}`;
|
|
54
|
+
|
|
55
|
+
webdrivers.push({ ...params, jrId });
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
hostAndPort,
|
|
59
|
+
runAsynchronously,
|
|
60
|
+
webdrivers,
|
|
61
|
+
};
|
|
62
|
+
} catch (e) {
|
|
63
|
+
if (e instanceof Error) return e;
|
|
64
|
+
return new Error("Config: failed to parse config params from string");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function createWebdriverParams(json: any): WebdriverConfig | Error {
|
|
69
|
+
let { command, url, title, timeout_ms, capabilities } = json;
|
|
70
|
+
|
|
71
|
+
if (typeof command !== "string")
|
|
72
|
+
return new Error("WebdriverParams: command is not a string");
|
|
73
|
+
|
|
74
|
+
let parsedUrl: URL | null = URL.parse(url);
|
|
75
|
+
if (null === parsedUrl)
|
|
76
|
+
return new Error("WebdriverParams: url is not a valid URL");
|
|
77
|
+
if (typeof title !== "string")
|
|
78
|
+
return new Error("WebdriverParams: title is not a string");
|
|
79
|
+
if (typeof timeout_ms !== "number")
|
|
80
|
+
return new Error("WebdriverParams: timeout_ms is not a number");
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
command,
|
|
84
|
+
url: parsedUrl,
|
|
85
|
+
title,
|
|
86
|
+
timeoutMs: timeout_ms,
|
|
87
|
+
capabilities,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { LoggerAction } from "../../core/dist/jackrabbit_types.js";
|
|
2
|
+
|
|
3
|
+
export interface WebdriverSessionAction {
|
|
4
|
+
id: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface WebdriverSessionStartAction extends WebdriverSessionAction {
|
|
8
|
+
type: "session_start";
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface WebdriverSessionErrorAction extends WebdriverSessionAction {
|
|
12
|
+
type: "session_error";
|
|
13
|
+
error: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface WebdriverSessionClosedAction extends WebdriverSessionAction {
|
|
17
|
+
type: "session_closed";
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface WebdriverRunCompleteAction extends WebdriverSessionAction {
|
|
21
|
+
type: "run_complete";
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface WebdriverLogAction extends WebdriverSessionAction {
|
|
25
|
+
type: "log";
|
|
26
|
+
loggerAction: LoggerAction;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface WebdriverCliOutpuAction extends WebdriverSessionAction {
|
|
30
|
+
type: "stdout";
|
|
31
|
+
output: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface WebdriverCliErrorOutpuAction extends WebdriverSessionAction {
|
|
35
|
+
type: "stderr";
|
|
36
|
+
output: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface WebdriverEndAction {
|
|
40
|
+
type: "end";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface WebdriverActionMap {
|
|
44
|
+
end: WebdriverEndAction;
|
|
45
|
+
log: WebdriverLogAction;
|
|
46
|
+
run_complete: WebdriverRunCompleteAction;
|
|
47
|
+
session_closed: WebdriverSessionClosedAction;
|
|
48
|
+
session_error: WebdriverSessionErrorAction;
|
|
49
|
+
session_start: WebdriverSessionStartAction;
|
|
50
|
+
stderr: WebdriverCliErrorOutpuAction;
|
|
51
|
+
stdout: WebdriverCliOutpuAction;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type WebdriverActions =
|
|
55
|
+
| WebdriverCliErrorOutpuAction
|
|
56
|
+
| WebdriverCliOutpuAction
|
|
57
|
+
| WebdriverEndAction
|
|
58
|
+
| WebdriverLogAction
|
|
59
|
+
| WebdriverRunCompleteAction
|
|
60
|
+
| WebdriverSessionClosedAction
|
|
61
|
+
| WebdriverSessionErrorAction
|
|
62
|
+
| WebdriverSessionStartAction;
|
|
63
|
+
|
|
64
|
+
interface TypedEventBusListener<
|
|
65
|
+
K extends keyof WebdriverActionMap = keyof WebdriverActionMap,
|
|
66
|
+
> {
|
|
67
|
+
(action: WebdriverActionMap[K]): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
interface EventBusListener extends TypedEventBusListener {
|
|
71
|
+
(action: WebdriverActions): void;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface EventBusInterface {
|
|
75
|
+
addListener<K extends keyof WebdriverActionMap>(
|
|
76
|
+
type: K,
|
|
77
|
+
listener: TypedEventBusListener<K>,
|
|
78
|
+
): void;
|
|
79
|
+
dispatchAction(action: WebdriverActions): void;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export class EventBus implements EventBusInterface {
|
|
83
|
+
#eventMap: Map<string, EventBusListener[]> = new Map();
|
|
84
|
+
|
|
85
|
+
addListener<K extends keyof WebdriverActionMap>(
|
|
86
|
+
type: K,
|
|
87
|
+
cb: TypedEventBusListener<K>,
|
|
88
|
+
) {
|
|
89
|
+
let listeners = this.#eventMap.get(type);
|
|
90
|
+
if (!listeners) {
|
|
91
|
+
listeners = [];
|
|
92
|
+
this.#eventMap.set(type, listeners);
|
|
93
|
+
}
|
|
94
|
+
listeners.push(cb as EventBusListener);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
dispatchAction(action: WebdriverActions) {
|
|
98
|
+
let listeners = this.#eventMap.get(action.type);
|
|
99
|
+
if (listeners)
|
|
100
|
+
for (const listener of listeners) {
|
|
101
|
+
listener(action);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|