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.
Files changed (150) hide show
  1. package/README.md +134 -119
  2. package/bin/codecept.js +12 -2
  3. package/bin/test-server.js +53 -0
  4. package/docs/webapi/clearCookie.mustache +1 -1
  5. package/lib/actor.js +66 -102
  6. package/lib/ai.js +130 -121
  7. package/lib/assert/empty.js +3 -5
  8. package/lib/assert/equal.js +4 -7
  9. package/lib/assert/include.js +4 -6
  10. package/lib/assert/throws.js +2 -4
  11. package/lib/assert/truth.js +2 -2
  12. package/lib/codecept.js +139 -87
  13. package/lib/command/check.js +201 -0
  14. package/lib/command/configMigrate.js +2 -4
  15. package/lib/command/definitions.js +8 -26
  16. package/lib/command/generate.js +10 -14
  17. package/lib/command/gherkin/snippets.js +75 -73
  18. package/lib/command/gherkin/steps.js +1 -1
  19. package/lib/command/info.js +42 -8
  20. package/lib/command/init.js +13 -12
  21. package/lib/command/interactive.js +10 -2
  22. package/lib/command/list.js +1 -1
  23. package/lib/command/run-multiple/chunk.js +48 -45
  24. package/lib/command/run-multiple.js +12 -35
  25. package/lib/command/run-workers.js +21 -58
  26. package/lib/command/utils.js +5 -6
  27. package/lib/command/workers/runTests.js +262 -220
  28. package/lib/container.js +386 -238
  29. package/lib/data/context.js +10 -13
  30. package/lib/data/dataScenarioConfig.js +8 -8
  31. package/lib/data/dataTableArgument.js +6 -6
  32. package/lib/data/table.js +5 -11
  33. package/lib/effects.js +223 -0
  34. package/lib/element/WebElement.js +327 -0
  35. package/lib/els.js +158 -0
  36. package/lib/event.js +21 -17
  37. package/lib/heal.js +88 -80
  38. package/lib/helper/AI.js +2 -1
  39. package/lib/helper/ApiDataFactory.js +3 -6
  40. package/lib/helper/Appium.js +47 -51
  41. package/lib/helper/FileSystem.js +3 -3
  42. package/lib/helper/GraphQLDataFactory.js +3 -3
  43. package/lib/helper/JSONResponse.js +75 -37
  44. package/lib/helper/Mochawesome.js +31 -9
  45. package/lib/helper/Nightmare.js +35 -53
  46. package/lib/helper/Playwright.js +262 -267
  47. package/lib/helper/Protractor.js +54 -77
  48. package/lib/helper/Puppeteer.js +246 -260
  49. package/lib/helper/REST.js +5 -17
  50. package/lib/helper/TestCafe.js +21 -44
  51. package/lib/helper/WebDriver.js +151 -170
  52. package/lib/helper/extras/Popup.js +22 -22
  53. package/lib/helper/testcafe/testcafe-utils.js +26 -27
  54. package/lib/listener/emptyRun.js +55 -0
  55. package/lib/listener/exit.js +7 -10
  56. package/lib/listener/{retry.js → globalRetry.js} +5 -5
  57. package/lib/listener/globalTimeout.js +165 -0
  58. package/lib/listener/helpers.js +15 -15
  59. package/lib/listener/mocha.js +1 -1
  60. package/lib/listener/result.js +12 -0
  61. package/lib/listener/retryEnhancer.js +85 -0
  62. package/lib/listener/steps.js +32 -18
  63. package/lib/listener/store.js +20 -0
  64. package/lib/mocha/asyncWrapper.js +231 -0
  65. package/lib/{interfaces → mocha}/bdd.js +3 -3
  66. package/lib/mocha/cli.js +308 -0
  67. package/lib/mocha/factory.js +104 -0
  68. package/lib/{interfaces → mocha}/featureConfig.js +32 -12
  69. package/lib/{interfaces → mocha}/gherkin.js +26 -28
  70. package/lib/mocha/hooks.js +112 -0
  71. package/lib/mocha/index.js +12 -0
  72. package/lib/mocha/inject.js +29 -0
  73. package/lib/{interfaces → mocha}/scenarioConfig.js +31 -7
  74. package/lib/mocha/suite.js +82 -0
  75. package/lib/mocha/test.js +181 -0
  76. package/lib/mocha/types.d.ts +42 -0
  77. package/lib/mocha/ui.js +232 -0
  78. package/lib/output.js +82 -62
  79. package/lib/pause.js +160 -138
  80. package/lib/plugin/analyze.js +396 -0
  81. package/lib/plugin/auth.js +435 -0
  82. package/lib/plugin/autoDelay.js +8 -8
  83. package/lib/plugin/autoLogin.js +3 -338
  84. package/lib/plugin/commentStep.js +6 -1
  85. package/lib/plugin/coverage.js +10 -19
  86. package/lib/plugin/customLocator.js +3 -3
  87. package/lib/plugin/customReporter.js +52 -0
  88. package/lib/plugin/eachElement.js +1 -1
  89. package/lib/plugin/fakerTransform.js +1 -1
  90. package/lib/plugin/heal.js +36 -9
  91. package/lib/plugin/htmlReporter.js +1947 -0
  92. package/lib/plugin/pageInfo.js +140 -0
  93. package/lib/plugin/retryFailedStep.js +17 -18
  94. package/lib/plugin/retryTo.js +2 -113
  95. package/lib/plugin/screenshotOnFail.js +17 -58
  96. package/lib/plugin/selenoid.js +15 -35
  97. package/lib/plugin/standardActingHelpers.js +4 -1
  98. package/lib/plugin/stepByStepReport.js +56 -17
  99. package/lib/plugin/stepTimeout.js +5 -12
  100. package/lib/plugin/subtitles.js +4 -4
  101. package/lib/plugin/tryTo.js +3 -102
  102. package/lib/plugin/wdio.js +8 -10
  103. package/lib/recorder.js +155 -124
  104. package/lib/rerun.js +43 -42
  105. package/lib/result.js +161 -0
  106. package/lib/secret.js +1 -1
  107. package/lib/step/base.js +239 -0
  108. package/lib/step/comment.js +10 -0
  109. package/lib/step/config.js +50 -0
  110. package/lib/step/func.js +46 -0
  111. package/lib/step/helper.js +50 -0
  112. package/lib/step/meta.js +99 -0
  113. package/lib/step/record.js +74 -0
  114. package/lib/step/retry.js +11 -0
  115. package/lib/step/section.js +55 -0
  116. package/lib/step.js +21 -332
  117. package/lib/steps.js +50 -0
  118. package/lib/store.js +37 -5
  119. package/lib/template/heal.js +2 -11
  120. package/lib/test-server.js +323 -0
  121. package/lib/timeout.js +66 -0
  122. package/lib/utils.js +351 -218
  123. package/lib/within.js +75 -55
  124. package/lib/workerStorage.js +2 -1
  125. package/lib/workers.js +386 -276
  126. package/package.json +76 -70
  127. package/translations/de-DE.js +4 -3
  128. package/translations/fr-FR.js +4 -3
  129. package/translations/index.js +1 -0
  130. package/translations/it-IT.js +4 -3
  131. package/translations/ja-JP.js +4 -3
  132. package/translations/nl-NL.js +76 -0
  133. package/translations/pl-PL.js +4 -3
  134. package/translations/pt-BR.js +4 -3
  135. package/translations/ru-RU.js +4 -3
  136. package/translations/utils.js +9 -0
  137. package/translations/zh-CN.js +4 -3
  138. package/translations/zh-TW.js +4 -3
  139. package/typings/index.d.ts +188 -186
  140. package/typings/promiseBasedTypes.d.ts +18 -705
  141. package/typings/types.d.ts +301 -804
  142. package/lib/cli.js +0 -256
  143. package/lib/helper/ExpectHelper.js +0 -391
  144. package/lib/helper/SoftExpectHelper.js +0 -381
  145. package/lib/listener/artifacts.js +0 -19
  146. package/lib/listener/timeout.js +0 -109
  147. package/lib/mochaFactory.js +0 -113
  148. package/lib/plugin/debugErrors.js +0 -67
  149. package/lib/scenario.js +0 -224
  150. package/lib/ui.js +0 -236
