codeceptjs 4.0.0-beta.4 → 4.0.0-beta.5
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 +134 -119
- package/bin/codecept.js +12 -2
- package/bin/test-server.js +53 -0
- 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 +139 -87
- package/lib/command/check.js +201 -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 +75 -73
- package/lib/command/gherkin/steps.js +1 -1
- package/lib/command/info.js +42 -8
- package/lib/command/init.js +13 -12
- package/lib/command/interactive.js +10 -2
- package/lib/command/list.js +1 -1
- package/lib/command/run-multiple/chunk.js +48 -45
- package/lib/command/run-multiple.js +12 -35
- package/lib/command/run-workers.js +21 -58
- package/lib/command/utils.js +5 -6
- package/lib/command/workers/runTests.js +262 -220
- package/lib/container.js +386 -238
- 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 +223 -0
- package/lib/element/WebElement.js +327 -0
- package/lib/els.js +158 -0
- package/lib/event.js +21 -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 +47 -51
- package/lib/helper/FileSystem.js +3 -3
- package/lib/helper/GraphQLDataFactory.js +3 -3
- package/lib/helper/JSONResponse.js +75 -37
- package/lib/helper/Mochawesome.js +31 -9
- package/lib/helper/Nightmare.js +35 -53
- package/lib/helper/Playwright.js +262 -267
- package/lib/helper/Protractor.js +54 -77
- package/lib/helper/Puppeteer.js +246 -260
- package/lib/helper/REST.js +5 -17
- package/lib/helper/TestCafe.js +21 -44
- package/lib/helper/WebDriver.js +151 -170
- package/lib/helper/extras/Popup.js +22 -22
- 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/retryEnhancer.js +85 -0
- package/lib/listener/steps.js +32 -18
- package/lib/listener/store.js +20 -0
- package/lib/mocha/asyncWrapper.js +231 -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 +32 -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 +31 -7
- package/lib/mocha/suite.js +82 -0
- package/lib/mocha/test.js +181 -0
- package/lib/mocha/types.d.ts +42 -0
- package/lib/mocha/ui.js +232 -0
- package/lib/output.js +82 -62
- package/lib/pause.js +160 -138
- package/lib/plugin/analyze.js +396 -0
- package/lib/plugin/auth.js +435 -0
- package/lib/plugin/autoDelay.js +8 -8
- package/lib/plugin/autoLogin.js +3 -338
- 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/htmlReporter.js +1947 -0
- package/lib/plugin/pageInfo.js +140 -0
- package/lib/plugin/retryFailedStep.js +17 -18
- package/lib/plugin/retryTo.js +2 -113
- package/lib/plugin/screenshotOnFail.js +17 -58
- 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 +3 -102
- package/lib/plugin/wdio.js +8 -10
- package/lib/recorder.js +155 -124
- package/lib/rerun.js +43 -42
- package/lib/result.js +161 -0
- package/lib/secret.js +1 -1
- package/lib/step/base.js +239 -0
- package/lib/step/comment.js +10 -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 +37 -5
- package/lib/template/heal.js +2 -11
- package/lib/test-server.js +323 -0
- package/lib/timeout.js +66 -0
- package/lib/utils.js +351 -218
- package/lib/within.js +75 -55
- package/lib/workerStorage.js +2 -1
- package/lib/workers.js +386 -276
- package/package.json +76 -70
- package/translations/de-DE.js +4 -3
- package/translations/fr-FR.js +4 -3
- package/translations/index.js +1 -0
- package/translations/it-IT.js +4 -3
- package/translations/ja-JP.js +4 -3
- package/translations/nl-NL.js +76 -0
- package/translations/pl-PL.js +4 -3
- package/translations/pt-BR.js +4 -3
- package/translations/ru-RU.js +4 -3
- package/translations/utils.js +9 -0
- package/translations/zh-CN.js +4 -3
- package/translations/zh-TW.js +4 -3
- package/typings/index.d.ts +188 -186
- package/typings/promiseBasedTypes.d.ts +18 -705
- package/typings/types.d.ts +301 -804
- 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/Puppeteer.js
CHANGED
|
@@ -36,19 +36,9 @@ const Popup = require('./extras/Popup')
|
|
|
36
36
|
const Console = require('./extras/Console')
|
|
37
37
|
const { highlightElement } = require('./scripts/highlightElement')
|
|
38
38
|
const { blurElement } = require('./scripts/blurElement')
|
|
39
|
-
const {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
dontSeeElementInDOMError,
|
|
43
|
-
seeElementInDOMError,
|
|
44
|
-
} = require('./errors/ElementAssertion')
|
|
45
|
-
const {
|
|
46
|
-
dontSeeTraffic,
|
|
47
|
-
seeTraffic,
|
|
48
|
-
grabRecordedNetworkTraffics,
|
|
49
|
-
stopRecordingTraffic,
|
|
50
|
-
flushNetworkTraffics,
|
|
51
|
-
} = require('./network/actions')
|
|
39
|
+
const { dontSeeElementError, seeElementError, dontSeeElementInDOMError, seeElementInDOMError } = require('./errors/ElementAssertion')
|
|
40
|
+
const { dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics } = require('./network/actions')
|
|
41
|
+
const WebElement = require('../element/WebElement')
|
|
52
42
|
|
|
53
43
|
let puppeteer
|
|
54
44
|
let perfTiming
|
|
@@ -271,9 +261,7 @@ class Puppeteer extends Helper {
|
|
|
271
261
|
}
|
|
272
262
|
|
|
273
263
|
_getOptions(config) {
|
|
274
|
-
return config.browser === 'firefox'
|
|
275
|
-
? Object.assign(this.options.firefox, { product: 'firefox' })
|
|
276
|
-
: this.options.chrome
|
|
264
|
+
return config.browser === 'firefox' ? Object.assign(this.options.firefox, { product: 'firefox' }) : this.options.chrome
|
|
277
265
|
}
|
|
278
266
|
|
|
279
267
|
_setConfig(config) {
|
|
@@ -325,8 +313,8 @@ class Puppeteer extends Helper {
|
|
|
325
313
|
this.sessionPages = {}
|
|
326
314
|
this.currentRunningTest = test
|
|
327
315
|
recorder.retry({
|
|
328
|
-
retries:
|
|
329
|
-
when:
|
|
316
|
+
retries: test?.opts?.conditionalRetries || 3,
|
|
317
|
+
when: err => {
|
|
330
318
|
if (!err || typeof err.message !== 'string') {
|
|
331
319
|
return false
|
|
332
320
|
}
|
|
@@ -346,7 +334,7 @@ class Puppeteer extends Helper {
|
|
|
346
334
|
const contexts = this.browser.browserContexts()
|
|
347
335
|
const defaultCtx = contexts.shift()
|
|
348
336
|
|
|
349
|
-
await Promise.all(contexts.map(
|
|
337
|
+
await Promise.all(contexts.map(c => c.close()))
|
|
350
338
|
|
|
351
339
|
if (this.options.restart) {
|
|
352
340
|
this.isRunning = false
|
|
@@ -355,7 +343,7 @@ class Puppeteer extends Helper {
|
|
|
355
343
|
|
|
356
344
|
// ensure this.page is from default context
|
|
357
345
|
if (this.page) {
|
|
358
|
-
const existingPages = defaultCtx.targets().filter(
|
|
346
|
+
const existingPages = defaultCtx.targets().filter(t => t.type() === 'page')
|
|
359
347
|
await this._setPage(await existingPages[0].page())
|
|
360
348
|
}
|
|
361
349
|
|
|
@@ -368,10 +356,10 @@ class Puppeteer extends Helper {
|
|
|
368
356
|
const currentUrl = await this.grabCurrentUrl()
|
|
369
357
|
|
|
370
358
|
if (currentUrl.startsWith('http')) {
|
|
371
|
-
await this.executeScript('localStorage.clear();').catch(
|
|
359
|
+
await this.executeScript('localStorage.clear();').catch(err => {
|
|
372
360
|
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
373
361
|
})
|
|
374
|
-
await this.executeScript('sessionStorage.clear();').catch(
|
|
362
|
+
await this.executeScript('sessionStorage.clear();').catch(err => {
|
|
375
363
|
if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
|
|
376
364
|
})
|
|
377
365
|
}
|
|
@@ -400,12 +388,12 @@ class Puppeteer extends Helper {
|
|
|
400
388
|
stop: async () => {
|
|
401
389
|
// is closed by _after
|
|
402
390
|
},
|
|
403
|
-
loadVars: async
|
|
404
|
-
const existingPages = context.targets().filter(
|
|
391
|
+
loadVars: async context => {
|
|
392
|
+
const existingPages = context.targets().filter(t => t.type() === 'page')
|
|
405
393
|
this.sessionPages[this.activeSessionName] = await existingPages[0].page()
|
|
406
394
|
return this._setPage(this.sessionPages[this.activeSessionName])
|
|
407
395
|
},
|
|
408
|
-
restoreVars: async
|
|
396
|
+
restoreVars: async session => {
|
|
409
397
|
this.withinLocator = null
|
|
410
398
|
|
|
411
399
|
if (!session) {
|
|
@@ -414,7 +402,7 @@ class Puppeteer extends Helper {
|
|
|
414
402
|
this.activeSessionName = session
|
|
415
403
|
}
|
|
416
404
|
const defaultCtx = this.browser.defaultBrowserContext()
|
|
417
|
-
const existingPages = defaultCtx.targets().filter(
|
|
405
|
+
const existingPages = defaultCtx.targets().filter(t => t.type() === 'page')
|
|
418
406
|
await this._setPage(await existingPages[0].page())
|
|
419
407
|
|
|
420
408
|
return this._waitForAction()
|
|
@@ -517,7 +505,7 @@ class Puppeteer extends Helper {
|
|
|
517
505
|
if (!page) {
|
|
518
506
|
return
|
|
519
507
|
}
|
|
520
|
-
page.on('error', async
|
|
508
|
+
page.on('error', async error => {
|
|
521
509
|
console.error('Puppeteer page error', error)
|
|
522
510
|
})
|
|
523
511
|
}
|
|
@@ -533,7 +521,7 @@ class Puppeteer extends Helper {
|
|
|
533
521
|
if (!page) {
|
|
534
522
|
return
|
|
535
523
|
}
|
|
536
|
-
page.on('dialog', async
|
|
524
|
+
page.on('dialog', async dialog => {
|
|
537
525
|
popupStore.popup = dialog
|
|
538
526
|
const action = popupStore.actionType || this.options.defaultPopupAction
|
|
539
527
|
await this._waitForAction()
|
|
@@ -588,15 +576,15 @@ class Puppeteer extends Helper {
|
|
|
588
576
|
this.browser = await puppeteer.launch(this.puppeteerOptions)
|
|
589
577
|
}
|
|
590
578
|
|
|
591
|
-
this.browser.on('targetcreated',
|
|
579
|
+
this.browser.on('targetcreated', target =>
|
|
592
580
|
target
|
|
593
581
|
.page()
|
|
594
|
-
.then(
|
|
595
|
-
.catch(
|
|
582
|
+
.then(page => targetCreatedHandler.call(this, page))
|
|
583
|
+
.catch(e => {
|
|
596
584
|
console.error('Puppeteer page error', e)
|
|
597
585
|
}),
|
|
598
586
|
)
|
|
599
|
-
this.browser.on('targetchanged',
|
|
587
|
+
this.browser.on('targetchanged', target => {
|
|
600
588
|
this.debugSection('Url', target.url())
|
|
601
589
|
})
|
|
602
590
|
|
|
@@ -639,18 +627,18 @@ class Puppeteer extends Helper {
|
|
|
639
627
|
|
|
640
628
|
if (frame) {
|
|
641
629
|
if (Array.isArray(frame)) {
|
|
642
|
-
return this.switchTo(null).then(() =>
|
|
643
|
-
frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve()),
|
|
644
|
-
)
|
|
630
|
+
return this.switchTo(null).then(() => frame.reduce((p, frameLocator) => p.then(() => this.switchTo(frameLocator)), Promise.resolve()))
|
|
645
631
|
}
|
|
646
632
|
await this.switchTo(frame)
|
|
647
633
|
this.withinLocator = new Locator(frame)
|
|
648
634
|
return
|
|
649
635
|
}
|
|
650
636
|
|
|
651
|
-
const
|
|
652
|
-
|
|
653
|
-
|
|
637
|
+
const el = await this._locateElement(locator)
|
|
638
|
+
if (!el) {
|
|
639
|
+
throw new ElementNotFound(locator, 'Element for within context')
|
|
640
|
+
}
|
|
641
|
+
this.context = el
|
|
654
642
|
|
|
655
643
|
this.withinLocator = new Locator(locator)
|
|
656
644
|
}
|
|
@@ -664,7 +652,7 @@ class Puppeteer extends Helper {
|
|
|
664
652
|
const navigationStart = timing.navigationStart
|
|
665
653
|
|
|
666
654
|
const extractedData = {}
|
|
667
|
-
dataNames.forEach(
|
|
655
|
+
dataNames.forEach(name => {
|
|
668
656
|
extractedData[name] = timing[name] - navigationStart
|
|
669
657
|
})
|
|
670
658
|
|
|
@@ -698,13 +686,7 @@ class Puppeteer extends Helper {
|
|
|
698
686
|
|
|
699
687
|
const performanceTiming = JSON.parse(await this.page.evaluate(() => JSON.stringify(window.performance.timing)))
|
|
700
688
|
|
|
701
|
-
perfTiming = this._extractDataFromPerformanceTiming(
|
|
702
|
-
performanceTiming,
|
|
703
|
-
'responseEnd',
|
|
704
|
-
'domInteractive',
|
|
705
|
-
'domContentLoadedEventEnd',
|
|
706
|
-
'loadEventEnd',
|
|
707
|
-
)
|
|
689
|
+
perfTiming = this._extractDataFromPerformanceTiming(performanceTiming, 'responseEnd', 'domInteractive', 'domContentLoadedEventEnd', 'loadEventEnd')
|
|
708
690
|
|
|
709
691
|
return this._waitForAction()
|
|
710
692
|
}
|
|
@@ -750,11 +732,13 @@ class Puppeteer extends Helper {
|
|
|
750
732
|
* {{ react }}
|
|
751
733
|
*/
|
|
752
734
|
async moveCursorTo(locator, offsetX = 0, offsetY = 0) {
|
|
753
|
-
const
|
|
754
|
-
|
|
735
|
+
const el = await this._locateElement(locator)
|
|
736
|
+
if (!el) {
|
|
737
|
+
throw new ElementNotFound(locator, 'Element to move cursor to')
|
|
738
|
+
}
|
|
755
739
|
|
|
756
740
|
// Use manual mouse.move instead of .hover() so the offset can be added to the coordinates
|
|
757
|
-
const { x, y } = await getClickablePoint(
|
|
741
|
+
const { x, y } = await getClickablePoint(el)
|
|
758
742
|
await this.page.mouse.move(x + offsetX, y + offsetY)
|
|
759
743
|
return this._waitForAction()
|
|
760
744
|
}
|
|
@@ -764,9 +748,10 @@ class Puppeteer extends Helper {
|
|
|
764
748
|
*
|
|
765
749
|
*/
|
|
766
750
|
async focus(locator) {
|
|
767
|
-
const
|
|
768
|
-
|
|
769
|
-
|
|
751
|
+
const el = await this._locateElement(locator)
|
|
752
|
+
if (!el) {
|
|
753
|
+
throw new ElementNotFound(locator, 'Element to focus')
|
|
754
|
+
}
|
|
770
755
|
|
|
771
756
|
await el.click()
|
|
772
757
|
await el.focus()
|
|
@@ -778,10 +763,12 @@ class Puppeteer extends Helper {
|
|
|
778
763
|
*
|
|
779
764
|
*/
|
|
780
765
|
async blur(locator) {
|
|
781
|
-
const
|
|
782
|
-
|
|
766
|
+
const el = await this._locateElement(locator)
|
|
767
|
+
if (!el) {
|
|
768
|
+
throw new ElementNotFound(locator, 'Element to blur')
|
|
769
|
+
}
|
|
783
770
|
|
|
784
|
-
await blurElement(
|
|
771
|
+
await blurElement(el, this.page)
|
|
785
772
|
return this._waitForAction()
|
|
786
773
|
}
|
|
787
774
|
|
|
@@ -815,10 +802,7 @@ class Puppeteer extends Helper {
|
|
|
815
802
|
return this.executeScript(() => {
|
|
816
803
|
const body = document.body
|
|
817
804
|
const html = document.documentElement
|
|
818
|
-
window.scrollTo(
|
|
819
|
-
0,
|
|
820
|
-
Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
|
|
821
|
-
)
|
|
805
|
+
window.scrollTo(0, Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight))
|
|
822
806
|
})
|
|
823
807
|
}
|
|
824
808
|
|
|
@@ -833,16 +817,13 @@ class Puppeteer extends Helper {
|
|
|
833
817
|
}
|
|
834
818
|
|
|
835
819
|
if (locator) {
|
|
836
|
-
const
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
await
|
|
842
|
-
|
|
843
|
-
elementCoordinates.x + offsetX,
|
|
844
|
-
elementCoordinates.y + offsetY,
|
|
845
|
-
)
|
|
820
|
+
const el = await this._locateElement(locator)
|
|
821
|
+
if (!el) {
|
|
822
|
+
throw new ElementNotFound(locator, 'Element to scroll into view')
|
|
823
|
+
}
|
|
824
|
+
await el.evaluate(el => el.scrollIntoView())
|
|
825
|
+
const elementCoordinates = await getClickablePoint(el)
|
|
826
|
+
await this.executeScript((x, y) => window.scrollBy(x, y), elementCoordinates.x + offsetX, elementCoordinates.y + offsetY)
|
|
846
827
|
} else {
|
|
847
828
|
await this.executeScript((x, y) => window.scrollTo(x, y), offsetX, offsetY)
|
|
848
829
|
}
|
|
@@ -909,6 +890,21 @@ class Puppeteer extends Helper {
|
|
|
909
890
|
return findElements.call(this, context, locator)
|
|
910
891
|
}
|
|
911
892
|
|
|
893
|
+
/**
|
|
894
|
+
* Get single element by different locator types, including strict locator
|
|
895
|
+
* Should be used in custom helpers:
|
|
896
|
+
*
|
|
897
|
+
* ```js
|
|
898
|
+
* const element = await this.helpers['Puppeteer']._locateElement({name: 'password'});
|
|
899
|
+
* ```
|
|
900
|
+
*
|
|
901
|
+
* {{ react }}
|
|
902
|
+
*/
|
|
903
|
+
async _locateElement(locator) {
|
|
904
|
+
const context = await this.context
|
|
905
|
+
return findElement.call(this, context, locator)
|
|
906
|
+
}
|
|
907
|
+
|
|
912
908
|
/**
|
|
913
909
|
* Find a checkbox by providing human-readable text:
|
|
914
910
|
* NOTE: Assumes the checkable element exists
|
|
@@ -920,7 +916,9 @@ class Puppeteer extends Helper {
|
|
|
920
916
|
async _locateCheckable(locator, providedContext = null) {
|
|
921
917
|
const context = providedContext || (await this._getContext())
|
|
922
918
|
const els = await findCheckable.call(this, locator, context)
|
|
923
|
-
|
|
919
|
+
if (!els || els.length === 0) {
|
|
920
|
+
throw new ElementNotFound(locator, 'Checkbox or radio')
|
|
921
|
+
}
|
|
924
922
|
return els[0]
|
|
925
923
|
}
|
|
926
924
|
|
|
@@ -952,7 +950,20 @@ class Puppeteer extends Helper {
|
|
|
952
950
|
*
|
|
953
951
|
*/
|
|
954
952
|
async grabWebElements(locator) {
|
|
955
|
-
|
|
953
|
+
const elements = await this._locate(locator)
|
|
954
|
+
return elements.map(element => new WebElement(element, this))
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* {{> grabWebElement }}
|
|
959
|
+
*
|
|
960
|
+
*/
|
|
961
|
+
async grabWebElement(locator) {
|
|
962
|
+
const elements = await this._locate(locator)
|
|
963
|
+
if (elements.length === 0) {
|
|
964
|
+
throw new ElementNotFound(locator, 'Element')
|
|
965
|
+
}
|
|
966
|
+
return new WebElement(elements[0], this)
|
|
956
967
|
}
|
|
957
968
|
|
|
958
969
|
/**
|
|
@@ -1025,10 +1036,10 @@ class Puppeteer extends Helper {
|
|
|
1025
1036
|
*/
|
|
1026
1037
|
async closeOtherTabs() {
|
|
1027
1038
|
const pages = await this.browser.pages()
|
|
1028
|
-
const otherPages = pages.filter(
|
|
1039
|
+
const otherPages = pages.filter(page => page !== this.page)
|
|
1029
1040
|
|
|
1030
1041
|
let p = Promise.resolve()
|
|
1031
|
-
otherPages.forEach(
|
|
1042
|
+
otherPages.forEach(page => {
|
|
1032
1043
|
p = p.then(() => page.close())
|
|
1033
1044
|
})
|
|
1034
1045
|
await p
|
|
@@ -1061,19 +1072,11 @@ class Puppeteer extends Helper {
|
|
|
1061
1072
|
*/
|
|
1062
1073
|
async seeElement(locator) {
|
|
1063
1074
|
let els = await this._locate(locator)
|
|
1064
|
-
els = (await Promise.all(els.map(
|
|
1075
|
+
els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v)
|
|
1065
1076
|
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
|
|
1066
|
-
els = await Promise.all(
|
|
1067
|
-
els.map(
|
|
1068
|
-
async (el) =>
|
|
1069
|
-
(await el.evaluate(
|
|
1070
|
-
(node) =>
|
|
1071
|
-
window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none',
|
|
1072
|
-
)) && el,
|
|
1073
|
-
),
|
|
1074
|
-
)
|
|
1077
|
+
els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el))
|
|
1075
1078
|
try {
|
|
1076
|
-
return empty('visible elements').negate(els.filter(
|
|
1079
|
+
return empty('visible elements').negate(els.filter(v => v).fill('ELEMENT'))
|
|
1077
1080
|
} catch (e) {
|
|
1078
1081
|
dontSeeElementError(locator)
|
|
1079
1082
|
}
|
|
@@ -1085,19 +1088,11 @@ class Puppeteer extends Helper {
|
|
|
1085
1088
|
*/
|
|
1086
1089
|
async dontSeeElement(locator) {
|
|
1087
1090
|
let els = await this._locate(locator)
|
|
1088
|
-
els = (await Promise.all(els.map(
|
|
1091
|
+
els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v)
|
|
1089
1092
|
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
|
|
1090
|
-
els = await Promise.all(
|
|
1091
|
-
els.map(
|
|
1092
|
-
async (el) =>
|
|
1093
|
-
(await el.evaluate(
|
|
1094
|
-
(node) =>
|
|
1095
|
-
window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none',
|
|
1096
|
-
)) && el,
|
|
1097
|
-
),
|
|
1098
|
-
)
|
|
1093
|
+
els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el))
|
|
1099
1094
|
try {
|
|
1100
|
-
return empty('visible elements').assert(els.filter(
|
|
1095
|
+
return empty('visible elements').assert(els.filter(v => v).fill('ELEMENT'))
|
|
1101
1096
|
} catch (e) {
|
|
1102
1097
|
seeElementError(locator)
|
|
1103
1098
|
}
|
|
@@ -1109,7 +1104,7 @@ class Puppeteer extends Helper {
|
|
|
1109
1104
|
async seeElementInDOM(locator) {
|
|
1110
1105
|
const els = await this._locate(locator)
|
|
1111
1106
|
try {
|
|
1112
|
-
return empty('elements on page').negate(els.filter(
|
|
1107
|
+
return empty('elements on page').negate(els.filter(v => v).fill('ELEMENT'))
|
|
1113
1108
|
} catch (e) {
|
|
1114
1109
|
dontSeeElementInDOMError(locator)
|
|
1115
1110
|
}
|
|
@@ -1121,7 +1116,7 @@ class Puppeteer extends Helper {
|
|
|
1121
1116
|
async dontSeeElementInDOM(locator) {
|
|
1122
1117
|
const els = await this._locate(locator)
|
|
1123
1118
|
try {
|
|
1124
|
-
return empty('elements on a page').assert(els.filter(
|
|
1119
|
+
return empty('elements on a page').assert(els.filter(v => v).fill('ELEMENT'))
|
|
1125
1120
|
} catch (e) {
|
|
1126
1121
|
seeElementInDOMError(locator)
|
|
1127
1122
|
}
|
|
@@ -1151,17 +1146,12 @@ class Puppeteer extends Helper {
|
|
|
1151
1146
|
|
|
1152
1147
|
const els = await findClickable.call(this, matcher, locator)
|
|
1153
1148
|
if (context) {
|
|
1154
|
-
assertElementExists(
|
|
1155
|
-
els,
|
|
1156
|
-
locator,
|
|
1157
|
-
'Clickable element',
|
|
1158
|
-
`was not found inside element ${new Locator(context).toString()}`,
|
|
1159
|
-
)
|
|
1149
|
+
assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`)
|
|
1160
1150
|
} else {
|
|
1161
1151
|
assertElementExists(els, locator, 'Clickable element')
|
|
1162
1152
|
}
|
|
1163
1153
|
const elem = els[0]
|
|
1164
|
-
return this.executeScript(
|
|
1154
|
+
return this.executeScript(el => {
|
|
1165
1155
|
if (document.activeElement instanceof HTMLElement) {
|
|
1166
1156
|
document.activeElement.blur()
|
|
1167
1157
|
}
|
|
@@ -1220,8 +1210,8 @@ class Puppeteer extends Helper {
|
|
|
1220
1210
|
let fileName
|
|
1221
1211
|
await this.page.setRequestInterception(true)
|
|
1222
1212
|
|
|
1223
|
-
const xRequest = await new Promise(
|
|
1224
|
-
this.page.on('request',
|
|
1213
|
+
const xRequest = await new Promise(resolve => {
|
|
1214
|
+
this.page.on('request', request => {
|
|
1225
1215
|
console.log('rq', request, customName)
|
|
1226
1216
|
const grabbedFileName = request.url().split('/')[request.url().split('/').length - 1]
|
|
1227
1217
|
const fileExtension = request.url().split('/')[request.url().split('/').length - 1].split('.')[1]
|
|
@@ -1248,7 +1238,7 @@ class Puppeteer extends Helper {
|
|
|
1248
1238
|
}
|
|
1249
1239
|
|
|
1250
1240
|
const cookies = await this.page.cookies()
|
|
1251
|
-
options.headers.Cookie = cookies.map(
|
|
1241
|
+
options.headers.Cookie = cookies.map(ck => `${ck.name}=${ck.value}`).join(';')
|
|
1252
1242
|
|
|
1253
1243
|
const response = await axios({
|
|
1254
1244
|
method: options.method,
|
|
@@ -1302,7 +1292,7 @@ class Puppeteer extends Helper {
|
|
|
1302
1292
|
*/
|
|
1303
1293
|
async checkOption(field, context = null) {
|
|
1304
1294
|
const elm = await this._locateCheckable(field, context)
|
|
1305
|
-
const curentlyChecked = await elm.getProperty('checked').then(
|
|
1295
|
+
const curentlyChecked = await elm.getProperty('checked').then(checkedProperty => checkedProperty.jsonValue())
|
|
1306
1296
|
// Only check if NOT currently checked
|
|
1307
1297
|
if (!curentlyChecked) {
|
|
1308
1298
|
await elm.click()
|
|
@@ -1315,7 +1305,7 @@ class Puppeteer extends Helper {
|
|
|
1315
1305
|
*/
|
|
1316
1306
|
async uncheckOption(field, context = null) {
|
|
1317
1307
|
const elm = await this._locateCheckable(field, context)
|
|
1318
|
-
const curentlyChecked = await elm.getProperty('checked').then(
|
|
1308
|
+
const curentlyChecked = await elm.getProperty('checked').then(checkedProperty => checkedProperty.jsonValue())
|
|
1319
1309
|
// Only uncheck if currently checked
|
|
1320
1310
|
if (curentlyChecked) {
|
|
1321
1311
|
await elm.click()
|
|
@@ -1408,12 +1398,12 @@ class Puppeteer extends Helper {
|
|
|
1408
1398
|
const els = await findVisibleFields.call(this, field)
|
|
1409
1399
|
assertElementExists(els, field, 'Field')
|
|
1410
1400
|
const el = els[0]
|
|
1411
|
-
const tag = await el.getProperty('tagName').then(
|
|
1412
|
-
const editable = await el.getProperty('contenteditable').then(
|
|
1401
|
+
const tag = await el.getProperty('tagName').then(el => el.jsonValue())
|
|
1402
|
+
const editable = await el.getProperty('contenteditable').then(el => el.jsonValue())
|
|
1413
1403
|
if (tag === 'INPUT' || tag === 'TEXTAREA') {
|
|
1414
|
-
await this._evaluateHandeInContext(
|
|
1404
|
+
await this._evaluateHandeInContext(el => (el.value = ''), el)
|
|
1415
1405
|
} else if (editable) {
|
|
1416
|
-
await this._evaluateHandeInContext(
|
|
1406
|
+
await this._evaluateHandeInContext(el => (el.innerHTML = ''), el)
|
|
1417
1407
|
}
|
|
1418
1408
|
|
|
1419
1409
|
highlightActiveElement.call(this, el, await this._getContext())
|
|
@@ -1483,7 +1473,7 @@ class Puppeteer extends Helper {
|
|
|
1483
1473
|
const els = await findVisibleFields.call(this, select)
|
|
1484
1474
|
assertElementExists(els, select, 'Selectable field')
|
|
1485
1475
|
const el = els[0]
|
|
1486
|
-
if ((await el.getProperty('tagName').then(
|
|
1476
|
+
if ((await el.getProperty('tagName').then(t => t.jsonValue())) !== 'SELECT') {
|
|
1487
1477
|
throw new Error('Element is not <select>')
|
|
1488
1478
|
}
|
|
1489
1479
|
highlightActiveElement.call(this, els[0], await this._getContext())
|
|
@@ -1493,15 +1483,15 @@ class Puppeteer extends Helper {
|
|
|
1493
1483
|
const opt = xpathLocator.literal(option[key])
|
|
1494
1484
|
let optEl = await findElements.call(this, el, { xpath: Locator.select.byVisibleText(opt) })
|
|
1495
1485
|
if (optEl.length) {
|
|
1496
|
-
this._evaluateHandeInContext(
|
|
1486
|
+
this._evaluateHandeInContext(el => (el.selected = true), optEl[0])
|
|
1497
1487
|
continue
|
|
1498
1488
|
}
|
|
1499
1489
|
optEl = await findElements.call(this, el, { xpath: Locator.select.byValue(opt) })
|
|
1500
1490
|
if (optEl.length) {
|
|
1501
|
-
this._evaluateHandeInContext(
|
|
1491
|
+
this._evaluateHandeInContext(el => (el.selected = true), optEl[0])
|
|
1502
1492
|
}
|
|
1503
1493
|
}
|
|
1504
|
-
await this._evaluateHandeInContext(
|
|
1494
|
+
await this._evaluateHandeInContext(element => {
|
|
1505
1495
|
element.dispatchEvent(new Event('input', { bubbles: true }))
|
|
1506
1496
|
element.dispatchEvent(new Event('change', { bubbles: true }))
|
|
1507
1497
|
}, el)
|
|
@@ -1515,19 +1505,11 @@ class Puppeteer extends Helper {
|
|
|
1515
1505
|
*/
|
|
1516
1506
|
async grabNumberOfVisibleElements(locator) {
|
|
1517
1507
|
let els = await this._locate(locator)
|
|
1518
|
-
els = (await Promise.all(els.map(
|
|
1508
|
+
els = (await Promise.all(els.map(el => el.boundingBox() && el))).filter(v => v)
|
|
1519
1509
|
// Puppeteer visibility was ignored? | Remove when Puppeteer is fixed
|
|
1520
|
-
els = await Promise.all(
|
|
1521
|
-
els.map(
|
|
1522
|
-
async (el) =>
|
|
1523
|
-
(await el.evaluate(
|
|
1524
|
-
(node) =>
|
|
1525
|
-
window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none',
|
|
1526
|
-
)) && el,
|
|
1527
|
-
),
|
|
1528
|
-
)
|
|
1510
|
+
els = await Promise.all(els.map(async el => (await el.evaluate(node => window.getComputedStyle(node).visibility !== 'hidden' && window.getComputedStyle(node).display !== 'none')) && el))
|
|
1529
1511
|
|
|
1530
|
-
return els.filter(
|
|
1512
|
+
return els.filter(v => v).length
|
|
1531
1513
|
}
|
|
1532
1514
|
|
|
1533
1515
|
/**
|
|
@@ -1635,9 +1617,7 @@ class Puppeteer extends Helper {
|
|
|
1635
1617
|
*/
|
|
1636
1618
|
async seeNumberOfElements(locator, num) {
|
|
1637
1619
|
const elements = await this._locate(locator)
|
|
1638
|
-
return equals(
|
|
1639
|
-
`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`,
|
|
1640
|
-
).assert(elements.length, num)
|
|
1620
|
+
return equals(`expected number of elements (${new Locator(locator)}) is ${num}, but found ${elements.length}`).assert(elements.length, num)
|
|
1641
1621
|
}
|
|
1642
1622
|
|
|
1643
1623
|
/**
|
|
@@ -1647,10 +1627,7 @@ class Puppeteer extends Helper {
|
|
|
1647
1627
|
*/
|
|
1648
1628
|
async seeNumberOfVisibleElements(locator, num) {
|
|
1649
1629
|
const res = await this.grabNumberOfVisibleElements(locator)
|
|
1650
|
-
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(
|
|
1651
|
-
res,
|
|
1652
|
-
num,
|
|
1653
|
-
)
|
|
1630
|
+
return equals(`expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`).assert(res, num)
|
|
1654
1631
|
}
|
|
1655
1632
|
|
|
1656
1633
|
/**
|
|
@@ -1669,7 +1646,7 @@ class Puppeteer extends Helper {
|
|
|
1669
1646
|
*/
|
|
1670
1647
|
async seeCookie(name) {
|
|
1671
1648
|
const cookies = await this.page.cookies()
|
|
1672
|
-
empty(`cookie ${name} to be set`).negate(cookies.filter(
|
|
1649
|
+
empty(`cookie ${name} to be set`).negate(cookies.filter(c => c.name === name))
|
|
1673
1650
|
}
|
|
1674
1651
|
|
|
1675
1652
|
/**
|
|
@@ -1677,7 +1654,7 @@ class Puppeteer extends Helper {
|
|
|
1677
1654
|
*/
|
|
1678
1655
|
async dontSeeCookie(name) {
|
|
1679
1656
|
const cookies = await this.page.cookies()
|
|
1680
|
-
empty(`cookie ${name} not to be set`).assert(cookies.filter(
|
|
1657
|
+
empty(`cookie ${name} not to be set`).assert(cookies.filter(c => c.name === name))
|
|
1681
1658
|
}
|
|
1682
1659
|
|
|
1683
1660
|
/**
|
|
@@ -1688,7 +1665,7 @@ class Puppeteer extends Helper {
|
|
|
1688
1665
|
async grabCookie(name) {
|
|
1689
1666
|
const cookies = await this.page.cookies()
|
|
1690
1667
|
if (!name) return cookies
|
|
1691
|
-
const cookie = cookies.filter(
|
|
1668
|
+
const cookie = cookies.filter(c => c.name === name)
|
|
1692
1669
|
if (cookie[0]) return cookie[0]
|
|
1693
1670
|
}
|
|
1694
1671
|
|
|
@@ -1708,9 +1685,9 @@ class Puppeteer extends Helper {
|
|
|
1708
1685
|
|
|
1709
1686
|
return promiseRetry(
|
|
1710
1687
|
async (retry, number) => {
|
|
1711
|
-
const _grabCookie = async
|
|
1688
|
+
const _grabCookie = async name => {
|
|
1712
1689
|
const cookies = await this.page.cookies()
|
|
1713
|
-
const cookie = cookies.filter(
|
|
1690
|
+
const cookie = cookies.filter(c => c.name === name)
|
|
1714
1691
|
if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`)
|
|
1715
1692
|
}
|
|
1716
1693
|
|
|
@@ -1735,7 +1712,7 @@ class Puppeteer extends Helper {
|
|
|
1735
1712
|
if (!name) {
|
|
1736
1713
|
return this.page.deleteCookie.apply(this.page, cookies)
|
|
1737
1714
|
}
|
|
1738
|
-
const cookie = cookies.filter(
|
|
1715
|
+
const cookie = cookies.filter(c => c.name === name)
|
|
1739
1716
|
if (!cookie[0]) return
|
|
1740
1717
|
return this.page.deleteCookie(cookie[0])
|
|
1741
1718
|
}
|
|
@@ -1760,8 +1737,8 @@ class Puppeteer extends Helper {
|
|
|
1760
1737
|
async executeAsyncScript(...args) {
|
|
1761
1738
|
const asyncFn = function () {
|
|
1762
1739
|
const args = Array.from(arguments)
|
|
1763
|
-
const fn = eval(`(${args.shift()})`)
|
|
1764
|
-
return new Promise(
|
|
1740
|
+
const fn = eval(`(${args.shift()})`)
|
|
1741
|
+
return new Promise(done => {
|
|
1765
1742
|
args.push(done)
|
|
1766
1743
|
fn.apply(null, args)
|
|
1767
1744
|
})
|
|
@@ -1828,7 +1805,7 @@ class Puppeteer extends Helper {
|
|
|
1828
1805
|
*/
|
|
1829
1806
|
async grabHTMLFromAll(locator) {
|
|
1830
1807
|
const els = await this._locate(locator)
|
|
1831
|
-
const values = await Promise.all(els.map(
|
|
1808
|
+
const values = await Promise.all(els.map(el => el.evaluate(element => element.innerHTML, el)))
|
|
1832
1809
|
return values
|
|
1833
1810
|
}
|
|
1834
1811
|
|
|
@@ -1851,10 +1828,8 @@ class Puppeteer extends Helper {
|
|
|
1851
1828
|
*/
|
|
1852
1829
|
async grabCssPropertyFromAll(locator, cssProperty) {
|
|
1853
1830
|
const els = await this._locate(locator)
|
|
1854
|
-
const res = await Promise.all(
|
|
1855
|
-
|
|
1856
|
-
)
|
|
1857
|
-
const cssValues = res.map((props) => props[toCamelCase(cssProperty)])
|
|
1831
|
+
const res = await Promise.all(els.map(el => el.evaluate(el => JSON.parse(JSON.stringify(getComputedStyle(el))), el)))
|
|
1832
|
+
const cssValues = res.map(props => props[toCamelCase(cssProperty)])
|
|
1858
1833
|
|
|
1859
1834
|
return cssValues
|
|
1860
1835
|
}
|
|
@@ -1897,19 +1872,16 @@ class Puppeteer extends Helper {
|
|
|
1897
1872
|
}
|
|
1898
1873
|
}
|
|
1899
1874
|
|
|
1900
|
-
const values = Object.keys(cssPropertiesCamelCase).map(
|
|
1875
|
+
const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key])
|
|
1901
1876
|
if (!Array.isArray(props)) props = [props]
|
|
1902
1877
|
let chunked = chunkArray(props, values.length)
|
|
1903
|
-
chunked = chunked.filter(
|
|
1878
|
+
chunked = chunked.filter(val => {
|
|
1904
1879
|
for (let i = 0; i < val.length; ++i) {
|
|
1905
|
-
// eslint-disable-next-line eqeqeq
|
|
1906
1880
|
if (val[i] != values[i]) return false
|
|
1907
1881
|
}
|
|
1908
1882
|
return true
|
|
1909
1883
|
})
|
|
1910
|
-
return equals(
|
|
1911
|
-
`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
|
|
1912
|
-
).assert(chunked.length, elemAmount)
|
|
1884
|
+
return equals(`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount)
|
|
1913
1885
|
}
|
|
1914
1886
|
|
|
1915
1887
|
/**
|
|
@@ -1922,7 +1894,7 @@ class Puppeteer extends Helper {
|
|
|
1922
1894
|
|
|
1923
1895
|
const expectedAttributes = Object.entries(attributes)
|
|
1924
1896
|
|
|
1925
|
-
const valuesPromises = elements.map(async
|
|
1897
|
+
const valuesPromises = elements.map(async element => {
|
|
1926
1898
|
const elementAttributes = {}
|
|
1927
1899
|
await Promise.all(
|
|
1928
1900
|
expectedAttributes.map(async ([attribute, expectedValue]) => {
|
|
@@ -1935,7 +1907,7 @@ class Puppeteer extends Helper {
|
|
|
1935
1907
|
|
|
1936
1908
|
const actualAttributes = await Promise.all(valuesPromises)
|
|
1937
1909
|
|
|
1938
|
-
const matchingElements = actualAttributes.filter(
|
|
1910
|
+
const matchingElements = actualAttributes.filter(attrs =>
|
|
1939
1911
|
expectedAttributes.every(([attribute, expectedValue]) => {
|
|
1940
1912
|
const actualValue = attrs[attribute]
|
|
1941
1913
|
if (!actualValue) return false
|
|
@@ -1947,10 +1919,7 @@ class Puppeteer extends Helper {
|
|
|
1947
1919
|
const elementsCount = elements.length
|
|
1948
1920
|
const matchingCount = matchingElements.length
|
|
1949
1921
|
|
|
1950
|
-
return equals(`all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`).assert(
|
|
1951
|
-
matchingCount,
|
|
1952
|
-
elementsCount,
|
|
1953
|
-
)
|
|
1922
|
+
return equals(`all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`).assert(matchingCount, elementsCount)
|
|
1954
1923
|
}
|
|
1955
1924
|
|
|
1956
1925
|
/**
|
|
@@ -2080,7 +2049,7 @@ class Puppeteer extends Helper {
|
|
|
2080
2049
|
* {{> wait }}
|
|
2081
2050
|
*/
|
|
2082
2051
|
async wait(sec) {
|
|
2083
|
-
return new Promise(
|
|
2052
|
+
return new Promise(done => {
|
|
2084
2053
|
setTimeout(done, sec * 1000)
|
|
2085
2054
|
})
|
|
2086
2055
|
}
|
|
@@ -2100,20 +2069,18 @@ class Puppeteer extends Helper {
|
|
|
2100
2069
|
if (!els || els.length === 0) {
|
|
2101
2070
|
return false
|
|
2102
2071
|
}
|
|
2103
|
-
return Array.prototype.filter.call(els,
|
|
2072
|
+
return Array.prototype.filter.call(els, el => !el.disabled).length > 0
|
|
2104
2073
|
}
|
|
2105
2074
|
waiter = context.waitForFunction(enabledFn, { timeout: waitTimeout }, locator.value)
|
|
2106
2075
|
} else {
|
|
2107
2076
|
const enabledFn = function (locator, $XPath) {
|
|
2108
|
-
eval($XPath)
|
|
2109
|
-
return $XPath(null, locator).filter(
|
|
2077
|
+
eval($XPath)
|
|
2078
|
+
return $XPath(null, locator).filter(el => !el.disabled).length > 0
|
|
2110
2079
|
}
|
|
2111
2080
|
waiter = context.waitForFunction(enabledFn, { timeout: waitTimeout }, locator.value, $XPath.toString())
|
|
2112
2081
|
}
|
|
2113
|
-
return waiter.catch(
|
|
2114
|
-
throw new Error(
|
|
2115
|
-
`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2116
|
-
)
|
|
2082
|
+
return waiter.catch(err => {
|
|
2083
|
+
throw new Error(`element (${locator.toString()}) still not enabled after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2117
2084
|
})
|
|
2118
2085
|
}
|
|
2119
2086
|
|
|
@@ -2132,21 +2099,19 @@ class Puppeteer extends Helper {
|
|
|
2132
2099
|
if (!els || els.length === 0) {
|
|
2133
2100
|
return false
|
|
2134
2101
|
}
|
|
2135
|
-
return Array.prototype.filter.call(els,
|
|
2102
|
+
return Array.prototype.filter.call(els, el => (el.value.toString() || '').indexOf(value) !== -1).length > 0
|
|
2136
2103
|
}
|
|
2137
2104
|
waiter = context.waitForFunction(valueFn, { timeout: waitTimeout }, locator.value, value)
|
|
2138
2105
|
} else {
|
|
2139
2106
|
const valueFn = function (locator, $XPath, value) {
|
|
2140
|
-
eval($XPath)
|
|
2141
|
-
return $XPath(null, locator).filter(
|
|
2107
|
+
eval($XPath)
|
|
2108
|
+
return $XPath(null, locator).filter(el => (el.value || '').indexOf(value) !== -1).length > 0
|
|
2142
2109
|
}
|
|
2143
2110
|
waiter = context.waitForFunction(valueFn, { timeout: waitTimeout }, locator.value, $XPath.toString(), value)
|
|
2144
2111
|
}
|
|
2145
|
-
return waiter.catch(
|
|
2112
|
+
return waiter.catch(err => {
|
|
2146
2113
|
const loc = locator.toString()
|
|
2147
|
-
throw new Error(
|
|
2148
|
-
`element (${loc}) is not in DOM or there is no element(${loc}) with value "${value}" after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2149
|
-
)
|
|
2114
|
+
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}`)
|
|
2150
2115
|
})
|
|
2151
2116
|
}
|
|
2152
2117
|
|
|
@@ -2165,20 +2130,18 @@ class Puppeteer extends Helper {
|
|
|
2165
2130
|
if (!els || els.length === 0) {
|
|
2166
2131
|
return false
|
|
2167
2132
|
}
|
|
2168
|
-
return Array.prototype.filter.call(els,
|
|
2133
|
+
return Array.prototype.filter.call(els, el => el.offsetParent !== null).length === num
|
|
2169
2134
|
}
|
|
2170
2135
|
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, num)
|
|
2171
2136
|
} else {
|
|
2172
2137
|
const visibleFn = function (locator, $XPath, num) {
|
|
2173
|
-
eval($XPath)
|
|
2174
|
-
return $XPath(null, locator).filter(
|
|
2138
|
+
eval($XPath)
|
|
2139
|
+
return $XPath(null, locator).filter(el => el.offsetParent !== null).length === num
|
|
2175
2140
|
}
|
|
2176
2141
|
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, $XPath.toString(), num)
|
|
2177
2142
|
}
|
|
2178
|
-
return waiter.catch(
|
|
2179
|
-
throw new Error(
|
|
2180
|
-
`The number of elements (${locator.toString()}) is not ${num} after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2181
|
-
)
|
|
2143
|
+
return waiter.catch(err => {
|
|
2144
|
+
throw new Error(`The number of elements (${locator.toString()}) is not ${num} after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2182
2145
|
})
|
|
2183
2146
|
}
|
|
2184
2147
|
|
|
@@ -2186,14 +2149,14 @@ class Puppeteer extends Helper {
|
|
|
2186
2149
|
* {{> waitForClickable }}
|
|
2187
2150
|
*/
|
|
2188
2151
|
async waitForClickable(locator, waitTimeout) {
|
|
2189
|
-
const
|
|
2190
|
-
|
|
2152
|
+
const el = await this._locateElement(locator)
|
|
2153
|
+
if (!el) {
|
|
2154
|
+
throw new ElementNotFound(locator, 'Element to wait for clickable')
|
|
2155
|
+
}
|
|
2191
2156
|
|
|
2192
|
-
return this.waitForFunction(isElementClickable, [
|
|
2157
|
+
return this.waitForFunction(isElementClickable, [el], waitTimeout).catch(async e => {
|
|
2193
2158
|
if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
|
|
2194
|
-
throw new Error(
|
|
2195
|
-
`element ${new Locator(locator).toString()} still not clickable after ${waitTimeout || this.options.waitForTimeout / 1000} sec`,
|
|
2196
|
-
)
|
|
2159
|
+
throw new Error(`element ${new Locator(locator).toString()} still not clickable after ${waitTimeout || this.options.waitForTimeout / 1000} sec`)
|
|
2197
2160
|
} else {
|
|
2198
2161
|
throw e
|
|
2199
2162
|
}
|
|
@@ -2215,10 +2178,8 @@ class Puppeteer extends Helper {
|
|
|
2215
2178
|
} else {
|
|
2216
2179
|
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout })
|
|
2217
2180
|
}
|
|
2218
|
-
return waiter.catch(
|
|
2219
|
-
throw new Error(
|
|
2220
|
-
`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2221
|
-
)
|
|
2181
|
+
return waiter.catch(err => {
|
|
2182
|
+
throw new Error(`element (${locator.toString()}) still not present on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2222
2183
|
})
|
|
2223
2184
|
}
|
|
2224
2185
|
|
|
@@ -2238,10 +2199,8 @@ class Puppeteer extends Helper {
|
|
|
2238
2199
|
} else {
|
|
2239
2200
|
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, visible: true })
|
|
2240
2201
|
}
|
|
2241
|
-
return waiter.catch(
|
|
2242
|
-
throw new Error(
|
|
2243
|
-
`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2244
|
-
)
|
|
2202
|
+
return waiter.catch(err => {
|
|
2203
|
+
throw new Error(`element (${locator.toString()}) still not visible after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2245
2204
|
})
|
|
2246
2205
|
}
|
|
2247
2206
|
|
|
@@ -2259,7 +2218,7 @@ class Puppeteer extends Helper {
|
|
|
2259
2218
|
} else {
|
|
2260
2219
|
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true })
|
|
2261
2220
|
}
|
|
2262
|
-
return waiter.catch(
|
|
2221
|
+
return waiter.catch(err => {
|
|
2263
2222
|
throw new Error(`element (${locator.toString()}) still visible after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2264
2223
|
})
|
|
2265
2224
|
}
|
|
@@ -2277,10 +2236,8 @@ class Puppeteer extends Helper {
|
|
|
2277
2236
|
} else {
|
|
2278
2237
|
waiter = _waitForElement.call(this, locator, { timeout: waitTimeout, hidden: true })
|
|
2279
2238
|
}
|
|
2280
|
-
return waiter.catch(
|
|
2281
|
-
throw new Error(
|
|
2282
|
-
`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`,
|
|
2283
|
-
)
|
|
2239
|
+
return waiter.catch(err => {
|
|
2240
|
+
throw new Error(`element (${locator.toString()}) still not hidden after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2284
2241
|
})
|
|
2285
2242
|
}
|
|
2286
2243
|
|
|
@@ -2317,14 +2274,14 @@ class Puppeteer extends Helper {
|
|
|
2317
2274
|
|
|
2318
2275
|
return this.page
|
|
2319
2276
|
.waitForFunction(
|
|
2320
|
-
|
|
2277
|
+
urlPart => {
|
|
2321
2278
|
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
|
|
2322
2279
|
return currUrl.indexOf(urlPart) > -1
|
|
2323
2280
|
},
|
|
2324
2281
|
{ timeout: waitTimeout },
|
|
2325
2282
|
urlPart,
|
|
2326
2283
|
)
|
|
2327
|
-
.catch(async
|
|
2284
|
+
.catch(async e => {
|
|
2328
2285
|
const currUrl = await this._getPageUrl() // Required because the waitForFunction can't return data.
|
|
2329
2286
|
if (/Waiting failed:/i.test(e.message) || /failed: timeout/i.test(e.message)) {
|
|
2330
2287
|
throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
|
|
@@ -2347,14 +2304,14 @@ class Puppeteer extends Helper {
|
|
|
2347
2304
|
|
|
2348
2305
|
return this.page
|
|
2349
2306
|
.waitForFunction(
|
|
2350
|
-
|
|
2307
|
+
urlPart => {
|
|
2351
2308
|
const currUrl = decodeURIComponent(decodeURIComponent(decodeURIComponent(window.location.href)))
|
|
2352
2309
|
return currUrl.indexOf(urlPart) > -1
|
|
2353
2310
|
},
|
|
2354
2311
|
{ timeout: waitTimeout },
|
|
2355
2312
|
urlPart,
|
|
2356
2313
|
)
|
|
2357
|
-
.catch(async
|
|
2314
|
+
.catch(async e => {
|
|
2358
2315
|
const currUrl = await this._getPageUrl() // Required because the waitForFunction can't return data.
|
|
2359
2316
|
if (/Waiting failed/i.test(e.message) || /failed: timeout/i.test(e.message)) {
|
|
2360
2317
|
throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
|
|
@@ -2391,7 +2348,7 @@ class Puppeteer extends Helper {
|
|
|
2391
2348
|
if (locator.isXPath()) {
|
|
2392
2349
|
waiter = contextObject.waitForFunction(
|
|
2393
2350
|
(locator, text, $XPath) => {
|
|
2394
|
-
eval($XPath)
|
|
2351
|
+
eval($XPath)
|
|
2395
2352
|
const el = $XPath(null, locator)
|
|
2396
2353
|
if (!el.length) return false
|
|
2397
2354
|
return el[0].innerText.indexOf(text) > -1
|
|
@@ -2403,14 +2360,10 @@ class Puppeteer extends Helper {
|
|
|
2403
2360
|
)
|
|
2404
2361
|
}
|
|
2405
2362
|
} else {
|
|
2406
|
-
waiter = contextObject.waitForFunction(
|
|
2407
|
-
(text) => document.body && document.body.innerText.indexOf(text) > -1,
|
|
2408
|
-
{ timeout: waitTimeout },
|
|
2409
|
-
text,
|
|
2410
|
-
)
|
|
2363
|
+
waiter = contextObject.waitForFunction(text => document.body && document.body.innerText.indexOf(text) > -1, { timeout: waitTimeout }, text)
|
|
2411
2364
|
}
|
|
2412
2365
|
|
|
2413
|
-
return waiter.catch(
|
|
2366
|
+
return waiter.catch(err => {
|
|
2414
2367
|
throw new Error(`Text "${text}" was not found on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2415
2368
|
})
|
|
2416
2369
|
}
|
|
@@ -2543,12 +2496,12 @@ class Puppeteer extends Helper {
|
|
|
2543
2496
|
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value)
|
|
2544
2497
|
} else {
|
|
2545
2498
|
const visibleFn = function (locator, $XPath) {
|
|
2546
|
-
eval($XPath)
|
|
2499
|
+
eval($XPath)
|
|
2547
2500
|
return $XPath(null, locator).length === 0
|
|
2548
2501
|
}
|
|
2549
2502
|
waiter = context.waitForFunction(visibleFn, { timeout: waitTimeout }, locator.value, $XPath.toString())
|
|
2550
2503
|
}
|
|
2551
|
-
return waiter.catch(
|
|
2504
|
+
return waiter.catch(err => {
|
|
2552
2505
|
throw new Error(`element (${locator.toString()}) still on page after ${waitTimeout / 1000} sec\n${err.message}`)
|
|
2553
2506
|
})
|
|
2554
2507
|
}
|
|
@@ -2589,7 +2542,7 @@ class Puppeteer extends Helper {
|
|
|
2589
2542
|
async mockRoute(url, handler) {
|
|
2590
2543
|
await this.page.setRequestInterception(true)
|
|
2591
2544
|
|
|
2592
|
-
this.page.on('request',
|
|
2545
|
+
this.page.on('request', interceptedRequest => {
|
|
2593
2546
|
if (interceptedRequest.url().match(url)) {
|
|
2594
2547
|
// @ts-ignore
|
|
2595
2548
|
handler(interceptedRequest)
|
|
@@ -2614,7 +2567,7 @@ class Puppeteer extends Helper {
|
|
|
2614
2567
|
this.page.off('request')
|
|
2615
2568
|
|
|
2616
2569
|
// Resume normal request handling for the given URL
|
|
2617
|
-
this.page.on('request',
|
|
2570
|
+
this.page.on('request', interceptedRequest => {
|
|
2618
2571
|
if (interceptedRequest.url().includes(url)) {
|
|
2619
2572
|
interceptedRequest.continue()
|
|
2620
2573
|
} else {
|
|
@@ -2650,7 +2603,7 @@ class Puppeteer extends Helper {
|
|
|
2650
2603
|
|
|
2651
2604
|
await this.page.setRequestInterception(true)
|
|
2652
2605
|
|
|
2653
|
-
this.page.on('request',
|
|
2606
|
+
this.page.on('request', request => {
|
|
2654
2607
|
const information = {
|
|
2655
2608
|
url: request.url(),
|
|
2656
2609
|
method: request.method(),
|
|
@@ -2711,15 +2664,15 @@ class Puppeteer extends Helper {
|
|
|
2711
2664
|
await this.cdpSession.send('Network.enable')
|
|
2712
2665
|
await this.cdpSession.send('Page.enable')
|
|
2713
2666
|
|
|
2714
|
-
this.cdpSession.on('Network.webSocketFrameReceived',
|
|
2667
|
+
this.cdpSession.on('Network.webSocketFrameReceived', payload => {
|
|
2715
2668
|
this._logWebsocketMessages(this._getWebSocketLog('RECEIVED', payload))
|
|
2716
2669
|
})
|
|
2717
2670
|
|
|
2718
|
-
this.cdpSession.on('Network.webSocketFrameSent',
|
|
2671
|
+
this.cdpSession.on('Network.webSocketFrameSent', payload => {
|
|
2719
2672
|
this._logWebsocketMessages(this._getWebSocketLog('SENT', payload))
|
|
2720
2673
|
})
|
|
2721
2674
|
|
|
2722
|
-
this.cdpSession.on('Network.webSocketFrameError',
|
|
2675
|
+
this.cdpSession.on('Network.webSocketFrameError', payload => {
|
|
2723
2676
|
this._logWebsocketMessages(this._getWebSocketLog('ERROR', payload))
|
|
2724
2677
|
})
|
|
2725
2678
|
}
|
|
@@ -2743,9 +2696,7 @@ class Puppeteer extends Helper {
|
|
|
2743
2696
|
grabWebSocketMessages() {
|
|
2744
2697
|
if (!this.recordingWebSocketMessages) {
|
|
2745
2698
|
if (!this.recordedWebSocketMessagesAtLeastOnce) {
|
|
2746
|
-
throw new Error(
|
|
2747
|
-
'Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.',
|
|
2748
|
-
)
|
|
2699
|
+
throw new Error('Failure in test automation. You use "I.grabWebSocketMessages", but "I.startRecordingWebSocketMessages" was never called before.')
|
|
2749
2700
|
}
|
|
2750
2701
|
}
|
|
2751
2702
|
return this.webSocketMessages
|
|
@@ -2777,9 +2728,18 @@ class Puppeteer extends Helper {
|
|
|
2777
2728
|
|
|
2778
2729
|
module.exports = Puppeteer
|
|
2779
2730
|
|
|
2731
|
+
/**
|
|
2732
|
+
* Find elements using Puppeteer's native element discovery methods
|
|
2733
|
+
* Note: Unlike Playwright, Puppeteer's Locator API doesn't have .all() method for multiple elements
|
|
2734
|
+
* @param {Object} matcher - Puppeteer context to search within
|
|
2735
|
+
* @param {Object|string} locator - Locator specification
|
|
2736
|
+
* @returns {Promise<Array>} Array of ElementHandle objects
|
|
2737
|
+
*/
|
|
2780
2738
|
async function findElements(matcher, locator) {
|
|
2781
2739
|
if (locator.react) return findReactElements.call(this, locator)
|
|
2782
2740
|
locator = new Locator(locator, 'css')
|
|
2741
|
+
|
|
2742
|
+
// Use proven legacy approach - Puppeteer Locator API doesn't have .all() method
|
|
2783
2743
|
if (!locator.isXPath()) return matcher.$$(locator.simplify())
|
|
2784
2744
|
// puppeteer version < 19.4.0 is no longer supported. This one is backward support.
|
|
2785
2745
|
if (puppeteer.default?.defaultBrowserRevision) {
|
|
@@ -2788,6 +2748,31 @@ async function findElements(matcher, locator) {
|
|
|
2788
2748
|
return matcher.$x(locator.value)
|
|
2789
2749
|
}
|
|
2790
2750
|
|
|
2751
|
+
/**
|
|
2752
|
+
* Find a single element using Puppeteer's native element discovery methods
|
|
2753
|
+
* Note: Puppeteer Locator API doesn't have .first() method like Playwright
|
|
2754
|
+
* @param {Object} matcher - Puppeteer context to search within
|
|
2755
|
+
* @param {Object|string} locator - Locator specification
|
|
2756
|
+
* @returns {Promise<Object>} Single ElementHandle object
|
|
2757
|
+
*/
|
|
2758
|
+
async function findElement(matcher, locator) {
|
|
2759
|
+
if (locator.react) return findReactElements.call(this, locator)
|
|
2760
|
+
locator = new Locator(locator, 'css')
|
|
2761
|
+
|
|
2762
|
+
// Use proven legacy approach - Puppeteer Locator API doesn't have .first() method
|
|
2763
|
+
if (!locator.isXPath()) {
|
|
2764
|
+
const elements = await matcher.$$(locator.simplify())
|
|
2765
|
+
return elements[0]
|
|
2766
|
+
}
|
|
2767
|
+
// puppeteer version < 19.4.0 is no longer supported. This one is backward support.
|
|
2768
|
+
if (puppeteer.default?.defaultBrowserRevision) {
|
|
2769
|
+
const elements = await matcher.$$(`xpath/${locator.value}`)
|
|
2770
|
+
return elements[0]
|
|
2771
|
+
}
|
|
2772
|
+
const elements = await matcher.$x(locator.value)
|
|
2773
|
+
return elements[0]
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2791
2776
|
async function proceedClick(locator, context = null, options = {}) {
|
|
2792
2777
|
let matcher = await this.context
|
|
2793
2778
|
if (context) {
|
|
@@ -2797,12 +2782,7 @@ async function proceedClick(locator, context = null, options = {}) {
|
|
|
2797
2782
|
}
|
|
2798
2783
|
const els = await findClickable.call(this, matcher, locator)
|
|
2799
2784
|
if (context) {
|
|
2800
|
-
assertElementExists(
|
|
2801
|
-
els,
|
|
2802
|
-
locator,
|
|
2803
|
-
'Clickable element',
|
|
2804
|
-
`was not found inside element ${new Locator(context).toString()}`,
|
|
2805
|
-
)
|
|
2785
|
+
assertElementExists(els, locator, 'Clickable element', `was not found inside element ${new Locator(context).toString()}`)
|
|
2806
2786
|
} else {
|
|
2807
2787
|
assertElementExists(els, locator, 'Clickable element')
|
|
2808
2788
|
}
|
|
@@ -2854,23 +2834,25 @@ async function proceedSee(assertType, text, context, strict = false) {
|
|
|
2854
2834
|
el = await this.context.$('body')
|
|
2855
2835
|
}
|
|
2856
2836
|
|
|
2857
|
-
allText = [await el.getProperty('innerText').then(
|
|
2837
|
+
allText = [await el.getProperty('innerText').then(p => p.jsonValue())]
|
|
2858
2838
|
description = 'web application'
|
|
2859
2839
|
} else {
|
|
2860
2840
|
const locator = new Locator(context, 'css')
|
|
2861
2841
|
description = `element ${locator.toString()}`
|
|
2862
2842
|
const els = await this._locate(locator)
|
|
2863
2843
|
assertElementExists(els, locator.toString())
|
|
2864
|
-
allText = await Promise.all(els.map(
|
|
2844
|
+
allText = await Promise.all(els.map(el => el.getProperty('innerText').then(p => p.jsonValue())))
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
if (store?.currentStep?.opts?.ignoreCase === true) {
|
|
2848
|
+
text = text.toLowerCase()
|
|
2849
|
+
allText = allText.map(elText => elText.toLowerCase())
|
|
2865
2850
|
}
|
|
2866
2851
|
|
|
2867
2852
|
if (strict) {
|
|
2868
|
-
return allText.map(
|
|
2853
|
+
return allText.map(elText => equals(description)[assertType](text, elText))
|
|
2869
2854
|
}
|
|
2870
|
-
return stringIncludes(description)[assertType](
|
|
2871
|
-
normalizeSpacesInString(text),
|
|
2872
|
-
normalizeSpacesInString(allText.join(' | ')),
|
|
2873
|
-
)
|
|
2855
|
+
return stringIncludes(description)[assertType](normalizeSpacesInString(text), normalizeSpacesInString(allText.join(' | ')))
|
|
2874
2856
|
}
|
|
2875
2857
|
|
|
2876
2858
|
async function findCheckable(locator, context) {
|
|
@@ -2900,15 +2882,15 @@ async function findCheckable(locator, context) {
|
|
|
2900
2882
|
async function proceedIsChecked(assertType, option) {
|
|
2901
2883
|
let els = await findCheckable.call(this, option)
|
|
2902
2884
|
assertElementExists(els, option, 'Checkable')
|
|
2903
|
-
els = await Promise.all(els.map(
|
|
2904
|
-
els = await Promise.all(els.map(
|
|
2885
|
+
els = await Promise.all(els.map(el => el.getProperty('checked')))
|
|
2886
|
+
els = await Promise.all(els.map(el => el.jsonValue()))
|
|
2905
2887
|
const selected = els.reduce((prev, cur) => prev || cur)
|
|
2906
2888
|
return truth(`checkable ${option}`, 'to be checked')[assertType](selected)
|
|
2907
2889
|
}
|
|
2908
2890
|
|
|
2909
2891
|
async function findVisibleFields(locator) {
|
|
2910
2892
|
const els = await findFields.call(this, locator)
|
|
2911
|
-
const visible = await Promise.all(els.map(
|
|
2893
|
+
const visible = await Promise.all(els.map(el => el.boundingBox()))
|
|
2912
2894
|
return els.filter((el, index) => visible[index])
|
|
2913
2895
|
}
|
|
2914
2896
|
|
|
@@ -2936,15 +2918,19 @@ async function findFields(locator) {
|
|
|
2936
2918
|
}
|
|
2937
2919
|
|
|
2938
2920
|
async function proceedDragAndDrop(sourceLocator, destinationLocator) {
|
|
2939
|
-
const src = await this.
|
|
2940
|
-
|
|
2921
|
+
const src = await this._locateElement(sourceLocator)
|
|
2922
|
+
if (!src) {
|
|
2923
|
+
throw new ElementNotFound(sourceLocator, 'Source Element')
|
|
2924
|
+
}
|
|
2941
2925
|
|
|
2942
|
-
const dst = await this.
|
|
2943
|
-
|
|
2926
|
+
const dst = await this._locateElement(destinationLocator)
|
|
2927
|
+
if (!dst) {
|
|
2928
|
+
throw new ElementNotFound(destinationLocator, 'Destination Element')
|
|
2929
|
+
}
|
|
2944
2930
|
|
|
2945
|
-
// Note: Using public api .getClickablePoint
|
|
2946
|
-
const dragSource = await getClickablePoint(src
|
|
2947
|
-
const dragDestination = await getClickablePoint(dst
|
|
2931
|
+
// Note: Using public api .getClickablePoint because the .BoundingBox does not take into account iframe offsets
|
|
2932
|
+
const dragSource = await getClickablePoint(src)
|
|
2933
|
+
const dragDestination = await getClickablePoint(dst)
|
|
2948
2934
|
|
|
2949
2935
|
// Drag start point
|
|
2950
2936
|
await this.page.mouse.move(dragSource.x, dragSource.y, { steps: 5 })
|
|
@@ -2961,15 +2947,15 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
2961
2947
|
const els = await findVisibleFields.call(this, field)
|
|
2962
2948
|
assertElementExists(els, field, 'Field')
|
|
2963
2949
|
const el = els[0]
|
|
2964
|
-
const tag = await el.getProperty('tagName').then(
|
|
2965
|
-
const fieldType = await el.getProperty('type').then(
|
|
2950
|
+
const tag = await el.getProperty('tagName').then(el => el.jsonValue())
|
|
2951
|
+
const fieldType = await el.getProperty('type').then(el => el.jsonValue())
|
|
2966
2952
|
|
|
2967
|
-
const proceedMultiple = async
|
|
2953
|
+
const proceedMultiple = async elements => {
|
|
2968
2954
|
const fields = Array.isArray(elements) ? elements : [elements]
|
|
2969
2955
|
|
|
2970
2956
|
const elementValues = []
|
|
2971
2957
|
for (const element of fields) {
|
|
2972
|
-
elementValues.push(await element.getProperty('value').then(
|
|
2958
|
+
elementValues.push(await element.getProperty('value').then(el => el.jsonValue()))
|
|
2973
2959
|
}
|
|
2974
2960
|
|
|
2975
2961
|
if (typeof value === 'boolean') {
|
|
@@ -2978,7 +2964,7 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
2978
2964
|
if (assertType === 'assert') {
|
|
2979
2965
|
equals(`select option by ${field}`)[assertType](true, elementValues.length > 0)
|
|
2980
2966
|
}
|
|
2981
|
-
elementValues.forEach(
|
|
2967
|
+
elementValues.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val))
|
|
2982
2968
|
}
|
|
2983
2969
|
}
|
|
2984
2970
|
|
|
@@ -3006,14 +2992,14 @@ async function proceedSeeInField(assertType, field, value) {
|
|
|
3006
2992
|
}
|
|
3007
2993
|
return proceedMultiple(els[0])
|
|
3008
2994
|
}
|
|
3009
|
-
const fieldVal = await el.getProperty('value').then(
|
|
2995
|
+
const fieldVal = await el.getProperty('value').then(el => el.jsonValue())
|
|
3010
2996
|
return stringIncludes(`fields by ${field}`)[assertType](value, fieldVal)
|
|
3011
2997
|
}
|
|
3012
2998
|
|
|
3013
2999
|
async function filterFieldsByValue(elements, value, onlySelected) {
|
|
3014
3000
|
const matches = []
|
|
3015
3001
|
for (const element of elements) {
|
|
3016
|
-
const val = await element.getProperty('value').then(
|
|
3002
|
+
const val = await element.getProperty('value').then(el => el.jsonValue())
|
|
3017
3003
|
let isSelected = true
|
|
3018
3004
|
if (onlySelected) {
|
|
3019
3005
|
isSelected = await elementSelected(element)
|
|
@@ -3037,12 +3023,12 @@ async function filterFieldsBySelectionState(elements, state) {
|
|
|
3037
3023
|
}
|
|
3038
3024
|
|
|
3039
3025
|
async function elementSelected(element) {
|
|
3040
|
-
const type = await element.getProperty('type').then(
|
|
3026
|
+
const type = await element.getProperty('type').then(el => el.jsonValue())
|
|
3041
3027
|
|
|
3042
3028
|
if (type === 'checkbox' || type === 'radio') {
|
|
3043
|
-
return element.getProperty('checked').then(
|
|
3029
|
+
return element.getProperty('checked').then(el => el.jsonValue())
|
|
3044
3030
|
}
|
|
3045
|
-
return element.getProperty('selected').then(
|
|
3031
|
+
return element.getProperty('selected').then(el => el.jsonValue())
|
|
3046
3032
|
}
|
|
3047
3033
|
|
|
3048
3034
|
function isFrameLocator(locator) {
|
|
@@ -3077,9 +3063,9 @@ async function targetCreatedHandler(page) {
|
|
|
3077
3063
|
page
|
|
3078
3064
|
.$('body')
|
|
3079
3065
|
.catch(() => null)
|
|
3080
|
-
.then(
|
|
3066
|
+
.then(context => (this.context = context))
|
|
3081
3067
|
})
|
|
3082
|
-
page.on('console',
|
|
3068
|
+
page.on('console', msg => {
|
|
3083
3069
|
this.debugSection(`Browser:${ucfirst(msg.type())}`, (msg._text || '') + msg.args().join(' '))
|
|
3084
3070
|
consoleLogStore.add(msg)
|
|
3085
3071
|
})
|
|
@@ -3186,7 +3172,7 @@ async function findReactElements(locator, props = {}, state = {}) {
|
|
|
3186
3172
|
|
|
3187
3173
|
await this.page.evaluate(() => window.resq.waitToLoadReact())
|
|
3188
3174
|
const arrayHandle = await this.page.evaluateHandle(
|
|
3189
|
-
|
|
3175
|
+
obj => {
|
|
3190
3176
|
const { selector, props, state } = obj
|
|
3191
3177
|
let elements = window.resq.resq$$(selector)
|
|
3192
3178
|
if (Object.keys(props).length) {
|
|
@@ -3205,7 +3191,7 @@ async function findReactElements(locator, props = {}, state = {}) {
|
|
|
3205
3191
|
// [[div, div], [div, div]] => [div, div, div, div]
|
|
3206
3192
|
let nodes = []
|
|
3207
3193
|
|
|
3208
|
-
elements.forEach(
|
|
3194
|
+
elements.forEach(element => {
|
|
3209
3195
|
let { node, isFragment } = element
|
|
3210
3196
|
|
|
3211
3197
|
if (!node) {
|