@serenity-js/webdriverio 2.33.1 → 3.0.0-rc.11

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 (247) hide show
  1. package/CHANGELOG.md +455 -0
  2. package/lib/adapter/WebdriverIOFrameworkAdapter.js +1 -1
  3. package/lib/adapter/WebdriverIOFrameworkAdapter.js.map +1 -1
  4. package/lib/adapter/WebdriverIONotifier.d.ts +35 -1
  5. package/lib/adapter/WebdriverIONotifier.js +174 -13
  6. package/lib/adapter/WebdriverIONotifier.js.map +1 -1
  7. package/lib/index.d.ts +0 -3
  8. package/lib/index.js +0 -3
  9. package/lib/index.js.map +1 -1
  10. package/lib/screenplay/abilities/{BrowseTheWeb.d.ts → BrowseTheWebWithWebdriverIO.d.ts} +42 -27
  11. package/lib/screenplay/abilities/{BrowseTheWeb.js → BrowseTheWebWithWebdriverIO.js} +104 -32
  12. package/lib/screenplay/abilities/BrowseTheWebWithWebdriverIO.js.map +1 -0
  13. package/lib/screenplay/abilities/index.d.ts +1 -1
  14. package/lib/screenplay/abilities/index.js +1 -1
  15. package/lib/screenplay/abilities/index.js.map +1 -1
  16. package/lib/screenplay/index.d.ts +1 -2
  17. package/lib/screenplay/index.js +1 -2
  18. package/lib/screenplay/index.js.map +1 -1
  19. package/lib/screenplay/models/WebdriverIOCookie.d.ts +8 -0
  20. package/lib/screenplay/models/WebdriverIOCookie.js +39 -0
  21. package/lib/screenplay/models/WebdriverIOCookie.js.map +1 -0
  22. package/lib/screenplay/models/WebdriverIOFrame.d.ts +10 -0
  23. package/lib/screenplay/models/WebdriverIOFrame.js +34 -0
  24. package/lib/screenplay/models/WebdriverIOFrame.js.map +1 -0
  25. package/lib/screenplay/models/WebdriverIOModalDialog.d.ts +11 -0
  26. package/lib/screenplay/models/WebdriverIOModalDialog.js +40 -0
  27. package/lib/screenplay/models/WebdriverIOModalDialog.js.map +1 -0
  28. package/lib/screenplay/models/WebdriverIOPage.d.ts +26 -0
  29. package/lib/screenplay/models/WebdriverIOPage.js +104 -0
  30. package/lib/screenplay/models/WebdriverIOPage.js.map +1 -0
  31. package/lib/screenplay/models/WebdriverIOPageElement.d.ts +31 -0
  32. package/lib/screenplay/models/WebdriverIOPageElement.js +185 -0
  33. package/lib/screenplay/models/WebdriverIOPageElement.js.map +1 -0
  34. package/lib/screenplay/models/index.d.ts +5 -0
  35. package/lib/{stage/crew/photographer/strategies → screenplay/models}/index.js +5 -4
  36. package/lib/screenplay/models/index.js.map +1 -0
  37. package/lib/screenplay/models/locators/WebdriverIOLocator.d.ts +9 -0
  38. package/lib/screenplay/models/locators/WebdriverIOLocator.js +22 -0
  39. package/lib/screenplay/models/locators/WebdriverIOLocator.js.map +1 -0
  40. package/lib/screenplay/models/locators/WebdriverIONativeElementRoot.d.ts +2 -0
  41. package/lib/screenplay/{interactions/EnterBuilder.js → models/locators/WebdriverIONativeElementRoot.js} +1 -1
  42. package/lib/screenplay/models/locators/WebdriverIONativeElementRoot.js.map +1 -0
  43. package/lib/screenplay/models/locators/index.d.ts +2 -0
  44. package/lib/{stage/crew/photographer → screenplay/models/locators}/index.js +2 -2
  45. package/lib/screenplay/models/locators/index.js.map +1 -0
  46. package/package.json +14 -24
  47. package/src/adapter/WebdriverIOFrameworkAdapter.ts +2 -0
  48. package/src/adapter/WebdriverIONotifier.ts +225 -23
  49. package/src/index.ts +0 -3
  50. package/src/screenplay/abilities/{BrowseTheWeb.ts → BrowseTheWebWithWebdriverIO.ts} +125 -35
  51. package/src/screenplay/abilities/index.ts +1 -1
  52. package/src/screenplay/index.ts +1 -2
  53. package/src/screenplay/models/WebdriverIOCookie.ts +44 -0
  54. package/src/screenplay/models/WebdriverIOFrame.ts +38 -0
  55. package/src/screenplay/models/WebdriverIOModalDialog.ts +45 -0
  56. package/src/screenplay/models/WebdriverIOPage.ts +128 -0
  57. package/src/screenplay/models/WebdriverIOPageElement.ts +227 -0
  58. package/src/screenplay/models/index.ts +5 -0
  59. package/src/screenplay/models/locators/WebdriverIOLocator.ts +43 -0
  60. package/src/screenplay/models/locators/WebdriverIONativeElementRoot.ts +3 -0
  61. package/src/screenplay/models/locators/index.ts +2 -0
  62. package/lib/expectations/ElementExpectation.d.ts +0 -11
  63. package/lib/expectations/ElementExpectation.js +0 -27
  64. package/lib/expectations/ElementExpectation.js.map +0 -1
  65. package/lib/expectations/index.d.ts +0 -6
  66. package/lib/expectations/index.js +0 -19
  67. package/lib/expectations/index.js.map +0 -1
  68. package/lib/expectations/isActive.d.ts +0 -15
  69. package/lib/expectations/isActive.js +0 -21
  70. package/lib/expectations/isActive.js.map +0 -1
  71. package/lib/expectations/isClickable.d.ts +0 -20
  72. package/lib/expectations/isClickable.js +0 -26
  73. package/lib/expectations/isClickable.js.map +0 -1
  74. package/lib/expectations/isEnabled.d.ts +0 -14
  75. package/lib/expectations/isEnabled.js +0 -20
  76. package/lib/expectations/isEnabled.js.map +0 -1
  77. package/lib/expectations/isPresent.d.ts +0 -15
  78. package/lib/expectations/isPresent.js +0 -21
  79. package/lib/expectations/isPresent.js.map +0 -1
  80. package/lib/expectations/isSelected.d.ts +0 -14
  81. package/lib/expectations/isSelected.js +0 -20
  82. package/lib/expectations/isSelected.js.map +0 -1
  83. package/lib/expectations/isVisible.d.ts +0 -14
  84. package/lib/expectations/isVisible.js +0 -20
  85. package/lib/expectations/isVisible.js.map +0 -1
  86. package/lib/input/Key.d.ts +0 -73
  87. package/lib/input/Key.js +0 -84
  88. package/lib/input/Key.js.map +0 -1
  89. package/lib/input/index.d.ts +0 -1
  90. package/lib/input/index.js +0 -14
  91. package/lib/input/index.js.map +0 -1
  92. package/lib/screenplay/abilities/BrowseTheWeb.js.map +0 -1
  93. package/lib/screenplay/interactions/Clear.d.ts +0 -79
  94. package/lib/screenplay/interactions/Clear.js +0 -97
  95. package/lib/screenplay/interactions/Clear.js.map +0 -1
  96. package/lib/screenplay/interactions/Click.d.ts +0 -73
  97. package/lib/screenplay/interactions/Click.js +0 -84
  98. package/lib/screenplay/interactions/Click.js.map +0 -1
  99. package/lib/screenplay/interactions/DoubleClick.d.ts +0 -90
  100. package/lib/screenplay/interactions/DoubleClick.js +0 -101
  101. package/lib/screenplay/interactions/DoubleClick.js.map +0 -1
  102. package/lib/screenplay/interactions/Enter.d.ts +0 -73
  103. package/lib/screenplay/interactions/Enter.js +0 -87
  104. package/lib/screenplay/interactions/Enter.js.map +0 -1
  105. package/lib/screenplay/interactions/EnterBuilder.d.ts +0 -25
  106. package/lib/screenplay/interactions/EnterBuilder.js.map +0 -1
  107. package/lib/screenplay/interactions/ExecuteScript.d.ts +0 -206
  108. package/lib/screenplay/interactions/ExecuteScript.js +0 -311
  109. package/lib/screenplay/interactions/ExecuteScript.js.map +0 -1
  110. package/lib/screenplay/interactions/Hover.d.ts +0 -78
  111. package/lib/screenplay/interactions/Hover.js +0 -89
  112. package/lib/screenplay/interactions/Hover.js.map +0 -1
  113. package/lib/screenplay/interactions/Navigate.d.ts +0 -141
  114. package/lib/screenplay/interactions/Navigate.js +0 -197
  115. package/lib/screenplay/interactions/Navigate.js.map +0 -1
  116. package/lib/screenplay/interactions/Press.d.ts +0 -84
  117. package/lib/screenplay/interactions/Press.js +0 -152
  118. package/lib/screenplay/interactions/Press.js.map +0 -1
  119. package/lib/screenplay/interactions/PressBuilder.d.ts +0 -26
  120. package/lib/screenplay/interactions/PressBuilder.js +0 -3
  121. package/lib/screenplay/interactions/PressBuilder.js.map +0 -1
  122. package/lib/screenplay/interactions/RightClick.d.ts +0 -89
  123. package/lib/screenplay/interactions/RightClick.js +0 -100
  124. package/lib/screenplay/interactions/RightClick.js.map +0 -1
  125. package/lib/screenplay/interactions/Scroll.d.ts +0 -75
  126. package/lib/screenplay/interactions/Scroll.js +0 -86
  127. package/lib/screenplay/interactions/Scroll.js.map +0 -1
  128. package/lib/screenplay/interactions/Wait.d.ts +0 -143
  129. package/lib/screenplay/interactions/Wait.js +0 -247
  130. package/lib/screenplay/interactions/Wait.js.map +0 -1
  131. package/lib/screenplay/interactions/WaitBuilder.d.ts +0 -32
  132. package/lib/screenplay/interactions/WaitBuilder.js +0 -3
  133. package/lib/screenplay/interactions/WaitBuilder.js.map +0 -1
  134. package/lib/screenplay/interactions/WebElementInteraction.d.ts +0 -37
  135. package/lib/screenplay/interactions/WebElementInteraction.js +0 -52
  136. package/lib/screenplay/interactions/WebElementInteraction.js.map +0 -1
  137. package/lib/screenplay/interactions/index.d.ts +0 -13
  138. package/lib/screenplay/interactions/index.js +0 -26
  139. package/lib/screenplay/interactions/index.js.map +0 -1
  140. package/lib/screenplay/questions/Attribute.d.ts +0 -82
  141. package/lib/screenplay/questions/Attribute.js +0 -102
  142. package/lib/screenplay/questions/Attribute.js.map +0 -1
  143. package/lib/screenplay/questions/CSSClasses.d.ts +0 -92
  144. package/lib/screenplay/questions/CSSClasses.js +0 -112
  145. package/lib/screenplay/questions/CSSClasses.js.map +0 -1
  146. package/lib/screenplay/questions/LastScriptExecution.d.ts +0 -14
  147. package/lib/screenplay/questions/LastScriptExecution.js +0 -22
  148. package/lib/screenplay/questions/LastScriptExecution.js.map +0 -1
  149. package/lib/screenplay/questions/NestedTargetBuilder.d.ts +0 -27
  150. package/lib/screenplay/questions/NestedTargetBuilder.js +0 -3
  151. package/lib/screenplay/questions/NestedTargetBuilder.js.map +0 -1
  152. package/lib/screenplay/questions/TargetBuilder.d.ts +0 -25
  153. package/lib/screenplay/questions/TargetBuilder.js +0 -3
  154. package/lib/screenplay/questions/TargetBuilder.js.map +0 -1
  155. package/lib/screenplay/questions/Text.d.ts +0 -95
  156. package/lib/screenplay/questions/Text.js +0 -130
  157. package/lib/screenplay/questions/Text.js.map +0 -1
  158. package/lib/screenplay/questions/Value.d.ts +0 -63
  159. package/lib/screenplay/questions/Value.js +0 -78
  160. package/lib/screenplay/questions/Value.js.map +0 -1
  161. package/lib/screenplay/questions/Website.d.ts +0 -21
  162. package/lib/screenplay/questions/Website.js +0 -31
  163. package/lib/screenplay/questions/Website.js.map +0 -1
  164. package/lib/screenplay/questions/index.d.ts +0 -10
  165. package/lib/screenplay/questions/index.js +0 -23
  166. package/lib/screenplay/questions/index.js.map +0 -1
  167. package/lib/screenplay/questions/lists.d.ts +0 -86
  168. package/lib/screenplay/questions/lists.js +0 -137
  169. package/lib/screenplay/questions/lists.js.map +0 -1
  170. package/lib/screenplay/questions/locators.d.ts +0 -196
  171. package/lib/screenplay/questions/locators.js +0 -219
  172. package/lib/screenplay/questions/locators.js.map +0 -1
  173. package/lib/screenplay/questions/targets.d.ts +0 -254
  174. package/lib/screenplay/questions/targets.js +0 -334
  175. package/lib/screenplay/questions/targets.js.map +0 -1
  176. package/lib/stage/crew/index.d.ts +0 -1
  177. package/lib/stage/crew/index.js +0 -14
  178. package/lib/stage/crew/index.js.map +0 -1
  179. package/lib/stage/crew/photographer/Photographer.d.ts +0 -83
  180. package/lib/stage/crew/photographer/Photographer.js +0 -102
  181. package/lib/stage/crew/photographer/Photographer.js.map +0 -1
  182. package/lib/stage/crew/photographer/index.d.ts +0 -2
  183. package/lib/stage/crew/photographer/index.js.map +0 -1
  184. package/lib/stage/crew/photographer/strategies/PhotoTakingStrategy.d.ts +0 -28
  185. package/lib/stage/crew/photographer/strategies/PhotoTakingStrategy.js +0 -65
  186. package/lib/stage/crew/photographer/strategies/PhotoTakingStrategy.js.map +0 -1
  187. package/lib/stage/crew/photographer/strategies/TakePhotosBeforeAndAfterInteractions.d.ts +0 -18
  188. package/lib/stage/crew/photographer/strategies/TakePhotosBeforeAndAfterInteractions.js +0 -30
  189. package/lib/stage/crew/photographer/strategies/TakePhotosBeforeAndAfterInteractions.js.map +0 -1
  190. package/lib/stage/crew/photographer/strategies/TakePhotosOfFailures.d.ts +0 -17
  191. package/lib/stage/crew/photographer/strategies/TakePhotosOfFailures.js +0 -28
  192. package/lib/stage/crew/photographer/strategies/TakePhotosOfFailures.js.map +0 -1
  193. package/lib/stage/crew/photographer/strategies/TakePhotosOfInteractions.d.ts +0 -19
  194. package/lib/stage/crew/photographer/strategies/TakePhotosOfInteractions.js +0 -28
  195. package/lib/stage/crew/photographer/strategies/TakePhotosOfInteractions.js.map +0 -1
  196. package/lib/stage/crew/photographer/strategies/index.d.ts +0 -4
  197. package/lib/stage/crew/photographer/strategies/index.js.map +0 -1
  198. package/lib/stage/index.d.ts +0 -1
  199. package/lib/stage/index.js +0 -14
  200. package/lib/stage/index.js.map +0 -1
  201. package/src/expectations/ElementExpectation.ts +0 -31
  202. package/src/expectations/index.ts +0 -6
  203. package/src/expectations/isActive.ts +0 -21
  204. package/src/expectations/isClickable.ts +0 -26
  205. package/src/expectations/isEnabled.ts +0 -19
  206. package/src/expectations/isPresent.ts +0 -20
  207. package/src/expectations/isSelected.ts +0 -19
  208. package/src/expectations/isVisible.ts +0 -19
  209. package/src/input/Key.ts +0 -83
  210. package/src/input/index.ts +0 -1
  211. package/src/screenplay/interactions/Clear.ts +0 -102
  212. package/src/screenplay/interactions/Click.ts +0 -85
  213. package/src/screenplay/interactions/DoubleClick.ts +0 -102
  214. package/src/screenplay/interactions/Enter.ts +0 -93
  215. package/src/screenplay/interactions/EnterBuilder.ts +0 -27
  216. package/src/screenplay/interactions/ExecuteScript.ts +0 -344
  217. package/src/screenplay/interactions/Hover.ts +0 -90
  218. package/src/screenplay/interactions/Navigate.ts +0 -208
  219. package/src/screenplay/interactions/Press.ts +0 -172
  220. package/src/screenplay/interactions/PressBuilder.ts +0 -28
  221. package/src/screenplay/interactions/RightClick.ts +0 -100
  222. package/src/screenplay/interactions/Scroll.ts +0 -87
  223. package/src/screenplay/interactions/Wait.ts +0 -267
  224. package/src/screenplay/interactions/WaitBuilder.ts +0 -34
  225. package/src/screenplay/interactions/WebElementInteraction.ts +0 -56
  226. package/src/screenplay/interactions/index.ts +0 -13
  227. package/src/screenplay/questions/Attribute.ts +0 -112
  228. package/src/screenplay/questions/CSSClasses.ts +0 -116
  229. package/src/screenplay/questions/LastScriptExecution.ts +0 -21
  230. package/src/screenplay/questions/NestedTargetBuilder.ts +0 -30
  231. package/src/screenplay/questions/TargetBuilder.ts +0 -27
  232. package/src/screenplay/questions/Text.ts +0 -140
  233. package/src/screenplay/questions/Value.ts +0 -82
  234. package/src/screenplay/questions/Website.ts +0 -34
  235. package/src/screenplay/questions/index.ts +0 -10
  236. package/src/screenplay/questions/lists.ts +0 -161
  237. package/src/screenplay/questions/locators.ts +0 -254
  238. package/src/screenplay/questions/targets.ts +0 -401
  239. package/src/stage/crew/index.ts +0 -1
  240. package/src/stage/crew/photographer/Photographer.ts +0 -108
  241. package/src/stage/crew/photographer/index.ts +0 -2
  242. package/src/stage/crew/photographer/strategies/PhotoTakingStrategy.ts +0 -103
  243. package/src/stage/crew/photographer/strategies/TakePhotosBeforeAndAfterInteractions.ts +0 -28
  244. package/src/stage/crew/photographer/strategies/TakePhotosOfFailures.ts +0 -26
  245. package/src/stage/crew/photographer/strategies/TakePhotosOfInteractions.ts +0 -26
  246. package/src/stage/crew/photographer/strategies/index.ts +0 -4
  247. package/src/stage/index.ts +0 -1
