codeceptjs 3.6.10 → 3.7.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +89 -119
- package/bin/codecept.js +9 -2
- package/docs/webapi/clearCookie.mustache +1 -1
- package/lib/actor.js +66 -102
- package/lib/ai.js +130 -121
- package/lib/assert/empty.js +3 -5
- package/lib/assert/equal.js +4 -7
- package/lib/assert/include.js +4 -6
- package/lib/assert/throws.js +2 -4
- package/lib/assert/truth.js +2 -2
- package/lib/codecept.js +87 -83
- package/lib/command/check.js +186 -0
- package/lib/command/configMigrate.js +2 -4
- package/lib/command/definitions.js +8 -26
- package/lib/command/generate.js +10 -14
- package/lib/command/gherkin/snippets.js +10 -8
- package/lib/command/gherkin/steps.js +1 -1
- package/lib/command/info.js +1 -3
- package/lib/command/init.js +8 -12
- package/lib/command/interactive.js +2 -2
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +5 -57
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +68 -232
- package/lib/container.js +354 -237
- package/lib/data/context.js +10 -13
- package/lib/data/dataScenarioConfig.js +8 -8
- package/lib/data/dataTableArgument.js +6 -6
- package/lib/data/table.js +5 -11
- package/lib/effects.js +218 -0
- package/lib/els.js +158 -0
- package/lib/event.js +19 -17
- package/lib/heal.js +88 -80
- package/lib/helper/AI.js +2 -1
- package/lib/helper/ApiDataFactory.js +3 -6
- package/lib/helper/Appium.js +45 -51
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +3 -3
- package/lib/helper/JSONResponse.js +57 -37
- package/lib/helper/Nightmare.js +35 -53
- package/lib/helper/Playwright.js +211 -252
- package/lib/helper/Protractor.js +54 -77
- package/lib/helper/Puppeteer.js +139 -232
- package/lib/helper/REST.js +5 -17
- package/lib/helper/TestCafe.js +21 -44
- package/lib/helper/WebDriver.js +131 -169
- package/lib/helper/testcafe/testcafe-utils.js +26 -27
- package/lib/listener/emptyRun.js +55 -0
- package/lib/listener/exit.js +7 -10
- package/lib/listener/{retry.js → globalRetry.js} +5 -5
- package/lib/listener/globalTimeout.js +165 -0
- package/lib/listener/helpers.js +15 -15
- package/lib/listener/mocha.js +1 -1
- package/lib/listener/result.js +12 -0
- package/lib/listener/steps.js +20 -18
- package/lib/listener/store.js +20 -0
- package/lib/mocha/asyncWrapper.js +216 -0
- package/lib/{interfaces → mocha}/bdd.js +3 -3
- package/lib/mocha/cli.js +308 -0
- package/lib/mocha/factory.js +104 -0
- package/lib/{interfaces → mocha}/featureConfig.js +24 -12
- package/lib/{interfaces → mocha}/gherkin.js +26 -28
- package/lib/mocha/hooks.js +112 -0
- package/lib/mocha/index.js +12 -0
- package/lib/mocha/inject.js +29 -0
- package/lib/{interfaces → mocha}/scenarioConfig.js +21 -6
- package/lib/mocha/suite.js +81 -0
- package/lib/mocha/test.js +159 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +219 -0
- package/lib/output.js +82 -62
- package/lib/pause.js +155 -138
- package/lib/plugin/analyze.js +349 -0
- package/lib/plugin/autoDelay.js +6 -6
- package/lib/plugin/autoLogin.js +6 -7
- package/lib/plugin/commentStep.js +6 -1
- package/lib/plugin/coverage.js +10 -19
- package/lib/plugin/customLocator.js +3 -3
- package/lib/plugin/customReporter.js +52 -0
- package/lib/plugin/eachElement.js +1 -1
- package/lib/plugin/fakerTransform.js +1 -1
- package/lib/plugin/heal.js +36 -9
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/retryFailedStep.js +4 -4
- package/lib/plugin/retryTo.js +18 -118
- package/lib/plugin/screenshotOnFail.js +17 -49
- package/lib/plugin/selenoid.js +15 -35
- package/lib/plugin/standardActingHelpers.js +4 -1
- package/lib/plugin/stepByStepReport.js +56 -17
- package/lib/plugin/stepTimeout.js +5 -12
- package/lib/plugin/subtitles.js +4 -4
- package/lib/plugin/tryTo.js +17 -107
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +146 -125
- package/lib/rerun.js +43 -42
- package/lib/result.js +161 -0
- package/lib/secret.js +1 -1
- package/lib/step/base.js +228 -0
- package/lib/step/config.js +50 -0
- package/lib/step/func.js +46 -0
- package/lib/step/helper.js +50 -0
- package/lib/step/meta.js +99 -0
- package/lib/step/record.js +74 -0
- package/lib/step/retry.js +11 -0
- package/lib/step/section.js +55 -0
- package/lib/step.js +21 -332
- package/lib/steps.js +50 -0
- package/lib/store.js +10 -2
- package/lib/template/heal.js +2 -11
- package/lib/timeout.js +66 -0
- package/lib/utils.js +317 -216
- package/lib/within.js +73 -55
- package/lib/workers.js +259 -275
- package/package.json +56 -54
- package/typings/index.d.ts +175 -186
- package/typings/promiseBasedTypes.d.ts +164 -17
- package/typings/types.d.ts +284 -115
- package/lib/cli.js +0 -256
- package/lib/helper/ExpectHelper.js +0 -391
- package/lib/helper/SoftExpectHelper.js +0 -381
- package/lib/listener/artifacts.js +0 -19
- package/lib/listener/timeout.js +0 -109
- package/lib/mochaFactory.js +0 -113
- package/lib/plugin/debugErrors.js +0 -67
- package/lib/scenario.js +0 -224
- package/lib/ui.js +0 -236
package/lib/helper/Playwright.js
CHANGED
|
@@ -7,6 +7,7 @@ const assert = require('assert')
|
|
|
7
7
|
const promiseRetry = require('promise-retry')
|
|
8
8
|
const Locator = require('../locator')
|
|
9
9
|
const recorder = require('../recorder')
|
|
10
|
+
const store = require('../store')
|
|
10
11
|
const stringIncludes = require('../assert/include').includes
|
|
11
12
|
const { urlEquals } = require('../assert/equal')
|
|
12
13
|
const { equals } = require('../assert/equal')
|
|
@@ -24,6 +25,7 @@ const {
|
|
|
24
25
|
clearString,
|
|
25
26
|
requireWithFallback,
|
|
26
27
|
normalizeSpacesInString,
|
|
28
|
+
relativeDir,
|
|
27
29
|
} = require('../utils')
|
|
28
30
|
const { isColorProperty, convertColorToRGBA } = require('../colorUtils')
|
|
29
31
|
const ElementNotFound = require('./errors/ElementNotFound')
|
|
@@ -40,26 +42,10 @@ const popupStore = new Popup()
|
|
|
40
42
|
const consoleLogStore = new Console()
|
|
41
43
|
const availableBrowsers = ['chromium', 'webkit', 'firefox', 'electron']
|
|
42
44
|
|
|
43
|
-
const {
|
|
44
|
-
setRestartStrategy,
|
|
45
|
-
restartsSession,
|
|
46
|
-
restartsContext,
|
|
47
|
-
restartsBrowser,
|
|
48
|
-
} = require('./extras/PlaywrightRestartOpts')
|
|
45
|
+
const { setRestartStrategy, restartsSession, restartsContext, restartsBrowser } = require('./extras/PlaywrightRestartOpts')
|
|
49
46
|
const { createValueEngine, createDisabledEngine } = require('./extras/PlaywrightPropEngine')
|
|
50
|
-
const {
|
|
51
|
-
|
|
52
|
-
dontSeeElementError,
|
|
53
|
-
dontSeeElementInDOMError,
|
|
54
|
-
seeElementInDOMError,
|
|
55
|
-
} = require('./errors/ElementAssertion')
|
|
56
|
-
const {
|
|
57
|
-
dontSeeTraffic,
|
|
58
|
-
seeTraffic,
|
|
59
|
-
grabRecordedNetworkTraffics,
|
|
60
|
-
stopRecordingTraffic,
|
|
61
|
-
flushNetworkTraffics,
|
|
62
|
-
} = require('./network/actions')
|
|
47
|
+
const { seeElementError, dontSeeElementError, dontSeeElementInDOMError, seeElementInDOMError } = require('./errors/ElementAssertion')
|
|
48
|
+
const { dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics } = require('./network/actions')
|
|
63
49
|
|
|
64
50
|
const pathSeparator = path.sep
|
|
65
51
|
|
|
@@ -392,9 +378,7 @@ class Playwright extends Helper {
|
|
|
392
378
|
config = Object.assign(defaults, config)
|
|
393
379
|
|
|
394
380
|
if (availableBrowsers.indexOf(config.browser) < 0) {
|
|
395
|
-
throw new Error(
|
|
396
|
-
`Invalid config. Can't use browser "${config.browser}". Accepted values: ${availableBrowsers.join(', ')}`,
|
|
397
|
-
)
|
|
381
|
+
throw new Error(`Invalid config. Can't use browser "${config.browser}". Accepted values: ${availableBrowsers.join(', ')}`)
|
|
398
382
|
}
|
|
399
383
|
|
|
400
384
|
return config
|
|
@@ -440,9 +424,7 @@ class Playwright extends Helper {
|
|
|
440
424
|
}
|
|
441
425
|
this.isRemoteBrowser = !!this.playwrightOptions.browserWSEndpoint
|
|
442
426
|
this.isElectron = this.options.browser === 'electron'
|
|
443
|
-
this.userDataDir = this.playwrightOptions.userDataDir
|
|
444
|
-
? `${this.playwrightOptions.userDataDir}_${Date.now().toString()}`
|
|
445
|
-
: undefined
|
|
427
|
+
this.userDataDir = this.playwrightOptions.userDataDir ? `${this.playwrightOptions.userDataDir}_${Date.now().toString()}` : undefined
|
|
446
428
|
this.isCDPConnection = this.playwrightOptions.cdpConnection
|
|
447
429
|
popupStore.defaultAction = this.options.defaultPopupAction
|
|
448
430
|
}
|
|
@@ -458,14 +440,14 @@ class Playwright extends Helper {
|
|
|
458
440
|
name: 'url',
|
|
459
441
|
message: 'Base url of site to be tested',
|
|
460
442
|
default: 'http://localhost',
|
|
461
|
-
when:
|
|
443
|
+
when: answers => answers.Playwright_browser !== 'electron',
|
|
462
444
|
},
|
|
463
445
|
{
|
|
464
446
|
name: 'show',
|
|
465
447
|
message: 'Show browser window',
|
|
466
448
|
default: true,
|
|
467
449
|
type: 'confirm',
|
|
468
|
-
when:
|
|
450
|
+
when: answers => answers.Playwright_browser !== 'electron',
|
|
469
451
|
},
|
|
470
452
|
]
|
|
471
453
|
}
|
|
@@ -500,9 +482,10 @@ class Playwright extends Helper {
|
|
|
500
482
|
|
|
501
483
|
async _before(test) {
|
|
502
484
|
this.currentRunningTest = test
|
|
485
|
+
|
|
503
486
|
recorder.retry({
|
|
504
487
|
retries: process.env.FAILED_STEP_RETRIES || 3,
|
|
505
|
-
when:
|
|
488
|
+
when: err => {
|
|
506
489
|
if (!err || typeof err.message !== 'string') {
|
|
507
490
|
return false
|
|
508
491
|
}
|
|
@@ -540,12 +523,17 @@ class Playwright extends Helper {
|
|
|
540
523
|
this.currentRunningTest.artifacts.har = fileName
|
|
541
524
|
contextOptions.recordHar = this.options.recordHar
|
|
542
525
|
}
|
|
526
|
+
|
|
527
|
+
// load pre-saved cookies
|
|
528
|
+
if (test?.opts?.cookies) contextOptions.storageState = { cookies: test.opts.cookies }
|
|
529
|
+
|
|
543
530
|
if (this.storageState) contextOptions.storageState = this.storageState
|
|
544
531
|
if (this.options.userAgent) contextOptions.userAgent = this.options.userAgent
|
|
545
532
|
if (this.options.locale) contextOptions.locale = this.options.locale
|
|
546
533
|
if (this.options.colorScheme) contextOptions.colorScheme = this.options.colorScheme
|
|
547
534
|
this.contextOptions = contextOptions
|
|
548
535
|
if (!this.browserContext || !restartsSession()) {
|
|
536
|
+
this.debugSection('New Session', JSON.stringify(this.contextOptions))
|
|
549
537
|
this.browserContext = await this.browser.newContext(this.contextOptions) // Adding the HTTPSError ignore in the context so that we can ignore those errors
|
|
550
538
|
}
|
|
551
539
|
}
|
|
@@ -559,10 +547,7 @@ class Playwright extends Helper {
|
|
|
559
547
|
mainPage = existingPages[0] || (await this.browserContext.newPage())
|
|
560
548
|
} catch (e) {
|
|
561
549
|
if (this.playwrightOptions.userDataDir) {
|
|
562
|
-
this.browser = await playwright[this.options.browser].launchPersistentContext(
|
|
563
|
-
this.userDataDir,
|
|
564
|
-
this.playwrightOptions,
|
|
565
|
-
)
|
|
550
|
+
this.browser = await playwright[this.options.browser].launchPersistentContext(this.userDataDir, this.playwrightOptions)
|
|
566
551
|
this.browserContext = this.browser
|
|
567
552
|
const existingPages = await this.browserContext.pages()
|
|
568
553
|
mainPage = existingPages[0]
|
|
@@ -573,6 +558,15 @@ class Playwright extends Helper {
|
|
|
573
558
|
|
|
574
559
|
await this._setPage(mainPage)
|
|
575
560
|
|
|
561
|
+
try {
|
|
562
|
+
// set metadata for reporting
|
|
563
|
+
test.meta.browser = this.browser.browserType().name()
|
|
564
|
+
test.meta.browserVersion = this.browser.version()
|
|
565
|
+
test.meta.windowSize = `${this.page.viewportSize().width}x${this.page.viewportSize().height}`
|
|
566
|
+
} catch (e) {
|
|
567
|
+
this.debug('Failed to set metadata for reporting')
|
|
568
|
+
}
|
|
569
|
+
|
|
576
570
|
if (this.options.trace) await this.browserContext.tracing.start({ screenshots: true, snapshots: true })
|
|
577
571
|
|
|
578
572
|
return this.browser
|
|
@@ -583,7 +577,7 @@ class Playwright extends Helper {
|
|
|
583
577
|
|
|
584
578
|
if (this.isElectron) {
|
|
585
579
|
this.browser.close()
|
|
586
|
-
this.electronSessions.forEach(
|
|
580
|
+
this.electronSessions.forEach(session => session.close())
|
|
587
581
|
return
|
|
588
582
|
}
|
|
589
583
|
|
|
@@ -605,7 +599,7 @@ class Playwright extends Helper {
|
|
|
605
599
|
this.storageState = await currentContext.storageState()
|
|
606
600
|
}
|
|
607
601
|
|
|
608
|
-
await Promise.all(contexts.map(
|
|
602
|
+
await Promise.all(contexts.map(c => c.close()))
|
|
609
603
|
}
|
|
610
604
|
} catch (e) {
|
|
611
605
|
console.log(e)
|
|
@@ -641,10 +635,7 @@ class Playwright extends Helper {
|
|
|
641
635
|
page = await browserContext.newPage()
|
|
642
636
|
} catch (e) {
|
|
643
637
|
if (this.playwrightOptions.userDataDir) {
|
|
644
|
-
browserContext = await playwright[this.options.browser].launchPersistentContext(
|
|
645
|
-
`${this.userDataDir}_${this.activeSessionName}`,
|
|
646
|
-
this.playwrightOptions,
|
|
647
|
-
)
|
|
638
|
+
browserContext = await playwright[this.options.browser].launchPersistentContext(`${this.userDataDir}_${this.activeSessionName}`, this.playwrightOptions)
|
|
648
639
|
this.browser = browserContext
|
|
649
640
|
page = await browserContext.pages()[0]
|
|
650
641
|
}
|
|
@@ -660,7 +651,7 @@ class Playwright extends Helper {
|
|
|
660
651
|
stop: async () => {
|
|
661
652
|
// is closed by _after
|
|
662
653
|
},
|
|
663
|
-
loadVars: async
|
|
654
|
+
loadVars: async context => {
|
|
664
655
|
if (context) {
|
|
665
656
|
this.browserContext = context
|
|
666
657
|
const existingPages = await context.pages()
|
|
@@ -668,7 +659,7 @@ class Playwright extends Helper {
|
|
|
668
659
|
return this._setPage(this.sessionPages[this.activeSessionName])
|
|
669
660
|
}
|
|
670
661
|
},
|
|
671
|
-
restoreVars: async
|
|
662
|
+
restoreVars: async session => {
|
|
672
663
|
this.withinLocator = null
|
|
673
664
|
this.browserContext = defaultContext
|
|
674
665
|
|
|
@@ -793,7 +784,7 @@ class Playwright extends Helper {
|
|
|
793
784
|
return
|
|
794
785
|
}
|
|
795
786
|
page.removeAllListeners('dialog')
|
|
796
|
-
page.on('dialog', async
|
|
787
|
+
page.on('dialog', async dialog => {
|
|
797
788
|
popupStore.popup = dialog
|
|
798
789
|
const action = popupStore.actionType || this.options.defaultPopupAction
|
|
799
790
|
await this._waitForAction()
|
|
@@ -856,16 +847,13 @@ class Playwright extends Helper {
|
|
|
856
847
|
throw err
|
|
857
848
|
}
|
|
858
849
|
} else if (this.playwrightOptions.userDataDir) {
|
|
859
|
-
this.browser = await playwright[this.options.browser].launchPersistentContext(
|
|
860
|
-
this.userDataDir,
|
|
861
|
-
this.playwrightOptions,
|
|
862
|
-
)
|
|
850
|
+
this.browser = await playwright[this.options.browser].launchPersistentContext(this.userDataDir, this.playwrightOptions)
|
|
863
851
|
} else {
|
|
864
852
|
this.browser = await playwright[this.options.browser].launch(this.playwrightOptions)
|
|
865
853
|
}
|
|
866
854
|
|
|
867
855
|
// works only for Chromium
|
|
868
|
-
this.browser.on('targetchanged',
|
|
856
|
+
this.browser.on('targetchanged', target => {
|
|
869
857
|
this.debugSection('Url', target.url())
|
|
870
858
|
})
|
|
871
859
|
|
|
@@ -940,7 +928,7 @@ class Playwright extends Helper {
|
|
|
940
928
|
const navigationStart = timing.navigationStart
|
|
941
929
|
|
|
942
930
|
const extractedData = {}
|
|
943
|
-
dataNames.forEach(
|
|
931
|
+
dataNames.forEach(name => {
|
|
944
932
|
extractedData[name] = timing[name] - navigationStart
|
|
945
933
|
})
|
|
946
934
|
|
|
@@ -955,7 +943,8 @@ class Playwright extends Helper {
|
|
|
955
943
|
throw new Error('Cannot open pages inside an Electron container')
|
|
956
944
|
}
|
|
957
945
|
if (!/^\w+\:(\/\/|.+)/.test(url)) {
|
|
958
|
-
url = this.options.url + (url.startsWith('/') ? url : `/${url}`)
|
|
946
|
+
url = this.options.url + (!this.options.url.endsWith('/') && url.startsWith('/') ? url : `/${url}`)
|
|
947
|
+
this.debug(`Changed URL to base url + relative path: ${url}`)
|
|
959
948
|
}
|
|
960
949
|
|
|
961
950
|
if (this.options.basicAuth && this.isAuthenticated !== true) {
|
|
@@ -969,13 +958,7 @@ class Playwright extends Helper {
|
|
|
969
958
|
|
|
970
959
|
const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)))
|
|
971
960
|
|
|
972
|
-
perfTiming = this._extractDataFromPerformanceTiming(
|
|
973
|
-
performanceTiming,
|
|
974
|
-
'responseEnd',
|
|
975
|
-
'domInteractive',
|
|
976
|
-
'domContentLoadedEventEnd',
|
|
977
|
-
'loadEventEnd',
|
|
978
|
-
)
|
|
961
|
+
perfTiming = this._extractDataFromPerformanceTiming(performanceTiming, 'responseEnd', 'domInteractive', 'domContentLoadedEventEnd', 'loadEventEnd')
|
|
979
962
|
|
|
980
963
|
return this._waitForAction()
|
|
981
964
|
}
|
|
@@ -1197,10 +1180,7 @@ class Playwright extends Helper {
|
|
|
1197
1180
|
return this.executeScript(() => {
|
|
1198
1181
|
const body = document.body
|
|
1199
1182
|
const html = document.documentElement
|
|
1200
|
-
window.scrollTo(
|
|
1201
|
-
0,
|
|
1202
|
-
Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
|
|
1203
|
-
)
|
|
1183
|
+
window.scrollTo(0, Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight))
|
|
1204
1184
|
})
|
|
1205
1185
|
}
|
|
1206
1186
|
|
|
@@ -1287,7 +1267,22 @@ class Playwright extends Helper {
|
|
|
1287
1267
|
|
|
1288
1268
|
if (this.frame) return findElements(this.frame, locator)
|
|
1289
1269
|
|
|
1290
|
-
|
|
1270
|
+
const els = await findElements(context, locator)
|
|
1271
|
+
|
|
1272
|
+
if (store.debugMode) {
|
|
1273
|
+
const previewElements = els.slice(0, 3)
|
|
1274
|
+
let htmls = await Promise.all(previewElements.map(el => elToString(el, previewElements.length)))
|
|
1275
|
+
if (els.length > 3) htmls.push('...')
|
|
1276
|
+
if (els.length > 1) {
|
|
1277
|
+
this.debugSection(`Elements (${els.length})`, htmls.join('|').trim())
|
|
1278
|
+
} else if (els.length === 1) {
|
|
1279
|
+
this.debugSection('Element', htmls.join('|').trim())
|
|
1280
|
+
} else {
|
|
1281
|
+
this.debug(`No elements found by ${JSON.stringify(locator).slice(0, 50)}....`)
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
|
|
1285
|
+
return els
|
|
1291
1286
|
}
|
|
1292
1287
|
|
|
1293
1288
|
/**
|
|
@@ -1437,10 +1432,10 @@ class Playwright extends Helper {
|
|
|
1437
1432
|
*/
|
|
1438
1433
|
async closeOtherTabs() {
|
|
1439
1434
|
const pages = await this.browserContext.pages()
|
|
1440
|
-
const otherPages = pages.filter(
|
|
1435
|
+
const otherPages = pages.filter(page => page !== this.page)
|
|
1441
1436
|
if (otherPages.length) {
|
|
1442
1437
|
this.debug(`Closing ${otherPages.length} tabs`)
|
|
1443
|
-
return Promise.all(otherPages.map(
|
|
1438
|
+
return Promise.all(otherPages.map(p => p.close()))
|
|
1444
1439
|
}
|
|
1445
1440
|
return Promise.resolve()
|
|
1446
1441
|
}
|
|
@@ -1483,9 +1478,9 @@ class Playwright extends Helper {
|
|
|
1483
1478
|
*/
|
|
1484
1479
|
async seeElement(locator) {
|
|
1485
1480
|
let els = await this._locate(locator)
|
|
1486
|
-
els = await Promise.all(els.map(
|
|
1481
|
+
els = await Promise.all(els.map(el => el.isVisible()))
|
|
1487
1482
|
try {
|
|
1488
|
-
return empty('visible elements').negate(els.filter(
|
|
1483
|
+
return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'))
|
|
1489
1484
|
} catch (e) {
|
|
1490
1485
|
dontSeeElementError(locator)
|
|
1491
1486
|
}
|
|
@@ -1497,9 +1492,9 @@ class Playwright extends Helper {
|
|
|
1497
1492
|
*/
|
|
1498
1493
|
async dontSeeElement(locator) {
|
|
1499
1494
|
let els = await this._locate(locator)
|
|
1500
|
-
els = await Promise.all(els.map(
|
|
1495
|
+
els = await Promise.all(els.map(el => el.isVisible()))
|
|
1501
1496
|
try {
|
|
1502
|
-
return empty('visible elements').assert(els.filter(
|
|
1497
|
+
return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'))
|
|
1503
1498
|
} catch (e) {
|
|
1504
1499
|
seeElementError(locator)
|
|
1505
1500
|
}
|
|
@@ -1511,7 +1506,7 @@ class Playwright extends Helper {
|
|
|
1511
1506
|
async seeElementInDOM(locator) {
|
|
1512
1507
|
const els = await this._locate(locator)
|
|
1513
1508
|
try {
|
|
1514
|
-
return empty('elements on page').negate(els.filter(
|
|
1509
|
+
return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'))
|
|
1515
1510
|
} catch (e) {
|
|
1516
1511
|
dontSeeElementInDOMError(locator)
|
|
1517
1512
|
}
|
|
@@ -1523,7 +1518,7 @@ class Playwright extends Helper {
|
|
|
1523
1518
|
async dontSeeElementInDOM(locator) {
|
|
1524
1519
|
const els = await this._locate(locator)
|
|
1525
1520
|
try {
|
|
1526
|
-
return empty('elements on a page').assert(els.filter(
|
|
1521
|
+
return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'))
|
|
1527
1522
|
} catch (e) {
|
|
1528
1523
|
seeElementInDOMError(locator)
|
|
1529
1524
|
}
|
|
@@ -1547,7 +1542,7 @@ class Playwright extends Helper {
|
|
|
1547
1542
|
* @return {Promise<void>}
|
|
1548
1543
|
*/
|
|
1549
1544
|
async handleDownloads(fileName) {
|
|
1550
|
-
this.page.waitForEvent('download').then(async
|
|
1545
|
+
this.page.waitForEvent('download').then(async download => {
|
|
1551
1546
|
const filePath = await download.path()
|
|
1552
1547
|
fileName = fileName || `downloads/${path.basename(filePath)}`
|
|
1553
1548
|
|
|
@@ -1741,6 +1736,7 @@ class Playwright extends Helper {
|
|
|
1741
1736
|
const el = els[0]
|
|
1742
1737
|
|
|
1743
1738
|
await el.clear()
|
|
1739
|
+
if (store.debugMode) this.debugSection('Focused', await elToString(el, 1))
|
|
1744
1740
|
|
|
1745
1741
|
await highlightActiveElement.call(this, el)
|
|
1746
1742
|
|
|
@@ -1852,8 +1848,8 @@ class Playwright extends Helper {
|
|
|
1852
1848
|
*/
|
|
1853
1849
|
async grabNumberOfVisibleElements(locator) {
|
|
1854
1850
|
let els = await this._locate(locator)
|
|
1855
|
-
els = await Promise.all(els.map(
|
|
1856
|
-
return els.filter(
|
|
1851
|
+
els = await Promise.all(els.map(el => el.isVisible()))
|
|
1852
|
+
return els.filter(v => v).length
|
|
1857
1853
|
}
|
|
1858
1854
|
|
|
1859
1855
|
/**
|
|
@@ -1963,9 +1959,7 @@ class Playwright extends Helper {
|
|
|
1963
1959
|
*/
|
|
1964
1960
|
async seeNumberOfElements(locator, num) {
|
|
1965
1961
|
const elements = await this._locate(locator)
|
|
1966
|
-
return equals(
|
|
1967
|
-
`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`,
|
|
1968
|
-
).assert(elements.length, num)
|
|
1962
|
+
return equals(`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`).assert(elements.length, num)
|
|
1969
1963
|
}
|
|
1970
1964
|
|
|
1971
1965
|
/**
|
|
@@ -1975,10 +1969,7 @@ class Playwright extends Helper {
|
|
|
1975
1969
|
*/
|
|
1976
1970
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1977
1971
|
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1978
|
-
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(
|
|
1979
|
-
res,
|
|
1980
|
-
num,
|
|
1981
|
-
)
|
|
1972
|
+
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(res, num)
|
|
1982
1973
|
}
|
|
1983
1974
|
|
|
1984
1975
|
/**
|
|
@@ -1997,7 +1988,7 @@ class Playwright extends Helper {
|
|
|
1997
1988
|
*/
|
|
1998
1989
|
async seeCookie(name) {
|
|
1999
1990
|
const cookies = await this.browserContext.cookies()
|
|
2000
|
-
empty(`cookie ${name} to be set`).negate(cookies.filter(
|
|
1991
|
+
empty(`cookie ${name} to be set`).negate(cookies.filter(c => c.name === name))
|
|
2001
1992
|
}
|
|
2002
1993
|
|
|
2003
1994
|
/**
|
|
@@ -2005,7 +1996,7 @@ class Playwright extends Helper {
|
|
|
2005
1996
|
*/
|
|
2006
1997
|
async dontSeeCookie(name) {
|
|
2007
1998
|
const cookies = await this.browserContext.cookies()
|
|
2008
|
-
empty(`cookie ${name} not to be set`).assert(cookies.filter(
|
|
1999
|
+
empty(`cookie ${name} not to be set`).assert(cookies.filter(c => c.name === name))
|
|
2009
2000
|
}
|
|
2010
2001
|
|
|
2011
2002
|
/**
|
|
@@ -2016,17 +2007,18 @@ class Playwright extends Helper {
|
|
|
2016
2007
|
async grabCookie(name) {
|
|
2017
2008
|
const cookies = await this.browserContext.cookies()
|
|
2018
2009
|
if (!name) return cookies
|
|
2019
|
-
const cookie = cookies.filter(
|
|
2010
|
+
const cookie = cookies.filter(c => c.name === name)
|
|
2020
2011
|
if (cookie[0]) return cookie[0]
|
|
2021
2012
|
}
|
|
2022
2013
|
|
|
2023
2014
|
/**
|
|
2024
2015
|
* {{> clearCookie }}
|
|
2025
2016
|
*/
|
|
2026
|
-
async clearCookie() {
|
|
2027
|
-
// Playwright currently doesn't support to delete a certain cookie
|
|
2028
|
-
// https://github.com/microsoft/playwright/blob/main/docs/src/api/class-browsercontext.md#async-method-browsercontextclearcookies
|
|
2017
|
+
async clearCookie(cookieName) {
|
|
2029
2018
|
if (!this.browserContext) return
|
|
2019
|
+
if (cookieName) {
|
|
2020
|
+
return this.browserContext.clearCookies({ name: cookieName })
|
|
2021
|
+
}
|
|
2030
2022
|
return this.browserContext.clearCookies()
|
|
2031
2023
|
}
|
|
2032
2024
|
|
|
@@ -2098,7 +2090,7 @@ class Playwright extends Helper {
|
|
|
2098
2090
|
const els = await this._locate(locator)
|
|
2099
2091
|
const texts = []
|
|
2100
2092
|
for (const el of els) {
|
|
2101
|
-
texts.push(await
|
|
2093
|
+
texts.push(await el.innerText())
|
|
2102
2094
|
}
|
|
2103
2095
|
this.debug(`Matched ${els.length} elements`)
|
|
2104
2096
|
return texts
|
|
@@ -2120,7 +2112,7 @@ class Playwright extends Helper {
|
|
|
2120
2112
|
async grabValueFromAll(locator) {
|
|
2121
2113
|
const els = await findFields.call(this, locator)
|
|
2122
2114
|
this.debug(`Matched ${els.length} elements`)
|
|
2123
|
-
return Promise.all(els.map(
|
|
2115
|
+
return Promise.all(els.map(el => el.inputValue()))
|
|
2124
2116
|
}
|
|
2125
2117
|
|
|
2126
2118
|
/**
|
|
@@ -2139,7 +2131,7 @@ class Playwright extends Helper {
|
|
|
2139
2131
|
async grabHTMLFromAll(locator) {
|
|
2140
2132
|
const els = await this._locate(locator)
|
|
2141
2133
|
this.debug(`Matched ${els.length} elements`)
|
|
2142
|
-
return Promise.all(els.map(
|
|
2134
|
+
return Promise.all(els.map(el => el.innerHTML()))
|
|
2143
2135
|
}
|
|
2144
2136
|
|
|
2145
2137
|
/**
|
|
@@ -2160,11 +2152,7 @@ class Playwright extends Helper {
|
|
|
2160
2152
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
2161
2153
|
const els = await this._locate(locator)
|
|
2162
2154
|
this.debug(`Matched ${els.length} elements`)
|
|
2163
|
-
const cssValues = await Promise.all(
|
|
2164
|
-
els.map((el) =>
|
|
2165
|
-
el.evaluate((el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty),
|
|
2166
|
-
),
|
|
2167
|
-
)
|
|
2155
|
+
const cssValues = await Promise.all(els.map(el => el.evaluate((el, cssProperty) => getComputedStyle(el).getPropertyValue(cssProperty), cssProperty)))
|
|
2168
2156
|
|
|
2169
2157
|
return cssValues
|
|
2170
2158
|
}
|
|
@@ -2192,19 +2180,16 @@ class Playwright extends Helper {
|
|
|
2192
2180
|
}
|
|
2193
2181
|
}
|
|
2194
2182
|
|
|
2195
|
-
const values = Object.keys(cssPropertiesCamelCase).map(
|
|
2183
|
+
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key])
|
|
2196
2184
|
if (!Array.isArray(props)) props = [props]
|
|
2197
2185
|
let chunked = chunkArray(props, values.length)
|
|
2198
|
-
chunked = chunked.filter(
|
|
2186
|
+
chunked = chunked.filter(val => {
|
|
2199
2187
|
for (let i = 0; i < val.length; ++i) {
|
|
2200
|
-
// eslint-disable-next-line eqeqeq
|
|
2201
2188
|
if (val[i] != values[i]) return false
|
|
2202
2189
|
}
|
|
2203
2190
|
return true
|
|
2204
2191
|
})
|
|
2205
|
-
return equals(
|
|
2206
|
-
`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
2207
|
-
).assert(chunked.length, elemAmount)
|
|
2192
|
+
return equals(`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount)
|
|
2208
2193
|
}
|
|
2209
2194
|
|
|
2210
2195
|
/**
|
|
@@ -2217,16 +2202,16 @@ class Playwright extends Helper {
|
|
|
2217
2202
|
|
|
2218
2203
|
const elemAmount = res.length
|
|
2219
2204
|
const commands = []
|
|
2220
|
-
res.forEach(
|
|
2221
|
-
Object.keys(attributes).forEach(
|
|
2205
|
+
res.forEach(el => {
|
|
2206
|
+
Object.keys(attributes).forEach(prop => {
|
|
2222
2207
|
commands.push(el.evaluate((el, attr) => el[attr] || el.getAttribute(attr), prop))
|
|
2223
2208
|
})
|
|
2224
2209
|
})
|
|
2225
2210
|
let attrs = await Promise.all(commands)
|
|
2226
|
-
const values = Object.keys(attributes).map(
|
|
2211
|
+
const values = Object.keys(attributes).map(key => attributes[key])
|
|
2227
2212
|
if (!Array.isArray(attrs)) attrs = [attrs]
|
|
2228
2213
|
let chunked = chunkArray(attrs, values.length)
|
|
2229
|
-
chunked = chunked.filter(
|
|
2214
|
+
chunked = chunked.filter(val => {
|
|
2230
2215
|
for (let i = 0; i < val.length; ++i) {
|
|
2231
2216
|
// the attribute could be a boolean
|
|
2232
2217
|
if (typeof val[i] === 'boolean') return val[i] === values[i]
|
|
@@ -2235,10 +2220,7 @@ class Playwright extends Helper {
|
|
|
2235
2220
|
}
|
|
2236
2221
|
return true
|
|
2237
2222
|
})
|
|
2238
|
-
return equals(`all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`).assert(
|
|
2239
|
-
chunked.length,
|
|
2240
|
-
elemAmount,
|
|
2241
|
-
)
|
|
2223
|
+
return equals(`all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`).assert(chunked.length, elemAmount)
|
|
2242
2224
|
}
|
|
2243
2225
|
|
|
2244
2226
|
/**
|
|
@@ -2311,7 +2293,7 @@ class Playwright extends Helper {
|
|
|
2311
2293
|
const fullPageOption = fullPage || this.options.fullPageScreenshots
|
|
2312
2294
|
let outputFile = screenshotOutputFolder(fileName)
|
|
2313
2295
|
|
|
2314
|
-
this.
|
|
2296
|
+
this.debugSection('Screenshot', relativeDir(outputFile))
|
|
2315
2297
|
|
|
2316
2298
|
await this.page.screenshot({
|
|
2317
2299
|
path: outputFile,
|
|
@@ -2324,7 +2306,7 @@ class Playwright extends Helper {
|
|
|
2324
2306
|
const activeSessionPage = this.sessionPages[sessionName]
|
|
2325
2307
|
outputFile = screenshotOutputFolder(`${sessionName}_${fileName}`)
|
|
2326
2308
|
|
|
2327
|
-
this.
|
|
2309
|
+
this.debugSection('Screenshot', `${sessionName} - ${relativeDir(outputFile)}`)
|
|
2328
2310
|
|
|
2329
2311
|
if (activeSessionPage) {
|
|
2330
2312
|
await activeSessionPage.screenshot({
|
|
@@ -2358,9 +2340,7 @@ class Playwright extends Helper {
|
|
|
2358
2340
|
method = method.toLowerCase()
|
|
2359
2341
|
const allowedMethods = ['get', 'post', 'patch', 'head', 'fetch', 'delete']
|
|
2360
2342
|
if (!allowedMethods.includes(method)) {
|
|
2361
|
-
throw new Error(
|
|
2362
|
-
`Method ${method} is not allowed, use the one from a list ${allowedMethods} or switch to using REST helper`,
|
|
2363
|
-
)
|
|
2343
|
+
throw new Error(`Method ${method} is not allowed, use the one from a list ${allowedMethods} or switch to using REST helper`)
|
|
2364
2344
|
}
|
|
2365
2345
|
|
|
2366
2346
|
if (url.startsWith('/')) {
|
|
@@ -2397,10 +2377,7 @@ class Playwright extends Helper {
|
|
|
2397
2377
|
if (this.options.recordVideo && this.page && this.page.video()) {
|
|
2398
2378
|
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.failed`)
|
|
2399
2379
|
for (const sessionName in this.sessionPages) {
|
|
2400
|
-
test.artifacts[`video_${sessionName}`] = saveVideoForPage(
|
|
2401
|
-
this.sessionPages[sessionName],
|
|
2402
|
-
`${test.title}_${sessionName}.failed`,
|
|
2403
|
-
)
|
|
2380
|
+
test.artifacts[`video_${sessionName}`] = saveVideoForPage(this.sessionPages[sessionName], `${test.title}_${sessionName}.failed`)
|
|
2404
2381
|
}
|
|
2405
2382
|
}
|
|
2406
2383
|
|
|
@@ -2408,10 +2385,7 @@ class Playwright extends Helper {
|
|
|
2408
2385
|
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.failed`)
|
|
2409
2386
|
for (const sessionName in this.sessionPages) {
|
|
2410
2387
|
if (!this.sessionPages[sessionName].context) continue
|
|
2411
|
-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2412
|
-
this.sessionPages[sessionName].context,
|
|
2413
|
-
`${test.title}_${sessionName}.failed`,
|
|
2414
|
-
)
|
|
2388
|
+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(this.sessionPages[sessionName].context, `${test.title}_${sessionName}.failed`)
|
|
2415
2389
|
}
|
|
2416
2390
|
}
|
|
2417
2391
|
|
|
@@ -2425,16 +2399,13 @@ class Playwright extends Helper {
|
|
|
2425
2399
|
if (this.options.keepVideoForPassedTests) {
|
|
2426
2400
|
test.artifacts.video = saveVideoForPage(this.page, `${test.title}.passed`)
|
|
2427
2401
|
for (const sessionName of Object.keys(this.sessionPages)) {
|
|
2428
|
-
test.artifacts[`video_${sessionName}`] = saveVideoForPage(
|
|
2429
|
-
this.sessionPages[sessionName],
|
|
2430
|
-
`${test.title}_${sessionName}.passed`,
|
|
2431
|
-
)
|
|
2402
|
+
test.artifacts[`video_${sessionName}`] = saveVideoForPage(this.sessionPages[sessionName], `${test.title}_${sessionName}.passed`)
|
|
2432
2403
|
}
|
|
2433
2404
|
} else {
|
|
2434
2405
|
this.page
|
|
2435
2406
|
.video()
|
|
2436
2407
|
.delete()
|
|
2437
|
-
.catch(
|
|
2408
|
+
.catch(e => {})
|
|
2438
2409
|
}
|
|
2439
2410
|
}
|
|
2440
2411
|
|
|
@@ -2444,10 +2415,7 @@ class Playwright extends Helper {
|
|
|
2444
2415
|
test.artifacts.trace = await saveTraceForContext(this.browserContext, `${test.title}.passed`)
|
|
2445
2416
|
for (const sessionName in this.sessionPages) {
|
|
2446
2417
|
if (!this.sessionPages[sessionName].context) continue
|
|
2447
|
-
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(
|
|
2448
|
-
this.sessionPages[sessionName].context,
|
|
2449
|
-
`${test.title}_${sessionName}.passed`,
|
|
2450
|
-
)
|
|
2418
|
+
test.artifacts[`trace_${sessionName}`] = await saveTraceForContext(this.sessionPages[sessionName].context, `${test.title}_${sessionName}.passed`)
|
|
2451
2419
|
}
|
|
2452
2420
|
}
|
|
2453
2421
|
} else {
|
|
@@ -2464,7 +2432,7 @@ class Playwright extends Helper {
|
|
|
2464
2432
|
* {{> wait }}
|
|
2465
2433
|
*/
|
|
2466
2434
|
async wait(sec) {
|
|
2467
|
-
return new Promise(
|
|
2435
|
+
return new Promise(done => {
|
|
2468
2436
|
setTimeout(done, sec * 1000)
|
|
2469
2437
|
})
|
|
2470
2438
|
}
|
|
@@ -2480,20 +2448,18 @@ class Playwright extends Helper {
|
|
|
2480
2448
|
const context = await this._getContext()
|
|
2481
2449
|
if (!locator.isXPath()) {
|
|
2482
2450
|
const valueFn = function ([locator]) {
|
|
2483
|
-
return Array.from(document.querySelectorAll(locator)).filter(
|
|
2451
|
+
return Array.from(document.querySelectorAll(locator)).filter(el => !el.disabled).length > 0
|
|
2484
2452
|
}
|
|
2485
2453
|
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2486
2454
|
} else {
|
|
2487
2455
|
const enabledFn = function ([locator, $XPath]) {
|
|
2488
|
-
eval($XPath)
|
|
2489
|
-
return $XPath(null, locator).filter(
|
|
2456
|
+
eval($XPath)
|
|
2457
|
+
return $XPath(null, locator).filter(el => !el.disabled).length > 0
|
|
2490
2458
|
}
|
|
2491
2459
|
waiter = context.waitForFunction(enabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2492
2460
|
}
|
|
2493
|
-
return waiter.catch(
|
|
2494
|
-
throw new Error(
|
|
2495
|
-
`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2496
|
-
)
|
|
2461
|
+
return waiter.catch(err => {
|
|
2462
|
+
throw new Error(`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2497
2463
|
})
|
|
2498
2464
|
}
|
|
2499
2465
|
|
|
@@ -2508,20 +2474,18 @@ class Playwright extends Helper {
|
|
|
2508
2474
|
const context = await this._getContext()
|
|
2509
2475
|
if (!locator.isXPath()) {
|
|
2510
2476
|
const valueFn = function ([locator]) {
|
|
2511
|
-
return Array.from(document.querySelectorAll(locator)).filter(
|
|
2477
|
+
return Array.from(document.querySelectorAll(locator)).filter(el => el.disabled).length > 0
|
|
2512
2478
|
}
|
|
2513
2479
|
waiter = context.waitForFunction(valueFn, [locator.value], { timeout: waitTimeout })
|
|
2514
2480
|
} else {
|
|
2515
2481
|
const disabledFn = function ([locator, $XPath]) {
|
|
2516
|
-
eval($XPath)
|
|
2517
|
-
return $XPath(null, locator).filter(
|
|
2482
|
+
eval($XPath)
|
|
2483
|
+
return $XPath(null, locator).filter(el => el.disabled).length > 0
|
|
2518
2484
|
}
|
|
2519
2485
|
waiter = context.waitForFunction(disabledFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
2520
2486
|
}
|
|
2521
|
-
return waiter.catch(
|
|
2522
|
-
throw new Error(
|
|
2523
|
-
`element (${locator.toString()}) is still enabled after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2524
|
-
)
|
|
2487
|
+
return waiter.catch(err => {
|
|
2488
|
+
throw new Error(`element (${locator.toString()}) is still enabled after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2525
2489
|
})
|
|
2526
2490
|
}
|
|
2527
2491
|
|
|
@@ -2536,26 +2500,21 @@ class Playwright extends Helper {
|
|
|
2536
2500
|
const context = await this._getContext()
|
|
2537
2501
|
if (!locator.isXPath()) {
|
|
2538
2502
|
const valueFn = function ([locator, value]) {
|
|
2539
|
-
return (
|
|
2540
|
-
Array.from(document.querySelectorAll(locator)).filter((el) => (el.value || '').indexOf(value) !== -1).length >
|
|
2541
|
-
0
|
|
2542
|
-
)
|
|
2503
|
+
return Array.from(document.querySelectorAll(locator)).filter(el => (el.value || '').indexOf(value) !== -1).length > 0
|
|
2543
2504
|
}
|
|
2544
2505
|
waiter = context.waitForFunction(valueFn, [locator.value, value], { timeout: waitTimeout })
|
|
2545
2506
|
} else {
|
|
2546
2507
|
const valueFn = function ([locator, $XPath, value]) {
|
|
2547
|
-
eval($XPath)
|
|
2548
|
-
return $XPath(null, locator).filter(
|
|
2508
|
+
eval($XPath)
|
|
2509
|
+
return $XPath(null, locator).filter(el => (el.value || '').indexOf(value) !== -1).length > 0
|
|
2549
2510
|
}
|
|
2550
2511
|
waiter = context.waitForFunction(valueFn, [locator.value, $XPath.toString(), value], {
|
|
2551
2512
|
timeout: waitTimeout,
|
|
2552
2513
|
})
|
|
2553
2514
|
}
|
|
2554
|
-
return waiter.catch(
|
|
2515
|
+
return waiter.catch(err => {
|
|
2555
2516
|
const loc = locator.toString()
|
|
2556
|
-
throw new Error(
|
|
2557
|
-
`element (${loc}) is not in DOM or there is no element(${loc}) with value "${value}" after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2558
|
-
)
|
|
2517
|
+
throw new Error(`element (${loc}) is not in DOM or there is no element(${loc}) with value "${value}" after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2559
2518
|
})
|
|
2560
2519
|
}
|
|
2561
2520
|
|
|
@@ -2575,22 +2534,20 @@ class Playwright extends Helper {
|
|
|
2575
2534
|
if (!els || els.length === 0) {
|
|
2576
2535
|
return false
|
|
2577
2536
|
}
|
|
2578
|
-
return Array.prototype.filter.call(els,
|
|
2537
|
+
return Array.prototype.filter.call(els, el => el.offsetParent !== null).length === num
|
|
2579
2538
|
}
|
|
2580
2539
|
waiter = context.waitForFunction(visibleFn, [locator.value, num], { timeout: waitTimeout })
|
|
2581
2540
|
} else {
|
|
2582
2541
|
const visibleFn = function ([locator, $XPath, num]) {
|
|
2583
|
-
eval($XPath)
|
|
2584
|
-
return $XPath(null, locator).filter(
|
|
2542
|
+
eval($XPath)
|
|
2543
|
+
return $XPath(null, locator).filter(el => el.offsetParent !== null).length === num
|
|
2585
2544
|
}
|
|
2586
2545
|
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString(), num], {
|
|
2587
2546
|
timeout: waitTimeout,
|
|
2588
2547
|
})
|
|
2589
2548
|
}
|
|
2590
|
-
return waiter.catch(
|
|
2591
|
-
throw new Error(
|
|
2592
|
-
`The number of elements (${locator.toString()}) is not ${num} after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2593
|
-
)
|
|
2549
|
+
return waiter.catch(err => {
|
|
2550
|
+
throw new Error(`The number of elements (${locator.toString()}) is not ${num} after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2594
2551
|
})
|
|
2595
2552
|
}
|
|
2596
2553
|
|
|
@@ -2598,9 +2555,7 @@ class Playwright extends Helper {
|
|
|
2598
2555
|
* {{> waitForClickable }}
|
|
2599
2556
|
*/
|
|
2600
2557
|
async waitForClickable(locator, waitTimeout) {
|
|
2601
|
-
console.log(
|
|
2602
|
-
'I.waitForClickable is DEPRECATED: This is no longer needed, Playwright automatically waits for element to be clickable',
|
|
2603
|
-
)
|
|
2558
|
+
console.log('I.waitForClickable is DEPRECATED: This is no longer needed, Playwright automatically waits for element to be clickable')
|
|
2604
2559
|
console.log('Remove usage of this function')
|
|
2605
2560
|
}
|
|
2606
2561
|
|
|
@@ -2616,9 +2571,7 @@ class Playwright extends Helper {
|
|
|
2616
2571
|
try {
|
|
2617
2572
|
await context.locator(buildLocatorString(locator)).first().waitFor({ timeout: waitTimeout, state: 'attached' })
|
|
2618
2573
|
} catch (e) {
|
|
2619
|
-
throw new Error(
|
|
2620
|
-
`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${e.message}`,
|
|
2621
|
-
)
|
|
2574
|
+
throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${e.message}`)
|
|
2622
2575
|
}
|
|
2623
2576
|
}
|
|
2624
2577
|
|
|
@@ -2710,10 +2663,8 @@ class Playwright extends Helper {
|
|
|
2710
2663
|
.locator(buildLocatorString(locator))
|
|
2711
2664
|
.first()
|
|
2712
2665
|
.waitFor({ timeout: waitTimeout, state: 'hidden' })
|
|
2713
|
-
.catch(
|
|
2714
|
-
throw new Error(
|
|
2715
|
-
`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2716
|
-
)
|
|
2666
|
+
.catch(err => {
|
|
2667
|
+
throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2717
2668
|
})
|
|
2718
2669
|
}
|
|
2719
2670
|
|
|
@@ -2739,6 +2690,9 @@ class Playwright extends Helper {
|
|
|
2739
2690
|
if ((this.context && this.context.constructor.name === 'FrameLocator') || this.context) {
|
|
2740
2691
|
return this.context
|
|
2741
2692
|
}
|
|
2693
|
+
if (this.frame) {
|
|
2694
|
+
return this.frame
|
|
2695
|
+
}
|
|
2742
2696
|
return this.page
|
|
2743
2697
|
}
|
|
2744
2698
|
|
|
@@ -2750,14 +2704,14 @@ class Playwright extends Helper {
|
|
|
2750
2704
|
|
|
2751
2705
|
return this.page
|
|
2752
2706
|
.waitForFunction(
|
|
2753
|
-
|
|
2707
|
+
urlPart => {
|
|
2754
2708
|
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
|
|
2755
2709
|
return currUrl.indexOf(urlPart) > -1
|
|
2756
2710
|
},
|
|
2757
2711
|
urlPart,
|
|
2758
2712
|
{ timeout: waitTimeout },
|
|
2759
2713
|
)
|
|
2760
|
-
.catch(async
|
|
2714
|
+
.catch(async e => {
|
|
2761
2715
|
const currUrl = await this._getPageUrl() // Required because the waitForFunction can't return data.
|
|
2762
2716
|
if (/Timeout/i.test(e.message)) {
|
|
2763
2717
|
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
|
|
@@ -2780,14 +2734,14 @@ class Playwright extends Helper {
|
|
|
2780
2734
|
|
|
2781
2735
|
return this.page
|
|
2782
2736
|
.waitForFunction(
|
|
2783
|
-
|
|
2737
|
+
urlPart => {
|
|
2784
2738
|
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
|
|
2785
2739
|
return currUrl.indexOf(urlPart) > -1
|
|
2786
2740
|
},
|
|
2787
2741
|
urlPart,
|
|
2788
2742
|
{ timeout: waitTimeout },
|
|
2789
2743
|
)
|
|
2790
|
-
.catch(async
|
|
2744
|
+
.catch(async e => {
|
|
2791
2745
|
const currUrl = await this._getPageUrl() // Required because the waitForFunction can't return data.
|
|
2792
2746
|
if (/Timeout/i.test(e.message)) {
|
|
2793
2747
|
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
|
|
@@ -2803,28 +2757,23 @@ class Playwright extends Helper {
|
|
|
2803
2757
|
async waitForText(text, sec = null, context = null) {
|
|
2804
2758
|
const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeout
|
|
2805
2759
|
const errorMessage = `Text "${text}" was not found on page after ${waitTimeout / 1000} sec.`
|
|
2806
|
-
let waiter
|
|
2807
2760
|
|
|
2808
2761
|
const contextObject = await this._getContext()
|
|
2809
2762
|
|
|
2810
2763
|
if (context) {
|
|
2811
2764
|
const locator = new Locator(context, 'css')
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2765
|
+
try {
|
|
2766
|
+
if (!locator.isXPath()) {
|
|
2767
|
+
return contextObject
|
|
2815
2768
|
.locator(`${locator.isCustom() ? `${locator.type}=${locator.value}` : locator.simplify()} >> text=${text}`)
|
|
2816
2769
|
.first()
|
|
2817
2770
|
.waitFor({ timeout: waitTimeout, state: 'visible' })
|
|
2818
|
-
} catch (e) {
|
|
2819
|
-
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2820
2771
|
}
|
|
2821
|
-
}
|
|
2822
2772
|
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
await contextObject.waitForFunction(
|
|
2773
|
+
if (locator.isXPath()) {
|
|
2774
|
+
return contextObject.waitForFunction(
|
|
2826
2775
|
([locator, text, $XPath]) => {
|
|
2827
|
-
eval($XPath)
|
|
2776
|
+
eval($XPath)
|
|
2828
2777
|
const el = $XPath(null, locator)
|
|
2829
2778
|
if (!el.length) return false
|
|
2830
2779
|
return el[0].innerText.indexOf(text) > -1
|
|
@@ -2832,27 +2781,34 @@ class Playwright extends Helper {
|
|
|
2832
2781
|
[locator.value, text, $XPath.toString()],
|
|
2833
2782
|
{ timeout: waitTimeout },
|
|
2834
2783
|
)
|
|
2835
|
-
} catch (e) {
|
|
2836
|
-
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2837
2784
|
}
|
|
2785
|
+
} catch (e) {
|
|
2786
|
+
throw new Error(`${errorMessage}\n${e.message}`)
|
|
2838
2787
|
}
|
|
2839
|
-
} else {
|
|
2840
|
-
// we have this as https://github.com/microsoft/playwright/issues/26829 is not yet implemented
|
|
2841
|
-
|
|
2842
|
-
const _contextObject = this.frame ? this.frame : contextObject
|
|
2843
|
-
let count = 0
|
|
2844
|
-
do {
|
|
2845
|
-
waiter = await _contextObject
|
|
2846
|
-
.locator(`:has-text(${JSON.stringify(text)})`)
|
|
2847
|
-
.first()
|
|
2848
|
-
.isVisible()
|
|
2849
|
-
if (waiter) break
|
|
2850
|
-
await this.wait(1)
|
|
2851
|
-
count += 1000
|
|
2852
|
-
} while (count <= waitTimeout)
|
|
2853
|
-
|
|
2854
|
-
if (!waiter) throw new Error(`${errorMessage}`)
|
|
2855
2788
|
}
|
|
2789
|
+
|
|
2790
|
+
const timeoutGap = waitTimeout + 1000
|
|
2791
|
+
|
|
2792
|
+
// We add basic timeout to make sure we don't wait forever
|
|
2793
|
+
// We apply 2 strategies here: wait for text as innert text on page (wide strategy) - older
|
|
2794
|
+
// or we use native Playwright matcher to wait for text in element (narrow strategy) - newer
|
|
2795
|
+
// If a user waits for text on a page they are mostly expect it to be there, so wide strategy can be helpful even PW strategy is available
|
|
2796
|
+
return Promise.race([
|
|
2797
|
+
new Promise((_, reject) => {
|
|
2798
|
+
setTimeout(() => reject(errorMessage), waitTimeout)
|
|
2799
|
+
}),
|
|
2800
|
+
this.page.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, text, { timeout: timeoutGap }),
|
|
2801
|
+
promiseRetry(
|
|
2802
|
+
async retry => {
|
|
2803
|
+
const textPresent = await contextObject
|
|
2804
|
+
.locator(`:has-text(${JSON.stringify(text)})`)
|
|
2805
|
+
.first()
|
|
2806
|
+
.isVisible()
|
|
2807
|
+
if (!textPresent) retry(errorMessage)
|
|
2808
|
+
},
|
|
2809
|
+
{ retries: 1000, minTimeout: 500, maxTimeout: 500, factor: 1 },
|
|
2810
|
+
),
|
|
2811
|
+
])
|
|
2856
2812
|
}
|
|
2857
2813
|
|
|
2858
2814
|
/**
|
|
@@ -3021,11 +2977,11 @@ class Playwright extends Helper {
|
|
|
3021
2977
|
}
|
|
3022
2978
|
} else {
|
|
3023
2979
|
const visibleFn = function ([locator, $XPath]) {
|
|
3024
|
-
eval($XPath)
|
|
2980
|
+
eval($XPath)
|
|
3025
2981
|
return $XPath(null, locator).length === 0
|
|
3026
2982
|
}
|
|
3027
2983
|
waiter = context.waitForFunction(visibleFn, [locator.value, $XPath.toString()], { timeout: waitTimeout })
|
|
3028
|
-
return waiter.catch(
|
|
2984
|
+
return waiter.catch(err => {
|
|
3029
2985
|
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
3030
2986
|
})
|
|
3031
2987
|
}
|
|
@@ -3047,9 +3003,9 @@ class Playwright extends Helper {
|
|
|
3047
3003
|
|
|
3048
3004
|
return promiseRetry(
|
|
3049
3005
|
async (retry, number) => {
|
|
3050
|
-
const _grabCookie = async
|
|
3006
|
+
const _grabCookie = async name => {
|
|
3051
3007
|
const cookies = await this.browserContext.cookies()
|
|
3052
|
-
const cookie = cookies.filter(
|
|
3008
|
+
const cookie = cookies.filter(c => c.name === name)
|
|
3053
3009
|
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`)
|
|
3054
3010
|
}
|
|
3055
3011
|
|
|
@@ -3128,7 +3084,7 @@ class Playwright extends Helper {
|
|
|
3128
3084
|
this.recording = true
|
|
3129
3085
|
this.recordedAtLeastOnce = true
|
|
3130
3086
|
|
|
3131
|
-
this.page.on('requestfinished', async
|
|
3087
|
+
this.page.on('requestfinished', async request => {
|
|
3132
3088
|
const information = {
|
|
3133
3089
|
url: request.url(),
|
|
3134
3090
|
method: request.method(),
|
|
@@ -3167,20 +3123,20 @@ class Playwright extends Helper {
|
|
|
3167
3123
|
*/
|
|
3168
3124
|
blockTraffic(urls) {
|
|
3169
3125
|
if (Array.isArray(urls)) {
|
|
3170
|
-
urls.forEach(
|
|
3171
|
-
this.page.route(url,
|
|
3126
|
+
urls.forEach(url => {
|
|
3127
|
+
this.page.route(url, route => {
|
|
3172
3128
|
route
|
|
3173
3129
|
.abort()
|
|
3174
3130
|
// Sometimes it happens that browser has been closed in the meantime. It is ok to ignore error then.
|
|
3175
|
-
.catch(
|
|
3131
|
+
.catch(e => {})
|
|
3176
3132
|
})
|
|
3177
3133
|
})
|
|
3178
3134
|
} else {
|
|
3179
|
-
this.page.route(urls,
|
|
3135
|
+
this.page.route(urls, route => {
|
|
3180
3136
|
route
|
|
3181
3137
|
.abort()
|
|
3182
3138
|
// Sometimes it happens that browser has been closed in the meantime. It is ok to ignore error then.
|
|
3183
|
-
.catch(
|
|
3139
|
+
.catch(e => {})
|
|
3184
3140
|
})
|
|
3185
3141
|
}
|
|
3186
3142
|
}
|
|
@@ -3209,8 +3165,8 @@ class Playwright extends Helper {
|
|
|
3209
3165
|
urls = [urls]
|
|
3210
3166
|
}
|
|
3211
3167
|
|
|
3212
|
-
urls.forEach(
|
|
3213
|
-
this.page.route(url,
|
|
3168
|
+
urls.forEach(url => {
|
|
3169
|
+
this.page.route(url, route => {
|
|
3214
3170
|
if (this.page.isClosed()) {
|
|
3215
3171
|
// Sometimes it happens that browser has been closed in the meantime.
|
|
3216
3172
|
// In this case we just don't fulfill to prevent error in test scenario.
|
|
@@ -3256,13 +3212,10 @@ class Playwright extends Helper {
|
|
|
3256
3212
|
*/
|
|
3257
3213
|
grabTrafficUrl(urlMatch) {
|
|
3258
3214
|
if (!this.recordedAtLeastOnce) {
|
|
3259
|
-
throw new Error(
|
|
3260
|
-
'Failure in test automation. You use "I.grabTrafficUrl", but "I.startRecordingTraffic" was never called before.',
|
|
3261
|
-
)
|
|
3215
|
+
throw new Error('Failure in test automation. You use "I.grabTrafficUrl", but "I.startRecordingTraffic" was never called before.')
|
|
3262
3216
|
}
|
|
3263
3217
|
|
|
3264
3218
|
for (const i in this.requests) {
|
|
3265
|
-
// eslint-disable-next-line no-prototype-builtins
|
|
3266
3219
|
if (this.requests.hasOwnProperty(i)) {
|
|
3267
3220
|
const request = this.requests[i]
|
|
3268
3221
|
|
|
@@ -3312,15 +3265,15 @@ class Playwright extends Helper {
|
|
|
3312
3265
|
await this.cdpSession.send('Network.enable')
|
|
3313
3266
|
await this.cdpSession.send('Page.enable')
|
|
3314
3267
|
|
|
3315
|
-
this.cdpSession.on('Network.webSocketFrameReceived',
|
|
3268
|
+
this.cdpSession.on('Network.webSocketFrameReceived', payload => {
|
|
3316
3269
|
this._logWebsocketMessages(this._getWebSocketLog('RECEIVED', payload))
|
|
3317
3270
|
})
|
|
3318
3271
|
|
|
3319
|
-
this.cdpSession.on('Network.webSocketFrameSent',
|
|
3272
|
+
this.cdpSession.on('Network.webSocketFrameSent', payload => {
|
|
3320
3273
|
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload))
|
|
3321
3274
|
})
|
|
3322
3275
|
|
|
3323
|
-
this.cdpSession.on('Network.webSocketFrameError',
|
|
3276
|
+
this.cdpSession.on('Network.webSocketFrameError', payload => {
|
|
3324
3277
|
this._logWebsocketMessages(this._getWebSocketLog('ERROR', payload))
|
|
3325
3278
|
})
|
|
3326
3279
|
}
|
|
@@ -3344,9 +3297,7 @@ class Playwright extends Helper {
|
|
|
3344
3297
|
grabWebSocketMessages() {
|
|
3345
3298
|
if (!this.recordingWebSocketMessages) {
|
|
3346
3299
|
if (!this.recordedWebSocketMessagesAtLeastOnce) {
|
|
3347
|
-
throw new Error(
|
|
3348
|
-
'Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.',
|
|
3349
|
-
)
|
|
3300
|
+
throw new Error('Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.')
|
|
3350
3301
|
}
|
|
3351
3302
|
}
|
|
3352
3303
|
return this.webSocketMessages
|
|
@@ -3493,17 +3444,13 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
3493
3444
|
}
|
|
3494
3445
|
const els = await findClickable.call(this, matcher, locator)
|
|
3495
3446
|
if (context) {
|
|
3496
|
-
assertElementExists(
|
|
3497
|
-
els,
|
|
3498
|
-
locator,
|
|
3499
|
-
'Clickable element',
|
|
3500
|
-
`was not found inside element ${new Locator(context).toString()}`,
|
|
3501
|
-
)
|
|
3447
|
+
assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`)
|
|
3502
3448
|
} else {
|
|
3503
3449
|
assertElementExists(els, locator, 'Clickable element')
|
|
3504
3450
|
}
|
|
3505
3451
|
|
|
3506
3452
|
await highlightActiveElement.call(this, els[0])
|
|
3453
|
+
if (store.debugMode) this.debugSection('Clicked', await elToString(els[0], 1))
|
|
3507
3454
|
|
|
3508
3455
|
/*
|
|
3509
3456
|
using the force true options itself but instead dispatching a click
|
|
@@ -3565,16 +3512,18 @@ async function proceedSee(assertType, text, context, strict = false) {
|
|
|
3565
3512
|
description = `element ${locator.toString()}`
|
|
3566
3513
|
const els = await this._locate(locator)
|
|
3567
3514
|
assertElementExists(els, locator.toString())
|
|
3568
|
-
allText = await Promise.all(els.map(
|
|
3515
|
+
allText = await Promise.all(els.map(el => el.innerText()))
|
|
3516
|
+
}
|
|
3517
|
+
|
|
3518
|
+
if (store?.currentStep?.opts?.ignoreCase === true) {
|
|
3519
|
+
text = text.toLowerCase()
|
|
3520
|
+
allText = allText.map(elText => elText.toLowerCase())
|
|
3569
3521
|
}
|
|
3570
3522
|
|
|
3571
3523
|
if (strict) {
|
|
3572
|
-
return allText.map(
|
|
3524
|
+
return allText.map(elText => equals(description)[assertType](text, elText))
|
|
3573
3525
|
}
|
|
3574
|
-
return stringIncludes(description)[assertType](
|
|
3575
|
-
normalizeSpacesInString(text),
|
|
3576
|
-
normalizeSpacesInString(allText.join(' | ')),
|
|
3577
|
-
)
|
|
3526
|
+
return stringIncludes(description)[assertType](normalizeSpacesInString(text), normalizeSpacesInString(allText.join(' | ')))
|
|
3578
3527
|
}
|
|
3579
3528
|
|
|
3580
3529
|
async function findCheckable(locator, context) {
|
|
@@ -3604,7 +3553,7 @@ async function findCheckable(locator, context) {
|
|
|
3604
3553
|
async function proceedIsChecked(assertType, option) {
|
|
3605
3554
|
let els = await findCheckable.call(this, option)
|
|
3606
3555
|
assertElementExists(els, option, 'Checkable')
|
|
3607
|
-
els = await Promise.all(els.map(
|
|
3556
|
+
els = await Promise.all(els.map(el => el.isChecked()))
|
|
3608
3557
|
const selected = els.reduce((prev, cur) => prev || cur)
|
|
3609
3558
|
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
3610
3559
|
}
|
|
@@ -3636,10 +3585,10 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
3636
3585
|
const els = await findFields.call(this, field)
|
|
3637
3586
|
assertElementExists(els, field, 'Field')
|
|
3638
3587
|
const el = els[0]
|
|
3639
|
-
const tag = await el.evaluate(
|
|
3588
|
+
const tag = await el.evaluate(e => e.tagName)
|
|
3640
3589
|
const fieldType = await el.getAttribute('type')
|
|
3641
3590
|
|
|
3642
|
-
const proceedMultiple = async
|
|
3591
|
+
const proceedMultiple = async elements => {
|
|
3643
3592
|
const fields = Array.isArray(elements) ? elements : [elements]
|
|
3644
3593
|
|
|
3645
3594
|
const elementValues = []
|
|
@@ -3653,7 +3602,7 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
3653
3602
|
if (assertType === 'assert') {
|
|
3654
3603
|
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0)
|
|
3655
3604
|
}
|
|
3656
|
-
elementValues.forEach(
|
|
3605
|
+
elementValues.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val))
|
|
3657
3606
|
}
|
|
3658
3607
|
}
|
|
3659
3608
|
|
|
@@ -3740,6 +3689,8 @@ function isFrameLocator(locator) {
|
|
|
3740
3689
|
}
|
|
3741
3690
|
|
|
3742
3691
|
function assertElementExists(res, locator, prefix, suffix) {
|
|
3692
|
+
// if element text is an empty string, just exit this check
|
|
3693
|
+
if (typeof res === 'string' && res === '') return
|
|
3743
3694
|
if (!res || res.length === 0) {
|
|
3744
3695
|
throw new ElementNotFound(locator, prefix, suffix)
|
|
3745
3696
|
}
|
|
@@ -3776,12 +3727,9 @@ async function targetCreatedHandler(page) {
|
|
|
3776
3727
|
this.contextLocator = null
|
|
3777
3728
|
})
|
|
3778
3729
|
})
|
|
3779
|
-
page.on('console',
|
|
3730
|
+
page.on('console', msg => {
|
|
3780
3731
|
if (!consoleLogStore.includes(msg) && this.options.ignoreLog && !this.options.ignoreLog.includes(msg.type())) {
|
|
3781
|
-
this.debugSection(
|
|
3782
|
-
`Browser:${ucfirst(msg.type())}`,
|
|
3783
|
-
((msg.text && msg.text()) || msg._text || '') + msg.args().join(' '),
|
|
3784
|
-
)
|
|
3732
|
+
this.debugSection(`Browser:${ucfirst(msg.type())}`, ((msg.text && msg.text()) || msg._text || '') + msg.args().join(' '))
|
|
3785
3733
|
}
|
|
3786
3734
|
consoleLogStore.add(msg)
|
|
3787
3735
|
})
|
|
@@ -3889,7 +3837,7 @@ async function refreshContextSession() {
|
|
|
3889
3837
|
const contexts = await this.browser.contexts()
|
|
3890
3838
|
contexts.shift()
|
|
3891
3839
|
|
|
3892
|
-
await Promise.all(contexts.map(
|
|
3840
|
+
await Promise.all(contexts.map(c => c.close()))
|
|
3893
3841
|
} catch (e) {
|
|
3894
3842
|
console.log(e)
|
|
3895
3843
|
}
|
|
@@ -3908,10 +3856,10 @@ async function refreshContextSession() {
|
|
|
3908
3856
|
const currentUrl = await this.grabCurrentUrl()
|
|
3909
3857
|
|
|
3910
3858
|
if (currentUrl.startsWith('http')) {
|
|
3911
|
-
await this.executeScript('localStorage.clear();').catch(
|
|
3859
|
+
await this.executeScript('localStorage.clear();').catch(err => {
|
|
3912
3860
|
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
3913
3861
|
})
|
|
3914
|
-
await this.executeScript('sessionStorage.clear();').catch(
|
|
3862
|
+
await this.executeScript('sessionStorage.clear();').catch(err => {
|
|
3915
3863
|
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
3916
3864
|
})
|
|
3917
3865
|
}
|
|
@@ -3941,11 +3889,22 @@ async function saveTraceForContext(context, name) {
|
|
|
3941
3889
|
}
|
|
3942
3890
|
|
|
3943
3891
|
async function highlightActiveElement(element) {
|
|
3944
|
-
if (this.options.highlightElement &&
|
|
3945
|
-
await element.evaluate(
|
|
3892
|
+
if ((this.options.highlightElement || store.onPause) && store.debugMode) {
|
|
3893
|
+
await element.evaluate(el => {
|
|
3946
3894
|
const prevStyle = el.style.boxShadow
|
|
3947
|
-
el.style.boxShadow = '0px 0px 4px 3px rgba(
|
|
3895
|
+
el.style.boxShadow = '0px 0px 4px 3px rgba(147, 51, 234, 0.8)' // Bright purple that works on both dark/light modes
|
|
3948
3896
|
setTimeout(() => (el.style.boxShadow = prevStyle), 2000)
|
|
3949
3897
|
})
|
|
3950
3898
|
}
|
|
3951
3899
|
}
|
|
3900
|
+
|
|
3901
|
+
async function elToString(el, numberOfElements) {
|
|
3902
|
+
const html = await el.evaluate(node => node.outerHTML)
|
|
3903
|
+
return (
|
|
3904
|
+
html
|
|
3905
|
+
.replace(/\n/g, '')
|
|
3906
|
+
.replace(/\s+/g, ' ')
|
|
3907
|
+
.substring(0, 100 / numberOfElements)
|
|
3908
|
+
.trim() + '...'
|
|
3909
|
+
)
|
|
3910
|
+
}
|