@testspectra/matchers 1.1.0 → 1.1.8-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/__tests__/matcher-contracts.test.d.ts +1 -0
- package/dist/__tests__/matcher-contracts.test.js +498 -0
- package/dist/contract.d.ts +173 -0
- package/dist/contract.js +10 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/matchers.d.ts +10 -3
- package/dist/matchers.js +576 -135
- package/dist/proto.d.ts +8 -0
- package/dist/reporter.js +13 -1
- package/dist/runner/collection.js +32 -6
- package/dist/runner/single.d.ts +2 -2
- package/dist/runner/single.js +24 -7
- package/dist/semantic.d.ts +11 -0
- package/dist/semantic.js +141 -0
- package/dist/types.d.ts +246 -115
- package/package.json +11 -10
- package/src/contract.ts +223 -0
- package/src/index.ts +1 -0
- package/src/proto.ts +8 -0
- package/src/runtime/assertions.ts +453 -0
- package/src/runtime/element_actions.ts +178 -0
- package/src/runtime/element_proxy.ts +200 -0
- package/src/runtime/element_state.ts +94 -0
- package/src/runtime/spectra.ts +110 -0
- package/src/types.ts +251 -124
- package/tsconfig.json +1 -1
package/dist/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
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
-
|
|
121
|
-
|
|
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
|
-
|
|
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
|
})();
|
package/dist/runner/single.d.ts
CHANGED
|
@@ -90,11 +90,11 @@ export declare class SingleElementRunner implements PromiseLike<void>, ElementRe
|
|
|
90
90
|
*/
|
|
91
91
|
select(value: string): this;
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* Hovers over the target element.
|
|
94
94
|
*
|
|
95
95
|
* @example
|
|
96
96
|
* ```ts
|
|
97
|
-
* await Spectra.get("#dropdown-
|
|
97
|
+
* await Spectra.get("#dropdown-trigger").hover();
|
|
98
98
|
* ```
|
|
99
99
|
* @returns Current runner instance for method chaining.
|
|
100
100
|
*/
|
package/dist/runner/single.js
CHANGED
|
@@ -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
|
-
|
|
123
|
+
if (typeof el.clearValue === 'function')
|
|
124
|
+
await el.clearValue();
|
|
125
|
+
else if (typeof el.clear === 'function')
|
|
126
|
+
await el.clear();
|
|
124
127
|
}
|
|
125
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
*
|
|
181
|
+
* Hovers over the target element.
|
|
168
182
|
*
|
|
169
183
|
* @example
|
|
170
184
|
* ```ts
|
|
171
|
-
* await Spectra.get("#dropdown-
|
|
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
|
-
|
|
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;
|
package/dist/semantic.js
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @packageDocumentation
|
|
3
|
+
* # @testspectra/matchers - Semantic Natural Language Formatter
|
|
4
|
+
*
|
|
5
|
+
* Formats commands, actions, and assertions into clean, grammatically
|
|
6
|
+
* correct English sentences for real-time reporting.
|
|
7
|
+
*/
|
|
8
|
+
export function formatSemanticTarget(target, isCollection = false) {
|
|
9
|
+
if (target === undefined || target === null)
|
|
10
|
+
return isCollection ? 'collection' : 'element';
|
|
11
|
+
if (typeof target === 'string') {
|
|
12
|
+
return isCollection ? `collection "${target}"` : `element "${target}"`;
|
|
13
|
+
}
|
|
14
|
+
if (typeof target === 'object') {
|
|
15
|
+
if (target.selector) {
|
|
16
|
+
return isCollection ? `collection "${target.selector}"` : `element "${target.selector}"`;
|
|
17
|
+
}
|
|
18
|
+
if (target.constructor && target.constructor.name && target.constructor.name !== 'Object') {
|
|
19
|
+
return `element "${target.constructor.name}"`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return `element "${String(target)}"`;
|
|
23
|
+
}
|
|
24
|
+
export function formatSemanticAction(action, target, args = []) {
|
|
25
|
+
const targetStr = formatSemanticTarget(target, false);
|
|
26
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
27
|
+
switch (action) {
|
|
28
|
+
case 'type':
|
|
29
|
+
return `Type "${arg0}" into ${targetStr}`;
|
|
30
|
+
case 'click':
|
|
31
|
+
return `Click on ${targetStr}`;
|
|
32
|
+
case 'doubleClick':
|
|
33
|
+
return `Double-click on ${targetStr}`;
|
|
34
|
+
case 'rightClick':
|
|
35
|
+
return `Right-click on ${targetStr}`;
|
|
36
|
+
case 'check':
|
|
37
|
+
return `Check checkbox ${targetStr}`;
|
|
38
|
+
case 'uncheck':
|
|
39
|
+
return `Uncheck checkbox ${targetStr}`;
|
|
40
|
+
case 'selectOption':
|
|
41
|
+
return `Select option "${arg0}" in ${targetStr}`;
|
|
42
|
+
case 'clear':
|
|
43
|
+
return `Clear text in ${targetStr}`;
|
|
44
|
+
case 'hover':
|
|
45
|
+
return `Hover over ${targetStr}`;
|
|
46
|
+
case 'scrollIntoView':
|
|
47
|
+
return `Scroll ${targetStr} into view`;
|
|
48
|
+
case 'navigate':
|
|
49
|
+
return `Navigate to "${arg0}"`;
|
|
50
|
+
case 'wait':
|
|
51
|
+
return `Wait for ${arg0}ms`;
|
|
52
|
+
case 'pause':
|
|
53
|
+
return `Pause execution for ${arg0}ms`;
|
|
54
|
+
default:
|
|
55
|
+
return `Perform ${action} on ${targetStr}`;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export function formatSemanticMatcher(target, matcher, args = [], isCollection = false) {
|
|
59
|
+
const targetStr = formatSemanticTarget(target, isCollection);
|
|
60
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
61
|
+
const arg1 = args[1] !== undefined ? String(args[1]) : '';
|
|
62
|
+
switch (matcher) {
|
|
63
|
+
case 'be.visible':
|
|
64
|
+
return `Expect ${targetStr} to be visible`;
|
|
65
|
+
case 'not.be.visible':
|
|
66
|
+
return `Expect ${targetStr} not to be visible`;
|
|
67
|
+
case 'exist':
|
|
68
|
+
return `Expect ${targetStr} to exist in DOM`;
|
|
69
|
+
case 'not.exist':
|
|
70
|
+
return `Expect ${targetStr} not to exist in DOM`;
|
|
71
|
+
case 'be.enabled':
|
|
72
|
+
return `Expect ${targetStr} to be enabled`;
|
|
73
|
+
case 'be.disabled':
|
|
74
|
+
return `Expect ${targetStr} to be disabled`;
|
|
75
|
+
case 'be.checked':
|
|
76
|
+
return `Expect ${targetStr} to be checked`;
|
|
77
|
+
case 'not.be.checked':
|
|
78
|
+
return `Expect ${targetStr} not to be checked`;
|
|
79
|
+
case 'have.text':
|
|
80
|
+
return `Expect ${targetStr} to have text "${arg0}"`;
|
|
81
|
+
case 'not.have.text':
|
|
82
|
+
return `Expect ${targetStr} not to have text "${arg0}"`;
|
|
83
|
+
case 'contain.text':
|
|
84
|
+
return `Expect ${targetStr} to contain text "${arg0}"`;
|
|
85
|
+
case 'not.contain.text':
|
|
86
|
+
return `Expect ${targetStr} not to contain text "${arg0}"`;
|
|
87
|
+
case 'have.value':
|
|
88
|
+
return `Expect ${targetStr} to have value "${arg0}"`;
|
|
89
|
+
case 'not.have.value':
|
|
90
|
+
return `Expect ${targetStr} not to have value "${arg0}"`;
|
|
91
|
+
case 'contain.value':
|
|
92
|
+
return `Expect ${targetStr} to contain value "${arg0}"`;
|
|
93
|
+
case 'not.contain.value':
|
|
94
|
+
return `Expect ${targetStr} not to contain value "${arg0}"`;
|
|
95
|
+
case 'have.class':
|
|
96
|
+
return `Expect ${targetStr} to have class "${arg0}"`;
|
|
97
|
+
case 'not.have.class':
|
|
98
|
+
return `Expect ${targetStr} not to have class "${arg0}"`;
|
|
99
|
+
case 'have.attr':
|
|
100
|
+
return args.length >= 2
|
|
101
|
+
? `Expect ${targetStr} to have attribute "${arg0}" = "${arg1}"`
|
|
102
|
+
: `Expect ${targetStr} to have attribute "${arg0}"`;
|
|
103
|
+
case 'not.have.attr':
|
|
104
|
+
return `Expect ${targetStr} not to have attribute "${arg0}"`;
|
|
105
|
+
case 'be.focused':
|
|
106
|
+
return `Expect ${targetStr} to be focused`;
|
|
107
|
+
case 'be.selected':
|
|
108
|
+
return `Expect ${targetStr} to be selected`;
|
|
109
|
+
case 'be.empty':
|
|
110
|
+
return `Expect ${targetStr} to be empty`;
|
|
111
|
+
case 'not.be.empty':
|
|
112
|
+
return `Expect ${targetStr} not to be empty`;
|
|
113
|
+
case 'have.length':
|
|
114
|
+
return `Expect ${targetStr} count to equal ${arg0}`;
|
|
115
|
+
case 'have.length.greaterThan':
|
|
116
|
+
return `Expect ${targetStr} count to be greater than ${arg0}`;
|
|
117
|
+
case 'have.length.lessThan':
|
|
118
|
+
return `Expect ${targetStr} count to be less than ${arg0}`;
|
|
119
|
+
case 'have.length.atLeast':
|
|
120
|
+
return `Expect ${targetStr} count to be at least ${arg0}`;
|
|
121
|
+
case 'have.length.atMost':
|
|
122
|
+
return `Expect ${targetStr} count to be at most ${arg0}`;
|
|
123
|
+
default:
|
|
124
|
+
return `Expect ${targetStr} to match ${matcher}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
export function formatSemanticBrowser(method, args = []) {
|
|
128
|
+
const arg0 = args[0] !== undefined ? String(args[0]) : '';
|
|
129
|
+
switch (method) {
|
|
130
|
+
case 'shouldContainTitle':
|
|
131
|
+
return `Expect browser title to contain "${arg0}"`;
|
|
132
|
+
case 'shouldHaveTitle':
|
|
133
|
+
return `Expect browser title to be "${arg0}"`;
|
|
134
|
+
case 'shouldContainUrl':
|
|
135
|
+
return `Expect browser URL to contain "${arg0}"`;
|
|
136
|
+
case 'shouldHaveUrl':
|
|
137
|
+
return `Expect browser URL to be "${arg0}"`;
|
|
138
|
+
default:
|
|
139
|
+
return `Expect browser to ${method} "${arg0}"`;
|
|
140
|
+
}
|
|
141
|
+
}
|