codeceptjs 3.6.10 → 3.7.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/README.md +81 -110
  2. package/bin/codecept.js +2 -2
  3. package/docs/webapi/clearCookie.mustache +1 -1
  4. package/lib/actor.js +46 -36
  5. package/lib/assert/empty.js +3 -5
  6. package/lib/assert/equal.js +4 -7
  7. package/lib/assert/include.js +4 -6
  8. package/lib/assert/throws.js +2 -4
  9. package/lib/assert/truth.js +2 -2
  10. package/lib/codecept.js +87 -83
  11. package/lib/command/configMigrate.js +2 -4
  12. package/lib/command/definitions.js +5 -25
  13. package/lib/command/generate.js +10 -14
  14. package/lib/command/gherkin/snippets.js +10 -8
  15. package/lib/command/gherkin/steps.js +1 -1
  16. package/lib/command/info.js +1 -3
  17. package/lib/command/init.js +8 -12
  18. package/lib/command/interactive.js +1 -1
  19. package/lib/command/list.js +1 -1
  20. package/lib/command/run-multiple.js +12 -35
  21. package/lib/command/run-workers.js +10 -10
  22. package/lib/command/utils.js +5 -6
  23. package/lib/command/workers/runTests.js +14 -17
  24. package/lib/container.js +327 -237
  25. package/lib/data/context.js +10 -13
  26. package/lib/data/dataScenarioConfig.js +8 -8
  27. package/lib/data/dataTableArgument.js +6 -6
  28. package/lib/data/table.js +5 -11
  29. package/lib/els.js +177 -0
  30. package/lib/event.js +1 -0
  31. package/lib/heal.js +78 -80
  32. package/lib/helper/ApiDataFactory.js +3 -6
  33. package/lib/helper/Appium.js +15 -30
  34. package/lib/helper/FileSystem.js +3 -3
  35. package/lib/helper/GraphQLDataFactory.js +3 -3
  36. package/lib/helper/JSONResponse.js +57 -37
  37. package/lib/helper/Nightmare.js +35 -53
  38. package/lib/helper/Playwright.js +189 -251
  39. package/lib/helper/Protractor.js +54 -77
  40. package/lib/helper/Puppeteer.js +134 -232
  41. package/lib/helper/REST.js +5 -17
  42. package/lib/helper/TestCafe.js +21 -44
  43. package/lib/helper/WebDriver.js +103 -162
  44. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  45. package/lib/listener/artifacts.js +2 -2
  46. package/lib/listener/emptyRun.js +58 -0
  47. package/lib/listener/exit.js +4 -4
  48. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  49. package/lib/listener/{timeout.js → globalTimeout.js} +9 -8
  50. package/lib/listener/helpers.js +15 -15
  51. package/lib/listener/mocha.js +1 -1
  52. package/lib/listener/steps.js +17 -12
  53. package/lib/listener/store.js +12 -0
  54. package/lib/mocha/asyncWrapper.js +204 -0
  55. package/lib/{interfaces → mocha}/bdd.js +3 -3
  56. package/lib/mocha/cli.js +257 -0
  57. package/lib/mocha/factory.js +104 -0
  58. package/lib/{interfaces → mocha}/featureConfig.js +11 -12
  59. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  60. package/lib/mocha/hooks.js +83 -0
  61. package/lib/mocha/index.js +12 -0
  62. package/lib/mocha/inject.js +24 -0
  63. package/lib/{interfaces → mocha}/scenarioConfig.js +10 -6
  64. package/lib/mocha/suite.js +55 -0
  65. package/lib/mocha/test.js +60 -0
  66. package/lib/mocha/types.d.ts +31 -0
  67. package/lib/mocha/ui.js +219 -0
  68. package/lib/output.js +28 -10
  69. package/lib/pause.js +159 -135
  70. package/lib/plugin/autoDelay.js +4 -4
  71. package/lib/plugin/autoLogin.js +6 -7
  72. package/lib/plugin/commentStep.js +1 -1
  73. package/lib/plugin/coverage.js +10 -19
  74. package/lib/plugin/customLocator.js +3 -3
  75. package/lib/plugin/debugErrors.js +2 -2
  76. package/lib/plugin/eachElement.js +1 -1
  77. package/lib/plugin/fakerTransform.js +1 -1
  78. package/lib/plugin/heal.js +6 -9
  79. package/lib/plugin/retryFailedStep.js +4 -4
  80. package/lib/plugin/retryTo.js +2 -2
  81. package/lib/plugin/screenshotOnFail.js +9 -36
  82. package/lib/plugin/selenoid.js +15 -35
  83. package/lib/plugin/stepByStepReport.js +51 -13
  84. package/lib/plugin/stepTimeout.js +4 -11
  85. package/lib/plugin/subtitles.js +4 -4
  86. package/lib/plugin/tryTo.js +1 -1
  87. package/lib/plugin/wdio.js +8 -10
  88. package/lib/recorder.js +142 -121
  89. package/lib/secret.js +1 -1
  90. package/lib/step.js +160 -144
  91. package/lib/store.js +6 -2
  92. package/lib/template/heal.js +2 -11
  93. package/lib/utils.js +224 -216
  94. package/lib/within.js +73 -55
  95. package/lib/workers.js +265 -261
  96. package/package.json +46 -47
  97. package/typings/index.d.ts +172 -184
  98. package/typings/promiseBasedTypes.d.ts +95 -516
  99. package/typings/types.d.ts +169 -587
  100. package/lib/cli.js +0 -256
  101. package/lib/helper/ExpectHelper.js +0 -391
  102. package/lib/helper/SoftExpectHelper.js +0 -381
  103. package/lib/mochaFactory.js +0 -113
  104. package/lib/scenario.js +0 -224
  105. package/lib/ui.js +0 -236
