system-testing 1.0.20 → 1.0.22
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "system-testing",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.22",
|
|
4
4
|
"description": "System testing with Selenium and browsers.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"system",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"htmlfy": "^1.0.0",
|
|
30
30
|
"mime": "^4.0.7",
|
|
31
31
|
"moment": "^2.30.1",
|
|
32
|
+
"scoundrel-remote-eval": "^1.0.6",
|
|
32
33
|
"ws": "^8.18.3"
|
|
33
34
|
},
|
|
34
35
|
"peerDependencies": {
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import Client from "scoundrel-remote-eval/src/client/index.js"
|
|
2
|
+
import ClientWebSocket from "scoundrel-remote-eval/src/client/connections/web-socket/index.js"
|
|
1
3
|
import {digg} from "diggerize"
|
|
2
4
|
import EventEmitter from "events"
|
|
3
5
|
|
|
@@ -20,6 +22,38 @@ export default class SystemTestBrowserHelper {
|
|
|
20
22
|
this.events = new EventEmitter()
|
|
21
23
|
|
|
22
24
|
shared.systemTestBrowserHelper = this
|
|
25
|
+
|
|
26
|
+
this.startScoundrel()
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async startScoundrel() {
|
|
30
|
+
this.scoundrelWs = new WebSocket("http://localhost:8090")
|
|
31
|
+
this.scoundrelClientWebSocket = new ClientWebSocket(this.scoundrelWs)
|
|
32
|
+
|
|
33
|
+
await this.scoundrelClientWebSocket.waitForOpened()
|
|
34
|
+
|
|
35
|
+
this.scoundrelClient = new Client(this.scoundrelClientWebSocket)
|
|
36
|
+
this.events.emit("scoundrelStarted")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
waitForScoundrelStarted() {
|
|
40
|
+
return new Promise((resolve) => {
|
|
41
|
+
if (this.scoundrelClient) {
|
|
42
|
+
resolve()
|
|
43
|
+
} else {
|
|
44
|
+
this.events.once("scoundrelStarted", () => {
|
|
45
|
+
resolve()
|
|
46
|
+
})
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
getScoundrel() {
|
|
52
|
+
if (!this.scoundrelClient) {
|
|
53
|
+
throw new Error("Scoundrel client is not started yet")
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return this.scoundrelClient
|
|
23
57
|
}
|
|
24
58
|
|
|
25
59
|
connectOnError() {
|
|
@@ -83,8 +117,8 @@ export default class SystemTestBrowserHelper {
|
|
|
83
117
|
this.overrideConsoleLog()
|
|
84
118
|
}
|
|
85
119
|
|
|
86
|
-
getEnabled
|
|
87
|
-
getEvents
|
|
120
|
+
getEnabled() { return this._enabled }
|
|
121
|
+
getEvents() { return this.events }
|
|
88
122
|
|
|
89
123
|
fakeConsoleError = (...args) => {
|
|
90
124
|
this.communicator.sendCommand({type: "console.error", value: this.consoleLogMessage(args)})
|
|
@@ -63,6 +63,12 @@ export default class SystemTestCommunicator {
|
|
|
63
63
|
}
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Sends a command and returns a promise that resolves with the response.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} data - The command data to send.
|
|
70
|
+
* @returns {Promise} A promise that resolves with the response data.
|
|
71
|
+
*/
|
|
66
72
|
sendCommand(data) {
|
|
67
73
|
return new Promise((resolve, error) => {
|
|
68
74
|
const id = this._sendQueueCount
|
package/src/system-test.js
CHANGED
|
@@ -5,6 +5,8 @@ import fs from "node:fs/promises"
|
|
|
5
5
|
import logging from "selenium-webdriver/lib/logging.js"
|
|
6
6
|
import moment from "moment"
|
|
7
7
|
import {prettify} from "htmlfy"
|
|
8
|
+
import Server from "scoundrel-remote-eval/src/server/index.js"
|
|
9
|
+
import ServerWebSocket from "scoundrel-remote-eval/src/server/connections/web-socket/index.js"
|
|
8
10
|
import SystemTestCommunicator from "./system-test-communicator.js"
|
|
9
11
|
import SystemTestHttpServer from "./system-test-http-server.js"
|
|
10
12
|
import {wait, waitFor} from "awaitery"
|
|
@@ -39,7 +41,7 @@ export default class SystemTest {
|
|
|
39
41
|
await systemTest.visit("/blank")
|
|
40
42
|
|
|
41
43
|
try {
|
|
42
|
-
await systemTest.findByTestID("blankText")
|
|
44
|
+
await systemTest.findByTestID("blankText", {useBaseSelector: false})
|
|
43
45
|
await callback(systemTest)
|
|
44
46
|
} catch (error) {
|
|
45
47
|
await systemTest.takeScreenshot()
|
|
@@ -62,11 +64,48 @@ export default class SystemTest {
|
|
|
62
64
|
throw new Error(`Unknown arguments: ${restArgsKeys.join(", ")}`)
|
|
63
65
|
}
|
|
64
66
|
|
|
65
|
-
this.communicator = new SystemTestCommunicator({onCommand: this.onCommandReceived})
|
|
66
67
|
this._host = host
|
|
67
68
|
this._port = port
|
|
68
69
|
this._responses = {}
|
|
69
70
|
this._sendCount = 0
|
|
71
|
+
this.startScoundrel()
|
|
72
|
+
this.communicator = new SystemTestCommunicator({onCommand: this.onCommandReceived})
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Gets the base selector for scoping element searches
|
|
77
|
+
*
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
getBaseSelector() { return this._baseSelector }
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Sets the base selector for scoping element searches
|
|
84
|
+
*
|
|
85
|
+
* @param {string} baseSelector
|
|
86
|
+
*/
|
|
87
|
+
setBaseSelector(baseSelector) { this._baseSelector = baseSelector }
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Gets a selector scoped to the base selector
|
|
91
|
+
*
|
|
92
|
+
* @param {string} selector
|
|
93
|
+
* @returns {string}
|
|
94
|
+
*/
|
|
95
|
+
getSelector(selector) {
|
|
96
|
+
return this.getBaseSelector() ? `${this.getBaseSelector()} ${selector}` : selector
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Starts Scoundrel server which the browser connects to for remote evaluation in the browser */
|
|
100
|
+
startScoundrel() {
|
|
101
|
+
this.wss = new WebSocketServer({port: 8090})
|
|
102
|
+
this.serverWebSocket = new ServerWebSocket(this.wss)
|
|
103
|
+
this.server = new Server(this.serverWebSocket)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
stopScoundrel() {
|
|
107
|
+
this.server?.close()
|
|
108
|
+
this.wss?.close()
|
|
70
109
|
}
|
|
71
110
|
|
|
72
111
|
/**
|
|
@@ -78,8 +117,15 @@ export default class SystemTest {
|
|
|
78
117
|
* @returns {import("selenium-webdriver").WebElement[]}
|
|
79
118
|
*/
|
|
80
119
|
async all(selector, args = {}) {
|
|
81
|
-
const {visible = true} = args
|
|
82
|
-
const
|
|
120
|
+
const {visible = true, useBaseSelector = true, ...restArgs} = args
|
|
121
|
+
const restArgsKeys = Object.keys(restArgs)
|
|
122
|
+
|
|
123
|
+
if (restArgsKeys.length > 0) {
|
|
124
|
+
throw new Error(`Unknown arguments: ${restArgsKeys.join(", ")}`)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const actualSelector = useBaseSelector ? this.getSelector(selector) : selector
|
|
128
|
+
const elements = await this.driver.findElements(By.css(actualSelector))
|
|
83
129
|
const activeElements = []
|
|
84
130
|
|
|
85
131
|
for (const element of elements) {
|
|
@@ -127,15 +173,15 @@ export default class SystemTest {
|
|
|
127
173
|
elements = await this.all(selector, args)
|
|
128
174
|
} catch (error) {
|
|
129
175
|
// Re-throw to recover stack trace
|
|
130
|
-
throw new Error(`${error.message} (selector: ${selector})`)
|
|
176
|
+
throw new Error(`${error.message} (selector: ${this.getSelector(selector)})`)
|
|
131
177
|
}
|
|
132
178
|
|
|
133
179
|
if (elements.length > 1) {
|
|
134
|
-
throw new Error(`More than 1 elements (${elements.length}) was found by CSS: ${selector}`)
|
|
180
|
+
throw new Error(`More than 1 elements (${elements.length}) was found by CSS: ${this.getSelector(selector)}`)
|
|
135
181
|
}
|
|
136
182
|
|
|
137
183
|
if (!elements[0]) {
|
|
138
|
-
throw new ElementNotFoundError(`Element couldn't be found by CSS: ${selector}`)
|
|
184
|
+
throw new ElementNotFoundError(`Element couldn't be found after ${(this.getTimeouts() / 1000).toFixed(2)}s by CSS: ${this.getSelector(selector)}`)
|
|
139
185
|
}
|
|
140
186
|
|
|
141
187
|
return elements[0]
|
|
@@ -156,11 +202,11 @@ export default class SystemTest {
|
|
|
156
202
|
* @param {string} selector
|
|
157
203
|
* @returns {import("selenium-webdriver").WebElement}
|
|
158
204
|
*/
|
|
159
|
-
async findNoWait(selector) {
|
|
205
|
+
async findNoWait(selector, args) {
|
|
160
206
|
await this.driverSetTimeouts(0)
|
|
161
207
|
|
|
162
208
|
try {
|
|
163
|
-
return await this.find(selector)
|
|
209
|
+
return await this.find(selector, args)
|
|
164
210
|
} finally {
|
|
165
211
|
await this.restoreTimeouts()
|
|
166
212
|
}
|
|
@@ -195,6 +241,8 @@ export default class SystemTest {
|
|
|
195
241
|
return await this.driver.getCurrentUrl()
|
|
196
242
|
}
|
|
197
243
|
|
|
244
|
+
getTimeouts() { return this._timeouts }
|
|
245
|
+
|
|
198
246
|
/**
|
|
199
247
|
* Interacts with an element by calling a method on it with the given arguments.
|
|
200
248
|
* Retrying on ElementNotInteractableError.
|
|
@@ -268,7 +316,7 @@ export default class SystemTest {
|
|
|
268
316
|
* @returns {Promise<string[]>}
|
|
269
317
|
*/
|
|
270
318
|
async notificationMessages() {
|
|
271
|
-
const notificationMessageElements = await this.all("[data-class='notification-message']")
|
|
319
|
+
const notificationMessageElements = await this.all("[data-class='notification-message']", {useBaseSelector: false})
|
|
272
320
|
const notificationMessageTexts = []
|
|
273
321
|
|
|
274
322
|
for (const notificationMessageElement of notificationMessageElements) {
|
|
@@ -357,8 +405,8 @@ export default class SystemTest {
|
|
|
357
405
|
await this.driverVisit("/?systemTest=true")
|
|
358
406
|
|
|
359
407
|
try {
|
|
360
|
-
await this.find("body > #root")
|
|
361
|
-
await this.find("[data-testid='systemTestingComponent']", {visible: null})
|
|
408
|
+
await this.find("body > #root", {useBaseSelector: false})
|
|
409
|
+
await this.find("[data-testid='systemTestingComponent']", {visible: null, useBaseSelector: false})
|
|
362
410
|
} catch (error) {
|
|
363
411
|
await systemTest.takeScreenshot()
|
|
364
412
|
|
|
@@ -369,17 +417,18 @@ export default class SystemTest {
|
|
|
369
417
|
await this.waitForClientWebSocket()
|
|
370
418
|
|
|
371
419
|
this._started = true
|
|
420
|
+
systemTest.setBaseSelector("[data-testid='systemTestingComponent'][data-focussed='true']")
|
|
372
421
|
}
|
|
373
422
|
|
|
374
423
|
/**
|
|
375
424
|
* Restores previously set timeouts
|
|
376
425
|
*/
|
|
377
426
|
async restoreTimeouts() {
|
|
378
|
-
if (!this.
|
|
427
|
+
if (!this.getTimeouts()) {
|
|
379
428
|
throw new Error("Timeouts haven't previously been set")
|
|
380
429
|
}
|
|
381
430
|
|
|
382
|
-
await this.driverSetTimeouts(this.
|
|
431
|
+
await this.driverSetTimeouts(this.getTimeouts())
|
|
383
432
|
}
|
|
384
433
|
|
|
385
434
|
/**
|
|
@@ -425,10 +474,19 @@ export default class SystemTest {
|
|
|
425
474
|
this.wss.on("close", this.onWebSocketClose)
|
|
426
475
|
}
|
|
427
476
|
|
|
477
|
+
/**
|
|
478
|
+
* Sets the on command callback
|
|
479
|
+
*/
|
|
428
480
|
onCommand(callback) {
|
|
429
481
|
this._onCommandCallback = callback
|
|
430
482
|
}
|
|
431
483
|
|
|
484
|
+
/**
|
|
485
|
+
* Handles a command received from the browser
|
|
486
|
+
*
|
|
487
|
+
* @param {Object} data
|
|
488
|
+
* @returns {Promise<any>}
|
|
489
|
+
*/
|
|
432
490
|
onCommandReceived = async ({data}) => {
|
|
433
491
|
const type = data.type
|
|
434
492
|
let result
|
|
@@ -457,6 +515,11 @@ export default class SystemTest {
|
|
|
457
515
|
return result
|
|
458
516
|
}
|
|
459
517
|
|
|
518
|
+
/**
|
|
519
|
+
* Handles a new web socket connection
|
|
520
|
+
*
|
|
521
|
+
* @param {WebSocket} ws
|
|
522
|
+
*/
|
|
460
523
|
onWebSocketConnection = async (ws) => {
|
|
461
524
|
this.ws = ws
|
|
462
525
|
this.communicator.ws = ws
|
|
@@ -475,6 +538,11 @@ export default class SystemTest {
|
|
|
475
538
|
this.communicator.ws = null
|
|
476
539
|
}
|
|
477
540
|
|
|
541
|
+
/**
|
|
542
|
+
* Handles an error reported from the browser
|
|
543
|
+
*
|
|
544
|
+
* @param {Object} data
|
|
545
|
+
*/
|
|
478
546
|
handleError(data) {
|
|
479
547
|
if (data.message.includes("Minified React error #419")) {
|
|
480
548
|
// Ignore this error message
|
|
@@ -494,6 +562,7 @@ export default class SystemTest {
|
|
|
494
562
|
* Stops the system test
|
|
495
563
|
*/
|
|
496
564
|
async stop() {
|
|
565
|
+
this.stopScoundrel()
|
|
497
566
|
this.systemTestHttpServer?.close()
|
|
498
567
|
this.wss?.close()
|
|
499
568
|
await this.driver.quit()
|