@@ -7,19 +7,11 @@ const Helper = require('@codeceptjs/helper')
7
7
  const promiseRetry = require('promise-retry')
8
8
  const stringIncludes = require('../assert/include').includes
9
9
  const { urlEquals, equals } = require('../assert/equal')
10
+ const store = require('../store')
10
11
  const { debug } = require('../output')
11
12
  const { empty } = require('../assert/empty')
12
13
  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')
14
+ const { xpathLocator, fileExists, decodeUrl, chunkArray, convertCssPropertiesToCamelCase, screenshotOutputFolder, getNormalizedKeyAttributeValue, modifierKeys } = require('../utils')
23
15
  const { isColorProperty, convertColorToRGBA } = require('../colorUtils')
24
16
  const ElementNotFound = require('./errors/ElementNotFound')
25
17
  const ConnectionRefused = require('./errors/ConnectionRefused')
@@ -27,22 +19,13 @@ const Locator = require('../locator')
27
19
  const { highlightElement } = require('./scripts/highlightElement')
28
20
  const { focusElement } = require('./scripts/focusElement')
29
21
  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')
22
+ const { dontSeeElementError, seeElementError, seeElementInDOMError, dontSeeElementInDOMError } = require('./errors/ElementAssertion')
23
+ const { dontSeeTraffic, seeTraffic, grabRecordedNetworkTraffics, stopRecordingTraffic, flushNetworkTraffics } = require('./network/actions')
24
+ const WebElement = require('../element/WebElement')
43
25
 
44
26
  const SHADOW = 'shadow'
45
27
  const webRoot = 'body'
28
+ let browserLogs = []
46
29
 
