codeceptjs 3.6.7 → 4.0.0-beta.2
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/bin/codecept.js +81 -84
- package/lib/actor.js +13 -13
- package/lib/ai.js +13 -10
- package/lib/assert/empty.js +21 -20
- package/lib/assert/equal.js +39 -37
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +47 -46
- package/lib/assert/throws.js +11 -13
- package/lib/assert/truth.js +22 -19
- package/lib/assert.js +2 -4
- package/lib/cli.js +49 -57
- package/lib/codecept.js +155 -142
- package/lib/colorUtils.js +3 -3
- package/lib/command/configMigrate.js +52 -58
- package/lib/command/definitions.js +89 -88
- package/lib/command/dryRun.js +68 -71
- package/lib/command/generate.js +188 -197
- package/lib/command/gherkin/init.js +16 -27
- package/lib/command/gherkin/snippets.js +20 -20
- package/lib/command/gherkin/steps.js +8 -8
- package/lib/command/info.js +38 -40
- package/lib/command/init.js +288 -290
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +3 -3
- package/lib/command/run-multiple/run.js +2 -6
- package/lib/command/run-multiple.js +93 -113
- package/lib/command/run-rerun.js +25 -20
- package/lib/command/run-workers.js +66 -64
- package/lib/command/run.js +29 -26
- package/lib/command/utils.js +65 -80
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +9 -10
- package/lib/container.js +48 -40
- package/lib/data/context.js +59 -60
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +20 -26
- package/lib/dirname.js +5 -0
- package/lib/event.js +167 -163
- package/lib/heal.js +17 -13
- package/lib/helper/AI.js +41 -130
- package/lib/helper/ApiDataFactory.js +69 -73
- package/lib/helper/Appium.js +381 -412
- package/lib/helper/Expect.js +425 -0
- package/lib/helper/ExpectHelper.js +48 -40
- package/lib/helper/FileSystem.js +79 -80
- package/lib/helper/GraphQL.js +43 -44
- package/lib/helper/GraphQLDataFactory.js +50 -50
- package/lib/helper/JSONResponse.js +62 -65
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +14 -12
- package/lib/helper/Nightmare.js +566 -662
- package/lib/helper/Playwright.js +1216 -1361
- package/lib/helper/Protractor.js +627 -663
- package/lib/helper/Puppeteer.js +1128 -1231
- package/lib/helper/REST.js +68 -159
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +484 -490
- package/lib/helper/WebDriver.js +1156 -1297
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
- package/lib/helper/extras/PlaywrightRestartOpts.js +18 -21
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +3 -3
- package/lib/helper/network/actions.js +7 -14
- package/lib/helper/network/utils.js +2 -3
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +7 -6
- package/lib/helper.js +3 -1
- package/lib/history.js +5 -6
- package/lib/hooks.js +6 -6
- package/lib/html.js +7 -7
- package/lib/index.js +41 -25
- package/lib/interfaces/bdd.js +64 -47
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +118 -124
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -24
- package/lib/listener/exit.js +12 -12
- package/lib/listener/helpers.js +42 -42
- package/lib/listener/mocha.js +11 -11
- package/lib/listener/retry.js +30 -32
- package/lib/listener/steps.js +53 -50
- package/lib/listener/timeout.js +54 -54
- package/lib/locator.js +10 -6
- package/lib/mochaFactory.js +15 -18
- package/lib/output.js +10 -6
- package/lib/parser.js +12 -15
- package/lib/pause.js +33 -40
- package/lib/plugin/allure.js +15 -15
- package/lib/plugin/autoDelay.js +37 -29
- package/lib/plugin/autoLogin.js +65 -70
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +67 -115
- package/lib/plugin/customLocator.js +20 -21
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +38 -38
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +108 -67
- package/lib/plugin/pauseOnFail.js +11 -11
- package/lib/plugin/retryFailedStep.js +39 -32
- package/lib/plugin/retryTo.js +40 -46
- package/lib/plugin/screenshotOnFail.js +87 -109
- package/lib/plugin/selenoid.js +118 -131
- package/lib/plugin/standardActingHelpers.js +8 -2
- package/lib/plugin/stepByStepReport.js +91 -110
- package/lib/plugin/stepTimeout.js +23 -24
- package/lib/plugin/subtitles.js +35 -34
- package/lib/plugin/tryTo.js +30 -40
- package/lib/plugin/wdio.js +75 -78
- package/lib/recorder.js +17 -14
- package/lib/rerun.js +10 -11
- package/lib/scenario.js +23 -25
- package/lib/secret.js +2 -4
- package/lib/session.js +10 -10
- package/lib/step.js +9 -12
- package/lib/store.js +3 -2
- package/lib/transform.js +1 -1
- package/lib/translation.js +8 -7
- package/lib/ui.js +14 -12
- package/lib/utils.js +72 -70
- package/lib/within.js +10 -10
- package/lib/workerStorage.js +25 -27
- package/lib/workers.js +32 -29
- package/package.json +53 -51
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +13 -9
- package/translations/it-IT.js +1 -1
- package/translations/ja-JP.js +1 -1
- package/translations/pl-PL.js +1 -1
- package/translations/pt-BR.js +1 -1
- package/translations/ru-RU.js +1 -1
- package/translations/zh-CN.js +1 -1
- package/translations/zh-TW.js +1 -1
- package/typings/index.d.ts +65 -415
package/lib/helper/Puppeteer.js
CHANGED
|
@@ -1,21 +1,20 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const {
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import fsExtra from 'fs-extra';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
import Helper from '@codeceptjs/helper';
|
|
6
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
7
|
+
import promiseRetry from 'promise-retry';
|
|
8
|
+
import Locator from '../locator.js';
|
|
9
|
+
import recorder from '../recorder.js';
|
|
10
|
+
import { store } from '../store.js';
|
|
11
|
+
import { includes as stringIncludes } from '../assert/include.js';
|
|
12
|
+
import { urlEquals, equals } from '../assert/equal.js';
|
|
13
|
+
import { empty } from '../assert/empty.js';
|
|
14
|
+
import { truth } from '../assert/truth';
|
|
15
|
+
import isElementClickable from './scripts/isElementClickable';
|
|
16
|
+
|
|
17
|
+
import {
|
|
19
18
|
xpathLocator,
|
|
20
19
|
ucfirst,
|
|
21
20
|
fileExists,
|
|
@@ -28,32 +27,33 @@ const {
|
|
|
28
27
|
isModifierKey,
|
|
29
28
|
requireWithFallback,
|
|
30
29
|
normalizeSpacesInString,
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
30
|
+
} from '../utils.js';
|
|
31
|
+
|
|
32
|
+
import { isColorProperty, convertColorToRGBA } from '../colorUtils';
|
|
33
|
+
import ElementNotFound from './errors/ElementNotFound.js';
|
|
34
|
+
import RemoteBrowserConnectionRefused from './errors/RemoteBrowserConnectionRefused';
|
|
35
|
+
import Popup from './extras/Popup';
|
|
36
|
+
import Console from './extras/Console';
|
|
37
|
+
import findReact from './extras/React';
|
|
38
|
+
import { highlightElement } from './scripts/highlightElement';
|
|
39
|
+
import { blurElement } from './scripts/blurElement';
|
|
40
|
+
import { focusElement } from './scripts/focusElement';
|
|
41
|
+
|
|
42
|
+
import {
|
|
40
43
|
dontSeeElementError,
|
|
41
44
|
seeElementError,
|
|
42
45
|
dontSeeElementInDOMError,
|
|
43
46
|
seeElementInDOMError,
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
seeTraffic,
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
let perfTiming
|
|
55
|
-
const popupStore = new Popup()
|
|
56
|
-
const consoleLogStore = new Console()
|
|
47
|
+
} from './errors/ElementAssertion';
|
|
48
|
+
|
|
49
|
+
import {
|
|
50
|
+
dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics,
|
|
51
|
+
} from './network/actions';
|
|
52
|
+
|
|
53
|
+
let puppeteer;
|
|
54
|
+
let perfTiming;
|
|
55
|
+
const popupStore = new Popup();
|
|
56
|
+
const consoleLogStore = new Console();
|
|
57
57
|
|
|
58
58
|
/**
|
|
59
59
|
* ## Configuration
|
|
@@ -85,7 +85,7 @@ const consoleLogStore = new Console()
|
|
|
85
85
|
* @prop {object} [chrome] - pass additional [Puppeteer run options](https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#puppeteerlaunchoptions).
|
|
86
86
|
* @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
|
|
87
87
|
*/
|
|
88
|
-
const config = {}
|
|
88
|
+
const config = {};
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* Uses [Google Chrome's Puppeteer](https://github.com/GoogleChrome/puppeteer) library to run tests inside headless Chrome.
|
|
@@ -222,29 +222,29 @@ const config = {}
|
|
|
222
222
|
*/
|
|
223
223
|
class Puppeteer extends Helper {
|
|
224
224
|
constructor(config) {
|
|
225
|
-
super(config)
|
|
225
|
+
super(config);
|
|
226
226
|
|
|
227
|
-
puppeteer = requireWithFallback('puppeteer', 'puppeteer-core')
|
|
227
|
+
puppeteer = requireWithFallback('puppeteer', 'puppeteer-core');
|
|
228
228
|
// set defaults
|
|
229
|
-
this.isRemoteBrowser = false
|
|
230
|
-
this.isRunning = false
|
|
231
|
-
this.isAuthenticated = false
|
|
232
|
-
this.sessionPages = {}
|
|
233
|
-
this.activeSessionName = ''
|
|
229
|
+
this.isRemoteBrowser = false;
|
|
230
|
+
this.isRunning = false;
|
|
231
|
+
this.isAuthenticated = false;
|
|
232
|
+
this.sessionPages = {};
|
|
233
|
+
this.activeSessionName = '';
|
|
234
234
|
|
|
235
235
|
// for network stuff
|
|
236
|
-
this.requests = []
|
|
237
|
-
this.recording = false
|
|
238
|
-
this.recordedAtLeastOnce = false
|
|
236
|
+
this.requests = [];
|
|
237
|
+
this.recording = false;
|
|
238
|
+
this.recordedAtLeastOnce = false;
|
|
239
239
|
|
|
240
240
|
// for websocket messages
|
|
241
|
-
this.webSocketMessages = []
|
|
242
|
-
this.recordingWebSocketMessages = false
|
|
243
|
-
this.recordedWebSocketMessagesAtLeastOnce = false
|
|
244
|
-
this.cdpSession = null
|
|
241
|
+
this.webSocketMessages = [];
|
|
242
|
+
this.recordingWebSocketMessages = false;
|
|
243
|
+
this.recordedWebSocketMessagesAtLeastOnce = false;
|
|
244
|
+
this.cdpSession = null;
|
|
245
245
|
|
|
246
246
|
// override defaults with config
|
|
247
|
-
this._setConfig(config)
|
|
247
|
+
this._setConfig(config);
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
_validateConfig(config) {
|
|
@@ -265,161 +265,156 @@ class Puppeteer extends Helper {
|
|
|
265
265
|
show: false,
|
|
266
266
|
defaultPopupAction: 'accept',
|
|
267
267
|
highlightElement: false,
|
|
268
|
-
}
|
|
268
|
+
};
|
|
269
269
|
|
|
270
|
-
return Object.assign(defaults, config)
|
|
270
|
+
return Object.assign(defaults, config);
|
|
271
271
|
}
|
|
272
272
|
|
|
273
273
|
_getOptions(config) {
|
|
274
|
-
return config.browser === 'firefox'
|
|
275
|
-
? Object.assign(this.options.firefox, { product: 'firefox' })
|
|
276
|
-
: this.options.chrome
|
|
274
|
+
return config.browser === 'firefox' ? Object.assign(this.options.firefox, { product: 'firefox' }) : this.options.chrome;
|
|
277
275
|
}
|
|
278
276
|
|
|
279
277
|
_setConfig(config) {
|
|
280
|
-
this.options = this._validateConfig(config)
|
|
278
|
+
this.options = this._validateConfig(config);
|
|
281
279
|
this.puppeteerOptions = {
|
|
282
280
|
headless: !this.options.show,
|
|
283
281
|
...this._getOptions(config),
|
|
284
|
-
}
|
|
285
|
-
if (this.puppeteerOptions.headless) this.puppeteerOptions.headless = 'new'
|
|
286
|
-
this.isRemoteBrowser = !!this.puppeteerOptions.browserWSEndpoint
|
|
287
|
-
popupStore.defaultAction = this.options.defaultPopupAction
|
|
282
|
+
};
|
|
283
|
+
if (this.puppeteerOptions.headless) this.puppeteerOptions.headless = 'new';
|
|
284
|
+
this.isRemoteBrowser = !!this.puppeteerOptions.browserWSEndpoint;
|
|
285
|
+
popupStore.defaultAction = this.options.defaultPopupAction;
|
|
288
286
|
}
|
|
289
287
|
|
|
290
288
|
static _config() {
|
|
291
289
|
return [
|
|
292
290
|
{ name: 'url', message: 'Base url of site to be tested', default: 'http://localhost' },
|
|
293
291
|
{
|
|
294
|
-
name: 'show',
|
|
295
|
-
message: 'Show browser window',
|
|
296
|
-
default: true,
|
|
297
|
-
type: 'confirm',
|
|
292
|
+
name: 'show', message: 'Show browser window', default: true, type: 'confirm',
|
|
298
293
|
},
|
|
299
294
|
{
|
|
300
|
-
name: 'windowSize',
|
|
301
|
-
message: 'Browser viewport size',
|
|
302
|
-
default: '1200x900',
|
|
295
|
+
name: 'windowSize', message: 'Browser viewport size', default: '1200x900',
|
|
303
296
|
},
|
|
304
|
-
]
|
|
297
|
+
];
|
|
305
298
|
}
|
|
306
299
|
|
|
307
300
|
static _checkRequirements() {
|
|
308
301
|
try {
|
|
309
|
-
requireWithFallback('puppeteer', 'puppeteer-core')
|
|
302
|
+
requireWithFallback('puppeteer', 'puppeteer-core');
|
|
310
303
|
} catch (e) {
|
|
311
|
-
return ['puppeteer']
|
|
304
|
+
return ['puppeteer'];
|
|
312
305
|
}
|
|
313
306
|
}
|
|
314
307
|
|
|
315
|
-
_init() {
|
|
308
|
+
_init() {
|
|
309
|
+
}
|
|
316
310
|
|
|
317
311
|
_beforeSuite() {
|
|
318
312
|
if (!this.options.restart && !this.options.manualStart && !this.isRunning) {
|
|
319
|
-
this.debugSection('Session', 'Starting singleton browser session')
|
|
320
|
-
return this._startBrowser()
|
|
313
|
+
this.debugSection('Session', 'Starting singleton browser session');
|
|
314
|
+
return this._startBrowser();
|
|
321
315
|
}
|
|
322
316
|
}
|
|
323
317
|
|
|
324
318
|
async _before(test) {
|
|
325
|
-
this.sessionPages = {}
|
|
326
|
-
this.currentRunningTest = test
|
|
319
|
+
this.sessionPages = {};
|
|
320
|
+
this.currentRunningTest = test;
|
|
327
321
|
recorder.retry({
|
|
328
322
|
retries: process.env.FAILED_STEP_RETRIES || 3,
|
|
329
|
-
when:
|
|
330
|
-
if (!err || typeof err.message !== 'string') {
|
|
331
|
-
return false
|
|
323
|
+
when: err => {
|
|
324
|
+
if (!err || typeof (err.message) !== 'string') {
|
|
325
|
+
return false;
|
|
332
326
|
}
|
|
333
327
|
// ignore context errors
|
|
334
|
-
return err.message.includes('context')
|
|
328
|
+
return err.message.includes('context');
|
|
335
329
|
},
|
|
336
|
-
})
|
|
337
|
-
if (this.options.restart && !this.options.manualStart) return this._startBrowser()
|
|
338
|
-
if (!this.isRunning && !this.options.manualStart) return this._startBrowser()
|
|
339
|
-
return this.browser
|
|
330
|
+
});
|
|
331
|
+
if (this.options.restart && !this.options.manualStart) return this._startBrowser();
|
|
332
|
+
if (!this.isRunning && !this.options.manualStart) return this._startBrowser();
|
|
333
|
+
return this.browser;
|
|
340
334
|
}
|
|
341
335
|
|
|
342
336
|
async _after() {
|
|
343
|
-
if (!this.isRunning) return
|
|
337
|
+
if (!this.isRunning) return;
|
|
344
338
|
|
|
345
339
|
// close other sessions
|
|
346
|
-
const contexts = this.browser.browserContexts()
|
|
347
|
-
const defaultCtx = contexts.shift()
|
|
340
|
+
const contexts = this.browser.browserContexts();
|
|
341
|
+
const defaultCtx = contexts.shift();
|
|
348
342
|
|
|
349
|
-
await Promise.all(contexts.map(
|
|
343
|
+
await Promise.all(contexts.map(c => c.close()));
|
|
350
344
|
|
|
351
345
|
if (this.options.restart) {
|
|
352
|
-
this.isRunning = false
|
|
353
|
-
return this._stopBrowser()
|
|
346
|
+
this.isRunning = false;
|
|
347
|
+
return this._stopBrowser();
|
|
354
348
|
}
|
|
355
349
|
|
|
356
350
|
// ensure this.page is from default context
|
|
357
351
|
if (this.page) {
|
|
358
|
-
const existingPages = defaultCtx.targets().filter(
|
|
359
|
-
await this._setPage(await existingPages[0].page())
|
|
352
|
+
const existingPages = defaultCtx.targets().filter(t => t.type() === 'page');
|
|
353
|
+
await this._setPage(await existingPages[0].page());
|
|
360
354
|
}
|
|
361
355
|
|
|
362
|
-
if (this.options.keepBrowserState) return
|
|
356
|
+
if (this.options.keepBrowserState) return;
|
|
363
357
|
|
|
364
358
|
if (!this.options.keepCookies) {
|
|
365
|
-
this.debugSection('Session', 'cleaning cookies and localStorage')
|
|
366
|
-
await this.clearCookie()
|
|
359
|
+
this.debugSection('Session', 'cleaning cookies and localStorage');
|
|
360
|
+
await this.clearCookie();
|
|
367
361
|
}
|
|
368
|
-
const currentUrl = await this.grabCurrentUrl()
|
|
362
|
+
const currentUrl = await this.grabCurrentUrl();
|
|
369
363
|
|
|
370
364
|
if (currentUrl.startsWith('http')) {
|
|
371
365
|
await this.executeScript('localStorage.clear();').catch((err) => {
|
|
372
|
-
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
373
|
-
})
|
|
366
|
+
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
|
|
367
|
+
});
|
|
374
368
|
await this.executeScript('sessionStorage.clear();').catch((err) => {
|
|
375
|
-
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
376
|
-
})
|
|
369
|
+
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err;
|
|
370
|
+
});
|
|
377
371
|
}
|
|
378
|
-
await this.closeOtherTabs()
|
|
379
|
-
return this.browser
|
|
372
|
+
await this.closeOtherTabs();
|
|
373
|
+
return this.browser;
|
|
380
374
|
}
|
|
381
375
|
|
|
382
|
-
_afterSuite() {
|
|
376
|
+
_afterSuite() {
|
|
377
|
+
}
|
|
383
378
|
|
|
384
379
|
_finishTest() {
|
|
385
|
-
if (!this.options.restart && this.isRunning) return this._stopBrowser()
|
|
380
|
+
if (!this.options.restart && this.isRunning) return this._stopBrowser();
|
|
386
381
|
}
|
|
387
382
|
|
|
388
383
|
_session() {
|
|
389
384
|
return {
|
|
390
385
|
start: async (name = '') => {
|
|
391
|
-
this.debugSection('Incognito Tab', 'opened')
|
|
392
|
-
this.activeSessionName = name
|
|
386
|
+
this.debugSection('Incognito Tab', 'opened');
|
|
387
|
+
this.activeSessionName = name;
|
|
393
388
|
|
|
394
|
-
const bc = await this.browser.createBrowserContext()
|
|
395
|
-
await bc.newPage()
|
|
389
|
+
const bc = await this.browser.createBrowserContext();
|
|
390
|
+
await bc.newPage();
|
|
396
391
|
|
|
397
392
|
// Create a new page inside context.
|
|
398
|
-
return bc
|
|
393
|
+
return bc;
|
|
399
394
|
},
|
|
400
395
|
stop: async () => {
|
|
401
396
|
// is closed by _after
|
|
402
397
|
},
|
|
403
398
|
loadVars: async (context) => {
|
|
404
|
-
const existingPages = context.targets().filter(
|
|
405
|
-
this.sessionPages[this.activeSessionName] = await existingPages[0].page()
|
|
406
|
-
return this._setPage(this.sessionPages[this.activeSessionName])
|
|
399
|
+
const existingPages = context.targets().filter(t => t.type() === 'page');
|
|
400
|
+
this.sessionPages[this.activeSessionName] = await existingPages[0].page();
|
|
401
|
+
return this._setPage(this.sessionPages[this.activeSessionName]);
|
|
407
402
|
},
|
|
408
403
|
restoreVars: async (session) => {
|
|
409
|
-
this.withinLocator = null
|
|
404
|
+
this.withinLocator = null;
|
|
410
405
|
|
|
411
406
|
if (!session) {
|
|
412
|
-
this.activeSessionName = ''
|
|
407
|
+
this.activeSessionName = '';
|
|
413
408
|
} else {
|
|
414
|
-
this.activeSessionName = session
|
|
409
|
+
this.activeSessionName = session;
|
|
415
410
|
}
|
|
416
|
-
const defaultCtx = this.browser.defaultBrowserContext()
|
|
417
|
-
const existingPages = defaultCtx.targets().filter(
|
|
418
|
-
await this._setPage(await existingPages[0].page())
|
|
411
|
+
const defaultCtx = this.browser.defaultBrowserContext();
|
|
412
|
+
const existingPages = defaultCtx.targets().filter(t => t.type() === 'page');
|
|
413
|
+
await this._setPage(await existingPages[0].page());
|
|
419
414
|
|
|
420
|
-
return this._waitForAction()
|
|
415
|
+
return this._waitForAction();
|
|
421
416
|
},
|
|
422
|
-
}
|
|
417
|
+
};
|
|
423
418
|
}
|
|
424
419
|
|
|
425
420
|
/**
|
|
@@ -440,7 +435,7 @@ class Puppeteer extends Helper {
|
|
|
440
435
|
* @param {function} fn async function that is executed with Puppeteer as argument
|
|
441
436
|
*/
|
|
442
437
|
usePuppeteerTo(description, fn) {
|
|
443
|
-
return this._useTo(...arguments)
|
|
438
|
+
return this._useTo(...arguments);
|
|
444
439
|
}
|
|
445
440
|
|
|
446
441
|
/**
|
|
@@ -454,7 +449,7 @@ class Puppeteer extends Helper {
|
|
|
454
449
|
* ```
|
|
455
450
|
*/
|
|
456
451
|
amAcceptingPopups() {
|
|
457
|
-
popupStore.actionType = 'accept'
|
|
452
|
+
popupStore.actionType = 'accept';
|
|
458
453
|
}
|
|
459
454
|
|
|
460
455
|
/**
|
|
@@ -463,7 +458,7 @@ class Puppeteer extends Helper {
|
|
|
463
458
|
* libraries](http://jster.net/category/windows-modals-popups).
|
|
464
459
|
*/
|
|
465
460
|
acceptPopup() {
|
|
466
|
-
popupStore.assertPopupActionType('accept')
|
|
461
|
+
popupStore.assertPopupActionType('accept');
|
|
467
462
|
}
|
|
468
463
|
|
|
469
464
|
/**
|
|
@@ -477,23 +472,23 @@ class Puppeteer extends Helper {
|
|
|
477
472
|
* ```
|
|
478
473
|
*/
|
|
479
474
|
amCancellingPopups() {
|
|
480
|
-
popupStore.actionType = 'cancel'
|
|
475
|
+
popupStore.actionType = 'cancel';
|
|
481
476
|
}
|
|
482
477
|
|
|
483
478
|
/**
|
|
484
479
|
* Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt.
|
|
485
480
|
*/
|
|
486
481
|
cancelPopup() {
|
|
487
|
-
popupStore.assertPopupActionType('cancel')
|
|
482
|
+
popupStore.assertPopupActionType('cancel');
|
|
488
483
|
}
|
|
489
484
|
|
|
490
485
|
/**
|
|
491
486
|
* {{> seeInPopup }}
|
|
492
487
|
*/
|
|
493
488
|
async seeInPopup(text) {
|
|
494
|
-
popupStore.assertPopupVisible()
|
|
495
|
-
const popupText = await popupStore.popup.message()
|
|
496
|
-
stringIncludes('text in popup').assert(text, popupText)
|
|
489
|
+
popupStore.assertPopupVisible();
|
|
490
|
+
const popupText = await popupStore.popup.message();
|
|
491
|
+
stringIncludes('text in popup').assert(text, popupText);
|
|
497
492
|
}
|
|
498
493
|
|
|
499
494
|
/**
|
|
@@ -501,25 +496,25 @@ class Puppeteer extends Helper {
|
|
|
501
496
|
* @param {object} page page to set
|
|
502
497
|
*/
|
|
503
498
|
async _setPage(page) {
|
|
504
|
-
page = await page
|
|
505
|
-
this._addPopupListener(page)
|
|
506
|
-
this._addErrorListener(page)
|
|
507
|
-
this.page = page
|
|
508
|
-
if (!page) return
|
|
509
|
-
page.setDefaultNavigationTimeout(this.options.getPageTimeout)
|
|
510
|
-
this.context = await this.page.$('body')
|
|
499
|
+
page = await page;
|
|
500
|
+
this._addPopupListener(page);
|
|
501
|
+
this._addErrorListener(page);
|
|
502
|
+
this.page = page;
|
|
503
|
+
if (!page) return;
|
|
504
|
+
page.setDefaultNavigationTimeout(this.options.getPageTimeout);
|
|
505
|
+
this.context = await this.page.$('body');
|
|
511
506
|
if (this.options.browser === 'chrome') {
|
|
512
|
-
await page.bringToFront()
|
|
507
|
+
await page.bringToFront();
|
|
513
508
|
}
|
|
514
509
|
}
|
|
515
510
|
|
|
516
511
|
async _addErrorListener(page) {
|
|
517
512
|
if (!page) {
|
|
518
|
-
return
|
|
513
|
+
return;
|
|
519
514
|
}
|
|
520
515
|
page.on('error', async (error) => {
|
|
521
|
-
console.error('Puppeteer page error', error)
|
|
522
|
-
})
|
|
516
|
+
console.error('Puppeteer page error', error);
|
|
517
|
+
});
|
|
523
518
|
}
|
|
524
519
|
|
|
525
520
|
/**
|
|
@@ -531,32 +526,32 @@ class Puppeteer extends Helper {
|
|
|
531
526
|
*/
|
|
532
527
|
_addPopupListener(page) {
|
|
533
528
|
if (!page) {
|
|
534
|
-
return
|
|
529
|
+
return;
|
|
535
530
|
}
|
|
536
531
|
page.on('dialog', async (dialog) => {
|
|
537
|
-
popupStore.popup = dialog
|
|
538
|
-
const action = popupStore.actionType || this.options.defaultPopupAction
|
|
539
|
-
await this._waitForAction()
|
|
532
|
+
popupStore.popup = dialog;
|
|
533
|
+
const action = popupStore.actionType || this.options.defaultPopupAction;
|
|
534
|
+
await this._waitForAction();
|
|
540
535
|
|
|
541
536
|
switch (action) {
|
|
542
537
|
case 'accept':
|
|
543
|
-
return dialog.accept()
|
|
538
|
+
return dialog.accept();
|
|
544
539
|
|
|
545
540
|
case 'cancel':
|
|
546
|
-
return dialog.dismiss()
|
|
541
|
+
return dialog.dismiss();
|
|
547
542
|
|
|
548
543
|
default: {
|
|
549
|
-
throw new Error('Unknown popup action type. Only "accept" or "cancel" are accepted')
|
|
544
|
+
throw new Error('Unknown popup action type. Only "accept" or "cancel" are accepted');
|
|
550
545
|
}
|
|
551
546
|
}
|
|
552
|
-
})
|
|
547
|
+
});
|
|
553
548
|
}
|
|
554
549
|
|
|
555
550
|
/**
|
|
556
551
|
* Gets page URL including hash.
|
|
557
552
|
*/
|
|
558
553
|
async _getPageUrl() {
|
|
559
|
-
return this.executeScript(() => window.location.href)
|
|
554
|
+
return this.executeScript(() => window.location.href);
|
|
560
555
|
}
|
|
561
556
|
|
|
562
557
|
/**
|
|
@@ -569,134 +564,128 @@ class Puppeteer extends Helper {
|
|
|
569
564
|
*/
|
|
570
565
|
async grabPopupText() {
|
|
571
566
|
if (popupStore.popup) {
|
|
572
|
-
return popupStore.popup.message()
|
|
567
|
+
return popupStore.popup.message();
|
|
573
568
|
}
|
|
574
|
-
return null
|
|
569
|
+
return null;
|
|
575
570
|
}
|
|
576
571
|
|
|
577
572
|
async _startBrowser() {
|
|
578
573
|
if (this.isRemoteBrowser) {
|
|
579
574
|
try {
|
|
580
|
-
this.browser = await puppeteer.connect(this.puppeteerOptions)
|
|
575
|
+
this.browser = await puppeteer.connect(this.puppeteerOptions);
|
|
581
576
|
} catch (err) {
|
|
582
577
|
if (err.toString().indexOf('ECONNREFUSED')) {
|
|
583
|
-
throw new RemoteBrowserConnectionRefused(err)
|
|
578
|
+
throw new RemoteBrowserConnectionRefused(err);
|
|
584
579
|
}
|
|
585
|
-
throw err
|
|
580
|
+
throw err;
|
|
586
581
|
}
|
|
587
582
|
} else {
|
|
588
|
-
this.browser = await puppeteer.launch(this.puppeteerOptions)
|
|
583
|
+
this.browser = await puppeteer.launch(this.puppeteerOptions);
|
|
589
584
|
}
|
|
590
585
|
|
|
591
|
-
this.browser.on('targetcreated', (
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
.then((page) => targetCreatedHandler.call(this, page))
|
|
595
|
-
.catch((e) => {
|
|
596
|
-
console.error('Puppeteer page error', e)
|
|
597
|
-
}),
|
|
598
|
-
)
|
|
586
|
+
this.browser.on('targetcreated', target => target.page().then(page => targetCreatedHandler.call(this, page)).catch((e) => {
|
|
587
|
+
console.error('Puppeteer page error', e);
|
|
588
|
+
}));
|
|
599
589
|
this.browser.on('targetchanged', (target) => {
|
|
600
|
-
this.debugSection('Url', target.url())
|
|
601
|
-
})
|
|
590
|
+
this.debugSection('Url', target.url());
|
|
591
|
+
});
|
|
602
592
|
|
|
603
|
-
const existingPages = await this.browser.pages()
|
|
604
|
-
const mainPage = existingPages[0] || (await this.browser.newPage())
|
|
593
|
+
const existingPages = await this.browser.pages();
|
|
594
|
+
const mainPage = existingPages[0] || (await this.browser.newPage());
|
|
605
595
|
|
|
606
596
|
if (existingPages.length) {
|
|
607
597
|
// Run the handler as it will not be triggered if the page already exists
|
|
608
|
-
targetCreatedHandler.call(this, mainPage)
|
|
598
|
+
targetCreatedHandler.call(this, mainPage);
|
|
609
599
|
}
|
|
610
|
-
await this._setPage(mainPage)
|
|
611
|
-
await this.closeOtherTabs()
|
|
600
|
+
await this._setPage(mainPage);
|
|
601
|
+
await this.closeOtherTabs();
|
|
612
602
|
|
|
613
|
-
this.isRunning = true
|
|
603
|
+
this.isRunning = true;
|
|
614
604
|
}
|
|
615
605
|
|
|
616
606
|
async _stopBrowser() {
|
|
617
|
-
this.withinLocator = null
|
|
618
|
-
this._setPage(null)
|
|
619
|
-
this.context = null
|
|
620
|
-
popupStore.clear()
|
|
621
|
-
this.isAuthenticated = false
|
|
622
|
-
await this.browser.close()
|
|
607
|
+
this.withinLocator = null;
|
|
608
|
+
this._setPage(null);
|
|
609
|
+
this.context = null;
|
|
610
|
+
popupStore.clear();
|
|
611
|
+
this.isAuthenticated = false;
|
|
612
|
+
await this.browser.close();
|
|
623
613
|
if (this.isRemoteBrowser) {
|
|
624
|
-
await this.browser.disconnect()
|
|
614
|
+
await this.browser.disconnect();
|
|
625
615
|
}
|
|
626
616
|
}
|
|
627
617
|
|
|
628
618
|
async _evaluateHandeInContext(...args) {
|
|
629
|
-
const context = await this._getContext()
|
|
630
|
-
return context.evaluateHandle(...args)
|
|
619
|
+
const context = await this._getContext();
|
|
620
|
+
return context.evaluateHandle(...args);
|
|
631
621
|
}
|
|
632
622
|
|
|
633
623
|
async _withinBegin(locator) {
|
|
634
624
|
if (this.withinLocator) {
|
|
635
|
-
throw new Error(
|
|
625
|
+
throw new Error('Can\'t start within block inside another within block');
|
|
636
626
|
}
|
|
637
627
|
|
|
638
|
-
const frame = isFrameLocator(locator)
|
|
628
|
+
const frame = isFrameLocator(locator);
|
|
639
629
|
|
|
640
630
|
if (frame) {
|
|
641
631
|
if (Array.isArray(frame)) {
|
|
642
|
-
return this.switchTo(null)
|
|
643
|
-
frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve())
|
|
644
|
-
)
|
|
632
|
+
return this.switchTo(null)
|
|
633
|
+
.then(() => frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve()));
|
|
645
634
|
}
|
|
646
|
-
await this.switchTo(frame)
|
|
647
|
-
this.withinLocator = new Locator(frame)
|
|
648
|
-
return
|
|
635
|
+
await this.switchTo(frame);
|
|
636
|
+
this.withinLocator = new Locator(frame);
|
|
637
|
+
return;
|
|
649
638
|
}
|
|
650
639
|
|
|
651
|
-
const els = await this._locate(locator)
|
|
652
|
-
assertElementExists(els, locator)
|
|
653
|
-
this.context = els[0]
|
|
640
|
+
const els = await this._locate(locator);
|
|
641
|
+
assertElementExists(els, locator);
|
|
642
|
+
this.context = els[0];
|
|
654
643
|
|
|
655
|
-
this.withinLocator = new Locator(locator)
|
|
644
|
+
this.withinLocator = new Locator(locator);
|
|
656
645
|
}
|
|
657
646
|
|
|
658
647
|
async _withinEnd() {
|
|
659
|
-
this.withinLocator = null
|
|
660
|
-
this.context = await this.page.mainFrame().$('body')
|
|
648
|
+
this.withinLocator = null;
|
|
649
|
+
this.context = await this.page.mainFrame().$('body');
|
|
661
650
|
}
|
|
662
651
|
|
|
663
652
|
_extractDataFromPerformanceTiming(timing, ...dataNames) {
|
|
664
|
-
const navigationStart = timing.navigationStart
|
|
653
|
+
const navigationStart = timing.navigationStart;
|
|
665
654
|
|
|
666
|
-
const extractedData = {}
|
|
655
|
+
const extractedData = {};
|
|
667
656
|
dataNames.forEach((name) => {
|
|
668
|
-
extractedData[name] = timing[name] - navigationStart
|
|
669
|
-
})
|
|
657
|
+
extractedData[name] = timing[name] - navigationStart;
|
|
658
|
+
});
|
|
670
659
|
|
|
671
|
-
return extractedData
|
|
660
|
+
return extractedData;
|
|
672
661
|
}
|
|
673
662
|
|
|
674
663
|
/**
|
|
675
664
|
* {{> amOnPage }}
|
|
676
665
|
*/
|
|
677
666
|
async amOnPage(url) {
|
|
678
|
-
if (
|
|
679
|
-
url = this.options.url + url
|
|
667
|
+
if (!(/^\w+\:\/\//.test(url))) {
|
|
668
|
+
url = this.options.url + url;
|
|
680
669
|
}
|
|
681
670
|
|
|
682
|
-
if (this.options.basicAuth && this.isAuthenticated !== true) {
|
|
671
|
+
if (this.options.basicAuth && (this.isAuthenticated !== true)) {
|
|
683
672
|
if (url.includes(this.options.url)) {
|
|
684
|
-
await this.page.authenticate(this.options.basicAuth)
|
|
685
|
-
this.isAuthenticated = true
|
|
673
|
+
await this.page.authenticate(this.options.basicAuth);
|
|
674
|
+
this.isAuthenticated = true;
|
|
686
675
|
}
|
|
687
676
|
}
|
|
688
677
|
|
|
689
678
|
if (this.options.trace) {
|
|
690
|
-
const fileName = `${`${global.output_dir}${path.sep}trace${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.json
|
|
691
|
-
const dir = path.dirname(fileName)
|
|
692
|
-
if (!fileExists(dir)) fs.mkdirSync(dir)
|
|
693
|
-
await this.page.tracing.start({ screenshots: true, path: fileName })
|
|
694
|
-
this.currentRunningTest.artifacts.trace = fileName
|
|
679
|
+
const fileName = `${`${global.output_dir}${path.sep}trace${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.json`;
|
|
680
|
+
const dir = path.dirname(fileName);
|
|
681
|
+
if (!fileExists(dir)) fs.mkdirSync(dir);
|
|
682
|
+
await this.page.tracing.start({ screenshots: true, path: fileName });
|
|
683
|
+
this.currentRunningTest.artifacts.trace = fileName;
|
|
695
684
|
}
|
|
696
685
|
|
|
697
|
-
await this.page.goto(url, { waitUntil: this.options.waitForNavigation })
|
|
686
|
+
await this.page.goto(url, { waitUntil: this.options.waitForNavigation });
|
|
698
687
|
|
|
699
|
-
const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)))
|
|
688
|
+
const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)));
|
|
700
689
|
|
|
701
690
|
perfTiming = this._extractDataFromPerformanceTiming(
|
|
702
691
|
performanceTiming,
|
|
@@ -704,9 +693,9 @@ class Puppeteer extends Helper {
|
|
|
704
693
|
'domInteractive',
|
|
705
694
|
'domContentLoadedEventEnd',
|
|
706
695
|
'loadEventEnd',
|
|
707
|
-
)
|
|
696
|
+
);
|
|
708
697
|
|
|
709
|
-
return this._waitForAction()
|
|
698
|
+
return this._waitForAction();
|
|
710
699
|
}
|
|
711
700
|
|
|
712
701
|
/**
|
|
@@ -720,11 +709,11 @@ class Puppeteer extends Helper {
|
|
|
720
709
|
*/
|
|
721
710
|
async resizeWindow(width, height) {
|
|
722
711
|
if (width === 'maximize') {
|
|
723
|
-
throw new Error(
|
|
712
|
+
throw new Error('Puppeteer can\'t control windows, so it can\'t maximize it');
|
|
724
713
|
}
|
|
725
714
|
|
|
726
|
-
await this.page.setViewport({ width, height })
|
|
727
|
-
return this._waitForAction()
|
|
715
|
+
await this.page.setViewport({ width, height });
|
|
716
|
+
return this._waitForAction();
|
|
728
717
|
}
|
|
729
718
|
|
|
730
719
|
/**
|
|
@@ -740,9 +729,9 @@ class Puppeteer extends Helper {
|
|
|
740
729
|
*/
|
|
741
730
|
async setPuppeteerRequestHeaders(customHeaders) {
|
|
742
731
|
if (!customHeaders) {
|
|
743
|
-
throw new Error('Cannot send empty headers.')
|
|
732
|
+
throw new Error('Cannot send empty headers.');
|
|
744
733
|
}
|
|
745
|
-
return this.page.setExtraHTTPHeaders(customHeaders)
|
|
734
|
+
return this.page.setExtraHTTPHeaders(customHeaders);
|
|
746
735
|
}
|
|
747
736
|
|
|
748
737
|
/**
|
|
@@ -750,13 +739,13 @@ class Puppeteer extends Helper {
|
|
|
750
739
|
* {{ react }}
|
|
751
740
|
*/
|
|
752
741
|
async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
|
|
753
|
-
const els = await this._locate(locator)
|
|
754
|
-
assertElementExists(els, locator)
|
|
742
|
+
const els = await this._locate(locator);
|
|
743
|
+
assertElementExists(els, locator);
|
|
755
744
|
|
|
756
745
|
// Use manual mouse.move instead of .hover() so the offset can be added to the coordinates
|
|
757
|
-
const { x, y } = await getClickablePoint(els[0])
|
|
758
|
-
await this.page.mouse.move(x + offsetX, y + offsetY)
|
|
759
|
-
return this._waitForAction()
|
|
746
|
+
const { x, y } = await getClickablePoint(els[0]);
|
|
747
|
+
await this.page.mouse.move(x + offsetX, y + offsetY);
|
|
748
|
+
return this._waitForAction();
|
|
760
749
|
}
|
|
761
750
|
|
|
762
751
|
/**
|
|
@@ -764,13 +753,13 @@ class Puppeteer extends Helper {
|
|
|
764
753
|
*
|
|
765
754
|
*/
|
|
766
755
|
async focus(locator) {
|
|
767
|
-
const els = await this._locate(locator)
|
|
768
|
-
assertElementExists(els, locator, 'Element to focus')
|
|
769
|
-
const el = els[0]
|
|
756
|
+
const els = await this._locate(locator);
|
|
757
|
+
assertElementExists(els, locator, 'Element to focus');
|
|
758
|
+
const el = els[0];
|
|
770
759
|
|
|
771
|
-
await el.click()
|
|
772
|
-
await el.focus()
|
|
773
|
-
return this._waitForAction()
|
|
760
|
+
await el.click();
|
|
761
|
+
await el.focus();
|
|
762
|
+
return this._waitForAction();
|
|
774
763
|
}
|
|
775
764
|
|
|
776
765
|
/**
|
|
@@ -778,25 +767,25 @@ class Puppeteer extends Helper {
|
|
|
778
767
|
*
|
|
779
768
|
*/
|
|
780
769
|
async blur(locator) {
|
|
781
|
-
const els = await this._locate(locator)
|
|
782
|
-
assertElementExists(els, locator, 'Element to blur')
|
|
770
|
+
const els = await this._locate(locator);
|
|
771
|
+
assertElementExists(els, locator, 'Element to blur');
|
|
783
772
|
|
|
784
|
-
await blurElement(els[0], this.page)
|
|
785
|
-
return this._waitForAction()
|
|
773
|
+
await blurElement(els[0], this.page);
|
|
774
|
+
return this._waitForAction();
|
|
786
775
|
}
|
|
787
776
|
|
|
788
777
|
/**
|
|
789
778
|
* {{> dragAndDrop }}
|
|
790
779
|
*/
|
|
791
780
|
async dragAndDrop(srcElement, destElement) {
|
|
792
|
-
return proceedDragAndDrop.call(this, srcElement, destElement)
|
|
781
|
+
return proceedDragAndDrop.call(this, srcElement, destElement);
|
|
793
782
|
}
|
|
794
783
|
|
|
795
784
|
/**
|
|
796
785
|
* {{> refreshPage }}
|
|
797
786
|
*/
|
|
798
787
|
async refreshPage() {
|
|
799
|
-
return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation })
|
|
788
|
+
return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation });
|
|
800
789
|
}
|
|
801
790
|
|
|
802
791
|
/**
|
|
@@ -804,8 +793,8 @@ class Puppeteer extends Helper {
|
|
|
804
793
|
*/
|
|
805
794
|
scrollPageToTop() {
|
|
806
795
|
return this.executeScript(() => {
|
|
807
|
-
window.scrollTo(0, 0)
|
|
808
|
-
})
|
|
796
|
+
window.scrollTo(0, 0);
|
|
797
|
+
});
|
|
809
798
|
}
|
|
810
799
|
|
|
811
800
|
/**
|
|
@@ -813,13 +802,16 @@ class Puppeteer extends Helper {
|
|
|
813
802
|
*/
|
|
814
803
|
scrollPageToBottom() {
|
|
815
804
|
return this.executeScript(() => {
|
|
816
|
-
const body = document.body
|
|
817
|
-
const html = document.documentElement
|
|
818
|
-
window.scrollTo(
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
805
|
+
const body = document.body;
|
|
806
|
+
const html = document.documentElement;
|
|
807
|
+
window.scrollTo(0, Math.max(
|
|
808
|
+
body.scrollHeight,
|
|
809
|
+
body.offsetHeight,
|
|
810
|
+
html.clientHeight,
|
|
811
|
+
html.scrollHeight,
|
|
812
|
+
html.offsetHeight,
|
|
813
|
+
));
|
|
814
|
+
});
|
|
823
815
|
}
|
|
824
816
|
|
|
825
817
|
/**
|
|
@@ -827,34 +819,30 @@ class Puppeteer extends Helper {
|
|
|
827
819
|
*/
|
|
828
820
|
async scrollTo(locator, offsetX = 0, offsetY = 0) {
|
|
829
821
|
if (typeof locator === 'number' && typeof offsetX === 'number') {
|
|
830
|
-
offsetY = offsetX
|
|
831
|
-
offsetX = locator
|
|
832
|
-
locator = null
|
|
822
|
+
offsetY = offsetX;
|
|
823
|
+
offsetX = locator;
|
|
824
|
+
locator = null;
|
|
833
825
|
}
|
|
834
826
|
|
|
835
827
|
if (locator) {
|
|
836
|
-
const els = await this._locate(locator)
|
|
837
|
-
assertElementExists(els, locator, 'Element')
|
|
838
|
-
const el = els[0]
|
|
839
|
-
await el.evaluate((el) => el.scrollIntoView())
|
|
840
|
-
const elementCoordinates = await getClickablePoint(els[0])
|
|
841
|
-
await this.executeScript(
|
|
842
|
-
(x, y) => window.scrollBy(x, y),
|
|
843
|
-
elementCoordinates.x + offsetX,
|
|
844
|
-
elementCoordinates.y + offsetY,
|
|
845
|
-
)
|
|
828
|
+
const els = await this._locate(locator);
|
|
829
|
+
assertElementExists(els, locator, 'Element');
|
|
830
|
+
const el = els[0];
|
|
831
|
+
await el.evaluate((el) => el.scrollIntoView());
|
|
832
|
+
const elementCoordinates = await getClickablePoint(els[0]);
|
|
833
|
+
await this.executeScript((x, y) => window.scrollBy(x, y), elementCoordinates.x + offsetX, elementCoordinates.y + offsetY);
|
|
846
834
|
} else {
|
|
847
|
-
await this.executeScript((x, y) => window.scrollTo(x, y), offsetX, offsetY)
|
|
835
|
+
await this.executeScript((x, y) => window.scrollTo(x, y), offsetX, offsetY);
|
|
848
836
|
}
|
|
849
|
-
return this._waitForAction()
|
|
837
|
+
return this._waitForAction();
|
|
850
838
|
}
|
|
851
839
|
|
|
852
840
|
/**
|
|
853
841
|
* {{> seeInTitle }}
|
|
854
842
|
*/
|
|
855
843
|
async seeInTitle(text) {
|
|
856
|
-
const title = await this.page.title()
|
|
857
|
-
stringIncludes('web page title').assert(text, title)
|
|
844
|
+
const title = await this.page.title();
|
|
845
|
+
stringIncludes('web page title').assert(text, title);
|
|
858
846
|
}
|
|
859
847
|
|
|
860
848
|
/**
|
|
@@ -865,34 +853,34 @@ class Puppeteer extends Helper {
|
|
|
865
853
|
function getScrollPosition() {
|
|
866
854
|
return {
|
|
867
855
|
x: window.pageXOffset,
|
|
868
|
-
y: window.pageYOffset
|
|
869
|
-
}
|
|
856
|
+
y: window.pageYOffset
|
|
857
|
+
};
|
|
870
858
|
}
|
|
871
859
|
/* eslint-enable comma-dangle */
|
|
872
|
-
return this.executeScript(getScrollPosition)
|
|
860
|
+
return this.executeScript(getScrollPosition);
|
|
873
861
|
}
|
|
874
862
|
|
|
875
863
|
/**
|
|
876
864
|
* {{> seeTitleEquals }}
|
|
877
865
|
*/
|
|
878
866
|
async seeTitleEquals(text) {
|
|
879
|
-
const title = await this.page.title()
|
|
880
|
-
return equals('web page title').assert(title, text)
|
|
867
|
+
const title = await this.page.title();
|
|
868
|
+
return equals('web page title').assert(title, text);
|
|
881
869
|
}
|
|
882
870
|
|
|
883
871
|
/**
|
|
884
872
|
* {{> dontSeeInTitle }}
|
|
885
873
|
*/
|
|
886
874
|
async dontSeeInTitle(text) {
|
|
887
|
-
const title = await this.page.title()
|
|
888
|
-
stringIncludes('web page title').negate(text, title)
|
|
875
|
+
const title = await this.page.title();
|
|
876
|
+
stringIncludes('web page title').negate(text, title);
|
|
889
877
|
}
|
|
890
878
|
|
|
891
879
|
/**
|
|
892
880
|
* {{> grabTitle }}
|
|
893
881
|
*/
|
|
894
882
|
async grabTitle() {
|
|
895
|
-
return this.page.title()
|
|
883
|
+
return this.page.title();
|
|
896
884
|
}
|
|
897
885
|
|
|
898
886
|
/**
|
|
@@ -906,8 +894,8 @@ class Puppeteer extends Helper {
|
|
|
906
894
|
* {{ react }}
|
|
907
895
|
*/
|
|
908
896
|
async _locate(locator) {
|
|
909
|
-
const context = await this.context
|
|
910
|
-
return findElements.call(this, context, locator)
|
|
897
|
+
const context = await this.context;
|
|
898
|
+
return findElements.call(this, context, locator);
|
|
911
899
|
}
|
|
912
900
|
|
|
913
901
|
/**
|
|
@@ -919,10 +907,10 @@ class Puppeteer extends Helper {
|
|
|
919
907
|
* ```
|
|
920
908
|
*/
|
|
921
909
|
async _locateCheckable(locator, providedContext = null) {
|
|
922
|
-
const context = providedContext || (await this._getContext())
|
|
923
|
-
const els = await findCheckable.call(this, locator, context)
|
|
924
|
-
assertElementExists(els[0], locator, 'Checkbox or radio')
|
|
925
|
-
return els[0]
|
|
910
|
+
const context = providedContext || (await this._getContext());
|
|
911
|
+
const els = await findCheckable.call(this, locator, context);
|
|
912
|
+
assertElementExists(els[0], locator, 'Checkbox or radio');
|
|
913
|
+
return els[0];
|
|
926
914
|
}
|
|
927
915
|
|
|
928
916
|
/**
|
|
@@ -933,8 +921,8 @@ class Puppeteer extends Helper {
|
|
|
933
921
|
* ```
|
|
934
922
|
*/
|
|
935
923
|
async _locateClickable(locator) {
|
|
936
|
-
const context = await this.context
|
|
937
|
-
return findClickable.call(this, context, locator)
|
|
924
|
+
const context = await this.context;
|
|
925
|
+
return findClickable.call(this, context, locator);
|
|
938
926
|
}
|
|
939
927
|
|
|
940
928
|
/**
|
|
@@ -945,7 +933,7 @@ class Puppeteer extends Helper {
|
|
|
945
933
|
* ```
|
|
946
934
|
*/
|
|
947
935
|
async _locateFields(locator) {
|
|
948
|
-
return findFields.call(this, locator)
|
|
936
|
+
return findFields.call(this, locator);
|
|
949
937
|
}
|
|
950
938
|
|
|
951
939
|
/**
|
|
@@ -953,7 +941,7 @@ class Puppeteer extends Helper {
|
|
|
953
941
|
*
|
|
954
942
|
*/
|
|
955
943
|
async grabWebElements(locator) {
|
|
956
|
-
return this._locate(locator)
|
|
944
|
+
return this._locate(locator);
|
|
957
945
|
}
|
|
958
946
|
|
|
959
947
|
/**
|
|
@@ -967,17 +955,17 @@ class Puppeteer extends Helper {
|
|
|
967
955
|
* @param {number} [num=1]
|
|
968
956
|
*/
|
|
969
957
|
async switchToNextTab(num = 1) {
|
|
970
|
-
const pages = await this.browser.pages()
|
|
971
|
-
const index = pages.indexOf(this.page)
|
|
972
|
-
this.withinLocator = null
|
|
973
|
-
const page = pages[index + num]
|
|
958
|
+
const pages = await this.browser.pages();
|
|
959
|
+
const index = pages.indexOf(this.page);
|
|
960
|
+
this.withinLocator = null;
|
|
961
|
+
const page = pages[index + num];
|
|
974
962
|
|
|
975
963
|
if (!page) {
|
|
976
|
-
throw new Error(`There is no ability to switch to next tab with offset ${num}`)
|
|
964
|
+
throw new Error(`There is no ability to switch to next tab with offset ${num}`);
|
|
977
965
|
}
|
|
978
966
|
|
|
979
|
-
await this._setPage(page)
|
|
980
|
-
return this._waitForAction()
|
|
967
|
+
await this._setPage(page);
|
|
968
|
+
return this._waitForAction();
|
|
981
969
|
}
|
|
982
970
|
|
|
983
971
|
/**
|
|
@@ -990,17 +978,17 @@ class Puppeteer extends Helper {
|
|
|
990
978
|
* @param {number} [num=1]
|
|
991
979
|
*/
|
|
992
980
|
async switchToPreviousTab(num = 1) {
|
|
993
|
-
const pages = await this.browser.pages()
|
|
994
|
-
const index = pages.indexOf(this.page)
|
|
995
|
-
this.withinLocator = null
|
|
996
|
-
const page = pages[index - num]
|
|
981
|
+
const pages = await this.browser.pages();
|
|
982
|
+
const index = pages.indexOf(this.page);
|
|
983
|
+
this.withinLocator = null;
|
|
984
|
+
const page = pages[index - num];
|
|
997
985
|
|
|
998
986
|
if (!page) {
|
|
999
|
-
throw new Error(`There is no ability to switch to previous tab with offset ${num}`)
|
|
987
|
+
throw new Error(`There is no ability to switch to previous tab with offset ${num}`);
|
|
1000
988
|
}
|
|
1001
989
|
|
|
1002
|
-
await this._setPage(page)
|
|
1003
|
-
return this._waitForAction()
|
|
990
|
+
await this._setPage(page);
|
|
991
|
+
return this._waitForAction();
|
|
1004
992
|
}
|
|
1005
993
|
|
|
1006
994
|
/**
|
|
@@ -1011,10 +999,10 @@ class Puppeteer extends Helper {
|
|
|
1011
999
|
* ```
|
|
1012
1000
|
*/
|
|
1013
1001
|
async closeCurrentTab() {
|
|
1014
|
-
const oldPage = this.page
|
|
1015
|
-
await this.switchToPreviousTab()
|
|
1016
|
-
await oldPage.close()
|
|
1017
|
-
return this._waitForAction()
|
|
1002
|
+
const oldPage = this.page;
|
|
1003
|
+
await this.switchToPreviousTab();
|
|
1004
|
+
await oldPage.close();
|
|
1005
|
+
return this._waitForAction();
|
|
1018
1006
|
}
|
|
1019
1007
|
|
|
1020
1008
|
/**
|
|
@@ -1025,15 +1013,15 @@ class Puppeteer extends Helper {
|
|
|
1025
1013
|
* ```
|
|
1026
1014
|
*/
|
|
1027
1015
|
async closeOtherTabs() {
|
|
1028
|
-
const pages = await this.browser.pages()
|
|
1029
|
-
const otherPages = pages.filter(
|
|
1016
|
+
const pages = await this.browser.pages();
|
|
1017
|
+
const otherPages = pages.filter(page => page !== this.page);
|
|
1030
1018
|
|
|
1031
|
-
let p = Promise.resolve()
|
|
1019
|
+
let p = Promise.resolve();
|
|
1032
1020
|
otherPages.forEach((page) => {
|
|
1033
|
-
p = p.then(() => page.close())
|
|
1034
|
-
})
|
|
1035
|
-
await p
|
|
1036
|
-
return this._waitForAction()
|
|
1021
|
+
p = p.then(() => page.close());
|
|
1022
|
+
});
|
|
1023
|
+
await p;
|
|
1024
|
+
return this._waitForAction();
|
|
1037
1025
|
}
|
|
1038
1026
|
|
|
1039
1027
|
/**
|
|
@@ -1044,16 +1032,16 @@ class Puppeteer extends Helper {
|
|
|
1044
1032
|
* ```
|
|
1045
1033
|
*/
|
|
1046
1034
|
async openNewTab() {
|
|
1047
|
-
await this._setPage(await this.browser.newPage())
|
|
1048
|
-
return this._waitForAction()
|
|
1035
|
+
await this._setPage(await this.browser.newPage());
|
|
1036
|
+
return this._waitForAction();
|
|
1049
1037
|
}
|
|
1050
1038
|
|
|
1051
1039
|
/**
|
|
1052
1040
|
* {{> grabNumberOfOpenTabs }}
|
|
1053
1041
|
*/
|
|
1054
1042
|
async grabNumberOfOpenTabs() {
|
|
1055
|
-
const pages = await this.browser.pages()
|
|
1056
|
-
return pages.length
|
|
1043
|
+
const pages = await this.browser.pages();
|
|
1044
|
+
return pages.length;
|
|
1057
1045
|
}
|
|
1058
1046
|
|
|
1059
1047
|
/**
|
|
@@ -1061,22 +1049,14 @@ class Puppeteer extends Helper {
|
|
|
1061
1049
|
* {{ react }}
|
|
1062
1050
|
*/
|
|
1063
1051
|
async seeElement(locator) {
|
|
1064
|
-
let els = await this._locate(locator)
|
|
1065
|
-
els = (await Promise.all(els.map(
|
|
1052
|
+
let els = await this._locate(locator);
|
|
1053
|
+
els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v);
|
|
1066
1054
|
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
|
|
1067
|
-
els = await Promise.all(
|
|
1068
|
-
els.map(
|
|
1069
|
-
async (el) =>
|
|
1070
|
-
(await el.evaluate(
|
|
1071
|
-
(node) =>
|
|
1072
|
-
window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none',
|
|
1073
|
-
)) && el,
|
|
1074
|
-
),
|
|
1075
|
-
)
|
|
1055
|
+
els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el));
|
|
1076
1056
|
try {
|
|
1077
|
-
return empty('visible elements').negate(els.filter(
|
|
1057
|
+
return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'));
|
|
1078
1058
|
} catch (e) {
|
|
1079
|
-
dontSeeElementError(locator)
|
|
1059
|
+
dontSeeElementError(locator);
|
|
1080
1060
|
}
|
|
1081
1061
|
}
|
|
1082
1062
|
|
|
@@ -1085,22 +1065,14 @@ class Puppeteer extends Helper {
|
|
|
1085
1065
|
* {{ react }}
|
|
1086
1066
|
*/
|
|
1087
1067
|
async dontSeeElement(locator) {
|
|
1088
|
-
let els = await this._locate(locator)
|
|
1089
|
-
els = (await Promise.all(els.map(
|
|
1068
|
+
let els = await this._locate(locator);
|
|
1069
|
+
els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v);
|
|
1090
1070
|
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
|
|
1091
|
-
els = await Promise.all(
|
|
1092
|
-
els.map(
|
|
1093
|
-
async (el) =>
|
|
1094
|
-
(await el.evaluate(
|
|
1095
|
-
(node) =>
|
|
1096
|
-
window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none',
|
|
1097
|
-
)) && el,
|
|
1098
|
-
),
|
|
1099
|
-
)
|
|
1071
|
+
els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el));
|
|
1100
1072
|
try {
|
|
1101
|
-
return empty('visible elements').assert(els.filter(
|
|
1073
|
+
return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'));
|
|
1102
1074
|
} catch (e) {
|
|
1103
|
-
seeElementError(locator)
|
|
1075
|
+
seeElementError(locator);
|
|
1104
1076
|
}
|
|
1105
1077
|
}
|
|
1106
1078
|
|
|
@@ -1108,11 +1080,11 @@ class Puppeteer extends Helper {
|
|
|
1108
1080
|
* {{> seeElementInDOM }}
|
|
1109
1081
|
*/
|
|
1110
1082
|
async seeElementInDOM(locator) {
|
|
1111
|
-
const els = await this._locate(locator)
|
|
1083
|
+
const els = await this._locate(locator);
|
|
1112
1084
|
try {
|
|
1113
|
-
return empty('elements on page').negate(els.filter(
|
|
1085
|
+
return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'));
|
|
1114
1086
|
} catch (e) {
|
|
1115
|
-
dontSeeElementInDOMError(locator)
|
|
1087
|
+
dontSeeElementInDOMError(locator);
|
|
1116
1088
|
}
|
|
1117
1089
|
}
|
|
1118
1090
|
|
|
@@ -1120,11 +1092,11 @@ class Puppeteer extends Helper {
|
|
|
1120
1092
|
* {{> dontSeeElementInDOM }}
|
|
1121
1093
|
*/
|
|
1122
1094
|
async dontSeeElementInDOM(locator) {
|
|
1123
|
-
const els = await this._locate(locator)
|
|
1095
|
+
const els = await this._locate(locator);
|
|
1124
1096
|
try {
|
|
1125
|
-
return empty('elements on a page').assert(els.filter(
|
|
1097
|
+
return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'));
|
|
1126
1098
|
} catch (e) {
|
|
1127
|
-
seeElementInDOMError(locator)
|
|
1099
|
+
seeElementInDOMError(locator);
|
|
1128
1100
|
}
|
|
1129
1101
|
}
|
|
1130
1102
|
|
|
@@ -1134,7 +1106,7 @@ class Puppeteer extends Helper {
|
|
|
1134
1106
|
* {{ react }}
|
|
1135
1107
|
*/
|
|
1136
1108
|
async click(locator, context = null) {
|
|
1137
|
-
return proceedClick.call(this, locator, context)
|
|
1109
|
+
return proceedClick.call(this, locator, context);
|
|
1138
1110
|
}
|
|
1139
1111
|
|
|
1140
1112
|
/**
|
|
@@ -1143,33 +1115,28 @@ class Puppeteer extends Helper {
|
|
|
1143
1115
|
* {{ react }}
|
|
1144
1116
|
*/
|
|
1145
1117
|
async forceClick(locator, context = null) {
|
|
1146
|
-
let matcher = await this.context
|
|
1118
|
+
let matcher = await this.context;
|
|
1147
1119
|
if (context) {
|
|
1148
|
-
const els = await this._locate(context)
|
|
1149
|
-
assertElementExists(els, context)
|
|
1150
|
-
matcher = els[0]
|
|
1120
|
+
const els = await this._locate(context);
|
|
1121
|
+
assertElementExists(els, context);
|
|
1122
|
+
matcher = els[0];
|
|
1151
1123
|
}
|
|
1152
1124
|
|
|
1153
|
-
const els = await findClickable.call(this, matcher, locator)
|
|
1125
|
+
const els = await findClickable.call(this, matcher, locator);
|
|
1154
1126
|
if (context) {
|
|
1155
|
-
assertElementExists(
|
|
1156
|
-
els,
|
|
1157
|
-
locator,
|
|
1158
|
-
'Clickable element',
|
|
1159
|
-
`was not found inside element ${new Locator(context).toString()}`,
|
|
1160
|
-
)
|
|
1127
|
+
assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`);
|
|
1161
1128
|
} else {
|
|
1162
|
-
assertElementExists(els, locator, 'Clickable element')
|
|
1129
|
+
assertElementExists(els, locator, 'Clickable element');
|
|
1163
1130
|
}
|
|
1164
|
-
const elem = els[0]
|
|
1131
|
+
const elem = els[0];
|
|
1165
1132
|
return this.executeScript((el) => {
|
|
1166
1133
|
if (document.activeElement instanceof HTMLElement) {
|
|
1167
|
-
document.activeElement.blur()
|
|
1134
|
+
document.activeElement.blur();
|
|
1168
1135
|
}
|
|
1169
|
-
const event = document.createEvent('MouseEvent')
|
|
1170
|
-
event.initEvent('click', true, true)
|
|
1171
|
-
return el.dispatchEvent(event)
|
|
1172
|
-
}, elem)
|
|
1136
|
+
const event = document.createEvent('MouseEvent');
|
|
1137
|
+
event.initEvent('click', true, true);
|
|
1138
|
+
return el.dispatchEvent(event);
|
|
1139
|
+
}, elem);
|
|
1173
1140
|
}
|
|
1174
1141
|
|
|
1175
1142
|
/**
|
|
@@ -1178,7 +1145,7 @@ class Puppeteer extends Helper {
|
|
|
1178
1145
|
* {{ react }}
|
|
1179
1146
|
*/
|
|
1180
1147
|
async clickLink(locator, context = null) {
|
|
1181
|
-
return proceedClick.call(this, locator, context, { waitForNavigation: true })
|
|
1148
|
+
return proceedClick.call(this, locator, context, { waitForNavigation: true });
|
|
1182
1149
|
}
|
|
1183
1150
|
|
|
1184
1151
|
/**
|
|
@@ -1199,16 +1166,16 @@ class Puppeteer extends Helper {
|
|
|
1199
1166
|
* @param {string} [downloadPath='downloads'] change this parameter to set another directory for saving
|
|
1200
1167
|
*/
|
|
1201
1168
|
async handleDownloads(downloadPath = 'downloads') {
|
|
1202
|
-
downloadPath = path.join(global.output_dir, downloadPath)
|
|
1169
|
+
downloadPath = path.join(global.output_dir, downloadPath);
|
|
1203
1170
|
if (!fs.existsSync(downloadPath)) {
|
|
1204
|
-
fs.mkdirSync(downloadPath, '0777')
|
|
1171
|
+
fs.mkdirSync(downloadPath, '0777');
|
|
1205
1172
|
}
|
|
1206
|
-
fsExtra.emptyDirSync(downloadPath)
|
|
1173
|
+
fsExtra.emptyDirSync(downloadPath);
|
|
1207
1174
|
|
|
1208
1175
|
try {
|
|
1209
|
-
return this.page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath })
|
|
1176
|
+
return this.page._client.send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath });
|
|
1210
1177
|
} catch (e) {
|
|
1211
|
-
return this.page._client().send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath })
|
|
1178
|
+
return this.page._client().send('Page.setDownloadBehavior', { behavior: 'allow', downloadPath });
|
|
1212
1179
|
}
|
|
1213
1180
|
}
|
|
1214
1181
|
|
|
@@ -1218,27 +1185,27 @@ class Puppeteer extends Helper {
|
|
|
1218
1185
|
* Please use `handleDownloads()` instead.
|
|
1219
1186
|
*/
|
|
1220
1187
|
async downloadFile(locator, customName) {
|
|
1221
|
-
let fileName
|
|
1222
|
-
await this.page.setRequestInterception(true)
|
|
1188
|
+
let fileName;
|
|
1189
|
+
await this.page.setRequestInterception(true);
|
|
1223
1190
|
|
|
1224
1191
|
const xRequest = await new Promise((resolve) => {
|
|
1225
1192
|
this.page.on('request', (request) => {
|
|
1226
|
-
console.log('rq', request, customName)
|
|
1227
|
-
const grabbedFileName = request.url().split('/')[request.url().split('/').length - 1]
|
|
1228
|
-
const fileExtension = request.url().split('/')[request.url().split('/').length - 1].split('.')[1]
|
|
1229
|
-
console.log('nm', customName, fileExtension)
|
|
1193
|
+
console.log('rq', request, customName);
|
|
1194
|
+
const grabbedFileName = request.url().split('/')[request.url().split('/').length - 1];
|
|
1195
|
+
const fileExtension = request.url().split('/')[request.url().split('/').length - 1].split('.')[1];
|
|
1196
|
+
console.log('nm', customName, fileExtension);
|
|
1230
1197
|
if (customName && path.extname(customName) !== fileExtension) {
|
|
1231
|
-
console.log('bypassing a request')
|
|
1232
|
-
request.continue()
|
|
1233
|
-
return
|
|
1198
|
+
console.log('bypassing a request');
|
|
1199
|
+
request.continue();
|
|
1200
|
+
return;
|
|
1234
1201
|
}
|
|
1235
|
-
customName ?
|
|
1236
|
-
request.abort()
|
|
1237
|
-
resolve(request)
|
|
1238
|
-
})
|
|
1239
|
-
})
|
|
1202
|
+
customName ? fileName = `${customName}.${fileExtension}` : fileName = grabbedFileName;
|
|
1203
|
+
request.abort();
|
|
1204
|
+
resolve(request);
|
|
1205
|
+
});
|
|
1206
|
+
});
|
|
1240
1207
|
|
|
1241
|
-
await this.click(locator)
|
|
1208
|
+
await this.click(locator);
|
|
1242
1209
|
|
|
1243
1210
|
const options = {
|
|
1244
1211
|
encoding: null,
|
|
@@ -1246,10 +1213,10 @@ class Puppeteer extends Helper {
|
|
|
1246
1213
|
uri: xRequest._url,
|
|
1247
1214
|
body: xRequest._postData,
|
|
1248
1215
|
headers: xRequest._headers,
|
|
1249
|
-
}
|
|
1216
|
+
};
|
|
1250
1217
|
|
|
1251
|
-
const cookies = await this.page.cookies()
|
|
1252
|
-
options.headers.Cookie = cookies.map(
|
|
1218
|
+
const cookies = await this.page.cookies();
|
|
1219
|
+
options.headers.Cookie = cookies.map(ck => `${ck.name}=${ck.value}`).join(';');
|
|
1253
1220
|
|
|
1254
1221
|
const response = await axios({
|
|
1255
1222
|
method: options.method,
|
|
@@ -1257,26 +1224,24 @@ class Puppeteer extends Helper {
|
|
|
1257
1224
|
headers: options.headers,
|
|
1258
1225
|
responseType: 'arraybuffer',
|
|
1259
1226
|
onDownloadProgress(e) {
|
|
1260
|
-
console.log('+', e)
|
|
1227
|
+
console.log('+', e);
|
|
1261
1228
|
},
|
|
1262
|
-
})
|
|
1229
|
+
});
|
|
1263
1230
|
|
|
1264
|
-
const outputFile = path.join(`${global.output_dir}/${fileName}`)
|
|
1231
|
+
const outputFile = path.join(`${global.output_dir}/${fileName}`);
|
|
1265
1232
|
|
|
1266
1233
|
try {
|
|
1267
1234
|
await new Promise((resolve, reject) => {
|
|
1268
|
-
const wstream = fs.createWriteStream(outputFile)
|
|
1269
|
-
console.log(response)
|
|
1270
|
-
wstream.write(response.data)
|
|
1271
|
-
wstream.end()
|
|
1272
|
-
this.debug(`File is downloaded in ${outputFile}`)
|
|
1273
|
-
wstream.on('finish', () => {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
wstream.on('error', reject)
|
|
1277
|
-
})
|
|
1235
|
+
const wstream = fs.createWriteStream(outputFile);
|
|
1236
|
+
console.log(response);
|
|
1237
|
+
wstream.write(response.data);
|
|
1238
|
+
wstream.end();
|
|
1239
|
+
this.debug(`File is downloaded in ${outputFile}`);
|
|
1240
|
+
wstream.on('finish', () => { resolve(fileName); });
|
|
1241
|
+
wstream.on('error', reject);
|
|
1242
|
+
});
|
|
1278
1243
|
} catch (error) {
|
|
1279
|
-
throw new Error(`There is something wrong with downloaded file. ${error}`)
|
|
1244
|
+
throw new Error(`There is something wrong with downloaded file. ${error}`);
|
|
1280
1245
|
}
|
|
1281
1246
|
}
|
|
1282
1247
|
|
|
@@ -1286,7 +1251,7 @@ class Puppeteer extends Helper {
|
|
|
1286
1251
|
* {{ react }}
|
|
1287
1252
|
*/
|
|
1288
1253
|
async doubleClick(locator, context = null) {
|
|
1289
|
-
return proceedClick.call(this, locator, context, { clickCount: 2 })
|
|
1254
|
+
return proceedClick.call(this, locator, context, { clickCount: 2 });
|
|
1290
1255
|
}
|
|
1291
1256
|
|
|
1292
1257
|
/**
|
|
@@ -1295,19 +1260,20 @@ class Puppeteer extends Helper {
|
|
|
1295
1260
|
* {{ react }}
|
|
1296
1261
|
*/
|
|
1297
1262
|
async rightClick(locator, context = null) {
|
|
1298
|
-
return proceedClick.call(this, locator, context, { button: 'right' })
|
|
1263
|
+
return proceedClick.call(this, locator, context, { button: 'right' });
|
|
1299
1264
|
}
|
|
1300
1265
|
|
|
1301
1266
|
/**
|
|
1302
1267
|
* {{> checkOption }}
|
|
1303
1268
|
*/
|
|
1304
1269
|
async checkOption(field, context = null) {
|
|
1305
|
-
const elm = await this._locateCheckable(field, context)
|
|
1306
|
-
const curentlyChecked = await elm.getProperty('checked')
|
|
1270
|
+
const elm = await this._locateCheckable(field, context);
|
|
1271
|
+
const curentlyChecked = await elm.getProperty('checked')
|
|
1272
|
+
.then(checkedProperty => checkedProperty.jsonValue());
|
|
1307
1273
|
// Only check if NOT currently checked
|
|
1308
1274
|
if (!curentlyChecked) {
|
|
1309
|
-
await elm.click()
|
|
1310
|
-
return this._waitForAction()
|
|
1275
|
+
await elm.click();
|
|
1276
|
+
return this._waitForAction();
|
|
1311
1277
|
}
|
|
1312
1278
|
}
|
|
1313
1279
|
|
|
@@ -1315,12 +1281,13 @@ class Puppeteer extends Helper {
|
|
|
1315
1281
|
* {{> uncheckOption }}
|
|
1316
1282
|
*/
|
|
1317
1283
|
async uncheckOption(field, context = null) {
|
|
1318
|
-
const elm = await this._locateCheckable(field, context)
|
|
1319
|
-
const curentlyChecked = await elm.getProperty('checked')
|
|
1284
|
+
const elm = await this._locateCheckable(field, context);
|
|
1285
|
+
const curentlyChecked = await elm.getProperty('checked')
|
|
1286
|
+
.then(checkedProperty => checkedProperty.jsonValue());
|
|
1320
1287
|
// Only uncheck if currently checked
|
|
1321
1288
|
if (curentlyChecked) {
|
|
1322
|
-
await elm.click()
|
|
1323
|
-
return this._waitForAction()
|
|
1289
|
+
await elm.click();
|
|
1290
|
+
return this._waitForAction();
|
|
1324
1291
|
}
|
|
1325
1292
|
}
|
|
1326
1293
|
|
|
@@ -1328,32 +1295,32 @@ class Puppeteer extends Helper {
|
|
|
1328
1295
|
* {{> seeCheckboxIsChecked }}
|
|
1329
1296
|
*/
|
|
1330
1297
|
async seeCheckboxIsChecked(field) {
|
|
1331
|
-
return proceedIsChecked.call(this, 'assert', field)
|
|
1298
|
+
return proceedIsChecked.call(this, 'assert', field);
|
|
1332
1299
|
}
|
|
1333
1300
|
|
|
1334
1301
|
/**
|
|
1335
1302
|
* {{> dontSeeCheckboxIsChecked }}
|
|
1336
1303
|
*/
|
|
1337
1304
|
async dontSeeCheckboxIsChecked(field) {
|
|
1338
|
-
return proceedIsChecked.call(this, 'negate', field)
|
|
1305
|
+
return proceedIsChecked.call(this, 'negate', field);
|
|
1339
1306
|
}
|
|
1340
1307
|
|
|
1341
1308
|
/**
|
|
1342
1309
|
* {{> pressKeyDown }}
|
|
1343
1310
|
*/
|
|
1344
1311
|
async pressKeyDown(key) {
|
|
1345
|
-
key = getNormalizedKey.call(this, key)
|
|
1346
|
-
await this.page.keyboard.down(key)
|
|
1347
|
-
return this._waitForAction()
|
|
1312
|
+
key = getNormalizedKey.call(this, key);
|
|
1313
|
+
await this.page.keyboard.down(key);
|
|
1314
|
+
return this._waitForAction();
|
|
1348
1315
|
}
|
|
1349
1316
|
|
|
1350
1317
|
/**
|
|
1351
1318
|
* {{> pressKeyUp }}
|
|
1352
1319
|
*/
|
|
1353
1320
|
async pressKeyUp(key) {
|
|
1354
|
-
key = getNormalizedKey.call(this, key)
|
|
1355
|
-
await this.page.keyboard.up(key)
|
|
1356
|
-
return this._waitForAction()
|
|
1321
|
+
key = getNormalizedKey.call(this, key);
|
|
1322
|
+
await this.page.keyboard.up(key);
|
|
1323
|
+
return this._waitForAction();
|
|
1357
1324
|
}
|
|
1358
1325
|
|
|
1359
1326
|
/**
|
|
@@ -1362,28 +1329,28 @@ class Puppeteer extends Helper {
|
|
|
1362
1329
|
* {{> pressKeyWithKeyNormalization }}
|
|
1363
1330
|
*/
|
|
1364
1331
|
async pressKey(key) {
|
|
1365
|
-
const modifiers = []
|
|
1332
|
+
const modifiers = [];
|
|
1366
1333
|
if (Array.isArray(key)) {
|
|
1367
1334
|
for (let k of key) {
|
|
1368
|
-
k = getNormalizedKey.call(this, k)
|
|
1335
|
+
k = getNormalizedKey.call(this, k);
|
|
1369
1336
|
if (isModifierKey(k)) {
|
|
1370
|
-
modifiers.push(k)
|
|
1337
|
+
modifiers.push(k);
|
|
1371
1338
|
} else {
|
|
1372
|
-
key = k
|
|
1373
|
-
break
|
|
1339
|
+
key = k;
|
|
1340
|
+
break;
|
|
1374
1341
|
}
|
|
1375
1342
|
}
|
|
1376
1343
|
} else {
|
|
1377
|
-
key = getNormalizedKey.call(this, key)
|
|
1344
|
+
key = getNormalizedKey.call(this, key);
|
|
1378
1345
|
}
|
|
1379
1346
|
for (const modifier of modifiers) {
|
|
1380
|
-
await this.page.keyboard.down(modifier)
|
|
1347
|
+
await this.page.keyboard.down(modifier);
|
|
1381
1348
|
}
|
|
1382
|
-
await this.page.keyboard.press(key)
|
|
1349
|
+
await this.page.keyboard.press(key);
|
|
1383
1350
|
for (const modifier of modifiers) {
|
|
1384
|
-
await this.page.keyboard.up(modifier)
|
|
1351
|
+
await this.page.keyboard.up(modifier);
|
|
1385
1352
|
}
|
|
1386
|
-
return this._waitForAction()
|
|
1353
|
+
return this._waitForAction();
|
|
1387
1354
|
}
|
|
1388
1355
|
|
|
1389
1356
|
/**
|
|
@@ -1391,13 +1358,13 @@ class Puppeteer extends Helper {
|
|
|
1391
1358
|
*/
|
|
1392
1359
|
async type(keys, delay = null) {
|
|
1393
1360
|
if (!Array.isArray(keys)) {
|
|
1394
|
-
keys = keys.toString()
|
|
1395
|
-
keys = keys.split('')
|
|
1361
|
+
keys = keys.toString();
|
|
1362
|
+
keys = keys.split('');
|
|
1396
1363
|
}
|
|
1397
1364
|
|
|
1398
1365
|
for (const key of keys) {
|
|
1399
|
-
await this.page.keyboard.press(key)
|
|
1400
|
-
if (delay) await this.wait(delay / 1000)
|
|
1366
|
+
await this.page.keyboard.press(key);
|
|
1367
|
+
if (delay) await this.wait(delay / 1000);
|
|
1401
1368
|
}
|
|
1402
1369
|
}
|
|
1403
1370
|
|
|
@@ -1406,28 +1373,28 @@ class Puppeteer extends Helper {
|
|
|
1406
1373
|
* {{ react }}
|
|
1407
1374
|
*/
|
|
1408
1375
|
async fillField(field, value) {
|
|
1409
|
-
const els = await findVisibleFields.call(this, field)
|
|
1410
|
-
assertElementExists(els, field, 'Field')
|
|
1411
|
-
const el = els[0]
|
|
1412
|
-
const tag = await el.getProperty('tagName').then(
|
|
1413
|
-
const editable = await el.getProperty('contenteditable').then(
|
|
1376
|
+
const els = await findVisibleFields.call(this, field);
|
|
1377
|
+
assertElementExists(els, field, 'Field');
|
|
1378
|
+
const el = els[0];
|
|
1379
|
+
const tag = await el.getProperty('tagName').then(el => el.jsonValue());
|
|
1380
|
+
const editable = await el.getProperty('contenteditable').then(el => el.jsonValue());
|
|
1414
1381
|
if (tag === 'INPUT' || tag === 'TEXTAREA') {
|
|
1415
|
-
await this._evaluateHandeInContext(
|
|
1382
|
+
await this._evaluateHandeInContext(el => el.value = '', el);
|
|
1416
1383
|
} else if (editable) {
|
|
1417
|
-
await this._evaluateHandeInContext(
|
|
1384
|
+
await this._evaluateHandeInContext(el => el.innerHTML = '', el);
|
|
1418
1385
|
}
|
|
1419
1386
|
|
|
1420
|
-
highlightActiveElement.call(this, el, await this._getContext())
|
|
1421
|
-
await el.type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1387
|
+
highlightActiveElement.call(this, el, await this._getContext());
|
|
1388
|
+
await el.type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1422
1389
|
|
|
1423
|
-
return this._waitForAction()
|
|
1390
|
+
return this._waitForAction();
|
|
1424
1391
|
}
|
|
1425
1392
|
|
|
1426
1393
|
/**
|
|
1427
1394
|
* {{> clearField }}
|
|
1428
1395
|
*/
|
|
1429
1396
|
async clearField(field) {
|
|
1430
|
-
return this.fillField(field, '')
|
|
1397
|
+
return this.fillField(field, '');
|
|
1431
1398
|
}
|
|
1432
1399
|
|
|
1433
1400
|
/**
|
|
@@ -1436,28 +1403,28 @@ class Puppeteer extends Helper {
|
|
|
1436
1403
|
* {{ react }}
|
|
1437
1404
|
*/
|
|
1438
1405
|
async appendField(field, value) {
|
|
1439
|
-
const els = await findVisibleFields.call(this, field)
|
|
1440
|
-
assertElementExists(els, field, 'Field')
|
|
1441
|
-
highlightActiveElement.call(this, els[0], await this._getContext())
|
|
1442
|
-
await els[0].press('End')
|
|
1443
|
-
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1444
|
-
return this._waitForAction()
|
|
1406
|
+
const els = await findVisibleFields.call(this, field);
|
|
1407
|
+
assertElementExists(els, field, 'Field');
|
|
1408
|
+
highlightActiveElement.call(this, els[0], await this._getContext());
|
|
1409
|
+
await els[0].press('End');
|
|
1410
|
+
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay });
|
|
1411
|
+
return this._waitForAction();
|
|
1445
1412
|
}
|
|
1446
1413
|
|
|
1447
1414
|
/**
|
|
1448
1415
|
* {{> seeInField }}
|
|
1449
1416
|
*/
|
|
1450
1417
|
async seeInField(field, value) {
|
|
1451
|
-
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1452
|
-
return proceedSeeInField.call(this, 'assert', field, _value)
|
|
1418
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1419
|
+
return proceedSeeInField.call(this, 'assert', field, _value);
|
|
1453
1420
|
}
|
|
1454
1421
|
|
|
1455
1422
|
/**
|
|
1456
1423
|
* {{> dontSeeInField }}
|
|
1457
1424
|
*/
|
|
1458
1425
|
async dontSeeInField(field, value) {
|
|
1459
|
-
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1460
|
-
return proceedSeeInField.call(this, 'negate', field, _value)
|
|
1426
|
+
const _value = (typeof value === 'boolean') ? value : value.toString();
|
|
1427
|
+
return proceedSeeInField.call(this, 'negate', field, _value);
|
|
1461
1428
|
}
|
|
1462
1429
|
|
|
1463
1430
|
/**
|
|
@@ -1466,48 +1433,48 @@ class Puppeteer extends Helper {
|
|
|
1466
1433
|
* {{> attachFile }}
|
|
1467
1434
|
*/
|
|
1468
1435
|
async attachFile(locator, pathToFile) {
|
|
1469
|
-
const file = path.join(global.codecept_dir, pathToFile)
|
|
1436
|
+
const file = path.join(global.codecept_dir, pathToFile);
|
|
1470
1437
|
|
|
1471
1438
|
if (!fileExists(file)) {
|
|
1472
|
-
throw new Error(`File at ${file} can not be found on local system`)
|
|
1439
|
+
throw new Error(`File at ${file} can not be found on local system`);
|
|
1473
1440
|
}
|
|
1474
|
-
const els = await findFields.call(this, locator)
|
|
1475
|
-
assertElementExists(els, locator, 'Field')
|
|
1476
|
-
await els[0].uploadFile(file)
|
|
1477
|
-
return this._waitForAction()
|
|
1441
|
+
const els = await findFields.call(this, locator);
|
|
1442
|
+
assertElementExists(els, locator, 'Field');
|
|
1443
|
+
await els[0].uploadFile(file);
|
|
1444
|
+
return this._waitForAction();
|
|
1478
1445
|
}
|
|
1479
1446
|
|
|
1480
1447
|
/**
|
|
1481
1448
|
* {{> selectOption }}
|
|
1482
1449
|
*/
|
|
1483
1450
|
async selectOption(select, option) {
|
|
1484
|
-
const els = await findVisibleFields.call(this, select)
|
|
1485
|
-
assertElementExists(els, select, 'Selectable field')
|
|
1486
|
-
const el = els[0]
|
|
1487
|
-
if ((await el.getProperty('tagName').then(
|
|
1488
|
-
throw new Error('Element is not <select>')
|
|
1451
|
+
const els = await findVisibleFields.call(this, select);
|
|
1452
|
+
assertElementExists(els, select, 'Selectable field');
|
|
1453
|
+
const el = els[0];
|
|
1454
|
+
if ((await el.getProperty('tagName').then(t => t.jsonValue())) !== 'SELECT') {
|
|
1455
|
+
throw new Error('Element is not <select>');
|
|
1489
1456
|
}
|
|
1490
|
-
highlightActiveElement.call(this, els[0], await this._getContext())
|
|
1491
|
-
if (!Array.isArray(option)) option = [option]
|
|
1457
|
+
highlightActiveElement.call(this, els[0], await this._getContext());
|
|
1458
|
+
if (!Array.isArray(option)) option = [option];
|
|
1492
1459
|
|
|
1493
1460
|
for (const key in option) {
|
|
1494
|
-
const opt = xpathLocator.literal(option[key])
|
|
1495
|
-
let optEl = await findElements.call(this, el, { xpath: Locator.select.byVisibleText(opt) })
|
|
1461
|
+
const opt = xpathLocator.literal(option[key]);
|
|
1462
|
+
let optEl = await findElements.call(this, el, { xpath: Locator.select.byVisibleText(opt) });
|
|
1496
1463
|
if (optEl.length) {
|
|
1497
|
-
this._evaluateHandeInContext(
|
|
1498
|
-
continue
|
|
1464
|
+
this._evaluateHandeInContext(el => el.selected = true, optEl[0]);
|
|
1465
|
+
continue;
|
|
1499
1466
|
}
|
|
1500
|
-
optEl = await findElements.call(this, el, { xpath: Locator.select.byValue(opt) })
|
|
1467
|
+
optEl = await findElements.call(this, el, { xpath: Locator.select.byValue(opt) });
|
|
1501
1468
|
if (optEl.length) {
|
|
1502
|
-
this._evaluateHandeInContext(
|
|
1469
|
+
this._evaluateHandeInContext(el => el.selected = true, optEl[0]);
|
|
1503
1470
|
}
|
|
1504
1471
|
}
|
|
1505
1472
|
await this._evaluateHandeInContext((element) => {
|
|
1506
|
-
element.dispatchEvent(new Event('input', { bubbles: true }))
|
|
1507
|
-
element.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1508
|
-
}, el)
|
|
1473
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1474
|
+
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1475
|
+
}, el);
|
|
1509
1476
|
|
|
1510
|
-
return this._waitForAction()
|
|
1477
|
+
return this._waitForAction();
|
|
1511
1478
|
}
|
|
1512
1479
|
|
|
1513
1480
|
/**
|
|
@@ -1515,48 +1482,40 @@ class Puppeteer extends Helper {
|
|
|
1515
1482
|
* {{ react }}
|
|
1516
1483
|
*/
|
|
1517
1484
|
async grabNumberOfVisibleElements(locator) {
|
|
1518
|
-
let els = await this._locate(locator)
|
|
1519
|
-
els = (await Promise.all(els.map(
|
|
1485
|
+
let els = await this._locate(locator);
|
|
1486
|
+
els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v);
|
|
1520
1487
|
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
|
|
1521
|
-
els = await Promise.all(
|
|
1522
|
-
els.map(
|
|
1523
|
-
async (el) =>
|
|
1524
|
-
(await el.evaluate(
|
|
1525
|
-
(node) =>
|
|
1526
|
-
window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none',
|
|
1527
|
-
)) && el,
|
|
1528
|
-
),
|
|
1529
|
-
)
|
|
1488
|
+
els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el));
|
|
1530
1489
|
|
|
1531
|
-
return els.filter(
|
|
1490
|
+
return els.filter(v => v).length;
|
|
1532
1491
|
}
|
|
1533
1492
|
|
|
1534
1493
|
/**
|
|
1535
1494
|
* {{> seeInCurrentUrl }}
|
|
1536
1495
|
*/
|
|
1537
1496
|
async seeInCurrentUrl(url) {
|
|
1538
|
-
stringIncludes('url').assert(url, await this._getPageUrl())
|
|
1497
|
+
stringIncludes('url').assert(url, await this._getPageUrl());
|
|
1539
1498
|
}
|
|
1540
1499
|
|
|
1541
1500
|
/**
|
|
1542
1501
|
* {{> dontSeeInCurrentUrl }}
|
|
1543
1502
|
*/
|
|
1544
1503
|
async dontSeeInCurrentUrl(url) {
|
|
1545
|
-
stringIncludes('url').negate(url, await this._getPageUrl())
|
|
1504
|
+
stringIncludes('url').negate(url, await this._getPageUrl());
|
|
1546
1505
|
}
|
|
1547
1506
|
|
|
1548
1507
|
/**
|
|
1549
1508
|
* {{> seeCurrentUrlEquals }}
|
|
1550
1509
|
*/
|
|
1551
1510
|
async seeCurrentUrlEquals(url) {
|
|
1552
|
-
urlEquals(this.options.url).assert(url, await this._getPageUrl())
|
|
1511
|
+
urlEquals(this.options.url).assert(url, await this._getPageUrl());
|
|
1553
1512
|
}
|
|
1554
1513
|
|
|
1555
1514
|
/**
|
|
1556
1515
|
* {{> dontSeeCurrentUrlEquals }}
|
|
1557
1516
|
*/
|
|
1558
1517
|
async dontSeeCurrentUrlEquals(url) {
|
|
1559
|
-
urlEquals(this.options.url).negate(url, await this._getPageUrl())
|
|
1518
|
+
urlEquals(this.options.url).negate(url, await this._getPageUrl());
|
|
1560
1519
|
}
|
|
1561
1520
|
|
|
1562
1521
|
/**
|
|
@@ -1565,14 +1524,14 @@ class Puppeteer extends Helper {
|
|
|
1565
1524
|
* {{ react }}
|
|
1566
1525
|
*/
|
|
1567
1526
|
async see(text, context = null) {
|
|
1568
|
-
return proceedSee.call(this, 'assert', text, context)
|
|
1527
|
+
return proceedSee.call(this, 'assert', text, context);
|
|
1569
1528
|
}
|
|
1570
1529
|
|
|
1571
1530
|
/**
|
|
1572
1531
|
* {{> seeTextEquals }}
|
|
1573
1532
|
*/
|
|
1574
1533
|
async seeTextEquals(text, context = null) {
|
|
1575
|
-
return proceedSee.call(this, 'assert', text, context, true)
|
|
1534
|
+
return proceedSee.call(this, 'assert', text, context, true);
|
|
1576
1535
|
}
|
|
1577
1536
|
|
|
1578
1537
|
/**
|
|
@@ -1581,14 +1540,14 @@ class Puppeteer extends Helper {
|
|
|
1581
1540
|
* {{ react }}
|
|
1582
1541
|
*/
|
|
1583
1542
|
async dontSee(text, context = null) {
|
|
1584
|
-
return proceedSee.call(this, 'negate', text, context)
|
|
1543
|
+
return proceedSee.call(this, 'negate', text, context);
|
|
1585
1544
|
}
|
|
1586
1545
|
|
|
1587
1546
|
/**
|
|
1588
1547
|
* {{> grabSource }}
|
|
1589
1548
|
*/
|
|
1590
1549
|
async grabSource() {
|
|
1591
|
-
return this.page.content()
|
|
1550
|
+
return this.page.content();
|
|
1592
1551
|
}
|
|
1593
1552
|
|
|
1594
1553
|
/**
|
|
@@ -1601,32 +1560,32 @@ class Puppeteer extends Helper {
|
|
|
1601
1560
|
* @return {Promise<any[]>}
|
|
1602
1561
|
*/
|
|
1603
1562
|
async grabBrowserLogs() {
|
|
1604
|
-
const logs = consoleLogStore.entries
|
|
1605
|
-
consoleLogStore.clear()
|
|
1606
|
-
return logs
|
|
1563
|
+
const logs = consoleLogStore.entries;
|
|
1564
|
+
consoleLogStore.clear();
|
|
1565
|
+
return logs;
|
|
1607
1566
|
}
|
|
1608
1567
|
|
|
1609
1568
|
/**
|
|
1610
1569
|
* {{> grabCurrentUrl }}
|
|
1611
1570
|
*/
|
|
1612
1571
|
async grabCurrentUrl() {
|
|
1613
|
-
return this._getPageUrl()
|
|
1572
|
+
return this._getPageUrl();
|
|
1614
1573
|
}
|
|
1615
1574
|
|
|
1616
1575
|
/**
|
|
1617
1576
|
* {{> seeInSource }}
|
|
1618
1577
|
*/
|
|
1619
1578
|
async seeInSource(text) {
|
|
1620
|
-
const source = await this.page.content()
|
|
1621
|
-
stringIncludes('HTML source of a page').assert(text, source)
|
|
1579
|
+
const source = await this.page.content();
|
|
1580
|
+
stringIncludes('HTML source of a page').assert(text, source);
|
|
1622
1581
|
}
|
|
1623
1582
|
|
|
1624
1583
|
/**
|
|
1625
1584
|
* {{> dontSeeInSource }}
|
|
1626
1585
|
*/
|
|
1627
1586
|
async dontSeeInSource(text) {
|
|
1628
|
-
const source = await this.page.content()
|
|
1629
|
-
stringIncludes('HTML source of a page').negate(text, source)
|
|
1587
|
+
const source = await this.page.content();
|
|
1588
|
+
stringIncludes('HTML source of a page').negate(text, source);
|
|
1630
1589
|
}
|
|
1631
1590
|
|
|
1632
1591
|
/**
|
|
@@ -1635,10 +1594,8 @@ class Puppeteer extends Helper {
|
|
|
1635
1594
|
* {{ react }}
|
|
1636
1595
|
*/
|
|
1637
1596
|
async seeNumberOfElements(locator, num) {
|
|
1638
|
-
const elements = await this._locate(locator)
|
|
1639
|
-
return equals(
|
|
1640
|
-
`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`,
|
|
1641
|
-
).assert(elements.length, num)
|
|
1597
|
+
const elements = await this._locate(locator);
|
|
1598
|
+
return equals(`expected number of elements (${(new Locator(locator))}) is ${num}, but found ${elements.length}`).assert(elements.length, num);
|
|
1642
1599
|
}
|
|
1643
1600
|
|
|
1644
1601
|
/**
|
|
@@ -1647,11 +1604,8 @@ class Puppeteer extends Helper {
|
|
|
1647
1604
|
* {{ react }}
|
|
1648
1605
|
*/
|
|
1649
1606
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1650
|
-
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1651
|
-
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(
|
|
1652
|
-
res,
|
|
1653
|
-
num,
|
|
1654
|
-
)
|
|
1607
|
+
const res = await this.grabNumberOfVisibleElements(locator);
|
|
1608
|
+
return equals(`expected number of visible elements (${(new Locator(locator))}) is ${num}, but found ${res}`).assert(res, num);
|
|
1655
1609
|
}
|
|
1656
1610
|
|
|
1657
1611
|
/**
|
|
@@ -1659,9 +1613,9 @@ class Puppeteer extends Helper {
|
|
|
1659
1613
|
*/
|
|
1660
1614
|
async setCookie(cookie) {
|
|
1661
1615
|
if (Array.isArray(cookie)) {
|
|
1662
|
-
return this.page.setCookie(...cookie)
|
|
1616
|
+
return this.page.setCookie(...cookie);
|
|
1663
1617
|
}
|
|
1664
|
-
return this.page.setCookie(cookie)
|
|
1618
|
+
return this.page.setCookie(cookie);
|
|
1665
1619
|
}
|
|
1666
1620
|
|
|
1667
1621
|
/**
|
|
@@ -1669,16 +1623,16 @@ class Puppeteer extends Helper {
|
|
|
1669
1623
|
*
|
|
1670
1624
|
*/
|
|
1671
1625
|
async seeCookie(name) {
|
|
1672
|
-
const cookies = await this.page.cookies()
|
|
1673
|
-
empty(`cookie ${name} to be set`).negate(cookies.filter(
|
|
1626
|
+
const cookies = await this.page.cookies();
|
|
1627
|
+
empty(`cookie ${name} to be set`).negate(cookies.filter(c => c.name === name));
|
|
1674
1628
|
}
|
|
1675
1629
|
|
|
1676
1630
|
/**
|
|
1677
1631
|
* {{> dontSeeCookie }}
|
|
1678
1632
|
*/
|
|
1679
1633
|
async dontSeeCookie(name) {
|
|
1680
|
-
const cookies = await this.page.cookies()
|
|
1681
|
-
empty(`cookie ${name}
|
|
1634
|
+
const cookies = await this.page.cookies();
|
|
1635
|
+
empty(`cookie ${name} to be set`).assert(cookies.filter(c => c.name === name));
|
|
1682
1636
|
}
|
|
1683
1637
|
|
|
1684
1638
|
/**
|
|
@@ -1687,10 +1641,10 @@ class Puppeteer extends Helper {
|
|
|
1687
1641
|
* Returns cookie in JSON format. If name not passed returns all cookies for this domain.
|
|
1688
1642
|
*/
|
|
1689
1643
|
async grabCookie(name) {
|
|
1690
|
-
const cookies = await this.page.cookies()
|
|
1691
|
-
if (!name) return cookies
|
|
1692
|
-
const cookie = cookies.filter(
|
|
1693
|
-
if (cookie[0]) return cookie[0]
|
|
1644
|
+
const cookies = await this.page.cookies();
|
|
1645
|
+
if (!name) return cookies;
|
|
1646
|
+
const cookie = cookies.filter(c => c.name === name);
|
|
1647
|
+
if (cookie[0]) return cookie[0];
|
|
1694
1648
|
}
|
|
1695
1649
|
|
|
1696
1650
|
/**
|
|
@@ -1698,47 +1652,44 @@ class Puppeteer extends Helper {
|
|
|
1698
1652
|
*/
|
|
1699
1653
|
async waitForCookie(name, sec) {
|
|
1700
1654
|
// by default, we will retry 3 times
|
|
1701
|
-
let retries = 3
|
|
1702
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
1655
|
+
let retries = 3;
|
|
1656
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
1703
1657
|
|
|
1704
1658
|
if (sec) {
|
|
1705
|
-
retries = sec
|
|
1659
|
+
retries = sec;
|
|
1706
1660
|
} else {
|
|
1707
|
-
retries = Math.ceil(waitTimeout / 1000) - 1
|
|
1661
|
+
retries = Math.ceil(waitTimeout / 1000) - 1;
|
|
1708
1662
|
}
|
|
1709
1663
|
|
|
1710
|
-
return promiseRetry(
|
|
1711
|
-
async (
|
|
1712
|
-
const
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
}
|
|
1664
|
+
return promiseRetry(async (retry, number) => {
|
|
1665
|
+
const _grabCookie = async (name) => {
|
|
1666
|
+
const cookies = await this.page.cookies();
|
|
1667
|
+
const cookie = cookies.filter(c => c.name === name);
|
|
1668
|
+
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`);
|
|
1669
|
+
};
|
|
1717
1670
|
|
|
1718
|
-
|
|
1719
|
-
|
|
1671
|
+
this.debugSection('Wait for cookie: ', name);
|
|
1672
|
+
if (number > 1) this.debugSection('Retrying... Attempt #', number);
|
|
1720
1673
|
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
{ retries, maxTimeout: 1000 },
|
|
1728
|
-
)
|
|
1674
|
+
try {
|
|
1675
|
+
await _grabCookie(name);
|
|
1676
|
+
} catch (e) {
|
|
1677
|
+
retry(e);
|
|
1678
|
+
}
|
|
1679
|
+
}, { retries, maxTimeout: 1000 });
|
|
1729
1680
|
}
|
|
1730
1681
|
|
|
1731
1682
|
/**
|
|
1732
1683
|
* {{> clearCookie }}
|
|
1733
1684
|
*/
|
|
1734
1685
|
async clearCookie(name) {
|
|
1735
|
-
const cookies = await this.page.cookies()
|
|
1686
|
+
const cookies = await this.page.cookies();
|
|
1736
1687
|
if (!name) {
|
|
1737
|
-
return this.page.deleteCookie.apply(this.page, cookies)
|
|
1688
|
+
return this.page.deleteCookie.apply(this.page, cookies);
|
|
1738
1689
|
}
|
|
1739
|
-
const cookie = cookies.filter(
|
|
1740
|
-
if (!cookie[0]) return
|
|
1741
|
-
return this.page.deleteCookie(cookie[0])
|
|
1690
|
+
const cookie = cookies.filter(c => c.name === name);
|
|
1691
|
+
if (!cookie[0]) return;
|
|
1692
|
+
return this.page.deleteCookie(cookie[0]);
|
|
1742
1693
|
}
|
|
1743
1694
|
|
|
1744
1695
|
/**
|
|
@@ -1747,11 +1698,11 @@ class Puppeteer extends Helper {
|
|
|
1747
1698
|
* {{> executeScript }}
|
|
1748
1699
|
*/
|
|
1749
1700
|
async executeScript(...args) {
|
|
1750
|
-
let context = await this._getContext()
|
|
1701
|
+
let context = await this._getContext();
|
|
1751
1702
|
if (this.context && this.context.constructor.name === 'CdpFrame') {
|
|
1752
|
-
context = this.context // switching to iframe context
|
|
1703
|
+
context = this.context; // switching to iframe context
|
|
1753
1704
|
}
|
|
1754
|
-
return context.evaluate.apply(context, args)
|
|
1705
|
+
return context.evaluate.apply(context, args);
|
|
1755
1706
|
}
|
|
1756
1707
|
|
|
1757
1708
|
/**
|
|
@@ -1760,16 +1711,16 @@ class Puppeteer extends Helper {
|
|
|
1760
1711
|
*/
|
|
1761
1712
|
async executeAsyncScript(...args) {
|
|
1762
1713
|
const asyncFn = function () {
|
|
1763
|
-
const args = Array.from(arguments)
|
|
1764
|
-
const fn = eval(`(${args.shift()})`) // eslint-disable-line no-eval
|
|
1714
|
+
const args = Array.from(arguments);
|
|
1715
|
+
const fn = eval(`(${args.shift()})`); // eslint-disable-line no-eval
|
|
1765
1716
|
return new Promise((done) => {
|
|
1766
|
-
args.push(done)
|
|
1767
|
-
fn.apply(null, args)
|
|
1768
|
-
})
|
|
1769
|
-
}
|
|
1770
|
-
args[0] = args[0].toString()
|
|
1771
|
-
args.unshift(asyncFn)
|
|
1772
|
-
return this.page.evaluate.apply(this.page, args)
|
|
1717
|
+
args.push(done);
|
|
1718
|
+
fn.apply(null, args);
|
|
1719
|
+
});
|
|
1720
|
+
};
|
|
1721
|
+
args[0] = args[0].toString();
|
|
1722
|
+
args.unshift(asyncFn);
|
|
1723
|
+
return this.page.evaluate.apply(this.page, args);
|
|
1773
1724
|
}
|
|
1774
1725
|
|
|
1775
1726
|
/**
|
|
@@ -1777,12 +1728,12 @@ class Puppeteer extends Helper {
|
|
|
1777
1728
|
* {{ react }}
|
|
1778
1729
|
*/
|
|
1779
1730
|
async grabTextFromAll(locator) {
|
|
1780
|
-
const els = await this._locate(locator)
|
|
1781
|
-
const texts = []
|
|
1731
|
+
const els = await this._locate(locator);
|
|
1732
|
+
const texts = [];
|
|
1782
1733
|
for (const el of els) {
|
|
1783
|
-
texts.push(await (await el.getProperty('innerText')).jsonValue())
|
|
1734
|
+
texts.push(await (await el.getProperty('innerText')).jsonValue());
|
|
1784
1735
|
}
|
|
1785
|
-
return texts
|
|
1736
|
+
return texts;
|
|
1786
1737
|
}
|
|
1787
1738
|
|
|
1788
1739
|
/**
|
|
@@ -1790,60 +1741,60 @@ class Puppeteer extends Helper {
|
|
|
1790
1741
|
* {{ react }}
|
|
1791
1742
|
*/
|
|
1792
1743
|
async grabTextFrom(locator) {
|
|
1793
|
-
const texts = await this.grabTextFromAll(locator)
|
|
1794
|
-
assertElementExists(texts, locator)
|
|
1744
|
+
const texts = await this.grabTextFromAll(locator);
|
|
1745
|
+
assertElementExists(texts, locator);
|
|
1795
1746
|
if (texts.length > 1) {
|
|
1796
|
-
this.debugSection('GrabText', `Using first element out of ${texts.length}`)
|
|
1747
|
+
this.debugSection('GrabText', `Using first element out of ${texts.length}`);
|
|
1797
1748
|
}
|
|
1798
1749
|
|
|
1799
|
-
return texts[0]
|
|
1750
|
+
return texts[0];
|
|
1800
1751
|
}
|
|
1801
1752
|
|
|
1802
1753
|
/**
|
|
1803
1754
|
* {{> grabValueFromAll }}
|
|
1804
1755
|
*/
|
|
1805
1756
|
async grabValueFromAll(locator) {
|
|
1806
|
-
const els = await findFields.call(this, locator)
|
|
1807
|
-
const values = []
|
|
1757
|
+
const els = await findFields.call(this, locator);
|
|
1758
|
+
const values = [];
|
|
1808
1759
|
for (const el of els) {
|
|
1809
|
-
values.push(await (await el.getProperty('value')).jsonValue())
|
|
1760
|
+
values.push(await (await el.getProperty('value')).jsonValue());
|
|
1810
1761
|
}
|
|
1811
|
-
return values
|
|
1762
|
+
return values;
|
|
1812
1763
|
}
|
|
1813
1764
|
|
|
1814
1765
|
/**
|
|
1815
1766
|
* {{> grabValueFrom }}
|
|
1816
1767
|
*/
|
|
1817
1768
|
async grabValueFrom(locator) {
|
|
1818
|
-
const values = await this.grabValueFromAll(locator)
|
|
1819
|
-
assertElementExists(values, locator)
|
|
1769
|
+
const values = await this.grabValueFromAll(locator);
|
|
1770
|
+
assertElementExists(values, locator);
|
|
1820
1771
|
if (values.length > 1) {
|
|
1821
|
-
this.debugSection('GrabValue', `Using first element out of ${values.length}`)
|
|
1772
|
+
this.debugSection('GrabValue', `Using first element out of ${values.length}`);
|
|
1822
1773
|
}
|
|
1823
1774
|
|
|
1824
|
-
return values[0]
|
|
1775
|
+
return values[0];
|
|
1825
1776
|
}
|
|
1826
1777
|
|
|
1827
1778
|
/**
|
|
1828
1779
|
* {{> grabHTMLFromAll }}
|
|
1829
1780
|
*/
|
|
1830
1781
|
async grabHTMLFromAll(locator) {
|
|
1831
|
-
const els = await this._locate(locator)
|
|
1832
|
-
const values = await Promise.all(els.map(
|
|
1833
|
-
return values
|
|
1782
|
+
const els = await this._locate(locator);
|
|
1783
|
+
const values = await Promise.all(els.map(el => el.evaluate(element => element.innerHTML, el)));
|
|
1784
|
+
return values;
|
|
1834
1785
|
}
|
|
1835
1786
|
|
|
1836
1787
|
/**
|
|
1837
1788
|
* {{> grabHTMLFrom }}
|
|
1838
1789
|
*/
|
|
1839
1790
|
async grabHTMLFrom(locator) {
|
|
1840
|
-
const html = await this.grabHTMLFromAll(locator)
|
|
1841
|
-
assertElementExists(html, locator)
|
|
1791
|
+
const html = await this.grabHTMLFromAll(locator);
|
|
1792
|
+
assertElementExists(html, locator);
|
|
1842
1793
|
if (html.length > 1) {
|
|
1843
|
-
this.debugSection('GrabHTML', `Using first element out of ${html.length}`)
|
|
1794
|
+
this.debugSection('GrabHTML', `Using first element out of ${html.length}`);
|
|
1844
1795
|
}
|
|
1845
1796
|
|
|
1846
|
-
return html[0]
|
|
1797
|
+
return html[0];
|
|
1847
1798
|
}
|
|
1848
1799
|
|
|
1849
1800
|
/**
|
|
@@ -1851,13 +1802,11 @@ class Puppeteer extends Helper {
|
|
|
1851
1802
|
* {{ react }}
|
|
1852
1803
|
*/
|
|
1853
1804
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
1854
|
-
const els = await this._locate(locator)
|
|
1855
|
-
const res = await Promise.all(
|
|
1856
|
-
|
|
1857
|
-
)
|
|
1858
|
-
const cssValues = res.map((props) => props[toCamelCase(cssProperty)])
|
|
1805
|
+
const els = await this._locate(locator);
|
|
1806
|
+
const res = await Promise.all(els.map(el => el.evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)));
|
|
1807
|
+
const cssValues = res.map(props => props[toCamelCase(cssProperty)]);
|
|
1859
1808
|
|
|
1860
|
-
return cssValues
|
|
1809
|
+
return cssValues;
|
|
1861
1810
|
}
|
|
1862
1811
|
|
|
1863
1812
|
/**
|
|
@@ -1865,14 +1814,14 @@ class Puppeteer extends Helper {
|
|
|
1865
1814
|
* {{ react }}
|
|
1866
1815
|
*/
|
|
1867
1816
|
async grabCssPropertyFrom(locator, cssProperty) {
|
|
1868
|
-
const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty)
|
|
1869
|
-
assertElementExists(cssValues, locator)
|
|
1817
|
+
const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty);
|
|
1818
|
+
assertElementExists(cssValues, locator);
|
|
1870
1819
|
|
|
1871
1820
|
if (cssValues.length > 1) {
|
|
1872
|
-
this.debugSection('GrabCSS', `Using first element out of ${cssValues.length}`)
|
|
1821
|
+
this.debugSection('GrabCSS', `Using first element out of ${cssValues.length}`);
|
|
1873
1822
|
}
|
|
1874
1823
|
|
|
1875
|
-
return cssValues[0]
|
|
1824
|
+
return cssValues[0];
|
|
1876
1825
|
}
|
|
1877
1826
|
|
|
1878
1827
|
/**
|
|
@@ -1880,37 +1829,35 @@ class Puppeteer extends Helper {
|
|
|
1880
1829
|
* {{ react }}
|
|
1881
1830
|
*/
|
|
1882
1831
|
async seeCssPropertiesOnElements(locator, cssProperties) {
|
|
1883
|
-
const res = await this._locate(locator)
|
|
1884
|
-
assertElementExists(res, locator)
|
|
1832
|
+
const res = await this._locate(locator);
|
|
1833
|
+
assertElementExists(res, locator);
|
|
1885
1834
|
|
|
1886
|
-
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties)
|
|
1887
|
-
const elemAmount = res.length
|
|
1888
|
-
let props = []
|
|
1835
|
+
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
|
|
1836
|
+
const elemAmount = res.length;
|
|
1837
|
+
let props = [];
|
|
1889
1838
|
|
|
1890
1839
|
for (const element of res) {
|
|
1891
1840
|
for (const prop of Object.keys(cssProperties)) {
|
|
1892
|
-
const cssProp = await this.grabCssPropertyFrom(locator, prop)
|
|
1841
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop);
|
|
1893
1842
|
if (isColorProperty(prop)) {
|
|
1894
|
-
props.push(convertColorToRGBA(cssProp))
|
|
1843
|
+
props.push(convertColorToRGBA(cssProp));
|
|
1895
1844
|
} else {
|
|
1896
|
-
props.push(cssProp)
|
|
1845
|
+
props.push(cssProp);
|
|
1897
1846
|
}
|
|
1898
1847
|
}
|
|
1899
1848
|
}
|
|
1900
1849
|
|
|
1901
|
-
const values = Object.keys(cssPropertiesCamelCase).map(
|
|
1902
|
-
if (!Array.isArray(props)) props = [props]
|
|
1903
|
-
let chunked = chunkArray(props, values.length)
|
|
1850
|
+
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
|
|
1851
|
+
if (!Array.isArray(props)) props = [props];
|
|
1852
|
+
let chunked = chunkArray(props, values.length);
|
|
1904
1853
|
chunked = chunked.filter((val) => {
|
|
1905
1854
|
for (let i = 0; i < val.length; ++i) {
|
|
1906
1855
|
// eslint-disable-next-line eqeqeq
|
|
1907
|
-
if (val[i] != values[i]) return false
|
|
1856
|
+
if (val[i] != values[i]) return false;
|
|
1908
1857
|
}
|
|
1909
|
-
return true
|
|
1910
|
-
})
|
|
1911
|
-
return equals(
|
|
1912
|
-
`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
1913
|
-
).assert(chunked.length, elemAmount)
|
|
1858
|
+
return true;
|
|
1859
|
+
});
|
|
1860
|
+
return equals(`all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount);
|
|
1914
1861
|
}
|
|
1915
1862
|
|
|
1916
1863
|
/**
|
|
@@ -1918,40 +1865,34 @@ class Puppeteer extends Helper {
|
|
|
1918
1865
|
* {{ react }}
|
|
1919
1866
|
*/
|
|
1920
1867
|
async seeAttributesOnElements(locator, attributes) {
|
|
1921
|
-
const elements = await this._locate(locator)
|
|
1922
|
-
assertElementExists(elements, locator)
|
|
1868
|
+
const elements = await this._locate(locator);
|
|
1869
|
+
assertElementExists(elements, locator);
|
|
1923
1870
|
|
|
1924
|
-
const expectedAttributes = Object.entries(attributes)
|
|
1871
|
+
const expectedAttributes = Object.entries(attributes);
|
|
1925
1872
|
|
|
1926
1873
|
const valuesPromises = elements.map(async (element) => {
|
|
1927
|
-
const elementAttributes = {}
|
|
1928
|
-
await Promise.all(
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
return elementAttributes
|
|
1935
|
-
})
|
|
1874
|
+
const elementAttributes = {};
|
|
1875
|
+
await Promise.all(expectedAttributes.map(async ([attribute, expectedValue]) => {
|
|
1876
|
+
const actualValue = await element.evaluate((el, attr) => el[attr] || el.getAttribute(attr), attribute);
|
|
1877
|
+
elementAttributes[attribute] = actualValue;
|
|
1878
|
+
}));
|
|
1879
|
+
return elementAttributes;
|
|
1880
|
+
});
|
|
1936
1881
|
|
|
1937
|
-
const actualAttributes = await Promise.all(valuesPromises)
|
|
1882
|
+
const actualAttributes = await Promise.all(valuesPromises);
|
|
1938
1883
|
|
|
1939
|
-
const matchingElements = actualAttributes.filter((attrs) =>
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
}),
|
|
1946
|
-
)
|
|
1884
|
+
const matchingElements = actualAttributes.filter((attrs) => expectedAttributes.every(([attribute, expectedValue]) => {
|
|
1885
|
+
const actualValue = attrs[attribute];
|
|
1886
|
+
if (!actualValue) return false;
|
|
1887
|
+
if (actualValue.toString().match(new RegExp(expectedValue.toString()))) return true;
|
|
1888
|
+
return expectedValue === actualValue;
|
|
1889
|
+
}));
|
|
1947
1890
|
|
|
1948
|
-
const elementsCount = elements.length
|
|
1949
|
-
const matchingCount = matchingElements.length
|
|
1891
|
+
const elementsCount = elements.length;
|
|
1892
|
+
const matchingCount = matchingElements.length;
|
|
1950
1893
|
|
|
1951
|
-
return equals(`all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`)
|
|
1952
|
-
matchingCount,
|
|
1953
|
-
elementsCount,
|
|
1954
|
-
)
|
|
1894
|
+
return equals(`all elements (${(new Locator(locator))}) to have attributes ${JSON.stringify(attributes)}`)
|
|
1895
|
+
.assert(matchingCount, elementsCount);
|
|
1955
1896
|
}
|
|
1956
1897
|
|
|
1957
1898
|
/**
|
|
@@ -1959,21 +1900,21 @@ class Puppeteer extends Helper {
|
|
|
1959
1900
|
* {{ react }}
|
|
1960
1901
|
*/
|
|
1961
1902
|
async dragSlider(locator, offsetX = 0) {
|
|
1962
|
-
const src = await this._locate(locator)
|
|
1963
|
-
assertElementExists(src, locator, 'Slider Element')
|
|
1903
|
+
const src = await this._locate(locator);
|
|
1904
|
+
assertElementExists(src, locator, 'Slider Element');
|
|
1964
1905
|
|
|
1965
1906
|
// Note: Using public api .getClickablePoint because the .BoundingBox does not take into account iframe offsets
|
|
1966
|
-
const sliderSource = await getClickablePoint(src[0])
|
|
1907
|
+
const sliderSource = await getClickablePoint(src[0]);
|
|
1967
1908
|
|
|
1968
1909
|
// Drag start point
|
|
1969
|
-
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 })
|
|
1970
|
-
await this.page.mouse.down()
|
|
1910
|
+
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 });
|
|
1911
|
+
await this.page.mouse.down();
|
|
1971
1912
|
|
|
1972
1913
|
// Drag destination
|
|
1973
|
-
await this.page.mouse.move(sliderSource.x + offsetX, sliderSource.y, { steps: 5 })
|
|
1974
|
-
await this.page.mouse.up()
|
|
1914
|
+
await this.page.mouse.move(sliderSource.x + offsetX, sliderSource.y, { steps: 5 });
|
|
1915
|
+
await this.page.mouse.up();
|
|
1975
1916
|
|
|
1976
|
-
await this._waitForAction()
|
|
1917
|
+
await this._waitForAction();
|
|
1977
1918
|
}
|
|
1978
1919
|
|
|
1979
1920
|
/**
|
|
@@ -1981,13 +1922,13 @@ class Puppeteer extends Helper {
|
|
|
1981
1922
|
* {{ react }}
|
|
1982
1923
|
*/
|
|
1983
1924
|
async grabAttributeFromAll(locator, attr) {
|
|
1984
|
-
const els = await this._locate(locator)
|
|
1985
|
-
const array = []
|
|
1925
|
+
const els = await this._locate(locator);
|
|
1926
|
+
const array = [];
|
|
1986
1927
|
for (let index = 0; index < els.length; index++) {
|
|
1987
|
-
const a = await this._evaluateHandeInContext((el, attr) => el[attr] || el.getAttribute(attr), els[index], attr)
|
|
1988
|
-
array.push(await a.jsonValue())
|
|
1928
|
+
const a = await this._evaluateHandeInContext((el, attr) => el[attr] || el.getAttribute(attr), els[index], attr);
|
|
1929
|
+
array.push(await a.jsonValue());
|
|
1989
1930
|
}
|
|
1990
|
-
return array
|
|
1931
|
+
return array;
|
|
1991
1932
|
}
|
|
1992
1933
|
|
|
1993
1934
|
/**
|
|
@@ -1995,84 +1936,84 @@ class Puppeteer extends Helper {
|
|
|
1995
1936
|
* {{ react }}
|
|
1996
1937
|
*/
|
|
1997
1938
|
async grabAttributeFrom(locator, attr) {
|
|
1998
|
-
const attrs = await this.grabAttributeFromAll(locator, attr)
|
|
1999
|
-
assertElementExists(attrs, locator)
|
|
1939
|
+
const attrs = await this.grabAttributeFromAll(locator, attr);
|
|
1940
|
+
assertElementExists(attrs, locator);
|
|
2000
1941
|
if (attrs.length > 1) {
|
|
2001
|
-
this.debugSection('GrabAttribute', `Using first element out of ${attrs.length}`)
|
|
1942
|
+
this.debugSection('GrabAttribute', `Using first element out of ${attrs.length}`);
|
|
2002
1943
|
}
|
|
2003
1944
|
|
|
2004
|
-
return attrs[0]
|
|
1945
|
+
return attrs[0];
|
|
2005
1946
|
}
|
|
2006
1947
|
|
|
2007
1948
|
/**
|
|
2008
1949
|
* {{> saveElementScreenshot }}
|
|
2009
1950
|
*/
|
|
2010
1951
|
async saveElementScreenshot(locator, fileName) {
|
|
2011
|
-
const outputFile = screenshotOutputFolder(fileName)
|
|
1952
|
+
const outputFile = screenshotOutputFolder(fileName);
|
|
2012
1953
|
|
|
2013
|
-
const res = await this._locate(locator)
|
|
2014
|
-
assertElementExists(res, locator)
|
|
2015
|
-
if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`)
|
|
2016
|
-
const elem = res[0]
|
|
2017
|
-
this.debug(`Screenshot of ${new Locator(locator)} element has been saved to ${outputFile}`)
|
|
2018
|
-
return elem.screenshot({ path: outputFile, type: 'png' })
|
|
1954
|
+
const res = await this._locate(locator);
|
|
1955
|
+
assertElementExists(res, locator);
|
|
1956
|
+
if (res.length > 1) this.debug(`[Elements] Using first element out of ${res.length}`);
|
|
1957
|
+
const elem = res[0];
|
|
1958
|
+
this.debug(`Screenshot of ${(new Locator(locator))} element has been saved to ${outputFile}`);
|
|
1959
|
+
return elem.screenshot({ path: outputFile, type: 'png' });
|
|
2019
1960
|
}
|
|
2020
1961
|
|
|
2021
1962
|
/**
|
|
2022
1963
|
* {{> saveScreenshot }}
|
|
2023
1964
|
*/
|
|
2024
1965
|
async saveScreenshot(fileName, fullPage) {
|
|
2025
|
-
const fullPageOption = fullPage || this.options.fullPageScreenshots
|
|
2026
|
-
let outputFile = screenshotOutputFolder(fileName)
|
|
1966
|
+
const fullPageOption = fullPage || this.options.fullPageScreenshots;
|
|
1967
|
+
let outputFile = screenshotOutputFolder(fileName);
|
|
2027
1968
|
|
|
2028
|
-
this.debug(`Screenshot is saving to ${outputFile}`)
|
|
1969
|
+
this.debug(`Screenshot is saving to ${outputFile}`);
|
|
2029
1970
|
|
|
2030
1971
|
await this.page.screenshot({
|
|
2031
1972
|
path: outputFile,
|
|
2032
1973
|
fullPage: fullPageOption,
|
|
2033
1974
|
type: 'png',
|
|
2034
|
-
})
|
|
1975
|
+
});
|
|
2035
1976
|
|
|
2036
1977
|
if (this.activeSessionName) {
|
|
2037
1978
|
for (const sessionName in this.sessionPages) {
|
|
2038
|
-
const activeSessionPage = this.sessionPages[sessionName]
|
|
2039
|
-
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`)
|
|
1979
|
+
const activeSessionPage = this.sessionPages[sessionName];
|
|
1980
|
+
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`);
|
|
2040
1981
|
|
|
2041
|
-
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`)
|
|
1982
|
+
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`);
|
|
2042
1983
|
|
|
2043
1984
|
if (activeSessionPage) {
|
|
2044
1985
|
await activeSessionPage.screenshot({
|
|
2045
1986
|
path: outputFile,
|
|
2046
1987
|
fullPage: fullPageOption,
|
|
2047
1988
|
type: 'png',
|
|
2048
|
-
})
|
|
1989
|
+
});
|
|
2049
1990
|
}
|
|
2050
1991
|
}
|
|
2051
1992
|
}
|
|
2052
1993
|
}
|
|
2053
1994
|
|
|
2054
1995
|
async _failed(test) {
|
|
2055
|
-
await this._withinEnd()
|
|
1996
|
+
await this._withinEnd();
|
|
2056
1997
|
|
|
2057
1998
|
if (this.options.trace) {
|
|
2058
|
-
await this.page.tracing.stop()
|
|
2059
|
-
const _traceName = this.currentRunningTest.artifacts.trace.replace('.json', '.failed.json')
|
|
2060
|
-
fs.renameSync(this.currentRunningTest.artifacts.trace, _traceName)
|
|
2061
|
-
test.artifacts.trace = _traceName
|
|
1999
|
+
await this.page.tracing.stop();
|
|
2000
|
+
const _traceName = this.currentRunningTest.artifacts.trace.replace('.json', '.failed.json');
|
|
2001
|
+
fs.renameSync(this.currentRunningTest.artifacts.trace, _traceName);
|
|
2002
|
+
test.artifacts.trace = _traceName;
|
|
2062
2003
|
}
|
|
2063
2004
|
}
|
|
2064
2005
|
|
|
2065
2006
|
async _passed(test) {
|
|
2066
|
-
await this._withinEnd()
|
|
2007
|
+
await this._withinEnd();
|
|
2067
2008
|
|
|
2068
2009
|
if (this.options.trace) {
|
|
2069
|
-
await this.page.tracing.stop()
|
|
2010
|
+
await this.page.tracing.stop();
|
|
2070
2011
|
if (this.options.keepTraceForPassedTests) {
|
|
2071
|
-
const _traceName = this.currentRunningTest.artifacts.trace.replace('.json', '.passed.json')
|
|
2072
|
-
fs.renameSync(this.currentRunningTest.artifacts.trace, _traceName)
|
|
2073
|
-
test.artifacts.trace = _traceName
|
|
2012
|
+
const _traceName = this.currentRunningTest.artifacts.trace.replace('.json', '.passed.json');
|
|
2013
|
+
fs.renameSync(this.currentRunningTest.artifacts.trace, _traceName);
|
|
2014
|
+
test.artifacts.trace = _traceName;
|
|
2074
2015
|
} else {
|
|
2075
|
-
fs.unlinkSync(this.currentRunningTest.artifacts.trace)
|
|
2016
|
+
fs.unlinkSync(this.currentRunningTest.artifacts.trace);
|
|
2076
2017
|
}
|
|
2077
2018
|
}
|
|
2078
2019
|
}
|
|
@@ -2081,74 +2022,70 @@ class Puppeteer extends Helper {
|
|
|
2081
2022
|
* {{> wait }}
|
|
2082
2023
|
*/
|
|
2083
2024
|
async wait(sec) {
|
|
2084
|
-
return new Promise((done) => {
|
|
2085
|
-
setTimeout(done, sec * 1000)
|
|
2086
|
-
})
|
|
2025
|
+
return new Promise(((done) => {
|
|
2026
|
+
setTimeout(done, sec * 1000);
|
|
2027
|
+
}));
|
|
2087
2028
|
}
|
|
2088
2029
|
|
|
2089
2030
|
/**
|
|
2090
2031
|
* {{> waitForEnabled }}
|
|
2091
2032
|
*/
|
|
2092
2033
|
async waitForEnabled(locator, sec) {
|
|
2093
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2094
|
-
locator = new Locator(locator, 'css')
|
|
2095
|
-
await this.context
|
|
2096
|
-
let waiter
|
|
2097
|
-
const context = await this._getContext()
|
|
2034
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2035
|
+
locator = new Locator(locator, 'css');
|
|
2036
|
+
await this.context;
|
|
2037
|
+
let waiter;
|
|
2038
|
+
const context = await this._getContext();
|
|
2098
2039
|
if (locator.isCSS()) {
|
|
2099
2040
|
const enabledFn = function (locator) {
|
|
2100
|
-
const els = document.querySelectorAll(locator)
|
|
2041
|
+
const els = document.querySelectorAll(locator);
|
|
2101
2042
|
if (!els || els.length === 0) {
|
|
2102
|
-
return false
|
|
2043
|
+
return false;
|
|
2103
2044
|
}
|
|
2104
|
-
return Array.prototype.filter.call(els,
|
|
2105
|
-
}
|
|
2106
|
-
waiter = context.waitForFunction(enabledFn, { timeout: waitTimeout }, locator.value)
|
|
2045
|
+
return Array.prototype.filter.call(els, el => !el.disabled).length > 0;
|
|
2046
|
+
};
|
|
2047
|
+
waiter = context.waitForFunction(enabledFn, { timeout: waitTimeout }, locator.value);
|
|
2107
2048
|
} else {
|
|
2108
2049
|
const enabledFn = function (locator, $XPath) {
|
|
2109
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
2110
|
-
return $XPath(null, locator).filter(
|
|
2111
|
-
}
|
|
2112
|
-
waiter = context.waitForFunction(enabledFn, { timeout: waitTimeout }, locator.value, $XPath.toString())
|
|
2050
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2051
|
+
return $XPath(null, locator).filter(el => !el.disabled).length > 0;
|
|
2052
|
+
};
|
|
2053
|
+
waiter = context.waitForFunction(enabledFn, { timeout: waitTimeout }, locator.value, $XPath.toString());
|
|
2113
2054
|
}
|
|
2114
2055
|
return waiter.catch((err) => {
|
|
2115
|
-
throw new Error(
|
|
2116
|
-
|
|
2117
|
-
)
|
|
2118
|
-
})
|
|
2056
|
+
throw new Error(`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2057
|
+
});
|
|
2119
2058
|
}
|
|
2120
2059
|
|
|
2121
2060
|
/**
|
|
2122
2061
|
* {{> waitForValue }}
|
|
2123
2062
|
*/
|
|
2124
2063
|
async waitForValue(field, value, sec) {
|
|
2125
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2126
|
-
const locator = new Locator(field, 'css')
|
|
2127
|
-
await this.context
|
|
2128
|
-
let waiter
|
|
2129
|
-
const context = await this._getContext()
|
|
2064
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2065
|
+
const locator = new Locator(field, 'css');
|
|
2066
|
+
await this.context;
|
|
2067
|
+
let waiter;
|
|
2068
|
+
const context = await this._getContext();
|
|
2130
2069
|
if (locator.isCSS()) {
|
|
2131
2070
|
const valueFn = function (locator, value) {
|
|
2132
|
-
const els = document.querySelectorAll(locator)
|
|
2071
|
+
const els = document.querySelectorAll(locator);
|
|
2133
2072
|
if (!els || els.length === 0) {
|
|
2134
|
-
return false
|
|
2073
|
+
return false;
|
|
2135
2074
|
}
|
|
2136
|
-
return Array.prototype.filter.call(els,
|
|
2137
|
-
}
|
|
2138
|
-
waiter = context.waitForFunction(valueFn, { timeout: waitTimeout }, locator.value, value)
|
|
2075
|
+
return Array.prototype.filter.call(els, el => (el.value.toString() || '').indexOf(value) !== -1).length > 0;
|
|
2076
|
+
};
|
|
2077
|
+
waiter = context.waitForFunction(valueFn, { timeout: waitTimeout }, locator.value, value);
|
|
2139
2078
|
} else {
|
|
2140
2079
|
const valueFn = function (locator, $XPath, value) {
|
|
2141
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
2142
|
-
return $XPath(null, locator).filter(
|
|
2143
|
-
}
|
|
2144
|
-
waiter = context.waitForFunction(valueFn, { timeout: waitTimeout }, locator.value, $XPath.toString(), value)
|
|
2080
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2081
|
+
return $XPath(null, locator).filter(el => (el.value || '').indexOf(value) !== -1).length > 0;
|
|
2082
|
+
};
|
|
2083
|
+
waiter = context.waitForFunction(valueFn, { timeout: waitTimeout }, locator.value, $XPath.toString(), value);
|
|
2145
2084
|
}
|
|
2146
2085
|
return waiter.catch((err) => {
|
|
2147
|
-
const loc = locator.toString()
|
|
2148
|
-
throw new Error(
|
|
2149
|
-
|
|
2150
|
-
)
|
|
2151
|
-
})
|
|
2086
|
+
const loc = locator.toString();
|
|
2087
|
+
throw new Error(`element (${loc}) is not in DOM or there is no element(${loc}) with value "${value}" after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2088
|
+
});
|
|
2152
2089
|
}
|
|
2153
2090
|
|
|
2154
2091
|
/**
|
|
@@ -2156,49 +2093,45 @@ class Puppeteer extends Helper {
|
|
|
2156
2093
|
* {{ react }}
|
|
2157
2094
|
*/
|
|
2158
2095
|
async waitNumberOfVisibleElements(locator, num, sec) {
|
|
2159
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2160
|
-
locator = new Locator(locator, 'css')
|
|
2161
|
-
let waiter
|
|
2162
|
-
const context = await this._getContext()
|
|
2096
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2097
|
+
locator = new Locator(locator, 'css');
|
|
2098
|
+
let waiter;
|
|
2099
|
+
const context = await this._getContext();
|
|
2163
2100
|
if (locator.isCSS()) {
|
|
2164
2101
|
const visibleFn = function (locator, num) {
|
|
2165
|
-
const els = document.querySelectorAll(locator)
|
|
2102
|
+
const els = document.querySelectorAll(locator);
|
|
2166
2103
|
if (!els || els.length === 0) {
|
|
2167
|
-
return false
|
|
2104
|
+
return false;
|
|
2168
2105
|
}
|
|
2169
|
-
return Array.prototype.filter.call(els,
|
|
2170
|
-
}
|
|
2171
|
-
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, num)
|
|
2106
|
+
return Array.prototype.filter.call(els, el => el.offsetParent !== null).length === num;
|
|
2107
|
+
};
|
|
2108
|
+
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, num);
|
|
2172
2109
|
} else {
|
|
2173
2110
|
const visibleFn = function (locator, $XPath, num) {
|
|
2174
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
2175
|
-
return $XPath(null, locator).filter(
|
|
2176
|
-
}
|
|
2177
|
-
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, $XPath.toString(), num)
|
|
2111
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2112
|
+
return $XPath(null, locator).filter(el => el.offsetParent !== null).length === num;
|
|
2113
|
+
};
|
|
2114
|
+
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, $XPath.toString(), num);
|
|
2178
2115
|
}
|
|
2179
2116
|
return waiter.catch((err) => {
|
|
2180
|
-
throw new Error(
|
|
2181
|
-
|
|
2182
|
-
)
|
|
2183
|
-
})
|
|
2117
|
+
throw new Error(`The number of elements (${locator.toString()}) is not ${num} after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2118
|
+
});
|
|
2184
2119
|
}
|
|
2185
2120
|
|
|
2186
2121
|
/**
|
|
2187
2122
|
* {{> waitForClickable }}
|
|
2188
2123
|
*/
|
|
2189
2124
|
async waitForClickable(locator, waitTimeout) {
|
|
2190
|
-
const els = await this._locate(locator)
|
|
2191
|
-
assertElementExists(els, locator)
|
|
2125
|
+
const els = await this._locate(locator);
|
|
2126
|
+
assertElementExists(els, locator);
|
|
2192
2127
|
|
|
2193
2128
|
return this.waitForFunction(isElementClickable, [els[0]], waitTimeout).catch(async (e) => {
|
|
2194
2129
|
if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
|
|
2195
|
-
throw new Error(
|
|
2196
|
-
`element ${new Locator(locator).toString()} still not clickable after ${waitTimeout || this.options.waitForTimeout / 1000} sec`,
|
|
2197
|
-
)
|
|
2130
|
+
throw new Error(`element ${new Locator(locator).toString()} still not clickable after ${waitTimeout || this.options.waitForTimeout / 1000} sec`);
|
|
2198
2131
|
} else {
|
|
2199
|
-
throw e
|
|
2132
|
+
throw e;
|
|
2200
2133
|
}
|
|
2201
|
-
})
|
|
2134
|
+
});
|
|
2202
2135
|
}
|
|
2203
2136
|
|
|
2204
2137
|
/**
|
|
@@ -2206,21 +2139,19 @@ class Puppeteer extends Helper {
|
|
|
2206
2139
|
* {{ react }}
|
|
2207
2140
|
*/
|
|
2208
2141
|
async waitForElement(locator, sec) {
|
|
2209
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2210
|
-
locator = new Locator(locator, 'css')
|
|
2142
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2143
|
+
locator = new Locator(locator, 'css');
|
|
2211
2144
|
|
|
2212
|
-
let waiter
|
|
2213
|
-
const context = await this._getContext()
|
|
2145
|
+
let waiter;
|
|
2146
|
+
const context = await this._getContext();
|
|
2214
2147
|
if (locator.isCSS()) {
|
|
2215
|
-
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout })
|
|
2148
|
+
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout });
|
|
2216
2149
|
} else {
|
|
2217
|
-
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout })
|
|
2150
|
+
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout });
|
|
2218
2151
|
}
|
|
2219
2152
|
return waiter.catch((err) => {
|
|
2220
|
-
throw new Error(
|
|
2221
|
-
|
|
2222
|
-
)
|
|
2223
|
-
})
|
|
2153
|
+
throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2154
|
+
});
|
|
2224
2155
|
}
|
|
2225
2156
|
|
|
2226
2157
|
/**
|
|
@@ -2229,191 +2160,160 @@ class Puppeteer extends Helper {
|
|
|
2229
2160
|
* {{ react }}
|
|
2230
2161
|
*/
|
|
2231
2162
|
async waitForVisible(locator, sec) {
|
|
2232
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2233
|
-
locator = new Locator(locator, 'css')
|
|
2234
|
-
await this.context
|
|
2235
|
-
let waiter
|
|
2236
|
-
const context = await this._getContext()
|
|
2163
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2164
|
+
locator = new Locator(locator, 'css');
|
|
2165
|
+
await this.context;
|
|
2166
|
+
let waiter;
|
|
2167
|
+
const context = await this._getContext();
|
|
2237
2168
|
if (locator.isCSS()) {
|
|
2238
|
-
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, visible: true })
|
|
2169
|
+
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, visible: true });
|
|
2239
2170
|
} else {
|
|
2240
|
-
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, visible: true })
|
|
2171
|
+
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, visible: true });
|
|
2241
2172
|
}
|
|
2242
2173
|
return waiter.catch((err) => {
|
|
2243
|
-
throw new Error(
|
|
2244
|
-
|
|
2245
|
-
)
|
|
2246
|
-
})
|
|
2174
|
+
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2175
|
+
});
|
|
2247
2176
|
}
|
|
2248
2177
|
|
|
2249
2178
|
/**
|
|
2250
2179
|
* {{> waitForInvisible }}
|
|
2251
2180
|
*/
|
|
2252
2181
|
async waitForInvisible(locator, sec) {
|
|
2253
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2254
|
-
locator = new Locator(locator, 'css')
|
|
2255
|
-
await this.context
|
|
2256
|
-
let waiter
|
|
2257
|
-
const context = await this._getContext()
|
|
2182
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2183
|
+
locator = new Locator(locator, 'css');
|
|
2184
|
+
await this.context;
|
|
2185
|
+
let waiter;
|
|
2186
|
+
const context = await this._getContext();
|
|
2258
2187
|
if (locator.isCSS()) {
|
|
2259
|
-
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true })
|
|
2188
|
+
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
|
|
2260
2189
|
} else {
|
|
2261
|
-
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true })
|
|
2190
|
+
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true });
|
|
2262
2191
|
}
|
|
2263
2192
|
return waiter.catch((err) => {
|
|
2264
|
-
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2265
|
-
})
|
|
2193
|
+
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2194
|
+
});
|
|
2266
2195
|
}
|
|
2267
2196
|
|
|
2268
2197
|
/**
|
|
2269
2198
|
* {{> waitToHide }}
|
|
2270
2199
|
*/
|
|
2271
2200
|
async waitToHide(locator, sec) {
|
|
2272
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2273
|
-
locator = new Locator(locator, 'css')
|
|
2274
|
-
let waiter
|
|
2275
|
-
const context = await this._getContext()
|
|
2201
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2202
|
+
locator = new Locator(locator, 'css');
|
|
2203
|
+
let waiter;
|
|
2204
|
+
const context = await this._getContext();
|
|
2276
2205
|
if (locator.isCSS()) {
|
|
2277
|
-
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true })
|
|
2206
|
+
waiter = context.waitForSelector(locator.simplify(), { timeout: waitTimeout, hidden: true });
|
|
2278
2207
|
} else {
|
|
2279
|
-
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true })
|
|
2208
|
+
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true });
|
|
2280
2209
|
}
|
|
2281
2210
|
return waiter.catch((err) => {
|
|
2282
|
-
throw new Error(
|
|
2283
|
-
|
|
2284
|
-
)
|
|
2285
|
-
})
|
|
2211
|
+
throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2212
|
+
});
|
|
2286
2213
|
}
|
|
2287
2214
|
|
|
2288
2215
|
/**
|
|
2289
2216
|
* {{> waitForNumberOfTabs }}
|
|
2290
2217
|
*/
|
|
2291
2218
|
async waitForNumberOfTabs(expectedTabs, sec) {
|
|
2292
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2293
|
-
let currentTabs
|
|
2294
|
-
let count = 0
|
|
2219
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2220
|
+
let currentTabs;
|
|
2221
|
+
let count = 0;
|
|
2295
2222
|
|
|
2296
2223
|
do {
|
|
2297
|
-
currentTabs = await this.grabNumberOfOpenTabs()
|
|
2298
|
-
await this.wait(1)
|
|
2299
|
-
count += 1000
|
|
2300
|
-
if (currentTabs >= expectedTabs) return
|
|
2301
|
-
} while (count <= waitTimeout)
|
|
2224
|
+
currentTabs = await this.grabNumberOfOpenTabs();
|
|
2225
|
+
await this.wait(1);
|
|
2226
|
+
count += 1000;
|
|
2227
|
+
if (currentTabs >= expectedTabs) return;
|
|
2228
|
+
} while (count <= waitTimeout);
|
|
2302
2229
|
|
|
2303
|
-
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`)
|
|
2230
|
+
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`);
|
|
2304
2231
|
}
|
|
2305
2232
|
|
|
2306
2233
|
async _getContext() {
|
|
2307
2234
|
if (this.context && this.context.constructor.name === 'CdpFrame') {
|
|
2308
|
-
return this.context
|
|
2235
|
+
return this.context;
|
|
2309
2236
|
}
|
|
2310
|
-
return this.page
|
|
2237
|
+
return this.page;
|
|
2311
2238
|
}
|
|
2312
2239
|
|
|
2313
2240
|
/**
|
|
2314
2241
|
* {{> waitInUrl }}
|
|
2315
2242
|
*/
|
|
2316
2243
|
async waitInUrl(urlPart, sec = null) {
|
|
2317
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2318
|
-
|
|
2319
|
-
return this.page
|
|
2320
|
-
.
|
|
2321
|
-
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
{
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
if (/Waiting failed:/i.test(e.message) || /failed: timeout/i.test(e.message)) {
|
|
2331
|
-
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
|
|
2332
|
-
} else {
|
|
2333
|
-
throw e
|
|
2334
|
-
}
|
|
2335
|
-
})
|
|
2244
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2245
|
+
|
|
2246
|
+
return this.page.waitForFunction((urlPart) => {
|
|
2247
|
+
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)));
|
|
2248
|
+
return currUrl.indexOf(urlPart) > -1;
|
|
2249
|
+
}, { timeout: waitTimeout }, urlPart).catch(async (e) => {
|
|
2250
|
+
const currUrl = await this._getPageUrl(); // Required because the waitForFunction can't return data.
|
|
2251
|
+
if (/Waiting failed:/i.test(e.message) || /failed: timeout/i.test(e.message)) {
|
|
2252
|
+
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`);
|
|
2253
|
+
} else {
|
|
2254
|
+
throw e;
|
|
2255
|
+
}
|
|
2256
|
+
});
|
|
2336
2257
|
}
|
|
2337
2258
|
|
|
2338
2259
|
/**
|
|
2339
2260
|
* {{> waitUrlEquals }}
|
|
2340
2261
|
*/
|
|
2341
2262
|
async waitUrlEquals(urlPart, sec = null) {
|
|
2342
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2263
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2343
2264
|
|
|
2344
|
-
const baseUrl = this.options.url
|
|
2265
|
+
const baseUrl = this.options.url;
|
|
2345
2266
|
if (urlPart.indexOf('http') < 0) {
|
|
2346
|
-
urlPart = baseUrl + urlPart
|
|
2347
|
-
}
|
|
2348
|
-
|
|
2349
|
-
return this.page
|
|
2350
|
-
.
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
{
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2360
|
-
if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
|
|
2361
|
-
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
|
|
2362
|
-
} else {
|
|
2363
|
-
throw e
|
|
2364
|
-
}
|
|
2365
|
-
})
|
|
2267
|
+
urlPart = baseUrl + urlPart;
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
return this.page.waitForFunction((urlPart) => {
|
|
2271
|
+
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)));
|
|
2272
|
+
return currUrl.indexOf(urlPart) > -1;
|
|
2273
|
+
}, { timeout: waitTimeout }, urlPart).catch(async (e) => {
|
|
2274
|
+
const currUrl = await this._getPageUrl(); // Required because the waitForFunction can't return data.
|
|
2275
|
+
if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
|
|
2276
|
+
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`);
|
|
2277
|
+
} else {
|
|
2278
|
+
throw e;
|
|
2279
|
+
}
|
|
2280
|
+
});
|
|
2366
2281
|
}
|
|
2367
2282
|
|
|
2368
2283
|
/**
|
|
2369
2284
|
* {{> waitForText }}
|
|
2370
2285
|
*/
|
|
2371
2286
|
async waitForText(text, sec = null, context = null) {
|
|
2372
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2373
|
-
let waiter
|
|
2287
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2288
|
+
let waiter;
|
|
2374
2289
|
|
|
2375
|
-
const contextObject = await this._getContext()
|
|
2290
|
+
const contextObject = await this._getContext();
|
|
2376
2291
|
|
|
2377
2292
|
if (context) {
|
|
2378
|
-
const locator = new Locator(context, 'css')
|
|
2293
|
+
const locator = new Locator(context, 'css');
|
|
2379
2294
|
if (locator.isCSS()) {
|
|
2380
|
-
waiter = contextObject.waitForFunction(
|
|
2381
|
-
(locator
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2385
|
-
},
|
|
2386
|
-
{ timeout: waitTimeout },
|
|
2387
|
-
locator.value,
|
|
2388
|
-
text,
|
|
2389
|
-
)
|
|
2295
|
+
waiter = contextObject.waitForFunction((locator, text) => {
|
|
2296
|
+
const el = document.querySelector(locator);
|
|
2297
|
+
if (!el) return false;
|
|
2298
|
+
return el.innerText.indexOf(text) > -1;
|
|
2299
|
+
}, { timeout: waitTimeout }, locator.value, text);
|
|
2390
2300
|
}
|
|
2391
2301
|
|
|
2392
2302
|
if (locator.isXPath()) {
|
|
2393
|
-
waiter = contextObject.waitForFunction(
|
|
2394
|
-
(
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
},
|
|
2400
|
-
{ timeout: waitTimeout },
|
|
2401
|
-
locator.value,
|
|
2402
|
-
text,
|
|
2403
|
-
$XPath.toString(),
|
|
2404
|
-
)
|
|
2303
|
+
waiter = contextObject.waitForFunction((locator, text, $XPath) => {
|
|
2304
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2305
|
+
const el = $XPath(null, locator);
|
|
2306
|
+
if (!el.length) return false;
|
|
2307
|
+
return el[0].innerText.indexOf(text) > -1;
|
|
2308
|
+
}, { timeout: waitTimeout }, locator.value, text, $XPath.toString());
|
|
2405
2309
|
}
|
|
2406
2310
|
} else {
|
|
2407
|
-
waiter = contextObject.waitForFunction(
|
|
2408
|
-
(text) => document.body && document.body.innerText.indexOf(text) > -1,
|
|
2409
|
-
{ timeout: waitTimeout },
|
|
2410
|
-
text,
|
|
2411
|
-
)
|
|
2311
|
+
waiter = contextObject.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, { timeout: waitTimeout }, text);
|
|
2412
2312
|
}
|
|
2413
2313
|
|
|
2414
2314
|
return waiter.catch((err) => {
|
|
2415
|
-
throw new Error(`Text "${text}" was not found on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2416
|
-
})
|
|
2315
|
+
throw new Error(`Text "${text}" was not found on page after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2316
|
+
});
|
|
2417
2317
|
}
|
|
2418
2318
|
|
|
2419
2319
|
/**
|
|
@@ -2428,8 +2328,8 @@ class Puppeteer extends Helper {
|
|
|
2428
2328
|
* @param {?number} [sec=null] seconds to wait
|
|
2429
2329
|
*/
|
|
2430
2330
|
async waitForRequest(urlOrPredicate, sec = null) {
|
|
2431
|
-
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2432
|
-
return this.page.waitForRequest(urlOrPredicate, { timeout })
|
|
2331
|
+
const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2332
|
+
return this.page.waitForRequest(urlOrPredicate, { timeout });
|
|
2433
2333
|
}
|
|
2434
2334
|
|
|
2435
2335
|
/**
|
|
@@ -2444,8 +2344,8 @@ class Puppeteer extends Helper {
|
|
|
2444
2344
|
* @param {?number} [sec=null] number of seconds to wait
|
|
2445
2345
|
*/
|
|
2446
2346
|
async waitForResponse(urlOrPredicate, sec = null) {
|
|
2447
|
-
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2448
|
-
return this.page.waitForResponse(urlOrPredicate, { timeout })
|
|
2347
|
+
const timeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2348
|
+
return this.page.waitForResponse(urlOrPredicate, { timeout });
|
|
2449
2349
|
}
|
|
2450
2350
|
|
|
2451
2351
|
/**
|
|
@@ -2454,37 +2354,37 @@ class Puppeteer extends Helper {
|
|
|
2454
2354
|
async switchTo(locator) {
|
|
2455
2355
|
if (Number.isInteger(locator)) {
|
|
2456
2356
|
// Select by frame index of current context
|
|
2457
|
-
let frames = []
|
|
2357
|
+
let frames = [];
|
|
2458
2358
|
if (this.context && typeof this.context.childFrames === 'function') {
|
|
2459
|
-
frames = await this.context.childFrames()
|
|
2359
|
+
frames = await this.context.childFrames();
|
|
2460
2360
|
} else {
|
|
2461
|
-
frames = await this.page.mainFrame().childFrames()
|
|
2361
|
+
frames = await this.page.mainFrame().childFrames();
|
|
2462
2362
|
}
|
|
2463
2363
|
|
|
2464
2364
|
if (locator >= 0 && locator < frames.length) {
|
|
2465
|
-
this.context = frames[locator]
|
|
2365
|
+
this.context = frames[locator];
|
|
2466
2366
|
} else {
|
|
2467
|
-
throw new Error('Frame index out of bounds')
|
|
2367
|
+
throw new Error('Frame index out of bounds');
|
|
2468
2368
|
}
|
|
2469
|
-
return
|
|
2369
|
+
return;
|
|
2470
2370
|
}
|
|
2471
2371
|
|
|
2472
2372
|
if (!locator) {
|
|
2473
|
-
this.context = await this.page.mainFrame()
|
|
2474
|
-
return
|
|
2373
|
+
this.context = await this.page.mainFrame();
|
|
2374
|
+
return;
|
|
2475
2375
|
}
|
|
2476
2376
|
|
|
2477
2377
|
// Select iframe by selector
|
|
2478
|
-
const els = await this._locate(locator)
|
|
2479
|
-
assertElementExists(els, locator)
|
|
2378
|
+
const els = await this._locate(locator);
|
|
2379
|
+
assertElementExists(els, locator);
|
|
2480
2380
|
|
|
2481
|
-
const iframeElement = els[0]
|
|
2482
|
-
const contentFrame = await iframeElement.contentFrame()
|
|
2381
|
+
const iframeElement = els[0];
|
|
2382
|
+
const contentFrame = await iframeElement.contentFrame();
|
|
2483
2383
|
|
|
2484
2384
|
if (contentFrame) {
|
|
2485
|
-
this.context = contentFrame
|
|
2385
|
+
this.context = contentFrame;
|
|
2486
2386
|
} else {
|
|
2487
|
-
throw new Error('Element "#invalidIframeSelector" was not found by text|CSS|XPath')
|
|
2387
|
+
throw new Error('Element "#invalidIframeSelector" was not found by text|CSS|XPath');
|
|
2488
2388
|
}
|
|
2489
2389
|
}
|
|
2490
2390
|
|
|
@@ -2492,17 +2392,17 @@ class Puppeteer extends Helper {
|
|
|
2492
2392
|
* {{> waitForFunction }}
|
|
2493
2393
|
*/
|
|
2494
2394
|
async waitForFunction(fn, argsOrSec = null, sec = null) {
|
|
2495
|
-
let args = []
|
|
2395
|
+
let args = [];
|
|
2496
2396
|
if (argsOrSec) {
|
|
2497
2397
|
if (Array.isArray(argsOrSec)) {
|
|
2498
|
-
args = argsOrSec
|
|
2398
|
+
args = argsOrSec;
|
|
2499
2399
|
} else if (typeof argsOrSec === 'number') {
|
|
2500
|
-
sec = argsOrSec
|
|
2400
|
+
sec = argsOrSec;
|
|
2501
2401
|
}
|
|
2502
2402
|
}
|
|
2503
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2504
|
-
const context = await this._getContext()
|
|
2505
|
-
return context.waitForFunction(fn, { timeout: waitTimeout }, ...args)
|
|
2403
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2404
|
+
const context = await this._getContext();
|
|
2405
|
+
return context.waitForFunction(fn, { timeout: waitTimeout }, ...args);
|
|
2506
2406
|
}
|
|
2507
2407
|
|
|
2508
2408
|
/**
|
|
@@ -2517,87 +2417,87 @@ class Puppeteer extends Helper {
|
|
|
2517
2417
|
timeout: this.options.getPageTimeout,
|
|
2518
2418
|
waitUntil: this.options.waitForNavigation,
|
|
2519
2419
|
...opts,
|
|
2520
|
-
}
|
|
2521
|
-
return this.page.waitForNavigation(opts)
|
|
2420
|
+
};
|
|
2421
|
+
return this.page.waitForNavigation(opts);
|
|
2522
2422
|
}
|
|
2523
2423
|
|
|
2524
2424
|
async waitUntilExists(locator, sec) {
|
|
2525
2425
|
console.log(`waitUntilExists deprecated:
|
|
2526
2426
|
* use 'waitForElement' to wait for element to be attached
|
|
2527
|
-
* use 'waitForDetached to wait for element to be removed'`)
|
|
2528
|
-
return this.waitForDetached(locator, sec)
|
|
2427
|
+
* use 'waitForDetached to wait for element to be removed'`);
|
|
2428
|
+
return this.waitForDetached(locator, sec);
|
|
2529
2429
|
}
|
|
2530
2430
|
|
|
2531
2431
|
/**
|
|
2532
2432
|
* {{> waitForDetached }}
|
|
2533
2433
|
*/
|
|
2534
2434
|
async waitForDetached(locator, sec) {
|
|
2535
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2536
|
-
locator = new Locator(locator, 'css')
|
|
2435
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout;
|
|
2436
|
+
locator = new Locator(locator, 'css');
|
|
2537
2437
|
|
|
2538
|
-
let waiter
|
|
2539
|
-
const context = await this._getContext()
|
|
2438
|
+
let waiter;
|
|
2439
|
+
const context = await this._getContext();
|
|
2540
2440
|
if (locator.isCSS()) {
|
|
2541
2441
|
const visibleFn = function (locator) {
|
|
2542
|
-
return document.querySelector(locator) === null
|
|
2543
|
-
}
|
|
2544
|
-
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value)
|
|
2442
|
+
return document.querySelector(locator) === null;
|
|
2443
|
+
};
|
|
2444
|
+
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value);
|
|
2545
2445
|
} else {
|
|
2546
2446
|
const visibleFn = function (locator, $XPath) {
|
|
2547
|
-
eval($XPath) // eslint-disable-line no-eval
|
|
2548
|
-
return $XPath(null, locator).length === 0
|
|
2549
|
-
}
|
|
2550
|
-
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, $XPath.toString())
|
|
2447
|
+
eval($XPath); // eslint-disable-line no-eval
|
|
2448
|
+
return $XPath(null, locator).length === 0;
|
|
2449
|
+
};
|
|
2450
|
+
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, $XPath.toString());
|
|
2551
2451
|
}
|
|
2552
2452
|
return waiter.catch((err) => {
|
|
2553
|
-
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2554
|
-
})
|
|
2453
|
+
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`);
|
|
2454
|
+
});
|
|
2555
2455
|
}
|
|
2556
2456
|
|
|
2557
2457
|
async _waitForAction() {
|
|
2558
|
-
return this.wait(this.options.waitForAction / 1000)
|
|
2458
|
+
return this.wait(this.options.waitForAction / 1000);
|
|
2559
2459
|
}
|
|
2560
2460
|
|
|
2561
2461
|
/**
|
|
2562
2462
|
* {{> grabDataFromPerformanceTiming }}
|
|
2563
2463
|
*/
|
|
2564
2464
|
async grabDataFromPerformanceTiming() {
|
|
2565
|
-
return perfTiming
|
|
2465
|
+
return perfTiming;
|
|
2566
2466
|
}
|
|
2567
2467
|
|
|
2568
2468
|
/**
|
|
2569
2469
|
* {{> grabElementBoundingRect }}
|
|
2570
2470
|
*/
|
|
2571
2471
|
async grabElementBoundingRect(locator, prop) {
|
|
2572
|
-
const els = await this._locate(locator)
|
|
2573
|
-
assertElementExists(els, locator)
|
|
2574
|
-
const rect = await els[0].boundingBox()
|
|
2575
|
-
if (prop) return rect[prop]
|
|
2576
|
-
return rect
|
|
2472
|
+
const els = await this._locate(locator);
|
|
2473
|
+
assertElementExists(els, locator);
|
|
2474
|
+
const rect = await els[0].boundingBox();
|
|
2475
|
+
if (prop) return rect[prop];
|
|
2476
|
+
return rect;
|
|
2577
2477
|
}
|
|
2578
2478
|
|
|
2579
2479
|
/**
|
|
2580
|
-
* Mocks network request using [`Request Interception`](https://pptr.dev/guides/
|
|
2480
|
+
* Mocks network request using [`Request Interception`](https://pptr.dev/next/guides/request-interception)
|
|
2581
2481
|
*
|
|
2582
2482
|
* ```js
|
|
2583
2483
|
* I.mockRoute(/(\.png$)|(\.jpg$)/, route => route.abort());
|
|
2584
2484
|
* ```
|
|
2585
|
-
* This method allows intercepting and mocking requests & responses. [Learn more about it](https://pptr.dev/guides/
|
|
2485
|
+
* This method allows intercepting and mocking requests & responses. [Learn more about it](https://pptr.dev/next/guides/request-interception)
|
|
2586
2486
|
*
|
|
2587
2487
|
* @param {string|RegExp} [url] URL, regex or pattern for to match URL
|
|
2588
2488
|
* @param {function} [handler] a function to process request
|
|
2589
2489
|
*/
|
|
2590
2490
|
async mockRoute(url, handler) {
|
|
2591
|
-
await this.page.setRequestInterception(true)
|
|
2491
|
+
await this.page.setRequestInterception(true);
|
|
2592
2492
|
|
|
2593
|
-
this.page.on('request',
|
|
2493
|
+
this.page.on('request', interceptedRequest => {
|
|
2594
2494
|
if (interceptedRequest.url().match(url)) {
|
|
2595
2495
|
// @ts-ignore
|
|
2596
|
-
handler(interceptedRequest)
|
|
2496
|
+
handler(interceptedRequest);
|
|
2597
2497
|
} else {
|
|
2598
|
-
interceptedRequest.continue()
|
|
2498
|
+
interceptedRequest.continue();
|
|
2599
2499
|
}
|
|
2600
|
-
})
|
|
2500
|
+
});
|
|
2601
2501
|
}
|
|
2602
2502
|
|
|
2603
2503
|
/**
|
|
@@ -2610,18 +2510,18 @@ class Puppeteer extends Helper {
|
|
|
2610
2510
|
* @param {string|RegExp} [url] URL, regex or pattern for to match URL
|
|
2611
2511
|
*/
|
|
2612
2512
|
async stopMockingRoute(url) {
|
|
2613
|
-
await this.page.setRequestInterception(true)
|
|
2513
|
+
await this.page.setRequestInterception(true);
|
|
2614
2514
|
|
|
2615
|
-
this.page.off('request')
|
|
2515
|
+
this.page.off('request');
|
|
2616
2516
|
|
|
2617
2517
|
// Resume normal request handling for the given URL
|
|
2618
|
-
this.page.on('request',
|
|
2518
|
+
this.page.on('request', interceptedRequest => {
|
|
2619
2519
|
if (interceptedRequest.url().includes(url)) {
|
|
2620
|
-
interceptedRequest.continue()
|
|
2520
|
+
interceptedRequest.continue();
|
|
2621
2521
|
} else {
|
|
2622
|
-
interceptedRequest.continue()
|
|
2522
|
+
interceptedRequest.continue();
|
|
2623
2523
|
}
|
|
2624
|
-
})
|
|
2524
|
+
});
|
|
2625
2525
|
}
|
|
2626
2526
|
|
|
2627
2527
|
/**
|
|
@@ -2629,7 +2529,7 @@ class Puppeteer extends Helper {
|
|
|
2629
2529
|
* {{> flushNetworkTraffics }}
|
|
2630
2530
|
*/
|
|
2631
2531
|
flushNetworkTraffics() {
|
|
2632
|
-
flushNetworkTraffics.call(this)
|
|
2532
|
+
flushNetworkTraffics.call(this);
|
|
2633
2533
|
}
|
|
2634
2534
|
|
|
2635
2535
|
/**
|
|
@@ -2637,7 +2537,7 @@ class Puppeteer extends Helper {
|
|
|
2637
2537
|
* {{> stopRecordingTraffic }}
|
|
2638
2538
|
*/
|
|
2639
2539
|
stopRecordingTraffic() {
|
|
2640
|
-
stopRecordingTraffic.call(this)
|
|
2540
|
+
stopRecordingTraffic.call(this);
|
|
2641
2541
|
}
|
|
2642
2542
|
|
|
2643
2543
|
/**
|
|
@@ -2645,11 +2545,11 @@ class Puppeteer extends Helper {
|
|
|
2645
2545
|
*
|
|
2646
2546
|
*/
|
|
2647
2547
|
async startRecordingTraffic() {
|
|
2648
|
-
this.flushNetworkTraffics()
|
|
2649
|
-
this.recording = true
|
|
2650
|
-
this.recordedAtLeastOnce = true
|
|
2548
|
+
this.flushNetworkTraffics();
|
|
2549
|
+
this.recording = true;
|
|
2550
|
+
this.recordedAtLeastOnce = true;
|
|
2651
2551
|
|
|
2652
|
-
await this.page.setRequestInterception(true)
|
|
2552
|
+
await this.page.setRequestInterception(true);
|
|
2653
2553
|
|
|
2654
2554
|
this.page.on('request', (request) => {
|
|
2655
2555
|
const information = {
|
|
@@ -2658,16 +2558,16 @@ class Puppeteer extends Helper {
|
|
|
2658
2558
|
requestHeaders: request.headers(),
|
|
2659
2559
|
requestPostData: request.postData(),
|
|
2660
2560
|
response: request.response(),
|
|
2661
|
-
}
|
|
2561
|
+
};
|
|
2662
2562
|
|
|
2663
|
-
this.debugSection('REQUEST: ', JSON.stringify(information))
|
|
2563
|
+
this.debugSection('REQUEST: ', JSON.stringify(information));
|
|
2664
2564
|
|
|
2665
2565
|
if (typeof information.requestPostData === 'object') {
|
|
2666
|
-
information.requestPostData = JSON.parse(information.requestPostData)
|
|
2566
|
+
information.requestPostData = JSON.parse(information.requestPostData);
|
|
2667
2567
|
}
|
|
2668
|
-
request.continue()
|
|
2669
|
-
this.requests.push(information)
|
|
2670
|
-
})
|
|
2568
|
+
request.continue();
|
|
2569
|
+
this.requests.push(information);
|
|
2570
|
+
});
|
|
2671
2571
|
}
|
|
2672
2572
|
|
|
2673
2573
|
/**
|
|
@@ -2675,15 +2575,17 @@ class Puppeteer extends Helper {
|
|
|
2675
2575
|
* {{> grabRecordedNetworkTraffics }}
|
|
2676
2576
|
*/
|
|
2677
2577
|
async grabRecordedNetworkTraffics() {
|
|
2678
|
-
return grabRecordedNetworkTraffics.call(this)
|
|
2578
|
+
return grabRecordedNetworkTraffics.call(this);
|
|
2679
2579
|
}
|
|
2680
2580
|
|
|
2681
2581
|
/**
|
|
2682
2582
|
*
|
|
2683
2583
|
* {{> seeTraffic }}
|
|
2684
2584
|
*/
|
|
2685
|
-
async seeTraffic({
|
|
2686
|
-
|
|
2585
|
+
async seeTraffic({
|
|
2586
|
+
name, url, parameters, requestPostData, timeout = 10,
|
|
2587
|
+
}) {
|
|
2588
|
+
await seeTraffic.call(this, ...arguments);
|
|
2687
2589
|
}
|
|
2688
2590
|
|
|
2689
2591
|
/**
|
|
@@ -2692,47 +2594,56 @@ class Puppeteer extends Helper {
|
|
|
2692
2594
|
*
|
|
2693
2595
|
*/
|
|
2694
2596
|
dontSeeTraffic({ name, url }) {
|
|
2695
|
-
dontSeeTraffic.call(this, ...arguments)
|
|
2597
|
+
dontSeeTraffic.call(this, ...arguments);
|
|
2696
2598
|
}
|
|
2697
2599
|
|
|
2698
2600
|
async getNewCDPSession() {
|
|
2699
|
-
const client = await this.page.target().createCDPSession()
|
|
2700
|
-
return client
|
|
2601
|
+
const client = await this.page.target().createCDPSession();
|
|
2602
|
+
return client;
|
|
2701
2603
|
}
|
|
2702
2604
|
|
|
2703
2605
|
/**
|
|
2704
2606
|
* {{> startRecordingWebSocketMessages }}
|
|
2705
2607
|
*/
|
|
2706
2608
|
async startRecordingWebSocketMessages() {
|
|
2707
|
-
this.flushWebSocketMessages()
|
|
2708
|
-
this.recordingWebSocketMessages = true
|
|
2709
|
-
this.recordedWebSocketMessagesAtLeastOnce = true
|
|
2710
|
-
|
|
2711
|
-
this.cdpSession = await this.getNewCDPSession()
|
|
2712
|
-
await this.cdpSession.send('Network.enable')
|
|
2713
|
-
await this.cdpSession.send('Page.enable')
|
|
2714
|
-
|
|
2715
|
-
this.cdpSession.on(
|
|
2716
|
-
|
|
2717
|
-
|
|
2609
|
+
this.flushWebSocketMessages();
|
|
2610
|
+
this.recordingWebSocketMessages = true;
|
|
2611
|
+
this.recordedWebSocketMessagesAtLeastOnce = true;
|
|
2612
|
+
|
|
2613
|
+
this.cdpSession = await this.getNewCDPSession();
|
|
2614
|
+
await this.cdpSession.send('Network.enable');
|
|
2615
|
+
await this.cdpSession.send('Page.enable');
|
|
2616
|
+
|
|
2617
|
+
this.cdpSession.on(
|
|
2618
|
+
'Network.webSocketFrameReceived',
|
|
2619
|
+
(payload) => {
|
|
2620
|
+
this._logWebsocketMessages(this._getWebSocketLog('RECEIVED', payload));
|
|
2621
|
+
},
|
|
2622
|
+
);
|
|
2718
2623
|
|
|
2719
|
-
this.cdpSession.on(
|
|
2720
|
-
|
|
2721
|
-
|
|
2624
|
+
this.cdpSession.on(
|
|
2625
|
+
'Network.webSocketFrameSent',
|
|
2626
|
+
(payload) => {
|
|
2627
|
+
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload));
|
|
2628
|
+
},
|
|
2629
|
+
);
|
|
2722
2630
|
|
|
2723
|
-
this.cdpSession.on(
|
|
2724
|
-
|
|
2725
|
-
|
|
2631
|
+
this.cdpSession.on(
|
|
2632
|
+
'Network.webSocketFrameError',
|
|
2633
|
+
(payload) => {
|
|
2634
|
+
this._logWebsocketMessages(this._getWebSocketLog('ERROR', payload));
|
|
2635
|
+
},
|
|
2636
|
+
);
|
|
2726
2637
|
}
|
|
2727
2638
|
|
|
2728
2639
|
/**
|
|
2729
2640
|
* {{> stopRecordingWebSocketMessages }}
|
|
2730
2641
|
*/
|
|
2731
2642
|
async stopRecordingWebSocketMessages() {
|
|
2732
|
-
await this.cdpSession.send('Network.disable')
|
|
2733
|
-
await this.cdpSession.send('Page.disable')
|
|
2734
|
-
this.page.removeAllListeners('Network')
|
|
2735
|
-
this.recordingWebSocketMessages = false
|
|
2643
|
+
await this.cdpSession.send('Network.disable');
|
|
2644
|
+
await this.cdpSession.send('Page.disable');
|
|
2645
|
+
this.page.removeAllListeners('Network');
|
|
2646
|
+
this.recordingWebSocketMessages = false;
|
|
2736
2647
|
}
|
|
2737
2648
|
|
|
2738
2649
|
/**
|
|
@@ -2744,405 +2655,394 @@ class Puppeteer extends Helper {
|
|
|
2744
2655
|
grabWebSocketMessages() {
|
|
2745
2656
|
if (!this.recordingWebSocketMessages) {
|
|
2746
2657
|
if (!this.recordedWebSocketMessagesAtLeastOnce) {
|
|
2747
|
-
throw new Error(
|
|
2748
|
-
'Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.',
|
|
2749
|
-
)
|
|
2658
|
+
throw new Error('Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.');
|
|
2750
2659
|
}
|
|
2751
2660
|
}
|
|
2752
|
-
return this.webSocketMessages
|
|
2661
|
+
return this.webSocketMessages;
|
|
2753
2662
|
}
|
|
2754
2663
|
|
|
2755
2664
|
/**
|
|
2756
2665
|
* Resets all recorded WS messages.
|
|
2757
2666
|
*/
|
|
2758
2667
|
flushWebSocketMessages() {
|
|
2759
|
-
this.webSocketMessages = []
|
|
2668
|
+
this.webSocketMessages = [];
|
|
2760
2669
|
}
|
|
2761
2670
|
|
|
2762
2671
|
_getWebSocketMessage(payload) {
|
|
2763
2672
|
if (payload.errorMessage) {
|
|
2764
|
-
return payload.errorMessage
|
|
2673
|
+
return payload.errorMessage;
|
|
2765
2674
|
}
|
|
2766
2675
|
|
|
2767
|
-
return payload.response.payloadData
|
|
2676
|
+
return payload.response.payloadData;
|
|
2768
2677
|
}
|
|
2769
2678
|
|
|
2770
2679
|
_getWebSocketLog(prefix, payload) {
|
|
2771
|
-
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n
|
|
2680
|
+
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n`;
|
|
2772
2681
|
}
|
|
2773
2682
|
|
|
2774
2683
|
_logWebsocketMessages(message) {
|
|
2775
|
-
this.webSocketMessages += message
|
|
2684
|
+
this.webSocketMessages += message;
|
|
2776
2685
|
}
|
|
2777
2686
|
}
|
|
2778
2687
|
|
|
2779
|
-
|
|
2688
|
+
export default Puppeteer;
|
|
2780
2689
|
|
|
2781
2690
|
async function findElements(matcher, locator) {
|
|
2782
|
-
if (locator.react) return findReactElements.call(this, locator)
|
|
2783
|
-
locator = new Locator(locator, 'css')
|
|
2784
|
-
if (!locator.isXPath()) return matcher.$$(locator.simplify())
|
|
2691
|
+
if (locator.react) return findReactElements.call(this, locator);
|
|
2692
|
+
locator = new Locator(locator, 'css');
|
|
2693
|
+
if (!locator.isXPath()) return matcher.$$(locator.simplify());
|
|
2785
2694
|
// puppeteer version < 19.4.0 is no longer supported. This one is backward support.
|
|
2786
2695
|
if (puppeteer.default?.defaultBrowserRevision) {
|
|
2787
|
-
return matcher.$$(`xpath/${locator.value}`)
|
|
2696
|
+
return matcher.$$(`xpath/${locator.value}`);
|
|
2788
2697
|
}
|
|
2789
|
-
return matcher.$x(locator.value)
|
|
2698
|
+
return matcher.$x(locator.value);
|
|
2790
2699
|
}
|
|
2791
2700
|
|
|
2792
2701
|
async function proceedClick(locator, context = null, options = {}) {
|
|
2793
|
-
let matcher = await this.context
|
|
2702
|
+
let matcher = await this.context;
|
|
2794
2703
|
if (context) {
|
|
2795
|
-
const els = await this._locate(context)
|
|
2796
|
-
assertElementExists(els, context)
|
|
2797
|
-
matcher = els[0]
|
|
2704
|
+
const els = await this._locate(context);
|
|
2705
|
+
assertElementExists(els, context);
|
|
2706
|
+
matcher = els[0];
|
|
2798
2707
|
}
|
|
2799
|
-
const els = await findClickable.call(this, matcher, locator)
|
|
2708
|
+
const els = await findClickable.call(this, matcher, locator);
|
|
2800
2709
|
if (context) {
|
|
2801
|
-
assertElementExists(
|
|
2802
|
-
els,
|
|
2803
|
-
locator,
|
|
2804
|
-
'Clickable element',
|
|
2805
|
-
`was not found inside element ${new Locator(context).toString()}`,
|
|
2806
|
-
)
|
|
2710
|
+
assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`);
|
|
2807
2711
|
} else {
|
|
2808
|
-
assertElementExists(els, locator, 'Clickable element')
|
|
2712
|
+
assertElementExists(els, locator, 'Clickable element');
|
|
2809
2713
|
}
|
|
2810
2714
|
|
|
2811
|
-
highlightActiveElement.call(this, els[0], await this._getContext())
|
|
2715
|
+
highlightActiveElement.call(this, els[0], await this._getContext());
|
|
2812
2716
|
|
|
2813
|
-
await els[0].click(options)
|
|
2814
|
-
const promises = []
|
|
2717
|
+
await els[0].click(options);
|
|
2718
|
+
const promises = [];
|
|
2815
2719
|
if (options.waitForNavigation) {
|
|
2816
|
-
promises.push(this.waitForNavigation())
|
|
2720
|
+
promises.push(this.waitForNavigation());
|
|
2817
2721
|
}
|
|
2818
|
-
promises.push(this._waitForAction())
|
|
2722
|
+
promises.push(this._waitForAction());
|
|
2819
2723
|
|
|
2820
|
-
return Promise.all(promises)
|
|
2724
|
+
return Promise.all(promises);
|
|
2821
2725
|
}
|
|
2822
2726
|
|
|
2823
2727
|
async function findClickable(matcher, locator) {
|
|
2824
|
-
if (locator.react) return findReactElements.call(this, locator)
|
|
2825
|
-
locator = new Locator(locator)
|
|
2826
|
-
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator)
|
|
2728
|
+
if (locator.react) return findReactElements.call(this, locator);
|
|
2729
|
+
locator = new Locator(locator);
|
|
2730
|
+
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator);
|
|
2827
2731
|
|
|
2828
|
-
let els
|
|
2829
|
-
const literal = xpathLocator.literal(locator.value)
|
|
2732
|
+
let els;
|
|
2733
|
+
const literal = xpathLocator.literal(locator.value);
|
|
2830
2734
|
|
|
2831
|
-
els = await findElements.call(this, matcher, Locator.clickable.narrow(literal))
|
|
2832
|
-
if (els.length) return els
|
|
2735
|
+
els = await findElements.call(this, matcher, Locator.clickable.narrow(literal));
|
|
2736
|
+
if (els.length) return els;
|
|
2833
2737
|
|
|
2834
|
-
els = await findElements.call(this, matcher, Locator.clickable.wide(literal))
|
|
2835
|
-
if (els.length) return els
|
|
2738
|
+
els = await findElements.call(this, matcher, Locator.clickable.wide(literal));
|
|
2739
|
+
if (els.length) return els;
|
|
2836
2740
|
|
|
2837
2741
|
try {
|
|
2838
|
-
els = await findElements.call(this, matcher, Locator.clickable.self(literal))
|
|
2839
|
-
if (els.length) return els
|
|
2742
|
+
els = await findElements.call(this, matcher, Locator.clickable.self(literal));
|
|
2743
|
+
if (els.length) return els;
|
|
2840
2744
|
} catch (err) {
|
|
2841
2745
|
// Do nothing
|
|
2842
2746
|
}
|
|
2843
2747
|
|
|
2844
|
-
return findElements.call(this, matcher, locator.value) // by css or xpath
|
|
2748
|
+
return findElements.call(this, matcher, locator.value); // by css or xpath
|
|
2845
2749
|
}
|
|
2846
2750
|
|
|
2847
2751
|
async function proceedSee(assertType, text, context, strict = false) {
|
|
2848
|
-
let description
|
|
2849
|
-
let allText
|
|
2752
|
+
let description;
|
|
2753
|
+
let allText;
|
|
2850
2754
|
if (!context) {
|
|
2851
|
-
let el = await this.context
|
|
2755
|
+
let el = await this.context;
|
|
2852
2756
|
|
|
2853
2757
|
if (el && !el.getProperty) {
|
|
2854
2758
|
// Fallback to body
|
|
2855
|
-
el = await this.context.$('body')
|
|
2759
|
+
el = await this.context.$('body');
|
|
2856
2760
|
}
|
|
2857
2761
|
|
|
2858
|
-
allText = [await el.getProperty('innerText').then(
|
|
2859
|
-
description = 'web application'
|
|
2762
|
+
allText = [await el.getProperty('innerText').then(p => p.jsonValue())];
|
|
2763
|
+
description = 'web application';
|
|
2860
2764
|
} else {
|
|
2861
|
-
const locator = new Locator(context, 'css')
|
|
2862
|
-
description = `element ${locator.toString()}
|
|
2863
|
-
const els = await this._locate(locator)
|
|
2864
|
-
assertElementExists(els, locator.toString())
|
|
2865
|
-
allText = await Promise.all(els.map(
|
|
2765
|
+
const locator = new Locator(context, 'css');
|
|
2766
|
+
description = `element ${locator.toString()}`;
|
|
2767
|
+
const els = await this._locate(locator);
|
|
2768
|
+
assertElementExists(els, locator.toString());
|
|
2769
|
+
allText = await Promise.all(els.map(el => el.getProperty('innerText').then(p => p.jsonValue())));
|
|
2866
2770
|
}
|
|
2867
2771
|
|
|
2868
2772
|
if (strict) {
|
|
2869
|
-
return allText.map(
|
|
2773
|
+
return allText.map(elText => equals(description)[assertType](text, elText));
|
|
2870
2774
|
}
|
|
2871
|
-
return stringIncludes(description)[assertType](
|
|
2872
|
-
normalizeSpacesInString(text),
|
|
2873
|
-
normalizeSpacesInString(allText.join(' | ')),
|
|
2874
|
-
)
|
|
2775
|
+
return stringIncludes(description)[assertType](normalizeSpacesInString(text), normalizeSpacesInString(allText.join(' | ')));
|
|
2875
2776
|
}
|
|
2876
2777
|
|
|
2877
2778
|
async function findCheckable(locator, context) {
|
|
2878
|
-
let contextEl = await this.context
|
|
2779
|
+
let contextEl = await this.context;
|
|
2879
2780
|
if (typeof context === 'string') {
|
|
2880
|
-
contextEl = await findElements.call(this, contextEl, new Locator(context, 'css').simplify())
|
|
2881
|
-
contextEl = contextEl[0]
|
|
2781
|
+
contextEl = await findElements.call(this, contextEl, (new Locator(context, 'css')).simplify());
|
|
2782
|
+
contextEl = contextEl[0];
|
|
2882
2783
|
}
|
|
2883
2784
|
|
|
2884
|
-
const matchedLocator = new Locator(locator)
|
|
2785
|
+
const matchedLocator = new Locator(locator);
|
|
2885
2786
|
if (!matchedLocator.isFuzzy()) {
|
|
2886
|
-
return findElements.call(this, contextEl, matchedLocator.simplify())
|
|
2787
|
+
return findElements.call(this, contextEl, matchedLocator.simplify());
|
|
2887
2788
|
}
|
|
2888
2789
|
|
|
2889
|
-
const literal = xpathLocator.literal(locator)
|
|
2890
|
-
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal))
|
|
2790
|
+
const literal = xpathLocator.literal(locator);
|
|
2791
|
+
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal));
|
|
2891
2792
|
if (els.length) {
|
|
2892
|
-
return els
|
|
2793
|
+
return els;
|
|
2893
2794
|
}
|
|
2894
|
-
els = await findElements.call(this, contextEl, Locator.checkable.byName(literal))
|
|
2795
|
+
els = await findElements.call(this, contextEl, Locator.checkable.byName(literal));
|
|
2895
2796
|
if (els.length) {
|
|
2896
|
-
return els
|
|
2797
|
+
return els;
|
|
2897
2798
|
}
|
|
2898
|
-
return findElements.call(this, contextEl, locator)
|
|
2799
|
+
return findElements.call(this, contextEl, locator);
|
|
2899
2800
|
}
|
|
2900
2801
|
|
|
2901
2802
|
async function proceedIsChecked(assertType, option) {
|
|
2902
|
-
let els = await findCheckable.call(this, option)
|
|
2903
|
-
assertElementExists(els, option, 'Checkable')
|
|
2904
|
-
els = await Promise.all(els.map(
|
|
2905
|
-
els = await Promise.all(els.map(
|
|
2906
|
-
const selected = els.reduce((prev, cur) => prev || cur)
|
|
2907
|
-
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
2803
|
+
let els = await findCheckable.call(this, option);
|
|
2804
|
+
assertElementExists(els, option, 'Checkable');
|
|
2805
|
+
els = await Promise.all(els.map(el => el.getProperty('checked')));
|
|
2806
|
+
els = await Promise.all(els.map(el => el.jsonValue()));
|
|
2807
|
+
const selected = els.reduce((prev, cur) => prev || cur);
|
|
2808
|
+
return truth(`checkable ${option}`, 'to be checked')[assertType](selected);
|
|
2908
2809
|
}
|
|
2909
2810
|
|
|
2910
2811
|
async function findVisibleFields(locator) {
|
|
2911
|
-
const els = await findFields.call(this, locator)
|
|
2912
|
-
const visible = await Promise.all(els.map(
|
|
2913
|
-
return els.filter((el, index) => visible[index])
|
|
2812
|
+
const els = await findFields.call(this, locator);
|
|
2813
|
+
const visible = await Promise.all(els.map(el => el.boundingBox()));
|
|
2814
|
+
return els.filter((el, index) => visible[index]);
|
|
2914
2815
|
}
|
|
2915
2816
|
|
|
2916
2817
|
async function findFields(locator) {
|
|
2917
|
-
const matchedLocator = new Locator(locator)
|
|
2818
|
+
const matchedLocator = new Locator(locator);
|
|
2918
2819
|
if (!matchedLocator.isFuzzy()) {
|
|
2919
|
-
return this._locate(matchedLocator)
|
|
2820
|
+
return this._locate(matchedLocator);
|
|
2920
2821
|
}
|
|
2921
|
-
const literal = xpathLocator.literal(locator)
|
|
2822
|
+
const literal = xpathLocator.literal(locator);
|
|
2922
2823
|
|
|
2923
|
-
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) })
|
|
2824
|
+
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) });
|
|
2924
2825
|
if (els.length) {
|
|
2925
|
-
return els
|
|
2826
|
+
return els;
|
|
2926
2827
|
}
|
|
2927
2828
|
|
|
2928
|
-
els = await this._locate({ xpath: Locator.field.labelContains(literal) })
|
|
2829
|
+
els = await this._locate({ xpath: Locator.field.labelContains(literal) });
|
|
2929
2830
|
if (els.length) {
|
|
2930
|
-
return els
|
|
2831
|
+
return els;
|
|
2931
2832
|
}
|
|
2932
|
-
els = await this._locate({ xpath: Locator.field.byName(literal) })
|
|
2833
|
+
els = await this._locate({ xpath: Locator.field.byName(literal) });
|
|
2933
2834
|
if (els.length) {
|
|
2934
|
-
return els
|
|
2835
|
+
return els;
|
|
2935
2836
|
}
|
|
2936
|
-
return this._locate({ css: locator })
|
|
2837
|
+
return this._locate({ css: locator });
|
|
2937
2838
|
}
|
|
2938
2839
|
|
|
2939
2840
|
async function proceedDragAndDrop(sourceLocator, destinationLocator) {
|
|
2940
|
-
const src = await this._locate(sourceLocator)
|
|
2941
|
-
assertElementExists(src, sourceLocator, 'Source Element')
|
|
2841
|
+
const src = await this._locate(sourceLocator);
|
|
2842
|
+
assertElementExists(src, sourceLocator, 'Source Element');
|
|
2942
2843
|
|
|
2943
|
-
const dst = await this._locate(destinationLocator)
|
|
2944
|
-
assertElementExists(dst, destinationLocator, 'Destination Element')
|
|
2844
|
+
const dst = await this._locate(destinationLocator);
|
|
2845
|
+
assertElementExists(dst, destinationLocator, 'Destination Element');
|
|
2945
2846
|
|
|
2946
2847
|
// Note: Using public api .getClickablePoint becaues the .BoundingBox does not take into account iframe offsets
|
|
2947
|
-
const dragSource = await getClickablePoint(src[0])
|
|
2948
|
-
const dragDestination = await getClickablePoint(dst[0])
|
|
2848
|
+
const dragSource = await getClickablePoint(src[0]);
|
|
2849
|
+
const dragDestination = await getClickablePoint(dst[0]);
|
|
2949
2850
|
|
|
2950
2851
|
// Drag start point
|
|
2951
|
-
await this.page.mouse.move(dragSource.x, dragSource.y, { steps: 5 })
|
|
2952
|
-
await this.page.mouse.down()
|
|
2852
|
+
await this.page.mouse.move(dragSource.x, dragSource.y, { steps: 5 });
|
|
2853
|
+
await this.page.mouse.down();
|
|
2953
2854
|
|
|
2954
2855
|
// Drag destination
|
|
2955
|
-
await this.page.mouse.move(dragDestination.x, dragDestination.y, { steps: 5 })
|
|
2956
|
-
await this.page.mouse.up()
|
|
2856
|
+
await this.page.mouse.move(dragDestination.x, dragDestination.y, { steps: 5 });
|
|
2857
|
+
await this.page.mouse.up();
|
|
2957
2858
|
|
|
2958
|
-
await this._waitForAction()
|
|
2859
|
+
await this._waitForAction();
|
|
2959
2860
|
}
|
|
2960
2861
|
|
|
2961
2862
|
async function proceedSeeInField(assertType, field, value) {
|
|
2962
|
-
const els = await findVisibleFields.call(this, field)
|
|
2963
|
-
assertElementExists(els, field, 'Field')
|
|
2964
|
-
const el = els[0]
|
|
2965
|
-
const tag = await el.getProperty('tagName').then(
|
|
2966
|
-
const fieldType = await el.getProperty('type').then(
|
|
2863
|
+
const els = await findVisibleFields.call(this, field);
|
|
2864
|
+
assertElementExists(els, field, 'Field');
|
|
2865
|
+
const el = els[0];
|
|
2866
|
+
const tag = await el.getProperty('tagName').then(el => el.jsonValue());
|
|
2867
|
+
const fieldType = await el.getProperty('type').then(el => el.jsonValue());
|
|
2967
2868
|
|
|
2968
2869
|
const proceedMultiple = async (elements) => {
|
|
2969
|
-
const fields = Array.isArray(elements) ? elements : [elements]
|
|
2870
|
+
const fields = Array.isArray(elements) ? elements : [elements];
|
|
2970
2871
|
|
|
2971
|
-
const elementValues = []
|
|
2872
|
+
const elementValues = [];
|
|
2972
2873
|
for (const element of fields) {
|
|
2973
|
-
elementValues.push(await element.getProperty('value').then(
|
|
2874
|
+
elementValues.push(await element.getProperty('value').then(el => el.jsonValue()));
|
|
2974
2875
|
}
|
|
2975
2876
|
|
|
2976
2877
|
if (typeof value === 'boolean') {
|
|
2977
|
-
equals(`no. of items matching > 0: ${field}`)[assertType](value, !!elementValues.length)
|
|
2878
|
+
equals(`no. of items matching > 0: ${field}`)[assertType](value, !!elementValues.length);
|
|
2978
2879
|
} else {
|
|
2979
2880
|
if (assertType === 'assert') {
|
|
2980
|
-
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0)
|
|
2881
|
+
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0);
|
|
2981
2882
|
}
|
|
2982
|
-
elementValues.forEach(
|
|
2883
|
+
elementValues.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val));
|
|
2983
2884
|
}
|
|
2984
|
-
}
|
|
2885
|
+
};
|
|
2985
2886
|
|
|
2986
2887
|
if (tag === 'SELECT') {
|
|
2987
|
-
const selectedOptions = await el.$$('option:checked')
|
|
2888
|
+
const selectedOptions = await el.$$('option:checked');
|
|
2988
2889
|
// locate option by values and check them
|
|
2989
2890
|
if (value === '') {
|
|
2990
|
-
return proceedMultiple(selectedOptions)
|
|
2891
|
+
return proceedMultiple(selectedOptions);
|
|
2991
2892
|
}
|
|
2992
2893
|
|
|
2993
|
-
const options = await filterFieldsByValue(selectedOptions, value, true)
|
|
2994
|
-
return proceedMultiple(options)
|
|
2894
|
+
const options = await filterFieldsByValue(selectedOptions, value, true);
|
|
2895
|
+
return proceedMultiple(options);
|
|
2995
2896
|
}
|
|
2996
2897
|
|
|
2997
2898
|
if (tag === 'INPUT') {
|
|
2998
2899
|
if (fieldType === 'checkbox' || fieldType === 'radio') {
|
|
2999
2900
|
if (typeof value === 'boolean') {
|
|
3000
2901
|
// Filter by values
|
|
3001
|
-
const options = await filterFieldsBySelectionState(els, true)
|
|
3002
|
-
return proceedMultiple(options)
|
|
2902
|
+
const options = await filterFieldsBySelectionState(els, true);
|
|
2903
|
+
return proceedMultiple(options);
|
|
3003
2904
|
}
|
|
3004
2905
|
|
|
3005
|
-
const options = await filterFieldsByValue(els, value, true)
|
|
3006
|
-
return proceedMultiple(options)
|
|
2906
|
+
const options = await filterFieldsByValue(els, value, true);
|
|
2907
|
+
return proceedMultiple(options);
|
|
3007
2908
|
}
|
|
3008
|
-
return proceedMultiple(els[0])
|
|
2909
|
+
return proceedMultiple(els[0]);
|
|
3009
2910
|
}
|
|
3010
|
-
const fieldVal = await el.getProperty('value').then(
|
|
3011
|
-
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal)
|
|
2911
|
+
const fieldVal = await el.getProperty('value').then(el => el.jsonValue());
|
|
2912
|
+
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal);
|
|
3012
2913
|
}
|
|
3013
2914
|
|
|
3014
2915
|
async function filterFieldsByValue(elements, value, onlySelected) {
|
|
3015
|
-
const matches = []
|
|
2916
|
+
const matches = [];
|
|
3016
2917
|
for (const element of elements) {
|
|
3017
|
-
const val = await element.getProperty('value').then(
|
|
3018
|
-
let isSelected = true
|
|
2918
|
+
const val = await element.getProperty('value').then(el => el.jsonValue());
|
|
2919
|
+
let isSelected = true;
|
|
3019
2920
|
if (onlySelected) {
|
|
3020
|
-
isSelected = await elementSelected(element)
|
|
2921
|
+
isSelected = await elementSelected(element);
|
|
3021
2922
|
}
|
|
3022
2923
|
if ((value == null || val.indexOf(value) > -1) && isSelected) {
|
|
3023
|
-
matches.push(element)
|
|
2924
|
+
matches.push(element);
|
|
3024
2925
|
}
|
|
3025
2926
|
}
|
|
3026
|
-
return matches
|
|
2927
|
+
return matches;
|
|
3027
2928
|
}
|
|
3028
2929
|
|
|
3029
2930
|
async function filterFieldsBySelectionState(elements, state) {
|
|
3030
|
-
const matches = []
|
|
2931
|
+
const matches = [];
|
|
3031
2932
|
for (const element of elements) {
|
|
3032
|
-
const isSelected = await elementSelected(element)
|
|
2933
|
+
const isSelected = await elementSelected(element);
|
|
3033
2934
|
if (isSelected === state) {
|
|
3034
|
-
matches.push(element)
|
|
2935
|
+
matches.push(element);
|
|
3035
2936
|
}
|
|
3036
2937
|
}
|
|
3037
|
-
return matches
|
|
2938
|
+
return matches;
|
|
3038
2939
|
}
|
|
3039
2940
|
|
|
3040
2941
|
async function elementSelected(element) {
|
|
3041
|
-
const type = await element.getProperty('type').then(
|
|
2942
|
+
const type = await element.getProperty('type').then(el => el.jsonValue());
|
|
3042
2943
|
|
|
3043
2944
|
if (type === 'checkbox' || type === 'radio') {
|
|
3044
|
-
return element.getProperty('checked').then(
|
|
2945
|
+
return element.getProperty('checked').then(el => el.jsonValue());
|
|
3045
2946
|
}
|
|
3046
|
-
return element.getProperty('selected').then(
|
|
2947
|
+
return element.getProperty('selected').then(el => el.jsonValue());
|
|
3047
2948
|
}
|
|
3048
2949
|
|
|
3049
2950
|
function isFrameLocator(locator) {
|
|
3050
|
-
locator = new Locator(locator)
|
|
2951
|
+
locator = new Locator(locator);
|
|
3051
2952
|
if (locator.isFrame()) {
|
|
3052
|
-
const _locator = new Locator(locator)
|
|
3053
|
-
return _locator.value
|
|
2953
|
+
const _locator = new Locator(locator);
|
|
2954
|
+
return _locator.value;
|
|
3054
2955
|
}
|
|
3055
|
-
return false
|
|
2956
|
+
return false;
|
|
3056
2957
|
}
|
|
3057
2958
|
|
|
3058
2959
|
function assertElementExists(res, locator, prefix, suffix) {
|
|
3059
2960
|
if (!res || res.length === 0) {
|
|
3060
|
-
throw new ElementNotFound(locator, prefix, suffix)
|
|
2961
|
+
throw new ElementNotFound(locator, prefix, suffix);
|
|
3061
2962
|
}
|
|
3062
2963
|
}
|
|
3063
2964
|
|
|
3064
2965
|
function $XPath(element, selector) {
|
|
3065
|
-
const found = document.evaluate(selector, element || document.body, null, 5, null)
|
|
3066
|
-
const res = []
|
|
3067
|
-
let current = null
|
|
3068
|
-
while (
|
|
3069
|
-
res.push(current)
|
|
2966
|
+
const found = document.evaluate(selector, element || document.body, null, 5, null);
|
|
2967
|
+
const res = [];
|
|
2968
|
+
let current = null;
|
|
2969
|
+
while (current = found.iterateNext()) {
|
|
2970
|
+
res.push(current);
|
|
3070
2971
|
}
|
|
3071
|
-
return res
|
|
2972
|
+
return res;
|
|
3072
2973
|
}
|
|
3073
2974
|
|
|
3074
2975
|
async function targetCreatedHandler(page) {
|
|
3075
|
-
if (!page) return
|
|
3076
|
-
this.withinLocator = null
|
|
2976
|
+
if (!page) return;
|
|
2977
|
+
this.withinLocator = null;
|
|
3077
2978
|
page.on('load', () => {
|
|
3078
|
-
page
|
|
3079
|
-
.$('body')
|
|
2979
|
+
page.$('body')
|
|
3080
2980
|
.catch(() => null)
|
|
3081
|
-
.then(
|
|
3082
|
-
})
|
|
2981
|
+
.then(context => this.context = context);
|
|
2982
|
+
});
|
|
3083
2983
|
page.on('console', (msg) => {
|
|
3084
|
-
this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg._text || '') + msg.args().join(' '))
|
|
3085
|
-
consoleLogStore.add(msg)
|
|
3086
|
-
})
|
|
2984
|
+
this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg._text || '') + msg.args().join(' '));
|
|
2985
|
+
consoleLogStore.add(msg);
|
|
2986
|
+
});
|
|
3087
2987
|
|
|
3088
2988
|
if (this.options.userAgent) {
|
|
3089
|
-
await page.setUserAgent(this.options.userAgent)
|
|
2989
|
+
await page.setUserAgent(this.options.userAgent);
|
|
3090
2990
|
}
|
|
3091
2991
|
if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0) {
|
|
3092
|
-
const dimensions = this.options.windowSize.split('x')
|
|
3093
|
-
const width = parseInt(dimensions[0], 10)
|
|
3094
|
-
const height = parseInt(dimensions[1], 10)
|
|
3095
|
-
await page.setViewport({ width, height })
|
|
2992
|
+
const dimensions = this.options.windowSize.split('x');
|
|
2993
|
+
const width = parseInt(dimensions[0], 10);
|
|
2994
|
+
const height = parseInt(dimensions[1], 10);
|
|
2995
|
+
await page.setViewport({ width, height });
|
|
3096
2996
|
}
|
|
3097
2997
|
}
|
|
3098
2998
|
|
|
3099
2999
|
// BC compatibility for Puppeteer < 10
|
|
3100
3000
|
async function getClickablePoint(el) {
|
|
3101
|
-
if (el.clickablePoint) return el.clickablePoint()
|
|
3102
|
-
if (el._clickablePoint) return el._clickablePoint()
|
|
3103
|
-
return null
|
|
3001
|
+
if (el.clickablePoint) return el.clickablePoint();
|
|
3002
|
+
if (el._clickablePoint) return el._clickablePoint();
|
|
3003
|
+
return null;
|
|
3104
3004
|
}
|
|
3105
3005
|
|
|
3106
3006
|
// List of key values to key definitions
|
|
3107
3007
|
// https://github.com/GoogleChrome/puppeteer/blob/v1.20.0/lib/USKeyboardLayout.js
|
|
3108
3008
|
const keyDefinitionMap = {
|
|
3109
3009
|
/* eslint-disable quote-props */
|
|
3110
|
-
0: 'Digit0',
|
|
3111
|
-
1: 'Digit1',
|
|
3112
|
-
2: 'Digit2',
|
|
3113
|
-
3: 'Digit3',
|
|
3114
|
-
4: 'Digit4',
|
|
3115
|
-
5: 'Digit5',
|
|
3116
|
-
6: 'Digit6',
|
|
3117
|
-
7: 'Digit7',
|
|
3118
|
-
8: 'Digit8',
|
|
3119
|
-
9: 'Digit9',
|
|
3120
|
-
a: 'KeyA',
|
|
3121
|
-
b: 'KeyB',
|
|
3122
|
-
c: 'KeyC',
|
|
3123
|
-
d: 'KeyD',
|
|
3124
|
-
e: 'KeyE',
|
|
3125
|
-
f: 'KeyF',
|
|
3126
|
-
g: 'KeyG',
|
|
3127
|
-
h: 'KeyH',
|
|
3128
|
-
i: 'KeyI',
|
|
3129
|
-
j: 'KeyJ',
|
|
3130
|
-
k: 'KeyK',
|
|
3131
|
-
l: 'KeyL',
|
|
3132
|
-
m: 'KeyM',
|
|
3133
|
-
n: 'KeyN',
|
|
3134
|
-
o: 'KeyO',
|
|
3135
|
-
p: 'KeyP',
|
|
3136
|
-
q: 'KeyQ',
|
|
3137
|
-
r: 'KeyR',
|
|
3138
|
-
s: 'KeyS',
|
|
3139
|
-
t: 'KeyT',
|
|
3140
|
-
u: 'KeyU',
|
|
3141
|
-
v: 'KeyV',
|
|
3142
|
-
w: 'KeyW',
|
|
3143
|
-
x: 'KeyX',
|
|
3144
|
-
y: 'KeyY',
|
|
3145
|
-
z: 'KeyZ',
|
|
3010
|
+
'0': 'Digit0',
|
|
3011
|
+
'1': 'Digit1',
|
|
3012
|
+
'2': 'Digit2',
|
|
3013
|
+
'3': 'Digit3',
|
|
3014
|
+
'4': 'Digit4',
|
|
3015
|
+
'5': 'Digit5',
|
|
3016
|
+
'6': 'Digit6',
|
|
3017
|
+
'7': 'Digit7',
|
|
3018
|
+
'8': 'Digit8',
|
|
3019
|
+
'9': 'Digit9',
|
|
3020
|
+
'a': 'KeyA',
|
|
3021
|
+
'b': 'KeyB',
|
|
3022
|
+
'c': 'KeyC',
|
|
3023
|
+
'd': 'KeyD',
|
|
3024
|
+
'e': 'KeyE',
|
|
3025
|
+
'f': 'KeyF',
|
|
3026
|
+
'g': 'KeyG',
|
|
3027
|
+
'h': 'KeyH',
|
|
3028
|
+
'i': 'KeyI',
|
|
3029
|
+
'j': 'KeyJ',
|
|
3030
|
+
'k': 'KeyK',
|
|
3031
|
+
'l': 'KeyL',
|
|
3032
|
+
'm': 'KeyM',
|
|
3033
|
+
'n': 'KeyN',
|
|
3034
|
+
'o': 'KeyO',
|
|
3035
|
+
'p': 'KeyP',
|
|
3036
|
+
'q': 'KeyQ',
|
|
3037
|
+
'r': 'KeyR',
|
|
3038
|
+
's': 'KeyS',
|
|
3039
|
+
't': 'KeyT',
|
|
3040
|
+
'u': 'KeyU',
|
|
3041
|
+
'v': 'KeyV',
|
|
3042
|
+
'w': 'KeyW',
|
|
3043
|
+
'x': 'KeyX',
|
|
3044
|
+
'y': 'KeyY',
|
|
3045
|
+
'z': 'KeyZ',
|
|
3146
3046
|
';': 'Semicolon',
|
|
3147
3047
|
'=': 'Equal',
|
|
3148
3048
|
',': 'Comma',
|
|
@@ -3153,94 +3053,91 @@ const keyDefinitionMap = {
|
|
|
3153
3053
|
'[': 'BracketLeft',
|
|
3154
3054
|
'\\': 'Backslash',
|
|
3155
3055
|
']': 'BracketRight',
|
|
3156
|
-
|
|
3056
|
+
'\'': 'Quote',
|
|
3157
3057
|
/* eslint-enable quote-props */
|
|
3158
|
-
}
|
|
3058
|
+
};
|
|
3159
3059
|
|
|
3160
3060
|
function getNormalizedKey(key) {
|
|
3161
|
-
const normalizedKey = getNormalizedKeyAttributeValue(key)
|
|
3061
|
+
const normalizedKey = getNormalizedKeyAttributeValue(key);
|
|
3162
3062
|
if (key !== normalizedKey) {
|
|
3163
|
-
this.debugSection('Input', `Mapping key '${key}' to '${normalizedKey}'`)
|
|
3063
|
+
this.debugSection('Input', `Mapping key '${key}' to '${normalizedKey}'`);
|
|
3164
3064
|
}
|
|
3165
3065
|
// Use key definition to ensure correct key is displayed when Shift modifier is active
|
|
3166
3066
|
if (Object.prototype.hasOwnProperty.call(keyDefinitionMap, normalizedKey)) {
|
|
3167
|
-
return keyDefinitionMap[normalizedKey]
|
|
3067
|
+
return keyDefinitionMap[normalizedKey];
|
|
3168
3068
|
}
|
|
3169
|
-
return normalizedKey
|
|
3069
|
+
return normalizedKey;
|
|
3170
3070
|
}
|
|
3171
3071
|
|
|
3172
3072
|
function highlightActiveElement(element, context) {
|
|
3173
3073
|
if (this.options.highlightElement && global.debugMode) {
|
|
3174
|
-
highlightElement(element, context)
|
|
3074
|
+
highlightElement(element, context);
|
|
3175
3075
|
}
|
|
3176
3076
|
}
|
|
3177
3077
|
|
|
3178
3078
|
function _waitForElement(locator, options) {
|
|
3179
3079
|
try {
|
|
3180
|
-
return this.context.waitForXPath(locator.value, options)
|
|
3080
|
+
return this.context.waitForXPath(locator.value, options);
|
|
3181
3081
|
} catch (e) {
|
|
3182
|
-
return this.context.waitForSelector(`::-p-xpath(${locator.value})`, options)
|
|
3082
|
+
return this.context.waitForSelector(`::-p-xpath(${locator.value})`, options);
|
|
3183
3083
|
}
|
|
3184
3084
|
}
|
|
3185
3085
|
|
|
3186
3086
|
async function findReactElements(locator, props = {}, state = {}) {
|
|
3187
|
-
const resqScript = await fs.promises.readFile(require.resolve('resq'), 'utf-8')
|
|
3188
|
-
await this.page.evaluate(resqScript.toString())
|
|
3189
|
-
|
|
3190
|
-
await this.page.evaluate(() => window.resq.waitToLoadReact())
|
|
3191
|
-
const arrayHandle = await this.page.evaluateHandle(
|
|
3192
|
-
(obj) => {
|
|
3193
|
-
const { selector, props, state } = obj
|
|
3194
|
-
let elements = window.resq.resq$$(selector)
|
|
3195
|
-
if (Object.keys(props).length) {
|
|
3196
|
-
elements = elements.byProps(props)
|
|
3197
|
-
}
|
|
3198
|
-
if (Object.keys(state).length) {
|
|
3199
|
-
elements = elements.byState(state)
|
|
3200
|
-
}
|
|
3087
|
+
const resqScript = await fs.promises.readFile(require.resolve('resq'), 'utf-8');
|
|
3088
|
+
await this.page.evaluate(resqScript.toString());
|
|
3201
3089
|
|
|
3202
|
-
|
|
3203
|
-
|
|
3204
|
-
|
|
3090
|
+
await this.page.evaluate(() => window.resq.waitToLoadReact());
|
|
3091
|
+
const arrayHandle = await this.page.evaluateHandle((obj) => {
|
|
3092
|
+
const { selector, props, state } = obj;
|
|
3093
|
+
let elements = window.resq.resq$$(selector);
|
|
3094
|
+
if (Object.keys(props).length) {
|
|
3095
|
+
elements = elements.byProps(props);
|
|
3096
|
+
}
|
|
3097
|
+
if (Object.keys(state).length) {
|
|
3098
|
+
elements = elements.byState(state);
|
|
3099
|
+
}
|
|
3205
3100
|
|
|
3206
|
-
|
|
3207
|
-
|
|
3208
|
-
|
|
3209
|
-
let nodes = []
|
|
3101
|
+
if (!elements.length) {
|
|
3102
|
+
return [];
|
|
3103
|
+
}
|
|
3210
3104
|
|
|
3211
|
-
|
|
3212
|
-
|
|
3105
|
+
// resq returns an array of HTMLElements if the React component is a fragment
|
|
3106
|
+
// this avoids having nested arrays of nodes which the driver does not understand
|
|
3107
|
+
// [[div, div], [div, div]] => [div, div, div, div]
|
|
3108
|
+
let nodes = [];
|
|
3213
3109
|
|
|
3214
|
-
|
|
3215
|
-
|
|
3216
|
-
node = element.children
|
|
3217
|
-
}
|
|
3110
|
+
elements.forEach((element) => {
|
|
3111
|
+
let { node, isFragment } = element;
|
|
3218
3112
|
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3113
|
+
if (!node) {
|
|
3114
|
+
isFragment = true;
|
|
3115
|
+
node = element.children;
|
|
3116
|
+
}
|
|
3117
|
+
|
|
3118
|
+
if (isFragment) {
|
|
3119
|
+
nodes = nodes.concat(node);
|
|
3120
|
+
} else {
|
|
3121
|
+
nodes.push(node);
|
|
3122
|
+
}
|
|
3123
|
+
});
|
|
3124
|
+
|
|
3125
|
+
return [...nodes];
|
|
3126
|
+
}, {
|
|
3127
|
+
selector: locator.react,
|
|
3128
|
+
props: locator.props || {},
|
|
3129
|
+
state: locator.state || {},
|
|
3130
|
+
});
|
|
3131
|
+
|
|
3132
|
+
const properties = await arrayHandle.getProperties();
|
|
3133
|
+
const result = [];
|
|
3237
3134
|
for (const property of properties.values()) {
|
|
3238
|
-
const elementHandle = property.asElement()
|
|
3135
|
+
const elementHandle = property.asElement();
|
|
3239
3136
|
if (elementHandle) {
|
|
3240
|
-
result.push(elementHandle)
|
|
3137
|
+
result.push(elementHandle);
|
|
3241
3138
|
}
|
|
3242
3139
|
}
|
|
3243
3140
|
|
|
3244
|
-
await arrayHandle.dispose()
|
|
3245
|
-
return result
|
|
3141
|
+
await arrayHandle.dispose();
|
|
3142
|
+
return result;
|
|
3246
3143
|
}
|