codeceptjs 3.5.0 → 3.5.1-2.beta.7

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 (273) hide show
  1. package/README.md +24 -25
  2. package/lib/actor.js +6 -3
  3. package/lib/ai.js +12 -3
  4. package/lib/cli.js +12 -2
  5. package/lib/codecept.js +4 -0
  6. package/lib/colorUtils.js +10 -0
  7. package/lib/command/definitions.js +2 -7
  8. package/lib/command/dryRun.js +2 -1
  9. package/lib/command/info.js +24 -0
  10. package/lib/command/init.js +51 -5
  11. package/lib/command/run-multiple/collection.js +17 -5
  12. package/lib/command/run-multiple.js +4 -2
  13. package/lib/command/run-workers.js +66 -4
  14. package/lib/command/run.js +7 -0
  15. package/lib/command/workers/runTests.js +39 -0
  16. package/lib/data/context.js +14 -6
  17. package/lib/event.js +4 -0
  18. package/lib/helper/ApiDataFactory.js +2 -1
  19. package/lib/helper/Appium.js +73 -24
  20. package/lib/helper/Expect.js +422 -0
  21. package/lib/helper/FileSystem.js +1 -1
  22. package/lib/helper/GraphQL.js +25 -0
  23. package/lib/helper/Nightmare.js +9 -4
  24. package/lib/helper/OpenAI.js +14 -10
  25. package/lib/helper/Playwright.js +1205 -288
  26. package/lib/helper/Protractor.js +11 -6
  27. package/lib/helper/Puppeteer.js +173 -61
  28. package/lib/helper/TestCafe.js +44 -9
  29. package/lib/helper/WebDriver.js +231 -82
  30. package/lib/helper/errors/ElementNotFound.js +2 -1
  31. package/lib/helper/extras/PlaywrightReactVueLocator.js +38 -0
  32. package/lib/helper/scripts/blurElement.js +17 -0
  33. package/lib/helper/scripts/focusElement.js +17 -0
  34. package/lib/helper/scripts/highlightElement.js +2 -2
  35. package/lib/html.js +3 -3
  36. package/lib/interfaces/bdd.js +1 -1
  37. package/lib/interfaces/gherkin.js +37 -3
  38. package/lib/interfaces/scenarioConfig.js +1 -0
  39. package/lib/locator.js +17 -4
  40. package/lib/mochaFactory.js +2 -1
  41. package/lib/output.js +1 -1
  42. package/lib/pause.js +12 -9
  43. package/lib/plugin/autoLogin.js +45 -10
  44. package/lib/plugin/heal.js +47 -17
  45. package/lib/plugin/retryFailedStep.js +10 -1
  46. package/lib/plugin/retryTo.js +2 -4
  47. package/lib/plugin/selenoid.js +6 -1
  48. package/lib/plugin/standardActingHelpers.js +0 -2
  49. package/lib/plugin/stepByStepReport.js +2 -2
  50. package/lib/plugin/tryTo.js +5 -7
  51. package/lib/plugin/wdio.js +0 -1
  52. package/lib/recorder.js +20 -9
  53. package/lib/session.js +1 -1
  54. package/lib/step.js +30 -11
  55. package/lib/ui.js +1 -0
  56. package/lib/utils.js +18 -1
  57. package/lib/workers.js +28 -3
  58. package/package.json +108 -98
  59. package/translations/de-DE.js +5 -0
  60. package/translations/fr-FR.js +14 -1
  61. package/translations/it-IT.js +1 -0
  62. package/translations/ja-JP.js +5 -0
  63. package/translations/pl-PL.js +5 -0
  64. package/translations/pt-BR.js +1 -0
  65. package/translations/ru-RU.js +1 -0
  66. package/translations/zh-CN.js +5 -0
  67. package/translations/zh-TW.js +5 -0
  68. package/typings/index.d.ts +8 -6
  69. package/typings/promiseBasedTypes.d.ts +784 -822
  70. package/typings/types.d.ts +1214 -727
  71. package/CHANGELOG.md +0 -2492
  72. package/docs/advanced.md +0 -351
  73. package/docs/ai.md +0 -246
  74. package/docs/api.md +0 -323
  75. package/docs/basics.md +0 -980
  76. package/docs/bdd.md +0 -535
  77. package/docs/best.md +0 -237
  78. package/docs/books.md +0 -37
  79. package/docs/bootstrap.md +0 -135
  80. package/docs/build/ApiDataFactory.js +0 -409
  81. package/docs/build/Appium.js +0 -1978
  82. package/docs/build/FileSystem.js +0 -228
  83. package/docs/build/GraphQL.js +0 -204
  84. package/docs/build/GraphQLDataFactory.js +0 -309
  85. package/docs/build/JSONResponse.js +0 -338
  86. package/docs/build/Mochawesome.js +0 -71
  87. package/docs/build/Nightmare.js +0 -2147
  88. package/docs/build/OpenAI.js +0 -122
  89. package/docs/build/Playwright.js +0 -4134
  90. package/docs/build/Polly.js +0 -42
  91. package/docs/build/Protractor.js +0 -2701
  92. package/docs/build/Puppeteer.js +0 -3743
  93. package/docs/build/REST.js +0 -344
  94. package/docs/build/SeleniumWebdriver.js +0 -76
  95. package/docs/build/TestCafe.js +0 -2059
  96. package/docs/build/WebDriver.js +0 -4042
  97. package/docs/changelog.md +0 -2501
  98. package/docs/commands.md +0 -254
  99. package/docs/community-helpers.md +0 -58
  100. package/docs/configuration.md +0 -157
  101. package/docs/continuous-integration.md +0 -22
  102. package/docs/custom-helpers.md +0 -306
  103. package/docs/data.md +0 -375
  104. package/docs/detox.md +0 -235
  105. package/docs/docker.md +0 -137
  106. package/docs/email.md +0 -183
  107. package/docs/examples.md +0 -149
  108. package/docs/helpers/ApiDataFactory.md +0 -266
  109. package/docs/helpers/Appium.md +0 -1317
  110. package/docs/helpers/Detox.md +0 -586
  111. package/docs/helpers/FileSystem.md +0 -152
  112. package/docs/helpers/GraphQL.md +0 -130
  113. package/docs/helpers/GraphQLDataFactory.md +0 -226
  114. package/docs/helpers/JSONResponse.md +0 -254
  115. package/docs/helpers/Mochawesome.md +0 -8
  116. package/docs/helpers/MockRequest.md +0 -377
  117. package/docs/helpers/Nightmare.md +0 -1258
  118. package/docs/helpers/OpenAI.md +0 -70
  119. package/docs/helpers/Playwright.md +0 -2250
  120. package/docs/helpers/Polly.md +0 -44
  121. package/docs/helpers/Puppeteer-firefox.md +0 -86
  122. package/docs/helpers/Puppeteer.md +0 -2147
  123. package/docs/helpers/REST.md +0 -218
  124. package/docs/helpers/TestCafe.md +0 -1224
  125. package/docs/helpers/WebDriver.md +0 -2325
  126. package/docs/hooks.md +0 -340
  127. package/docs/index.md +0 -111
  128. package/docs/installation.md +0 -75
  129. package/docs/internal-api.md +0 -265
  130. package/docs/locators.md +0 -331
  131. package/docs/mobile-react-native-locators.md +0 -67
  132. package/docs/mobile.md +0 -344
  133. package/docs/nightmare.md +0 -223
  134. package/docs/pageobjects.md +0 -291
  135. package/docs/parallel.md +0 -288
  136. package/docs/playwright.md +0 -609
  137. package/docs/plugins.md +0 -1225
  138. package/docs/puppeteer.md +0 -316
  139. package/docs/quickstart.md +0 -163
  140. package/docs/react.md +0 -69
  141. package/docs/reports.md +0 -392
  142. package/docs/secrets.md +0 -36
  143. package/docs/shadow.md +0 -68
  144. package/docs/shared/keys.mustache +0 -31
  145. package/docs/shared/react.mustache +0 -1
  146. package/docs/testcafe.md +0 -174
  147. package/docs/translation.md +0 -247
  148. package/docs/tutorial.md +0 -271
  149. package/docs/typescript.md +0 -180
  150. package/docs/ui.md +0 -59
  151. package/docs/videos.md +0 -28
  152. package/docs/visual.md +0 -202
  153. package/docs/vue.md +0 -121
  154. package/docs/webapi/amOnPage.mustache +0 -11
  155. package/docs/webapi/appendField.mustache +0 -11
  156. package/docs/webapi/attachFile.mustache +0 -12
  157. package/docs/webapi/checkOption.mustache +0 -13
  158. package/docs/webapi/clearCookie.mustache +0 -10
  159. package/docs/webapi/clearField.mustache +0 -9
  160. package/docs/webapi/click.mustache +0 -25
  161. package/docs/webapi/clickLink.mustache +0 -8
  162. package/docs/webapi/closeCurrentTab.mustache +0 -7
  163. package/docs/webapi/closeOtherTabs.mustache +0 -8
  164. package/docs/webapi/dontSee.mustache +0 -11
  165. package/docs/webapi/dontSeeCheckboxIsChecked.mustache +0 -10
  166. package/docs/webapi/dontSeeCookie.mustache +0 -8
  167. package/docs/webapi/dontSeeCurrentUrlEquals.mustache +0 -10
  168. package/docs/webapi/dontSeeElement.mustache +0 -8
  169. package/docs/webapi/dontSeeElementInDOM.mustache +0 -8
  170. package/docs/webapi/dontSeeInCurrentUrl.mustache +0 -4
  171. package/docs/webapi/dontSeeInField.mustache +0 -11
  172. package/docs/webapi/dontSeeInSource.mustache +0 -8
  173. package/docs/webapi/dontSeeInTitle.mustache +0 -8
  174. package/docs/webapi/doubleClick.mustache +0 -13
  175. package/docs/webapi/downloadFile.mustache +0 -12
  176. package/docs/webapi/dragAndDrop.mustache +0 -9
  177. package/docs/webapi/dragSlider.mustache +0 -11
  178. package/docs/webapi/executeAsyncScript.mustache +0 -24
  179. package/docs/webapi/executeScript.mustache +0 -26
  180. package/docs/webapi/fillField.mustache +0 -16
  181. package/docs/webapi/forceClick.mustache +0 -28
  182. package/docs/webapi/forceRightClick.mustache +0 -18
  183. package/docs/webapi/grabAllWindowHandles.mustache +0 -7
  184. package/docs/webapi/grabAttributeFrom.mustache +0 -10
  185. package/docs/webapi/grabAttributeFromAll.mustache +0 -9
  186. package/docs/webapi/grabBrowserLogs.mustache +0 -9
  187. package/docs/webapi/grabCookie.mustache +0 -11
  188. package/docs/webapi/grabCssPropertyFrom.mustache +0 -11
  189. package/docs/webapi/grabCssPropertyFromAll.mustache +0 -10
  190. package/docs/webapi/grabCurrentUrl.mustache +0 -9
  191. package/docs/webapi/grabCurrentWindowHandle.mustache +0 -6
  192. package/docs/webapi/grabDataFromPerformanceTiming.mustache +0 -20
  193. package/docs/webapi/grabElementBoundingRect.mustache +0 -20
  194. package/docs/webapi/grabGeoLocation.mustache +0 -8
  195. package/docs/webapi/grabHTMLFrom.mustache +0 -10
  196. package/docs/webapi/grabHTMLFromAll.mustache +0 -9
  197. package/docs/webapi/grabNumberOfOpenTabs.mustache +0 -8
  198. package/docs/webapi/grabNumberOfVisibleElements.mustache +0 -9
  199. package/docs/webapi/grabPageScrollPosition.mustache +0 -8
  200. package/docs/webapi/grabPopupText.mustache +0 -5
  201. package/docs/webapi/grabSource.mustache +0 -8
  202. package/docs/webapi/grabTextFrom.mustache +0 -10
  203. package/docs/webapi/grabTextFromAll.mustache +0 -9
  204. package/docs/webapi/grabTitle.mustache +0 -8
  205. package/docs/webapi/grabValueFrom.mustache +0 -9
  206. package/docs/webapi/grabValueFromAll.mustache +0 -8
  207. package/docs/webapi/moveCursorTo.mustache +0 -12
  208. package/docs/webapi/openNewTab.mustache +0 -7
  209. package/docs/webapi/pressKey.mustache +0 -12
  210. package/docs/webapi/pressKeyDown.mustache +0 -12
  211. package/docs/webapi/pressKeyUp.mustache +0 -12
  212. package/docs/webapi/pressKeyWithKeyNormalization.mustache +0 -60
  213. package/docs/webapi/refreshPage.mustache +0 -6
  214. package/docs/webapi/resizeWindow.mustache +0 -6
  215. package/docs/webapi/rightClick.mustache +0 -14
  216. package/docs/webapi/saveElementScreenshot.mustache +0 -10
  217. package/docs/webapi/saveScreenshot.mustache +0 -12
  218. package/docs/webapi/say.mustache +0 -10
  219. package/docs/webapi/scrollIntoView.mustache +0 -11
  220. package/docs/webapi/scrollPageToBottom.mustache +0 -6
  221. package/docs/webapi/scrollPageToTop.mustache +0 -6
  222. package/docs/webapi/scrollTo.mustache +0 -12
  223. package/docs/webapi/see.mustache +0 -11
  224. package/docs/webapi/seeAttributesOnElements.mustache +0 -9
  225. package/docs/webapi/seeCheckboxIsChecked.mustache +0 -10
  226. package/docs/webapi/seeCookie.mustache +0 -8
  227. package/docs/webapi/seeCssPropertiesOnElements.mustache +0 -9
  228. package/docs/webapi/seeCurrentUrlEquals.mustache +0 -11
  229. package/docs/webapi/seeElement.mustache +0 -8
  230. package/docs/webapi/seeElementInDOM.mustache +0 -8
  231. package/docs/webapi/seeInCurrentUrl.mustache +0 -8
  232. package/docs/webapi/seeInField.mustache +0 -12
  233. package/docs/webapi/seeInPopup.mustache +0 -8
  234. package/docs/webapi/seeInSource.mustache +0 -7
  235. package/docs/webapi/seeInTitle.mustache +0 -8
  236. package/docs/webapi/seeNumberOfElements.mustache +0 -11
  237. package/docs/webapi/seeNumberOfVisibleElements.mustache +0 -10
  238. package/docs/webapi/seeTextEquals.mustache +0 -9
  239. package/docs/webapi/seeTitleEquals.mustache +0 -8
  240. package/docs/webapi/selectOption.mustache +0 -21
  241. package/docs/webapi/setCookie.mustache +0 -16
  242. package/docs/webapi/setGeoLocation.mustache +0 -12
  243. package/docs/webapi/switchTo.mustache +0 -9
  244. package/docs/webapi/switchToNextTab.mustache +0 -10
  245. package/docs/webapi/switchToPreviousTab.mustache +0 -10
  246. package/docs/webapi/type.mustache +0 -21
  247. package/docs/webapi/uncheckOption.mustache +0 -13
  248. package/docs/webapi/wait.mustache +0 -8
  249. package/docs/webapi/waitForClickable.mustache +0 -11
  250. package/docs/webapi/waitForDetached.mustache +0 -10
  251. package/docs/webapi/waitForElement.mustache +0 -11
  252. package/docs/webapi/waitForEnabled.mustache +0 -6
  253. package/docs/webapi/waitForFunction.mustache +0 -17
  254. package/docs/webapi/waitForInvisible.mustache +0 -10
  255. package/docs/webapi/waitForText.mustache +0 -13
  256. package/docs/webapi/waitForValue.mustache +0 -10
  257. package/docs/webapi/waitForVisible.mustache +0 -10
  258. package/docs/webapi/waitInUrl.mustache +0 -9
  259. package/docs/webapi/waitNumberOfVisibleElements.mustache +0 -10
  260. package/docs/webapi/waitToHide.mustache +0 -10
  261. package/docs/webapi/waitUrlEquals.mustache +0 -10
  262. package/docs/webdriver.md +0 -657
  263. package/docs/wiki/Books-&-Posts.md +0 -27
  264. package/docs/wiki/Community-Helpers-&-Plugins.md +0 -49
  265. package/docs/wiki/Converting-Playwright-to-Istanbul-Coverage.md +0 -29
  266. package/docs/wiki/Examples.md +0 -139
  267. package/docs/wiki/Google-Summer-of-Code-(GSoC)-2020.md +0 -68
  268. package/docs/wiki/Home.md +0 -16
  269. package/docs/wiki/Release-Process.md +0 -24
  270. package/docs/wiki/Roadmap.md +0 -23
  271. package/docs/wiki/Tests.md +0 -1393
  272. package/docs/wiki/Upgrading-to-CodeceptJS-3.md +0 -153
  273. package/docs/wiki/Videos.md +0 -19
