@serenity-js/webdriverio-8 3.31.0

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 (285) hide show
  1. package/CHANGELOG.md +1232 -0
  2. package/LICENSE.md +201 -0
  3. package/NOTICE.md +1 -0
  4. package/README.md +234 -0
  5. package/esm/adapter/TestRunnerLoader.d.ts +17 -0
  6. package/esm/adapter/TestRunnerLoader.d.ts.map +1 -0
  7. package/esm/adapter/TestRunnerLoader.js +63 -0
  8. package/esm/adapter/TestRunnerLoader.js.map +1 -0
  9. package/esm/adapter/WebdriverIOFrameworkAdapter.d.ts +25 -0
  10. package/esm/adapter/WebdriverIOFrameworkAdapter.d.ts.map +1 -0
  11. package/esm/adapter/WebdriverIOFrameworkAdapter.js +90 -0
  12. package/esm/adapter/WebdriverIOFrameworkAdapter.js.map +1 -0
  13. package/esm/adapter/WebdriverIOFrameworkAdapterFactory.d.ts +18 -0
  14. package/esm/adapter/WebdriverIOFrameworkAdapterFactory.d.ts.map +1 -0
  15. package/esm/adapter/WebdriverIOFrameworkAdapterFactory.js +18 -0
  16. package/esm/adapter/WebdriverIOFrameworkAdapterFactory.js.map +1 -0
  17. package/esm/adapter/WebdriverIONotifier.d.ts +71 -0
  18. package/esm/adapter/WebdriverIONotifier.d.ts.map +1 -0
  19. package/esm/adapter/WebdriverIONotifier.js +390 -0
  20. package/esm/adapter/WebdriverIONotifier.js.map +1 -0
  21. package/esm/adapter/index.d.ts +2 -0
  22. package/esm/adapter/index.d.ts.map +1 -0
  23. package/esm/adapter/index.js +2 -0
  24. package/esm/adapter/index.js.map +1 -0
  25. package/esm/adapter/reporter/BrowserCapabilitiesReporter.d.ts +18 -0
  26. package/esm/adapter/reporter/BrowserCapabilitiesReporter.d.ts.map +1 -0
  27. package/esm/adapter/reporter/BrowserCapabilitiesReporter.js +42 -0
  28. package/esm/adapter/reporter/BrowserCapabilitiesReporter.js.map +1 -0
  29. package/esm/adapter/reporter/InitialisesReporters.d.ts +8 -0
  30. package/esm/adapter/reporter/InitialisesReporters.d.ts.map +1 -0
  31. package/esm/adapter/reporter/InitialisesReporters.js +2 -0
  32. package/esm/adapter/reporter/InitialisesReporters.js.map +1 -0
  33. package/esm/adapter/reporter/OutputStreamBuffer.d.ts +13 -0
  34. package/esm/adapter/reporter/OutputStreamBuffer.d.ts.map +1 -0
  35. package/esm/adapter/reporter/OutputStreamBuffer.js +25 -0
  36. package/esm/adapter/reporter/OutputStreamBuffer.js.map +1 -0
  37. package/esm/adapter/reporter/OutputStreamBufferPrinter.d.ts +16 -0
  38. package/esm/adapter/reporter/OutputStreamBufferPrinter.d.ts.map +1 -0
  39. package/esm/adapter/reporter/OutputStreamBufferPrinter.js +24 -0
  40. package/esm/adapter/reporter/OutputStreamBufferPrinter.js.map +1 -0
  41. package/esm/adapter/reporter/ProvidesWriteStream.d.ts +8 -0
  42. package/esm/adapter/reporter/ProvidesWriteStream.d.ts.map +1 -0
  43. package/esm/adapter/reporter/ProvidesWriteStream.js +2 -0
  44. package/esm/adapter/reporter/ProvidesWriteStream.js.map +1 -0
  45. package/esm/adapter/reporter/TagPrinter.d.ts +17 -0
  46. package/esm/adapter/reporter/TagPrinter.d.ts.map +1 -0
  47. package/esm/adapter/reporter/TagPrinter.js +52 -0
  48. package/esm/adapter/reporter/TagPrinter.js.map +1 -0
  49. package/esm/adapter/reporter/index.d.ts +6 -0
  50. package/esm/adapter/reporter/index.d.ts.map +1 -0
  51. package/esm/adapter/reporter/index.js +6 -0
  52. package/esm/adapter/reporter/index.js.map +1 -0
  53. package/esm/api.d.ts +3 -0
  54. package/esm/api.d.ts.map +1 -0
  55. package/esm/api.js +2 -0
  56. package/esm/api.js.map +1 -0
  57. package/esm/config/WebdriverIOConfig.d.ts +95 -0
  58. package/esm/config/WebdriverIOConfig.d.ts.map +1 -0
  59. package/esm/config/WebdriverIOConfig.js +2 -0
  60. package/esm/config/WebdriverIOConfig.js.map +1 -0
  61. package/esm/config/index.d.ts +2 -0
  62. package/esm/config/index.d.ts.map +1 -0
  63. package/esm/config/index.js +2 -0
  64. package/esm/config/index.js.map +1 -0
  65. package/esm/index.d.ts +16 -0
  66. package/esm/index.d.ts.map +1 -0
  67. package/esm/index.js +14 -0
  68. package/esm/index.js.map +1 -0
  69. package/esm/screenplay/abilities/BrowseTheWebWithWebdriverIO.d.ts +40 -0
  70. package/esm/screenplay/abilities/BrowseTheWebWithWebdriverIO.d.ts.map +1 -0
  71. package/esm/screenplay/abilities/BrowseTheWebWithWebdriverIO.js +43 -0
  72. package/esm/screenplay/abilities/BrowseTheWebWithWebdriverIO.js.map +1 -0
  73. package/esm/screenplay/abilities/index.d.ts +2 -0
  74. package/esm/screenplay/abilities/index.d.ts.map +1 -0
  75. package/esm/screenplay/abilities/index.js +2 -0
  76. package/esm/screenplay/abilities/index.js.map +1 -0
  77. package/esm/screenplay/index.d.ts +3 -0
  78. package/esm/screenplay/index.d.ts.map +1 -0
  79. package/esm/screenplay/index.js +3 -0
  80. package/esm/screenplay/index.js.map +1 -0
  81. package/esm/screenplay/models/WebdriverIOBrowsingSession.d.ts +25 -0
  82. package/esm/screenplay/models/WebdriverIOBrowsingSession.d.ts.map +1 -0
  83. package/esm/screenplay/models/WebdriverIOBrowsingSession.js +125 -0
  84. package/esm/screenplay/models/WebdriverIOBrowsingSession.js.map +1 -0
  85. package/esm/screenplay/models/WebdriverIOCookie.d.ts +15 -0
  86. package/esm/screenplay/models/WebdriverIOCookie.d.ts.map +1 -0
  87. package/esm/screenplay/models/WebdriverIOCookie.js +42 -0
  88. package/esm/screenplay/models/WebdriverIOCookie.js.map +1 -0
  89. package/esm/screenplay/models/WebdriverIOErrorHandler.d.ts +9 -0
  90. package/esm/screenplay/models/WebdriverIOErrorHandler.d.ts.map +1 -0
  91. package/esm/screenplay/models/WebdriverIOErrorHandler.js +20 -0
  92. package/esm/screenplay/models/WebdriverIOErrorHandler.js.map +1 -0
  93. package/esm/screenplay/models/WebdriverIOModalDialogHandler.d.ts +27 -0
  94. package/esm/screenplay/models/WebdriverIOModalDialogHandler.d.ts.map +1 -0
  95. package/esm/screenplay/models/WebdriverIOModalDialogHandler.js +74 -0
  96. package/esm/screenplay/models/WebdriverIOModalDialogHandler.js.map +1 -0
  97. package/esm/screenplay/models/WebdriverIOPage.d.ts +50 -0
  98. package/esm/screenplay/models/WebdriverIOPage.d.ts.map +1 -0
  99. package/esm/screenplay/models/WebdriverIOPage.js +219 -0
  100. package/esm/screenplay/models/WebdriverIOPage.js.map +1 -0
  101. package/esm/screenplay/models/WebdriverIOPageElement.d.ts +34 -0
  102. package/esm/screenplay/models/WebdriverIOPageElement.d.ts.map +1 -0
  103. package/esm/screenplay/models/WebdriverIOPageElement.js +220 -0
  104. package/esm/screenplay/models/WebdriverIOPageElement.js.map +1 -0
  105. package/esm/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.d.ts +30 -0
  106. package/esm/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.d.ts.map +1 -0
  107. package/esm/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.js +77 -0
  108. package/esm/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.js.map +1 -0
  109. package/esm/screenplay/models/WebdriverProtocolErrorCode.d.ts +39 -0
  110. package/esm/screenplay/models/WebdriverProtocolErrorCode.d.ts.map +1 -0
  111. package/esm/screenplay/models/WebdriverProtocolErrorCode.js +40 -0
  112. package/esm/screenplay/models/WebdriverProtocolErrorCode.js.map +1 -0
  113. package/esm/screenplay/models/index.d.ts +5 -0
  114. package/esm/screenplay/models/index.d.ts.map +1 -0
  115. package/esm/screenplay/models/index.js +5 -0
  116. package/esm/screenplay/models/index.js.map +1 -0
  117. package/esm/screenplay/models/locators/WebdriverIOLocator.d.ts +33 -0
  118. package/esm/screenplay/models/locators/WebdriverIOLocator.d.ts.map +1 -0
  119. package/esm/screenplay/models/locators/WebdriverIOLocator.js +123 -0
  120. package/esm/screenplay/models/locators/WebdriverIOLocator.js.map +1 -0
  121. package/esm/screenplay/models/locators/WebdriverIORootLocator.d.ts +17 -0
  122. package/esm/screenplay/models/locators/WebdriverIORootLocator.d.ts.map +1 -0
  123. package/esm/screenplay/models/locators/WebdriverIORootLocator.js +30 -0
  124. package/esm/screenplay/models/locators/WebdriverIORootLocator.js.map +1 -0
  125. package/esm/screenplay/models/locators/index.d.ts +3 -0
  126. package/esm/screenplay/models/locators/index.d.ts.map +1 -0
  127. package/esm/screenplay/models/locators/index.js +3 -0
  128. package/esm/screenplay/models/locators/index.js.map +1 -0
  129. package/lib/adapter/TestRunnerLoader.d.ts +17 -0
  130. package/lib/adapter/TestRunnerLoader.d.ts.map +1 -0
  131. package/lib/adapter/TestRunnerLoader.js +67 -0
  132. package/lib/adapter/TestRunnerLoader.js.map +1 -0
  133. package/lib/adapter/WebdriverIOFrameworkAdapter.d.ts +25 -0
  134. package/lib/adapter/WebdriverIOFrameworkAdapter.d.ts.map +1 -0
  135. package/lib/adapter/WebdriverIOFrameworkAdapter.js +127 -0
  136. package/lib/adapter/WebdriverIOFrameworkAdapter.js.map +1 -0
  137. package/lib/adapter/WebdriverIOFrameworkAdapterFactory.d.ts +18 -0
  138. package/lib/adapter/WebdriverIOFrameworkAdapterFactory.d.ts.map +1 -0
  139. package/lib/adapter/WebdriverIOFrameworkAdapterFactory.js +22 -0
  140. package/lib/adapter/WebdriverIOFrameworkAdapterFactory.js.map +1 -0
  141. package/lib/adapter/WebdriverIONotifier.d.ts +71 -0
  142. package/lib/adapter/WebdriverIONotifier.d.ts.map +1 -0
  143. package/lib/adapter/WebdriverIONotifier.js +394 -0
  144. package/lib/adapter/WebdriverIONotifier.js.map +1 -0
  145. package/lib/adapter/index.d.ts +2 -0
  146. package/lib/adapter/index.d.ts.map +1 -0
  147. package/lib/adapter/index.js +18 -0
  148. package/lib/adapter/index.js.map +1 -0
  149. package/lib/adapter/reporter/BrowserCapabilitiesReporter.d.ts +18 -0
  150. package/lib/adapter/reporter/BrowserCapabilitiesReporter.d.ts.map +1 -0
  151. package/lib/adapter/reporter/BrowserCapabilitiesReporter.js +49 -0
  152. package/lib/adapter/reporter/BrowserCapabilitiesReporter.js.map +1 -0
  153. package/lib/adapter/reporter/InitialisesReporters.d.ts +8 -0
  154. package/lib/adapter/reporter/InitialisesReporters.d.ts.map +1 -0
  155. package/lib/adapter/reporter/InitialisesReporters.js +3 -0
  156. package/lib/adapter/reporter/InitialisesReporters.js.map +1 -0
  157. package/lib/adapter/reporter/OutputStreamBuffer.d.ts +13 -0
  158. package/lib/adapter/reporter/OutputStreamBuffer.d.ts.map +1 -0
  159. package/lib/adapter/reporter/OutputStreamBuffer.js +29 -0
  160. package/lib/adapter/reporter/OutputStreamBuffer.js.map +1 -0
  161. package/lib/adapter/reporter/OutputStreamBufferPrinter.d.ts +16 -0
  162. package/lib/adapter/reporter/OutputStreamBufferPrinter.d.ts.map +1 -0
  163. package/lib/adapter/reporter/OutputStreamBufferPrinter.js +28 -0
  164. package/lib/adapter/reporter/OutputStreamBufferPrinter.js.map +1 -0
  165. package/lib/adapter/reporter/ProvidesWriteStream.d.ts +8 -0
  166. package/lib/adapter/reporter/ProvidesWriteStream.d.ts.map +1 -0
  167. package/lib/adapter/reporter/ProvidesWriteStream.js +3 -0
  168. package/lib/adapter/reporter/ProvidesWriteStream.js.map +1 -0
  169. package/lib/adapter/reporter/TagPrinter.d.ts +17 -0
  170. package/lib/adapter/reporter/TagPrinter.d.ts.map +1 -0
  171. package/lib/adapter/reporter/TagPrinter.js +56 -0
  172. package/lib/adapter/reporter/TagPrinter.js.map +1 -0
  173. package/lib/adapter/reporter/index.d.ts +6 -0
  174. package/lib/adapter/reporter/index.d.ts.map +1 -0
  175. package/lib/adapter/reporter/index.js +22 -0
  176. package/lib/adapter/reporter/index.js.map +1 -0
  177. package/lib/api.d.ts +3 -0
  178. package/lib/api.d.ts.map +1 -0
  179. package/lib/api.js +18 -0
  180. package/lib/api.js.map +1 -0
  181. package/lib/config/WebdriverIOConfig.d.ts +95 -0
  182. package/lib/config/WebdriverIOConfig.d.ts.map +1 -0
  183. package/lib/config/WebdriverIOConfig.js +3 -0
  184. package/lib/config/WebdriverIOConfig.js.map +1 -0
  185. package/lib/config/index.d.ts +2 -0
  186. package/lib/config/index.d.ts.map +1 -0
  187. package/lib/config/index.js +18 -0
  188. package/lib/config/index.js.map +1 -0
  189. package/lib/index.d.ts +16 -0
  190. package/lib/index.d.ts.map +1 -0
  191. package/lib/index.js +52 -0
  192. package/lib/index.js.map +1 -0
  193. package/lib/package.json +1 -0
  194. package/lib/screenplay/abilities/BrowseTheWebWithWebdriverIO.d.ts +40 -0
  195. package/lib/screenplay/abilities/BrowseTheWebWithWebdriverIO.d.ts.map +1 -0
  196. package/lib/screenplay/abilities/BrowseTheWebWithWebdriverIO.js +47 -0
  197. package/lib/screenplay/abilities/BrowseTheWebWithWebdriverIO.js.map +1 -0
  198. package/lib/screenplay/abilities/index.d.ts +2 -0
  199. package/lib/screenplay/abilities/index.d.ts.map +1 -0
  200. package/lib/screenplay/abilities/index.js +18 -0
  201. package/lib/screenplay/abilities/index.js.map +1 -0
  202. package/lib/screenplay/index.d.ts +3 -0
  203. package/lib/screenplay/index.d.ts.map +1 -0
  204. package/lib/screenplay/index.js +19 -0
  205. package/lib/screenplay/index.js.map +1 -0
  206. package/lib/screenplay/models/WebdriverIOBrowsingSession.d.ts +25 -0
  207. package/lib/screenplay/models/WebdriverIOBrowsingSession.d.ts.map +1 -0
  208. package/lib/screenplay/models/WebdriverIOBrowsingSession.js +129 -0
  209. package/lib/screenplay/models/WebdriverIOBrowsingSession.js.map +1 -0
  210. package/lib/screenplay/models/WebdriverIOCookie.d.ts +15 -0
  211. package/lib/screenplay/models/WebdriverIOCookie.d.ts.map +1 -0
  212. package/lib/screenplay/models/WebdriverIOCookie.js +46 -0
  213. package/lib/screenplay/models/WebdriverIOCookie.js.map +1 -0
  214. package/lib/screenplay/models/WebdriverIOErrorHandler.d.ts +9 -0
  215. package/lib/screenplay/models/WebdriverIOErrorHandler.d.ts.map +1 -0
  216. package/lib/screenplay/models/WebdriverIOErrorHandler.js +24 -0
  217. package/lib/screenplay/models/WebdriverIOErrorHandler.js.map +1 -0
  218. package/lib/screenplay/models/WebdriverIOModalDialogHandler.d.ts +27 -0
  219. package/lib/screenplay/models/WebdriverIOModalDialogHandler.d.ts.map +1 -0
  220. package/lib/screenplay/models/WebdriverIOModalDialogHandler.js +78 -0
  221. package/lib/screenplay/models/WebdriverIOModalDialogHandler.js.map +1 -0
  222. package/lib/screenplay/models/WebdriverIOPage.d.ts +50 -0
  223. package/lib/screenplay/models/WebdriverIOPage.d.ts.map +1 -0
  224. package/lib/screenplay/models/WebdriverIOPage.js +256 -0
  225. package/lib/screenplay/models/WebdriverIOPage.js.map +1 -0
  226. package/lib/screenplay/models/WebdriverIOPageElement.d.ts +34 -0
  227. package/lib/screenplay/models/WebdriverIOPageElement.d.ts.map +1 -0
  228. package/lib/screenplay/models/WebdriverIOPageElement.js +257 -0
  229. package/lib/screenplay/models/WebdriverIOPageElement.js.map +1 -0
  230. package/lib/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.d.ts +30 -0
  231. package/lib/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.d.ts.map +1 -0
  232. package/lib/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.js +81 -0
  233. package/lib/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.js.map +1 -0
  234. package/lib/screenplay/models/WebdriverProtocolErrorCode.d.ts +39 -0
  235. package/lib/screenplay/models/WebdriverProtocolErrorCode.d.ts.map +1 -0
  236. package/lib/screenplay/models/WebdriverProtocolErrorCode.js +43 -0
  237. package/lib/screenplay/models/WebdriverProtocolErrorCode.js.map +1 -0
  238. package/lib/screenplay/models/index.d.ts +5 -0
  239. package/lib/screenplay/models/index.d.ts.map +1 -0
  240. package/lib/screenplay/models/index.js +21 -0
  241. package/lib/screenplay/models/index.js.map +1 -0
  242. package/lib/screenplay/models/locators/WebdriverIOLocator.d.ts +33 -0
  243. package/lib/screenplay/models/locators/WebdriverIOLocator.d.ts.map +1 -0
  244. package/lib/screenplay/models/locators/WebdriverIOLocator.js +128 -0
  245. package/lib/screenplay/models/locators/WebdriverIOLocator.js.map +1 -0
  246. package/lib/screenplay/models/locators/WebdriverIORootLocator.d.ts +17 -0
  247. package/lib/screenplay/models/locators/WebdriverIORootLocator.d.ts.map +1 -0
  248. package/lib/screenplay/models/locators/WebdriverIORootLocator.js +34 -0
  249. package/lib/screenplay/models/locators/WebdriverIORootLocator.js.map +1 -0
  250. package/lib/screenplay/models/locators/index.d.ts +3 -0
  251. package/lib/screenplay/models/locators/index.d.ts.map +1 -0
  252. package/lib/screenplay/models/locators/index.js +19 -0
  253. package/lib/screenplay/models/locators/index.js.map +1 -0
  254. package/package.json +122 -0
  255. package/src/adapter/TestRunnerLoader.ts +90 -0
  256. package/src/adapter/WebdriverIOFrameworkAdapter.ts +130 -0
  257. package/src/adapter/WebdriverIOFrameworkAdapterFactory.ts +39 -0
  258. package/src/adapter/WebdriverIONotifier.ts +480 -0
  259. package/src/adapter/index.ts +1 -0
  260. package/src/adapter/reporter/BrowserCapabilitiesReporter.ts +63 -0
  261. package/src/adapter/reporter/InitialisesReporters.ts +8 -0
  262. package/src/adapter/reporter/OutputStreamBuffer.ts +30 -0
  263. package/src/adapter/reporter/OutputStreamBufferPrinter.ts +30 -0
  264. package/src/adapter/reporter/ProvidesWriteStream.ts +8 -0
  265. package/src/adapter/reporter/TagPrinter.ts +69 -0
  266. package/src/adapter/reporter/index.ts +5 -0
  267. package/src/api.ts +2 -0
  268. package/src/config/WebdriverIOConfig.ts +94 -0
  269. package/src/config/index.ts +1 -0
  270. package/src/index.ts +32 -0
  271. package/src/screenplay/abilities/BrowseTheWebWithWebdriverIO.ts +46 -0
  272. package/src/screenplay/abilities/index.ts +1 -0
  273. package/src/screenplay/index.ts +2 -0
  274. package/src/screenplay/models/WebdriverIOBrowsingSession.ts +176 -0
  275. package/src/screenplay/models/WebdriverIOCookie.ts +51 -0
  276. package/src/screenplay/models/WebdriverIOErrorHandler.ts +25 -0
  277. package/src/screenplay/models/WebdriverIOModalDialogHandler.ts +99 -0
  278. package/src/screenplay/models/WebdriverIOPage.ts +311 -0
  279. package/src/screenplay/models/WebdriverIOPageElement.ts +280 -0
  280. package/src/screenplay/models/WebdriverIOPuppeteerModalDialogHandler.ts +100 -0
  281. package/src/screenplay/models/WebdriverProtocolErrorCode.ts +38 -0
  282. package/src/screenplay/models/index.ts +4 -0
  283. package/src/screenplay/models/locators/WebdriverIOLocator.ts +176 -0
  284. package/src/screenplay/models/locators/WebdriverIORootLocator.ts +34 -0
  285. package/src/screenplay/models/locators/index.ts +2 -0
