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