@serenity-js/web 3.0.0-rc.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 (250) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/LICENSE.md +201 -0
  3. package/NOTICE.md +1 -0
  4. package/README.md +21 -0
  5. package/lib/errors/CookieMissingError.d.ts +4 -0
  6. package/lib/errors/CookieMissingError.js +11 -0
  7. package/lib/errors/CookieMissingError.js.map +1 -0
  8. package/lib/errors/index.d.ts +1 -0
  9. package/lib/errors/index.js +14 -0
  10. package/lib/errors/index.js.map +1 -0
  11. package/lib/expectations/ElementExpectation.d.ts +11 -0
  12. package/lib/expectations/ElementExpectation.js +27 -0
  13. package/lib/expectations/ElementExpectation.js.map +1 -0
  14. package/lib/expectations/index.d.ts +6 -0
  15. package/lib/expectations/index.js +19 -0
  16. package/lib/expectations/index.js.map +1 -0
  17. package/lib/expectations/isActive.d.ts +15 -0
  18. package/lib/expectations/isActive.js +22 -0
  19. package/lib/expectations/isActive.js.map +1 -0
  20. package/lib/expectations/isClickable.d.ts +20 -0
  21. package/lib/expectations/isClickable.js +30 -0
  22. package/lib/expectations/isClickable.js.map +1 -0
  23. package/lib/expectations/isEnabled.d.ts +14 -0
  24. package/lib/expectations/isEnabled.js +20 -0
  25. package/lib/expectations/isEnabled.js.map +1 -0
  26. package/lib/expectations/isPresent.d.ts +15 -0
  27. package/lib/expectations/isPresent.js +22 -0
  28. package/lib/expectations/isPresent.js.map +1 -0
  29. package/lib/expectations/isSelected.d.ts +14 -0
  30. package/lib/expectations/isSelected.js +23 -0
  31. package/lib/expectations/isSelected.js.map +1 -0
  32. package/lib/expectations/isVisible.d.ts +14 -0
  33. package/lib/expectations/isVisible.js +26 -0
  34. package/lib/expectations/isVisible.js.map +1 -0
  35. package/lib/index.d.ts +5 -0
  36. package/lib/index.js +18 -0
  37. package/lib/index.js.map +1 -0
  38. package/lib/input/Key.d.ts +73 -0
  39. package/lib/input/Key.js +84 -0
  40. package/lib/input/Key.js.map +1 -0
  41. package/lib/input/index.d.ts +1 -0
  42. package/lib/input/index.js +14 -0
  43. package/lib/input/index.js.map +1 -0
  44. package/lib/screenplay/abilities/BrowseTheWeb.d.ts +58 -0
  45. package/lib/screenplay/abilities/BrowseTheWeb.js +19 -0
  46. package/lib/screenplay/abilities/BrowseTheWeb.js.map +1 -0
  47. package/lib/screenplay/abilities/BrowserCapabilities.d.ts +5 -0
  48. package/lib/screenplay/abilities/BrowserCapabilities.js +3 -0
  49. package/lib/screenplay/abilities/BrowserCapabilities.js.map +1 -0
  50. package/lib/screenplay/abilities/index.d.ts +2 -0
  51. package/lib/screenplay/abilities/index.js +15 -0
  52. package/lib/screenplay/abilities/index.js.map +1 -0
  53. package/lib/screenplay/index.d.ts +4 -0
  54. package/lib/screenplay/index.js +17 -0
  55. package/lib/screenplay/index.js.map +1 -0
  56. package/lib/screenplay/interactions/Clear.d.ts +79 -0
  57. package/lib/screenplay/interactions/Clear.js +97 -0
  58. package/lib/screenplay/interactions/Clear.js.map +1 -0
  59. package/lib/screenplay/interactions/Click.d.ts +73 -0
  60. package/lib/screenplay/interactions/Click.js +85 -0
  61. package/lib/screenplay/interactions/Click.js.map +1 -0
  62. package/lib/screenplay/interactions/DoubleClick.d.ts +90 -0
  63. package/lib/screenplay/interactions/DoubleClick.js +101 -0
  64. package/lib/screenplay/interactions/DoubleClick.js.map +1 -0
  65. package/lib/screenplay/interactions/Enter.d.ts +73 -0
  66. package/lib/screenplay/interactions/Enter.js +86 -0
  67. package/lib/screenplay/interactions/Enter.js.map +1 -0
  68. package/lib/screenplay/interactions/EnterBuilder.d.ts +25 -0
  69. package/lib/screenplay/interactions/EnterBuilder.js +3 -0
  70. package/lib/screenplay/interactions/EnterBuilder.js.map +1 -0
  71. package/lib/screenplay/interactions/ExecuteScript.d.ts +206 -0
  72. package/lib/screenplay/interactions/ExecuteScript.js +312 -0
  73. package/lib/screenplay/interactions/ExecuteScript.js.map +1 -0
  74. package/lib/screenplay/interactions/Hover.d.ts +78 -0
  75. package/lib/screenplay/interactions/Hover.js +89 -0
  76. package/lib/screenplay/interactions/Hover.js.map +1 -0
  77. package/lib/screenplay/interactions/Navigate.d.ts +142 -0
  78. package/lib/screenplay/interactions/Navigate.js +198 -0
  79. package/lib/screenplay/interactions/Navigate.js.map +1 -0
  80. package/lib/screenplay/interactions/PageElementInteraction.d.ts +39 -0
  81. package/lib/screenplay/interactions/PageElementInteraction.js +54 -0
  82. package/lib/screenplay/interactions/PageElementInteraction.js.map +1 -0
  83. package/lib/screenplay/interactions/Press.d.ts +84 -0
  84. package/lib/screenplay/interactions/Press.js +171 -0
  85. package/lib/screenplay/interactions/Press.js.map +1 -0
  86. package/lib/screenplay/interactions/PressBuilder.d.ts +26 -0
  87. package/lib/screenplay/interactions/PressBuilder.js +3 -0
  88. package/lib/screenplay/interactions/PressBuilder.js.map +1 -0
  89. package/lib/screenplay/interactions/RightClick.d.ts +89 -0
  90. package/lib/screenplay/interactions/RightClick.js +100 -0
  91. package/lib/screenplay/interactions/RightClick.js.map +1 -0
  92. package/lib/screenplay/interactions/Scroll.d.ts +83 -0
  93. package/lib/screenplay/interactions/Scroll.js +97 -0
  94. package/lib/screenplay/interactions/Scroll.js.map +1 -0
  95. package/lib/screenplay/interactions/Select.d.ts +212 -0
  96. package/lib/screenplay/interactions/Select.js +291 -0
  97. package/lib/screenplay/interactions/Select.js.map +1 -0
  98. package/lib/screenplay/interactions/SelectBuilder.d.ts +33 -0
  99. package/lib/screenplay/interactions/SelectBuilder.js +3 -0
  100. package/lib/screenplay/interactions/SelectBuilder.js.map +1 -0
  101. package/lib/screenplay/interactions/Switch.d.ts +150 -0
  102. package/lib/screenplay/interactions/Switch.js +209 -0
  103. package/lib/screenplay/interactions/Switch.js.map +1 -0
  104. package/lib/screenplay/interactions/TakeScreenshot.d.ts +67 -0
  105. package/lib/screenplay/interactions/TakeScreenshot.js +86 -0
  106. package/lib/screenplay/interactions/TakeScreenshot.js.map +1 -0
  107. package/lib/screenplay/interactions/Wait.d.ts +143 -0
  108. package/lib/screenplay/interactions/Wait.js +242 -0
  109. package/lib/screenplay/interactions/Wait.js.map +1 -0
  110. package/lib/screenplay/interactions/WaitBuilder.d.ts +32 -0
  111. package/lib/screenplay/interactions/WaitBuilder.js +3 -0
  112. package/lib/screenplay/interactions/WaitBuilder.js.map +1 -0
  113. package/lib/screenplay/interactions/index.d.ts +16 -0
  114. package/lib/screenplay/interactions/index.js +29 -0
  115. package/lib/screenplay/interactions/index.js.map +1 -0
  116. package/lib/screenplay/models/Cookie.d.ts +117 -0
  117. package/lib/screenplay/models/Cookie.js +176 -0
  118. package/lib/screenplay/models/Cookie.js.map +1 -0
  119. package/lib/screenplay/models/CookieData.d.ts +89 -0
  120. package/lib/screenplay/models/CookieData.js +3 -0
  121. package/lib/screenplay/models/CookieData.js.map +1 -0
  122. package/lib/screenplay/models/ModalDialog.d.ts +9 -0
  123. package/lib/screenplay/models/ModalDialog.js +14 -0
  124. package/lib/screenplay/models/ModalDialog.js.map +1 -0
  125. package/lib/screenplay/models/Page.d.ts +83 -0
  126. package/lib/screenplay/models/Page.js +52 -0
  127. package/lib/screenplay/models/Page.js.map +1 -0
  128. package/lib/screenplay/models/PageElement.d.ts +30 -0
  129. package/lib/screenplay/models/PageElement.js +62 -0
  130. package/lib/screenplay/models/PageElement.js.map +1 -0
  131. package/lib/screenplay/models/PageElements.d.ts +20 -0
  132. package/lib/screenplay/models/PageElements.js +49 -0
  133. package/lib/screenplay/models/PageElements.js.map +1 -0
  134. package/lib/screenplay/models/index.d.ts +6 -0
  135. package/lib/screenplay/models/index.js +19 -0
  136. package/lib/screenplay/models/index.js.map +1 -0
  137. package/lib/screenplay/questions/Attribute.d.ts +83 -0
  138. package/lib/screenplay/questions/Attribute.js +103 -0
  139. package/lib/screenplay/questions/Attribute.js.map +1 -0
  140. package/lib/screenplay/questions/CssClasses.d.ts +93 -0
  141. package/lib/screenplay/questions/CssClasses.js +113 -0
  142. package/lib/screenplay/questions/CssClasses.js.map +1 -0
  143. package/lib/screenplay/questions/ElementQuestion.d.ts +34 -0
  144. package/lib/screenplay/questions/ElementQuestion.js +53 -0
  145. package/lib/screenplay/questions/ElementQuestion.js.map +1 -0
  146. package/lib/screenplay/questions/LastScriptExecution.d.ts +14 -0
  147. package/lib/screenplay/questions/LastScriptExecution.js +22 -0
  148. package/lib/screenplay/questions/LastScriptExecution.js.map +1 -0
  149. package/lib/screenplay/questions/Selected.d.ts +185 -0
  150. package/lib/screenplay/questions/Selected.js +210 -0
  151. package/lib/screenplay/questions/Selected.js.map +1 -0
  152. package/lib/screenplay/questions/Text.d.ts +99 -0
  153. package/lib/screenplay/questions/Text.js +131 -0
  154. package/lib/screenplay/questions/Text.js.map +1 -0
  155. package/lib/screenplay/questions/Value.d.ts +64 -0
  156. package/lib/screenplay/questions/Value.js +78 -0
  157. package/lib/screenplay/questions/Value.js.map +1 -0
  158. package/lib/screenplay/questions/index.d.ts +6 -0
  159. package/lib/screenplay/questions/index.js +19 -0
  160. package/lib/screenplay/questions/index.js.map +1 -0
  161. package/lib/stage/crew/index.d.ts +1 -0
  162. package/lib/stage/crew/index.js +14 -0
  163. package/lib/stage/crew/index.js.map +1 -0
  164. package/lib/stage/crew/photographer/Photographer.d.ts +83 -0
  165. package/lib/stage/crew/photographer/Photographer.js +102 -0
  166. package/lib/stage/crew/photographer/Photographer.js.map +1 -0
  167. package/lib/stage/crew/photographer/index.d.ts +2 -0
  168. package/lib/stage/crew/photographer/index.js +15 -0
  169. package/lib/stage/crew/photographer/index.js.map +1 -0
  170. package/lib/stage/crew/photographer/strategies/PhotoTakingStrategy.d.ts +28 -0
  171. package/lib/stage/crew/photographer/strategies/PhotoTakingStrategy.js +81 -0
  172. package/lib/stage/crew/photographer/strategies/PhotoTakingStrategy.js.map +1 -0
  173. package/lib/stage/crew/photographer/strategies/TakePhotosBeforeAndAfterInteractions.d.ts +18 -0
  174. package/lib/stage/crew/photographer/strategies/TakePhotosBeforeAndAfterInteractions.js +30 -0
  175. package/lib/stage/crew/photographer/strategies/TakePhotosBeforeAndAfterInteractions.js.map +1 -0
  176. package/lib/stage/crew/photographer/strategies/TakePhotosOfFailures.d.ts +17 -0
  177. package/lib/stage/crew/photographer/strategies/TakePhotosOfFailures.js +28 -0
  178. package/lib/stage/crew/photographer/strategies/TakePhotosOfFailures.js.map +1 -0
  179. package/lib/stage/crew/photographer/strategies/TakePhotosOfInteractions.d.ts +19 -0
  180. package/lib/stage/crew/photographer/strategies/TakePhotosOfInteractions.js +28 -0
  181. package/lib/stage/crew/photographer/strategies/TakePhotosOfInteractions.js.map +1 -0
  182. package/lib/stage/crew/photographer/strategies/index.d.ts +4 -0
  183. package/lib/stage/crew/photographer/strategies/index.js +17 -0
  184. package/lib/stage/crew/photographer/strategies/index.js.map +1 -0
  185. package/lib/stage/index.d.ts +1 -0
  186. package/lib/stage/index.js +14 -0
  187. package/lib/stage/index.js.map +1 -0
  188. package/package.json +85 -0
  189. package/src/errors/CookieMissingError.ts +7 -0
  190. package/src/errors/index.ts +1 -0
  191. package/src/expectations/ElementExpectation.ts +32 -0
  192. package/src/expectations/index.ts +6 -0
  193. package/src/expectations/isActive.ts +22 -0
  194. package/src/expectations/isClickable.ts +32 -0
  195. package/src/expectations/isEnabled.ts +19 -0
  196. package/src/expectations/isPresent.ts +21 -0
  197. package/src/expectations/isSelected.ts +24 -0
  198. package/src/expectations/isVisible.ts +28 -0
  199. package/src/index.ts +5 -0
  200. package/src/input/Key.ts +83 -0
  201. package/src/input/index.ts +1 -0
  202. package/src/screenplay/abilities/BrowseTheWeb.ts +89 -0
  203. package/src/screenplay/abilities/BrowserCapabilities.ts +5 -0
  204. package/src/screenplay/abilities/index.ts +2 -0
  205. package/src/screenplay/index.ts +4 -0
  206. package/src/screenplay/interactions/Clear.ts +102 -0
  207. package/src/screenplay/interactions/Click.ts +86 -0
  208. package/src/screenplay/interactions/DoubleClick.ts +102 -0
  209. package/src/screenplay/interactions/Enter.ts +92 -0
  210. package/src/screenplay/interactions/EnterBuilder.ts +28 -0
  211. package/src/screenplay/interactions/ExecuteScript.ts +345 -0
  212. package/src/screenplay/interactions/Hover.ts +90 -0
  213. package/src/screenplay/interactions/Navigate.ts +209 -0
  214. package/src/screenplay/interactions/PageElementInteraction.ts +59 -0
  215. package/src/screenplay/interactions/Press.ts +194 -0
  216. package/src/screenplay/interactions/PressBuilder.ts +29 -0
  217. package/src/screenplay/interactions/RightClick.ts +100 -0
  218. package/src/screenplay/interactions/Scroll.ts +99 -0
  219. package/src/screenplay/interactions/Select.ts +317 -0
  220. package/src/screenplay/interactions/SelectBuilder.ts +36 -0
  221. package/src/screenplay/interactions/Switch.ts +225 -0
  222. package/src/screenplay/interactions/TakeScreenshot.ts +89 -0
  223. package/src/screenplay/interactions/Wait.ts +264 -0
  224. package/src/screenplay/interactions/WaitBuilder.ts +34 -0
  225. package/src/screenplay/interactions/index.ts +16 -0
  226. package/src/screenplay/models/Cookie.ts +219 -0
  227. package/src/screenplay/models/CookieData.ts +97 -0
  228. package/src/screenplay/models/ModalDialog.ts +19 -0
  229. package/src/screenplay/models/Page.ts +147 -0
  230. package/src/screenplay/models/PageElement.ts +95 -0
  231. package/src/screenplay/models/PageElements.ts +70 -0
  232. package/src/screenplay/models/index.ts +6 -0
  233. package/src/screenplay/questions/Attribute.ts +112 -0
  234. package/src/screenplay/questions/CssClasses.ts +118 -0
  235. package/src/screenplay/questions/ElementQuestion.ts +60 -0
  236. package/src/screenplay/questions/LastScriptExecution.ts +21 -0
  237. package/src/screenplay/questions/Selected.ts +212 -0
  238. package/src/screenplay/questions/Text.ts +153 -0
  239. package/src/screenplay/questions/Value.ts +82 -0
  240. package/src/screenplay/questions/index.ts +6 -0
  241. package/src/stage/crew/index.ts +1 -0
  242. package/src/stage/crew/photographer/Photographer.ts +108 -0
  243. package/src/stage/crew/photographer/index.ts +2 -0
  244. package/src/stage/crew/photographer/strategies/PhotoTakingStrategy.ts +116 -0
  245. package/src/stage/crew/photographer/strategies/TakePhotosBeforeAndAfterInteractions.ts +28 -0
  246. package/src/stage/crew/photographer/strategies/TakePhotosOfFailures.ts +26 -0
  247. package/src/stage/crew/photographer/strategies/TakePhotosOfInteractions.ts +26 -0
  248. package/src/stage/crew/photographer/strategies/index.ts +4 -0
  249. package/src/stage/index.ts +1 -0
  250. package/tsconfig.eslint.json +10 -0
