@testspectra/matchers 1.1.0 → 1.1.8-rc.1
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/dist/__tests__/matcher-contracts.test.d.ts +1 -0
- package/dist/__tests__/matcher-contracts.test.js +498 -0
- package/dist/contract.d.ts +173 -0
- package/dist/contract.js +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/matchers.d.ts +10 -3
- package/dist/matchers.js +576 -135
- package/dist/proto.d.ts +8 -0
- package/dist/reporter.js +13 -1
- package/dist/runner/collection.js +32 -6
- package/dist/runner/single.d.ts +2 -2
- package/dist/runner/single.js +24 -7
- package/dist/semantic.d.ts +11 -0
- package/dist/semantic.js +141 -0
- package/dist/types.d.ts +246 -115
- package/package.json +11 -10
- package/src/contract.ts +223 -0
- package/src/index.ts +1 -0
- package/src/proto.ts +8 -0
- package/src/runtime/assertions.ts +453 -0
- package/src/runtime/element_actions.ts +178 -0
- package/src/runtime/element_proxy.ts +200 -0
- package/src/runtime/element_state.ts +94 -0
- package/src/runtime/spectra.ts +110 -0
- package/src/types.ts +251 -124
- package/tsconfig.json +1 -1
package/dist/types.d.ts
CHANGED
|
@@ -37,6 +37,25 @@ export type Direction = 'up' | 'down' | 'left' | 'right';
|
|
|
37
37
|
* ```
|
|
38
38
|
*/
|
|
39
39
|
export type ElementTarget = string | SingleElementProxy | Promise<SingleElementProxy>;
|
|
40
|
+
/**
|
|
41
|
+
* Internal descriptor for a selector resolved relative to a parent element, produced by
|
|
42
|
+
* `element.get()` / `element.getAll()` chaining (see `SingleElementProxy.get`). Recursive so
|
|
43
|
+
* arbitrarily deep chains (e.g. `Spectra.getAll('.card').nth(2).get('.buy-btn')`) carry their
|
|
44
|
+
* full ancestry down to the driver.
|
|
45
|
+
*
|
|
46
|
+
* Resolution differs per platform: web (CDP) nests real DOM `.querySelector()` calls against the
|
|
47
|
+
* resolved parent; Android has no ancestor/descendant API, so the parent's bounds rectangle is
|
|
48
|
+
* used to filter candidates via containment (a child is "within" the parent if its bounds sit
|
|
49
|
+
* inside the parent's).
|
|
50
|
+
*/
|
|
51
|
+
export interface ScopedSelector {
|
|
52
|
+
/** This level's own selector string (`~`/`#` shorthand or raw CSS/xpath), not the full chain. */
|
|
53
|
+
selector: string;
|
|
54
|
+
/** Positional index at this level, or null. */
|
|
55
|
+
index: number | null;
|
|
56
|
+
/** The element this selector is scoped within, or undefined for a root-level selector. */
|
|
57
|
+
parent?: ScopedSelector;
|
|
58
|
+
}
|
|
40
59
|
/**
|
|
41
60
|
* Configuration options for scroll actions.
|
|
42
61
|
*
|
|
@@ -105,6 +124,25 @@ export interface TypeOptions {
|
|
|
105
124
|
/** Whether to clear existing input value before typing new text (default: false) */
|
|
106
125
|
clearFirst?: boolean;
|
|
107
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Per-call override for how long an assertion polls before failing, mirroring Playwright's
|
|
129
|
+
* `{ timeout }` option on web-first assertions (`expect(locator).toHaveText(x, { timeout })`).
|
|
130
|
+
*
|
|
131
|
+
* Defaults to the adaptive assertion timeout (`Spectra`'s fail-fast cap, distinct from
|
|
132
|
+
* `implicitWait`) when omitted — pass this when a specific assertion is known to need longer,
|
|
133
|
+
* e.g. right after a deliberately delayed `Spectra.intercept(..., { delayMs })` mock, without
|
|
134
|
+
* raising the timeout for every other assertion in the test.
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* const mock = await Spectra.intercept('/api/report', { response: { delayMs: 4000, body } });
|
|
139
|
+
* await ReportPage.statusBadge.shouldHaveText('Ready', { timeoutMs: 5000 });
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
export interface AssertionOptions {
|
|
143
|
+
/** Maximum time (in milliseconds) to keep polling before the assertion fails. */
|
|
144
|
+
timeoutMs?: number;
|
|
145
|
+
}
|
|
108
146
|
/**
|
|
109
147
|
* Canonical assertion matcher keys for single element verification.
|
|
110
148
|
* Dispatched internally by semantic receiver methods (`.shouldBeVisible()`, `.shouldHaveText()`, etc.).
|
|
@@ -129,7 +167,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
129
167
|
* await LoginPage.submitButton.shouldBeVisible();
|
|
130
168
|
* ```
|
|
131
169
|
*/
|
|
132
|
-
shouldBeVisible(): TReturn;
|
|
170
|
+
shouldBeVisible(options?: AssertionOptions): TReturn;
|
|
133
171
|
/**
|
|
134
172
|
* Asserts that the target element is hidden, detached, or not displayed on the page.
|
|
135
173
|
*
|
|
@@ -138,7 +176,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
138
176
|
* await Spectra.get('.loading-spinner').shouldNotBeVisible();
|
|
139
177
|
* ```
|
|
140
178
|
*/
|
|
141
|
-
shouldNotBeVisible(): TReturn;
|
|
179
|
+
shouldNotBeVisible(options?: AssertionOptions): TReturn;
|
|
142
180
|
/**
|
|
143
181
|
* Asserts that the target element exists in the DOM.
|
|
144
182
|
*
|
|
@@ -147,7 +185,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
147
185
|
* await Spectra.get('#cookie-consent-modal').shouldExist();
|
|
148
186
|
* ```
|
|
149
187
|
*/
|
|
150
|
-
shouldExist(): TReturn;
|
|
188
|
+
shouldExist(options?: AssertionOptions): TReturn;
|
|
151
189
|
/**
|
|
152
190
|
* Asserts that the target element does not exist in the DOM.
|
|
153
191
|
*
|
|
@@ -156,7 +194,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
156
194
|
* await Spectra.get('#deleted-record-row').shouldNotExist();
|
|
157
195
|
* ```
|
|
158
196
|
*/
|
|
159
|
-
shouldNotExist(): TReturn;
|
|
197
|
+
shouldNotExist(options?: AssertionOptions): TReturn;
|
|
160
198
|
/**
|
|
161
199
|
* Asserts that the target element is visible, enabled, and clickable.
|
|
162
200
|
*
|
|
@@ -165,7 +203,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
165
203
|
* await Spectra.get('button[type="submit"]').shouldBeClickable();
|
|
166
204
|
* ```
|
|
167
205
|
*/
|
|
168
|
-
shouldBeClickable(): TReturn;
|
|
206
|
+
shouldBeClickable(options?: AssertionOptions): TReturn;
|
|
169
207
|
/**
|
|
170
208
|
* Asserts that the target element is disabled, covered, or not clickable.
|
|
171
209
|
*
|
|
@@ -174,7 +212,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
174
212
|
* await Spectra.get('button.disabled-action').shouldNotBeClickable();
|
|
175
213
|
* ```
|
|
176
214
|
*/
|
|
177
|
-
shouldNotBeClickable(): TReturn;
|
|
215
|
+
shouldNotBeClickable(options?: AssertionOptions): TReturn;
|
|
178
216
|
/**
|
|
179
217
|
* Asserts that the target form input/button is enabled (not disabled).
|
|
180
218
|
*
|
|
@@ -183,7 +221,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
183
221
|
* await Spectra.get('#username-field').shouldBeEnabled();
|
|
184
222
|
* ```
|
|
185
223
|
*/
|
|
186
|
-
shouldBeEnabled(): TReturn;
|
|
224
|
+
shouldBeEnabled(options?: AssertionOptions): TReturn;
|
|
187
225
|
/**
|
|
188
226
|
* Asserts that the target form input/button has the disabled state/attribute.
|
|
189
227
|
*
|
|
@@ -192,7 +230,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
192
230
|
* await Spectra.get('#submit-order-btn').shouldBeDisabled();
|
|
193
231
|
* ```
|
|
194
232
|
*/
|
|
195
|
-
shouldBeDisabled(): TReturn;
|
|
233
|
+
shouldBeDisabled(options?: AssertionOptions): TReturn;
|
|
196
234
|
/**
|
|
197
235
|
* Asserts that the target checkbox or radio input is checked/selected.
|
|
198
236
|
*
|
|
@@ -201,7 +239,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
201
239
|
* await Spectra.get('#terms-checkbox').shouldBeChecked();
|
|
202
240
|
* ```
|
|
203
241
|
*/
|
|
204
|
-
shouldBeChecked(): TReturn;
|
|
242
|
+
shouldBeChecked(options?: AssertionOptions): TReturn;
|
|
205
243
|
/**
|
|
206
244
|
* Asserts that the target checkbox or radio input is unchecked/deselected.
|
|
207
245
|
*
|
|
@@ -210,7 +248,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
210
248
|
* await Spectra.get('#subscribe-newsletter').shouldNotBeChecked();
|
|
211
249
|
* ```
|
|
212
250
|
*/
|
|
213
|
-
shouldNotBeChecked(): TReturn;
|
|
251
|
+
shouldNotBeChecked(options?: AssertionOptions): TReturn;
|
|
214
252
|
/**
|
|
215
253
|
* Asserts that the target element currently holds active document focus.
|
|
216
254
|
*
|
|
@@ -219,7 +257,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
219
257
|
* await Spectra.get('#search-input').shouldBeFocused();
|
|
220
258
|
* ```
|
|
221
259
|
*/
|
|
222
|
-
shouldBeFocused(): TReturn;
|
|
260
|
+
shouldBeFocused(options?: AssertionOptions): TReturn;
|
|
223
261
|
/**
|
|
224
262
|
* Asserts that the target element does not hold active document focus.
|
|
225
263
|
*
|
|
@@ -228,7 +266,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
228
266
|
* await Spectra.get('#blur-input').shouldNotBeFocused();
|
|
229
267
|
* ```
|
|
230
268
|
*/
|
|
231
|
-
shouldNotBeFocused(): TReturn;
|
|
269
|
+
shouldNotBeFocused(options?: AssertionOptions): TReturn;
|
|
232
270
|
/**
|
|
233
271
|
* Asserts that the target element's text content matches the expected string or regular expression.
|
|
234
272
|
*
|
|
@@ -239,7 +277,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
239
277
|
* await Spectra.get('.badge').shouldHaveText(/Active|Pending/);
|
|
240
278
|
* ```
|
|
241
279
|
*/
|
|
242
|
-
shouldHaveText(expected: string | RegExp): TReturn;
|
|
280
|
+
shouldHaveText(expected: string | RegExp, options?: AssertionOptions): TReturn;
|
|
243
281
|
/**
|
|
244
282
|
* Asserts that the target element's text content does not match the expected string or regular expression.
|
|
245
283
|
*
|
|
@@ -249,7 +287,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
249
287
|
* await Spectra.get('.status-label').shouldNotHaveText('Error');
|
|
250
288
|
* ```
|
|
251
289
|
*/
|
|
252
|
-
shouldNotHaveText(expected: string | RegExp): TReturn;
|
|
290
|
+
shouldNotHaveText(expected: string | RegExp, options?: AssertionOptions): TReturn;
|
|
253
291
|
/**
|
|
254
292
|
* Asserts that the target element's text content contains the specified substring.
|
|
255
293
|
*
|
|
@@ -259,7 +297,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
259
297
|
* await Spectra.get('.toast-message').shouldContainText('Successfully saved');
|
|
260
298
|
* ```
|
|
261
299
|
*/
|
|
262
|
-
shouldContainText(substring: string): TReturn;
|
|
300
|
+
shouldContainText(substring: string, options?: AssertionOptions): TReturn;
|
|
263
301
|
/**
|
|
264
302
|
* Asserts that the target element's text content does not contain the specified substring.
|
|
265
303
|
*
|
|
@@ -269,7 +307,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
269
307
|
* await Spectra.get('.log-output').shouldNotContainText('Fatal Exception');
|
|
270
308
|
* ```
|
|
271
309
|
*/
|
|
272
|
-
shouldNotContainText(substring: string): TReturn;
|
|
310
|
+
shouldNotContainText(substring: string, options?: AssertionOptions): TReturn;
|
|
273
311
|
/**
|
|
274
312
|
* Asserts that the form input or textarea element's value exactly equals the specified string.
|
|
275
313
|
*
|
|
@@ -279,7 +317,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
279
317
|
* await Spectra.get('input[name="email"]').shouldHaveValue('admin@testspectra.dev');
|
|
280
318
|
* ```
|
|
281
319
|
*/
|
|
282
|
-
shouldHaveValue(value: string): TReturn;
|
|
320
|
+
shouldHaveValue(value: string, options?: AssertionOptions): TReturn;
|
|
283
321
|
/**
|
|
284
322
|
* Asserts that the form input or textarea element's value does not equal the specified string.
|
|
285
323
|
*
|
|
@@ -289,7 +327,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
289
327
|
* await Spectra.get('input[name="role"]').shouldNotHaveValue('guest');
|
|
290
328
|
* ```
|
|
291
329
|
*/
|
|
292
|
-
shouldNotHaveValue(value: string): TReturn;
|
|
330
|
+
shouldNotHaveValue(value: string, options?: AssertionOptions): TReturn;
|
|
293
331
|
/**
|
|
294
332
|
* Asserts that the form input or textarea element's value contains the specified substring.
|
|
295
333
|
*
|
|
@@ -299,7 +337,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
299
337
|
* await Spectra.get('input[name="email"]').shouldContainValue('@testspectra.dev');
|
|
300
338
|
* ```
|
|
301
339
|
*/
|
|
302
|
-
shouldContainValue(substring: string): TReturn;
|
|
340
|
+
shouldContainValue(substring: string, options?: AssertionOptions): TReturn;
|
|
303
341
|
/**
|
|
304
342
|
* Asserts that the form input or textarea element's value does not contain the specified substring.
|
|
305
343
|
*
|
|
@@ -309,7 +347,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
309
347
|
* await Spectra.get('input[name="url"]').shouldNotContainValue('http://');
|
|
310
348
|
* ```
|
|
311
349
|
*/
|
|
312
|
-
shouldNotContainValue(substring: string): TReturn;
|
|
350
|
+
shouldNotContainValue(substring: string, options?: AssertionOptions): TReturn;
|
|
313
351
|
/**
|
|
314
352
|
* Asserts that the element has the specified attribute, and optionally that its value matches.
|
|
315
353
|
*
|
|
@@ -321,7 +359,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
321
359
|
* await Spectra.get('input.required-field').shouldHaveAttribute('required');
|
|
322
360
|
* ```
|
|
323
361
|
*/
|
|
324
|
-
shouldHaveAttribute(name: string, value?: string): TReturn;
|
|
362
|
+
shouldHaveAttribute(name: string, value?: string, options?: AssertionOptions): TReturn;
|
|
325
363
|
/**
|
|
326
364
|
* Asserts that the element does not have the specified attribute.
|
|
327
365
|
*
|
|
@@ -331,7 +369,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
331
369
|
* await Spectra.get('button#action-btn').shouldNotHaveAttribute('disabled');
|
|
332
370
|
* ```
|
|
333
371
|
*/
|
|
334
|
-
shouldNotHaveAttribute(name: string): TReturn;
|
|
372
|
+
shouldNotHaveAttribute(name: string, options?: AssertionOptions): TReturn;
|
|
335
373
|
/**
|
|
336
374
|
* Asserts that the element contains the specified CSS class name.
|
|
337
375
|
*
|
|
@@ -341,7 +379,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
341
379
|
* await Spectra.get('.nav-tab').shouldHaveClass('active');
|
|
342
380
|
* ```
|
|
343
381
|
*/
|
|
344
|
-
shouldHaveClass(className: string): TReturn;
|
|
382
|
+
shouldHaveClass(className: string, options?: AssertionOptions): TReturn;
|
|
345
383
|
/**
|
|
346
384
|
* Asserts that the element does not contain the specified CSS class name.
|
|
347
385
|
*
|
|
@@ -351,7 +389,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
351
389
|
* await Spectra.get('.modal-backdrop').shouldNotHaveClass('hidden');
|
|
352
390
|
* ```
|
|
353
391
|
*/
|
|
354
|
-
shouldNotHaveClass(className: string): TReturn;
|
|
392
|
+
shouldNotHaveClass(className: string, options?: AssertionOptions): TReturn;
|
|
355
393
|
/**
|
|
356
394
|
* Asserts that the computed CSS style property of the element equals the specified value.
|
|
357
395
|
*
|
|
@@ -362,7 +400,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
362
400
|
* await Spectra.get('.badge-success').shouldHaveCss('color', 'rgb(0, 128, 0)');
|
|
363
401
|
* ```
|
|
364
402
|
*/
|
|
365
|
-
shouldHaveCss(property: string, value: string): TReturn;
|
|
403
|
+
shouldHaveCss(property: string, value: string, options?: AssertionOptions): TReturn;
|
|
366
404
|
/**
|
|
367
405
|
* Asserts that the computed CSS style property of the element does not equal the specified value.
|
|
368
406
|
*
|
|
@@ -373,7 +411,7 @@ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
|
|
|
373
411
|
* await Spectra.get('.main-content').shouldNotHaveCss('display', 'none');
|
|
374
412
|
* ```
|
|
375
413
|
*/
|
|
376
|
-
shouldNotHaveCss(property: string, value: string): TReturn;
|
|
414
|
+
shouldNotHaveCss(property: string, value: string, options?: AssertionOptions): TReturn;
|
|
377
415
|
}
|
|
378
416
|
/**
|
|
379
417
|
* Receiver-oriented collection assertion methods.
|
|
@@ -389,7 +427,7 @@ export interface CollectionReceiverAssertions<TReturn = Promise<void>> {
|
|
|
389
427
|
* await Spectra.getAll('.user-table-row').shouldHaveLength(10);
|
|
390
428
|
* ```
|
|
391
429
|
*/
|
|
392
|
-
shouldHaveLength(count: number): TReturn;
|
|
430
|
+
shouldHaveLength(count: number, options?: AssertionOptions): TReturn;
|
|
393
431
|
/**
|
|
394
432
|
* Asserts that the collection does not contain the specified number of matching elements.
|
|
395
433
|
*
|
|
@@ -399,7 +437,7 @@ export interface CollectionReceiverAssertions<TReturn = Promise<void>> {
|
|
|
399
437
|
* await Spectra.getAll('.error-item').shouldNotHaveLength(0);
|
|
400
438
|
* ```
|
|
401
439
|
*/
|
|
402
|
-
shouldNotHaveLength(count: number): TReturn;
|
|
440
|
+
shouldNotHaveLength(count: number, options?: AssertionOptions): TReturn;
|
|
403
441
|
/**
|
|
404
442
|
* Asserts that the collection contains strictly more than `min` matching elements.
|
|
405
443
|
*
|
|
@@ -409,7 +447,7 @@ export interface CollectionReceiverAssertions<TReturn = Promise<void>> {
|
|
|
409
447
|
* await Spectra.getAll('.search-result-card').shouldHaveLengthGreaterThan(0);
|
|
410
448
|
* ```
|
|
411
449
|
*/
|
|
412
|
-
shouldHaveLengthGreaterThan(min: number): TReturn;
|
|
450
|
+
shouldHaveLengthGreaterThan(min: number, options?: AssertionOptions): TReturn;
|
|
413
451
|
/**
|
|
414
452
|
* Asserts that the collection contains strictly fewer than `max` matching elements.
|
|
415
453
|
*
|
|
@@ -419,7 +457,7 @@ export interface CollectionReceiverAssertions<TReturn = Promise<void>> {
|
|
|
419
457
|
* await Spectra.getAll('.warning-banner').shouldHaveLengthLessThan(5);
|
|
420
458
|
* ```
|
|
421
459
|
*/
|
|
422
|
-
shouldHaveLengthLessThan(max: number): TReturn;
|
|
460
|
+
shouldHaveLengthLessThan(max: number, options?: AssertionOptions): TReturn;
|
|
423
461
|
/**
|
|
424
462
|
* Asserts that the collection contains zero matching elements.
|
|
425
463
|
*
|
|
@@ -428,7 +466,7 @@ export interface CollectionReceiverAssertions<TReturn = Promise<void>> {
|
|
|
428
466
|
* await Spectra.getAll('.unread-notification-badge').shouldBeEmpty();
|
|
429
467
|
* ```
|
|
430
468
|
*/
|
|
431
|
-
shouldBeEmpty(): TReturn;
|
|
469
|
+
shouldBeEmpty(options?: AssertionOptions): TReturn;
|
|
432
470
|
/**
|
|
433
471
|
* Asserts that the collection contains at least one matching element.
|
|
434
472
|
*
|
|
@@ -437,7 +475,7 @@ export interface CollectionReceiverAssertions<TReturn = Promise<void>> {
|
|
|
437
475
|
* await Spectra.getAll('.product-card').shouldNotBeEmpty();
|
|
438
476
|
* ```
|
|
439
477
|
*/
|
|
440
|
-
shouldNotBeEmpty(): TReturn;
|
|
478
|
+
shouldNotBeEmpty(options?: AssertionOptions): TReturn;
|
|
441
479
|
}
|
|
442
480
|
/**
|
|
443
481
|
* Browser-level context assertion methods.
|
|
@@ -453,7 +491,7 @@ export interface BrowserReceiverAssertions {
|
|
|
453
491
|
* await Spectra.browser.shouldHaveUrl('https://app.testspectra.dev/dashboard');
|
|
454
492
|
* ```
|
|
455
493
|
*/
|
|
456
|
-
shouldHaveUrl(expectedUrl: string): Promise<void>;
|
|
494
|
+
shouldHaveUrl(expectedUrl: string, options?: AssertionOptions): Promise<void>;
|
|
457
495
|
/**
|
|
458
496
|
* Asserts that current browser URL contains the specified substring.
|
|
459
497
|
*
|
|
@@ -463,7 +501,7 @@ export interface BrowserReceiverAssertions {
|
|
|
463
501
|
* await Spectra.browser.shouldContainUrl('/dashboard');
|
|
464
502
|
* ```
|
|
465
503
|
*/
|
|
466
|
-
shouldContainUrl(expectedSubstr: string): Promise<void>;
|
|
504
|
+
shouldContainUrl(expectedSubstr: string, options?: AssertionOptions): Promise<void>;
|
|
467
505
|
/**
|
|
468
506
|
* Asserts that current page title exactly matches the expected title.
|
|
469
507
|
*
|
|
@@ -473,7 +511,7 @@ export interface BrowserReceiverAssertions {
|
|
|
473
511
|
* await Spectra.browser.shouldHaveTitle('Dashboard - TestSpectra');
|
|
474
512
|
* ```
|
|
475
513
|
*/
|
|
476
|
-
shouldHaveTitle(expectedTitle: string): Promise<void>;
|
|
514
|
+
shouldHaveTitle(expectedTitle: string, options?: AssertionOptions): Promise<void>;
|
|
477
515
|
/**
|
|
478
516
|
* Asserts that current page title contains the specified substring.
|
|
479
517
|
*
|
|
@@ -483,7 +521,7 @@ export interface BrowserReceiverAssertions {
|
|
|
483
521
|
* await Spectra.browser.shouldContainTitle('Dashboard');
|
|
484
522
|
* ```
|
|
485
523
|
*/
|
|
486
|
-
shouldContainTitle(expectedSubstr: string): Promise<void>;
|
|
524
|
+
shouldContainTitle(expectedSubstr: string, options?: AssertionOptions): Promise<void>;
|
|
487
525
|
/**
|
|
488
526
|
* Asserts that the browser document `readyState` is 'complete' or 'interactive'.
|
|
489
527
|
*
|
|
@@ -492,7 +530,7 @@ export interface BrowserReceiverAssertions {
|
|
|
492
530
|
* await Spectra.browser.shouldBeLoaded();
|
|
493
531
|
* ```
|
|
494
532
|
*/
|
|
495
|
-
shouldBeLoaded(): Promise<void>;
|
|
533
|
+
shouldBeLoaded(options?: AssertionOptions): Promise<void>;
|
|
496
534
|
/**
|
|
497
535
|
* Asserts that no severe or unhandled JavaScript errors occurred in the browser console.
|
|
498
536
|
*
|
|
@@ -501,7 +539,7 @@ export interface BrowserReceiverAssertions {
|
|
|
501
539
|
* await Spectra.browser.shouldHaveNoConsoleErrors();
|
|
502
540
|
* ```
|
|
503
541
|
*/
|
|
504
|
-
shouldHaveNoConsoleErrors(): Promise<void>;
|
|
542
|
+
shouldHaveNoConsoleErrors(options?: AssertionOptions): Promise<void>;
|
|
505
543
|
/**
|
|
506
544
|
* Clears all browser cookies for the active domain.
|
|
507
545
|
*
|
|
@@ -511,6 +549,28 @@ export interface BrowserReceiverAssertions {
|
|
|
511
549
|
* ```
|
|
512
550
|
*/
|
|
513
551
|
clearCookies(): Promise<void>;
|
|
552
|
+
/**
|
|
553
|
+
* Applies one or more raw `Set-Cookie` header values — exactly as received from a `fetch()`
|
|
554
|
+
* response, e.g. `response.headers.getSetCookie()` — to the browser's cookie jar. Operates at
|
|
555
|
+
* the CDP `Network` domain level rather than through `document.cookie`, so `HttpOnly` cookies
|
|
556
|
+
* are fully supported (set, not just read-blocked). Useful for seeding an authenticated session
|
|
557
|
+
* by logging in via a direct API call instead of driving the real login UI — see
|
|
558
|
+
* `docs/v2/features/authentication-and-session-seeding.md`.
|
|
559
|
+
*
|
|
560
|
+
* `url` is required to resolve `Domain`/`Path`/`Secure` defaults for any `Set-Cookie` value that
|
|
561
|
+
* doesn't specify them explicitly (an omitted `Domain` defaults to the issuing request's own
|
|
562
|
+
* host, per RFC 6265) — pass the URL the response actually came from.
|
|
563
|
+
*
|
|
564
|
+
* Web only — Android has no browser/cookie-jar concept; see the docs above for the mobile
|
|
565
|
+
* equivalent (deep-link-triggered, Keystore-backed session seeding).
|
|
566
|
+
*
|
|
567
|
+
* @example
|
|
568
|
+
* ```ts
|
|
569
|
+
* const res = await fetch('https://api.example.com/auth/login', { method: 'POST', body: ... });
|
|
570
|
+
* await Spectra.browser.setCookies(res.headers.getSetCookie(), res.url);
|
|
571
|
+
* ```
|
|
572
|
+
*/
|
|
573
|
+
setCookies(setCookieHeaders: string | string[], url: string): Promise<void>;
|
|
514
574
|
/**
|
|
515
575
|
* Clears all key-value entries in browser `localStorage`.
|
|
516
576
|
*
|
|
@@ -529,6 +589,29 @@ export interface SingleElementProxy extends ElementReceiverAssertions<Promise<vo
|
|
|
529
589
|
selector: string;
|
|
530
590
|
/** Positional index when matched from a collection (or null for standalone selectors). */
|
|
531
591
|
index: number | null;
|
|
592
|
+
/**
|
|
593
|
+
* Finds `childSelector` scoped to this element's subtree, mirroring Playwright's locator
|
|
594
|
+
* chaining (`parent.locator(child)`) instead of a separate `within()`/`findWithin()` verb.
|
|
595
|
+
* Resolution is a real DOM descendant query on web; on Android (no ancestor API) it's a
|
|
596
|
+
* bounds-containment heuristic over the flat accessibility-tree dump.
|
|
597
|
+
*
|
|
598
|
+
* @example
|
|
599
|
+
* ```ts
|
|
600
|
+
* const modal = Spectra.get('#modal');
|
|
601
|
+
* await modal.get('#save-btn').click();
|
|
602
|
+
* ```
|
|
603
|
+
*/
|
|
604
|
+
get(childSelector: string, index?: number | null): SingleElementProxy;
|
|
605
|
+
/**
|
|
606
|
+
* Finds all elements matching `childSelector` scoped to this element's subtree — the
|
|
607
|
+
* collection equivalent of `get()`.
|
|
608
|
+
*
|
|
609
|
+
* @example
|
|
610
|
+
* ```ts
|
|
611
|
+
* await Spectra.get('#modal').getAll('.list-item').shouldHaveLength(3);
|
|
612
|
+
* ```
|
|
613
|
+
*/
|
|
614
|
+
getAll(childSelector: string): CollectionProxy;
|
|
532
615
|
/**
|
|
533
616
|
* Waits until the element exists in the DOM within the specified timeout.
|
|
534
617
|
*
|
|
@@ -794,10 +877,15 @@ export interface CollectionProxy extends CollectionReceiverAssertions<Promise<vo
|
|
|
794
877
|
/**
|
|
795
878
|
* Returns a `SingleElementProxy` pointing to the matching element at the specified zero-based index.
|
|
796
879
|
*
|
|
880
|
+
* When this collection itself came from a `.getAll()` chain, `first()`/`last()`/`nth()` carry
|
|
881
|
+
* that scope forward instead of reverting to an unscoped lookup — e.g. the button below is
|
|
882
|
+
* resolved within the 3rd `.card`, not just anywhere on the page:
|
|
883
|
+
*
|
|
797
884
|
* @param index Zero-based index of the target element.
|
|
798
885
|
* @example
|
|
799
886
|
* ```ts
|
|
800
887
|
* await Spectra.getAll('.list-item').nth(2).click();
|
|
888
|
+
* await Spectra.getAll('.card').nth(2).get('.buy-btn').click();
|
|
801
889
|
* ```
|
|
802
890
|
*/
|
|
803
891
|
nth(index: number): SingleElementProxy;
|
|
@@ -811,15 +899,20 @@ export type SpectraCollection = CollectionProxy;
|
|
|
811
899
|
*/
|
|
812
900
|
export interface SpectraBrowserBridge extends BrowserReceiverAssertions {
|
|
813
901
|
/**
|
|
814
|
-
*
|
|
902
|
+
* All network requests recorded so far this session — both genuine (non-intercepted) traffic
|
|
903
|
+
* and requests an active `intercept()` mock fulfilled. Recording is always on; nothing needs
|
|
904
|
+
* to be explicitly enabled first. On Android, only requests routed through the worker's local
|
|
905
|
+
* mock proxy are recorded (see `Spectra.intercept`'s docs on why mobile needs an absolute URL
|
|
906
|
+
* rather than a relative path) — a request the app makes that never reaches the proxy at all
|
|
907
|
+
* won't appear here.
|
|
815
908
|
*
|
|
816
|
-
* @param targetUrl Target destination URL.
|
|
817
909
|
* @example
|
|
818
910
|
* ```ts
|
|
819
|
-
* await Spectra.
|
|
911
|
+
* await Spectra.get('~fetch-api-btn').click();
|
|
912
|
+
* const entry = Spectra.browser.recordedNetwork.find((e) => e.url.includes('/posts'));
|
|
820
913
|
* ```
|
|
821
914
|
*/
|
|
822
|
-
|
|
915
|
+
recordedNetwork: CDPNetworkEntry[];
|
|
823
916
|
/**
|
|
824
917
|
* Retrieves the current browser URL.
|
|
825
918
|
*
|
|
@@ -838,54 +931,6 @@ export interface SpectraBrowserBridge extends BrowserReceiverAssertions {
|
|
|
838
931
|
* ```
|
|
839
932
|
*/
|
|
840
933
|
getTitle(): Promise<string>;
|
|
841
|
-
/**
|
|
842
|
-
* Navigates one step back in the browser history.
|
|
843
|
-
*
|
|
844
|
-
* @example
|
|
845
|
-
* ```ts
|
|
846
|
-
* await Spectra.browser.back();
|
|
847
|
-
* ```
|
|
848
|
-
*/
|
|
849
|
-
back(): Promise<void>;
|
|
850
|
-
/**
|
|
851
|
-
* Navigates one step forward in the browser history.
|
|
852
|
-
*
|
|
853
|
-
* @example
|
|
854
|
-
* ```ts
|
|
855
|
-
* await Spectra.browser.forward();
|
|
856
|
-
* ```
|
|
857
|
-
*/
|
|
858
|
-
forward(): Promise<void>;
|
|
859
|
-
/**
|
|
860
|
-
* Reloads / refreshes the current active page.
|
|
861
|
-
*
|
|
862
|
-
* @example
|
|
863
|
-
* ```ts
|
|
864
|
-
* await Spectra.browser.refresh();
|
|
865
|
-
* ```
|
|
866
|
-
*/
|
|
867
|
-
refresh(): Promise<void>;
|
|
868
|
-
/**
|
|
869
|
-
* Pauses test execution for the specified number of milliseconds.
|
|
870
|
-
*
|
|
871
|
-
* @param ms Duration in milliseconds to pause.
|
|
872
|
-
* @example
|
|
873
|
-
* ```ts
|
|
874
|
-
* await Spectra.browser.pause(1000);
|
|
875
|
-
* ```
|
|
876
|
-
*/
|
|
877
|
-
pause(ms: number): Promise<void>;
|
|
878
|
-
/**
|
|
879
|
-
* Sets the browser viewport dimensions (width and height in pixels).
|
|
880
|
-
*
|
|
881
|
-
* @param width Viewport width in pixels.
|
|
882
|
-
* @param height Viewport height in pixels.
|
|
883
|
-
* @example
|
|
884
|
-
* ```ts
|
|
885
|
-
* await Spectra.browser.setViewport(1920, 1080);
|
|
886
|
-
* ```
|
|
887
|
-
*/
|
|
888
|
-
setViewport(width: number, height: number): Promise<void>;
|
|
889
934
|
/**
|
|
890
935
|
* Executes a JavaScript function or script snippet in the browser context and returns the result.
|
|
891
936
|
*
|
|
@@ -933,32 +978,41 @@ export interface SpectraBrowserBridge extends BrowserReceiverAssertions {
|
|
|
933
978
|
level: string;
|
|
934
979
|
message: string;
|
|
935
980
|
}>>;
|
|
936
|
-
/**
|
|
937
|
-
* Intercepts and mocks HTTP network requests matching the specified URL pattern.
|
|
938
|
-
*
|
|
939
|
-
* @param pattern URL substring or glob pattern to intercept.
|
|
940
|
-
* @param method HTTP method (GET, POST, PUT, DELETE, etc.).
|
|
941
|
-
* @param fixture Mock response payload object or string.
|
|
942
|
-
* @param options Mock response options (status code, custom headers).
|
|
943
|
-
* @example
|
|
944
|
-
* ```ts
|
|
945
|
-
* const mock = await Spectra.browser.intercept('/api/v1/users', 'GET', [{ id: 1, name: 'Alice' }], {
|
|
946
|
-
* statusCode: 200,
|
|
947
|
-
* });
|
|
948
|
-
* ```
|
|
949
|
-
*/
|
|
950
|
-
intercept(pattern: string, method?: string, fixture?: unknown, options?: {
|
|
951
|
-
statusCode?: number;
|
|
952
|
-
headers?: Record<string, string>;
|
|
953
|
-
}): Promise<MockInterceptHandle>;
|
|
954
981
|
}
|
|
955
982
|
/**
|
|
956
|
-
*
|
|
983
|
+
* Intercepted HTTP request metadata captured during test execution.
|
|
984
|
+
*/
|
|
985
|
+
export interface InterceptedRequest {
|
|
986
|
+
/** Optional unique identifier for the request */
|
|
987
|
+
id?: string;
|
|
988
|
+
/** Target request URL */
|
|
989
|
+
url: string;
|
|
990
|
+
/** HTTP method */
|
|
991
|
+
method: string;
|
|
992
|
+
/** Request headers */
|
|
993
|
+
headers?: Record<string, string>;
|
|
994
|
+
/** Request body / payload if available */
|
|
995
|
+
postData?: string;
|
|
996
|
+
/** Timestamp when intercepted */
|
|
997
|
+
timestamp: number;
|
|
998
|
+
}
|
|
999
|
+
/**
|
|
1000
|
+
* Queued one-time mock response item for FIFO polling and retry flows.
|
|
1001
|
+
*/
|
|
1002
|
+
export interface QueuedMockResponse {
|
|
1003
|
+
response: unknown;
|
|
1004
|
+
statusCode: number;
|
|
1005
|
+
headers?: Record<string, string>;
|
|
1006
|
+
/** Milliseconds to wait before fulfilling this response — simulates a late/slow network reply. */
|
|
1007
|
+
delayMs?: number;
|
|
1008
|
+
}
|
|
1009
|
+
/**
|
|
1010
|
+
* Mock rule configuration for CDP & Mobile network request interception.
|
|
957
1011
|
*/
|
|
958
1012
|
export interface MockRule {
|
|
959
1013
|
/** URL pattern or substring to match. */
|
|
960
1014
|
pattern: string;
|
|
961
|
-
/** HTTP method (e.g. GET, POST). */
|
|
1015
|
+
/** HTTP method (e.g. GET, POST, ALL). */
|
|
962
1016
|
method: string;
|
|
963
1017
|
/** Mock response payload. */
|
|
964
1018
|
response: unknown;
|
|
@@ -966,15 +1020,30 @@ export interface MockRule {
|
|
|
966
1020
|
statusCode: number;
|
|
967
1021
|
/** Custom HTTP response headers. */
|
|
968
1022
|
headers?: Record<string, string>;
|
|
1023
|
+
/** Milliseconds to wait before fulfilling the default response — simulates a late/slow reply. */
|
|
1024
|
+
delayMs?: number;
|
|
969
1025
|
/** Total times this mock rule matched and intercepted requests. */
|
|
970
1026
|
callCount: number;
|
|
1027
|
+
/** FIFO queue of one-time responses (respondOnce) */
|
|
1028
|
+
respondOnceQueue: QueuedMockResponse[];
|
|
1029
|
+
/** Whether requests matching this rule should be aborted / failed */
|
|
1030
|
+
aborted?: boolean;
|
|
1031
|
+
/** Specific error code for network abort simulation */
|
|
1032
|
+
abortReason?: 'Failed' | 'Aborted' | 'TimedOut' | 'ConnectionReset';
|
|
1033
|
+
/** Recorded list of intercepted requests matching this rule */
|
|
1034
|
+
calls: InterceptedRequest[];
|
|
1035
|
+
/** Internal pending waitForCall resolvers (keyed by expected call count). */
|
|
1036
|
+
waitResolvers: Array<{
|
|
1037
|
+
count: number;
|
|
1038
|
+
resolve: (req: InterceptedRequest) => void;
|
|
1039
|
+
}>;
|
|
971
1040
|
}
|
|
972
1041
|
/**
|
|
973
1042
|
* Handle returned by `Spectra.intercept()` to dynamically inspect and update mock responses.
|
|
974
1043
|
*/
|
|
975
1044
|
export interface MockInterceptHandle {
|
|
976
1045
|
/**
|
|
977
|
-
* Dynamically updates the response payload for this active mock rule.
|
|
1046
|
+
* Dynamically updates the default response payload for this active mock rule.
|
|
978
1047
|
*
|
|
979
1048
|
* @param newFixture New response payload object or string.
|
|
980
1049
|
* @param newOptions Optional status code and header overrides.
|
|
@@ -982,11 +1051,42 @@ export interface MockInterceptHandle {
|
|
|
982
1051
|
respondWith: (newFixture: unknown, newOptions?: {
|
|
983
1052
|
statusCode?: number;
|
|
984
1053
|
headers?: Record<string, string>;
|
|
1054
|
+
delayMs?: number;
|
|
1055
|
+
}) => Promise<void>;
|
|
1056
|
+
/**
|
|
1057
|
+
* Queues a one-time mock response for the next matching request (FIFO queue for polling/retries).
|
|
1058
|
+
*
|
|
1059
|
+
* @param newFixture One-time response payload object or string.
|
|
1060
|
+
* @param newOptions Optional status code and header overrides.
|
|
1061
|
+
*/
|
|
1062
|
+
respondOnce: (newFixture: unknown, newOptions?: {
|
|
1063
|
+
statusCode?: number;
|
|
1064
|
+
headers?: Record<string, string>;
|
|
1065
|
+
delayMs?: number;
|
|
985
1066
|
}) => Promise<void>;
|
|
1067
|
+
/**
|
|
1068
|
+
* Simulates a network failure or connection abort for matching requests.
|
|
1069
|
+
*
|
|
1070
|
+
* @param errorCode Network failure reason (default: 'Failed').
|
|
1071
|
+
*/
|
|
1072
|
+
abort: (errorCode?: 'Failed' | 'Aborted' | 'TimedOut' | 'ConnectionReset') => Promise<void>;
|
|
1073
|
+
/**
|
|
1074
|
+
* Awaits until the mock rule has intercepted at least `count` matching requests.
|
|
1075
|
+
*
|
|
1076
|
+
* @param options Timeout and expected request count.
|
|
1077
|
+
*/
|
|
1078
|
+
waitForCall: (options?: {
|
|
1079
|
+
timeout?: number;
|
|
1080
|
+
count?: number;
|
|
1081
|
+
}) => Promise<InterceptedRequest>;
|
|
986
1082
|
/**
|
|
987
1083
|
* Returns the number of times this mock intercepted network requests.
|
|
988
1084
|
*/
|
|
989
|
-
callCount: () => number;
|
|
1085
|
+
callCount: (() => number) & number;
|
|
1086
|
+
/**
|
|
1087
|
+
* Historical array of all intercepted requests matching this rule.
|
|
1088
|
+
*/
|
|
1089
|
+
calls: InterceptedRequest[];
|
|
990
1090
|
}
|
|
991
1091
|
/**
|
|
992
1092
|
* Audit log entry for captured CDP network requests.
|
|
@@ -1047,12 +1147,16 @@ export interface SpectraStatic {
|
|
|
1047
1147
|
*/
|
|
1048
1148
|
getAll(selector: string): CollectionProxy;
|
|
1049
1149
|
/**
|
|
1050
|
-
* Navigates the active browser window
|
|
1150
|
+
* Navigates the active browser window to the specified URL. On Android, deep-links directly
|
|
1151
|
+
* into the app via `adb shell am start` instead — pass a full URI matching a scheme the app
|
|
1152
|
+
* registers (e.g. `expo-router`'s `scheme` in `app.json`), not a bare path, since there's no
|
|
1153
|
+
* configured base scheme to combine one against.
|
|
1051
1154
|
*
|
|
1052
|
-
* @param url Absolute or relative URL
|
|
1155
|
+
* @param url Absolute or relative URL on web; a full deep-link URI on Android.
|
|
1053
1156
|
* @example
|
|
1054
1157
|
* ```ts
|
|
1055
|
-
* await Spectra.navigate('/dashboard');
|
|
1158
|
+
* await Spectra.navigate('/dashboard'); // web
|
|
1159
|
+
* await Spectra.navigate('testspectra-demo://permission-rationale'); // Android
|
|
1056
1160
|
* ```
|
|
1057
1161
|
*/
|
|
1058
1162
|
navigate(url: string): Promise<void>;
|
|
@@ -1241,6 +1345,20 @@ export interface SpectraStatic {
|
|
|
1241
1345
|
* ```
|
|
1242
1346
|
*/
|
|
1243
1347
|
pressKey(key: KeyOption | string): Promise<void>;
|
|
1348
|
+
/**
|
|
1349
|
+
* Grants an Android runtime permission on demand — typically called right after confirming an
|
|
1350
|
+
* in-app rationale dialog, so a test can exercise its own permission-request UX instead of
|
|
1351
|
+
* having every permission pre-granted before the app even launches. No-op on platforms without
|
|
1352
|
+
* an OS-level runtime permission model (e.g. web).
|
|
1353
|
+
*
|
|
1354
|
+
* @param name Fully-qualified Android permission name (e.g. 'android.permission.CAMERA').
|
|
1355
|
+
* @example
|
|
1356
|
+
* ```ts
|
|
1357
|
+
* await Spectra.get('~rationale-allow-btn').click();
|
|
1358
|
+
* await Spectra.grantPermission('android.permission.CAMERA');
|
|
1359
|
+
* ```
|
|
1360
|
+
*/
|
|
1361
|
+
grantPermission(name: string): Promise<void>;
|
|
1244
1362
|
/**
|
|
1245
1363
|
* Pauses test execution for the specified number of milliseconds.
|
|
1246
1364
|
*
|
|
@@ -1266,6 +1384,18 @@ export interface SpectraStatic {
|
|
|
1266
1384
|
* Direct access to browser context commands and page assertions.
|
|
1267
1385
|
*/
|
|
1268
1386
|
browser: SpectraBrowserBridge;
|
|
1387
|
+
/**
|
|
1388
|
+
* Typed environment variables from `spectra.config.ts`'s `executionConfig.environmentVariables`.
|
|
1389
|
+
* Each configured key is generated into `.testspectra/types/env.d.ts` as a `SpectraEnv` member
|
|
1390
|
+
* (TypeScript interface merging), so `Spectra.env.MY_KEY` resolves to `string` — never
|
|
1391
|
+
* `string | undefined` like raw `process.env.MY_KEY` would.
|
|
1392
|
+
*
|
|
1393
|
+
* @example
|
|
1394
|
+
* ```ts
|
|
1395
|
+
* const mode = Spectra.env.API_MODE;
|
|
1396
|
+
* ```
|
|
1397
|
+
*/
|
|
1398
|
+
env: SpectraEnv;
|
|
1269
1399
|
/**
|
|
1270
1400
|
* Intercepts and mocks HTTP network requests matching the specified pattern or options.
|
|
1271
1401
|
*
|
|
@@ -1285,6 +1415,7 @@ export interface SpectraStatic {
|
|
|
1285
1415
|
}, method?: string, fixture?: unknown, options?: {
|
|
1286
1416
|
statusCode?: number;
|
|
1287
1417
|
headers?: Record<string, string>;
|
|
1418
|
+
delayMs?: number;
|
|
1288
1419
|
}): Promise<MockInterceptHandle>;
|
|
1289
1420
|
/**
|
|
1290
1421
|
* Clears and resets all active CDP network interception rules.
|