@interactors/core 0.3.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 (193) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/README.md +9 -0
  3. package/dist/cjs/constructor.js +260 -0
  4. package/dist/cjs/constructor.js.map +1 -0
  5. package/dist/cjs/converge.js +27 -0
  6. package/dist/cjs/converge.js.map +1 -0
  7. package/dist/cjs/create-interactor.js +24 -0
  8. package/dist/cjs/create-interactor.js.map +1 -0
  9. package/dist/cjs/dispatch.js +34 -0
  10. package/dist/cjs/dispatch.js.map +1 -0
  11. package/dist/cjs/element/click.js +12 -0
  12. package/dist/cjs/element/click.js.map +1 -0
  13. package/dist/cjs/element/fill-in.js +71 -0
  14. package/dist/cjs/element/fill-in.js.map +1 -0
  15. package/dist/cjs/errors.js +32 -0
  16. package/dist/cjs/errors.js.map +1 -0
  17. package/dist/cjs/escape-html.js +13 -0
  18. package/dist/cjs/escape-html.js.map +1 -0
  19. package/dist/cjs/filter-set.js +50 -0
  20. package/dist/cjs/filter-set.js.map +1 -0
  21. package/dist/cjs/focused.js +47 -0
  22. package/dist/cjs/focused.js.map +1 -0
  23. package/dist/cjs/format-table.js +30 -0
  24. package/dist/cjs/format-table.js.map +1 -0
  25. package/dist/cjs/index.js +47 -0
  26. package/dist/cjs/index.js.map +1 -0
  27. package/dist/cjs/inspector.js +22 -0
  28. package/dist/cjs/inspector.js.map +1 -0
  29. package/dist/cjs/interaction.js +53 -0
  30. package/dist/cjs/interaction.js.map +1 -0
  31. package/dist/cjs/locator.js +19 -0
  32. package/dist/cjs/locator.js.map +1 -0
  33. package/dist/cjs/match.js +135 -0
  34. package/dist/cjs/match.js.map +1 -0
  35. package/dist/cjs/matcher.js +30 -0
  36. package/dist/cjs/matcher.js.map +1 -0
  37. package/dist/cjs/matchers/and.js +16 -0
  38. package/dist/cjs/matchers/and.js.map +1 -0
  39. package/dist/cjs/matchers/every.js +16 -0
  40. package/dist/cjs/matchers/every.js.map +1 -0
  41. package/dist/cjs/matchers/including.js +15 -0
  42. package/dist/cjs/matchers/including.js.map +1 -0
  43. package/dist/cjs/matchers/matching.js +15 -0
  44. package/dist/cjs/matchers/matching.js.map +1 -0
  45. package/dist/cjs/matchers/not.js +16 -0
  46. package/dist/cjs/matchers/not.js.map +1 -0
  47. package/dist/cjs/matchers/or.js +16 -0
  48. package/dist/cjs/matchers/or.js.map +1 -0
  49. package/dist/cjs/matchers/some.js +16 -0
  50. package/dist/cjs/matchers/some.js.map +1 -0
  51. package/dist/cjs/merge-objects.js +3 -0
  52. package/dist/cjs/merge-objects.js.map +1 -0
  53. package/dist/cjs/perform.js +13 -0
  54. package/dist/cjs/perform.js.map +1 -0
  55. package/dist/cjs/specification.js +4 -0
  56. package/dist/cjs/specification.js.map +1 -0
  57. package/dist/constructor.d.ts +11 -0
  58. package/dist/constructor.d.ts.map +1 -0
  59. package/dist/converge.d.ts +2 -0
  60. package/dist/converge.d.ts.map +1 -0
  61. package/dist/create-interactor.d.ts +18 -0
  62. package/dist/create-interactor.d.ts.map +1 -0
  63. package/dist/dispatch.d.ts +6 -0
  64. package/dist/dispatch.d.ts.map +1 -0
  65. package/dist/element/click.d.ts +2 -0
  66. package/dist/element/click.d.ts.map +1 -0
  67. package/dist/element/fill-in.d.ts +12 -0
  68. package/dist/element/fill-in.d.ts.map +1 -0
  69. package/dist/errors.d.ts +13 -0
  70. package/dist/errors.d.ts.map +1 -0
  71. package/dist/escape-html.d.ts +2 -0
  72. package/dist/escape-html.d.ts.map +1 -0
  73. package/dist/esm/constructor.js +248 -0
  74. package/dist/esm/constructor.js.map +1 -0
  75. package/dist/esm/converge.js +23 -0
  76. package/dist/esm/converge.js.map +1 -0
  77. package/dist/esm/create-interactor.js +20 -0
  78. package/dist/esm/create-interactor.js.map +1 -0
  79. package/dist/esm/dispatch.js +26 -0
  80. package/dist/esm/dispatch.js.map +1 -0
  81. package/dist/esm/element/click.js +5 -0
  82. package/dist/esm/element/click.js.map +1 -0
  83. package/dist/esm/element/fill-in.js +67 -0
  84. package/dist/esm/element/fill-in.js.map +1 -0
  85. package/dist/esm/errors.js +25 -0
  86. package/dist/esm/errors.js.map +1 -0
  87. package/dist/esm/escape-html.js +9 -0
  88. package/dist/esm/escape-html.js.map +1 -0
  89. package/dist/esm/filter-set.js +46 -0
  90. package/dist/esm/filter-set.js.map +1 -0
  91. package/dist/esm/focused.js +41 -0
  92. package/dist/esm/focused.js.map +1 -0
  93. package/dist/esm/format-table.js +26 -0
  94. package/dist/esm/format-table.js.map +1 -0
  95. package/dist/esm/index.js +17 -0
  96. package/dist/esm/index.js.map +1 -0
  97. package/dist/esm/inspector.js +18 -0
  98. package/dist/esm/inspector.js.map +1 -0
  99. package/dist/esm/interaction.js +45 -0
  100. package/dist/esm/interaction.js.map +1 -0
  101. package/dist/esm/locator.js +15 -0
  102. package/dist/esm/locator.js.map +1 -0
  103. package/dist/esm/match.js +127 -0
  104. package/dist/esm/match.js.map +1 -0
  105. package/dist/esm/matcher.js +21 -0
  106. package/dist/esm/matcher.js.map +1 -0
  107. package/dist/esm/matchers/and.js +12 -0
  108. package/dist/esm/matchers/and.js.map +1 -0
  109. package/dist/esm/matchers/every.js +12 -0
  110. package/dist/esm/matchers/every.js.map +1 -0
  111. package/dist/esm/matchers/including.js +11 -0
  112. package/dist/esm/matchers/including.js.map +1 -0
  113. package/dist/esm/matchers/matching.js +11 -0
  114. package/dist/esm/matchers/matching.js.map +1 -0
  115. package/dist/esm/matchers/not.js +12 -0
  116. package/dist/esm/matchers/not.js.map +1 -0
  117. package/dist/esm/matchers/or.js +12 -0
  118. package/dist/esm/matchers/or.js.map +1 -0
  119. package/dist/esm/matchers/some.js +12 -0
  120. package/dist/esm/matchers/some.js.map +1 -0
  121. package/dist/esm/merge-objects.js +2 -0
  122. package/dist/esm/merge-objects.js.map +1 -0
  123. package/dist/esm/perform.js +9 -0
  124. package/dist/esm/perform.js.map +1 -0
  125. package/dist/esm/specification.js +3 -0
  126. package/dist/esm/specification.js.map +1 -0
  127. package/dist/filter-set.d.ts +10 -0
  128. package/dist/filter-set.d.ts.map +1 -0
  129. package/dist/focused.d.ts +30 -0
  130. package/dist/focused.d.ts.map +1 -0
  131. package/dist/format-table.d.ts +6 -0
  132. package/dist/format-table.d.ts.map +1 -0
  133. package/dist/index.d.ts +19 -0
  134. package/dist/index.d.ts.map +1 -0
  135. package/dist/inspector.d.ts +17 -0
  136. package/dist/inspector.d.ts.map +1 -0
  137. package/dist/interaction.d.ts +42 -0
  138. package/dist/interaction.d.ts.map +1 -0
  139. package/dist/locator.d.ts +10 -0
  140. package/dist/locator.d.ts.map +1 -0
  141. package/dist/match.d.ts +54 -0
  142. package/dist/match.d.ts.map +1 -0
  143. package/dist/matcher.d.ts +9 -0
  144. package/dist/matcher.d.ts.map +1 -0
  145. package/dist/matchers/and.d.ts +3 -0
  146. package/dist/matchers/and.d.ts.map +1 -0
  147. package/dist/matchers/every.d.ts +3 -0
  148. package/dist/matchers/every.d.ts.map +1 -0
  149. package/dist/matchers/including.d.ts +3 -0
  150. package/dist/matchers/including.d.ts.map +1 -0
  151. package/dist/matchers/matching.d.ts +3 -0
  152. package/dist/matchers/matching.d.ts.map +1 -0
  153. package/dist/matchers/not.d.ts +3 -0
  154. package/dist/matchers/not.d.ts.map +1 -0
  155. package/dist/matchers/or.d.ts +3 -0
  156. package/dist/matchers/or.d.ts.map +1 -0
  157. package/dist/matchers/some.d.ts +3 -0
  158. package/dist/matchers/some.d.ts.map +1 -0
  159. package/dist/merge-objects.d.ts +2 -0
  160. package/dist/merge-objects.d.ts.map +1 -0
  161. package/dist/perform.d.ts +3 -0
  162. package/dist/perform.d.ts.map +1 -0
  163. package/dist/specification.d.ts +217 -0
  164. package/dist/specification.d.ts.map +1 -0
  165. package/dist/tsconfig.build.tsbuildinfo +1 -0
  166. package/package.json +65 -0
  167. package/src/constructor.ts +298 -0
  168. package/src/converge.ts +22 -0
  169. package/src/create-interactor.ts +21 -0
  170. package/src/dispatch.ts +24 -0
  171. package/src/element/click.ts +5 -0
  172. package/src/element/fill-in.ts +76 -0
  173. package/src/errors.ts +15 -0
  174. package/src/escape-html.ts +8 -0
  175. package/src/filter-set.ts +45 -0
  176. package/src/focused.ts +43 -0
  177. package/src/format-table.ts +34 -0
  178. package/src/index.ts +20 -0
  179. package/src/inspector.ts +42 -0
  180. package/src/interaction.ts +82 -0
  181. package/src/locator.ts +15 -0
  182. package/src/match.ts +162 -0
  183. package/src/matcher.ts +28 -0
  184. package/src/matchers/and.ts +12 -0
  185. package/src/matchers/every.ts +12 -0
  186. package/src/matchers/including.ts +12 -0
  187. package/src/matchers/matching.ts +12 -0
  188. package/src/matchers/not.ts +12 -0
  189. package/src/matchers/or.ts +12 -0
  190. package/src/matchers/some.ts +12 -0
  191. package/src/merge-objects.ts +2 -0
  192. package/src/perform.ts +11 -0
  193. package/src/specification.ts +253 -0