@@ -10,16 +10,7 @@ const { urlEquals, equals } = require('../assert/equal')
10
10
  const { debug } = require('../output')
11
11
  const { empty } = require('../assert/empty')
12
12
  const { truth } = require('../assert/truth')
13
- const {
14
- xpathLocator,
15
- fileExists,
16
- decodeUrl,
17
- chunkArray,
18
- convertCssPropertiesToCamelCase,
19
- screenshotOutputFolder,
20
- getNormalizedKeyAttributeValue,
21
- modifierKeys,
22
- } = require('../utils')
13
+ const { xpathLocator, fileExists, decodeUrl, chunkArray, convertCssPropertiesToCamelCase, screenshotOutputFolder, getNormalizedKeyAttributeValue, modifierKeys } = require('../utils')
23
14
  const { isColorProperty, convertColorToRGBA } = require('../colorUtils')
24
15
  const ElementNotFound = require('./errors/ElementNotFound')
25
16
  const ConnectionRefused = require('./errors/ConnectionRefused')
@@ -27,19 +18,8 @@ const Locator = require('../locator')
27
18
  const { highlightElement } = require('./scripts/highlightElement')
28
19
  const { focusElement } = require('./scripts/focusElement')
29
20
  const { blurElement } = require('./scripts/blurElement')
30
- const {
31
- dontSeeElementError,
32
- seeElementError,
33
- seeElementInDOMError,
34
- dontSeeElementInDOMError,
35
- } = require('./errors/ElementAssertion')
36
- const {
37
- dontSeeTraffic,
38
- seeTraffic,
39
- grabRecordedNetworkTraffics,
40
- stopRecordingTraffic,
41
- flushNetworkTraffics,
42
- } = require('./network/actions')
21
+ const { dontSeeElementError, seeElementError, seeElementInDOMError, dontSeeElementInDOMError } = require('./errors/ElementAssertion')
22
+ const { dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics } = require('./network/actions')
43
23
 
44
24
  const SHADOW = 'shadow'
45
25
  const webRoot = 'body'
@@ -595,10 +575,7 @@ class WebDriver extends Helper {
595
575
  }
596
576
 
597
577
  async _res(locator) {
598
- const res =
599
- this._isShadowLocator(locator) || this._isCustomLocator(locator)
600
- ? await this._locate(locator)
601
- : await this.$$(withStrictLocator(locator))
578
+ const res = this._isShadowLocator(locator) || this._isCustomLocator(locator) ? await this._locate(locator) : await this.$$(withStrictLocator(locator))
602
579
  return res
603
580
  }
604
581
 