47
30
  /**
48
31
  * ## Configuration
@@ -53,6 +36,7 @@ const webRoot = 'body'
53
36
  * @type {object}
54
37
  * @prop {string} url - base url of website to be tested.
55
38
  * @prop {string} browser - Browser in which to perform testing.
39
+ * @prop {boolean} [bidiProtocol=false] - WebDriver Bidi Protocol. Default: false. More info: https://webdriver.io/docs/api/webdriverBidi/
56
40
  * @prop {string} [basicAuth] - (optional) the basic authentication to pass to base url. Example: {username: 'username', password: 'password'}
57
41
  * @prop {string} [host=localhost] - WebDriver host to connect.
58
42
  * @prop {number} [port=4444] - WebDriver port to connect.
@@ -506,6 +490,10 @@ class WebDriver extends Helper {
506
490
  config.capabilities = config.desiredCapabilities
507
491
  }
508
492
  config.capabilities.browserName = config.browser || config.capabilities.browserName
493
+
494
+ // WebDriver Bidi Protocol. Default: false
495
+ config.capabilities.webSocketUrl = config.bidiProtocol ?? config.capabilities.webSocketUrl ?? true
496
+
509
497
  config.capabilities.browserVersion = config.browserVersion || config.capabilities.browserVersion
510
498
  if (config.capabilities.chromeOptions) {
511
499
  config.capabilities['goog:chromeOptions'] = config.capabilities.chromeOptions
@@ -595,10 +583,7 @@ class WebDriver extends Helper {
595
583
  }
596
584
 
597
585
  async _res(locator) {
598
- const res =
599
- this._isShadowLocator(locator) || this._isCustomLocator(locator)
600
- ? await this._locate(locator)
601
- : await this.$$(withStrictLocator(locator))
586
+ const res = this._isShadowLocator(locator) || this._isCustomLocator(locator) ? await this._locate(locator) : await this.$$(withStrictLocator(locator))
602
587
  return res
603
588
  }
604
589
 
@@ -631,7 +616,7 @@ class WebDriver extends Helper {
631
616
  this.$$ = this.browser.$$.bind(this.browser)
632
617
 
633
618
  if (this._isCustomLocatorStrategyDefined()) {
634
- Object.keys(this.customLocatorStrategies).forEach(async (customLocator) => {
619
+ Object.keys(this.customLocatorStrategies).forEach(async customLocator => {
635
620
  this.debugSection('Weddriver', `adding custom locator strategy: ${customLocator}`)
636
621
  const locatorFunction = this._lookupCustomLocator(customLocator)
637
622
  this.browser.addLocatorStrategy(customLocator, locatorFunction)
@@ -642,6 +627,11 @@ class WebDriver extends Helper {
642
627
  this.browser.capabilities.platformName = this.browser.capabilities.platformName.toLowerCase()
643
628
  }
644
629
 
630
+ this.browser.on('dialog', () => {})
631
+
632
+ await this.browser.sessionSubscribe({ events: ['log.entryAdded'] })
633
+ this.browser.on('log.entryAdded', logEvents)
634
+
645
635
  return this.browser
646
636
  }
647
637
 
@@ -667,7 +657,7 @@ class WebDriver extends Helper {
667
657
  this.isRunning = false
668
658
  return this.browser.deleteSession()
669
659
  }
670
- if (this.browser.isInsideFrame) await this.browser.switchToFrame(null)
660
+ if (this.browser.isInsideFrame) await this.browser.switchFrame(null)
671
661
 
672
662
  if (this.options.keepBrowserState) return
673
663
 
@@ -675,10 +665,11 @@ class WebDriver extends Helper {
675
665
  this.debugSection('Session', 'cleaning cookies and localStorage')
676
666
  await this.browser.deleteCookies()
677
667
  }
678
- await this.browser.execute('localStorage.clear();').catch((err) => {
668
+ await this.browser.execute('localStorage.clear();').catch(err => {
679
669
  if (!(err.message.indexOf("Storage is disabled inside 'data:' URLs.") > -1)) throw err
680
670
  })
681
671
  await this.closeOtherTabs()
672
+ browserLogs = []
682
673
  return this.browser
683
674
  }
684
675
 
@@ -705,17 +696,17 @@ class WebDriver extends Helper {
705
696
 
706
697
  return browser
707
698
  },
708
- stop: async (browser) => {
699
+ stop: async browser => {
709
700
  if (!browser) return
710
701
  return browser.deleteSession()
711
702
  },
712
- loadVars: async (browser) => {
703
+ loadVars: async browser => {
713
704
  if (this.context !== this.root) throw new Error("Can't start session inside within block")
714
705
  this.browser = browser
715
706
  this.$$ = this.browser.$$.bind(this.browser)
716
707
  this.sessionWindows[this.activeSessionName] = browser
717
708
  },
718
- restoreVars: async (session) => {
709
+ restoreVars: async session => {
719
710
  if (!session) {
720
711
  this.activeSessionName = ''
721
712
  }
@@ -757,7 +748,7 @@ class WebDriver extends Helper {
757
748
  this.browser.isInsideFrame = true
758
749
  if (Array.isArray(frame)) {
759
750
  // this.switchTo(null);
760
- await forEachAsync(frame, async (f) => this.switchTo(f))
751
+ await forEachAsync(frame, async f => this.switchTo(f))
761
752
  return
762
753
  }
763
754
  await this.switchTo(frame)
@@ -835,10 +826,7 @@ class WebDriver extends Helper {
835
826
  * @param {object} locator
836
827
  */
837
828
  async _smartWait(locator) {
838
- this.debugSection(
839
- `SmartWait (${this.options.smartWait}ms)`,
840
- `Locating ${JSON.stringify(locator)} in ${this.options.smartWait}`,
841
- )
829
+ this.debugSection(`SmartWait (${this.options.smartWait}ms)`, `Locating ${JSON.stringify(locator)} in ${this.options.smartWait}`)
842
830
  await this.defineTimeout({ implicit: this.options.smartWait })
843
831
  }
844
832
 
