@testspectra/matchers 1.0.70 → 1.1.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.
- package/LICENSE.md +48 -0
- package/dist/__tests__/intercept.test.d.ts +1 -0
- package/dist/__tests__/intercept.test.js +183 -0
- package/dist/__tests__/matcher-contracts.test.d.ts +1 -0
- package/dist/__tests__/matcher-contracts.test.js +498 -0
- package/dist/__tests__/matchers.test.d.ts +1 -0
- package/dist/__tests__/matchers.test.js +161 -0
- package/dist/contract.d.ts +141 -0
- package/dist/contract.js +10 -0
- package/dist/index.d.ts +4 -21
- package/dist/index.js +4 -21
- package/dist/intercept/cdp-handler.d.ts +22 -0
- package/dist/intercept/cdp-handler.js +123 -0
- package/dist/intercept/index.d.ts +4 -0
- package/dist/intercept/index.js +4 -0
- package/dist/intercept/mock-handle.d.ts +49 -0
- package/dist/intercept/mock-handle.js +124 -0
- package/dist/intercept/mock-registry.d.ts +18 -0
- package/dist/intercept/mock-registry.js +97 -0
- package/dist/intercept/types.d.ts +43 -0
- package/dist/intercept/types.js +1 -0
- package/dist/matchers.d.ts +15 -4
- package/dist/matchers.js +609 -81
- package/dist/proto.d.ts +18 -283
- package/dist/proto.js +1 -114
- package/dist/reporter.d.ts +30 -0
- package/dist/reporter.js +97 -0
- package/dist/runner/collection.d.ts +33 -20
- package/dist/runner/collection.js +104 -26
- package/dist/runner/single.d.ts +125 -34
- package/dist/runner/single.js +290 -55
- package/dist/semantic.d.ts +11 -0
- package/dist/semantic.js +141 -0
- package/dist/spectra.d.ts +32 -8
- package/dist/spectra.js +157 -40
- package/dist/types.d.ts +1226 -28
- package/package.json +13 -8
- package/src/contract.ts +182 -0
- package/src/index.ts +4 -22
- package/src/proto.ts +22 -433
- package/src/runtime/assertions.ts +450 -0
- package/src/runtime/element_actions.ts +169 -0
- package/src/runtime/element_proxy.ts +159 -0
- package/src/runtime/element_state.ts +91 -0
- package/src/runtime/spectra.ts +109 -0
- package/src/types.ts +1424 -96
- package/tsconfig.json +2 -2
- package/src/matchers.ts +0 -146
- package/src/runner/collection.ts +0 -153
- package/src/runner/single.ts +0 -301
- package/src/spectra.ts +0 -376
package/dist/runner/single.js
CHANGED
|
@@ -1,31 +1,46 @@
|
|
|
1
|
-
import { executeSingleMatcher, resolveElement } from
|
|
1
|
+
import { executeSingleMatcher, resolveElement } from '../matchers.js';
|
|
2
|
+
import { trackCommand } from '../reporter.js';
|
|
3
|
+
function getRawTarget(target) {
|
|
4
|
+
if (!target)
|
|
5
|
+
return '';
|
|
6
|
+
if (typeof target === 'string')
|
|
7
|
+
return target;
|
|
8
|
+
if (target.selector)
|
|
9
|
+
return target.selector;
|
|
10
|
+
if (target.constructor && target.constructor.name && target.constructor.name !== 'Object')
|
|
11
|
+
return target.constructor.name;
|
|
12
|
+
return String(target);
|
|
13
|
+
}
|
|
2
14
|
/**
|
|
3
15
|
* Fluent interaction and assertion runner for a single element or browser context.
|
|
4
16
|
*
|
|
5
|
-
* Implements `PromiseLike<void
|
|
6
|
-
* multiple actions and `.should
|
|
17
|
+
* Implements `PromiseLike<void>` and `ElementReceiverAssertions<SingleElementRunner>`,
|
|
18
|
+
* allowing it to be awaited directly or chained with multiple actions and direct `.should...` assertions.
|
|
7
19
|
*
|
|
8
20
|
* @example
|
|
9
21
|
* ```ts
|
|
10
22
|
* // Direct action + chained assertion
|
|
11
23
|
* await Spectra.get("#submit-btn")
|
|
12
24
|
* .click()
|
|
13
|
-
* .
|
|
25
|
+
* .shouldNotBeVisible();
|
|
14
26
|
*
|
|
15
27
|
* // Query + multiple assertions
|
|
16
28
|
* await Spectra.get(LoginPage.usernameInput)
|
|
17
|
-
* .
|
|
18
|
-
* .
|
|
19
|
-
* .
|
|
29
|
+
* .shouldBeVisible()
|
|
30
|
+
* .shouldBeEnabled()
|
|
31
|
+
* .shouldHaveValue("admin");
|
|
20
32
|
* ```
|
|
21
33
|
*/
|
|
22
34
|
export class SingleElementRunner {
|
|
23
35
|
_target;
|
|
24
36
|
_action = null;
|
|
37
|
+
_actionPayload = null;
|
|
25
38
|
_assertions = [];
|
|
26
|
-
constructor(target, action) {
|
|
39
|
+
constructor(target, action, actionPayload) {
|
|
27
40
|
this._target = target;
|
|
28
41
|
this._action = action || null;
|
|
42
|
+
this._actionPayload =
|
|
43
|
+
actionPayload || (action ? { type: 'action', key: 'action', target: getRawTarget(target), args: [] } : null);
|
|
29
44
|
}
|
|
30
45
|
// --- Chained Actions on current target ---
|
|
31
46
|
/**
|
|
@@ -38,7 +53,8 @@ export class SingleElementRunner {
|
|
|
38
53
|
* @returns Current runner instance for method chaining.
|
|
39
54
|
*/
|
|
40
55
|
click() {
|
|
41
|
-
const target = this.requireTarget(
|
|
56
|
+
const target = this.requireTarget('click');
|
|
57
|
+
this._actionPayload = { type: 'action', key: 'click', target: getRawTarget(target), args: [] };
|
|
42
58
|
this._action = async () => {
|
|
43
59
|
const el = await resolveElement(target);
|
|
44
60
|
await el.click();
|
|
@@ -55,7 +71,8 @@ export class SingleElementRunner {
|
|
|
55
71
|
* @returns Current runner instance for method chaining.
|
|
56
72
|
*/
|
|
57
73
|
doubleClick() {
|
|
58
|
-
const target = this.requireTarget(
|
|
74
|
+
const target = this.requireTarget('doubleClick');
|
|
75
|
+
this._actionPayload = { type: 'action', key: 'doubleClick', target: getRawTarget(target), args: [] };
|
|
59
76
|
this._action = async () => {
|
|
60
77
|
const el = await resolveElement(target);
|
|
61
78
|
await el.doubleClick();
|
|
@@ -73,14 +90,12 @@ export class SingleElementRunner {
|
|
|
73
90
|
* @returns Current runner instance for method chaining.
|
|
74
91
|
*/
|
|
75
92
|
longPress(durationMs = 1000) {
|
|
76
|
-
const target = this.requireTarget(
|
|
93
|
+
const target = this.requireTarget('longPress');
|
|
94
|
+
this._actionPayload = { type: 'action', key: 'longPress', target: getRawTarget(target), args: [durationMs] };
|
|
77
95
|
this._action = async () => {
|
|
78
96
|
const el = await resolveElement(target);
|
|
79
|
-
if (typeof el.touchAction ===
|
|
80
|
-
await el.touchAction([
|
|
81
|
-
{ action: "longPress", ms: durationMs },
|
|
82
|
-
{ action: "release" },
|
|
83
|
-
]);
|
|
97
|
+
if (typeof el.touchAction === 'function') {
|
|
98
|
+
await el.touchAction([{ action: 'longPress', ms: durationMs }, { action: 'release' }]);
|
|
84
99
|
}
|
|
85
100
|
else {
|
|
86
101
|
await el.click();
|
|
@@ -100,13 +115,22 @@ export class SingleElementRunner {
|
|
|
100
115
|
* @returns Current runner instance for method chaining.
|
|
101
116
|
*/
|
|
102
117
|
type(value, options) {
|
|
103
|
-
const target = this.requireTarget(
|
|
118
|
+
const target = this.requireTarget('type');
|
|
119
|
+
this._actionPayload = { type: 'action', key: 'type', target: getRawTarget(target), args: [value] };
|
|
104
120
|
this._action = async () => {
|
|
105
121
|
const el = await resolveElement(target);
|
|
106
122
|
if (options?.clearFirst) {
|
|
107
|
-
|
|
123
|
+
if (typeof el.clearValue === 'function')
|
|
124
|
+
await el.clearValue();
|
|
125
|
+
else if (typeof el.clear === 'function')
|
|
126
|
+
await el.clear();
|
|
108
127
|
}
|
|
109
|
-
|
|
128
|
+
if (typeof el.setValue === 'function')
|
|
129
|
+
await el.setValue(value);
|
|
130
|
+
else if (typeof el.fill === 'function')
|
|
131
|
+
await el.fill(value);
|
|
132
|
+
else if (typeof el.type === 'function')
|
|
133
|
+
await el.type(value);
|
|
110
134
|
};
|
|
111
135
|
return this;
|
|
112
136
|
}
|
|
@@ -120,10 +144,14 @@ export class SingleElementRunner {
|
|
|
120
144
|
* @returns Current runner instance for method chaining.
|
|
121
145
|
*/
|
|
122
146
|
clear() {
|
|
123
|
-
const target = this.requireTarget(
|
|
147
|
+
const target = this.requireTarget('clear');
|
|
148
|
+
this._actionPayload = { type: 'action', key: 'clear', target: getRawTarget(target), args: [] };
|
|
124
149
|
this._action = async () => {
|
|
125
150
|
const el = await resolveElement(target);
|
|
126
|
-
|
|
151
|
+
if (typeof el.clearValue === 'function')
|
|
152
|
+
await el.clearValue();
|
|
153
|
+
else if (typeof el.clear === 'function')
|
|
154
|
+
await el.clear();
|
|
127
155
|
};
|
|
128
156
|
return this;
|
|
129
157
|
}
|
|
@@ -138,27 +166,35 @@ export class SingleElementRunner {
|
|
|
138
166
|
* @returns Current runner instance for method chaining.
|
|
139
167
|
*/
|
|
140
168
|
select(value) {
|
|
141
|
-
const target = this.requireTarget(
|
|
169
|
+
const target = this.requireTarget('select');
|
|
170
|
+
this._actionPayload = { type: 'action', key: 'selectOption', target: getRawTarget(target), args: [value] };
|
|
142
171
|
this._action = async () => {
|
|
143
172
|
const el = await resolveElement(target);
|
|
144
|
-
|
|
173
|
+
if (typeof el.selectByVisibleText === 'function')
|
|
174
|
+
await el.selectByVisibleText(value);
|
|
175
|
+
else if (typeof el.selectOption === 'function')
|
|
176
|
+
await el.selectOption({ label: value });
|
|
145
177
|
};
|
|
146
178
|
return this;
|
|
147
179
|
}
|
|
148
180
|
/**
|
|
149
|
-
*
|
|
181
|
+
* Hovers over the target element.
|
|
150
182
|
*
|
|
151
183
|
* @example
|
|
152
184
|
* ```ts
|
|
153
|
-
* await Spectra.get("#dropdown-
|
|
185
|
+
* await Spectra.get("#dropdown-trigger").hover();
|
|
154
186
|
* ```
|
|
155
187
|
* @returns Current runner instance for method chaining.
|
|
156
188
|
*/
|
|
157
189
|
hover() {
|
|
158
|
-
const target = this.requireTarget(
|
|
190
|
+
const target = this.requireTarget('hover');
|
|
191
|
+
this._actionPayload = { type: 'action', key: 'hover', target: getRawTarget(target), args: [] };
|
|
159
192
|
this._action = async () => {
|
|
160
193
|
const el = await resolveElement(target);
|
|
161
|
-
|
|
194
|
+
if (typeof el.moveTo === 'function')
|
|
195
|
+
await el.moveTo();
|
|
196
|
+
else if (typeof el.hover === 'function')
|
|
197
|
+
await el.hover();
|
|
162
198
|
};
|
|
163
199
|
return this;
|
|
164
200
|
}
|
|
@@ -173,40 +209,223 @@ export class SingleElementRunner {
|
|
|
173
209
|
* @returns Current runner instance for method chaining.
|
|
174
210
|
*/
|
|
175
211
|
waitForElement(timeoutMs = 10000) {
|
|
176
|
-
const target = this.requireTarget(
|
|
212
|
+
const target = this.requireTarget('waitForElement');
|
|
213
|
+
this._actionPayload = { type: 'action', key: 'waitForElement', target: getRawTarget(target), args: [timeoutMs] };
|
|
177
214
|
this._action = async () => {
|
|
178
215
|
const el = await resolveElement(target);
|
|
179
216
|
await el.waitForDisplayed({ timeout: timeoutMs });
|
|
180
217
|
};
|
|
181
218
|
return this;
|
|
182
219
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
220
|
+
/**
|
|
221
|
+
* Performs a right-click (context menu) interaction on the target element.
|
|
222
|
+
*/
|
|
223
|
+
rightClick() {
|
|
224
|
+
const target = this.requireTarget('rightClick');
|
|
225
|
+
this._actionPayload = { type: 'action', key: 'rightClick', target: getRawTarget(target), args: [] };
|
|
226
|
+
this._action = async () => {
|
|
227
|
+
const el = await resolveElement(target);
|
|
228
|
+
await el.click({ button: 'right' });
|
|
229
|
+
};
|
|
230
|
+
return this;
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Drags the target element and drops it onto the specified destination element.
|
|
234
|
+
*/
|
|
235
|
+
dragDrop(destination) {
|
|
236
|
+
const target = this.requireTarget('dragDrop');
|
|
237
|
+
this._actionPayload = {
|
|
238
|
+
type: 'action',
|
|
239
|
+
key: 'dragDrop',
|
|
240
|
+
target: getRawTarget(target),
|
|
241
|
+
args: [getRawTarget(destination)],
|
|
242
|
+
};
|
|
243
|
+
this._action = async () => {
|
|
244
|
+
const sourceEl = await resolveElement(target);
|
|
245
|
+
const destEl = await resolveElement(destination);
|
|
246
|
+
await sourceEl.dragAndDrop(destEl);
|
|
247
|
+
};
|
|
248
|
+
return this;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Scrolls the target element into view.
|
|
252
|
+
*/
|
|
253
|
+
scrollIntoView() {
|
|
254
|
+
const target = this.requireTarget('scrollIntoView');
|
|
255
|
+
this._actionPayload = { type: 'action', key: 'scrollIntoView', target: getRawTarget(target), args: [] };
|
|
256
|
+
this._action = async () => {
|
|
257
|
+
const el = await resolveElement(target);
|
|
258
|
+
await el.scrollIntoView();
|
|
259
|
+
};
|
|
260
|
+
return this;
|
|
261
|
+
}
|
|
262
|
+
_pushAssertion(matcher, args = []) {
|
|
263
|
+
this._assertions.push({
|
|
264
|
+
target: this._target,
|
|
265
|
+
matcher,
|
|
266
|
+
args,
|
|
267
|
+
});
|
|
208
268
|
return this;
|
|
209
269
|
}
|
|
270
|
+
// --- Fluent Assertion Matchers ---
|
|
271
|
+
/**
|
|
272
|
+
* Asserts that the target element is displayed and visible to the user.
|
|
273
|
+
*/
|
|
274
|
+
shouldBeVisible() {
|
|
275
|
+
return this._pushAssertion('be.visible');
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Asserts that the target element is not displayed or hidden from view.
|
|
279
|
+
*/
|
|
280
|
+
shouldNotBeVisible() {
|
|
281
|
+
return this._pushAssertion('not.be.visible');
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Asserts that the target element exists in the DOM.
|
|
285
|
+
*/
|
|
286
|
+
shouldExist() {
|
|
287
|
+
return this._pushAssertion('exist');
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Asserts that the target element does not exist in the DOM.
|
|
291
|
+
*/
|
|
292
|
+
shouldNotExist() {
|
|
293
|
+
return this._pushAssertion('not.exist');
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* Asserts that the target element is clickable.
|
|
297
|
+
*/
|
|
298
|
+
shouldBeClickable() {
|
|
299
|
+
return this._pushAssertion('be.clickable');
|
|
300
|
+
}
|
|
301
|
+
/**
|
|
302
|
+
* Asserts that the target element is not clickable.
|
|
303
|
+
*/
|
|
304
|
+
shouldNotBeClickable() {
|
|
305
|
+
return this._pushAssertion('not.be.clickable');
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Asserts that the target element is enabled (not disabled).
|
|
309
|
+
*/
|
|
310
|
+
shouldBeEnabled() {
|
|
311
|
+
return this._pushAssertion('be.enabled');
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Asserts that the target element is disabled.
|
|
315
|
+
*/
|
|
316
|
+
shouldBeDisabled() {
|
|
317
|
+
return this._pushAssertion('be.disabled');
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Asserts that the target checkbox or radio element is checked.
|
|
321
|
+
*/
|
|
322
|
+
shouldBeChecked() {
|
|
323
|
+
return this._pushAssertion('be.checked');
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Asserts that the target checkbox or radio element is not checked.
|
|
327
|
+
*/
|
|
328
|
+
shouldNotBeChecked() {
|
|
329
|
+
return this._pushAssertion('not.be.checked');
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* Asserts that the target element currently has focus.
|
|
333
|
+
*/
|
|
334
|
+
shouldBeFocused() {
|
|
335
|
+
return this._pushAssertion('be.focused');
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Asserts that the target element does not have focus.
|
|
339
|
+
*/
|
|
340
|
+
shouldNotBeFocused() {
|
|
341
|
+
return this._pushAssertion('not.be.focused');
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Asserts that the target element has exact inner text matching the expected value.
|
|
345
|
+
*/
|
|
346
|
+
shouldHaveText(expected) {
|
|
347
|
+
return this._pushAssertion('have.text', [expected]);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Asserts that the target element does not have inner text matching the expected value.
|
|
351
|
+
*/
|
|
352
|
+
shouldNotHaveText(expected) {
|
|
353
|
+
return this._pushAssertion('not.have.text', [expected]);
|
|
354
|
+
}
|
|
355
|
+
/**
|
|
356
|
+
* Asserts that the target element's text contains the given substring.
|
|
357
|
+
*/
|
|
358
|
+
shouldContainText(substring) {
|
|
359
|
+
return this._pushAssertion('contain.text', [substring]);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Asserts that the target element's text does not contain the given substring.
|
|
363
|
+
*/
|
|
364
|
+
shouldNotContainText(substring) {
|
|
365
|
+
return this._pushAssertion('not.contain.text', [substring]);
|
|
366
|
+
}
|
|
367
|
+
/**
|
|
368
|
+
* Asserts that the target input element has the exact expected value.
|
|
369
|
+
*/
|
|
370
|
+
shouldHaveValue(value) {
|
|
371
|
+
return this._pushAssertion('have.value', [value]);
|
|
372
|
+
}
|
|
373
|
+
/**
|
|
374
|
+
* Asserts that the target input element does not have the expected value.
|
|
375
|
+
*/
|
|
376
|
+
shouldNotHaveValue(value) {
|
|
377
|
+
return this._pushAssertion('not.have.value', [value]);
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Asserts that the target input element's value contains the given substring.
|
|
381
|
+
*/
|
|
382
|
+
shouldContainValue(substring) {
|
|
383
|
+
return this._pushAssertion('contain.value', [substring]);
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Asserts that the target input element's value does not contain the given substring.
|
|
387
|
+
*/
|
|
388
|
+
shouldNotContainValue(substring) {
|
|
389
|
+
return this._pushAssertion('not.contain.value', [substring]);
|
|
390
|
+
}
|
|
391
|
+
/**
|
|
392
|
+
* Asserts that the target element has the specified HTML attribute and optional value.
|
|
393
|
+
*/
|
|
394
|
+
shouldHaveAttribute(name, value) {
|
|
395
|
+
return value !== undefined
|
|
396
|
+
? this._pushAssertion('have.attr', [name, value])
|
|
397
|
+
: this._pushAssertion('have.attr', [name]);
|
|
398
|
+
}
|
|
399
|
+
/**
|
|
400
|
+
* Asserts that the target element does not have the specified HTML attribute.
|
|
401
|
+
*/
|
|
402
|
+
shouldNotHaveAttribute(name) {
|
|
403
|
+
return this._pushAssertion('not.have.attr', [name]);
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Asserts that the target element has the specified CSS class.
|
|
407
|
+
*/
|
|
408
|
+
shouldHaveClass(className) {
|
|
409
|
+
return this._pushAssertion('have.class', [className]);
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Asserts that the target element does not have the specified CSS class.
|
|
413
|
+
*/
|
|
414
|
+
shouldNotHaveClass(className) {
|
|
415
|
+
return this._pushAssertion('not.have.class', [className]);
|
|
416
|
+
}
|
|
417
|
+
/**
|
|
418
|
+
* Asserts that the target element has the specified computed CSS property value.
|
|
419
|
+
*/
|
|
420
|
+
shouldHaveCss(property, value) {
|
|
421
|
+
return this._pushAssertion('have.css', [property, value]);
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* Asserts that the target element does not have the specified computed CSS property value.
|
|
425
|
+
*/
|
|
426
|
+
shouldNotHaveCss(property, value) {
|
|
427
|
+
return this._pushAssertion('not.have.css', [property, value]);
|
|
428
|
+
}
|
|
210
429
|
// --- Execution when awaited ---
|
|
211
430
|
/**
|
|
212
431
|
* Standard Promise `then` implementation.
|
|
@@ -215,10 +434,26 @@ export class SingleElementRunner {
|
|
|
215
434
|
async then(onfulfilled, onrejected) {
|
|
216
435
|
try {
|
|
217
436
|
if (this._action) {
|
|
218
|
-
|
|
437
|
+
const payload = this._actionPayload || {
|
|
438
|
+
type: 'action',
|
|
439
|
+
key: 'action',
|
|
440
|
+
target: getRawTarget(this._target),
|
|
441
|
+
args: [],
|
|
442
|
+
};
|
|
443
|
+
await trackCommand('action', payload, async () => {
|
|
444
|
+
await this._action();
|
|
445
|
+
});
|
|
219
446
|
}
|
|
220
447
|
for (const assert of this._assertions) {
|
|
221
|
-
|
|
448
|
+
const payload = {
|
|
449
|
+
type: 'assertion',
|
|
450
|
+
key: assert.matcher,
|
|
451
|
+
target: getRawTarget(assert.target),
|
|
452
|
+
args: assert.args,
|
|
453
|
+
};
|
|
454
|
+
await trackCommand('assertion', payload, async () => {
|
|
455
|
+
await executeSingleMatcher(assert.target, assert.matcher, assert.args);
|
|
456
|
+
});
|
|
222
457
|
}
|
|
223
458
|
const res = (onfulfilled ? await onfulfilled() : undefined);
|
|
224
459
|
return res;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* # @testspectra/matchers - Semantic Natural Language Formatter
|
|
4
|
+
*
|
|
5
|
+
* Formats commands, actions, and assertions into clean, grammatically
|
|
6
|
+
* correct English sentences for real-time reporting.
|
|
7
|
+
*/
|
|
8
|
+
export declare function formatSemanticTarget(target: any, isCollection?: boolean): string;
|
|
9
|
+
export declare function formatSemanticAction(action: string, target: any, args?: any[]): string;
|
|
10
|
+
export declare function formatSemanticMatcher(target: any, matcher: string, args?: any[], isCollection?: boolean): string;
|
|
11
|
+
export declare function formatSemanticBrowser(method: string, args?: any[]): string;
|
package/dist/semantic.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* # @testspectra/matchers - Semantic Natural Language Formatter
|
|
4
|
+
*
|
|
5
|
+
* Formats commands, actions, and assertions into clean, grammatically
|
|
6
|
+
* correct English sentences for real-time reporting.
|
|
7
|
+
*/
|
|
8
|
+
export function formatSemanticTarget(target, isCollection = false) {
|
|
9
|
+
if (target === undefined || target === null)
|
|
10
|
+
return isCollection ? 'collection' : 'element';
|
|
11
|
+
if (typeof target === 'string') {
|
|
12
|
+
return isCollection ? `collection "${target}"` : `element "${target}"`;
|
|
13
|
+
}
|
|
14
|
+
if (typeof target === 'object') {
|
|
15
|
+
if (target.selector) {
|
|
16
|
+
return isCollection ? `collection "${target.selector}"` : `element "${target.selector}"`;
|
|
17
|
+
}
|
|
18
|
+
if (target.constructor && target.constructor.name && target.constructor.name !== 'Object') {
|
|
19
|
+
return `element "${target.constructor.name}"`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return `element "${String(target)}"`;
|
|
23
|
+
}
|
|
24
|
+
export function formatSemanticAction(action, target, args = []) {
|
|
25
|
+
const targetStr = formatSemanticTarget(target, false);
|
|
26
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
27
|
+
switch (action) {
|
|
28
|
+
case 'type':
|
|
29
|
+
return `Type "${arg0}" into ${targetStr}`;
|
|
30
|
+
case 'click':
|
|
31
|
+
return `Click on ${targetStr}`;
|
|
32
|
+
case 'doubleClick':
|
|
33
|
+
return `Double-click on ${targetStr}`;
|
|
34
|
+
case 'rightClick':
|
|
35
|
+
return `Right-click on ${targetStr}`;
|
|
36
|
+
case 'check':
|
|
37
|
+
return `Check checkbox ${targetStr}`;
|
|
38
|
+
case 'uncheck':
|
|
39
|
+
return `Uncheck checkbox ${targetStr}`;
|
|
40
|
+
case 'selectOption':
|
|
41
|
+
return `Select option "${arg0}" in ${targetStr}`;
|
|
42
|
+
case 'clear':
|
|
43
|
+
return `Clear text in ${targetStr}`;
|
|
44
|
+
case 'hover':
|
|
45
|
+
return `Hover over ${targetStr}`;
|
|
46
|
+
case 'scrollIntoView':
|
|
47
|
+
return `Scroll ${targetStr} into view`;
|
|
48
|
+
case 'navigate':
|
|
49
|
+
return `Navigate to "${arg0}"`;
|
|
50
|
+
case 'wait':
|
|
51
|
+
return `Wait for ${arg0}ms`;
|
|
52
|
+
case 'pause':
|
|
53
|
+
return `Pause execution for ${arg0}ms`;
|
|
54
|
+
default:
|
|
55
|
+
return `Perform ${action} on ${targetStr}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function formatSemanticMatcher(target, matcher, args = [], isCollection = false) {
|
|
59
|
+
const targetStr = formatSemanticTarget(target, isCollection);
|
|
60
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
61
|
+
const arg1 = args[1] !== undefined ? String(args[1]) : '';
|
|
62
|
+
switch (matcher) {
|
|
63
|
+
case 'be.visible':
|
|
64
|
+
return `Expect ${targetStr} to be visible`;
|
|
65
|
+
case 'not.be.visible':
|
|
66
|
+
return `Expect ${targetStr} not to be visible`;
|
|
67
|
+
case 'exist':
|
|
68
|
+
return `Expect ${targetStr} to exist in DOM`;
|
|
69
|
+
case 'not.exist':
|
|
70
|
+
return `Expect ${targetStr} not to exist in DOM`;
|
|
71
|
+
case 'be.enabled':
|
|
72
|
+
return `Expect ${targetStr} to be enabled`;
|
|
73
|
+
case 'be.disabled':
|
|
74
|
+
return `Expect ${targetStr} to be disabled`;
|
|
75
|
+
case 'be.checked':
|
|
76
|
+
return `Expect ${targetStr} to be checked`;
|
|
77
|
+
case 'not.be.checked':
|
|
78
|
+
return `Expect ${targetStr} not to be checked`;
|
|
79
|
+
case 'have.text':
|
|
80
|
+
return `Expect ${targetStr} to have text "${arg0}"`;
|
|
81
|
+
case 'not.have.text':
|
|
82
|
+
return `Expect ${targetStr} not to have text "${arg0}"`;
|
|
83
|
+
case 'contain.text':
|
|
84
|
+
return `Expect ${targetStr} to contain text "${arg0}"`;
|
|
85
|
+
case 'not.contain.text':
|
|
86
|
+
return `Expect ${targetStr} not to contain text "${arg0}"`;
|
|
87
|
+
case 'have.value':
|
|
88
|
+
return `Expect ${targetStr} to have value "${arg0}"`;
|
|
89
|
+
case 'not.have.value':
|
|
90
|
+
return `Expect ${targetStr} not to have value "${arg0}"`;
|
|
91
|
+
case 'contain.value':
|
|
92
|
+
return `Expect ${targetStr} to contain value "${arg0}"`;
|
|
93
|
+
case 'not.contain.value':
|
|
94
|
+
return `Expect ${targetStr} not to contain value "${arg0}"`;
|
|
95
|
+
case 'have.class':
|
|
96
|
+
return `Expect ${targetStr} to have class "${arg0}"`;
|
|
97
|
+
case 'not.have.class':
|
|
98
|
+
return `Expect ${targetStr} not to have class "${arg0}"`;
|
|
99
|
+
case 'have.attr':
|
|
100
|
+
return args.length >= 2
|
|
101
|
+
? `Expect ${targetStr} to have attribute "${arg0}" = "${arg1}"`
|
|
102
|
+
: `Expect ${targetStr} to have attribute "${arg0}"`;
|
|
103
|
+
case 'not.have.attr':
|
|
104
|
+
return `Expect ${targetStr} not to have attribute "${arg0}"`;
|
|
105
|
+
case 'be.focused':
|
|
106
|
+
return `Expect ${targetStr} to be focused`;
|
|
107
|
+
case 'be.selected':
|
|
108
|
+
return `Expect ${targetStr} to be selected`;
|
|
109
|
+
case 'be.empty':
|
|
110
|
+
return `Expect ${targetStr} to be empty`;
|
|
111
|
+
case 'not.be.empty':
|
|
112
|
+
return `Expect ${targetStr} not to be empty`;
|
|
113
|
+
case 'have.length':
|
|
114
|
+
return `Expect ${targetStr} count to equal ${arg0}`;
|
|
115
|
+
case 'have.length.greaterThan':
|
|
116
|
+
return `Expect ${targetStr} count to be greater than ${arg0}`;
|
|
117
|
+
case 'have.length.lessThan':
|
|
118
|
+
return `Expect ${targetStr} count to be less than ${arg0}`;
|
|
119
|
+
case 'have.length.atLeast':
|
|
120
|
+
return `Expect ${targetStr} count to be at least ${arg0}`;
|
|
121
|
+
case 'have.length.atMost':
|
|
122
|
+
return `Expect ${targetStr} count to be at most ${arg0}`;
|
|
123
|
+
default:
|
|
124
|
+
return `Expect ${targetStr} to match ${matcher}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function formatSemanticBrowser(method, args = []) {
|
|
128
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
129
|
+
switch (method) {
|
|
130
|
+
case 'shouldContainTitle':
|
|
131
|
+
return `Expect browser title to contain "${arg0}"`;
|
|
132
|
+
case 'shouldHaveTitle':
|
|
133
|
+
return `Expect browser title to be "${arg0}"`;
|
|
134
|
+
case 'shouldContainUrl':
|
|
135
|
+
return `Expect browser URL to contain "${arg0}"`;
|
|
136
|
+
case 'shouldHaveUrl':
|
|
137
|
+
return `Expect browser URL to be "${arg0}"`;
|
|
138
|
+
default:
|
|
139
|
+
return `Expect browser to ${method} "${arg0}"`;
|
|
140
|
+
}
|
|
141
|
+
}
|