@@ -0,0 +1,38 @@
1
+ import { LogicError } from '@serenity-js/core';
2
+ import { Frame, Locator } from '@serenity-js/web';
3
+ import * as wdio from 'webdriverio';
4
+
5
+ export class WebdriverIOFrame extends Frame<wdio.Element<'async'>> {
6
+ constructor(
7
+ private readonly browser: wdio.Browser<'async'>,
8
+ locator: Locator<wdio.Element<'async'>>
9
+ ) {
10
+ super(locator);
11
+ }
12
+
13
+ async isPresent(): Promise<boolean> {
14
+ const element = await this.locator.nativeElement();
15
+ return element.isExisting();
16
+ }
17
+
18
+ async switchTo(): Promise<{ switchBack(): Promise<void> }> {
19
+ const element: wdio.Element<'async'> = await this.locator.nativeElement()
20
+
21
+ if (element.error) {
22
+ throw new LogicError(`Couldn't locate frame ${ this.locator }`, element.error);
23
+ }
24
+
25
+ try {
26
+ await this.browser.switchToFrame(element);
27
+
28
+ return {
29
+ switchBack: async (): Promise<void> => {
30
+ await this.browser.switchToParentFrame();
31
+ }
32
+ }
33
+ }
34
+ catch (error) {
35
+ throw new LogicError(`Couldn't switch to a frame located ${ this.locator }`, error);
36
+ }
37
+ }
38
+ }
@@ -0,0 +1,45 @@
1
+ import { ModalDialog } from '@serenity-js/web';
2
+ import { Browser } from 'webdriverio';
3
+
4
+ export class WebdriverIOModalDialog extends ModalDialog {
5
+ constructor(private readonly browser: Browser<'async'>) {
6
+ super();
7
+ }
8
+
9
+ accept(): Promise<void> {
10
+ return this.browser.acceptAlert();
11
+ }
12
+
13
+ dismiss(): Promise<void> {
14
+ return this.browser.dismissAlert();
15
+ }
16
+ text(): Promise<string> {
17
+ return this.browser.getAlertText();
18
+ }
19
+
20
+ async enterValue(value: string | number | (string | number)[]): Promise<void> {
21
+ const text = [].concat(value).join('');
22
+
23
+ await this.browser.sendAlertText(text);
24
+
25
+ // Puppeteer auto-accepts the dialog upon sendAlertText
26
+ // other implementations require the dialog to be closed
27
+ if (! this.browser.isDevTools) {
28
+ await this.browser.acceptAlert();
29
+ }
30
+ }
31
+
32
+ isPresent(): Promise<boolean> {
33
+ return this.browser.getAlertText()
34
+ .then(() => true, error => {
35
+ if (error && (
36
+ error.name === 'no such alert'
37
+ || error.message.startsWith('no such alert')
38
+ )) {
39
+ return false;
40
+ }
41
+
42
+ throw error;
43
+ });
44
+ }
45
+ }
@@ -0,0 +1,128 @@
1
+ import { Page } from '@serenity-js/web';
2
+ import { URL } from 'url';
3
+ import * as wdio from 'webdriverio';
4
+
5
+ export class WebdriverIOPage extends Page {
6
+ constructor(
7
+ private readonly browser: wdio.Browser<'async'>,
8
+ handle: string
9
+ ) {
10
+ super(handle);
11
+ }
12
+
13
+ title(): Promise<string> {
14
+ return this.switchToAndPerform(async browser => {
15
+ return browser.getTitle();
16
+ });
17
+ }
18
+
19
+ name(): Promise<string> {
20
+ return this.switchToAndPerform(async browser => {
21
+ return browser.execute(`return window.name`);
22
+ });
23
+ }
24
+
25
+ async url(): Promise<URL> {
26
+ return this.switchToAndPerform(async browser => {
27
+ return new URL(await browser.getUrl());
28
+ });
29
+ }
30
+
31
+ async viewportSize(): Promise<{ width: number, height: number }> {
32
+ return this.switchToAndPerform(async browser => {
33
+ if (! browser.isDevTools) {
34
+ const calculatedViewportSize = await browser.execute(`
35
+ return {
36
+ width: Math.max(document.documentElement.clientWidth, window.innerWidth || 0),
37
+ height: Math.max(document.documentElement.clientHeight, window.innerHeight || 0),
38
+ }
39
+ `) as { width: number, height: number };
40
+
41
+ // Chrome headless hard-codes window.innerWidth and window.innerHeight to 0
42
+ if (calculatedViewportSize.width > 0 && calculatedViewportSize.height > 0) {
43
+ return calculatedViewportSize;
44
+ }
45
+ }
46
+
47
+ return browser.getWindowSize();
48
+ });
49
+ }
50
+
51
+ setViewportSize(size: { width: number, height: number }): Promise<void> {
52
+ return this.switchToAndPerform(async browser => {
53
+ let desiredWindowSize = size;
54
+
55
+ if (! browser.isDevTools) {
56
+ desiredWindowSize = await browser.execute(`
57
+ var currentViewportWidth = Math.max(document.documentElement.clientWidth, window.innerWidth || 0)
58
+ var currentViewportHeight = Math.max(document.documentElement.clientHeight, window.innerHeight || 0)
59
+
60
+ return {
61
+ width: Math.max(window.outerWidth - currentViewportWidth + ${ size.width }, ${ size.width }),
62
+ height: Math.max(window.outerHeight - currentViewportHeight + ${ size.height }, ${ size.height }),
63
+ };
64
+ `);
65
+ }
66
+
67
+ return browser.setWindowSize(desiredWindowSize.width, desiredWindowSize.height);
68
+ });
69
+ }
70
+
71
+ async close(): Promise<void> {
72
+ return this.switchToAndPerform(browser => browser.closeWindow());
73
+ }
74
+
75
+ async closeOthers(): Promise<void> {
76
+ const windowHandles = await this.browser.getWindowHandles();
77
+
78
+ for (const handle of windowHandles) {
79
+ if (handle !== this.handle) {
80
+ await this.browser.switchToWindow(handle);
81
+ await this.browser.closeWindow();
82
+ }
83
+ }
84
+
85
+ await this.browser.switchToWindow(this.handle);
86
+ }
87
+
88
+ async isPresent(): Promise<boolean> {
89
+ const currentPageHandle = await this.browser.getWindowHandle();
90
+ const desiredPageHandle = this.handle;
91
+
92
+ const isOpen = await this.browser.switchToWindow(desiredPageHandle).then(() => true, _error => false);
93
+
94
+ await this.browser.switchToWindow(currentPageHandle);
95
+
96
+ return isOpen;
97
+ }
98
+
99
+ async switchTo(): Promise<{ switchBack(): Promise<void> }> {
100
+ const originalWindowHandle = await this.browser.getWindowHandle();
101
+
102
+ await this.browser.switchToWindow(this.handle);
103
+
104
+ return {
105
+ switchBack: async (): Promise<void> => {
106
+ await this.browser.switchToWindow(originalWindowHandle);
107
+ }
108
+ }
109
+ }
110
+
111
+ private async switchToAndPerform<T>(action: (browser: wdio.Browser<'async'>) => Promise<T> | T): Promise<T> {
112
+ const currentPageHandle = await this.browser.getWindowHandle();
113
+ const desiredPageHandle = this.handle;
114
+ const shouldSwitch = currentPageHandle !== desiredPageHandle;
115
+
116
+ if (shouldSwitch) {
117
+ await this.browser.switchToWindow(desiredPageHandle);
118
+ }
119
+
120
+ const result = await action(this.browser);
121
+
122
+ if (shouldSwitch) {
123
+ await this.browser.switchToWindow(currentPageHandle);
124
+ }
125
+
126
+ return result;
127
+ }
128
+ }
@@ -0,0 +1,227 @@
1
+ import { PageElement } from '@serenity-js/web';
2
+ import * as wdio from 'webdriverio';
3
+
4
+ export class WebdriverIOPageElement
5
+ extends PageElement<wdio.Element<'async'>>
6
+ {
7
+ of(parent: WebdriverIOPageElement): WebdriverIOPageElement {
8
+ return new WebdriverIOPageElement(this.locator.of(parent.locator))
9
+ }
10
+
11
+ async clearValue(): Promise<void> {
12
+ const element = await this.nativeElement();
13
+ return element.clearValue();
14
+ }
15
+
16
+ async click(): Promise<void> {
17
+ const element = await this.nativeElement();
18
+ return element.click();
19
+ }
20
+
21
+ async doubleClick(): Promise<void> {
22
+ const element = await this.nativeElement();
23
+ return element.doubleClick();
24
+ }
25
+
26
+ async enterValue(value: string | number | Array<string | number>): Promise<void> {
27
+ const element = await this.nativeElement();
28
+ return element.addValue(value);
29
+ }
30
+
31
+ async scrollIntoView(): Promise<void> {
32
+ const element = await this.nativeElement();
33
+ return element.scrollIntoView();
34
+ }
35
+
36
+ async hoverOver(): Promise<void> {
37
+ const element = await this.nativeElement();
38
+ return element.moveTo();
39
+ }
40
+
41
+ async rightClick(): Promise<void> {
42
+ const element = await this.nativeElement();
43
+ return element.click({ button: 'right' });
44
+ }
45
+
46
+ async attribute(name: string): Promise<string> {
47
+ const element = await this.nativeElement();
48
+ return element.getAttribute(name);
49
+ }
50
+
51
+ async text(): Promise<string> {
52
+ const element = await this.nativeElement();
53
+ return element.getText();
54
+ }
55
+
56
+ async value(): Promise<string> {
57
+ const element = await this.nativeElement();
58
+ return element.getValue();
59
+ }
60
+
61
+ async isActive(): Promise<boolean> {
62
+ const element = await this.nativeElement();
63
+ return element.isFocused();
64
+ }
65
+
66
+ async isClickable(): Promise<boolean> {
67
+ const element = await this.nativeElement();
68
+ return element.isClickable();
69
+ }
70
+
71
+ async isEnabled(): Promise<boolean> {
72
+ const element = await this.nativeElement();
73
+ return element.isEnabled();
74
+ }
75
+
76
+ async isPresent(): Promise<boolean> {
77
+ const element = await this.nativeElement();
78
+ return element.isExisting();
79
+ }
80
+
81
+ async isSelected(): Promise<boolean> {
82
+ const element = await this.nativeElement();
83
+ return element.isSelected();
84
+ }
85
+
86
+ /**
87
+ * @desc
88
+ * Checks if the PageElement:
89
+ * - is displayed,
90
+ * - is visible within the browser viewport,
91
+ * - has not its center covered by other elements
92
+ *
93
+ * @see https://webdriver.io/docs/api/element/isDisplayedInViewport/
94
+ */
95
+ async isVisible(): Promise<boolean> { // isVisible?
96
+ const element = await this.nativeElement();
97
+
98
+ if (! await element.isDisplayed()) {
99
+ return false;
100
+ }
101
+
102
+ if (! await element.isDisplayedInViewport()) {
103
+ return false;
104
+ }
105
+
106
+ const browser = await this.browserFor(element);
107
+
108
+ /* eslint-disable no-var,unicorn/consistent-function-scoping,unicorn/prevent-abbreviations,@typescript-eslint/ban-ts-comment,unicorn/no-for-loop,prefer-const */
109
+
110
+ // get element at cx/cy and see if the element we found is our element, and therefore it's visible.
111
+ return browser.execute(
112
+ /* istanbul ignore next */
113
+ function isVisible(element: any) {
114
+
115
+ if (!element.getBoundingClientRect || !element.scrollIntoView || !element.contains || !element.getClientRects || !document.elementFromPoint) {
116
+ return false
117
+ }
118
+
119
+ // Edge before switching to Chromium
120
+ const isOldEdge = !!window['StyleMedia']
121
+
122
+ // get overlapping element
123
+ function getOverlappingElement (elem: HTMLElement, context?: Document) {
124
+ context = context || document
125
+ const elemDimension = elem.getBoundingClientRect()
126
+ const x = elemDimension.left + (elem.clientWidth / 2)
127
+ const y = elemDimension.top + (elem.clientHeight / 2)
128
+ return context.elementFromPoint(x, y)
129
+ }
130
+
131
+ // get overlapping element rects (currently only the first)
132
+ // applicable if element's text is multiline.
133
+ function getOverlappingRects (elem: HTMLElement, context?: Document) {
134
+ context = context || document
135
+ const elems = []
136
+
137
+ const rects = elem.getClientRects()
138
+ // webdriver clicks on center of the first element's rect (line of text), it might change in future
139
+ const rect = rects[0]
140
+ const x = rect.left + (rect.width / 2)
141
+ const y = rect.top + (rect.height / 2)
142
+ elems.push(context.elementFromPoint(x, y))
143
+
144
+ return elems
145
+ }
146
+
147
+ // get overlapping elements
148
+ function getOverlappingElements (elem: HTMLElement, context?: Document) {
149
+ return [getOverlappingElement(elem, context)].concat(getOverlappingRects(elem, context))
150
+ }
151
+
152
+ // is a node a descendant of a given node
153
+ function nodeContains (elem: HTMLElement, otherNode: HTMLElement) {
154
+ // Edge doesn't support neither Shadow Dom nor contains if ShadowRoot polyfill is used
155
+ if (isOldEdge) {
156
+ let tmpElement = otherNode as HTMLElement | ShadowRoot | Element
157
+ while (tmpElement) {
158
+ if (tmpElement === elem) {
159
+ return true
160
+ }
161
+
162
+ tmpElement = tmpElement.parentNode as ShadowRoot
163
+ // DocumentFragment / ShadowRoot polyfill like ShadyRoot
164
+ if (tmpElement && tmpElement.nodeType === 11 && tmpElement.host) {
165
+ tmpElement = tmpElement.host
166
+ }
167
+ }
168
+ return false
169
+ }
170
+
171
+ return elem.contains(otherNode)
172
+ }
173
+
174
+ // is one of overlapping elements the `elem` or one of its child
175
+ function isOverlappingElementMatch (elementsFromPoint: HTMLElement[], elem: HTMLElement): boolean {
176
+ if (elementsFromPoint.some(function (elementFromPoint) {
177
+ return elementFromPoint === elem || nodeContains(elem, elementFromPoint)
178
+ })) {
179
+ return true
180
+ }
181
+
182
+ // shadow root
183
+ // filter unique elements with shadowRoot
184
+ // @ts-ignore
185
+ let elemsWithShadowRoot = [].concat(elementsFromPoint)
186
+ elemsWithShadowRoot = elemsWithShadowRoot.filter(function (x: HTMLElement) {
187
+ return x && x.shadowRoot && x.shadowRoot.elementFromPoint
188
+ })
189
+
190
+ // getOverlappingElements of every element with shadowRoot
191
+ let shadowElementsFromPoint: HTMLElement[] = []
192
+ for (let i = 0; i < elemsWithShadowRoot.length; ++i) {
193
+ let shadowElement = elemsWithShadowRoot[i]
194
+ shadowElementsFromPoint = shadowElementsFromPoint.concat(
195
+ getOverlappingElements(elem, (shadowElement as HTMLElement).shadowRoot as any) as any
196
+ )
197
+ }
198
+ // remove duplicates and parents
199
+ // @ts-ignore
200
+ shadowElementsFromPoint = [].concat(shadowElementsFromPoint)
201
+ shadowElementsFromPoint = shadowElementsFromPoint.filter(function (x) {
202
+ return !elementsFromPoint.includes(x)
203
+ })
204
+
205
+ if (shadowElementsFromPoint.length === 0) {
206
+ return false
207
+ }
208
+
209
+ return isOverlappingElementMatch(shadowElementsFromPoint, elem)
210
+ }
211
+
212
+ return isOverlappingElementMatch(getOverlappingElements(element) as any as HTMLElement[], element)
213
+ },
214
+ element,
215
+ );
216
+
217
+ /* eslint-enable no-var,unicorn/consistent-function-scoping,unicorn/prevent-abbreviations,@typescript-eslint/ban-ts-comment,unicorn/no-for-loop,prefer-const */
218
+ }
219
+
220
+ // based on https://github.com/webdriverio/webdriverio/blob/dec6da76b0e218af935dbf39735ae3491d5edd8c/packages/webdriverio/src/utils/index.ts#L98
221
+ private async browserFor(nativeElement: wdio.Element<'async'> | wdio.Browser<'async'>): Promise<wdio.Browser<'async'>> {
222
+ const element = nativeElement as wdio.Element<'async'>;
223
+ return element.parent
224
+ ? this.browserFor(element.parent)
225
+ : nativeElement
226
+ }
227
+ }
@@ -0,0 +1,5 @@
1
+ export * from './WebdriverIOCookie';
2
+ export * from './WebdriverIOFrame';
3
+ export * from './WebdriverIOModalDialog';
4
+ export * from './WebdriverIOPage';
5
+ export * from './WebdriverIOPageElement';
@@ -0,0 +1,43 @@
1
+ import { Locator, PageElement, Selector } from '@serenity-js/web';
2
+ import * as wdio from 'webdriverio';
3
+
4
+ import { WebdriverIOPageElement } from '../WebdriverIOPageElement';
5
+ import { WebdriverIONativeElementRoot } from './WebdriverIONativeElementRoot';
6
+
7
+ export class WebdriverIOLocator<Selector_Type extends Selector> extends Locator<wdio.Element<'async'>, WebdriverIONativeElementRoot, Selector_Type> {
8
+
9
+ static createRootLocator<ST extends Selector>(browser: WebdriverIONativeElementRoot, selector: ST, webdriverioSelector: string): WebdriverIOLocator<ST> {
10
+ return new WebdriverIOLocator(
11
+ () => browser,
12
+ selector,
13
+ (root: WebdriverIONativeElementRoot) => root.$(webdriverioSelector) as unknown as wdio.Element<'async'>,
14
+ (root: WebdriverIONativeElementRoot) => root.$$(webdriverioSelector)
15
+ );
16
+ }
17
+
18
+ of(parent: WebdriverIOLocator<unknown>): Locator<wdio.Element<'async'>, WebdriverIONativeElementRoot, Selector_Type> {
19
+ return new WebdriverIOLocator(
20
+ () => parent.nativeElement(),
21
+ this.selector,
22
+ (parentRoot: WebdriverIONativeElementRoot) => this.locateElement(parentRoot),
23
+ (parentRoot: WebdriverIONativeElementRoot) => this.locateAllElements(parentRoot),
24
+ );
25
+ }
26
+
27
+ element(): PageElement<wdio.Element<'async'>> {
28
+ return new WebdriverIOPageElement(this);
29
+ }
30
+
31
+ async allElements(): Promise<Array<PageElement<wdio.Element<'async'>>>> {
32
+ const elements = await this.locateAllElements(await this.parentRoot());
33
+
34
+ return elements.map(childElement =>
35
+ new WebdriverIOPageElement(new WebdriverIOLocator(
36
+ () => this.parentRoot(),
37
+ this.selector,
38
+ () => childElement,
39
+ () => [ childElement ],
40
+ ))
41
+ );
42
+ }
43
+ }
@@ -0,0 +1,3 @@
1
+ import * as wdio from 'webdriverio';
2
+
3
+ export type WebdriverIONativeElementRoot = Pick<wdio.Browser<'async'>, '$' | '$$'>;
@@ -0,0 +1,2 @@
1
+ export * from './WebdriverIOLocator';
2
+ export * from './WebdriverIONativeElementRoot';
@@ -1,11 +0,0 @@
1
- import { AnswersQuestions, Expectation, ExpectationOutcome } from '@serenity-js/core';
2
- import { Element } from 'webdriverio';
3
- /**
4
- * @access private
5
- */
6
- export declare class ElementExpectation extends Expectation<any, Element<'async'>> {
7
- private readonly fn;
8
- static forElementTo(message: string, fn: (actual: Element<'async'>) => Promise<boolean>): Expectation<any, Element<'async'>>;
9
- constructor(subject: string, fn: (actual: Element<'async'>) => Promise<boolean>);
10
- answeredBy(actor: AnswersQuestions): (actual: Element<'async'>) => Promise<ExpectationOutcome<boolean, Element<'async'>>>;
11
- }
@@ -1,27 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ElementExpectation = void 0;
4
- const core_1 = require("@serenity-js/core");
5
- /**
6
- * @access private
7
- */
8
- class ElementExpectation extends core_1.Expectation {
9
- constructor(subject, fn) {
10
- super(subject);
11
- this.fn = fn;
12
- }
13
- static forElementTo(message, fn) {
14
- return new ElementExpectation(message, fn);
15
- }
16
- answeredBy(actor) {
17
- return (actual) => this.fn(actual)
18
- .then(expectationMet => expectationMet
19
- ? new core_1.ExpectationMet(this.toString(), undefined, actual)
20
- : new core_1.ExpectationNotMet(this.toString(), undefined, actual))
21
- .catch(error => {
22
- return new core_1.ExpectationNotMet(`${this.toString()} (${error.message})`, undefined, actual);
23
- });
24
- }
25
- }
26
- exports.ElementExpectation = ElementExpectation;
27
- //# sourceMappingURL=ElementExpectation.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"ElementExpectation.js","sourceRoot":"","sources":["../../src/expectations/ElementExpectation.ts"],"names":[],"mappings":";;;AAAA,4CAAyH;AAGzH;;GAEG;AACH,MAAa,kBAAmB,SAAQ,kBAAkC;IAKtE,YACI,OAAe,EACE,EAAkD;QAEnE,KAAK,CAAC,OAAO,CAAC,CAAC;QAFE,OAAE,GAAF,EAAE,CAAgD;IAGvE,CAAC;IATD,MAAM,CAAC,YAAY,CAAC,OAAe,EAAE,EAAkD;QACnF,OAAO,IAAI,kBAAkB,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IAC/C,CAAC;IASD,UAAU,CAAC,KAAuB;QAC9B,OAAO,CAAC,MAAwB,EAAE,EAAE,CAChC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC;aACV,IAAI,CAAC,cAAc,CAAC,EAAE,CACnB,cAAc;YACV,CAAC,CAAC,IAAI,qBAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC;YACxD,CAAC,CAAC,IAAI,wBAAiB,CAAC,IAAI,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,CAClE;aACA,KAAK,CAAC,KAAK,CAAC,EAAE;YACX,OAAO,IAAI,wBAAiB,CAAC,GAAI,IAAI,CAAC,QAAQ,EAAG,KAAM,KAAK,CAAC,OAAQ,GAAG,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QACjG,CAAC,CAAC,CAAC;IACf,CAAC;CACJ;AAxBD,gDAwBC"}
@@ -1,6 +0,0 @@
1
- export * from './isActive';
2
- export * from './isClickable';
3
- export * from './isEnabled';
4
- export * from './isPresent';
5
- export * from './isSelected';
6
- export * from './isVisible';
@@ -1,19 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
- }) : (function(o, m, k, k2) {
6
- if (k2 === undefined) k2 = k;
7
- o[k2] = m[k];
8
- }));
9
- var __exportStar = (this && this.__exportStar) || function(m, exports) {
10
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
11
- };
12
- Object.defineProperty(exports, "__esModule", { value: true });
13
- __exportStar(require("./isActive"), exports);
14
- __exportStar(require("./isClickable"), exports);
15
- __exportStar(require("./isEnabled"), exports);
16
- __exportStar(require("./isPresent"), exports);
17
- __exportStar(require("./isSelected"), exports);
18
- __exportStar(require("./isVisible"), exports);
19
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/expectations/index.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,6CAA2B;AAC3B,gDAA8B;AAC9B,8CAA4B;AAC5B,8CAA4B;AAC5B,+CAA6B;AAC7B,8CAA4B"}
@@ -1,15 +0,0 @@
1
- import { Expectation } from '@serenity-js/core';
2
- import { Element } from 'webdriverio';
3
- /**
4
- * @desc
5
- * Expectation that the element is active (has focus).
6
- * If the selector matches multiple elements, it will return true if one of the elements has focus.
7
- *
8
- * @returns {@serenity-js/core/lib/screenplay/questions~Expectation<boolean, Element<'async'>>}
9
- *
10
- * @see https://webdriver.io/docs/api/element/isFocused/
11
- * @see {@link @serenity-js/assertions~Ensure}
12
- * @see {@link @serenity-js/core/lib/screenplay/questions~Check}
13
- * @see {@link Wait}
14
- */
15
- export declare function isActive(): Expectation<boolean, Element<'async'>>;
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isActive = void 0;
4
- const ElementExpectation_1 = require("./ElementExpectation");
5
- /**
6
- * @desc
7
- * Expectation that the element is active (has focus).
8
- * If the selector matches multiple elements, it will return true if one of the elements has focus.
9
- *
10
- * @returns {@serenity-js/core/lib/screenplay/questions~Expectation<boolean, Element<'async'>>}
11
- *
12
- * @see https://webdriver.io/docs/api/element/isFocused/
13
- * @see {@link @serenity-js/assertions~Ensure}
14
- * @see {@link @serenity-js/core/lib/screenplay/questions~Check}
15
- * @see {@link Wait}
16
- */
17
- function isActive() {
18
- return ElementExpectation_1.ElementExpectation.forElementTo('become active', actual => actual.isFocused());
19
- }
20
- exports.isActive = isActive;
21
- //# sourceMappingURL=isActive.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"isActive.js","sourceRoot":"","sources":["../../src/expectations/isActive.ts"],"names":[],"mappings":";;;AAGA,6DAA0D;AAE1D;;;;;;;;;;;GAWG;AACH,SAAgB,QAAQ;IACpB,OAAO,uCAAkB,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;AAC1F,CAAC;AAFD,4BAEC"}
@@ -1,20 +0,0 @@
1
- import { Expectation } from '@serenity-js/core';
2
- import { Element } from 'webdriverio';
3
- /**
4
- * @desc
5
- * Expectation that an element is clickable, which means:
6
- * - it exists
7
- * - it is visible
8
- * - it is within viewport (if not, try to scroll to it)
9
- * - its center is not overlapped with another element
10
- * - it is not disabled
11
- * otherwise return false.
12
- *
13
- * @returns {@serenity-js/core/lib/screenplay/questions~Expectation<boolean, Element<'async'>>}
14
- *
15
- * @see https://webdriver.io/docs/api/element/isClickable/
16
- * @see {@link @serenity-js/assertions~Ensure}
17
- * @see {@link @serenity-js/core/lib/screenplay/questions~Check}
18
- * @see {@link Wait}
19
- */
20
- export declare function isClickable(): Expectation<boolean, Element<'async'>>;
@@ -1,26 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.isClickable = void 0;
4
- const ElementExpectation_1 = require("./ElementExpectation");
5
- /**
6
- * @desc
7
- * Expectation that an element is clickable, which means:
8
- * - it exists
9
- * - it is visible
10
- * - it is within viewport (if not, try to scroll to it)
11
- * - its center is not overlapped with another element
12
- * - it is not disabled
13
- * otherwise return false.
14
- *
15
- * @returns {@serenity-js/core/lib/screenplay/questions~Expectation<boolean, Element<'async'>>}
16
- *
17
- * @see https://webdriver.io/docs/api/element/isClickable/
18
- * @see {@link @serenity-js/assertions~Ensure}
19
- * @see {@link @serenity-js/core/lib/screenplay/questions~Check}
20
- * @see {@link Wait}
21
- */
22
- function isClickable() {
23
- return ElementExpectation_1.ElementExpectation.forElementTo('become clickable', actual => actual.isClickable());
24
- }
25
- exports.isClickable = isClickable;
26
- //# sourceMappingURL=isClickable.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"isClickable.js","sourceRoot":"","sources":["../../src/expectations/isClickable.ts"],"names":[],"mappings":";;;AAGA,6DAA0D;AAE1D;;;;;;;;;;;;;;;;GAgBG;AACH,SAAgB,WAAW;IACvB,OAAO,uCAAkB,CAAC,YAAY,CAAC,kBAAkB,EAAE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;AAC/F,CAAC;AAFD,kCAEC"}