@@ -0,0 +1,253 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+
3
+ import { FilterSet } from './filter-set';
4
+ import { Locator } from './locator';
5
+ import { Interaction, ReadonlyInteraction } from './interaction';
6
+ import { MergeObjects } from './merge-objects';
7
+ import { MaybeMatcher } from './matcher';
8
+
9
+ export type EmptyObject = Record<never, never>;
10
+
11
+ export interface ExistsAssertionsImplementation {
12
+
13
+ /**
14
+ * An assertion which checks that an element matching the interactor exists.
15
+ * Throws an error if the element does not exist.
16
+ *
17
+ * ## Example
18
+ *
19
+ * ``` typescript
20
+ * await Link('Next').exists();
21
+ * ```
22
+ */
23
+ exists(): ReadonlyInteraction<void> & FilterObject<boolean, Element>;
24
+
25
+ /**
26
+ * An assertion which checks that an element matching the interactor does not
27
+ * exist. Throws an error if the element exists.
28
+ *
29
+ * ## Example
30
+ *
31
+ * ``` typescript
32
+ * await Link('Next').absent();
33
+ * ```
34
+ */
35
+ absent(): ReadonlyInteraction<void> & FilterObject<boolean, Element>;
36
+ }
37
+
38
+ export interface BaseInteractor<E extends Element, F extends FilterParams<any, any>> {
39
+ /**
40
+ * @hidden
41
+ */
42
+ options: InteractorOptions<E, any, any>;
43
+
44
+ /**
45
+ * @returns a human readable description of this interactor
46
+ */
47
+ description: string;
48
+
49
+ /**
50
+ * Perform a one-off action on the given interactor. Takes a function which
51
+ * receives an element. This function converges, which means that it is rerun
52
+ * in a loop until it does not throw an error or times out.
53
+ *
54
+ * We recommend using this function for debugging only. You should normally
55
+ * define an action in an {@link InteractorSpecification}.
56
+ *
57
+ * ## Example
58
+ *
59
+ * ``` typescript
60
+ * await Link('Next').perform((e) => e.click());
61
+ * ```
62
+ */
63
+ perform(fn: (element: E) => void): Interaction<void>;
64
+
65
+ /**
66
+ * Perform a one-off assertion on the given interactor. Takes a function which
67
+ * receives an element. This function converges, which means that it is rerun
68
+ * in a loop until it does not throw an error or times out.
69
+ *
70
+ * We recommend using this function for debugging only. You should normally
71
+ * define a filter in an {@link InteractorSpecification}.
72
+ *
73
+ * ## Example
74
+ *
75
+ * ``` typescript
76
+ * await Link('Next').assert((e) => assert(e.tagName === 'A'));
77
+ * ```
78
+ */
79
+ assert(fn: (element: E) => void): ReadonlyInteraction<void>;
80
+
81
+ /**
82
+ * Checks that there is one element matching the interactor, and that this
83
+ * element matches the given filters. The available filters are defined by
84
+ * the {@link InteractorSpecification}.
85
+ *
86
+ * ## Example
87
+ *
88
+ * ``` typescript
89
+ * await Link('Home').has({ href: '/' })
90
+ * ```
91
+ */
92
+ has(filters: F): ReadonlyInteraction<void>;
93
+
94
+ /**
95
+ * Identical to {@link has}, but reads better with some filters.
96
+ *
97
+ * ## Example
98
+ *
99
+ * ``` typescript
100
+ * await CheckBox('Accept conditions').is({ checked: true })
101
+ * ```
102
+ */
103
+ is(filters: F): ReadonlyInteraction<void>;
104
+
105
+ /**
106
+ * @hidden
107
+ */
108
+ apply: FilterFn<string, Element>;
109
+ }
110
+
111
+ /**
112
+ * Instances of an interactor returned by an {@link InteractorConstructor}, use
113
+ * this class as its base. They are also extended with any additional actions
114
+ * defined in their {@link InteractorSpecification}.
115
+ */
116
+ export interface Interactor<E extends Element, F extends FilterParams<any, any>> extends BaseInteractor<E, F>, ExistsAssertionsImplementation {
117
+ /**
118
+ * Returns a copy of the given interactor which is scoped to this interactor.
119
+ * When there are multiple matches for an interactor, this makes it possible
120
+ * to make them more specific by limiting the interactor to a section of the
121
+ * page.
122
+ *
123
+ * ## Example
124
+ *
125
+ * ``` typescript
126
+ * await Fieldset('Owner').find(TextField('Name')).fillIn('Jonas');
127
+ * await Fieldset('Brand').find(TextField('Name')).fillIn('Volkswagen');
128
+ * ```
129
+ * @param interactor the interactor which should be scoped
130
+ * @returns a scoped copy of the initial interactor
131
+ * @typeParam T the type of the interactor that we are going to scope
132
+ */
133
+ find<T extends Interactor<any, any>>(interactor: T): T;
134
+ }
135
+
136
+ export type ActionFn<E extends Element> = (interactor: Interactor<E, EmptyObject>, ...args: any[]) => Promise<unknown>;
137
+
138
+ export type FilterFn<T, E extends Element> = (element: E) => T;
139
+
140
+ export type FilterObject<T, E extends Element> = {
141
+ apply: FilterFn<T, E>;
142
+ default?: T;
143
+ }
144
+
145
+ export type FilterDefinition<T, E extends Element> = FilterFn<T, E> | FilterObject<T, E>;
146
+
147
+ export type Filters<E extends Element> = Record<string, FilterDefinition<unknown, E>>;
148
+
149
+ export type Actions<E extends Element> = Record<string, ActionFn<E>>;
150
+
151
+ export type SelectorFn<E extends Element> = (parentElement: Element) => E[];
152
+
153
+ export type InteractorSpecification<E extends Element, F extends Filters<E>, A extends Actions<E>> = {
154
+ /**
155
+ * The CSS selector that this interactor uses to find matching elements
156
+ */
157
+ selector?: string | SelectorFn<E>;
158
+ actions?: A;
159
+ filters?: F;
160
+ /**
161
+ * A function which returns a string value for a matched element, which can
162
+ * be used to locate a specific instance of this interactor. The `value`
163
+ * parameter of an {@link InteractorConstructor} must match the value
164
+ * returned from the locator function.
165
+ */
166
+ locator?: FilterDefinition<string, E>;
167
+ }
168
+
169
+ export type ActionMethods<E extends Element, A extends Actions<E>> = {
170
+ [P in keyof A]: A[P] extends ((interactor: Interactor<E, EmptyObject>, ...args: infer TArgs) => Promise<infer TReturn>)
171
+ ? ((...args: TArgs) => Interaction<TReturn>)
172
+ : never;
173
+ }
174
+
175
+ export type FilterMethods<E extends Element, F extends Filters<E>> = {
176
+ [P in keyof F]:
177
+ F[P] extends FilterFn<infer TReturn, any> ? (() => Interaction<TReturn> & FilterObject<TReturn, Element>) :
178
+ F[P] extends FilterObject<infer TReturn, any> ? (() => Interaction<TReturn> & FilterObject<TReturn, Element>) :
179
+ never;
180
+ }
181
+
182
+ export type FilterReturn<F> = {
183
+ [P in keyof F]?: F[P] extends MaybeMatcher<infer T> ? T : never;
184
+ }
185
+
186
+ export type FilterParams<E extends Element, F extends Filters<E>> = keyof F extends never ? never : {
187
+ [P in keyof F]?:
188
+ F[P] extends FilterFn<infer TArg, E> ? MaybeMatcher<TArg> :
189
+ F[P] extends FilterObject<infer TArg, E> ? MaybeMatcher<TArg> :
190
+ never;
191
+ }
192
+
193
+ /**
194
+ * An interactor constructor is a function which can be used to initialize an
195
+ * {@link Interactor}. When calling {@link createInteractor}, you will get
196
+ * back an interactor constructor.
197
+ *
198
+ * The constructor can be called with a locator value, and an object of
199
+ * filters. Both are optional, and can be omitted.
200
+ *
201
+ * @typeParam E The type of DOM Element that this interactor operates on.
202
+ * @typeParam F the filters of this interactor, this is usually inferred from the specification
203
+ * @typeParam A the actions of this interactor, this is usually inferred from the specification
204
+ */
205
+ export interface InteractorConstructor<E extends Element, FP extends FilterParams<any, any>, FM extends FilterMethods<any, any>, AM extends ActionMethods<any, any>> {
206
+ selector(value: string | SelectorFn<E>): InteractorConstructor<E, FP, FM, AM>;
207
+ locator(value: FilterDefinition<string, E>): InteractorConstructor<E, FP, FM, AM>;
208
+ filters<FR extends Filters<E>>(filters: FR): InteractorConstructor<E, MergeObjects<FP, FilterParams<E, FR>>, MergeObjects<FM, FilterMethods<E, FR>>, AM>;
209
+ actions<AR extends Actions<E>>(actions: AR): InteractorConstructor<E, FP, FM, MergeObjects<AM, ActionMethods<E, AR>>>;
210
+ extend<ER extends E = E>(name: string): InteractorConstructor<ER, FP, FM, AM>;
211
+
212
+ /**
213
+ * The constructor can be called with filters only:
214
+ *
215
+ * ``` typescript
216
+ * Link({ id: 'home-link', href: '/' });
217
+ * ```
218
+ *
219
+ * Or with no arguments, this can be especially useful when finding a nested element.
220
+ *
221
+ * ```
222
+ * ListItem('JavaScript').find(Link()).click(); // click the only link within a specific list item
223
+ * ```
224
+ *
225
+ * @param filters An object describing a set of filters to apply, which should match the value of applying the filters defined in the {@link InteractorSpecification} to the element.
226
+ */
227
+ (filters?: FP): Interactor<E, FP> & FM & AM;
228
+ /**
229
+ * The constructor can be called with a locator:
230
+ *
231
+ * ``` typescript
232
+ * Link('Home');
233
+ * ```
234
+ *
235
+ * Or with a locator and options:
236
+ *
237
+ * ``` typescript
238
+ * Link('Home', { href: '/' });
239
+ * ```
240
+ *
241
+ * @param value The locator value, which should match the value of applying the locator function defined in the {@link InteractorSpecification} to the element.
242
+ * @param filters An object describing a set of filters to apply, which should match the value of applying the filters defined in the {@link InteractorSpecification} to the element.
243
+ */
244
+ (value: MaybeMatcher<string>, filters?: FP): Interactor<E, FP> & FM & AM;
245
+ }
246
+
247
+ export type InteractorOptions<E extends Element, F extends Filters<E>, A extends Actions<E>> = {
248
+ name: string;
249
+ specification: InteractorSpecification<E, F, A>;
250
+ locator?: Locator<E>;
251
+ filter: FilterSet<E, F>;
252
+ ancestors: InteractorOptions<any, any, any>[];
253
+ };