@@ -0,0 +1,311 @@
1
+ import 'webdriverio';
2
+
3
+ import { List, LogicError } from '@serenity-js/core';
4
+ import type { CorrelationId } from '@serenity-js/core/lib/model/index.js';
5
+ import type { Cookie, CookieData, ModalDialogHandler, PageElements, Selector } from '@serenity-js/web';
6
+ import { ArgumentDehydrator, BrowserWindowClosedError, ByCss, Key, Page, PageElement, PageElementsLocator } from '@serenity-js/web';
7
+ import * as scripts from '@serenity-js/web/lib/scripts/index.js';
8
+ import { URL } from 'url';
9
+
10
+ import { WebdriverIOExistingElementLocator, WebdriverIOLocator, WebdriverIORootLocator } from './locators/index.js';
11
+ import type { WebdriverIOBrowsingSession } from './WebdriverIOBrowsingSession.js';
12
+ import { WebdriverIOCookie } from './WebdriverIOCookie.js';
13
+ import type { WebdriverIOErrorHandler } from './WebdriverIOErrorHandler.js';
14
+ import { WebdriverIOPageElement } from './WebdriverIOPageElement.js';
15
+
16
+ /**
17
+ * WebdriverIO-specific implementation of [`Page`](https://serenity-js.org/api/web/class/Page/).
18
+ *
19
+ * @group Models
20
+ */
21
+ export class WebdriverIOPage extends Page<WebdriverIO.Element> {
22
+
23
+ private lastScriptExecutionSummary: LastScriptExecutionSummary;
24
+
25
+ /* eslint-disable unicorn/consistent-function-scoping */
26
+ private dehydrator: ArgumentDehydrator<PageElement<WebdriverIO.Element>, WebdriverIO.Element> = new ArgumentDehydrator(
27
+ (item: any): item is PageElement<WebdriverIO.Element> => item instanceof PageElement,
28
+ (item: PageElement<WebdriverIO.Element>) => item.nativeElement(),
29
+ );
30
+ /* eslint-enable */
31
+
32
+ constructor(
33
+ session: WebdriverIOBrowsingSession,
34
+ private readonly browser: WebdriverIO.Browser,
35
+ modalDialogHandler: ModalDialogHandler,
36
+ private readonly errorHandler: WebdriverIOErrorHandler,
37
+ pageId: CorrelationId,
38
+ ) {
39
+ super(
40
+ session,
41
+ new WebdriverIORootLocator(browser),
42
+ modalDialogHandler,
43
+ pageId,
44
+ );
45
+ }
46
+
47
+ createPageElement(nativeElement: WebdriverIO.Element): PageElement<WebdriverIO.Element> {
48
+ return new WebdriverIOPageElement(
49
+ new WebdriverIOExistingElementLocator(
50
+ this.rootLocator,
51
+ new ByCss(String(nativeElement.selector)),
52
+ this.errorHandler,
53
+ nativeElement
54
+ )
55
+ );
56
+ }
57
+
58
+ locate(selector: Selector): PageElement<WebdriverIO.Element> {
59
+ return new WebdriverIOPageElement(
60
+ new WebdriverIOLocator(this.rootLocator, selector, this.errorHandler)
61
+ )
62
+ }
63
+
64
+ locateAll(selector: Selector): PageElements<WebdriverIO.Element> {
65
+ return List.of(
66
+ new PageElementsLocator(
67
+ new WebdriverIOLocator(this.rootLocator, selector, this.errorHandler)
68
+ )
69
+ );
70
+ }
71
+
72
+ async navigateTo(destination: string): Promise<void> {
73
+ await this.inContextOfThisPage(() => this.browser.url(destination));
74
+ }
75
+
76
+ async navigateBack(): Promise<void> {
77
+ await this.inContextOfThisPage(() => this.browser.back());
78
+ }
79
+
80
+ async navigateForward(): Promise<void> {
81
+ await this.inContextOfThisPage(() => this.browser.forward());
82
+ }
83
+
84
+ async reload(): Promise<void> {
85
+ await this.inContextOfThisPage(() => this.browser.refresh());
86
+ }
87
+
88
+ async sendKeys(keys: Array<Key | string>): Promise<void> {
89
+ const keySequence = keys.map(key => {
90
+ if (! Key.isKey(key)) {
91
+ return key;
92
+ }
93
+
94
+ return key.utf16codePoint;
95
+ });
96
+
97
+ await this.inContextOfThisPage(() => this.browser.keys(keySequence));
98
+ }
99
+
100
+ async executeScript<Result, InnerArguments extends any[]>(
101
+ script: string | ((...parameters: InnerArguments) => Result),
102
+ ...args: InnerArguments
103
+ ): Promise<Result> {
104
+
105
+ const serialisedScript = typeof script === 'function'
106
+ ? String(script)
107
+ : String(`function script() { ${ script } }`);
108
+
109
+ const executableScript = new Function(`
110
+ var parameters = (${ scripts.rehydrate }).apply(null, arguments);
111
+ return (${ serialisedScript }).apply(null, parameters);
112
+ `);
113
+
114
+ const result = await this.inContextOfThisPage<Result>(async () => {
115
+
116
+ const dehydratedArguments = await this.dehydrator.dehydrate(args);
117
+
118
+ return await this.browser.execute(executableScript as any, ...dehydratedArguments);
119
+ });
120
+
121
+ this.lastScriptExecutionSummary = new LastScriptExecutionSummary(result);
122
+
123
+ return result;
124
+ }
125
+
126
+ async executeAsyncScript<Result, Parameters extends any[]>(
127
+ script: string | ((...args: [...parameters: Parameters, callback: (result: Result) => void]) => void),
128
+ ...args: Parameters
129
+ ): Promise<Result> {
130
+
131
+ const serialisedScript = typeof script === 'function'
132
+ ? String(script)
133
+ : String(`function script() { ${ script } }`);
134
+
135
+ const executableScript = new Function(`
136
+ var args = Array.prototype.slice.call(arguments, 0, -1);
137
+ var callback = arguments[arguments.length - 1];
138
+ var parameters = (${ scripts.rehydrate }).apply(null, args);
139
+ (${ serialisedScript }).apply(null, parameters.concat(callback));
140
+ `);
141
+
142
+ const result = await this.inContextOfThisPage<Result>(async () => {
143
+
144
+ const dehydratedArguments = await this.dehydrator.dehydrate(args);
145
+
146
+ return this.browser.executeAsync<Result, [ { argsCount: number, refsCount: number }, ...any[] ]>(
147
+ executableScript as (...args: [ { argsCount: number, refsCount: number }, ...any[], callback: (result: Result) => void ]) => void,
148
+ ...dehydratedArguments as [ { argsCount: number, refsCount: number }, ...any[] ],
149
+ );
150
+ });
151
+
152
+ this.lastScriptExecutionSummary = new LastScriptExecutionSummary(result);
153
+
154
+ return result;
155
+ }
156
+
157
+ lastScriptExecutionResult<Result = any>(): Result {
158
+ if (! this.lastScriptExecutionSummary) {
159
+ throw new LogicError(`Make sure to execute a script before checking on the result`);
160
+ }
161
+
162
+ // Selenium returns `null` when the script it executed returns `undefined`
163
+ // so we're mapping the result back.
164
+ return this.lastScriptExecutionSummary.result === null
165
+ ? undefined
166
+ : this.lastScriptExecutionSummary.result;
167
+ }
168
+
169
+ async takeScreenshot(): Promise<string> {
170
+ return await this.inContextOfThisPage(async () => {
171
+ try {
172
+ return await this.browser.takeScreenshot();
173
+ }
174
+ catch (error) {
175
+
176
+ if (error.name === 'ProtocolError' && error.message.includes('Target closed')) {
177
+ throw new BrowserWindowClosedError(
178
+ `Couldn't take screenshot since the browser window is already closed`,
179
+ error
180
+ );
181
+ }
182
+
183
+ throw error;
184
+ }
185
+ });
186
+ }
187
+
188
+ async cookie(name: string): Promise<Cookie> {
189
+ return new WebdriverIOCookie(this.browser, name);
190
+ }
191
+
192
+ async setCookie(cookieData: CookieData): Promise<void> {
193
+ return await this.inContextOfThisPage(() => {
194
+ return this.browser.setCookies({
195
+ name: cookieData.name,
196
+ value: cookieData.value,
197
+ path: cookieData.path,
198
+ domain: cookieData.domain,
199
+ secure: cookieData.secure,
200
+ httpOnly: cookieData.httpOnly,
201
+ expiry: cookieData.expiry
202
+ ? cookieData.expiry.toSeconds()
203
+ : undefined,
204
+ sameSite: cookieData.sameSite,
205
+ });
206
+ });
207
+ }
208
+
209
+ async deleteAllCookies(): Promise<void> {
210
+ return await this.inContextOfThisPage(() => {
211
+ return this.browser.deleteCookies() as Promise<void>;
212
+ });
213
+ }
214
+
215
+ async title(): Promise<string> {
216
+ return await this.inContextOfThisPage(() => this.browser.execute(`return document.title`));
217
+ }
218
+
219
+ async name(): Promise<string> {
220
+ return await this.inContextOfThisPage(() => this.browser.execute(`return window.name`));
221
+ }
222
+
223
+ async url(): Promise<URL> {
224
+ return await this.inContextOfThisPage(async () => {
225
+ return new URL(await this.browser.execute(`return window.location.href`));
226
+ });
227
+ }
228
+
229
+ async viewportSize(): Promise<{ width: number, height: number }> {
230
+ return await this.inContextOfThisPage(async () => {
231
+ if (! this.browser.isDevTools) {
232
+ const calculatedViewportSize = await this.browser.execute(`
233
+ return {
234
+ width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
235
+ height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
236
+ }
237
+ `) as { width: number, height: number };
238
+
239
+ // Chrome headless hard-codes window.innerWidth and window.innerHeight to 0
240
+ if (calculatedViewportSize.width > 0 && calculatedViewportSize.height > 0) {
241
+ return calculatedViewportSize;
242
+ }
243
+ }
244
+
245
+ return this.browser.getWindowSize();
246
+ });
247
+ }
248
+
249
+ async setViewportSize(size: { width: number, height: number }): Promise<void> {
250
+ return await this.inContextOfThisPage(async () => {
251
+ let desiredWindowSize = size;
252
+
253
+ if (! this.browser.isDevTools) {
254
+ desiredWindowSize = await this.browser.execute(`
255
+ var currentViewportWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
256
+ var currentViewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
257
+
258
+ return {
259
+ width: Math.max(window.outerWidth - currentViewportWidth + ${ size.width }, ${ size.width }),
260
+ height: Math.max(window.outerHeight - currentViewportHeight + ${ size.height }, ${ size.height }),
261
+ };
262
+ `);
263
+ }
264
+
265
+ return this.browser.setWindowSize(desiredWindowSize.width, desiredWindowSize.height);
266
+ });
267
+ }
268
+
269
+ async close(): Promise<void> {
270
+ await this.inContextOfThisPage(() => this.browser.closeWindow());
271
+ }
272
+
273
+ async closeOthers(): Promise<void> {
274
+ await this.session.closePagesOtherThan(this);
275
+ }
276
+
277
+ async isPresent(): Promise<boolean> {
278
+ const allPages = await this.session.allPages();
279
+ for (const page of allPages) {
280
+ if (page === this) {
281
+ return true;
282
+ }
283
+ }
284
+ return false;
285
+ }
286
+
287
+ private async inContextOfThisPage<T>(action: () => Promise<T> | T): Promise<T> {
288
+ let originalCurrentPage;
289
+
290
+ try {
291
+ originalCurrentPage = await this.session.currentPage();
292
+
293
+ await this.session.changeCurrentPageTo(this);
294
+
295
+ return await action();
296
+ }
297
+ catch (error) {
298
+ return await this.errorHandler.executeIfHandled(error, action);
299
+ }
300
+ finally {
301
+ await this.session.changeCurrentPageTo(originalCurrentPage);
302
+ }
303
+ }
304
+ }
305
+
306
+ /**
307
+ * @package
308
+ */
309
+ class LastScriptExecutionSummary<Result = any> {
310
+ constructor(public readonly result: Result) {}
311
+ }
@@ -0,0 +1,280 @@
1
+ import 'webdriverio';
2
+
3
+ import { LogicError } from '@serenity-js/core';
4
+ import type { SwitchableOrigin } from '@serenity-js/web';
5
+ import { Key, PageElement, SelectOption } from '@serenity-js/web';
6
+ import * as scripts from '@serenity-js/web/lib/scripts/index.js';
7
+
8
+ import type { WebdriverIOLocator } from './locators/index.js';
9
+ import { WebdriverProtocolErrorCode } from './WebdriverProtocolErrorCode.js';
10
+
11
+ /**
12
+ * WebdriverIO-specific implementation of [`PageElement`](https://serenity-js.org/api/web/class/PageElement/).
13
+ *
14
+ * @group Models
15
+ */
16
+ export class WebdriverIOPageElement extends PageElement<WebdriverIO.Element> {
17
+ of(parent: WebdriverIOPageElement): WebdriverIOPageElement {
18
+ return new WebdriverIOPageElement(this.locator.of(parent.locator))
19
+ }
20
+
21
+ closestTo(child: WebdriverIOPageElement): WebdriverIOPageElement {
22
+ return new WebdriverIOPageElement(this.locator.closestTo(child.locator))
23
+ }
24
+
25
+ async clearValue(): Promise<void> {
26
+ // eslint-disable-next-line unicorn/consistent-function-scoping
27
+ function times(length: number, key: string) {
28
+ return Array.from({ length }).map(() => key);
29
+ }
30
+
31
+ // eslint-disable-next-line unicorn/consistent-function-scoping
32
+ async function removeCharactersFrom(browser: WebdriverIO.Browser, inputElement: WebdriverIO.Element, numberOfCharacters: number): Promise<void> {
33
+ await browser.execute(
34
+ /* c8 ignore next */
35
+ function focusOn(element: any) {
36
+ element.focus();
37
+ },
38
+ element
39
+ );
40
+ await browser.keys([
41
+ Key.Home.utf16codePoint,
42
+ ...times(numberOfCharacters, Key.Delete.utf16codePoint),
43
+ ]);
44
+ }
45
+
46
+ const element = await this.nativeElement();
47
+ const value = await this.value();
48
+ const hasValue = value !== null && value !== undefined && value.length > 0;
49
+ const browser = await this.browserFor(element);
50
+
51
+ if (hasValue) {
52
+ return await removeCharactersFrom(browser, element, value.length);
53
+ }
54
+
55
+ const contentEditable = await element.getAttribute('contenteditable');
56
+ const hasContentEditable = contentEditable !== null && contentEditable !== undefined && contentEditable !== 'false';
57
+
58
+ if (hasContentEditable) {
59
+ const text = await element.getText();
60
+ return await removeCharactersFrom(browser, element, text.length);
61
+ }
62
+ }
63
+
64
+ async click(): Promise<void> {
65
+ const element = await this.nativeElement();
66
+ await element.click();
67
+ }
68
+
69
+ async doubleClick(): Promise<void> {
70
+ const element = await this.nativeElement();
71
+ await element.doubleClick();
72
+ }
73
+
74
+ async enterValue(value: string | number | Array<string | number>): Promise<void> {
75
+ const text = Array.isArray(value) ? value.join('') : value;
76
+ const element = await this.nativeElement();
77
+
78
+ await element.addValue(text);
79
+ }
80
+
81
+ async scrollIntoView(): Promise<void> {
82
+ const element = await this.nativeElement();
83
+ await element.scrollIntoView();
84
+ }
85
+
86
+ async hoverOver(): Promise<void> {
87
+ const element = await this.nativeElement();
88
+ await element.moveTo();
89
+ }
90
+
91
+ async rightClick(): Promise<void> {
92
+ const element = await this.nativeElement();
93
+ await element.click({ button: 'right' });
94
+ }
95
+
96
+ async selectOptions(...options: SelectOption[]): Promise<void> {
97
+ const element = await this.nativeElement();
98
+
99
+ for (const option of options) {
100
+ if (option.value) {
101
+ await element.selectByAttribute('value', option.value);
102
+ }
103
+ else if (option.label) {
104
+ await element.selectByVisibleText(option.label);
105
+ }
106
+ }
107
+ }
108
+
109
+ async selectedOptions(): Promise<SelectOption[]> {
110
+ const element = await this.nativeElement();
111
+ const browser = await this.browserFor(element);
112
+
113
+ const options = await browser.execute(
114
+ /* c8 ignore start */
115
+ (select: HTMLSelectElement) => {
116
+ const options = [];
117
+ select.querySelectorAll('option').forEach((option: HTMLOptionElement) => {
118
+ options.push({
119
+ selected: option.selected,
120
+ disabled: option.disabled,
121
+ label: option.label,
122
+ value: option.value,
123
+ });
124
+ });
125
+
126
+ return options;
127
+ },
128
+ element as unknown
129
+ /* c8 ignore stop */
130
+ );
131
+
132
+ return options.map(option =>
133
+ new SelectOption(option.label, option.value, option.selected, option.disabled)
134
+ );
135
+ }
136
+
137
+ async attribute(name: string): Promise<string> {
138
+ const element = await this.nativeElement();
139
+ return await element.getAttribute(name);
140
+ }
141
+
142
+ async text(): Promise<string> {
143
+ const element = await this.nativeElement();
144
+ return await element.getText();
145
+ }
146
+
147
+ async value(): Promise<string> {
148
+ const element = await this.nativeElement();
149
+ return await element.getValue();
150
+ }
151
+
152
+ async html(): Promise<string> {
153
+ const element = await this.nativeElement();
154
+ return await element.getHTML(true);
155
+ }
156
+
157
+ async switchTo(): Promise<SwitchableOrigin> {
158
+ try {
159
+ const element: WebdriverIO.Element = await this.locator.nativeElement()
160
+
161
+ if (element.error) {
162
+ throw element.error;
163
+ }
164
+
165
+ const tagName = await element.getTagName();
166
+
167
+ const browser = await this.browserFor(element);
168
+
169
+ if ([ 'iframe', 'frame' ].includes(tagName)) {
170
+ const locator = (this.locator as WebdriverIOLocator);
171
+
172
+ await locator.switchToFrame(element);
173
+
174
+ return {
175
+ switchBack: async (): Promise<void> => {
176
+ try {
177
+ await locator.switchToParentFrame();
178
+ }
179
+ catch {
180
+ // switchToParentFrame doesn't work on iOS devices, so we need a workaround
181
+ // https://github.com/appium/appium/issues/14882#issuecomment-1693326102
182
+ await locator.switchToFrame(null); // eslint-disable-line unicorn/no-null
183
+ }
184
+ }
185
+ }
186
+ }
187
+ else {
188
+ // focus on element
189
+ const previouslyFocusedElement = await browser.execute(
190
+ /* c8 ignore next */
191
+ function focusOn(element: any) {
192
+ const currentlyFocusedElement = document.activeElement;
193
+ element.focus();
194
+ return currentlyFocusedElement;
195
+ },
196
+ element
197
+ );
198
+
199
+ return {
200
+ switchBack: async (): Promise<void> => {
201
+ await browser.execute(
202
+ /* c8 ignore next */
203
+ function focusOn(element: any) {
204
+ element.focus();
205
+ },
206
+ previouslyFocusedElement
207
+ );
208
+ }
209
+ }
210
+ }
211
+ }
212
+ catch(error) {
213
+ throw new LogicError(`Couldn't switch to page element located ${ this.locator }`, error);
214
+ }
215
+ }
216
+
217
+ async isActive(): Promise<boolean> {
218
+ const element = await this.nativeElement();
219
+ return await element.isFocused();
220
+ }
221
+
222
+ async isClickable(): Promise<boolean> {
223
+ const element = await this.nativeElement();
224
+ return await element.isClickable();
225
+ }
226
+
227
+ async isEnabled(): Promise<boolean> {
228
+ const element = await this.nativeElement();
229
+ return await element.isEnabled();
230
+ }
231
+
232
+ async isPresent(): Promise<boolean> {
233
+ const element = await this.nativeElement();
234
+ return await element.isExisting();
235
+ }
236
+
237
+ async isSelected(): Promise<boolean> {
238
+ const element = await this.nativeElement();
239
+ return await element.isSelected();
240
+ }
241
+
242
+ async isVisible(): Promise<boolean> {
243
+ try {
244
+ const element = await this.nativeElement();
245
+
246
+ if (! await element.isDisplayed()) {
247
+ return false;
248
+ }
249
+
250
+ if (! await element.isDisplayedInViewport()) {
251
+ return false;
252
+ }
253
+
254
+ const browser = await this.browserFor(element);
255
+
256
+ return await browser.execute(scripts.isVisible, element as unknown as HTMLElement);
257
+ }
258
+ catch (error) {
259
+ // an element that doesn't exist is treated as not visible
260
+ if (
261
+ error.name === WebdriverProtocolErrorCode.NoSuchElementError ||
262
+ error.error === WebdriverProtocolErrorCode.NoSuchElementError ||
263
+ /element.*not found/i.test(error.message)
264
+ ) {
265
+ return false;
266
+ }
267
+
268
+ throw error;
269
+ }
270
+ }
271
+
272
+ // based on https://github.com/webdriverio/webdriverio/blob/dec6da76b0e218af935dbf39735ae3491d5edd8c/packages/webdriverio/src/utils/index.ts#L98
273
+
274
+ private async browserFor(nativeElement: WebdriverIO.Element | WebdriverIO.Browser): Promise<WebdriverIO.Browser> {
275
+ const element = nativeElement as WebdriverIO.Element;
276
+ return element.parent
277
+ ? this.browserFor(element.parent)
278
+ : nativeElement as WebdriverIO.Browser
279
+ }
280
+ }
@@ -0,0 +1,100 @@
1
+ import type {
2
+ ModalDialog} from '@serenity-js/web';
3
+ import {
4
+ AbsentModalDialog,
5
+ AcceptedModalDialog,
6
+ DismissedModalDialog,
7
+ ModalDialogHandler
8
+ } from '@serenity-js/web';
9
+ import type { Dialog, Page } from 'puppeteer-core';
10
+
11
+ /**
12
+ * WebdriverIO-specific implementation of [`ModalDialogHandler`](https://serenity-js.org/api/web/class/ModalDialogHandler/), applicable when WebdriverIO
13
+ * uses [Puppeteer](https://developer.chrome.com/docs/puppeteer/).
14
+ *
15
+ * Automatically handles any simple JavaScript modal dialog windows, such as
16
+ * those opened by [`window.alert()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/alert),
17
+ * [`window.confirm()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm),
18
+ * or [`window.prompt()`](https://developer.mozilla.org/en-US/docs/Web/API/Window/prompt).
19
+ *
20
+ * This helps to avoid Puppeteer hanging when there's an interaction pending
21
+ * and a dialog is already open, see https://github.com/puppeteer/puppeteer/issues/2481
22
+ */
23
+ export class WebdriverIOPuppeteerModalDialogHandler extends ModalDialogHandler {
24
+
25
+ private dialogHandlingInProgress = false;
26
+
27
+ private readonly defaultHandler: (dialog: Dialog) => Promise<ModalDialog> =
28
+ async (dialog: Dialog) => {
29
+ await dialog.dismiss();
30
+ return new DismissedModalDialog(dialog.message());
31
+ }
32
+
33
+ private currentHandler: (dialog: Dialog) => Promise<ModalDialog>;
34
+
35
+ constructor(private readonly page: Page) {
36
+ super();
37
+
38
+ this.currentHandler = this.defaultHandler;
39
+
40
+ // remove the default WebdriverIO handler because it makes any other listeners hang
41
+ // https://github.com/webdriverio/webdriverio/blob/518c56a61353b2f65436b83c618117021da7e622/packages/devtools/src/devtoolsdriver.ts#L69
42
+ this.page.removeAllListeners('dialog');
43
+
44
+ // register Serenity/JS handler instead
45
+ this.page.on('dialog', async (dialog: Dialog) => {
46
+ // Puppeteer doesn't seem to synchronise async handlers correctly,
47
+ // hence the handleModalDialog wrapper and the `dialogHandlingInProgress` lock.
48
+ await this.handleModalDialog(() => this.currentHandler(dialog));
49
+ });
50
+ }
51
+
52
+ async acceptNext(): Promise<void> {
53
+ this.currentHandler = async (dialog: Dialog) => {
54
+ await dialog.accept(dialog.defaultValue());
55
+ return new AcceptedModalDialog(dialog.message());
56
+ }
57
+ }
58
+
59
+ async acceptNextWithValue(text: string | number): Promise<void> {
60
+ this.currentHandler = async (dialog: Dialog) => {
61
+ await dialog.accept(String(text));
62
+ return new AcceptedModalDialog(dialog.message());
63
+ }
64
+ }
65
+
66
+ async dismissNext(): Promise<void> {
67
+ this.currentHandler = async (dialog: Dialog) => {
68
+ await dialog.dismiss();
69
+ return new DismissedModalDialog(dialog.message());
70
+ }
71
+ }
72
+
73
+ async reset(): Promise<void> {
74
+ this.modalDialog = new AbsentModalDialog();
75
+ this.currentHandler = this.defaultHandler;
76
+ }
77
+
78
+ async discard(): Promise<void> {
79
+ this.page.off('dialog', this.currentHandler);
80
+ }
81
+
82
+ async last(): Promise<ModalDialog> {
83
+ return new Promise((resolve, reject) => {
84
+ const interval = setInterval(() => {
85
+ if (!this.dialogHandlingInProgress) {
86
+ clearInterval(interval);
87
+ resolve(this.modalDialog);
88
+ }
89
+ }, 25);
90
+ });
91
+ }
92
+
93
+ private async handleModalDialog(handler: () => Promise<ModalDialog> | ModalDialog): Promise<void> {
94
+ this.dialogHandlingInProgress = true;
95
+
96
+ this.modalDialog = await handler();
97
+
98
+ this.dialogHandlingInProgress = false;
99
+ }
100
+ }