@@ -30,6 +30,8 @@ const ConnectionRefused = require('./errors/ConnectionRefused');
30
30
  const Locator = require('../locator');
31
31
  const { highlightElement } = require('./scripts/highlightElement');
32
32
  const store = require('../store');
33
+ const { focusElement } = require('./scripts/focusElement');
34
+ const { blurElement } = require('./scripts/blurElement');
33
35
 
34
36
  const SHADOW = 'shadow';
35
37
  const webRoot = 'body';
@@ -60,7 +62,9 @@ const webRoot = 'body';
60
62
  * @prop {object} [desiredCapabilities] Selenium's [desired capabilities](https://github.com/SeleniumHQ/selenium/wiki/DesiredCapabilities).
61
63
  * @prop {boolean} [manualStart=false] - do not start browser before a test, start it manually inside a helper with `this.helpers["WebDriver"]._startBrowser()`.
62
64
  * @prop {object} [timeouts] [WebDriver timeouts](http://webdriver.io/docs/timeouts.html) defined as hash.
63
- * @prop {boolean} [highlightElement] - highlight the interacting elements
65
+ * @prop {boolean} [highlightElement] - highlight the interacting elements. Default: false. Note: only activate under verbose mode (--verbose).
66
+ * @prop {string} [logLevel=silent] - level of logging verbosity. Default: silent. Options: trace | debug | info | warn | error | silent. More info: https://webdriver.io/docs/configuration/#loglevel
67
+ * @prop {boolean} [devtoolsProtocol=false] - enable devtools protocol. Default: false. More info: https://webdriver.io/docs/automationProtocols/#devtools-protocol.
64
68
  */
65
69
  const config = {};
66
70
 
@@ -70,6 +74,13 @@ const config = {};
70
74
  *
71
75
  * WebDriver requires Selenium Server and ChromeDriver/GeckoDriver to be installed. Those tools can be easily installed via NPM. Please check [Testing with WebDriver](https://codecept.io/webdriver/#testing-with-webdriver) for more details.
72
76
  *
77
+ * With the release of WebdriverIO version v8.14.0, and onwards, all driver management hassles are now a thing of the past 🙌. Read more [here](https://webdriver.io/blog/2023/07/31/driver-management/).
78
+ * One of the significant advantages of this update is that you can now get rid of any driver services you previously had to manage, such as
79
+ * `wdio-chromedriver-service`, `wdio-geckodriver-service`, `wdio-edgedriver-service`, `wdio-safaridriver-service`, and even `@wdio/selenium-standalone-service`.
80
+ *
81
+ * For those who require custom driver options, fear not; WebDriver Helper allows you to pass in driver options through custom WebDriver configuration.
82
+ * If you have a custom grid, use a cloud service, or prefer to run your own driver, there's no need to worry since WebDriver Helper will only start a driver when there are no other connection information settings like hostname or port specified.
83
+ *
73
84
  * <!-- configuration -->
74
85
  *
75
86
  * Example:
@@ -91,6 +102,28 @@ const config = {};
91
102
  * }
92
103
  * ```
93
104
  *
105
+ * Testing Chrome locally is now more convenient than ever. You can define a browser channel, and WebDriver Helper will take care of downloading the specified browser version for you.
106
+ * For example:
107
+ *
108
+ * ```js
109
+ * {
110
+ * helpers: {
111
+ * WebDriver : {
112
+ * smartWait: 5000,
113
+ * browser: "chrome",
114
+ * browserVersion: '116.0.5793.0', // or 'stable', 'beta', 'dev' or 'canary'
115
+ * restart: false,
116
+ * windowSize: "maximize",
117
+ * timeouts: {
118
+ * "script": 60000,
119
+ * "page load": 10000
120
+ * }
121
+ * }
122
+ * }
123
+ * }
124
+ * ```
125
+ *
126
+ *
94
127
  * Example with basic authentication
95
128
  * ```js
96
129
  * {
@@ -131,6 +164,25 @@ const config = {};
131
164
  * }
132
165
  * ```
133
166
  *
167
+ * ### Running with devtools protocol
168
+ *
169
+ * ```js
170
+ * {
171
+ * helpers: {
172
+ * WebDriver : {
173
+ * url: "http://localhost",
174
+ * browser: "chrome",
175
+ * devtoolsProtocol: true,
176
+ * desiredCapabilities: {
177
+ * chromeOptions: {
178
+ * args: [ "--headless", "--disable-gpu", "--no-sandbox" ]
179
+ * }
180
+ * }
181
+ * }
182
+ * }
183
+ * }
184
+ * ```
185
+ *
134
186
  * ### Internet Explorer
135
187
  *
136
188
  * Additional configuration params can be used from [IE options](https://seleniumhq.github.io/selenium/docs/api/rb/Selenium/WebDriver/IE/Options.html)
@@ -413,7 +465,6 @@ class WebDriver extends Helper {
413
465
  _validateConfig(config) {
414
466
  const defaults = {
415
467
  logLevel: 'silent',
416
- path: '/wd/hub',
417
468
  // codeceptjs
418
469
  remoteFileUpload: true,
419
470
  smartWait: 0,
@@ -427,17 +478,23 @@ class WebDriver extends Helper {
427
478
  keepCookies: false,
428
479
  keepBrowserState: false,
429
480
  deprecationWarnings: false,
481
+ highlightElement: false,
430
482
  };
431
483
 
432
484
  // override defaults with config
433
485
  config = Object.assign(defaults, config);
434
486
 
435
- if (typeof config.host !== 'undefined') config.hostname = config.host; // webdriverio spec
487
+ if (config.host) {
488
+ // webdriverio spec
489
+ config.hostname = config.host;
490
+ config.path = '/wd/hub';
491
+ }
436
492
  config.baseUrl = config.url || config.baseUrl;
437
493
  if (config.desiredCapabilities && Object.keys(config.desiredCapabilities).length) {
438
494
  config.capabilities = config.desiredCapabilities;
439
495
  }
440
496
  config.capabilities.browserName = config.browser || config.capabilities.browserName;
497
+ config.capabilities.browserVersion = config.browserVersion || config.capabilities.browserVersion;
441
498
  if (config.capabilities.chromeOptions) {
442
499
  config.capabilities['goog:chromeOptions'] = config.capabilities.chromeOptions;
443
500
  delete config.capabilities.chromeOptions;
@@ -539,6 +596,10 @@ class WebDriver extends Helper {
539
596
  delete this.options.capabilities.hostname;
540
597
  delete this.options.capabilities.port;
541
598
  delete this.options.capabilities.path;
599
+ if (this.options.devtoolsProtocol) {
600
+ if (!['chrome', 'chromium'].includes(this.options.browser.toLowerCase())) throw Error('The devtools protocol is only working with Chrome or Chromium');
601
+ this.options.automationProtocol = 'devtools';
602
+ }
542
603
  this.browser = await webdriverio.remote(this.options);
543
604
  }
544
605
  } catch (err) {
@@ -866,6 +927,14 @@ class WebDriver extends Helper {
866
927
  return findFields.call(this, locator).then(res => res);
867
928
  }
868
929
 
930
+ /**
931
+ * {{> grabWebElements }}
932
+ *
933
+ */
934
+ async grabWebElements(locator) {
935
+ return this._locate(locator);
936
+ }
937
+
869
938
  /**
870
939
  * Set [WebDriver timeouts](https://webdriver.io/docs/timeouts.html) in realtime.
871
940
  *
@@ -1032,7 +1101,8 @@ class WebDriver extends Helper {
1032
1101
  assertElementExists(res, field, 'Field');
1033
1102
  const elem = usingFirstElement(res);
1034
1103
  highlightActiveElement.call(this, elem);
1035
- return elem.setValue(value.toString());
1104
+ await elem.clearValue();
1105
+ await elem.setValue(value.toString());
1036
1106
  }
1037
1107
 
1038
1108
  /**
@@ -1044,6 +1114,10 @@ class WebDriver extends Helper {
1044
1114
  assertElementExists(res, field, 'Field');
1045
1115
  const elem = usingFirstElement(res);
1046
1116
  highlightActiveElement.call(this, elem);
1117
+ if (this.options.automationProtocol) {
1118
+ const curentValue = await elem.getValue();
1119
+ return elem.setValue(curentValue + value.toString());
1120
+ }
1047
1121
  return elem.addValue(value.toString());
1048
1122
  }
1049
1123
 
@@ -1056,6 +1130,9 @@ class WebDriver extends Helper {
1056
1130
  assertElementExists(res, field, 'Field');
1057
1131
  const elem = usingFirstElement(res);
1058
1132
  highlightActiveElement.call(this, elem);
1133
+ if (this.options.automationProtocol) {
1134
+ return elem.setValue('');
1135
+ }
1059
1136
  return elem.clearValue(getElementId(elem));
1060
1137
  }
1061
1138
 
@@ -1093,8 +1170,9 @@ class WebDriver extends Helper {
1093
1170
  }
1094
1171
 
1095
1172
  /**
1096
- * {{> attachFile }}
1097
1173
  * Appium: not tested
1174
+ *
1175
+ * {{> attachFile }}
1098
1176
  */
1099
1177
  async attachFile(locator, pathToFile) {
1100
1178
  let file = path.join(global.codecept_dir, pathToFile);
@@ -1108,7 +1186,7 @@ class WebDriver extends Helper {
1108
1186
  const el = usingFirstElement(res);
1109
1187
 
1110
1188
  // Remote Upload (when running Selenium Server)
1111
- if (this.options.remoteFileUpload) {
1189
+ if (this.options.remoteFileUpload && !this.options.automationProtocol) {
1112
1190
  try {
1113
1191
  this.debugSection('File', 'Uploading file to remote server');
1114
1192
  file = await this.browser.uploadFile(file);
@@ -1121,8 +1199,8 @@ class WebDriver extends Helper {
1121
1199
  }
1122
1200
 
1123
1201
  /**
1124
- * {{> checkOption }}
1125
1202
  * Appium: not tested
1203
+ * {{> checkOption }}
1126
1204
  */
1127
1205
  async checkOption(field, context = null) {
1128
1206
  const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
@@ -1141,8 +1219,8 @@ class WebDriver extends Helper {
1141
1219
  }
1142
1220
 
1143
1221
  /**
1144
- * {{> uncheckOption }}
1145
1222
  * Appium: not tested
1223
+ * {{> uncheckOption }}
1146
1224
  */
1147
1225
  async uncheckOption(field, context = null) {
1148
1226
  const clickMethod = this.browser.isMobile && !this.browser.isW3C ? 'touchClick' : 'elementClick';
@@ -1345,7 +1423,8 @@ class WebDriver extends Helper {
1345
1423
  *
1346
1424
  */
1347
1425
  async seeInField(field, value) {
1348
- return proceedSeeField.call(this, 'assert', field, value);
1426
+ const _value = (typeof value === 'boolean') ? value : value.toString();
1427
+ return proceedSeeField.call(this, 'assert', field, _value);
1349
1428
  }
1350
1429
 
1351
1430
  /**
@@ -1353,20 +1432,21 @@ class WebDriver extends Helper {
1353
1432
  *
1354
1433
  */
1355
1434
  async dontSeeInField(field, value) {
1356
- return proceedSeeField.call(this, 'negate', field, value);
1435
+ const _value = (typeof value === 'boolean') ? value : value.toString();
1436
+ return proceedSeeField.call(this, 'negate', field, _value);
1357
1437
  }
1358
1438
 
1359
1439
  /**
1360
- * {{> seeCheckboxIsChecked }}
1361
1440
  * Appium: not tested
1441
+ * {{> seeCheckboxIsChecked }}
1362
1442
  */
1363
1443
  async seeCheckboxIsChecked(field) {
1364
1444
  return proceedSeeCheckbox.call(this, 'assert', field);
1365
1445
  }
1366
1446
 
1367
1447
  /**
1368
- * {{> dontSeeCheckboxIsChecked }}
1369
1448
  * Appium: not tested
1449
+ * {{> dontSeeCheckboxIsChecked }}
1370
1450
  */
1371
1451
  async dontSeeCheckboxIsChecked(field) {
1372
1452
  return proceedSeeCheckbox.call(this, 'negate', field);
@@ -1484,33 +1564,33 @@ class WebDriver extends Helper {
1484
1564
  async seeCssPropertiesOnElements(locator, cssProperties) {
1485
1565
  const res = await this._locate(locator);
1486
1566
  assertElementExists(res, locator);
1567
+
1568
+ const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
1487
1569
  const elemAmount = res.length;
1570
+ let props = [];
1488
1571
 
1489
- let props = await forEachAsync(res, async (el) => {
1490
- return forEachAsync(Object.keys(cssProperties), async (prop) => {
1491
- const propValue = await this.browser.getElementCSSValue(getElementId(el), prop);
1492
- if (isColorProperty(prop) && propValue && propValue.value) {
1493
- return convertColorToRGBA(propValue.value);
1572
+ for (const element of res) {
1573
+ for (const prop of Object.keys(cssProperties)) {
1574
+ const cssProp = await this.grabCssPropertyFrom(locator, prop);
1575
+ if (isColorProperty(prop)) {
1576
+ props.push(convertColorToRGBA(cssProp));
1577
+ } else {
1578
+ props.push(cssProp);
1494
1579
  }
1495
- return propValue;
1496
- });
1497
- });
1498
-
1499
- const cssPropertiesCamelCase = convertCssPropertiesToCamelCase(cssProperties);
1580
+ }
1581
+ }
1500
1582
 
1501
1583
  const values = Object.keys(cssPropertiesCamelCase).map(key => cssPropertiesCamelCase[key]);
1502
1584
  if (!Array.isArray(props)) props = [props];
1503
1585
  let chunked = chunkArray(props, values.length);
1504
1586
  chunked = chunked.filter((val) => {
1505
1587
  for (let i = 0; i < val.length; ++i) {
1506
- if (val[i] !== values[i]) return false;
1588
+ // eslint-disable-next-line eqeqeq
1589
+ if (val[i] != values[i]) return false;
1507
1590
  }
1508
1591
  return true;
1509
1592
  });
1510
- return assert.ok(
1511
- chunked.length === elemAmount,
1512
- `expected all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`,
1513
- );
1593
+ return equals(`all elements (${(new Locator(locator))}) to have CSS property ${JSON.stringify(cssProperties)}`).assert(chunked.length, elemAmount);
1514
1594
  }
1515
1595
 
1516
1596
  /**
@@ -1530,7 +1610,9 @@ class WebDriver extends Helper {
1530
1610
  let chunked = chunkArray(attrs, values.length);
1531
1611
  chunked = chunked.filter((val) => {
1532
1612
  for (let i = 0; i < val.length; ++i) {
1533
- if (val[i] !== values[i]) return false;
1613
+ const _actual = Number.isNaN(val[i]) || (typeof values[i]) === 'string' ? val[i] : Number.parseInt(val[i], 10);
1614
+ const _expected = Number.isNaN(values[i]) || (typeof values[i]) === 'string' ? values[i] : Number.parseInt(values[i], 10);
1615
+ if (_actual !== _expected) return false;
1534
1616
  }
1535
1617
  return true;
1536
1618
  });
@@ -1589,10 +1671,9 @@ class WebDriver extends Helper {
1589
1671
  }
1590
1672
 
1591
1673
  /**
1592
- * {{> executeScript }}
1593
- *
1594
- *
1595
1674
  * Wraps [execute](http://webdriver.io/api/protocol/execute.html) command.
1675
+ *
1676
+ * {{> executeScript }}
1596
1677
  */
1597
1678
  executeScript(...args) {
1598
1679
  return this.browser.execute.apply(this.browser, args);
@@ -1655,7 +1736,11 @@ class WebDriver extends Helper {
1655
1736
  const res = await this._locate(withStrictLocator(locator), true);
1656
1737
  assertElementExists(res, locator);
1657
1738
  const elem = usingFirstElement(res);
1658
- return elem.moveTo({ xOffset, yOffset });
1739
+ try {
1740
+ await elem.moveTo({ xOffset, yOffset });
1741
+ } catch (e) {
1742
+ debug(e.message);
1743
+ }
1659
1744
  }
1660
1745
 
1661
1746
  /**
@@ -1714,11 +1799,8 @@ class WebDriver extends Helper {
1714
1799
  }
1715
1800
 
1716
1801
  /**
1802
+ * Uses Selenium's JSON [cookie format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
1717
1803
  * {{> setCookie }}
1718
- *
1719
- *
1720
- * Uses Selenium's JSON [cookie
1721
- * format](https://code.google.com/p/selenium/wiki/JsonWireProtocol#Cookie_JSON_Object).
1722
1804
  */
1723
1805
  async setCookie(cookie) {
1724
1806
  return this.browser.setCookies(cookie);
@@ -1771,7 +1853,7 @@ class WebDriver extends Helper {
1771
1853
  }
1772
1854
 
1773
1855
  /**
1774
- * Dismisses the active JavaScript popup, as created by window.alert|window.confirm|window.prompt.
1856
+ * Dismisses the active JavaScript popup, as created by `window.alert|window.confirm|window.prompt`.
1775
1857
  *
1776
1858
  */
1777
1859
  async cancelPopup() {
@@ -1845,9 +1927,9 @@ class WebDriver extends Helper {
1845
1927
  }
1846
1928
 
1847
1929
  /**
1848
- * {{> pressKeyWithKeyNormalization }}
1849
- *
1850
1930
  * _Note:_ In case a text field or textarea is focused be aware that some browsers do not respect active modifier when combining modifier keys with other keys.
1931
+ *
1932
+ * {{> pressKeyWithKeyNormalization }}
1851
1933
  */
1852
1934
  async pressKey(key) {
1853
1935
  const modifiers = [];
@@ -1906,11 +1988,12 @@ class WebDriver extends Helper {
1906
1988
  }
1907
1989
 
1908
1990
  /**
1909
- * {{> resizeWindow }}
1910
1991
  * Appium: not tested in web, in apps doesn't work
1992
+ *
1993
+ * {{> resizeWindow }}
1911
1994
  */
1912
1995
  async resizeWindow(width, height) {
1913
- return this._resizeBrowserWindow(this.browser, width, height);
1996
+ return this.browser.setWindowSize(width, height);
1914
1997
  }
1915
1998
 
1916
1999
  async _resizeBrowserWindow(browser, width, height) {
@@ -1935,8 +2018,32 @@ class WebDriver extends Helper {
1935
2018
  }
1936
2019
 
1937
2020
  /**
1938
- * {{> dragAndDrop }}
2021
+ * {{> focus }}
2022
+ *
2023
+ */
2024
+ async focus(locator) {
2025
+ const els = await this._locate(locator);
2026
+ assertElementExists(els, locator, 'Element to focus');
2027
+ const el = usingFirstElement(els);
2028
+
2029
+ await focusElement(el, this.browser);
2030
+ }
2031
+
2032
+ /**
2033
+ * {{> blur }}
2034
+ *
2035
+ */
2036
+ async blur(locator) {
2037
+ const els = await this._locate(locator);
2038
+ assertElementExists(els, locator, 'Element to blur');
2039
+ const el = usingFirstElement(els);
2040
+
2041
+ await blurElement(el, this.browser);
2042
+ }
2043
+
2044
+ /**
1939
2045
  * Appium: not tested
2046
+ * {{> dragAndDrop }}
1940
2047
  */
1941
2048
  async dragAndDrop(srcElement, destElement) {
1942
2049
  let sourceEl = await this._locate(srcElement);
@@ -2036,7 +2143,9 @@ class WebDriver extends Helper {
2036
2143
  * {{> wait }}
2037
2144
  */
2038
2145
  async wait(sec) {
2039
- return new Promise(resolve => setTimeout(resolve, sec * 1000));
2146
+ return new Promise(resolve => {
2147
+ setTimeout(resolve, sec * 1000);
2148
+ });
2040
2149
  }
2041
2150
 
2042
2151
  /**
@@ -2141,20 +2250,18 @@ class WebDriver extends Helper {
2141
2250
  const aSec = sec || this.options.waitForTimeoutInSeconds;
2142
2251
  const _context = context || this.root;
2143
2252
 
2144
- return this.browser.waitUntil(
2145
- async () => {
2146
- const res = await this.$$(withStrictLocator.call(this, _context));
2147
- if (!res || res.length === 0) return false;
2148
- const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)));
2149
- if (Array.isArray(selected)) {
2150
- return selected.filter(part => part.indexOf(text) >= 0).length > 0;
2151
- }
2152
- return selected.indexOf(text) >= 0;
2153
- }, {
2154
- timeout: aSec * 1000,
2155
- timeoutMsg: `element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
2156
- },
2157
- );
2253
+ return this.browser.waitUntil(async () => {
2254
+ const res = await this.$$(withStrictLocator.call(this, _context));
2255
+ if (!res || res.length === 0) return false;
2256
+ const selected = await forEachAsync(res, async el => this.browser.getElementText(getElementId(el)));
2257
+ if (Array.isArray(selected)) {
2258
+ return selected.filter(part => part.indexOf(text) >= 0).length > 0;
2259
+ }
2260
+ return selected.indexOf(text) >= 0;
2261
+ }, {
2262
+ timeout: aSec * 1000,
2263
+ timeoutMsg: `element (${_context}) is not in DOM or there is no element(${_context}) with text "${text}" after ${aSec} sec`,
2264
+ });
2158
2265
  }
