@allwright.dev/vitest 0.1.15 → 0.1.16

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,6 +1,7 @@
1
1
  # @allwright.dev/vitest
2
2
 
3
- Vitest fixtures for allwright with Playwright-style `browser` and `page` injection, plus Android mobile fixtures for hybrid tests.
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
5
 
5
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.
6
7
 
@@ -35,6 +36,10 @@ test("opens a page", async ({ page }) => {
35
36
  test("opens an Android app", async ({ androidApp }) => {
36
37
  await androidApp.click('Id=com.example.airticket:id/bottom_nav_account');
37
38
  });
39
+
40
+ test("opens an iOS app", async ({ iosApp }) => {
41
+ await iosApp.locator("text=Login").click();
42
+ });
38
43
  ```
39
44
 
40
45
  A checked-in example spec also lives in [examples/basic.spec.ts](./examples/basic.spec.ts).
@@ -105,7 +110,40 @@ test("hybrid web and android", async ({ page, androidApp }) => {
105
110
  });
106
111
  ```
107
112
 
108
- Fixtures are lazy on first use. Injecting `browser`, `page`, `android`, or `androidApp` does not launch or connect immediately; the underlying session is created only when the test first performs an action through that fixture. Sync metadata properties like `browser.sessionId` or `android.sessionId` are only available after the lazy fixture has been realized by a prior awaited call.
113
+ iOS uses the same app and locator shape. Configure the native agent endpoint
114
+ and bundle identifier once, then keep platform bootstrap out of test bodies:
115
+
116
+ ```yaml
117
+ # allwright.config.yaml
118
+ mobile:
119
+ ios:
120
+ device: iPhone 17 Pro
121
+ app:
122
+ binary: https://allwright.dev/Flights-simulator.ipa
123
+ ```
124
+
125
+ Set `ALLWRIGHT_IOS_AGENT_ENDPOINT` when the agent is not reachable at
126
+ `http://127.0.0.1:8100`. The app value may also be a local `.app`, `.zip`, or
127
+ `.ipa`; the fixture downloads/extracts it when needed, installs it on the
128
+ selected simulator, derives its bundle identifier, and launches it. Override
129
+ it with `ALLWRIGHT_IOS_APP_PATH`. No manual installation or polling belongs in
130
+ the test. The test remains platform-client code only:
131
+
132
+ ```ts
133
+ import { expect, test } from "@allwright.dev/vitest";
134
+
135
+ test("logs in", async ({ iosApp }) => {
136
+ await iosApp.locator("text=Login").click();
137
+ await iosApp.locator("className=XCUIElementTypeTextField").fill("user@example.com");
138
+ await expect(iosApp.locator("text=Welcome back")).toBeVisible();
139
+ });
140
+ ```
141
+
142
+ 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.
109
147
 
110
148
  Available fixtures:
111
149
 
@@ -113,10 +151,17 @@ Available fixtures:
113
151
  - `page`: initial web page
114
152
  - `android`: connected Android device session
115
153
  - `androidApp`: launched Android app page
154
+ - `ios`: connected iOS device session
155
+ - `iosApp`: launched iOS app page
116
156
 
117
157
  `androidApp` launches using `allwright.android.launchOptions` first, then falls back to `config.mobile.android`.
158
+ `iosApp` does the same with `allwright.ios.launchOptions` and `config.mobile.ios`.
118
159
 
119
- Android apps and locators support the Android-applicable action and expectation subset: `click`, `count`, `focus`, `fill`, `press`, `textContent`, `innerText`, `waitForSelector`, and `screenshot`. `expect(androidApp)` and `expect(androidApp.locator(...))` provide `toHaveText`, `toContainText`, `toHaveCount`, and `toBeVisible` with the same retry controls as web fixtures. Hover and highlight remain web-only.
160
+ Android and iOS apps and locators support the mobile action and expectation
161
+ subset: `click`, `count`, `focus`, `fill`, `press`, `textContent`, `innerText`,
162
+ `waitForSelector`, and `screenshot`. Mobile expectations provide `toHaveText`,
163
+ `toContainText`, `toHaveCount`, and `toBeVisible` with the same retry controls
164
+ as web fixtures. Hover and highlight remain web-only.
120
165
 
121
166
  The opt-in accessibility integration test exercises the public page fixture,
122
167
  bundled protobuf, running engine and dynamically loaded web plugin. From this
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 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 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;
@@ -11,6 +11,10 @@ export interface AllwrightVitestOptions {
11
11
  connectOptions?: MobileAndroidConnectOptions;
12
12
  launchOptions?: MobileAndroidLaunchOptions;
13
13
  };
14
+ ios?: {
15
+ connectOptions?: MobileIosConnectOptions;
16
+ launchOptions?: MobileIosLaunchOptions;
17
+ };
14
18
  configFile?: string;
15
19
  suite?: string;
16
20
  }
@@ -19,6 +23,8 @@ export interface AllwrightVitestFixtures {
19
23
  page: Page;
20
24
  android: MobileAndroidDevice;
21
25
  androidApp: MobileAndroidApp;
26
+ ios: MobileIosDevice;
27
+ iosApp: MobileIosApp;
22
28
  }
23
29
  interface LazyResource<T> {
24
30
  peek(): T | null;
@@ -30,9 +36,12 @@ export declare const test: import("vitest").TestAPI<{
30
36
  page: Page;
31
37
  android: MobileAndroidDevice;
32
38
  androidApp: MobileAndroidApp;
39
+ ios: MobileIosDevice;
40
+ iosApp: MobileAndroidApp;
33
41
  allwright: AllwrightVitestOptions;
34
42
  _browserResource: LazyResource<Browser>;
35
43
  _androidResource: LazyResource<MobileAndroidDevice>;
44
+ _iosResource: LazyResource<MobileIosDevice>;
36
45
  }>;
37
46
  type VitestExpect = typeof vitestExpect;
38
47
  interface AllwrightExpect extends VitestExpect {
package/dist/index.js CHANGED
@@ -3,6 +3,8 @@ import { launchConfiguredBrowser, mobile, resolveConfig, setServerAddr, } from "
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
+ const DEFAULT_IOS_CONNECT_TIMEOUT_MS = 30_000;
7
+ const DEFAULT_IOS_LAUNCH_TIMEOUT_MS = 60_000;
6
8
  function createLazyResource(factory) {
7
9
  let value = null;
8
10
  let promise = null;
@@ -330,13 +332,13 @@ function createLazyBrowser(browserResource) {
330
332
  };
331
333
  return lazyBrowser;
332
334
  }
333
- function createLazyAndroidApp(appResource) {
335
+ function createLazyMobileApp(appResource) {
334
336
  const lazyApp = {
335
337
  get sessionId() {
336
338
  return getLazySyncProperty(appResource, "sessionId");
337
339
  },
338
340
  locator(selector) {
339
- return createLazyAndroidLocator(appResource, async () => selector);
341
+ return createLazyMobileLocator(appResource, async () => selector);
340
342
  },
341
343
  async registerHook(type) {
342
344
  return (await appResource.get()).registerHook(type);
@@ -374,13 +376,13 @@ function createLazyAndroidApp(appResource) {
374
376
  };
375
377
  return lazyApp;
376
378
  }
377
- function createLazyAndroidLocator(appResource, selectorFactory) {
379
+ function createLazyMobileLocator(appResource, selectorFactory) {
378
380
  const lazyLocator = {
379
381
  get page() {
380
- return createLazyAndroidApp(appResource);
382
+ return createLazyMobileApp(appResource);
381
383
  },
382
384
  get selector() {
383
- throw new Error("lazy Android locator selector is not available before the app fixture is initialized");
385
+ throw new Error("lazy mobile locator selector is not available before the app fixture is initialized");
384
386
  },
385
387
  async click(options) {
386
388
  return (await appResource.get()).locator(await selectorFactory()).click(options);
@@ -407,7 +409,7 @@ function createLazyAndroidLocator(appResource, selectorFactory) {
407
409
  return (await appResource.get()).locator(await selectorFactory()).waitFor(options);
408
410
  },
409
411
  locator(selector) {
410
- return createLazyAndroidLocator(appResource, async () => (await appResource.get()).locator(await selectorFactory()).locator(selector).selector);
412
+ return createLazyMobileLocator(appResource, async () => (await appResource.get()).locator(await selectorFactory()).locator(selector).selector);
411
413
  },
412
414
  };
413
415
  return lazyLocator;
@@ -419,10 +421,10 @@ function createLazyAndroidDevice(deviceResource) {
419
421
  return getLazySyncProperty(deviceResource, "sessionId");
420
422
  },
421
423
  app() {
422
- return createLazyAndroidApp(initialAppResource);
424
+ return createLazyMobileApp(initialAppResource);
423
425
  },
424
426
  initialApp() {
425
- return createLazyAndroidApp(initialAppResource);
427
+ return createLazyMobileApp(initialAppResource);
426
428
  },
427
429
  async launch(options) {
428
430
  return (await deviceResource.get()).launch(options);
@@ -430,6 +432,23 @@ function createLazyAndroidDevice(deviceResource) {
430
432
  };
431
433
  return lazyDevice;
432
434
  }
435
+ function createLazyIosDevice(deviceResource) {
436
+ const initialAppResource = createLazyResource(async () => (await deviceResource.get()).app());
437
+ return {
438
+ get sessionId() {
439
+ return getLazySyncProperty(deviceResource, "sessionId");
440
+ },
441
+ app() {
442
+ return createLazyMobileApp(initialAppResource);
443
+ },
444
+ initialApp() {
445
+ return createLazyMobileApp(initialAppResource);
446
+ },
447
+ async launch(options) {
448
+ return (await deviceResource.get()).launch(options);
449
+ },
450
+ };
451
+ }
433
452
  export const test = base.extend({
434
453
  allwright: async ({}, use) => {
435
454
  await use({});
@@ -459,6 +478,14 @@ export const test = base.extend({
459
478
  const androidResource = createLazyResource(async () => mobile.android.connect(resolveAndroidConnectOptions(config, allwright)));
460
479
  await use(androidResource);
461
480
  },
481
+ _iosResource: async ({ allwright }, use) => {
482
+ const config = resolveVitestConfig(allwright);
483
+ if (config.serverAddr) {
484
+ setServerAddr(config.serverAddr);
485
+ }
486
+ const iosResource = createLazyResource(async () => mobile.ios.connect(resolveIosConnectOptions(config, allwright)));
487
+ await use(iosResource);
488
+ },
462
489
  browser: async ({ _browserResource }, use) => {
463
490
  try {
464
491
  await use(createLazyBrowser(_browserResource));
@@ -479,7 +506,18 @@ export const test = base.extend({
479
506
  const launchOptions = resolveAndroidLaunchOptions(config, allwright);
480
507
  return (await _androidResource.get()).launch(launchOptions);
481
508
  });
482
- await use(createLazyAndroidApp(appResource));
509
+ await use(createLazyMobileApp(appResource));
510
+ },
511
+ ios: async ({ _iosResource }, use) => {
512
+ await use(createLazyIosDevice(_iosResource));
513
+ },
514
+ iosApp: async ({ _iosResource, allwright }, use) => {
515
+ const appResource = createLazyResource(async () => {
516
+ const config = resolveVitestConfig(allwright);
517
+ const launchOptions = resolveIosLaunchOptions(config, allwright);
518
+ return (await _iosResource.get()).launch(launchOptions);
519
+ });
520
+ await use(createLazyMobileApp(appResource));
483
521
  },
484
522
  });
485
523
  function isPage(value) {
@@ -498,7 +536,7 @@ function resolveVitestConfig(options) {
498
536
  });
499
537
  return {
500
538
  ...config,
501
- serverAddr: options.serverAddr ?? config.serverAddr,
539
+ serverAddr: options.serverAddr ?? process.env.ALLWRIGHT_SERVER_ADDR ?? config.serverAddr,
502
540
  browserName: options.browser ?? config.browserName,
503
541
  browserBinary: options.browserBinary ?? config.browserBinary,
504
542
  launchOptions: {
@@ -535,6 +573,35 @@ function resolveAndroidLaunchOptions(config, options) {
535
573
  }
536
574
  return launchOptions;
537
575
  }
576
+ function resolveIosConnectOptions(config, options) {
577
+ return {
578
+ device: options.ios?.connectOptions?.device ??
579
+ process.env.ALLWRIGHT_IOS_DEVICE ??
580
+ config.mobile.ios?.device,
581
+ agentEndpoint: options.ios?.connectOptions?.agentEndpoint ??
582
+ process.env.ALLWRIGHT_IOS_AGENT_ENDPOINT,
583
+ preserveAppState: options.ios?.connectOptions?.preserveAppState ?? false,
584
+ timeoutMs: options.ios?.connectOptions?.timeoutMs ??
585
+ DEFAULT_IOS_CONNECT_TIMEOUT_MS,
586
+ };
587
+ }
588
+ function resolveIosLaunchOptions(config, options) {
589
+ const launchOptions = {
590
+ appPath: options.ios?.launchOptions?.appPath ??
591
+ process.env.ALLWRIGHT_IOS_APP_PATH ??
592
+ config.mobile.ios?.appBinary,
593
+ appId: options.ios?.launchOptions?.appId ??
594
+ process.env.ALLWRIGHT_IOS_APP_ID ??
595
+ config.mobile.ios?.appId,
596
+ stopBeforeLaunch: options.ios?.launchOptions?.stopBeforeLaunch ?? false,
597
+ timeoutMs: options.ios?.launchOptions?.timeoutMs ??
598
+ DEFAULT_IOS_LAUNCH_TIMEOUT_MS,
599
+ };
600
+ if (!launchOptions.appPath && !launchOptions.appId) {
601
+ throw new Error("iosApp fixture requires iOS launch options with `appPath` or `appId`, or config.mobile.ios.app configured");
602
+ }
603
+ return launchOptions;
604
+ }
538
605
  let activeExpectDefaults = {};
539
606
  function currentExpectDefaults() {
540
607
  return activeExpectDefaults;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@allwright.dev/vitest",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
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.15"
50
+ "@allwright.dev/core": "0.1.16"
51
51
  },
52
52
  "devDependencies": {
53
53
  "typescript": "^5.9.2",