system-testing 1.0.77 → 1.0.79
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 +220 -7
- package/build/browser-command-client.d.ts +19 -0
- package/build/browser-command-client.js +39 -0
- package/build/browser-command-runner.d.ts +27 -0
- package/build/browser-command-runner.js +144 -0
- package/build/browser-process.d.ts +45 -0
- package/build/browser-process.js +127 -0
- package/build/browser-registry.d.ts +34 -0
- package/build/browser-registry.js +110 -0
- package/build/browser.d.ts +222 -0
- package/build/browser.js +358 -0
- package/build/cli-helpers.d.ts +16 -0
- package/build/cli-helpers.js +141 -0
- package/build/cli.d.ts +2 -0
- package/build/cli.js +72 -0
- package/build/drivers/appium-driver.d.ts +3 -3
- package/build/drivers/appium-driver.js +2 -2
- package/build/drivers/webdriver-driver.d.ts +6 -6
- package/build/drivers/webdriver-driver.js +11 -10
- package/build/index.d.ts +9 -1
- package/build/index.js +10 -3
- package/build/system-test-browser-helper.d.ts +6 -12
- package/build/system-test-browser-helper.js +12 -13
- package/build/system-test.d.ts +6 -192
- package/build/system-test.js +7 -221
- package/build/use-system-test-expo.d.ts +16 -0
- package/build/use-system-test-expo.js +34 -0
- package/build/use-system-test-react-native.d.ts +25 -0
- package/build/use-system-test-react-native.js +20 -0
- package/build/use-system-test-shape-hook.d.ts +35 -0
- package/build/use-system-test-shape-hook.js +74 -0
- package/build/use-system-test.d.ts +19 -10
- package/build/use-system-test.js +26 -72
- package/package.json +10 -4
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import Browser from "./browser.js";
|
|
2
|
+
import BrowserCommandRunner from "./browser-command-runner.js";
|
|
3
|
+
import BrowserRegistry from "./browser-registry.js";
|
|
4
|
+
import { WebSocketServer } from "ws";
|
|
5
|
+
/** Long-running browser daemon exposing browser commands over WebSocket. */
|
|
6
|
+
export default class BrowserProcess {
|
|
7
|
+
/**
|
|
8
|
+
* @param {object} args
|
|
9
|
+
* @param {string} args.name
|
|
10
|
+
* @param {Browser} [args.browser]
|
|
11
|
+
* @param {Record<string, any>} [args.browserArgs]
|
|
12
|
+
* @param {string} [args.baseUrl]
|
|
13
|
+
* @param {boolean} [args.debug]
|
|
14
|
+
* @param {number} [args.port]
|
|
15
|
+
*/
|
|
16
|
+
constructor({ name, browser, browserArgs = {}, baseUrl, debug = false, port = 0 }) {
|
|
17
|
+
/**
|
|
18
|
+
* @param {import("ws").WebSocket} ws
|
|
19
|
+
* @returns {void}
|
|
20
|
+
*/
|
|
21
|
+
this.onConnection = (ws) => {
|
|
22
|
+
ws.on("message", async (rawData) => {
|
|
23
|
+
const requestId = `${Date.now()}-${this.requestCount++}`;
|
|
24
|
+
try {
|
|
25
|
+
const payload = JSON.parse(rawData.toString());
|
|
26
|
+
const result = await this.handlePayload(payload);
|
|
27
|
+
ws.send(JSON.stringify({ ok: true, requestId, result, type: "browser-command-result" }));
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
ws.send(JSON.stringify({
|
|
31
|
+
error: error instanceof Error ? error.message : String(error),
|
|
32
|
+
ok: false,
|
|
33
|
+
requestId,
|
|
34
|
+
type: "browser-command-result"
|
|
35
|
+
}));
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
this.name = name;
|
|
40
|
+
this.browser = browser ?? new Browser({ debug, ...browserArgs });
|
|
41
|
+
this.baseUrl = baseUrl;
|
|
42
|
+
this.debug = debug;
|
|
43
|
+
this.requestRunner = new BrowserCommandRunner({ browser: this.browser });
|
|
44
|
+
this.requestCount = 0;
|
|
45
|
+
this.port = port;
|
|
46
|
+
}
|
|
47
|
+
/** @returns {Promise<void>} */
|
|
48
|
+
async start() {
|
|
49
|
+
if (!this.name) {
|
|
50
|
+
throw new Error("Browser process requires a name");
|
|
51
|
+
}
|
|
52
|
+
if (this.baseUrl) {
|
|
53
|
+
this.browser.getDriverAdapter().setBaseUrl(this.baseUrl);
|
|
54
|
+
}
|
|
55
|
+
await this.browser.getDriverAdapter().start();
|
|
56
|
+
await this.browser.setTimeouts(10000);
|
|
57
|
+
this.wss = new WebSocketServer({ port: this.port });
|
|
58
|
+
await new Promise((resolve) => {
|
|
59
|
+
this.wss.once("listening", resolve);
|
|
60
|
+
});
|
|
61
|
+
const address = this.wss.address();
|
|
62
|
+
if (!address || typeof address === "string") {
|
|
63
|
+
throw new Error("Could not resolve browser process port");
|
|
64
|
+
}
|
|
65
|
+
this.port = address.port;
|
|
66
|
+
this.wss.on("connection", this.onConnection);
|
|
67
|
+
await BrowserRegistry.register({
|
|
68
|
+
baseUrl: this.baseUrl,
|
|
69
|
+
name: this.name,
|
|
70
|
+
pid: process.pid,
|
|
71
|
+
port: this.port,
|
|
72
|
+
startedAt: new Date().toISOString()
|
|
73
|
+
});
|
|
74
|
+
const stop = async () => {
|
|
75
|
+
await this.stop();
|
|
76
|
+
process.exit(0);
|
|
77
|
+
};
|
|
78
|
+
process.once("SIGINT", stop);
|
|
79
|
+
process.once("SIGTERM", stop);
|
|
80
|
+
}
|
|
81
|
+
/** @returns {Promise<void>} */
|
|
82
|
+
async stop() {
|
|
83
|
+
if (this.stopped) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
this.stopped = true;
|
|
87
|
+
await BrowserRegistry.unregister(this.name);
|
|
88
|
+
if (this.wss) {
|
|
89
|
+
await new Promise((resolve, reject) => {
|
|
90
|
+
this.wss.close((error) => {
|
|
91
|
+
if (error) {
|
|
92
|
+
reject(error);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
resolve(undefined);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
await this.browser.stopDriver();
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @param {Record<string, any>} payload
|
|
104
|
+
* @returns {Promise<any>}
|
|
105
|
+
*/
|
|
106
|
+
async handlePayload(payload) {
|
|
107
|
+
if (payload.type !== "browser-command") {
|
|
108
|
+
throw new Error(`Unknown payload type: ${payload.type}`);
|
|
109
|
+
}
|
|
110
|
+
const command = payload.command;
|
|
111
|
+
const commandArgs = payload.args ? { ...payload.args } : {};
|
|
112
|
+
if (payload.url && !commandArgs.url) {
|
|
113
|
+
commandArgs.url = payload.url;
|
|
114
|
+
}
|
|
115
|
+
if (payload.path && !commandArgs.path) {
|
|
116
|
+
commandArgs.path = payload.path;
|
|
117
|
+
}
|
|
118
|
+
if (payload.selector && !commandArgs.selector) {
|
|
119
|
+
commandArgs.selector = payload.selector;
|
|
120
|
+
}
|
|
121
|
+
if (payload.testID && !commandArgs.testID) {
|
|
122
|
+
commandArgs.testID = payload.testID;
|
|
123
|
+
}
|
|
124
|
+
return await this.requestRunner.run(command, commandArgs);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJvd3Nlci1wcm9jZXNzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2Jyb3dzZXItcHJvY2Vzcy5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLE9BQU8sTUFBTSxjQUFjLENBQUE7QUFDbEMsT0FBTyxvQkFBb0IsTUFBTSw2QkFBNkIsQ0FBQTtBQUM5RCxPQUFPLGVBQWUsTUFBTSx1QkFBdUIsQ0FBQTtBQUNuRCxPQUFPLEVBQUMsZUFBZSxFQUFDLE1BQU0sSUFBSSxDQUFBO0FBRWxDLDRFQUE0RTtBQUM1RSxNQUFNLENBQUMsT0FBTyxPQUFPLGNBQWM7SUFDakM7Ozs7Ozs7O09BUUc7SUFDSCxZQUFZLEVBQUMsSUFBSSxFQUFFLE9BQU8sRUFBRSxXQUFXLEdBQUcsRUFBRSxFQUFFLE9BQU8sRUFBRSxLQUFLLEdBQUcsS0FBSyxFQUFFLElBQUksR0FBRyxDQUFDLEVBQUM7UUE4RS9FOzs7V0FHRztRQUNILGlCQUFZLEdBQUcsQ0FBQyxFQUFFLEVBQUUsRUFBRTtZQUNwQixFQUFFLENBQUMsRUFBRSxDQUFDLFNBQVMsRUFBRSxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7Z0JBQ2pDLE1BQU0sU0FBUyxHQUFHLEdBQUcsSUFBSSxDQUFDLEdBQUcsRUFBRSxJQUFJLElBQUksQ0FBQyxZQUFZLEVBQUUsRUFBRSxDQUFBO2dCQUV4RCxJQUFJLENBQUM7b0JBQ0gsTUFBTSxPQUFPLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsUUFBUSxFQUFFLENBQUMsQ0FBQTtvQkFDOUMsTUFBTSxNQUFNLEdBQUcsTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLE9BQU8sQ0FBQyxDQUFBO29CQUVoRCxFQUFFLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxTQUFTLENBQUMsRUFBQyxFQUFFLEVBQUUsSUFBSSxFQUFFLFNBQVMsRUFBRSxNQUFNLEVBQUUsSUFBSSxFQUFFLHdCQUF3QixFQUFDLENBQUMsQ0FBQyxDQUFBO2dC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QUE7UUFDMUQsQ0FBQztRQUVELE1BQU0sT0FBTyxHQUFHLE9BQU8sQ0FBQyxPQUFPLENBQUE7UUFDL0IsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBQyxHQUFHLE9BQU8sQ0FBQyxJQUFJLEVBQUMsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFBO1FBRXpELElBQUksT0FBTyxDQUFDLEdBQUcsSUFBSSxDQUFDLFdBQVcsQ0FBQyxHQUFHLEVBQUUsQ0FBQztZQUNwQyxXQUFXLENBQUMsR0FBRyxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUE7UUFDL0IsQ0FBQztRQUVELElBQUksT0FBTyxDQUFDLElBQUksSUFBSSxDQUFDLFdBQVcsQ0FBQyxJQUFJLEVBQUUsQ0FBQztZQUN0QyxXQUFXLENBQUMsSUFBSSxHQUFHLE9BQU8sQ0FBQyxJQUFJLENBQUE7UUFDakMsQ0FBQztRQUVELElBQUksT0FBTyxDQUFDLFFBQVEsSUFBSSxDQUFDLFdBQVcsQ0FBQyxRQUFRLEVBQUUsQ0FBQztZQUM5QyxXQUFXLENBQUMsUUFBUSxHQUFHLE9BQU8sQ0FBQyxRQUFRLENBQUE7UUFDekMsQ0FBQztRQUVELElBQUksT0FBTyxDQUFDLE1BQU0sSUFBSSxDQUFDLFdBQVcsQ0FBQyxNQUFNLEVBQUUsQ0FBQztZQUMxQyxXQUFXLENBQUMsTUFBTSxHQUFHLE9BQU8sQ0FBQyxNQUFNLENBQUE7UUFDckMsQ0FBQztRQUVELE9BQU8sTUFBTSxJQUFJLENBQUMsYUFBYSxDQUFDLEdBQUcsQ0FBQyxPQUFPLEVBQUUsV0FBVyxDQUFDLENBQUE7SUFDM0QsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IEJyb3dzZXIgZnJvbSBcIi4vYnJvd3Nlci5qc1wiXG5pbXBvcnQgQnJvd3NlckNvbW1hbmRSdW5uZXIgZnJvbSBcIi4vYnJvd3Nlci1jb21tYW5kLXJ1bm5lci5qc1wiXG5pbXBvcnQgQnJvd3NlclJlZ2lzdHJ5IGZyb20gXCIuL2Jyb3dzZXItcmVnaXN0cnkuanNcIlxuaW1wb3J0IHtXZWJTb2NrZXRTZXJ2ZXJ9IGZyb20gXCJ3c1wiXG5cbi8qKiBMb25nLXJ1bm5pbmcgYnJvd3NlciBkYWVtb24gZXhwb3NpbmcgYnJvd3NlciBjb21tYW5kcyBvdmVyIFdlYlNvY2tldC4gKi9cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIEJyb3dzZXJQcm9jZXNzIHtcbiAgLyoqXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBhcmdzXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBhcmdzLm5hbWVcbiAgICogQHBhcmFtIHtCcm93c2VyfSBbYXJncy5icm93c2VyXVxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIGFueT59IFthcmdzLmJyb3dzZXJBcmdzXVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2FyZ3MuYmFzZVVybF1cbiAgICogQHBhcmFtIHtib29sZWFufSBbYXJncy5kZWJ1Z11cbiAgICogQHBhcmFtIHtudW1iZXJ9IFthcmdzLnBvcnRdXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7bmFtZSwgYnJvd3NlciwgYnJvd3NlckFyZ3MgPSB7fSwgYmFzZVVybCwgZGVidWcgPSBmYWxzZSwgcG9ydCA9IDB9KSB7XG4gICAgdGhpcy5uYW1lID0gbmFtZVxuICAgIHRoaXMuYnJvd3NlciA9IGJyb3dzZXIgPz8gbmV3IEJyb3dzZXIoe2RlYnVnLCAuLi5icm93c2VyQXJnc30pXG4gICAgdGhpcy5iYXNlVXJsID0gYmFzZVVybFxuICAgIHRoaXMuZGVidWcgPSBkZWJ1Z1xuICAgIHRoaXMucmVxdWVzdFJ1bm5lciA9IG5ldyBCcm93c2VyQ29tbWFuZFJ1bm5lcih7YnJvd3NlcjogdGhpcy5icm93c2VyfSlcbiAgICB0aGlzLnJlcXVlc3RDb3VudCA9IDBcbiAgICB0aGlzLnBvcnQgPSBwb3J0XG4gIH1cblxuICAvKiogQHJldHVybnMge1Byb21pc2U8dm9pZD59ICovXG4gIGFzeW5jIHN0YXJ0KCkge1xuICAgIGlmICghdGhpcy5uYW1lKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJCcm93c2VyIHByb2Nlc3MgcmVxdWlyZXMgYSBuYW1lXCIpXG4gICAgfVxuXG4gICAgaWYgKHRoaXMuYmFzZVVybCkge1xuICAgICAgdGhpcy5icm93c2VyLmdldERyaXZlckFkYXB0ZXIoKS5zZXRCYXNlVXJsKHRoaXMuYmFzZVVybClcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLmJyb3dzZXIuZ2V0RHJpdmVyQWRhcHRlcigpLnN0YXJ0KClcbiAgICBhd2FpdCB0aGlzLmJyb3dzZXIuc2V0VGltZW91dHMoMTAwMDApXG5cbiAgICB0aGlzLndzcyA9IG5ldyBXZWJTb2NrZXRTZXJ2ZXIoe3BvcnQ6IHRoaXMucG9ydH0pXG4gICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUpID0+IHtcbiAgICAgIHRoaXMud3NzLm9uY2UoXCJsaXN0ZW5pbmdcIiwgcmVzb2x2ZSlcbiAgICB9KVxuXG4gICAgY29uc3QgYWRkcmVzcyA9IHRoaXMud3NzLmFkZHJlc3MoKVxuXG4gICAgaWYgKCFhZGRyZXNzIHx8IHR5cGVvZiBhZGRyZXNzID09PSBcInN0cmluZ1wiKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDb3VsZCBub3QgcmVzb2x2ZSBicm93c2VyIHByb2Nlc3MgcG9ydFwiKVxuICAgIH1cblxuICAgIHRoaXMucG9ydCA9IGFkZHJlc3MucG9ydFxuICAgIHRoaXMud3NzLm9uKFwiY29ubmVjdGlvblwiLCB0aGlzLm9uQ29ubmVjdGlvbilcblxuICAgIGF3YWl0IEJyb3dzZXJSZWdpc3RyeS5yZWdpc3Rlcih7XG4gICAgICBiYXNlVXJsOiB0aGlzLmJhc2VVcmwsXG4gICAgICBuYW1lOiB0aGlzLm5hbWUsXG4gICAgICBwaWQ6IHByb2Nlc3MucGlkLFxuICAgICAgcG9ydDogdGhpcy5wb3J0LFxuICAgICAgc3RhcnRlZEF0OiBuZXcgRGF0ZSgpLnRvSVNPU3RyaW5nKClcbiAgICB9KVxuXG4gICAgY29uc3Qgc3RvcCA9IGFzeW5jICgpID0+IHtcbiAgICAgIGF3YWl0IHRoaXMuc3RvcCgpXG4gICAgICBwcm9jZXNzLmV4aXQoMClcbiAgICB9XG5cbiAgICBwcm9jZXNzLm9uY2UoXCJTSUdJTlRcIiwgc3RvcClcbiAgICBwcm9jZXNzLm9uY2UoXCJTSUdURVJNXCIsIHN0b3ApXG4gIH1cblxuICAvKiogQHJldHVybnMge1Byb21pc2U8dm9pZD59ICovXG4gIGFzeW5jIHN0b3AoKSB7XG4gICAgaWYgKHRoaXMuc3RvcHBlZCkge1xuICAgICAgcmV0dXJuXG4gICAgfVxuXG4gICAgdGhpcy5zdG9wcGVkID0gdHJ1ZVxuICAgIGF3YWl0IEJyb3dzZXJSZWdpc3RyeS51bnJlZ2lzdGVyKHRoaXMubmFtZSlcblxuICAgIGlmICh0aGlzLndzcykge1xuICAgICAgYXdhaXQgbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgICB0aGlzLndzcy5jbG9zZSgoZXJyb3IpID0+IHtcbiAgICAgICAgICBpZiAoZXJyb3IpIHtcbiAgICAgICAgICAgIHJlamVjdChlcnJvcilcbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgcmVzb2x2ZSh1bmRlZmluZWQpXG4gICAgICAgICAgfVxuICAgICAgICB9KVxuICAgICAgfSlcbiAgICB9XG5cbiAgICBhd2FpdCB0aGlzLmJyb3dzZXIuc3RvcERyaXZlcigpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtpbXBvcnQoXCJ3c1wiKS5XZWJTb2NrZXR9IHdzXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgb25Db25uZWN0aW9uID0gKHdzKSA9PiB7XG4gICAgd3Mub24oXCJtZXNzYWdlXCIsIGFzeW5jIChyYXdEYXRhKSA9PiB7XG4gICAgICBjb25zdCByZXF1ZXN0SWQgPSBgJHtEYXRlLm5vdygpfS0ke3RoaXMucmVxdWVzdENvdW50Kyt9YFxuXG4gICAgICB0cnkge1xuICAgICAgICBjb25zdCBwYXlsb2FkID0gSlNPTi5wYXJzZShyYXdEYXRhLnRvU3RyaW5nKCkpXG4gICAgICAgIGNvbnN0IHJlc3VsdCA9IGF3YWl0IHRoaXMuaGFuZGxlUGF5bG9hZChwYXlsb2FkKVxuXG4gICAgICAgIHdzLnNlbmQoSlNPTi5zdHJpbmdpZnkoe29rOiB0cnVlLCByZXF1ZXN0SWQsIHJlc3VsdCwgdHlwZTogXCJicm93c2VyLWNvbW1hbmQtcmVzdWx0XCJ9KSlcbiAgICAgIH0gY2F0Y2ggKGVycm9yKSB7XG4gICAgICAgIHdzLnNlbmQoSlNPTi5zdHJpbmdpZnkoe1xuICAgICAgICAgIGVycm9yOiBlcnJvciBpbnN0YW5jZW9mIEVycm9yID8gZXJyb3IubWVzc2FnZSA6IFN0cmluZyhlcnJvciksXG4gICAgICAgICAgb2s6IGZhbHNlLFxuICAgICAgICAgIHJlcXVlc3RJZCxcbiAgICAgICAgICB0eXBlOiBcImJyb3dzZXItY29tbWFuZC1yZXN1bHRcIlxuICAgICAgICB9KSlcbiAgICAgIH1cbiAgICB9KVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgYW55Pn0gcGF5bG9hZFxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTxhbnk+fVxuICAgKi9cbiAgYXN5bmMgaGFuZGxlUGF5bG9hZChwYXlsb2FkKSB7XG4gICAgaWYgKHBheWxvYWQudHlwZSAhPT0gXCJicm93c2VyLWNvbW1hbmRcIikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIHBheWxvYWQgdHlwZTogJHtwYXlsb2FkLnR5cGV9YClcbiAgICB9XG5cbiAgICBjb25zdCBjb21tYW5kID0gcGF5bG9hZC5jb21tYW5kXG4gICAgY29uc3QgY29tbWFuZEFyZ3MgPSBwYXlsb2FkLmFyZ3MgPyB7Li4ucGF5bG9hZC5hcmdzfSA6IHt9XG5cbiAgICBpZiAocGF5bG9hZC51cmwgJiYgIWNvbW1hbmRBcmdzLnVybCkge1xuICAgICAgY29tbWFuZEFyZ3MudXJsID0gcGF5bG9hZC51cmxcbiAgICB9XG5cbiAgICBpZiAocGF5bG9hZC5wYXRoICYmICFjb21tYW5kQXJncy5wYXRoKSB7XG4gICAgICBjb21tYW5kQXJncy5wYXRoID0gcGF5bG9hZC5wYXRoXG4gICAgfVxuXG4gICAgaWYgKHBheWxvYWQuc2VsZWN0b3IgJiYgIWNvbW1hbmRBcmdzLnNlbGVjdG9yKSB7XG4gICAgICBjb21tYW5kQXJncy5zZWxlY3RvciA9IHBheWxvYWQuc2VsZWN0b3JcbiAgICB9XG5cbiAgICBpZiAocGF5bG9hZC50ZXN0SUQgJiYgIWNvbW1hbmRBcmdzLnRlc3RJRCkge1xuICAgICAgY29tbWFuZEFyZ3MudGVzdElEID0gcGF5bG9hZC50ZXN0SURcbiAgICB9XG5cbiAgICByZXR1cm4gYXdhaXQgdGhpcy5yZXF1ZXN0UnVubmVyLnJ1bihjb21tYW5kLCBjb21tYW5kQXJncylcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Browser process registry. */
|
|
2
|
+
export default class BrowserRegistry {
|
|
3
|
+
/** @returns {string} */
|
|
4
|
+
static getRegistryPath(): string;
|
|
5
|
+
/** @returns {Promise<Array<Record<string, any>>>} */
|
|
6
|
+
static list(): Promise<Array<Record<string, any>>>;
|
|
7
|
+
/**
|
|
8
|
+
* @param {Record<string, any>} entry
|
|
9
|
+
* @returns {Promise<void>}
|
|
10
|
+
*/
|
|
11
|
+
static register(entry: Record<string, any>): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} name
|
|
14
|
+
* @returns {Promise<void>}
|
|
15
|
+
*/
|
|
16
|
+
static unregister(name: string): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} [name]
|
|
19
|
+
* @returns {Promise<Record<string, any>>}
|
|
20
|
+
*/
|
|
21
|
+
static resolve(name?: string): Promise<Record<string, any>>;
|
|
22
|
+
/**
|
|
23
|
+
* @param {number} pid
|
|
24
|
+
* @returns {boolean}
|
|
25
|
+
*/
|
|
26
|
+
static isProcessAlive(pid: number): boolean;
|
|
27
|
+
/** @returns {Promise<Array<Record<string, any>>>} */
|
|
28
|
+
static _readRegistry(): Promise<Array<Record<string, any>>>;
|
|
29
|
+
/**
|
|
30
|
+
* @param {Array<Record<string, any>>} entries
|
|
31
|
+
* @returns {Promise<void>}
|
|
32
|
+
*/
|
|
33
|
+
static _writeRegistry(entries: Array<Record<string, any>>): Promise<void>;
|
|
34
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
const registryPath = path.join(os.tmpdir(), "system-testing-browser-registry.json");
|
|
5
|
+
/** Browser process registry. */
|
|
6
|
+
export default class BrowserRegistry {
|
|
7
|
+
/** @returns {string} */
|
|
8
|
+
static getRegistryPath() {
|
|
9
|
+
return registryPath;
|
|
10
|
+
}
|
|
11
|
+
/** @returns {Promise<Array<Record<string, any>>>} */
|
|
12
|
+
static async list() {
|
|
13
|
+
const entries = await this._readRegistry();
|
|
14
|
+
const aliveEntries = [];
|
|
15
|
+
let dirty = false;
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
if (this.isProcessAlive(entry.pid)) {
|
|
18
|
+
aliveEntries.push(entry);
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
dirty = true;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (dirty) {
|
|
25
|
+
await this._writeRegistry(aliveEntries);
|
|
26
|
+
}
|
|
27
|
+
return aliveEntries;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* @param {Record<string, any>} entry
|
|
31
|
+
* @returns {Promise<void>}
|
|
32
|
+
*/
|
|
33
|
+
static async register(entry) {
|
|
34
|
+
const entries = await this.list();
|
|
35
|
+
const filteredEntries = entries.filter((existingEntry) => existingEntry.name !== entry.name);
|
|
36
|
+
filteredEntries.push(entry);
|
|
37
|
+
await this._writeRegistry(filteredEntries);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @param {string} name
|
|
41
|
+
* @returns {Promise<void>}
|
|
42
|
+
*/
|
|
43
|
+
static async unregister(name) {
|
|
44
|
+
const entries = await this.list();
|
|
45
|
+
const filteredEntries = entries.filter((entry) => entry.name !== name);
|
|
46
|
+
await this._writeRegistry(filteredEntries);
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} [name]
|
|
50
|
+
* @returns {Promise<Record<string, any>>}
|
|
51
|
+
*/
|
|
52
|
+
static async resolve(name) {
|
|
53
|
+
const entries = await this.list();
|
|
54
|
+
if (name) {
|
|
55
|
+
const entry = entries.find((candidate) => candidate.name === name);
|
|
56
|
+
if (!entry) {
|
|
57
|
+
throw new Error(`No running browser process found with name: ${name}`);
|
|
58
|
+
}
|
|
59
|
+
return entry;
|
|
60
|
+
}
|
|
61
|
+
if (entries.length === 1) {
|
|
62
|
+
return entries[0];
|
|
63
|
+
}
|
|
64
|
+
if (entries.length === 0) {
|
|
65
|
+
throw new Error("No running browser processes found");
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`Multiple browser processes are running (${entries.length}); pass --name`);
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* @param {number} pid
|
|
71
|
+
* @returns {boolean}
|
|
72
|
+
*/
|
|
73
|
+
static isProcessAlive(pid) {
|
|
74
|
+
if (!pid || typeof pid !== "number") {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
process.kill(pid, 0);
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/** @returns {Promise<Array<Record<string, any>>>} */
|
|
86
|
+
static async _readRegistry() {
|
|
87
|
+
try {
|
|
88
|
+
const fileContent = await fs.readFile(this.getRegistryPath(), "utf8");
|
|
89
|
+
const parsed = JSON.parse(fileContent);
|
|
90
|
+
if (!Array.isArray(parsed)) {
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
throw error;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* @param {Array<Record<string, any>>} entries
|
|
104
|
+
* @returns {Promise<void>}
|
|
105
|
+
*/
|
|
106
|
+
static async _writeRegistry(entries) {
|
|
107
|
+
await fs.writeFile(this.getRegistryPath(), JSON.stringify(entries, null, 2));
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYnJvd3Nlci1yZWdpc3RyeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9icm93c2VyLXJlZ2lzdHJ5LmpzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBQ2pDLE9BQU8sRUFBRSxNQUFNLFNBQVMsQ0FBQTtBQUN4QixPQUFPLElBQUksTUFBTSxXQUFXLENBQUE7QUFFNUIsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQUMsTUFBTSxFQUFFLEVBQUUsc0NBQXNDLENBQUMsQ0FBQTtBQUVuRixnQ0FBZ0M7QUFDaEMsTUFBTSxDQUFDLE9BQU8sT0FBTyxlQUFlO0lBQ2xDLHdCQUF3QjtJQUN4QixNQUFNLENBQUMsZUFBZTtRQUNwQixPQUFPLFlBQVksQ0FBQTtJQUNyQixDQUFDO0lBRUQscURBQXFEO0lBQ3JELE1BQU0sQ0FBQyxLQUFLLENBQUMsSUFBSTtRQUNmLE1BQU0sT0FBTyxHQUFHLE1BQU0sSUFBSSxDQUFDLGFBQWEsRUFBRSxDQUFBO1FBQzFDLE1BQU0sWUFBWSxHQUFHLEVBQUUsQ0FBQTtRQUN2QixJQUFJLEtBQUssR0FBRyxLQUFLLENBQUE7UUFFakIsS0FBSyxNQUFNLEtBQUssSUFBSSxPQUFPLEVBQUUsQ0FBQztZQUM1QixJQUFJLElBQUksQ0FBQyxjQUFjLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxFQUFFLENBQUM7Z0JBQ25DLFlBQVksQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUE7WUFDMUIsQ0FBQztpQkFBTSxDQUFDO2dCQUNOLEtBQUssR0FBRyxJQUFJLENBQUE7WUFDZCxDQUFDO1FBQ0gsQ0FBQztRQUVELElBQUksS0FBSyxFQUFFLENBQUM7WUFDVixNQUFNLElBQUksQ0FBQyxjQUFjLENBQUMsWUFBWSxDQUFDLENBQUE7UUFDekMsQ0FBQztRQUVELE9BQU8sWUFBWSxDQUFBO0lBQ3JCLENBQUM7SUFFRDs7O09BR0c7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QUFDO1lBQ2YsSUFBSSxLQUFLLFlBQVksS0FBSyxJQUFJLE1BQU0sSUFBSSxLQUFLLElBQUksS0FBSyxDQUFDLElBQUksS0FBSyxRQUFRLEVBQUUsQ0FBQztnQkFDekUsT0FBTyxFQUFFLENBQUE7WUFDWCxDQUFDO1lBRUQsTUFBTSxLQUFLLENBQUE7UUFDYixDQUFDO0lBQ0gsQ0FBQztJQUVEOzs7T0FHRztJQUNILE1BQU0sQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLE9BQU87UUFDakMsTUFBTSxFQUFFLENBQUMsU0FBUyxDQUFDLElBQUksQ0FBQyxlQUFlLEVBQUUsRUFBRSxJQUFJLENBQUMsU0FBUyxDQUFDLE9BQU8sRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FBQTtJQUM5RSxDQUFDO0NBQ0YiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgZnMgZnJvbSBcIm5vZGU6ZnMvcHJvbWlzZXNcIlxuaW1wb3J0IG9zIGZyb20gXCJub2RlOm9zXCJcbmltcG9ydCBwYXRoIGZyb20gXCJub2RlOnBhdGhcIlxuXG5jb25zdCByZWdpc3RyeVBhdGggPSBwYXRoLmpvaW4ob3MudG1wZGlyKCksIFwic3lzdGVtLXRlc3RpbmctYnJvd3Nlci1yZWdpc3RyeS5qc29uXCIpXG5cbi8qKiBCcm93c2VyIHByb2Nlc3MgcmVnaXN0cnkuICovXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBCcm93c2VyUmVnaXN0cnkge1xuICAvKiogQHJldHVybnMge3N0cmluZ30gKi9cbiAgc3RhdGljIGdldFJlZ2lzdHJ5UGF0aCgpIHtcbiAgICByZXR1cm4gcmVnaXN0cnlQYXRoXG4gIH1cblxuICAvKiogQHJldHVybnMge1Byb21pc2U8QXJyYXk8UmVjb3JkPHN0cmluZywgYW55Pj4+fSAqL1xuICBzdGF0aWMgYXN5bmMgbGlzdCgpIHtcbiAgICBjb25zdCBlbnRyaWVzID0gYXdhaXQgdGhpcy5fcmVhZFJlZ2lzdHJ5KClcbiAgICBjb25zdCBhbGl2ZUVudHJpZXMgPSBbXVxuICAgIGxldCBkaXJ0eSA9IGZhbHNlXG5cbiAgICBmb3IgKGNvbnN0IGVudHJ5IG9mIGVudHJpZXMpIHtcbiAgICAgIGlmICh0aGlzLmlzUHJvY2Vzc0FsaXZlKGVudHJ5LnBpZCkpIHtcbiAgICAgICAgYWxpdmVFbnRyaWVzLnB1c2goZW50cnkpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBkaXJ0eSA9IHRydWVcbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoZGlydHkpIHtcbiAgICAgIGF3YWl0IHRoaXMuX3dyaXRlUmVnaXN0cnkoYWxpdmVFbnRyaWVzKVxuICAgIH1cblxuICAgIHJldHVybiBhbGl2ZUVudHJpZXNcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge1JlY29yZDxzdHJpbmcsIGFueT59IGVudHJ5XG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgc3RhdGljIGFzeW5jIHJlZ2lzdGVyKGVudHJ5KSB7XG4gICAgY29uc3QgZW50cmllcyA9IGF3YWl0IHRoaXMubGlzdCgpXG4gICAgY29uc3QgZmlsdGVyZWRFbnRyaWVzID0gZW50cmllcy5maWx0ZXIoKGV4aXN0aW5nRW50cnkpID0+IGV4aXN0aW5nRW50cnkubmFtZSAhPT0gZW50cnkubmFtZSlcblxuICAgIGZpbHRlcmVkRW50cmllcy5wdXNoKGVudHJ5KVxuICAgIGF3YWl0IHRoaXMuX3dyaXRlUmVnaXN0cnkoZmlsdGVyZWRFbnRyaWVzKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBuYW1lXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgc3RhdGljIGFzeW5jIHVucmVnaXN0ZXIobmFtZSkge1xuICAgIGNvbnN0IGVudHJpZXMgPSBhd2FpdCB0aGlzLmxpc3QoKVxuICAgIGNvbnN0IGZpbHRlcmVkRW50cmllcyA9IGVudHJpZXMuZmlsdGVyKChlbnRyeSkgPT4gZW50cnkubmFtZSAhPT0gbmFtZSlcblxuICAgIGF3YWl0IHRoaXMuX3dyaXRlUmVnaXN0cnkoZmlsdGVyZWRFbnRyaWVzKVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbbmFtZV1cbiAgICogQHJldHVybnMge1Byb21pc2U8UmVjb3JkPHN0cmluZywgYW55Pj59XG4gICAqL1xuICBzdGF0aWMgYXN5bmMgcmVzb2x2ZShuYW1lKSB7XG4gICAgY29uc3QgZW50cmllcyA9IGF3YWl0IHRoaXMubGlzdCgpXG5cbiAgICBpZiAobmFtZSkge1xuICAgICAgY29uc3QgZW50cnkgPSBlbnRyaWVzLmZpbmQoKGNhbmRpZGF0ZSkgPT4gY2FuZGlkYXRlLm5hbWUgPT09IG5hbWUpXG5cbiAgICAgIGlmICghZW50cnkpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBydW5uaW5nIGJyb3dzZXIgcHJvY2VzcyBmb3VuZCB3aXRoIG5hbWU6ICR7bmFtZX1gKVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gZW50cnlcbiAgICB9XG5cbiAgICBpZiAoZW50cmllcy5sZW5ndGggPT09IDEpIHtcbiAgICAgIHJldHVybiBlbnRyaWVzWzBdXG4gICAgfVxuXG4gICAgaWYgKGVudHJpZXMubGVuZ3RoID09PSAwKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJObyBydW5uaW5nIGJyb3dzZXIgcHJvY2Vzc2VzIGZvdW5kXCIpXG4gICAgfVxuXG4gICAgdGhyb3cgbmV3IEVycm9yKGBNdWx0aXBsZSBicm93c2VyIHByb2Nlc3NlcyBhcmUgcnVubmluZyAoJHtlbnRyaWVzLmxlbmd0aH0pOyBwYXNzIC0tbmFtZWApXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtudW1iZXJ9IHBpZFxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIHN0YXRpYyBpc1Byb2Nlc3NBbGl2ZShwaWQpIHtcbiAgICBpZiAoIXBpZCB8fCB0eXBlb2YgcGlkICE9PSBcIm51bWJlclwiKSB7XG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG5cbiAgICB0cnkge1xuICAgICAgcHJvY2Vzcy5raWxsKHBpZCwgMClcbiAgICAgIHJldHVybiB0cnVlXG4gICAgfSBjYXRjaCB7XG4gICAgICByZXR1cm4gZmFsc2VcbiAgICB9XG4gIH1cblxuICAvKiogQHJldHVybnMge1Byb21pc2U8QXJyYXk8UmVjb3JkPHN0cmluZywgYW55Pj4+fSAqL1xuICBzdGF0aWMgYXN5bmMgX3JlYWRSZWdpc3RyeSgpIHtcbiAgICB0cnkge1xuICAgICAgY29uc3QgZmlsZUNvbnRlbnQgPSBhd2FpdCBmcy5yZWFkRmlsZSh0aGlzLmdldFJlZ2lzdHJ5UGF0aCgpLCBcInV0ZjhcIilcbiAgICAgIGNvbnN0IHBhcnNlZCA9IEpTT04ucGFyc2UoZmlsZUNvbnRlbnQpXG5cbiAgICAgIGlmICghQXJyYXkuaXNBcnJheShwYXJzZWQpKSB7XG4gICAgICAgIHJldHVybiBbXVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gcGFyc2VkXG4gICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgIGlmIChlcnJvciBpbnN0YW5jZW9mIEVycm9yICYmIFwiY29kZVwiIGluIGVycm9yICYmIGVycm9yLmNvZGUgPT09IFwiRU5PRU5UXCIpIHtcbiAgICAgICAgcmV0dXJuIFtdXG4gICAgICB9XG5cbiAgICAgIHRocm93IGVycm9yXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7QXJyYXk8UmVjb3JkPHN0cmluZywgYW55Pj59IGVudHJpZXNcbiAgICogQHJldHVybnMge1Byb21pc2U8dm9pZD59XG4gICAqL1xuICBzdGF0aWMgYXN5bmMgX3dyaXRlUmVnaXN0cnkoZW50cmllcykge1xuICAgIGF3YWl0IGZzLndyaXRlRmlsZSh0aGlzLmdldFJlZ2lzdHJ5UGF0aCgpLCBKU09OLnN0cmluZ2lmeShlbnRyaWVzLCBudWxsLCAyKSlcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} BrowserArgs
|
|
3
|
+
* @property {boolean} [debug] Enable debug logging.
|
|
4
|
+
* @property {BrowserDriverConfig} [driver] Driver configuration.
|
|
5
|
+
* @property {import("./system-test-communicator.js").default} [communicator] Optional command communicator for helper-driven navigation.
|
|
6
|
+
* @property {string} [screenshotsPath] Directory used for saved screenshots and browser artifacts.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {object} BrowserDriverConfig
|
|
10
|
+
* @property {"selenium"|"appium"} [type] Driver implementation to use.
|
|
11
|
+
* @property {Record<string, any>} [options] Driver-specific options.
|
|
12
|
+
*/
|
|
13
|
+
/** Generic browser session wrapper around the configured driver. */
|
|
14
|
+
export default class Browser {
|
|
15
|
+
/** @param {BrowserArgs} [args] */
|
|
16
|
+
constructor({ debug, driver, communicator, screenshotsPath, ...restArgs }?: BrowserArgs);
|
|
17
|
+
/** @type {import("selenium-webdriver").WebDriver | undefined} */
|
|
18
|
+
driver: import("selenium-webdriver").WebDriver | undefined;
|
|
19
|
+
/** @type {import("./drivers/webdriver-driver.js").default | undefined} */
|
|
20
|
+
driverAdapter: import("./drivers/webdriver-driver.js").default | undefined;
|
|
21
|
+
_debug: boolean;
|
|
22
|
+
/** @type {BrowserDriverConfig | undefined} */
|
|
23
|
+
_driverConfig: BrowserDriverConfig | undefined;
|
|
24
|
+
/** @type {Error | undefined} */
|
|
25
|
+
_httpServerError: Error | undefined;
|
|
26
|
+
_screenshotsPath: string;
|
|
27
|
+
communicator: import("./system-test-communicator.js").default;
|
|
28
|
+
/**
|
|
29
|
+
* @param {BrowserDriverConfig} [driverConfig]
|
|
30
|
+
* @returns {import("./drivers/webdriver-driver.js").default}
|
|
31
|
+
*/
|
|
32
|
+
createDriver(driverConfig?: BrowserDriverConfig): import("./drivers/webdriver-driver.js").default;
|
|
33
|
+
/**
|
|
34
|
+
* @param {import("./system-test-communicator.js").default | undefined} communicator
|
|
35
|
+
* @returns {void}
|
|
36
|
+
*/
|
|
37
|
+
setCommunicator(communicator: import("./system-test-communicator.js").default | undefined): void;
|
|
38
|
+
/** @returns {boolean} */
|
|
39
|
+
communicatorExists(): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* @param {string} baseSelector
|
|
42
|
+
* @returns {void}
|
|
43
|
+
*/
|
|
44
|
+
setBaseSelector(baseSelector: string): void;
|
|
45
|
+
_baseSelector: string;
|
|
46
|
+
/** @returns {string | undefined} */
|
|
47
|
+
getBaseSelector(): string | undefined;
|
|
48
|
+
/**
|
|
49
|
+
* @param {string} selector
|
|
50
|
+
* @returns {string}
|
|
51
|
+
*/
|
|
52
|
+
getSelector(selector: string): string;
|
|
53
|
+
/**
|
|
54
|
+
* @param {...any} args
|
|
55
|
+
* @returns {void}
|
|
56
|
+
*/
|
|
57
|
+
debugError(...args: any[]): void;
|
|
58
|
+
/**
|
|
59
|
+
* @param {...any} args
|
|
60
|
+
* @returns {void}
|
|
61
|
+
*/
|
|
62
|
+
debugLog(...args: any[]): void;
|
|
63
|
+
/** @returns {void} */
|
|
64
|
+
throwIfHttpServerError(): void;
|
|
65
|
+
/**
|
|
66
|
+
* @param {Error} error
|
|
67
|
+
* @returns {void}
|
|
68
|
+
*/
|
|
69
|
+
onHttpServerError: (error: Error) => void;
|
|
70
|
+
/** @returns {import("selenium-webdriver").WebDriver} */
|
|
71
|
+
getDriver(): import("selenium-webdriver").WebDriver;
|
|
72
|
+
/** @returns {import("./drivers/webdriver-driver.js").default} */
|
|
73
|
+
getDriverAdapter(): import("./drivers/webdriver-driver.js").default;
|
|
74
|
+
/** @returns {number} */
|
|
75
|
+
getTimeouts(): number;
|
|
76
|
+
/** @returns {Promise<void>} */
|
|
77
|
+
restoreTimeouts(): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* @param {number} newTimeout
|
|
80
|
+
* @returns {Promise<void>}
|
|
81
|
+
*/
|
|
82
|
+
driverSetTimeouts(newTimeout: number): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* @param {number} newTimeout
|
|
85
|
+
* @returns {Promise<void>}
|
|
86
|
+
*/
|
|
87
|
+
setTimeouts(newTimeout: number): Promise<void>;
|
|
88
|
+
/** @returns {Promise<string[]>} */
|
|
89
|
+
getBrowserLogs(): Promise<string[]>;
|
|
90
|
+
/** @returns {Promise<string>} */
|
|
91
|
+
getCurrentUrl(): Promise<string>;
|
|
92
|
+
/**
|
|
93
|
+
* @param {string} selector
|
|
94
|
+
* @param {import("./system-test.js").FindArgs} [args]
|
|
95
|
+
* @returns {Promise<import("selenium-webdriver").WebElement[]>}
|
|
96
|
+
*/
|
|
97
|
+
all(selector: string, args?: import("./system-test.js").FindArgs): Promise<import("selenium-webdriver").WebElement[]>;
|
|
98
|
+
/**
|
|
99
|
+
* @param {string} selector
|
|
100
|
+
* @param {import("./system-test.js").FindArgs} [args]
|
|
101
|
+
* @returns {Promise<import("selenium-webdriver").WebElement>}
|
|
102
|
+
*/
|
|
103
|
+
find(selector: string, args?: import("./system-test.js").FindArgs): Promise<import("selenium-webdriver").WebElement>;
|
|
104
|
+
/**
|
|
105
|
+
* @param {string} testID
|
|
106
|
+
* @param {import("./system-test.js").FindArgs} [args]
|
|
107
|
+
* @returns {Promise<import("selenium-webdriver").WebElement>}
|
|
108
|
+
*/
|
|
109
|
+
findByTestID(testID: string, args?: import("./system-test.js").FindArgs): Promise<import("selenium-webdriver").WebElement>;
|
|
110
|
+
/**
|
|
111
|
+
* @param {string} selector
|
|
112
|
+
* @param {import("./system-test.js").FindArgs} [args]
|
|
113
|
+
* @returns {Promise<import("selenium-webdriver").WebElement>}
|
|
114
|
+
*/
|
|
115
|
+
findNoWait(selector: string, args?: import("./system-test.js").FindArgs): Promise<import("selenium-webdriver").WebElement>;
|
|
116
|
+
/**
|
|
117
|
+
* @param {string | import("selenium-webdriver").WebElement} elementOrIdentifier
|
|
118
|
+
* @param {import("./system-test.js").FindArgs} [args]
|
|
119
|
+
* @returns {Promise<void>}
|
|
120
|
+
*/
|
|
121
|
+
click(elementOrIdentifier: string | import("selenium-webdriver").WebElement, args?: import("./system-test.js").FindArgs): Promise<void>;
|
|
122
|
+
/**
|
|
123
|
+
* @param {import("selenium-webdriver").WebElement|string|{selector: string} & import("./system-test.js").FindArgs} elementOrIdentifier
|
|
124
|
+
* @param {string} methodName
|
|
125
|
+
* @param {...any} args
|
|
126
|
+
* @returns {Promise<any>}
|
|
127
|
+
*/
|
|
128
|
+
interact(elementOrIdentifier: import("selenium-webdriver").WebElement | string | ({
|
|
129
|
+
selector: string;
|
|
130
|
+
} & import("./system-test.js").FindArgs), methodName: string, ...args: any[]): Promise<any>;
|
|
131
|
+
/**
|
|
132
|
+
* @param {string} selector
|
|
133
|
+
* @param {import("./system-test.js").WaitForNoSelectorArgs} [args]
|
|
134
|
+
* @returns {Promise<void>}
|
|
135
|
+
*/
|
|
136
|
+
waitForNoSelector(selector: string, args?: import("./system-test.js").WaitForNoSelectorArgs): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* @param {string} selector
|
|
139
|
+
* @param {import("./system-test.js").FindArgs} [args]
|
|
140
|
+
* @returns {Promise<void>}
|
|
141
|
+
*/
|
|
142
|
+
expectNoElement(selector: string, args?: import("./system-test.js").FindArgs): Promise<void>;
|
|
143
|
+
/** @returns {Promise<string>} */
|
|
144
|
+
getHTML(): Promise<string>;
|
|
145
|
+
/**
|
|
146
|
+
* @param {string} path
|
|
147
|
+
* @returns {Promise<void>}
|
|
148
|
+
*/
|
|
149
|
+
driverVisit(path: string): Promise<void>;
|
|
150
|
+
/**
|
|
151
|
+
* @param {string} type
|
|
152
|
+
* @param {string} path
|
|
153
|
+
* @returns {Promise<void>}
|
|
154
|
+
*/
|
|
155
|
+
sendBrowserCommand(type: string, path: string): Promise<void>;
|
|
156
|
+
/**
|
|
157
|
+
* Visits a path using the injected browser helper when available, otherwise navigates directly with the driver.
|
|
158
|
+
* @param {string} path
|
|
159
|
+
* @returns {Promise<void>}
|
|
160
|
+
*/
|
|
161
|
+
visit(path: string): Promise<void>;
|
|
162
|
+
/**
|
|
163
|
+
* Dismisses to a path via the injected browser helper when available, otherwise navigates directly with the driver.
|
|
164
|
+
* @param {string} path
|
|
165
|
+
* @returns {Promise<void>}
|
|
166
|
+
*/
|
|
167
|
+
dismissTo(path: string): Promise<void>;
|
|
168
|
+
/**
|
|
169
|
+
* Formats browser logs for console output and truncates overly long output.
|
|
170
|
+
* @param {string[]} logs
|
|
171
|
+
* @param {number} [maxLines]
|
|
172
|
+
* @returns {string[]}
|
|
173
|
+
*/
|
|
174
|
+
formatBrowserLogsForConsole(logs: string[], maxLines?: number): string[];
|
|
175
|
+
/**
|
|
176
|
+
* @param {string[]} logs
|
|
177
|
+
* @returns {void}
|
|
178
|
+
*/
|
|
179
|
+
printBrowserLogsForFailure(logs: string[]): void;
|
|
180
|
+
/**
|
|
181
|
+
* Takes a screenshot, writes HTML/browser logs to disk, and returns the collected artifacts.
|
|
182
|
+
* @returns {Promise<{currentUrl: string, html: string, htmlPath: string, logs: string[], logsPath: string, screenshotPath: string}>}
|
|
183
|
+
*/
|
|
184
|
+
takeScreenshot(): Promise<{
|
|
185
|
+
currentUrl: string;
|
|
186
|
+
html: string;
|
|
187
|
+
htmlPath: string;
|
|
188
|
+
logs: string[];
|
|
189
|
+
logsPath: string;
|
|
190
|
+
screenshotPath: string;
|
|
191
|
+
}>;
|
|
192
|
+
/** @returns {Promise<void>} */
|
|
193
|
+
stopDriver(): Promise<void>;
|
|
194
|
+
}
|
|
195
|
+
export type BrowserArgs = {
|
|
196
|
+
/**
|
|
197
|
+
* Enable debug logging.
|
|
198
|
+
*/
|
|
199
|
+
debug?: boolean;
|
|
200
|
+
/**
|
|
201
|
+
* Driver configuration.
|
|
202
|
+
*/
|
|
203
|
+
driver?: BrowserDriverConfig;
|
|
204
|
+
/**
|
|
205
|
+
* Optional command communicator for helper-driven navigation.
|
|
206
|
+
*/
|
|
207
|
+
communicator?: import("./system-test-communicator.js").default;
|
|
208
|
+
/**
|
|
209
|
+
* Directory used for saved screenshots and browser artifacts.
|
|
210
|
+
*/
|
|
211
|
+
screenshotsPath?: string;
|
|
212
|
+
};
|
|
213
|
+
export type BrowserDriverConfig = {
|
|
214
|
+
/**
|
|
215
|
+
* Driver implementation to use.
|
|
216
|
+
*/
|
|
217
|
+
type?: "selenium" | "appium";
|
|
218
|
+
/**
|
|
219
|
+
* Driver-specific options.
|
|
220
|
+
*/
|
|
221
|
+
options?: Record<string, any>;
|
|
222
|
+
};
|