@allwright.dev/vitest 0.1.24 → 0.1.26
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/README.md +6 -1
- package/dist/expectations.d.ts +20 -8
- package/dist/expectations.js +83 -18
- package/dist/index.js +30 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -289,13 +289,18 @@ await expect(page.locator('select')).toHaveSelectedOptions(['a', 'b']);
|
|
|
289
289
|
await expect(page.locator('select')).toHaveSelectedOptions([{ value: 'a', label: /Alpha/ }]);
|
|
290
290
|
await expect(page.locator('textarea')).toHaveSelectedText('selected words');
|
|
291
291
|
await expect(page.locator('input[type=checkbox]')).toBeChecked();
|
|
292
|
+
await expect(page.locator('button[type=submit]')).toBeEnabled();
|
|
293
|
+
await expect(page.locator('input')).toBeEditable();
|
|
294
|
+
await expect(page.locator('input')).toBeFocused();
|
|
295
|
+
await expect(page.locator('.spinner')).toBeHidden();
|
|
296
|
+
await expect(page.locator('main')).toBeAttached();
|
|
292
297
|
await expect(page.locator('h1')).toHaveText('Welcome');
|
|
293
298
|
await expect(page.locator('a')).toHaveAttribute('href', /dashboard/);
|
|
294
299
|
await expect(page.locator('button')).toHaveBoundingBox({ width: 100, height: 40 });
|
|
295
300
|
await expect(page.locator('input[type=radio]')).not().toBeChecked();
|
|
296
301
|
```
|
|
297
302
|
|
|
298
|
-
Every element matcher also has a page form, such as `expect(page).toHaveValue('#name', 'Alice')` or `expect(
|
|
303
|
+
Every element matcher also has a page/app form, such as `expect(page).toHaveValue('#name', 'Alice')` or `expect(androidApp).toBeEnabled('text="Continue"')`. Value, checked-state, attribute, bounding-box, attached/hidden, enabled/disabled, focused, and editable matchers work across web, Android, iOS, macOS, and Windows. URL, selected-option, and selected-text matchers remain web-only.
|
|
299
304
|
|
|
300
305
|
All Allwright page/app/locator expectations retry until they match or the timeout expires, including `.not` and `.not()`. Defaults come from the shared assertion configuration, falling back to 5 seconds and a 100 ms interval. Override these with `{ timeoutMs, intervalMs }`. In-flight reads and nested command timeout hints are bounded by the remaining assertion budget; `timeoutMs: 0` makes one observation without polling. Ordinary assertions on already captured values, such as `expect(await page.url()).toBe(...)`, use standard Vitest behavior and do not re-read the page.
|
|
301
306
|
|
package/dist/expectations.d.ts
CHANGED
|
@@ -15,6 +15,16 @@ export interface MobilePageExpectMatchers {
|
|
|
15
15
|
toContainText(selector: string, expected: string | RegExp, options?: TextExpectationOptions): Promise<void>;
|
|
16
16
|
toHaveCount(selector: string, expected: number, options?: RetryExpectationOptions): Promise<void>;
|
|
17
17
|
toBeVisible(selector: string, options?: VisibleExpectationOptions): Promise<void>;
|
|
18
|
+
toBeHidden(selector: string, options?: VisibleExpectationOptions): Promise<void>;
|
|
19
|
+
toBeAttached(selector: string, options?: TextExpectationOptions): Promise<void>;
|
|
20
|
+
toBeEnabled(selector: string, options?: TextExpectationOptions): Promise<void>;
|
|
21
|
+
toBeDisabled(selector: string, options?: TextExpectationOptions): Promise<void>;
|
|
22
|
+
toBeFocused(selector: string, options?: TextExpectationOptions): Promise<void>;
|
|
23
|
+
toBeEditable(selector: string, options?: TextExpectationOptions): Promise<void>;
|
|
24
|
+
toHaveValue(selector: string, expected: string | RegExp, options?: TextExpectationOptions): Promise<void>;
|
|
25
|
+
toBeChecked(selector: string, options?: TextExpectationOptions): Promise<void>;
|
|
26
|
+
toHaveAttribute(selector: string, name: string, expected: string | RegExp | null, options?: TextExpectationOptions): Promise<void>;
|
|
27
|
+
toHaveBoundingBox(selector: string, expected: Partial<BoundingBox> | null, options?: TextExpectationOptions): Promise<void>;
|
|
18
28
|
}
|
|
19
29
|
export interface MobileLocatorExpectMatchers {
|
|
20
30
|
readonly not: MobileLocatorExpectMatchers & (() => MobileLocatorExpectMatchers);
|
|
@@ -22,6 +32,16 @@ export interface MobileLocatorExpectMatchers {
|
|
|
22
32
|
toContainText(expected: string | RegExp, options?: TextExpectationOptions): Promise<void>;
|
|
23
33
|
toHaveCount(expected: number, options?: RetryExpectationOptions): Promise<void>;
|
|
24
34
|
toBeVisible(options?: VisibleExpectationOptions): Promise<void>;
|
|
35
|
+
toBeHidden(options?: VisibleExpectationOptions): Promise<void>;
|
|
36
|
+
toBeAttached(options?: TextExpectationOptions): Promise<void>;
|
|
37
|
+
toBeEnabled(options?: TextExpectationOptions): Promise<void>;
|
|
38
|
+
toBeDisabled(options?: TextExpectationOptions): Promise<void>;
|
|
39
|
+
toBeFocused(options?: TextExpectationOptions): Promise<void>;
|
|
40
|
+
toBeEditable(options?: TextExpectationOptions): Promise<void>;
|
|
41
|
+
toHaveValue(expected: string | RegExp, options?: TextExpectationOptions): Promise<void>;
|
|
42
|
+
toBeChecked(options?: TextExpectationOptions): Promise<void>;
|
|
43
|
+
toHaveAttribute(name: string, expected: string | RegExp | null, options?: TextExpectationOptions): Promise<void>;
|
|
44
|
+
toHaveBoundingBox(expected: Partial<BoundingBox> | null, options?: TextExpectationOptions): Promise<void>;
|
|
25
45
|
}
|
|
26
46
|
export type SelectedOptionExpectation = string | RegExp | {
|
|
27
47
|
value?: string | RegExp;
|
|
@@ -30,22 +50,14 @@ export type SelectedOptionExpectation = string | RegExp | {
|
|
|
30
50
|
};
|
|
31
51
|
export interface LocatorExpectMatchers extends MobileLocatorExpectMatchers {
|
|
32
52
|
readonly not: LocatorExpectMatchers & (() => LocatorExpectMatchers);
|
|
33
|
-
toHaveValue(expected: string | RegExp, options?: TextExpectationOptions): Promise<void>;
|
|
34
53
|
toHaveSelectedOptions(expected: SelectedOptionExpectation[], options?: TextExpectationOptions): Promise<void>;
|
|
35
54
|
toHaveSelectedText(expected: string | RegExp | null, options?: TextExpectationOptions): Promise<void>;
|
|
36
|
-
toBeChecked(options?: TextExpectationOptions): Promise<void>;
|
|
37
|
-
toHaveAttribute(name: string, expected: string | RegExp | null, options?: TextExpectationOptions): Promise<void>;
|
|
38
|
-
toHaveBoundingBox(expected: Partial<BoundingBox> | null, options?: TextExpectationOptions): Promise<void>;
|
|
39
55
|
}
|
|
40
56
|
export interface PageExpectMatchers extends MobilePageExpectMatchers {
|
|
41
57
|
readonly not: PageExpectMatchers & (() => PageExpectMatchers);
|
|
42
58
|
toHaveURL(expected: string | RegExp, options?: TextExpectationOptions): Promise<void>;
|
|
43
|
-
toHaveValue(selector: string, expected: string | RegExp, options?: TextExpectationOptions): Promise<void>;
|
|
44
59
|
toHaveSelectedOptions(selector: string, expected: SelectedOptionExpectation[], options?: TextExpectationOptions): Promise<void>;
|
|
45
60
|
toHaveSelectedText(selector: string, expected: string | RegExp | null, options?: TextExpectationOptions): Promise<void>;
|
|
46
|
-
toBeChecked(selector: string, options?: TextExpectationOptions): Promise<void>;
|
|
47
|
-
toHaveAttribute(selector: string, name: string, expected: string | RegExp | null, options?: TextExpectationOptions): Promise<void>;
|
|
48
|
-
toHaveBoundingBox(selector: string, expected: Partial<BoundingBox> | null, options?: TextExpectationOptions): Promise<void>;
|
|
49
61
|
}
|
|
50
62
|
type Defaults = () => RetryExpectationOptions;
|
|
51
63
|
type AnyLocator = Locator | NativeAppLocator;
|
package/dist/expectations.js
CHANGED
|
@@ -57,11 +57,20 @@ async function isVisible(locator, command) {
|
|
|
57
57
|
return true;
|
|
58
58
|
}
|
|
59
59
|
catch (error) {
|
|
60
|
-
if (error instanceof Error && error.message.startsWith("selector found but not visible: ")
|
|
60
|
+
if (error instanceof Error && (error.message.startsWith("selector found but not visible: ")
|
|
61
|
+
|| error.message.includes("did not become visible")))
|
|
61
62
|
return false;
|
|
62
63
|
throw error;
|
|
63
64
|
}
|
|
64
65
|
}
|
|
66
|
+
async function nativeOrWebState(locator, state, command) {
|
|
67
|
+
const attribute = "filter" in locator ? `__allwright_state_${state}` : state;
|
|
68
|
+
const value = await locator.getAttribute(attribute, command);
|
|
69
|
+
if (value !== "true" && value !== "false") {
|
|
70
|
+
throw new Error(`Element did not expose a ${state} state`);
|
|
71
|
+
}
|
|
72
|
+
return value === "true";
|
|
73
|
+
}
|
|
65
74
|
export function createLocatorExpect(locator, defaults, negated = false) {
|
|
66
75
|
const assertion = (value) => negated ? vitestExpect(value).not : vitestExpect(value);
|
|
67
76
|
const common = {
|
|
@@ -94,18 +103,64 @@ export function createLocatorExpect(locator, defaults, negated = false) {
|
|
|
94
103
|
assertion(await isVisible(locator, command)).toBe(true);
|
|
95
104
|
}, options, defaults());
|
|
96
105
|
},
|
|
106
|
+
async toBeHidden(options = {}) {
|
|
107
|
+
await retryExpectation(async (command) => {
|
|
108
|
+
assertion(await isVisible(locator, command)).toBe(false);
|
|
109
|
+
}, options, defaults());
|
|
110
|
+
},
|
|
111
|
+
async toBeAttached(options = {}) {
|
|
112
|
+
await retryExpectation(async (command) => {
|
|
113
|
+
assertion((await locator.count(command)) > 0).toBe(true);
|
|
114
|
+
}, options, defaults());
|
|
115
|
+
},
|
|
116
|
+
async toBeEnabled(options = {}) {
|
|
117
|
+
await retryExpectation(async (command) => {
|
|
118
|
+
assertion(await nativeOrWebState(locator, "enabled", command)).toBe(true);
|
|
119
|
+
}, options, defaults());
|
|
120
|
+
},
|
|
121
|
+
async toBeDisabled(options = {}) {
|
|
122
|
+
await retryExpectation(async (command) => {
|
|
123
|
+
assertion(await nativeOrWebState(locator, "enabled", command)).toBe(false);
|
|
124
|
+
}, options, defaults());
|
|
125
|
+
},
|
|
126
|
+
async toBeFocused(options = {}) {
|
|
127
|
+
await retryExpectation(async (command) => {
|
|
128
|
+
assertion(await nativeOrWebState(locator, "focused", command)).toBe(true);
|
|
129
|
+
}, options, defaults());
|
|
130
|
+
},
|
|
131
|
+
async toBeEditable(options = {}) {
|
|
132
|
+
await retryExpectation(async (command) => {
|
|
133
|
+
assertion(await nativeOrWebState(locator, "editable", command)).toBe(true);
|
|
134
|
+
}, options, defaults());
|
|
135
|
+
},
|
|
136
|
+
async toHaveValue(expected, options = {}) {
|
|
137
|
+
await retryExpectation(async (command) => assertion(await locator.inputValue(command)).toEqual(expectedText(expected)), options, defaults());
|
|
138
|
+
},
|
|
139
|
+
async toBeChecked(options = {}) {
|
|
140
|
+
await retryExpectation(async (command) => assertion(await locator.isChecked(command)).toBe(true), options, defaults());
|
|
141
|
+
},
|
|
142
|
+
async toHaveAttribute(name, expected, options = {}) {
|
|
143
|
+
await retryExpectation(async (command) => assertion(await locator.getAttribute(name, command)).toEqual(expectedText(expected)), options, defaults());
|
|
144
|
+
},
|
|
145
|
+
async toHaveBoundingBox(expected, options = {}) {
|
|
146
|
+
await retryExpectation(async (command) => assertion(await locator.boundingBox(command)).toEqual(expected === null ? null : vitestExpect.objectContaining(expected)), options, defaults());
|
|
147
|
+
},
|
|
97
148
|
};
|
|
98
|
-
if (!("
|
|
149
|
+
if (!("selectedOptions" in locator))
|
|
99
150
|
return common;
|
|
100
151
|
const web = {
|
|
101
152
|
toHaveText: common.toHaveText,
|
|
102
153
|
toContainText: common.toContainText,
|
|
103
154
|
toHaveCount: common.toHaveCount,
|
|
104
155
|
toBeVisible: common.toBeVisible,
|
|
156
|
+
toBeHidden: common.toBeHidden,
|
|
157
|
+
toBeAttached: common.toBeAttached,
|
|
158
|
+
toBeEnabled: common.toBeEnabled,
|
|
159
|
+
toBeDisabled: common.toBeDisabled,
|
|
160
|
+
toBeFocused: common.toBeFocused,
|
|
161
|
+
toBeEditable: common.toBeEditable,
|
|
105
162
|
get not() { return callableMatchers(createLocatorExpect(locator, defaults, !negated)); },
|
|
106
|
-
|
|
107
|
-
await retryExpectation(async (command) => assertion(await locator.inputValue(command)).toEqual(expectedText(expected)), options, defaults());
|
|
108
|
-
},
|
|
163
|
+
toHaveValue: common.toHaveValue,
|
|
109
164
|
async toHaveSelectedOptions(expected, options = {}) {
|
|
110
165
|
await retryExpectation(async (command) => {
|
|
111
166
|
const wanted = expected.map(option => {
|
|
@@ -123,15 +178,9 @@ export function createLocatorExpect(locator, defaults, negated = false) {
|
|
|
123
178
|
async toHaveSelectedText(expected, options = {}) {
|
|
124
179
|
await retryExpectation(async (command) => assertion(await locator.selectedText(command)).toEqual(expectedText(expected)), options, defaults());
|
|
125
180
|
},
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
async toHaveAttribute(name, expected, options = {}) {
|
|
130
|
-
await retryExpectation(async (command) => assertion(await locator.getAttribute(name, command)).toEqual(expectedText(expected)), options, defaults());
|
|
131
|
-
},
|
|
132
|
-
async toHaveBoundingBox(expected, options = {}) {
|
|
133
|
-
await retryExpectation(async (command) => assertion(await locator.boundingBox(command)).toEqual(expected === null ? null : vitestExpect.objectContaining(expected)), options, defaults());
|
|
134
|
-
},
|
|
181
|
+
toBeChecked: common.toBeChecked,
|
|
182
|
+
toHaveAttribute: common.toHaveAttribute,
|
|
183
|
+
toHaveBoundingBox: common.toHaveBoundingBox,
|
|
135
184
|
};
|
|
136
185
|
return web;
|
|
137
186
|
}
|
|
@@ -143,6 +192,16 @@ export function createPageExpect(page, defaults, negated = false) {
|
|
|
143
192
|
toContainText: (selector, expected, options) => matcher(selector).toContainText(expected, options),
|
|
144
193
|
toHaveCount: (selector, expected, options) => matcher(selector).toHaveCount(expected, options),
|
|
145
194
|
toBeVisible: (selector, options) => matcher(selector).toBeVisible(options),
|
|
195
|
+
toBeHidden: (selector, options) => matcher(selector).toBeHidden(options),
|
|
196
|
+
toBeAttached: (selector, options) => matcher(selector).toBeAttached(options),
|
|
197
|
+
toBeEnabled: (selector, options) => matcher(selector).toBeEnabled(options),
|
|
198
|
+
toBeDisabled: (selector, options) => matcher(selector).toBeDisabled(options),
|
|
199
|
+
toBeFocused: (selector, options) => matcher(selector).toBeFocused(options),
|
|
200
|
+
toBeEditable: (selector, options) => matcher(selector).toBeEditable(options),
|
|
201
|
+
toHaveValue: (selector, expected, options) => matcher(selector).toHaveValue(expected, options),
|
|
202
|
+
toBeChecked: (selector, options) => matcher(selector).toBeChecked(options),
|
|
203
|
+
toHaveAttribute: (selector, name, expected, options) => matcher(selector).toHaveAttribute(name, expected, options),
|
|
204
|
+
toHaveBoundingBox: (selector, expected, options) => matcher(selector).toHaveBoundingBox(expected, options),
|
|
146
205
|
};
|
|
147
206
|
if (!("url" in page))
|
|
148
207
|
return common;
|
|
@@ -152,6 +211,12 @@ export function createPageExpect(page, defaults, negated = false) {
|
|
|
152
211
|
toContainText: common.toContainText,
|
|
153
212
|
toHaveCount: common.toHaveCount,
|
|
154
213
|
toBeVisible: common.toBeVisible,
|
|
214
|
+
toBeHidden: common.toBeHidden,
|
|
215
|
+
toBeAttached: common.toBeAttached,
|
|
216
|
+
toBeEnabled: common.toBeEnabled,
|
|
217
|
+
toBeDisabled: common.toBeDisabled,
|
|
218
|
+
toBeFocused: common.toBeFocused,
|
|
219
|
+
toBeEditable: common.toBeEditable,
|
|
155
220
|
get not() { return callableMatchers(createPageExpect(page, defaults, !negated)); },
|
|
156
221
|
async toHaveURL(expected, options = {}) {
|
|
157
222
|
await retryExpectation(async (command) => {
|
|
@@ -159,12 +224,12 @@ export function createPageExpect(page, defaults, negated = false) {
|
|
|
159
224
|
(negated ? vitestExpect(url).not : vitestExpect(url)).toEqual(expectedText(expected));
|
|
160
225
|
}, options, defaults());
|
|
161
226
|
},
|
|
162
|
-
toHaveValue:
|
|
227
|
+
toHaveValue: common.toHaveValue,
|
|
163
228
|
toHaveSelectedOptions: (selector, expected, options) => webMatcher(selector).toHaveSelectedOptions(expected, options),
|
|
164
229
|
toHaveSelectedText: (selector, expected, options) => webMatcher(selector).toHaveSelectedText(expected, options),
|
|
165
|
-
toBeChecked:
|
|
166
|
-
toHaveAttribute:
|
|
167
|
-
toHaveBoundingBox:
|
|
230
|
+
toBeChecked: common.toBeChecked,
|
|
231
|
+
toHaveAttribute: common.toHaveAttribute,
|
|
232
|
+
toHaveBoundingBox: common.toHaveBoundingBox,
|
|
168
233
|
};
|
|
169
234
|
return web;
|
|
170
235
|
}
|
package/dist/index.js
CHANGED
|
@@ -68,6 +68,9 @@ function createLazyLocator(pageResource, selectorFactory) {
|
|
|
68
68
|
async click(options) {
|
|
69
69
|
return (await pageResource.get()).locator(await selectorFactory()).click(options);
|
|
70
70
|
},
|
|
71
|
+
async dblclick(options) {
|
|
72
|
+
return (await pageResource.get()).locator(await selectorFactory()).dblclick(options);
|
|
73
|
+
},
|
|
71
74
|
async count(options) {
|
|
72
75
|
return (await pageResource.get()).locator(await selectorFactory()).count(options);
|
|
73
76
|
},
|
|
@@ -178,6 +181,9 @@ function createLazyPage(pageResource) {
|
|
|
178
181
|
async click(selector, options) {
|
|
179
182
|
return (await pageResource.get()).click(selector, options);
|
|
180
183
|
},
|
|
184
|
+
async dblclick(selector, options) {
|
|
185
|
+
return (await pageResource.get()).dblclick(selector, options);
|
|
186
|
+
},
|
|
181
187
|
async count(selector, options) {
|
|
182
188
|
return (await pageResource.get()).count(selector, options);
|
|
183
189
|
},
|
|
@@ -386,6 +392,18 @@ function createLazyMobileApp(appResource) {
|
|
|
386
392
|
async innerText(selector, options) {
|
|
387
393
|
return (await appResource.get()).innerText(selector, options);
|
|
388
394
|
},
|
|
395
|
+
async inputValue(selector, options) {
|
|
396
|
+
return (await appResource.get()).inputValue(selector, options);
|
|
397
|
+
},
|
|
398
|
+
async isChecked(selector, options) {
|
|
399
|
+
return (await appResource.get()).isChecked(selector, options);
|
|
400
|
+
},
|
|
401
|
+
async getAttribute(selector, name, options) {
|
|
402
|
+
return (await appResource.get()).getAttribute(selector, name, options);
|
|
403
|
+
},
|
|
404
|
+
async boundingBox(selector, options) {
|
|
405
|
+
return (await appResource.get()).boundingBox(selector, options);
|
|
406
|
+
},
|
|
389
407
|
async waitForSelector(selector, options) {
|
|
390
408
|
return (await appResource.get()).waitForSelector(selector, options);
|
|
391
409
|
},
|
|
@@ -427,6 +445,18 @@ function createLazyMobileLocator(appResource, selectorFactory) {
|
|
|
427
445
|
async innerText(options) {
|
|
428
446
|
return (await appResource.get()).locator(await selectorFactory()).innerText(options);
|
|
429
447
|
},
|
|
448
|
+
async inputValue(options) {
|
|
449
|
+
return (await appResource.get()).locator(await selectorFactory()).inputValue(options);
|
|
450
|
+
},
|
|
451
|
+
async isChecked(options) {
|
|
452
|
+
return (await appResource.get()).locator(await selectorFactory()).isChecked(options);
|
|
453
|
+
},
|
|
454
|
+
async getAttribute(name, options) {
|
|
455
|
+
return (await appResource.get()).locator(await selectorFactory()).getAttribute(name, options);
|
|
456
|
+
},
|
|
457
|
+
async boundingBox(options) {
|
|
458
|
+
return (await appResource.get()).locator(await selectorFactory()).boundingBox(options);
|
|
459
|
+
},
|
|
430
460
|
async waitFor(options) {
|
|
431
461
|
return (await appResource.get()).locator(await selectorFactory()).waitFor(options);
|
|
432
462
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@allwright.dev/vitest",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.26",
|
|
4
4
|
"description": "Vitest fixtures for the allwright TypeScript client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"prepublishOnly": "bun run build"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@allwright.dev/core": "0.1.
|
|
50
|
+
"@allwright.dev/core": "0.1.26"
|
|
51
51
|
},
|
|
52
52
|
"devDependencies": {
|
|
53
53
|
"typescript": "^5.9.2",
|