@allwright.dev/vitest 0.1.24 → 0.1.25

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 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(page).toHaveAttribute('a', 'href', '/home')`. These state matchers are web-only; Android retains its text/count/visibility matchers.
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
 
@@ -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;
@@ -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 (!("inputValue" in locator))
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
- async toHaveValue(expected, options = {}) {
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
- async toBeChecked(options = {}) {
127
- await retryExpectation(async (command) => assertion(await locator.isChecked(command)).toBe(true), options, defaults());
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: (selector, expected, options) => webMatcher(selector).toHaveValue(expected, options),
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: (selector, options) => webMatcher(selector).toBeChecked(options),
166
- toHaveAttribute: (selector, name, expected, options) => webMatcher(selector).toHaveAttribute(name, expected, options),
167
- toHaveBoundingBox: (selector, expected, options) => webMatcher(selector).toHaveBoundingBox(expected, options),
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.24",
3
+ "version": "0.1.25",
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.24"
50
+ "@allwright.dev/core": "0.1.25"
51
51
  },
52
52
  "devDependencies": {
53
53
  "typescript": "^5.9.2",