@testspectra/matchers 1.0.70 → 1.1.0-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.
Files changed (51) hide show
  1. package/LICENSE.md +48 -0
  2. package/dist/__tests__/intercept.test.d.ts +1 -0
  3. package/dist/__tests__/intercept.test.js +183 -0
  4. package/dist/__tests__/matcher-contracts.test.d.ts +1 -0
  5. package/dist/__tests__/matcher-contracts.test.js +498 -0
  6. package/dist/__tests__/matchers.test.d.ts +1 -0
  7. package/dist/__tests__/matchers.test.js +161 -0
  8. package/dist/contract.d.ts +173 -0
  9. package/dist/contract.js +10 -0
  10. package/dist/index.d.ts +4 -21
  11. package/dist/index.js +4 -21
  12. package/dist/intercept/cdp-handler.d.ts +22 -0
  13. package/dist/intercept/cdp-handler.js +123 -0
  14. package/dist/intercept/index.d.ts +4 -0
  15. package/dist/intercept/index.js +4 -0
  16. package/dist/intercept/mock-handle.d.ts +49 -0
  17. package/dist/intercept/mock-handle.js +124 -0
  18. package/dist/intercept/mock-registry.d.ts +18 -0
  19. package/dist/intercept/mock-registry.js +97 -0
  20. package/dist/intercept/types.d.ts +43 -0
  21. package/dist/intercept/types.js +1 -0
  22. package/dist/matchers.d.ts +15 -4
  23. package/dist/matchers.js +609 -81
  24. package/dist/proto.d.ts +26 -283
  25. package/dist/proto.js +1 -114
  26. package/dist/reporter.d.ts +30 -0
  27. package/dist/reporter.js +97 -0
  28. package/dist/runner/collection.d.ts +33 -20
  29. package/dist/runner/collection.js +104 -26
  30. package/dist/runner/single.d.ts +125 -34
  31. package/dist/runner/single.js +290 -55
  32. package/dist/semantic.d.ts +11 -0
  33. package/dist/semantic.js +141 -0
  34. package/dist/spectra.d.ts +32 -8
  35. package/dist/spectra.js +157 -40
  36. package/dist/types.d.ts +1322 -24
  37. package/package.json +13 -8
  38. package/src/contract.ts +223 -0
  39. package/src/index.ts +4 -22
  40. package/src/proto.ts +30 -433
  41. package/src/runtime/assertions.ts +453 -0
  42. package/src/runtime/element_actions.ts +178 -0
  43. package/src/runtime/element_proxy.ts +200 -0
  44. package/src/runtime/element_state.ts +94 -0
  45. package/src/runtime/spectra.ts +110 -0
  46. package/src/types.ts +1526 -92
  47. package/tsconfig.json +2 -2
  48. package/src/matchers.ts +0 -146
  49. package/src/runner/collection.ts +0 -153
  50. package/src/runner/single.ts +0 -301
  51. package/src/spectra.ts +0 -376
package/dist/types.d.ts CHANGED
@@ -2,12 +2,12 @@
2
2
  * Canonical action keys supported across TestSpectra runner and database models.
3
3
  * @see backend/src/models/test_step.rs
4
4
  */
5
- export type ActionKey = "navigate" | "click" | "type" | "clear" | "select" | "scroll" | "swipe" | "wait" | "waitForElement" | "pressKey" | "longPress" | "doubleClick" | "hover" | "dragDrop" | "back" | "refresh";
5
+ export type ActionKey = 'navigate' | 'click' | 'type' | 'clear' | 'select' | 'scroll' | 'swipe' | 'wait' | 'waitForElement' | 'pressKey' | 'longPress' | 'doubleClick' | 'hover' | 'dragDrop' | 'back' | 'refresh';
6
6
  /**
7
7
  * Canonical assertion keys supported across TestSpectra runner and database models.
8
8
  * @see backend/src/models/test_step.rs
9
9
  */
10
- export type AssertionKey = "elementDisplayed" | "elementNotDisplayed" | "elementExists" | "elementEnabled" | "elementDisabled" | "textContains" | "textEquals" | "textNotContains" | "urlContains" | "urlEquals" | "valueEquals" | "valueContains" | "attributeEquals" | "attributeContains" | "hasClass" | "hasAttribute" | "isSelected" | "elementCount" | "elementCountGreaterThan" | "pageLoaded" | "noErrors";
10
+ export type AssertionKey = 'elementDisplayed' | 'elementNotDisplayed' | 'elementExists' | 'elementNotExists' | 'elementClickable' | 'elementNotClickable' | 'elementEnabled' | 'elementDisabled' | 'elementChecked' | 'elementNotChecked' | 'elementFocused' | 'elementNotFocused' | 'textEquals' | 'textNotEquals' | 'textContains' | 'textNotContains' | 'valueEquals' | 'valueNotEquals' | 'valueContains' | 'valueNotContains' | 'attributeEquals' | 'attributeNotEquals' | 'hasClass' | 'notHasClass' | 'hasCss' | 'notHasCss' | 'collectionLengthEquals' | 'collectionLengthNotEquals' | 'collectionLengthGreaterThan' | 'collectionLengthLessThan' | 'collectionEmpty' | 'collectionNotEmpty' | 'urlEquals' | 'urlContains' | 'titleEquals' | 'titleContains' | 'pageLoaded' | 'noConsoleErrors';
11
11
  /**
12
12
  * Supported keyboard key names for `Spectra.pressKey(key)`.
13
13
  *
@@ -17,15 +17,15 @@ export type AssertionKey = "elementDisplayed" | "elementNotDisplayed" | "element
17
17
  * await Spectra.pressKey("Tab");
18
18
  * ```
19
19
  */
20
- export type KeyOption = "Enter" | "Tab" | "Escape" | "Backspace" | "Delete" | "ArrowUp" | "ArrowDown" | "ArrowLeft" | "ArrowRight" | "Space";
20
+ export type KeyOption = 'Enter' | 'Tab' | 'Escape' | 'Backspace' | 'Delete' | 'ArrowUp' | 'ArrowDown' | 'ArrowLeft' | 'ArrowRight' | 'Space';
21
21
  /**
22
22
  * Cardinal directions for gestures such as swipe and scroll.
23
23
  */
24
- export type Direction = "up" | "down" | "left" | "right";
24
+ export type Direction = 'up' | 'down' | 'left' | 'right';
25
25
  /**
26
26
  * Target reference for locating an element.
27
- * Can be a CSS/XPath selector string, a resolved `WebdriverIO.Element`,
28
- * or a chainable element promise `ChainablePromiseElement`.
27
+ * Can be a CSS/XPath selector string, a resolved `SpectraElement` proxy,
28
+ * or a chainable element promise `Promise<SpectraElement>`.
29
29
  *
30
30
  * @example
31
31
  * ```ts
@@ -36,7 +36,26 @@ export type Direction = "up" | "down" | "left" | "right";
36
36
  * Spectra.get(LoginPage.submitButton);
37
37
  * ```
38
38
  */