2159
2266
 
2160
2267
  /**
@@ -2164,20 +2271,18 @@ class WebDriver extends Helper {
2164
2271
  const client = this.browser;
2165
2272
  const aSec = sec || this.options.waitForTimeoutInSeconds;
2166
2273
 
2167
- return client.waitUntil(
2168
- async () => {
2169
- const res = await findFields.call(this, field);
2170
- if (!res || res.length === 0) return false;
2171
- const selected = await forEachAsync(res, async el => el.getValue());
2172
- if (Array.isArray(selected)) {
2173
- return selected.filter(part => part.indexOf(value) >= 0).length > 0;
2174
- }
2175
- return selected.indexOf(value) >= 0;
2176
- }, {
2177
- timeout: aSec * 1000,
2178
- timeoutMsg: `element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
2179
- },
2180
- );
2274
+ return client.waitUntil(async () => {
2275
+ const res = await findFields.call(this, field);
2276
+ if (!res || res.length === 0) return false;
2277
+ const selected = await forEachAsync(res, async el => el.getValue());
2278
+ if (Array.isArray(selected)) {
2279
+ return selected.filter(part => part.indexOf(value) >= 0).length > 0;
2280
+ }
2281
+ return selected.indexOf(value) >= 0;
2282
+ }, {
2283
+ timeout: aSec * 1000,
2284
+ timeoutMsg: `element (${field}) is not in DOM or there is no element(${field}) with value "${value}" after ${aSec} sec`,
2285
+ });
2181
2286
  }
2182
2287
 
2183
2288
  /**
@@ -2269,12 +2374,33 @@ class WebDriver extends Helper {
2269
2374
  return this.browser.waitUntil(async () => this.browser.execute(fn, ...args), { timeout: aSec * 1000, timeoutMsg: '' });
2270
2375
  }
2271
2376
 
2377
+ /**
2378
+ * {{> waitForNumberOfTabs }}
2379
+ */
2380
+ async waitForNumberOfTabs(expectedTabs, sec) {
2381
+ const waitTimeout = sec ? sec * 1000 : this.options.waitForTimeoutInSeconds;
2382
+ let currentTabs;
2383
+ let count = 0;
2384
+
2385
+ do {
2386
+ currentTabs = await this.grabNumberOfOpenTabs();
2387
+ await this.wait(1);
2388
+ count += 1000;
2389
+ if (currentTabs >= expectedTabs) return;
2390
+ } while (count <= waitTimeout);
2391
+
2392
+ throw new Error(`Expected ${expectedTabs} tabs are not met after ${waitTimeout / 1000} sec.`);
2393
+ }
2394
+
2272
2395
  /**
2273
2396
  * {{> switchTo }}
2274
2397
  */
