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