39
- export type ElementTarget = string | WebdriverIO.Element | ChainablePromiseElement;
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
  *
@@ -91,7 +110,7 @@ export interface ClickOptions {
91
110
  /** Optional inner text filter */
92
111
  text?: string;
93
112
  /** Click type: standard single click or double click */
94
- clickType?: "single" | "double";
113
+ clickType?: 'single' | 'double';
95
114
  }
96
115
  /**
97
116
  * Configuration options for typing input into form elements.
@@ -106,26 +125,1305 @@ export interface TypeOptions {
106
125
  clearFirst?: boolean;
107
126
  }
108
127
  /**
109
- * Fluent assertion matcher strings for single element verification.
110
- * Used in `Spectra.get(target).should(matcher, ...args)`.
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.
111
135
  *
112
136
  * @example
113
137
  * ```ts
114
- * await Spectra.get("#username").should("be.visible");
115
- * await Spectra.get("#greeting").should("have.text", "Welcome back");
116
- * await Spectra.get("#status").should("have.class", "active");
138
+ * const mock = await Spectra.intercept('/api/report', { response: { delayMs: 4000, body } });
139
+ * await ReportPage.statusBadge.shouldHaveText('Ready', { timeoutMs: 5000 });
117
140
  * ```
118
141
  */
119
- export type SingleElementMatcher = "be.visible" | "not.be.visible" | "exist" | "not.exist" | "be.enabled" | "be.disabled" | "be.checked" | "not.be.checked" | "be.selected" | "not.be.selected" | "have.value" | "contain.value" | "have.text" | "contain.text" | "have.class" | "have.attr" | "have.url" | "contain.url" | "have.title" | "contain.title";
142
+ export interface AssertionOptions {
143
+ /** Maximum time (in milliseconds) to keep polling before the assertion fails. */
144
+ timeoutMs?: number;
145
+ }
120
146
  /**
121
- * Fluent assertion matcher strings for multi-element collections.
122
- * Used in `Spectra.getAll(selector).should(matcher, ...args)`.
123
- *
124
- * @example
125
- * ```ts
126
- * await Spectra.getAll(".product-item").should("have.length", 4);
127
- * await Spectra.getAll(".product-item").should("have.length.greaterThan", 0);
128
- * await Spectra.getAll(".badge").should("not.be.empty");
129
- * ```
147
+ * Canonical assertion matcher keys for single element verification.
148
+ * Dispatched internally by semantic receiver methods (`.shouldBeVisible()`, `.shouldHaveText()`, etc.).
149
+ */
150
+ export type SingleElementMatcher = 'be.visible' | 'not.be.visible' | 'exist' | 'not.exist' | 'be.clickable' | 'not.be.clickable' | 'be.enabled' | 'be.disabled' | 'be.checked' | 'not.be.checked' | 'be.selected' | 'not.be.selected' | 'be.focused' | 'not.be.focused' | 'have.value' | 'not.have.value' | 'contain.value' | 'not.contain.value' | 'have.text' | 'not.have.text' | 'contain.text' | 'not.contain.text' | 'have.class' | 'not.have.class' | 'have.attr' | 'not.have.attr' | 'have.css' | 'not.have.css' | 'have.url' | 'contain.url' | 'have.title' | 'contain.title';
151
+ /**
152
+ * Canonical assertion matcher keys for multi-element collections.
153
+ * Dispatched internally by semantic collection methods (`.shouldHaveLength()`, `.shouldBeEmpty()`, etc.).
154
+ */
155
+ export type MultiElementMatcher = 'have.length' | 'not.have.length' | 'have.length.greaterThan' | 'have.length.lessThan' | 'be.empty' | 'not.be.empty' | 'exist';
156
+ /**
157
+ * Receiver-oriented element assertion methods.
158
+ * Attached directly to SingleElementProxy instances.
159
+ */
160
+ export interface ElementReceiverAssertions<TReturn = Promise<void>> {
161
+ /**
162
+ * Asserts that the target element is visible and displayed on the page.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * await Spectra.get('#submit-btn').shouldBeVisible();
167
+ * await LoginPage.submitButton.shouldBeVisible();
168
+ * ```
169
+ */
170
+ shouldBeVisible(options?: AssertionOptions): TReturn;
171
+ /**
172
+ * Asserts that the target element is hidden, detached, or not displayed on the page.
173
+ *
174
+ * @example
175
+ * ```ts
176
+ * await Spectra.get('.loading-spinner').shouldNotBeVisible();
177
+ * ```
178
+ */
179
+ shouldNotBeVisible(options?: AssertionOptions): TReturn;
180
+ /**
181
+ * Asserts that the target element exists in the DOM.
182
+ *
183
+ * @example
184
+ * ```ts
185
+ * await Spectra.get('#cookie-consent-modal').shouldExist();
186
+ * ```
187
+ */
188
+ shouldExist(options?: AssertionOptions): TReturn;
189
+ /**
190
+ * Asserts that the target element does not exist in the DOM.
191
+ *
192
+ * @example
193
+ * ```ts
194
+ * await Spectra.get('#deleted-record-row').shouldNotExist();
195
+ * ```
196
+ */
197
+ shouldNotExist(options?: AssertionOptions): TReturn;
198
+ /**
199
+ * Asserts that the target element is visible, enabled, and clickable.
200
+ *
201
+ * @example
202
+ * ```ts
203
+ * await Spectra.get('button[type="submit"]').shouldBeClickable();
204
+ * ```
205
+ */
206
+ shouldBeClickable(options?: AssertionOptions): TReturn;
207
+ /**
208
+ * Asserts that the target element is disabled, covered, or not clickable.
209
+ *
210
+ * @example
211
+ * ```ts
212
+ * await Spectra.get('button.disabled-action').shouldNotBeClickable();
213
+ * ```
214
+ */
215
+ shouldNotBeClickable(options?: AssertionOptions): TReturn;
216
+ /**
217
+ * Asserts that the target form input/button is enabled (not disabled).
218
+ *
219
+ * @example
220
+ * ```ts
221
+ * await Spectra.get('#username-field').shouldBeEnabled();
222
+ * ```
223
+ */
224
+ shouldBeEnabled(options?: AssertionOptions): TReturn;
225
+ /**
226
+ * Asserts that the target form input/button has the disabled state/attribute.
227
+ *
228
+ * @example
229
+ * ```ts
230
+ * await Spectra.get('#submit-order-btn').shouldBeDisabled();
231
+ * ```
232
+ */
233
+ shouldBeDisabled(options?: AssertionOptions): TReturn;
234
+ /**
235
+ * Asserts that the target checkbox or radio input is checked/selected.
236
+ *
237
+ * @example
238
+ * ```ts
239
+ * await Spectra.get('#terms-checkbox').shouldBeChecked();
240
+ * ```
241
+ */
242
+ shouldBeChecked(options?: AssertionOptions): TReturn;
243
+ /**
244
+ * Asserts that the target checkbox or radio input is unchecked/deselected.
245
+ *
246
+ * @example
247
+ * ```ts
248
+ * await Spectra.get('#subscribe-newsletter').shouldNotBeChecked();
249
+ * ```
250
+ */
251
+ shouldNotBeChecked(options?: AssertionOptions): TReturn;
252
+ /**
253
+ * Asserts that the target element currently holds active document focus.
254
+ *
255
+ * @example
256
+ * ```ts
257
+ * await Spectra.get('#search-input').shouldBeFocused();
258
+ * ```
259
+ */
260
+ shouldBeFocused(options?: AssertionOptions): TReturn;
261
+ /**
262
+ * Asserts that the target element does not hold active document focus.
263
+ *
264
+ * @example
265
+ * ```ts
266
+ * await Spectra.get('#blur-input').shouldNotBeFocused();
267
+ * ```
268
+ */
269
+ shouldNotBeFocused(options?: AssertionOptions): TReturn;
270
+ /**
271
+ * Asserts that the target element's text content matches the expected string or regular expression.
272
+ *
273
+ * @param expected Exact string or RegExp pattern to match against element text.
274
+ * @example
275
+ * ```ts
276
+ * await Spectra.get('h1.page-title').shouldHaveText('Dashboard Overview');
277
+ * await Spectra.get('.badge').shouldHaveText(/Active|Pending/);
278
+ * ```
279
+ */
280
+ shouldHaveText(expected: string | RegExp, options?: AssertionOptions): TReturn;
281
+ /**
282
+ * Asserts that the target element's text content does not match the expected string or regular expression.
283
+ *
284
+ * @param expected String or RegExp pattern that the element text must NOT match.
285
+ * @example
286
+ * ```ts
287
+ * await Spectra.get('.status-label').shouldNotHaveText('Error');
288
+ * ```
289
+ */
290
+ shouldNotHaveText(expected: string | RegExp, options?: AssertionOptions): TReturn;
291
+ /**
292
+ * Asserts that the target element's text content contains the specified substring.
293
+ *
294
+ * @param substring Substring expected to be present within the element text.
295
+ * @example
296
+ * ```ts
297
+ * await Spectra.get('.toast-message').shouldContainText('Successfully saved');
298
+ * ```
299
+ */
300
+ shouldContainText(substring: string, options?: AssertionOptions): TReturn;
301
+ /**
302
+ * Asserts that the target element's text content does not contain the specified substring.
303
+ *
304
+ * @param substring Substring that must NOT be present within the element text.
305
+ * @example
306
+ * ```ts
307
+ * await Spectra.get('.log-output').shouldNotContainText('Fatal Exception');
308
+ * ```
309
+ */
310
+ shouldNotContainText(substring: string, options?: AssertionOptions): TReturn;
311
+ /**
312
+ * Asserts that the form input or textarea element's value exactly equals the specified string.
313
+ *
314
+ * @param value Expected input field value.
315
+ * @example
316
+ * ```ts
317
+ * await Spectra.get('input[name="email"]').shouldHaveValue('admin@testspectra.dev');
318
+ * ```
319
+ */
320
+ shouldHaveValue(value: string, options?: AssertionOptions): TReturn;
321
+ /**
322
+ * Asserts that the form input or textarea element's value does not equal the specified string.
323
+ *
324
+ * @param value Value that the input field must NOT equal.
325
+ * @example
326
+ * ```ts
327
+ * await Spectra.get('input[name="role"]').shouldNotHaveValue('guest');
328
+ * ```
329
+ */
330
+ shouldNotHaveValue(value: string, options?: AssertionOptions): TReturn;
331
+ /**
332
+ * Asserts that the form input or textarea element's value contains the specified substring.
333
+ *
334
+ * @param substring Substring expected to be contained within the input value.
335
+ * @example
336
+ * ```ts
337
+ * await Spectra.get('input[name="email"]').shouldContainValue('@testspectra.dev');
338
+ * ```
339
+ */
340
+ shouldContainValue(substring: string, options?: AssertionOptions): TReturn;
341
+ /**
342
+ * Asserts that the form input or textarea element's value does not contain the specified substring.
343
+ *
344
+ * @param substring Substring that must NOT be contained within the input value.
345
+ * @example
346
+ * ```ts
347
+ * await Spectra.get('input[name="url"]').shouldNotContainValue('http://');
348
+ * ```
349
+ */
350
+ shouldNotContainValue(substring: string, options?: AssertionOptions): TReturn;
351
+ /**
352
+ * Asserts that the element has the specified attribute, and optionally that its value matches.
353
+ *
354
+ * @param name Attribute name to inspect.
355
+ * @param value Optional expected attribute value string.
356
+ * @example
357
+ * ```ts
358
+ * await Spectra.get('a.external-link').shouldHaveAttribute('target', '_blank');
359
+ * await Spectra.get('input.required-field').shouldHaveAttribute('required');
360
+ * ```
361
+ */
362
+ shouldHaveAttribute(name: string, value?: string, options?: AssertionOptions): TReturn;
363
+ /**
364
+ * Asserts that the element does not have the specified attribute.
365
+ *
366
+ * @param name Attribute name that must NOT exist on the element.
367
+ * @example
368
+ * ```ts
369
+ * await Spectra.get('button#action-btn').shouldNotHaveAttribute('disabled');
370
+ * ```
371
+ */
372
+ shouldNotHaveAttribute(name: string, options?: AssertionOptions): TReturn;
373
+ /**
374
+ * Asserts that the element contains the specified CSS class name.
375
+ *
376
+ * @param className CSS class name expected in the element's classList.
377
+ * @example
378
+ * ```ts
379
+ * await Spectra.get('.nav-tab').shouldHaveClass('active');
380
+ * ```
381
+ */
382
+ shouldHaveClass(className: string, options?: AssertionOptions): TReturn;
383
+ /**
384
+ * Asserts that the element does not contain the specified CSS class name.
385
+ *
386
+ * @param className CSS class name that must NOT be in the element's classList.
387
+ * @example
388
+ * ```ts
389
+ * await Spectra.get('.modal-backdrop').shouldNotHaveClass('hidden');
390
+ * ```
391
+ */
392
+ shouldNotHaveClass(className: string, options?: AssertionOptions): TReturn;
393
+ /**
394
+ * Asserts that the computed CSS style property of the element equals the specified value.
395
+ *
396
+ * @param property CSS style property name (e.g. 'color', 'display', 'opacity').
397
+ * @param value Expected computed CSS property value.
398
+ * @example
399
+ * ```ts
400
+ * await Spectra.get('.badge-success').shouldHaveCss('color', 'rgb(0, 128, 0)');
401
+ * ```
402
+ */
403
+ shouldHaveCss(property: string, value: string, options?: AssertionOptions): TReturn;
404
+ /**
405
+ * Asserts that the computed CSS style property of the element does not equal the specified value.
406
+ *
407
+ * @param property CSS style property name.
408
+ * @param value Value that the computed CSS property must NOT equal.
409
+ * @example
410
+ * ```ts
411
+ * await Spectra.get('.main-content').shouldNotHaveCss('display', 'none');
412
+ * ```
413
+ */
414
+ shouldNotHaveCss(property: string, value: string, options?: AssertionOptions): TReturn;
415
+ }
416
+ /**
417
+ * Receiver-oriented collection assertion methods.
418
+ * Attached directly to CollectionProxy and collection prototypes.
419
+ */
420
+ export interface CollectionReceiverAssertions<TReturn = Promise<void>> {
421
+ /**
422
+ * Asserts that the collection contains exactly the specified number of matching elements.
423
+ *
424
+ * @param count Expected exact count of elements.
425
+ * @example
426
+ * ```ts
427
+ * await Spectra.getAll('.user-table-row').shouldHaveLength(10);
428
+ * ```
429
+ */
430
+ shouldHaveLength(count: number, options?: AssertionOptions): TReturn;
431
+ /**
432
+ * Asserts that the collection does not contain the specified number of matching elements.
433
+ *
434
+ * @param count Count that the element collection must NOT equal.
435
+ * @example
436
+ * ```ts
437
+ * await Spectra.getAll('.error-item').shouldNotHaveLength(0);
438
+ * ```
439
+ */
440
+ shouldNotHaveLength(count: number, options?: AssertionOptions): TReturn;
441
+ /**
442
+ * Asserts that the collection contains strictly more than `min` matching elements.
443
+ *
444
+ * @param min Minimum threshold (exclusive).
445
+ * @example
446
+ * ```ts
447
+ * await Spectra.getAll('.search-result-card').shouldHaveLengthGreaterThan(0);
448
+ * ```
449
+ */
450
+ shouldHaveLengthGreaterThan(min: number, options?: AssertionOptions): TReturn;
451
+ /**
452
+ * Asserts that the collection contains strictly fewer than `max` matching elements.
453
+ *
454
+ * @param max Maximum threshold (exclusive).
455
+ * @example
456
+ * ```ts
457
+ * await Spectra.getAll('.warning-banner').shouldHaveLengthLessThan(5);
458
+ * ```
459
+ */
460
+ shouldHaveLengthLessThan(max: number, options?: AssertionOptions): TReturn;
461
+ /**
462
+ * Asserts that the collection contains zero matching elements.
463
+ *
464
+ * @example
465
+ * ```ts
466
+ * await Spectra.getAll('.unread-notification-badge').shouldBeEmpty();
467
+ * ```
468
+ */
469
+ shouldBeEmpty(options?: AssertionOptions): TReturn;
470
+ /**
471
+ * Asserts that the collection contains at least one matching element.
472
+ *
473
+ * @example
474
+ * ```ts
475
+ * await Spectra.getAll('.product-card').shouldNotBeEmpty();
476
+ * ```
477
+ */
478
+ shouldNotBeEmpty(options?: AssertionOptions): TReturn;
479
+ }
480
+ /**
481
+ * Browser-level context assertion methods.
482
+ * Hosted on Spectra.browser and the global browser object.
483
+ */
484
+ export interface BrowserReceiverAssertions {
485
+ /**
486
+ * Asserts that current browser URL exactly matches the expected URL.
487
+ *
488
+ * @param expectedUrl Full expected URL string.
489
+ * @example
490
+ * ```ts
491
+ * await Spectra.browser.shouldHaveUrl('https://app.testspectra.dev/dashboard');
492
+ * ```
493
+ */
494
+ shouldHaveUrl(expectedUrl: string, options?: AssertionOptions): Promise<void>;
495
+ /**
496
+ * Asserts that current browser URL contains the specified substring.
497
+ *
498
+ * @param expectedSubstr Substring expected in browser URL.
499
+ * @example
500
+ * ```ts
501
+ * await Spectra.browser.shouldContainUrl('/dashboard');
502
+ * ```
503
+ */
504
+ shouldContainUrl(expectedSubstr: string, options?: AssertionOptions): Promise<void>;
505
+ /**
506
+ * Asserts that current page title exactly matches the expected title.
507
+ *
508
+ * @param expectedTitle Full expected page title string.
509
+ * @example
510
+ * ```ts
511
+ * await Spectra.browser.shouldHaveTitle('Dashboard - TestSpectra');
512
+ * ```
513
+ */
514
+ shouldHaveTitle(expectedTitle: string, options?: AssertionOptions): Promise<void>;
515
+ /**
516
+ * Asserts that current page title contains the specified substring.
517
+ *
518
+ * @param expectedSubstr Substring expected in page title.
519
+ * @example
520
+ * ```ts
521
+ * await Spectra.browser.shouldContainTitle('Dashboard');
522
+ * ```
523
+ */
524
+ shouldContainTitle(expectedSubstr: string, options?: AssertionOptions): Promise<void>;
525
+ /**
526
+ * Asserts that the browser document `readyState` is 'complete' or 'interactive'.
527
+ *
528
+ * @example
529
+ * ```ts
530
+ * await Spectra.browser.shouldBeLoaded();
531
+ * ```
532
+ */
533
+ shouldBeLoaded(options?: AssertionOptions): Promise<void>;
534
+ /**
535
+ * Asserts that no severe or unhandled JavaScript errors occurred in the browser console.
536
+ *
537
+ * @example
538
+ * ```ts
539
+ * await Spectra.browser.shouldHaveNoConsoleErrors();
540
+ * ```
541
+ */
542
+ shouldHaveNoConsoleErrors(options?: AssertionOptions): Promise<void>;
543
+ /**
544
+ * Clears all browser cookies for the active domain.
545
+ *
546
+ * @example
547
+ * ```ts
548
+ * await Spectra.browser.clearCookies();
549
+ * ```
550
+ */
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>;
574
+ /**
575
+ * Clears all key-value entries in browser `localStorage`.
576
+ *
577
+ * @example
578
+ * ```ts
579
+ * await Spectra.browser.clearLocalStorage();
580
+ * ```
581
+ */
582
+ clearLocalStorage(): Promise<void>;
583
+ }
584
+ /**
585
+ * Native proxy interface for interacting with a single DOM / UI element.
586
+ */
587
+ export interface SingleElementProxy extends ElementReceiverAssertions<Promise<void>> {
588
+ /** Target CSS or XPath selector string for this element. */
589
+ selector: string;
590
+ /** Positional index when matched from a collection (or null for standalone selectors). */
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;
615
+ /**
616
+ * Waits until the element exists in the DOM within the specified timeout.
617
+ *
618
+ * @param timeoutMs Timeout in milliseconds (default: 5000ms).
619
+ */
620
+ waitForElement(timeoutMs?: number): Promise<boolean>;
621
+ /**
622
+ * Clicks on the element.
623
+ *
624
+ * @param options Optional click interaction options.
625
+ * @example
626
+ * ```ts
627
+ * await Spectra.get('#submit-btn').click();
628
+ * ```
629
+ */
630
+ click(options?: ClickOptions): Promise<void>;
631
+ /**
632
+ * Performs a double-click interaction on the element.
633
+ *
634
+ * @example
635
+ * ```ts
636
+ * await Spectra.get('.editable-cell').doubleClick();
637
+ * ```
638
+ */
639
+ doubleClick(): Promise<void>;
640
+ /**
641
+ * Performs a context-click (right-click) on the element.
642
+ *
643
+ * @example
644
+ * ```ts
645
+ * await Spectra.get('.file-item').rightClick();
646
+ * ```
647
+ */
648
+ rightClick(): Promise<void>;
649
+ /**
650
+ * Sets or replaces the value of a form input element.
651
+ *
652
+ * @param value Text value to set.
653
+ * @example
654
+ * ```ts
655
+ * await Spectra.get('#username').setValue('admin');
656
+ * ```
657
+ */
658
+ setValue(value: unknown): Promise<void>;
659
+ /**
660
+ * Types text keystrokes into the element, optionally clearing existing content first.
661
+ *
662
+ * @param value Text string to type.
663
+ * @param options Optional type options (e.g. `{ clearFirst: true }`).
664
+ * @example
665
+ * ```ts
666
+ * await LoginPage.emailInput.type('admin@testspectra.dev', { clearFirst: true });
667
+ * ```
668
+ */
669
+ type(value: unknown, options?: TypeOptions): Promise<void>;
670
+ /**
671
+ * Clears the current text value from an input or textarea element.
672
+ *
673
+ * @example
674
+ * ```ts
675
+ * await Spectra.get('#search-bar').clearValue();
676
+ * ```
677
+ */
678
+ clearValue(): Promise<void>;
679
+ /**
680
+ * Clears the current text value from an input or textarea element (alias to `clearValue()`).
681
+ *
682
+ * @example
683
+ * ```ts
684
+ * await Spectra.get('#search-bar').clear();
685
+ * ```
686
+ */
687
+ clear(): Promise<void>;
688
+ /**
689
+ * Selects an option in a `<select>` element by its visible text or value.
690
+ *
691
+ * @param option Visible text or value of the option to select.
692
+ * @example
693
+ * ```ts
694
+ * await Spectra.get('#country-dropdown').select('Indonesia');
695
+ * ```
696
+ */
697
+ select(option: string): Promise<void>;
698
+ /**
699
+ * Selects an option in a `<select>` dropdown by its visible text.
700
+ *
701
+ * @param text Visible text label of the option.
702
+ */
703
+ selectByVisibleText(text: string): Promise<void>;
704
+ /**
705
+ * Hovers the mouse cursor over the center of the element.
706
+ *
707
+ * @example
708
+ * ```ts
709
+ * await Spectra.get('.dropdown-trigger').hover();
710
+ * ```
711
+ */
712
+ hover(): Promise<void>;
713
+ /**
714
+ * Focuses the target element (triggers focus event and active document focus).
715
+ *
716
+ * @example
717
+ * ```ts
718
+ * await Spectra.get('#email-input').focus();
719
+ * ```
720
+ */
721
+ focus(): Promise<void>;
722
+ /**
723
+ * Moves the mouse cursor to the element (alias to `hover()`).
724
+ */
725
+ moveTo(): Promise<void>;
726
+ /**
727
+ * Drags this element and drops it onto the target element destination.
728
+ *
729
+ * @param target Destination element target selector or proxy.
730
+ * @example
731
+ * ```ts
732
+ * await Spectra.get('#drag-source').dragDrop('#drop-zone');
733
+ * ```
734
+ */
735
+ dragDrop(target: ElementTarget): Promise<void>;
736
+ /**
737
+ * Drags this element and drops it onto the target element destination (alias to `dragDrop()`).
738
+ */
739
+ dragAndDrop(target: ElementTarget): Promise<void>;
740
+ /**
741
+ * Scrolls the page until this element is aligned into the visible viewport.
742
+ *
743
+ * @example
744
+ * ```ts
745
+ * await Spectra.get('#footer-links').scrollIntoView();
746
+ * ```
747
+ */
748
+ scrollIntoView(): Promise<void>;
749
+ /**
750
+ * Long-presses on the element for touch/mobile gestures.
751
+ *
752
+ * @param options Long-press duration in milliseconds or configuration object.
753
+ * @example
754
+ * ```ts
755
+ * await Spectra.get('.draggable-card').longPress({ duration: 1500 });
756
+ * ```
757
+ */
758
+ longPress(options?: number | LongPressOptions): Promise<void>;
759
+ /**
760
+ * Waits until the element is displayed and visible in the viewport.
761
+ *
762
+ * @param opts Optional timeout configuration object.
763
+ */
764
+ waitForDisplayed(opts?: {
765
+ timeout?: number;
766
+ }): Promise<boolean>;
767
+ /**
768
+ * Returns the inner text content of the element.
769
+ *
770
+ * @example
771
+ * ```ts
772
+ * const headingText = await Spectra.get('h1').getText();
773
+ * ```
774
+ */
775
+ getText(): Promise<string>;
776
+ /**
777
+ * Returns the current `value` property of an input or textarea element.
778
+ *
779
+ * @example
780
+ * ```ts
781
+ * const query = await Spectra.get('input#search').getValue();
782
+ * ```
783
+ */
784
+ getValue(): Promise<string>;
785
+ /**
786
+ * Returns true if the element is currently visible and rendered in the DOM.
787
+ */
788
+ isDisplayed(): Promise<boolean>;
789
+ /**
790
+ * Returns true if the element is currently visible (alias to `isDisplayed()`).
791
+ */
792
+ isVisible(): Promise<boolean>;
793
+ /**
794
+ * Returns true if the element exists in the DOM.
795
+ */
796
+ isExisting(): Promise<boolean>;
797
+ /**
798
+ * Returns true if the element is enabled (not disabled).
799
+ */
800
+ isEnabled(): Promise<boolean>;
801
+ /**
802
+ * Returns true if the checkbox, radio, or `<option>` is selected.
803
+ */
804
+ isSelected(): Promise<boolean>;
805
+ /**
806
+ * Returns true if the checkbox or radio button is checked.
807
+ */
808
+ isChecked(): Promise<boolean>;
809
+ /**
810
+ * Returns true if the element is the active focused element in document.
811
+ */
812
+ isFocused(): Promise<boolean>;
813
+ /**
814
+ * Retrieves the value of a specified HTML attribute from the element.
815
+ *
816
+ * @param name Attribute name (e.g. 'href', 'src', 'data-id').
817
+ * @example
818
+ * ```ts
819
+ * const linkUrl = await Spectra.get('a#download').getAttribute('href');
820
+ * ```
821
+ */
822
+ getAttribute(name: string): Promise<string | null>;
823
+ /**
824
+ * Retrieves the computed CSS property value of the element.
825
+ *
826
+ * @param name CSS property name (e.g. 'background-color', 'font-size').
827
+ * @example
828
+ * ```ts
829
+ * const color = await Spectra.get('.btn-primary').getCSSProperty('background-color');
830
+ * ```
831
+ */
832
+ getCSSProperty(name: string): Promise<{
833
+ value: string;
834
+ }>;
835
+ }
836
+ /**
837
+ * TestSpectra native element interface alias.
130
838
  */
