codeceptjs 4.0.0-beta.2 → 4.0.0-beta.4
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/README.md +2 -2
- 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 +56 -49
- package/lib/codecept.js +145 -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 +79 -81
- package/lib/command/generate.js +197 -188
- package/lib/command/gherkin/init.js +27 -16
- package/lib/command/gherkin/snippets.js +21 -21
- 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 +11 -12
- 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 +14 -18
- package/lib/helper/AI.js +130 -41
- package/lib/helper/ApiDataFactory.js +74 -70
- package/lib/helper/Appium.js +416 -388
- 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 +51 -51
- package/lib/helper/JSONResponse.js +65 -62
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/Nightmare.js +664 -571
- package/lib/helper/Playwright.js +1367 -1222
- package/lib/helper/Protractor.js +663 -635
- package/lib/helper/Puppeteer.js +1232 -1132
- package/lib/helper/REST.js +183 -68
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +490 -486
- package/lib/helper/WebDriver.js +1246 -1297
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +4 -4
- 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 +4 -3
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +7 -8
- 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 +7 -11
- package/lib/mochaFactory.js +18 -15
- package/lib/output.js +19 -15
- package/lib/parser.js +15 -12
- package/lib/pause.js +45 -38
- 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 +112 -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 -3
- 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 -33
- package/package.json +67 -68
- package/translations/de-DE.js +2 -1
- package/translations/fr-FR.js +2 -2
- 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 +423 -65
- package/typings/promiseBasedTypes.d.ts +41 -172
- package/typings/types.d.ts +43 -178
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
- package/lib/helper/MockServer.js +0 -223
package/lib/helper/Playwright.js
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
const path = require('path')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
|
|
4
|
+
const Helper = require('@codeceptjs/helper')
|
|
5
|
+
const { v4: uuidv4 } = require('uuid')
|
|
6
|
+
const assert = require('assert')
|
|
7
|
+
const promiseRetry = require('promise-retry')
|
|
8
|
+
const Locator = require('../locator')
|
|
9
|
+
const recorder = require('../recorder')
|
|
10
|
+
const stringIncludes = require('../assert/include').includes
|
|
11
|
+
const { urlEquals } = require('../assert/equal')
|
|
12
|
+
const { equals } = require('../assert/equal')
|
|
13
|
+
const { empty } = require('../assert/empty')
|
|
14
|
+
const { truth } = require('../assert/truth')
|
|
15
|
+
const {
|
|
15
16
|
xpathLocator,
|
|
16
17
|
ucfirst,
|
|
17
18
|
fileExists,
|
|
@@ -23,40 +24,44 @@ import {
|
|
|
23
24
|
clearString,
|
|
24
25
|
requireWithFallback,
|
|
25
26
|
normalizeSpacesInString,
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
27
|
+
} = require('../utils')
|
|
28
|
+
const { isColorProperty, convertColorToRGBA } = require('../colorUtils')
|
|
29
|
+
const ElementNotFound = require('./errors/ElementNotFound')
|
|
30
|
+
const RemoteBrowserConnectionRefused = require('./errors/RemoteBrowserConnectionRefused')
|
|
31
|
+
const Popup = require('./extras/Popup')
|
|
32
|
+
const Console = require('./extras/Console')
|
|
33
|
+
const { findReact, findVue, findByPlaywrightLocator } = require('./extras/PlaywrightReactVueLocator')
|
|
34
|
+
|
|
35
|
+
let playwright
|
|
36
|
+
let perfTiming
|
|
37
|
+
let defaultSelectorEnginesInitialized = false
|
|
38
|
+
|
|
39
|
+
const popupStore = new Popup()
|
|
40
|
+
const consoleLogStore = new Console()
|
|
41
|
+
const availableBrowsers = ['chromium', 'webkit', 'firefox', 'electron']
|
|
42
|
+
|
|
43
|
+
const {
|
|
44
|
+
setRestartStrategy,
|
|
45
|
+
restartsSession,
|
|
46
|
+
restartsContext,
|
|
47
|
+
restartsBrowser,
|
|
48
|
+
} = require('./extras/PlaywrightRestartOpts')
|
|
49
|
+
const { createValueEngine, createDisabledEngine } = require('./extras/PlaywrightPropEngine')
|
|
50
|
+
const {
|
|
41
51
|
seeElementError,
|
|
42
52
|
dontSeeElementError,
|
|
43
53
|
dontSeeElementInDOMError,
|
|
44
54
|
seeElementInDOMError,
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
let defaultSelectorEnginesInitialized = false;
|
|
54
|
-
|
|
55
|
-
const popupStore = new Popup();
|
|
56
|
-
const consoleLogStore = new Console();
|
|
57
|
-
const availableBrowsers = ['chromium', 'webkit', 'firefox', 'electron'];
|
|
55
|
+
} = require('./errors/ElementAssertion')
|
|
56
|
+
const {
|
|
57
|
+
dontSeeTraffic,
|
|
58
|
+
seeTraffic,
|
|
59
|
+
grabRecordedNetworkTraffics,
|
|
60
|
+
stopRecordingTraffic,
|
|
61
|
+
flushNetworkTraffics,
|
|
62
|
+
} = require('./network/actions')
|
|
58
63
|
|
|
59
|
-
const pathSeparator = path.sep
|
|
64
|
+
const pathSeparator = path.sep
|
|
60
65
|
|
|
61
66
|
/**
|
|
62
67
|
* ## Configuration
|
|
@@ -105,7 +110,7 @@ const pathSeparator = path.sep;
|
|
|
105
110
|
* @prop {object} [recordHar] - record HAR and will be saved to `output/har`. See more of [HAR options](https://playwright.dev/docs/api/class-browser#browser-new-context-option-record-har).
|
|
106
111
|
* @prop {string} [testIdAttribute=data-testid] - locate elements based on the testIdAttribute. See more of [locate by test id](https://playwright.dev/docs/locators#locate-by-test-id).
|
|
107
112
|
*/
|
|
108
|
-
const config = {}
|
|
113
|
+
const config = {}
|
|
109
114
|
|
|
110
115
|
/**
|
|
111
116
|
* Uses [Playwright](https://github.com/microsoft/playwright) library to run tests inside:
|
|
@@ -327,34 +332,34 @@ const config = {};
|
|
|
327
332
|
*/
|
|
328
333
|
class Playwright extends Helper {
|
|
329
334
|
constructor(config) {
|
|
330
|
-
super(config)
|
|
335
|
+
super(config)
|
|
331
336
|
|
|
332
|
-
playwright = requireWithFallback('playwright', 'playwright-core')
|
|
337
|
+
playwright = requireWithFallback('playwright', 'playwright-core')
|
|
333
338
|
|
|
334
339
|
// set defaults
|
|
335
|
-
this.isRemoteBrowser = false
|
|
336
|
-
this.isRunning = false
|
|
337
|
-
this.isAuthenticated = false
|
|
338
|
-
this.sessionPages = {}
|
|
339
|
-
this.activeSessionName = ''
|
|
340
|
-
this.isElectron = false
|
|
341
|
-
this.isCDPConnection = false
|
|
342
|
-
this.electronSessions = []
|
|
343
|
-
this.storageState = null
|
|
340
|
+
this.isRemoteBrowser = false
|
|
341
|
+
this.isRunning = false
|
|
342
|
+
this.isAuthenticated = false
|
|
343
|
+
this.sessionPages = {}
|
|
344
|
+
this.activeSessionName = ''
|
|
345
|
+
this.isElectron = false
|
|
346
|
+
this.isCDPConnection = false
|
|
347
|
+
this.electronSessions = []
|
|
348
|
+
this.storageState = null
|
|
344
349
|
|
|
345
350
|
// for network stuff
|
|
346
|
-
this.requests = []
|
|
347
|
-
this.recording = false
|
|
348
|
-
this.recordedAtLeastOnce = false
|
|
351
|
+
this.requests = []
|
|
352
|
+
this.recording = false
|
|
353
|
+
this.recordedAtLeastOnce = false
|
|
349
354
|
|
|
350
355
|
// for websocket messages
|
|
351
|
-
this.webSocketMessages = []
|
|
352
|
-
this.recordingWebSocketMessages = false
|
|
353
|
-
this.recordedWebSocketMessagesAtLeastOnce = false
|
|
354
|
-
this.cdpSession = null
|
|
356
|
+
this.webSocketMessages = []
|
|
357
|
+
this.recordingWebSocketMessages = false
|
|
358
|
+
this.recordedWebSocketMessagesAtLeastOnce = false
|
|
359
|
+
this.cdpSession = null
|
|
355
360
|
|
|
356
361
|
// override defaults with config
|
|
357
|
-
this._setConfig(config)
|
|
362
|
+
this._setConfig(config)
|
|
358
363
|
}
|
|
359
364
|
|
|
360
365
|
_validateConfig(config) {
|
|
@@ -381,54 +386,65 @@ class Playwright extends Helper {
|
|
|
381
386
|
use: { actionTimeout: 0 },
|
|
382
387
|
ignoreHTTPSErrors: false, // Adding it here o that context can be set up to ignore the SSL errors,
|
|
383
388
|
highlightElement: false,
|
|
384
|
-
}
|
|
389
|
+
}
|
|
385
390
|
|
|
386
|
-
process.env.testIdAttribute = 'data-testid'
|
|
387
|
-
config = Object.assign(defaults, config)
|
|
391
|
+
process.env.testIdAttribute = 'data-testid'
|
|
392
|
+
config = Object.assign(defaults, config)
|
|
388
393
|
|
|
389
394
|
if (availableBrowsers.indexOf(config.browser) < 0) {
|
|
390
|
-
throw new Error(
|
|
395
|
+
throw new Error(
|
|
396
|
+
`Invalid config. Can't use browser "${config.browser}". Accepted values: ${availableBrowsers.join(', ')}`,
|
|
397
|
+
)
|
|
391
398
|
}
|
|
392
399
|
|
|
393
|
-
return config
|
|
400
|
+
return config
|
|
394
401
|
}
|
|
395
402
|
|
|
396
403
|
_getOptionsForBrowser(config) {
|
|
397
404
|
if (config[config.browser]) {
|
|
398
405
|
if (config[config.browser].browserWSEndpoint && config[config.browser].browserWSEndpoint.wsEndpoint) {
|
|
399
|
-
config[config.browser].browserWSEndpoint = config[config.browser].browserWSEndpoint.wsEndpoint
|
|
406
|
+
config[config.browser].browserWSEndpoint = config[config.browser].browserWSEndpoint.wsEndpoint
|
|
400
407
|
}
|
|
401
408
|
return {
|
|
402
409
|
...config[config.browser],
|
|
403
410
|
wsEndpoint: config[config.browser].browserWSEndpoint,
|
|
404
|
-
}
|
|
411
|
+
}
|
|
405
412
|
}
|
|
406
|
-
return {}
|
|
413
|
+
return {}
|
|
407
414
|
}
|
|
408
415
|
|
|
409
416
|
_setConfig(config) {
|
|
410
|
-
this.options = this._validateConfig(config)
|
|
411
|
-
setRestartStrategy(this.options)
|
|
417
|
+
this.options = this._validateConfig(config)
|
|
418
|
+
setRestartStrategy(this.options)
|
|
412
419
|
this.playwrightOptions = {
|
|
413
420
|
headless: !this.options.show,
|
|
414
421
|
...this._getOptionsForBrowser(config),
|
|
415
|
-
}
|
|
422
|
+
}
|
|
416
423
|
|
|
417
424
|
if (this.options.channel && this.options.browser === 'chromium') {
|
|
418
|
-
this.playwrightOptions.channel = this.options.channel
|
|
425
|
+
this.playwrightOptions.channel = this.options.channel
|
|
419
426
|
}
|
|
420
427
|
|
|
421
428
|
if (this.options.video) {
|
|
422
|
-
|
|
429
|
+
// set the video resolution with window size
|
|
430
|
+
let size = parseWindowSize(this.options.windowSize)
|
|
431
|
+
|
|
432
|
+
// if the video resolution is passed, set the record resoultion with that resolution
|
|
433
|
+
if (this.options.recordVideo && this.options.recordVideo.size) {
|
|
434
|
+
size = parseWindowSize(this.options.recordVideo.size)
|
|
435
|
+
}
|
|
436
|
+
this.options.recordVideo = { size }
|
|
423
437
|
}
|
|
424
438
|
if (this.options.recordVideo && !this.options.recordVideo.dir) {
|
|
425
|
-
this.options.recordVideo.dir = `${global.output_dir}/videos
|
|
439
|
+
this.options.recordVideo.dir = `${global.output_dir}/videos/`
|
|
426
440
|
}
|
|
427
|
-
this.isRemoteBrowser = !!this.playwrightOptions.browserWSEndpoint
|
|
428
|
-
this.isElectron = this.options.browser === 'electron'
|
|
429
|
-
this.userDataDir = this.playwrightOptions.userDataDir
|
|
430
|
-
|
|
431
|
-
|
|
441
|
+
this.isRemoteBrowser = !!this.playwrightOptions.browserWSEndpoint
|
|
442
|
+
this.isElectron = this.options.browser === 'electron'
|
|
443
|
+
this.userDataDir = this.playwrightOptions.userDataDir
|
|
444
|
+
? `${this.playwrightOptions.userDataDir}_${Date.now().toString()}`
|
|
445
|
+
: undefined
|
|
446
|
+
this.isCDPConnection = this.playwrightOptions.cdpConnection
|
|
447
|
+
popupStore.defaultAction = this.options.defaultPopupAction
|
|
432
448
|
}
|
|
433
449
|
|
|
434
450
|
static _config() {
|
|
@@ -451,216 +467,222 @@ class Playwright extends Helper {
|
|
|
451
467
|
type: 'confirm',
|
|
452
468
|
when: (answers) => answers.Playwright_browser !== 'electron',
|
|
453
469
|
},
|
|
454
|
-
]
|
|
470
|
+
]
|
|
455
471
|
}
|
|
456
472
|
|
|
457
473
|
static _checkRequirements() {
|
|
458
474
|
try {
|
|
459
|
-
requireWithFallback('playwright', 'playwright-core')
|
|
475
|
+
requireWithFallback('playwright', 'playwright-core')
|
|
460
476
|
} catch (e) {
|
|
461
|
-
return ['playwright@^1.18']
|
|
477
|
+
return ['playwright@^1.18']
|
|
462
478
|
}
|
|
463
479
|
}
|
|
464
480
|
|
|
465
481
|
async _init() {
|
|
466
482
|
// register an internal selector engine for reading value property of elements in a selector
|
|
467
|
-
if (defaultSelectorEnginesInitialized) return
|
|
468
|
-
defaultSelectorEnginesInitialized = true
|
|
483
|
+
if (defaultSelectorEnginesInitialized) return
|
|
484
|
+
defaultSelectorEnginesInitialized = true
|
|
469
485
|
try {
|
|
470
|
-
await playwright.selectors.register('__value', createValueEngine)
|
|
471
|
-
await playwright.selectors.register('__disabled', createDisabledEngine)
|
|
472
|
-
if (process.env.testIdAttribute) await playwright.selectors.setTestIdAttribute(process.env.testIdAttribute)
|
|
486
|
+
await playwright.selectors.register('__value', createValueEngine)
|
|
487
|
+
await playwright.selectors.register('__disabled', createDisabledEngine)
|
|
488
|
+
if (process.env.testIdAttribute) await playwright.selectors.setTestIdAttribute(process.env.testIdAttribute)
|
|
473
489
|
} catch (e) {
|
|
474
|
-
console.warn(e)
|
|
490
|
+
console.warn(e)
|
|
475
491
|
}
|
|
476
492
|
}
|
|
477
493
|
|
|
478
494
|
_beforeSuite() {
|
|
479
495
|
if ((restartsSession() || restartsContext()) && !this.options.manualStart && !this.isRunning) {
|
|
480
|
-
this.debugSection('Session', 'Starting singleton browser session')
|
|
481
|
-
return this._startBrowser()
|
|
496
|
+
this.debugSection('Session', 'Starting singleton browser session')
|
|
497
|
+
return this._startBrowser()
|
|
482
498
|
}
|
|
483
499
|
}
|
|
484
500
|
|
|
485
501
|
async _before(test) {
|
|
486
|
-
this.currentRunningTest = test
|
|
502
|
+
this.currentRunningTest = test
|
|
487
503
|
recorder.retry({
|
|
488
504
|
retries: process.env.FAILED_STEP_RETRIES || 3,
|
|
489
|
-
when: err => {
|
|
490
|
-
if (!err || typeof
|
|
491
|
-
return false
|
|
505
|
+
when: (err) => {
|
|
506
|
+
if (!err || typeof err.message !== 'string') {
|
|
507
|
+
return false
|
|
492
508
|
}
|
|
493
509
|
// ignore context errors
|
|
494
|
-
return err.message.includes('context')
|
|
510
|
+
return err.message.includes('context')
|
|
495
511
|
},
|
|
496
|
-
})
|
|
512
|
+
})
|
|
497
513
|
|
|
498
|
-
if (restartsBrowser() && !this.options.manualStart) await this._startBrowser()
|
|
499
|
-
if (!this.isRunning && !this.options.manualStart) await this._startBrowser()
|
|
514
|
+
if (restartsBrowser() && !this.options.manualStart) await this._startBrowser()
|
|
515
|
+
if (!this.isRunning && !this.options.manualStart) await this._startBrowser()
|
|
500
516
|
|
|
501
|
-
this.isAuthenticated = false
|
|
517
|
+
this.isAuthenticated = false
|
|
502
518
|
if (this.isElectron) {
|
|
503
|
-
this.browserContext = this.browser.context()
|
|
519
|
+
this.browserContext = this.browser.context()
|
|
504
520
|
} else if (this.playwrightOptions.userDataDir) {
|
|
505
|
-
this.browserContext = this.browser
|
|
521
|
+
this.browserContext = this.browser
|
|
506
522
|
} else {
|
|
507
523
|
const contextOptions = {
|
|
508
524
|
ignoreHTTPSErrors: this.options.ignoreHTTPSErrors,
|
|
509
525
|
acceptDownloads: true,
|
|
510
526
|
...this.options.emulate,
|
|
511
|
-
}
|
|
527
|
+
}
|
|
512
528
|
if (this.options.basicAuth) {
|
|
513
|
-
contextOptions.httpCredentials = this.options.basicAuth
|
|
514
|
-
this.isAuthenticated = true
|
|
529
|
+
contextOptions.httpCredentials = this.options.basicAuth
|
|
530
|
+
this.isAuthenticated = true
|
|
515
531
|
}
|
|
516
|
-
if (this.options.bypassCSP) contextOptions.bypassCSP = this.options.bypassCSP
|
|
517
|
-
if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo
|
|
532
|
+
if (this.options.bypassCSP) contextOptions.bypassCSP = this.options.bypassCSP
|
|
533
|
+
if (this.options.recordVideo) contextOptions.recordVideo = this.options.recordVideo
|
|
518
534
|
if (this.options.recordHar) {
|
|
519
|
-
const harExt = this.options.recordHar.content && this.options.recordHar.content === 'attach' ? 'zip' : 'har'
|
|
520
|
-
const fileName = `${`${global.output_dir}${path.sep}har${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.${harExt}
|
|
521
|
-
const dir = path.dirname(fileName)
|
|
522
|
-
if (!fileExists(dir)) fs.mkdirSync(dir)
|
|
523
|
-
this.options.recordHar.path = fileName
|
|
524
|
-
this.currentRunningTest.artifacts.har = fileName
|
|
525
|
-
contextOptions.recordHar = this.options.recordHar
|
|
535
|
+
const harExt = this.options.recordHar.content && this.options.recordHar.content === 'attach' ? 'zip' : 'har'
|
|
536
|
+
const fileName = `${`${global.output_dir}${path.sep}har${path.sep}${uuidv4()}_${clearString(this.currentRunningTest.title)}`.slice(0, 245)}.${harExt}`
|
|
537
|
+
const dir = path.dirname(fileName)
|
|
538
|
+
if (!fileExists(dir)) fs.mkdirSync(dir)
|
|
539
|
+
this.options.recordHar.path = fileName
|
|
540
|
+
this.currentRunningTest.artifacts.har = fileName
|
|
541
|
+
contextOptions.recordHar = this.options.recordHar
|
|
526
542
|
}
|
|
527
|
-
if (this.storageState) contextOptions.storageState = this.storageState
|
|
528
|
-
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent
|
|
529
|
-
if (this.options.locale) contextOptions.locale = this.options.locale
|
|
530
|
-
if (this.options.colorScheme) contextOptions.colorScheme = this.options.colorScheme
|
|
531
|
-
this.contextOptions = contextOptions
|
|
543
|
+
if (this.storageState) contextOptions.storageState = this.storageState
|
|
544
|
+
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent
|
|
545
|
+
if (this.options.locale) contextOptions.locale = this.options.locale
|
|
546
|
+
if (this.options.colorScheme) contextOptions.colorScheme = this.options.colorScheme
|
|
547
|
+
this.contextOptions = contextOptions
|
|
532
548
|
if (!this.browserContext || !restartsSession()) {
|
|
533
|
-
this.browserContext = await this.browser.newContext(this.contextOptions)
|
|
549
|
+
this.browserContext = await this.browser.newContext(this.contextOptions) // Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
534
550
|
}
|
|
535
551
|
}
|
|
536
552
|
|
|
537
|
-
let mainPage
|
|
553
|
+
let mainPage
|
|
538
554
|
if (this.isElectron) {
|
|
539
|
-
mainPage = await this.browser.firstWindow()
|
|
555
|
+
mainPage = await this.browser.firstWindow()
|
|
540
556
|
} else {
|
|
541
557
|
try {
|
|
542
|
-
const existingPages = await this.browserContext.pages()
|
|
543
|
-
mainPage = existingPages[0] || (await this.browserContext.newPage())
|
|
558
|
+
const existingPages = await this.browserContext.pages()
|
|
559
|
+
mainPage = existingPages[0] || (await this.browserContext.newPage())
|
|
544
560
|
} catch (e) {
|
|
545
561
|
if (this.playwrightOptions.userDataDir) {
|
|
546
|
-
this.browser = await playwright[this.options.browser].launchPersistentContext(
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
562
|
+
this.browser = await playwright[this.options.browser].launchPersistentContext(
|
|
563
|
+
this.userDataDir,
|
|
564
|
+
this.playwrightOptions,
|
|
565
|
+
)
|
|
566
|
+
this.browserContext = this.browser
|
|
567
|
+
const existingPages = await this.browserContext.pages()
|
|
568
|
+
mainPage = existingPages[0]
|
|
550
569
|
}
|
|
551
570
|
}
|
|
552
571
|
}
|
|
553
|
-
await targetCreatedHandler.call(this, mainPage)
|
|
572
|
+
await targetCreatedHandler.call(this, mainPage)
|
|
554
573
|
|
|
555
|
-
await this._setPage(mainPage)
|
|
574
|
+
await this._setPage(mainPage)
|
|
556
575
|
|
|
557
|
-
if (this.options.trace) await this.browserContext.tracing.start({ screenshots: true, snapshots: true })
|
|
576
|
+
if (this.options.trace) await this.browserContext.tracing.start({ screenshots: true, snapshots: true })
|
|
558
577
|
|
|
559
|
-
return this.browser
|
|
578
|
+
return this.browser
|
|
560
579
|
}
|
|
561
580
|
|
|
562
581
|
async _after() {
|
|
563
|
-
if (!this.isRunning) return
|
|
582
|
+
if (!this.isRunning) return
|
|
564
583
|
|
|
565
584
|
if (this.isElectron) {
|
|
566
|
-
this.browser.close()
|
|
567
|
-
this.electronSessions.forEach(session => session.close())
|
|
568
|
-
return
|
|
585
|
+
this.browser.close()
|
|
586
|
+
this.electronSessions.forEach((session) => session.close())
|
|
587
|
+
return
|
|
569
588
|
}
|
|
570
589
|
|
|
571
590
|
if (restartsSession()) {
|
|
572
|
-
return refreshContextSession.bind(this)()
|
|
591
|
+
return refreshContextSession.bind(this)()
|
|
573
592
|
}
|
|
574
593
|
|
|
575
594
|
if (restartsBrowser()) {
|
|
576
|
-
this.isRunning = false
|
|
577
|
-
return this._stopBrowser()
|
|
595
|
+
this.isRunning = false
|
|
596
|
+
return this._stopBrowser()
|
|
578
597
|
}
|
|
579
598
|
|
|
580
599
|
// close other sessions
|
|
581
600
|
try {
|
|
582
601
|
if ((await this.browser)._type === 'Browser') {
|
|
583
|
-
const contexts = await this.browser.contexts()
|
|
584
|
-
const currentContext = contexts[0]
|
|
602
|
+
const contexts = await this.browser.contexts()
|
|
603
|
+
const currentContext = contexts[0]
|
|
585
604
|
if (currentContext && (this.options.keepCookies || this.options.keepBrowserState)) {
|
|
586
|
-
this.storageState = await currentContext.storageState()
|
|
605
|
+
this.storageState = await currentContext.storageState()
|
|
587
606
|
}
|
|
588
607
|
|
|
589
|
-
await Promise.all(contexts.map(c => c.close()))
|
|
608
|
+
await Promise.all(contexts.map((c) => c.close()))
|
|
590
609
|
}
|
|
591
610
|
} catch (e) {
|
|
592
|
-
console.log(e)
|
|
611
|
+
console.log(e)
|
|
593
612
|
}
|
|
594
613
|
|
|
595
614
|
// await this.closeOtherTabs();
|
|
596
|
-
return this.browser
|
|
615
|
+
return this.browser
|
|
597
616
|
}
|
|
598
617
|
|
|
599
618
|
_afterSuite() {}
|
|
600
619
|
|
|
601
620
|
async _finishTest() {
|
|
602
|
-
if ((restartsSession() || restartsContext()) && this.isRunning) return this._stopBrowser()
|
|
621
|
+
if ((restartsSession() || restartsContext()) && this.isRunning) return this._stopBrowser()
|
|
603
622
|
}
|
|
604
623
|
|
|
605
624
|
_session() {
|
|
606
|
-
const defaultContext = this.browserContext
|
|
625
|
+
const defaultContext = this.browserContext
|
|
607
626
|
return {
|
|
608
627
|
start: async (sessionName = '', config) => {
|
|
609
|
-
this.debugSection('New Context', config ? JSON.stringify(config) : 'opened')
|
|
610
|
-
this.activeSessionName = sessionName
|
|
628
|
+
this.debugSection('New Context', config ? JSON.stringify(config) : 'opened')
|
|
629
|
+
this.activeSessionName = sessionName
|
|
611
630
|
|
|
612
|
-
let browserContext
|
|
613
|
-
let page
|
|
631
|
+
let browserContext
|
|
632
|
+
let page
|
|
614
633
|
if (this.isElectron) {
|
|
615
|
-
const browser = await playwright._electron.launch(this.playwrightOptions)
|
|
616
|
-
this.electronSessions.push(browser)
|
|
617
|
-
browserContext = browser.context()
|
|
618
|
-
page = await browser.firstWindow()
|
|
634
|
+
const browser = await playwright._electron.launch(this.playwrightOptions)
|
|
635
|
+
this.electronSessions.push(browser)
|
|
636
|
+
browserContext = browser.context()
|
|
637
|
+
page = await browser.firstWindow()
|
|
619
638
|
} else {
|
|
620
639
|
try {
|
|
621
|
-
browserContext = await this.browser.newContext(Object.assign(this.contextOptions, config))
|
|
622
|
-
page = await browserContext.newPage()
|
|
640
|
+
browserContext = await this.browser.newContext(Object.assign(this.contextOptions, config))
|
|
641
|
+
page = await browserContext.newPage()
|
|
623
642
|
} catch (e) {
|
|
624
643
|
if (this.playwrightOptions.userDataDir) {
|
|
625
|
-
browserContext = await playwright[this.options.browser].launchPersistentContext(
|
|
626
|
-
|
|
627
|
-
|
|
644
|
+
browserContext = await playwright[this.options.browser].launchPersistentContext(
|
|
645
|
+
`${this.userDataDir}_${this.activeSessionName}`,
|
|
646
|
+
this.playwrightOptions,
|
|
647
|
+
)
|
|
648
|
+
this.browser = browserContext
|
|
649
|
+
page = await browserContext.pages()[0]
|
|
628
650
|
}
|
|
629
651
|
}
|
|
630
652
|
}
|
|
631
653
|
|
|
632
|
-
if (this.options.trace) await browserContext.tracing.start({ screenshots: true, snapshots: true })
|
|
633
|
-
await targetCreatedHandler.call(this, page)
|
|
634
|
-
await this._setPage(page)
|
|
654
|
+
if (this.options.trace) await browserContext.tracing.start({ screenshots: true, snapshots: true })
|
|
655
|
+
await targetCreatedHandler.call(this, page)
|
|
656
|
+
await this._setPage(page)
|
|
635
657
|
// Create a new page inside context.
|
|
636
|
-
return browserContext
|
|
658
|
+
return browserContext
|
|
637
659
|
},
|
|
638
660
|
stop: async () => {
|
|
639
661
|
// is closed by _after
|
|
640
662
|
},
|
|
641
663
|
loadVars: async (context) => {
|
|
642
664
|
if (context) {
|
|
643
|
-
this.browserContext = context
|
|
644
|
-
const existingPages = await context.pages()
|
|
645
|
-
this.sessionPages[this.activeSessionName] = existingPages[0]
|
|
646
|
-
return this._setPage(this.sessionPages[this.activeSessionName])
|
|
665
|
+
this.browserContext = context
|
|
666
|
+
const existingPages = await context.pages()
|
|
667
|
+
this.sessionPages[this.activeSessionName] = existingPages[0]
|
|
668
|
+
return this._setPage(this.sessionPages[this.activeSessionName])
|
|
647
669
|
}
|
|
648
670
|
},
|
|
649
671
|
restoreVars: async (session) => {
|
|
650
|
-
this.withinLocator = null
|
|
651
|
-
this.browserContext = defaultContext
|
|
672
|
+
this.withinLocator = null
|
|
673
|
+
this.browserContext = defaultContext
|
|
652
674
|
|
|
653
675
|
if (!session) {
|
|
654
|
-
this.activeSessionName = ''
|
|
676
|
+
this.activeSessionName = ''
|
|
655
677
|
} else {
|
|
656
|
-
this.activeSessionName = session
|
|
678
|
+
this.activeSessionName = session
|
|
657
679
|
}
|
|
658
|
-
const existingPages = await this.browserContext.pages()
|
|
659
|
-
await this._setPage(existingPages[0])
|
|
680
|
+
const existingPages = await this.browserContext.pages()
|
|
681
|
+
await this._setPage(existingPages[0])
|
|
660
682
|
|
|
661
|
-
return this._waitForAction()
|
|
683
|
+
return this._waitForAction()
|
|
662
684
|
},
|
|
663
|
-
}
|
|
685
|
+
}
|
|
664
686
|
}
|
|
665
687
|
|
|
666
688
|
/**
|
|
@@ -681,7 +703,7 @@ class Playwright extends Helper {
|
|
|
681
703
|
* @param {function} fn async function that executed with Playwright helper as arguments
|
|
682
704
|
*/
|
|
683
705
|
usePlaywrightTo(description, fn) {
|
|
684
|
-
return this._useTo(...arguments)
|
|
706
|
+
return this._useTo(...arguments)
|
|
685
707
|
}
|
|
686
708
|
|
|
687
709
|
/**
|
|
@@ -695,7 +717,7 @@ class Playwright extends Helper {
|
|
|
695
717
|
* ```
|
|
696
718
|
*/
|
|
697
719
|
amAcceptingPopups() {
|
|
698
|
-
popupStore.actionType = 'accept'
|
|
720
|
+
popupStore.actionType = 'accept'
|
|
699
721
|
}
|
|
700
722
|
|
|
701
723
|
/**
|
|
@@ -704,7 +726,7 @@ class Playwright extends Helper {
|
|
|
704
726
|
* libraries](http://jster.net/category/windows-modals-popups).
|
|
705
727
|
*/
|
|
706
728
|
acceptPopup() {
|
|
707
|
-
popupStore.assertPopupActionType('accept')
|
|
729
|
+
popupStore.assertPopupActionType('accept')
|
|
708
730
|
}
|
|
709
731
|
|
|
710
732
|
/**
|
|
@@ -718,23 +740,23 @@ class Playwright extends Helper {
|
|
|
718
740
|
* ```
|
|
719
741
|
*/
|
|
720
742
|
amCancellingPopups() {
|
|
721
|
-
popupStore.actionType = 'cancel'
|
|
743
|
+
popupStore.actionType = 'cancel'
|
|
722
744
|
}
|
|
723
745
|
|
|
724
746
|
/**
|
|
725
747
|
* Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt.
|
|
726
748
|
*/
|
|
727
749
|
cancelPopup() {
|
|
728
|
-
popupStore.assertPopupActionType('cancel')
|
|
750
|
+
popupStore.assertPopupActionType('cancel')
|
|
729
751
|
}
|
|
730
752
|
|
|
731
753
|
/**
|
|
732
754
|
* {{> seeInPopup }}
|
|
733
755
|
*/
|
|
734
756
|
async seeInPopup(text) {
|
|
735
|
-
popupStore.assertPopupVisible()
|
|
736
|
-
const popupText = await popupStore.popup.message()
|
|
737
|
-
stringIncludes('text in popup').assert(text, popupText)
|
|
757
|
+
popupStore.assertPopupVisible()
|
|
758
|
+
const popupText = await popupStore.popup.message()
|
|
759
|
+
stringIncludes('text in popup').assert(text, popupText)
|
|
738
760
|
}
|
|
739
761
|
|
|
740
762
|
/**
|
|
@@ -742,21 +764,21 @@ class Playwright extends Helper {
|
|
|
742
764
|
* @param {object} page page to set
|
|
743
765
|
*/
|
|
744
766
|
async _setPage(page) {
|
|
745
|
-
page = await page
|
|
746
|
-
this._addPopupListener(page)
|
|
747
|
-
this.page = page
|
|
748
|
-
if (!page) return
|
|
749
|
-
this.browserContext.setDefaultTimeout(0)
|
|
750
|
-
page.setDefaultNavigationTimeout(this.options.getPageTimeout)
|
|
751
|
-
page.setDefaultTimeout(this.options.timeout)
|
|
767
|
+
page = await page
|
|
768
|
+
this._addPopupListener(page)
|
|
769
|
+
this.page = page
|
|
770
|
+
if (!page) return
|
|
771
|
+
this.browserContext.setDefaultTimeout(0)
|
|
772
|
+
page.setDefaultNavigationTimeout(this.options.getPageTimeout)
|
|
773
|
+
page.setDefaultTimeout(this.options.timeout)
|
|
752
774
|
|
|
753
775
|
page.on('crash', async () => {
|
|
754
|
-
console.log('ERROR: Page has crashed, closing page!')
|
|
755
|
-
await page.close()
|
|
756
|
-
})
|
|
757
|
-
this.context = await this.page
|
|
758
|
-
this.contextLocator = null
|
|
759
|
-
await page.bringToFront()
|
|
776
|
+
console.log('ERROR: Page has crashed, closing page!')
|
|
777
|
+
await page.close()
|
|
778
|
+
})
|
|
779
|
+
this.context = await this.page
|
|
780
|
+
this.contextLocator = null
|
|
781
|
+
await page.bringToFront()
|
|
760
782
|
}
|
|
761
783
|
|
|
762
784
|
/**
|
|
@@ -768,33 +790,33 @@ class Playwright extends Helper {
|
|
|
768
790
|
*/
|
|
769
791
|
_addPopupListener(page) {
|
|
770
792
|
if (!page) {
|
|
771
|
-
return
|
|
793
|
+
return
|
|
772
794
|
}
|
|
773
|
-
page.removeAllListeners('dialog')
|
|
795
|
+
page.removeAllListeners('dialog')
|
|
774
796
|
page.on('dialog', async (dialog) => {
|
|
775
|
-
popupStore.popup = dialog
|
|
776
|
-
const action = popupStore.actionType || this.options.defaultPopupAction
|
|
777
|
-
await this._waitForAction()
|
|
797
|
+
popupStore.popup = dialog
|
|
798
|
+
const action = popupStore.actionType || this.options.defaultPopupAction
|
|
799
|
+
await this._waitForAction()
|
|
778
800
|
|
|
779
801
|
switch (action) {
|
|
780
802
|
case 'accept':
|
|
781
|
-
return dialog.accept()
|
|
803
|
+
return dialog.accept()
|
|
782
804
|
|
|
783
805
|
case 'cancel':
|
|
784
|
-
return dialog.dismiss()
|
|
806
|
+
return dialog.dismiss()
|
|
785
807
|
|
|
786
808
|
default: {
|
|
787
|
-
throw new Error('Unknown popup action type. Only "accept" or "cancel" are accepted')
|
|
809
|
+
throw new Error('Unknown popup action type. Only "accept" or "cancel" are accepted')
|
|
788
810
|
}
|
|
789
811
|
}
|
|
790
|
-
})
|
|
812
|
+
})
|
|
791
813
|
}
|
|
792
814
|
|
|
793
815
|
/**
|
|
794
816
|
* Gets page URL including hash.
|
|
795
817
|
*/
|
|
796
818
|
async _getPageUrl() {
|
|
797
|
-
return this.executeScript(() => window.location.href)
|
|
819
|
+
return this.executeScript(() => window.location.href)
|
|
798
820
|
}
|
|
799
821
|
|
|
800
822
|
/**
|
|
@@ -807,45 +829,48 @@ class Playwright extends Helper {
|
|
|
807
829
|
*/
|
|
808
830
|
async grabPopupText() {
|
|
809
831
|
if (popupStore.popup) {
|
|
810
|
-
return popupStore.popup.message()
|
|
832
|
+
return popupStore.popup.message()
|
|
811
833
|
}
|
|
812
|
-
return null
|
|
834
|
+
return null
|
|
813
835
|
}
|
|
814
836
|
|
|
815
837
|
async _startBrowser() {
|
|
816
838
|
if (this.isElectron) {
|
|
817
|
-
this.browser = await playwright._electron.launch(this.playwrightOptions)
|
|
839
|
+
this.browser = await playwright._electron.launch(this.playwrightOptions)
|
|
818
840
|
} else if (this.isRemoteBrowser && this.isCDPConnection) {
|
|
819
841
|
try {
|
|
820
|
-
this.browser = await playwright[this.options.browser].connectOverCDP(this.playwrightOptions)
|
|
842
|
+
this.browser = await playwright[this.options.browser].connectOverCDP(this.playwrightOptions)
|
|
821
843
|
} catch (err) {
|
|
822
844
|
if (err.toString().indexOf('ECONNREFUSED')) {
|
|
823
|
-
throw new RemoteBrowserConnectionRefused(err)
|
|
845
|
+
throw new RemoteBrowserConnectionRefused(err)
|
|
824
846
|
}
|
|
825
|
-
throw err
|
|
847
|
+
throw err
|
|
826
848
|
}
|
|
827
849
|
} else if (this.isRemoteBrowser) {
|
|
828
850
|
try {
|
|
829
|
-
this.browser = await playwright[this.options.browser].connect(this.playwrightOptions)
|
|
851
|
+
this.browser = await playwright[this.options.browser].connect(this.playwrightOptions)
|
|
830
852
|
} catch (err) {
|
|
831
853
|
if (err.toString().indexOf('ECONNREFUSED')) {
|
|
832
|
-
throw new RemoteBrowserConnectionRefused(err)
|
|
854
|
+
throw new RemoteBrowserConnectionRefused(err)
|
|
833
855
|
}
|
|
834
|
-
throw err
|
|
856
|
+
throw err
|
|
835
857
|
}
|
|
836
858
|
} else if (this.playwrightOptions.userDataDir) {
|
|
837
|
-
this.browser = await playwright[this.options.browser].launchPersistentContext(
|
|
859
|
+
this.browser = await playwright[this.options.browser].launchPersistentContext(
|
|
860
|
+
this.userDataDir,
|
|
861
|
+
this.playwrightOptions,
|
|
862
|
+
)
|
|
838
863
|
} else {
|
|
839
|
-
this.browser = await playwright[this.options.browser].launch(this.playwrightOptions)
|
|
864
|
+
this.browser = await playwright[this.options.browser].launch(this.playwrightOptions)
|
|
840
865
|
}
|
|
841
866
|
|
|
842
867
|
// works only for Chromium
|
|
843
868
|
this.browser.on('targetchanged', (target) => {
|
|
844
|
-
this.debugSection('Url', target.url())
|
|
845
|
-
})
|
|
869
|
+
this.debugSection('Url', target.url())
|
|
870
|
+
})
|
|
846
871
|
|
|
847
|
-
this.isRunning = true
|
|
848
|
-
return this.browser
|
|
872
|
+
this.isRunning = true
|
|
873
|
+
return this.browser
|
|
849
874
|
}
|
|
850
875
|
|
|
851
876
|
/**
|
|
@@ -854,72 +879,72 @@ class Playwright extends Helper {
|
|
|
854
879
|
* @param {object} [contextOptions] See https://playwright.dev/docs/api/class-browser#browser-new-context
|
|
855
880
|
*/
|
|
856
881
|
async _createContextPage(contextOptions) {
|
|
857
|
-
this.browserContext = await this.browser.newContext(contextOptions)
|
|
858
|
-
const page = await this.browserContext.newPage()
|
|
859
|
-
targetCreatedHandler.call(this, page)
|
|
860
|
-
await this._setPage(page)
|
|
882
|
+
this.browserContext = await this.browser.newContext(contextOptions)
|
|
883
|
+
const page = await this.browserContext.newPage()
|
|
884
|
+
targetCreatedHandler.call(this, page)
|
|
885
|
+
await this._setPage(page)
|
|
861
886
|
}
|
|
862
887
|
|
|
863
888
|
_getType() {
|
|
864
|
-
return this.browser._type
|
|
889
|
+
return this.browser._type
|
|
865
890
|
}
|
|
866
891
|
|
|
867
892
|
async _stopBrowser() {
|
|
868
|
-
this.withinLocator = null
|
|
869
|
-
await this._setPage(null)
|
|
870
|
-
this.context = null
|
|
871
|
-
this.frame = null
|
|
872
|
-
popupStore.clear()
|
|
873
|
-
if (this.options.recordHar) await this.browserContext.close()
|
|
874
|
-
await this.browser.close()
|
|
893
|
+
this.withinLocator = null
|
|
894
|
+
await this._setPage(null)
|
|
895
|
+
this.context = null
|
|
896
|
+
this.frame = null
|
|
897
|
+
popupStore.clear()
|
|
898
|
+
if (this.options.recordHar) await this.browserContext.close()
|
|
899
|
+
await this.browser.close()
|
|
875
900
|
}
|
|
876
901
|
|
|
877
902
|
async _evaluateHandeInContext(...args) {
|
|
878
|
-
const context = await this._getContext()
|
|
879
|
-
return context.evaluateHandle(...args)
|
|
903
|
+
const context = await this._getContext()
|
|
904
|
+
return context.evaluateHandle(...args)
|
|
880
905
|
}
|
|
881
906
|
|
|
882
907
|
async _withinBegin(locator) {
|
|
883
908
|
if (this.withinLocator) {
|
|
884
|
-
throw new Error(
|
|
909
|
+
throw new Error("Can't start within block inside another within block")
|
|
885
910
|
}
|
|
886
911
|
|
|
887
|
-
const frame = isFrameLocator(locator)
|
|
912
|
+
const frame = isFrameLocator(locator)
|
|
888
913
|
|
|
889
914
|
if (frame) {
|
|
890
915
|
if (Array.isArray(frame)) {
|
|
891
|
-
await this.switchTo(null)
|
|
892
|
-
return frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve())
|
|
916
|
+
await this.switchTo(null)
|
|
917
|
+
return frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve())
|
|
893
918
|
}
|
|
894
|
-
await this.switchTo(frame)
|
|
895
|
-
this.withinLocator = new Locator(frame)
|
|
896
|
-
return
|
|
919
|
+
await this.switchTo(frame)
|
|
920
|
+
this.withinLocator = new Locator(frame)
|
|
921
|
+
return
|
|
897
922
|
}
|
|
898
923
|
|
|
899
|
-
const el = await this._locateElement(locator)
|
|
900
|
-
assertElementExists(el, locator)
|
|
901
|
-
this.context = el
|
|
902
|
-
this.contextLocator = locator
|
|
924
|
+
const el = await this._locateElement(locator)
|
|
925
|
+
assertElementExists(el, locator)
|
|
926
|
+
this.context = el
|
|
927
|
+
this.contextLocator = locator
|
|
903
928
|
|
|
904
|
-
this.withinLocator = new Locator(locator)
|
|
929
|
+
this.withinLocator = new Locator(locator)
|
|
905
930
|
}
|
|
906
931
|
|
|
907
932
|
async _withinEnd() {
|
|
908
|
-
this.withinLocator = null
|
|
909
|
-
this.context = await this.page
|
|
910
|
-
this.contextLocator = null
|
|
911
|
-
this.frame = null
|
|
933
|
+
this.withinLocator = null
|
|
934
|
+
this.context = await this.page
|
|
935
|
+
this.contextLocator = null
|
|
936
|
+
this.frame = null
|
|
912
937
|
}
|
|
913
938
|
|
|
914
939
|
_extractDataFromPerformanceTiming(timing, ...dataNames) {
|
|
915
|
-
const navigationStart = timing.navigationStart
|
|
940
|
+
const navigationStart = timing.navigationStart
|
|
916
941
|
|
|
917
|
-
const extractedData = {}
|
|
942
|
+
const extractedData = {}
|
|
918
943
|
dataNames.forEach((name) => {
|
|
919
|
-
extractedData[name] = timing[name] - navigationStart
|
|
920
|
-
})
|
|
944
|
+
extractedData[name] = timing[name] - navigationStart
|
|
945
|
+
})
|
|
921
946
|
|
|
922
|
-
return extractedData
|
|
947
|
+
return extractedData
|
|
923
948
|
}
|
|
924
949
|
|
|
925
950
|
/**
|
|
@@ -927,22 +952,22 @@ class Playwright extends Helper {
|
|
|
927
952
|
*/
|
|
928
953
|
async amOnPage(url) {
|
|
929
954
|
if (this.isElectron) {
|
|
930
|
-
throw new Error('Cannot open pages inside an Electron container')
|
|
955
|
+
throw new Error('Cannot open pages inside an Electron container')
|
|
931
956
|
}
|
|
932
|
-
if (
|
|
933
|
-
url = this.options.url + (url.startsWith('/') ? url : `/${url}`)
|
|
957
|
+
if (!/^\w+\:(\/\/|.+)/.test(url)) {
|
|
958
|
+
url = this.options.url + (url.startsWith('/') ? url : `/${url}`)
|
|
934
959
|
}
|
|
935
960
|
|
|
936
|
-
if (this.options.basicAuth &&
|
|
961
|
+
if (this.options.basicAuth && this.isAuthenticated !== true) {
|
|
937
962
|
if (url.includes(this.options.url)) {
|
|
938
|
-
await this.browserContext.setHTTPCredentials(this.options.basicAuth)
|
|
939
|
-
this.isAuthenticated = true
|
|
963
|
+
await this.browserContext.setHTTPCredentials(this.options.basicAuth)
|
|
964
|
+
this.isAuthenticated = true
|
|
940
965
|
}
|
|
941
966
|
}
|
|
942
967
|
|
|
943
|
-
await this.page.goto(url, { waitUntil: this.options.waitForNavigation })
|
|
968
|
+
await this.page.goto(url, { waitUntil: this.options.waitForNavigation })
|
|
944
969
|
|
|
945
|
-
const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)))
|
|
970
|
+
const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)))
|
|
946
971
|
|
|
947
972
|
perfTiming = this._extractDataFromPerformanceTiming(
|
|
948
973
|
performanceTiming,
|
|
@@ -950,9 +975,9 @@ class Playwright extends Helper {
|
|
|
950
975
|
'domInteractive',
|
|
951
976
|
'domContentLoadedEventEnd',
|
|
952
977
|
'loadEventEnd',
|
|
953
|
-
)
|
|
978
|
+
)
|
|
954
979
|
|
|
955
|
-
return this._waitForAction()
|
|
980
|
+
return this._waitForAction()
|
|
956
981
|
}
|
|
957
982
|
|
|
958
983
|
/**
|
|
@@ -973,11 +998,11 @@ class Playwright extends Helper {
|
|
|
973
998
|
*/
|
|
974
999
|
async resizeWindow(width, height) {
|
|
975
1000
|
if (width === 'maximize') {
|
|
976
|
-
throw new Error(
|
|
1001
|
+
throw new Error("Playwright can't control windows, so it can't maximize it")
|
|
977
1002
|
}
|
|
978
1003
|
|
|
979
|
-
await this.page.setViewportSize({ width, height })
|
|
980
|
-
return this._waitForAction()
|
|
1004
|
+
await this.page.setViewportSize({ width, height })
|
|
1005
|
+
return this._waitForAction()
|
|
981
1006
|
}
|
|
982
1007
|
|
|
983
1008
|
/**
|
|
@@ -993,9 +1018,9 @@ class Playwright extends Helper {
|
|
|
993
1018
|
*/
|
|
994
1019
|
async setPlaywrightRequestHeaders(customHeaders) {
|
|
995
1020
|
if (!customHeaders) {
|
|
996
|
-
throw new Error('Cannot send empty headers.')
|
|
1021
|
+
throw new Error('Cannot send empty headers.')
|
|
997
1022
|
}
|
|
998
|
-
return this.browserContext.setExtraHTTPHeaders(customHeaders)
|
|
1023
|
+
return this.browserContext.setExtraHTTPHeaders(customHeaders)
|
|
999
1024
|
}
|
|
1000
1025
|
|
|
1001
1026
|
/**
|
|
@@ -1003,13 +1028,13 @@ class Playwright extends Helper {
|
|
|
1003
1028
|
*
|
|
1004
1029
|
*/
|
|
1005
1030
|
async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
|
|
1006
|
-
const el = await this._locateElement(locator)
|
|
1007
|
-
assertElementExists(el, locator)
|
|
1031
|
+
const el = await this._locateElement(locator)
|
|
1032
|
+
assertElementExists(el, locator)
|
|
1008
1033
|
|
|
1009
1034
|
// Use manual mouse.move instead of .hover() so the offset can be added to the coordinates
|
|
1010
|
-
const { x, y } = await clickablePoint(el)
|
|
1011
|
-
await this.page.mouse.move(x + offsetX, y + offsetY)
|
|
1012
|
-
return this._waitForAction()
|
|
1035
|
+
const { x, y } = await clickablePoint(el)
|
|
1036
|
+
await this.page.mouse.move(x + offsetX, y + offsetY)
|
|
1037
|
+
return this._waitForAction()
|
|
1013
1038
|
}
|
|
1014
1039
|
|
|
1015
1040
|
/**
|
|
@@ -1017,11 +1042,11 @@ class Playwright extends Helper {
|
|
|
1017
1042
|
*
|
|
1018
1043
|
*/
|
|
1019
1044
|
async focus(locator, options = {}) {
|
|
1020
|
-
const el = await this._locateElement(locator)
|
|
1021
|
-
assertElementExists(el, locator, 'Element to focus')
|
|
1045
|
+
const el = await this._locateElement(locator)
|
|
1046
|
+
assertElementExists(el, locator, 'Element to focus')
|
|
1022
1047
|
|
|
1023
|
-
await el.focus(options)
|
|
1024
|
-
return this._waitForAction()
|
|
1048
|
+
await el.focus(options)
|
|
1049
|
+
return this._waitForAction()
|
|
1025
1050
|
}
|
|
1026
1051
|
|
|
1027
1052
|
/**
|
|
@@ -1029,11 +1054,11 @@ class Playwright extends Helper {
|
|
|
1029
1054
|
*
|
|
1030
1055
|
*/
|
|
1031
1056
|
async blur(locator, options = {}) {
|
|
1032
|
-
const el = await this._locateElement(locator)
|
|
1033
|
-
assertElementExists(el, locator, 'Element to blur')
|
|
1057
|
+
const el = await this._locateElement(locator)
|
|
1058
|
+
assertElementExists(el, locator, 'Element to blur')
|
|
1034
1059
|
|
|
1035
|
-
await el.blur(options)
|
|
1036
|
-
return this._waitForAction()
|
|
1060
|
+
await el.blur(options)
|
|
1061
|
+
return this._waitForAction()
|
|
1037
1062
|
}
|
|
1038
1063
|
/**
|
|
1039
1064
|
* Return the checked status of given element.
|
|
@@ -1045,14 +1070,14 @@ class Playwright extends Helper {
|
|
|
1045
1070
|
*/
|
|
1046
1071
|
|
|
1047
1072
|
async grabCheckedElementStatus(locator, options = {}) {
|
|
1048
|
-
const supportedTypes = ['checkbox', 'radio']
|
|
1049
|
-
const el = await this._locateElement(locator)
|
|
1050
|
-
const type = await el.getAttribute('type')
|
|
1073
|
+
const supportedTypes = ['checkbox', 'radio']
|
|
1074
|
+
const el = await this._locateElement(locator)
|
|
1075
|
+
const type = await el.getAttribute('type')
|
|
1051
1076
|
|
|
1052
1077
|
if (supportedTypes.includes(type)) {
|
|
1053
|
-
return el.isChecked(options)
|
|
1078
|
+
return el.isChecked(options)
|
|
1054
1079
|
}
|
|
1055
|
-
throw new Error(`Element is not a ${supportedTypes.join(' or ')} input`)
|
|
1080
|
+
throw new Error(`Element is not a ${supportedTypes.join(' or ')} input`)
|
|
1056
1081
|
}
|
|
1057
1082
|
/**
|
|
1058
1083
|
* Return the disabled status of given element.
|
|
@@ -1064,8 +1089,8 @@ class Playwright extends Helper {
|
|
|
1064
1089
|
*/
|
|
1065
1090
|
|
|
1066
1091
|
async grabDisabledElementStatus(locator, options = {}) {
|
|
1067
|
-
const el = await this._locateElement(locator)
|
|
1068
|
-
return el.isDisabled(options)
|
|
1092
|
+
const el = await this._locateElement(locator)
|
|
1093
|
+
return el.isDisabled(options)
|
|
1069
1094
|
}
|
|
1070
1095
|
|
|
1071
1096
|
/**
|
|
@@ -1082,24 +1107,24 @@ class Playwright extends Helper {
|
|
|
1082
1107
|
*
|
|
1083
1108
|
*/
|
|
1084
1109
|
async dragAndDrop(srcElement, destElement, options) {
|
|
1085
|
-
const src = new Locator(srcElement)
|
|
1086
|
-
const dst = new Locator(destElement)
|
|
1110
|
+
const src = new Locator(srcElement)
|
|
1111
|
+
const dst = new Locator(destElement)
|
|
1087
1112
|
|
|
1088
1113
|
if (options) {
|
|
1089
|
-
return this.page.dragAndDrop(buildLocatorString(src), buildLocatorString(dst), options)
|
|
1114
|
+
return this.page.dragAndDrop(buildLocatorString(src), buildLocatorString(dst), options)
|
|
1090
1115
|
}
|
|
1091
1116
|
|
|
1092
|
-
const _smallWaitInMs = 600
|
|
1093
|
-
await this.page.locator(buildLocatorString(src)).hover()
|
|
1094
|
-
await this.page.mouse.down()
|
|
1095
|
-
await this.page.waitForTimeout(_smallWaitInMs)
|
|
1117
|
+
const _smallWaitInMs = 600
|
|
1118
|
+
await this.page.locator(buildLocatorString(src)).hover()
|
|
1119
|
+
await this.page.mouse.down()
|
|
1120
|
+
await this.page.waitForTimeout(_smallWaitInMs)
|
|
1096
1121
|
|
|
1097
|
-
const destElBox = await this.page.locator(buildLocatorString(dst)).boundingBox()
|
|
1122
|
+
const destElBox = await this.page.locator(buildLocatorString(dst)).boundingBox()
|
|
1098
1123
|
|
|
1099
|
-
await this.page.mouse.move(destElBox.x + destElBox.width / 2, destElBox.y + destElBox.height / 2)
|
|
1100
|
-
await this.page.locator(buildLocatorString(dst)).hover({ position: { x: 10, y: 10 } })
|
|
1101
|
-
await this.page.waitForTimeout(_smallWaitInMs)
|
|
1102
|
-
await this.page.mouse.up()
|
|
1124
|
+
await this.page.mouse.move(destElBox.x + destElBox.width / 2, destElBox.y + destElBox.height / 2)
|
|
1125
|
+
await this.page.locator(buildLocatorString(dst)).hover({ position: { x: 10, y: 10 } })
|
|
1126
|
+
await this.page.waitForTimeout(_smallWaitInMs)
|
|
1127
|
+
await this.page.mouse.up()
|
|
1103
1128
|
}
|
|
1104
1129
|
|
|
1105
1130
|
/**
|
|
@@ -1117,16 +1142,16 @@ class Playwright extends Helper {
|
|
|
1117
1142
|
* @param {object} [contextOptions] [Options for browser context](https://playwright.dev/docs/api/class-browser#browser-new-context) when starting new browser
|
|
1118
1143
|
*/
|
|
1119
1144
|
async restartBrowser(contextOptions) {
|
|
1120
|
-
await this._stopBrowser()
|
|
1121
|
-
await this._startBrowser()
|
|
1122
|
-
await this._createContextPage(contextOptions)
|
|
1145
|
+
await this._stopBrowser()
|
|
1146
|
+
await this._startBrowser()
|
|
1147
|
+
await this._createContextPage(contextOptions)
|
|
1123
1148
|
}
|
|
1124
1149
|
|
|
1125
1150
|
/**
|
|
1126
1151
|
* {{> refreshPage }}
|
|
1127
1152
|
*/
|
|
1128
1153
|
async refreshPage() {
|
|
1129
|
-
return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation })
|
|
1154
|
+
return this.page.reload({ timeout: this.options.getPageTimeout, waitUntil: this.options.waitForNavigation })
|
|
1130
1155
|
}
|
|
1131
1156
|
|
|
1132
1157
|
/**
|
|
@@ -1147,13 +1172,13 @@ class Playwright extends Helper {
|
|
|
1147
1172
|
* @returns Promise<void>
|
|
1148
1173
|
*/
|
|
1149
1174
|
async replayFromHar(harFilePath, opts) {
|
|
1150
|
-
const file = path.join(global.codecept_dir, harFilePath)
|
|
1175
|
+
const file = path.join(global.codecept_dir, harFilePath)
|
|
1151
1176
|
|
|
1152
1177
|
if (!fileExists(file)) {
|
|
1153
|
-
throw new Error(`File at ${file} cannot be found on local system`)
|
|
1178
|
+
throw new Error(`File at ${file} cannot be found on local system`)
|
|
1154
1179
|
}
|
|
1155
1180
|
|
|
1156
|
-
await this.page.routeFromHAR(harFilePath, opts)
|
|
1181
|
+
await this.page.routeFromHAR(harFilePath, opts)
|
|
1157
1182
|
}
|
|
1158
1183
|
|
|
1159
1184
|
/**
|
|
@@ -1161,8 +1186,8 @@ class Playwright extends Helper {
|
|
|
1161
1186
|
*/
|
|
1162
1187
|
scrollPageToTop() {
|
|
1163
1188
|
return this.executeScript(() => {
|
|
1164
|
-
window.scrollTo(0, 0)
|
|
1165
|
-
})
|
|
1189
|
+
window.scrollTo(0, 0)
|
|
1190
|
+
})
|
|
1166
1191
|
}
|
|
1167
1192
|
|
|
1168
1193
|
/**
|
|
@@ -1170,16 +1195,13 @@ class Playwright extends Helper {
|
|
|
1170
1195
|
*/
|
|
1171
1196
|
async scrollPageToBottom() {
|
|
1172
1197
|
return this.executeScript(() => {
|
|
1173
|
-
const body = document.body
|
|
1174
|
-
const html = document.documentElement
|
|
1175
|
-
window.scrollTo(
|
|
1176
|
-
|
|
1177
|
-
body.offsetHeight,
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
html.offsetHeight,
|
|
1181
|
-
));
|
|
1182
|
-
});
|
|
1198
|
+
const body = document.body
|
|
1199
|
+
const html = document.documentElement
|
|
1200
|
+
window.scrollTo(
|
|
1201
|
+
0,
|
|
1202
|
+
Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
|
|
1203
|
+
)
|
|
1204
|
+
})
|
|
1183
1205
|
}
|
|
1184
1206
|
|
|
1185
1207
|
/**
|
|
@@ -1187,67 +1209,69 @@ class Playwright extends Helper {
|
|
|
1187
1209
|
*/
|
|
1188
1210
|
async scrollTo(locator, offsetX = 0, offsetY = 0) {
|
|
1189
1211
|
if (typeof locator === 'number' && typeof offsetX === 'number') {
|
|
1190
|
-
offsetY = offsetX
|
|
1191
|
-
offsetX = locator
|
|
1192
|
-
locator = null
|
|
1212
|
+
offsetY = offsetX
|
|
1213
|
+
offsetX = locator
|
|
1214
|
+
locator = null
|
|
1193
1215
|
}
|
|
1194
1216
|
|
|
1195
1217
|
if (locator) {
|
|
1196
|
-
const el = await this._locateElement(locator)
|
|
1197
|
-
assertElementExists(el, locator, 'Element')
|
|
1198
|
-
await el.scrollIntoViewIfNeeded()
|
|
1199
|
-
const elementCoordinates = await clickablePoint(el)
|
|
1200
|
-
await this.executeScript((offsetX, offsetY) => window.scrollBy(offsetX, offsetY), {
|
|
1218
|
+
const el = await this._locateElement(locator)
|
|
1219
|
+
assertElementExists(el, locator, 'Element')
|
|
1220
|
+
await el.scrollIntoViewIfNeeded()
|
|
1221
|
+
const elementCoordinates = await clickablePoint(el)
|
|
1222
|
+
await this.executeScript((offsetX, offsetY) => window.scrollBy(offsetX, offsetY), {
|
|
1223
|
+
offsetX: elementCoordinates.x + offsetX,
|
|
1224
|
+
offsetY: elementCoordinates.y + offsetY,
|
|
1225
|
+
})
|
|
1201
1226
|
} else {
|
|
1202
|
-
await this.executeScript(({ offsetX, offsetY }) => window.scrollTo(offsetX, offsetY), { offsetX, offsetY })
|
|
1227
|
+
await this.executeScript(({ offsetX, offsetY }) => window.scrollTo(offsetX, offsetY), { offsetX, offsetY })
|
|
1203
1228
|
}
|
|
1204
|
-
return this._waitForAction()
|
|
1229
|
+
return this._waitForAction()
|
|
1205
1230
|
}
|
|
1206
1231
|
|
|
1207
1232
|
/**
|
|
1208
1233
|
* {{> seeInTitle }}
|
|
1209
1234
|
*/
|
|
1210
1235
|
async seeInTitle(text) {
|
|
1211
|
-
const title = await this.page.title()
|
|
1212
|
-
stringIncludes('web page title').assert(text, title)
|
|
1236
|
+
const title = await this.page.title()
|
|
1237
|
+
stringIncludes('web page title').assert(text, title)
|
|
1213
1238
|
}
|
|
1214
1239
|
|
|
1215
1240
|
/**
|
|
1216
1241
|
* {{> grabPageScrollPosition }}
|
|
1217
1242
|
*/
|
|
1218
1243
|
async grabPageScrollPosition() {
|
|
1219
|
-
/* eslint-disable comma-dangle */
|
|
1220
1244
|
function getScrollPosition() {
|
|
1221
1245
|
return {
|
|
1222
1246
|
x: window.pageXOffset,
|
|
1223
|
-
y: window.pageYOffset
|
|
1224
|
-
}
|
|
1247
|
+
y: window.pageYOffset,
|
|
1248
|
+
}
|
|
1225
1249
|
}
|
|
1226
|
-
|
|
1227
|
-
return this.executeScript(getScrollPosition)
|
|
1250
|
+
|
|
1251
|
+
return this.executeScript(getScrollPosition)
|
|
1228
1252
|
}
|
|
1229
1253
|
|
|
1230
1254
|
/**
|
|
1231
1255
|
* {{> seeTitleEquals }}
|
|
1232
1256
|
*/
|
|
1233
1257
|
async seeTitleEquals(text) {
|
|
1234
|
-
const title = await this.page.title()
|
|
1235
|
-
return equals('web page title').assert(title, text)
|
|
1258
|
+
const title = await this.page.title()
|
|
1259
|
+
return equals('web page title').assert(title, text)
|
|
1236
1260
|
}
|
|
1237
1261
|
|
|
1238
1262
|
/**
|
|
1239
1263
|
* {{> dontSeeInTitle }}
|
|
1240
1264
|
*/
|
|
1241
1265
|
async dontSeeInTitle(text) {
|
|
1242
|
-
const title = await this.page.title()
|
|
1243
|
-
stringIncludes('web page title').negate(text, title)
|
|
1266
|
+
const title = await this.page.title()
|
|
1267
|
+
stringIncludes('web page title').negate(text, title)
|
|
1244
1268
|
}
|
|
1245
1269
|
|
|
1246
1270
|
/**
|
|
1247
1271
|
* {{> grabTitle }}
|
|
1248
1272
|
*/
|
|
1249
1273
|
async grabTitle() {
|
|
1250
|
-
return this.page.title()
|
|
1274
|
+
return this.page.title()
|
|
1251
1275
|
}
|
|
1252
1276
|
|
|
1253
1277
|
/**
|
|
@@ -1259,11 +1283,11 @@ class Playwright extends Helper {
|
|
|
1259
1283
|
* ```
|
|
1260
1284
|
*/
|
|
1261
1285
|
async _locate(locator) {
|
|
1262
|
-
const context =
|
|
1286
|
+
const context = await this._getContext()
|
|
1263
1287
|
|
|
1264
|
-
if (this.frame) return findElements(this.frame, locator)
|
|
1288
|
+
if (this.frame) return findElements(this.frame, locator)
|
|
1265
1289
|
|
|
1266
|
-
return findElements(context, locator)
|
|
1290
|
+
return findElements(context, locator)
|
|
1267
1291
|
}
|
|
1268
1292
|
|
|
1269
1293
|
/**
|
|
@@ -1275,8 +1299,8 @@ class Playwright extends Helper {
|
|
|
1275
1299
|
* ```
|
|
1276
1300
|
*/
|
|
1277
1301
|
async _locateElement(locator) {
|
|
1278
|
-
const context =
|
|
1279
|
-
return findElement(context, locator)
|
|
1302
|
+
const context = await this._getContext()
|
|
1303
|
+
return findElement(context, locator)
|
|
1280
1304
|
}
|
|
1281
1305
|
|
|
1282
1306
|
/**
|
|
@@ -1288,10 +1312,10 @@ class Playwright extends Helper {
|
|
|
1288
1312
|
* ```
|
|
1289
1313
|
*/
|
|
1290
1314
|
async _locateCheckable(locator, providedContext = null) {
|
|
1291
|
-
const context = providedContext || (await this._getContext())
|
|
1292
|
-
const els = await findCheckable.call(this, locator, context)
|
|
1293
|
-
assertElementExists(els[0], locator, 'Checkbox or radio')
|
|
1294
|
-
return els[0]
|
|
1315
|
+
const context = providedContext || (await this._getContext())
|
|
1316
|
+
const els = await findCheckable.call(this, locator, context)
|
|
1317
|
+
assertElementExists(els[0], locator, 'Checkbox or radio')
|
|
1318
|
+
return els[0]
|
|
1295
1319
|
}
|
|
1296
1320
|
|
|
1297
1321
|
/**
|
|
@@ -1302,8 +1326,8 @@ class Playwright extends Helper {
|
|
|
1302
1326
|
* ```
|
|
1303
1327
|
*/
|
|
1304
1328
|
async _locateClickable(locator) {
|
|
1305
|
-
const context = await this._getContext()
|
|
1306
|
-
return findClickable.call(this, context, locator)
|
|
1329
|
+
const context = await this._getContext()
|
|
1330
|
+
return findClickable.call(this, context, locator)
|
|
1307
1331
|
}
|
|
1308
1332
|
|
|
1309
1333
|
/**
|
|
@@ -1314,7 +1338,7 @@ class Playwright extends Helper {
|
|
|
1314
1338
|
* ```
|
|
1315
1339
|
*/
|
|
1316
1340
|
async _locateFields(locator) {
|
|
1317
|
-
return findFields.call(this, locator)
|
|
1341
|
+
return findFields.call(this, locator)
|
|
1318
1342
|
}
|
|
1319
1343
|
|
|
1320
1344
|
/**
|
|
@@ -1322,7 +1346,7 @@ class Playwright extends Helper {
|
|
|
1322
1346
|
*
|
|
1323
1347
|
*/
|
|
1324
1348
|
async grabWebElements(locator) {
|
|
1325
|
-
return this._locate(locator)
|
|
1349
|
+
return this._locate(locator)
|
|
1326
1350
|
}
|
|
1327
1351
|
|
|
1328
1352
|
/**
|
|
@@ -1330,7 +1354,7 @@ class Playwright extends Helper {
|
|
|
1330
1354
|
*
|
|
1331
1355
|
*/
|
|
1332
1356
|
async grabWebElement(locator) {
|
|
1333
|
-
return this._locateElement(locator)
|
|
1357
|
+
return this._locateElement(locator)
|
|
1334
1358
|
}
|
|
1335
1359
|
|
|
1336
1360
|
/**
|
|
@@ -1345,20 +1369,20 @@ class Playwright extends Helper {
|
|
|
1345
1369
|
*/
|
|
1346
1370
|
async switchToNextTab(num = 1) {
|
|
1347
1371
|
if (this.isElectron) {
|
|
1348
|
-
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1372
|
+
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1349
1373
|
}
|
|
1350
|
-
const pages = await this.browserContext.pages()
|
|
1374
|
+
const pages = await this.browserContext.pages()
|
|
1351
1375
|
|
|
1352
|
-
const index = pages.indexOf(this.page)
|
|
1353
|
-
this.withinLocator = null
|
|
1354
|
-
const page = pages[index + num]
|
|
1376
|
+
const index = pages.indexOf(this.page)
|
|
1377
|
+
this.withinLocator = null
|
|
1378
|
+
const page = pages[index + num]
|
|
1355
1379
|
|
|
1356
1380
|
if (!page) {
|
|
1357
|
-
throw new Error(`There is no ability to switch to next tab with offset ${num}`)
|
|
1381
|
+
throw new Error(`There is no ability to switch to next tab with offset ${num}`)
|
|
1358
1382
|
}
|
|
1359
|
-
await targetCreatedHandler.call(this, page)
|
|
1360
|
-
await this._setPage(page)
|
|
1361
|
-
return this._waitForAction()
|
|
1383
|
+
await targetCreatedHandler.call(this, page)
|
|
1384
|
+
await this._setPage(page)
|
|
1385
|
+
return this._waitForAction()
|
|
1362
1386
|
}
|
|
1363
1387
|
|
|
1364
1388
|
/**
|
|
@@ -1372,19 +1396,19 @@ class Playwright extends Helper {
|
|
|
1372
1396
|
*/
|
|
1373
1397
|
async switchToPreviousTab(num = 1) {
|
|
1374
1398
|
if (this.isElectron) {
|
|
1375
|
-
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1399
|
+
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1376
1400
|
}
|
|
1377
|
-
const pages = await this.browserContext.pages()
|
|
1378
|
-
const index = pages.indexOf(this.page)
|
|
1379
|
-
this.withinLocator = null
|
|
1380
|
-
const page = pages[index - num]
|
|
1401
|
+
const pages = await this.browserContext.pages()
|
|
1402
|
+
const index = pages.indexOf(this.page)
|
|
1403
|
+
this.withinLocator = null
|
|
1404
|
+
const page = pages[index - num]
|
|
1381
1405
|
|
|
1382
1406
|
if (!page) {
|
|
1383
|
-
throw new Error(`There is no ability to switch to previous tab with offset ${num}`)
|
|
1407
|
+
throw new Error(`There is no ability to switch to previous tab with offset ${num}`)
|
|
1384
1408
|
}
|
|
1385
1409
|
|
|
1386
|
-
await this._setPage(page)
|
|
1387
|
-
return this._waitForAction()
|
|
1410
|
+
await this._setPage(page)
|
|
1411
|
+
return this._waitForAction()
|
|
1388
1412
|
}
|
|
1389
1413
|
|
|
1390
1414
|
/**
|
|
@@ -1396,12 +1420,12 @@ class Playwright extends Helper {
|
|
|
1396
1420
|
*/
|
|
1397
1421
|
async closeCurrentTab() {
|
|
1398
1422
|
if (this.isElectron) {
|
|
1399
|
-
throw new Error('Cannot close current tab inside an Electron container')
|
|
1423
|
+
throw new Error('Cannot close current tab inside an Electron container')
|
|
1400
1424
|
}
|
|
1401
|
-
const oldPage = this.page
|
|
1402
|
-
await this.switchToPreviousTab()
|
|
1403
|
-
await oldPage.close()
|
|
1404
|
-
return this._waitForAction()
|
|
1425
|
+
const oldPage = this.page
|
|
1426
|
+
await this.switchToPreviousTab()
|
|
1427
|
+
await oldPage.close()
|
|
1428
|
+
return this._waitForAction()
|
|
1405
1429
|
}
|
|
1406
1430
|
|
|
1407
1431
|
/**
|
|
@@ -1412,13 +1436,13 @@ class Playwright extends Helper {
|
|
|
1412
1436
|
* ```
|
|
1413
1437
|
*/
|
|
1414
1438
|
async closeOtherTabs() {
|
|
1415
|
-
const pages = await this.browserContext.pages()
|
|
1416
|
-
const otherPages = pages.filter(page => page !== this.page)
|
|
1439
|
+
const pages = await this.browserContext.pages()
|
|
1440
|
+
const otherPages = pages.filter((page) => page !== this.page)
|
|
1417
1441
|
if (otherPages.length) {
|
|
1418
|
-
this.debug(`Closing ${otherPages.length} tabs`)
|
|
1419
|
-
return Promise.all(otherPages.map(p => p.close()))
|
|
1442
|
+
this.debug(`Closing ${otherPages.length} tabs`)
|
|
1443
|
+
return Promise.all(otherPages.map((p) => p.close()))
|
|
1420
1444
|
}
|
|
1421
|
-
return Promise.resolve()
|
|
1445
|
+
return Promise.resolve()
|
|
1422
1446
|
}
|
|
1423
1447
|
|
|
1424
1448
|
/**
|
|
@@ -1437,20 +1461,20 @@ class Playwright extends Helper {
|
|
|
1437
1461
|
*/
|
|
1438
1462
|
async openNewTab(options) {
|
|
1439
1463
|
if (this.isElectron) {
|
|
1440
|
-
throw new Error('Cannot open new tabs inside an Electron container')
|
|
1464
|
+
throw new Error('Cannot open new tabs inside an Electron container')
|
|
1441
1465
|
}
|
|
1442
|
-
const page = await this.browserContext.newPage(options)
|
|
1443
|
-
await targetCreatedHandler.call(this, page)
|
|
1444
|
-
await this._setPage(page)
|
|
1445
|
-
return this._waitForAction()
|
|
1466
|
+
const page = await this.browserContext.newPage(options)
|
|
1467
|
+
await targetCreatedHandler.call(this, page)
|
|
1468
|
+
await this._setPage(page)
|
|
1469
|
+
return this._waitForAction()
|
|
1446
1470
|
}
|
|
1447
1471
|
|
|
1448
1472
|
/**
|
|
1449
1473
|
* {{> grabNumberOfOpenTabs }}
|
|
1450
1474
|
*/
|
|
1451
1475
|
async grabNumberOfOpenTabs() {
|
|
1452
|
-
const pages = await this.browserContext.pages()
|
|
1453
|
-
return pages.length
|
|
1476
|
+
const pages = await this.browserContext.pages()
|
|
1477
|
+
return pages.length
|
|
1454
1478
|
}
|
|
1455
1479
|
|
|
1456
1480
|
/**
|
|
@@ -1458,12 +1482,12 @@ class Playwright extends Helper {
|
|
|
1458
1482
|
*
|
|
1459
1483
|
*/
|
|
1460
1484
|
async seeElement(locator) {
|
|
1461
|
-
let els = await this._locate(locator)
|
|
1462
|
-
els = await Promise.all(els.map(el => el.isVisible()))
|
|
1485
|
+
let els = await this._locate(locator)
|
|
1486
|
+
els = await Promise.all(els.map((el) => el.isVisible()))
|
|
1463
1487
|
try {
|
|
1464
|
-
return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'))
|
|
1488
|
+
return empty('visible elements').negate(els.filter((v) => v).fill('ELEMENT'))
|
|
1465
1489
|
} catch (e) {
|
|
1466
|
-
dontSeeElementError(locator)
|
|
1490
|
+
dontSeeElementError(locator)
|
|
1467
1491
|
}
|
|
1468
1492
|
}
|
|
1469
1493
|
|
|
@@ -1472,12 +1496,12 @@ class Playwright extends Helper {
|
|
|
1472
1496
|
*
|
|
1473
1497
|
*/
|
|
1474
1498
|
async dontSeeElement(locator) {
|
|
1475
|
-
let els = await this._locate(locator)
|
|
1476
|
-
els = await Promise.all(els.map(el => el.isVisible()))
|
|
1499
|
+
let els = await this._locate(locator)
|
|
1500
|
+
els = await Promise.all(els.map((el) => el.isVisible()))
|
|
1477
1501
|
try {
|
|
1478
|
-
return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'))
|
|
1502
|
+
return empty('visible elements').assert(els.filter((v) => v).fill('ELEMENT'))
|
|
1479
1503
|
} catch (e) {
|
|
1480
|
-
seeElementError(locator)
|
|
1504
|
+
seeElementError(locator)
|
|
1481
1505
|
}
|
|
1482
1506
|
}
|
|
1483
1507
|
|
|
@@ -1485,11 +1509,11 @@ class Playwright extends Helper {
|
|
|
1485
1509
|
* {{> seeElementInDOM }}
|
|
1486
1510
|
*/
|
|
1487
1511
|
async seeElementInDOM(locator) {
|
|
1488
|
-
const els = await this._locate(locator)
|
|
1512
|
+
const els = await this._locate(locator)
|
|
1489
1513
|
try {
|
|
1490
|
-
return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'))
|
|
1514
|
+
return empty('elements on page').negate(els.filter((v) => v).fill('ELEMENT'))
|
|
1491
1515
|
} catch (e) {
|
|
1492
|
-
dontSeeElementInDOMError(locator)
|
|
1516
|
+
dontSeeElementInDOMError(locator)
|
|
1493
1517
|
}
|
|
1494
1518
|
}
|
|
1495
1519
|
|
|
@@ -1497,11 +1521,11 @@ class Playwright extends Helper {
|
|
|
1497
1521
|
* {{> dontSeeElementInDOM }}
|
|
1498
1522
|
*/
|
|
1499
1523
|
async dontSeeElementInDOM(locator) {
|
|
1500
|
-
const els = await this._locate(locator)
|
|
1524
|
+
const els = await this._locate(locator)
|
|
1501
1525
|
try {
|
|
1502
|
-
return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'))
|
|
1526
|
+
return empty('elements on a page').assert(els.filter((v) => v).fill('ELEMENT'))
|
|
1503
1527
|
} catch (e) {
|
|
1504
|
-
seeElementInDOMError(locator)
|
|
1528
|
+
seeElementInDOMError(locator)
|
|
1505
1529
|
}
|
|
1506
1530
|
}
|
|
1507
1531
|
|
|
@@ -1524,18 +1548,18 @@ class Playwright extends Helper {
|
|
|
1524
1548
|
*/
|
|
1525
1549
|
async handleDownloads(fileName) {
|
|
1526
1550
|
this.page.waitForEvent('download').then(async (download) => {
|
|
1527
|
-
const filePath = await download.path()
|
|
1528
|
-
fileName = fileName || `downloads/${path.basename(filePath)}
|
|
1551
|
+
const filePath = await download.path()
|
|
1552
|
+
fileName = fileName || `downloads/${path.basename(filePath)}`
|
|
1529
1553
|
|
|
1530
|
-
const downloadPath = path.join(global.output_dir, fileName)
|
|
1554
|
+
const downloadPath = path.join(global.output_dir, fileName)
|
|
1531
1555
|
if (!fs.existsSync(path.dirname(downloadPath))) {
|
|
1532
|
-
fs.mkdirSync(path.dirname(downloadPath), '0777')
|
|
1556
|
+
fs.mkdirSync(path.dirname(downloadPath), '0777')
|
|
1533
1557
|
}
|
|
1534
|
-
fs.copyFileSync(filePath, downloadPath)
|
|
1535
|
-
this.debug('Download completed')
|
|
1536
|
-
this.debugSection('Downloaded From', await download.url())
|
|
1537
|
-
this.debugSection('Downloaded To', downloadPath)
|
|
1538
|
-
})
|
|
1558
|
+
fs.copyFileSync(filePath, downloadPath)
|
|
1559
|
+
this.debug('Download completed')
|
|
1560
|
+
this.debugSection('Downloaded From', await download.url())
|
|
1561
|
+
this.debugSection('Downloaded To', downloadPath)
|
|
1562
|
+
})
|
|
1539
1563
|
}
|
|
1540
1564
|
|
|
1541
1565
|
/**
|
|
@@ -1555,37 +1579,37 @@ class Playwright extends Helper {
|
|
|
1555
1579
|
*
|
|
1556
1580
|
*/
|
|
1557
1581
|
async click(locator, context = null, options = {}) {
|
|
1558
|
-
return proceedClick.call(this, locator, context, options)
|
|
1582
|
+
return proceedClick.call(this, locator, context, options)
|
|
1559
1583
|
}
|
|
1560
1584
|
|
|
1561
1585
|
/**
|
|
1562
1586
|
* Clicks link and waits for navigation (deprecated)
|
|
1563
1587
|
*/
|
|
1564
1588
|
async clickLink(locator, context = null) {
|
|
1565
|
-
console.log('clickLink deprecated: Playwright automatically waits for navigation to happen.')
|
|
1566
|
-
console.log('Replace I.clickLink with I.click')
|
|
1567
|
-
return this.click(locator, context)
|
|
1589
|
+
console.log('clickLink deprecated: Playwright automatically waits for navigation to happen.')
|
|
1590
|
+
console.log('Replace I.clickLink with I.click')
|
|
1591
|
+
return this.click(locator, context)
|
|
1568
1592
|
}
|
|
1569
1593
|
|
|
1570
1594
|
/**
|
|
1571
1595
|
* {{> forceClick }}
|
|
1572
1596
|
*/
|
|
1573
1597
|
async forceClick(locator, context = null) {
|
|
1574
|
-
return proceedClick.call(this, locator, context, { force: true })
|
|
1598
|
+
return proceedClick.call(this, locator, context, { force: true })
|
|
1575
1599
|
}
|
|
1576
1600
|
|
|
1577
1601
|
/**
|
|
1578
1602
|
* {{> doubleClick }}
|
|
1579
1603
|
*/
|
|
1580
1604
|
async doubleClick(locator, context = null) {
|
|
1581
|
-
return proceedClick.call(this, locator, context, { clickCount: 2 })
|
|
1605
|
+
return proceedClick.call(this, locator, context, { clickCount: 2 })
|
|
1582
1606
|
}
|
|
1583
1607
|
|
|
1584
1608
|
/**
|
|
1585
1609
|
* {{> rightClick }}
|
|
1586
1610
|
*/
|
|
1587
1611
|
async rightClick(locator, context = null) {
|
|
1588
|
-
return proceedClick.call(this, locator, context, { button: 'right' })
|
|
1612
|
+
return proceedClick.call(this, locator, context, { button: 'right' })
|
|
1589
1613
|
}
|
|
1590
1614
|
|
|
1591
1615
|
/**
|
|
@@ -1604,9 +1628,9 @@ class Playwright extends Helper {
|
|
|
1604
1628
|
*
|
|
1605
1629
|
*/
|
|
1606
1630
|
async checkOption(field, context = null, options = { force: true }) {
|
|
1607
|
-
const elm = await this._locateCheckable(field, context)
|
|
1608
|
-
await elm.check(options)
|
|
1609
|
-
return this._waitForAction()
|
|
1631
|
+
const elm = await this._locateCheckable(field, context)
|
|
1632
|
+
await elm.check(options)
|
|
1633
|
+
return this._waitForAction()
|
|
1610
1634
|
}
|
|
1611
1635
|
|
|
1612
1636
|
/**
|
|
@@ -1624,41 +1648,41 @@ class Playwright extends Helper {
|
|
|
1624
1648
|
* {{> uncheckOption }}
|
|
1625
1649
|
*/
|
|
1626
1650
|
async uncheckOption(field, context = null, options = { force: true }) {
|
|
1627
|
-
const elm = await this._locateCheckable(field, context)
|
|
1628
|
-
await elm.uncheck(options)
|
|
1629
|
-
return this._waitForAction()
|
|
1651
|
+
const elm = await this._locateCheckable(field, context)
|
|
1652
|
+
await elm.uncheck(options)
|
|
1653
|
+
return this._waitForAction()
|
|
1630
1654
|
}
|
|
1631
1655
|
|
|
1632
1656
|
/**
|
|
1633
1657
|
* {{> seeCheckboxIsChecked }}
|
|
1634
1658
|
*/
|
|
1635
1659
|
async seeCheckboxIsChecked(field) {
|
|
1636
|
-
return proceedIsChecked.call(this, 'assert', field)
|
|
1660
|
+
return proceedIsChecked.call(this, 'assert', field)
|
|
1637
1661
|
}
|
|
1638
1662
|
|
|
1639
1663
|
/**
|
|
1640
1664
|
* {{> dontSeeCheckboxIsChecked }}
|
|
1641
1665
|
*/
|
|
1642
1666
|
async dontSeeCheckboxIsChecked(field) {
|
|
1643
|
-
return proceedIsChecked.call(this, 'negate', field)
|
|
1667
|
+
return proceedIsChecked.call(this, 'negate', field)
|
|
1644
1668
|
}
|
|
1645
1669
|
|
|
1646
1670
|
/**
|
|
1647
1671
|
* {{> pressKeyDown }}
|
|
1648
1672
|
*/
|
|
1649
1673
|
async pressKeyDown(key) {
|
|
1650
|
-
key = getNormalizedKey.call(this, key)
|
|
1651
|
-
await this.page.keyboard.down(key)
|
|
1652
|
-
return this._waitForAction()
|
|
1674
|
+
key = getNormalizedKey.call(this, key)
|
|
1675
|
+
await this.page.keyboard.down(key)
|
|
1676
|
+
return this._waitForAction()
|
|
1653
1677
|
}
|
|
1654
1678
|
|
|
1655
1679
|
/**
|
|
1656
1680
|
* {{> pressKeyUp }}
|
|
1657
1681
|
*/
|
|
1658
1682
|
async pressKeyUp(key) {
|
|
1659
|
-
key = getNormalizedKey.call(this, key)
|
|
1660
|
-
await this.page.keyboard.up(key)
|
|
1661
|
-
return this._waitForAction()
|
|
1683
|
+
key = getNormalizedKey.call(this, key)
|
|
1684
|
+
await this.page.keyboard.up(key)
|
|
1685
|
+
return this._waitForAction()
|
|
1662
1686
|
}
|
|
1663
1687
|
|
|
1664
1688
|
/**
|
|
@@ -1668,28 +1692,28 @@ class Playwright extends Helper {
|
|
|
1668
1692
|
* {{> pressKeyWithKeyNormalization }}
|
|
1669
1693
|
*/
|
|
1670
1694
|
async pressKey(key) {
|
|
1671
|
-
const modifiers = []
|
|
1695
|
+
const modifiers = []
|
|
1672
1696
|
if (Array.isArray(key)) {
|
|
1673
1697
|
for (let k of key) {
|
|
1674
|
-
k = getNormalizedKey.call(this, k)
|
|
1698
|
+
k = getNormalizedKey.call(this, k)
|
|
1675
1699
|
if (isModifierKey(k)) {
|
|
1676
|
-
modifiers.push(k)
|
|
1700
|
+
modifiers.push(k)
|
|
1677
1701
|
} else {
|
|
1678
|
-
key = k
|
|
1679
|
-
break
|
|
1702
|
+
key = k
|
|
1703
|
+
break
|
|
1680
1704
|
}
|
|
1681
1705
|
}
|
|
1682
1706
|
} else {
|
|
1683
|
-
key = getNormalizedKey.call(this, key)
|
|
1707
|
+
key = getNormalizedKey.call(this, key)
|
|
1684
1708
|
}
|
|
1685
1709
|
for (const modifier of modifiers) {
|
|
1686
|
-
await this.page.keyboard.down(modifier)
|
|
1710
|
+
await this.page.keyboard.down(modifier)
|
|
1687
1711
|
}
|
|
1688
|
-
await this.page.keyboard.press(key)
|
|
1712
|
+
await this.page.keyboard.press(key)
|
|
1689
1713
|
for (const modifier of modifiers) {
|
|
1690
|
-
await this.page.keyboard.up(modifier)
|
|
1714
|
+
await this.page.keyboard.up(modifier)
|
|
1691
1715
|
}
|
|
1692
|
-
return this._waitForAction()
|
|
1716
|
+
return this._waitForAction()
|
|
1693
1717
|
}
|
|
1694
1718
|
|
|
1695
1719
|
/**
|
|
@@ -1697,13 +1721,13 @@ class Playwright extends Helper {
|
|
|
1697
1721
|
*/
|
|
1698
1722
|
async type(keys, delay = null) {
|
|
1699
1723
|
if (!Array.isArray(keys)) {
|
|
1700
|
-
keys = keys.toString()
|
|
1701
|
-
keys = keys.split('')
|
|
1724
|
+
keys = keys.toString()
|
|
1725
|
+
keys = keys.split('')
|
|
1702
1726
|
}
|
|
1703
1727
|
|
|
1704
1728
|
for (const key of keys) {
|
|
1705
|
-
await this.page.keyboard.press(key)
|
|
1706
|
-
if (delay) await this.wait(delay / 1000)
|
|
1729
|
+
await this.page.keyboard.press(key)
|
|
1730
|
+
if (delay) await this.wait(delay / 1000)
|
|
1707
1731
|
}
|
|
1708
1732
|
}
|
|
1709
1733
|
|
|
@@ -1712,75 +1736,75 @@ class Playwright extends Helper {
|
|
|
1712
1736
|
*
|
|
1713
1737
|
*/
|
|
1714
1738
|
async fillField(field, value) {
|
|
1715
|
-
const els = await findFields.call(this, field)
|
|
1716
|
-
assertElementExists(els, field, 'Field')
|
|
1717
|
-
const el = els[0]
|
|
1739
|
+
const els = await findFields.call(this, field)
|
|
1740
|
+
assertElementExists(els, field, 'Field')
|
|
1741
|
+
const el = els[0]
|
|
1718
1742
|
|
|
1719
|
-
await el.clear()
|
|
1743
|
+
await el.clear()
|
|
1720
1744
|
|
|
1721
|
-
await highlightActiveElement.call(this, el)
|
|
1745
|
+
await highlightActiveElement.call(this, el)
|
|
1722
1746
|
|
|
1723
|
-
await el.type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1747
|
+
await el.type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1724
1748
|
|
|
1725
|
-
return this._waitForAction()
|
|
1749
|
+
return this._waitForAction()
|
|
1726
1750
|
}
|
|
1727
1751
|
|
|
1728
1752
|
/**
|
|
1729
1753
|
* Clears the text input element: `<input>`, `<textarea>` or `[contenteditable]` .
|
|
1730
1754
|
*
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1755
|
+
*
|
|
1756
|
+
* Examples:
|
|
1757
|
+
*
|
|
1758
|
+
* ```js
|
|
1759
|
+
* I.clearField('.text-area')
|
|
1760
|
+
*
|
|
1761
|
+
* // if this doesn't work use force option
|
|
1762
|
+
* I.clearField('#submit', { force: true })
|
|
1763
|
+
* ```
|
|
1764
|
+
* Use `force` to bypass the [actionability](https://playwright.dev/docs/actionability) checks.
|
|
1765
|
+
*
|
|
1742
1766
|
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
1743
1767
|
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-locator#locator-clear) for available options object as 2nd argument.
|
|
1744
1768
|
*/
|
|
1745
1769
|
async clearField(locator, options = {}) {
|
|
1746
|
-
const els = await findFields.call(this, locator)
|
|
1747
|
-
assertElementExists(els, locator, 'Field to clear')
|
|
1770
|
+
const els = await findFields.call(this, locator)
|
|
1771
|
+
assertElementExists(els, locator, 'Field to clear')
|
|
1748
1772
|
|
|
1749
|
-
const el = els[0]
|
|
1773
|
+
const el = els[0]
|
|
1750
1774
|
|
|
1751
|
-
await highlightActiveElement.call(this, el)
|
|
1775
|
+
await highlightActiveElement.call(this, el)
|
|
1752
1776
|
|
|
1753
|
-
await el.clear()
|
|
1777
|
+
await el.clear()
|
|
1754
1778
|
|
|
1755
|
-
return this._waitForAction()
|
|
1779
|
+
return this._waitForAction()
|
|
1756
1780
|
}
|
|
1757
1781
|
|
|
1758
1782
|
/**
|
|
1759
1783
|
* {{> appendField }}
|
|
1760
1784
|
*/
|
|
1761
1785
|
async appendField(field, value) {
|
|
1762
|
-
const els = await findFields.call(this, field)
|
|
1763
|
-
assertElementExists(els, field, 'Field')
|
|
1764
|
-
await highlightActiveElement.call(this, els[0])
|
|
1765
|
-
await els[0].press('End')
|
|
1766
|
-
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1767
|
-
return this._waitForAction()
|
|
1786
|
+
const els = await findFields.call(this, field)
|
|
1787
|
+
assertElementExists(els, field, 'Field')
|
|
1788
|
+
await highlightActiveElement.call(this, els[0])
|
|
1789
|
+
await els[0].press('End')
|
|
1790
|
+
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1791
|
+
return this._waitForAction()
|
|
1768
1792
|
}
|
|
1769
1793
|
|
|
1770
1794
|
/**
|
|
1771
1795
|
* {{> seeInField }}
|
|
1772
1796
|
*/
|
|
1773
1797
|
async seeInField(field, value) {
|
|
1774
|
-
const _value =
|
|
1775
|
-
return proceedSeeInField.call(this, 'assert', field, _value)
|
|
1798
|
+
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1799
|
+
return proceedSeeInField.call(this, 'assert', field, _value)
|
|
1776
1800
|
}
|
|
1777
1801
|
|
|
1778
1802
|
/**
|
|
1779
1803
|
* {{> dontSeeInField }}
|
|
1780
1804
|
*/
|
|
1781
1805
|
async dontSeeInField(field, value) {
|
|
1782
|
-
const _value =
|
|
1783
|
-
return proceedSeeInField.call(this, 'negate', field, _value)
|
|
1806
|
+
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1807
|
+
return proceedSeeInField.call(this, 'negate', field, _value)
|
|
1784
1808
|
}
|
|
1785
1809
|
|
|
1786
1810
|
/**
|
|
@@ -1788,38 +1812,38 @@ class Playwright extends Helper {
|
|
|
1788
1812
|
*
|
|
1789
1813
|
*/
|
|
1790
1814
|
async attachFile(locator, pathToFile) {
|
|
1791
|
-
const file = path.join(global.codecept_dir, pathToFile)
|
|
1815
|
+
const file = path.join(global.codecept_dir, pathToFile)
|
|
1792
1816
|
|
|
1793
1817
|
if (!fileExists(file)) {
|
|
1794
|
-
throw new Error(`File at ${file} can not be found on local system`)
|
|
1818
|
+
throw new Error(`File at ${file} can not be found on local system`)
|
|
1795
1819
|
}
|
|
1796
|
-
const els = await findFields.call(this, locator)
|
|
1797
|
-
assertElementExists(els, locator, 'Field')
|
|
1798
|
-
await els[0].setInputFiles(file)
|
|
1799
|
-
return this._waitForAction()
|
|
1820
|
+
const els = await findFields.call(this, locator)
|
|
1821
|
+
assertElementExists(els, locator, 'Field')
|
|
1822
|
+
await els[0].setInputFiles(file)
|
|
1823
|
+
return this._waitForAction()
|
|
1800
1824
|
}
|
|
1801
1825
|
|
|
1802
1826
|
/**
|
|
1803
1827
|
* {{> selectOption }}
|
|
1804
1828
|
*/
|
|
1805
1829
|
async selectOption(select, option) {
|
|
1806
|
-
const els = await findFields.call(this, select)
|
|
1807
|
-
assertElementExists(els, select, 'Selectable field')
|
|
1808
|
-
const el = els[0]
|
|
1830
|
+
const els = await findFields.call(this, select)
|
|
1831
|
+
assertElementExists(els, select, 'Selectable field')
|
|
1832
|
+
const el = els[0]
|
|
1809
1833
|
|
|
1810
|
-
await highlightActiveElement.call(this, el)
|
|
1811
|
-
let optionToSelect = ''
|
|
1834
|
+
await highlightActiveElement.call(this, el)
|
|
1835
|
+
let optionToSelect = ''
|
|
1812
1836
|
|
|
1813
1837
|
try {
|
|
1814
|
-
optionToSelect = (await el.locator('option', { hasText: option }).textContent()).trim()
|
|
1838
|
+
optionToSelect = (await el.locator('option', { hasText: option }).textContent()).trim()
|
|
1815
1839
|
} catch (e) {
|
|
1816
|
-
optionToSelect = option
|
|
1840
|
+
optionToSelect = option
|
|
1817
1841
|
}
|
|
1818
1842
|
|
|
1819
|
-
if (!Array.isArray(option)) option = [optionToSelect]
|
|
1843
|
+
if (!Array.isArray(option)) option = [optionToSelect]
|
|
1820
1844
|
|
|
1821
|
-
await el.selectOption(option)
|
|
1822
|
-
return this._waitForAction()
|
|
1845
|
+
await el.selectOption(option)
|
|
1846
|
+
return this._waitForAction()
|
|
1823
1847
|
}
|
|
1824
1848
|
|
|
1825
1849
|
/**
|
|
@@ -1827,37 +1851,37 @@ class Playwright extends Helper {
|
|
|
1827
1851
|
*
|
|
1828
1852
|
*/
|
|
1829
1853
|
async grabNumberOfVisibleElements(locator) {
|
|
1830
|
-
let els = await this._locate(locator)
|
|
1831
|
-
els = await Promise.all(els.map(el => el.isVisible()))
|
|
1832
|
-
return els.filter(v => v).length
|
|
1854
|
+
let els = await this._locate(locator)
|
|
1855
|
+
els = await Promise.all(els.map((el) => el.isVisible()))
|
|
1856
|
+
return els.filter((v) => v).length
|
|
1833
1857
|
}
|
|
1834
1858
|
|
|
1835
1859
|
/**
|
|
1836
1860
|
* {{> seeInCurrentUrl }}
|
|
1837
1861
|
*/
|
|
1838
1862
|
async seeInCurrentUrl(url) {
|
|
1839
|
-
stringIncludes('url').assert(url, await this._getPageUrl())
|
|
1863
|
+
stringIncludes('url').assert(url, await this._getPageUrl())
|
|
1840
1864
|
}
|
|
1841
1865
|
|
|
1842
1866
|
/**
|
|
1843
1867
|
* {{> dontSeeInCurrentUrl }}
|
|
1844
1868
|
*/
|
|
1845
1869
|
async dontSeeInCurrentUrl(url) {
|
|
1846
|
-
stringIncludes('url').negate(url, await this._getPageUrl())
|
|
1870
|
+
stringIncludes('url').negate(url, await this._getPageUrl())
|
|
1847
1871
|
}
|
|
1848
1872
|
|
|
1849
1873
|
/**
|
|
1850
1874
|
* {{> seeCurrentUrlEquals }}
|
|
1851
1875
|
*/
|
|
1852
1876
|
async seeCurrentUrlEquals(url) {
|
|
1853
|
-
urlEquals(this.options.url).assert(url, await this._getPageUrl())
|
|
1877
|
+
urlEquals(this.options.url).assert(url, await this._getPageUrl())
|
|
1854
1878
|
}
|
|
1855
1879
|
|
|
1856
1880
|
/**
|
|
1857
1881
|
* {{> dontSeeCurrentUrlEquals }}
|
|
1858
1882
|
*/
|
|
1859
1883
|
async dontSeeCurrentUrlEquals(url) {
|
|
1860
|
-
urlEquals(this.options.url).negate(url, await this._getPageUrl())
|
|
1884
|
+
urlEquals(this.options.url).negate(url, await this._getPageUrl())
|
|
1861
1885
|
}
|
|
1862
1886
|
|
|
1863
1887
|
/**
|
|
@@ -1866,14 +1890,14 @@ class Playwright extends Helper {
|
|
|
1866
1890
|
*
|
|
1867
1891
|
*/
|
|
1868
1892
|
async see(text, context = null) {
|
|
1869
|
-
return proceedSee.call(this, 'assert', text, context)
|
|
1893
|
+
return proceedSee.call(this, 'assert', text, context)
|
|
1870
1894
|
}
|
|
1871
1895
|
|
|
1872
1896
|
/**
|
|
1873
1897
|
* {{> seeTextEquals }}
|
|
1874
1898
|
*/
|
|
1875
1899
|
async seeTextEquals(text, context = null) {
|
|
1876
|
-
return proceedSee.call(this, 'assert', text, context, true)
|
|
1900
|
+
return proceedSee.call(this, 'assert', text, context, true)
|
|
1877
1901
|
}
|
|
1878
1902
|
|
|
1879
1903
|
/**
|
|
@@ -1882,14 +1906,14 @@ class Playwright extends Helper {
|
|
|
1882
1906
|
*
|
|
1883
1907
|
*/
|
|
1884
1908
|
async dontSee(text, context = null) {
|
|
1885
|
-
return proceedSee.call(this, 'negate', text, context)
|
|
1909
|
+
return proceedSee.call(this, 'negate', text, context)
|
|
1886
1910
|
}
|
|
1887
1911
|
|
|
1888
1912
|
/**
|
|
1889
1913
|
* {{> grabSource }}
|
|
1890
1914
|
*/
|
|
1891
1915
|
async grabSource() {
|
|
1892
|
-
return this.page.content()
|
|
1916
|
+
return this.page.content()
|
|
1893
1917
|
}
|
|
1894
1918
|
|
|
1895
1919
|
/**
|
|
@@ -1904,32 +1928,32 @@ class Playwright extends Helper {
|
|
|
1904
1928
|
* @return {Promise<any[]>}
|
|
1905
1929
|
*/
|
|
1906
1930
|
async grabBrowserLogs() {
|
|
1907
|
-
const logs = consoleLogStore.entries
|
|
1908
|
-
consoleLogStore.clear()
|
|
1909
|
-
return logs
|
|
1931
|
+
const logs = consoleLogStore.entries
|
|
1932
|
+
consoleLogStore.clear()
|
|
1933
|
+
return logs
|
|
1910
1934
|
}
|
|
1911
1935
|
|
|
1912
1936
|
/**
|
|
1913
1937
|
* {{> grabCurrentUrl }}
|
|
1914
1938
|
*/
|
|
1915
1939
|
async grabCurrentUrl() {
|
|
1916
|
-
return this._getPageUrl()
|
|
1940
|
+
return this._getPageUrl()
|
|
1917
1941
|
}
|
|
1918
1942
|
|
|
1919
1943
|
/**
|
|
1920
1944
|
* {{> seeInSource }}
|
|
1921
1945
|
*/
|
|
1922
1946
|
async seeInSource(text) {
|
|
1923
|
-
const source = await this.page.content()
|
|
1924
|
-
stringIncludes('HTML source of a page').assert(text, source)
|
|
1947
|
+
const source = await this.page.content()
|
|
1948
|
+
stringIncludes('HTML source of a page').assert(text, source)
|
|
1925
1949
|
}
|
|
1926
1950
|
|
|
1927
1951
|
/**
|
|
1928
1952
|
* {{> dontSeeInSource }}
|
|
1929
1953
|
*/
|
|
1930
1954
|
async dontSeeInSource(text) {
|
|
1931
|
-
const source = await this.page.content()
|
|
1932
|
-
stringIncludes('HTML source of a page').negate(text, source)
|
|
1955
|
+
const source = await this.page.content()
|
|
1956
|
+
stringIncludes('HTML source of a page').negate(text, source)
|
|
1933
1957
|
}
|
|
1934
1958
|
|
|
1935
1959
|
/**
|
|
@@ -1938,8 +1962,10 @@ class Playwright extends Helper {
|
|
|
1938
1962
|
*
|
|
1939
1963
|
*/
|
|
1940
1964
|
async seeNumberOfElements(locator, num) {
|
|
1941
|
-
const elements = await this._locate(locator)
|
|
1942
|
-
return equals(
|
|
1965
|
+
const elements = await this._locate(locator)
|
|
1966
|
+
return equals(
|
|
1967
|
+
`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`,
|
|
1968
|
+
).assert(elements.length, num)
|
|
1943
1969
|
}
|
|
1944
1970
|
|
|
1945
1971
|
/**
|
|
@@ -1948,8 +1974,11 @@ class Playwright extends Helper {
|
|
|
1948
1974
|
*
|
|
1949
1975
|
*/
|
|
1950
1976
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1951
|
-
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1952
|
-
return equals(`expected number of visible elements (${
|
|
1977
|
+
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1978
|
+
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(
|
|
1979
|
+
res,
|
|
1980
|
+
num,
|
|
1981
|
+
)
|
|
1953
1982
|
}
|
|
1954
1983
|
|
|
1955
1984
|
/**
|
|
@@ -1957,9 +1986,9 @@ class Playwright extends Helper {
|
|
|
1957
1986
|
*/
|
|
1958
1987
|
async setCookie(cookie) {
|
|
1959
1988
|
if (Array.isArray(cookie)) {
|
|
1960
|
-
return this.browserContext.addCookies(cookie)
|
|
1989
|
+
return this.browserContext.addCookies(cookie)
|
|
1961
1990
|
}
|
|
1962
|
-
return this.browserContext.addCookies([cookie])
|
|
1991
|
+
return this.browserContext.addCookies([cookie])
|
|
1963
1992
|
}
|
|
1964
1993
|
|
|
1965
1994
|
/**
|
|
@@ -1967,16 +1996,16 @@ class Playwright extends Helper {
|
|
|
1967
1996
|
*
|
|
1968
1997
|
*/
|
|
1969
1998
|
async seeCookie(name) {
|
|
1970
|
-
const cookies = await this.browserContext.cookies()
|
|
1971
|
-
empty(`cookie ${name} to be set`).negate(cookies.filter(c => c.name === name))
|
|
1999
|
+
const cookies = await this.browserContext.cookies()
|
|
2000
|
+
empty(`cookie ${name} to be set`).negate(cookies.filter((c) => c.name === name))
|
|
1972
2001
|
}
|
|
1973
2002
|
|
|
1974
2003
|
/**
|
|
1975
2004
|
* {{> dontSeeCookie }}
|
|
1976
2005
|
*/
|
|
1977
2006
|
async dontSeeCookie(name) {
|
|
1978
|
-
const cookies = await this.browserContext.cookies()
|
|
1979
|
-
empty(`cookie ${name} to be set`).assert(cookies.filter(c => c.name === name))
|
|
2007
|
+
const cookies = await this.browserContext.cookies()
|
|
2008
|
+
empty(`cookie ${name} not to be set`).assert(cookies.filter((c) => c.name === name))
|
|
1980
2009
|
}
|
|
1981
2010
|
|
|
1982
2011
|
/**
|
|
@@ -1985,10 +2014,10 @@ class Playwright extends Helper {
|
|
|
1985
2014
|
* {{> grabCookie }}
|
|
1986
2015
|
*/
|
|
1987
2016
|
async grabCookie(name) {
|
|
1988
|
-
const cookies = await this.browserContext.cookies()
|
|
1989
|
-
if (!name) return cookies
|
|
1990
|
-
const cookie = cookies.filter(c => c.name === name)
|
|
1991
|
-
if (cookie[0]) return cookie[0]
|
|
2017
|
+
const cookies = await this.browserContext.cookies()
|
|
2018
|
+
if (!name) return cookies
|
|
2019
|
+
const cookie = cookies.filter((c) => c.name === name)
|
|
2020
|
+
if (cookie[0]) return cookie[0]
|
|
1992
2021
|
}
|
|
1993
2022
|
|
|
1994
2023
|
/**
|
|
@@ -1997,8 +2026,8 @@ class Playwright extends Helper {
|
|
|
1997
2026
|
async clearCookie() {
|
|
1998
2027
|
// Playwright currently doesn't support to delete a certain cookie
|
|
1999
2028
|
// https://github.com/microsoft/playwright/blob/main/docs/src/api/class-browsercontext.md#async-method-browsercontextclearcookies
|
|
2000
|
-
if (!this.browserContext) return
|
|
2001
|
-
return this.browserContext.clearCookies()
|
|
2029
|
+
if (!this.browserContext) return
|
|
2030
|
+
return this.browserContext.clearCookies()
|
|
2002
2031
|
}
|
|
2003
2032
|
|
|
2004
2033
|
/**
|
|
@@ -2028,9 +2057,9 @@ class Playwright extends Helper {
|
|
|
2028
2057
|
async executeScript(fn, arg) {
|
|
2029
2058
|
if (this.context && this.context.constructor.name === 'FrameLocator') {
|
|
2030
2059
|
// switching to iframe context
|
|
2031
|
-
return this.context.locator(':root').evaluate(fn, arg)
|
|
2060
|
+
return this.context.locator(':root').evaluate(fn, arg)
|
|
2032
2061
|
}
|
|
2033
|
-
return this.page.evaluate.apply(this.page, [fn, arg])
|
|
2062
|
+
return this.page.evaluate.apply(this.page, [fn, arg])
|
|
2034
2063
|
}
|
|
2035
2064
|
|
|
2036
2065
|
/**
|
|
@@ -2039,14 +2068,14 @@ class Playwright extends Helper {
|
|
|
2039
2068
|
* @param {*} locator
|
|
2040
2069
|
*/
|
|
2041
2070
|
_contextLocator(locator) {
|
|
2042
|
-
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2071
|
+
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2043
2072
|
|
|
2044
2073
|
if (this.contextLocator) {
|
|
2045
|
-
const contextLocator = buildLocatorString(new Locator(this.contextLocator, 'css'))
|
|
2046
|
-
locator = `${contextLocator} >> ${locator}
|
|
2074
|
+
const contextLocator = buildLocatorString(new Locator(this.contextLocator, 'css'))
|
|
2075
|
+
locator = `${contextLocator} >> ${locator}`
|
|
2047
2076
|
}
|
|
2048
2077
|
|
|
2049
|
-
return locator
|
|
2078
|
+
return locator
|
|
2050
2079
|
}
|
|
2051
2080
|
|
|
2052
2081
|
/**
|
|
@@ -2054,11 +2083,11 @@ class Playwright extends Helper {
|
|
|
2054
2083
|
*
|
|
2055
2084
|
*/
|
|
2056
2085
|
async grabTextFrom(locator) {
|
|
2057
|
-
locator = this._contextLocator(locator)
|
|
2058
|
-
const text = await this.page.textContent(locator)
|
|
2059
|
-
assertElementExists(text, locator)
|
|
2060
|
-
this.debugSection('Text', text)
|
|
2061
|
-
return text
|
|
2086
|
+
locator = this._contextLocator(locator)
|
|
2087
|
+
const text = await this.page.textContent(locator)
|
|
2088
|
+
assertElementExists(text, locator)
|
|
2089
|
+
this.debugSection('Text', text)
|
|
2090
|
+
return text
|
|
2062
2091
|
}
|
|
2063
2092
|
|
|
2064
2093
|
/**
|
|
@@ -2066,51 +2095,51 @@ class Playwright extends Helper {
|
|
|
2066
2095
|
*
|
|
2067
2096
|
*/
|
|
2068
2097
|
async grabTextFromAll(locator) {
|
|
2069
|
-
const els = await this._locate(locator)
|
|
2070
|
-
const texts = []
|
|
2098
|
+
const els = await this._locate(locator)
|
|
2099
|
+
const texts = []
|
|
2071
2100
|
for (const el of els) {
|
|
2072
|
-
texts.push(await
|
|
2101
|
+
texts.push(await await el.innerText())
|
|
2073
2102
|
}
|
|
2074
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2075
|
-
return texts
|
|
2103
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2104
|
+
return texts
|
|
2076
2105
|
}
|
|
2077
2106
|
|
|
2078
2107
|
/**
|
|
2079
2108
|
* {{> grabValueFrom }}
|
|
2080
2109
|
*/
|
|
2081
2110
|
async grabValueFrom(locator) {
|
|
2082
|
-
const values = await this.grabValueFromAll(locator)
|
|
2083
|
-
assertElementExists(values, locator)
|
|
2084
|
-
this.debugSection('Value', values[0])
|
|
2085
|
-
return values[0]
|
|
2111
|
+
const values = await this.grabValueFromAll(locator)
|
|
2112
|
+
assertElementExists(values, locator)
|
|
2113
|
+
this.debugSection('Value', values[0])
|
|
2114
|
+
return values[0]
|
|
2086
2115
|
}
|
|
2087
2116
|
|
|
2088
2117
|
/**
|
|
2089
2118
|
* {{> grabValueFromAll }}
|
|
2090
2119
|
*/
|
|
2091
2120
|
async grabValueFromAll(locator) {
|
|
2092
|
-
const els = await findFields.call(this, locator)
|
|
2093
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2094
|
-
return Promise.all(els.map(el => el.inputValue()))
|
|
2121
|
+
const els = await findFields.call(this, locator)
|
|
2122
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2123
|
+
return Promise.all(els.map((el) => el.inputValue()))
|
|
2095
2124
|
}
|
|
2096
2125
|
|
|
2097
2126
|
/**
|
|
2098
2127
|
* {{> grabHTMLFrom }}
|
|
2099
2128
|
*/
|
|
2100
2129
|
async grabHTMLFrom(locator) {
|
|
2101
|
-
const html = await this.grabHTMLFromAll(locator)
|
|
2102
|
-
assertElementExists(html, locator)
|
|
2103
|
-
this.debugSection('HTML', html[0])
|
|
2104
|
-
return html[0]
|
|
2130
|
+
const html = await this.grabHTMLFromAll(locator)
|
|
2131
|
+
assertElementExists(html, locator)
|
|
2132
|
+
this.debugSection('HTML', html[0])
|
|
2133
|
+
return html[0]
|
|
2105
2134
|
}
|
|
2106
2135
|
|
|
2107
2136
|
/**
|
|
2108
2137
|
* {{> grabHTMLFromAll }}
|
|
2109
2138
|
*/
|
|
2110
2139
|
async grabHTMLFromAll(locator) {
|
|
2111
|
-
const els = await this._locate(locator)
|
|
2112
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2113
|
-
return Promise.all(els.map(el => el.innerHTML()))
|
|
2140
|
+
const els = await this._locate(locator)
|
|
2141
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2142
|
+
return Promise.all(els.map((el) => el.innerHTML()))
|
|
2114
2143
|
}
|
|
2115
2144
|
|
|
2116
2145
|
/**
|
|
@@ -2118,10 +2147,10 @@ class Playwright extends Helper {
|
|
|
2118
2147
|
*
|
|
2119
2148
|
*/
|
|
2120
2149
|
async grabCssPropertyFrom(locator, cssProperty) {
|
|
2121
|
-
const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty)
|
|
2122
|
-
assertElementExists(cssValues, locator)
|
|
2123
|
-
this.debugSection('CSS', cssValues[0])
|
|
2124
|
-
return cssValues[0]
|
|
2150
|
+
const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty)
|
|
2151
|
+
assertElementExists(cssValues, locator)
|
|
2152
|
+
this.debugSection('CSS', cssValues[0])
|
|
2153
|
+
return cssValues[0]
|
|
2125
2154
|
}
|
|
2126
2155
|
|
|
2127
2156
|
/**
|
|
@@ -2129,11 +2158,15 @@ class Playwright extends Helper {
|
|
|
2129
2158
|
*
|
|
2130
2159
|
*/
|
|
2131
2160
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
2132
|
-
const els = await this._locate(locator)
|
|
2133
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2134
|
-
const cssValues = await Promise.all(
|
|
2161
|
+
const els = await this._locate(locator)
|
|
2162
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2163
|
+
const cssValues = await Promise.all(
|
|
2164
|
+
els.map((el) =>
|
|
2165
|
+
el.evaluate((el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty),
|
|
2166
|
+
),
|
|
2167
|
+
)
|
|
2135
2168
|
|
|
2136
|
-
return cssValues
|
|
2169
|
+
return cssValues
|
|
2137
2170
|
}
|
|
2138
2171
|
|
|
2139
2172
|
/**
|
|
@@ -2141,35 +2174,37 @@ class Playwright extends Helper {
|
|
|
2141
2174
|
*
|
|
2142
2175
|
*/
|
|
2143
2176
|
async seeCssPropertiesOnElements(locator, cssProperties) {
|
|
2144
|
-
const res = await this._locate(locator)
|
|
2145
|
-
assertElementExists(res, locator)
|
|
2177
|
+
const res = await this._locate(locator)
|
|
2178
|
+
assertElementExists(res, locator)
|
|
2146
2179
|
|
|
2147
|
-
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties)
|
|
2148
|
-
const elemAmount = res.length
|
|
2149
|
-
let props = []
|
|
2180
|
+
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties)
|
|
2181
|
+
const elemAmount = res.length
|
|
2182
|
+
let props = []
|
|
2150
2183
|
|
|
2151
2184
|
for (const element of res) {
|
|
2152
2185
|
for (const prop of Object.keys(cssProperties)) {
|
|
2153
|
-
const cssProp = await this.grabCssPropertyFrom(locator, prop)
|
|
2186
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop)
|
|
2154
2187
|
if (isColorProperty(prop)) {
|
|
2155
|
-
props.push(convertColorToRGBA(cssProp))
|
|
2188
|
+
props.push(convertColorToRGBA(cssProp))
|
|
2156
2189
|
} else {
|
|
2157
|
-
props.push(cssProp)
|
|
2190
|
+
props.push(cssProp)
|
|
2158
2191
|
}
|
|
2159
2192
|
}
|
|
2160
2193
|
}
|
|
2161
2194
|
|
|
2162
|
-
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key])
|
|
2163
|
-
if (!Array.isArray(props)) props = [props]
|
|
2164
|
-
let chunked = chunkArray(props, values.length)
|
|
2195
|
+
const values = Object.keys(cssPropertiesCamelCase).map((key) => cssPropertiesCamelCase[key])
|
|
2196
|
+
if (!Array.isArray(props)) props = [props]
|
|
2197
|
+
let chunked = chunkArray(props, values.length)
|
|
2165
2198
|
chunked = chunked.filter((val) => {
|
|
2166
2199
|
for (let i = 0; i < val.length; ++i) {
|
|
2167
2200
|
// eslint-disable-next-line eqeqeq
|
|
2168
|
-
if (val[i] != values[i]) return false
|
|
2201
|
+
if (val[i] != values[i]) return false
|
|
2169
2202
|
}
|
|
2170
|
-
return true
|
|
2171
|
-
})
|
|
2172
|
-
return equals(
|
|
2203
|
+
return true
|
|
2204
|
+
})
|
|
2205
|
+
return equals(
|
|
2206
|
+
`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
2207
|
+
).assert(chunked.length, elemAmount)
|
|
2173
2208
|
}
|
|
2174
2209
|
|
|
2175
2210
|
/**
|
|
@@ -2177,31 +2212,33 @@ class Playwright extends Helper {
|
|
|
2177
2212
|
*
|
|
2178
2213
|
*/
|
|
2179
2214
|
async seeAttributesOnElements(locator, attributes) {
|
|
2180
|
-
const res = await this._locate(locator)
|
|
2181
|
-
assertElementExists(res, locator)
|
|
2215
|
+
const res = await this._locate(locator)
|
|
2216
|
+
assertElementExists(res, locator)
|
|
2182
2217
|
|
|
2183
|
-
const elemAmount = res.length
|
|
2184
|
-
const commands = []
|
|
2218
|
+
const elemAmount = res.length
|
|
2219
|
+
const commands = []
|
|
2185
2220
|
res.forEach((el) => {
|
|
2186
2221
|
Object.keys(attributes).forEach((prop) => {
|
|
2187
|
-
commands.push(el
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
let chunked = chunkArray(attrs, values.length);
|
|
2222
|
+
commands.push(el.evaluate((el, attr) => el[attr] || el.getAttribute(attr), prop))
|
|
2223
|
+
})
|
|
2224
|
+
})
|
|
2225
|
+
let attrs = await Promise.all(commands)
|
|
2226
|
+
const values = Object.keys(attributes).map((key) => attributes[key])
|
|
2227
|
+
if (!Array.isArray(attrs)) attrs = [attrs]
|
|
2228
|
+
let chunked = chunkArray(attrs, values.length)
|
|
2195
2229
|
chunked = chunked.filter((val) => {
|
|
2196
2230
|
for (let i = 0; i < val.length; ++i) {
|
|
2197
2231
|
// the attribute could be a boolean
|
|
2198
|
-
if (typeof val[i] === 'boolean') return val[i] === values[i]
|
|
2232
|
+
if (typeof val[i] === 'boolean') return val[i] === values[i]
|
|
2199
2233
|
// if the attribute doesn't exist, returns false as well
|
|
2200
|
-
if (!val[i] || !val[i].includes(values[i])) return false
|
|
2234
|
+
if (!val[i] || !val[i].includes(values[i])) return false
|
|
2201
2235
|
}
|
|
2202
|
-
return true
|
|
2203
|
-
})
|
|
2204
|
-
return equals(`all elements (${
|
|
2236
|
+
return true
|
|
2237
|
+
})
|
|
2238
|
+
return equals(`all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`).assert(
|
|
2239
|
+
chunked.length,
|
|
2240
|
+
elemAmount,
|
|
2241
|
+
)
|
|
2205
2242
|
}
|
|
2206
2243
|
|
|
2207
2244
|
/**
|
|
@@ -2209,21 +2246,21 @@ class Playwright extends Helper {
|
|
|
2209
2246
|
*
|
|
2210
2247
|
*/
|
|
2211
2248
|
async dragSlider(locator, offsetX = 0) {
|
|
2212
|
-
const src = await this._locateElement(locator)
|
|
2213
|
-
assertElementExists(src, locator, 'Slider Element')
|
|
2249
|
+
const src = await this._locateElement(locator)
|
|
2250
|
+
assertElementExists(src, locator, 'Slider Element')
|
|
2214
2251
|
|
|
2215
2252
|
// Note: Using clickablePoint private api because the .BoundingBox does not take into account iframe offsets!
|
|
2216
|
-
const sliderSource = await clickablePoint(src)
|
|
2253
|
+
const sliderSource = await clickablePoint(src)
|
|
2217
2254
|
|
|
2218
2255
|
// Drag start point
|
|
2219
|
-
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 })
|
|
2220
|
-
await this.page.mouse.down()
|
|
2256
|
+
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 })
|
|
2257
|
+
await this.page.mouse.down()
|
|
2221
2258
|
|
|
2222
2259
|
// Drag destination
|
|
2223
|
-
await this.page.mouse.move(sliderSource.x + offsetX, sliderSource.y, { steps: 5 })
|
|
2224
|
-
await this.page.mouse.up()
|
|
2260
|
+
await this.page.mouse.move(sliderSource.x + offsetX, sliderSource.y, { steps: 5 })
|
|
2261
|
+
await this.page.mouse.up()
|
|
2225
2262
|
|
|
2226
|
-
return this._waitForAction()
|
|
2263
|
+
return this._waitForAction()
|
|
2227
2264
|
}
|
|
2228
2265
|
|
|
2229
2266
|
/**
|
|
@@ -2231,10 +2268,10 @@ class Playwright extends Helper {
|
|
|
2231
2268
|
*
|
|
2232
2269
|
*/
|
|
2233
2270
|
async grabAttributeFrom(locator, attr) {
|
|
2234
|
-
const attrs = await this.grabAttributeFromAll(locator, attr)
|
|
2235
|
-
assertElementExists(attrs, locator)
|
|
2236
|
-
this.debugSection('Attribute', attrs[0])
|
|
2237
|
-
return attrs[0]
|
|
2271
|
+
const attrs = await this.grabAttributeFromAll(locator, attr)
|
|
2272
|
+
assertElementExists(attrs, locator)
|
|
2273
|
+
this.debugSection('Attribute', attrs[0])
|
|
2274
|
+
return attrs[0]
|
|
2238
2275
|
}
|
|
2239
2276
|
|
|
2240
2277
|
/**
|
|
@@ -2242,15 +2279,15 @@ class Playwright extends Helper {
|
|
|
2242
2279
|
*
|
|
2243
2280
|
*/
|
|
2244
2281
|
async grabAttributeFromAll(locator, attr) {
|
|
2245
|
-
const els = await this._locate(locator)
|
|
2246
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2247
|
-
const array = []
|
|
2282
|
+
const els = await this._locate(locator)
|
|
2283
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2284
|
+
const array = []
|
|
2248
2285
|
|
|
2249
2286
|
for (let index = 0; index < els.length; index++) {
|
|
2250
|
-
array.push(await els[index].getAttribute(attr))
|
|
2287
|
+
array.push(await els[index].getAttribute(attr))
|
|
2251
2288
|
}
|
|
2252
2289
|
|
|
2253
|
-
return array
|
|
2290
|
+
return array
|
|
2254
2291
|
}
|
|
2255
2292
|
|
|
2256
2293
|
/**
|
|
@@ -2258,43 +2295,43 @@ class Playwright extends Helper {
|
|
|
2258
2295
|
*
|
|
2259
2296
|
*/
|
|
2260
2297
|
async saveElementScreenshot(locator, fileName) {
|
|
2261
|
-
const outputFile = screenshotOutputFolder(fileName)
|
|
2298
|
+
const outputFile = screenshotOutputFolder(fileName)
|
|
2262
2299
|
|
|
2263
|
-
const res = await this._locateElement(locator)
|
|
2264
|
-
assertElementExists(res, locator)
|
|
2265
|
-
const elem = res
|
|
2266
|
-
this.debug(`Screenshot of ${
|
|
2267
|
-
return elem.screenshot({ path: outputFile, type: 'png' })
|
|
2300
|
+
const res = await this._locateElement(locator)
|
|
2301
|
+
assertElementExists(res, locator)
|
|
2302
|
+
const elem = res
|
|
2303
|
+
this.debug(`Screenshot of ${new Locator(locator)} element has been saved to ${outputFile}`)
|
|
2304
|
+
return elem.screenshot({ path: outputFile, type: 'png' })
|
|
2268
2305
|
}
|
|
2269
2306
|
|
|
2270
2307
|
/**
|
|
2271
2308
|
* {{> saveScreenshot }}
|
|
2272
2309
|
*/
|
|
2273
2310
|
async saveScreenshot(fileName, fullPage) {
|
|
2274
|
-
const fullPageOption = fullPage || this.options.fullPageScreenshots
|
|
2275
|
-
let outputFile = screenshotOutputFolder(fileName)
|
|
2311
|
+
const fullPageOption = fullPage || this.options.fullPageScreenshots
|
|
2312
|
+
let outputFile = screenshotOutputFolder(fileName)
|
|
2276
2313
|
|
|
2277
|
-
this.debug(`Screenshot is saving to ${outputFile}`)
|
|
2314
|
+
this.debug(`Screenshot is saving to ${outputFile}`)
|
|
2278
2315
|
|
|
2279
2316
|
await this.page.screenshot({
|
|
2280
2317
|
path: outputFile,
|
|
2281
2318
|
fullPage: fullPageOption,
|
|
2282
2319
|
type: 'png',
|
|
2283
|
-
})
|
|
2320
|
+
})
|
|
2284
2321
|
|
|
2285
2322
|
if (this.activeSessionName) {
|
|
2286
2323
|
for (const sessionName in this.sessionPages) {
|
|
2287
|
-
const activeSessionPage = this.sessionPages[sessionName]
|
|
2288
|
-
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`)
|
|
2324
|
+
const activeSessionPage = this.sessionPages[sessionName]
|
|
2325
|
+
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`)
|
|
2289
2326
|
|
|
2290
|
-
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`)
|
|
2327
|
+
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`)
|
|
2291
2328
|
|
|
2292
2329
|
if (activeSessionPage) {
|
|
2293
2330
|
await activeSessionPage.screenshot({
|
|
2294
2331
|
path: outputFile,
|
|
2295
2332
|
fullPage: fullPageOption,
|
|
2296
2333
|
type: 'png',
|
|
2297
|
-
})
|
|
2334
|
+
})
|
|
2298
2335
|
}
|
|
2299
2336
|
}
|
|
2300
2337
|
}
|
|
@@ -2318,20 +2355,23 @@ class Playwright extends Helper {
|
|
|
2318
2355
|
* @returns {Promise<object>} response
|
|
2319
2356
|
*/
|
|
2320
2357
|
async makeApiRequest(method, url, options) {
|
|
2321
|
-
method = method.toLowerCase()
|
|
2322
|
-
const allowedMethods = ['get', 'post', 'patch', 'head', 'fetch', 'delete']
|
|
2358
|
+
method = method.toLowerCase()
|
|
2359
|
+
const allowedMethods = ['get', 'post', 'patch', 'head', 'fetch', 'delete']
|
|
2323
2360
|
if (!allowedMethods.includes(method)) {
|
|
2324
|
-
throw new Error(
|
|
2361
|
+
throw new Error(
|
|
2362
|
+
`Method ${method} is not allowed, use the one from a list ${allowedMethods} or switch to using REST helper`,
|
|
2363
|
+
)
|
|
2325
2364
|
}
|
|
2326
2365
|
|
|
2327
|
-
if (url.startsWith('/')) {
|
|
2328
|
-
|
|
2329
|
-
this.
|
|
2366
|
+
if (url.startsWith('/')) {
|
|
2367
|
+
// local url
|
|
2368
|
+
url = this.options.url + url
|
|
2369
|
+
this.debugSection('URL', url)
|
|
2330
2370
|
}
|
|
2331
2371
|
|
|
2332
|
-
const response = await this.page.request[method](url, options)
|
|
2333
|
-
this.debugSection('Status', response.status())
|
|
2334
|
-
this.debugSection('Response', await response.text())
|
|
2372
|
+
const response = await this.page.request[method](url, options)
|
|
2373
|
+
this.debugSection('Status', response.status())
|
|
2374
|
+
this.debugSection('Response', await response.text())
|
|
2335
2375
|
|
|
2336
2376
|
// hook to allow JSON response handle this
|
|
2337
2377
|
if (this.config.onResponse) {
|
|
@@ -2340,68 +2380,83 @@ class Playwright extends Helper {
|
|
|
2340
2380
|
status: response.status(),
|
|
2341
2381
|
statusText: response.statusText(),
|
|
2342
2382
|
headers: response.headers(),
|
|
2343
|
-
}
|
|
2344
|
-
this.config.onResponse(axiosResponse)
|
|
2383
|
+
}
|
|
2384
|
+
this.config.onResponse(axiosResponse)
|
|
2345
2385
|
}
|
|
2346
2386
|
|
|
2347
|
-
return response
|
|
2387
|
+
return response
|
|
2348
2388
|
}
|
|
2349
2389
|
|
|
2350
2390
|
async _failed(test) {
|
|
2351
|
-
await this._withinEnd()
|
|
2391
|
+
await this._withinEnd()
|
|
2352
2392
|
|
|
2353
2393
|
if (!test.artifacts) {
|
|
2354
|
-
test.artifacts = {}
|
|
2394
|
+
test.artifacts = {}
|
|
2355
2395
|
}
|
|
2356
2396
|
|
|
2357
2397
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2358
|
-
test.artifacts.video =
|
|
2398
|
+
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.failed`)
|
|
2359
2399
|
for (const sessionName in this.sessionPages) {
|
|
2360
|
-
test.artifacts[`video_${sessionName}`] =
|
|
2400
|
+
test.artifacts[`video_${sessionName}`] = saveVideoForPage(
|
|
2401
|
+
this.sessionPages[sessionName],
|
|
2402
|
+
`${test.title}_${sessionName}.failed`,
|
|
2403
|
+
)
|
|
2361
2404
|
}
|
|
2362
2405
|
}
|
|
2363
2406
|
|
|
2364
2407
|
if (this.options.trace) {
|
|
2365
|
-
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.failed`)
|
|
2408
|
+
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.failed`)
|
|
2366
2409
|
for (const sessionName in this.sessionPages) {
|
|
2367
|
-
if (!this.sessionPages[sessionName].context) continue
|
|
2368
|
-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2410
|
+
if (!this.sessionPages[sessionName].context) continue
|
|
2411
|
+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2412
|
+
this.sessionPages[sessionName].context,
|
|
2413
|
+
`${test.title}_${sessionName}.failed`,
|
|
2414
|
+
)
|
|
2369
2415
|
}
|
|
2370
2416
|
}
|
|
2371
2417
|
|
|
2372
2418
|
if (this.options.recordHar) {
|
|
2373
|
-
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2419
|
+
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2374
2420
|
}
|
|
2375
2421
|
}
|
|
2376
2422
|
|
|
2377
2423
|
async _passed(test) {
|
|
2378
2424
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2379
2425
|
if (this.options.keepVideoForPassedTests) {
|
|
2380
|
-
test.artifacts.video =
|
|
2426
|
+
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.passed`)
|
|
2381
2427
|
for (const sessionName of Object.keys(this.sessionPages)) {
|
|
2382
|
-
test.artifacts[`video_${sessionName}`] =
|
|
2428
|
+
test.artifacts[`video_${sessionName}`] = saveVideoForPage(
|
|
2429
|
+
this.sessionPages[sessionName],
|
|
2430
|
+
`${test.title}_${sessionName}.passed`,
|
|
2431
|
+
)
|
|
2383
2432
|
}
|
|
2384
2433
|
} else {
|
|
2385
|
-
this.page
|
|
2434
|
+
this.page
|
|
2435
|
+
.video()
|
|
2436
|
+
.delete()
|
|
2437
|
+
.catch((e) => {})
|
|
2386
2438
|
}
|
|
2387
2439
|
}
|
|
2388
2440
|
|
|
2389
2441
|
if (this.options.trace) {
|
|
2390
2442
|
if (this.options.keepTraceForPassedTests) {
|
|
2391
2443
|
if (this.options.trace) {
|
|
2392
|
-
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.passed`)
|
|
2444
|
+
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.passed`)
|
|
2393
2445
|
for (const sessionName in this.sessionPages) {
|
|
2394
|
-
if (!this.sessionPages[sessionName].context) continue
|
|
2395
|
-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2446
|
+
if (!this.sessionPages[sessionName].context) continue
|
|
2447
|
+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2448
|
+
this.sessionPages[sessionName].context,
|
|
2449
|
+
`${test.title}_${sessionName}.passed`,
|
|
2450
|
+
)
|
|
2396
2451
|
}
|
|
2397
2452
|
}
|
|
2398
2453
|
} else {
|
|
2399
|
-
await this.browserContext.tracing.stop()
|
|
2454
|
+
await this.browserContext.tracing.stop()
|
|
2400
2455
|
}
|
|
2401
2456
|
}
|
|
2402
2457
|
|
|
2403
2458
|
if (this.options.recordHar) {
|
|
2404
|
-
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2459
|
+
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2405
2460
|
}
|
|
2406
2461
|
}
|
|
2407
2462
|
|
|
@@ -2409,62 +2464,99 @@ class Playwright extends Helper {
|
|
|
2409
2464
|
* {{> wait }}
|
|
2410
2465
|
*/
|
|
2411
2466
|
async wait(sec) {
|
|
2412
|
-
return new Promise((
|
|
2413
|
-
setTimeout(done, sec * 1000)
|
|
2414
|
-
})
|
|
2467
|
+
return new Promise((done) => {
|
|
2468
|
+
setTimeout(done, sec * 1000)
|
|
2469
|
+
})
|
|
2415
2470
|
}
|
|
2416
2471
|
|
|
2417
2472
|
/**
|
|
2418
2473
|
* {{> waitForEnabled }}
|
|
2419
2474
|
*/
|
|
2420
2475
|
async waitForEnabled(locator, sec) {
|
|
2421
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2422
|
-
locator = new Locator(locator, 'css')
|
|
2423
|
-
|
|
2424
|
-
let waiter
|
|
2425
|
-
const context = await this._getContext()
|
|
2476
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2477
|
+
locator = new Locator(locator, 'css')
|
|
2478
|
+
|
|
2479
|
+
let waiter
|
|
2480
|
+
const context = await this._getContext()
|
|
2426
2481
|
if (!locator.isXPath()) {
|
|
2427
2482
|
const valueFn = function ([locator]) {
|
|
2428
|
-
return Array.from(document.querySelectorAll(locator)).filter(el => !el.disabled).length > 0
|
|
2429
|
-
}
|
|
2430
|
-
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2483
|
+
return Array.from(document.querySelectorAll(locator)).filter((el) => !el.disabled).length > 0
|
|
2484
|
+
}
|
|
2485
|
+
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2431
2486
|
} else {
|
|
2432
2487
|
const enabledFn = function ([locator, $XPath]) {
|
|
2433
|
-
eval($XPath)
|
|
2434
|
-
return $XPath(null, locator).filter(el => !el.disabled).length > 0
|
|
2435
|
-
}
|
|
2436
|
-
waiter = context.waitForFunction(enabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2488
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2489
|
+
return $XPath(null, locator).filter((el) => !el.disabled).length > 0
|
|
2490
|
+
}
|
|
2491
|
+
waiter = context.waitForFunction(enabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2492
|
+
}
|
|
2493
|
+
return waiter.catch((err) => {
|
|
2494
|
+
throw new Error(
|
|
2495
|
+
`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2496
|
+
)
|
|
2497
|
+
})
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
/**
|
|
2501
|
+
* {{> waitForDisabled }}
|
|
2502
|
+
*/
|
|
2503
|
+
async waitForDisabled(locator, sec) {
|
|
2504
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2505
|
+
locator = new Locator(locator, 'css')
|
|
2506
|
+
|
|
2507
|
+
let waiter
|
|
2508
|
+
const context = await this._getContext()
|
|
2509
|
+
if (!locator.isXPath()) {
|
|
2510
|
+
const valueFn = function ([locator]) {
|
|
2511
|
+
return Array.from(document.querySelectorAll(locator)).filter((el) => el.disabled).length > 0
|
|
2512
|
+
}
|
|
2513
|
+
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2514
|
+
} else {
|
|
2515
|
+
const disabledFn = function ([locator, $XPath]) {
|
|
2516
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2517
|
+
return $XPath(null, locator).filter((el) => el.disabled).length > 0
|
|
2518
|
+
}
|
|
2519
|
+
waiter = context.waitForFunction(disabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2437
2520
|
}
|
|
2438
2521
|
return waiter.catch((err) => {
|
|
2439
|
-
throw new Error(
|
|
2440
|
-
|
|
2522
|
+
throw new Error(
|
|
2523
|
+
`element (${locator.toString()}) is still enabled after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2524
|
+
)
|
|
2525
|
+
})
|
|
2441
2526
|
}
|
|
2442
2527
|
|
|
2443
2528
|
/**
|
|
2444
2529
|
* {{> waitForValue }}
|
|
2445
2530
|
*/
|
|
2446
2531
|
async waitForValue(field, value, sec) {
|
|
2447
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2448
|
-
const locator = new Locator(field, 'css')
|
|
2449
|
-
const matcher = await this.context
|
|
2450
|
-
let waiter
|
|
2451
|
-
const context = await this._getContext()
|
|
2532
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2533
|
+
const locator = new Locator(field, 'css')
|
|
2534
|
+
const matcher = await this.context
|
|
2535
|
+
let waiter
|
|
2536
|
+
const context = await this._getContext()
|
|
2452
2537
|
if (!locator.isXPath()) {
|
|
2453
2538
|
const valueFn = function ([locator, value]) {
|
|
2454
|
-
return
|
|
2455
|
-
|
|
2456
|
-
|
|
2539
|
+
return (
|
|
2540
|
+
Array.from(document.querySelectorAll(locator)).filter((el) => (el.value || '').indexOf(value) !== -1).length >
|
|
2541
|
+
0
|
|
2542
|
+
)
|
|
2543
|
+
}
|
|
2544
|
+
waiter = context.waitForFunction(valueFn, [locator.value, value], { timeout: waitTimeout })
|
|
2457
2545
|
} else {
|
|
2458
2546
|
const valueFn = function ([locator, $XPath, value]) {
|
|
2459
|
-
eval($XPath)
|
|
2460
|
-
return $XPath(null, locator).filter(el => (el.value || '').indexOf(value) !== -1).length > 0
|
|
2461
|
-
}
|
|
2462
|
-
waiter = context.waitForFunction(valueFn, [locator.value, $XPath.toString(), value], {
|
|
2547
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2548
|
+
return $XPath(null, locator).filter((el) => (el.value || '').indexOf(value) !== -1).length > 0
|
|
2549
|
+
}
|
|
2550
|
+
waiter = context.waitForFunction(valueFn, [locator.value, $XPath.toString(), value], {
|
|
2551
|
+
timeout: waitTimeout,
|
|
2552
|
+
})
|
|
2463
2553
|
}
|
|
2464
2554
|
return waiter.catch((err) => {
|
|
2465
|
-
const loc = locator.toString()
|
|
2466
|
-
throw new Error(
|
|
2467
|
-
|
|
2555
|
+
const loc = locator.toString()
|
|
2556
|
+
throw new Error(
|
|
2557
|
+
`element (${loc}) is not in DOM or there is no element(${loc}) with value "${value}" after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2558
|
+
)
|
|
2559
|
+
})
|
|
2468
2560
|
}
|
|
2469
2561
|
|
|
2470
2562
|
/**
|
|
@@ -2472,38 +2564,44 @@ class Playwright extends Helper {
|
|
|
2472
2564
|
*
|
|
2473
2565
|
*/
|
|
2474
2566
|
async waitNumberOfVisibleElements(locator, num, sec) {
|
|
2475
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2476
|
-
locator = new Locator(locator, 'css')
|
|
2567
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2568
|
+
locator = new Locator(locator, 'css')
|
|
2477
2569
|
|
|
2478
|
-
let waiter
|
|
2479
|
-
const context = await this._getContext()
|
|
2570
|
+
let waiter
|
|
2571
|
+
const context = await this._getContext()
|
|
2480
2572
|
if (locator.isCSS()) {
|
|
2481
2573
|
const visibleFn = function ([locator, num]) {
|
|
2482
|
-
const els = document.querySelectorAll(locator)
|
|
2574
|
+
const els = document.querySelectorAll(locator)
|
|
2483
2575
|
if (!els || els.length === 0) {
|
|
2484
|
-
return false
|
|
2576
|
+
return false
|
|
2485
2577
|
}
|
|
2486
|
-
return Array.prototype.filter.call(els, el => el.offsetParent !== null).length === num
|
|
2487
|
-
}
|
|
2488
|
-
waiter = context.waitForFunction(visibleFn, [locator.value, num], { timeout: waitTimeout })
|
|
2578
|
+
return Array.prototype.filter.call(els, (el) => el.offsetParent !== null).length === num
|
|
2579
|
+
}
|
|
2580
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, num], { timeout: waitTimeout })
|
|
2489
2581
|
} else {
|
|
2490
2582
|
const visibleFn = function ([locator, $XPath, num]) {
|
|
2491
|
-
eval($XPath)
|
|
2492
|
-
return $XPath(null, locator).filter(el => el.offsetParent !== null).length === num
|
|
2493
|
-
}
|
|
2494
|
-
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString(), num], {
|
|
2583
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2584
|
+
return $XPath(null, locator).filter((el) => el.offsetParent !== null).length === num
|
|
2585
|
+
}
|
|
2586
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString(), num], {
|
|
2587
|
+
timeout: waitTimeout,
|
|
2588
|
+
})
|
|
2495
2589
|
}
|
|
2496
2590
|
return waiter.catch((err) => {
|
|
2497
|
-
throw new Error(
|
|
2498
|
-
|
|
2591
|
+
throw new Error(
|
|
2592
|
+
`The number of elements (${locator.toString()}) is not ${num} after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2593
|
+
)
|
|
2594
|
+
})
|
|
2499
2595
|
}
|
|
2500
2596
|
|
|
2501
2597
|
/**
|
|
2502
2598
|
* {{> waitForClickable }}
|
|
2503
2599
|
*/
|
|
2504
2600
|
async waitForClickable(locator, waitTimeout) {
|
|
2505
|
-
console.log(
|
|
2506
|
-
|
|
2601
|
+
console.log(
|
|
2602
|
+
'I.waitForClickable is DEPRECATED: This is no longer needed, Playwright automatically waits for element to be clickable',
|
|
2603
|
+
)
|
|
2604
|
+
console.log('Remove usage of this function')
|
|
2507
2605
|
}
|
|
2508
2606
|
|
|
2509
2607
|
/**
|
|
@@ -2511,14 +2609,16 @@ class Playwright extends Helper {
|
|
|
2511
2609
|
*
|
|
2512
2610
|
*/
|
|
2513
2611
|
async waitForElement(locator, sec) {
|
|
2514
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2515
|
-
locator = new Locator(locator, 'css')
|
|
2612
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2613
|
+
locator = new Locator(locator, 'css')
|
|
2516
2614
|
|
|
2517
|
-
const context = await this._getContext()
|
|
2615
|
+
const context = await this._getContext()
|
|
2518
2616
|
try {
|
|
2519
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' })
|
|
2617
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' })
|
|
2520
2618
|
} catch (e) {
|
|
2521
|
-
throw new Error(
|
|
2619
|
+
throw new Error(
|
|
2620
|
+
`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${e.message}`,
|
|
2621
|
+
)
|
|
2522
2622
|
}
|
|
2523
2623
|
}
|
|
2524
2624
|
|
|
@@ -2528,28 +2628,28 @@ class Playwright extends Helper {
|
|
|
2528
2628
|
* {{> waitForVisible }}
|
|
2529
2629
|
*/
|
|
2530
2630
|
async waitForVisible(locator, sec) {
|
|
2531
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2532
|
-
locator = new Locator(locator, 'css')
|
|
2533
|
-
const context = await this._getContext()
|
|
2534
|
-
let count = 0
|
|
2631
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2632
|
+
locator = new Locator(locator, 'css')
|
|
2633
|
+
const context = await this._getContext()
|
|
2634
|
+
let count = 0
|
|
2535
2635
|
|
|
2536
2636
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2537
|
-
let waiter
|
|
2637
|
+
let waiter
|
|
2538
2638
|
if (this.frame) {
|
|
2539
2639
|
do {
|
|
2540
|
-
waiter = await this.frame.locator(buildLocatorString(locator)).first().isVisible()
|
|
2541
|
-
await this.wait(1)
|
|
2542
|
-
count += 1000
|
|
2543
|
-
if (waiter) break
|
|
2544
|
-
} while (count <= waitTimeout)
|
|
2640
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isVisible()
|
|
2641
|
+
await this.wait(1)
|
|
2642
|
+
count += 1000
|
|
2643
|
+
if (waiter) break
|
|
2644
|
+
} while (count <= waitTimeout)
|
|
2545
2645
|
|
|
2546
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec.`)
|
|
2646
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec.`)
|
|
2547
2647
|
}
|
|
2548
2648
|
|
|
2549
2649
|
try {
|
|
2550
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2650
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2551
2651
|
} catch (e) {
|
|
2552
|
-
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2652
|
+
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2553
2653
|
}
|
|
2554
2654
|
}
|
|
2555
2655
|
|
|
@@ -2557,29 +2657,29 @@ class Playwright extends Helper {
|
|
|
2557
2657
|
* {{> waitForInvisible }}
|
|
2558
2658
|
*/
|
|
2559
2659
|
async waitForInvisible(locator, sec) {
|
|
2560
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2561
|
-
locator = new Locator(locator, 'css')
|
|
2562
|
-
const context = await this._getContext()
|
|
2563
|
-
let waiter
|
|
2564
|
-
let count = 0
|
|
2660
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2661
|
+
locator = new Locator(locator, 'css')
|
|
2662
|
+
const context = await this._getContext()
|
|
2663
|
+
let waiter
|
|
2664
|
+
let count = 0
|
|
2565
2665
|
|
|
2566
2666
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2567
2667
|
if (this.frame) {
|
|
2568
2668
|
do {
|
|
2569
|
-
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden()
|
|
2570
|
-
await this.wait(1)
|
|
2571
|
-
count += 1000
|
|
2572
|
-
if (waiter) break
|
|
2573
|
-
} while (count <= waitTimeout)
|
|
2669
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden()
|
|
2670
|
+
await this.wait(1)
|
|
2671
|
+
count += 1000
|
|
2672
|
+
if (waiter) break
|
|
2673
|
+
} while (count <= waitTimeout)
|
|
2574
2674
|
|
|
2575
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec.`)
|
|
2576
|
-
return
|
|
2675
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec.`)
|
|
2676
|
+
return
|
|
2577
2677
|
}
|
|
2578
2678
|
|
|
2579
2679
|
try {
|
|
2580
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2680
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2581
2681
|
} catch (e) {
|
|
2582
|
-
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2682
|
+
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2583
2683
|
}
|
|
2584
2684
|
}
|
|
2585
2685
|
|
|
@@ -2587,143 +2687,171 @@ class Playwright extends Helper {
|
|
|
2587
2687
|
* {{> waitToHide }}
|
|
2588
2688
|
*/
|
|
2589
2689
|
async waitToHide(locator, sec) {
|
|
2590
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2591
|
-
locator = new Locator(locator, 'css')
|
|
2592
|
-
const context = await this._getContext()
|
|
2593
|
-
let waiter
|
|
2594
|
-
let count = 0
|
|
2690
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2691
|
+
locator = new Locator(locator, 'css')
|
|
2692
|
+
const context = await this._getContext()
|
|
2693
|
+
let waiter
|
|
2694
|
+
let count = 0
|
|
2595
2695
|
|
|
2596
2696
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2597
2697
|
if (this.frame) {
|
|
2598
2698
|
do {
|
|
2599
|
-
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden()
|
|
2600
|
-
await this.wait(1)
|
|
2601
|
-
count += 1000
|
|
2602
|
-
if (waiter) break
|
|
2603
|
-
} while (count <= waitTimeout)
|
|
2699
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden()
|
|
2700
|
+
await this.wait(1)
|
|
2701
|
+
count += 1000
|
|
2702
|
+
if (waiter) break
|
|
2703
|
+
} while (count <= waitTimeout)
|
|
2604
2704
|
|
|
2605
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec.`)
|
|
2606
|
-
return
|
|
2705
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec.`)
|
|
2706
|
+
return
|
|
2607
2707
|
}
|
|
2608
2708
|
|
|
2609
|
-
return context
|
|
2610
|
-
|
|
2611
|
-
|
|
2709
|
+
return context
|
|
2710
|
+
.locator(buildLocatorString(locator))
|
|
2711
|
+
.first()
|
|
2712
|
+
.waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2713
|
+
.catch((err) => {
|
|
2714
|
+
throw new Error(
|
|
2715
|
+
`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2716
|
+
)
|
|
2717
|
+
})
|
|
2612
2718
|
}
|
|
2613
2719
|
|
|
2614
2720
|
/**
|
|
2615
2721
|
* {{> waitForNumberOfTabs }}
|
|
2616
2722
|
*/
|
|
2617
2723
|
async waitForNumberOfTabs(expectedTabs, sec) {
|
|
2618
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2619
|
-
let currentTabs
|
|
2620
|
-
let count = 0
|
|
2724
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2725
|
+
let currentTabs
|
|
2726
|
+
let count = 0
|
|
2621
2727
|
|
|
2622
2728
|
do {
|
|
2623
|
-
currentTabs = await this.grabNumberOfOpenTabs()
|
|
2624
|
-
await this.wait(1)
|
|
2625
|
-
count += 1000
|
|
2626
|
-
if (currentTabs >= expectedTabs) return
|
|
2627
|
-
} while (count <= waitTimeout)
|
|
2729
|
+
currentTabs = await this.grabNumberOfOpenTabs()
|
|
2730
|
+
await this.wait(1)
|
|
2731
|
+
count += 1000
|
|
2732
|
+
if (currentTabs >= expectedTabs) return
|
|
2733
|
+
} while (count <= waitTimeout)
|
|
2628
2734
|
|
|
2629
|
-
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`)
|
|
2735
|
+
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`)
|
|
2630
2736
|
}
|
|
2631
2737
|
|
|
2632
2738
|
async _getContext() {
|
|
2633
|
-
if (this.context && this.context.constructor.name === 'FrameLocator') {
|
|
2634
|
-
return this.context
|
|
2739
|
+
if ((this.context && this.context.constructor.name === 'FrameLocator') || this.context) {
|
|
2740
|
+
return this.context
|
|
2635
2741
|
}
|
|
2636
|
-
return this.page
|
|
2742
|
+
return this.page
|
|
2637
2743
|
}
|
|
2638
2744
|
|
|
2639
2745
|
/**
|
|
2640
2746
|
* {{> waitInUrl }}
|
|
2641
2747
|
*/
|
|
2642
2748
|
async waitInUrl(urlPart, sec = null) {
|
|
2643
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2644
|
-
|
|
2645
|
-
return this.page
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
|
|
2749
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2750
|
+
|
|
2751
|
+
return this.page
|
|
2752
|
+
.waitForFunction(
|
|
2753
|
+
(urlPart) => {
|
|
2754
|
+
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
|
|
2755
|
+
return currUrl.indexOf(urlPart) > -1
|
|
2756
|
+
},
|
|
2757
|
+
urlPart,
|
|
2758
|
+
{ timeout: waitTimeout },
|
|
2759
|
+
)
|
|
2760
|
+
.catch(async (e) => {
|
|
2761
|
+
const currUrl = await this._getPageUrl() // Required because the waitForFunction can't return data.
|
|
2762
|
+
if (/Timeout/i.test(e.message)) {
|
|
2763
|
+
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
|
|
2764
|
+
} else {
|
|
2765
|
+
throw e
|
|
2766
|
+
}
|
|
2767
|
+
})
|
|
2656
2768
|
}
|
|
2657
2769
|
|
|
2658
2770
|
/**
|
|
2659
2771
|
* {{> waitUrlEquals }}
|
|
2660
2772
|
*/
|
|
2661
2773
|
async waitUrlEquals(urlPart, sec = null) {
|
|
2662
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2774
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2663
2775
|
|
|
2664
|
-
const baseUrl = this.options.url
|
|
2776
|
+
const baseUrl = this.options.url
|
|
2665
2777
|
if (urlPart.indexOf('http') < 0) {
|
|
2666
|
-
urlPart = baseUrl + urlPart
|
|
2667
|
-
}
|
|
2668
|
-
|
|
2669
|
-
return this.page
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2778
|
+
urlPart = baseUrl + urlPart
|
|
2779
|
+
}
|
|
2780
|
+
|
|
2781
|
+
return this.page
|
|
2782
|
+
.waitForFunction(
|
|
2783
|
+
(urlPart) => {
|
|
2784
|
+
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
|
|
2785
|
+
return currUrl.indexOf(urlPart) > -1
|
|
2786
|
+
},
|
|
2787
|
+
urlPart,
|
|
2788
|
+
{ timeout: waitTimeout },
|
|
2789
|
+
)
|
|
2790
|
+
.catch(async (e) => {
|
|
2791
|
+
const currUrl = await this._getPageUrl() // Required because the waitForFunction can't return data.
|
|
2792
|
+
if (/Timeout/i.test(e.message)) {
|
|
2793
|
+
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
|
|
2794
|
+
} else {
|
|
2795
|
+
throw e
|
|
2796
|
+
}
|
|
2797
|
+
})
|
|
2680
2798
|
}
|
|
2681
2799
|
|
|
2682
2800
|
/**
|
|
2683
2801
|
* {{> waitForText }}
|
|
2684
2802
|
*/
|
|
2685
2803
|
async waitForText(text, sec = null, context = null) {
|
|
2686
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2687
|
-
const errorMessage = `Text "${text}" was not found on page after ${waitTimeout / 1000} sec
|
|
2688
|
-
let waiter
|
|
2804
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2805
|
+
const errorMessage = `Text "${text}" was not found on page after ${waitTimeout / 1000} sec.`
|
|
2806
|
+
let waiter
|
|
2689
2807
|
|
|
2690
|
-
const contextObject = await this._getContext()
|
|
2808
|
+
const contextObject = await this._getContext()
|
|
2691
2809
|
|
|
2692
2810
|
if (context) {
|
|
2693
|
-
const locator = new Locator(context, 'css')
|
|
2811
|
+
const locator = new Locator(context, 'css')
|
|
2694
2812
|
if (!locator.isXPath()) {
|
|
2695
2813
|
try {
|
|
2696
|
-
await contextObject
|
|
2814
|
+
await contextObject
|
|
2815
|
+
.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`)
|
|
2816
|
+
.first()
|
|
2817
|
+
.waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2697
2818
|
} catch (e) {
|
|
2698
|
-
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2819
|
+
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2699
2820
|
}
|
|
2700
2821
|
}
|
|
2701
2822
|
|
|
2702
2823
|
if (locator.isXPath()) {
|
|
2703
2824
|
try {
|
|
2704
|
-
await contextObject.waitForFunction(
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2825
|
+
await contextObject.waitForFunction(
|
|
2826
|
+
([locator, text, $XPath]) => {
|
|
2827
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2828
|
+
const el = $XPath(null, locator)
|
|
2829
|
+
if (!el.length) return false
|
|
2830
|
+
return el[0].innerText.indexOf(text) > -1
|
|
2831
|
+
},
|
|
2832
|
+
[locator.value, text, $XPath.toString()],
|
|
2833
|
+
{ timeout: waitTimeout },
|
|
2834
|
+
)
|
|
2710
2835
|
} catch (e) {
|
|
2711
|
-
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2836
|
+
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2712
2837
|
}
|
|
2713
2838
|
}
|
|
2714
2839
|
} else {
|
|
2715
2840
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2716
|
-
|
|
2717
|
-
const _contextObject = this.frame ? this.frame : contextObject
|
|
2718
|
-
let count = 0
|
|
2841
|
+
|
|
2842
|
+
const _contextObject = this.frame ? this.frame : contextObject
|
|
2843
|
+
let count = 0
|
|
2719
2844
|
do {
|
|
2720
|
-
waiter = await _contextObject
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2845
|
+
waiter = await _contextObject
|
|
2846
|
+
.locator(`:has-text(${JSON.stringify(text)})`)
|
|
2847
|
+
.first()
|
|
2848
|
+
.isVisible()
|
|
2849
|
+
if (waiter) break
|
|
2850
|
+
await this.wait(1)
|
|
2851
|
+
count += 1000
|
|
2852
|
+
} while (count <= waitTimeout)
|
|
2725
2853
|
|
|
2726
|
-
if (!waiter) throw new Error(`${errorMessage}`)
|
|
2854
|
+
if (!waiter) throw new Error(`${errorMessage}`)
|
|
2727
2855
|
}
|
|
2728
2856
|
}
|
|
2729
2857
|
|
|
@@ -2739,8 +2867,8 @@ class Playwright extends Helper {
|
|
|
2739
2867
|
* @param {?number} [sec=null] seconds to wait
|
|
2740
2868
|
*/
|
|
2741
2869
|
async waitForRequest(urlOrPredicate, sec = null) {
|
|
2742
|
-
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2743
|
-
return this.page.waitForRequest(urlOrPredicate, { timeout })
|
|
2870
|
+
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2871
|
+
return this.page.waitForRequest(urlOrPredicate, { timeout })
|
|
2744
2872
|
}
|
|
2745
2873
|
|
|
2746
2874
|
/**
|
|
@@ -2755,8 +2883,8 @@ class Playwright extends Helper {
|
|
|
2755
2883
|
* @param {?number} [sec=null] number of seconds to wait
|
|
2756
2884
|
*/
|
|
2757
2885
|
async waitForResponse(urlOrPredicate, sec = null) {
|
|
2758
|
-
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2759
|
-
return this.page.waitForResponse(urlOrPredicate, { timeout })
|
|
2886
|
+
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2887
|
+
return this.page.waitForResponse(urlOrPredicate, { timeout })
|
|
2760
2888
|
}
|
|
2761
2889
|
|
|
2762
2890
|
/**
|
|
@@ -2766,51 +2894,51 @@ class Playwright extends Helper {
|
|
|
2766
2894
|
if (Number.isInteger(locator)) {
|
|
2767
2895
|
// Select by frame index of current context
|
|
2768
2896
|
|
|
2769
|
-
let childFrames = null
|
|
2897
|
+
let childFrames = null
|
|
2770
2898
|
if (this.context && typeof this.context.childFrames === 'function') {
|
|
2771
|
-
childFrames = this.context.childFrames()
|
|
2899
|
+
childFrames = this.context.childFrames()
|
|
2772
2900
|
} else {
|
|
2773
|
-
childFrames = this.page.mainFrame().childFrames()
|
|
2901
|
+
childFrames = this.page.mainFrame().childFrames()
|
|
2774
2902
|
}
|
|
2775
2903
|
|
|
2776
2904
|
if (locator >= 0 && locator < childFrames.length) {
|
|
2777
|
-
this.context = await this.page.frameLocator('iframe').nth(locator)
|
|
2778
|
-
this.contextLocator = locator
|
|
2905
|
+
this.context = await this.page.frameLocator('iframe').nth(locator)
|
|
2906
|
+
this.contextLocator = locator
|
|
2779
2907
|
} else {
|
|
2780
|
-
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath')
|
|
2908
|
+
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath')
|
|
2781
2909
|
}
|
|
2782
|
-
return
|
|
2910
|
+
return
|
|
2783
2911
|
}
|
|
2784
2912
|
|
|
2785
2913
|
if (!locator) {
|
|
2786
|
-
this.context = this.page
|
|
2787
|
-
this.contextLocator = null
|
|
2788
|
-
this.frame = null
|
|
2789
|
-
return
|
|
2914
|
+
this.context = this.page
|
|
2915
|
+
this.contextLocator = null
|
|
2916
|
+
this.frame = null
|
|
2917
|
+
return
|
|
2790
2918
|
}
|
|
2791
2919
|
|
|
2792
2920
|
// iframe by selector
|
|
2793
|
-
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2794
|
-
const frame = await this._locateElement(locator)
|
|
2921
|
+
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2922
|
+
const frame = await this._locateElement(locator)
|
|
2795
2923
|
|
|
2796
2924
|
if (!frame) {
|
|
2797
|
-
throw new Error(`Frame ${JSON.stringify(locator)} was not found by text|CSS|XPath`)
|
|
2925
|
+
throw new Error(`Frame ${JSON.stringify(locator)} was not found by text|CSS|XPath`)
|
|
2798
2926
|
}
|
|
2799
2927
|
|
|
2800
2928
|
if (this.frame) {
|
|
2801
|
-
this.frame = await this.frame.frameLocator(locator)
|
|
2929
|
+
this.frame = await this.frame.frameLocator(locator)
|
|
2802
2930
|
} else {
|
|
2803
|
-
this.frame = await this.page.frameLocator(locator)
|
|
2931
|
+
this.frame = await this.page.frameLocator(locator)
|
|
2804
2932
|
}
|
|
2805
2933
|
|
|
2806
|
-
const contentFrame = this.frame
|
|
2934
|
+
const contentFrame = this.frame
|
|
2807
2935
|
|
|
2808
2936
|
if (contentFrame) {
|
|
2809
|
-
this.context = contentFrame
|
|
2810
|
-
this.contextLocator = null
|
|
2937
|
+
this.context = contentFrame
|
|
2938
|
+
this.contextLocator = null
|
|
2811
2939
|
} else {
|
|
2812
|
-
this.context = this.page.frame(this.page.frames()[1].name())
|
|
2813
|
-
this.contextLocator = locator
|
|
2940
|
+
this.context = this.page.frame(this.page.frames()[1].name())
|
|
2941
|
+
this.contextLocator = locator
|
|
2814
2942
|
}
|
|
2815
2943
|
}
|
|
2816
2944
|
|
|
@@ -2818,17 +2946,17 @@ class Playwright extends Helper {
|
|
|
2818
2946
|
* {{> waitForFunction }}
|
|
2819
2947
|
*/
|
|
2820
2948
|
async waitForFunction(fn, argsOrSec = null, sec = null) {
|
|
2821
|
-
let args = []
|
|
2949
|
+
let args = []
|
|
2822
2950
|
if (argsOrSec) {
|
|
2823
2951
|
if (Array.isArray(argsOrSec)) {
|
|
2824
|
-
args = argsOrSec
|
|
2952
|
+
args = argsOrSec
|
|
2825
2953
|
} else if (typeof argsOrSec === 'number') {
|
|
2826
|
-
sec = argsOrSec
|
|
2954
|
+
sec = argsOrSec
|
|
2827
2955
|
}
|
|
2828
2956
|
}
|
|
2829
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2830
|
-
const context = await this._getContext()
|
|
2831
|
-
return context.waitForFunction(fn, args, { timeout: waitTimeout })
|
|
2957
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2958
|
+
const context = await this._getContext()
|
|
2959
|
+
return context.waitForFunction(fn, args, { timeout: waitTimeout })
|
|
2832
2960
|
}
|
|
2833
2961
|
|
|
2834
2962
|
/**
|
|
@@ -2840,13 +2968,13 @@ class Playwright extends Helper {
|
|
|
2840
2968
|
*/
|
|
2841
2969
|
async waitForNavigation(options = {}) {
|
|
2842
2970
|
console.log(`waitForNavigation deprecated:
|
|
2843
|
-
* This method is inherently racy, please use 'waitForURL' instead.`)
|
|
2971
|
+
* This method is inherently racy, please use 'waitForURL' instead.`)
|
|
2844
2972
|
options = {
|
|
2845
2973
|
timeout: this.options.getPageTimeout,
|
|
2846
2974
|
waitUntil: this.options.waitForNavigation,
|
|
2847
2975
|
...options,
|
|
2848
|
-
}
|
|
2849
|
-
return this.page.waitForNavigation(options)
|
|
2976
|
+
}
|
|
2977
|
+
return this.page.waitForNavigation(options)
|
|
2850
2978
|
}
|
|
2851
2979
|
|
|
2852
2980
|
/**
|
|
@@ -2862,41 +2990,44 @@ class Playwright extends Helper {
|
|
|
2862
2990
|
timeout: this.options.getPageTimeout,
|
|
2863
2991
|
waitUntil: this.options.waitForNavigation,
|
|
2864
2992
|
...options,
|
|
2865
|
-
}
|
|
2866
|
-
return this.page.waitForURL(url, options)
|
|
2993
|
+
}
|
|
2994
|
+
return this.page.waitForURL(url, options)
|
|
2867
2995
|
}
|
|
2868
2996
|
|
|
2869
2997
|
async waitUntilExists(locator, sec) {
|
|
2870
2998
|
console.log(`waitUntilExists deprecated:
|
|
2871
2999
|
* use 'waitForElement' to wait for element to be attached
|
|
2872
|
-
* use 'waitForDetached to wait for element to be removed'`)
|
|
2873
|
-
return this.waitForDetached(locator, sec)
|
|
3000
|
+
* use 'waitForDetached to wait for element to be removed'`)
|
|
3001
|
+
return this.waitForDetached(locator, sec)
|
|
2874
3002
|
}
|
|
2875
3003
|
|
|
2876
3004
|
/**
|
|
2877
3005
|
* {{> waitForDetached }}
|
|
2878
3006
|
*/
|
|
2879
3007
|
async waitForDetached(locator, sec) {
|
|
2880
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2881
|
-
locator = new Locator(locator, 'css')
|
|
3008
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
3009
|
+
locator = new Locator(locator, 'css')
|
|
2882
3010
|
|
|
2883
|
-
let waiter
|
|
2884
|
-
const context = await this._getContext()
|
|
3011
|
+
let waiter
|
|
3012
|
+
const context = await this._getContext()
|
|
2885
3013
|
if (!locator.isXPath()) {
|
|
2886
3014
|
try {
|
|
2887
|
-
await context
|
|
3015
|
+
await context
|
|
3016
|
+
.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()}`)
|
|
3017
|
+
.first()
|
|
3018
|
+
.waitFor({ timeout: waitTimeout, state: 'detached' })
|
|
2888
3019
|
} catch (e) {
|
|
2889
|
-
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
3020
|
+
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2890
3021
|
}
|
|
2891
3022
|
} else {
|
|
2892
3023
|
const visibleFn = function ([locator, $XPath]) {
|
|
2893
|
-
eval($XPath)
|
|
2894
|
-
return $XPath(null, locator).length === 0
|
|
2895
|
-
}
|
|
2896
|
-
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
3024
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
3025
|
+
return $XPath(null, locator).length === 0
|
|
3026
|
+
}
|
|
3027
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2897
3028
|
return waiter.catch((err) => {
|
|
2898
|
-
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2899
|
-
})
|
|
3029
|
+
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
3030
|
+
})
|
|
2900
3031
|
}
|
|
2901
3032
|
}
|
|
2902
3033
|
|
|
@@ -2905,53 +3036,56 @@ class Playwright extends Helper {
|
|
|
2905
3036
|
*/
|
|
2906
3037
|
async waitForCookie(name, sec) {
|
|
2907
3038
|
// by default, we will retry 3 times
|
|
2908
|
-
let retries = 3
|
|
2909
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
3039
|
+
let retries = 3
|
|
3040
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2910
3041
|
|
|
2911
3042
|
if (sec) {
|
|
2912
|
-
retries = sec
|
|
3043
|
+
retries = sec
|
|
2913
3044
|
} else {
|
|
2914
|
-
retries = Math.ceil(waitTimeout / 1000) - 1
|
|
3045
|
+
retries = Math.ceil(waitTimeout / 1000) - 1
|
|
2915
3046
|
}
|
|
2916
3047
|
|
|
2917
|
-
return promiseRetry(
|
|
2918
|
-
|
|
2919
|
-
const
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
3048
|
+
return promiseRetry(
|
|
3049
|
+
async (retry, number) => {
|
|
3050
|
+
const _grabCookie = async (name) => {
|
|
3051
|
+
const cookies = await this.browserContext.cookies()
|
|
3052
|
+
const cookie = cookies.filter((c) => c.name === name)
|
|
3053
|
+
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`)
|
|
3054
|
+
}
|
|
2923
3055
|
|
|
2924
|
-
|
|
2925
|
-
|
|
3056
|
+
this.debugSection('Wait for cookie: ', name)
|
|
3057
|
+
if (number > 1) this.debugSection('Retrying... Attempt #', number)
|
|
2926
3058
|
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
3059
|
+
try {
|
|
3060
|
+
await _grabCookie(name)
|
|
3061
|
+
} catch (e) {
|
|
3062
|
+
retry(e)
|
|
3063
|
+
}
|
|
3064
|
+
},
|
|
3065
|
+
{ retries, maxTimeout: 1000 },
|
|
3066
|
+
)
|
|
2933
3067
|
}
|
|
2934
3068
|
|
|
2935
3069
|
async _waitForAction() {
|
|
2936
|
-
return this.wait(this.options.waitForAction / 1000)
|
|
3070
|
+
return this.wait(this.options.waitForAction / 1000)
|
|
2937
3071
|
}
|
|
2938
3072
|
|
|
2939
3073
|
/**
|
|
2940
3074
|
* {{> grabDataFromPerformanceTiming }}
|
|
2941
3075
|
*/
|
|
2942
3076
|
async grabDataFromPerformanceTiming() {
|
|
2943
|
-
return perfTiming
|
|
3077
|
+
return perfTiming
|
|
2944
3078
|
}
|
|
2945
3079
|
|
|
2946
3080
|
/**
|
|
2947
3081
|
* {{> grabElementBoundingRect }}
|
|
2948
3082
|
*/
|
|
2949
3083
|
async grabElementBoundingRect(locator, prop) {
|
|
2950
|
-
const el = await this._locateElement(locator)
|
|
2951
|
-
assertElementExists(el, locator)
|
|
2952
|
-
const rect = await el.boundingBox()
|
|
2953
|
-
if (prop) return rect[prop]
|
|
2954
|
-
return rect
|
|
3084
|
+
const el = await this._locateElement(locator)
|
|
3085
|
+
assertElementExists(el, locator)
|
|
3086
|
+
const rect = await el.boundingBox()
|
|
3087
|
+
if (prop) return rect[prop]
|
|
3088
|
+
return rect
|
|
2955
3089
|
}
|
|
2956
3090
|
|
|
2957
3091
|
/**
|
|
@@ -2966,7 +3100,7 @@ class Playwright extends Helper {
|
|
|
2966
3100
|
* @param {function} [handler] a function to process request
|
|
2967
3101
|
*/
|
|
2968
3102
|
async mockRoute(url, handler) {
|
|
2969
|
-
return this.browserContext.route(...arguments)
|
|
3103
|
+
return this.browserContext.route(...arguments)
|
|
2970
3104
|
}
|
|
2971
3105
|
|
|
2972
3106
|
/**
|
|
@@ -2982,17 +3116,17 @@ class Playwright extends Helper {
|
|
|
2982
3116
|
* @param {function} [handler] a function to process request
|
|
2983
3117
|
*/
|
|
2984
3118
|
async stopMockingRoute(url, handler) {
|
|
2985
|
-
return this.browserContext.unroute(...arguments)
|
|
3119
|
+
return this.browserContext.unroute(...arguments)
|
|
2986
3120
|
}
|
|
2987
3121
|
|
|
2988
3122
|
/**
|
|
2989
|
-
* {{>
|
|
3123
|
+
* {{> startRecordingTraffic }}
|
|
2990
3124
|
*
|
|
2991
3125
|
*/
|
|
2992
3126
|
startRecordingTraffic() {
|
|
2993
|
-
this.flushNetworkTraffics()
|
|
2994
|
-
this.recording = true
|
|
2995
|
-
this.recordedAtLeastOnce = true
|
|
3127
|
+
this.flushNetworkTraffics()
|
|
3128
|
+
this.recording = true
|
|
3129
|
+
this.recordedAtLeastOnce = true
|
|
2996
3130
|
|
|
2997
3131
|
this.page.on('requestfinished', async (request) => {
|
|
2998
3132
|
const information = {
|
|
@@ -3001,16 +3135,16 @@ class Playwright extends Helper {
|
|
|
3001
3135
|
requestHeaders: request.headers(),
|
|
3002
3136
|
requestPostData: request.postData(),
|
|
3003
3137
|
response: request.response(),
|
|
3004
|
-
}
|
|
3138
|
+
}
|
|
3005
3139
|
|
|
3006
|
-
this.debugSection('REQUEST: ', JSON.stringify(information))
|
|
3140
|
+
this.debugSection('REQUEST: ', JSON.stringify(information))
|
|
3007
3141
|
|
|
3008
3142
|
if (typeof information.requestPostData === 'object') {
|
|
3009
|
-
information.requestPostData = JSON.parse(information.requestPostData)
|
|
3143
|
+
information.requestPostData = JSON.parse(information.requestPostData)
|
|
3010
3144
|
}
|
|
3011
3145
|
|
|
3012
|
-
this.requests.push(information)
|
|
3013
|
-
})
|
|
3146
|
+
this.requests.push(information)
|
|
3147
|
+
})
|
|
3014
3148
|
}
|
|
3015
3149
|
|
|
3016
3150
|
/**
|
|
@@ -3033,21 +3167,21 @@ class Playwright extends Helper {
|
|
|
3033
3167
|
*/
|
|
3034
3168
|
blockTraffic(urls) {
|
|
3035
3169
|
if (Array.isArray(urls)) {
|
|
3036
|
-
urls.forEach(url => {
|
|
3170
|
+
urls.forEach((url) => {
|
|
3037
3171
|
this.page.route(url, (route) => {
|
|
3038
3172
|
route
|
|
3039
3173
|
.abort()
|
|
3040
3174
|
// Sometimes it happens that browser has been closed in the meantime. It is ok to ignore error then.
|
|
3041
|
-
.catch((e) => {})
|
|
3042
|
-
})
|
|
3043
|
-
})
|
|
3175
|
+
.catch((e) => {})
|
|
3176
|
+
})
|
|
3177
|
+
})
|
|
3044
3178
|
} else {
|
|
3045
3179
|
this.page.route(urls, (route) => {
|
|
3046
3180
|
route
|
|
3047
3181
|
.abort()
|
|
3048
3182
|
// Sometimes it happens that browser has been closed in the meantime. It is ok to ignore error then.
|
|
3049
|
-
.catch((e) => {})
|
|
3050
|
-
})
|
|
3183
|
+
.catch((e) => {})
|
|
3184
|
+
})
|
|
3051
3185
|
}
|
|
3052
3186
|
}
|
|
3053
3187
|
|
|
@@ -3069,10 +3203,10 @@ class Playwright extends Helper {
|
|
|
3069
3203
|
*/
|
|
3070
3204
|
mockTraffic(urls, responseString, contentType = 'application/json') {
|
|
3071
3205
|
// Required to mock cross-domain requests
|
|
3072
|
-
const headers = { 'access-control-allow-origin': '*' }
|
|
3206
|
+
const headers = { 'access-control-allow-origin': '*' }
|
|
3073
3207
|
|
|
3074
3208
|
if (typeof urls === 'string') {
|
|
3075
|
-
urls = [urls]
|
|
3209
|
+
urls = [urls]
|
|
3076
3210
|
}
|
|
3077
3211
|
|
|
3078
3212
|
urls.forEach((url) => {
|
|
@@ -3080,15 +3214,15 @@ class Playwright extends Helper {
|
|
|
3080
3214
|
if (this.page.isClosed()) {
|
|
3081
3215
|
// Sometimes it happens that browser has been closed in the meantime.
|
|
3082
3216
|
// In this case we just don't fulfill to prevent error in test scenario.
|
|
3083
|
-
return
|
|
3217
|
+
return
|
|
3084
3218
|
}
|
|
3085
3219
|
route.fulfill({
|
|
3086
3220
|
contentType,
|
|
3087
3221
|
headers,
|
|
3088
3222
|
body: responseString,
|
|
3089
|
-
})
|
|
3090
|
-
})
|
|
3091
|
-
})
|
|
3223
|
+
})
|
|
3224
|
+
})
|
|
3225
|
+
})
|
|
3092
3226
|
}
|
|
3093
3227
|
|
|
3094
3228
|
/**
|
|
@@ -3096,7 +3230,7 @@ class Playwright extends Helper {
|
|
|
3096
3230
|
* {{> flushNetworkTraffics }}
|
|
3097
3231
|
*/
|
|
3098
3232
|
flushNetworkTraffics() {
|
|
3099
|
-
flushNetworkTraffics.call(this)
|
|
3233
|
+
flushNetworkTraffics.call(this)
|
|
3100
3234
|
}
|
|
3101
3235
|
|
|
3102
3236
|
/**
|
|
@@ -3104,14 +3238,12 @@ class Playwright extends Helper {
|
|
|
3104
3238
|
* {{> stopRecordingTraffic }}
|
|
3105
3239
|
*/
|
|
3106
3240
|
stopRecordingTraffic() {
|
|
3107
|
-
stopRecordingTraffic.call(this)
|
|
3241
|
+
stopRecordingTraffic.call(this)
|
|
3108
3242
|
}
|
|
3109
3243
|
|
|
3110
3244
|
/**
|
|
3111
3245
|
* Returns full URL of request matching parameter "urlMatch".
|
|
3112
3246
|
*
|
|
3113
|
-
* @param {string|RegExp} urlMatch Expected URL of request in network traffic. Can be a string or a regular expression.
|
|
3114
|
-
*
|
|
3115
3247
|
* Examples:
|
|
3116
3248
|
*
|
|
3117
3249
|
* ```js
|
|
@@ -3119,25 +3251,28 @@ class Playwright extends Helper {
|
|
|
3119
3251
|
* I.grabTrafficUrl(/session.*start/);
|
|
3120
3252
|
* ```
|
|
3121
3253
|
*
|
|
3254
|
+
* @param {string|RegExp} urlMatch Expected URL of request in network traffic. Can be a string or a regular expression.
|
|
3122
3255
|
* @return {Promise<*>}
|
|
3123
3256
|
*/
|
|
3124
3257
|
grabTrafficUrl(urlMatch) {
|
|
3125
3258
|
if (!this.recordedAtLeastOnce) {
|
|
3126
|
-
throw new Error(
|
|
3259
|
+
throw new Error(
|
|
3260
|
+
'Failure in test automation. You use "I.grabTrafficUrl", but "I.startRecordingTraffic" was never called before.',
|
|
3261
|
+
)
|
|
3127
3262
|
}
|
|
3128
3263
|
|
|
3129
3264
|
for (const i in this.requests) {
|
|
3130
3265
|
// eslint-disable-next-line no-prototype-builtins
|
|
3131
3266
|
if (this.requests.hasOwnProperty(i)) {
|
|
3132
|
-
const request = this.requests[i]
|
|
3267
|
+
const request = this.requests[i]
|
|
3133
3268
|
|
|
3134
3269
|
if (request.url && request.url.match(new RegExp(urlMatch))) {
|
|
3135
|
-
return request.url
|
|
3270
|
+
return request.url
|
|
3136
3271
|
}
|
|
3137
3272
|
}
|
|
3138
3273
|
}
|
|
3139
3274
|
|
|
3140
|
-
assert.fail(`Method "getTrafficUrl" failed: No request found in traffic that matches ${urlMatch}`)
|
|
3275
|
+
assert.fail(`Method "getTrafficUrl" failed: No request found in traffic that matches ${urlMatch}`)
|
|
3141
3276
|
}
|
|
3142
3277
|
|
|
3143
3278
|
/**
|
|
@@ -3145,17 +3280,15 @@ class Playwright extends Helper {
|
|
|
3145
3280
|
* {{> grabRecordedNetworkTraffics }}
|
|
3146
3281
|
*/
|
|
3147
3282
|
async grabRecordedNetworkTraffics() {
|
|
3148
|
-
return grabRecordedNetworkTraffics.call(this)
|
|
3283
|
+
return grabRecordedNetworkTraffics.call(this)
|
|
3149
3284
|
}
|
|
3150
3285
|
|
|
3151
3286
|
/**
|
|
3152
3287
|
*
|
|
3153
3288
|
* {{> seeTraffic }}
|
|
3154
3289
|
*/
|
|
3155
|
-
async seeTraffic({
|
|
3156
|
-
|
|
3157
|
-
}) {
|
|
3158
|
-
await seeTraffic.call(this, ...arguments);
|
|
3290
|
+
async seeTraffic({ name, url, parameters, requestPostData, timeout = 10 }) {
|
|
3291
|
+
await seeTraffic.call(this, ...arguments)
|
|
3159
3292
|
}
|
|
3160
3293
|
|
|
3161
3294
|
/**
|
|
@@ -3164,51 +3297,42 @@ class Playwright extends Helper {
|
|
|
3164
3297
|
*
|
|
3165
3298
|
*/
|
|
3166
3299
|
dontSeeTraffic({ name, url }) {
|
|
3167
|
-
dontSeeTraffic.call(this, ...arguments)
|
|
3300
|
+
dontSeeTraffic.call(this, ...arguments)
|
|
3168
3301
|
}
|
|
3169
3302
|
|
|
3170
3303
|
/**
|
|
3171
3304
|
* {{> startRecordingWebSocketMessages }}
|
|
3172
3305
|
*/
|
|
3173
3306
|
async startRecordingWebSocketMessages() {
|
|
3174
|
-
this.flushWebSocketMessages()
|
|
3175
|
-
this.recordingWebSocketMessages = true
|
|
3176
|
-
this.recordedWebSocketMessagesAtLeastOnce = true
|
|
3177
|
-
|
|
3178
|
-
this.cdpSession = await this.getNewCDPSession();
|
|
3179
|
-
await this.cdpSession.send('Network.enable');
|
|
3180
|
-
await this.cdpSession.send('Page.enable');
|
|
3181
|
-
|
|
3182
|
-
this.cdpSession.on(
|
|
3183
|
-
'Network.webSocketFrameReceived',
|
|
3184
|
-
(payload) => {
|
|
3185
|
-
this._logWebsocketMessages(this._getWebSocketLog('RECEIVED', payload));
|
|
3186
|
-
},
|
|
3187
|
-
);
|
|
3307
|
+
this.flushWebSocketMessages()
|
|
3308
|
+
this.recordingWebSocketMessages = true
|
|
3309
|
+
this.recordedWebSocketMessagesAtLeastOnce = true
|
|
3188
3310
|
|
|
3189
|
-
this.cdpSession.
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload));
|
|
3193
|
-
},
|
|
3194
|
-
);
|
|
3311
|
+
this.cdpSession = await this.getNewCDPSession()
|
|
3312
|
+
await this.cdpSession.send('Network.enable')
|
|
3313
|
+
await this.cdpSession.send('Page.enable')
|
|
3195
3314
|
|
|
3196
|
-
this.cdpSession.on(
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3315
|
+
this.cdpSession.on('Network.webSocketFrameReceived', (payload) => {
|
|
3316
|
+
this._logWebsocketMessages(this._getWebSocketLog('RECEIVED', payload))
|
|
3317
|
+
})
|
|
3318
|
+
|
|
3319
|
+
this.cdpSession.on('Network.webSocketFrameSent', (payload) => {
|
|
3320
|
+
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload))
|
|
3321
|
+
})
|
|
3322
|
+
|
|
3323
|
+
this.cdpSession.on('Network.webSocketFrameError', (payload) => {
|
|
3324
|
+
this._logWebsocketMessages(this._getWebSocketLog('ERROR', payload))
|
|
3325
|
+
})
|
|
3202
3326
|
}
|
|
3203
3327
|
|
|
3204
3328
|
/**
|
|
3205
3329
|
* {{> stopRecordingWebSocketMessages }}
|
|
3206
3330
|
*/
|
|
3207
3331
|
async stopRecordingWebSocketMessages() {
|
|
3208
|
-
await this.cdpSession.send('Network.disable')
|
|
3209
|
-
await this.cdpSession.send('Page.disable')
|
|
3210
|
-
this.page.removeAllListeners('Network')
|
|
3211
|
-
this.recordingWebSocketMessages = false
|
|
3332
|
+
await this.cdpSession.send('Network.disable')
|
|
3333
|
+
await this.cdpSession.send('Page.disable')
|
|
3334
|
+
this.page.removeAllListeners('Network')
|
|
3335
|
+
this.recordingWebSocketMessages = false
|
|
3212
3336
|
}
|
|
3213
3337
|
|
|
3214
3338
|
/**
|
|
@@ -3220,17 +3344,19 @@ class Playwright extends Helper {
|
|
|
3220
3344
|
grabWebSocketMessages() {
|
|
3221
3345
|
if (!this.recordingWebSocketMessages) {
|
|
3222
3346
|
if (!this.recordedWebSocketMessagesAtLeastOnce) {
|
|
3223
|
-
throw new Error(
|
|
3347
|
+
throw new Error(
|
|
3348
|
+
'Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.',
|
|
3349
|
+
)
|
|
3224
3350
|
}
|
|
3225
3351
|
}
|
|
3226
|
-
return this.webSocketMessages
|
|
3352
|
+
return this.webSocketMessages
|
|
3227
3353
|
}
|
|
3228
3354
|
|
|
3229
3355
|
/**
|
|
3230
3356
|
* Resets all recorded WS messages.
|
|
3231
3357
|
*/
|
|
3232
3358
|
flushWebSocketMessages() {
|
|
3233
|
-
this.webSocketMessages = []
|
|
3359
|
+
this.webSocketMessages = []
|
|
3234
3360
|
}
|
|
3235
3361
|
|
|
3236
3362
|
/**
|
|
@@ -3288,426 +3414,443 @@ class Playwright extends Helper {
|
|
|
3288
3414
|
* @return {Promise<Array<Object>>}
|
|
3289
3415
|
*/
|
|
3290
3416
|
async grabMetrics() {
|
|
3291
|
-
const client = await this.page.context().newCDPSession(this.page)
|
|
3292
|
-
await client.send('Performance.enable')
|
|
3293
|
-
const perfMetricObject = await client.send('Performance.getMetrics')
|
|
3294
|
-
return perfMetricObject?.metrics
|
|
3417
|
+
const client = await this.page.context().newCDPSession(this.page)
|
|
3418
|
+
await client.send('Performance.enable')
|
|
3419
|
+
const perfMetricObject = await client.send('Performance.getMetrics')
|
|
3420
|
+
return perfMetricObject?.metrics
|
|
3295
3421
|
}
|
|
3296
3422
|
|
|
3297
3423
|
_getWebSocketMessage(payload) {
|
|
3298
3424
|
if (payload.errorMessage) {
|
|
3299
|
-
return payload.errorMessage
|
|
3425
|
+
return payload.errorMessage
|
|
3300
3426
|
}
|
|
3301
3427
|
|
|
3302
|
-
return payload.response.payloadData
|
|
3428
|
+
return payload.response.payloadData
|
|
3303
3429
|
}
|
|
3304
3430
|
|
|
3305
3431
|
_getWebSocketLog(prefix, payload) {
|
|
3306
|
-
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n
|
|
3432
|
+
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n`
|
|
3307
3433
|
}
|
|
3308
3434
|
|
|
3309
3435
|
async getNewCDPSession() {
|
|
3310
|
-
return this.page.context().newCDPSession(this.page)
|
|
3436
|
+
return this.page.context().newCDPSession(this.page)
|
|
3311
3437
|
}
|
|
3312
3438
|
|
|
3313
3439
|
_logWebsocketMessages(message) {
|
|
3314
|
-
this.webSocketMessages
|
|
3440
|
+
this.webSocketMessages.push(message)
|
|
3315
3441
|
}
|
|
3316
3442
|
}
|
|
3317
3443
|
|
|
3318
|
-
|
|
3444
|
+
module.exports = Playwright
|
|
3319
3445
|
|
|
3320
3446
|
function buildLocatorString(locator) {
|
|
3321
3447
|
if (locator.isCustom()) {
|
|
3322
|
-
return `${locator.type}=${locator.value}
|
|
3323
|
-
} if (locator.isXPath()) {
|
|
3324
|
-
return `xpath=${locator.value}`;
|
|
3448
|
+
return `${locator.type}=${locator.value}`
|
|
3325
3449
|
}
|
|
3326
|
-
|
|
3450
|
+
if (locator.isXPath()) {
|
|
3451
|
+
return `xpath=${locator.value}`
|
|
3452
|
+
}
|
|
3453
|
+
return locator.simplify()
|
|
3327
3454
|
}
|
|
3328
3455
|
|
|
3329
3456
|
async function findElements(matcher, locator) {
|
|
3330
|
-
if (locator.react) return findReact(matcher, locator)
|
|
3331
|
-
if (locator.vue) return findVue(matcher, locator)
|
|
3332
|
-
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3333
|
-
locator = new Locator(locator, 'css')
|
|
3457
|
+
if (locator.react) return findReact(matcher, locator)
|
|
3458
|
+
if (locator.vue) return findVue(matcher, locator)
|
|
3459
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3460
|
+
locator = new Locator(locator, 'css')
|
|
3334
3461
|
|
|
3335
|
-
return matcher.locator(buildLocatorString(locator)).all()
|
|
3462
|
+
return matcher.locator(buildLocatorString(locator)).all()
|
|
3336
3463
|
}
|
|
3337
3464
|
|
|
3338
3465
|
async function findElement(matcher, locator) {
|
|
3339
|
-
if (locator.react) return findReact(matcher, locator)
|
|
3340
|
-
if (locator.vue) return findVue(matcher, locator)
|
|
3341
|
-
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3342
|
-
locator = new Locator(locator, 'css')
|
|
3466
|
+
if (locator.react) return findReact(matcher, locator)
|
|
3467
|
+
if (locator.vue) return findVue(matcher, locator)
|
|
3468
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3469
|
+
locator = new Locator(locator, 'css')
|
|
3343
3470
|
|
|
3344
|
-
return matcher.locator(buildLocatorString(locator)).first()
|
|
3471
|
+
return matcher.locator(buildLocatorString(locator)).first()
|
|
3345
3472
|
}
|
|
3346
3473
|
|
|
3347
3474
|
async function getVisibleElements(elements) {
|
|
3348
|
-
const visibleElements = []
|
|
3475
|
+
const visibleElements = []
|
|
3349
3476
|
for (const element of elements) {
|
|
3350
3477
|
if (await element.isVisible()) {
|
|
3351
|
-
visibleElements.push(element)
|
|
3478
|
+
visibleElements.push(element)
|
|
3352
3479
|
}
|
|
3353
3480
|
}
|
|
3354
3481
|
if (visibleElements.length === 0) {
|
|
3355
|
-
return elements
|
|
3482
|
+
return elements
|
|
3356
3483
|
}
|
|
3357
|
-
return visibleElements
|
|
3484
|
+
return visibleElements
|
|
3358
3485
|
}
|
|
3359
3486
|
|
|
3360
3487
|
async function proceedClick(locator, context = null, options = {}) {
|
|
3361
|
-
let matcher = await this._getContext()
|
|
3488
|
+
let matcher = await this._getContext()
|
|
3362
3489
|
if (context) {
|
|
3363
|
-
const els = await this._locate(context)
|
|
3364
|
-
assertElementExists(els, context)
|
|
3365
|
-
matcher = els[0]
|
|
3490
|
+
const els = await this._locate(context)
|
|
3491
|
+
assertElementExists(els, context)
|
|
3492
|
+
matcher = els[0]
|
|
3366
3493
|
}
|
|
3367
|
-
const els = await findClickable.call(this, matcher, locator)
|
|
3494
|
+
const els = await findClickable.call(this, matcher, locator)
|
|
3368
3495
|
if (context) {
|
|
3369
|
-
assertElementExists(
|
|
3496
|
+
assertElementExists(
|
|
3497
|
+
els,
|
|
3498
|
+
locator,
|
|
3499
|
+
'Clickable element',
|
|
3500
|
+
`was not found inside element ${new Locator(context).toString()}`,
|
|
3501
|
+
)
|
|
3370
3502
|
} else {
|
|
3371
|
-
assertElementExists(els, locator, 'Clickable element')
|
|
3503
|
+
assertElementExists(els, locator, 'Clickable element')
|
|
3372
3504
|
}
|
|
3373
3505
|
|
|
3374
|
-
await highlightActiveElement.call(this, els[0])
|
|
3506
|
+
await highlightActiveElement.call(this, els[0])
|
|
3375
3507
|
|
|
3376
3508
|
/*
|
|
3377
3509
|
using the force true options itself but instead dispatching a click
|
|
3378
3510
|
*/
|
|
3379
3511
|
if (options.force) {
|
|
3380
|
-
await els[0].dispatchEvent('click')
|
|
3512
|
+
await els[0].dispatchEvent('click')
|
|
3381
3513
|
} else {
|
|
3382
|
-
const element = els.length > 1 ? (await getVisibleElements(els))[0] : els[0]
|
|
3383
|
-
await element.click(options)
|
|
3514
|
+
const element = els.length > 1 ? (await getVisibleElements(els))[0] : els[0]
|
|
3515
|
+
await element.click(options)
|
|
3384
3516
|
}
|
|
3385
|
-
const promises = []
|
|
3517
|
+
const promises = []
|
|
3386
3518
|
if (options.waitForNavigation) {
|
|
3387
|
-
promises.push(this.waitForURL(/.*/, { waitUntil: options.waitForNavigation }))
|
|
3519
|
+
promises.push(this.waitForURL(/.*/, { waitUntil: options.waitForNavigation }))
|
|
3388
3520
|
}
|
|
3389
|
-
promises.push(this._waitForAction())
|
|
3521
|
+
promises.push(this._waitForAction())
|
|
3390
3522
|
|
|
3391
|
-
return Promise.all(promises)
|
|
3523
|
+
return Promise.all(promises)
|
|
3392
3524
|
}
|
|
3393
3525
|
|
|
3394
3526
|
async function findClickable(matcher, locator) {
|
|
3395
|
-
if (locator.react) return findReact(matcher, locator)
|
|
3396
|
-
if (locator.vue) return findVue(matcher, locator)
|
|
3397
|
-
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3527
|
+
if (locator.react) return findReact(matcher, locator)
|
|
3528
|
+
if (locator.vue) return findVue(matcher, locator)
|
|
3529
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3398
3530
|
|
|
3399
|
-
locator = new Locator(locator)
|
|
3400
|
-
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator)
|
|
3531
|
+
locator = new Locator(locator)
|
|
3532
|
+
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator)
|
|
3401
3533
|
|
|
3402
|
-
let els
|
|
3403
|
-
const literal = xpathLocator.literal(locator.value)
|
|
3534
|
+
let els
|
|
3535
|
+
const literal = xpathLocator.literal(locator.value)
|
|
3404
3536
|
|
|
3405
|
-
els = await findElements.call(this, matcher, Locator.clickable.narrow(literal))
|
|
3406
|
-
if (els.length) return els
|
|
3537
|
+
els = await findElements.call(this, matcher, Locator.clickable.narrow(literal))
|
|
3538
|
+
if (els.length) return els
|
|
3407
3539
|
|
|
3408
|
-
els = await findElements.call(this, matcher, Locator.clickable.wide(literal))
|
|
3409
|
-
if (els.length) return els
|
|
3540
|
+
els = await findElements.call(this, matcher, Locator.clickable.wide(literal))
|
|
3541
|
+
if (els.length) return els
|
|
3410
3542
|
|
|
3411
3543
|
try {
|
|
3412
|
-
els = await findElements.call(this, matcher, Locator.clickable.self(literal))
|
|
3413
|
-
if (els.length) return els
|
|
3544
|
+
els = await findElements.call(this, matcher, Locator.clickable.self(literal))
|
|
3545
|
+
if (els.length) return els
|
|
3414
3546
|
} catch (err) {
|
|
3415
3547
|
// Do nothing
|
|
3416
3548
|
}
|
|
3417
3549
|
|
|
3418
|
-
return findElements.call(this, matcher, locator.value)
|
|
3550
|
+
return findElements.call(this, matcher, locator.value) // by css or xpath
|
|
3419
3551
|
}
|
|
3420
3552
|
|
|
3421
3553
|
async function proceedSee(assertType, text, context, strict = false) {
|
|
3422
|
-
let description
|
|
3423
|
-
let allText
|
|
3554
|
+
let description
|
|
3555
|
+
let allText
|
|
3424
3556
|
|
|
3425
3557
|
if (!context) {
|
|
3426
|
-
const el = await this.context
|
|
3558
|
+
const el = await this.context
|
|
3427
3559
|
|
|
3428
|
-
allText = el.constructor.name !== 'Locator' ? [await el.locator('body').innerText()] : [await el.innerText()]
|
|
3560
|
+
allText = el.constructor.name !== 'Locator' ? [await el.locator('body').innerText()] : [await el.innerText()]
|
|
3429
3561
|
|
|
3430
|
-
description = 'web application'
|
|
3562
|
+
description = 'web application'
|
|
3431
3563
|
} else {
|
|
3432
|
-
const locator = new Locator(context, 'css')
|
|
3433
|
-
description = `element ${locator.toString()}
|
|
3434
|
-
const els = await this._locate(locator)
|
|
3435
|
-
assertElementExists(els, locator.toString())
|
|
3436
|
-
allText = await Promise.all(els.map(el => el.innerText()))
|
|
3564
|
+
const locator = new Locator(context, 'css')
|
|
3565
|
+
description = `element ${locator.toString()}`
|
|
3566
|
+
const els = await this._locate(locator)
|
|
3567
|
+
assertElementExists(els, locator.toString())
|
|
3568
|
+
allText = await Promise.all(els.map((el) => el.innerText()))
|
|
3437
3569
|
}
|
|
3438
3570
|
|
|
3439
3571
|
if (strict) {
|
|
3440
|
-
return allText.map(elText => equals(description)[assertType](text, elText))
|
|
3572
|
+
return allText.map((elText) => equals(description)[assertType](text, elText))
|
|
3441
3573
|
}
|
|
3442
|
-
return stringIncludes(description)[assertType](
|
|
3574
|
+
return stringIncludes(description)[assertType](
|
|
3575
|
+
normalizeSpacesInString(text),
|
|
3576
|
+
normalizeSpacesInString(allText.join(' | ')),
|
|
3577
|
+
)
|
|
3443
3578
|
}
|
|
3444
3579
|
|
|
3445
3580
|
async function findCheckable(locator, context) {
|
|
3446
|
-
let contextEl = await this.context
|
|
3581
|
+
let contextEl = await this.context
|
|
3447
3582
|
if (typeof context === 'string') {
|
|
3448
|
-
contextEl = await findElements.call(this, contextEl,
|
|
3449
|
-
contextEl = contextEl[0]
|
|
3583
|
+
contextEl = await findElements.call(this, contextEl, new Locator(context, 'css').simplify())
|
|
3584
|
+
contextEl = contextEl[0]
|
|
3450
3585
|
}
|
|
3451
3586
|
|
|
3452
|
-
const matchedLocator = new Locator(locator)
|
|
3587
|
+
const matchedLocator = new Locator(locator)
|
|
3453
3588
|
if (!matchedLocator.isFuzzy()) {
|
|
3454
|
-
return findElements.call(this, contextEl, matchedLocator.simplify())
|
|
3589
|
+
return findElements.call(this, contextEl, matchedLocator.simplify())
|
|
3455
3590
|
}
|
|
3456
3591
|
|
|
3457
|
-
const literal = xpathLocator.literal(locator)
|
|
3458
|
-
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal))
|
|
3592
|
+
const literal = xpathLocator.literal(locator)
|
|
3593
|
+
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal))
|
|
3459
3594
|
if (els.length) {
|
|
3460
|
-
return els
|
|
3595
|
+
return els
|
|
3461
3596
|
}
|
|
3462
|
-
els = await findElements.call(this, contextEl, Locator.checkable.byName(literal))
|
|
3597
|
+
els = await findElements.call(this, contextEl, Locator.checkable.byName(literal))
|
|
3463
3598
|
if (els.length) {
|
|
3464
|
-
return els
|
|
3599
|
+
return els
|
|
3465
3600
|
}
|
|
3466
|
-
return findElements.call(this, contextEl, locator)
|
|
3601
|
+
return findElements.call(this, contextEl, locator)
|
|
3467
3602
|
}
|
|
3468
3603
|
|
|
3469
3604
|
async function proceedIsChecked(assertType, option) {
|
|
3470
|
-
let els = await findCheckable.call(this, option)
|
|
3471
|
-
assertElementExists(els, option, 'Checkable')
|
|
3472
|
-
els = await Promise.all(els.map(el => el.isChecked()))
|
|
3473
|
-
const selected = els.reduce((prev, cur) => prev || cur)
|
|
3474
|
-
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
3605
|
+
let els = await findCheckable.call(this, option)
|
|
3606
|
+
assertElementExists(els, option, 'Checkable')
|
|
3607
|
+
els = await Promise.all(els.map((el) => el.isChecked()))
|
|
3608
|
+
const selected = els.reduce((prev, cur) => prev || cur)
|
|
3609
|
+
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
3475
3610
|
}
|
|
3476
3611
|
|
|
3477
3612
|
async function findFields(locator) {
|
|
3478
|
-
const matchedLocator = new Locator(locator)
|
|
3613
|
+
const matchedLocator = new Locator(locator)
|
|
3479
3614
|
if (!matchedLocator.isFuzzy()) {
|
|
3480
|
-
return this._locate(matchedLocator)
|
|
3615
|
+
return this._locate(matchedLocator)
|
|
3481
3616
|
}
|
|
3482
|
-
const literal = xpathLocator.literal(locator)
|
|
3617
|
+
const literal = xpathLocator.literal(locator)
|
|
3483
3618
|
|
|
3484
|
-
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) })
|
|
3619
|
+
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) })
|
|
3485
3620
|
if (els.length) {
|
|
3486
|
-
return els
|
|
3621
|
+
return els
|
|
3487
3622
|
}
|
|
3488
3623
|
|
|
3489
|
-
els = await this._locate({ xpath: Locator.field.labelContains(literal) })
|
|
3624
|
+
els = await this._locate({ xpath: Locator.field.labelContains(literal) })
|
|
3490
3625
|
if (els.length) {
|
|
3491
|
-
return els
|
|
3626
|
+
return els
|
|
3492
3627
|
}
|
|
3493
|
-
els = await this._locate({ xpath: Locator.field.byName(literal) })
|
|
3628
|
+
els = await this._locate({ xpath: Locator.field.byName(literal) })
|
|
3494
3629
|
if (els.length) {
|
|
3495
|
-
return els
|
|
3630
|
+
return els
|
|
3496
3631
|
}
|
|
3497
|
-
return this._locate({ css: locator })
|
|
3632
|
+
return this._locate({ css: locator })
|
|
3498
3633
|
}
|
|
3499
3634
|
|
|
3500
3635
|
async function proceedSeeInField(assertType, field, value) {
|
|
3501
|
-
const els = await findFields.call(this, field)
|
|
3502
|
-
assertElementExists(els, field, 'Field')
|
|
3503
|
-
const el = els[0]
|
|
3504
|
-
const tag = await el.evaluate(e => e.tagName)
|
|
3505
|
-
const fieldType = await el.getAttribute('type')
|
|
3636
|
+
const els = await findFields.call(this, field)
|
|
3637
|
+
assertElementExists(els, field, 'Field')
|
|
3638
|
+
const el = els[0]
|
|
3639
|
+
const tag = await el.evaluate((e) => e.tagName)
|
|
3640
|
+
const fieldType = await el.getAttribute('type')
|
|
3506
3641
|
|
|
3507
3642
|
const proceedMultiple = async (elements) => {
|
|
3508
|
-
const fields = Array.isArray(elements) ? elements : [elements]
|
|
3643
|
+
const fields = Array.isArray(elements) ? elements : [elements]
|
|
3509
3644
|
|
|
3510
|
-
const elementValues = []
|
|
3645
|
+
const elementValues = []
|
|
3511
3646
|
for (const element of fields) {
|
|
3512
|
-
elementValues.push(await element.inputValue())
|
|
3647
|
+
elementValues.push(await element.inputValue())
|
|
3513
3648
|
}
|
|
3514
3649
|
|
|
3515
3650
|
if (typeof value === 'boolean') {
|
|
3516
|
-
equals(`no. of items matching > 0: ${field}`)[assertType](value, !!elementValues.length)
|
|
3651
|
+
equals(`no. of items matching > 0: ${field}`)[assertType](value, !!elementValues.length)
|
|
3517
3652
|
} else {
|
|
3518
3653
|
if (assertType === 'assert') {
|
|
3519
|
-
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0)
|
|
3654
|
+
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0)
|
|
3520
3655
|
}
|
|
3521
|
-
elementValues.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val))
|
|
3656
|
+
elementValues.forEach((val) => stringIncludes(`fields by ${field}`)[assertType](value, val))
|
|
3522
3657
|
}
|
|
3523
|
-
}
|
|
3658
|
+
}
|
|
3524
3659
|
|
|
3525
3660
|
if (tag === 'SELECT') {
|
|
3526
3661
|
if (await el.getAttribute('multiple')) {
|
|
3527
|
-
const selectedOptions = await el.all('option:checked')
|
|
3528
|
-
if (!selectedOptions.length) return null
|
|
3662
|
+
const selectedOptions = await el.all('option:checked')
|
|
3663
|
+
if (!selectedOptions.length) return null
|
|
3529
3664
|
|
|
3530
|
-
const options = await filterFieldsByValue(selectedOptions, value, true)
|
|
3531
|
-
return proceedMultiple(options)
|
|
3665
|
+
const options = await filterFieldsByValue(selectedOptions, value, true)
|
|
3666
|
+
return proceedMultiple(options)
|
|
3532
3667
|
}
|
|
3533
3668
|
|
|
3534
|
-
return el.inputValue()
|
|
3669
|
+
return el.inputValue()
|
|
3535
3670
|
}
|
|
3536
3671
|
|
|
3537
3672
|
if (tag === 'INPUT') {
|
|
3538
3673
|
if (fieldType === 'checkbox' || fieldType === 'radio') {
|
|
3539
3674
|
if (typeof value === 'boolean') {
|
|
3540
3675
|
// Filter by values
|
|
3541
|
-
const options = await filterFieldsBySelectionState(els, true)
|
|
3542
|
-
return proceedMultiple(options)
|
|
3676
|
+
const options = await filterFieldsBySelectionState(els, true)
|
|
3677
|
+
return proceedMultiple(options)
|
|
3543
3678
|
}
|
|
3544
3679
|
|
|
3545
|
-
const options = await filterFieldsByValue(els, value, true)
|
|
3546
|
-
return proceedMultiple(options)
|
|
3680
|
+
const options = await filterFieldsByValue(els, value, true)
|
|
3681
|
+
return proceedMultiple(options)
|
|
3547
3682
|
}
|
|
3548
|
-
return proceedMultiple(els[0])
|
|
3683
|
+
return proceedMultiple(els[0])
|
|
3549
3684
|
}
|
|
3550
3685
|
|
|
3551
|
-
let fieldVal
|
|
3686
|
+
let fieldVal
|
|
3552
3687
|
|
|
3553
3688
|
try {
|
|
3554
|
-
fieldVal = await el.inputValue()
|
|
3689
|
+
fieldVal = await el.inputValue()
|
|
3555
3690
|
} catch (e) {
|
|
3556
3691
|
if (e.message.includes('Error: Node is not an <input>, <textarea> or <select> element')) {
|
|
3557
|
-
fieldVal = await el.innerText()
|
|
3692
|
+
fieldVal = await el.innerText()
|
|
3558
3693
|
}
|
|
3559
3694
|
}
|
|
3560
3695
|
|
|
3561
|
-
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal)
|
|
3696
|
+
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal)
|
|
3562
3697
|
}
|
|
3563
3698
|
|
|
3564
3699
|
async function filterFieldsByValue(elements, value, onlySelected) {
|
|
3565
|
-
const matches = []
|
|
3700
|
+
const matches = []
|
|
3566
3701
|
for (const element of elements) {
|
|
3567
|
-
const val = await element.getAttribute('value')
|
|
3568
|
-
let isSelected = true
|
|
3702
|
+
const val = await element.getAttribute('value')
|
|
3703
|
+
let isSelected = true
|
|
3569
3704
|
if (onlySelected) {
|
|
3570
|
-
isSelected = await elementSelected(element)
|
|
3705
|
+
isSelected = await elementSelected(element)
|
|
3571
3706
|
}
|
|
3572
3707
|
if ((value == null || val.indexOf(value) > -1) && isSelected) {
|
|
3573
|
-
matches.push(element)
|
|
3708
|
+
matches.push(element)
|
|
3574
3709
|
}
|
|
3575
3710
|
}
|
|
3576
|
-
return matches
|
|
3711
|
+
return matches
|
|
3577
3712
|
}
|
|
3578
3713
|
|
|
3579
3714
|
async function filterFieldsBySelectionState(elements, state) {
|
|
3580
|
-
const matches = []
|
|
3715
|
+
const matches = []
|
|
3581
3716
|
for (const element of elements) {
|
|
3582
|
-
const isSelected = await elementSelected(element)
|
|
3717
|
+
const isSelected = await elementSelected(element)
|
|
3583
3718
|
if (isSelected === state) {
|
|
3584
|
-
matches.push(element)
|
|
3719
|
+
matches.push(element)
|
|
3585
3720
|
}
|
|
3586
3721
|
}
|
|
3587
|
-
return matches
|
|
3722
|
+
return matches
|
|
3588
3723
|
}
|
|
3589
3724
|
|
|
3590
3725
|
async function elementSelected(element) {
|
|
3591
|
-
const type = await element.getAttribute('type')
|
|
3726
|
+
const type = await element.getAttribute('type')
|
|
3592
3727
|
|
|
3593
3728
|
if (type === 'checkbox' || type === 'radio') {
|
|
3594
|
-
return element.isChecked()
|
|
3729
|
+
return element.isChecked()
|
|
3595
3730
|
}
|
|
3596
|
-
return element.getAttribute('selected')
|
|
3731
|
+
return element.getAttribute('selected')
|
|
3597
3732
|
}
|
|
3598
3733
|
|
|
3599
3734
|
function isFrameLocator(locator) {
|
|
3600
|
-
locator = new Locator(locator)
|
|
3735
|
+
locator = new Locator(locator)
|
|
3601
3736
|
if (locator.isFrame()) {
|
|
3602
|
-
return locator.value
|
|
3737
|
+
return locator.value
|
|
3603
3738
|
}
|
|
3604
|
-
return false
|
|
3739
|
+
return false
|
|
3605
3740
|
}
|
|
3606
3741
|
|
|
3607
3742
|
function assertElementExists(res, locator, prefix, suffix) {
|
|
3608
3743
|
if (!res || res.length === 0) {
|
|
3609
|
-
throw new ElementNotFound(locator, prefix, suffix)
|
|
3744
|
+
throw new ElementNotFound(locator, prefix, suffix)
|
|
3610
3745
|
}
|
|
3611
3746
|
}
|
|
3612
3747
|
|
|
3613
3748
|
function $XPath(element, selector) {
|
|
3614
|
-
const found = document.evaluate(selector, element || document.body, null, 5, null)
|
|
3615
|
-
const res = []
|
|
3616
|
-
let current = null
|
|
3617
|
-
while (current = found.iterateNext()) {
|
|
3618
|
-
res.push(current)
|
|
3749
|
+
const found = document.evaluate(selector, element || document.body, null, 5, null)
|
|
3750
|
+
const res = []
|
|
3751
|
+
let current = null
|
|
3752
|
+
while ((current = found.iterateNext())) {
|
|
3753
|
+
res.push(current)
|
|
3619
3754
|
}
|
|
3620
|
-
return res
|
|
3755
|
+
return res
|
|
3621
3756
|
}
|
|
3622
3757
|
|
|
3623
3758
|
async function targetCreatedHandler(page) {
|
|
3624
|
-
if (!page) return
|
|
3625
|
-
this.withinLocator = null
|
|
3759
|
+
if (!page) return
|
|
3760
|
+
this.withinLocator = null
|
|
3626
3761
|
page.on('load', () => {
|
|
3627
|
-
page
|
|
3762
|
+
page
|
|
3763
|
+
.$('body')
|
|
3628
3764
|
.catch(() => null)
|
|
3629
3765
|
.then(async () => {
|
|
3630
3766
|
if (this.context && this.context._type === 'Frame') {
|
|
3631
3767
|
// we are inside iframe?
|
|
3632
|
-
const frameEl = await this.context.frameElement()
|
|
3633
|
-
this.context = await frameEl.contentFrame()
|
|
3634
|
-
this.contextLocator = null
|
|
3635
|
-
return
|
|
3768
|
+
const frameEl = await this.context.frameElement()
|
|
3769
|
+
this.context = await frameEl.contentFrame()
|
|
3770
|
+
this.contextLocator = null
|
|
3771
|
+
return
|
|
3636
3772
|
}
|
|
3637
3773
|
// if context element was in iframe - keep it
|
|
3638
3774
|
// if (await this.context.ownerFrame()) return;
|
|
3639
|
-
this.context = page
|
|
3640
|
-
this.contextLocator = null
|
|
3641
|
-
})
|
|
3642
|
-
})
|
|
3775
|
+
this.context = page
|
|
3776
|
+
this.contextLocator = null
|
|
3777
|
+
})
|
|
3778
|
+
})
|
|
3643
3779
|
page.on('console', (msg) => {
|
|
3644
3780
|
if (!consoleLogStore.includes(msg) && this.options.ignoreLog && !this.options.ignoreLog.includes(msg.type())) {
|
|
3645
|
-
this.debugSection(
|
|
3781
|
+
this.debugSection(
|
|
3782
|
+
`Browser:${ucfirst(msg.type())}`,
|
|
3783
|
+
((msg.text && msg.text()) || msg._text || '') + msg.args().join(' '),
|
|
3784
|
+
)
|
|
3646
3785
|
}
|
|
3647
|
-
consoleLogStore.add(msg)
|
|
3648
|
-
})
|
|
3786
|
+
consoleLogStore.add(msg)
|
|
3787
|
+
})
|
|
3649
3788
|
|
|
3650
3789
|
if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0 && this._getType() === 'Browser') {
|
|
3651
3790
|
try {
|
|
3652
|
-
await page.setViewportSize(parseWindowSize(this.options.windowSize))
|
|
3791
|
+
await page.setViewportSize(parseWindowSize(this.options.windowSize))
|
|
3653
3792
|
} catch (err) {
|
|
3654
|
-
this.debug('Target can be already closed, ignoring...')
|
|
3793
|
+
this.debug('Target can be already closed, ignoring...')
|
|
3655
3794
|
}
|
|
3656
3795
|
}
|
|
3657
3796
|
}
|
|
3658
3797
|
|
|
3659
3798
|
function parseWindowSize(windowSize) {
|
|
3660
|
-
if (!windowSize) return { width: 800, height: 600 }
|
|
3661
|
-
|
|
3799
|
+
if (!windowSize) return { width: 800, height: 600 }
|
|
3800
|
+
|
|
3801
|
+
if (windowSize.width && windowSize.height) {
|
|
3802
|
+
return { width: parseInt(windowSize.width, 10), height: parseInt(windowSize.height, 10) }
|
|
3803
|
+
}
|
|
3804
|
+
|
|
3805
|
+
const dimensions = windowSize.split('x')
|
|
3662
3806
|
if (dimensions.length < 2 || windowSize === 'maximize') {
|
|
3663
|
-
console.log('Invalid window size, setting window to default values')
|
|
3664
|
-
return { width: 800, height: 600 }
|
|
3807
|
+
console.log('Invalid window size, setting window to default values')
|
|
3808
|
+
return { width: 800, height: 600 } // invalid size
|
|
3665
3809
|
}
|
|
3666
|
-
const width = parseInt(dimensions[0], 10)
|
|
3667
|
-
const height = parseInt(dimensions[1], 10)
|
|
3668
|
-
return { width, height }
|
|
3810
|
+
const width = parseInt(dimensions[0], 10)
|
|
3811
|
+
const height = parseInt(dimensions[1], 10)
|
|
3812
|
+
return { width, height }
|
|
3669
3813
|
}
|
|
3670
3814
|
|
|
3671
3815
|
// List of key values to key definitions
|
|
3672
3816
|
// https://github.com/puppeteer/puppeteer/blob/v1.20.0/lib/USKeyboardLayout.js
|
|
3673
3817
|
const keyDefinitionMap = {
|
|
3674
|
-
|
|
3675
|
-
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
'z': 'KeyZ',
|
|
3818
|
+
0: 'Digit0',
|
|
3819
|
+
1: 'Digit1',
|
|
3820
|
+
2: 'Digit2',
|
|
3821
|
+
3: 'Digit3',
|
|
3822
|
+
4: 'Digit4',
|
|
3823
|
+
5: 'Digit5',
|
|
3824
|
+
6: 'Digit6',
|
|
3825
|
+
7: 'Digit7',
|
|
3826
|
+
8: 'Digit8',
|
|
3827
|
+
9: 'Digit9',
|
|
3828
|
+
a: 'KeyA',
|
|
3829
|
+
b: 'KeyB',
|
|
3830
|
+
c: 'KeyC',
|
|
3831
|
+
d: 'KeyD',
|
|
3832
|
+
e: 'KeyE',
|
|
3833
|
+
f: 'KeyF',
|
|
3834
|
+
g: 'KeyG',
|
|
3835
|
+
h: 'KeyH',
|
|
3836
|
+
i: 'KeyI',
|
|
3837
|
+
j: 'KeyJ',
|
|
3838
|
+
k: 'KeyK',
|
|
3839
|
+
l: 'KeyL',
|
|
3840
|
+
m: 'KeyM',
|
|
3841
|
+
n: 'KeyN',
|
|
3842
|
+
o: 'KeyO',
|
|
3843
|
+
p: 'KeyP',
|
|
3844
|
+
q: 'KeyQ',
|
|
3845
|
+
r: 'KeyR',
|
|
3846
|
+
s: 'KeyS',
|
|
3847
|
+
t: 'KeyT',
|
|
3848
|
+
u: 'KeyU',
|
|
3849
|
+
v: 'KeyV',
|
|
3850
|
+
w: 'KeyW',
|
|
3851
|
+
x: 'KeyX',
|
|
3852
|
+
y: 'KeyY',
|
|
3853
|
+
z: 'KeyZ',
|
|
3711
3854
|
';': 'Semicolon',
|
|
3712
3855
|
'=': 'Equal',
|
|
3713
3856
|
',': 'Comma',
|
|
@@ -3718,89 +3861,91 @@ const keyDefinitionMap = {
|
|
|
3718
3861
|
'[': 'BracketLeft',
|
|
3719
3862
|
'\\': 'Backslash',
|
|
3720
3863
|
']': 'BracketRight',
|
|
3721
|
-
'
|
|
3722
|
-
|
|
3723
|
-
};
|
|
3864
|
+
"'": 'Quote',
|
|
3865
|
+
}
|
|
3724
3866
|
|
|
3725
3867
|
function getNormalizedKey(key) {
|
|
3726
|
-
const normalizedKey = getNormalizedKeyAttributeValue(key)
|
|
3868
|
+
const normalizedKey = getNormalizedKeyAttributeValue(key)
|
|
3727
3869
|
if (key !== normalizedKey) {
|
|
3728
|
-
this.debugSection('Input', `Mapping key '${key}' to '${normalizedKey}'`)
|
|
3870
|
+
this.debugSection('Input', `Mapping key '${key}' to '${normalizedKey}'`)
|
|
3729
3871
|
}
|
|
3730
3872
|
// Use key definition to ensure correct key is displayed when Shift modifier is active
|
|
3731
3873
|
if (Object.prototype.hasOwnProperty.call(keyDefinitionMap, normalizedKey)) {
|
|
3732
|
-
return keyDefinitionMap[normalizedKey]
|
|
3874
|
+
return keyDefinitionMap[normalizedKey]
|
|
3733
3875
|
}
|
|
3734
|
-
return normalizedKey
|
|
3876
|
+
return normalizedKey
|
|
3735
3877
|
}
|
|
3736
3878
|
|
|
3737
3879
|
async function clickablePoint(el) {
|
|
3738
|
-
const rect = await el.boundingBox()
|
|
3739
|
-
if (!rect) throw new ElementNotFound(el)
|
|
3740
|
-
const {
|
|
3741
|
-
|
|
3742
|
-
} = rect;
|
|
3743
|
-
return { x: x + width / 2, y: y + height / 2 };
|
|
3880
|
+
const rect = await el.boundingBox()
|
|
3881
|
+
if (!rect) throw new ElementNotFound(el)
|
|
3882
|
+
const { x, y, width, height } = rect
|
|
3883
|
+
return { x: x + width / 2, y: y + height / 2 }
|
|
3744
3884
|
}
|
|
3745
3885
|
|
|
3746
3886
|
async function refreshContextSession() {
|
|
3747
3887
|
// close other sessions
|
|
3748
3888
|
try {
|
|
3749
|
-
const contexts = await this.browser.contexts()
|
|
3750
|
-
contexts.shift()
|
|
3889
|
+
const contexts = await this.browser.contexts()
|
|
3890
|
+
contexts.shift()
|
|
3751
3891
|
|
|
3752
|
-
await Promise.all(contexts.map(c => c.close()))
|
|
3892
|
+
await Promise.all(contexts.map((c) => c.close()))
|
|
3753
3893
|
} catch (e) {
|
|
3754
|
-
console.log(e)
|
|
3894
|
+
console.log(e)
|
|
3755
3895
|
}
|
|
3756
3896
|
|
|
3757
3897
|
if (this.page) {
|
|
3758
|
-
const existingPages = await this.browserContext.pages()
|
|
3759
|
-
await this._setPage(existingPages[0])
|
|
3898
|
+
const existingPages = await this.browserContext.pages()
|
|
3899
|
+
await this._setPage(existingPages[0])
|
|
3760
3900
|
}
|
|
3761
3901
|
|
|
3762
|
-
if (this.options.keepBrowserState) return
|
|
3902
|
+
if (this.options.keepBrowserState) return
|
|
3763
3903
|
|
|
3764
3904
|
if (!this.options.keepCookies) {
|
|
3765
|
-
this.debugSection('Session', 'cleaning cookies and localStorage')
|
|
3766
|
-
await this.clearCookie()
|
|
3905
|
+
this.debugSection('Session', 'cleaning cookies and localStorage')
|
|
3906
|
+
await this.clearCookie()
|
|
3767
3907
|
}
|
|
3768
|
-
const currentUrl = await this.grabCurrentUrl()
|
|
3908
|
+
const currentUrl = await this.grabCurrentUrl()
|
|
3769
3909
|
|
|
3770
3910
|
if (currentUrl.startsWith('http')) {
|
|
3771
3911
|
await this.executeScript('localStorage.clear();').catch((err) => {
|
|
3772
|
-
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
3773
|
-
})
|
|
3912
|
+
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
3913
|
+
})
|
|
3774
3914
|
await this.executeScript('sessionStorage.clear();').catch((err) => {
|
|
3775
|
-
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
3776
|
-
})
|
|
3915
|
+
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
3916
|
+
})
|
|
3777
3917
|
}
|
|
3778
3918
|
}
|
|
3779
3919
|
|
|
3780
|
-
|
|
3781
|
-
if (!page.video()) return null
|
|
3782
|
-
const fileName = `${`${global.output_dir}${pathSeparator}videos${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.webm
|
|
3783
|
-
page
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3920
|
+
function saveVideoForPage(page, name) {
|
|
3921
|
+
if (!page.video()) return null
|
|
3922
|
+
const fileName = `${`${global.output_dir}${pathSeparator}videos${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.webm`
|
|
3923
|
+
page
|
|
3924
|
+
.video()
|
|
3925
|
+
.saveAs(fileName)
|
|
3926
|
+
.then(() => {
|
|
3927
|
+
if (!page) return
|
|
3928
|
+
page
|
|
3929
|
+
.video()
|
|
3930
|
+
.delete()
|
|
3931
|
+
.catch(() => {})
|
|
3932
|
+
})
|
|
3933
|
+
return fileName
|
|
3788
3934
|
}
|
|
3789
|
-
|
|
3790
3935
|
async function saveTraceForContext(context, name) {
|
|
3791
|
-
if (!context) return
|
|
3792
|
-
if (!context.tracing) return
|
|
3793
|
-
const fileName = `${`${global.output_dir}${pathSeparator}trace${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.zip
|
|
3794
|
-
await context.tracing.stop({ path: fileName })
|
|
3795
|
-
return fileName
|
|
3936
|
+
if (!context) return
|
|
3937
|
+
if (!context.tracing) return
|
|
3938
|
+
const fileName = `${`${global.output_dir}${pathSeparator}trace${pathSeparator}${uuidv4()}_${clearString(name)}`.slice(0, 245)}.zip`
|
|
3939
|
+
await context.tracing.stop({ path: fileName })
|
|
3940
|
+
return fileName
|
|
3796
3941
|
}
|
|
3797
3942
|
|
|
3798
3943
|
async function highlightActiveElement(element) {
|
|
3799
3944
|
if (this.options.highlightElement && global.debugMode) {
|
|
3800
|
-
await element.evaluate(el => {
|
|
3801
|
-
const prevStyle = el.style.boxShadow
|
|
3802
|
-
el.style.boxShadow = '0px 0px 4px 3px rgba(255, 0, 0, 0.7)'
|
|
3803
|
-
setTimeout(() => el.style.boxShadow = prevStyle, 2000)
|
|
3804
|
-
})
|
|
3945
|
+
await element.evaluate((el) => {
|
|
3946
|
+
const prevStyle = el.style.boxShadow
|
|
3947
|
+
el.style.boxShadow = '0px 0px 4px 3px rgba(255, 0, 0, 0.7)'
|
|
3948
|
+
setTimeout(() => (el.style.boxShadow = prevStyle), 2000)
|
|
3949
|
+
})
|
|
3805
3950
|
}
|
|
3806
3951
|
}
|