@@ -913,7 +901,7 @@ class WebDriver extends Helper {
913
901
  * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
914
902
  */
915
903
  async _locateCheckable(locator) {
916
- return findCheckable.call(this, locator, this.$$.bind(this)).then((res) => res)
904
+ return findCheckable.call(this, locator, this.$$.bind(this)).then(res => res)
917
905
  }
918
906
 
919
907
  /**
@@ -941,7 +929,7 @@ class WebDriver extends Helper {
941
929
  * @param {CodeceptJS.LocatorOrString} locator element located by CSS|XPath|strict locator.
942
930
  */
943
931
  async _locateFields(locator) {
944
- return findFields.call(this, locator).then((res) => res)
932
+ return findFields.call(this, locator).then(res => res)
945
933
  }
946
934
 
947
935
  /**
@@ -949,7 +937,20 @@ class WebDriver extends Helper {
949
937
  *
950
938
  */
951
939
  async grabWebElements(locator) {
952
- return this._locate(locator)
940
+ const elements = await this._locate(locator)
941
+ return elements.map(element => new WebElement(element, this))
942
+ }
943
+
944
+ /**
945
+ * {{> grabWebElement }}
946
+ *
947
+ */
948
+ async grabWebElement(locator) {
949
+ const elements = await this._locate(locator)
950
+ if (elements.length === 0) {
951
+ throw new ElementNotFound(locator, 'Element')
952
+ }
953
+ return new WebElement(elements[0], this)
953
954
  }
954
955
 
955
956
  /**
@@ -1025,7 +1026,7 @@ class WebDriver extends Helper {
1025
1026
  const elem = usingFirstElement(res)
1026
1027
  highlightActiveElement.call(this, elem)
1027
1028
 
1028
- return this.executeScript((el) => {
1029
+ return this.executeScript(el => {
1029
1030
  if (document.activeElement instanceof HTMLElement) {
1030
1031
  document.activeElement.blur()
1031
1032
  }
@@ -1097,7 +1098,7 @@ class WebDriver extends Helper {
1097
1098
  }
1098
1099
  const elem = usingFirstElement(res)
1099
1100
 
1100
- return this.executeScript((el) => {
1101
+ return this.executeScript(el => {
1101
1102
  if (document.activeElement instanceof HTMLElement) {
1102
1103
  document.activeElement.blur()
1103
1104
  }
@@ -1160,15 +1161,9 @@ class WebDriver extends Helper {
1160
1161
  }
1161
1162
 
1162
1163
  // 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
- )
1164
+ let els = await forEachAsync(option, async opt => this.browser.findElementsFromElement(getElementId(elem), 'xpath', Locator.select.byVisibleText(xpathLocator.literal(opt))))
1170
1165
 
1171
- const clickOptionFn = async (el) => {
1166
+ const clickOptionFn = async el => {
1172
1167
  if (el[0]) el = el[0]
1173
1168
  const elementId = getElementId(el)
1174
1169
  if (elementId) return this.browser.elementClick(elementId)
@@ -1178,19 +1173,9 @@ class WebDriver extends Helper {
1178
1173
  return forEachAsync(els, clickOptionFn)
1179
1174
  }
1180
1175
  // 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
- )
1176
+ els = await forEachAsync(option, async opt => this.browser.findElementsFromElement(getElementId(elem), 'xpath', Locator.select.byValue(xpathLocator.literal(opt))))
1188
1177
  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
- )
1178
+ throw new ElementNotFound(select, `Option "${option}" in`, 'was not found neither by a visible text nor by a value')
1194
1179
  }
1195
1180
  return forEachAsync(els, clickOptionFn)
1196
1181
  }
@@ -1217,9 +1202,7 @@ class WebDriver extends Helper {
1217
1202
  this.debugSection('File', 'Uploading file to remote server')
1218
1203
  file = await this.browser.uploadFile(file)
1219
1204
  } 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
- )
1205
+ throw new Error(`File can't be transferred to remote server. Set \`remoteFileUpload: false\` in config to upload file locally.\n${err.message}`)
1223
1206
  }
1224
1207
  }
1225
1208
 
@@ -1272,7 +1255,11 @@ class WebDriver extends Helper {
1272
1255
  */
1273
1256
  async grabTextFromAll(locator) {
1274
1257
  const res = await this._locate(locator, true)
1275
- const val = await forEachAsync(res, (el) => this.browser.getElementText(getElementId(el)))
1258
+ let val = []
1259
+ await forEachAsync(res, async el => {
1260
+ const text = await this.browser.getElementText(getElementId(el))
1261
+ val.push(text)
1262
+ })
1276
1263
  this.debugSection('GrabText', String(val))
1277
1264
  return val
1278
1265
  }
@@ -1297,7 +1284,7 @@ class WebDriver extends Helper {
1297
1284
  */
1298
1285
  async grabHTMLFromAll(locator) {
1299
1286
  const elems = await this._locate(locator, true)
1300
- const html = await forEachAsync(elems, (elem) => elem.getHTML(false))
1287
+ const html = await forEachAsync(elems, elem => elem.getHTML(false))
1301
1288
  this.debugSection('GrabHTML', String(html))
1302
1289
  return html
1303
1290
  }
@@ -1322,7 +1309,7 @@ class WebDriver extends Helper {
1322
1309
  */
1323
1310
  async grabValueFromAll(locator) {
1324
1311
  const res = await this._locate(locator, true)
1325
- const val = await forEachAsync(res, (el) => el.getValue())
1312
+ const val = await forEachAsync(res, el => el.getValue())
1326
1313
  this.debugSection('GrabValue', String(val))
1327
1314
 
1328
1315
  return val
@@ -1347,7 +1334,7 @@ class WebDriver extends Helper {
1347
1334
  */
1348
1335
  async grabCssPropertyFromAll(locator, cssProperty) {
1349
1336
  const res = await this._locate(locator, true)
1350
- const val = await forEachAsync(res, async (el) => this.browser.getElementCSSValue(getElementId(el), cssProperty))
1337
+ const val = await forEachAsync(res, async el => this.browser.getElementCSSValue(getElementId(el), cssProperty))
1351
1338
  this.debugSection('Grab', String(val))
1352
1339
  return val
1353
1340
  }
@@ -1371,7 +1358,7 @@ class WebDriver extends Helper {
1371
1358
  */
1372
1359
  async grabAttributeFromAll(locator, attr) {
1373
1360
  const res = await this._locate(locator, true)
1374
- const val = await forEachAsync(res, async (el) => el.getAttribute(attr))
1361
+ const val = await forEachAsync(res, async el => el.getAttribute(attr))
1375
1362
  this.debugSection('GrabAttribute', String(val))
1376
1363
  return val
1377
1364
  }
@@ -1488,7 +1475,7 @@ class WebDriver extends Helper {
1488
1475
  async seeElement(locator) {
1489
1476
  const res = await this._locate(locator, true)
1490
1477
  assertElementExists(res, locator)
1491
- const selected = await forEachAsync(res, async (el) => el.isDisplayed())
1478
+ const selected = await forEachAsync(res, async el => el.isDisplayed())
1492
1479
  try {
1493
1480
  return truth(`elements of ${new Locator(locator)}`, 'to be seen').assert(selected)
1494
1481
  } catch (e) {
@@ -1505,7 +1492,7 @@ class WebDriver extends Helper {
1505
1492
  if (!res || res.length === 0) {
1506
1493
  return truth(`elements of ${new Locator(locator)}`, 'to be seen').negate(false)
1507
1494
  }
1508
- const selected = await forEachAsync(res, async (el) => el.isDisplayed())
1495
+ const selected = await forEachAsync(res, async el => el.isDisplayed())
1509
1496
  try {
1510
1497
  return truth(`elements of ${new Locator(locator)}`, 'to be seen').negate(selected)
1511
1498
  } catch (e) {
@@ -1560,11 +1547,7 @@ class WebDriver extends Helper {
1560
1547
  * {{> grabBrowserLogs }}
1561
1548
  */
1562
1549
  async grabBrowserLogs() {
1563
- if (this.browser.isW3C) {
1564
- this.debug('Logs not available in W3C specification')
1565
- return
1566
- }
1567
- return this.browser.getLogs('browser')
1550
+ return browserLogs
1568
1551
  }
1569
1552
 
1570
1553
  /**
@@ -1590,11 +1573,7 @@ class WebDriver extends Helper {
1590
1573
  */
1591
1574
  async seeNumberOfElements(locator, num) {
1592
1575
  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
- )
1576
+ return assert.equal(res.length, num, `expected number of elements (${new Locator(locator)}) is ${num}, but found ${res.length}`)
1598
1577
  }
1599
1578
 
1600
1579
  /**
@@ -1603,11 +1582,7 @@ class WebDriver extends Helper {
1603
1582
  */
1604
1583
  async seeNumberOfVisibleElements(locator, num) {
1605
1584
  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
- )
1585
+ return assert.equal(res, num, `expected number of visible elements (${new Locator(locator)}) is ${num}, but found ${res}`)
1611
1586
  }
1612
1587
 
1613
1588
  /**
@@ -1632,19 +1607,16 @@ class WebDriver extends Helper {
1632
1607
  }
1633
1608
  }
1634
1609
 
1635
- const values = Object.keys(cssPropertiesCamelCase).map((key) => cssPropertiesCamelCase[key])
1610
+ const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key])
1636
1611
  if (!Array.isArray(props)) props = [props]
1637
1612
  let chunked = chunkArray(props, values.length)
1638
- chunked = chunked.filter((val) => {
1613
+ chunked = chunked.filter(val => {
1639
1614
  for (let i = 0; i < val.length; ++i) {
1640
- // eslint-disable-next-line eqeqeq
1641
1615
  if (val[i] != values[i]) return false
1642
1616
  }
1643
1617
  return true
1644
1618
  })
1645
- return equals(
1646
- `all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`,
1647
- ).assert(chunked.length, elemAmount)
1619
+ return equals(`all elements (${new Locator(locator)}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount)
1648
1620
  }
1649
1621
 
1650
1622
  /**
@@ -1655,28 +1627,24 @@ class WebDriver extends Helper {
1655
1627
  assertElementExists(res, locator)
1656
1628
  const elemAmount = res.length
1657
1629
 
1658
- let attrs = await forEachAsync(res, async (el) => {
1659
- return forEachAsync(Object.keys(attributes), async (attr) => el.getAttribute(attr))
1630
+ let attrs = await forEachAsync(res, async el => {
1631
+ return forEachAsync(Object.keys(attributes), async attr => el.getAttribute(attr))
1660
1632
  })
1661
1633
 
1662
- const values = Object.keys(attributes).map((key) => attributes[key])
1634
+ const values = Object.keys(attributes).map(key => attributes[key])
1663
1635
  if (!Array.isArray(attrs)) attrs = [attrs]
1664
1636
  let chunked = chunkArray(attrs, values.length)
1665
- chunked = chunked.filter((val) => {
1637
+ chunked = chunked.filter(val => {
1666
1638
  for (let i = 0; i < val.length; ++i) {
1667
1639
  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)
1640
+ const _expected = Number.isNaN(values[i]) || typeof values[i] === 'string' ? values[i] : Number.parseInt(values[i], 10)
1670
1641
  // the attribute could be a boolean
1671
1642
  if (typeof _actual === 'boolean') return _actual === _expected
1672
1643
  if (_actual !== _expected) return false
1673
1644
  }
1674
1645
  return true
1675
1646
  })
1676
- return assert.ok(
1677
- chunked.length === elemAmount,
1678
- `expected all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`,
1679
- )
1647
+ return assert.ok(chunked.length === elemAmount, `expected all elements (${new Locator(locator)}) to have attributes ${JSON.stringify(attributes)}`)
1680
1648
  }
1681
1649
 
1682
1650
  /**
@@ -1685,9 +1653,9 @@ class WebDriver extends Helper {
1685
1653
  async grabNumberOfVisibleElements(locator) {
1686
1654
  const res = await this._locate(locator)
1687
1655
 
1688
- let selected = await forEachAsync(res, async (el) => el.isDisplayed())
1656
+ let selected = await forEachAsync(res, async el => el.isDisplayed())
1689
1657
  if (!Array.isArray(selected)) selected = [selected]
1690
- selected = selected.filter((val) => val === true)
1658
+ selected = selected.filter(val => val === true)
1691
1659
  return selected.length
1692
1660
  }
1693
1661
 
@@ -1841,18 +1809,25 @@ class WebDriver extends Helper {
1841
1809
 
1842
1810
  if (browser) {
1843
1811
  this.debug(`Screenshot of ${sessionName} session has been saved to ${outputFile}`)
1844
- return browser.saveScreenshot(outputFile)
1812
+ await browser.saveScreenshot(outputFile)
1845
1813
  }
1846
1814
  }
1847
1815
  }
1848
1816
 
1849
1817
  if (!fullPage) {
1850
1818
  this.debug(`Screenshot has been saved to ${outputFile}`)
1851
- return this.browser.saveScreenshot(outputFile)
1819
+ await this.browser.saveScreenshot(outputFile)
1852
1820
  }
1853
1821
 
1854
1822
  const originalWindowSize = await this.browser.getWindowSize()
1855
1823
 
1824
+ // this case running on device, so we could not set the windowSize
1825
+ if (this.browser.isMobile) {
1826
+ this.debug(`Screenshot has been saved to ${outputFile}, size: ${originalWindowSize.width}x${originalWindowSize.height}`)
1827
+ const buffer = await this.browser.saveScreenshot(outputFile)
1828
+ return buffer
1829
+ }
1830
+
1856
1831
  let { width, height } = await this.browser
1857
1832
  .execute(function () {
1858
1833
  return {
@@ -1860,7 +1835,7 @@ class WebDriver extends Helper {
1860
1835
  width: document.body.scrollWidth,
1861
1836
  }
1862
1837
  })
1863
- .then((res) => res)
1838
+ .then(res => res)
1864
1839
 
1865
1840
  if (height < 100) height = 500 // errors for very small height
1866
1841
 
@@ -1928,7 +1903,7 @@ class WebDriver extends Helper {
1928
1903
 
1929
1904
  return promiseRetry(
1930
1905
  async (retry, number) => {
1931
- const _grabCookie = async (name) => {
1906
+ const _grabCookie = async name => {
1932
1907
  const cookie = await this.browser.getCookies([name])
1933
1908
  if (cookie.length === 0) throw Error(`Cookie ${name} is not found after ${retries}s`)
1934
1909
  }
@@ -1952,11 +1927,10 @@ class WebDriver extends Helper {
1952
1927
  * libraries](http://jster.net/category/windows-modals-popups).
1953
1928
  */
1954
1929
  async acceptPopup() {
1955
- return this.browser.getAlertText().then((res) => {
1956
- if (res !== null) {
1957
- return this.browser.acceptAlert()
1958
- }
1959
- })
1930
+ const text = await this.browser.getAlertText()
1931
+ if (text) {
1932
+ return await this.browser.acceptAlert()
1933
+ }
1960
1934
  }
1961
1935
 
1962
1936
  /**
@@ -1964,11 +1938,10 @@ class WebDriver extends Helper {
1964
1938
  *
1965
1939
  */
1966
1940
  async cancelPopup() {
1967
- return this.browser.getAlertText().then((res) => {
1968
- if (res !== null) {
1969
- return this.browser.dismissAlert()
1970
- }
1971
- })
1941
+ const text = await this.browser.getAlertText()
1942
+ if (text) {
1943
+ return await this.browser.dismissAlert()
1944
+ }
1972
1945
  }
1973
1946
 
1974
1947
  /**
@@ -1978,7 +1951,7 @@ class WebDriver extends Helper {
1978
1951
  * @param {string} text value to check.
1979
1952
  */
1980
1953
  async seeInPopup(text) {
1981
- return this.browser.getAlertText().then((res) => {
1954
+ return await this.browser.getAlertText().then(res => {
1982
1955
  if (res === null) {
1983
1956
  throw new Error('Popup is not opened')
1984
1957
  }
@@ -2259,9 +2232,9 @@ class WebDriver extends Helper {
2259
2232
  async closeOtherTabs() {
2260
2233
  const handles = await this.browser.getWindowHandles()
2261
2234
  const currentHandle = await this.browser.getWindowHandle()
2262
- const otherHandles = handles.filter((handle) => handle !== currentHandle)
2235
+ const otherHandles = handles.filter(handle => handle !== currentHandle)
2263
2236
 
2264
- await forEachAsync(otherHandles, async (handle) => {
2237
+ await forEachAsync(otherHandles, async handle => {
2265
2238
  await this.browser.switchToWindow(handle)
2266
2239
  await this.browser.closeWindow()
2267
2240
  })
@@ -2272,7 +2245,7 @@ class WebDriver extends Helper {
2272
2245
  * {{> wait }}
2273
2246
  */
2274
2247
  async wait(sec) {
2275
- return new Promise((resolve) => {
2248
+ return new Promise(resolve => {
2276
2249
  setTimeout(resolve, sec * 1000)
2277
2250
  })
2278
2251
  }
@@ -2289,9 +2262,9 @@ class WebDriver extends Helper {
2289
2262
  if (!res || res.length === 0) {
2290
2263
  return false
2291
2264
  }
2292
- const selected = await forEachAsync(res, async (el) => this.browser.isElementEnabled(getElementId(el)))
2265
+ const selected = await forEachAsync(res, async el => this.browser.isElementEnabled(getElementId(el)))
2293
2266
  if (Array.isArray(selected)) {
2294
- return selected.filter((val) => val === true).length > 0
2267
+ return selected.filter(val => val === true).length > 0
2295
2268
  }
2296
2269
  return selected
2297
2270
  },
@@ -2346,14 +2319,14 @@ class WebDriver extends Helper {
2346
2319
  return client
2347
2320
  .waitUntil(
2348
2321
  function () {
2349
- return this.getUrl().then((res) => {
2322
+ return this.getUrl().then(res => {
2350
2323
  currUrl = decodeUrl(res)
2351
2324
  return currUrl.indexOf(urlPart) > -1
2352
2325
  })
2353
2326
  },
2354
2327
  { timeout: aSec * 1000 },
2355
2328
  )
2356
- .catch((e) => {
2329
+ .catch(e => {
2357
2330
  if (e.message.indexOf('timeout')) {
2358
2331
  throw new Error(`expected url to include ${urlPart}, but found ${currUrl}`)
2359
2332
  }
@@ -2373,12 +2346,12 @@ class WebDriver extends Helper {
2373
2346
  let currUrl = ''
2374
2347
  return this.browser
2375
2348
  .waitUntil(function () {
2376
- return this.getUrl().then((res) => {
2349
+ return this.getUrl().then(res => {
2377
2350
  currUrl = decodeUrl(res)
2378
2351
  return currUrl === urlPart
2379
2352
  })
2380
2353
  }, aSec * 1000)
2381
- .catch((e) => {
2354
+ .catch(e => {
2382
2355
  if (e.message.indexOf('timeout')) {
2383
2356
  throw new Error(`expected url to be ${urlPart}, but found ${currUrl}`)
2384
2357
  }
@@ -2398,9 +2371,9 @@ class WebDriver extends Helper {
2398
2371
  async () => {
2399
2372
  const res = await this.$$(withStrictLocator.call(this, _context))
2400
2373
  if (!res || res.length === 0) return false
2401
- const selected = await forEachAsync(res, async (el) => this.browser.getElementText(getElementId(el)))
2374
+ const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)))
2402
2375
  if (Array.isArray(selected)) {
2403
- return selected.filter((part) => part.indexOf(text) >= 0).length > 0
2376
+ return selected.filter(part => part.indexOf(text) >= 0).length > 0
2404
2377
  }
2405
2378
  return selected.indexOf(text) >= 0
2406
2379
  },
@@ -2422,9 +2395,9 @@ class WebDriver extends Helper {
2422
2395
  async () => {
2423
2396
  const res = await findFields.call(this, field)
2424
2397
  if (!res || res.length === 0) return false
2425
- const selected = await forEachAsync(res, async (el) => el.getValue())
2398
+ const selected = await forEachAsync(res, async el => el.getValue())
2426
2399
  if (Array.isArray(selected)) {
2427
- return selected.filter((part) => part.indexOf(value) >= 0).length > 0
2400
+ return selected.filter(part => part.indexOf(value) >= 0).length > 0
2428
2401
  }
2429
2402
  return selected.indexOf(value) >= 0
2430
2403
  },
@@ -2446,9 +2419,9 @@ class WebDriver extends Helper {
2446
2419
  async () => {
2447
2420
  const res = await this._res(locator)
2448
2421
  if (!res || res.length === 0) return false
2449
- const selected = await forEachAsync(res, async (el) => el.isDisplayed())
2422
+ const selected = await forEachAsync(res, async el => el.isDisplayed())
2450
2423
  if (Array.isArray(selected)) {
2451
- return selected.filter((val) => val === true).length > 0
2424
+ return selected.filter(val => val === true).length > 0
2452
2425
  }
2453
2426
  return selected
2454
2427
  },
@@ -2469,10 +2442,10 @@ class WebDriver extends Helper {
2469
2442
  async () => {
2470
2443
  const res = await this._res(locator)
2471
2444
  if (!res || res.length === 0) return false
2472
- let selected = await forEachAsync(res, async (el) => el.isDisplayed())
2445
+ let selected = await forEachAsync(res, async el => el.isDisplayed())
2473
2446
 
2474
2447
  if (!Array.isArray(selected)) selected = [selected]
2475
- selected = selected.filter((val) => val === true)
2448
+ selected = selected.filter(val => val === true)
2476
2449
  return selected.length === num
2477
2450
  },
2478
2451
  {
@@ -2492,7 +2465,7 @@ class WebDriver extends Helper {
2492
2465
  async () => {
2493
2466
  const res = await this._res(locator)
2494
2467
  if (!res || res.length === 0) return true
2495
- const selected = await forEachAsync(res, async (el) => el.isDisplayed())
2468
+ const selected = await forEachAsync(res, async el => el.isDisplayed())
2496
2469
  return !selected.length
2497
2470
  },
2498
2471
  { timeout: aSec * 1000, timeoutMsg: `element (${new Locator(locator)}) still visible after ${aSec} sec` },
@@ -2568,17 +2541,14 @@ class WebDriver extends Helper {
2568
2541
  */
2569
2542
  async switchTo(locator) {
2570
2543
  this.browser.isInsideFrame = true
2571
- if (Number.isInteger(locator)) {
2572
- return this.browser.switchToFrame(locator)
2573
- }
2574
2544
  if (!locator) {
2575
- return this.browser.switchToFrame(null)
2545
+ return this.browser.switchFrame(null)
2576
2546
  }
2577
2547
 
2578
2548
  let res = await this._locate(locator, true)
2579
2549
  assertElementExists(res, locator)
2580
2550
  res = usingFirstElement(res)
2581
- return this.browser.switchToFrame(res)
2551
+ return this.browser.switchFrame(res)
2582
2552
  }
2583
2553
 
2584
2554
  /**
@@ -2591,7 +2561,7 @@ class WebDriver extends Helper {
2591
2561
 
2592
2562
  await this.browser.waitUntil(
2593
2563
  async () => {
2594
- await this.browser.getWindowHandles().then((handles) => {
2564
+ await this.browser.getWindowHandles().then(handles => {
2595
2565
  if (handles.indexOf(current) + num + 1 <= handles.length) {
2596
2566
  target = handles[handles.indexOf(current) + num]
2597
2567
  }
@@ -2613,7 +2583,7 @@ class WebDriver extends Helper {
2613
2583
 
2614
2584
  await this.browser.waitUntil(
2615
2585
  async () => {
2616
- await this.browser.getWindowHandles().then((handles) => {
2586
+ await this.browser.getWindowHandles().then(handles => {
2617
2587
  if (handles.indexOf(current) - num > -1) {
2618
2588
  target = handles[handles.indexOf(current) - num]
2619
2589
  }
@@ -2683,10 +2653,7 @@ class WebDriver extends Helper {
2683
2653
  return client.execute(function () {
2684
2654
  const body = document.body
2685
2655
  const html = document.documentElement
2686
- window.scrollTo(
2687
- 0,
2688
- Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight),
2689
- )
2656
+ window.scrollTo(0, Math.max(body.scrollHeight, body.offsetHeight, html.clientHeight, html.scrollHeight, html.offsetHeight))
2690
2657
  })
2691
2658
  }
2692
2659
 
@@ -2760,10 +2727,17 @@ async function proceedSee(assertType, text, context, strict = false) {
2760
2727
  const smartWaitEnabled = assertType === 'assert'
2761
2728
  const res = await this._locate(withStrictLocator(context), smartWaitEnabled)
2762
2729
  assertElementExists(res, context)
2763
- const selected = await forEachAsync(res, async (el) => this.browser.getElementText(getElementId(el)))
2730
+ let selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)))
2731
+
2732
+ // apply ignoreCase option
2733
+ if (store?.currentStep?.opts?.ignoreCase === true) {
2734
+ text = text.toLowerCase()
2735
+ selected = selected.map(elText => elText.toLowerCase())
2736
+ }
2737
+
2764
2738
  if (strict) {
2765
2739
  if (Array.isArray(selected) && selected.length !== 0) {
2766
- return selected.map((elText) => equals(description)[assertType](text, elText))
2740
+ return selected.map(elText => equals(description)[assertType](text, elText))
2767
2741
  }
2768
2742
  return equals(description)[assertType](text, selected)
2769
2743
  }
@@ -2791,7 +2765,7 @@ async function forEachAsync(array, callback, options = { expandArrayResults: tru
2791
2765
  const res = await callback(inputArray[index], index, inputArray)
2792
2766
 
2793
2767
  if (Array.isArray(res) && expandArrayResults) {
2794
- res.forEach((val) => values.push(val))
2768
+ res.forEach(val => values.push(val))
2795
2769
  } else if (res) {
2796
2770
  values.push(res)
2797
2771
  }
@@ -2877,11 +2851,11 @@ async function proceedSeeField(assertType, field, value) {
2877
2851
  const elem = usingFirstElement(res)
2878
2852
  const elemId = getElementId(elem)
2879
2853
 
2880
- const proceedMultiple = async (fields) => {
2854
+ const proceedMultiple = async fields => {
2881
2855
  const fieldResults = toArray(
2882
- await forEachAsync(fields, async (el) => {
2856
+ await forEachAsync(fields, async el => {
2883
2857
  const elementId = getElementId(el)
2884
- return this.browser.isW3C ? el.getValue() : this.browser.getElementAttribute(elementId, 'value')
2858
+ return this.browser.getElementAttribute(elementId, 'value')
2885
2859
  }),
2886
2860
  )
2887
2861
 
@@ -2890,27 +2864,24 @@ async function proceedSeeField(assertType, field, value) {
2890
2864
  } else {
2891
2865
  // Assert that results were found so the forEach assert does not silently pass
2892
2866
  equals(`no. of items matching > 0: ${field}`)[assertType](true, !!fieldResults.length)
2893
- fieldResults.forEach((val) => stringIncludes(`fields by ${field}`)[assertType](value, val))
2867
+ fieldResults.forEach(val => stringIncludes(`fields by ${field}`)[assertType](value, val))
2894
2868
  }
2895
2869
  }
2896
2870
 
2897
- const proceedSingle = (el) =>
2898
- el.getValue().then((res) => {
2871
+ const proceedSingle = el =>
2872
+ el.getValue().then(res => {
2899
2873
  if (res === null) {
2900
2874
  throw new Error(`Element ${el.selector} has no value attribute`)
2901
2875
  }
2902
2876
  stringIncludes(`fields by ${field}`)[assertType](value, res)
2903
2877
  })
2904
2878
 
2905
- const filterBySelected = async (elements) =>
2906
- filterAsync(elements, async (el) => this.browser.isElementSelected(getElementId(el)))
2879
+ const filterBySelected = async elements => filterAsync(elements, async el => this.browser.isElementSelected(getElementId(el)))
2907
2880
 
2908
2881
  const filterSelectedByValue = async (elements, value) => {
2909
- return filterAsync(elements, async (el) => {
2882
+ return filterAsync(elements, async el => {
2910
2883
  const elementId = getElementId(el)
2911
- const currentValue = this.browser.isW3C
2912
- ? await el.getValue()
2913
- : await this.browser.getElementAttribute(elementId, 'value')
2884
+ const currentValue = await this.browser.getElementAttribute(elementId, 'value')
2914
2885
  const isSelected = await this.browser.isElementSelected(elementId)
2915
2886
  return currentValue === value && isSelected
2916
2887
  })
@@ -2918,7 +2889,13 @@ async function proceedSeeField(assertType, field, value) {
2918
2889
 
2919
2890
  const tag = await elem.getTagName()
2920
2891
  if (tag === 'select') {
2921
- const subOptions = await this.browser.findElementsFromElement(elemId, 'css', 'option')
2892
+ let subOptions
2893
+
2894
+ try {
2895
+ subOptions = await this.browser.findElementsFromElement(elemId, 'css', 'option')
2896
+ } catch (e) {
2897
+ subOptions = await this.browser.findElementsFromElement(elemId, 'xpath', 'option')
2898
+ }
2922
2899
 
2923
2900
  if (value === '') {
2924
2901
  // Don't filter by value
@@ -2959,7 +2936,7 @@ async function proceedSeeCheckbox(assertType, field) {
2959
2936
  const res = await findFields.call(this, field)
2960
2937
  assertElementExists(res, field, 'Field')
2961
2938
 
2962
- const selected = await forEachAsync(res, async (el) => this.browser.isElementSelected(getElementId(el)))
2939
+ const selected = await forEachAsync(res, async el => this.browser.isElementSelected(getElementId(el)))
2963
2940
  return truth(`checkable field "${field}"`, 'to be checked')[assertType](selected)
2964
2941
  }
2965
2942
 
@@ -3160,7 +3137,7 @@ function getNormalizedKey(key) {
3160
3137
  return convertKeyToRawKey(normalizedKey)
3161
3138
  }
3162
3139
 
3163
- const unicodeModifierKeys = modifierKeys.map((k) => convertKeyToRawKey(k))
3140
+ const unicodeModifierKeys = modifierKeys.map(k => convertKeyToRawKey(k))
3164
3141
  function isModifierKey(key) {
3165
3142
  return unicodeModifierKeys.includes(key)
3166
3143
  }
@@ -3173,9 +3150,9 @@ function highlightActiveElement(element) {
3173
3150
 
3174
3151
  function prepareLocateFn(context) {
3175
3152
  if (!context) return this._locate.bind(this)
3176
- return (l) => {
3153
+ return l => {
3177
3154
  l = new Locator(l, 'css')
3178
- return this._locate(context, true).then(async (res) => {
3155
+ return this._locate(context, true).then(async res => {
3179
3156
  assertElementExists(res, context, 'Context element')
3180
3157
  if (l.react) {
3181
3158
  return res[0].react$$(l.react, l.props || undefined)
@@ -3185,4 +3162,8 @@ function prepareLocateFn(context) {
3185
3162
  }
3186
3163
  }
3187
3164
 
3165
+ function logEvents(event) {
3166
+ browserLogs.push(event.text) // add log message to the array
3167
+ }
3168
+
3188
3169
  module.exports = WebDriver