131
- export type MultiElementMatcher = "have.length" | "have.length.greaterThan" | "be.empty" | "exist";
839
+ export type SpectraElement = SingleElementProxy;
840
+ /**
841
+ * Native proxy interface for interacting with multiple matching elements in a collection.
842
+ */
843
+ export interface CollectionProxy extends CollectionReceiverAssertions<Promise<void>> {
844
+ /** Target CSS or XPath selector string for this collection. */
845
+ selector: string;
846
+ /**
847
+ * Returns the total count of elements matching this collection selector.
848
+ *
849
+ * @example
850
+ * ```ts
851
+ * const totalRows = await Spectra.getAll('.table-row').count();
852
+ * ```
853
+ */
854
+ count(): Promise<number>;
855
+ /**
856
+ * Total count of elements matching this collection selector (Promise).
857
+ */
858
+ readonly length: Promise<number>;
859
+ /**
860
+ * Returns a `SingleElementProxy` pointing to the first matching element in the collection (index 0).
861
+ *
862
+ * @example
863
+ * ```ts
864
+ * await Spectra.getAll('.list-item').first().click();
865
+ * ```
866
+ */
867
+ first(): SingleElementProxy;
868
+ /**
869
+ * Returns a `SingleElementProxy` pointing to the last matching element in the collection.
870
+ *
871
+ * @example
872
+ * ```ts
873
+ * await Spectra.getAll('.list-item').last().click();
874
+ * ```
875
+ */
876
+ last(): SingleElementProxy;
877
+ /**
878
+ * Returns a `SingleElementProxy` pointing to the matching element at the specified zero-based index.
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
+ *
884
+ * @param index Zero-based index of the target element.
885
+ * @example
886
+ * ```ts
887
+ * await Spectra.getAll('.list-item').nth(2).click();
888
+ * await Spectra.getAll('.card').nth(2).get('.buy-btn').click();
889
+ * ```
890
+ */
891
+ nth(index: number): SingleElementProxy;
892
+ }
893
+ /**
894
+ * TestSpectra native collection interface alias.
895
+ */
896
+ export type SpectraCollection = CollectionProxy;
897
+ /**
898
+ * Native browser bridge interface for page navigation, execution, and network interception.
899
+ */
900
+ export interface SpectraBrowserBridge extends BrowserReceiverAssertions {
901
+ /**
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.
908
+ *
909
+ * @example
910
+ * ```ts
911
+ * await Spectra.get('~fetch-api-btn').click();
912
+ * const entry = Spectra.browser.recordedNetwork.find((e) => e.url.includes('/posts'));
913
+ * ```
914
+ */
915
+ recordedNetwork: CDPNetworkEntry[];
916
+ /**
917
+ * Retrieves the current browser URL.
918
+ *
919
+ * @example
920
+ * ```ts
921
+ * const currentUrl = await Spectra.browser.getUrl();
922
+ * ```
923
+ */
924
+ getUrl(): Promise<string>;
925
+ /**
926
+ * Retrieves the current page title.
927
+ *
928
+ * @example
929
+ * ```ts
930
+ * const title = await Spectra.browser.getTitle();
931
+ * ```
932
+ */
933
+ getTitle(): Promise<string>;
934
+ /**
935
+ * Executes a JavaScript function or script snippet in the browser context and returns the result.
936
+ *
937
+ * @param fn Function or script string to execute.
938
+ * @param args Arguments to pass into the function.
939
+ * @example
940
+ * ```ts
941
+ * const docWidth = await Spectra.browser.execute(() => document.body.clientWidth);
942
+ * ```
943
+ */
944
+ execute<R = unknown>(fn: ((...args: unknown[]) => R) | string, ...args: unknown[]): Promise<R>;
945
+ /**
946
+ * Evaluates a JavaScript expression string in the browser context.
947
+ *
948
+ * @param expr JavaScript expression string.
949
+ * @example
950
+ * ```ts
951
+ * const isReady = await Spectra.browser.evaluate('document.readyState === "complete"');
952
+ * ```
953
+ */
954
+ evaluate<R = unknown>(expr: string): Promise<R>;
955
+ /**
956
+ * Repeatedly polls a condition function until it returns true or times out.
957
+ *
958
+ * @param fn Condition function returning a boolean or boolean promise.
959
+ * @param opts Optional timeout and message options.
960
+ * @example
961
+ * ```ts
962
+ * await Spectra.browser.waitUntil(async () => (await Spectra.get('#status').getText()) === 'Ready', {
963
+ * timeout: 5000,
964
+ * timeoutMsg: 'Status never reached Ready',
965
+ * });
966
+ * ```
967
+ */
968
+ waitUntil(fn: () => Promise<boolean> | boolean, opts?: {
969
+ timeout?: number;
970
+ timeoutMsg?: string;
971
+ }): Promise<boolean>;
972
+ /**
973
+ * Retrieves captured browser console log entries.
974
+ *
975
+ * @param type Optional log type filter (e.g. 'browser', 'error').
976
+ */
977
+ getLogs(type?: string): Promise<Array<{
978
+ level: string;
979
+ message: string;
980
+ }>>;
981
+ }
982
+ /**
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.
1011
+ */
1012
+ export interface MockRule {
1013
+ /** URL pattern or substring to match. */
1014
+ pattern: string;
1015
+ /** HTTP method (e.g. GET, POST, ALL). */
1016
+ method: string;
1017
+ /** Mock response payload. */
1018
+ response: unknown;
1019
+ /** HTTP status code (e.g. 200, 404). */
1020
+ statusCode: number;
1021
+ /** Custom HTTP response headers. */
1022
+ headers?: Record<string, string>;
1023
+ /** Milliseconds to wait before fulfilling the default response — simulates a late/slow reply. */
1024
+ delayMs?: number;
1025
+ /** Total times this mock rule matched and intercepted requests. */
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
+ }>;
1040
+ }
1041
+ /**
1042
+ * Handle returned by `Spectra.intercept()` to dynamically inspect and update mock responses.
1043
+ */
1044
+ export interface MockInterceptHandle {
1045
+ /**
1046
+ * Dynamically updates the default response payload for this active mock rule.
1047
+ *
1048
+ * @param newFixture New response payload object or string.
1049
+ * @param newOptions Optional status code and header overrides.
1050
+ */
1051
+ respondWith: (newFixture: unknown, newOptions?: {
1052
+ statusCode?: number;
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;
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>;
1082
+ /**
1083
+ * Returns the number of times this mock intercepted network requests.
1084
+ */
1085
+ callCount: (() => number) & number;
1086
+ /**
1087
+ * Historical array of all intercepted requests matching this rule.
1088
+ */
1089
+ calls: InterceptedRequest[];
1090
+ }
1091
+ /**
1092
+ * Audit log entry for captured CDP network requests.
1093
+ */
1094
+ export interface CDPNetworkEntry {
1095
+ /** Unique CDP request ID. */
1096
+ requestId: string;
1097
+ /** Target request URL. */
1098
+ url: string;
1099
+ /** HTTP request method. */
1100
+ method: string;
1101
+ /** Resource type (e.g. 'Fetch', 'XHR', 'Document', 'Stylesheet'). */
1102
+ type: string;
1103
+ /** HTTP response status code. */
1104
+ status: number;
1105
+ /** HTTP response status message. */
1106
+ statusText: string;
1107
+ /** Host domain name. */
1108
+ domain: string;
1109
+ /** Protocol name (e.g. 'h2', 'http/1.1'). */
1110
+ protocol: string;
1111
+ /** Request timing metrics in milliseconds. */
1112
+ timing: {
1113
+ waiting: number;
1114
+ download: number;
1115
+ };
1116
+ /** Content body size in bytes. */
1117
+ size: number;
1118
+ /** Wire transfer size in bytes. */
1119
+ transferSize: number;
1120
+ /** Start timestamp epoch. */
1121
+ startTime: number;
1122
+ }
1123
+ /**
1124
+ * Main TestSpectra cross-platform automation and assertion interface contract.
1125
+ */
1126
+ export interface SpectraStatic {
1127
+ /**
1128
+ * Locates a single DOM / UI element proxy by selector or target reference.
1129
+ *
1130
+ * @param target CSS selector string, XPath string, or element proxy.
1131
+ * @example
1132
+ * ```ts
1133
+ * const submitBtn = Spectra.get('#btn-submit');
1134
+ * await submitBtn.click();
1135
+ * ```
1136
+ */
1137
+ get(target: ElementTarget): SingleElementProxy;
1138
+ /**
1139
+ * Locates a collection proxy of multiple matching DOM / UI elements by selector.
1140
+ *
1141
+ * @param selector CSS or XPath selector matching multiple elements.
1142
+ * @example
1143
+ * ```ts
1144
+ * const rows = Spectra.getAll('table tr');
1145
+ * await rows.shouldHaveLength(5);
1146
+ * ```
1147
+ */
1148
+ getAll(selector: string): CollectionProxy;
1149
+ /**
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.
1154
+ *
1155
+ * @param url Absolute or relative URL on web; a full deep-link URI on Android.
1156
+ * @example
1157
+ * ```ts
1158
+ * await Spectra.navigate('/dashboard'); // web
1159
+ * await Spectra.navigate('testspectra-demo://permission-rationale'); // Android
1160
+ * ```
1161
+ */
1162
+ navigate(url: string): Promise<void>;
1163
+ /**
1164
+ * Navigates one step backward in browser history.
1165
+ *
1166
+ * @example
1167
+ * ```ts
1168
+ * await Spectra.back();
1169
+ * ```
1170
+ */
1171
+ back(): Promise<void>;
1172
+ /**
1173
+ * Navigates one step forward in browser history.
1174
+ *
1175
+ * @example
1176
+ * ```ts
1177
+ * await Spectra.forward();
1178
+ * ```
1179
+ */
1180
+ forward(): Promise<void>;
1181
+ /**
1182
+ * Reloads / refreshes the current active page.
1183
+ *
1184
+ * @example
1185
+ * ```ts
1186
+ * await Spectra.refresh();
1187
+ * ```
1188
+ */
1189
+ refresh(): Promise<void>;
1190
+ /**
1191
+ * Sets the browser viewport dimensions (width and height in pixels).
1192
+ *
1193
+ * @param width Viewport width in pixels.
1194
+ * @param height Viewport height in pixels.
1195
+ * @example
1196
+ * ```ts
1197
+ * await Spectra.setViewport(1920, 1080);
1198
+ * ```
1199
+ */
1200
+ setViewport(width: number, height: number): Promise<void>;
1201
+ /**
1202
+ * Clicks on the specified element target.
1203
+ *
1204
+ * @param target Element selector string, Page Object element, or proxy.
1205
+ * @param textOrOptions Optional text filter or click options.
1206
+ * @example
1207
+ * ```ts
1208
+ * await Spectra.click('#submit-btn');
1209
+ * await Spectra.click(LoginPage.submitButton);
1210
+ * ```
1211
+ */
1212
+ click(target: ElementTarget, textOrOptions?: string | ClickOptions): Promise<void>;
1213
+ /**
1214
+ * Performs a double-click interaction on the specified element target.
1215
+ *
1216
+ * @param target Target element selector or proxy.
1217
+ * @example
1218
+ * ```ts
1219
+ * await Spectra.doubleClick('.grid-cell');
1220
+ * ```
1221
+ */
1222
+ doubleClick(target: ElementTarget): Promise<void>;
1223
+ /**
1224
+ * Performs a context-click (right-click) interaction on the specified element target.
1225
+ *
1226
+ * @param target Target element selector or proxy.
1227
+ * @example
1228
+ * ```ts
1229
+ * await Spectra.rightClick('#context-menu-area');
1230
+ * ```
1231
+ */
1232
+ rightClick(target: ElementTarget): Promise<void>;
1233
+ /**
1234
+ * Types text keystrokes into the specified element target.
1235
+ *
1236
+ * @param target Target input or textarea element selector or proxy.
1237
+ * @param text String content to type.
1238
+ * @param options Optional typing configuration (e.g. `{ clearFirst: true }`).
1239
+ * @example
1240
+ * ```ts
1241
+ * await Spectra.type('#username', 'john_doe', { clearFirst: true });
1242
+ * ```
1243
+ */
1244
+ type(target: ElementTarget, text: string, options?: TypeOptions): Promise<void>;
1245
+ /**
1246
+ * Clears the current text value from an input or textarea element.
1247
+ *
1248
+ * @param target Target element selector or proxy.
1249
+ * @example
1250
+ * ```ts
1251
+ * await Spectra.clear('#search-input');
1252
+ * ```
1253
+ */
1254
+ clear(target: ElementTarget): Promise<void>;
1255
+ /**
1256
+ * Selects an option in a `<select>` dropdown by its visible text or value.
1257
+ *
1258
+ * @param target Target select element selector or proxy.
1259
+ * @param option Visible text label or value of the option.
1260
+ * @example
1261
+ * ```ts
1262
+ * await Spectra.select('#country-dropdown', 'United States');
1263
+ * ```
1264
+ */
1265
+ select(target: ElementTarget, option: string): Promise<void>;
1266
+ /**
1267
+ * Hovers the mouse cursor over the specified element target.
1268
+ *
1269
+ * @param target Target element selector or proxy.
1270
+ * @example
1271
+ * ```ts
1272
+ * await Spectra.hover('.profile-menu-trigger');
1273
+ * ```
1274
+ */
1275
+ hover(target: ElementTarget): Promise<void>;
1276
+ /**
1277
+ * Focuses the target element (triggers focus event and active document focus).
1278
+ *
1279
+ * @param target Target element selector or proxy.
1280
+ * @example
1281
+ * ```ts
1282
+ * await Spectra.focus('#email-input');
1283
+ * ```
1284
+ */
1285
+ focus(target: ElementTarget): Promise<void>;
1286
+ /**
1287
+ * Drags a source element and drops it onto a destination element target.
1288
+ *
1289
+ * @param source Draggable source element selector or proxy.
1290
+ * @param destination Drop zone destination element selector or proxy.
1291
+ * @example
1292
+ * ```ts
1293
+ * await Spectra.dragDrop('#item-1', '#kanban-column-done');
1294
+ * ```
1295
+ */
1296
+ dragDrop(source: ElementTarget, destination: ElementTarget): Promise<void>;
1297
+ /**
1298
+ * Scrolls the page until the specified element is aligned into the visible viewport.
1299
+ *
1300
+ * @param target Target element selector or proxy.
1301
+ * @example
1302
+ * ```ts
1303
+ * await Spectra.scrollIntoView('#footer-contact');
1304
+ * ```
1305
+ */
1306
+ scrollIntoView(target: ElementTarget): Promise<void>;
1307
+ /**
1308
+ * Performs a directional scroll on the page.
1309
+ *
1310
+ * @param options Scroll options (direction, pixel distance, or target selector).
1311
+ * @example
1312
+ * ```ts
1313
+ * await Spectra.scroll({ direction: 'down', pixels: 400 });
1314
+ * ```
1315
+ */
1316
+ scroll(options?: ScrollOptions): Promise<void>;
1317
+ /**
1318
+ * Performs a directional touch swipe gesture (mobile & web).
1319
+ *
1320
+ * @param options Swipe options (direction, distance in pixels, anchor selector).
1321
+ * @example
1322
+ * ```ts
1323
+ * await Spectra.swipe({ direction: 'left', distance: 300 });
1324
+ * ```
1325
+ */
1326
+ swipe(options: SwipeOptions): Promise<void>;
1327
+ /**
1328
+ * Performs a long-press touch gesture on the target element.
1329
+ *
1330
+ * @param target Target element selector or proxy.
1331
+ * @param options Long-press duration in milliseconds or configuration object.
1332
+ * @example
1333
+ * ```ts
1334
+ * await Spectra.longPress('#sortable-item', { duration: 1500 });
1335
+ * ```
1336
+ */
1337
+ longPress(target: ElementTarget, options?: LongPressOptions | number): Promise<void>;
1338
+ /**
1339
+ * Presses a specific keyboard key into the active document element.
1340
+ *
1341
+ * @param key Key name to press (e.g. 'Enter', 'Tab', 'Escape', 'Backspace').
1342
+ * @example
1343
+ * ```ts
1344
+ * await Spectra.pressKey('Enter');
1345
+ * ```
1346
+ */
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>;
1362
+ /**
1363
+ * Pauses test execution for the specified number of milliseconds.
1364
+ *
1365
+ * @param ms Milliseconds to wait.
1366
+ * @example
1367
+ * ```ts
1368
+ * await Spectra.wait(1000);
1369
+ * ```
1370
+ */
1371
+ wait(ms: number): Promise<void>;
1372
+ /**
1373
+ * Waits for the specified element to appear and exist in the DOM.
1374
+ *
1375
+ * @param target Target element selector or proxy.
1376
+ * @param timeoutMs Maximum time to wait in milliseconds (default: 5000ms).
1377
+ * @example
1378
+ * ```ts
1379
+ * await Spectra.waitForElement('#dynamic-content', 10000);
1380
+ * ```
1381
+ */
1382
+ waitForElement(target: ElementTarget, timeoutMs?: number): Promise<void>;
1383
+ /**
1384
+ * Direct access to browser context commands and page assertions.
1385
+ */
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;
1399
+ /**
1400
+ * Intercepts and mocks HTTP network requests matching the specified pattern or options.
1401
+ *
1402
+ * @param patternOrOptions URL pattern string or structured intercept configuration object.
1403
+ * @param method HTTP method (GET, POST, etc.) when pattern string is used.
1404
+ * @param fixture Mock response payload.
1405
+ * @param options Additional response options (status code, custom headers).
1406
+ * @example
1407
+ * ```ts
1408
+ * const mock = await Spectra.intercept('/api/v1/profile', 'GET', { name: 'Admin', role: 'root' });
1409
+ * ```
1410
+ */
1411
+ intercept(patternOrOptions: string | {
1412
+ url: string;
1413
+ method?: string;
1414
+ response?: unknown;
1415
+ }, method?: string, fixture?: unknown, options?: {
1416
+ statusCode?: number;
1417
+ headers?: Record<string, string>;
1418
+ delayMs?: number;
1419
+ }): Promise<MockInterceptHandle>;
1420
+ /**
1421
+ * Clears and resets all active CDP network interception rules.
1422
+ *
1423
+ * @example
1424
+ * ```ts
1425
+ * Spectra.clearMocks();
1426
+ * ```
1427
+ */
1428
+ clearMocks(): void;
1429
+ }