@testspectra/matchers 1.1.0 → 1.1.8-rc.2

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.
@@ -89,14 +89,6 @@ describe('Matchers Assertion Library & Receiver API', () => {
89
89
  expect(runner.dragDrop('#dest')).toBe(runner);
90
90
  expect(runner._actionPayload.key).toBe('dragDrop');
91
91
  });
92
- it('should support action to semantic assertion chaining', () => {
93
- const runner = new SingleElementRunner('#login-btn');
94
- runner.click().shouldNotBeVisible();
95
- expect(runner._actionPayload.key).toBe('click');
96
- const assertions = runner._assertions;
97
- expect(assertions).toHaveLength(1);
98
- expect(assertions[0].matcher).toBe('not.be.visible');
99
- });
100
92
  });
101
93
  describe('MultiElementRunner - 6 Collection Receiver Assertions', () => {
102
94
  it('should queue all 6 collection assertions', () => {
@@ -0,0 +1,173 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * # PlatformDriverBridge Contract
4
+ *
5
+ * The unified abstract driver contract consumed by `@testspectra/matchers` element proxies,
6
+ * fluent matchers, and the `Spectra` automation object. Every platform-specific driver
7
+ * (`WebCdpDriverBridge`, `AndroidTcpDriverBridge`) implements this interface so that the
8
+ * DSL layer contains zero platform branching.
9
+ */
10
+ import type { CDPNetworkEntry, MockInterceptHandle, MockRule, ScopedSelector, ScrollOptions, SpectraBrowserBridge, SwipeOptions } from './types.js';
11
+ /**
12
+ * Runtime configuration injected by the Rust orchestrator via `globalThis.__TESTSPECTRA_CONFIG__`.
13
+ */
14
+ export interface RuntimeWorkerConfig {
15
+ workerId?: number;
16
+ rootDir: string;
17
+ testFilePath?: string;
18
+ testFiles?: Array<{
19
+ id: string;
20
+ title: string;
21
+ filePath: string;
22
+ suite: string;
23
+ }>;
24
+ activePlatform: string;
25
+ configuredBaseUrl: string;
26
+ cdpWsUrl: string;
27
+ driverServerUrl: string;
28
+ manifest?: Record<string, any>;
29
+ /** Target app package (Android). Paired with androidSerial/adbPath for on-demand adb actions. */
30
+ appPackage?: string;
31
+ /** Target adb device serial for this worker (Android only). */
32
+ androidSerial?: string;
33
+ /** Resolved `adb` binary path (Android only), paired with androidSerial above. */
34
+ adbPath?: string;
35
+ /** Host port this worker's own MobileMockProxyServer should listen on (Android only) — derived
36
+ * per-device the same way androidSerial/the driver-server port already are, so each device in
37
+ * a multi-device run gets its own proxy instead of every worker racing for one shared port. */
38
+ mockProxyPort?: number;
39
+ implicitWaitMs?: number;
40
+ timeoutMs?: number;
41
+ stepDelayMs?: number;
42
+ /** Mirrors `spectra.config.ts`'s `executionConfig.networkMonitoringEnabled` (default: true). */
43
+ networkMonitoringEnabled?: boolean;
44
+ /** Mirrors `spectra.config.ts`'s `executionConfig.monitoredDomains`. Empty/absent = capture all. */
45
+ monitoredDomains?: {
46
+ domain: string;
47
+ enabled: boolean;
48
+ }[];
49
+ /** Mirrors `spectra.config.ts`'s `executionConfig.environmentVariables`. */
50
+ environmentVariables?: Record<string, string>;
51
+ }
52
+ /**
53
+ * Cross-cutting shared runtime state injected into the assembled worker script.
54
+ *
55
+ * The worker runtime is emitted as a single self-contained TypeScript module (concatenated
56
+ * by the Rust orchestrator). This object is created once in the harness prelude and mutated
57
+ * in place (`activeDriver` is bound after the platform bridge is instantiated), so fragments
58
+ * access it lazily at call-time through `globalThis.__TS_RUNTIME__`.
59
+ */
60
+ export interface RuntimeContext {
61
+ config: RuntimeWorkerConfig;
62
+ activeDriver: PlatformDriverBridge | null;
63
+ mobileMockRules: MockRule[];
64
+ lastFocusedSelector: string | null;
65
+ currentTargetItem?: any;
66
+ emitEvent(tag: string, payload: string): void;
67
+ trackStep<T>(category: 'action' | 'assertion' | 'browser', payloadObj: Record<string, any>, fn: () => Promise<T>): Promise<T>;
68
+ pollCondition(fn: () => Promise<boolean> | boolean, timeoutMs?: number, intervalMs?: number): Promise<boolean>;
69
+ getImplicitWaitMs(): number;
70
+ getAssertionTimeoutMs(): number;
71
+ matchesUrlPattern(pattern: string, url: string): boolean;
72
+ /** Whether a captured network request/response for `url` should be recorded, per
73
+ * `config.networkMonitoringEnabled` / `config.monitoredDomains`. */
74
+ shouldRecordNetworkEntry(url: string): boolean;
75
+ createMockHandle(rule: MockRule): MockInterceptHandle;
76
+ parseMockRule(patternOrOptions?: any, maybeMethodOrHandler?: any, maybeFixture?: any, maybeOptions?: any): MockRule;
77
+ recordDuration?: (label: string, category: string, durationMs: number, extra?: Record<string, unknown>) => void;
78
+ withDuration?: <T>(label: string, category: string, fn: () => Promise<T>, extra?: Record<string, unknown>) => Promise<T>;
79
+ durationLogPath?: string | null;
80
+ createElementActions?: (selector: string | ScopedSelector, index: number | null) => Record<string, any>;
81
+ createElementState?: (selector: string | ScopedSelector, index: number | null) => Record<string, any>;
82
+ createElementAssertions?: (selector: string | ScopedSelector, index: number | null, state: Record<string, any>) => Record<string, any>;
83
+ createElementProxy?: (selector: string | ScopedSelector, index?: number | null) => any;
84
+ createCollectionProxy?: (selector: string | ScopedSelector) => any;
85
+ resolveTargetProxy?: (target: any) => any;
86
+ buildSpectra?: () => any;
87
+ cdpBridgeClass?: any;
88
+ androidBridgeClass?: any;
89
+ mobileMockProxyClass?: any;
90
+ mobileMockProxy?: any;
91
+ beforeHook?: (() => Promise<void> | void) | null;
92
+ afterHook?: (() => Promise<void> | void) | null;
93
+ beforeEachHook?: (() => Promise<void> | void) | null;
94
+ afterEachHook?: (() => Promise<void> | void) | null;
95
+ bootstrapEnvironment?: () => Promise<void>;
96
+ runSuite?: () => Promise<void>;
97
+ }
98
+ /**
99
+ * The unified platform driver contract. It extends the browser surface documented in
100
+ * `SpectraBrowserBridge` (navigation, script execution, network interception, browser-level
101
+ * assertions) with the element interaction and state-inspection methods needed by the DSL,
102
+ * so the `tools/matchers` layer never branches on `activePlatform`.
103
+ */
104
+ export interface PlatformDriverBridge extends SpectraBrowserBridge {
105
+ /** Establishes connection to the underlying runtime endpoint (WebSocket / TCP Socket). */
106
+ connect(): Promise<void>;
107
+ /** Cleans up open connections, servers, and listeners. */
108
+ close(): Promise<void>;
109
+ /** Navigates to target URL (Web) or Activity / Deep Link (Android). */
110
+ navigate(targetUrl: string): Promise<void>;
111
+ /** Navigates one step backward in browser/navigation history. */
112
+ back(): Promise<void>;
113
+ /** Navigates one step forward in browser/navigation history. */
114
+ forward(): Promise<void>;
115
+ /** Reloads / refreshes the current active page. */
116
+ refresh(): Promise<void>;
117
+ /** Sets the browser viewport dimensions (width and height in pixels). */
118
+ setViewport(width: number, height: number): Promise<void>;
119
+ /** Pauses execution for the given duration in milliseconds. */
120
+ pause(ms: number): Promise<void>;
121
+ /** Intercepts and mocks HTTP network requests matching the specified pattern or options. */
122
+ intercept(patternOrOptions: string | {
123
+ url: string;
124
+ method?: string;
125
+ response?: unknown;
126
+ }, method?: string, fixture?: unknown, options?: {
127
+ statusCode?: number;
128
+ headers?: Record<string, string>;
129
+ delayMs?: number;
130
+ }): Promise<MockInterceptHandle>;
131
+ click(selector: string | ScopedSelector, index?: number | null): Promise<void>;
132
+ doubleClick(selector: string | ScopedSelector, index?: number | null): Promise<void>;
133
+ rightClick(selector: string | ScopedSelector, index?: number | null): Promise<void>;
134
+ setValue(selector: string | ScopedSelector, value: unknown, index?: number | null): Promise<void>;
135
+ clearValue(selector: string | ScopedSelector, index?: number | null): Promise<void>;
136
+ select(selector: string | ScopedSelector, option: string, index?: number | null): Promise<void>;
137
+ hover(selector: string | ScopedSelector, index?: number | null): Promise<void>;
138
+ focus(selector: string | ScopedSelector, index?: number | null): Promise<void>;
139
+ dragDrop(sourceSelector: string | ScopedSelector, targetSelector: string, sourceIndex?: number | null): Promise<void>;
140
+ longPress(selector: string | ScopedSelector, duration: number, index?: number | null): Promise<void>;
141
+ scrollIntoView(selector: string | ScopedSelector, index?: number | null): Promise<void>;
142
+ getText(selector: string | ScopedSelector, index?: number | null): Promise<string>;
143
+ getValue(selector: string | ScopedSelector, index?: number | null): Promise<string>;
144
+ isDisplayed(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
145
+ isExisting(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
146
+ isEnabled(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
147
+ isSelected(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
148
+ isFocused(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
149
+ isClickable(selector: string | ScopedSelector, index?: number | null): Promise<boolean>;
150
+ hasClass(selector: string | ScopedSelector, className: string, index?: number | null): Promise<boolean>;
151
+ getAttribute(selector: string | ScopedSelector, name: string, index?: number | null): Promise<string | null>;
152
+ getCSSProperty(selector: string | ScopedSelector, name: string, index?: number | null): Promise<{
153
+ value: string;
154
+ }>;
155
+ count(selector: string | ScopedSelector): Promise<number>;
156
+ pressKey(key: string | number): Promise<void>;
157
+ scroll(options?: ScrollOptions): Promise<void>;
158
+ swipe(options: SwipeOptions): Promise<void>;
159
+ /**
160
+ * Grants an Android runtime permission on demand (e.g. right after confirming an in-app
161
+ * rationale dialog), via `adb shell pm grant` on the host. No-op on platforms without an
162
+ * OS-level runtime permission model (e.g. web).
163
+ */
164
+ grantPermission(name: string): Promise<void>;
165
+ /** Clears all active network interception rules. */
166
+ clearMocks(): void;
167
+ mockRules: MockRule[];
168
+ consoleErrors: string[];
169
+ recordedNetwork: CDPNetworkEntry[];
170
+ }
171
+ declare global {
172
+ var __TS_RUNTIME__: RuntimeContext;
173
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * # PlatformDriverBridge Contract
4
+ *
5
+ * The unified abstract driver contract consumed by `@testspectra/matchers` element proxies,
6
+ * fluent matchers, and the `Spectra` automation object. Every platform-specific driver
7
+ * (`WebCdpDriverBridge`, `AndroidTcpDriverBridge`) implements this interface so that the
8
+ * DSL layer contains zero platform branching.
9
+ */
10
+ export {};
package/dist/index.d.ts CHANGED
@@ -5,4 +5,5 @@
5
5
  * Canonical TypeScript contracts, action/assertion types, and ambient type definitions for TestSpectra.
6
6
  */
7
7
  export * from './types.js';
8
+ export * from './contract.js';
8
9
  export * from './proto.js';
package/dist/index.js CHANGED
@@ -5,4 +5,5 @@
5
5
  * Canonical TypeScript contracts, action/assertion types, and ambient type definitions for TestSpectra.
6
6
  */
7
7
  export * from './types.js';
8
+ export * from './contract.js';
8
9
  export * from './proto.js';
package/dist/proto.d.ts CHANGED
@@ -16,6 +16,14 @@ declare global {
16
16
  * Global step library injected by TestSpectra runner.
17
17
  */
18
18
  var Step: Record<string, any>;
19
+ /**
20
+ * Typed shape of `Spectra.env`. Empty by default — each `spectra.config.ts`
21
+ * `executionConfig.environmentVariables` key is merged in as a `readonly <KEY>: string` member
22
+ * by the CLI's generated `.testspectra/types/env.d.ts` (plain TypeScript interface merging, the
23
+ * same mechanism `@types/node` uses to let projects augment `NodeJS.ProcessEnv`).
24
+ */
25
+ interface SpectraEnv {
26
+ }
19
27
  namespace WebdriverIO {
20
28
  interface Browser extends SpectraBrowserBridge {
21
29
  }
@@ -1,4 +1,4 @@
1
- import { CollectionReceiverAssertions } from '../types.js';
1
+ import { CollectionReceiverAssertions, MultiElementMatcher } from '../types.js';
2
2
  import { SingleElementRunner } from './single.js';
3
3
  /**
4
4
  * Fluent assertion, filtering, and iteration runner for multi-element collections.
@@ -26,7 +26,6 @@ export declare class MultiElementRunner implements PromiseLike<void>, Collection
26
26
  private _selector;
27
27
  private _assertions;
28
28
  constructor(selector: string);
29
- private _pushAssertion;
30
29
  /**
31
30
  * Asserts that the collection contains exactly `count` elements.
32
31
  */
@@ -51,6 +50,19 @@ export declare class MultiElementRunner implements PromiseLike<void>, Collection
51
50
  * Asserts that the collection is not empty (contains at least 1 element).
52
51
  */
53
52
  shouldNotBeEmpty(): this;
53
+ /**
54
+ * Queues an assertion matcher against the multi-element collection.
55
+ *
56
+ * @param matcher - Multi-element matcher string (e.g. `'have.length'`, `'be.empty'`).
57
+ * @param args - Expected values or limits for the matcher.
58
+ * @example
59
+ * ```ts
60
+ * await Spectra.getAll(".product-card").should("have.length.greaterThan", 0);
61
+ * await Spectra.getAll(".error-badge").should("be.empty");
62
+ * ```
63
+ * @returns Current collection runner instance for chaining.
64
+ */
65
+ should(matcher: MultiElementMatcher, ...args: any[]): this;
54
66
  /**
55
67
  * Returns a `SingleElementRunner` targeting the element at the specified 0-based index.
56
68
  *
@@ -77,7 +89,7 @@ export declare class MultiElementRunner implements PromiseLike<void>, Collection
77
89
  *
78
90
  * @example
79
91
  * ```ts
80
- * await Spectra.getAll(".step-indicator").last().shouldHaveClass("current");
92
+ * await Spectra.getAll(".step-indicator").last().should("have.class", "current");
81
93
  * ```
82
94
  * @returns `SingleElementRunner` for the last element.
83
95
  */
@@ -89,7 +101,7 @@ export declare class MultiElementRunner implements PromiseLike<void>, Collection
89
101
  * @example
90
102
  * ```ts
91
103
  * await Spectra.getAll(".checkbox").each(async (item, index) => {
92
- * await item.shouldBeVisible();
104
+ * await item.should("be.visible");
93
105
  * });
94
106
  * ```
95
107
  */
@@ -29,46 +29,58 @@ export class MultiElementRunner {
29
29
  constructor(selector) {
30
30
  this._selector = selector;
31
31
  }
32
- _pushAssertion(matcher, args = []) {
33
- this._assertions.push({ matcher, args });
34
- return this;
35
- }
36
32
  // --- Collection Assertions ---
37
33
  /**
38
34
  * Asserts that the collection contains exactly `count` elements.
39
35
  */
40
36
  shouldHaveLength(count) {
41
- return this._pushAssertion('have.length', [count]);
37
+ return this.should('have.length', count);
42
38
  }
43
39
  /**
44
40
  * Asserts that the collection does not contain `count` elements.
45
41
  */
46
42
  shouldNotHaveLength(count) {
47
- return this._pushAssertion('not.have.length', [count]);
43
+ return this.should('not.have.length', count);
48
44
  }
49
45
  /**
50
46
  * Asserts that the collection contains more than `min` elements.
51
47
  */
52
48
  shouldHaveLengthGreaterThan(min) {
53
- return this._pushAssertion('have.length.greaterThan', [min]);
49
+ return this.should('have.length.greaterThan', min);
54
50
  }
55
51
  /**
56
52
  * Asserts that the collection contains less than `max` elements.
57
53
  */
58
54
  shouldHaveLengthLessThan(max) {
59
- return this._pushAssertion('have.length.lessThan', [max]);
55
+ return this.should('have.length.lessThan', max);
60
56
  }
61
57
  /**
62
58
  * Asserts that the collection is empty (contains 0 elements).
63
59
  */
64
60
  shouldBeEmpty() {
65
- return this._pushAssertion('be.empty');
61
+ return this.should('be.empty');
66
62
  }
67
63
  /**
68
64
  * Asserts that the collection is not empty (contains at least 1 element).
69
65
  */
70
66
  shouldNotBeEmpty() {
71
- return this._pushAssertion('not.be.empty');
67
+ return this.should('not.be.empty');
68
+ }
69
+ /**
70
+ * Queues an assertion matcher against the multi-element collection.
71
+ *
72
+ * @param matcher - Multi-element matcher string (e.g. `'have.length'`, `'be.empty'`).
73
+ * @param args - Expected values or limits for the matcher.
74
+ * @example
75
+ * ```ts
76
+ * await Spectra.getAll(".product-card").should("have.length.greaterThan", 0);
77
+ * await Spectra.getAll(".error-badge").should("be.empty");
78
+ * ```
79
+ * @returns Current collection runner instance for chaining.
80
+ */
81
+ should(matcher, ...args) {
82
+ this._assertions.push({ matcher, args });
83
+ return this;
72
84
  }
73
85
  // --- Item Navigation ---
74
86
  /**
@@ -110,7 +122,7 @@ export class MultiElementRunner {
110
122
  *
111
123
  * @example
112
124
  * ```ts
113
- * await Spectra.getAll(".step-indicator").last().shouldHaveClass("current");
125
+ * await Spectra.getAll(".step-indicator").last().should("have.class", "current");
114
126
  * ```
115
127
  * @returns `SingleElementRunner` for the last element.
116
128
  */
@@ -135,7 +147,7 @@ export class MultiElementRunner {
135
147
  * @example
136
148
  * ```ts
137
149
  * await Spectra.getAll(".checkbox").each(async (item, index) => {
138
- * await item.shouldBeVisible();
150
+ * await item.should("be.visible");
139
151
  * });
140
152
  * ```
141
153
  */
@@ -1,4 +1,4 @@
1
- import { ElementReceiverAssertions, ElementTarget, TypeOptions } from '../types.js';
1
+ import { ElementReceiverAssertions, ElementTarget, SingleElementMatcher, TypeOptions } from '../types.js';
2
2
  /**
3
3
  * Fluent interaction and assertion runner for a single element or browser context.
4
4
  *
@@ -122,7 +122,6 @@ export declare class SingleElementRunner implements PromiseLike<void>, ElementRe
122
122
  * Scrolls the target element into view.
123
123
  */
124
124
  scrollIntoView(): this;
125
- private _pushAssertion;
126
125
  /**
127
126
  * Asserts that the target element is displayed and visible to the user.
128
127
  */
@@ -227,6 +226,33 @@ export declare class SingleElementRunner implements PromiseLike<void>, ElementRe
227
226
  * Asserts that the target element does not have the specified computed CSS property value.
228
227
  */
229
228
  shouldNotHaveCss(property: string, value: string): this;
229
+ /**
230
+ * Queues an assertion matcher against the current target element or page.
231
+ *
232
+ * @param matcher - Single element matcher string (e.g. `'be.visible'`, `'have.text'`).
233
+ * @param args - Additional parameters for the matcher (e.g. expected text or value).
234
+ * @example
235
+ * ```ts
236
+ * await Spectra.get("#status-badge").should("be.visible");
237
+ * await Spectra.get("#username").should("have.value", "alice");
238
+ * await Spectra.get("#banner").should("have.class", "alert-success");
239
+ * ```
240
+ * @returns Current runner instance for method chaining.
241
+ */
242
+ should(matcher: SingleElementMatcher, ...args: any[]): this;
243
+ /**
244
+ * Queues an assertion matcher targeting a specific element.
245
+ *
246
+ * @param target - Custom element target for this specific assertion.
247
+ * @param matcher - Single element matcher string.
248
+ * @param args - Additional parameters for the matcher.
249
+ * @example
250
+ * ```ts
251
+ * await Spectra.should(LoginPage.submitBtn, "be.enabled");
252
+ * ```
253
+ * @returns Current runner instance for method chaining.
254
+ */
255
+ should(target: ElementTarget, matcher: SingleElementMatcher, ...args: any[]): this;
230
256
  /**
231
257
  * Standard Promise `then` implementation.
232
258
  * Executes queued action first (if any), followed sequentially by all queued assertions.
@@ -242,172 +242,191 @@ export class SingleElementRunner {
242
242
  };
243
243
  return this;
244
244
  }
245
- _pushAssertion(matcher, args = []) {
246
- this._assertions.push({
247
- target: this._target,
248
- matcher,
249
- args,
250
- });
251
- return this;
252
- }
253
245
  // --- Fluent Assertion Matchers ---
254
246
  /**
255
247
  * Asserts that the target element is displayed and visible to the user.
256
248
  */
257
249
  shouldBeVisible() {
258
- return this._pushAssertion('be.visible');
250
+ return this.should('be.visible');
259
251
  }
260
252
  /**
261
253
  * Asserts that the target element is not displayed or hidden from view.
262
254
  */
263
255
  shouldNotBeVisible() {
264
- return this._pushAssertion('not.be.visible');
256
+ return this.should('not.be.visible');
265
257
  }
266
258
  /**
267
259
  * Asserts that the target element exists in the DOM.
268
260
  */
269
261
  shouldExist() {
270
- return this._pushAssertion('exist');
262
+ return this.should('exist');
271
263
  }
272
264
  /**
273
265
  * Asserts that the target element does not exist in the DOM.
274
266
  */
275
267
  shouldNotExist() {
276
- return this._pushAssertion('not.exist');
268
+ return this.should('not.exist');
277
269
  }
278
270
  /**
279
271
  * Asserts that the target element is clickable.
280
272
  */
281
273
  shouldBeClickable() {
282
- return this._pushAssertion('be.clickable');
274
+ return this.should('be.clickable');
283
275
  }
284
276
  /**
285
277
  * Asserts that the target element is not clickable.
286
278
  */
287
279
  shouldNotBeClickable() {
288
- return this._pushAssertion('not.be.clickable');
280
+ return this.should('not.be.clickable');
289
281
  }
290
282
  /**
291
283
  * Asserts that the target element is enabled (not disabled).
292
284
  */
293
285
  shouldBeEnabled() {
294
- return this._pushAssertion('be.enabled');
286
+ return this.should('be.enabled');
295
287
  }
296
288
  /**
297
289
  * Asserts that the target element is disabled.
298
290
  */
299
291
  shouldBeDisabled() {
300
- return this._pushAssertion('be.disabled');
292
+ return this.should('be.disabled');
301
293
  }
302
294
  /**
303
295
  * Asserts that the target checkbox or radio element is checked.
304
296
  */
305
297
  shouldBeChecked() {
306
- return this._pushAssertion('be.checked');
298
+ return this.should('be.checked');
307
299
  }
308
300
  /**
309
301
  * Asserts that the target checkbox or radio element is not checked.
310
302
  */
311
303
  shouldNotBeChecked() {
312
- return this._pushAssertion('not.be.checked');
304
+ return this.should('not.be.checked');
313
305
  }
314
306
  /**
315
307
  * Asserts that the target element currently has focus.
316
308
  */
317
309
  shouldBeFocused() {
318
- return this._pushAssertion('be.focused');
310
+ return this.should('be.focused');
319
311
  }
320
312
  /**
321
313
  * Asserts that the target element does not have focus.
322
314
  */
323
315
  shouldNotBeFocused() {
324
- return this._pushAssertion('not.be.focused');
316
+ return this.should('not.be.focused');
325
317
  }
326
318
  /**
327
319
  * Asserts that the target element has exact inner text matching the expected value.
328
320
  */
329
321
  shouldHaveText(expected) {
330
- return this._pushAssertion('have.text', [expected]);
322
+ return this.should('have.text', expected);
331
323
  }
332
324
  /**
333
325
  * Asserts that the target element does not have inner text matching the expected value.
334
326
  */
335
327
  shouldNotHaveText(expected) {
336
- return this._pushAssertion('not.have.text', [expected]);
328
+ return this.should('not.have.text', expected);
337
329
  }
338
330
  /**
339
331
  * Asserts that the target element's text contains the given substring.
340
332
  */
341
333
  shouldContainText(substring) {
342
- return this._pushAssertion('contain.text', [substring]);
334
+ return this.should('contain.text', substring);
343
335
  }
344
336
  /**
345
337
  * Asserts that the target element's text does not contain the given substring.
346
338
  */
347
339
  shouldNotContainText(substring) {
348
- return this._pushAssertion('not.contain.text', [substring]);
340
+ return this.should('not.contain.text', substring);
349
341
  }
350
342
  /**
351
343
  * Asserts that the target input element has the exact expected value.
352
344
  */
353
345
  shouldHaveValue(value) {
354
- return this._pushAssertion('have.value', [value]);
346
+ return this.should('have.value', value);
355
347
  }
356
348
  /**
357
349
  * Asserts that the target input element does not have the expected value.
358
350
  */
359
351
  shouldNotHaveValue(value) {
360
- return this._pushAssertion('not.have.value', [value]);
352
+ return this.should('not.have.value', value);
361
353
  }
362
354
  /**
363
355
  * Asserts that the target input element's value contains the given substring.
364
356
  */
365
357
  shouldContainValue(substring) {
366
- return this._pushAssertion('contain.value', [substring]);
358
+ return this.should('contain.value', substring);
367
359
  }
368
360
  /**
369
361
  * Asserts that the target input element's value does not contain the given substring.
370
362
  */
371
363
  shouldNotContainValue(substring) {
372
- return this._pushAssertion('not.contain.value', [substring]);
364
+ return this.should('not.contain.value', substring);
373
365
  }
374
366
  /**
375
367
  * Asserts that the target element has the specified HTML attribute and optional value.
376
368
  */
377
369
  shouldHaveAttribute(name, value) {
378
- return value !== undefined
379
- ? this._pushAssertion('have.attr', [name, value])
380
- : this._pushAssertion('have.attr', [name]);
370
+ return value !== undefined ? this.should('have.attr', name, value) : this.should('have.attr', name);
381
371
  }
382
372
  /**
383
373
  * Asserts that the target element does not have the specified HTML attribute.
384
374
  */
385
375
  shouldNotHaveAttribute(name) {
386
- return this._pushAssertion('not.have.attr', [name]);
376
+ return this.should('not.have.attr', name);
387
377
  }
388
378
  /**
389
379
  * Asserts that the target element has the specified CSS class.
390
380
  */
391
381
  shouldHaveClass(className) {
392
- return this._pushAssertion('have.class', [className]);
382
+ return this.should('have.class', className);
393
383
  }
394
384
  /**
395
385
  * Asserts that the target element does not have the specified CSS class.
396
386
  */
397
387
  shouldNotHaveClass(className) {
398
- return this._pushAssertion('not.have.class', [className]);
388
+ return this.should('not.have.class', className);
399
389
  }
400
390
  /**
401
391
  * Asserts that the target element has the specified computed CSS property value.
402
392
  */
403
393
  shouldHaveCss(property, value) {
404
- return this._pushAssertion('have.css', [property, value]);
394
+ return this.should('have.css', property, value);
405
395
  }
406
396
  /**
407
397
  * Asserts that the target element does not have the specified computed CSS property value.
408
398
  */
409
399
  shouldNotHaveCss(property, value) {
410
- return this._pushAssertion('not.have.css', [property, value]);
400
+ return this.should('not.have.css', property, value);
401
+ }
402
+ should(a, b, ...rest) {
403
+ const isFirstArgMatcher = typeof a === 'string' &&
404
+ (a.includes('.') ||
405
+ a.includes('exist') ||
406
+ a.includes('visible') ||
407
+ a.includes('clickable') ||
408
+ a.includes('focused') ||
409
+ a.includes('enabled') ||
410
+ a.includes('disabled') ||
411
+ a.includes('selected') ||
412
+ a.includes('checked'));
413
+ if (b !== undefined && !isFirstArgMatcher) {
414
+ // Form: should(target, matcher, ...args)
415
+ this._assertions.push({
416
+ target: a,
417
+ matcher: b,
418
+ args: rest,
419
+ });
420
+ }
421
+ else {
422
+ // Form: should(matcher, ...args) -> on current target
423
+ this._assertions.push({
424
+ target: this._target,
425
+ matcher: a,
426
+ args: b !== undefined ? [b, ...rest] : rest,
427
+ });
428
+ }
429
+ return this;
411
430
  }
412
431
  // --- Execution when awaited ---
413
432
  /**
@@ -0,0 +1,11 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * # @testspectra/matchers - Semantic Natural Language Formatter
4
+ *
5
+ * Formats commands, actions, and assertions into clean, grammatically
6
+ * correct English sentences for real-time reporting.
7
+ */
8
+ export declare function formatSemanticTarget(target: any, isCollection?: boolean): string;
9
+ export declare function formatSemanticAction(action: string, target: any, args?: any[]): string;
10
+ export declare function formatSemanticMatcher(target: any, matcher: string, args?: any[], isCollection?: boolean): string;
11
+ export declare function formatSemanticBrowser(method: string, args?: any[]): string;