system-testing 1.0.35 → 1.0.37

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/build/index.js ADDED
@@ -0,0 +1,3 @@
1
+ const stub = "Hello world";
2
+ export { stub };
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXguanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxJQUFJLEdBQUcsYUFBYSxDQUFBO0FBRTFCLE9BQU8sRUFBQyxJQUFJLEVBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImNvbnN0IHN0dWIgPSBcIkhlbGxvIHdvcmxkXCJcblxuZXhwb3J0IHtzdHVifVxuIl19
@@ -2,7 +2,7 @@ export default class SystemTestBrowserHelper {
2
2
  static current(): SystemTestBrowserHelper;
3
3
  communicator: SystemTestCommunicator;
4
4
  _enabled: boolean;
5
- events: EventEmitter<any>;
5
+ events: EventEmitter<string | symbol, any>;
6
6
  startScoundrel(): Promise<void>;
7
7
  scoundrelWs: WebSocket;
8
8
  scoundrelClientWebSocket: ClientWebSocket;
@@ -103,6 +103,6 @@ export default class SystemTestBrowserHelper {
103
103
  sendQuery(sql: string): Promise<Array<Record<string, any>>>;
104
104
  }
105
105
  import SystemTestCommunicator from "./system-test-communicator.js";
106
- import EventEmitter from "events";
106
+ import { EventEmitter } from "eventemitter3";
107
107
  import ClientWebSocket from "scoundrel-remote-eval/build/client/connections/web-socket/index.js";
108
108
  import Client from "scoundrel-remote-eval/build/client/index.js";
@@ -0,0 +1,238 @@
1
+ // @ts-check
2
+ import Client from "scoundrel-remote-eval/build/client/index.js";
3
+ import ClientWebSocket from "scoundrel-remote-eval/build/client/connections/web-socket/index.js";
4
+ import { digg } from "diggerize";
5
+ import { EventEmitter } from "eventemitter3";
6
+ import SystemTestCommunicator from "./system-test-communicator.js";
7
+ /** @type {{systemTestBrowserHelper: SystemTestBrowserHelper | null}} */
8
+ const shared = {
9
+ systemTestBrowserHelper: null
10
+ };
11
+ export default class SystemTestBrowserHelper {
12
+ static current() {
13
+ if (!shared.systemTestBrowserHelper) {
14
+ throw new Error("No current SystemTestBrowserHelper set");
15
+ }
16
+ return shared.systemTestBrowserHelper;
17
+ }
18
+ constructor() {
19
+ /**
20
+ * @param {any[]} args
21
+ * @returns {void}
22
+ */
23
+ this.fakeConsoleError = (...args) => {
24
+ this.communicator.sendCommand({ type: "console.error", value: this.consoleLogMessage(args) });
25
+ if (this.originalConsoleError) {
26
+ return this.originalConsoleError(...args);
27
+ }
28
+ };
29
+ /**
30
+ * @param {any[]} args
31
+ * @returns {void}
32
+ */
33
+ this.fakeConsoleLog = (...args) => {
34
+ this.communicator.sendCommand({ type: "console.log", value: this.consoleLogMessage(args) });
35
+ if (this.originalConsoleLog) {
36
+ return this.originalConsoleLog(...args);
37
+ }
38
+ };
39
+ /**
40
+ * @param {{data: {path: string, type: string}}} args
41
+ * @returns {Promise<{result: string} | void>}
42
+ */
43
+ this.onCommand = async ({ data }) => {
44
+ if (data.type == "initialize") {
45
+ this.events.emit("initialize");
46
+ if (this._onInitializeCallback) {
47
+ await this._onInitializeCallback();
48
+ }
49
+ return { result: "initialized" };
50
+ }
51
+ else if (data.type == "visit") {
52
+ this.events.emit("navigate", { path: data.path });
53
+ }
54
+ else if (data.type == "dismissTo") {
55
+ this.events.emit("dismissTo", { path: data.path });
56
+ }
57
+ else {
58
+ throw new Error(`Unknown command type for SystemTestBrowserHelper: ${data.type}`);
59
+ }
60
+ };
61
+ this.communicator = new SystemTestCommunicator({ parent: this, onCommand: this.onCommand });
62
+ this._enabled = false;
63
+ this.events = new EventEmitter();
64
+ shared.systemTestBrowserHelper = this;
65
+ this.startScoundrel();
66
+ }
67
+ async startScoundrel() {
68
+ this.scoundrelWs = new WebSocket("http://localhost:8090");
69
+ // @ts-expect-error
70
+ this.scoundrelClientWebSocket = new ClientWebSocket(this.scoundrelWs);
71
+ await this.scoundrelClientWebSocket.waitForOpened();
72
+ this.scoundrelClient = new Client(this.scoundrelClientWebSocket);
73
+ this.events.emit("scoundrelStarted");
74
+ }
75
+ waitForScoundrelStarted() {
76
+ return new Promise((resolve) => {
77
+ if (this.scoundrelClient) {
78
+ resolve(undefined);
79
+ }
80
+ else {
81
+ this.events.once("scoundrelStarted", () => {
82
+ resolve(undefined);
83
+ });
84
+ }
85
+ });
86
+ }
87
+ getScoundrel() {
88
+ if (!this.scoundrelClient) {
89
+ throw new Error("Scoundrel client is not started yet");
90
+ }
91
+ return this.scoundrelClient;
92
+ }
93
+ connectOnError() {
94
+ window.addEventListener("error", (event) => {
95
+ this.handleError({
96
+ type: "error",
97
+ error: event.error,
98
+ errorClass: event.error?.name,
99
+ file: event.filename,
100
+ line: event.lineno,
101
+ message: event.message || "Unknown error",
102
+ url: window.location.href
103
+ });
104
+ });
105
+ }
106
+ connectUnhandledRejection() {
107
+ window.addEventListener("unhandledrejection", (event) => {
108
+ this.handleError({
109
+ type: "unhandledrejection",
110
+ error: event.reason,
111
+ errorClass: "UnhandledRejection",
112
+ message: event.reason.message || event.reason || "Unhandled promise rejection without a message",
113
+ url: window.location.href
114
+ });
115
+ });
116
+ }
117
+ /**
118
+ * @param {object} data
119
+ * @param {string} [data.backtrace]
120
+ * @param {Error} [data.error]
121
+ * @param {string} [data.errorClass]
122
+ * @param {string} [data.file]
123
+ * @param {number} [data.line]
124
+ * @param {string} [data.message]
125
+ * @param {string} [data.type]
126
+ * @param {string} [data.url]
127
+ * @returns {void}
128
+ */
129
+ handleError(data) {
130
+ let backtrace;
131
+ if (data.error && data.error.stack) {
132
+ backtrace = data.error.stack.split("\n");
133
+ backtrace.shift();
134
+ backtrace = backtrace.join("\n");
135
+ }
136
+ else if (data.file) {
137
+ backtrace = `${data.file}:${data.line}`;
138
+ }
139
+ data.backtrace = backtrace;
140
+ this.communicator.sendCommand(data);
141
+ }
142
+ /**
143
+ * @returns {void}
144
+ */
145
+ connectWebSocket() {
146
+ this.ws = new WebSocket("ws://localhost:1985");
147
+ this.communicator.ws = this.ws;
148
+ this.ws.addEventListener("error", digg(this, "communicator", "onError"));
149
+ this.ws.addEventListener("open", digg(this, "communicator", "onOpen"));
150
+ this.ws.addEventListener("message", (event) => this.communicator.onMessage(event.data));
151
+ }
152
+ /**
153
+ * @returns {void}
154
+ */
155
+ enableOnBrowser() {
156
+ this._enabled = true;
157
+ this.connectWebSocket();
158
+ this.connectOnError();
159
+ this.connectUnhandledRejection();
160
+ this.overrideConsoleLog();
161
+ }
162
+ /**
163
+ * @returns {boolean}
164
+ */
165
+ getEnabled() { return this._enabled; }
166
+ /**
167
+ * @returns {EventEmitter}
168
+ */
169
+ getEvents() { return this.events; }
170
+ /**
171
+ * @param {any} arg
172
+ * @param {any[]} [scannedObjects]
173
+ * @returns {any}
174
+ */
175
+ consoleLogMessage(arg, scannedObjects = []) {
176
+ if (Array.isArray(arg)) {
177
+ if (scannedObjects.includes(arg)) {
178
+ return "[recursive]";
179
+ }
180
+ else {
181
+ scannedObjects.push(arg);
182
+ }
183
+ const result = [];
184
+ for (const value of arg) {
185
+ result.push(this.consoleLogMessage(value, scannedObjects));
186
+ }
187
+ return result;
188
+ }
189
+ else if (Object.prototype.toString.call(arg) === '[object Object]') {
190
+ if (scannedObjects.includes(arg)) {
191
+ return "[recursive]";
192
+ }
193
+ else {
194
+ scannedObjects.push(arg);
195
+ }
196
+ /** @type {Record<string, any>} */
197
+ const result = {};
198
+ for (const key in arg) {
199
+ result[key] = this.consoleLogMessage(arg[key], scannedObjects);
200
+ }
201
+ return result;
202
+ }
203
+ else if (typeof arg == "object") {
204
+ return `[object ${arg?.constructor?.name}]`;
205
+ }
206
+ else {
207
+ return arg;
208
+ }
209
+ }
210
+ /**
211
+ * @param {function() : void} callback
212
+ * @returns {void}
213
+ */
214
+ onInitialize(callback) {
215
+ this._onInitializeCallback = callback;
216
+ }
217
+ /**
218
+ * @returns {void}
219
+ */
220
+ overrideConsoleLog() {
221
+ if (this.originalConsoleError || this.originalConsoleLog) {
222
+ throw new Error("Console methods has already been overridden!");
223
+ }
224
+ this.originalConsoleError = console.error;
225
+ this.originalConsoleLog = console.log;
226
+ console.error = this.fakeConsoleError;
227
+ console.log = this.fakeConsoleLog;
228
+ }
229
+ /**
230
+ * @param {string} sql
231
+ * @returns {Promise<Array<Record<string, any>>>}
232
+ */
233
+ async sendQuery(sql) {
234
+ // @ts-expect-error
235
+ return await this.communicator.sendCommand({ type: "query", sql });
236
+ }
237
+ }
238
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3lzdGVtLXRlc3QtYnJvd3Nlci1oZWxwZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc3lzdGVtLXRlc3QtYnJvd3Nlci1oZWxwZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sTUFBTSxNQUFNLDZDQUE2QyxDQUFBO0FBQ2hFLE9BQU8sZUFBZSxNQUFNLG9FQUFvRSxDQUFBO0FBQ2hHLE9BQU8sRUFBQyxJQUFJLEVBQUMsTUFBTSxXQUFXLENBQUE7QUFDOUIsT0FBTyxFQUFDLFlBQVksRUFBQyxNQUFNLGVBQWUsQ0FBQTtBQUUxQyxPQUFPLHNCQUFzQixNQUFNLCtCQUErQixDQUFBO0FBRWxFLHdFQUF3RTtBQUN4RSxNQUFNLE1BQU0sR0FBRztJQUNiLHVCQUF1QixFQUFFLElBQUk7Q0FDOUIsQ0FBQTtBQUVELE1BQU0sQ0FBQyxPQUFPLE9BQU8sdUJBQXVCO0lBQzFDLE1BQU0sQ0FBQyxPQUFPO1FBQ1osSUFBSSxDQUFDLE1BQU0sQ0FBQyx1QkFBdUIsRUFBRSxDQUFDO1lBQ3BDLE1BQU0sSUFBSSxLQUFLLENBQUMsd0NBQXdDLENBQUMsQ0FBQTtRQUMzRCxDQUFDO1FBRUQsT0FBTyxNQUFNLENBQUMsdUJBQXVCLENBQUE7SUFDdkMsQ0FBQztJQUVEO1FBZ0lBOzs7V0FHRztRQUNILHFCQUFnQixHQUFHLENBQUMsR0FBRyxJQUFJLEVBQUUsRUFBRTtZQUM3QixJQUFJLENBQUMsWUFBWSxDQUFDLFdBQVcsQ0FBQyxFQUFDLElBQUksRUFBRSxlQUFlLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxpQkFBaUIsQ0FBQyxJQUFJLENBQUMsRUFBQyxDQUFDLENBQUE7WUFFM0YsSUFBSSxJQUFJLENBQUMsb0JBQW9CLEVBQUUsQ0FBQztnQkFDOUIsT0FBTyxJQUFJLENBQUMsb0JBQW9CLENBQUMsR0FBRyxJQUFJLENBQUMsQ0FBQTtZQUMzQyxDQUFDO1FBQ0gsQ0FBQyxDQUFBO1FBRUQ7OztXQUdH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RyxDQUFBO1FBRXJDLE9BQU8sQ0FBQyxLQUFLLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixDQUFBO1FBQ3JDLE9BQU8sQ0FBQyxHQUFHLEdBQUcsSUFBSSxDQUFDLGNBQWMsQ0FBQTtJQUNuQyxDQUFDO0lBRUQ7OztPQUdHO0lBQ0gsS0FBSyxDQUFDLFNBQVMsQ0FBQyxHQUFHO1FBQ2pCLG1CQUFtQjtRQUNuQixPQUFPLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxXQUFXLENBQUMsRUFBQyxJQUFJLEVBQUUsT0FBTyxFQUFFLEdBQUcsRUFBQyxDQUFDLENBQUE7SUFDbEUsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmltcG9ydCBDbGllbnQgZnJvbSBcInNjb3VuZHJlbC1yZW1vdGUtZXZhbC9idWlsZC9jbGllbnQvaW5kZXguanNcIlxuaW1wb3J0IENsaWVudFdlYlNvY2tldCBmcm9tIFwic2NvdW5kcmVsLXJlbW90ZS1ldmFsL2J1aWxkL2NsaWVudC9jb25uZWN0aW9ucy93ZWItc29ja2V0L2luZGV4LmpzXCJcbmltcG9ydCB7ZGlnZ30gZnJvbSBcImRpZ2dlcml6ZVwiXG5pbXBvcnQge0V2ZW50RW1pdHRlcn0gZnJvbSBcImV2ZW50ZW1pdHRlcjNcIlxuXG5pbXBvcnQgU3lzdGVtVGVzdENvbW11bmljYXRvciBmcm9tIFwiLi9zeXN0ZW0tdGVzdC1jb21tdW5pY2F0b3IuanNcIlxuXG4vKiogQHR5cGUge3tzeXN0ZW1UZXN0QnJvd3NlckhlbHBlcjogU3lzdGVtVGVzdEJyb3dzZXJIZWxwZXIgfCBudWxsfX0gKi9cbmNvbnN0IHNoYXJlZCA9IHtcbiAgc3lzdGVtVGVzdEJyb3dzZXJIZWxwZXI6IG51bGxcbn1cblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgU3lzdGVtVGVzdEJyb3dzZXJIZWxwZXIge1xuICBzdGF0aWMgY3VycmVudCgpIHtcbiAgICBpZiAoIXNoYXJlZC5zeXN0ZW1UZXN0QnJvd3NlckhlbHBlcikge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFwiTm8gY3VycmVudCBTeXN0ZW1UZXN0QnJvd3NlckhlbHBlciBzZXRcIilcbiAgICB9XG5cbiAgICByZXR1cm4gc2hhcmVkLnN5c3RlbVRlc3RCcm93c2VySGVscGVyXG4gIH1cblxuICBjb25zdHJ1Y3RvcigpIHtcbiAgICB0aGlzLmNvbW11bmljYXRvciA9IG5ldyBTeXN0ZW1UZXN0Q29tbXVuaWNhdG9yKHtwYXJlbnQ6IHRoaXMsIG9uQ29tbWFuZDogdGhpcy5vbkNvbW1hbmR9KVxuICAgIHRoaXMuX2VuYWJsZWQgPSBmYWxzZVxuICAgIHRoaXMuZXZlbnRzID0gbmV3IEV2ZW50RW1pdHRlcigpXG5cbiAgICBzaGFyZWQuc3lzdGVtVGVzdEJyb3dzZXJIZWxwZXIgPSB0aGlzXG5cbiAgICB0aGlzLnN0YXJ0U2NvdW5kcmVsKClcbiAgfVxuXG4gIGFzeW5jIHN0YXJ0U2NvdW5kcmVsKCkge1xuICAgIHRoaXMuc2NvdW5kcmVsV3MgPSBuZXcgV2ViU29ja2V0KFwiaHR0cDovL2xvY2FsaG9zdDo4MDkwXCIpXG5cbiAgICAvLyBAdHMtZXhwZWN0LWVycm9yXG4gICAgdGhpcy5zY291bmRyZWxDbGllbnRXZWJTb2NrZXQgPSBuZXcgQ2xpZW50V2ViU29ja2V0KHRoaXMuc2NvdW5kcmVsV3MpXG5cbiAgICBhd2FpdCB0aGlzLnNjb3VuZHJlbENsaWVudFdlYlNvY2tldC53YWl0Rm9yT3BlbmVkKClcblxuICAgIHRoaXMuc2NvdW5kcmVsQ2xpZW50ID0gbmV3IENsaWVudCh0aGlzLnNjb3VuZHJlbENsaWVudFdlYlNvY2tldClcbiAgICB0aGlzLmV2ZW50cy5lbWl0KFwic2NvdW5kcmVsU3RhcnRlZFwiKVxuICB9XG5cbiAgd2FpdEZvclNjb3VuZHJlbFN0YXJ0ZWQoKSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICBpZiAodGhpcy5zY291bmRyZWxDbGllbnQpIHtcbiAgICAgICAgcmVzb2x2ZSh1bmRlZmluZWQpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICB0aGlzLmV2ZW50cy5vbmNlKFwic2NvdW5kcmVsU3RhcnRlZFwiLCAoKSA9PiB7XG4gICAgICAgICAgcmVzb2x2ZSh1bmRlZmluZWQpXG4gICAgICAgIH0pXG4gICAgICB9XG4gICAgfSlcbiAgfVxuXG4gIGdldFNjb3VuZHJlbCgpIHtcbiAgICBpZiAoIXRoaXMuc2NvdW5kcmVsQ2xpZW50KSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJTY291bmRyZWwgY2xpZW50IGlzIG5vdCBzdGFydGVkIHlldFwiKVxuICAgIH1cblxuICAgIHJldHVybiB0aGlzLnNjb3VuZHJlbENsaWVudFxuICB9XG5cbiAgY29ubmVjdE9uRXJyb3IoKSB7XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJlcnJvclwiLCAoZXZlbnQpID0+IHtcbiAgICAgIHRoaXMuaGFuZGxlRXJyb3Ioe1xuICAgICAgICB0eXBlOiBcImVycm9yXCIsXG4gICAgICAgIGVycm9yOiBldmVudC5lcnJvcixcbiAgICAgICAgZXJyb3JDbGFzczogZXZlbnQuZXJyb3I/Lm5hbWUsXG4gICAgICAgIGZpbGU6IGV2ZW50LmZpbGVuYW1lLFxuICAgICAgICBsaW5lOiBldmVudC5saW5lbm8sXG4gICAgICAgIG1lc3NhZ2U6IGV2ZW50Lm1lc3NhZ2UgfHwgXCJVbmtub3duIGVycm9yXCIsXG4gICAgICAgIHVybDogd2luZG93LmxvY2F0aW9uLmhyZWZcbiAgICAgIH0pXG4gICAgfSlcbiAgfVxuXG4gIGNvbm5lY3RVbmhhbmRsZWRSZWplY3Rpb24oKSB7XG4gICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJ1bmhhbmRsZWRyZWplY3Rpb25cIiwgKGV2ZW50KSA9PiB7XG4gICAgICB0aGlzLmhhbmRsZUVycm9yKHtcbiAgICAgICAgdHlwZTogXCJ1bmhhbmRsZWRyZWplY3Rpb25cIixcbiAgICAgICAgZXJyb3I6IGV2ZW50LnJlYXNvbixcbiAgICAgICAgZXJyb3JDbGFzczogXCJVbmhhbmRsZWRSZWplY3Rpb25cIixcbiAgICAgICAgbWVzc2FnZTogZXZlbnQucmVhc29uLm1lc3NhZ2UgfHwgZXZlbnQucmVhc29uIHx8IFwiVW5oYW5kbGVkIHByb21pc2UgcmVqZWN0aW9uIHdpdGhvdXQgYSBtZXNzYWdlXCIsXG4gICAgICAgIHVybDogd2luZG93LmxvY2F0aW9uLmhyZWZcbiAgICAgIH0pXG4gICAgfSlcbiAgfVxuXG4gIC8qKlxuICAgKiBAcGFyYW0ge29iamVjdH0gZGF0YVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2RhdGEuYmFja3RyYWNlXVxuICAgKiBAcGFyYW0ge0Vycm9yfSBbZGF0YS5lcnJvcl1cbiAgICogQHBhcmFtIHtzdHJpbmd9IFtkYXRhLmVycm9yQ2xhc3NdXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbZGF0YS5maWxlXVxuICAgKiBAcGFyYW0ge251bWJlcn0gW2RhdGEubGluZV1cbiAgICogQHBhcmFtIHtzdHJpbmd9IFtkYXRhLm1lc3NhZ2VdXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBbZGF0YS50eXBlXVxuICAgKiBAcGFyYW0ge3N0cmluZ30gW2RhdGEudXJsXVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIGhhbmRsZUVycm9yKGRhdGEpIHtcbiAgICBsZXQgYmFja3RyYWNlXG5cbiAgICBpZiAoZGF0YS5lcnJvciAmJiBkYXRhLmVycm9yLnN0YWNrKSB7XG4gICAgICBiYWNrdHJhY2UgPSBkYXRhLmVycm9yLnN0YWNrLnNwbGl0KFwiXFxuXCIpXG4gICAgICBiYWNrdHJhY2Uuc2hpZnQoKVxuICAgICAgYmFja3RyYWNlID0gYmFja3RyYWNlLmpvaW4oXCJcXG5cIilcbiAgICB9IGVsc2UgaWYgKGRhdGEuZmlsZSkge1xuICAgICAgYmFja3RyYWNlID0gYCR7ZGF0YS5maWxlfToke2RhdGEubGluZX1gXG4gICAgfVxuXG4gICAgZGF0YS5iYWNrdHJhY2UgPSBiYWNrdHJhY2VcblxuICAgIHRoaXMuY29tbXVuaWNhdG9yLnNlbmRDb21tYW5kKGRhdGEpXG4gIH1cblxuICAvKipcbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBjb25uZWN0V2ViU29ja2V0KCkge1xuICAgIHRoaXMud3MgPSBuZXcgV2ViU29ja2V0KFwid3M6Ly9sb2NhbGhvc3Q6MTk4NVwiKVxuICAgIHRoaXMuY29tbXVuaWNhdG9yLndzID0gdGhpcy53c1xuICAgIHRoaXMud3MuYWRkRXZlbnRMaXN0ZW5lcihcImVycm9yXCIsIGRpZ2codGhpcywgXCJjb21tdW5pY2F0b3JcIiwgXCJvbkVycm9yXCIpKVxuICAgIHRoaXMud3MuYWRkRXZlbnRMaXN0ZW5lcihcIm9wZW5cIiwgZGlnZyh0aGlzLCBcImNvbW11bmljYXRvclwiLCBcIm9uT3BlblwiKSlcbiAgICB0aGlzLndzLmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIChldmVudCkgPT4gdGhpcy5jb21tdW5pY2F0b3Iub25NZXNzYWdlKGV2ZW50LmRhdGEpKVxuICB9XG5cbiAgLyoqXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgZW5hYmxlT25Ccm93c2VyKCkge1xuICAgIHRoaXMuX2VuYWJsZWQgPSB0cnVlXG4gICAgdGhpcy5jb25uZWN0V2ViU29ja2V0KClcbiAgICB0aGlzLmNvbm5lY3RPbkVycm9yKClcbiAgICB0aGlzLmNvbm5lY3RVbmhhbmRsZWRSZWplY3Rpb24oKVxuICAgIHRoaXMub3ZlcnJpZGVDb25zb2xlTG9nKClcbiAgfVxuXG4gIC8qKlxuICAgKiBAcmV0dXJucyB7Ym9vbGVhbn1cbiAgICovXG4gIGdldEVuYWJsZWQoKSB7IHJldHVybiB0aGlzLl9lbmFibGVkIH1cblxuICAvKipcbiAgICogQHJldHVybnMge0V2ZW50RW1pdHRlcn1cbiAgICovXG4gIGdldEV2ZW50cygpIHsgcmV0dXJuIHRoaXMuZXZlbnRzIH1cblxuICAvKipcbiAgICogQHBhcmFtIHthbnlbXX0gYXJnc1xuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIGZha2VDb25zb2xlRXJyb3IgPSAoLi4uYXJncykgPT4ge1xuICAgIHRoaXMuY29tbXVuaWNhdG9yLnNlbmRDb21tYW5kKHt0eXBlOiBcImNvbnNvbGUuZXJyb3JcIiwgdmFsdWU6IHRoaXMuY29uc29sZUxvZ01lc3NhZ2UoYXJncyl9KVxuXG4gICAgaWYgKHRoaXMub3JpZ2luYWxDb25zb2xlRXJyb3IpIHtcbiAgICAgIHJldHVybiB0aGlzLm9yaWdpbmFsQ29uc29sZUVycm9yKC4uLmFyZ3MpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7YW55W119IGFyZ3NcbiAgICogQHJldHVybnMge3ZvaWR9XG4gICAqL1xuICBmYWtlQ29uc29sZUxvZyA9ICguLi5hcmdzKSA9PiB7XG4gICAgdGhpcy5jb21tdW5pY2F0b3Iuc2VuZENvbW1hbmQoe3R5cGU6IFwiY29uc29sZS5sb2dcIiwgdmFsdWU6IHRoaXMuY29uc29sZUxvZ01lc3NhZ2UoYXJncyl9KVxuXG4gICAgaWYgKHRoaXMub3JpZ2luYWxDb25zb2xlTG9nKSB7XG4gICAgICByZXR1cm4gdGhpcy5vcmlnaW5hbENvbnNvbGVMb2coLi4uYXJncylcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHthbnl9IGFyZ1xuICAgKiBAcGFyYW0ge2FueVtdfSBbc2Nhbm5lZE9iamVjdHNdXG4gICAqIEByZXR1cm5zIHthbnl9XG4gICAqL1xuICBjb25zb2xlTG9nTWVzc2FnZShhcmcsIHNjYW5uZWRPYmplY3RzID0gW10pIHtcbiAgICBpZiAoQXJyYXkuaXNBcnJheShhcmcpKSB7XG4gICAgICBpZiAoc2Nhbm5lZE9iamVjdHMuaW5jbHVkZXMoYXJnKSkge1xuICAgICAgICByZXR1cm4gXCJbcmVjdXJzaXZlXVwiXG4gICAgICB9IGVsc2Uge1xuICAgICAgICBzY2FubmVkT2JqZWN0cy5wdXNoKGFyZylcbiAgICAgIH1cblxuICAgICAgY29uc3QgcmVzdWx0ID0gW11cblxuICAgICAgZm9yIChjb25zdCB2YWx1ZSBvZiBhcmcpIHtcbiAgICAgICAgcmVzdWx0LnB1c2godGhpcy5jb25zb2xlTG9nTWVzc2FnZSh2YWx1ZSwgc2Nhbm5lZE9iamVjdHMpKVxuICAgICAgfVxuXG4gICAgICByZXR1cm4gcmVzdWx0XG4gICAgfSBlbHNlIGlmIChPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwoYXJnKSA9PT0gJ1tvYmplY3QgT2JqZWN0XScpIHtcbiAgICAgIGlmIChzY2FubmVkT2JqZWN0cy5pbmNsdWRlcyhhcmcpKSB7XG4gICAgICAgIHJldHVybiBcIltyZWN1cnNpdmVdXCJcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHNjYW5uZWRPYmplY3RzLnB1c2goYXJnKVxuICAgICAgfVxuXG4gICAgICAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIGFueT59ICovXG4gICAgICBjb25zdCByZXN1bHQgPSB7fVxuXG4gICAgICBmb3IgKGNvbnN0IGtleSBpbiBhcmcpIHtcbiAgICAgICAgcmVzdWx0W2tleV0gPSB0aGlzLmNvbnNvbGVMb2dNZXNzYWdlKGFyZ1trZXldLCBzY2FubmVkT2JqZWN0cylcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHJlc3VsdFxuICAgIH0gZWxzZSBpZiAodHlwZW9mIGFyZyA9PSBcIm9iamVjdFwiKSB7XG4gICAgICByZXR1cm4gYFtvYmplY3QgJHthcmc/LmNvbnN0cnVjdG9yPy5uYW1lfV1gXG4gICAgfSBlbHNlIHtcbiAgICAgIHJldHVybiBhcmdcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHt7ZGF0YToge3BhdGg6IHN0cmluZywgdHlwZTogc3RyaW5nfX19IGFyZ3NcbiAgICogQHJldHVybnMge1Byb21pc2U8e3Jlc3VsdDogc3RyaW5nfSB8IHZvaWQ+fVxuICAgKi9cbiAgb25Db21tYW5kID0gYXN5bmMgKHtkYXRhfSkgPT4ge1xuICAgIGlmIChkYXRhLnR5cGUgPT0gXCJpbml0aWFsaXplXCIpIHtcbiAgICAgIHRoaXMuZXZlbnRzLmVtaXQoXCJpbml0aWFsaXplXCIpXG5cbiAgICAgIGlmICh0aGlzLl9vbkluaXRpYWxpemVDYWxsYmFjaykge1xuICAgICAgICBhd2FpdCB0aGlzLl9vbkluaXRpYWxpemVDYWxsYmFjaygpXG4gICAgICB9XG5cbiAgICAgIHJldHVybiB7cmVzdWx0OiBcImluaXRpYWxpemVkXCJ9XG4gICAgfSBlbHNlIGlmIChkYXRhLnR5cGUgPT0gXCJ2aXNpdFwiKSB7XG4gICAgICB0aGlzLmV2ZW50cy5lbWl0KFwibmF2aWdhdGVcIiwge3BhdGg6IGRhdGEucGF0aH0pXG4gICAgfSBlbHNlIGlmIChkYXRhLnR5cGUgPT0gXCJkaXNtaXNzVG9cIikge1xuICAgICAgdGhpcy5ldmVudHMuZW1pdChcImRpc21pc3NUb1wiLCB7cGF0aDogZGF0YS5wYXRofSlcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBVbmtub3duIGNvbW1hbmQgdHlwZSBmb3IgU3lzdGVtVGVzdEJyb3dzZXJIZWxwZXI6ICR7ZGF0YS50eXBlfWApXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7ZnVuY3Rpb24oKSA6IHZvaWR9IGNhbGxiYWNrXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgb25Jbml0aWFsaXplKGNhbGxiYWNrKSB7XG4gICAgdGhpcy5fb25Jbml0aWFsaXplQ2FsbGJhY2sgPSBjYWxsYmFja1xuICB9XG5cbiAgLyoqXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgb3ZlcnJpZGVDb25zb2xlTG9nKCkge1xuICAgIGlmICh0aGlzLm9yaWdpbmFsQ29uc29sZUVycm9yIHx8IHRoaXMub3JpZ2luYWxDb25zb2xlTG9nKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoXCJDb25zb2xlIG1ldGhvZHMgaGFzIGFscmVhZHkgYmVlbiBvdmVycmlkZGVuIVwiKVxuICAgIH1cblxuICAgIHRoaXMub3JpZ2luYWxDb25zb2xlRXJyb3IgPSBjb25zb2xlLmVycm9yXG4gICAgdGhpcy5vcmlnaW5hbENvbnNvbGVMb2cgPSBjb25zb2xlLmxvZ1xuXG4gICAgY29uc29sZS5lcnJvciA9IHRoaXMuZmFrZUNvbnNvbGVFcnJvclxuICAgIGNvbnNvbGUubG9nID0gdGhpcy5mYWtlQ29uc29sZUxvZ1xuICB9XG5cbiAgLyoqXG4gICAqIEBwYXJhbSB7c3RyaW5nfSBzcWxcbiAgICogQHJldHVybnMge1Byb21pc2U8QXJyYXk8UmVjb3JkPHN0cmluZywgYW55Pj4+fVxuICAgKi9cbiAgYXN5bmMgc2VuZFF1ZXJ5KHNxbCkge1xuICAgIC8vIEB0cy1leHBlY3QtZXJyb3JcbiAgICByZXR1cm4gYXdhaXQgdGhpcy5jb21tdW5pY2F0b3Iuc2VuZENvbW1hbmQoe3R5cGU6IFwicXVlcnlcIiwgc3FsfSlcbiAgfVxufVxuIl19
@@ -0,0 +1,105 @@
1
+ // @ts-check
2
+ export default class SystemTestCommunicator {
3
+ /**
4
+ * @param {object} args
5
+ * @param {(args: Record<string, any>) => Promise<{result: string} | void>} args.onCommand
6
+ * @param {object} [args.parent]
7
+ */
8
+ constructor({ onCommand, parent }) {
9
+ /** @type {Record<string, {resolve: (data: any) => void, reject: (data: any) => void}>} */
10
+ this._responses = {};
11
+ /** @type {Record<string, any>} */
12
+ this._sendQueue = [];
13
+ this._sendQueueCount = 0;
14
+ /** @type {WebSocket | null} */
15
+ this.ws = null;
16
+ /** @param {Error} error */
17
+ this.onError = (error) => {
18
+ console.error("onWebSocketClientError", error);
19
+ };
20
+ /** @param {string} rawData */
21
+ this.onMessage = async (rawData) => {
22
+ /** @type {{data: any, id: number, type: string, isTrusted?: boolean}} */
23
+ const data = JSON.parse(rawData);
24
+ if (data.isTrusted) {
25
+ // Ignore
26
+ }
27
+ else if (data.type == "command") {
28
+ try {
29
+ const result = await this.onCommand({ data: data.data });
30
+ this.respond(data.id, { result });
31
+ }
32
+ catch (error) {
33
+ if (error instanceof Error) {
34
+ this.respond(data.id, { error: error.message });
35
+ }
36
+ else {
37
+ this.respond(data.id, { error: error });
38
+ }
39
+ }
40
+ }
41
+ else if (data.type == "response") {
42
+ const response = this._responses[data.id];
43
+ if (!response) {
44
+ throw new Error(`No such response: ${data.id}`);
45
+ }
46
+ delete this._responses[data.id];
47
+ if (data.data.error) {
48
+ response.reject(data.data.error);
49
+ }
50
+ else {
51
+ response.resolve(data.data.result);
52
+ }
53
+ }
54
+ else {
55
+ throw new Error(`Unknown type for SystemTestCommunicator: ${data.type}: ${JSON.stringify(data)}`);
56
+ }
57
+ };
58
+ this.onOpen = () => {
59
+ this.flushSendQueue();
60
+ };
61
+ this.onCommand = onCommand;
62
+ this.parent = parent;
63
+ }
64
+ flushSendQueue() {
65
+ while (this._sendQueue.length !== 0) {
66
+ const data = this._sendQueue.shift();
67
+ if (!this.ws || this.ws.readyState !== 1) {
68
+ throw new Error("WebSocket is not open");
69
+ }
70
+ this.ws.send(JSON.stringify(data));
71
+ }
72
+ }
73
+ /**
74
+ * @param {Record<string, any>} data
75
+ * @returns {void}
76
+ */
77
+ send(data) {
78
+ this._sendQueue.push(data);
79
+ if (this.ws.readyState == 1) {
80
+ this.flushSendQueue();
81
+ }
82
+ }
83
+ /**
84
+ * Sends a command and returns a promise that resolves with the response.
85
+ * @param {Record<string, any>} data - The command data to send.
86
+ * @returns {Promise<void>} A promise that resolves with the response data.
87
+ */
88
+ sendCommand(data) {
89
+ return new Promise((resolve, reject) => {
90
+ const id = this._sendQueueCount;
91
+ this._sendQueueCount += 1;
92
+ this._responses[id] = { resolve, reject };
93
+ this.send({ type: "command", id, data });
94
+ });
95
+ }
96
+ /**
97
+ * @param {number} id
98
+ * @param {Record<string, any>} data
99
+ * @returns {void}
100
+ */
101
+ respond(id, data) {
102
+ this.send({ type: "response", id, data });
103
+ }
104
+ }
105
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3lzdGVtLXRlc3QtY29tbXVuaWNhdG9yLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL3N5c3RlbS10ZXN0LWNvbW11bmljYXRvci5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxZQUFZO0FBRVosTUFBTSxDQUFDLE9BQU8sT0FBTyxzQkFBc0I7SUFZekM7Ozs7T0FJRztJQUNILFlBQVksRUFBQyxTQUFTLEVBQUUsTUFBTSxFQUFDO1FBaEIvQiwwRkFBMEY7UUFDMUYsZUFBVSxHQUFHLEVBQUUsQ0FBQTtRQUVmLGtDQUFrQztRQUNsQyxlQUFVLEdBQUcsRUFBRSxDQUFBO1FBRWYsb0JBQWUsR0FBRyxDQUFDLENBQUE7UUFFbkIsK0JBQStCO1FBQy9CLE9BQUUsR0FBRyxJQUFJLENBQUE7UUF3QlQsMkJBQTJCO1FBQzNCLFlBQU8sR0FBRyxDQUFDLEtBQUssRUFBRSxFQUFFO1lBQ2xCLE9BQU8sQ0FBQyxLQUFLLENBQUMsd0JBQXdCLEVBQUUsS0FBSyxDQUFDLENBQUE7UUFDaEQsQ0FBQyxDQUFBO1FBRUQsOEJBQThCO1FBQzlCLGNBQVMsR0FBRyxLQUFLLEVBQUUsT0FBTyxFQUFFLEVBQUU7WUFDNUIseUVBQXlFO1lBQ3pFLE1BQU0sSUFBSSxHQUFHLElBQUksQ0FBQyxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUE7WUFFaEMsSUFBSSxJQUFJLENBQUMsU0FBUyxFQUFFLENBQUM7Z0JBQ25CLFNBQVM7WUFDWCxDQUFDO2lCQUFNLElBQUksSUFBSSxDQUFDLElBQUksSUFBSSxTQUFTLEVBQUUsQ0FBQztnQkFDbEMsSUFBSSxDQUFDO29CQUNILE1BQU0sTUFBTSxHQUFHLE1BQU0sSUFBSSxDQUFDLFNBQVMsQ0FBQyxFQUFDLElBQUksRUFBRSxJQUFJLENBQUMsSUFBSSxFQUFDLENBQUMsQ0FBQTtvQkFFdEQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxJQUFJLENBQUMsRUFBRSxFQUFFLEVBQUMsTUFBTSxFQUFDLENBQUMsQ0FBQTtnQkFD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QUFDLFVBQVUsQ0FBQyxFQUFFLENBQUMsR0FBRyxFQUFDLE9BQU8sRUFBRSxNQUFNLEVBQUMsQ0FBQTtZQUV2QyxJQUFJLENBQUMsSUFBSSxDQUFDLEVBQUMsSUFBSSxFQUFFLFNBQVMsRUFBRSxFQUFFLEVBQUUsSUFBSSxFQUFDLENBQUMsQ0FBQTtRQUN4QyxDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsT0FBTyxDQUFDLEVBQUUsRUFBRSxJQUFJO1FBQ2QsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFDLElBQUksRUFBRSxVQUFVLEVBQUUsRUFBRSxFQUFFLElBQUksRUFBQyxDQUFDLENBQUE7SUFDekMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiLy8gQHRzLWNoZWNrXG5cbmV4cG9ydCBkZWZhdWx0IGNsYXNzIFN5c3RlbVRlc3RDb21tdW5pY2F0b3Ige1xuICAvKiogQHR5cGUge1JlY29yZDxzdHJpbmcsIHtyZXNvbHZlOiAoZGF0YTogYW55KSA9PiB2b2lkLCByZWplY3Q6IChkYXRhOiBhbnkpID0+IHZvaWR9Pn0gKi9cbiAgX3Jlc3BvbnNlcyA9IHt9XG5cbiAgLyoqIEB0eXBlIHtSZWNvcmQ8c3RyaW5nLCBhbnk+fSAqL1xuICBfc2VuZFF1ZXVlID0gW11cblxuICBfc2VuZFF1ZXVlQ291bnQgPSAwXG5cbiAgLyoqIEB0eXBlIHtXZWJTb2NrZXQgfCBudWxsfSAqL1xuICB3cyA9IG51bGxcblxuICAvKipcbiAgICogQHBhcmFtIHtvYmplY3R9IGFyZ3NcbiAgICogQHBhcmFtIHsoYXJnczogUmVjb3JkPHN0cmluZywgYW55PikgPT4gUHJvbWlzZTx7cmVzdWx0OiBzdHJpbmd9IHwgdm9pZD59IGFyZ3Mub25Db21tYW5kXG4gICAqIEBwYXJhbSB7b2JqZWN0fSBbYXJncy5wYXJlbnRdXG4gICAqL1xuICBjb25zdHJ1Y3Rvcih7b25Db21tYW5kLCBwYXJlbnR9KSB7XG4gICAgdGhpcy5vbkNvbW1hbmQgPSBvbkNvbW1hbmRcbiAgICB0aGlzLnBhcmVudCA9IHBhcmVudFxuICB9XG5cbiAgZmx1c2hTZW5kUXVldWUoKSB7XG4gICAgd2hpbGUgKHRoaXMuX3NlbmRRdWV1ZS5sZW5ndGggIT09IDApIHtcbiAgICAgIGNvbnN0IGRhdGEgPSB0aGlzLl9zZW5kUXVldWUuc2hpZnQoKVxuXG4gICAgICBpZiAoIXRoaXMud3MgfHwgdGhpcy53cy5yZWFkeVN0YXRlICE9PSAxKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIldlYlNvY2tldCBpcyBub3Qgb3BlblwiKVxuICAgICAgfVxuXG4gICAgICB0aGlzLndzLnNlbmQoSlNPTi5zdHJpbmdpZnkoZGF0YSkpXG4gICAgfVxuICB9XG5cbiAgLyoqIEBwYXJhbSB7RXJyb3J9IGVycm9yICovXG4gIG9uRXJyb3IgPSAoZXJyb3IpID0+IHtcbiAgICBjb25zb2xlLmVycm9yKFwib25XZWJTb2NrZXRDbGllbnRFcnJvclwiLCBlcnJvcilcbiAgfVxuXG4gIC8qKiBAcGFyYW0ge3N0cmluZ30gcmF3RGF0YSAqL1xuICBvbk1lc3NhZ2UgPSBhc3luYyAocmF3RGF0YSkgPT4ge1xuICAgIC8qKiBAdHlwZSB7e2RhdGE6IGFueSwgaWQ6IG51bWJlciwgdHlwZTogc3RyaW5nLCBpc1RydXN0ZWQ/OiBib29sZWFufX0gKi9cbiAgICBjb25zdCBkYXRhID0gSlNPTi5wYXJzZShyYXdEYXRhKVxuXG4gICAgaWYgKGRhdGEuaXNUcnVzdGVkKSB7XG4gICAgICAvLyBJZ25vcmVcbiAgICB9IGVsc2UgaWYgKGRhdGEudHlwZSA9PSBcImNvbW1hbmRcIikge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5vbkNvbW1hbmQoe2RhdGE6IGRhdGEuZGF0YX0pXG5cbiAgICAgICAgdGhpcy5yZXNwb25kKGRhdGEuaWQsIHtyZXN1bHR9KVxuICAgICAgfSBjYXRjaCAoZXJyb3IpIHtcbiAgICAgICAgaWYgKGVycm9yIGluc3RhbmNlb2YgRXJyb3IpIHtcbiAgICAgICAgICB0aGlzLnJlc3BvbmQoZGF0YS5pZCwge2Vycm9yOiBlcnJvci5tZXNzYWdlfSlcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICB0aGlzLnJlc3BvbmQoZGF0YS5pZCwge2Vycm9yOiBlcnJvcn0pXG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9IGVsc2UgaWYgKGRhdGEudHlwZSA9PSBcInJlc3BvbnNlXCIpIHtcbiAgICAgIGNvbnN0IHJlc3BvbnNlID0gdGhpcy5fcmVzcG9uc2VzW2RhdGEuaWRdXG5cbiAgICAgIGlmICghcmVzcG9uc2UpIHtcbiAgICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBzdWNoIHJlc3BvbnNlOiAke2RhdGEuaWR9YClcbiAgICAgIH1cblxuICAgICAgZGVsZXRlIHRoaXMuX3Jlc3BvbnNlc1tkYXRhLmlkXVxuXG4gICAgICBpZiAoZGF0YS5kYXRhLmVycm9yKSB7XG4gICAgICAgIHJlc3BvbnNlLnJlamVjdChkYXRhLmRhdGEuZXJyb3IpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXNwb25zZS5yZXNvbHZlKGRhdGEuZGF0YS5yZXN1bHQpXG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgVW5rbm93biB0eXBlIGZvciBTeXN0ZW1UZXN0Q29tbXVuaWNhdG9yOiAke2RhdGEudHlwZX06ICR7SlNPTi5zdHJpbmdpZnkoZGF0YSl9YClcbiAgICB9XG4gIH1cblxuICBvbk9wZW4gPSAoKSA9PiB7XG4gICAgdGhpcy5mbHVzaFNlbmRRdWV1ZSgpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCBhbnk+fSBkYXRhXG4gICAqIEByZXR1cm5zIHt2b2lkfVxuICAgKi9cbiAgc2VuZChkYXRhKSB7XG4gICAgdGhpcy5fc2VuZFF1ZXVlLnB1c2goZGF0YSlcblxuICAgIGlmICh0aGlzLndzLnJlYWR5U3RhdGUgPT0gMSkge1xuICAgICAgdGhpcy5mbHVzaFNlbmRRdWV1ZSgpXG4gICAgfVxuICB9XG5cbiAgLyoqXG4gICAqIFNlbmRzIGEgY29tbWFuZCBhbmQgcmV0dXJucyBhIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aXRoIHRoZSByZXNwb25zZS5cbiAgICogQHBhcmFtIHtSZWNvcmQ8c3RyaW5nLCBhbnk+fSBkYXRhIC0gVGhlIGNvbW1hbmQgZGF0YSB0byBzZW5kLlxuICAgKiBAcmV0dXJucyB7UHJvbWlzZTx2b2lkPn0gQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgd2l0aCB0aGUgcmVzcG9uc2UgZGF0YS5cbiAgICovXG4gIHNlbmRDb21tYW5kKGRhdGEpIHtcbiAgICByZXR1cm4gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xuICAgICAgY29uc3QgaWQgPSB0aGlzLl9zZW5kUXVldWVDb3VudFxuXG4gICAgICB0aGlzLl9zZW5kUXVldWVDb3VudCArPSAxXG4gICAgICB0aGlzLl9yZXNwb25zZXNbaWRdID0ge3Jlc29sdmUsIHJlamVjdH1cblxuICAgICAgdGhpcy5zZW5kKHt0eXBlOiBcImNvbW1hbmRcIiwgaWQsIGRhdGF9KVxuICAgIH0pXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtudW1iZXJ9IGlkXG4gICAqIEBwYXJhbSB7UmVjb3JkPHN0cmluZywgYW55Pn0gZGF0YVxuICAgKiBAcmV0dXJucyB7dm9pZH1cbiAgICovXG4gIHJlc3BvbmQoaWQsIGRhdGEpIHtcbiAgICB0aGlzLnNlbmQoe3R5cGU6IFwicmVzcG9uc2VcIiwgaWQsIGRhdGF9KVxuICB9XG59XG4iXX0=
@@ -0,0 +1,66 @@
1
+ // @ts-check
2
+ import fs from "node:fs/promises";
3
+ import http from "node:http";
4
+ import mime from "mime";
5
+ import url from "url";
6
+ export default class SystemTestHttpServer {
7
+ constructor() {
8
+ /**
9
+ * @param {http.IncomingMessage} request
10
+ * @param {http.ServerResponse} response
11
+ * @returns {Promise<void>}
12
+ */
13
+ this.onHttpServerRequest = async (request, response) => {
14
+ if (!request.url) {
15
+ response.statusCode = 400;
16
+ response.end("Bad Request");
17
+ return;
18
+ }
19
+ const parsedUrl = url.parse(request.url);
20
+ let filePath = `${process.cwd()}/dist${parsedUrl.pathname}`;
21
+ if (filePath.endsWith("/")) {
22
+ filePath += "index.html";
23
+ }
24
+ let fileExists;
25
+ try {
26
+ await fs.stat(filePath);
27
+ fileExists = true;
28
+ }
29
+ catch (_error) { // eslint-disable-line no-unused-vars
30
+ fileExists = false;
31
+ }
32
+ if (!fileExists) {
33
+ filePath = `${process.cwd()}/dist/index.html`;
34
+ }
35
+ const fileContent = await fs.readFile(filePath);
36
+ const mimeType = mime.getType(filePath);
37
+ response.statusCode = 200;
38
+ if (mimeType) {
39
+ response.setHeader("Content-Type", mimeType);
40
+ }
41
+ response.end(fileContent);
42
+ };
43
+ }
44
+ /** @returns {void} */
45
+ close() {
46
+ if (!this.httpServer) {
47
+ throw new Error("HTTP server is not initialized");
48
+ }
49
+ this.httpServer.close();
50
+ }
51
+ /** @returns {Promise<void>} */
52
+ async start() {
53
+ this.basePath = await fs.realpath(`${__dirname}/../..`);
54
+ await this.startHttpServer();
55
+ }
56
+ /** @returns {Promise<void>} */
57
+ startHttpServer() {
58
+ return new Promise((resolve) => {
59
+ this.httpServer = http.createServer(this.onHttpServerRequest);
60
+ this.httpServer.listen(1984, "localhost", () => {
61
+ resolve();
62
+ });
63
+ });
64
+ }
65
+ }
66
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3lzdGVtLXRlc3QtaHR0cC1zZXJ2ZXIuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc3lzdGVtLXRlc3QtaHR0cC1zZXJ2ZXIuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsWUFBWTtBQUVaLE9BQU8sRUFBRSxNQUFNLGtCQUFrQixDQUFBO0FBQ2pDLE9BQU8sSUFBSSxNQUFNLFdBQVcsQ0FBQTtBQUM1QixPQUFPLElBQUksTUFBTSxNQUFNLENBQUE7QUFDdkIsT0FBTyxHQUFHLE1BQU0sS0FBSyxDQUFBO0FBRXJCLE1BQU0sQ0FBQyxPQUFPLE9BQU8sb0JBQW9CO0lBQXpDO1FBVUU7Ozs7V0FJRztRQUNILHdCQUFtQixHQUFHLEtBQUssRUFBRSxPQUFPLEVBQUUsUUFBUSxFQUFFLEVBQUU7WUFDaEQsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsQ0FBQztnQkFDakIsUUFBUSxDQUFDLFVBQVUsR0FBRyxHQUFHLENBQUE7Z0JBQ3pCLFFBQVEsQ0FBQyxHQUFHLENBQUMsYUFBYSxDQUFDLENBQUE7Z0JBQzNCLE9BQU07WUFDUixDQUFDO1lBRUQsTUFBTSxTQUFTLEdBQUcsR0FBRyxDQUFDLEtBQUssQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLENBQUE7WUFDeEMsSUFBSSxRQUFRLEdBQUcsR0FBRyxPQUFPLENBQUMsR0FBRyxFQUFFLFFBQVEsU0FBUyxDQUFDLFFBQVEsRUFBRSxDQUFBO1lBRTNELElBQUksUUFBUSxDQUFDLFFBQVEsQ0FBQyxHQUFHLENBQUMsRUFBRSxDQUFDO2dCQUMzQixRQUFRLElBQUksWUFBWSxDQUFBO1lBQzFCLENBQUM7WUFFRCxJQUFJLFVBQVUsQ0FBQTtZQUVkLElBQUksQ0FBQztnQkFDSCxNQUFNLEVBQUUsQ0FBQyxJQUFJLENBQUMsUUFBUSxDQUFDLENBQUE7Z0JBQ3ZCLFVBQVUsR0FBRyxJQUFJLENBQUE7WUFDbkIsQ0FBQztZQUFDLE9BQU8sTUFBTSxFQUFFLENBQUMsQ0FBQyxxQ0FBcUM7Z0JBQ3RELFVBQVUsR0FBRyxLQUFLLENBQUE7WUFDcEIsQ0FBQztZQUVELElBQUksQ0FBQyxVQUFVLEVBQUUsQ0FBQztnQkFDaEIsUUFBUSxHQUFHLEdBQUcsT0FBTyxDQUFDLEdBQUcsRUFBRSxrQkFBa0IsQ0FBQTtZQUMvQyxDQUFDO1lBRUQsTUFBTSxXQUFXLEdBQUcsTUFBTSxFQUFFLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxDQUFBO1lBQy9DLE1BQU0sUUFBUSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsUUFBUSxDQUFDLENBQUE7WUFFdkMsUUFBUSxDQUFDLFVBQVUsR0FBRyxHQUFHLENBQUE7WUFFekIsSUFBSSxRQUFRLEVBQUUsQ0FBQztnQkFDYixRQUFRLENBQUMsU0FBUyxDQUFDLGNBQWMsRUFBRSxRQUFRLENBQUMsQ0FBQTtZQUM5QyxDQUFDO1lBRUQsUUFBUSxDQUFDLEdBQUcsQ0FBQyxXQUFXLENBQUMsQ0FBQTtRQUMzQixDQUFDLENBQUE7SUFpQkgsQ0FBQztJQXBFQyxzQkFBc0I7SUFDdEIsS0FBSztRQUNILElBQUksQ0FBQyxJQUFJLENBQUMsVUFBVSxFQUFFLENBQUM7WUFDckIsTUFBTSxJQUFJLEtBQUssQ0FBQyxnQ0FBZ0MsQ0FBQyxDQUFBO1FBQ25ELENBQUM7UUFFRCxJQUFJLENBQUMsVUFBVSxDQUFDLEtBQUssRUFBRSxDQUFBO0lBQ3pCLENBQUM7SUE4Q0QsK0JBQStCO0lBQy9CLEtBQUssQ0FBQyxLQUFLO1FBQ1QsSUFBSSxDQUFDLFFBQVEsR0FBRyxNQUFNLEVBQUUsQ0FBQyxRQUFRLENBQUMsR0FBRyxTQUFTLFFBQVEsQ0FBQyxDQUFBO1FBQ3ZELE1BQU0sSUFBSSxDQUFDLGVBQWUsRUFBRSxDQUFBO0lBQzlCLENBQUM7SUFFRCwrQkFBK0I7SUFDL0IsZUFBZTtRQUNiLE9BQU8sSUFBSSxPQUFPLENBQUMsQ0FBQyxPQUFPLEVBQUUsRUFBRTtZQUM3QixJQUFJLENBQUMsVUFBVSxHQUFHLElBQUksQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLG1CQUFtQixDQUFDLENBQUE7WUFDN0QsSUFBSSxDQUFDLFVBQVUsQ0FBQyxNQUFNLENBQUMsSUFBSSxFQUFFLFdBQVcsRUFBRSxHQUFHLEVBQUU7Z0JBQzdDLE9BQU8sRUFBRSxDQUFBO1lBQ1gsQ0FBQyxDQUFDLENBQUE7UUFDSixDQUFDLENBQUMsQ0FBQTtJQUNKLENBQUM7Q0FDRiIsInNvdXJjZXNDb250ZW50IjpbIi8vIEB0cy1jaGVja1xuXG5pbXBvcnQgZnMgZnJvbSBcIm5vZGU6ZnMvcHJvbWlzZXNcIlxuaW1wb3J0IGh0dHAgZnJvbSBcIm5vZGU6aHR0cFwiXG5pbXBvcnQgbWltZSBmcm9tIFwibWltZVwiXG5pbXBvcnQgdXJsIGZyb20gXCJ1cmxcIlxuXG5leHBvcnQgZGVmYXVsdCBjbGFzcyBTeXN0ZW1UZXN0SHR0cFNlcnZlciB7XG4gIC8qKiBAcmV0dXJucyB7dm9pZH0gKi9cbiAgY2xvc2UoKSB7XG4gICAgaWYgKCF0aGlzLmh0dHBTZXJ2ZXIpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihcIkhUVFAgc2VydmVyIGlzIG5vdCBpbml0aWFsaXplZFwiKVxuICAgIH1cblxuICAgIHRoaXMuaHR0cFNlcnZlci5jbG9zZSgpXG4gIH1cblxuICAvKipcbiAgICogQHBhcmFtIHtodHRwLkluY29taW5nTWVzc2FnZX0gcmVxdWVzdFxuICAgKiBAcGFyYW0ge2h0dHAuU2VydmVyUmVzcG9uc2V9IHJlc3BvbnNlXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fVxuICAgKi9cbiAgb25IdHRwU2VydmVyUmVxdWVzdCA9IGFzeW5jIChyZXF1ZXN0LCByZXNwb25zZSkgPT4ge1xuICAgIGlmICghcmVxdWVzdC51cmwpIHtcbiAgICAgIHJlc3BvbnNlLnN0YXR1c0NvZGUgPSA0MDBcbiAgICAgIHJlc3BvbnNlLmVuZChcIkJhZCBSZXF1ZXN0XCIpXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBjb25zdCBwYXJzZWRVcmwgPSB1cmwucGFyc2UocmVxdWVzdC51cmwpXG4gICAgbGV0IGZpbGVQYXRoID0gYCR7cHJvY2Vzcy5jd2QoKX0vZGlzdCR7cGFyc2VkVXJsLnBhdGhuYW1lfWBcblxuICAgIGlmIChmaWxlUGF0aC5lbmRzV2l0aChcIi9cIikpIHtcbiAgICAgIGZpbGVQYXRoICs9IFwiaW5kZXguaHRtbFwiXG4gICAgfVxuXG4gICAgbGV0IGZpbGVFeGlzdHNcblxuICAgIHRyeSB7XG4gICAgICBhd2FpdCBmcy5zdGF0KGZpbGVQYXRoKVxuICAgICAgZmlsZUV4aXN0cyA9IHRydWVcbiAgICB9IGNhdGNoIChfZXJyb3IpIHsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby11bnVzZWQtdmFyc1xuICAgICAgZmlsZUV4aXN0cyA9IGZhbHNlXG4gICAgfVxuXG4gICAgaWYgKCFmaWxlRXhpc3RzKSB7XG4gICAgICBmaWxlUGF0aCA9IGAke3Byb2Nlc3MuY3dkKCl9L2Rpc3QvaW5kZXguaHRtbGBcbiAgICB9XG5cbiAgICBjb25zdCBmaWxlQ29udGVudCA9IGF3YWl0IGZzLnJlYWRGaWxlKGZpbGVQYXRoKVxuICAgIGNvbnN0IG1pbWVUeXBlID0gbWltZS5nZXRUeXBlKGZpbGVQYXRoKVxuXG4gICAgcmVzcG9uc2Uuc3RhdHVzQ29kZSA9IDIwMFxuXG4gICAgaWYgKG1pbWVUeXBlKSB7XG4gICAgICByZXNwb25zZS5zZXRIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgbWltZVR5cGUpXG4gICAgfVxuXG4gICAgcmVzcG9uc2UuZW5kKGZpbGVDb250ZW50KVxuICB9XG5cbiAgLyoqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAqL1xuICBhc3luYyBzdGFydCgpIHtcbiAgICB0aGlzLmJhc2VQYXRoID0gYXdhaXQgZnMucmVhbHBhdGgoYCR7X19kaXJuYW1lfS8uLi8uLmApXG4gICAgYXdhaXQgdGhpcy5zdGFydEh0dHBTZXJ2ZXIoKVxuICB9XG5cbiAgLyoqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAqL1xuICBzdGFydEh0dHBTZXJ2ZXIoKSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XG4gICAgICB0aGlzLmh0dHBTZXJ2ZXIgPSBodHRwLmNyZWF0ZVNlcnZlcih0aGlzLm9uSHR0cFNlcnZlclJlcXVlc3QpXG4gICAgICB0aGlzLmh0dHBTZXJ2ZXIubGlzdGVuKDE5ODQsIFwibG9jYWxob3N0XCIsICgpID0+IHtcbiAgICAgICAgcmVzb2x2ZSgpXG4gICAgICB9KVxuICAgIH0pXG4gIH1cbn1cbiJdfQ==
@@ -34,6 +34,7 @@ export default class SystemTest {
34
34
  _started: boolean;
35
35
  _driverTimeouts: number;
36
36
  _timeouts: number;
37
+ /** @returns {SystemTestCommunicator} */
37
38
  getCommunicator(): SystemTestCommunicator;
38
39
  _host: string;
39
40
  _port: number;
@@ -45,9 +46,7 @@ export default class SystemTest {
45
46
  * @returns {string | undefined}
46
47
  */
47
48
  getBaseSelector(): string | undefined;
48
- /**
49
- * @returns {import("selenium-webdriver").WebDriver}
50
- */
49
+ /** @returns {import("selenium-webdriver").WebDriver} */
51
50
  getDriver(): import("selenium-webdriver").WebDriver;
52
51
  /**
53
52
  * Sets the base selector for scoping element searches
@@ -76,7 +75,7 @@ export default class SystemTest {
76
75
  /**
77
76
  * Finds all elements by CSS selector
78
77
  * @param {string} selector
79
- * @param {object} args
78
+ * @param {object} [args]
80
79
  * @param {number} [args.timeout]
81
80
  * @param {boolean} [args.visible]
82
81
  * @param {boolean} [args.useBaseSelector]
@@ -96,17 +95,17 @@ export default class SystemTest {
96
95
  /**
97
96
  * Finds a single element by CSS selector
98
97
  * @param {string} selector
99
- * @param {object} args
98
+ * @param {object} [args]
100
99
  * @returns {Promise<import("selenium-webdriver").WebElement>}
101
100
  */
102
101
  find(selector: string, args?: object): Promise<import("selenium-webdriver").WebElement>;
103
102
  /**
104
103
  * Finds a single element by test ID
105
104
  * @param {string} testID
106
- * @param {object} args
105
+ * @param {object} [args]
107
106
  * @returns {Promise<import("selenium-webdriver").WebElement>}
108
107
  */
109
- findByTestID(testID: string, args: object): Promise<import("selenium-webdriver").WebElement>;
108
+ findByTestID(testID: string, args?: object): Promise<import("selenium-webdriver").WebElement>;
110
109
  /**
111
110
  * @param {string|import("selenium-webdriver").WebElement} elementOrIdentifier
112
111
  * @returns {Promise<import("selenium-webdriver").WebElement>}
@@ -124,20 +123,16 @@ export default class SystemTest {
124
123
  * @returns {Promise<string[]>}
125
124
  */
126
125
  getBrowserLogs(): Promise<string[]>;
127
- /**
128
- * @returns {Promise<string>}
129
- */
126
+ /** @returns {Promise<string>} */
130
127
  getCurrentUrl(): Promise<string>;
131
- /**
132
- * @returns {number}
133
- */
128
+ /** @returns {number} */
134
129
  getTimeouts(): number;
135
130
  /**
136
131
  * Interacts with an element by calling a method on it with the given arguments.
137
132
  * Retrying on ElementNotInteractableError.
138
- * @param {import("selenium-webdriver").WebElement|string} elementOrIdentifier - The element or a CSS selector to find the element.
139
- * @param {string} methodName - The method name to call on the element.
140
- * @param {...any} args - Arguments to pass to the method.
133
+ * @param {import("selenium-webdriver").WebElement|string} elementOrIdentifier The element or a CSS selector to find the element.
134
+ * @param {string} methodName The method name to call on the element.
135
+ * @param {...any} args Arguments to pass to the method.
141
136
  * @returns {Promise<any>}
142
137
  */
143
138
  interact(elementOrIdentifier: import("selenium-webdriver").WebElement | string, methodName: string, ...args: any[]): Promise<any>;
@@ -149,11 +144,11 @@ export default class SystemTest {
149
144
  expectNoElement(selector: string): Promise<void>;
150
145
  /**
151
146
  * @param {string} selector
152
- * @param {object} args
147
+ * @param {object} [args]
153
148
  * @param {boolean} [args.useBaseSelector]
154
149
  * @returns {Promise<void>}
155
150
  */
156
- waitForNoSelector(selector: string, args: {
151
+ waitForNoSelector(selector: string, args?: {
157
152
  useBaseSelector?: boolean;
158
153
  }): Promise<void>;
159
154
  /**
@@ -167,9 +162,7 @@ export default class SystemTest {
167
162
  * @returns {Promise<void>}
168
163
  */
169
164
  expectNotificationMessage(expectedNotificationMessage: string): Promise<void>;
170
- /**
171
- * @returns {Promise<void>}
172
- */
165
+ /** @returns {Promise<void>} */
173
166
  dismissNotificationMessages(): Promise<void>;
174
167
  /**
175
168
  * Indicates whether the system test has been started
@@ -249,9 +242,7 @@ export default class SystemTest {
249
242
  */
250
243
  onWebSocketConnection: (ws: WebSocket) => Promise<void>;
251
244
  ws: WebSocket;
252
- /**
253
- * @returns {void}
254
- */
245
+ /** @returns {void} */
255
246
  onWebSocketClose: () => void;
256
247
  /**
257
248
  * Handles an error reported from the browser