codeceptjs 4.0.0-beta.1 → 4.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/codecept.js +84 -81
- package/lib/actor.js +13 -13
- package/lib/ai.js +10 -13
- package/lib/assert/empty.js +20 -21
- package/lib/assert/equal.js +37 -39
- package/lib/assert/error.js +14 -14
- package/lib/assert/include.js +46 -47
- package/lib/assert/throws.js +13 -11
- package/lib/assert/truth.js +19 -22
- package/lib/assert.js +4 -2
- package/lib/cli.js +57 -49
- package/lib/codecept.js +142 -155
- package/lib/colorUtils.js +3 -3
- package/lib/command/configMigrate.js +58 -52
- package/lib/command/definitions.js +88 -89
- package/lib/command/dryRun.js +71 -68
- package/lib/command/generate.js +197 -188
- package/lib/command/gherkin/init.js +27 -16
- package/lib/command/gherkin/snippets.js +20 -20
- package/lib/command/gherkin/steps.js +8 -8
- package/lib/command/info.js +40 -38
- package/lib/command/init.js +290 -288
- package/lib/command/interactive.js +32 -32
- package/lib/command/list.js +26 -26
- package/lib/command/run-multiple/chunk.js +5 -5
- package/lib/command/run-multiple/collection.js +3 -3
- package/lib/command/run-multiple/run.js +6 -2
- package/lib/command/run-multiple.js +113 -93
- package/lib/command/run-rerun.js +20 -25
- package/lib/command/run-workers.js +64 -66
- package/lib/command/run.js +26 -29
- package/lib/command/utils.js +80 -65
- package/lib/command/workers/runTests.js +10 -10
- package/lib/config.js +10 -9
- package/lib/container.js +40 -48
- package/lib/data/context.js +60 -59
- package/lib/data/dataScenarioConfig.js +47 -47
- package/lib/data/dataTableArgument.js +29 -29
- package/lib/data/table.js +26 -20
- package/lib/event.js +163 -167
- package/lib/heal.js +13 -17
- package/lib/helper/AI.js +130 -41
- package/lib/helper/ApiDataFactory.js +73 -69
- package/lib/helper/Appium.js +413 -382
- package/lib/helper/ExpectHelper.js +40 -48
- package/lib/helper/FileSystem.js +80 -79
- package/lib/helper/GraphQL.js +44 -43
- package/lib/helper/GraphQLDataFactory.js +50 -50
- package/lib/helper/JSONResponse.js +65 -62
- package/lib/helper/Mochawesome.js +28 -28
- package/lib/helper/MockServer.js +12 -14
- package/lib/helper/Nightmare.js +662 -566
- package/lib/helper/Playwright.js +1361 -1216
- package/lib/helper/Protractor.js +663 -627
- package/lib/helper/Puppeteer.js +1231 -1128
- package/lib/helper/REST.js +159 -68
- package/lib/helper/SoftExpectHelper.js +2 -2
- package/lib/helper/TestCafe.js +490 -484
- package/lib/helper/WebDriver.js +1297 -1156
- package/lib/helper/clientscripts/PollyWebDriverExt.js +1 -1
- package/lib/helper/errors/ConnectionRefused.js +1 -1
- package/lib/helper/errors/ElementAssertion.js +2 -2
- package/lib/helper/errors/ElementNotFound.js +2 -2
- package/lib/helper/errors/RemoteBrowserConnectionRefused.js +1 -1
- package/lib/helper/extras/Console.js +1 -1
- package/lib/helper/extras/PlaywrightPropEngine.js +2 -2
- package/lib/helper/extras/PlaywrightReactVueLocator.js +1 -1
- package/lib/helper/extras/PlaywrightRestartOpts.js +21 -18
- package/lib/helper/extras/Popup.js +1 -1
- package/lib/helper/extras/React.js +3 -3
- package/lib/helper/network/actions.js +14 -7
- package/lib/helper/network/utils.js +3 -2
- package/lib/helper/scripts/blurElement.js +1 -1
- package/lib/helper/scripts/focusElement.js +1 -1
- package/lib/helper/scripts/highlightElement.js +1 -1
- package/lib/helper/scripts/isElementClickable.js +1 -1
- package/lib/helper/testcafe/testControllerHolder.js +1 -1
- package/lib/helper/testcafe/testcafe-utils.js +6 -7
- package/lib/helper.js +1 -3
- package/lib/history.js +6 -5
- package/lib/hooks.js +6 -6
- package/lib/html.js +7 -7
- package/lib/index.js +25 -41
- package/lib/interfaces/bdd.js +47 -64
- package/lib/interfaces/featureConfig.js +19 -19
- package/lib/interfaces/gherkin.js +124 -118
- package/lib/interfaces/scenarioConfig.js +29 -29
- package/lib/listener/artifacts.js +9 -9
- package/lib/listener/config.js +24 -24
- package/lib/listener/exit.js +12 -12
- package/lib/listener/helpers.js +42 -42
- package/lib/listener/mocha.js +11 -11
- package/lib/listener/retry.js +32 -30
- package/lib/listener/steps.js +50 -53
- package/lib/listener/timeout.js +54 -54
- package/lib/locator.js +6 -10
- package/lib/mochaFactory.js +18 -15
- package/lib/output.js +6 -10
- package/lib/parser.js +15 -12
- package/lib/pause.js +40 -33
- package/lib/plugin/allure.js +15 -15
- package/lib/plugin/autoDelay.js +29 -37
- package/lib/plugin/autoLogin.js +70 -65
- package/lib/plugin/commentStep.js +18 -18
- package/lib/plugin/coverage.js +115 -67
- package/lib/plugin/customLocator.js +21 -20
- package/lib/plugin/debugErrors.js +24 -24
- package/lib/plugin/eachElement.js +38 -38
- package/lib/plugin/fakerTransform.js +6 -6
- package/lib/plugin/heal.js +67 -108
- package/lib/plugin/pauseOnFail.js +11 -11
- package/lib/plugin/retryFailedStep.js +32 -39
- package/lib/plugin/retryTo.js +46 -40
- package/lib/plugin/screenshotOnFail.js +109 -87
- package/lib/plugin/selenoid.js +131 -118
- package/lib/plugin/standardActingHelpers.js +2 -8
- package/lib/plugin/stepByStepReport.js +110 -91
- package/lib/plugin/stepTimeout.js +24 -23
- package/lib/plugin/subtitles.js +34 -35
- package/lib/plugin/tryTo.js +40 -30
- package/lib/plugin/wdio.js +78 -75
- package/lib/recorder.js +14 -17
- package/lib/rerun.js +11 -10
- package/lib/scenario.js +25 -23
- package/lib/secret.js +4 -2
- package/lib/session.js +10 -10
- package/lib/step.js +12 -9
- package/lib/store.js +2 -3
- package/lib/transform.js +1 -1
- package/lib/translation.js +7 -8
- package/lib/ui.js +12 -14
- package/lib/utils.js +70 -72
- package/lib/within.js +10 -10
- package/lib/workerStorage.js +27 -25
- package/lib/workers.js +29 -32
- package/package.json +56 -57
- package/translations/de-DE.js +1 -1
- package/translations/fr-FR.js +1 -1
- package/translations/index.js +9 -13
- package/translations/it-IT.js +1 -1
- package/translations/ja-JP.js +1 -1
- package/translations/pl-PL.js +1 -1
- package/translations/pt-BR.js +1 -1
- package/translations/ru-RU.js +1 -1
- package/translations/zh-CN.js +1 -1
- package/translations/zh-TW.js +1 -1
- package/typings/index.d.ts +415 -65
- package/typings/promiseBasedTypes.d.ts +32 -0
- package/typings/types.d.ts +32 -0
- package/lib/dirname.js +0 -5
- package/lib/helper/Expect.js +0 -425
package/lib/helper/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;
|
|
55
|
+
} = require('./errors/ElementAssertion')
|
|
56
|
+
const {
|
|
57
|
+
dontSeeTraffic,
|
|
58
|
+
seeTraffic,
|
|
59
|
+
grabRecordedNetworkTraffics,
|
|
60
|
+
stopRecordingTraffic,
|
|
61
|
+
flushNetworkTraffics,
|
|
62
|
+
} = require('./network/actions')
|
|
54
63
|
|
|
55
|
-
const
|
|
56
|
-
const consoleLogStore = new Console();
|
|
57
|
-
const availableBrowsers = ['chromium', 'webkit', 'firefox', 'electron'];
|
|
58
|
-
|
|
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,29 +1209,32 @@ 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
|
/**
|
|
@@ -1220,34 +1245,34 @@ class Playwright extends Helper {
|
|
|
1220
1245
|
function getScrollPosition() {
|
|
1221
1246
|
return {
|
|
1222
1247
|
x: window.pageXOffset,
|
|
1223
|
-
y: window.pageYOffset
|
|
1224
|
-
}
|
|
1248
|
+
y: window.pageYOffset,
|
|
1249
|
+
}
|
|
1225
1250
|
}
|
|
1226
1251
|
/* eslint-enable comma-dangle */
|
|
1227
|
-
return this.executeScript(getScrollPosition)
|
|
1252
|
+
return this.executeScript(getScrollPosition)
|
|
1228
1253
|
}
|
|
1229
1254
|
|
|
1230
1255
|
/**
|
|
1231
1256
|
* {{> seeTitleEquals }}
|
|
1232
1257
|
*/
|
|
1233
1258
|
async seeTitleEquals(text) {
|
|
1234
|
-
const title = await this.page.title()
|
|
1235
|
-
return equals('web page title').assert(title, text)
|
|
1259
|
+
const title = await this.page.title()
|
|
1260
|
+
return equals('web page title').assert(title, text)
|
|
1236
1261
|
}
|
|
1237
1262
|
|
|
1238
1263
|
/**
|
|
1239
1264
|
* {{> dontSeeInTitle }}
|
|
1240
1265
|
*/
|
|
1241
1266
|
async dontSeeInTitle(text) {
|
|
1242
|
-
const title = await this.page.title()
|
|
1243
|
-
stringIncludes('web page title').negate(text, title)
|
|
1267
|
+
const title = await this.page.title()
|
|
1268
|
+
stringIncludes('web page title').negate(text, title)
|
|
1244
1269
|
}
|
|
1245
1270
|
|
|
1246
1271
|
/**
|
|
1247
1272
|
* {{> grabTitle }}
|
|
1248
1273
|
*/
|
|
1249
1274
|
async grabTitle() {
|
|
1250
|
-
return this.page.title()
|
|
1275
|
+
return this.page.title()
|
|
1251
1276
|
}
|
|
1252
1277
|
|
|
1253
1278
|
/**
|
|
@@ -1259,11 +1284,11 @@ class Playwright extends Helper {
|
|
|
1259
1284
|
* ```
|
|
1260
1285
|
*/
|
|
1261
1286
|
async _locate(locator) {
|
|
1262
|
-
const context = (await this.context) || (await this._getContext())
|
|
1287
|
+
const context = (await this.context) || (await this._getContext())
|
|
1263
1288
|
|
|
1264
|
-
if (this.frame) return findElements(this.frame, locator)
|
|
1289
|
+
if (this.frame) return findElements(this.frame, locator)
|
|
1265
1290
|
|
|
1266
|
-
return findElements(context, locator)
|
|
1291
|
+
return findElements(context, locator)
|
|
1267
1292
|
}
|
|
1268
1293
|
|
|
1269
1294
|
/**
|
|
@@ -1275,8 +1300,8 @@ class Playwright extends Helper {
|
|
|
1275
1300
|
* ```
|
|
1276
1301
|
*/
|
|
1277
1302
|
async _locateElement(locator) {
|
|
1278
|
-
const context = (await this.context) || (await this._getContext())
|
|
1279
|
-
return findElement(context, locator)
|
|
1303
|
+
const context = (await this.context) || (await this._getContext())
|
|
1304
|
+
return findElement(context, locator)
|
|
1280
1305
|
}
|
|
1281
1306
|
|
|
1282
1307
|
/**
|
|
@@ -1288,10 +1313,10 @@ class Playwright extends Helper {
|
|
|
1288
1313
|
* ```
|
|
1289
1314
|
*/
|
|
1290
1315
|
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]
|
|
1316
|
+
const context = providedContext || (await this._getContext())
|
|
1317
|
+
const els = await findCheckable.call(this, locator, context)
|
|
1318
|
+
assertElementExists(els[0], locator, 'Checkbox or radio')
|
|
1319
|
+
return els[0]
|
|
1295
1320
|
}
|
|
1296
1321
|
|
|
1297
1322
|
/**
|
|
@@ -1302,8 +1327,8 @@ class Playwright extends Helper {
|
|
|
1302
1327
|
* ```
|
|
1303
1328
|
*/
|
|
1304
1329
|
async _locateClickable(locator) {
|
|
1305
|
-
const context = await this._getContext()
|
|
1306
|
-
return findClickable.call(this, context, locator)
|
|
1330
|
+
const context = await this._getContext()
|
|
1331
|
+
return findClickable.call(this, context, locator)
|
|
1307
1332
|
}
|
|
1308
1333
|
|
|
1309
1334
|
/**
|
|
@@ -1314,7 +1339,7 @@ class Playwright extends Helper {
|
|
|
1314
1339
|
* ```
|
|
1315
1340
|
*/
|
|
1316
1341
|
async _locateFields(locator) {
|
|
1317
|
-
return findFields.call(this, locator)
|
|
1342
|
+
return findFields.call(this, locator)
|
|
1318
1343
|
}
|
|
1319
1344
|
|
|
1320
1345
|
/**
|
|
@@ -1322,7 +1347,7 @@ class Playwright extends Helper {
|
|
|
1322
1347
|
*
|
|
1323
1348
|
*/
|
|
1324
1349
|
async grabWebElements(locator) {
|
|
1325
|
-
return this._locate(locator)
|
|
1350
|
+
return this._locate(locator)
|
|
1326
1351
|
}
|
|
1327
1352
|
|
|
1328
1353
|
/**
|
|
@@ -1330,7 +1355,7 @@ class Playwright extends Helper {
|
|
|
1330
1355
|
*
|
|
1331
1356
|
*/
|
|
1332
1357
|
async grabWebElement(locator) {
|
|
1333
|
-
return this._locateElement(locator)
|
|
1358
|
+
return this._locateElement(locator)
|
|
1334
1359
|
}
|
|
1335
1360
|
|
|
1336
1361
|
/**
|
|
@@ -1345,20 +1370,20 @@ class Playwright extends Helper {
|
|
|
1345
1370
|
*/
|
|
1346
1371
|
async switchToNextTab(num = 1) {
|
|
1347
1372
|
if (this.isElectron) {
|
|
1348
|
-
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1373
|
+
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1349
1374
|
}
|
|
1350
|
-
const pages = await this.browserContext.pages()
|
|
1375
|
+
const pages = await this.browserContext.pages()
|
|
1351
1376
|
|
|
1352
|
-
const index = pages.indexOf(this.page)
|
|
1353
|
-
this.withinLocator = null
|
|
1354
|
-
const page = pages[index + num]
|
|
1377
|
+
const index = pages.indexOf(this.page)
|
|
1378
|
+
this.withinLocator = null
|
|
1379
|
+
const page = pages[index + num]
|
|
1355
1380
|
|
|
1356
1381
|
if (!page) {
|
|
1357
|
-
throw new Error(`There is no ability to switch to next tab with offset ${num}`)
|
|
1382
|
+
throw new Error(`There is no ability to switch to next tab with offset ${num}`)
|
|
1358
1383
|
}
|
|
1359
|
-
await targetCreatedHandler.call(this, page)
|
|
1360
|
-
await this._setPage(page)
|
|
1361
|
-
return this._waitForAction()
|
|
1384
|
+
await targetCreatedHandler.call(this, page)
|
|
1385
|
+
await this._setPage(page)
|
|
1386
|
+
return this._waitForAction()
|
|
1362
1387
|
}
|
|
1363
1388
|
|
|
1364
1389
|
/**
|
|
@@ -1372,19 +1397,19 @@ class Playwright extends Helper {
|
|
|
1372
1397
|
*/
|
|
1373
1398
|
async switchToPreviousTab(num = 1) {
|
|
1374
1399
|
if (this.isElectron) {
|
|
1375
|
-
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1400
|
+
throw new Error('Cannot switch tabs inside an Electron container')
|
|
1376
1401
|
}
|
|
1377
|
-
const pages = await this.browserContext.pages()
|
|
1378
|
-
const index = pages.indexOf(this.page)
|
|
1379
|
-
this.withinLocator = null
|
|
1380
|
-
const page = pages[index - num]
|
|
1402
|
+
const pages = await this.browserContext.pages()
|
|
1403
|
+
const index = pages.indexOf(this.page)
|
|
1404
|
+
this.withinLocator = null
|
|
1405
|
+
const page = pages[index - num]
|
|
1381
1406
|
|
|
1382
1407
|
if (!page) {
|
|
1383
|
-
throw new Error(`There is no ability to switch to previous tab with offset ${num}`)
|
|
1408
|
+
throw new Error(`There is no ability to switch to previous tab with offset ${num}`)
|
|
1384
1409
|
}
|
|
1385
1410
|
|
|
1386
|
-
await this._setPage(page)
|
|
1387
|
-
return this._waitForAction()
|
|
1411
|
+
await this._setPage(page)
|
|
1412
|
+
return this._waitForAction()
|
|
1388
1413
|
}
|
|
1389
1414
|
|
|
1390
1415
|
/**
|
|
@@ -1396,12 +1421,12 @@ class Playwright extends Helper {
|
|
|
1396
1421
|
*/
|
|
1397
1422
|
async closeCurrentTab() {
|
|
1398
1423
|
if (this.isElectron) {
|
|
1399
|
-
throw new Error('Cannot close current tab inside an Electron container')
|
|
1424
|
+
throw new Error('Cannot close current tab inside an Electron container')
|
|
1400
1425
|
}
|
|
1401
|
-
const oldPage = this.page
|
|
1402
|
-
await this.switchToPreviousTab()
|
|
1403
|
-
await oldPage.close()
|
|
1404
|
-
return this._waitForAction()
|
|
1426
|
+
const oldPage = this.page
|
|
1427
|
+
await this.switchToPreviousTab()
|
|
1428
|
+
await oldPage.close()
|
|
1429
|
+
return this._waitForAction()
|
|
1405
1430
|
}
|
|
1406
1431
|
|
|
1407
1432
|
/**
|
|
@@ -1412,13 +1437,13 @@ class Playwright extends Helper {
|
|
|
1412
1437
|
* ```
|
|
1413
1438
|
*/
|
|
1414
1439
|
async closeOtherTabs() {
|
|
1415
|
-
const pages = await this.browserContext.pages()
|
|
1416
|
-
const otherPages = pages.filter(page => page !== this.page)
|
|
1440
|
+
const pages = await this.browserContext.pages()
|
|
1441
|
+
const otherPages = pages.filter((page) => page !== this.page)
|
|
1417
1442
|
if (otherPages.length) {
|
|
1418
|
-
this.debug(`Closing ${otherPages.length} tabs`)
|
|
1419
|
-
return Promise.all(otherPages.map(p => p.close()))
|
|
1443
|
+
this.debug(`Closing ${otherPages.length} tabs`)
|
|
1444
|
+
return Promise.all(otherPages.map((p) => p.close()))
|
|
1420
1445
|
}
|
|
1421
|
-
return Promise.resolve()
|
|
1446
|
+
return Promise.resolve()
|
|
1422
1447
|
}
|
|
1423
1448
|
|
|
1424
1449
|
/**
|
|
@@ -1437,20 +1462,20 @@ class Playwright extends Helper {
|
|
|
1437
1462
|
*/
|
|
1438
1463
|
async openNewTab(options) {
|
|
1439
1464
|
if (this.isElectron) {
|
|
1440
|
-
throw new Error('Cannot open new tabs inside an Electron container')
|
|
1465
|
+
throw new Error('Cannot open new tabs inside an Electron container')
|
|
1441
1466
|
}
|
|
1442
|
-
const page = await this.browserContext.newPage(options)
|
|
1443
|
-
await targetCreatedHandler.call(this, page)
|
|
1444
|
-
await this._setPage(page)
|
|
1445
|
-
return this._waitForAction()
|
|
1467
|
+
const page = await this.browserContext.newPage(options)
|
|
1468
|
+
await targetCreatedHandler.call(this, page)
|
|
1469
|
+
await this._setPage(page)
|
|
1470
|
+
return this._waitForAction()
|
|
1446
1471
|
}
|
|
1447
1472
|
|
|
1448
1473
|
/**
|
|
1449
1474
|
* {{> grabNumberOfOpenTabs }}
|
|
1450
1475
|
*/
|
|
1451
1476
|
async grabNumberOfOpenTabs() {
|
|
1452
|
-
const pages = await this.browserContext.pages()
|
|
1453
|
-
return pages.length
|
|
1477
|
+
const pages = await this.browserContext.pages()
|
|
1478
|
+
return pages.length
|
|
1454
1479
|
}
|
|
1455
1480
|
|
|
1456
1481
|
/**
|
|
@@ -1458,12 +1483,12 @@ class Playwright extends Helper {
|
|
|
1458
1483
|
*
|
|
1459
1484
|
*/
|
|
1460
1485
|
async seeElement(locator) {
|
|
1461
|
-
let els = await this._locate(locator)
|
|
1462
|
-
els = await Promise.all(els.map(el => el.isVisible()))
|
|
1486
|
+
let els = await this._locate(locator)
|
|
1487
|
+
els = await Promise.all(els.map((el) => el.isVisible()))
|
|
1463
1488
|
try {
|
|
1464
|
-
return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'))
|
|
1489
|
+
return empty('visible elements').negate(els.filter((v) => v).fill('ELEMENT'))
|
|
1465
1490
|
} catch (e) {
|
|
1466
|
-
dontSeeElementError(locator)
|
|
1491
|
+
dontSeeElementError(locator)
|
|
1467
1492
|
}
|
|
1468
1493
|
}
|
|
1469
1494
|
|
|
@@ -1472,12 +1497,12 @@ class Playwright extends Helper {
|
|
|
1472
1497
|
*
|
|
1473
1498
|
*/
|
|
1474
1499
|
async dontSeeElement(locator) {
|
|
1475
|
-
let els = await this._locate(locator)
|
|
1476
|
-
els = await Promise.all(els.map(el => el.isVisible()))
|
|
1500
|
+
let els = await this._locate(locator)
|
|
1501
|
+
els = await Promise.all(els.map((el) => el.isVisible()))
|
|
1477
1502
|
try {
|
|
1478
|
-
return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'))
|
|
1503
|
+
return empty('visible elements').assert(els.filter((v) => v).fill('ELEMENT'))
|
|
1479
1504
|
} catch (e) {
|
|
1480
|
-
seeElementError(locator)
|
|
1505
|
+
seeElementError(locator)
|
|
1481
1506
|
}
|
|
1482
1507
|
}
|
|
1483
1508
|
|
|
@@ -1485,11 +1510,11 @@ class Playwright extends Helper {
|
|
|
1485
1510
|
* {{> seeElementInDOM }}
|
|
1486
1511
|
*/
|
|
1487
1512
|
async seeElementInDOM(locator) {
|
|
1488
|
-
const els = await this._locate(locator)
|
|
1513
|
+
const els = await this._locate(locator)
|
|
1489
1514
|
try {
|
|
1490
|
-
return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'))
|
|
1515
|
+
return empty('elements on page').negate(els.filter((v) => v).fill('ELEMENT'))
|
|
1491
1516
|
} catch (e) {
|
|
1492
|
-
dontSeeElementInDOMError(locator)
|
|
1517
|
+
dontSeeElementInDOMError(locator)
|
|
1493
1518
|
}
|
|
1494
1519
|
}
|
|
1495
1520
|
|
|
@@ -1497,11 +1522,11 @@ class Playwright extends Helper {
|
|
|
1497
1522
|
* {{> dontSeeElementInDOM }}
|
|
1498
1523
|
*/
|
|
1499
1524
|
async dontSeeElementInDOM(locator) {
|
|
1500
|
-
const els = await this._locate(locator)
|
|
1525
|
+
const els = await this._locate(locator)
|
|
1501
1526
|
try {
|
|
1502
|
-
return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'))
|
|
1527
|
+
return empty('elements on a page').assert(els.filter((v) => v).fill('ELEMENT'))
|
|
1503
1528
|
} catch (e) {
|
|
1504
|
-
seeElementInDOMError(locator)
|
|
1529
|
+
seeElementInDOMError(locator)
|
|
1505
1530
|
}
|
|
1506
1531
|
}
|
|
1507
1532
|
|
|
@@ -1524,18 +1549,18 @@ class Playwright extends Helper {
|
|
|
1524
1549
|
*/
|
|
1525
1550
|
async handleDownloads(fileName) {
|
|
1526
1551
|
this.page.waitForEvent('download').then(async (download) => {
|
|
1527
|
-
const filePath = await download.path()
|
|
1528
|
-
fileName = fileName || `downloads/${path.basename(filePath)}
|
|
1552
|
+
const filePath = await download.path()
|
|
1553
|
+
fileName = fileName || `downloads/${path.basename(filePath)}`
|
|
1529
1554
|
|
|
1530
|
-
const downloadPath = path.join(global.output_dir, fileName)
|
|
1555
|
+
const downloadPath = path.join(global.output_dir, fileName)
|
|
1531
1556
|
if (!fs.existsSync(path.dirname(downloadPath))) {
|
|
1532
|
-
fs.mkdirSync(path.dirname(downloadPath), '0777')
|
|
1557
|
+
fs.mkdirSync(path.dirname(downloadPath), '0777')
|
|
1533
1558
|
}
|
|
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
|
-
})
|
|
1559
|
+
fs.copyFileSync(filePath, downloadPath)
|
|
1560
|
+
this.debug('Download completed')
|
|
1561
|
+
this.debugSection('Downloaded From', await download.url())
|
|
1562
|
+
this.debugSection('Downloaded To', downloadPath)
|
|
1563
|
+
})
|
|
1539
1564
|
}
|
|
1540
1565
|
|
|
1541
1566
|
/**
|
|
@@ -1555,37 +1580,37 @@ class Playwright extends Helper {
|
|
|
1555
1580
|
*
|
|
1556
1581
|
*/
|
|
1557
1582
|
async click(locator, context = null, options = {}) {
|
|
1558
|
-
return proceedClick.call(this, locator, context, options)
|
|
1583
|
+
return proceedClick.call(this, locator, context, options)
|
|
1559
1584
|
}
|
|
1560
1585
|
|
|
1561
1586
|
/**
|
|
1562
1587
|
* Clicks link and waits for navigation (deprecated)
|
|
1563
1588
|
*/
|
|
1564
1589
|
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)
|
|
1590
|
+
console.log('clickLink deprecated: Playwright automatically waits for navigation to happen.')
|
|
1591
|
+
console.log('Replace I.clickLink with I.click')
|
|
1592
|
+
return this.click(locator, context)
|
|
1568
1593
|
}
|
|
1569
1594
|
|
|
1570
1595
|
/**
|
|
1571
1596
|
* {{> forceClick }}
|
|
1572
1597
|
*/
|
|
1573
1598
|
async forceClick(locator, context = null) {
|
|
1574
|
-
return proceedClick.call(this, locator, context, { force: true })
|
|
1599
|
+
return proceedClick.call(this, locator, context, { force: true })
|
|
1575
1600
|
}
|
|
1576
1601
|
|
|
1577
1602
|
/**
|
|
1578
1603
|
* {{> doubleClick }}
|
|
1579
1604
|
*/
|
|
1580
1605
|
async doubleClick(locator, context = null) {
|
|
1581
|
-
return proceedClick.call(this, locator, context, { clickCount: 2 })
|
|
1606
|
+
return proceedClick.call(this, locator, context, { clickCount: 2 })
|
|
1582
1607
|
}
|
|
1583
1608
|
|
|
1584
1609
|
/**
|
|
1585
1610
|
* {{> rightClick }}
|
|
1586
1611
|
*/
|
|
1587
1612
|
async rightClick(locator, context = null) {
|
|
1588
|
-
return proceedClick.call(this, locator, context, { button: 'right' })
|
|
1613
|
+
return proceedClick.call(this, locator, context, { button: 'right' })
|
|
1589
1614
|
}
|
|
1590
1615
|
|
|
1591
1616
|
/**
|
|
@@ -1604,9 +1629,9 @@ class Playwright extends Helper {
|
|
|
1604
1629
|
*
|
|
1605
1630
|
*/
|
|
1606
1631
|
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()
|
|
1632
|
+
const elm = await this._locateCheckable(field, context)
|
|
1633
|
+
await elm.check(options)
|
|
1634
|
+
return this._waitForAction()
|
|
1610
1635
|
}
|
|
1611
1636
|
|
|
1612
1637
|
/**
|
|
@@ -1624,41 +1649,41 @@ class Playwright extends Helper {
|
|
|
1624
1649
|
* {{> uncheckOption }}
|
|
1625
1650
|
*/
|
|
1626
1651
|
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()
|
|
1652
|
+
const elm = await this._locateCheckable(field, context)
|
|
1653
|
+
await elm.uncheck(options)
|
|
1654
|
+
return this._waitForAction()
|
|
1630
1655
|
}
|
|
1631
1656
|
|
|
1632
1657
|
/**
|
|
1633
1658
|
* {{> seeCheckboxIsChecked }}
|
|
1634
1659
|
*/
|
|
1635
1660
|
async seeCheckboxIsChecked(field) {
|
|
1636
|
-
return proceedIsChecked.call(this, 'assert', field)
|
|
1661
|
+
return proceedIsChecked.call(this, 'assert', field)
|
|
1637
1662
|
}
|
|
1638
1663
|
|
|
1639
1664
|
/**
|
|
1640
1665
|
* {{> dontSeeCheckboxIsChecked }}
|
|
1641
1666
|
*/
|
|
1642
1667
|
async dontSeeCheckboxIsChecked(field) {
|
|
1643
|
-
return proceedIsChecked.call(this, 'negate', field)
|
|
1668
|
+
return proceedIsChecked.call(this, 'negate', field)
|
|
1644
1669
|
}
|
|
1645
1670
|
|
|
1646
1671
|
/**
|
|
1647
1672
|
* {{> pressKeyDown }}
|
|
1648
1673
|
*/
|
|
1649
1674
|
async pressKeyDown(key) {
|
|
1650
|
-
key = getNormalizedKey.call(this, key)
|
|
1651
|
-
await this.page.keyboard.down(key)
|
|
1652
|
-
return this._waitForAction()
|
|
1675
|
+
key = getNormalizedKey.call(this, key)
|
|
1676
|
+
await this.page.keyboard.down(key)
|
|
1677
|
+
return this._waitForAction()
|
|
1653
1678
|
}
|
|
1654
1679
|
|
|
1655
1680
|
/**
|
|
1656
1681
|
* {{> pressKeyUp }}
|
|
1657
1682
|
*/
|
|
1658
1683
|
async pressKeyUp(key) {
|
|
1659
|
-
key = getNormalizedKey.call(this, key)
|
|
1660
|
-
await this.page.keyboard.up(key)
|
|
1661
|
-
return this._waitForAction()
|
|
1684
|
+
key = getNormalizedKey.call(this, key)
|
|
1685
|
+
await this.page.keyboard.up(key)
|
|
1686
|
+
return this._waitForAction()
|
|
1662
1687
|
}
|
|
1663
1688
|
|
|
1664
1689
|
/**
|
|
@@ -1668,28 +1693,28 @@ class Playwright extends Helper {
|
|
|
1668
1693
|
* {{> pressKeyWithKeyNormalization }}
|
|
1669
1694
|
*/
|
|
1670
1695
|
async pressKey(key) {
|
|
1671
|
-
const modifiers = []
|
|
1696
|
+
const modifiers = []
|
|
1672
1697
|
if (Array.isArray(key)) {
|
|
1673
1698
|
for (let k of key) {
|
|
1674
|
-
k = getNormalizedKey.call(this, k)
|
|
1699
|
+
k = getNormalizedKey.call(this, k)
|
|
1675
1700
|
if (isModifierKey(k)) {
|
|
1676
|
-
modifiers.push(k)
|
|
1701
|
+
modifiers.push(k)
|
|
1677
1702
|
} else {
|
|
1678
|
-
key = k
|
|
1679
|
-
break
|
|
1703
|
+
key = k
|
|
1704
|
+
break
|
|
1680
1705
|
}
|
|
1681
1706
|
}
|
|
1682
1707
|
} else {
|
|
1683
|
-
key = getNormalizedKey.call(this, key)
|
|
1708
|
+
key = getNormalizedKey.call(this, key)
|
|
1684
1709
|
}
|
|
1685
1710
|
for (const modifier of modifiers) {
|
|
1686
|
-
await this.page.keyboard.down(modifier)
|
|
1711
|
+
await this.page.keyboard.down(modifier)
|
|
1687
1712
|
}
|
|
1688
|
-
await this.page.keyboard.press(key)
|
|
1713
|
+
await this.page.keyboard.press(key)
|
|
1689
1714
|
for (const modifier of modifiers) {
|
|
1690
|
-
await this.page.keyboard.up(modifier)
|
|
1715
|
+
await this.page.keyboard.up(modifier)
|
|
1691
1716
|
}
|
|
1692
|
-
return this._waitForAction()
|
|
1717
|
+
return this._waitForAction()
|
|
1693
1718
|
}
|
|
1694
1719
|
|
|
1695
1720
|
/**
|
|
@@ -1697,13 +1722,13 @@ class Playwright extends Helper {
|
|
|
1697
1722
|
*/
|
|
1698
1723
|
async type(keys, delay = null) {
|
|
1699
1724
|
if (!Array.isArray(keys)) {
|
|
1700
|
-
keys = keys.toString()
|
|
1701
|
-
keys = keys.split('')
|
|
1725
|
+
keys = keys.toString()
|
|
1726
|
+
keys = keys.split('')
|
|
1702
1727
|
}
|
|
1703
1728
|
|
|
1704
1729
|
for (const key of keys) {
|
|
1705
|
-
await this.page.keyboard.press(key)
|
|
1706
|
-
if (delay) await this.wait(delay / 1000)
|
|
1730
|
+
await this.page.keyboard.press(key)
|
|
1731
|
+
if (delay) await this.wait(delay / 1000)
|
|
1707
1732
|
}
|
|
1708
1733
|
}
|
|
1709
1734
|
|
|
@@ -1712,75 +1737,75 @@ class Playwright extends Helper {
|
|
|
1712
1737
|
*
|
|
1713
1738
|
*/
|
|
1714
1739
|
async fillField(field, value) {
|
|
1715
|
-
const els = await findFields.call(this, field)
|
|
1716
|
-
assertElementExists(els, field, 'Field')
|
|
1717
|
-
const el = els[0]
|
|
1740
|
+
const els = await findFields.call(this, field)
|
|
1741
|
+
assertElementExists(els, field, 'Field')
|
|
1742
|
+
const el = els[0]
|
|
1718
1743
|
|
|
1719
|
-
await el.clear()
|
|
1744
|
+
await el.clear()
|
|
1720
1745
|
|
|
1721
|
-
await highlightActiveElement.call(this, el)
|
|
1746
|
+
await highlightActiveElement.call(this, el)
|
|
1722
1747
|
|
|
1723
|
-
await el.type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1748
|
+
await el.type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1724
1749
|
|
|
1725
|
-
return this._waitForAction()
|
|
1750
|
+
return this._waitForAction()
|
|
1726
1751
|
}
|
|
1727
1752
|
|
|
1728
1753
|
/**
|
|
1729
1754
|
* Clears the text input element: `<input>`, `<textarea>` or `[contenteditable]` .
|
|
1730
1755
|
*
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1756
|
+
*
|
|
1757
|
+
* Examples:
|
|
1758
|
+
*
|
|
1759
|
+
* ```js
|
|
1760
|
+
* I.clearField('.text-area')
|
|
1761
|
+
*
|
|
1762
|
+
* // if this doesn't work use force option
|
|
1763
|
+
* I.clearField('#submit', { force: true })
|
|
1764
|
+
* ```
|
|
1765
|
+
* Use `force` to bypass the [actionability](https://playwright.dev/docs/actionability) checks.
|
|
1766
|
+
*
|
|
1742
1767
|
* @param {CodeceptJS.LocatorOrString} locator field located by label|name|CSS|XPath|strict locator.
|
|
1743
1768
|
* @param {any} [options] [Additional options](https://playwright.dev/docs/api/class-locator#locator-clear) for available options object as 2nd argument.
|
|
1744
1769
|
*/
|
|
1745
1770
|
async clearField(locator, options = {}) {
|
|
1746
|
-
const els = await findFields.call(this, locator)
|
|
1747
|
-
assertElementExists(els, locator, 'Field to clear')
|
|
1771
|
+
const els = await findFields.call(this, locator)
|
|
1772
|
+
assertElementExists(els, locator, 'Field to clear')
|
|
1748
1773
|
|
|
1749
|
-
const el = els[0]
|
|
1774
|
+
const el = els[0]
|
|
1750
1775
|
|
|
1751
|
-
await highlightActiveElement.call(this, el)
|
|
1776
|
+
await highlightActiveElement.call(this, el)
|
|
1752
1777
|
|
|
1753
|
-
await el.clear()
|
|
1778
|
+
await el.clear()
|
|
1754
1779
|
|
|
1755
|
-
return this._waitForAction()
|
|
1780
|
+
return this._waitForAction()
|
|
1756
1781
|
}
|
|
1757
1782
|
|
|
1758
1783
|
/**
|
|
1759
1784
|
* {{> appendField }}
|
|
1760
1785
|
*/
|
|
1761
1786
|
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()
|
|
1787
|
+
const els = await findFields.call(this, field)
|
|
1788
|
+
assertElementExists(els, field, 'Field')
|
|
1789
|
+
await highlightActiveElement.call(this, els[0])
|
|
1790
|
+
await els[0].press('End')
|
|
1791
|
+
await els[0].type(value.toString(), { delay: this.options.pressKeyDelay })
|
|
1792
|
+
return this._waitForAction()
|
|
1768
1793
|
}
|
|
1769
1794
|
|
|
1770
1795
|
/**
|
|
1771
1796
|
* {{> seeInField }}
|
|
1772
1797
|
*/
|
|
1773
1798
|
async seeInField(field, value) {
|
|
1774
|
-
const _value =
|
|
1775
|
-
return proceedSeeInField.call(this, 'assert', field, _value)
|
|
1799
|
+
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1800
|
+
return proceedSeeInField.call(this, 'assert', field, _value)
|
|
1776
1801
|
}
|
|
1777
1802
|
|
|
1778
1803
|
/**
|
|
1779
1804
|
* {{> dontSeeInField }}
|
|
1780
1805
|
*/
|
|
1781
1806
|
async dontSeeInField(field, value) {
|
|
1782
|
-
const _value =
|
|
1783
|
-
return proceedSeeInField.call(this, 'negate', field, _value)
|
|
1807
|
+
const _value = typeof value === 'boolean' ? value : value.toString()
|
|
1808
|
+
return proceedSeeInField.call(this, 'negate', field, _value)
|
|
1784
1809
|
}
|
|
1785
1810
|
|
|
1786
1811
|
/**
|
|
@@ -1788,38 +1813,38 @@ class Playwright extends Helper {
|
|
|
1788
1813
|
*
|
|
1789
1814
|
*/
|
|
1790
1815
|
async attachFile(locator, pathToFile) {
|
|
1791
|
-
const file = path.join(global.codecept_dir, pathToFile)
|
|
1816
|
+
const file = path.join(global.codecept_dir, pathToFile)
|
|
1792
1817
|
|
|
1793
1818
|
if (!fileExists(file)) {
|
|
1794
|
-
throw new Error(`File at ${file} can not be found on local system`)
|
|
1819
|
+
throw new Error(`File at ${file} can not be found on local system`)
|
|
1795
1820
|
}
|
|
1796
|
-
const els = await findFields.call(this, locator)
|
|
1797
|
-
assertElementExists(els, locator, 'Field')
|
|
1798
|
-
await els[0].setInputFiles(file)
|
|
1799
|
-
return this._waitForAction()
|
|
1821
|
+
const els = await findFields.call(this, locator)
|
|
1822
|
+
assertElementExists(els, locator, 'Field')
|
|
1823
|
+
await els[0].setInputFiles(file)
|
|
1824
|
+
return this._waitForAction()
|
|
1800
1825
|
}
|
|
1801
1826
|
|
|
1802
1827
|
/**
|
|
1803
1828
|
* {{> selectOption }}
|
|
1804
1829
|
*/
|
|
1805
1830
|
async selectOption(select, option) {
|
|
1806
|
-
const els = await findFields.call(this, select)
|
|
1807
|
-
assertElementExists(els, select, 'Selectable field')
|
|
1808
|
-
const el = els[0]
|
|
1831
|
+
const els = await findFields.call(this, select)
|
|
1832
|
+
assertElementExists(els, select, 'Selectable field')
|
|
1833
|
+
const el = els[0]
|
|
1809
1834
|
|
|
1810
|
-
await highlightActiveElement.call(this, el)
|
|
1811
|
-
let optionToSelect = ''
|
|
1835
|
+
await highlightActiveElement.call(this, el)
|
|
1836
|
+
let optionToSelect = ''
|
|
1812
1837
|
|
|
1813
1838
|
try {
|
|
1814
|
-
optionToSelect = (await el.locator('option', { hasText: option }).textContent()).trim()
|
|
1839
|
+
optionToSelect = (await el.locator('option', { hasText: option }).textContent()).trim()
|
|
1815
1840
|
} catch (e) {
|
|
1816
|
-
optionToSelect = option
|
|
1841
|
+
optionToSelect = option
|
|
1817
1842
|
}
|
|
1818
1843
|
|
|
1819
|
-
if (!Array.isArray(option)) option = [optionToSelect]
|
|
1844
|
+
if (!Array.isArray(option)) option = [optionToSelect]
|
|
1820
1845
|
|
|
1821
|
-
await el.selectOption(option)
|
|
1822
|
-
return this._waitForAction()
|
|
1846
|
+
await el.selectOption(option)
|
|
1847
|
+
return this._waitForAction()
|
|
1823
1848
|
}
|
|
1824
1849
|
|
|
1825
1850
|
/**
|
|
@@ -1827,37 +1852,37 @@ class Playwright extends Helper {
|
|
|
1827
1852
|
*
|
|
1828
1853
|
*/
|
|
1829
1854
|
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
|
|
1855
|
+
let els = await this._locate(locator)
|
|
1856
|
+
els = await Promise.all(els.map((el) => el.isVisible()))
|
|
1857
|
+
return els.filter((v) => v).length
|
|
1833
1858
|
}
|
|
1834
1859
|
|
|
1835
1860
|
/**
|
|
1836
1861
|
* {{> seeInCurrentUrl }}
|
|
1837
1862
|
*/
|
|
1838
1863
|
async seeInCurrentUrl(url) {
|
|
1839
|
-
stringIncludes('url').assert(url, await this._getPageUrl())
|
|
1864
|
+
stringIncludes('url').assert(url, await this._getPageUrl())
|
|
1840
1865
|
}
|
|
1841
1866
|
|
|
1842
1867
|
/**
|
|
1843
1868
|
* {{> dontSeeInCurrentUrl }}
|
|
1844
1869
|
*/
|
|
1845
1870
|
async dontSeeInCurrentUrl(url) {
|
|
1846
|
-
stringIncludes('url').negate(url, await this._getPageUrl())
|
|
1871
|
+
stringIncludes('url').negate(url, await this._getPageUrl())
|
|
1847
1872
|
}
|
|
1848
1873
|
|
|
1849
1874
|
/**
|
|
1850
1875
|
* {{> seeCurrentUrlEquals }}
|
|
1851
1876
|
*/
|
|
1852
1877
|
async seeCurrentUrlEquals(url) {
|
|
1853
|
-
urlEquals(this.options.url).assert(url, await this._getPageUrl())
|
|
1878
|
+
urlEquals(this.options.url).assert(url, await this._getPageUrl())
|
|
1854
1879
|
}
|
|
1855
1880
|
|
|
1856
1881
|
/**
|
|
1857
1882
|
* {{> dontSeeCurrentUrlEquals }}
|
|
1858
1883
|
*/
|
|
1859
1884
|
async dontSeeCurrentUrlEquals(url) {
|
|
1860
|
-
urlEquals(this.options.url).negate(url, await this._getPageUrl())
|
|
1885
|
+
urlEquals(this.options.url).negate(url, await this._getPageUrl())
|
|
1861
1886
|
}
|
|
1862
1887
|
|
|
1863
1888
|
/**
|
|
@@ -1866,14 +1891,14 @@ class Playwright extends Helper {
|
|
|
1866
1891
|
*
|
|
1867
1892
|
*/
|
|
1868
1893
|
async see(text, context = null) {
|
|
1869
|
-
return proceedSee.call(this, 'assert', text, context)
|
|
1894
|
+
return proceedSee.call(this, 'assert', text, context)
|
|
1870
1895
|
}
|
|
1871
1896
|
|
|
1872
1897
|
/**
|
|
1873
1898
|
* {{> seeTextEquals }}
|
|
1874
1899
|
*/
|
|
1875
1900
|
async seeTextEquals(text, context = null) {
|
|
1876
|
-
return proceedSee.call(this, 'assert', text, context, true)
|
|
1901
|
+
return proceedSee.call(this, 'assert', text, context, true)
|
|
1877
1902
|
}
|
|
1878
1903
|
|
|
1879
1904
|
/**
|
|
@@ -1882,14 +1907,14 @@ class Playwright extends Helper {
|
|
|
1882
1907
|
*
|
|
1883
1908
|
*/
|
|
1884
1909
|
async dontSee(text, context = null) {
|
|
1885
|
-
return proceedSee.call(this, 'negate', text, context)
|
|
1910
|
+
return proceedSee.call(this, 'negate', text, context)
|
|
1886
1911
|
}
|
|
1887
1912
|
|
|
1888
1913
|
/**
|
|
1889
1914
|
* {{> grabSource }}
|
|
1890
1915
|
*/
|
|
1891
1916
|
async grabSource() {
|
|
1892
|
-
return this.page.content()
|
|
1917
|
+
return this.page.content()
|
|
1893
1918
|
}
|
|
1894
1919
|
|
|
1895
1920
|
/**
|
|
@@ -1904,32 +1929,32 @@ class Playwright extends Helper {
|
|
|
1904
1929
|
* @return {Promise<any[]>}
|
|
1905
1930
|
*/
|
|
1906
1931
|
async grabBrowserLogs() {
|
|
1907
|
-
const logs = consoleLogStore.entries
|
|
1908
|
-
consoleLogStore.clear()
|
|
1909
|
-
return logs
|
|
1932
|
+
const logs = consoleLogStore.entries
|
|
1933
|
+
consoleLogStore.clear()
|
|
1934
|
+
return logs
|
|
1910
1935
|
}
|
|
1911
1936
|
|
|
1912
1937
|
/**
|
|
1913
1938
|
* {{> grabCurrentUrl }}
|
|
1914
1939
|
*/
|
|
1915
1940
|
async grabCurrentUrl() {
|
|
1916
|
-
return this._getPageUrl()
|
|
1941
|
+
return this._getPageUrl()
|
|
1917
1942
|
}
|
|
1918
1943
|
|
|
1919
1944
|
/**
|
|
1920
1945
|
* {{> seeInSource }}
|
|
1921
1946
|
*/
|
|
1922
1947
|
async seeInSource(text) {
|
|
1923
|
-
const source = await this.page.content()
|
|
1924
|
-
stringIncludes('HTML source of a page').assert(text, source)
|
|
1948
|
+
const source = await this.page.content()
|
|
1949
|
+
stringIncludes('HTML source of a page').assert(text, source)
|
|
1925
1950
|
}
|
|
1926
1951
|
|
|
1927
1952
|
/**
|
|
1928
1953
|
* {{> dontSeeInSource }}
|
|
1929
1954
|
*/
|
|
1930
1955
|
async dontSeeInSource(text) {
|
|
1931
|
-
const source = await this.page.content()
|
|
1932
|
-
stringIncludes('HTML source of a page').negate(text, source)
|
|
1956
|
+
const source = await this.page.content()
|
|
1957
|
+
stringIncludes('HTML source of a page').negate(text, source)
|
|
1933
1958
|
}
|
|
1934
1959
|
|
|
1935
1960
|
/**
|
|
@@ -1938,8 +1963,10 @@ class Playwright extends Helper {
|
|
|
1938
1963
|
*
|
|
1939
1964
|
*/
|
|
1940
1965
|
async seeNumberOfElements(locator, num) {
|
|
1941
|
-
const elements = await this._locate(locator)
|
|
1942
|
-
return equals(
|
|
1966
|
+
const elements = await this._locate(locator)
|
|
1967
|
+
return equals(
|
|
1968
|
+
`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`,
|
|
1969
|
+
).assert(elements.length, num)
|
|
1943
1970
|
}
|
|
1944
1971
|
|
|
1945
1972
|
/**
|
|
@@ -1948,8 +1975,11 @@ class Playwright extends Helper {
|
|
|
1948
1975
|
*
|
|
1949
1976
|
*/
|
|
1950
1977
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1951
|
-
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1952
|
-
return equals(`expected number of visible elements (${
|
|
1978
|
+
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1979
|
+
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(
|
|
1980
|
+
res,
|
|
1981
|
+
num,
|
|
1982
|
+
)
|
|
1953
1983
|
}
|
|
1954
1984
|
|
|
1955
1985
|
/**
|
|
@@ -1957,9 +1987,9 @@ class Playwright extends Helper {
|
|
|
1957
1987
|
*/
|
|
1958
1988
|
async setCookie(cookie) {
|
|
1959
1989
|
if (Array.isArray(cookie)) {
|
|
1960
|
-
return this.browserContext.addCookies(cookie)
|
|
1990
|
+
return this.browserContext.addCookies(cookie)
|
|
1961
1991
|
}
|
|
1962
|
-
return this.browserContext.addCookies([cookie])
|
|
1992
|
+
return this.browserContext.addCookies([cookie])
|
|
1963
1993
|
}
|
|
1964
1994
|
|
|
1965
1995
|
/**
|
|
@@ -1967,16 +1997,16 @@ class Playwright extends Helper {
|
|
|
1967
1997
|
*
|
|
1968
1998
|
*/
|
|
1969
1999
|
async seeCookie(name) {
|
|
1970
|
-
const cookies = await this.browserContext.cookies()
|
|
1971
|
-
empty(`cookie ${name} to be set`).negate(cookies.filter(c => c.name === name))
|
|
2000
|
+
const cookies = await this.browserContext.cookies()
|
|
2001
|
+
empty(`cookie ${name} to be set`).negate(cookies.filter((c) => c.name === name))
|
|
1972
2002
|
}
|
|
1973
2003
|
|
|
1974
2004
|
/**
|
|
1975
2005
|
* {{> dontSeeCookie }}
|
|
1976
2006
|
*/
|
|
1977
2007
|
async dontSeeCookie(name) {
|
|
1978
|
-
const cookies = await this.browserContext.cookies()
|
|
1979
|
-
empty(`cookie ${name} to be set`).assert(cookies.filter(c => c.name === name))
|
|
2008
|
+
const cookies = await this.browserContext.cookies()
|
|
2009
|
+
empty(`cookie ${name} not to be set`).assert(cookies.filter((c) => c.name === name))
|
|
1980
2010
|
}
|
|
1981
2011
|
|
|
1982
2012
|
/**
|
|
@@ -1985,10 +2015,10 @@ class Playwright extends Helper {
|
|
|
1985
2015
|
* {{> grabCookie }}
|
|
1986
2016
|
*/
|
|
1987
2017
|
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]
|
|
2018
|
+
const cookies = await this.browserContext.cookies()
|
|
2019
|
+
if (!name) return cookies
|
|
2020
|
+
const cookie = cookies.filter((c) => c.name === name)
|
|
2021
|
+
if (cookie[0]) return cookie[0]
|
|
1992
2022
|
}
|
|
1993
2023
|
|
|
1994
2024
|
/**
|
|
@@ -1997,8 +2027,8 @@ class Playwright extends Helper {
|
|
|
1997
2027
|
async clearCookie() {
|
|
1998
2028
|
// Playwright currently doesn't support to delete a certain cookie
|
|
1999
2029
|
// 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()
|
|
2030
|
+
if (!this.browserContext) return
|
|
2031
|
+
return this.browserContext.clearCookies()
|
|
2002
2032
|
}
|
|
2003
2033
|
|
|
2004
2034
|
/**
|
|
@@ -2028,9 +2058,9 @@ class Playwright extends Helper {
|
|
|
2028
2058
|
async executeScript(fn, arg) {
|
|
2029
2059
|
if (this.context && this.context.constructor.name === 'FrameLocator') {
|
|
2030
2060
|
// switching to iframe context
|
|
2031
|
-
return this.context.locator(':root').evaluate(fn, arg)
|
|
2061
|
+
return this.context.locator(':root').evaluate(fn, arg)
|
|
2032
2062
|
}
|
|
2033
|
-
return this.page.evaluate.apply(this.page, [fn, arg])
|
|
2063
|
+
return this.page.evaluate.apply(this.page, [fn, arg])
|
|
2034
2064
|
}
|
|
2035
2065
|
|
|
2036
2066
|
/**
|
|
@@ -2039,14 +2069,14 @@ class Playwright extends Helper {
|
|
|
2039
2069
|
* @param {*} locator
|
|
2040
2070
|
*/
|
|
2041
2071
|
_contextLocator(locator) {
|
|
2042
|
-
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2072
|
+
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2043
2073
|
|
|
2044
2074
|
if (this.contextLocator) {
|
|
2045
|
-
const contextLocator = buildLocatorString(new Locator(this.contextLocator, 'css'))
|
|
2046
|
-
locator = `${contextLocator} >> ${locator}
|
|
2075
|
+
const contextLocator = buildLocatorString(new Locator(this.contextLocator, 'css'))
|
|
2076
|
+
locator = `${contextLocator} >> ${locator}`
|
|
2047
2077
|
}
|
|
2048
2078
|
|
|
2049
|
-
return locator
|
|
2079
|
+
return locator
|
|
2050
2080
|
}
|
|
2051
2081
|
|
|
2052
2082
|
/**
|
|
@@ -2054,11 +2084,11 @@ class Playwright extends Helper {
|
|
|
2054
2084
|
*
|
|
2055
2085
|
*/
|
|
2056
2086
|
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
|
|
2087
|
+
locator = this._contextLocator(locator)
|
|
2088
|
+
const text = await this.page.textContent(locator)
|
|
2089
|
+
assertElementExists(text, locator)
|
|
2090
|
+
this.debugSection('Text', text)
|
|
2091
|
+
return text
|
|
2062
2092
|
}
|
|
2063
2093
|
|
|
2064
2094
|
/**
|
|
@@ -2066,51 +2096,51 @@ class Playwright extends Helper {
|
|
|
2066
2096
|
*
|
|
2067
2097
|
*/
|
|
2068
2098
|
async grabTextFromAll(locator) {
|
|
2069
|
-
const els = await this._locate(locator)
|
|
2070
|
-
const texts = []
|
|
2099
|
+
const els = await this._locate(locator)
|
|
2100
|
+
const texts = []
|
|
2071
2101
|
for (const el of els) {
|
|
2072
|
-
texts.push(await
|
|
2102
|
+
texts.push(await await el.innerText())
|
|
2073
2103
|
}
|
|
2074
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2075
|
-
return texts
|
|
2104
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2105
|
+
return texts
|
|
2076
2106
|
}
|
|
2077
2107
|
|
|
2078
2108
|
/**
|
|
2079
2109
|
* {{> grabValueFrom }}
|
|
2080
2110
|
*/
|
|
2081
2111
|
async grabValueFrom(locator) {
|
|
2082
|
-
const values = await this.grabValueFromAll(locator)
|
|
2083
|
-
assertElementExists(values, locator)
|
|
2084
|
-
this.debugSection('Value', values[0])
|
|
2085
|
-
return values[0]
|
|
2112
|
+
const values = await this.grabValueFromAll(locator)
|
|
2113
|
+
assertElementExists(values, locator)
|
|
2114
|
+
this.debugSection('Value', values[0])
|
|
2115
|
+
return values[0]
|
|
2086
2116
|
}
|
|
2087
2117
|
|
|
2088
2118
|
/**
|
|
2089
2119
|
* {{> grabValueFromAll }}
|
|
2090
2120
|
*/
|
|
2091
2121
|
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()))
|
|
2122
|
+
const els = await findFields.call(this, locator)
|
|
2123
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2124
|
+
return Promise.all(els.map((el) => el.inputValue()))
|
|
2095
2125
|
}
|
|
2096
2126
|
|
|
2097
2127
|
/**
|
|
2098
2128
|
* {{> grabHTMLFrom }}
|
|
2099
2129
|
*/
|
|
2100
2130
|
async grabHTMLFrom(locator) {
|
|
2101
|
-
const html = await this.grabHTMLFromAll(locator)
|
|
2102
|
-
assertElementExists(html, locator)
|
|
2103
|
-
this.debugSection('HTML', html[0])
|
|
2104
|
-
return html[0]
|
|
2131
|
+
const html = await this.grabHTMLFromAll(locator)
|
|
2132
|
+
assertElementExists(html, locator)
|
|
2133
|
+
this.debugSection('HTML', html[0])
|
|
2134
|
+
return html[0]
|
|
2105
2135
|
}
|
|
2106
2136
|
|
|
2107
2137
|
/**
|
|
2108
2138
|
* {{> grabHTMLFromAll }}
|
|
2109
2139
|
*/
|
|
2110
2140
|
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()))
|
|
2141
|
+
const els = await this._locate(locator)
|
|
2142
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2143
|
+
return Promise.all(els.map((el) => el.innerHTML()))
|
|
2114
2144
|
}
|
|
2115
2145
|
|
|
2116
2146
|
/**
|
|
@@ -2118,10 +2148,10 @@ class Playwright extends Helper {
|
|
|
2118
2148
|
*
|
|
2119
2149
|
*/
|
|
2120
2150
|
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]
|
|
2151
|
+
const cssValues = await this.grabCssPropertyFromAll(locator, cssProperty)
|
|
2152
|
+
assertElementExists(cssValues, locator)
|
|
2153
|
+
this.debugSection('CSS', cssValues[0])
|
|
2154
|
+
return cssValues[0]
|
|
2125
2155
|
}
|
|
2126
2156
|
|
|
2127
2157
|
/**
|
|
@@ -2129,11 +2159,15 @@ class Playwright extends Helper {
|
|
|
2129
2159
|
*
|
|
2130
2160
|
*/
|
|
2131
2161
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
2132
|
-
const els = await this._locate(locator)
|
|
2133
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2134
|
-
const cssValues = await Promise.all(
|
|
2162
|
+
const els = await this._locate(locator)
|
|
2163
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2164
|
+
const cssValues = await Promise.all(
|
|
2165
|
+
els.map((el) =>
|
|
2166
|
+
el.evaluate((el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty),
|
|
2167
|
+
),
|
|
2168
|
+
)
|
|
2135
2169
|
|
|
2136
|
-
return cssValues
|
|
2170
|
+
return cssValues
|
|
2137
2171
|
}
|
|
2138
2172
|
|
|
2139
2173
|
/**
|
|
@@ -2141,35 +2175,37 @@ class Playwright extends Helper {
|
|
|
2141
2175
|
*
|
|
2142
2176
|
*/
|
|
2143
2177
|
async seeCssPropertiesOnElements(locator, cssProperties) {
|
|
2144
|
-
const res = await this._locate(locator)
|
|
2145
|
-
assertElementExists(res, locator)
|
|
2178
|
+
const res = await this._locate(locator)
|
|
2179
|
+
assertElementExists(res, locator)
|
|
2146
2180
|
|
|
2147
|
-
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties)
|
|
2148
|
-
const elemAmount = res.length
|
|
2149
|
-
let props = []
|
|
2181
|
+
const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties)
|
|
2182
|
+
const elemAmount = res.length
|
|
2183
|
+
let props = []
|
|
2150
2184
|
|
|
2151
2185
|
for (const element of res) {
|
|
2152
2186
|
for (const prop of Object.keys(cssProperties)) {
|
|
2153
|
-
const cssProp = await this.grabCssPropertyFrom(locator, prop)
|
|
2187
|
+
const cssProp = await this.grabCssPropertyFrom(locator, prop)
|
|
2154
2188
|
if (isColorProperty(prop)) {
|
|
2155
|
-
props.push(convertColorToRGBA(cssProp))
|
|
2189
|
+
props.push(convertColorToRGBA(cssProp))
|
|
2156
2190
|
} else {
|
|
2157
|
-
props.push(cssProp)
|
|
2191
|
+
props.push(cssProp)
|
|
2158
2192
|
}
|
|
2159
2193
|
}
|
|
2160
2194
|
}
|
|
2161
2195
|
|
|
2162
|
-
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key])
|
|
2163
|
-
if (!Array.isArray(props)) props = [props]
|
|
2164
|
-
let chunked = chunkArray(props, values.length)
|
|
2196
|
+
const values = Object.keys(cssPropertiesCamelCase).map((key) => cssPropertiesCamelCase[key])
|
|
2197
|
+
if (!Array.isArray(props)) props = [props]
|
|
2198
|
+
let chunked = chunkArray(props, values.length)
|
|
2165
2199
|
chunked = chunked.filter((val) => {
|
|
2166
2200
|
for (let i = 0; i < val.length; ++i) {
|
|
2167
2201
|
// eslint-disable-next-line eqeqeq
|
|
2168
|
-
if (val[i] != values[i]) return false
|
|
2202
|
+
if (val[i] != values[i]) return false
|
|
2169
2203
|
}
|
|
2170
|
-
return true
|
|
2171
|
-
})
|
|
2172
|
-
return equals(
|
|
2204
|
+
return true
|
|
2205
|
+
})
|
|
2206
|
+
return equals(
|
|
2207
|
+
`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
2208
|
+
).assert(chunked.length, elemAmount)
|
|
2173
2209
|
}
|
|
2174
2210
|
|
|
2175
2211
|
/**
|
|
@@ -2177,31 +2213,33 @@ class Playwright extends Helper {
|
|
|
2177
2213
|
*
|
|
2178
2214
|
*/
|
|
2179
2215
|
async seeAttributesOnElements(locator, attributes) {
|
|
2180
|
-
const res = await this._locate(locator)
|
|
2181
|
-
assertElementExists(res, locator)
|
|
2216
|
+
const res = await this._locate(locator)
|
|
2217
|
+
assertElementExists(res, locator)
|
|
2182
2218
|
|
|
2183
|
-
const elemAmount = res.length
|
|
2184
|
-
const commands = []
|
|
2219
|
+
const elemAmount = res.length
|
|
2220
|
+
const commands = []
|
|
2185
2221
|
res.forEach((el) => {
|
|
2186
2222
|
Object.keys(attributes).forEach((prop) => {
|
|
2187
|
-
commands.push(el
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
let chunked = chunkArray(attrs, values.length);
|
|
2223
|
+
commands.push(el.evaluate((el, attr) => el[attr] || el.getAttribute(attr), prop))
|
|
2224
|
+
})
|
|
2225
|
+
})
|
|
2226
|
+
let attrs = await Promise.all(commands)
|
|
2227
|
+
const values = Object.keys(attributes).map((key) => attributes[key])
|
|
2228
|
+
if (!Array.isArray(attrs)) attrs = [attrs]
|
|
2229
|
+
let chunked = chunkArray(attrs, values.length)
|
|
2195
2230
|
chunked = chunked.filter((val) => {
|
|
2196
2231
|
for (let i = 0; i < val.length; ++i) {
|
|
2197
2232
|
// the attribute could be a boolean
|
|
2198
|
-
if (typeof val[i] === 'boolean') return val[i] === values[i]
|
|
2233
|
+
if (typeof val[i] === 'boolean') return val[i] === values[i]
|
|
2199
2234
|
// if the attribute doesn't exist, returns false as well
|
|
2200
|
-
if (!val[i] || !val[i].includes(values[i])) return false
|
|
2235
|
+
if (!val[i] || !val[i].includes(values[i])) return false
|
|
2201
2236
|
}
|
|
2202
|
-
return true
|
|
2203
|
-
})
|
|
2204
|
-
return equals(`all elements (${
|
|
2237
|
+
return true
|
|
2238
|
+
})
|
|
2239
|
+
return equals(`all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`).assert(
|
|
2240
|
+
chunked.length,
|
|
2241
|
+
elemAmount,
|
|
2242
|
+
)
|
|
2205
2243
|
}
|
|
2206
2244
|
|
|
2207
2245
|
/**
|
|
@@ -2209,21 +2247,21 @@ class Playwright extends Helper {
|
|
|
2209
2247
|
*
|
|
2210
2248
|
*/
|
|
2211
2249
|
async dragSlider(locator, offsetX = 0) {
|
|
2212
|
-
const src = await this._locateElement(locator)
|
|
2213
|
-
assertElementExists(src, locator, 'Slider Element')
|
|
2250
|
+
const src = await this._locateElement(locator)
|
|
2251
|
+
assertElementExists(src, locator, 'Slider Element')
|
|
2214
2252
|
|
|
2215
2253
|
// Note: Using clickablePoint private api because the .BoundingBox does not take into account iframe offsets!
|
|
2216
|
-
const sliderSource = await clickablePoint(src)
|
|
2254
|
+
const sliderSource = await clickablePoint(src)
|
|
2217
2255
|
|
|
2218
2256
|
// Drag start point
|
|
2219
|
-
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 })
|
|
2220
|
-
await this.page.mouse.down()
|
|
2257
|
+
await this.page.mouse.move(sliderSource.x, sliderSource.y, { steps: 5 })
|
|
2258
|
+
await this.page.mouse.down()
|
|
2221
2259
|
|
|
2222
2260
|
// Drag destination
|
|
2223
|
-
await this.page.mouse.move(sliderSource.x + offsetX, sliderSource.y, { steps: 5 })
|
|
2224
|
-
await this.page.mouse.up()
|
|
2261
|
+
await this.page.mouse.move(sliderSource.x + offsetX, sliderSource.y, { steps: 5 })
|
|
2262
|
+
await this.page.mouse.up()
|
|
2225
2263
|
|
|
2226
|
-
return this._waitForAction()
|
|
2264
|
+
return this._waitForAction()
|
|
2227
2265
|
}
|
|
2228
2266
|
|
|
2229
2267
|
/**
|
|
@@ -2231,10 +2269,10 @@ class Playwright extends Helper {
|
|
|
2231
2269
|
*
|
|
2232
2270
|
*/
|
|
2233
2271
|
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]
|
|
2272
|
+
const attrs = await this.grabAttributeFromAll(locator, attr)
|
|
2273
|
+
assertElementExists(attrs, locator)
|
|
2274
|
+
this.debugSection('Attribute', attrs[0])
|
|
2275
|
+
return attrs[0]
|
|
2238
2276
|
}
|
|
2239
2277
|
|
|
2240
2278
|
/**
|
|
@@ -2242,15 +2280,15 @@ class Playwright extends Helper {
|
|
|
2242
2280
|
*
|
|
2243
2281
|
*/
|
|
2244
2282
|
async grabAttributeFromAll(locator, attr) {
|
|
2245
|
-
const els = await this._locate(locator)
|
|
2246
|
-
this.debug(`Matched ${els.length} elements`)
|
|
2247
|
-
const array = []
|
|
2283
|
+
const els = await this._locate(locator)
|
|
2284
|
+
this.debug(`Matched ${els.length} elements`)
|
|
2285
|
+
const array = []
|
|
2248
2286
|
|
|
2249
2287
|
for (let index = 0; index < els.length; index++) {
|
|
2250
|
-
array.push(await els[index].getAttribute(attr))
|
|
2288
|
+
array.push(await els[index].getAttribute(attr))
|
|
2251
2289
|
}
|
|
2252
2290
|
|
|
2253
|
-
return array
|
|
2291
|
+
return array
|
|
2254
2292
|
}
|
|
2255
2293
|
|
|
2256
2294
|
/**
|
|
@@ -2258,43 +2296,43 @@ class Playwright extends Helper {
|
|
|
2258
2296
|
*
|
|
2259
2297
|
*/
|
|
2260
2298
|
async saveElementScreenshot(locator, fileName) {
|
|
2261
|
-
const outputFile = screenshotOutputFolder(fileName)
|
|
2299
|
+
const outputFile = screenshotOutputFolder(fileName)
|
|
2262
2300
|
|
|
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' })
|
|
2301
|
+
const res = await this._locateElement(locator)
|
|
2302
|
+
assertElementExists(res, locator)
|
|
2303
|
+
const elem = res
|
|
2304
|
+
this.debug(`Screenshot of ${new Locator(locator)} element has been saved to ${outputFile}`)
|
|
2305
|
+
return elem.screenshot({ path: outputFile, type: 'png' })
|
|
2268
2306
|
}
|
|
2269
2307
|
|
|
2270
2308
|
/**
|
|
2271
2309
|
* {{> saveScreenshot }}
|
|
2272
2310
|
*/
|
|
2273
2311
|
async saveScreenshot(fileName, fullPage) {
|
|
2274
|
-
const fullPageOption = fullPage || this.options.fullPageScreenshots
|
|
2275
|
-
let outputFile = screenshotOutputFolder(fileName)
|
|
2312
|
+
const fullPageOption = fullPage || this.options.fullPageScreenshots
|
|
2313
|
+
let outputFile = screenshotOutputFolder(fileName)
|
|
2276
2314
|
|
|
2277
|
-
this.debug(`Screenshot is saving to ${outputFile}`)
|
|
2315
|
+
this.debug(`Screenshot is saving to ${outputFile}`)
|
|
2278
2316
|
|
|
2279
2317
|
await this.page.screenshot({
|
|
2280
2318
|
path: outputFile,
|
|
2281
2319
|
fullPage: fullPageOption,
|
|
2282
2320
|
type: 'png',
|
|
2283
|
-
})
|
|
2321
|
+
})
|
|
2284
2322
|
|
|
2285
2323
|
if (this.activeSessionName) {
|
|
2286
2324
|
for (const sessionName in this.sessionPages) {
|
|
2287
|
-
const activeSessionPage = this.sessionPages[sessionName]
|
|
2288
|
-
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`)
|
|
2325
|
+
const activeSessionPage = this.sessionPages[sessionName]
|
|
2326
|
+
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`)
|
|
2289
2327
|
|
|
2290
|
-
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`)
|
|
2328
|
+
this.debug(`${sessionName} - Screenshot is saving to ${outputFile}`)
|
|
2291
2329
|
|
|
2292
2330
|
if (activeSessionPage) {
|
|
2293
2331
|
await activeSessionPage.screenshot({
|
|
2294
2332
|
path: outputFile,
|
|
2295
2333
|
fullPage: fullPageOption,
|
|
2296
2334
|
type: 'png',
|
|
2297
|
-
})
|
|
2335
|
+
})
|
|
2298
2336
|
}
|
|
2299
2337
|
}
|
|
2300
2338
|
}
|
|
@@ -2318,20 +2356,23 @@ class Playwright extends Helper {
|
|
|
2318
2356
|
* @returns {Promise<object>} response
|
|
2319
2357
|
*/
|
|
2320
2358
|
async makeApiRequest(method, url, options) {
|
|
2321
|
-
method = method.toLowerCase()
|
|
2322
|
-
const allowedMethods = ['get', 'post', 'patch', 'head', 'fetch', 'delete']
|
|
2359
|
+
method = method.toLowerCase()
|
|
2360
|
+
const allowedMethods = ['get', 'post', 'patch', 'head', 'fetch', 'delete']
|
|
2323
2361
|
if (!allowedMethods.includes(method)) {
|
|
2324
|
-
throw new Error(
|
|
2362
|
+
throw new Error(
|
|
2363
|
+
`Method ${method} is not allowed, use the one from a list ${allowedMethods} or switch to using REST helper`,
|
|
2364
|
+
)
|
|
2325
2365
|
}
|
|
2326
2366
|
|
|
2327
|
-
if (url.startsWith('/')) {
|
|
2328
|
-
|
|
2329
|
-
this.
|
|
2367
|
+
if (url.startsWith('/')) {
|
|
2368
|
+
// local url
|
|
2369
|
+
url = this.options.url + url
|
|
2370
|
+
this.debugSection('URL', url)
|
|
2330
2371
|
}
|
|
2331
2372
|
|
|
2332
|
-
const response = await this.page.request[method](url, options)
|
|
2333
|
-
this.debugSection('Status', response.status())
|
|
2334
|
-
this.debugSection('Response', await response.text())
|
|
2373
|
+
const response = await this.page.request[method](url, options)
|
|
2374
|
+
this.debugSection('Status', response.status())
|
|
2375
|
+
this.debugSection('Response', await response.text())
|
|
2335
2376
|
|
|
2336
2377
|
// hook to allow JSON response handle this
|
|
2337
2378
|
if (this.config.onResponse) {
|
|
@@ -2340,68 +2381,83 @@ class Playwright extends Helper {
|
|
|
2340
2381
|
status: response.status(),
|
|
2341
2382
|
statusText: response.statusText(),
|
|
2342
2383
|
headers: response.headers(),
|
|
2343
|
-
}
|
|
2344
|
-
this.config.onResponse(axiosResponse)
|
|
2384
|
+
}
|
|
2385
|
+
this.config.onResponse(axiosResponse)
|
|
2345
2386
|
}
|
|
2346
2387
|
|
|
2347
|
-
return response
|
|
2388
|
+
return response
|
|
2348
2389
|
}
|
|
2349
2390
|
|
|
2350
2391
|
async _failed(test) {
|
|
2351
|
-
await this._withinEnd()
|
|
2392
|
+
await this._withinEnd()
|
|
2352
2393
|
|
|
2353
2394
|
if (!test.artifacts) {
|
|
2354
|
-
test.artifacts = {}
|
|
2395
|
+
test.artifacts = {}
|
|
2355
2396
|
}
|
|
2356
2397
|
|
|
2357
2398
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2358
|
-
test.artifacts.video =
|
|
2399
|
+
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.failed`)
|
|
2359
2400
|
for (const sessionName in this.sessionPages) {
|
|
2360
|
-
test.artifacts[`video_${sessionName}`] =
|
|
2401
|
+
test.artifacts[`video_${sessionName}`] = saveVideoForPage(
|
|
2402
|
+
this.sessionPages[sessionName],
|
|
2403
|
+
`${test.title}_${sessionName}.failed`,
|
|
2404
|
+
)
|
|
2361
2405
|
}
|
|
2362
2406
|
}
|
|
2363
2407
|
|
|
2364
2408
|
if (this.options.trace) {
|
|
2365
|
-
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.failed`)
|
|
2409
|
+
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.failed`)
|
|
2366
2410
|
for (const sessionName in this.sessionPages) {
|
|
2367
|
-
if (!this.sessionPages[sessionName].context) continue
|
|
2368
|
-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2411
|
+
if (!this.sessionPages[sessionName].context) continue
|
|
2412
|
+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2413
|
+
this.sessionPages[sessionName].context,
|
|
2414
|
+
`${test.title}_${sessionName}.failed`,
|
|
2415
|
+
)
|
|
2369
2416
|
}
|
|
2370
2417
|
}
|
|
2371
2418
|
|
|
2372
2419
|
if (this.options.recordHar) {
|
|
2373
|
-
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2420
|
+
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2374
2421
|
}
|
|
2375
2422
|
}
|
|
2376
2423
|
|
|
2377
2424
|
async _passed(test) {
|
|
2378
2425
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2379
2426
|
if (this.options.keepVideoForPassedTests) {
|
|
2380
|
-
test.artifacts.video =
|
|
2427
|
+
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.passed`)
|
|
2381
2428
|
for (const sessionName of Object.keys(this.sessionPages)) {
|
|
2382
|
-
test.artifacts[`video_${sessionName}`] =
|
|
2429
|
+
test.artifacts[`video_${sessionName}`] = saveVideoForPage(
|
|
2430
|
+
this.sessionPages[sessionName],
|
|
2431
|
+
`${test.title}_${sessionName}.passed`,
|
|
2432
|
+
)
|
|
2383
2433
|
}
|
|
2384
2434
|
} else {
|
|
2385
|
-
this.page
|
|
2435
|
+
this.page
|
|
2436
|
+
.video()
|
|
2437
|
+
.delete()
|
|
2438
|
+
.catch((e) => {})
|
|
2386
2439
|
}
|
|
2387
2440
|
}
|
|
2388
2441
|
|
|
2389
2442
|
if (this.options.trace) {
|
|
2390
2443
|
if (this.options.keepTraceForPassedTests) {
|
|
2391
2444
|
if (this.options.trace) {
|
|
2392
|
-
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.passed`)
|
|
2445
|
+
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.passed`)
|
|
2393
2446
|
for (const sessionName in this.sessionPages) {
|
|
2394
|
-
if (!this.sessionPages[sessionName].context) continue
|
|
2395
|
-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2447
|
+
if (!this.sessionPages[sessionName].context) continue
|
|
2448
|
+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2449
|
+
this.sessionPages[sessionName].context,
|
|
2450
|
+
`${test.title}_${sessionName}.passed`,
|
|
2451
|
+
)
|
|
2396
2452
|
}
|
|
2397
2453
|
}
|
|
2398
2454
|
} else {
|
|
2399
|
-
await this.browserContext.tracing.stop()
|
|
2455
|
+
await this.browserContext.tracing.stop()
|
|
2400
2456
|
}
|
|
2401
2457
|
}
|
|
2402
2458
|
|
|
2403
2459
|
if (this.options.recordHar) {
|
|
2404
|
-
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2460
|
+
test.artifacts.har = this.currentRunningTest.artifacts.har
|
|
2405
2461
|
}
|
|
2406
2462
|
}
|
|
2407
2463
|
|
|
@@ -2409,62 +2465,99 @@ class Playwright extends Helper {
|
|
|
2409
2465
|
* {{> wait }}
|
|
2410
2466
|
*/
|
|
2411
2467
|
async wait(sec) {
|
|
2412
|
-
return new Promise((
|
|
2413
|
-
setTimeout(done, sec * 1000)
|
|
2414
|
-
})
|
|
2468
|
+
return new Promise((done) => {
|
|
2469
|
+
setTimeout(done, sec * 1000)
|
|
2470
|
+
})
|
|
2415
2471
|
}
|
|
2416
2472
|
|
|
2417
2473
|
/**
|
|
2418
2474
|
* {{> waitForEnabled }}
|
|
2419
2475
|
*/
|
|
2420
2476
|
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()
|
|
2477
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2478
|
+
locator = new Locator(locator, 'css')
|
|
2479
|
+
|
|
2480
|
+
let waiter
|
|
2481
|
+
const context = await this._getContext()
|
|
2426
2482
|
if (!locator.isXPath()) {
|
|
2427
2483
|
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 })
|
|
2484
|
+
return Array.from(document.querySelectorAll(locator)).filter((el) => !el.disabled).length > 0
|
|
2485
|
+
}
|
|
2486
|
+
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2431
2487
|
} else {
|
|
2432
2488
|
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 })
|
|
2489
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2490
|
+
return $XPath(null, locator).filter((el) => !el.disabled).length > 0
|
|
2491
|
+
}
|
|
2492
|
+
waiter = context.waitForFunction(enabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2493
|
+
}
|
|
2494
|
+
return waiter.catch((err) => {
|
|
2495
|
+
throw new Error(
|
|
2496
|
+
`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2497
|
+
)
|
|
2498
|
+
})
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
/**
|
|
2502
|
+
* {{> waitForDisabled }}
|
|
2503
|
+
*/
|
|
2504
|
+
async waitForDisabled(locator, sec) {
|
|
2505
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2506
|
+
locator = new Locator(locator, 'css')
|
|
2507
|
+
|
|
2508
|
+
let waiter
|
|
2509
|
+
const context = await this._getContext()
|
|
2510
|
+
if (!locator.isXPath()) {
|
|
2511
|
+
const valueFn = function ([locator]) {
|
|
2512
|
+
return Array.from(document.querySelectorAll(locator)).filter((el) => el.disabled).length > 0
|
|
2513
|
+
}
|
|
2514
|
+
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2515
|
+
} else {
|
|
2516
|
+
const disabledFn = function ([locator, $XPath]) {
|
|
2517
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2518
|
+
return $XPath(null, locator).filter((el) => el.disabled).length > 0
|
|
2519
|
+
}
|
|
2520
|
+
waiter = context.waitForFunction(disabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2437
2521
|
}
|
|
2438
2522
|
return waiter.catch((err) => {
|
|
2439
|
-
throw new Error(
|
|
2440
|
-
|
|
2523
|
+
throw new Error(
|
|
2524
|
+
`element (${locator.toString()}) is still enabled after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2525
|
+
)
|
|
2526
|
+
})
|
|
2441
2527
|
}
|
|
2442
2528
|
|
|
2443
2529
|
/**
|
|
2444
2530
|
* {{> waitForValue }}
|
|
2445
2531
|
*/
|
|
2446
2532
|
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()
|
|
2533
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2534
|
+
const locator = new Locator(field, 'css')
|
|
2535
|
+
const matcher = await this.context
|
|
2536
|
+
let waiter
|
|
2537
|
+
const context = await this._getContext()
|
|
2452
2538
|
if (!locator.isXPath()) {
|
|
2453
2539
|
const valueFn = function ([locator, value]) {
|
|
2454
|
-
return
|
|
2455
|
-
|
|
2456
|
-
|
|
2540
|
+
return (
|
|
2541
|
+
Array.from(document.querySelectorAll(locator)).filter((el) => (el.value || '').indexOf(value) !== -1).length >
|
|
2542
|
+
0
|
|
2543
|
+
)
|
|
2544
|
+
}
|
|
2545
|
+
waiter = context.waitForFunction(valueFn, [locator.value, value], { timeout: waitTimeout })
|
|
2457
2546
|
} else {
|
|
2458
2547
|
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], {
|
|
2548
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2549
|
+
return $XPath(null, locator).filter((el) => (el.value || '').indexOf(value) !== -1).length > 0
|
|
2550
|
+
}
|
|
2551
|
+
waiter = context.waitForFunction(valueFn, [locator.value, $XPath.toString(), value], {
|
|
2552
|
+
timeout: waitTimeout,
|
|
2553
|
+
})
|
|
2463
2554
|
}
|
|
2464
2555
|
return waiter.catch((err) => {
|
|
2465
|
-
const loc = locator.toString()
|
|
2466
|
-
throw new Error(
|
|
2467
|
-
|
|
2556
|
+
const loc = locator.toString()
|
|
2557
|
+
throw new Error(
|
|
2558
|
+
`element (${loc}) is not in DOM or there is no element(${loc}) with value "${value}" after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2559
|
+
)
|
|
2560
|
+
})
|
|
2468
2561
|
}
|
|
2469
2562
|
|
|
2470
2563
|
/**
|
|
@@ -2472,38 +2565,44 @@ class Playwright extends Helper {
|
|
|
2472
2565
|
*
|
|
2473
2566
|
*/
|
|
2474
2567
|
async waitNumberOfVisibleElements(locator, num, sec) {
|
|
2475
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2476
|
-
locator = new Locator(locator, 'css')
|
|
2568
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2569
|
+
locator = new Locator(locator, 'css')
|
|
2477
2570
|
|
|
2478
|
-
let waiter
|
|
2479
|
-
const context = await this._getContext()
|
|
2571
|
+
let waiter
|
|
2572
|
+
const context = await this._getContext()
|
|
2480
2573
|
if (locator.isCSS()) {
|
|
2481
2574
|
const visibleFn = function ([locator, num]) {
|
|
2482
|
-
const els = document.querySelectorAll(locator)
|
|
2575
|
+
const els = document.querySelectorAll(locator)
|
|
2483
2576
|
if (!els || els.length === 0) {
|
|
2484
|
-
return false
|
|
2577
|
+
return false
|
|
2485
2578
|
}
|
|
2486
|
-
return Array.prototype.filter.call(els, el => el.offsetParent !== null).length === num
|
|
2487
|
-
}
|
|
2488
|
-
waiter = context.waitForFunction(visibleFn, [locator.value, num], { timeout: waitTimeout })
|
|
2579
|
+
return Array.prototype.filter.call(els, (el) => el.offsetParent !== null).length === num
|
|
2580
|
+
}
|
|
2581
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, num], { timeout: waitTimeout })
|
|
2489
2582
|
} else {
|
|
2490
2583
|
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], {
|
|
2584
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2585
|
+
return $XPath(null, locator).filter((el) => el.offsetParent !== null).length === num
|
|
2586
|
+
}
|
|
2587
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString(), num], {
|
|
2588
|
+
timeout: waitTimeout,
|
|
2589
|
+
})
|
|
2495
2590
|
}
|
|
2496
2591
|
return waiter.catch((err) => {
|
|
2497
|
-
throw new Error(
|
|
2498
|
-
|
|
2592
|
+
throw new Error(
|
|
2593
|
+
`The number of elements (${locator.toString()}) is not ${num} after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2594
|
+
)
|
|
2595
|
+
})
|
|
2499
2596
|
}
|
|
2500
2597
|
|
|
2501
2598
|
/**
|
|
2502
2599
|
* {{> waitForClickable }}
|
|
2503
2600
|
*/
|
|
2504
2601
|
async waitForClickable(locator, waitTimeout) {
|
|
2505
|
-
console.log(
|
|
2506
|
-
|
|
2602
|
+
console.log(
|
|
2603
|
+
'I.waitForClickable is DEPRECATED: This is no longer needed, Playwright automatically waits for element to be clickable',
|
|
2604
|
+
)
|
|
2605
|
+
console.log('Remove usage of this function')
|
|
2507
2606
|
}
|
|
2508
2607
|
|
|
2509
2608
|
/**
|
|
@@ -2511,14 +2610,16 @@ class Playwright extends Helper {
|
|
|
2511
2610
|
*
|
|
2512
2611
|
*/
|
|
2513
2612
|
async waitForElement(locator, sec) {
|
|
2514
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2515
|
-
locator = new Locator(locator, 'css')
|
|
2613
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2614
|
+
locator = new Locator(locator, 'css')
|
|
2516
2615
|
|
|
2517
|
-
const context = await this._getContext()
|
|
2616
|
+
const context = await this._getContext()
|
|
2518
2617
|
try {
|
|
2519
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' })
|
|
2618
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' })
|
|
2520
2619
|
} catch (e) {
|
|
2521
|
-
throw new Error(
|
|
2620
|
+
throw new Error(
|
|
2621
|
+
`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${e.message}`,
|
|
2622
|
+
)
|
|
2522
2623
|
}
|
|
2523
2624
|
}
|
|
2524
2625
|
|
|
@@ -2528,28 +2629,28 @@ class Playwright extends Helper {
|
|
|
2528
2629
|
* {{> waitForVisible }}
|
|
2529
2630
|
*/
|
|
2530
2631
|
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
|
|
2632
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2633
|
+
locator = new Locator(locator, 'css')
|
|
2634
|
+
const context = await this._getContext()
|
|
2635
|
+
let count = 0
|
|
2535
2636
|
|
|
2536
2637
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2537
|
-
let waiter
|
|
2638
|
+
let waiter
|
|
2538
2639
|
if (this.frame) {
|
|
2539
2640
|
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)
|
|
2641
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isVisible()
|
|
2642
|
+
await this.wait(1)
|
|
2643
|
+
count += 1000
|
|
2644
|
+
if (waiter) break
|
|
2645
|
+
} while (count <= waitTimeout)
|
|
2545
2646
|
|
|
2546
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec.`)
|
|
2647
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec.`)
|
|
2547
2648
|
}
|
|
2548
2649
|
|
|
2549
2650
|
try {
|
|
2550
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2651
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2551
2652
|
} catch (e) {
|
|
2552
|
-
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2653
|
+
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2553
2654
|
}
|
|
2554
2655
|
}
|
|
2555
2656
|
|
|
@@ -2557,29 +2658,29 @@ class Playwright extends Helper {
|
|
|
2557
2658
|
* {{> waitForInvisible }}
|
|
2558
2659
|
*/
|
|
2559
2660
|
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
|
|
2661
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2662
|
+
locator = new Locator(locator, 'css')
|
|
2663
|
+
const context = await this._getContext()
|
|
2664
|
+
let waiter
|
|
2665
|
+
let count = 0
|
|
2565
2666
|
|
|
2566
2667
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2567
2668
|
if (this.frame) {
|
|
2568
2669
|
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)
|
|
2670
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden()
|
|
2671
|
+
await this.wait(1)
|
|
2672
|
+
count += 1000
|
|
2673
|
+
if (waiter) break
|
|
2674
|
+
} while (count <= waitTimeout)
|
|
2574
2675
|
|
|
2575
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec.`)
|
|
2576
|
-
return
|
|
2676
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec.`)
|
|
2677
|
+
return
|
|
2577
2678
|
}
|
|
2578
2679
|
|
|
2579
2680
|
try {
|
|
2580
|
-
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2681
|
+
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2581
2682
|
} catch (e) {
|
|
2582
|
-
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2683
|
+
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2583
2684
|
}
|
|
2584
2685
|
}
|
|
2585
2686
|
|
|
@@ -2587,143 +2688,168 @@ class Playwright extends Helper {
|
|
|
2587
2688
|
* {{> waitToHide }}
|
|
2588
2689
|
*/
|
|
2589
2690
|
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
|
|
2691
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2692
|
+
locator = new Locator(locator, 'css')
|
|
2693
|
+
const context = await this._getContext()
|
|
2694
|
+
let waiter
|
|
2695
|
+
let count = 0
|
|
2595
2696
|
|
|
2596
2697
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2597
2698
|
if (this.frame) {
|
|
2598
2699
|
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)
|
|
2700
|
+
waiter = await this.frame.locator(buildLocatorString(locator)).first().isHidden()
|
|
2701
|
+
await this.wait(1)
|
|
2702
|
+
count += 1000
|
|
2703
|
+
if (waiter) break
|
|
2704
|
+
} while (count <= waitTimeout)
|
|
2604
2705
|
|
|
2605
|
-
if (!waiter) throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec.`)
|
|
2606
|
-
return
|
|
2706
|
+
if (!waiter) throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec.`)
|
|
2707
|
+
return
|
|
2607
2708
|
}
|
|
2608
2709
|
|
|
2609
|
-
return context
|
|
2610
|
-
|
|
2611
|
-
|
|
2710
|
+
return context
|
|
2711
|
+
.locator(buildLocatorString(locator))
|
|
2712
|
+
.first()
|
|
2713
|
+
.waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2714
|
+
.catch((err) => {
|
|
2715
|
+
throw new Error(
|
|
2716
|
+
`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2717
|
+
)
|
|
2718
|
+
})
|
|
2612
2719
|
}
|
|
2613
2720
|
|
|
2614
2721
|
/**
|
|
2615
2722
|
* {{> waitForNumberOfTabs }}
|
|
2616
2723
|
*/
|
|
2617
2724
|
async waitForNumberOfTabs(expectedTabs, sec) {
|
|
2618
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2619
|
-
let currentTabs
|
|
2620
|
-
let count = 0
|
|
2725
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2726
|
+
let currentTabs
|
|
2727
|
+
let count = 0
|
|
2621
2728
|
|
|
2622
2729
|
do {
|
|
2623
|
-
currentTabs = await this.grabNumberOfOpenTabs()
|
|
2624
|
-
await this.wait(1)
|
|
2625
|
-
count += 1000
|
|
2626
|
-
if (currentTabs >= expectedTabs) return
|
|
2627
|
-
} while (count <= waitTimeout)
|
|
2730
|
+
currentTabs = await this.grabNumberOfOpenTabs()
|
|
2731
|
+
await this.wait(1)
|
|
2732
|
+
count += 1000
|
|
2733
|
+
if (currentTabs >= expectedTabs) return
|
|
2734
|
+
} while (count <= waitTimeout)
|
|
2628
2735
|
|
|
2629
|
-
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`)
|
|
2736
|
+
throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`)
|
|
2630
2737
|
}
|
|
2631
2738
|
|
|
2632
2739
|
async _getContext() {
|
|
2633
2740
|
if (this.context && this.context.constructor.name === 'FrameLocator') {
|
|
2634
|
-
return this.context
|
|
2741
|
+
return this.context
|
|
2635
2742
|
}
|
|
2636
|
-
return this.page
|
|
2743
|
+
return this.page
|
|
2637
2744
|
}
|
|
2638
2745
|
|
|
2639
2746
|
/**
|
|
2640
2747
|
* {{> waitInUrl }}
|
|
2641
2748
|
*/
|
|
2642
2749
|
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
|
-
|
|
2750
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2751
|
+
|
|
2752
|
+
return this.page
|
|
2753
|
+
.waitForFunction(
|
|
2754
|
+
(urlPart) => {
|
|
2755
|
+
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
|
|
2756
|
+
return currUrl.indexOf(urlPart) > -1
|
|
2757
|
+
},
|
|
2758
|
+
urlPart,
|
|
2759
|
+
{ timeout: waitTimeout },
|
|
2760
|
+
)
|
|
2761
|
+
.catch(async (e) => {
|
|
2762
|
+
const currUrl = await this._getPageUrl() // Required because the waitForFunction can't return data.
|
|
2763
|
+
if (/Timeout/i.test(e.message)) {
|
|
2764
|
+
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
|
|
2765
|
+
} else {
|
|
2766
|
+
throw e
|
|
2767
|
+
}
|
|
2768
|
+
})
|
|
2656
2769
|
}
|
|
2657
2770
|
|
|
2658
2771
|
/**
|
|
2659
2772
|
* {{> waitUrlEquals }}
|
|
2660
2773
|
*/
|
|
2661
2774
|
async waitUrlEquals(urlPart, sec = null) {
|
|
2662
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2775
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2663
2776
|
|
|
2664
|
-
const baseUrl = this.options.url
|
|
2777
|
+
const baseUrl = this.options.url
|
|
2665
2778
|
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
|
-
|
|
2779
|
+
urlPart = baseUrl + urlPart
|
|
2780
|
+
}
|
|
2781
|
+
|
|
2782
|
+
return this.page
|
|
2783
|
+
.waitForFunction(
|
|
2784
|
+
(urlPart) => {
|
|
2785
|
+
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
|
|
2786
|
+
return currUrl.indexOf(urlPart) > -1
|
|
2787
|
+
},
|
|
2788
|
+
urlPart,
|
|
2789
|
+
{ timeout: waitTimeout },
|
|
2790
|
+
)
|
|
2791
|
+
.catch(async (e) => {
|
|
2792
|
+
const currUrl = await this._getPageUrl() // Required because the waitForFunction can't return data.
|
|
2793
|
+
if (/Timeout/i.test(e.message)) {
|
|
2794
|
+
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
|
|
2795
|
+
} else {
|
|
2796
|
+
throw e
|
|
2797
|
+
}
|
|
2798
|
+
})
|
|
2680
2799
|
}
|
|
2681
2800
|
|
|
2682
2801
|
/**
|
|
2683
2802
|
* {{> waitForText }}
|
|
2684
2803
|
*/
|
|
2685
2804
|
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
|
|
2805
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2806
|
+
const errorMessage = `Text "${text}" was not found on page after ${waitTimeout / 1000} sec.`
|
|
2807
|
+
let waiter
|
|
2689
2808
|
|
|
2690
|
-
const contextObject = await this._getContext()
|
|
2809
|
+
const contextObject = await this._getContext()
|
|
2691
2810
|
|
|
2692
2811
|
if (context) {
|
|
2693
|
-
const locator = new Locator(context, 'css')
|
|
2812
|
+
const locator = new Locator(context, 'css')
|
|
2694
2813
|
if (!locator.isXPath()) {
|
|
2695
2814
|
try {
|
|
2696
|
-
await contextObject
|
|
2815
|
+
await contextObject
|
|
2816
|
+
.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`)
|
|
2817
|
+
.first()
|
|
2818
|
+
.waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2697
2819
|
} catch (e) {
|
|
2698
|
-
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2820
|
+
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2699
2821
|
}
|
|
2700
2822
|
}
|
|
2701
2823
|
|
|
2702
2824
|
if (locator.isXPath()) {
|
|
2703
2825
|
try {
|
|
2704
|
-
await contextObject.waitForFunction(
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2826
|
+
await contextObject.waitForFunction(
|
|
2827
|
+
([locator, text, $XPath]) => {
|
|
2828
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
2829
|
+
const el = $XPath(null, locator)
|
|
2830
|
+
if (!el.length) return false
|
|
2831
|
+
return el[0].innerText.indexOf(text) > -1
|
|
2832
|
+
},
|
|
2833
|
+
[locator.value, text, $XPath.toString()],
|
|
2834
|
+
{ timeout: waitTimeout },
|
|
2835
|
+
)
|
|
2710
2836
|
} catch (e) {
|
|
2711
|
-
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2837
|
+
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2712
2838
|
}
|
|
2713
2839
|
}
|
|
2714
2840
|
} else {
|
|
2715
2841
|
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2716
2842
|
// eslint-disable-next-line no-lonely-if
|
|
2717
|
-
const _contextObject = this.frame ? this.frame : contextObject
|
|
2718
|
-
let count = 0
|
|
2843
|
+
const _contextObject = this.frame ? this.frame : contextObject
|
|
2844
|
+
let count = 0
|
|
2719
2845
|
do {
|
|
2720
|
-
waiter = await _contextObject.locator(`:has-text("${text}")`).first().isVisible()
|
|
2721
|
-
if (waiter) break
|
|
2722
|
-
await this.wait(1)
|
|
2723
|
-
count += 1000
|
|
2724
|
-
} while (count <= waitTimeout)
|
|
2846
|
+
waiter = await _contextObject.locator(`:has-text("${text}")`).first().isVisible()
|
|
2847
|
+
if (waiter) break
|
|
2848
|
+
await this.wait(1)
|
|
2849
|
+
count += 1000
|
|
2850
|
+
} while (count <= waitTimeout)
|
|
2725
2851
|
|
|
2726
|
-
if (!waiter) throw new Error(`${errorMessage}`)
|
|
2852
|
+
if (!waiter) throw new Error(`${errorMessage}`)
|
|
2727
2853
|
}
|
|
2728
2854
|
}
|
|
2729
2855
|
|
|
@@ -2739,8 +2865,8 @@ class Playwright extends Helper {
|
|
|
2739
2865
|
* @param {?number} [sec=null] seconds to wait
|
|
2740
2866
|
*/
|
|
2741
2867
|
async waitForRequest(urlOrPredicate, sec = null) {
|
|
2742
|
-
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2743
|
-
return this.page.waitForRequest(urlOrPredicate, { timeout })
|
|
2868
|
+
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2869
|
+
return this.page.waitForRequest(urlOrPredicate, { timeout })
|
|
2744
2870
|
}
|
|
2745
2871
|
|
|
2746
2872
|
/**
|
|
@@ -2755,8 +2881,8 @@ class Playwright extends Helper {
|
|
|
2755
2881
|
* @param {?number} [sec=null] number of seconds to wait
|
|
2756
2882
|
*/
|
|
2757
2883
|
async waitForResponse(urlOrPredicate, sec = null) {
|
|
2758
|
-
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2759
|
-
return this.page.waitForResponse(urlOrPredicate, { timeout })
|
|
2884
|
+
const timeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2885
|
+
return this.page.waitForResponse(urlOrPredicate, { timeout })
|
|
2760
2886
|
}
|
|
2761
2887
|
|
|
2762
2888
|
/**
|
|
@@ -2766,51 +2892,51 @@ class Playwright extends Helper {
|
|
|
2766
2892
|
if (Number.isInteger(locator)) {
|
|
2767
2893
|
// Select by frame index of current context
|
|
2768
2894
|
|
|
2769
|
-
let childFrames = null
|
|
2895
|
+
let childFrames = null
|
|
2770
2896
|
if (this.context && typeof this.context.childFrames === 'function') {
|
|
2771
|
-
childFrames = this.context.childFrames()
|
|
2897
|
+
childFrames = this.context.childFrames()
|
|
2772
2898
|
} else {
|
|
2773
|
-
childFrames = this.page.mainFrame().childFrames()
|
|
2899
|
+
childFrames = this.page.mainFrame().childFrames()
|
|
2774
2900
|
}
|
|
2775
2901
|
|
|
2776
2902
|
if (locator >= 0 && locator < childFrames.length) {
|
|
2777
|
-
this.context = await this.page.frameLocator('iframe').nth(locator)
|
|
2778
|
-
this.contextLocator = locator
|
|
2903
|
+
this.context = await this.page.frameLocator('iframe').nth(locator)
|
|
2904
|
+
this.contextLocator = locator
|
|
2779
2905
|
} else {
|
|
2780
|
-
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath')
|
|
2906
|
+
throw new Error('Element #invalidIframeSelector was not found by text|CSS|XPath')
|
|
2781
2907
|
}
|
|
2782
|
-
return
|
|
2908
|
+
return
|
|
2783
2909
|
}
|
|
2784
2910
|
|
|
2785
2911
|
if (!locator) {
|
|
2786
|
-
this.context = this.page
|
|
2787
|
-
this.contextLocator = null
|
|
2788
|
-
this.frame = null
|
|
2789
|
-
return
|
|
2912
|
+
this.context = this.page
|
|
2913
|
+
this.contextLocator = null
|
|
2914
|
+
this.frame = null
|
|
2915
|
+
return
|
|
2790
2916
|
}
|
|
2791
2917
|
|
|
2792
2918
|
// iframe by selector
|
|
2793
|
-
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2794
|
-
const frame = await this._locateElement(locator)
|
|
2919
|
+
locator = buildLocatorString(new Locator(locator, 'css'))
|
|
2920
|
+
const frame = await this._locateElement(locator)
|
|
2795
2921
|
|
|
2796
2922
|
if (!frame) {
|
|
2797
|
-
throw new Error(`Frame ${JSON.stringify(locator)} was not found by text|CSS|XPath`)
|
|
2923
|
+
throw new Error(`Frame ${JSON.stringify(locator)} was not found by text|CSS|XPath`)
|
|
2798
2924
|
}
|
|
2799
2925
|
|
|
2800
2926
|
if (this.frame) {
|
|
2801
|
-
this.frame = await this.frame.frameLocator(locator)
|
|
2927
|
+
this.frame = await this.frame.frameLocator(locator)
|
|
2802
2928
|
} else {
|
|
2803
|
-
this.frame = await this.page.frameLocator(locator)
|
|
2929
|
+
this.frame = await this.page.frameLocator(locator)
|
|
2804
2930
|
}
|
|
2805
2931
|
|
|
2806
|
-
const contentFrame = this.frame
|
|
2932
|
+
const contentFrame = this.frame
|
|
2807
2933
|
|
|
2808
2934
|
if (contentFrame) {
|
|
2809
|
-
this.context = contentFrame
|
|
2810
|
-
this.contextLocator = null
|
|
2935
|
+
this.context = contentFrame
|
|
2936
|
+
this.contextLocator = null
|
|
2811
2937
|
} else {
|
|
2812
|
-
this.context = this.page.frame(this.page.frames()[1].name())
|
|
2813
|
-
this.contextLocator = locator
|
|
2938
|
+
this.context = this.page.frame(this.page.frames()[1].name())
|
|
2939
|
+
this.contextLocator = locator
|
|
2814
2940
|
}
|
|
2815
2941
|
}
|
|
2816
2942
|
|
|
@@ -2818,17 +2944,17 @@ class Playwright extends Helper {
|
|
|
2818
2944
|
* {{> waitForFunction }}
|
|
2819
2945
|
*/
|
|
2820
2946
|
async waitForFunction(fn, argsOrSec = null, sec = null) {
|
|
2821
|
-
let args = []
|
|
2947
|
+
let args = []
|
|
2822
2948
|
if (argsOrSec) {
|
|
2823
2949
|
if (Array.isArray(argsOrSec)) {
|
|
2824
|
-
args = argsOrSec
|
|
2950
|
+
args = argsOrSec
|
|
2825
2951
|
} else if (typeof argsOrSec === 'number') {
|
|
2826
|
-
sec = argsOrSec
|
|
2952
|
+
sec = argsOrSec
|
|
2827
2953
|
}
|
|
2828
2954
|
}
|
|
2829
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2830
|
-
const context = await this._getContext()
|
|
2831
|
-
return context.waitForFunction(fn, args, { timeout: waitTimeout })
|
|
2955
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2956
|
+
const context = await this._getContext()
|
|
2957
|
+
return context.waitForFunction(fn, args, { timeout: waitTimeout })
|
|
2832
2958
|
}
|
|
2833
2959
|
|
|
2834
2960
|
/**
|
|
@@ -2840,13 +2966,13 @@ class Playwright extends Helper {
|
|
|
2840
2966
|
*/
|
|
2841
2967
|
async waitForNavigation(options = {}) {
|
|
2842
2968
|
console.log(`waitForNavigation deprecated:
|
|
2843
|
-
* This method is inherently racy, please use 'waitForURL' instead.`)
|
|
2969
|
+
* This method is inherently racy, please use 'waitForURL' instead.`)
|
|
2844
2970
|
options = {
|
|
2845
2971
|
timeout: this.options.getPageTimeout,
|
|
2846
2972
|
waitUntil: this.options.waitForNavigation,
|
|
2847
2973
|
...options,
|
|
2848
|
-
}
|
|
2849
|
-
return this.page.waitForNavigation(options)
|
|
2974
|
+
}
|
|
2975
|
+
return this.page.waitForNavigation(options)
|
|
2850
2976
|
}
|
|
2851
2977
|
|
|
2852
2978
|
/**
|
|
@@ -2862,41 +2988,44 @@ class Playwright extends Helper {
|
|
|
2862
2988
|
timeout: this.options.getPageTimeout,
|
|
2863
2989
|
waitUntil: this.options.waitForNavigation,
|
|
2864
2990
|
...options,
|
|
2865
|
-
}
|
|
2866
|
-
return this.page.waitForURL(url, options)
|
|
2991
|
+
}
|
|
2992
|
+
return this.page.waitForURL(url, options)
|
|
2867
2993
|
}
|
|
2868
2994
|
|
|
2869
2995
|
async waitUntilExists(locator, sec) {
|
|
2870
2996
|
console.log(`waitUntilExists deprecated:
|
|
2871
2997
|
* 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)
|
|
2998
|
+
* use 'waitForDetached to wait for element to be removed'`)
|
|
2999
|
+
return this.waitForDetached(locator, sec)
|
|
2874
3000
|
}
|
|
2875
3001
|
|
|
2876
3002
|
/**
|
|
2877
3003
|
* {{> waitForDetached }}
|
|
2878
3004
|
*/
|
|
2879
3005
|
async waitForDetached(locator, sec) {
|
|
2880
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2881
|
-
locator = new Locator(locator, 'css')
|
|
3006
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
3007
|
+
locator = new Locator(locator, 'css')
|
|
2882
3008
|
|
|
2883
|
-
let waiter
|
|
2884
|
-
const context = await this._getContext()
|
|
3009
|
+
let waiter
|
|
3010
|
+
const context = await this._getContext()
|
|
2885
3011
|
if (!locator.isXPath()) {
|
|
2886
3012
|
try {
|
|
2887
|
-
await context
|
|
3013
|
+
await context
|
|
3014
|
+
.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()}`)
|
|
3015
|
+
.first()
|
|
3016
|
+
.waitFor({ timeout: waitTimeout, state: 'detached' })
|
|
2888
3017
|
} catch (e) {
|
|
2889
|
-
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
3018
|
+
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2890
3019
|
}
|
|
2891
3020
|
} else {
|
|
2892
3021
|
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 })
|
|
3022
|
+
eval($XPath) // eslint-disable-line no-eval
|
|
3023
|
+
return $XPath(null, locator).length === 0
|
|
3024
|
+
}
|
|
3025
|
+
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2897
3026
|
return waiter.catch((err) => {
|
|
2898
|
-
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2899
|
-
})
|
|
3027
|
+
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
3028
|
+
})
|
|
2900
3029
|
}
|
|
2901
3030
|
}
|
|
2902
3031
|
|
|
@@ -2905,53 +3034,56 @@ class Playwright extends Helper {
|
|
|
2905
3034
|
*/
|
|
2906
3035
|
async waitForCookie(name, sec) {
|
|
2907
3036
|
// by default, we will retry 3 times
|
|
2908
|
-
let retries = 3
|
|
2909
|
-
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
3037
|
+
let retries = 3
|
|
3038
|
+
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2910
3039
|
|
|
2911
3040
|
if (sec) {
|
|
2912
|
-
retries = sec
|
|
3041
|
+
retries = sec
|
|
2913
3042
|
} else {
|
|
2914
|
-
retries = Math.ceil(waitTimeout / 1000) - 1
|
|
3043
|
+
retries = Math.ceil(waitTimeout / 1000) - 1
|
|
2915
3044
|
}
|
|
2916
3045
|
|
|
2917
|
-
return promiseRetry(
|
|
2918
|
-
|
|
2919
|
-
const
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
3046
|
+
return promiseRetry(
|
|
3047
|
+
async (retry, number) => {
|
|
3048
|
+
const _grabCookie = async (name) => {
|
|
3049
|
+
const cookies = await this.browserContext.cookies()
|
|
3050
|
+
const cookie = cookies.filter((c) => c.name === name)
|
|
3051
|
+
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`)
|
|
3052
|
+
}
|
|
2923
3053
|
|
|
2924
|
-
|
|
2925
|
-
|
|
3054
|
+
this.debugSection('Wait for cookie: ', name)
|
|
3055
|
+
if (number > 1) this.debugSection('Retrying... Attempt #', number)
|
|
2926
3056
|
|
|
2927
|
-
|
|
2928
|
-
|
|
2929
|
-
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
3057
|
+
try {
|
|
3058
|
+
await _grabCookie(name)
|
|
3059
|
+
} catch (e) {
|
|
3060
|
+
retry(e)
|
|
3061
|
+
}
|
|
3062
|
+
},
|
|
3063
|
+
{ retries, maxTimeout: 1000 },
|
|
3064
|
+
)
|
|
2933
3065
|
}
|
|
2934
3066
|
|
|
2935
3067
|
async _waitForAction() {
|
|
2936
|
-
return this.wait(this.options.waitForAction / 1000)
|
|
3068
|
+
return this.wait(this.options.waitForAction / 1000)
|
|
2937
3069
|
}
|
|
2938
3070
|
|
|
2939
3071
|
/**
|
|
2940
3072
|
* {{> grabDataFromPerformanceTiming }}
|
|
2941
3073
|
*/
|
|
2942
3074
|
async grabDataFromPerformanceTiming() {
|
|
2943
|
-
return perfTiming
|
|
3075
|
+
return perfTiming
|
|
2944
3076
|
}
|
|
2945
3077
|
|
|
2946
3078
|
/**
|
|
2947
3079
|
* {{> grabElementBoundingRect }}
|
|
2948
3080
|
*/
|
|
2949
3081
|
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
|
|
3082
|
+
const el = await this._locateElement(locator)
|
|
3083
|
+
assertElementExists(el, locator)
|
|
3084
|
+
const rect = await el.boundingBox()
|
|
3085
|
+
if (prop) return rect[prop]
|
|
3086
|
+
return rect
|
|
2955
3087
|
}
|
|
2956
3088
|
|
|
2957
3089
|
/**
|
|
@@ -2966,7 +3098,7 @@ class Playwright extends Helper {
|
|
|
2966
3098
|
* @param {function} [handler] a function to process request
|
|
2967
3099
|
*/
|
|
2968
3100
|
async mockRoute(url, handler) {
|
|
2969
|
-
return this.browserContext.route(...arguments)
|
|
3101
|
+
return this.browserContext.route(...arguments)
|
|
2970
3102
|
}
|
|
2971
3103
|
|
|
2972
3104
|
/**
|
|
@@ -2982,17 +3114,17 @@ class Playwright extends Helper {
|
|
|
2982
3114
|
* @param {function} [handler] a function to process request
|
|
2983
3115
|
*/
|
|
2984
3116
|
async stopMockingRoute(url, handler) {
|
|
2985
|
-
return this.browserContext.unroute(...arguments)
|
|
3117
|
+
return this.browserContext.unroute(...arguments)
|
|
2986
3118
|
}
|
|
2987
3119
|
|
|
2988
3120
|
/**
|
|
2989
|
-
* {{>
|
|
3121
|
+
* {{> startRecordingTraffic }}
|
|
2990
3122
|
*
|
|
2991
3123
|
*/
|
|
2992
3124
|
startRecordingTraffic() {
|
|
2993
|
-
this.flushNetworkTraffics()
|
|
2994
|
-
this.recording = true
|
|
2995
|
-
this.recordedAtLeastOnce = true
|
|
3125
|
+
this.flushNetworkTraffics()
|
|
3126
|
+
this.recording = true
|
|
3127
|
+
this.recordedAtLeastOnce = true
|
|
2996
3128
|
|
|
2997
3129
|
this.page.on('requestfinished', async (request) => {
|
|
2998
3130
|
const information = {
|
|
@@ -3001,16 +3133,16 @@ class Playwright extends Helper {
|
|
|
3001
3133
|
requestHeaders: request.headers(),
|
|
3002
3134
|
requestPostData: request.postData(),
|
|
3003
3135
|
response: request.response(),
|
|
3004
|
-
}
|
|
3136
|
+
}
|
|
3005
3137
|
|
|
3006
|
-
this.debugSection('REQUEST: ', JSON.stringify(information))
|
|
3138
|
+
this.debugSection('REQUEST: ', JSON.stringify(information))
|
|
3007
3139
|
|
|
3008
3140
|
if (typeof information.requestPostData === 'object') {
|
|
3009
|
-
information.requestPostData = JSON.parse(information.requestPostData)
|
|
3141
|
+
information.requestPostData = JSON.parse(information.requestPostData)
|
|
3010
3142
|
}
|
|
3011
3143
|
|
|
3012
|
-
this.requests.push(information)
|
|
3013
|
-
})
|
|
3144
|
+
this.requests.push(information)
|
|
3145
|
+
})
|
|
3014
3146
|
}
|
|
3015
3147
|
|
|
3016
3148
|
/**
|
|
@@ -3033,21 +3165,21 @@ class Playwright extends Helper {
|
|
|
3033
3165
|
*/
|
|
3034
3166
|
blockTraffic(urls) {
|
|
3035
3167
|
if (Array.isArray(urls)) {
|
|
3036
|
-
urls.forEach(url => {
|
|
3168
|
+
urls.forEach((url) => {
|
|
3037
3169
|
this.page.route(url, (route) => {
|
|
3038
3170
|
route
|
|
3039
3171
|
.abort()
|
|
3040
3172
|
// Sometimes it happens that browser has been closed in the meantime. It is ok to ignore error then.
|
|
3041
|
-
.catch((e) => {})
|
|
3042
|
-
})
|
|
3043
|
-
})
|
|
3173
|
+
.catch((e) => {})
|
|
3174
|
+
})
|
|
3175
|
+
})
|
|
3044
3176
|
} else {
|
|
3045
3177
|
this.page.route(urls, (route) => {
|
|
3046
3178
|
route
|
|
3047
3179
|
.abort()
|
|
3048
3180
|
// Sometimes it happens that browser has been closed in the meantime. It is ok to ignore error then.
|
|
3049
|
-
.catch((e) => {})
|
|
3050
|
-
})
|
|
3181
|
+
.catch((e) => {})
|
|
3182
|
+
})
|
|
3051
3183
|
}
|
|
3052
3184
|
}
|
|
3053
3185
|
|
|
@@ -3069,10 +3201,10 @@ class Playwright extends Helper {
|
|
|
3069
3201
|
*/
|
|
3070
3202
|
mockTraffic(urls, responseString, contentType = 'application/json') {
|
|
3071
3203
|
// Required to mock cross-domain requests
|
|
3072
|
-
const headers = { 'access-control-allow-origin': '*' }
|
|
3204
|
+
const headers = { 'access-control-allow-origin': '*' }
|
|
3073
3205
|
|
|
3074
3206
|
if (typeof urls === 'string') {
|
|
3075
|
-
urls = [urls]
|
|
3207
|
+
urls = [urls]
|
|
3076
3208
|
}
|
|
3077
3209
|
|
|
3078
3210
|
urls.forEach((url) => {
|
|
@@ -3080,15 +3212,15 @@ class Playwright extends Helper {
|
|
|
3080
3212
|
if (this.page.isClosed()) {
|
|
3081
3213
|
// Sometimes it happens that browser has been closed in the meantime.
|
|
3082
3214
|
// In this case we just don't fulfill to prevent error in test scenario.
|
|
3083
|
-
return
|
|
3215
|
+
return
|
|
3084
3216
|
}
|
|
3085
3217
|
route.fulfill({
|
|
3086
3218
|
contentType,
|
|
3087
3219
|
headers,
|
|
3088
3220
|
body: responseString,
|
|
3089
|
-
})
|
|
3090
|
-
})
|
|
3091
|
-
})
|
|
3221
|
+
})
|
|
3222
|
+
})
|
|
3223
|
+
})
|
|
3092
3224
|
}
|
|
3093
3225
|
|
|
3094
3226
|
/**
|
|
@@ -3096,7 +3228,7 @@ class Playwright extends Helper {
|
|
|
3096
3228
|
* {{> flushNetworkTraffics }}
|
|
3097
3229
|
*/
|
|
3098
3230
|
flushNetworkTraffics() {
|
|
3099
|
-
flushNetworkTraffics.call(this)
|
|
3231
|
+
flushNetworkTraffics.call(this)
|
|
3100
3232
|
}
|
|
3101
3233
|
|
|
3102
3234
|
/**
|
|
@@ -3104,14 +3236,12 @@ class Playwright extends Helper {
|
|
|
3104
3236
|
* {{> stopRecordingTraffic }}
|
|
3105
3237
|
*/
|
|
3106
3238
|
stopRecordingTraffic() {
|
|
3107
|
-
stopRecordingTraffic.call(this)
|
|
3239
|
+
stopRecordingTraffic.call(this)
|
|
3108
3240
|
}
|
|
3109
3241
|
|
|
3110
3242
|
/**
|
|
3111
3243
|
* Returns full URL of request matching parameter "urlMatch".
|
|
3112
3244
|
*
|
|
3113
|
-
* @param {string|RegExp} urlMatch Expected URL of request in network traffic. Can be a string or a regular expression.
|
|
3114
|
-
*
|
|
3115
3245
|
* Examples:
|
|
3116
3246
|
*
|
|
3117
3247
|
* ```js
|
|
@@ -3119,25 +3249,28 @@ class Playwright extends Helper {
|
|
|
3119
3249
|
* I.grabTrafficUrl(/session.*start/);
|
|
3120
3250
|
* ```
|
|
3121
3251
|
*
|
|
3252
|
+
* @param {string|RegExp} urlMatch Expected URL of request in network traffic. Can be a string or a regular expression.
|
|
3122
3253
|
* @return {Promise<*>}
|
|
3123
3254
|
*/
|
|
3124
3255
|
grabTrafficUrl(urlMatch) {
|
|
3125
3256
|
if (!this.recordedAtLeastOnce) {
|
|
3126
|
-
throw new Error(
|
|
3257
|
+
throw new Error(
|
|
3258
|
+
'Failure in test automation. You use "I.grabTrafficUrl", but "I.startRecordingTraffic" was never called before.',
|
|
3259
|
+
)
|
|
3127
3260
|
}
|
|
3128
3261
|
|
|
3129
3262
|
for (const i in this.requests) {
|
|
3130
3263
|
// eslint-disable-next-line no-prototype-builtins
|
|
3131
3264
|
if (this.requests.hasOwnProperty(i)) {
|
|
3132
|
-
const request = this.requests[i]
|
|
3265
|
+
const request = this.requests[i]
|
|
3133
3266
|
|
|
3134
3267
|
if (request.url && request.url.match(new RegExp(urlMatch))) {
|
|
3135
|
-
return request.url
|
|
3268
|
+
return request.url
|
|
3136
3269
|
}
|
|
3137
3270
|
}
|
|
3138
3271
|
}
|
|
3139
3272
|
|
|
3140
|
-
assert.fail(`Method "getTrafficUrl" failed: No request found in traffic that matches ${urlMatch}`)
|
|
3273
|
+
assert.fail(`Method "getTrafficUrl" failed: No request found in traffic that matches ${urlMatch}`)
|
|
3141
3274
|
}
|
|
3142
3275
|
|
|
3143
3276
|
/**
|
|
@@ -3145,17 +3278,15 @@ class Playwright extends Helper {
|
|
|
3145
3278
|
* {{> grabRecordedNetworkTraffics }}
|
|
3146
3279
|
*/
|
|
3147
3280
|
async grabRecordedNetworkTraffics() {
|
|
3148
|
-
return grabRecordedNetworkTraffics.call(this)
|
|
3281
|
+
return grabRecordedNetworkTraffics.call(this)
|
|
3149
3282
|
}
|
|
3150
3283
|
|
|
3151
3284
|
/**
|
|
3152
3285
|
*
|
|
3153
3286
|
* {{> seeTraffic }}
|
|
3154
3287
|
*/
|
|
3155
|
-
async seeTraffic({
|
|
3156
|
-
|
|
3157
|
-
}) {
|
|
3158
|
-
await seeTraffic.call(this, ...arguments);
|
|
3288
|
+
async seeTraffic({ name, url, parameters, requestPostData, timeout = 10 }) {
|
|
3289
|
+
await seeTraffic.call(this, ...arguments)
|
|
3159
3290
|
}
|
|
3160
3291
|
|
|
3161
3292
|
/**
|
|
@@ -3164,51 +3295,42 @@ class Playwright extends Helper {
|
|
|
3164
3295
|
*
|
|
3165
3296
|
*/
|
|
3166
3297
|
dontSeeTraffic({ name, url }) {
|
|
3167
|
-
dontSeeTraffic.call(this, ...arguments)
|
|
3298
|
+
dontSeeTraffic.call(this, ...arguments)
|
|
3168
3299
|
}
|
|
3169
3300
|
|
|
3170
3301
|
/**
|
|
3171
3302
|
* {{> startRecordingWebSocketMessages }}
|
|
3172
3303
|
*/
|
|
3173
3304
|
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
|
-
);
|
|
3305
|
+
this.flushWebSocketMessages()
|
|
3306
|
+
this.recordingWebSocketMessages = true
|
|
3307
|
+
this.recordedWebSocketMessagesAtLeastOnce = true
|
|
3188
3308
|
|
|
3189
|
-
this.cdpSession.
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload));
|
|
3193
|
-
},
|
|
3194
|
-
);
|
|
3309
|
+
this.cdpSession = await this.getNewCDPSession()
|
|
3310
|
+
await this.cdpSession.send('Network.enable')
|
|
3311
|
+
await this.cdpSession.send('Page.enable')
|
|
3195
3312
|
|
|
3196
|
-
this.cdpSession.on(
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3201
|
-
|
|
3313
|
+
this.cdpSession.on('Network.webSocketFrameReceived', (payload) => {
|
|
3314
|
+
this._logWebsocketMessages(this._getWebSocketLog('RECEIVED', payload))
|
|
3315
|
+
})
|
|
3316
|
+
|
|
3317
|
+
this.cdpSession.on('Network.webSocketFrameSent', (payload) => {
|
|
3318
|
+
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload))
|
|
3319
|
+
})
|
|
3320
|
+
|
|
3321
|
+
this.cdpSession.on('Network.webSocketFrameError', (payload) => {
|
|
3322
|
+
this._logWebsocketMessages(this._getWebSocketLog('ERROR', payload))
|
|
3323
|
+
})
|
|
3202
3324
|
}
|
|
3203
3325
|
|
|
3204
3326
|
/**
|
|
3205
3327
|
* {{> stopRecordingWebSocketMessages }}
|
|
3206
3328
|
*/
|
|
3207
3329
|
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
|
|
3330
|
+
await this.cdpSession.send('Network.disable')
|
|
3331
|
+
await this.cdpSession.send('Page.disable')
|
|
3332
|
+
this.page.removeAllListeners('Network')
|
|
3333
|
+
this.recordingWebSocketMessages = false
|
|
3212
3334
|
}
|
|
3213
3335
|
|
|
3214
3336
|
/**
|
|
@@ -3220,17 +3342,19 @@ class Playwright extends Helper {
|
|
|
3220
3342
|
grabWebSocketMessages() {
|
|
3221
3343
|
if (!this.recordingWebSocketMessages) {
|
|
3222
3344
|
if (!this.recordedWebSocketMessagesAtLeastOnce) {
|
|
3223
|
-
throw new Error(
|
|
3345
|
+
throw new Error(
|
|
3346
|
+
'Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.',
|
|
3347
|
+
)
|
|
3224
3348
|
}
|
|
3225
3349
|
}
|
|
3226
|
-
return this.webSocketMessages
|
|
3350
|
+
return this.webSocketMessages
|
|
3227
3351
|
}
|
|
3228
3352
|
|
|
3229
3353
|
/**
|
|
3230
3354
|
* Resets all recorded WS messages.
|
|
3231
3355
|
*/
|
|
3232
3356
|
flushWebSocketMessages() {
|
|
3233
|
-
this.webSocketMessages = []
|
|
3357
|
+
this.webSocketMessages = []
|
|
3234
3358
|
}
|
|
3235
3359
|
|
|
3236
3360
|
/**
|
|
@@ -3288,426 +3412,444 @@ class Playwright extends Helper {
|
|
|
3288
3412
|
* @return {Promise<Array<Object>>}
|
|
3289
3413
|
*/
|
|
3290
3414
|
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
|
|
3415
|
+
const client = await this.page.context().newCDPSession(this.page)
|
|
3416
|
+
await client.send('Performance.enable')
|
|
3417
|
+
const perfMetricObject = await client.send('Performance.getMetrics')
|
|
3418
|
+
return perfMetricObject?.metrics
|
|
3295
3419
|
}
|
|
3296
3420
|
|
|
3297
3421
|
_getWebSocketMessage(payload) {
|
|
3298
3422
|
if (payload.errorMessage) {
|
|
3299
|
-
return payload.errorMessage
|
|
3423
|
+
return payload.errorMessage
|
|
3300
3424
|
}
|
|
3301
3425
|
|
|
3302
|
-
return payload.response.payloadData
|
|
3426
|
+
return payload.response.payloadData
|
|
3303
3427
|
}
|
|
3304
3428
|
|
|
3305
3429
|
_getWebSocketLog(prefix, payload) {
|
|
3306
|
-
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n
|
|
3430
|
+
return `${prefix} ID: ${payload.requestId} TIMESTAMP: ${payload.timestamp} (${new Date().toISOString()})\n\n${this._getWebSocketMessage(payload)}\n\n`
|
|
3307
3431
|
}
|
|
3308
3432
|
|
|
3309
3433
|
async getNewCDPSession() {
|
|
3310
|
-
return this.page.context().newCDPSession(this.page)
|
|
3434
|
+
return this.page.context().newCDPSession(this.page)
|
|
3311
3435
|
}
|
|
3312
3436
|
|
|
3313
3437
|
_logWebsocketMessages(message) {
|
|
3314
|
-
this.webSocketMessages
|
|
3438
|
+
this.webSocketMessages.push(message)
|
|
3315
3439
|
}
|
|
3316
3440
|
}
|
|
3317
3441
|
|
|
3318
|
-
|
|
3442
|
+
module.exports = Playwright
|
|
3319
3443
|
|
|
3320
3444
|
function buildLocatorString(locator) {
|
|
3321
3445
|
if (locator.isCustom()) {
|
|
3322
|
-
return `${locator.type}=${locator.value}
|
|
3323
|
-
}
|
|
3324
|
-
|
|
3446
|
+
return `${locator.type}=${locator.value}`
|
|
3447
|
+
}
|
|
3448
|
+
if (locator.isXPath()) {
|
|
3449
|
+
return `xpath=${locator.value}`
|
|
3325
3450
|
}
|
|
3326
|
-
return locator.simplify()
|
|
3451
|
+
return locator.simplify()
|
|
3327
3452
|
}
|
|
3328
3453
|
|
|
3329
3454
|
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')
|
|
3455
|
+
if (locator.react) return findReact(matcher, locator)
|
|
3456
|
+
if (locator.vue) return findVue(matcher, locator)
|
|
3457
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3458
|
+
locator = new Locator(locator, 'css')
|
|
3334
3459
|
|
|
3335
|
-
return matcher.locator(buildLocatorString(locator)).all()
|
|
3460
|
+
return matcher.locator(buildLocatorString(locator)).all()
|
|
3336
3461
|
}
|
|
3337
3462
|
|
|
3338
3463
|
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')
|
|
3464
|
+
if (locator.react) return findReact(matcher, locator)
|
|
3465
|
+
if (locator.vue) return findVue(matcher, locator)
|
|
3466
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3467
|
+
locator = new Locator(locator, 'css')
|
|
3343
3468
|
|
|
3344
|
-
return matcher.locator(buildLocatorString(locator)).first()
|
|
3469
|
+
return matcher.locator(buildLocatorString(locator)).first()
|
|
3345
3470
|
}
|
|
3346
3471
|
|
|
3347
3472
|
async function getVisibleElements(elements) {
|
|
3348
|
-
const visibleElements = []
|
|
3473
|
+
const visibleElements = []
|
|
3349
3474
|
for (const element of elements) {
|
|
3350
3475
|
if (await element.isVisible()) {
|
|
3351
|
-
visibleElements.push(element)
|
|
3476
|
+
visibleElements.push(element)
|
|
3352
3477
|
}
|
|
3353
3478
|
}
|
|
3354
3479
|
if (visibleElements.length === 0) {
|
|
3355
|
-
return elements
|
|
3480
|
+
return elements
|
|
3356
3481
|
}
|
|
3357
|
-
return visibleElements
|
|
3482
|
+
return visibleElements
|
|
3358
3483
|
}
|
|
3359
3484
|
|
|
3360
3485
|
async function proceedClick(locator, context = null, options = {}) {
|
|
3361
|
-
let matcher = await this._getContext()
|
|
3486
|
+
let matcher = await this._getContext()
|
|
3362
3487
|
if (context) {
|
|
3363
|
-
const els = await this._locate(context)
|
|
3364
|
-
assertElementExists(els, context)
|
|
3365
|
-
matcher = els[0]
|
|
3488
|
+
const els = await this._locate(context)
|
|
3489
|
+
assertElementExists(els, context)
|
|
3490
|
+
matcher = els[0]
|
|
3366
3491
|
}
|
|
3367
|
-
const els = await findClickable.call(this, matcher, locator)
|
|
3492
|
+
const els = await findClickable.call(this, matcher, locator)
|
|
3368
3493
|
if (context) {
|
|
3369
|
-
assertElementExists(
|
|
3494
|
+
assertElementExists(
|
|
3495
|
+
els,
|
|
3496
|
+
locator,
|
|
3497
|
+
'Clickable element',
|
|
3498
|
+
`was not found inside element ${new Locator(context).toString()}`,
|
|
3499
|
+
)
|
|
3370
3500
|
} else {
|
|
3371
|
-
assertElementExists(els, locator, 'Clickable element')
|
|
3501
|
+
assertElementExists(els, locator, 'Clickable element')
|
|
3372
3502
|
}
|
|
3373
3503
|
|
|
3374
|
-
await highlightActiveElement.call(this, els[0])
|
|
3504
|
+
await highlightActiveElement.call(this, els[0])
|
|
3375
3505
|
|
|
3376
3506
|
/*
|
|
3377
3507
|
using the force true options itself but instead dispatching a click
|
|
3378
3508
|
*/
|
|
3379
3509
|
if (options.force) {
|
|
3380
|
-
await els[0].dispatchEvent('click')
|
|
3510
|
+
await els[0].dispatchEvent('click')
|
|
3381
3511
|
} else {
|
|
3382
|
-
const element = els.length > 1 ? (await getVisibleElements(els))[0] : els[0]
|
|
3383
|
-
await element.click(options)
|
|
3512
|
+
const element = els.length > 1 ? (await getVisibleElements(els))[0] : els[0]
|
|
3513
|
+
await element.click(options)
|
|
3384
3514
|
}
|
|
3385
|
-
const promises = []
|
|
3515
|
+
const promises = []
|
|
3386
3516
|
if (options.waitForNavigation) {
|
|
3387
|
-
promises.push(this.waitForURL(/.*/, { waitUntil: options.waitForNavigation }))
|
|
3517
|
+
promises.push(this.waitForURL(/.*/, { waitUntil: options.waitForNavigation }))
|
|
3388
3518
|
}
|
|
3389
|
-
promises.push(this._waitForAction())
|
|
3519
|
+
promises.push(this._waitForAction())
|
|
3390
3520
|
|
|
3391
|
-
return Promise.all(promises)
|
|
3521
|
+
return Promise.all(promises)
|
|
3392
3522
|
}
|
|
3393
3523
|
|
|
3394
3524
|
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)
|
|
3525
|
+
if (locator.react) return findReact(matcher, locator)
|
|
3526
|
+
if (locator.vue) return findVue(matcher, locator)
|
|
3527
|
+
if (locator.pw) return findByPlaywrightLocator.call(this, matcher, locator)
|
|
3398
3528
|
|
|
3399
|
-
locator = new Locator(locator)
|
|
3400
|
-
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator)
|
|
3529
|
+
locator = new Locator(locator)
|
|
3530
|
+
if (!locator.isFuzzy()) return findElements.call(this, matcher, locator)
|
|
3401
3531
|
|
|
3402
|
-
let els
|
|
3403
|
-
const literal = xpathLocator.literal(locator.value)
|
|
3532
|
+
let els
|
|
3533
|
+
const literal = xpathLocator.literal(locator.value)
|
|
3404
3534
|
|
|
3405
|
-
els = await findElements.call(this, matcher, Locator.clickable.narrow(literal))
|
|
3406
|
-
if (els.length) return els
|
|
3535
|
+
els = await findElements.call(this, matcher, Locator.clickable.narrow(literal))
|
|
3536
|
+
if (els.length) return els
|
|
3407
3537
|
|
|
3408
|
-
els = await findElements.call(this, matcher, Locator.clickable.wide(literal))
|
|
3409
|
-
if (els.length) return els
|
|
3538
|
+
els = await findElements.call(this, matcher, Locator.clickable.wide(literal))
|
|
3539
|
+
if (els.length) return els
|
|
3410
3540
|
|
|
3411
3541
|
try {
|
|
3412
|
-
els = await findElements.call(this, matcher, Locator.clickable.self(literal))
|
|
3413
|
-
if (els.length) return els
|
|
3542
|
+
els = await findElements.call(this, matcher, Locator.clickable.self(literal))
|
|
3543
|
+
if (els.length) return els
|
|
3414
3544
|
} catch (err) {
|
|
3415
3545
|
// Do nothing
|
|
3416
3546
|
}
|
|
3417
3547
|
|
|
3418
|
-
return findElements.call(this, matcher, locator.value)
|
|
3548
|
+
return findElements.call(this, matcher, locator.value) // by css or xpath
|
|
3419
3549
|
}
|
|
3420
3550
|
|
|
3421
3551
|
async function proceedSee(assertType, text, context, strict = false) {
|
|
3422
|
-
let description
|
|
3423
|
-
let allText
|
|
3552
|
+
let description
|
|
3553
|
+
let allText
|
|
3424
3554
|
|
|
3425
3555
|
if (!context) {
|
|
3426
|
-
const el = await this.context
|
|
3556
|
+
const el = await this.context
|
|
3427
3557
|
|
|
3428
|
-
allText = el.constructor.name !== 'Locator' ? [await el.locator('body').innerText()] : [await el.innerText()]
|
|
3558
|
+
allText = el.constructor.name !== 'Locator' ? [await el.locator('body').innerText()] : [await el.innerText()]
|
|
3429
3559
|
|
|
3430
|
-
description = 'web application'
|
|
3560
|
+
description = 'web application'
|
|
3431
3561
|
} 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()))
|
|
3562
|
+
const locator = new Locator(context, 'css')
|
|
3563
|
+
description = `element ${locator.toString()}`
|
|
3564
|
+
const els = await this._locate(locator)
|
|
3565
|
+
assertElementExists(els, locator.toString())
|
|
3566
|
+
allText = await Promise.all(els.map((el) => el.innerText()))
|
|
3437
3567
|
}
|
|
3438
3568
|
|
|
3439
3569
|
if (strict) {
|
|
3440
|
-
return allText.map(elText => equals(description)[assertType](text, elText))
|
|
3570
|
+
return allText.map((elText) => equals(description)[assertType](text, elText))
|
|
3441
3571
|
}
|
|
3442
|
-
return stringIncludes(description)[assertType](
|
|
3572
|
+
return stringIncludes(description)[assertType](
|
|
3573
|
+
normalizeSpacesInString(text),
|
|
3574
|
+
normalizeSpacesInString(allText.join(' | ')),
|
|
3575
|
+
)
|
|
3443
3576
|
}
|
|
3444
3577
|
|
|
3445
3578
|
async function findCheckable(locator, context) {
|
|
3446
|
-
let contextEl = await this.context
|
|
3579
|
+
let contextEl = await this.context
|
|
3447
3580
|
if (typeof context === 'string') {
|
|
3448
|
-
contextEl = await findElements.call(this, contextEl,
|
|
3449
|
-
contextEl = contextEl[0]
|
|
3581
|
+
contextEl = await findElements.call(this, contextEl, new Locator(context, 'css').simplify())
|
|
3582
|
+
contextEl = contextEl[0]
|
|
3450
3583
|
}
|
|
3451
3584
|
|
|
3452
|
-
const matchedLocator = new Locator(locator)
|
|
3585
|
+
const matchedLocator = new Locator(locator)
|
|
3453
3586
|
if (!matchedLocator.isFuzzy()) {
|
|
3454
|
-
return findElements.call(this, contextEl, matchedLocator.simplify())
|
|
3587
|
+
return findElements.call(this, contextEl, matchedLocator.simplify())
|
|
3455
3588
|
}
|
|
3456
3589
|
|
|
3457
|
-
const literal = xpathLocator.literal(locator)
|
|
3458
|
-
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal))
|
|
3590
|
+
const literal = xpathLocator.literal(locator)
|
|
3591
|
+
let els = await findElements.call(this, contextEl, Locator.checkable.byText(literal))
|
|
3459
3592
|
if (els.length) {
|
|
3460
|
-
return els
|
|
3593
|
+
return els
|
|
3461
3594
|
}
|
|
3462
|
-
els = await findElements.call(this, contextEl, Locator.checkable.byName(literal))
|
|
3595
|
+
els = await findElements.call(this, contextEl, Locator.checkable.byName(literal))
|
|
3463
3596
|
if (els.length) {
|
|
3464
|
-
return els
|
|
3597
|
+
return els
|
|
3465
3598
|
}
|
|
3466
|
-
return findElements.call(this, contextEl, locator)
|
|
3599
|
+
return findElements.call(this, contextEl, locator)
|
|
3467
3600
|
}
|
|
3468
3601
|
|
|
3469
3602
|
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)
|
|
3603
|
+
let els = await findCheckable.call(this, option)
|
|
3604
|
+
assertElementExists(els, option, 'Checkable')
|
|
3605
|
+
els = await Promise.all(els.map((el) => el.isChecked()))
|
|
3606
|
+
const selected = els.reduce((prev, cur) => prev || cur)
|
|
3607
|
+
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
3475
3608
|
}
|
|
3476
3609
|
|
|
3477
3610
|
async function findFields(locator) {
|
|
3478
|
-
const matchedLocator = new Locator(locator)
|
|
3611
|
+
const matchedLocator = new Locator(locator)
|
|
3479
3612
|
if (!matchedLocator.isFuzzy()) {
|
|
3480
|
-
return this._locate(matchedLocator)
|
|
3613
|
+
return this._locate(matchedLocator)
|
|
3481
3614
|
}
|
|
3482
|
-
const literal = xpathLocator.literal(locator)
|
|
3615
|
+
const literal = xpathLocator.literal(locator)
|
|
3483
3616
|
|
|
3484
|
-
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) })
|
|
3617
|
+
let els = await this._locate({ xpath: Locator.field.labelEquals(literal) })
|
|
3485
3618
|
if (els.length) {
|
|
3486
|
-
return els
|
|
3619
|
+
return els
|
|
3487
3620
|
}
|
|
3488
3621
|
|
|
3489
|
-
els = await this._locate({ xpath: Locator.field.labelContains(literal) })
|
|
3622
|
+
els = await this._locate({ xpath: Locator.field.labelContains(literal) })
|
|
3490
3623
|
if (els.length) {
|
|
3491
|
-
return els
|
|
3624
|
+
return els
|
|
3492
3625
|
}
|
|
3493
|
-
els = await this._locate({ xpath: Locator.field.byName(literal) })
|
|
3626
|
+
els = await this._locate({ xpath: Locator.field.byName(literal) })
|
|
3494
3627
|
if (els.length) {
|
|
3495
|
-
return els
|
|
3628
|
+
return els
|
|
3496
3629
|
}
|
|
3497
|
-
return this._locate({ css: locator })
|
|
3630
|
+
return this._locate({ css: locator })
|
|
3498
3631
|
}
|
|
3499
3632
|
|
|
3500
3633
|
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')
|
|
3634
|
+
const els = await findFields.call(this, field)
|
|
3635
|
+
assertElementExists(els, field, 'Field')
|
|
3636
|
+
const el = els[0]
|
|
3637
|
+
const tag = await el.evaluate((e) => e.tagName)
|
|
3638
|
+
const fieldType = await el.getAttribute('type')
|
|
3506
3639
|
|
|
3507
3640
|
const proceedMultiple = async (elements) => {
|
|
3508
|
-
const fields = Array.isArray(elements) ? elements : [elements]
|
|
3641
|
+
const fields = Array.isArray(elements) ? elements : [elements]
|
|
3509
3642
|
|
|
3510
|
-
const elementValues = []
|
|
3643
|
+
const elementValues = []
|
|
3511
3644
|
for (const element of fields) {
|
|
3512
|
-
elementValues.push(await element.inputValue())
|
|
3645
|
+
elementValues.push(await element.inputValue())
|
|
3513
3646
|
}
|
|
3514
3647
|
|
|
3515
3648
|
if (typeof value === 'boolean') {
|
|
3516
|
-
equals(`no. of items matching > 0: ${field}`)[assertType](value, !!elementValues.length)
|
|
3649
|
+
equals(`no. of items matching > 0: ${field}`)[assertType](value, !!elementValues.length)
|
|
3517
3650
|
} else {
|
|
3518
3651
|
if (assertType === 'assert') {
|
|
3519
|
-
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0)
|
|
3652
|
+
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0)
|
|
3520
3653
|
}
|
|
3521
|
-
elementValues.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val))
|
|
3654
|
+
elementValues.forEach((val) => stringIncludes(`fields by ${field}`)[assertType](value, val))
|
|
3522
3655
|
}
|
|
3523
|
-
}
|
|
3656
|
+
}
|
|
3524
3657
|
|
|
3525
3658
|
if (tag === 'SELECT') {
|
|
3526
3659
|
if (await el.getAttribute('multiple')) {
|
|
3527
|
-
const selectedOptions = await el.all('option:checked')
|
|
3528
|
-
if (!selectedOptions.length) return null
|
|
3660
|
+
const selectedOptions = await el.all('option:checked')
|
|
3661
|
+
if (!selectedOptions.length) return null
|
|
3529
3662
|
|
|
3530
|
-
const options = await filterFieldsByValue(selectedOptions, value, true)
|
|
3531
|
-
return proceedMultiple(options)
|
|
3663
|
+
const options = await filterFieldsByValue(selectedOptions, value, true)
|
|
3664
|
+
return proceedMultiple(options)
|
|
3532
3665
|
}
|
|
3533
3666
|
|
|
3534
|
-
return el.inputValue()
|
|
3667
|
+
return el.inputValue()
|
|
3535
3668
|
}
|
|
3536
3669
|
|
|
3537
3670
|
if (tag === 'INPUT') {
|
|
3538
3671
|
if (fieldType === 'checkbox' || fieldType === 'radio') {
|
|
3539
3672
|
if (typeof value === 'boolean') {
|
|
3540
3673
|
// Filter by values
|
|
3541
|
-
const options = await filterFieldsBySelectionState(els, true)
|
|
3542
|
-
return proceedMultiple(options)
|
|
3674
|
+
const options = await filterFieldsBySelectionState(els, true)
|
|
3675
|
+
return proceedMultiple(options)
|
|
3543
3676
|
}
|
|
3544
3677
|
|
|
3545
|
-
const options = await filterFieldsByValue(els, value, true)
|
|
3546
|
-
return proceedMultiple(options)
|
|
3678
|
+
const options = await filterFieldsByValue(els, value, true)
|
|
3679
|
+
return proceedMultiple(options)
|
|
3547
3680
|
}
|
|
3548
|
-
return proceedMultiple(els[0])
|
|
3681
|
+
return proceedMultiple(els[0])
|
|
3549
3682
|
}
|
|
3550
3683
|
|
|
3551
|
-
let fieldVal
|
|
3684
|
+
let fieldVal
|
|
3552
3685
|
|
|
3553
3686
|
try {
|
|
3554
|
-
fieldVal = await el.inputValue()
|
|
3687
|
+
fieldVal = await el.inputValue()
|
|
3555
3688
|
} catch (e) {
|
|
3556
3689
|
if (e.message.includes('Error: Node is not an <input>, <textarea> or <select> element')) {
|
|
3557
|
-
fieldVal = await el.innerText()
|
|
3690
|
+
fieldVal = await el.innerText()
|
|
3558
3691
|
}
|
|
3559
3692
|
}
|
|
3560
3693
|
|
|
3561
|
-
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal)
|
|
3694
|
+
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal)
|
|
3562
3695
|
}
|
|
3563
3696
|
|
|
3564
3697
|
async function filterFieldsByValue(elements, value, onlySelected) {
|
|
3565
|
-
const matches = []
|
|
3698
|
+
const matches = []
|
|
3566
3699
|
for (const element of elements) {
|
|
3567
|
-
const val = await element.getAttribute('value')
|
|
3568
|
-
let isSelected = true
|
|
3700
|
+
const val = await element.getAttribute('value')
|
|
3701
|
+
let isSelected = true
|
|
3569
3702
|
if (onlySelected) {
|
|
3570
|
-
isSelected = await elementSelected(element)
|
|
3703
|
+
isSelected = await elementSelected(element)
|
|
3571
3704
|
}
|
|
3572
3705
|
if ((value == null || val.indexOf(value) > -1) && isSelected) {
|
|
3573
|
-
matches.push(element)
|
|
3706
|
+
matches.push(element)
|
|
3574
3707
|
}
|
|
3575
3708
|
}
|
|
3576
|
-
return matches
|
|
3709
|
+
return matches
|
|
3577
3710
|
}
|
|
3578
3711
|
|
|
3579
3712
|
async function filterFieldsBySelectionState(elements, state) {
|
|
3580
|
-
const matches = []
|
|
3713
|
+
const matches = []
|
|
3581
3714
|
for (const element of elements) {
|
|
3582
|
-
const isSelected = await elementSelected(element)
|
|
3715
|
+
const isSelected = await elementSelected(element)
|
|
3583
3716
|
if (isSelected === state) {
|
|
3584
|
-
matches.push(element)
|
|
3717
|
+
matches.push(element)
|
|
3585
3718
|
}
|
|
3586
3719
|
}
|
|
3587
|
-
return matches
|
|
3720
|
+
return matches
|
|
3588
3721
|
}
|
|
3589
3722
|
|
|
3590
3723
|
async function elementSelected(element) {
|
|
3591
|
-
const type = await element.getAttribute('type')
|
|
3724
|
+
const type = await element.getAttribute('type')
|
|
3592
3725
|
|
|
3593
3726
|
if (type === 'checkbox' || type === 'radio') {
|
|
3594
|
-
return element.isChecked()
|
|
3727
|
+
return element.isChecked()
|
|
3595
3728
|
}
|
|
3596
|
-
return element.getAttribute('selected')
|
|
3729
|
+
return element.getAttribute('selected')
|
|
3597
3730
|
}
|
|
3598
3731
|
|
|
3599
3732
|
function isFrameLocator(locator) {
|
|
3600
|
-
locator = new Locator(locator)
|
|
3733
|
+
locator = new Locator(locator)
|
|
3601
3734
|
if (locator.isFrame()) {
|
|
3602
|
-
return locator.value
|
|
3735
|
+
return locator.value
|
|
3603
3736
|
}
|
|
3604
|
-
return false
|
|
3737
|
+
return false
|
|
3605
3738
|
}
|
|
3606
3739
|
|
|
3607
3740
|
function assertElementExists(res, locator, prefix, suffix) {
|
|
3608
3741
|
if (!res || res.length === 0) {
|
|
3609
|
-
throw new ElementNotFound(locator, prefix, suffix)
|
|
3742
|
+
throw new ElementNotFound(locator, prefix, suffix)
|
|
3610
3743
|
}
|
|
3611
3744
|
}
|
|
3612
3745
|
|
|
3613
3746
|
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)
|
|
3747
|
+
const found = document.evaluate(selector, element || document.body, null, 5, null)
|
|
3748
|
+
const res = []
|
|
3749
|
+
let current = null
|
|
3750
|
+
while ((current = found.iterateNext())) {
|
|
3751
|
+
res.push(current)
|
|
3619
3752
|
}
|
|
3620
|
-
return res
|
|
3753
|
+
return res
|
|
3621
3754
|
}
|
|
3622
3755
|
|
|
3623
3756
|
async function targetCreatedHandler(page) {
|
|
3624
|
-
if (!page) return
|
|
3625
|
-
this.withinLocator = null
|
|
3757
|
+
if (!page) return
|
|
3758
|
+
this.withinLocator = null
|
|
3626
3759
|
page.on('load', () => {
|
|
3627
|
-
page
|
|
3760
|
+
page
|
|
3761
|
+
.$('body')
|
|
3628
3762
|
.catch(() => null)
|
|
3629
3763
|
.then(async () => {
|
|
3630
3764
|
if (this.context && this.context._type === 'Frame') {
|
|
3631
3765
|
// we are inside iframe?
|
|
3632
|
-
const frameEl = await this.context.frameElement()
|
|
3633
|
-
this.context = await frameEl.contentFrame()
|
|
3634
|
-
this.contextLocator = null
|
|
3635
|
-
return
|
|
3766
|
+
const frameEl = await this.context.frameElement()
|
|
3767
|
+
this.context = await frameEl.contentFrame()
|
|
3768
|
+
this.contextLocator = null
|
|
3769
|
+
return
|
|
3636
3770
|
}
|
|
3637
3771
|
// if context element was in iframe - keep it
|
|
3638
3772
|
// if (await this.context.ownerFrame()) return;
|
|
3639
|
-
this.context = page
|
|
3640
|
-
this.contextLocator = null
|
|
3641
|
-
})
|
|
3642
|
-
})
|
|
3773
|
+
this.context = page
|
|
3774
|
+
this.contextLocator = null
|
|
3775
|
+
})
|
|
3776
|
+
})
|
|
3643
3777
|
page.on('console', (msg) => {
|
|
3644
3778
|
if (!consoleLogStore.includes(msg) && this.options.ignoreLog && !this.options.ignoreLog.includes(msg.type())) {
|
|
3645
|
-
this.debugSection(
|
|
3779
|
+
this.debugSection(
|
|
3780
|
+
`Browser:${ucfirst(msg.type())}`,
|
|
3781
|
+
((msg.text && msg.text()) || msg._text || '') + msg.args().join(' '),
|
|
3782
|
+
)
|
|
3646
3783
|
}
|
|
3647
|
-
consoleLogStore.add(msg)
|
|
3648
|
-
})
|
|
3784
|
+
consoleLogStore.add(msg)
|
|
3785
|
+
})
|
|
3649
3786
|
|
|
3650
3787
|
if (this.options.windowSize && this.options.windowSize.indexOf('x') > 0 && this._getType() === 'Browser') {
|
|
3651
3788
|
try {
|
|
3652
|
-
await page.setViewportSize(parseWindowSize(this.options.windowSize))
|
|
3789
|
+
await page.setViewportSize(parseWindowSize(this.options.windowSize))
|
|
3653
3790
|
} catch (err) {
|
|
3654
|
-
this.debug('Target can be already closed, ignoring...')
|
|
3791
|
+
this.debug('Target can be already closed, ignoring...')
|
|
3655
3792
|
}
|
|
3656
3793
|
}
|
|
3657
3794
|
}
|
|
3658
3795
|
|
|
3659
3796
|
function parseWindowSize(windowSize) {
|
|
3660
|
-
if (!windowSize) return { width: 800, height: 600 }
|
|
3661
|
-
|
|
3797
|
+
if (!windowSize) return { width: 800, height: 600 }
|
|
3798
|
+
|
|
3799
|
+
if (windowSize.width && windowSize.height) {
|
|
3800
|
+
return { width: parseInt(windowSize.width, 10), height: parseInt(windowSize.height, 10) }
|
|
3801
|
+
}
|
|
3802
|
+
|
|
3803
|
+
const dimensions = windowSize.split('x')
|
|
3662
3804
|
if (dimensions.length < 2 || windowSize === 'maximize') {
|
|
3663
|
-
console.log('Invalid window size, setting window to default values')
|
|
3664
|
-
return { width: 800, height: 600 }
|
|
3805
|
+
console.log('Invalid window size, setting window to default values')
|
|
3806
|
+
return { width: 800, height: 600 } // invalid size
|
|
3665
3807
|
}
|
|
3666
|
-
const width = parseInt(dimensions[0], 10)
|
|
3667
|
-
const height = parseInt(dimensions[1], 10)
|
|
3668
|
-
return { width, height }
|
|
3808
|
+
const width = parseInt(dimensions[0], 10)
|
|
3809
|
+
const height = parseInt(dimensions[1], 10)
|
|
3810
|
+
return { width, height }
|
|
3669
3811
|
}
|
|
3670
3812
|
|
|
3671
3813
|
// List of key values to key definitions
|
|
3672
3814
|
// https://github.com/puppeteer/puppeteer/blob/v1.20.0/lib/USKeyboardLayout.js
|
|
3673
3815
|
const keyDefinitionMap = {
|
|
3674
3816
|
/* eslint-disable quote-props */
|
|
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
|
-
|
|
3817
|
+
0: 'Digit0',
|
|
3818
|
+
1: 'Digit1',
|
|
3819
|
+
2: 'Digit2',
|
|
3820
|
+
3: 'Digit3',
|
|
3821
|
+
4: 'Digit4',
|
|
3822
|
+
5: 'Digit5',
|
|
3823
|
+
6: 'Digit6',
|
|
3824
|
+
7: 'Digit7',
|
|
3825
|
+
8: 'Digit8',
|
|
3826
|
+
9: 'Digit9',
|
|
3827
|
+
a: 'KeyA',
|
|
3828
|
+
b: 'KeyB',
|
|
3829
|
+
c: 'KeyC',
|
|
3830
|
+
d: 'KeyD',
|
|
3831
|
+
e: 'KeyE',
|
|
3832
|
+
f: 'KeyF',
|
|
3833
|
+
g: 'KeyG',
|
|
3834
|
+
h: 'KeyH',
|
|
3835
|
+
i: 'KeyI',
|
|
3836
|
+
j: 'KeyJ',
|
|
3837
|
+
k: 'KeyK',
|
|
3838
|
+
l: 'KeyL',
|
|
3839
|
+
m: 'KeyM',
|
|
3840
|
+
n: 'KeyN',
|
|
3841
|
+
o: 'KeyO',
|
|
3842
|
+
p: 'KeyP',
|
|
3843
|
+
q: 'KeyQ',
|
|
3844
|
+
r: 'KeyR',
|
|
3845
|
+
s: 'KeyS',
|
|
3846
|
+
t: 'KeyT',
|
|
3847
|
+
u: 'KeyU',
|
|
3848
|
+
v: 'KeyV',
|
|
3849
|
+
w: 'KeyW',
|
|
3850
|
+
x: 'KeyX',
|
|
3851
|
+
y: 'KeyY',
|
|
3852
|
+
z: 'KeyZ',
|
|
3711
3853
|
';': 'Semicolon',
|
|
3712
3854
|
'=': 'Equal',
|
|
3713
3855
|
',': 'Comma',
|
|
@@ -3718,89 +3860,92 @@ const keyDefinitionMap = {
|
|
|
3718
3860
|
'[': 'BracketLeft',
|
|
3719
3861
|
'\\': 'Backslash',
|
|
3720
3862
|
']': 'BracketRight',
|
|
3721
|
-
'
|
|
3863
|
+
"'": 'Quote',
|
|
3722
3864
|
/* eslint-enable quote-props */
|
|
3723
|
-
}
|
|
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
|
}
|