@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/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
  }
package/dist/reporter.js CHANGED
@@ -67,7 +67,19 @@ export function formatArgs(args) {
67
67
  */
68
68
  export async function trackCommand(category, commandText, fn) {
69
69
  const start = Date.now();
70
- const payload = typeof commandText === 'object' ? JSON.stringify(commandText) : commandText;
70
+ const payloadObj = typeof commandText === 'object' ? { ...commandText } : { text: commandText };
71
+ if (typeof globalThis !== 'undefined') {
72
+ const g = globalThis;
73
+ const currentTest = g.__CURRENT_TEST_TITLE__ || g.__CURRENT_TEST_ID__;
74
+ if (currentTest && !payloadObj.test) {
75
+ payloadObj.test = currentTest;
76
+ }
77
+ const workerId = g.__TESTSPECTRA_WORKER_ID__;
78
+ if (workerId !== undefined && payloadObj.workerId === undefined) {
79
+ payloadObj.workerId = workerId;
80
+ }
81
+ }
82
+ const payload = JSON.stringify(payloadObj);
71
83
  console.log(`[TESTSPECTRA_STEP_START] ${payload}`);
72
84
  try {
73
85
  const result = await fn();
@@ -84,9 +84,22 @@ export class MultiElementRunner {
84
84
  eq(index) {
85
85
  const targetSelector = `${this._selector}[${index}]`;
86
86
  const elPromise = (async () => {
87
- if (typeof $$ !== 'undefined') {
88
- const elements = await $$(this._selector);
89
- return elements[index];
87
+ const getElements = typeof $$ !== 'undefined' ? $$ : globalThis.$$;
88
+ if (typeof getElements === 'function') {
89
+ const elements = await getElements(this._selector);
90
+ if (typeof elements?.nth === 'function') {
91
+ return elements.nth(index);
92
+ }
93
+ if (index === 0 && typeof elements?.first === 'function') {
94
+ return elements.first();
95
+ }
96
+ if (elements && elements[index] !== undefined) {
97
+ return elements[index];
98
+ }
99
+ }
100
+ const getDollar = typeof $ !== 'undefined' ? $ : globalThis.$;
101
+ if (typeof getDollar === 'function') {
102
+ return getDollar(targetSelector);
90
103
  }
91
104
  return { selector: targetSelector };
92
105
  })();
@@ -117,10 +130,23 @@ export class MultiElementRunner {
117
130
  last() {
118
131
  const targetSelector = `${this._selector}[last()]`;
119
132
  const elPromise = (async () => {
120
- if (typeof $$ !== 'undefined') {
121
- const elements = await $$(this._selector);
133
+ const getElements = typeof $$ !== 'undefined' ? $$ : globalThis.$$;
134
+ if (typeof getElements === 'function') {
135
+ const elements = await getElements(this._selector);
136
+ if (typeof elements?.last === 'function') {
137
+ return elements.last();
138
+ }
139
+ if (typeof elements?.nth === 'function') {
140
+ return elements.nth(-1);
141
+ }
122
142
  const arr = Array.isArray(elements) ? elements : await elements;
123
- return arr[arr.length - 1];
143
+ if (arr && arr.length > 0) {
144
+ return arr[arr.length - 1];
145
+ }
146
+ }
147
+ const getDollar = typeof $ !== 'undefined' ? $ : globalThis.$;
148
+ if (typeof getDollar === 'function') {
149
+ return getDollar(targetSelector);
124
150
  }
125
151
  return { selector: targetSelector };
126
152
  })();
@@ -90,11 +90,11 @@ export declare class SingleElementRunner implements PromiseLike<void>, ElementRe
90
90
  */
91
91
  select(value: string): this;
92
92
  /**
93
- * Moves the mouse cursor over the target element (hover).
93
+ * Hovers over the target element.
94
94
  *
95
95
  * @example
96
96
  * ```ts
97
- * await Spectra.get("#dropdown-menu-trigger").hover();
97
+ * await Spectra.get("#dropdown-trigger").hover();
98
98
  * ```
99
99
  * @returns Current runner instance for method chaining.
100
100
  */
@@ -120,9 +120,17 @@ export class SingleElementRunner {
120
120
  this._action = async () => {
121
121
  const el = await resolveElement(target);
122
122
  if (options?.clearFirst) {
123
- await el.clearValue();
123
+ if (typeof el.clearValue === 'function')
124
+ await el.clearValue();
125
+ else if (typeof el.clear === 'function')
126
+ await el.clear();
124
127
  }
125
- await el.setValue(value);
128
+ if (typeof el.setValue === 'function')
129
+ await el.setValue(value);
130
+ else if (typeof el.fill === 'function')
131
+ await el.fill(value);
132
+ else if (typeof el.type === 'function')
133
+ await el.type(value);
126
134
  };
127
135
  return this;
128
136
  }
@@ -140,7 +148,10 @@ export class SingleElementRunner {
140
148
  this._actionPayload = { type: 'action', key: 'clear', target: getRawTarget(target), args: [] };
141
149
  this._action = async () => {
142
150
  const el = await resolveElement(target);
143
- await el.clearValue();
151
+ if (typeof el.clearValue === 'function')
152
+ await el.clearValue();
153
+ else if (typeof el.clear === 'function')
154
+ await el.clear();
144
155
  };
145
156
  return this;
146
157
  }
@@ -159,16 +170,19 @@ export class SingleElementRunner {
159
170
  this._actionPayload = { type: 'action', key: 'selectOption', target: getRawTarget(target), args: [value] };
160
171
  this._action = async () => {
161
172
  const el = await resolveElement(target);
162
- await el.selectByVisibleText(value);
173
+ if (typeof el.selectByVisibleText === 'function')
174
+ await el.selectByVisibleText(value);
175
+ else if (typeof el.selectOption === 'function')
176
+ await el.selectOption({ label: value });
163
177
  };
164
178
  return this;
165
179
  }
166
180
  /**
167
- * Moves the mouse cursor over the target element (hover).
181
+ * Hovers over the target element.
168
182
  *
169
183
  * @example
170
184
  * ```ts
171
- * await Spectra.get("#dropdown-menu-trigger").hover();
185
+ * await Spectra.get("#dropdown-trigger").hover();
172
186
  * ```
173
187
  * @returns Current runner instance for method chaining.
174
188
  */
@@ -177,7 +191,10 @@ export class SingleElementRunner {
177
191
  this._actionPayload = { type: 'action', key: 'hover', target: getRawTarget(target), args: [] };
178
192
  this._action = async () => {
179
193
  const el = await resolveElement(target);
180
- await el.moveTo();
194
+ if (typeof el.moveTo === 'function')
195
+ await el.moveTo();
196
+ else if (typeof el.hover === 'function')
197
+ await el.hover();
181
198
  };
182
199
  return this;
183
200
  }
@@ -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;
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @packageDocumentation
3
+ * # @testspectra/matchers - Semantic Natural Language Formatter
4
+ *
5
+ * Formats commands, actions, and assertions into clean, grammatically
6
+ * correct English sentences for real-time reporting.
7
+ */
8
+ export function formatSemanticTarget(target, isCollection = false) {
9
+ if (target === undefined || target === null)
10
+ return isCollection ? 'collection' : 'element';
11
+ if (typeof target === 'string') {
12
+ return isCollection ? `collection "${target}"` : `element "${target}"`;
13
+ }
14
+ if (typeof target === 'object') {
15
+ if (target.selector) {
16
+ return isCollection ? `collection "${target.selector}"` : `element "${target.selector}"`;
17
+ }
18
+ if (target.constructor && target.constructor.name && target.constructor.name !== 'Object') {
19
+ return `element "${target.constructor.name}"`;
20
+ }
21
+ }
22
+ return `element "${String(target)}"`;
23
+ }
24
+ export function formatSemanticAction(action, target, args = []) {
25
+ const targetStr = formatSemanticTarget(target, false);
26
+ const arg0 = args[0] !== undefined ? String(args[0]) : '';
27
+ switch (action) {
28
+ case 'type':
29
+ return `Type "${arg0}" into ${targetStr}`;
30
+ case 'click':
31
+ return `Click on ${targetStr}`;
32
+ case 'doubleClick':
33
+ return `Double-click on ${targetStr}`;
34
+ case 'rightClick':
35
+ return `Right-click on ${targetStr}`;
36
+ case 'check':
37
+ return `Check checkbox ${targetStr}`;
38
+ case 'uncheck':
39
+ return `Uncheck checkbox ${targetStr}`;
40
+ case 'selectOption':
41
+ return `Select option "${arg0}" in ${targetStr}`;
42
+ case 'clear':
43
+ return `Clear text in ${targetStr}`;
44
+ case 'hover':
45
+ return `Hover over ${targetStr}`;
46
+ case 'scrollIntoView':
47
+ return `Scroll ${targetStr} into view`;
48
+ case 'navigate':
49
+ return `Navigate to "${arg0}"`;
50
+ case 'wait':
51
+ return `Wait for ${arg0}ms`;
52
+ case 'pause':
53
+ return `Pause execution for ${arg0}ms`;
54
+ default:
55
+ return `Perform ${action} on ${targetStr}`;
56
+ }
57
+ }
58
+ export function formatSemanticMatcher(target, matcher, args = [], isCollection = false) {
59
+ const targetStr = formatSemanticTarget(target, isCollection);
60
+ const arg0 = args[0] !== undefined ? String(args[0]) : '';
61
+ const arg1 = args[1] !== undefined ? String(args[1]) : '';
62
+ switch (matcher) {
63
+ case 'be.visible':
64
+ return `Expect ${targetStr} to be visible`;
65
+ case 'not.be.visible':
66
+ return `Expect ${targetStr} not to be visible`;
67
+ case 'exist':
68
+ return `Expect ${targetStr} to exist in DOM`;
69
+ case 'not.exist':
70
+ return `Expect ${targetStr} not to exist in DOM`;
71
+ case 'be.enabled':
72
+ return `Expect ${targetStr} to be enabled`;
73
+ case 'be.disabled':
74
+ return `Expect ${targetStr} to be disabled`;
75
+ case 'be.checked':
76
+ return `Expect ${targetStr} to be checked`;
77
+ case 'not.be.checked':
78
+ return `Expect ${targetStr} not to be checked`;
79
+ case 'have.text':
80
+ return `Expect ${targetStr} to have text "${arg0}"`;
81
+ case 'not.have.text':
82
+ return `Expect ${targetStr} not to have text "${arg0}"`;
83
+ case 'contain.text':
84
+ return `Expect ${targetStr} to contain text "${arg0}"`;
85
+ case 'not.contain.text':
86
+ return `Expect ${targetStr} not to contain text "${arg0}"`;
87
+ case 'have.value':
88
+ return `Expect ${targetStr} to have value "${arg0}"`;
89
+ case 'not.have.value':
90
+ return `Expect ${targetStr} not to have value "${arg0}"`;
91
+ case 'contain.value':
92
+ return `Expect ${targetStr} to contain value "${arg0}"`;
93
+ case 'not.contain.value':
94
+ return `Expect ${targetStr} not to contain value "${arg0}"`;
95
+ case 'have.class':
96
+ return `Expect ${targetStr} to have class "${arg0}"`;
97
+ case 'not.have.class':
98
+ return `Expect ${targetStr} not to have class "${arg0}"`;
99
+ case 'have.attr':
100
+ return args.length >= 2
101
+ ? `Expect ${targetStr} to have attribute "${arg0}" = "${arg1}"`
102
+ : `Expect ${targetStr} to have attribute "${arg0}"`;
103
+ case 'not.have.attr':
104
+ return `Expect ${targetStr} not to have attribute "${arg0}"`;
105
+ case 'be.focused':
106
+ return `Expect ${targetStr} to be focused`;
107
+ case 'be.selected':
108
+ return `Expect ${targetStr} to be selected`;
109
+ case 'be.empty':
110
+ return `Expect ${targetStr} to be empty`;
111
+ case 'not.be.empty':
112
+ return `Expect ${targetStr} not to be empty`;
113
+ case 'have.length':
114
+ return `Expect ${targetStr} count to equal ${arg0}`;
115
+ case 'have.length.greaterThan':
116
+ return `Expect ${targetStr} count to be greater than ${arg0}`;
117
+ case 'have.length.lessThan':
118
+ return `Expect ${targetStr} count to be less than ${arg0}`;
119
+ case 'have.length.atLeast':
120
+ return `Expect ${targetStr} count to be at least ${arg0}`;
121
+ case 'have.length.atMost':
122
+ return `Expect ${targetStr} count to be at most ${arg0}`;
123
+ default:
124
+ return `Expect ${targetStr} to match ${matcher}`;
125
+ }
126
+ }
127
+ export function formatSemanticBrowser(method, args = []) {
128
+ const arg0 = args[0] !== undefined ? String(args[0]) : '';
129
+ switch (method) {
130
+ case 'shouldContainTitle':
131
+ return `Expect browser title to contain "${arg0}"`;
132
+ case 'shouldHaveTitle':
133
+ return `Expect browser title to be "${arg0}"`;
134
+ case 'shouldContainUrl':
135
+ return `Expect browser URL to contain "${arg0}"`;
136
+ case 'shouldHaveUrl':
137
+ return `Expect browser URL to be "${arg0}"`;
138
+ default:
139
+ return `Expect browser to ${method} "${arg0}"`;
140
+ }
141
+ }