@@ -631,7 +608,7 @@ class WebDriver extends Helper {
631
608
  this.$$ = this.browser.$$.bind(this.browser)
632
609
 
633
610
  if (this._isCustomLocatorStrategyDefined()) {
634
- Object.keys(this.customLocatorStrategies).forEach(async (customLocator) => {
611
+ Object.keys(this.customLocatorStrategies).forEach(async customLocator => {
635
612
  this.debugSection('Weddriver', `adding custom locator strategy: ${customLocator}`)
636
613
  const locatorFunction = this._lookupCustomLocator(customLocator)
637
614
  this.browser.addLocatorStrategy(customLocator, locatorFunction)
@@ -642,6 +619,7 @@ class WebDriver extends Helper {
642
619
  this.browser.capabilities.platformName = this.browser.capabilities.platformName.toLowerCase()
643
620
  }
644
621
 
622
+ this.browser.on('dialog', () => {})
645
623
  return this.browser
646
624
  }
647
625
 
@@ -667,7 +645,7 @@ class WebDriver extends Helper {
667
645
  this.isRunning = false
668
646
  return this.browser.deleteSession()
669
647
  }
670
- if (this.browser.isInsideFrame) await this.browser.switchToFrame(null)
648
+ if (this.browser.isInsideFrame) await this.browser.switchFrame(null)
671
649
 
672
650
  if (this.options.keepBrowserState) return
673
651
 
@@ -675,7 +653,7 @@ class WebDriver extends Helper {
675
653
  this.debugSection('Session', 'cleaning cookies and localStorage')
676
654
  await this.browser.deleteCookies()
677
655
  }
678
- await this.browser.execute('localStorage.clear();').catch((err) => {
656
+ await this.browser.execute('localStorage.clear();').catch(err => {
679
657
  if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
680
658
  })
681
659
  await this.closeOtherTabs()
@@ -705,17 +683,17 @@ class WebDriver extends Helper {
705
683
 
706
684
  return browser
707
685
  },
708
- stop: async (browser) => {
686
+ stop: async browser => {
709
687
  if (!browser) return
710
688
  return browser.deleteSession()
711
689
  },
712
- loadVars: async (browser) => {
690
+ loadVars: async browser => {
713
691
  if (this.context !== this.root) throw new Error("Can't start session inside within block")
714
692
  this.browser = browser
715
693
  this.$$ = this.browser.$$.bind(this.browser)
716
694
  this.sessionWindows[this.activeSessionName] = browser
717
695
  },
718
- restoreVars: async (session) => {
696
+ restoreVars: async session => {
719
697
  if (!session) {
720
698
  this.activeSessionName = ''
721
699
  }
@@ -757,7 +735,7 @@ class WebDriver extends Helper {
757
735
  this.browser.isInsideFrame = true
758
736
  if (Array.isArray(frame)) {
759
737
  // this.switchTo(null);
760
- await forEachAsync(frame, async (f) => this.switchTo(f))
738
+ await forEachAsync(frame, async f => this.switchTo(f))
761
739
  return
762
740
  }
763
741
  await this.switchTo(frame)
@@ -835,10 +813,7 @@ class WebDriver extends Helper {
835
813
  * @param {object} locator
836
814
  */
837
815
  async _smartWait(locator) {
838
- this.debugSection(
839
- `SmartWait (${this.options.smartWait}ms)`,
840
- `Locating ${JSON.stringify(locator)} in ${this.options.smartWait}`,
841
- )
816
+ this.debugSection(`SmartWait (${this.options.smartWait}ms)`, `Locating ${JSON.stringify(locator)} in ${this.options.smartWait}`)
842
817
  await this.defineTimeout({ implicit: this.options.smartWait })
843
818
  }
844
819
 
@@ -913,7 +888,7 @@ class WebDriver extends Helper {
913
888
  * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
914
889
  */
915
890
  async _locateCheckable(locator) {
916
- return findCheckable.call(this, locator, this.$$.bind(this)).then((res) => res)
891
+ return findCheckable.call(this, locator, this.$$.bind(this)).then(res => res)
917
892
  }
918
893
 
919
894
  /**
@@ -941,7 +916,7 @@ class WebDriver extends Helper {
941
916
  * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
942
917
  */
943
918
  async _locateFields(locator) {
944
- return findFields.call(this, locator).then((res) => res)
919
+ return findFields.call(this, locator).then(res => res)
945
920
  }
946
921
 
947
922
  /**
@@ -1025,7 +1000,7 @@ class WebDriver extends Helper {
1025
1000
  const elem = usingFirstElement(res)
1026
1001
  highlightActiveElement.call(this, elem)
1027
1002
 
1028
- return this.executeScript((el) => {
1003
+ return this.executeScript(el => {
1029
1004
  if (document.activeElement instanceof HTMLElement) {
1030
1005
  document.activeElement.blur()
1031
1006
  }
@@ -1097,7 +1072,7 @@ class WebDriver extends Helper {
1097
1072
  }
1098
1073
  const elem = usingFirstElement(res)
1099
1074
 
1100
- return this.executeScript((el) => {
1075
+ return this.executeScript(el => {
1101
1076
  if (document.activeElement instanceof HTMLElement) {
1102
1077
  document.activeElement.blur()
1103
1078
  }
@@ -1160,15 +1135,9 @@ class WebDriver extends Helper {
1160
1135
  }
1161
1136
 
1162
1137
  // select options by visible text
1163
- let els = await forEachAsync(option, async (opt) =>
1164
- this.browser.findElementsFromElement(
1165
- getElementId(elem),
1166
- 'xpath',
1167
- Locator.select.byVisibleText(xpathLocator.literal(opt)),
1168
- ),
1169
- )
1138
+ let els = await forEachAsync(option, async opt => this.browser.findElementsFromElement(getElementId(elem), 'xpath', Locator.select.byVisibleText(xpathLocator.literal(opt))))
1170
1139
 
1171
- const clickOptionFn = async (el) => {
1140
+ const clickOptionFn = async el => {
1172
1141
  if (el[0]) el = el[0]
1173
1142
  const elementId = getElementId(el)
1174
1143
  if (elementId) return this.browser.elementClick(elementId)
@@ -1178,19 +1147,9 @@ class WebDriver extends Helper {
1178
1147
  return forEachAsync(els, clickOptionFn)
1179
1148
  }
1180
1149
  // select options by value
1181
- els = await forEachAsync(option, async (opt) =>
1182
- this.browser.findElementsFromElement(
1183
- getElementId(elem),
1184
- 'xpath',
1185
- Locator.select.byValue(xpathLocator.literal(opt)),
1186
- ),
1187
- )
1150
+ els = await forEachAsync(option, async opt => this.browser.findElementsFromElement(getElementId(elem), 'xpath', Locator.select.byValue(xpathLocator.literal(opt))))
1188
1151
  if (els.length === 0) {
1189
- throw new ElementNotFound(
1190
- select,
1191
- `Option "${option}" in`,
1192
- 'was not found neither by a visible text nor by a value',
1193
- )
1152
+ throw new ElementNotFound(select, `Option "${option}" in`, 'was not found neither by a visible text nor by a value')
1194
1153
  }
1195
1154
  return forEachAsync(els, clickOptionFn)
1196
1155
  }
@@ -1217,9 +1176,7 @@ class WebDriver extends Helper {
1217
1176
  this.debugSection('File', 'Uploading file to remote server')
1218
1177
  file = await this.browser.uploadFile(file)
1219
1178
  } catch (err) {
1220
- throw new Error(
1221
- `File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`,
1222
- )
1179
+ throw new Error(`File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`)
1223
1180
  }
1224
1181
  }
1225
1182
 
@@ -1272,7 +1229,11 @@ class WebDriver extends Helper {
1272
1229
  */
1273
1230
  async grabTextFromAll(locator) {
1274
1231
  const res = await this._locate(locator, true)
1275
- const val = await forEachAsync(res, (el) => this.browser.getElementText(getElementId(el)))
1232
+ let val = []
1233
+ await forEachAsync(res, async el => {
1234
+ const text = await this.browser.getElementText(getElementId(el))
1235
+ val.push(text)
1236
+ })
1276
1237
  this.debugSection('GrabText', String(val))
1277
1238
  return val
1278
1239
  }
@@ -1297,7 +1258,7 @@ class WebDriver extends Helper {
1297
1258
  */
1298
1259
  async grabHTMLFromAll(locator) {
1299
1260
  const elems = await this._locate(locator, true)
1300
- const html = await forEachAsync(elems, (elem) => elem.getHTML(false))
1261
+ const html = await forEachAsync(elems, elem => elem.getHTML(false))
1301
1262
  this.debugSection('GrabHTML', String(html))
1302
1263
  return html
1303
1264
  }
@@ -1322,7 +1283,7 @@ class WebDriver extends Helper {
1322
1283
  */
1323
1284
  async grabValueFromAll(locator) {
1324
1285
  const res = await this._locate(locator, true)
1325
- const val = await forEachAsync(res, (el) => el.getValue())
1286
+ const val = await forEachAsync(res, el => el.getValue())
1326
1287
  this.debugSection('GrabValue', String(val))
1327
1288
 
1328
1289
  return val
@@ -1347,7 +1308,7 @@ class WebDriver extends Helper {
1347
1308
  */
1348
1309
  async grabCssPropertyFromAll(locator, cssProperty) {
1349
1310
  const res = await this._locate(locator, true)
1350
- const val = await forEachAsync(res, async (el) => this.browser.getElementCSSValue(getElementId(el), cssProperty))
1311
+ const val = await forEachAsync(res, async el => this.browser.getElementCSSValue(getElementId(el), cssProperty))
1351
1312
  this.debugSection('Grab', String(val))
1352
1313
  return val
1353
1314
  }
@@ -1371,7 +1332,7 @@ class WebDriver extends Helper {
1371
1332
  */
1372
1333
  async grabAttributeFromAll(locator, attr) {
1373
1334
  const res = await this._locate(locator, true)
1374
- const val = await forEachAsync(res, async (el) => el.getAttribute(attr))
1335
+ const val = await forEachAsync(res, async el => el.getAttribute(attr))
1375
1336
  this.debugSection('GrabAttribute', String(val))
1376
1337
  return val
1377
1338
  }
@@ -1488,7 +1449,7 @@ class WebDriver extends Helper {
1488
1449
  async seeElement(locator) {
1489
1450
  const res = await this._locate(locator, true)
1490
1451
  assertElementExists(res, locator)
1491
- const selected = await forEachAsync(res, async (el) => el.isDisplayed())
1452
+ const selected = await forEachAsync(res, async el => el.isDisplayed())
1492
1453
  try {
1493
1454
  return truth(`elements of ${new Locator(locator)}`, 'to be seen').assert(selected)
1494
1455
  } catch (e) {
@@ -1505,7 +1466,7 @@ class WebDriver extends Helper {
1505
1466
  if (!res || res.length === 0) {
1506
1467
  return truth(`elements of ${new Locator(locator)}`, 'to be seen').negate(false)
1507
1468
  }
1508
- const selected = await forEachAsync(res, async (el) => el.isDisplayed())
1469
+ const selected = await forEachAsync(res, async el => el.isDisplayed())
1509
1470
  try {
1510
1471
  return truth(`elements of ${new Locator(locator)}`, 'to be seen').negate(selected)
1511
1472
  } catch (e) {
@@ -1590,11 +1551,7 @@ class WebDriver extends Helper {
1590
1551
  */
1591
1552
  async seeNumberOfElements(locator, num) {
1592
1553
  const res = await this._locate(locator)
1593
- return assert.equal(
1594
- res.length,
1595
- num,
1596
- `expected number of elements (${new Locator(locator)}) is ${num}, but found ${res.length}`,
1597
- )
1554
+ return assert.equal(res.length, num, `expected number of elements (${new Locator(locator)}) is ${num}, but found ${res.length}`)
1598
1555
  }
1599
1556
 
1600
1557
  /**
@@ -1603,11 +1560,7 @@ class WebDriver extends Helper {
1603
1560
  */
1604
1561
  async seeNumberOfVisibleElements(locator, num) {
1605
1562
  const res = await this.grabNumberOfVisibleElements(locator)
1606
- return assert.equal(
1607
- res,
1608
- num,
1609
- `expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`,
1610
- )
1563
+ return assert.equal(res, num, `expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`)
1611
1564
  }
1612
1565
 
1613
1566
  /**
@@ -1632,19 +1585,16 @@ class WebDriver extends Helper {
1632
1585
  }
1633
1586
  }
1634
1587
 
1635
- const values = Object.keys(cssPropertiesCamelCase).map((key) => cssPropertiesCamelCase[key])
1588
+ const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key])
1636
1589
  if (!Array.isArray(props)) props = [props]
1637
1590
  let chunked = chunkArray(props, values.length)
1638
- chunked = chunked.filter((val) => {
1591
+ chunked = chunked.filter(val => {
1639
1592
  for (let i = 0; i < val.length; ++i) {
1640
- // eslint-disable-next-line eqeqeq
1641
1593
  if (val[i] != values[i]) return false
1642
1594
  }
1643
1595
  return true
1644
1596
  })
1645
- return equals(
1646
- `all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
1647
- ).assert(chunked.length, elemAmount)
1597
+ return equals(`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount)
1648
1598
  }
1649
1599
 
1650
1600
  /**
@@ -1655,28 +1605,24 @@ class WebDriver extends Helper {
1655
1605
  assertElementExists(res, locator)
1656
1606
  const elemAmount = res.length
1657
1607
 
1658
- let attrs = await forEachAsync(res, async (el) => {
1659
- return forEachAsync(Object.keys(attributes), async (attr) => el.getAttribute(attr))
1608
+ let attrs = await forEachAsync(res, async el => {
1609
+ return forEachAsync(Object.keys(attributes), async attr => el.getAttribute(attr))
1660
1610
  })
1661
1611
 
1662
- const values = Object.keys(attributes).map((key) => attributes[key])
1612
+ const values = Object.keys(attributes).map(key => attributes[key])
1663
1613
  if (!Array.isArray(attrs)) attrs = [attrs]
1664
1614
  let chunked = chunkArray(attrs, values.length)
1665
- chunked = chunked.filter((val) => {
1615
+ chunked = chunked.filter(val => {
1666
1616
  for (let i = 0; i < val.length; ++i) {
1667
1617
  const _actual = Number.isNaN(val[i]) || typeof values[i] === 'string' ? val[i] : Number.parseInt(val[i], 10)
1668
- const _expected =
1669
- Number.isNaN(values[i]) || typeof values[i] === 'string' ? values[i] : Number.parseInt(values[i], 10)
1618
+ const _expected = Number.isNaN(values[i]) || typeof values[i] === 'string' ? values[i] : Number.parseInt(values[i], 10)
1670
1619
  // the attribute could be a boolean
1671
1620
  if (typeof _actual === 'boolean') return _actual === _expected
1672
1621
  if (_actual !== _expected) return false
1673
1622
  }
1674
1623
  return true
1675
1624
  })
1676
- return assert.ok(
1677
- chunked.length === elemAmount,
1678
- `expected all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`,
1679
- )
1625
+ return assert.ok(chunked.length === elemAmount, `expected all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`)
1680
1626
  }
1681
1627
 
1682
1628
  /**
@@ -1685,9 +1631,9 @@ class WebDriver extends Helper {
1685
1631
  async grabNumberOfVisibleElements(locator) {
1686
1632
  const res = await this._locate(locator)
1687
1633
 
1688
- let selected = await forEachAsync(res, async (el) => el.isDisplayed())
1634
+ let selected = await forEachAsync(res, async el => el.isDisplayed())
1689
1635
  if (!Array.isArray(selected)) selected = [selected]
1690
- selected = selected.filter((val) => val === true)
1636
+ selected = selected.filter(val => val === true)
1691
1637
  return selected.length
1692
1638
  }
1693
1639
 
@@ -1860,7 +1806,7 @@ class WebDriver extends Helper {
1860
1806
  width: document.body.scrollWidth,
1861
1807
  }
1862
1808
  })
1863
- .then((res) => res)
1809
+ .then(res => res)
1864
1810
 
1865
1811
  if (height < 100) height = 500 // errors for very small height
1866
1812
 
@@ -1928,7 +1874,7 @@ class WebDriver extends Helper {
1928
1874
 
1929
1875
  return promiseRetry(
1930
1876
  async (retry, number) => {
1931
- const _grabCookie = async (name) => {
1877
+ const _grabCookie = async name => {
1932
1878
  const cookie = await this.browser.getCookies([name])
1933
1879
  if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`)
1934
1880
  }
@@ -1952,11 +1898,10 @@ class WebDriver extends Helper {
1952
1898
  * libraries](http://jster.net/category/windows-modals-popups).
1953
1899
  */
1954
1900
  async acceptPopup() {
1955
- return this.browser.getAlertText().then((res) => {
1956
- if (res !== null) {
1957
- return this.browser.acceptAlert()
1958
- }
1959
- })
1901
+ const text = await this.browser.getAlertText()
1902
+ if (text) {
1903
+ return await this.browser.acceptAlert()
1904
+ }
1960
1905
  }
1961
1906
 
1962
1907
  /**
@@ -1964,11 +1909,10 @@ class WebDriver extends Helper {
1964
1909
  *
1965
1910
  */
1966
1911
  async cancelPopup() {
1967
- return this.browser.getAlertText().then((res) => {
1968
- if (res !== null) {
1969
- return this.browser.dismissAlert()
1970
- }
1971
- })
1912
+ const text = await this.browser.getAlertText()
1913
+ if (text) {
1914
+ return await this.browser.dismissAlert()
1915
+ }
1972
1916
  }
1973
1917
 
1974
1918
  /**
@@ -1978,7 +1922,7 @@ class WebDriver extends Helper {
1978
1922
  * @param {string} text value to check.
1979
1923
  */
1980
1924
  async seeInPopup(text) {
1981
- return this.browser.getAlertText().then((res) => {
1925
+ return await this.browser.getAlertText().then(res => {
1982
1926
  if (res === null) {
1983
1927
  throw new Error('Popup is not opened')
1984
1928
  }
@@ -2259,9 +2203,9 @@ class WebDriver extends Helper {
2259
2203
  async closeOtherTabs() {
2260
2204
  const handles = await this.browser.getWindowHandles()
2261
2205
  const currentHandle = await this.browser.getWindowHandle()
2262
- const otherHandles = handles.filter((handle) => handle !== currentHandle)
2206
+ const otherHandles = handles.filter(handle => handle !== currentHandle)
2263
2207
 
2264
- await forEachAsync(otherHandles, async (handle) => {
2208
+ await forEachAsync(otherHandles, async handle => {
2265
2209
  await this.browser.switchToWindow(handle)
2266
2210
  await this.browser.closeWindow()
2267
2211
  })
@@ -2272,7 +2216,7 @@ class WebDriver extends Helper {
2272
2216
  * {{> wait }}
2273
2217
  */
2274
2218
  async wait(sec) {
2275
- return new Promise((resolve) => {
2219
+ return new Promise(resolve => {
2276
2220
  setTimeout(resolve, sec * 1000)
2277
2221
  })
2278
2222
  }
@@ -2289,9 +2233,9 @@ class WebDriver extends Helper {
2289
2233
  if (!res || res.length === 0) {
2290
2234
  return false
2291
2235
  }
2292
- const selected = await forEachAsync(res, async (el) => this.browser.isElementEnabled(getElementId(el)))
2236
+ const selected = await forEachAsync(res, async el => this.browser.isElementEnabled(getElementId(el)))
2293
2237
  if (Array.isArray(selected)) {
2294
- return selected.filter((val) => val === true).length > 0
2238
+ return selected.filter(val => val === true).length > 0
2295
2239
  }
2296
2240
  return selected
2297
2241
  },
@@ -2346,14 +2290,14 @@ class WebDriver extends Helper {
2346
2290
  return client
2347
2291
  .waitUntil(
2348
2292
  function () {
2349
- return this.getUrl().then((res) => {
2293
+ return this.getUrl().then(res => {
2350
2294
  currUrl = decodeUrl(res)
2351
2295
  return currUrl.indexOf(urlPart) > -1
2352
2296
  })
2353
2297
  },
2354
2298
  { timeout: aSec * 1000 },
2355
2299
  )
2356
- .catch((e) => {
2300
+ .catch(e => {
2357
2301
  if (e.message.indexOf('timeout')) {
2358
2302
  throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
2359
2303
  }
@@ -2373,12 +2317,12 @@ class WebDriver extends Helper {
2373
2317
  let currUrl = ''
2374
2318
  return this.browser
2375
2319
  .waitUntil(function () {
2376
- return this.getUrl().then((res) => {
2320
+ return this.getUrl().then(res => {
2377
2321
  currUrl = decodeUrl(res)
2378
2322
  return currUrl === urlPart
2379
2323
  })
2380
2324
  }, aSec * 1000)
2381
- .catch((e) => {
2325
+ .catch(e => {
2382
2326
  if (e.message.indexOf('timeout')) {
2383
2327
  throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
2384
2328
  }
@@ -2398,9 +2342,9 @@ class WebDriver extends Helper {
2398
2342
  async () => {
2399
2343
  const res = await this.$$(withStrictLocator.call(this, _context))
2400
2344
  if (!res || res.length === 0) return false
2401
- const selected = await forEachAsync(res, async (el) => this.browser.getElementText(getElementId(el)))
2345
+ const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)))
2402
2346
  if (Array.isArray(selected)) {
2403
- return selected.filter((part) => part.indexOf(text) >= 0).length > 0
2347
+ return selected.filter(part => part.indexOf(text) >= 0).length > 0
2404
2348
  }
2405
2349
  return selected.indexOf(text) >= 0
2406
2350
  },
@@ -2422,9 +2366,9 @@ class WebDriver extends Helper {
2422
2366
  async () => {
2423
2367
  const res = await findFields.call(this, field)
2424
2368
  if (!res || res.length === 0) return false
2425
- const selected = await forEachAsync(res, async (el) => el.getValue())
2369
+ const selected = await forEachAsync(res, async el => el.getValue())
2426
2370
  if (Array.isArray(selected)) {
2427
- return selected.filter((part) => part.indexOf(value) >= 0).length > 0
2371
+ return selected.filter(part => part.indexOf(value) >= 0).length > 0
2428
2372
  }
2429
2373
  return selected.indexOf(value) >= 0
2430
2374
  },
@@ -2446,9 +2390,9 @@ class WebDriver extends Helper {
2446
2390
  async () => {
2447
2391
  const res = await this._res(locator)
2448
2392
  if (!res || res.length === 0) return false
2449
- const selected = await forEachAsync(res, async (el) => el.isDisplayed())
2393
+ const selected = await forEachAsync(res, async el => el.isDisplayed())
2450
2394
  if (Array.isArray(selected)) {
2451
- return selected.filter((val) => val === true).length > 0
2395
+ return selected.filter(val => val === true).length > 0
2452
2396
  }
2453
2397
  return selected
2454
2398
  },
@@ -2469,10 +2413,10 @@ class WebDriver extends Helper {
2469
2413
  async () => {
2470
2414
  const res = await this._res(locator)
2471
2415
  if (!res || res.length === 0) return false
2472
- let selected = await forEachAsync(res, async (el) => el.isDisplayed())
2416
+ let selected = await forEachAsync(res, async el => el.isDisplayed())
2473
2417
 
2474
2418
  if (!Array.isArray(selected)) selected = [selected]
2475
- selected = selected.filter((val) => val === true)
2419
+ selected = selected.filter(val => val === true)
2476
2420
  return selected.length === num
2477
2421
  },
2478
2422
  {
@@ -2492,7 +2436,7 @@ class WebDriver extends Helper {
2492
2436
  async () => {
2493
2437
  const res = await this._res(locator)
2494
2438
  if (!res || res.length === 0) return true
2495
- const selected = await forEachAsync(res, async (el) => el.isDisplayed())
2439
+ const selected = await forEachAsync(res, async el => el.isDisplayed())
2496
2440
  return !selected.length
2497
2441
  },
2498
2442
  { timeout: aSec * 1000, timeoutMsg: `element (${new Locator(locator)}) still visible after ${aSec} sec` },
@@ -2568,17 +2512,14 @@ class WebDriver extends Helper {
2568
2512
  */
2569
2513
  async switchTo(locator) {
2570
2514
  this.browser.isInsideFrame = true
2571
- if (Number.isInteger(locator)) {
2572
- return this.browser.switchToFrame(locator)
2573
- }
2574
2515
  if (!locator) {
2575
- return this.browser.switchToFrame(null)
2516
+ return this.browser.switchFrame(null)
2576
2517
  }
2577
2518
 
2578
2519
  let res = await this._locate(locator, true)
2579
2520
  assertElementExists(res, locator)
2580
2521
  res = usingFirstElement(res)
2581
- return this.browser.switchToFrame(res)
2522
+ return this.browser.switchFrame(res)
2582
2523
  }
2583
2524
 
2584
2525
  /**
@@ -2591,7 +2532,7 @@ class WebDriver extends Helper {
2591
2532
 
2592
2533
  await this.browser.waitUntil(
2593
2534
  async () => {
2594
- await this.browser.getWindowHandles().then((handles) => {
2535
+ await this.browser.getWindowHandles().then(handles => {
2595
2536
  if (handles.indexOf(current) + num + 1 <= handles.length) {
2596
2537
  target = handles[handles.indexOf(current) + num]
2597
2538
  }
@@ -2613,7 +2554,7 @@ class WebDriver extends Helper {
2613
2554
 
2614
2555
  await this.browser.waitUntil(
2615
2556
  async () => {
2616
- await this.browser.getWindowHandles().then((handles) => {
2557
+ await this.browser.getWindowHandles().then(handles => {
2617
2558
  if (handles.indexOf(current) - num > -1) {
2618
2559
  target = handles[handles.indexOf(current) - num]
2619
2560
  }
@@ -2683,10 +2624,7 @@ class WebDriver extends Helper {
2683
2624
  return client.execute(function () {
2684
2625
  const body = document.body
2685
2626
  const html = document.documentElement
2686
- window.scrollTo(
2687
- 0,
2688
- Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
2689
- )
2627
+ window.scrollTo(0, Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight))
2690
2628
  })
2691
2629
  }
2692
2630
 
@@ -2760,10 +2698,10 @@ async function proceedSee(assertType, text, context, strict = false) {
2760
2698
  const smartWaitEnabled = assertType === 'assert'
2761
2699
  const res = await this._locate(withStrictLocator(context), smartWaitEnabled)
2762
2700
  assertElementExists(res, context)
2763
- const selected = await forEachAsync(res, async (el) => this.browser.getElementText(getElementId(el)))
2701
+ const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)))
2764
2702
  if (strict) {
2765
2703
  if (Array.isArray(selected) && selected.length !== 0) {
2766
- return selected.map((elText) => equals(description)[assertType](text, elText))
2704
+ return selected.map(elText => equals(description)[assertType](text, elText))
2767
2705
  }
2768
2706
  return equals(description)[assertType](text, selected)
2769
2707
  }
@@ -2791,7 +2729,7 @@ async function forEachAsync(array, callback, options = { expandArrayResults: tru
2791
2729
  const res = await callback(inputArray[index], index, inputArray)
2792
2730
 
2793
2731
  if (Array.isArray(res) && expandArrayResults) {
2794
- res.forEach((val) => values.push(val))
2732
+ res.forEach(val => values.push(val))
2795
2733
  } else if (res) {
2796
2734
  values.push(res)
2797
2735
  }
@@ -2877,11 +2815,11 @@ async function proceedSeeField(assertType, field, value) {
2877
2815
  const elem = usingFirstElement(res)
2878
2816
  const elemId = getElementId(elem)
2879
2817
 
2880
- const proceedMultiple = async (fields) => {
2818
+ const proceedMultiple = async fields => {
2881
2819
  const fieldResults = toArray(
2882
- await forEachAsync(fields, async (el) => {
2820
+ await forEachAsync(fields, async el => {
2883
2821
  const elementId = getElementId(el)
2884
- return this.browser.isW3C ? el.getValue() : this.browser.getElementAttribute(elementId, 'value')
2822
+ return this.browser.getElementAttribute(elementId, 'value')
2885
2823
  }),
2886
2824
  )
2887
2825
 
@@ -2890,27 +2828,24 @@ async function proceedSeeField(assertType, field, value) {
2890
2828
  } else {
2891
2829
  // Assert that results were found so the forEach assert does not silently pass
2892
2830
  equals(`no. of items matching > 0: ${field}`)[assertType](true, !!fieldResults.length)
2893
- fieldResults.forEach((val) => stringIncludes(`fields by ${field}`)[assertType](value, val))
2831
+ fieldResults.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val))
2894
2832
  }
2895
2833
  }
2896
2834
 
2897
- const proceedSingle = (el) =>
2898
- el.getValue().then((res) => {
2835
+ const proceedSingle = el =>
2836
+ el.getValue().then(res => {
2899
2837
  if (res === null) {
2900
2838
  throw new Error(`Element ${el.selector} has no value attribute`)
2901
2839
  }
2902
2840
  stringIncludes(`fields by ${field}`)[assertType](value, res)
2903
2841
  })
2904
2842
 
2905
- const filterBySelected = async (elements) =>
2906
- filterAsync(elements, async (el) => this.browser.isElementSelected(getElementId(el)))
2843
+ const filterBySelected = async elements => filterAsync(elements, async el => this.browser.isElementSelected(getElementId(el)))
2907
2844
 
2908
2845
  const filterSelectedByValue = async (elements, value) => {
2909
- return filterAsync(elements, async (el) => {
2846
+ return filterAsync(elements, async el => {
2910
2847
  const elementId = getElementId(el)
2911
- const currentValue = this.browser.isW3C
2912
- ? await el.getValue()
2913
- : await this.browser.getElementAttribute(elementId, 'value')
2848
+ const currentValue = await this.browser.getElementAttribute(elementId, 'value')
2914
2849
  const isSelected = await this.browser.isElementSelected(elementId)
2915
2850
  return currentValue === value && isSelected
2916
2851
  })
@@ -2918,7 +2853,13 @@ async function proceedSeeField(assertType, field, value) {
2918
2853
 
2919
2854
  const tag = await elem.getTagName()
2920
2855
  if (tag === 'select') {
2921
- const subOptions = await this.browser.findElementsFromElement(elemId, 'css', 'option')
2856
+ let subOptions
2857
+
2858
+ try {
2859
+ subOptions = await this.browser.findElementsFromElement(elemId, 'css', 'option')
2860
+ } catch (e) {
2861
+ subOptions = await this.browser.findElementsFromElement(elemId, 'xpath', 'option')
2862
+ }
2922
2863
 
2923
2864
  if (value === '') {
2924
2865
  // Don't filter by value
@@ -2959,7 +2900,7 @@ async function proceedSeeCheckbox(assertType, field) {
2959
2900
  const res = await findFields.call(this, field)
2960
2901
  assertElementExists(res, field, 'Field')
2961
2902
 
2962
- const selected = await forEachAsync(res, async (el) => this.browser.isElementSelected(getElementId(el)))
2903
+ const selected = await forEachAsync(res, async el => this.browser.isElementSelected(getElementId(el)))
2963
2904
  return truth(`checkable field "${field}"`, 'to be checked')[assertType](selected)
2964
2905
  }
2965
2906
 
@@ -3160,7 +3101,7 @@ function getNormalizedKey(key) {
3160
3101
  return convertKeyToRawKey(normalizedKey)
3161
3102
  }
3162
3103
 
3163
- const unicodeModifierKeys = modifierKeys.map((k) => convertKeyToRawKey(k))
3104
+ const unicodeModifierKeys = modifierKeys.map(k => convertKeyToRawKey(k))
3164
3105
  function isModifierKey(key) {
3165
3106
  return unicodeModifierKeys.includes(key)
3166
3107
  }
@@ -3173,9 +3114,9 @@ function highlightActiveElement(element) {
3173
3114
 
3174
3115
  function prepareLocateFn(context) {
3175
3116
  if (!context) return this._locate.bind(this)
3176
- return (l) => {
3117
+ return l => {
3177
3118
  l = new Locator(l, 'css')
3178
- return this._locate(context, true).then(async (res) => {
3119
+ return this._locate(context, true).then(async res => {
3179
3120
  assertElementExists(res, context, 'Context element')
3180
3121
  if (l.react) {
3181
3122
  return res[0].react$$(l.react, l.props || undefined)