2275
2398
  async switchTo(locator) {
2276
2399
  this.browser.isInsideFrame = true;
2277
2400
  if (Number.isInteger(locator)) {
2401
+ if (this.options.automationProtocol) {
2402
+ return this.browser.switchToFrame(locator + 1);
2403
+ }
2278
2404
  return this.browser.switchToFrame(locator);
2279
2405
  }
2280
2406
  if (!locator) {
@@ -2385,8 +2511,11 @@ class WebDriver extends Helper {
2385
2511
  const body = document.body;
2386
2512
  const html = document.documentElement;
2387
2513
  window.scrollTo(0, Math.max(
2388
- body.scrollHeight, body.offsetHeight,
2389
- html.clientHeight, html.scrollHeight, html.offsetHeight
2514
+ body.scrollHeight,
2515
+ body.offsetHeight,
2516
+ html.clientHeight,
2517
+ html.scrollHeight,
2518
+ html.offsetHeight
2390
2519
  ));
2391
2520
  });
2392
2521
  /* eslint-enable */
@@ -2408,21 +2537,41 @@ class WebDriver extends Helper {
2408
2537
  }
2409
2538
 
2410
2539
  /**
2540
+ * This method is **deprecated**.
2541
+ *
2542
+ *
2411
2543
  * {{> setGeoLocation }}
2412
2544
  */
2413
- async setGeoLocation(latitude, longitude, altitude = null) {
2414
- if (altitude) {
2415
- return this.browser.setGeoLocation({ latitude, longitude });
2545
+ async setGeoLocation(latitude, longitude) {
2546
+ if (!this.options.automationProtocol) {
2547
+ console.log(`setGeoLocation deprecated:
2548
+ * This command is deprecated due to using deprecated JSON Wire Protocol command. More info: https://webdriver.io/docs/api/jsonwp/#setgeolocation
2549
+ * Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration`);
2550
+ return;
2416
2551
  }
2417
- return this.browser.setGeoLocation({ latitude, longitude, altitude });
2552
+ this.geoLocation = { latitude, longitude };
2553
+ const puppeteerBrowser = await this.browser.getPuppeteer();
2554
+ await this.browser.call(async () => {
2555
+ const pages = await puppeteerBrowser.pages();
2556
+ await pages[0].setGeolocation({ latitude, longitude });
2557
+ });
2418
2558
  }
2419
2559
 
2420
2560
  /**
2561
+ * This method is **deprecated**.
2562
+ *
2421
2563
  * {{> grabGeoLocation }}
2422
2564
  *
2423
2565
  */
2424
2566
  async grabGeoLocation() {
2425
- return this.browser.getGeoLocation();
2567
+ if (!this.options.automationProtocol) {
2568
+ console.log(`grabGeoLocation deprecated:
2569
+ * This command is deprecated due to using deprecated JSON Wire Protocol command. More info: https://webdriver.io/docs/api/jsonwp/#getgeolocation
2570
+ * Switch to devtools protocol to use this command by setting devtoolsProtocol: true in the configuration`);
2571
+ return;
2572
+ }
2573
+ if (!this.geoLocation) return 'No GeoLocation is set!';
2574
+ return this.geoLocation;
2426
2575
  }
2427
2576
 
2428
2577
  /**
@@ -2618,7 +2767,7 @@ async function proceedSeeField(assertType, field, value) {
2618
2767
  }
2619
2768
  };
2620
2769
 
2621
- const proceedSingle = el => this.browser.getElementAttribute(getElementId(el), 'value').then((res) => {
2770
+ const proceedSingle = el => el.getValue().then((res) => {
2622
2771
  if (res === null) {
2623
2772
  throw new Error(`Element ${el.selector} has no value attribute`);
2624
2773
  }
@@ -2888,9 +3037,9 @@ function isModifierKey(key) {
2888
3037
  }
2889
3038
 
2890
3039
  function highlightActiveElement(element) {
2891
- if (!this.options.enableHighlight && !store.debugMode) return;
2892
-
2893
- highlightElement(element, this.browser);
3040
+ if (this.options.highlightElement && global.debugMode) {
3041
+ highlightElement(element, this.browser);
3042
+ }
2894
3043
  }
2895
3044
 
2896
3045
  function prepareLocateFn(context) {
@@ -5,7 +5,8 @@ const Locator = require('../../locator');
5
5
  */
6
6
  class ElementNotFound {
7
7
  constructor(
8
- locator, prefixMessage = 'Element',
8
+ locator,
9
+ prefixMessage = 'Element',
9
10
  postfixMessage = 'was not found by text|CSS|XPath',
10
11
  ) {
11
12
  if (typeof locator === 'object') {
@@ -0,0 +1,38 @@
1
+ async function findReact(matcher, locator) {
2
+ let _locator = `_react=${locator.react}`;
3
+ let props = '';
4
+
5
+ if (locator.props) {
6
+ props += propBuilder(locator.props);
7
+ _locator += props;
8
+ }
9
+ return matcher.locator(_locator).all();
10
+ }
11
+
12
+ async function findVue(matcher, locator) {
13
+ let _locator = `_vue=${locator.vue}`;
14
+ let props = '';
15
+
16
+ if (locator.props) {
17
+ props += propBuilder(locator.props);
18
+ _locator += props;
19
+ }
20
+ return matcher.locator(_locator).all();
21
+ }
22
+
23
+ function propBuilder(props) {
24
+ let _props = '';
25
+
26
+ for (const [key, value] of Object.entries(props)) {
27
+ if (typeof value === 'object') {
28
+ for (const [k, v] of Object.entries(value)) {
29
+ _props += `[${key}.${k} = "${v}"]`;
30
+ }
31
+ } else {
32
+ _props += `[${key} = "${value}"]`;
33
+ }
34
+ }
35
+ return _props;
36
+ }
37
+
38
+ module.exports = { findReact, findVue };
@@ -0,0 +1,17 @@
1
+ module.exports.blurElement = (element, context) => {
2
+ const clientSideBlurFn = el => {
3
+ el.blur();
4
+ };
5
+
6
+ try {
7
+ // Puppeteer
8
+ context.evaluate(clientSideBlurFn, element);
9
+ } catch (e) {
10
+ // WebDriver
11
+ try {
12
+ context.execute(clientSideBlurFn, element);
13
+ } catch (err) {
14
+ // ignore
15
+ }
16
+ }
17
+ };