@testring/plugin-selenium-driver 0.5.36 → 0.6.1
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/dist/index.js +2 -3
- package/dist/plugin/index.js +136 -42
- package/index.d.ts +15 -0
- package/package.json +8 -11
- package/src/index.ts +6 -4
- package/src/plugin/index.ts +343 -110
- package/src/types.ts +2 -2
package/dist/index.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const path = require("path");
|
|
4
|
-
|
|
5
|
-
function default_1(pluginAPI, userConfig) {
|
|
4
|
+
function seleniumPlugin(pluginAPI, userConfig) {
|
|
6
5
|
const pluginPath = path.join(__dirname, './plugin');
|
|
7
6
|
const browserProxy = pluginAPI.getBrowserProxy();
|
|
8
7
|
browserProxy.proxyPlugin(pluginPath, userConfig || {});
|
|
9
8
|
}
|
|
10
|
-
exports.default =
|
|
9
|
+
exports.default = seleniumPlugin;
|
package/dist/plugin/index.js
CHANGED
|
@@ -27,6 +27,7 @@ const DEFAULT_CONFIG = {
|
|
|
27
27
|
capabilities: {
|
|
28
28
|
browserName: 'chrome',
|
|
29
29
|
'goog:chromeOptions': {
|
|
30
|
+
// for local ChromeDriver
|
|
30
31
|
args: [],
|
|
31
32
|
},
|
|
32
33
|
},
|
|
@@ -63,25 +64,63 @@ class SeleniumPlugin {
|
|
|
63
64
|
return {
|
|
64
65
|
capabilities: {
|
|
65
66
|
'goog:chromeOptions': {
|
|
66
|
-
args: [
|
|
67
|
-
`load-extension=${devtool_extension_1.absoluteExtensionPath}`,
|
|
68
|
-
],
|
|
67
|
+
args: [`load-extension=${devtool_extension_1.absoluteExtensionPath}`],
|
|
69
68
|
},
|
|
70
69
|
},
|
|
71
70
|
};
|
|
72
71
|
}
|
|
72
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
73
73
|
createConfig(config) {
|
|
74
|
-
let mergedConfig = deepmerge.all([
|
|
75
|
-
DEFAULT_CONFIG,
|
|
76
|
-
config,
|
|
77
|
-
], {
|
|
74
|
+
let mergedConfig = deepmerge.all([DEFAULT_CONFIG, config], {
|
|
78
75
|
clone: true,
|
|
79
76
|
});
|
|
80
77
|
if (mergedConfig.recorderExtension) {
|
|
81
|
-
mergedConfig = deepmerge.all([
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
mergedConfig = deepmerge.all([mergedConfig, this.getDevelopmentConfigAdditions()], {
|
|
79
|
+
customMerge: (mergeKey) => {
|
|
80
|
+
if (mergeKey === 'goog:chromeOptions') {
|
|
81
|
+
return (itemA, itemB) => {
|
|
82
|
+
if (!itemA.args || !itemB.args) {
|
|
83
|
+
return deepmerge(itemA, itemB);
|
|
84
|
+
}
|
|
85
|
+
const res = {};
|
|
86
|
+
res.args = deepmerge(itemA.args, itemB.args);
|
|
87
|
+
let ext = res.args.filter((argItem) => argItem.startsWith('load-extension'));
|
|
88
|
+
if (ext.length === 2) {
|
|
89
|
+
ext = [
|
|
90
|
+
`load-extension=${ext[0]
|
|
91
|
+
.split('=', 2)
|
|
92
|
+
.pop()},${ext[1]
|
|
93
|
+
.split('=', 2)
|
|
94
|
+
.pop()}`,
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
res.args = [
|
|
98
|
+
...ext,
|
|
99
|
+
...res.args.filter((argItem) => !argItem.startsWith('load-extension')),
|
|
100
|
+
];
|
|
101
|
+
Object.keys(itemA).forEach((key) => {
|
|
102
|
+
if (key === 'args') {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
if (itemB[key] !== undefined) {
|
|
106
|
+
res[key] = deepmerge(itemA[key], itemB[key]);
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
res[key] = itemA[key];
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
Object.keys(itemB).forEach((key) => {
|
|
113
|
+
if (key === 'args' ||
|
|
114
|
+
res[key] !== undefined) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
res[key] = itemB[key];
|
|
118
|
+
});
|
|
119
|
+
return res;
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
});
|
|
85
124
|
}
|
|
86
125
|
if (!mergedConfig.hostname && mergedConfig.host) {
|
|
87
126
|
mergedConfig.hostname = mergedConfig.host;
|
|
@@ -100,8 +139,8 @@ class SeleniumPlugin {
|
|
|
100
139
|
});
|
|
101
140
|
}
|
|
102
141
|
stopAllSessions() {
|
|
103
|
-
|
|
104
|
-
for (
|
|
142
|
+
const clientsRequests = [];
|
|
143
|
+
for (const [applicant] of this.browserClients) {
|
|
105
144
|
this.logger.debug(`Stopping sessions before process exit for applicant ${applicant}.`);
|
|
106
145
|
clientsRequests.push(this.end(applicant).catch((err) => {
|
|
107
146
|
this.logger.error(`Session stop before process exit error for applicant ${applicant}: \n`, err);
|
|
@@ -125,10 +164,12 @@ class SeleniumPlugin {
|
|
|
125
164
|
const seleniumJarPath = seleniumServer.path;
|
|
126
165
|
this.logger.debug('Init local selenium server');
|
|
127
166
|
try {
|
|
128
|
-
this.localSelenium = child_process_1.spawn('java', [
|
|
167
|
+
this.localSelenium = (0, child_process_1.spawn)('java', [
|
|
129
168
|
...this.getChromeDriverArgs(),
|
|
130
|
-
'-jar',
|
|
131
|
-
|
|
169
|
+
'-jar',
|
|
170
|
+
seleniumJarPath,
|
|
171
|
+
'-port',
|
|
172
|
+
this.config.port,
|
|
132
173
|
]);
|
|
133
174
|
this.waitForReadyState = new Promise((resolve, reject) => {
|
|
134
175
|
if (this.localSelenium.stderr) {
|
|
@@ -151,7 +192,7 @@ class SeleniumPlugin {
|
|
|
151
192
|
});
|
|
152
193
|
}
|
|
153
194
|
getApplicantSessionId(applicant) {
|
|
154
|
-
|
|
195
|
+
const item = this.browserClients.get(applicant);
|
|
155
196
|
if (item) {
|
|
156
197
|
return item.sessionId;
|
|
157
198
|
}
|
|
@@ -160,7 +201,7 @@ class SeleniumPlugin {
|
|
|
160
201
|
return this.browserClients.has(applicant);
|
|
161
202
|
}
|
|
162
203
|
getBrowserClient(applicant) {
|
|
163
|
-
|
|
204
|
+
const item = this.browserClients.get(applicant);
|
|
164
205
|
if (item) {
|
|
165
206
|
return item.client;
|
|
166
207
|
}
|
|
@@ -168,18 +209,20 @@ class SeleniumPlugin {
|
|
|
168
209
|
}
|
|
169
210
|
pingClients() {
|
|
170
211
|
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
-
for (
|
|
212
|
+
for (const [applicant] of this.browserClients) {
|
|
172
213
|
try {
|
|
173
214
|
yield this.execute(applicant, '(function () {})()', []);
|
|
174
215
|
}
|
|
175
|
-
catch (e) {
|
|
216
|
+
catch (e) {
|
|
217
|
+
/* ignore */
|
|
218
|
+
}
|
|
176
219
|
}
|
|
177
220
|
});
|
|
178
221
|
}
|
|
179
222
|
closeExpiredClients() {
|
|
180
223
|
return __awaiter(this, void 0, void 0, function* () {
|
|
181
224
|
const timeLimit = Date.now() - this.config.clientTimeout;
|
|
182
|
-
for (
|
|
225
|
+
for (const [applicant, clientData] of this.browserClients) {
|
|
183
226
|
if (clientData.initTime < timeLimit) {
|
|
184
227
|
this.logger.warn(`Session applicant ${applicant} marked as expired`);
|
|
185
228
|
try {
|
|
@@ -214,7 +257,7 @@ class SeleniumPlugin {
|
|
|
214
257
|
if (this.expiredBrowserClients.has(applicant)) {
|
|
215
258
|
throw Error(`This session expired in ${this.config.clientTimeout}ms`);
|
|
216
259
|
}
|
|
217
|
-
const client = yield webdriverio_1.remote(this.config);
|
|
260
|
+
const client = yield (0, webdriverio_1.remote)(this.config);
|
|
218
261
|
let sessionId;
|
|
219
262
|
if (client.sessionId) {
|
|
220
263
|
sessionId = client.sessionId;
|
|
@@ -235,10 +278,10 @@ class SeleniumPlugin {
|
|
|
235
278
|
// Creating our delete selenium session to be able to close
|
|
236
279
|
// session if it's id is changed while we are running test
|
|
237
280
|
client.addCommand('deleteSessionId', function (sessionId) {
|
|
238
|
-
const { w3cCaps, jsonwpCaps } = this.options.requestedCapabilities;
|
|
281
|
+
const { w3cCaps, jsonwpCaps, } = this.options.requestedCapabilities;
|
|
239
282
|
const sessionDeleteRequest = new WebDriverRequest('DELETE', '/session/:sessionId', {
|
|
240
283
|
capabilities: w3cCaps,
|
|
241
|
-
desiredCapabilities: jsonwpCaps,
|
|
284
|
+
desiredCapabilities: jsonwpCaps, // JSONWP compliant
|
|
242
285
|
});
|
|
243
286
|
return sessionDeleteRequest.makeRequest(this.options, sessionId);
|
|
244
287
|
}, false);
|
|
@@ -255,7 +298,9 @@ class SeleniumPlugin {
|
|
|
255
298
|
try {
|
|
256
299
|
yield this.alertDismiss(applicant);
|
|
257
300
|
}
|
|
258
|
-
catch (
|
|
301
|
+
catch (_a) {
|
|
302
|
+
/* ignore */
|
|
303
|
+
}
|
|
259
304
|
const startingSessionID = this.getApplicantSessionId(applicant);
|
|
260
305
|
const sessionID = client.sessionId;
|
|
261
306
|
if (startingSessionID === sessionID) {
|
|
@@ -263,8 +308,8 @@ class SeleniumPlugin {
|
|
|
263
308
|
yield client.deleteSession();
|
|
264
309
|
}
|
|
265
310
|
else {
|
|
266
|
-
yield this.logger.stepWarning(`Stopping sessions for applicant warning ${applicant}. `
|
|
267
|
-
|
|
311
|
+
yield this.logger.stepWarning(`Stopping sessions for applicant warning ${applicant}. ` +
|
|
312
|
+
`Session ids are not equal, started with - ${startingSessionID}, ended with - ${sessionID}`, () => __awaiter(this, void 0, void 0, function* () {
|
|
268
313
|
try {
|
|
269
314
|
if (startingSessionID) {
|
|
270
315
|
yield client.deleteSessionId(startingSessionID);
|
|
@@ -342,7 +387,10 @@ class SeleniumPlugin {
|
|
|
342
387
|
yield this.createClient(applicant);
|
|
343
388
|
const client = this.getBrowserClient(applicant);
|
|
344
389
|
const args = stringifyWindowFeatures(windowFeatures);
|
|
345
|
-
return client.newWindow(val,
|
|
390
|
+
return client.newWindow(val, {
|
|
391
|
+
windowName: windowName || this.generateWinId(),
|
|
392
|
+
windowFeatures: args,
|
|
393
|
+
});
|
|
346
394
|
});
|
|
347
395
|
}
|
|
348
396
|
waitForExist(applicant, xpath, timeout) {
|
|
@@ -350,7 +398,7 @@ class SeleniumPlugin {
|
|
|
350
398
|
yield this.createClient(applicant);
|
|
351
399
|
const client = this.getBrowserClient(applicant);
|
|
352
400
|
const selector = yield client.$(xpath);
|
|
353
|
-
return selector.waitForExist(timeout);
|
|
401
|
+
return selector.waitForExist({ timeout });
|
|
354
402
|
});
|
|
355
403
|
}
|
|
356
404
|
waitForVisible(applicant, xpath, timeout) {
|
|
@@ -358,7 +406,7 @@ class SeleniumPlugin {
|
|
|
358
406
|
yield this.createClient(applicant);
|
|
359
407
|
const client = this.getBrowserClient(applicant);
|
|
360
408
|
const selector = yield client.$(xpath);
|
|
361
|
-
return selector.waitForDisplayed(timeout);
|
|
409
|
+
return selector.waitForDisplayed({ timeout });
|
|
362
410
|
});
|
|
363
411
|
}
|
|
364
412
|
isVisible(applicant, xpath) {
|
|
@@ -369,12 +417,12 @@ class SeleniumPlugin {
|
|
|
369
417
|
return selector.isDisplayed();
|
|
370
418
|
});
|
|
371
419
|
}
|
|
372
|
-
moveToObject(applicant, xpath,
|
|
420
|
+
moveToObject(applicant, xpath, xOffset = 0, yOffset = 0) {
|
|
373
421
|
return __awaiter(this, void 0, void 0, function* () {
|
|
374
422
|
yield this.createClient(applicant);
|
|
375
423
|
const client = this.getBrowserClient(applicant);
|
|
376
424
|
const selector = yield client.$(xpath);
|
|
377
|
-
return selector.moveTo(
|
|
425
|
+
return selector.moveTo({ xOffset, yOffset });
|
|
378
426
|
});
|
|
379
427
|
}
|
|
380
428
|
execute(applicant, fn, args) {
|
|
@@ -424,10 +472,10 @@ class SeleniumPlugin {
|
|
|
424
472
|
return __awaiter(this, void 0, void 0, function* () {
|
|
425
473
|
yield this.createClient(applicant);
|
|
426
474
|
const client = this.getBrowserClient(applicant);
|
|
427
|
-
const elements = yield client.findElements('xpath', xpath);
|
|
475
|
+
const elements = (yield client.findElements('xpath', xpath));
|
|
428
476
|
return elements.map((o) => {
|
|
429
477
|
const keys = Object.keys(o);
|
|
430
|
-
return {
|
|
478
|
+
return { ELEMENT: o[keys[0]] };
|
|
431
479
|
});
|
|
432
480
|
});
|
|
433
481
|
}
|
|
@@ -508,13 +556,13 @@ class SeleniumPlugin {
|
|
|
508
556
|
return selector.isEnabled();
|
|
509
557
|
});
|
|
510
558
|
}
|
|
511
|
-
scroll(applicant, xpath,
|
|
559
|
+
scroll(applicant, xpath, xOffset, yOffset) {
|
|
512
560
|
return __awaiter(this, void 0, void 0, function* () {
|
|
513
561
|
yield this.createClient(applicant);
|
|
514
562
|
const client = this.getBrowserClient(applicant);
|
|
515
563
|
const element = yield client.$(xpath);
|
|
516
564
|
yield element.scrollIntoView();
|
|
517
|
-
return element.moveTo(
|
|
565
|
+
return element.moveTo({ xOffset, yOffset });
|
|
518
566
|
});
|
|
519
567
|
}
|
|
520
568
|
scrollIntoView(applicant, xpath, scrollIntoViewOptions) {
|
|
@@ -646,7 +694,7 @@ class SeleniumPlugin {
|
|
|
646
694
|
const client = this.getBrowserClient(applicant);
|
|
647
695
|
const result = yield client.switchToWindow(tabId);
|
|
648
696
|
const body = yield client.$('body');
|
|
649
|
-
yield client.waitUntil(() => __awaiter(this, void 0, void 0, function* () { return body.isExisting(); }), 10000);
|
|
697
|
+
yield client.waitUntil(() => __awaiter(this, void 0, void 0, function* () { return body.isExisting(); }), { timeout: 10000 });
|
|
650
698
|
return result;
|
|
651
699
|
});
|
|
652
700
|
}
|
|
@@ -738,7 +786,7 @@ class SeleniumPlugin {
|
|
|
738
786
|
return client.waitUntil(() => __awaiter(this, void 0, void 0, function* () {
|
|
739
787
|
const elemValue = yield (yield client.$(xpath)).getValue();
|
|
740
788
|
return reverse ? !elemValue : !!elemValue;
|
|
741
|
-
}), timeout);
|
|
789
|
+
}), { timeout });
|
|
742
790
|
});
|
|
743
791
|
}
|
|
744
792
|
waitForSelected(applicant, xpath, timeout, reverse) {
|
|
@@ -748,14 +796,14 @@ class SeleniumPlugin {
|
|
|
748
796
|
return client.waitUntil(() => __awaiter(this, void 0, void 0, function* () {
|
|
749
797
|
const isSelected = yield (yield client.$(xpath)).isSelected();
|
|
750
798
|
return reverse ? !isSelected : isSelected;
|
|
751
|
-
}), timeout);
|
|
799
|
+
}), { timeout });
|
|
752
800
|
});
|
|
753
801
|
}
|
|
754
802
|
waitUntil(applicant, condition, timeout, timeoutMsg, interval) {
|
|
755
803
|
return __awaiter(this, void 0, void 0, function* () {
|
|
756
804
|
yield this.createClient(applicant);
|
|
757
805
|
const client = this.getBrowserClient(applicant);
|
|
758
|
-
return client.waitUntil(condition, timeout, timeoutMsg, interval);
|
|
806
|
+
return client.waitUntil(condition, { timeout, timeoutMsg, interval });
|
|
759
807
|
});
|
|
760
808
|
}
|
|
761
809
|
selectByAttribute(applicant, xpath, attribute, value) {
|
|
@@ -766,6 +814,7 @@ class SeleniumPlugin {
|
|
|
766
814
|
return selector.selectByAttribute(attribute, value);
|
|
767
815
|
});
|
|
768
816
|
}
|
|
817
|
+
// @deprecated WAT-1872
|
|
769
818
|
gridProxyDetails(applicant) {
|
|
770
819
|
return __awaiter(this, void 0, void 0, function* () {
|
|
771
820
|
yield this.createClient(applicant);
|
|
@@ -778,6 +827,7 @@ class SeleniumPlugin {
|
|
|
778
827
|
return client.gridProxyDetails(client.sessionId);
|
|
779
828
|
});
|
|
780
829
|
}
|
|
830
|
+
// @deprecated WAT-1872
|
|
781
831
|
gridTestSession(applicant) {
|
|
782
832
|
return __awaiter(this, void 0, void 0, function* () {
|
|
783
833
|
yield this.createClient(applicant);
|
|
@@ -790,12 +840,13 @@ class SeleniumPlugin {
|
|
|
790
840
|
return client.gridTestSession(client.sessionId);
|
|
791
841
|
});
|
|
792
842
|
}
|
|
843
|
+
// @deprecated WAT-1872
|
|
793
844
|
getGridNodeDetails(applicant) {
|
|
794
845
|
return __awaiter(this, void 0, void 0, function* () {
|
|
795
846
|
yield this.createClient(applicant);
|
|
796
847
|
const client = this.getBrowserClient(applicant);
|
|
797
848
|
const testSession = yield this.gridTestSession(applicant);
|
|
798
|
-
if (!testSession.localSelenium) {
|
|
849
|
+
if (testSession && !testSession.localSelenium) {
|
|
799
850
|
const proxyDetails = yield client.gridProxyDetails(applicant);
|
|
800
851
|
delete testSession.msg;
|
|
801
852
|
delete testSession.success;
|
|
@@ -807,9 +858,52 @@ class SeleniumPlugin {
|
|
|
807
858
|
return testSession;
|
|
808
859
|
});
|
|
809
860
|
}
|
|
861
|
+
/**
|
|
862
|
+
* @param overwrites should NOT be an arrow function, Otherwise it would throw an error
|
|
863
|
+
*/
|
|
864
|
+
mock(applicant, url, overwrites, filterOptions, mockResponseParams) {
|
|
865
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
866
|
+
yield this.createClient(applicant);
|
|
867
|
+
const client = this.getBrowserClient(applicant);
|
|
868
|
+
const mock = yield client.mock(url, filterOptions);
|
|
869
|
+
if (typeof overwrites === 'function') {
|
|
870
|
+
mock.respond((res) => {
|
|
871
|
+
overwrites(res);
|
|
872
|
+
this.setMockData(applicant, url, res);
|
|
873
|
+
return res.body;
|
|
874
|
+
}, mockResponseParams);
|
|
875
|
+
}
|
|
876
|
+
else {
|
|
877
|
+
mock.respond(overwrites);
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
setMockData(applicant, url, data) {
|
|
882
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
883
|
+
yield this.createClient(applicant);
|
|
884
|
+
const client = this.getBrowserClient(applicant);
|
|
885
|
+
client.mockData = client.mockData || {};
|
|
886
|
+
if (client.mockData[url]) {
|
|
887
|
+
client.mockData[url].push(data);
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
client.mockData[url] = [data];
|
|
891
|
+
}
|
|
892
|
+
return true;
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
getMockData(applicant, url) {
|
|
896
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
897
|
+
yield this.createClient(applicant);
|
|
898
|
+
const client = this.getBrowserClient(applicant);
|
|
899
|
+
if (url) {
|
|
900
|
+
return client.mockData[url];
|
|
901
|
+
}
|
|
902
|
+
return client.mockData;
|
|
903
|
+
});
|
|
904
|
+
}
|
|
810
905
|
}
|
|
811
906
|
exports.SeleniumPlugin = SeleniumPlugin;
|
|
812
|
-
// eslint-disable-next-line import/no-default-export
|
|
813
907
|
function seleniumProxy(config) {
|
|
814
908
|
return new SeleniumPlugin(config);
|
|
815
909
|
}
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
interface FormDataEvent extends Event {
|
|
2
|
+
readonly formData: FormData;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
interface SubmitEvent extends Event {
|
|
6
|
+
readonly submitter: HTMLElement | null;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface ElementInternals {
|
|
10
|
+
readonly [key: string]: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface GetAnimationsOptions {
|
|
14
|
+
readonly [key: string]: any;
|
|
15
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@testring/plugin-selenium-driver",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"typings": "./src/index.ts",
|
|
6
6
|
"repository": {
|
|
@@ -10,19 +10,16 @@
|
|
|
10
10
|
"author": "RingCentral",
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"dependencies": {
|
|
13
|
-
"@testring/child-process": "0.
|
|
14
|
-
"@testring/devtool-extension": "0.
|
|
15
|
-
"@testring/logger": "0.
|
|
16
|
-
"@testring/plugin-api": "0.
|
|
17
|
-
"@testring/types": "0.
|
|
13
|
+
"@testring/child-process": "0.6.1",
|
|
14
|
+
"@testring/devtool-extension": "0.6.1",
|
|
15
|
+
"@testring/logger": "0.6.1",
|
|
16
|
+
"@testring/plugin-api": "0.6.1",
|
|
17
|
+
"@testring/types": "0.6.1",
|
|
18
18
|
"@types/deepmerge": "2.2.0",
|
|
19
|
-
"@types/node": "
|
|
20
|
-
"@types/puppeteer-core": "2.0.0",
|
|
19
|
+
"@types/node": "18.11.18",
|
|
21
20
|
"chromedriver": "*",
|
|
22
21
|
"deepmerge": "4.2.2",
|
|
23
|
-
"puppeteer-core": "5.2.1",
|
|
24
22
|
"selenium-server": "3.141.59",
|
|
25
|
-
"
|
|
26
|
-
"webdriverio": "5.22.3"
|
|
23
|
+
"webdriverio": "7.20.7"
|
|
27
24
|
}
|
|
28
25
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import * as path from 'path';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import {SeleniumPluginConfig} from './types';
|
|
3
|
+
import {PluginAPI} from '@testring/plugin-api';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
export default function seleniumPlugin(
|
|
6
|
+
pluginAPI: PluginAPI,
|
|
7
|
+
userConfig: SeleniumPluginConfig,
|
|
8
|
+
): void {
|
|
7
9
|
const pluginPath = path.join(__dirname, './plugin');
|
|
8
10
|
const browserProxy = pluginAPI.getBrowserProxy();
|
|
9
11
|
|
package/src/plugin/index.ts
CHANGED
|
@@ -1,25 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import {SeleniumPluginConfig} from '../types';
|
|
2
|
+
import {IBrowserProxyPlugin, WindowFeaturesConfig} from '@testring/types';
|
|
3
3
|
|
|
4
|
-
import {
|
|
4
|
+
import {ChildProcess} from 'child_process';
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {remote} from 'webdriverio';
|
|
7
7
|
import * as deepmerge from 'deepmerge';
|
|
8
8
|
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import {spawn} from '@testring/child-process';
|
|
10
|
+
import {loggerClient} from '@testring/logger';
|
|
11
|
+
import {absoluteExtensionPath} from '@testring/devtool-extension';
|
|
12
12
|
|
|
13
|
-
import Cookie
|
|
14
|
-
import
|
|
13
|
+
import type {Cookie} from '@wdio/protocols';
|
|
14
|
+
import type {
|
|
15
|
+
ClickOptions,
|
|
16
|
+
MockFilterOptions,
|
|
17
|
+
MockResponseParams,
|
|
18
|
+
Matches,
|
|
19
|
+
} from 'webdriverio';
|
|
15
20
|
|
|
16
21
|
// Stupidly needed thing for making our own requests
|
|
17
22
|
const _webdriverReq = require('webdriver/build/request');
|
|
18
23
|
const WebDriverRequest = _webdriverReq.default;
|
|
19
24
|
|
|
20
|
-
type BrowserObjectCustom =
|
|
25
|
+
type BrowserObjectCustom = WebdriverIO.Browser & {
|
|
26
|
+
sessionId: string;
|
|
21
27
|
deleteSessionId: (sessionId: string) => Promise<void>;
|
|
22
|
-
|
|
28
|
+
mockData: Record<string, Matches[]>;
|
|
29
|
+
};
|
|
23
30
|
|
|
24
31
|
type browserClientItem = {
|
|
25
32
|
client: BrowserObjectCustom;
|
|
@@ -35,7 +42,8 @@ const DEFAULT_CONFIG: SeleniumPluginConfig = {
|
|
|
35
42
|
logLevel: 'warn',
|
|
36
43
|
capabilities: {
|
|
37
44
|
browserName: 'chrome',
|
|
38
|
-
'goog:chromeOptions': {
|
|
45
|
+
'goog:chromeOptions': {
|
|
46
|
+
// for local ChromeDriver
|
|
39
47
|
args: [] as string[],
|
|
40
48
|
},
|
|
41
49
|
},
|
|
@@ -57,7 +65,6 @@ function stringifyWindowFeatures(windowFeatures: WindowFeaturesConfig) {
|
|
|
57
65
|
return result;
|
|
58
66
|
}
|
|
59
67
|
|
|
60
|
-
|
|
61
68
|
export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
62
69
|
private logger = loggerClient.withPrefix('[selenium-browser-process]');
|
|
63
70
|
|
|
@@ -73,7 +80,7 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
73
80
|
|
|
74
81
|
private config: SeleniumPluginConfig;
|
|
75
82
|
|
|
76
|
-
private incrementWinId
|
|
83
|
+
private incrementWinId = 0;
|
|
77
84
|
|
|
78
85
|
constructor(config: Partial<SeleniumPluginConfig> = {}) {
|
|
79
86
|
this.config = this.createConfig(config);
|
|
@@ -89,27 +96,86 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
89
96
|
return {
|
|
90
97
|
capabilities: {
|
|
91
98
|
'goog:chromeOptions': {
|
|
92
|
-
args: [
|
|
93
|
-
`load-extension=${absoluteExtensionPath}`,
|
|
94
|
-
],
|
|
99
|
+
args: [`load-extension=${absoluteExtensionPath}`],
|
|
95
100
|
},
|
|
96
101
|
},
|
|
97
102
|
} as any;
|
|
98
103
|
}
|
|
99
104
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
105
|
+
// eslint-disable-next-line sonarjs/cognitive-complexity
|
|
106
|
+
private createConfig(
|
|
107
|
+
config: Partial<SeleniumPluginConfig>,
|
|
108
|
+
): SeleniumPluginConfig {
|
|
109
|
+
let mergedConfig = deepmerge.all<SeleniumPluginConfig>(
|
|
110
|
+
[DEFAULT_CONFIG, config],
|
|
111
|
+
{
|
|
112
|
+
clone: true,
|
|
113
|
+
},
|
|
114
|
+
);
|
|
107
115
|
|
|
108
116
|
if (mergedConfig.recorderExtension) {
|
|
109
|
-
mergedConfig = deepmerge.all<SeleniumPluginConfig>(
|
|
110
|
-
mergedConfig,
|
|
111
|
-
|
|
112
|
-
|
|
117
|
+
mergedConfig = deepmerge.all<SeleniumPluginConfig>(
|
|
118
|
+
[mergedConfig, this.getDevelopmentConfigAdditions()],
|
|
119
|
+
{
|
|
120
|
+
customMerge: (mergeKey) => {
|
|
121
|
+
if (mergeKey === 'goog:chromeOptions') {
|
|
122
|
+
return (itemA, itemB) => {
|
|
123
|
+
if (!itemA.args || !itemB.args) {
|
|
124
|
+
return deepmerge(itemA, itemB);
|
|
125
|
+
}
|
|
126
|
+
const res: Record<string, any> = {};
|
|
127
|
+
res.args = deepmerge(itemA.args, itemB.args);
|
|
128
|
+
let ext: string[] = res.args.filter(
|
|
129
|
+
(argItem: string) =>
|
|
130
|
+
argItem.startsWith('load-extension'),
|
|
131
|
+
);
|
|
132
|
+
if (ext.length === 2) {
|
|
133
|
+
ext = [
|
|
134
|
+
`load-extension=${ext[0]
|
|
135
|
+
.split('=', 2)
|
|
136
|
+
.pop()},${ext[1]
|
|
137
|
+
.split('=', 2)
|
|
138
|
+
.pop()}`,
|
|
139
|
+
];
|
|
140
|
+
}
|
|
141
|
+
res.args = [
|
|
142
|
+
...ext,
|
|
143
|
+
...res.args.filter(
|
|
144
|
+
(argItem: string) =>
|
|
145
|
+
!argItem.startsWith(
|
|
146
|
+
'load-extension',
|
|
147
|
+
),
|
|
148
|
+
),
|
|
149
|
+
];
|
|
150
|
+
Object.keys(itemA).forEach((key) => {
|
|
151
|
+
if (key === 'args') {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
if (itemB[key] !== undefined) {
|
|
155
|
+
res[key] = deepmerge(
|
|
156
|
+
itemA[key],
|
|
157
|
+
itemB[key],
|
|
158
|
+
);
|
|
159
|
+
} else {
|
|
160
|
+
res[key] = itemA[key];
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
Object.keys(itemB).forEach((key) => {
|
|
164
|
+
if (
|
|
165
|
+
key === 'args' ||
|
|
166
|
+
res[key] !== undefined
|
|
167
|
+
) {
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
res[key] = itemB[key];
|
|
172
|
+
});
|
|
173
|
+
return res;
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
},
|
|
177
|
+
},
|
|
178
|
+
);
|
|
113
179
|
}
|
|
114
180
|
|
|
115
181
|
if (!mergedConfig.hostname && mergedConfig.host) {
|
|
@@ -136,13 +202,20 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
136
202
|
}
|
|
137
203
|
|
|
138
204
|
private stopAllSessions() {
|
|
139
|
-
|
|
205
|
+
const clientsRequests: Promise<any>[] = [];
|
|
140
206
|
|
|
141
|
-
for (
|
|
142
|
-
this.logger.debug(
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
207
|
+
for (const [applicant] of this.browserClients) {
|
|
208
|
+
this.logger.debug(
|
|
209
|
+
`Stopping sessions before process exit for applicant ${applicant}.`,
|
|
210
|
+
);
|
|
211
|
+
clientsRequests.push(
|
|
212
|
+
this.end(applicant).catch((err) => {
|
|
213
|
+
this.logger.error(
|
|
214
|
+
`Session stop before process exit error for applicant ${applicant}: \n`,
|
|
215
|
+
err,
|
|
216
|
+
);
|
|
217
|
+
}),
|
|
218
|
+
);
|
|
146
219
|
}
|
|
147
220
|
|
|
148
221
|
return Promise.all(clientsRequests);
|
|
@@ -168,8 +241,10 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
168
241
|
try {
|
|
169
242
|
this.localSelenium = spawn('java', [
|
|
170
243
|
...this.getChromeDriverArgs(),
|
|
171
|
-
'-jar',
|
|
172
|
-
|
|
244
|
+
'-jar',
|
|
245
|
+
seleniumJarPath,
|
|
246
|
+
'-port',
|
|
247
|
+
this.config.port,
|
|
173
248
|
]);
|
|
174
249
|
|
|
175
250
|
this.waitForReadyState = new Promise((resolve, reject) => {
|
|
@@ -193,7 +268,7 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
193
268
|
}
|
|
194
269
|
|
|
195
270
|
private getApplicantSessionId(applicant): string | undefined {
|
|
196
|
-
|
|
271
|
+
const item = this.browserClients.get(applicant);
|
|
197
272
|
|
|
198
273
|
if (item) {
|
|
199
274
|
return item.sessionId;
|
|
@@ -205,7 +280,7 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
205
280
|
}
|
|
206
281
|
|
|
207
282
|
private getBrowserClient(applicant): BrowserObjectCustom {
|
|
208
|
-
|
|
283
|
+
const item = this.browserClients.get(applicant);
|
|
209
284
|
|
|
210
285
|
if (item) {
|
|
211
286
|
return item.client;
|
|
@@ -215,23 +290,30 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
215
290
|
}
|
|
216
291
|
|
|
217
292
|
private async pingClients() {
|
|
218
|
-
for (
|
|
293
|
+
for (const [applicant] of this.browserClients) {
|
|
219
294
|
try {
|
|
220
295
|
await this.execute(applicant, '(function () {})()', []);
|
|
221
|
-
} catch (e) {
|
|
296
|
+
} catch (e) {
|
|
297
|
+
/* ignore */
|
|
298
|
+
}
|
|
222
299
|
}
|
|
223
300
|
}
|
|
224
301
|
|
|
225
302
|
private async closeExpiredClients() {
|
|
226
303
|
const timeLimit = Date.now() - this.config.clientTimeout;
|
|
227
304
|
|
|
228
|
-
for (
|
|
305
|
+
for (const [applicant, clientData] of this.browserClients) {
|
|
229
306
|
if (clientData.initTime < timeLimit) {
|
|
230
|
-
this.logger.warn(
|
|
307
|
+
this.logger.warn(
|
|
308
|
+
`Session applicant ${applicant} marked as expired`,
|
|
309
|
+
);
|
|
231
310
|
try {
|
|
232
311
|
await this.end(applicant);
|
|
233
312
|
} catch (e) {
|
|
234
|
-
this.logger.error(
|
|
313
|
+
this.logger.error(
|
|
314
|
+
`Session applicant ${applicant} failed to stop`,
|
|
315
|
+
e,
|
|
316
|
+
);
|
|
235
317
|
}
|
|
236
318
|
this.expiredBrowserClients.add(applicant);
|
|
237
319
|
}
|
|
@@ -260,7 +342,9 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
260
342
|
}
|
|
261
343
|
|
|
262
344
|
if (this.expiredBrowserClients.has(applicant)) {
|
|
263
|
-
throw Error(
|
|
345
|
+
throw Error(
|
|
346
|
+
`This session expired in ${this.config.clientTimeout}ms`,
|
|
347
|
+
);
|
|
264
348
|
}
|
|
265
349
|
|
|
266
350
|
const client = await remote(this.config);
|
|
@@ -272,7 +356,9 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
272
356
|
throw Error('Session can not be null');
|
|
273
357
|
}
|
|
274
358
|
|
|
275
|
-
const customClient = this.addCustromMethods(
|
|
359
|
+
const customClient = this.addCustromMethods(
|
|
360
|
+
client as BrowserObjectCustom,
|
|
361
|
+
);
|
|
276
362
|
|
|
277
363
|
this.browserClients.set(applicant, {
|
|
278
364
|
client: customClient,
|
|
@@ -280,32 +366,45 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
280
366
|
initTime: Date.now(),
|
|
281
367
|
});
|
|
282
368
|
|
|
283
|
-
this.logger.debug(
|
|
369
|
+
this.logger.debug(
|
|
370
|
+
`Started session for applicant: ${applicant}. Session id: ${sessionId}`,
|
|
371
|
+
);
|
|
284
372
|
}
|
|
285
373
|
|
|
286
|
-
protected addCustromMethods(
|
|
374
|
+
protected addCustromMethods(
|
|
375
|
+
client: BrowserObjectCustom,
|
|
376
|
+
): BrowserObjectCustom {
|
|
287
377
|
// Creating our delete selenium session to be able to close
|
|
288
378
|
// session if it's id is changed while we are running test
|
|
289
|
-
client.addCommand(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
379
|
+
client.addCommand(
|
|
380
|
+
'deleteSessionId',
|
|
381
|
+
function (sessionId) {
|
|
382
|
+
const {
|
|
383
|
+
w3cCaps,
|
|
384
|
+
jsonwpCaps,
|
|
385
|
+
} = this.options.requestedCapabilities;
|
|
386
|
+
|
|
387
|
+
const sessionDeleteRequest = new WebDriverRequest(
|
|
388
|
+
'DELETE',
|
|
389
|
+
'/session/:sessionId',
|
|
390
|
+
{
|
|
391
|
+
capabilities: w3cCaps, // W3C compliant
|
|
392
|
+
desiredCapabilities: jsonwpCaps, // JSONWP compliant
|
|
393
|
+
},
|
|
394
|
+
);
|
|
395
|
+
|
|
396
|
+
return sessionDeleteRequest.makeRequest(
|
|
397
|
+
this.options,
|
|
398
|
+
sessionId,
|
|
399
|
+
);
|
|
400
|
+
},
|
|
401
|
+
false,
|
|
402
|
+
);
|
|
303
403
|
|
|
304
404
|
return client as BrowserObjectCustom;
|
|
305
405
|
}
|
|
306
406
|
|
|
307
407
|
public async end(applicant: string) {
|
|
308
|
-
|
|
309
408
|
await this.waitForReadyState;
|
|
310
409
|
|
|
311
410
|
if (!this.hasBrowserClient(applicant)) {
|
|
@@ -317,33 +416,44 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
317
416
|
|
|
318
417
|
try {
|
|
319
418
|
await this.alertDismiss(applicant);
|
|
320
|
-
} catch {
|
|
419
|
+
} catch {
|
|
420
|
+
/* ignore */
|
|
421
|
+
}
|
|
321
422
|
|
|
322
423
|
const startingSessionID = this.getApplicantSessionId(applicant);
|
|
323
424
|
const sessionID = client.sessionId;
|
|
324
425
|
|
|
325
426
|
if (startingSessionID === sessionID) {
|
|
326
|
-
this.logger.debug(
|
|
427
|
+
this.logger.debug(
|
|
428
|
+
`Stopping sessions for applicant ${applicant}. Session id: ${sessionID}`,
|
|
429
|
+
);
|
|
327
430
|
await client.deleteSession();
|
|
328
431
|
} else {
|
|
329
432
|
await this.logger.stepWarning(
|
|
330
|
-
`Stopping sessions for applicant warning ${applicant}. `
|
|
331
|
-
|
|
433
|
+
`Stopping sessions for applicant warning ${applicant}. ` +
|
|
434
|
+
`Session ids are not equal, started with - ${startingSessionID}, ended with - ${sessionID}`,
|
|
332
435
|
async () => {
|
|
333
436
|
try {
|
|
334
437
|
if (startingSessionID) {
|
|
335
438
|
await client.deleteSessionId(startingSessionID);
|
|
336
439
|
}
|
|
337
440
|
} catch (err) {
|
|
338
|
-
this.logger.error(
|
|
441
|
+
this.logger.error(
|
|
442
|
+
`Old session ${startingSessionID} delete error`,
|
|
443
|
+
err,
|
|
444
|
+
);
|
|
339
445
|
}
|
|
340
446
|
|
|
341
447
|
try {
|
|
342
448
|
await client.deleteSession();
|
|
343
449
|
} catch (err) {
|
|
344
|
-
this.logger.error(
|
|
450
|
+
this.logger.error(
|
|
451
|
+
`New session ${client.sessionId} delete error`,
|
|
452
|
+
err,
|
|
453
|
+
);
|
|
345
454
|
}
|
|
346
|
-
}
|
|
455
|
+
},
|
|
456
|
+
);
|
|
347
457
|
}
|
|
348
458
|
|
|
349
459
|
this.browserClients.delete(applicant);
|
|
@@ -371,7 +481,11 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
371
481
|
return client.refresh();
|
|
372
482
|
}
|
|
373
483
|
|
|
374
|
-
public async click(
|
|
484
|
+
public async click(
|
|
485
|
+
applicant: string,
|
|
486
|
+
selector: string,
|
|
487
|
+
options?: ClickOptions,
|
|
488
|
+
) {
|
|
375
489
|
await this.createClient(applicant);
|
|
376
490
|
const client = this.getBrowserClient(applicant);
|
|
377
491
|
|
|
@@ -405,28 +519,44 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
405
519
|
return `window-${this.incrementWinId}`;
|
|
406
520
|
}
|
|
407
521
|
|
|
408
|
-
public async newWindow(
|
|
522
|
+
public async newWindow(
|
|
523
|
+
applicant: string,
|
|
524
|
+
val: string,
|
|
525
|
+
windowName: string,
|
|
526
|
+
windowFeatures: WindowFeaturesConfig = {},
|
|
527
|
+
) {
|
|
409
528
|
await this.createClient(applicant);
|
|
410
529
|
const client = this.getBrowserClient(applicant);
|
|
411
530
|
const args = stringifyWindowFeatures(windowFeatures);
|
|
412
531
|
|
|
413
|
-
return client.newWindow(val,
|
|
532
|
+
return client.newWindow(val, {
|
|
533
|
+
windowName: windowName || this.generateWinId(),
|
|
534
|
+
windowFeatures: args,
|
|
535
|
+
});
|
|
414
536
|
}
|
|
415
537
|
|
|
416
|
-
public async waitForExist(
|
|
538
|
+
public async waitForExist(
|
|
539
|
+
applicant: string,
|
|
540
|
+
xpath: string,
|
|
541
|
+
timeout: number,
|
|
542
|
+
) {
|
|
417
543
|
await this.createClient(applicant);
|
|
418
544
|
const client = this.getBrowserClient(applicant);
|
|
419
545
|
|
|
420
546
|
const selector = await client.$(xpath);
|
|
421
|
-
return selector.waitForExist(timeout);
|
|
547
|
+
return selector.waitForExist({timeout});
|
|
422
548
|
}
|
|
423
549
|
|
|
424
|
-
public async waitForVisible(
|
|
550
|
+
public async waitForVisible(
|
|
551
|
+
applicant: string,
|
|
552
|
+
xpath: string,
|
|
553
|
+
timeout: number,
|
|
554
|
+
) {
|
|
425
555
|
await this.createClient(applicant);
|
|
426
556
|
const client = this.getBrowserClient(applicant);
|
|
427
557
|
|
|
428
558
|
const selector = await client.$(xpath);
|
|
429
|
-
return selector.waitForDisplayed(timeout);
|
|
559
|
+
return selector.waitForDisplayed({timeout});
|
|
430
560
|
}
|
|
431
561
|
|
|
432
562
|
public async isVisible(applicant: string, xpath: string) {
|
|
@@ -437,12 +567,17 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
437
567
|
return selector.isDisplayed();
|
|
438
568
|
}
|
|
439
569
|
|
|
440
|
-
public async moveToObject(
|
|
570
|
+
public async moveToObject(
|
|
571
|
+
applicant: string,
|
|
572
|
+
xpath: string,
|
|
573
|
+
xOffset = 0,
|
|
574
|
+
yOffset = 0,
|
|
575
|
+
) {
|
|
441
576
|
await this.createClient(applicant);
|
|
442
577
|
const client = this.getBrowserClient(applicant);
|
|
443
578
|
|
|
444
579
|
const selector = await client.$(xpath);
|
|
445
|
-
return selector.moveTo(
|
|
580
|
+
return selector.moveTo({xOffset, yOffset});
|
|
446
581
|
}
|
|
447
582
|
|
|
448
583
|
public async execute(applicant: string, fn: any, args: Array<any>) {
|
|
@@ -492,14 +627,14 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
492
627
|
await this.createClient(applicant);
|
|
493
628
|
const client = this.getBrowserClient(applicant);
|
|
494
629
|
|
|
495
|
-
const elements = await client.findElements('xpath', xpath) as unknown;
|
|
630
|
+
const elements = (await client.findElements('xpath', xpath)) as unknown;
|
|
496
631
|
return (elements as Array<Record<string, string>>).map((o) => {
|
|
497
632
|
const keys = Object.keys(o);
|
|
498
|
-
return {
|
|
633
|
+
return {ELEMENT: o[keys[0]]};
|
|
499
634
|
});
|
|
500
635
|
}
|
|
501
636
|
|
|
502
|
-
public async frame(applicant: string, frameID:
|
|
637
|
+
public async frame(applicant: string, frameID: number) {
|
|
503
638
|
await this.createClient(applicant);
|
|
504
639
|
const client = this.getBrowserClient(applicant);
|
|
505
640
|
|
|
@@ -545,7 +680,11 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
545
680
|
return selector.selectByAttribute('value', value);
|
|
546
681
|
}
|
|
547
682
|
|
|
548
|
-
public async selectByVisibleText(
|
|
683
|
+
public async selectByVisibleText(
|
|
684
|
+
applicant: string,
|
|
685
|
+
xpath: string,
|
|
686
|
+
str: string,
|
|
687
|
+
) {
|
|
549
688
|
await this.createClient(applicant);
|
|
550
689
|
const client = this.getBrowserClient(applicant);
|
|
551
690
|
|
|
@@ -553,7 +692,11 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
553
692
|
return selector.selectByVisibleText(str);
|
|
554
693
|
}
|
|
555
694
|
|
|
556
|
-
public async getAttribute(
|
|
695
|
+
public async getAttribute(
|
|
696
|
+
applicant: string,
|
|
697
|
+
xpath: string,
|
|
698
|
+
attr: string,
|
|
699
|
+
): Promise<any> {
|
|
557
700
|
await this.createClient(applicant);
|
|
558
701
|
const client = this.getBrowserClient(applicant);
|
|
559
702
|
|
|
@@ -576,21 +719,32 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
576
719
|
return selector.isEnabled();
|
|
577
720
|
}
|
|
578
721
|
|
|
579
|
-
public async scroll(
|
|
722
|
+
public async scroll(
|
|
723
|
+
applicant: string,
|
|
724
|
+
xpath: string,
|
|
725
|
+
xOffset: number,
|
|
726
|
+
yOffset: number,
|
|
727
|
+
) {
|
|
580
728
|
await this.createClient(applicant);
|
|
581
729
|
const client = this.getBrowserClient(applicant);
|
|
582
730
|
|
|
583
731
|
const element = await client.$(xpath);
|
|
584
732
|
await element.scrollIntoView();
|
|
585
|
-
return element.moveTo(
|
|
733
|
+
return element.moveTo({xOffset, yOffset});
|
|
586
734
|
}
|
|
587
735
|
|
|
588
|
-
public async scrollIntoView(
|
|
736
|
+
public async scrollIntoView(
|
|
737
|
+
applicant: string,
|
|
738
|
+
xpath: string,
|
|
739
|
+
scrollIntoViewOptions?: boolean | null,
|
|
740
|
+
) {
|
|
589
741
|
await this.createClient(applicant);
|
|
590
742
|
const client = this.getBrowserClient(applicant);
|
|
591
743
|
|
|
592
744
|
const element = await client.$(xpath);
|
|
593
|
-
await element.scrollIntoView(
|
|
745
|
+
await element.scrollIntoView(
|
|
746
|
+
scrollIntoViewOptions !== null ? scrollIntoViewOptions : undefined,
|
|
747
|
+
);
|
|
594
748
|
}
|
|
595
749
|
|
|
596
750
|
public async isAlertOpen(applicant: string) {
|
|
@@ -633,7 +787,11 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
633
787
|
throw Error('There is no open alert');
|
|
634
788
|
}
|
|
635
789
|
|
|
636
|
-
public async dragAndDrop(
|
|
790
|
+
public async dragAndDrop(
|
|
791
|
+
applicant: string,
|
|
792
|
+
xpathSource: string,
|
|
793
|
+
xpathDestination: string,
|
|
794
|
+
) {
|
|
637
795
|
await this.createClient(applicant);
|
|
638
796
|
const client = this.getBrowserClient(applicant);
|
|
639
797
|
|
|
@@ -715,7 +873,7 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
715
873
|
|
|
716
874
|
const result = await client.switchToWindow(tabId);
|
|
717
875
|
const body = await client.$('body');
|
|
718
|
-
await client.waitUntil(async () => body.isExisting(), 10000);
|
|
876
|
+
await client.waitUntil(async () => body.isExisting(), {timeout: 10000});
|
|
719
877
|
|
|
720
878
|
return result;
|
|
721
879
|
}
|
|
@@ -732,10 +890,8 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
732
890
|
await client.switchToWindow(tabId);
|
|
733
891
|
|
|
734
892
|
return client.closeWindow();
|
|
735
|
-
|
|
736
893
|
}
|
|
737
894
|
|
|
738
|
-
|
|
739
895
|
public async getTagName(applicant: string, xpath: string) {
|
|
740
896
|
await this.createClient(applicant);
|
|
741
897
|
const client = this.getBrowserClient(applicant);
|
|
@@ -774,14 +930,21 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
774
930
|
return client.takeScreenshot();
|
|
775
931
|
}
|
|
776
932
|
|
|
777
|
-
public async uploadFile(
|
|
933
|
+
public async uploadFile(
|
|
934
|
+
applicant: string,
|
|
935
|
+
filePath: string,
|
|
936
|
+
): Promise<string | void> {
|
|
778
937
|
await this.createClient(applicant);
|
|
779
938
|
const client = this.getBrowserClient(applicant);
|
|
780
939
|
|
|
781
940
|
return client.uploadFile(filePath);
|
|
782
941
|
}
|
|
783
942
|
|
|
784
|
-
public async getCssProperty(
|
|
943
|
+
public async getCssProperty(
|
|
944
|
+
applicant: string,
|
|
945
|
+
xpath: string,
|
|
946
|
+
cssProperty: string,
|
|
947
|
+
): Promise<any> {
|
|
785
948
|
await this.createClient(applicant);
|
|
786
949
|
const client = this.getBrowserClient(applicant);
|
|
787
950
|
|
|
@@ -805,24 +968,40 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
805
968
|
return selector.isExisting();
|
|
806
969
|
}
|
|
807
970
|
|
|
808
|
-
public async waitForValue(
|
|
971
|
+
public async waitForValue(
|
|
972
|
+
applicant: string,
|
|
973
|
+
xpath: string,
|
|
974
|
+
timeout: number,
|
|
975
|
+
reverse: boolean,
|
|
976
|
+
) {
|
|
809
977
|
await this.createClient(applicant);
|
|
810
978
|
const client = this.getBrowserClient(applicant);
|
|
811
979
|
|
|
812
|
-
return client.waitUntil(
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
980
|
+
return client.waitUntil(
|
|
981
|
+
async () => {
|
|
982
|
+
const elemValue = await (await client.$(xpath)).getValue();
|
|
983
|
+
return reverse ? !elemValue : !!elemValue;
|
|
984
|
+
},
|
|
985
|
+
{timeout},
|
|
986
|
+
);
|
|
816
987
|
}
|
|
817
988
|
|
|
818
|
-
public async waitForSelected(
|
|
989
|
+
public async waitForSelected(
|
|
990
|
+
applicant: string,
|
|
991
|
+
xpath: string,
|
|
992
|
+
timeout: number,
|
|
993
|
+
reverse: boolean,
|
|
994
|
+
) {
|
|
819
995
|
await this.createClient(applicant);
|
|
820
996
|
const client = this.getBrowserClient(applicant);
|
|
821
997
|
|
|
822
|
-
return client.waitUntil(
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
998
|
+
return client.waitUntil(
|
|
999
|
+
async () => {
|
|
1000
|
+
const isSelected = await (await client.$(xpath)).isSelected();
|
|
1001
|
+
return reverse ? !isSelected : isSelected;
|
|
1002
|
+
},
|
|
1003
|
+
{timeout},
|
|
1004
|
+
);
|
|
826
1005
|
}
|
|
827
1006
|
|
|
828
1007
|
public async waitUntil(
|
|
@@ -832,14 +1011,18 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
832
1011
|
timeoutMsg?: string,
|
|
833
1012
|
interval?: number,
|
|
834
1013
|
) {
|
|
835
|
-
|
|
836
1014
|
await this.createClient(applicant);
|
|
837
1015
|
const client = this.getBrowserClient(applicant);
|
|
838
1016
|
|
|
839
|
-
return client.waitUntil(condition, timeout, timeoutMsg, interval);
|
|
1017
|
+
return client.waitUntil(condition, {timeout, timeoutMsg, interval});
|
|
840
1018
|
}
|
|
841
1019
|
|
|
842
|
-
public async selectByAttribute(
|
|
1020
|
+
public async selectByAttribute(
|
|
1021
|
+
applicant: string,
|
|
1022
|
+
xpath: string,
|
|
1023
|
+
attribute: string,
|
|
1024
|
+
value: string,
|
|
1025
|
+
) {
|
|
843
1026
|
await this.createClient(applicant);
|
|
844
1027
|
const client = this.getBrowserClient(applicant);
|
|
845
1028
|
|
|
@@ -847,6 +1030,7 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
847
1030
|
return selector.selectByAttribute(attribute, value);
|
|
848
1031
|
}
|
|
849
1032
|
|
|
1033
|
+
// @deprecated WAT-1872
|
|
850
1034
|
public async gridProxyDetails(applicant: string) {
|
|
851
1035
|
await this.createClient(applicant);
|
|
852
1036
|
const client = this.getBrowserClient(applicant);
|
|
@@ -860,6 +1044,7 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
860
1044
|
return client.gridProxyDetails(client.sessionId);
|
|
861
1045
|
}
|
|
862
1046
|
|
|
1047
|
+
// @deprecated WAT-1872
|
|
863
1048
|
public async gridTestSession(applicant: string) {
|
|
864
1049
|
await this.createClient(applicant);
|
|
865
1050
|
const client = this.getBrowserClient(applicant);
|
|
@@ -873,13 +1058,14 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
873
1058
|
return client.gridTestSession(client.sessionId);
|
|
874
1059
|
}
|
|
875
1060
|
|
|
1061
|
+
// @deprecated WAT-1872
|
|
876
1062
|
public async getGridNodeDetails(applicant: string) {
|
|
877
1063
|
await this.createClient(applicant);
|
|
878
1064
|
const client = this.getBrowserClient(applicant);
|
|
879
1065
|
|
|
880
1066
|
const testSession = await this.gridTestSession(applicant);
|
|
881
1067
|
|
|
882
|
-
if (!testSession.localSelenium) {
|
|
1068
|
+
if (testSession && !testSession.localSelenium) {
|
|
883
1069
|
const proxyDetails = await client.gridProxyDetails(applicant);
|
|
884
1070
|
|
|
885
1071
|
delete testSession.msg;
|
|
@@ -889,14 +1075,61 @@ export class SeleniumPlugin implements IBrowserProxyPlugin {
|
|
|
889
1075
|
delete proxyDetails.success;
|
|
890
1076
|
delete proxyDetails.id;
|
|
891
1077
|
|
|
892
|
-
return {
|
|
1078
|
+
return {...testSession, ...proxyDetails};
|
|
893
1079
|
}
|
|
894
1080
|
|
|
895
1081
|
return testSession;
|
|
896
1082
|
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* @param overwrites should NOT be an arrow function, Otherwise it would throw an error
|
|
1086
|
+
*/
|
|
1087
|
+
public async mock(
|
|
1088
|
+
applicant: string,
|
|
1089
|
+
url: string,
|
|
1090
|
+
overwrites: ((res: Matches) => void) | Object | string,
|
|
1091
|
+
filterOptions?: MockFilterOptions,
|
|
1092
|
+
mockResponseParams?: MockResponseParams,
|
|
1093
|
+
) {
|
|
1094
|
+
await this.createClient(applicant);
|
|
1095
|
+
const client = this.getBrowserClient(applicant);
|
|
1096
|
+
|
|
1097
|
+
const mock = await client.mock(url, filterOptions);
|
|
1098
|
+
if (typeof overwrites === 'function') {
|
|
1099
|
+
mock.respond((res) => {
|
|
1100
|
+
overwrites(res);
|
|
1101
|
+
this.setMockData(applicant, url, res);
|
|
1102
|
+
return res.body;
|
|
1103
|
+
}, mockResponseParams);
|
|
1104
|
+
} else {
|
|
1105
|
+
mock.respond(overwrites);
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
public async setMockData(applicant: string, url: string, data: Matches) {
|
|
1110
|
+
await this.createClient(applicant);
|
|
1111
|
+
const client = this.getBrowserClient(applicant);
|
|
1112
|
+
|
|
1113
|
+
client.mockData = client.mockData || {};
|
|
1114
|
+
if (client.mockData[url]) {
|
|
1115
|
+
client.mockData[url].push(data);
|
|
1116
|
+
} else {
|
|
1117
|
+
client.mockData[url] = [data];
|
|
1118
|
+
}
|
|
1119
|
+
return true;
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
public async getMockData(applicant: string, url: string) {
|
|
1123
|
+
await this.createClient(applicant);
|
|
1124
|
+
const client = this.getBrowserClient(applicant);
|
|
1125
|
+
|
|
1126
|
+
if (url) {
|
|
1127
|
+
return client.mockData[url];
|
|
1128
|
+
}
|
|
1129
|
+
return client.mockData;
|
|
1130
|
+
}
|
|
897
1131
|
}
|
|
898
1132
|
|
|
899
|
-
|
|
900
|
-
export default function seleniumProxy(config: Config) {
|
|
1133
|
+
export default function seleniumProxy(config: SeleniumPluginConfig) {
|
|
901
1134
|
return new SeleniumPlugin(config);
|
|
902
1135
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {RemoteOptions} from 'webdriverio';
|
|
2
2
|
|
|
3
|
-
export type SeleniumPluginConfig =
|
|
3
|
+
export type SeleniumPluginConfig = RemoteOptions & {
|
|
4
4
|
chromeDriverPath?: string;
|
|
5
5
|
recorderExtension: boolean;
|
|
6
6
|
clientCheckInterval: number;
|