@allwright.dev/vitest 0.1.23 → 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
@@ -1,7 +1,7 @@
1
1
  # @allwright.dev/vitest
2
2
 
3
3
  Vitest fixtures for allwright with Playwright-style `browser` and `page`
4
- injection, plus Android, iOS, and macOS app fixtures for native and hybrid tests.
4
+ injection, plus Android, iOS, macOS, and Windows app fixtures for native and hybrid tests.
5
5
 
6
6
  Wrap the Vitest configuration with `allwrightVitestConfig`. It starts Allwright once before workers run and shuts it down once after the complete test run. Existing user global setup files remain supported and run after Allwright starts, then tear down before Allwright stops.
7
7
 
@@ -44,10 +44,19 @@ test("opens an iOS app", async ({ iosApp }) => {
44
44
  test("opens a macOS app", async ({ macosApp }) => {
45
45
  await macosApp.getByRole("button", { name: "New Document", exact: true }).click();
46
46
  });
47
+
48
+ test("opens a Windows app", async ({ windowsApp }) => {
49
+ await windowsApp.getByRole("textbox").fill("Hello from Allwright");
50
+ });
47
51
  ```
48
52
 
49
- Checked-in example specs live in [examples/basic.spec.ts](./examples/basic.spec.ts)
50
- and [examples/ios-flights.spec.ts](./examples/ios-flights.spec.ts).
53
+ Checked-in example specs cover every implemented surface:
54
+ [Web](./examples/basic.spec.ts), [Android](./examples/android-flights.spec.ts),
55
+ [iOS](./examples/ios-flights.spec.ts),
56
+ [macOS](./examples/macos-calculator.spec.ts), and
57
+ [Windows](./examples/windows-notepad.spec.ts). Native specs use the app target
58
+ configured for their surface in `allwright.config.yaml` or
59
+ `allwright.config.json`.
51
60
 
52
61
  The fixture package reads the shared stack-agnostic config format through `@allwright.dev/core`.
53
62
  Use `allwright.config.yaml` by default, or `allwright.config.json` if you prefer. Both follow the same root schema in `allwright.schema.json`.
@@ -167,11 +176,35 @@ test("opens a document window", async ({ macosApp }) => {
167
176
  Use `ALLWRIGHT_MAC_AGENT_ENDPOINT` for an external agent or
168
177
  `ALLWRIGHT_MAC_APP_ID` to override the configured bundle identifier.
169
178
 
179
+ Windows apps use an executable path, command name, or packaged-app AUMID. The
180
+ fixture starts the bundled FlaUI UIA3 agent and launches the app lazily:
181
+
182
+ ```yaml
183
+ # allwright.config.yaml
184
+ desktop:
185
+ windows:
186
+ app:
187
+ id: notepad.exe
188
+ ```
189
+
190
+ ```ts
191
+ import { expect, test } from "@allwright.dev/vitest";
192
+
193
+ test("edits a document", async ({ windowsApp }) => {
194
+ await windowsApp.getByRole("textbox").fill("Hello from Allwright");
195
+ await expect(windowsApp.getByRole("textbox")).toContainText("Hello");
196
+ });
197
+ ```
198
+
199
+ Use `ALLWRIGHT_WINDOWS_AGENT_ENDPOINT` for an external agent or
200
+ `ALLWRIGHT_WINDOWS_APP_ID` to override the configured application identifier.
201
+
170
202
  Fixtures are lazy on first use. Injecting `browser`, `page`, `android`,
171
- `androidApp`, `ios`, `iosApp`, `macos`, or `macosApp` does not launch or connect
172
- immediately; the underlying session is created only when the test first
173
- performs an action through that fixture. Sync metadata properties are only
174
- available after the lazy fixture has been realized by a prior awaited call.
203
+ `androidApp`, `ios`, `iosApp`, `macos`, `macosApp`, `windows`, or `windowsApp`
204
+ does not launch or connect immediately; the underlying session is created only
205
+ when the test first performs an action through that fixture. Sync metadata
206
+ properties are only available after the lazy fixture has been realized by a
207
+ prior awaited call.
175
208
 
176
209
  Available fixtures:
177
210
 
@@ -183,13 +216,16 @@ Available fixtures:
183
216
  - `iosApp`: launched iOS app page
184
217
  - `macos`: connected macOS desktop session
185
218
  - `macosApp`: launched macOS app page
219
+ - `windows`: connected Windows desktop session
220
+ - `windowsApp`: launched Windows app page
186
221
 
187
222
  `androidApp` launches using `allwright.android.launchOptions` first, then falls back to `config.mobile.android`.
188
223
  `iosApp` does the same with `allwright.ios.launchOptions` and `config.mobile.ios`.
189
224
  `macosApp` does the same with `allwright.macos.launchOptions` and `config.desktop.mac`.
225
+ `windowsApp` does the same with `allwright.windows.launchOptions` and `config.desktop.windows`.
190
226
 
191
- Android, iOS, and macOS apps and locators support the native action and expectation
192
- subset: `click`, `count`, `focus`, `fill`, `press`, `textContent`, `innerText`,
227
+ Android, iOS, macOS, and Windows apps and locators support the native action and
228
+ expectation subset: `click`, `count`, `focus`, `fill`, `press`, `textContent`, `innerText`,
193
229
  `waitForSelector`, and `screenshot`. Mobile expectations provide `toHaveText`,
194
230
  `toContainText`, `toHaveCount`, and `toBeVisible` with the same retry controls
195
231
  as web fixtures. Hover and highlight remain web-only.
@@ -253,13 +289,18 @@ await expect(page.locator('select')).toHaveSelectedOptions(['a', 'b']);
253
289
  await expect(page.locator('select')).toHaveSelectedOptions([{ value: 'a', label: /Alpha/ }]);
254
290
  await expect(page.locator('textarea')).toHaveSelectedText('selected words');
255
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();
256
297
  await expect(page.locator('h1')).toHaveText('Welcome');
257
298
  await expect(page.locator('a')).toHaveAttribute('href', /dashboard/);
258
299
  await expect(page.locator('button')).toHaveBoundingBox({ width: 100, height: 40 });
259
300
  await expect(page.locator('input[type=radio]')).not().toBeChecked();
260
301
  ```
261
302
 
262
- 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.
263
304
 
264
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.
265
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.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import type { PageExpectMatchers, LocatorExpectMatchers, MobilePageExpectMatchers, MobileLocatorExpectMatchers } from "./expectations.js";
2
2
  export type { RetryExpectationOptions, TextExpectationOptions, VisibleExpectationOptions, SelectedOptionExpectation, PageExpectMatchers, LocatorExpectMatchers, MobilePageExpectMatchers, MobileLocatorExpectMatchers } from "./expectations.js";
3
- import { type MobileAndroidConnectOptions, type MobileAndroidDevice, type MobileAndroidLaunchOptions, type MobileAndroidApp, type NativeApp, type NativeAppLocator, type MobileIosConnectOptions, type MobileIosDevice, type MobileIosLaunchOptions, type MobileIosApp, type DesktopMacApp, type DesktopMacConnectOptions, type DesktopMacDesktop, type DesktopMacLaunchOptions, type Browser, type BrowserKind, type LaunchOptions, type Locator, type Page } from "@allwright.dev/core";
3
+ import { type MobileAndroidConnectOptions, type MobileAndroidDevice, type MobileAndroidLaunchOptions, type MobileAndroidApp, type NativeApp, type NativeAppLocator, type MobileIosConnectOptions, type MobileIosDevice, type MobileIosLaunchOptions, type MobileIosApp, type DesktopMacApp, type DesktopMacConnectOptions, type DesktopMacDesktop, type DesktopMacLaunchOptions, type DesktopWindowsApp, type DesktopWindowsConnectOptions, type DesktopWindowsDesktop, type DesktopWindowsLaunchOptions, type Browser, type BrowserKind, type LaunchOptions, type Locator, type Page } from "@allwright.dev/core";
4
4
  import { expect as vitestExpect, type Assertion } from "vitest";
5
5
  export interface AllwrightVitestOptions {
6
6
  launchOptions?: LaunchOptions;
@@ -19,6 +19,10 @@ export interface AllwrightVitestOptions {
19
19
  connectOptions?: DesktopMacConnectOptions;
20
20
  launchOptions?: DesktopMacLaunchOptions;
21
21
  };
22
+ windows?: {
23
+ connectOptions?: DesktopWindowsConnectOptions;
24
+ launchOptions?: DesktopWindowsLaunchOptions;
25
+ };
22
26
  configFile?: string;
23
27
  suite?: string;
24
28
  }
@@ -31,6 +35,8 @@ export interface AllwrightVitestFixtures {
31
35
  iosApp: MobileIosApp;
32
36
  macos: DesktopMacDesktop;
33
37
  macosApp: DesktopMacApp;
38
+ windows: DesktopWindowsDesktop;
39
+ windowsApp: DesktopWindowsApp;
34
40
  }
35
41
  interface LazyResource<T> {
36
42
  peek(): T | null;
@@ -46,11 +52,14 @@ export declare const test: import("vitest").TestAPI<{
46
52
  iosApp: NativeApp;
47
53
  macos: DesktopMacDesktop;
48
54
  macosApp: NativeApp;
55
+ windows: DesktopWindowsDesktop;
56
+ windowsApp: NativeApp;
49
57
  allwright: AllwrightVitestOptions;
50
58
  _browserResource: LazyResource<Browser>;
51
59
  _androidResource: LazyResource<MobileAndroidDevice>;
52
60
  _iosResource: LazyResource<MobileIosDevice>;
53
61
  _macosResource: LazyResource<DesktopMacDesktop>;
62
+ _windowsResource: LazyResource<DesktopWindowsDesktop>;
54
63
  }>;
55
64
  type VitestExpect = typeof vitestExpect;
56
65
  interface AllwrightExpect extends VitestExpect {
package/dist/index.js CHANGED
@@ -7,6 +7,8 @@ const DEFAULT_IOS_CONNECT_TIMEOUT_MS = 30_000;
7
7
  const DEFAULT_IOS_LAUNCH_TIMEOUT_MS = 60_000;
8
8
  const DEFAULT_MACOS_CONNECT_TIMEOUT_MS = 30_000;
9
9
  const DEFAULT_MACOS_LAUNCH_TIMEOUT_MS = 60_000;
10
+ const DEFAULT_WINDOWS_CONNECT_TIMEOUT_MS = 30_000;
11
+ const DEFAULT_WINDOWS_LAUNCH_TIMEOUT_MS = 60_000;
10
12
  function createLazyResource(factory) {
11
13
  let value = null;
12
14
  let promise = null;
@@ -66,6 +68,9 @@ function createLazyLocator(pageResource, selectorFactory) {
66
68
  async click(options) {
67
69
  return (await pageResource.get()).locator(await selectorFactory()).click(options);
68
70
  },
71
+ async dblclick(options) {
72
+ return (await pageResource.get()).locator(await selectorFactory()).dblclick(options);
73
+ },
69
74
  async count(options) {
70
75
  return (await pageResource.get()).locator(await selectorFactory()).count(options);
71
76
  },
@@ -176,6 +181,9 @@ function createLazyPage(pageResource) {
176
181
  async click(selector, options) {
177
182
  return (await pageResource.get()).click(selector, options);
178
183
  },
184
+ async dblclick(selector, options) {
185
+ return (await pageResource.get()).dblclick(selector, options);
186
+ },
179
187
  async count(selector, options) {
180
188
  return (await pageResource.get()).count(selector, options);
181
189
  },
@@ -384,6 +392,18 @@ function createLazyMobileApp(appResource) {
384
392
  async innerText(selector, options) {
385
393
  return (await appResource.get()).innerText(selector, options);
386
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
+ },
387
407
  async waitForSelector(selector, options) {
388
408
  return (await appResource.get()).waitForSelector(selector, options);
389
409
  },
@@ -425,6 +445,18 @@ function createLazyMobileLocator(appResource, selectorFactory) {
425
445
  async innerText(options) {
426
446
  return (await appResource.get()).locator(await selectorFactory()).innerText(options);
427
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
+ },
428
460
  async waitFor(options) {
429
461
  return (await appResource.get()).locator(await selectorFactory()).waitFor(options);
430
462
  },
@@ -495,6 +527,20 @@ function createLazyMacosDesktop(desktopResource) {
495
527
  },
496
528
  };
497
529
  }
530
+ function createLazyWindowsDesktop(desktopResource) {
531
+ const initialAppResource = createLazyResource(async () => (await desktopResource.get()).app());
532
+ return {
533
+ get sessionId() {
534
+ return getLazySyncProperty(desktopResource, "sessionId");
535
+ },
536
+ app() {
537
+ return createLazyMobileApp(initialAppResource);
538
+ },
539
+ async launch(options) {
540
+ return (await desktopResource.get()).launch(options);
541
+ },
542
+ };
543
+ }
498
544
  export const test = base.extend({
499
545
  allwright: async ({}, use) => {
500
546
  await use({});
@@ -540,6 +586,14 @@ export const test = base.extend({
540
586
  const macosResource = createLazyResource(async () => desktop.mac.connect(resolveMacosConnectOptions(config, allwright)));
541
587
  await use(macosResource);
542
588
  },
589
+ _windowsResource: async ({ allwright }, use) => {
590
+ const config = resolveVitestConfig(allwright);
591
+ if (config.serverAddr) {
592
+ setServerAddr(config.serverAddr);
593
+ }
594
+ const windowsResource = createLazyResource(async () => desktop.windows.connect(resolveWindowsConnectOptions(config, allwright)));
595
+ await use(windowsResource);
596
+ },
543
597
  browser: async ({ _browserResource }, use) => {
544
598
  try {
545
599
  await use(createLazyBrowser(_browserResource));
@@ -584,6 +638,17 @@ export const test = base.extend({
584
638
  });
585
639
  await use(createLazyMobileApp(appResource));
586
640
  },
641
+ windows: async ({ _windowsResource }, use) => {
642
+ await use(createLazyWindowsDesktop(_windowsResource));
643
+ },
644
+ windowsApp: async ({ _windowsResource, allwright }, use) => {
645
+ const appResource = createLazyResource(async () => {
646
+ const config = resolveVitestConfig(allwright);
647
+ const launchOptions = resolveWindowsLaunchOptions(config, allwright);
648
+ return (await _windowsResource.get()).launch(launchOptions);
649
+ });
650
+ await use(createLazyMobileApp(appResource));
651
+ },
587
652
  });
588
653
  function isPage(value) {
589
654
  return !!value && typeof value === "object" && typeof value.locator === "function";
@@ -689,6 +754,28 @@ function resolveMacosLaunchOptions(config, options) {
689
754
  DEFAULT_MACOS_LAUNCH_TIMEOUT_MS,
690
755
  };
691
756
  }
757
+ function resolveWindowsConnectOptions(config, options) {
758
+ return {
759
+ agentEndpoint: options.windows?.connectOptions?.agentEndpoint ??
760
+ process.env.ALLWRIGHT_WINDOWS_AGENT_ENDPOINT,
761
+ timeoutMs: options.windows?.connectOptions?.timeoutMs ??
762
+ DEFAULT_WINDOWS_CONNECT_TIMEOUT_MS,
763
+ };
764
+ }
765
+ function resolveWindowsLaunchOptions(config, options) {
766
+ const appId = options.windows?.launchOptions?.appId ??
767
+ process.env.ALLWRIGHT_WINDOWS_APP_ID ??
768
+ config.desktop.windows?.appId;
769
+ if (!appId) {
770
+ throw new Error("windowsApp fixture requires Windows launch options with `appId`, or config.desktop.windows.app configured");
771
+ }
772
+ return {
773
+ appId,
774
+ terminateRunning: options.windows?.launchOptions?.terminateRunning ?? false,
775
+ timeoutMs: options.windows?.launchOptions?.timeoutMs ??
776
+ DEFAULT_WINDOWS_LAUNCH_TIMEOUT_MS,
777
+ };
778
+ }
692
779
  let activeExpectDefaults = {};
693
780
  function currentExpectDefaults() {
694
781
  return activeExpectDefaults;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allwright.dev/vitest",
3
- "version": "0.1.23",
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.23"
50
+ "@allwright.dev/core": "0.1.25"
51
51
  },
52
52
  "devDependencies": {
53
53
  "typescript": "^5.9.2",