@@ -0,0 +1,264 @@
1
+ import { Answerable, AnswersQuestions, AssertionError, Duration, Expectation, ExpectationMet, ExpectationOutcome, Interaction, UsesAbilities } from '@serenity-js/core';
2
+ import { formatted } from '@serenity-js/core/lib/io';
3
+
4
+ import { BrowseTheWeb } from '../abilities';
5
+ import { WaitBuilder } from './WaitBuilder';
6
+
7
+ /**
8
+ * @desc
9
+ * Instructs the {@link @serenity-js/core/lib/screenplay/actor~Actor} to
10
+ * wait before proceeding either for a set {@link @serenity-js/core/lib/model~Duration}
11
+ * or until a given {@link @serenity-js/core/lib/screenplay/questions~Expectation} is met.
12
+ *
13
+ * Under the hood, `Wait` uses WebdriverIO ["explicit waiting"](https://webdriver.io/docs/autowait/)
14
+ * mechanism.
15
+ *
16
+ * @example <caption>Example widget</caption>
17
+ * <!--
18
+ * After about 1 second, the text will change from 'Loading...' to 'Ready!'
19
+ * -->
20
+ * <h1 id="status">Loading...</h1>
21
+ * <script>
22
+ * (function () {
23
+ * setTimeout(function () {
24
+ * document.getElementById('status').textContent = 'Ready!'
25
+ * }, 1000);
26
+ * })();
27
+ * </script>
28
+ *
29
+ * @example <caption>Lean Page Object describing the widget</caption>
30
+ * import { by, Target } from '@serenity-js/webdriverio';
31
+ *
32
+ * class App {
33
+ * static status = Target.the('status widget')
34
+ * .located(by.id('status'));
35
+ * }
36
+ *
37
+ * @example <caption>Waiting for a set amount of time</caption>
38
+ * import { actorCalled, Duration } from '@serenity-js/core';
39
+ * import { BrowseTheWeb, Wait } from '@serenity-js/webdriverio';
40
+ * import { Ensure, equals } from '@serenity-js/assertions';
41
+ *
42
+ * actorCalled('Wendy')
43
+ * .whoCan(BrowseTheWeb.using(browser))
44
+ * .attemptsTo(
45
+ * Wait.for(Duration.ofSeconds(1.5)),
46
+ * Ensure.that(App.status, equals('Ready!')),
47
+ * );
48
+ *
49
+ * // Please note that while the above implementation works,
50
+ * // this approach is inefficient because at best
51
+ * // the actor might wait too long and at worst the test
52
+ * // might become "flaky" if any external interference
53
+ * // (like network glitches, animations taking a bit too long etc.)
54
+ * // makes the actor wait not long enough.
55
+ *
56
+ * @example <caption>Waiting until a condition is met</caption>
57
+ * import { actorCalled } from '@serenity-js/core';
58
+ * import { BrowseTheWeb, Wait, Text } from '@serenity-js/webdriverio';
59
+ * import { equals } from '@serenity-js/assertions';
60
+ *
61
+ * actorCalled('Wendy')
62
+ * .whoCan(BrowseTheWeb.using(browser))
63
+ * .attemptsTo(
64
+ * Wait.until(Text.of(App.status), equals('Ready!')),
65
+ * // app is ready, proceed with the scenario
66
+ * );
67
+ *
68
+ * // Wait.until makes the Actor keep asking a Question,
69
+ * // in this case Text.of(App.status), until the answer meets
70
+ * // the expectation, or a timeout expires (default: 5s).
71
+ * //
72
+ * // Please note that both Ensure and Wait can be used with
73
+ * // the same expectations, like `equals`.
74
+ *
75
+ * @example <caption>Changing the default timeout</caption>
76
+ * import { actorCalled, Duration } from '@serenity-js/core';
77
+ * import { BrowseTheWeb, Wait, Text } from '@serenity-js/webdriverio';
78
+ * import { equals } from '@serenity-js/assertions';
79
+ *
80
+ * actorCalled('Wendy')
81
+ * .whoCan(BrowseTheWeb.using(browser))
82
+ * .attemptsTo(
83
+ * Wait.upTo(Duration.ofSeconds(3))
84
+ * .until(Text.of(App.status), equals('Ready!')),
85
+ * // app is ready, proceed with the scenario
86
+ * );
87
+ *
88
+ * @see {@link BrowseTheWeb}
89
+ * @see {@link Target}
90
+ * @see {@link Text}
91
+ * @see {@link @serenity-js/assertions~Ensure}
92
+ * @see {@link @serenity-js/assertions/lib/expectations~equals}
93
+ * @see {@link @serenity-js/core/lib/model~Duration}
94
+ *
95
+ * @see {@link @serenity-js/core/lib/screenplay~Interaction}
96
+ */
97
+ export class Wait {
98
+
99
+ /**
100
+ * @desc
101
+ * Default timeout of 5 seconds used with {@link Wait.until}.
102
+ *
103
+ * @type {@serenity-js/core~Duration}
104
+ */
105
+ static readonly Default_Timeout = Duration.ofSeconds(5);
106
+
107
+ /**
108
+ * @desc
109
+ * Instantiates a version of this {@link @serenity-js/core/lib/screenplay~Interaction}
110
+ * configured to wait for a set duration.
111
+ *
112
+ * @param {Answerable<Duration>} duration
113
+ * A set duration the {@link @serenity-js/core/lib/screenplay/actor~Actor} should wait for
114
+ * before proceeding
115
+ *
116
+ * @returns {@serenity-js/core/lib/screenplay~Interaction}
117
+ */
118
+ static for(duration: Answerable<Duration>): Interaction {
119
+ return new WaitFor(duration);
120
+ }
121
+
122
+ /**
123
+ * @desc
124
+ * Instantiates a version of this {@link @serenity-js/core/lib/screenplay~Interaction}
125
+ * configured to wait until the answer to the question (`actual`) meets the `expectation`,
126
+ * or a custom timeout expires.
127
+ *
128
+ * @param {Duration} duration
129
+ * Custom timeout to override {@link Wait.Default_Timeout}
130
+ *
131
+ * @returns {WaitBuilder}
132
+ */
133
+ static upTo(duration: Duration): WaitBuilder {
134
+ return {
135
+ until: <Actual>(actual: Answerable<Actual>, expectation: Expectation<any, Actual>): Interaction =>
136
+ new WaitUntil(actual, expectation, duration),
137
+ };
138
+ }
139
+
140
+ /**
141
+ * @desc
142
+ * Instantiates a version of this {@link @serenity-js/core/lib/screenplay~Interaction}
143
+ * configured to wait until the answer to the question (`actual`) meets the `expectation`,
144
+ * or a {@link Wait.Default_Timeout} expires.
145
+ *
146
+ * @param {Answerable<Actual>} actual
147
+ * A {@link @serenity-js/core/lib/screenplay~Question}
148
+ * that the {@link @serenity-js/core/lib/screenplay/actor~Actor}
149
+ * will keep asking until the answer meets
150
+ * the {@link @serenity-js/core/lib/screenplay/questions~Expectation} provided
151
+ *
152
+ * @param {@serenity-js/core/lib/screenplay/questions~<any,Actual>} expectation
153
+ * An {@link @serenity-js/core/lib/screenplay/questions~Expectation} to be met before proceeding
154
+ *
155
+ * @returns {@serenity-js/core/lib/screenplay~Interaction}
156
+ */
157
+ static until<Actual>(actual: Answerable<Actual>, expectation: Expectation<any, Actual>): Interaction {
158
+ return new WaitUntil(actual, expectation, Wait.Default_Timeout);
159
+ }
160
+ }
161
+
162
+ /**
163
+ * @package
164
+ */
165
+ class WaitFor extends Interaction {
166
+ constructor(private readonly duration: Answerable<Duration>) {
167
+ super();
168
+ }
169
+
170
+ /**
171
+ * @desc
172
+ * Makes the provided {@link @serenity-js/core/lib/screenplay/actor~Actor}
173
+ * perform this {@link @serenity-js/core/lib/screenplay~Interaction}.
174
+ *
175
+ * @param {UsesAbilities & AnswersQuestions} actor
176
+ * @returns {Promise<void>}
177
+ *
178
+ * @see {@link @serenity-js/core/lib/screenplay/actor~Actor}
179
+ * @see {@link @serenity-js/core/lib/screenplay/actor~UsesAbilities}
180
+ * @see {@link @serenity-js/core/lib/screenplay/actor~AnswersQuestions}
181
+ */
182
+ async performAs(actor: UsesAbilities & AnswersQuestions): Promise<void> {
183
+ return actor.answer(this.duration)
184
+ .then(duration => BrowseTheWeb.as(actor).waitFor(duration));
185
+ }
186
+
187
+ /**
188
+ * @desc
189
+ * Generates a description to be used when reporting this {@link @serenity-js/core/lib/screenplay~Activity}.
190
+ *
191
+ * @returns {string}
192
+ */
193
+ toString(): string {
194
+ return formatted`#actor waits for ${ this.duration }`;
195
+ }
196
+ }
197
+
198
+ /**
199
+ * @package
200
+ */
201
+ class WaitUntil<Actual> extends Interaction {
202
+ constructor(
203
+ private readonly actual: Answerable<Actual>,
204
+ private readonly expectation: Expectation<any, Actual>,
205
+ private readonly timeout: Duration,
206
+ ) {
207
+ super();
208
+ }
209
+
210
+ /**
211
+ * @desc
212
+ * Makes the provided {@link @serenity-js/core/lib/screenplay/actor~Actor}
213
+ * perform this {@link @serenity-js/core/lib/screenplay~Interaction}.
214
+ *
215
+ * @param {UsesAbilities & AnswersQuestions} actor
216
+ * @returns {Promise<void>}
217
+ *
218
+ * @see {@link @serenity-js/core/lib/screenplay/actor~Actor}
219
+ * @see {@link @serenity-js/core/lib/screenplay/actor~UsesAbilities}
220
+ * @see {@link @serenity-js/core/lib/screenplay/actor~AnswersQuestions}
221
+ */
222
+ performAs(actor: UsesAbilities & AnswersQuestions): PromiseLike<void> {
223
+ const
224
+ actual = this.actual,
225
+ expectation = this.expectation.answeredBy(actor);
226
+
227
+ let expectationOutcome: ExpectationOutcome<any, Actual>;
228
+
229
+ return BrowseTheWeb.as(actor)
230
+ .waitUntil(function () {
231
+ return actor.answer(actual)
232
+ .then(act => expectation(act))
233
+ .then(outcome => {
234
+ expectationOutcome = outcome;
235
+
236
+ return outcome instanceof ExpectationMet;
237
+ });
238
+ },
239
+ this.timeout
240
+ )
241
+ .catch(error => {
242
+ if (expectationOutcome) {
243
+ throw new AssertionError(
244
+ `Waited ${ this.timeout.toString() } for ${ formatted `${ this.actual }` } to ${ this.expectation.toString() }`,
245
+ expectationOutcome.expected,
246
+ expectationOutcome.actual,
247
+ error,
248
+ );
249
+ }
250
+
251
+ throw error;
252
+ });
253
+ }
254
+
255
+ /**
256
+ * @desc
257
+ * Generates a description to be used when reporting this {@link @serenity-js/core/lib/screenplay~Activity}.
258
+ *
259
+ * @returns {string}
260
+ */
261
+ toString(): string {
262
+ return formatted`#actor waits up to ${ this.timeout } until ${ this.actual } does ${ this.expectation }`;
263
+ }
264
+ }
@@ -0,0 +1,34 @@
1
+ import { Answerable, Expectation, Interaction } from '@serenity-js/core';
2
+
3
+ /**
4
+ * @desc
5
+ * Fluent interface to make the instantiation of
6
+ * the {@link @serenity-js/core/lib/screenplay~Interaction}
7
+ * to {@link Wait} more readable.
8
+ *
9
+ * @see {@link Wait}
10
+ *
11
+ * @interface
12
+ */
13
+ export interface WaitBuilder {
14
+
15
+ /**
16
+ * @desc
17
+ * Instantiates an {@link @serenity-js/core/lib/screenplay~Interaction}
18
+ * to {@link Wait}.
19
+ *
20
+ * @param {Answerable<Actual>} actual
21
+ * A {@link @serenity-js/core/lib/screenplay~Question}
22
+ * that the {@link @serenity-js/core/lib/screenplay/actor~Actor}
23
+ * will keep asking until the answer meets
24
+ * the {@link @serenity-js/core/lib/screenplay/questions~Expectation} provided
25
+ *
26
+ * @param {@serenity-js/core/lib/screenplay/questions~Expectation<any,Actual>} expectation
27
+ * An {@link @serenity-js/assertions~Expectation} to be met before proceeding
28
+ *
29
+ * @returns {Interaction}
30
+ *
31
+ * @see {@link Target}
32
+ */
33
+ until: <Actual>(actual: Answerable<Actual>, expectation: Expectation<any, Actual>) => Interaction;
34
+ }
@@ -0,0 +1,16 @@
1
+ export * from './Clear';
2
+ export * from './Click';
3
+ export * from './DoubleClick';
4
+ export * from './Enter';
5
+ export * from './ExecuteScript';
6
+ export * from './Hover';
7
+ export * from './Navigate';
8
+ export * from './Press';
9
+ export * from './PressBuilder';
10
+ export * from './RightClick';
11
+ export * from './Scroll';
12
+ export * from './Select';
13
+ export * from './SelectBuilder';
14
+ export * from './Switch';
15
+ export * from './TakeScreenshot';
16
+ export * from './Wait';
@@ -0,0 +1,219 @@
1
+ import { Adapter, Answerable, Interaction, Question, Timestamp } from '@serenity-js/core';
2
+ import { formatted } from '@serenity-js/core/lib/io';
3
+ import { ensure, isBoolean, isDefined, isInstanceOf, isOneOf, isPlainObject, isString, Predicate } from 'tiny-types';
4
+
5
+ import { CookieMissingError } from '../../errors';
6
+ import { BrowseTheWeb } from '../abilities';
7
+ import { CookieData } from './CookieData';
8
+
9
+ export abstract class Cookie {
10
+
11
+ /**
12
+ * @desc
13
+ * Creates a {@link @serenity-js/core/lib/screenplay~Question} about a Cookie
14
+ *
15
+ * @param {Answerable<string>} name
16
+ * @returns {Question<Promise<Cookie>> & Adapter<Cookie>}
17
+ */
18
+ static called(name: Answerable<string>): Question<Promise<Cookie>> & Adapter<Cookie> {
19
+ return Question.about(`"${ name }" cookie`, async actor => {
20
+ const cookieName = await actor.answer(name);
21
+ return BrowseTheWeb.as(actor).cookie(cookieName);
22
+ });
23
+ }
24
+
25
+ /**
26
+ * @desc
27
+ * Sets a cookie for the current page.
28
+ * Make sure that the actor performing this interaction is on the page that should receive the cookie.
29
+ * An actor can't set a cookie for an arbitrary page without being on that page.
30
+ *
31
+ * @param {Answerable<CookieData>} cookieData
32
+ * @returns {@serenity-js/core/lib/screenplay~Interaction}
33
+ */
34
+ static set(cookieData: Answerable<CookieData>): Interaction {
35
+
36
+ return Interaction.where(formatted `#actor sets cookie: ${ cookieData }`, async actor => {
37
+ const cookie = ensure('cookieData', await actor.answer(cookieData), isDefined(), isPlainObject());
38
+
39
+ const sanitisedCookieData: CookieData = {
40
+ name: ensure(`Cookie.set(cookieData.name)`, cookie.name, isDefined(), isString()),
41
+ value: ensure(`Cookie.set(cookieData.value)`, cookie.value, isDefined(), isString()),
42
+ path: ensureIfPresent(cookie, 'path', isString()),
43
+ domain: ensureIfPresent(cookie, 'domain', isString()),
44
+ secure: ensureIfPresent(cookie, 'secure', isBoolean()),
45
+ httpOnly: ensureIfPresent(cookie, 'httpOnly', isBoolean()),
46
+ expiry: ensureIfPresent(cookie, 'expiry', isInstanceOf(Timestamp)),
47
+ sameSite: ensureIfPresent(cookie, 'sameSite', isOneOf<'Lax' | 'Strict' | 'None'>('Lax', 'Strict', 'None')),
48
+ }
49
+
50
+ return BrowseTheWeb.as(actor).setCookie(sanitisedCookieData);
51
+ });
52
+ }
53
+
54
+ /**
55
+ * @desc
56
+ * Creates an {@link @serenity-js/core/lib/screenplay~Interaction} to delete all cookies currently set in the browser.
57
+ *
58
+ * @returns {@serenity-js/core/lib/screenplay~Interaction}
59
+ */
60
+ static deleteAll(): Interaction {
61
+ return Interaction.where(`#actor deletes all cookies`, actor => {
62
+ return BrowseTheWeb.as(actor).deleteAllCookies();
63
+ });
64
+ }
65
+
66
+ private cookie: CookieData;
67
+
68
+ protected constructor(protected readonly cookieName: string) {
69
+ ensure('browser', cookieName, isDefined());
70
+ }
71
+
72
+ name(): string {
73
+ return this.cookieName;
74
+ }
75
+
76
+ /**
77
+ * @desc
78
+ * Checks if a given cookie is set.
79
+ *
80
+ * @returns {Promise<boolean>}
81
+ */
82
+ async isPresent(): Promise<boolean> {
83
+ try {
84
+ const cookie = await this.lazyLoadCookie();
85
+ return cookie && cookie.name === this.cookieName;
86
+ }
87
+ catch(error) {
88
+ if (error instanceof CookieMissingError) {
89
+ return false;
90
+ }
91
+
92
+ throw error;
93
+ }
94
+ }
95
+
96
+ /**
97
+ * @desc
98
+ * Returns the value of a given cookie.
99
+ *
100
+ * @returns {Promise<string>}
101
+ */
102
+ async value(): Promise<string> {
103
+ const cookie = await this.lazyLoadCookie();
104
+ return cookie.value;
105
+ }
106
+
107
+ /**
108
+ * @desc
109
+ * Returns the path of a given cookie, if any was set.
110
+ *
111
+ * @returns {Promise<string>}
112
+ */
113
+ async path(): Promise<string> {
114
+ const cookie = await this.lazyLoadCookie();
115
+ return cookie.path;
116
+ }
117
+
118
+ /**
119
+ * @desc
120
+ * Returns the domain of a given cookie, if any was set.
121
+ *
122
+ * @returns {Promise<string>}
123
+ */
124
+ async domain(): Promise<string> {
125
+ const cookie = await this.lazyLoadCookie();
126
+ return cookie.domain;
127
+ }
128
+
129
+ /**
130
+ * @desc
131
+ * Checks if a given cookie is HTTP-only.
132
+ *
133
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies
134
+ *
135
+ * @returns {Promise<string>}
136
+ */
137
+ async isHttpOnly(): Promise<boolean> {
138
+ const cookie = await this.lazyLoadCookie();
139
+ return cookie.httpOnly;
140
+ }
141
+
142
+ /**
143
+ * @desc
144
+ * Checks if a given cookie is secure.
145
+ *
146
+ * @see https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies#restrict_access_to_cookies
147
+ *
148
+ * @returns {Promise<string>}
149
+ */
150
+ async isSecure(): Promise<boolean> {
151
+ const cookie = await this.lazyLoadCookie();
152
+ return cookie.secure;
153
+ }
154
+
155
+ /**
156
+ * @desc
157
+ * Returns the expiry date of a given cookie
158
+ *
159
+ * @returns {Promise<Timestamp>}
160
+ */
161
+ async expiry(): Promise<Timestamp> {
162
+ const cookie = await this.lazyLoadCookie();
163
+ return cookie.expiry;
164
+ }
165
+
166
+ /**
167
+ * @desc
168
+ * Deletes a given cookie.
169
+ *
170
+ * @abstract
171
+ *
172
+ * @returns {Promise<void>}
173
+ */
174
+ abstract delete(): Promise<void>;
175
+
176
+ /**
177
+ * @desc
178
+ * Reads a given cookie from the browser.
179
+ *
180
+ * This method is to be implemented by integration tool-specific adapters.
181
+ * **Please note**: you don't need to implement any response caching here
182
+ * since it is covered by {@link Cookie#lazyLoadCookie} method.
183
+ *
184
+ * @protected
185
+ * @abstract
186
+ *
187
+ * @returns {Promise<void>}
188
+ */
189
+ protected abstract read(): Promise<CookieData>;
190
+
191
+ /**
192
+ * @desc
193
+ * Invokes {@link Cookie#read} and caches the result in memory.
194
+ *
195
+ * @private
196
+ * @returns {Promise<CookieData>}
197
+ */
198
+ private async lazyLoadCookie(): Promise<CookieData> {
199
+ if (! this.cookie) {
200
+ this.cookie = await this.read();
201
+ }
202
+
203
+ return this.cookie;
204
+ }
205
+ }
206
+
207
+ /**
208
+ * @ignore
209
+ * @private
210
+ *
211
+ * @param data
212
+ * @param property
213
+ * @param predicates
214
+ */
215
+ function ensureIfPresent<K extends keyof CookieData>(data: CookieData, property: K, ...predicates: Array<Predicate<CookieData[K]>>): CookieData[K] | undefined {
216
+ return data[property] !== undefined
217
+ ? ensure(`Cookie.set(cookieData.${property})`, data[property], ...predicates)
218
+ : undefined;
219
+ }
@@ -0,0 +1,97 @@
1
+ import { Timestamp } from '@serenity-js/core';
2
+
3
+ /**
4
+ * @desc
5
+ * A data structure describing a {@link Cookie} to be set or that's been read.
6
+ *
7
+ * @public
8
+ */
9
+ export interface CookieData {
10
+ /**
11
+ * @desc
12
+ * The name of the cookie.
13
+ *
14
+ * @type {string}
15
+ * @public
16
+ */
17
+ name: string;
18
+
19
+ /**
20
+ * @desc
21
+ * The value of the cookie.
22
+ *
23
+ * @type {string}
24
+ * @public
25
+ */
26
+ value: string;
27
+
28
+ /**
29
+ * @desc
30
+ * The domain the cookie is visible to.
31
+ * Defaults to the current browsing context's document's URL when setting a cookie.
32
+ *
33
+ * Optional.
34
+ *
35
+ * @type {string}
36
+ * @public
37
+ */
38
+ domain?: string;
39
+
40
+ /**
41
+ * @desc
42
+ * The cookie path. Defaults to "/" when adding a cookie.
43
+ *
44
+ * Optional.
45
+ *
46
+ * @type {string}
47
+ * @public
48
+ */
49
+ path?: string;
50
+
51
+ /**
52
+ * @desc
53
+ * The {@link @serenity-js/core/lib/model~Timestamp} of when the cookie expires.
54
+ *
55
+ * Optional.
56
+ *
57
+ * @type {@serenity-js/core/lib/model~Timestamp}
58
+ * @public
59
+ */
60
+ expiry?: Timestamp;
61
+
62
+ /**
63
+ * @desc
64
+ * Whether the cookie is an HTTP-only cookie. Defaults to false when adding a new cookie.
65
+ *
66
+ * Optional.
67
+ *
68
+ * @type {boolean}
69
+ * @public
70
+ */
71
+ httpOnly?: boolean;
72
+
73
+ /**
74
+ * @desc
75
+ * Whether the cookie is a secure cookie. Defaults to false when adding a new cookie.
76
+ *
77
+ * Optional.
78
+ *
79
+ * @type {boolean}
80
+ * @public
81
+ */
82
+ secure?: boolean;
83
+
84
+ /**
85
+ * @desc
86
+ * Whether the cookie applies to a SameSite policy.
87
+ * Defaults to "None" if omitted when adding a cookie.
88
+ *
89
+ * Can be set to either "Lax" or "Strict".
90
+ *
91
+ * Optional.
92
+ *
93
+ * @type {string}
94
+ * @public
95
+ */
96
+ sameSite?: 'Lax' | 'Strict' | 'None';
97
+ }
@@ -0,0 +1,19 @@
1
+ import { Adapter, Question } from '@serenity-js/core';
2
+
3
+ import { BrowseTheWeb } from '../abilities';
4
+
5
+ export abstract class ModalDialog {
6
+ static window(): Question<Promise<ModalDialog>> & Adapter<ModalDialog> {
7
+ return Question.about<Promise<ModalDialog>>('modal dialog', actor => {
8
+ return BrowseTheWeb.as(actor).modalDialog();
9
+ });
10
+ }
11
+
12
+ abstract accept(): Promise<void>;
13
+ abstract dismiss(): Promise<void>;
14
+
15
+ abstract text(): Promise<string>;
16
+ abstract enterValue(value: string | number | Array<string | number>): Promise<void>;
17
+
18
+ abstract isPresent(): Promise<boolean>;
19
+ }