@allwright.dev/vitest 0.1.19 → 0.1.21

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 and iOS mobile fixtures for native and hybrid tests.
4
+ injection, plus Android, iOS, and macOS 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
 
@@ -40,9 +40,14 @@ test("opens an Android app", async ({ androidApp }) => {
40
40
  test("opens an iOS app", async ({ iosApp }) => {
41
41
  await iosApp.getByRole("button", { name: "Login", exact: true }).click();
42
42
  });
43
+
44
+ test("opens a macOS app", async ({ macosApp }) => {
45
+ await macosApp.getByRole("button", { name: "New Document", exact: true }).click();
46
+ });
43
47
  ```
44
48
 
45
- A checked-in example spec also lives in [examples/basic.spec.ts](./examples/basic.spec.ts).
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).
46
51
 
47
52
  The fixture package reads the shared stack-agnostic config format through `@allwright.dev/core`.
48
53
  Use `allwright.config.yaml` by default, or `allwright.config.json` if you prefer. Both follow the same root schema in `allwright.schema.json`.
@@ -139,11 +144,34 @@ test("logs in", async ({ iosApp }) => {
139
144
  });
140
145
  ```
141
146
 
147
+ macOS apps use an installed application bundle identifier. The fixture starts
148
+ the bundled desktop XCUITest runner and launches the configured app lazily:
149
+
150
+ ```yaml
151
+ # allwright.config.yaml
152
+ desktop:
153
+ mac:
154
+ app:
155
+ id: com.apple.TextEdit
156
+ ```
157
+
158
+ ```ts
159
+ import { expect, test } from "@allwright.dev/vitest";
160
+
161
+ test("opens a document window", async ({ macosApp }) => {
162
+ await macosApp.getByRole("button", { name: "New Document", exact: true }).click();
163
+ await expect(macosApp.getByRole("textField")).toBeVisible();
164
+ });
165
+ ```
166
+
167
+ Use `ALLWRIGHT_MAC_AGENT_ENDPOINT` for an external agent or
168
+ `ALLWRIGHT_MAC_APP_ID` to override the configured bundle identifier.
169
+
142
170
  Fixtures are lazy on first use. Injecting `browser`, `page`, `android`,
143
- `androidApp`, `ios`, or `iosApp` does not launch or connect immediately; the
144
- underlying session is created only when the test first performs an action
145
- through that fixture. Sync metadata properties are only available after the
146
- lazy fixture has been realized by a prior awaited call.
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.
147
175
 
148
176
  Available fixtures:
149
177
 
@@ -153,11 +181,14 @@ Available fixtures:
153
181
  - `androidApp`: launched Android app page
154
182
  - `ios`: connected iOS device session
155
183
  - `iosApp`: launched iOS app page
184
+ - `macos`: connected macOS desktop session
185
+ - `macosApp`: launched macOS app page
156
186
 
157
187
  `androidApp` launches using `allwright.android.launchOptions` first, then falls back to `config.mobile.android`.
158
188
  `iosApp` does the same with `allwright.ios.launchOptions` and `config.mobile.ios`.
189
+ `macosApp` does the same with `allwright.macos.launchOptions` and `config.desktop.mac`.
159
190
 
160
- Android and iOS apps and locators support the mobile action and expectation
191
+ Android, iOS, and macOS apps and locators support the native action and expectation
161
192
  subset: `click`, `count`, `focus`, `fill`, `press`, `textContent`, `innerText`,
162
193
  `waitForSelector`, and `screenshot`. Mobile expectations provide `toHaveText`,
163
194
  `toContainText`, `toHaveCount`, and `toBeVisible` with the same retry controls
@@ -46,14 +46,15 @@ function expectedText(expected) {
46
46
  }
47
47
  async function isVisible(locator, command) {
48
48
  if ("filter" in locator) {
49
- return (await locator.filter({ visible: true }).count(command)).count > 0;
49
+ return (await locator.filter({ visible: true }).count(command)) > 0;
50
50
  }
51
51
  // Android has no visibility-filter query. An absent node is not visible; only
52
52
  // its explicit native visibility failure is a negative observation.
53
- if ((await locator.count(command)).count === 0)
53
+ if ((await locator.count(command)) === 0)
54
54
  return false;
55
55
  try {
56
- return (await locator.waitFor({ ...command, visible: true })).visible;
56
+ await locator.waitFor({ ...command, visible: true });
57
+ return true;
57
58
  }
58
59
  catch (error) {
59
60
  if (error instanceof Error && error.message.startsWith("selector found but not visible: "))
@@ -67,7 +68,7 @@ export function createLocatorExpect(locator, defaults, negated = false) {
67
68
  get not() { return callableMatchers(createLocatorExpect(locator, defaults, !negated)); },
68
69
  async toHaveText(expected, options = {}) {
69
70
  await retryExpectation(async (command) => {
70
- const { text } = await locator.textContent(command);
71
+ const text = await locator.textContent(command);
71
72
  if (expected instanceof RegExp)
72
73
  assertion(text).toMatch(new RegExp(expected.source, expected.flags));
73
74
  else
@@ -76,7 +77,7 @@ export function createLocatorExpect(locator, defaults, negated = false) {
76
77
  },
77
78
  async toContainText(expected, options = {}) {
78
79
  await retryExpectation(async (command) => {
79
- const { text } = await locator.textContent(command);
80
+ const text = await locator.textContent(command);
80
81
  if (expected instanceof RegExp)
81
82
  assertion(text).toMatch(new RegExp(expected.source, expected.flags));
82
83
  else
@@ -85,7 +86,7 @@ export function createLocatorExpect(locator, defaults, negated = false) {
85
86
  },
86
87
  async toHaveCount(expected, options = {}) {
87
88
  await retryExpectation(async (command) => {
88
- assertion((await locator.count(command)).count).toBe(expected);
89
+ assertion(await locator.count(command)).toBe(expected);
89
90
  }, options, defaults());
90
91
  },
91
92
  async toBeVisible(options = {}) {
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 MobileIosConnectOptions, type MobileIosDevice, type MobileIosLaunchOptions, type MobileIosApp, type Browser, type BrowserKind, type LaunchOptions, type Locator, type MobileAndroidLocator, type Page } from "@allwright.dev/core";
3
+ import { type MobileAndroidConnectOptions, type MobileAndroidDevice, type MobileAndroidLaunchOptions, type MobileAndroidApp, 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 MobileAndroidLocator, 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;
@@ -15,6 +15,10 @@ export interface AllwrightVitestOptions {
15
15
  connectOptions?: MobileIosConnectOptions;
16
16
  launchOptions?: MobileIosLaunchOptions;
17
17
  };
18
+ macos?: {
19
+ connectOptions?: DesktopMacConnectOptions;
20
+ launchOptions?: DesktopMacLaunchOptions;
21
+ };
18
22
  configFile?: string;
19
23
  suite?: string;
20
24
  }
@@ -25,6 +29,8 @@ export interface AllwrightVitestFixtures {
25
29
  androidApp: MobileAndroidApp;
26
30
  ios: MobileIosDevice;
27
31
  iosApp: MobileIosApp;
32
+ macos: DesktopMacDesktop;
33
+ macosApp: DesktopMacApp;
28
34
  }
29
35
  interface LazyResource<T> {
30
36
  peek(): T | null;
@@ -38,10 +44,13 @@ export declare const test: import("vitest").TestAPI<{
38
44
  androidApp: MobileAndroidApp;
39
45
  ios: MobileIosDevice;
40
46
  iosApp: MobileAndroidApp;
47
+ macos: DesktopMacDesktop;
48
+ macosApp: MobileAndroidApp;
41
49
  allwright: AllwrightVitestOptions;
42
50
  _browserResource: LazyResource<Browser>;
43
51
  _androidResource: LazyResource<MobileAndroidDevice>;
44
52
  _iosResource: LazyResource<MobileIosDevice>;
53
+ _macosResource: LazyResource<DesktopMacDesktop>;
45
54
  }>;
46
55
  type VitestExpect = typeof vitestExpect;
47
56
  interface AllwrightExpect extends VitestExpect {
package/dist/index.js CHANGED
@@ -1,10 +1,12 @@
1
1
  import { createLocatorExpect, createPageExpect } from "./expectations.js";
2
- import { launchConfiguredBrowser, mobile, resolveConfig, setServerAddr, } from "@allwright.dev/core";
2
+ import { launchConfiguredBrowser, desktop, mobile, resolveConfig, setServerAddr, } from "@allwright.dev/core";
3
3
  import { expect as vitestExpect, test as base } from "vitest";
4
4
  const DEFAULT_ANDROID_CONNECT_TIMEOUT_MS = 30_000;
5
5
  const DEFAULT_ANDROID_LAUNCH_TIMEOUT_MS = 60_000;
6
6
  const DEFAULT_IOS_CONNECT_TIMEOUT_MS = 30_000;
7
7
  const DEFAULT_IOS_LAUNCH_TIMEOUT_MS = 60_000;
8
+ const DEFAULT_MACOS_CONNECT_TIMEOUT_MS = 30_000;
9
+ const DEFAULT_MACOS_LAUNCH_TIMEOUT_MS = 60_000;
8
10
  function createLazyResource(factory) {
9
11
  let value = null;
10
12
  let promise = null;
@@ -355,6 +357,12 @@ function createLazyMobileApp(appResource) {
355
357
  async registerHook(type) {
356
358
  return (await appResource.get()).registerHook(type);
357
359
  },
360
+ async goto(url, options) {
361
+ return (await appResource.get()).goto(url, options);
362
+ },
363
+ async navigate(url, options) {
364
+ return (await appResource.get()).navigate(url, options);
365
+ },
358
366
  async click(selector, options) {
359
367
  return (await appResource.get()).click(selector, options);
360
368
  },
@@ -473,6 +481,20 @@ function createLazyIosDevice(deviceResource) {
473
481
  },
474
482
  };
475
483
  }
484
+ function createLazyMacosDesktop(desktopResource) {
485
+ const initialAppResource = createLazyResource(async () => (await desktopResource.get()).app());
486
+ return {
487
+ get sessionId() {
488
+ return getLazySyncProperty(desktopResource, "sessionId");
489
+ },
490
+ app() {
491
+ return createLazyMobileApp(initialAppResource);
492
+ },
493
+ async launch(options) {
494
+ return (await desktopResource.get()).launch(options);
495
+ },
496
+ };
497
+ }
476
498
  export const test = base.extend({
477
499
  allwright: async ({}, use) => {
478
500
  await use({});
@@ -510,6 +532,14 @@ export const test = base.extend({
510
532
  const iosResource = createLazyResource(async () => mobile.ios.connect(resolveIosConnectOptions(config, allwright)));
511
533
  await use(iosResource);
512
534
  },
535
+ _macosResource: async ({ allwright }, use) => {
536
+ const config = resolveVitestConfig(allwright);
537
+ if (config.serverAddr) {
538
+ setServerAddr(config.serverAddr);
539
+ }
540
+ const macosResource = createLazyResource(async () => desktop.mac.connect(resolveMacosConnectOptions(config, allwright)));
541
+ await use(macosResource);
542
+ },
513
543
  browser: async ({ _browserResource }, use) => {
514
544
  try {
515
545
  await use(createLazyBrowser(_browserResource));
@@ -543,6 +573,17 @@ export const test = base.extend({
543
573
  });
544
574
  await use(createLazyMobileApp(appResource));
545
575
  },
576
+ macos: async ({ _macosResource }, use) => {
577
+ await use(createLazyMacosDesktop(_macosResource));
578
+ },
579
+ macosApp: async ({ _macosResource, allwright }, use) => {
580
+ const appResource = createLazyResource(async () => {
581
+ const config = resolveVitestConfig(allwright);
582
+ const launchOptions = resolveMacosLaunchOptions(config, allwright);
583
+ return (await _macosResource.get()).launch(launchOptions);
584
+ });
585
+ await use(createLazyMobileApp(appResource));
586
+ },
546
587
  });
547
588
  function isPage(value) {
548
589
  return !!value && typeof value === "object" && typeof value.locator === "function";
@@ -626,6 +667,28 @@ function resolveIosLaunchOptions(config, options) {
626
667
  }
627
668
  return launchOptions;
628
669
  }
670
+ function resolveMacosConnectOptions(config, options) {
671
+ return {
672
+ agentEndpoint: options.macos?.connectOptions?.agentEndpoint ??
673
+ process.env.ALLWRIGHT_MAC_AGENT_ENDPOINT,
674
+ timeoutMs: options.macos?.connectOptions?.timeoutMs ??
675
+ DEFAULT_MACOS_CONNECT_TIMEOUT_MS,
676
+ };
677
+ }
678
+ function resolveMacosLaunchOptions(config, options) {
679
+ const appId = options.macos?.launchOptions?.appId ??
680
+ process.env.ALLWRIGHT_MAC_APP_ID ??
681
+ config.desktop.mac?.appId;
682
+ if (!appId) {
683
+ throw new Error("macosApp fixture requires macOS launch options with `appId`, or config.desktop.mac.app configured");
684
+ }
685
+ return {
686
+ appId,
687
+ terminateRunning: options.macos?.launchOptions?.terminateRunning ?? false,
688
+ timeoutMs: options.macos?.launchOptions?.timeoutMs ??
689
+ DEFAULT_MACOS_LAUNCH_TIMEOUT_MS,
690
+ };
691
+ }
629
692
  let activeExpectDefaults = {};
630
693
  function currentExpectDefaults() {
631
694
  return activeExpectDefaults;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allwright.dev/vitest",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
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.19"
50
+ "@allwright.dev/core": "0.1.21"
51
51
  },
52
52
  "devDependencies": {
53
53
  "typescript": "^5.9.2",