@samsara-dev/appwright 0.6.0 → 0.7.0

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/AGENTS.md CHANGED
@@ -1,68 +1,54 @@
1
- # AGENTS OVERVIEW
1
+ # Appwright Agent Guide
2
2
 
3
- This document summarizes the Appwright test automation project so autonomous agents understand the environment, capabilities, and workflows before making changes.
4
-
5
- ## Project Snapshot
6
-
7
- - **Package name:** `@samsara-dev/appwright`
8
- - **Language:** TypeScript targeting Node.js ≥ 20.19
9
- - **Distribution entry:** `dist/index.js` (compiled via `npm run build`)
10
- - **Primary domains:** Mobile app end-to-end automation on real devices or device farms (BrowserStack, LambdaTest, local emulators)
11
-
12
- ## Repository Layout
13
-
14
- - `src/` – TypeScript sources (single source of truth)
15
- - `dist/` – Generated JavaScript and type declarations; never edit manually
16
- - `example/` – Sample usage and fixtures
17
- - `docs/` – Generated documentation assets
18
- - `.changeset/` – Release notes authored via Changesets
19
- - `node_modules/` – Managed dependencies (do not commit edits)
20
-
21
- ## Core Concepts & Types
22
-
23
- - `Device` class wraps a WebDriver client for mobile-specific flows and is exported from `src/index.ts`.
24
- - `Locator` utilities hold find strategies and timeout behavior derived from `TimeoutOptions`.
25
- - Providers (`src/providers/*`) bootstrap sessions for BrowserStack, LambdaTest, local, and emulator contexts.
26
- - Vision utilities in `src/vision` offer computer-vision-assisted interactions.
27
-
28
- ## Key Commands
3
+ ## 1. Project Snapshot
4
+ - **Repo type:** Single-package TypeScript library (no workspaces)
5
+ - **Stack:** Node.js ≥20, TypeScript, Playwright fixtures, Vitest, ESLint (`@empiricalrun`)
6
+ - **Docs:** Each major directory ships its own `AGENTS.md`; nearest file wins
29
7
 
8
+ ## 2. Root Setup Commands
30
9
  ```bash
31
- npm run build # Compile TypeScript to dist/
32
- npm test # Run Vitest test suite once
33
-
34
- npm run lint # Lint via ESLint configuration
35
- npm run changeset # Create or update release notes
10
+ npm ci # install dependencies (use npm install for incremental updates)
11
+ npm run lint # ESLint with @empiricalrun rules
12
+ npm run build # tsc --build (typecheck + emit to dist/)
13
+ npm test -- --run # Vitest single pass (avoids interactive watch mode)
14
+ npm run changeset # prepare release notes when changes ship
36
15
  ```
37
16
 
38
- ## Device Helper Summary
39
-
40
- - `device.getTimeouts()` Returns the underlying WebDriver timeout configuration (implicit, pageLoad, script, command if available).
41
- - `device.getWindowRect()` Exposes the active window rectangle `{ width, height, x, y }` for layout-aware actions.
42
-
43
- ## Development Guardrails
44
-
45
- - Always apply edits in `src/` and rebuild; never patch `dist/` directly.
46
- - Ensure additions remain backward compatible; prefer optional or additive APIs.
47
- - Run build + tests before concluding any workflow to catch regressions.
48
- - Use Changesets for versioned changes and keep release notes concise.
49
-
50
- ## Provider Notes
51
-
52
- - BrowserStack-specific capabilities live under `src/providers/browserstack` and may take advantage of executor scripts (e.g., camera image injection, keyboard state).
53
- - LambdaTest support mirrors BrowserStack patterns but with differing executor APIs.
54
- - Local and emulator providers rely on Appium capabilities configured through project fixtures.
55
-
56
- ## Telemetry & Vision
57
-
58
- - Visual trace services are coordinated via `src/visualTrace` to capture screenshots across retries.
59
- - Vision-based interactions are powered by `@empiricalrun/llm` models; see `Device.beta` helpers for experimental flows.
60
-
61
- ## Release Workflow
62
-
63
- 1. Author a Changeset describing user-facing impact.
64
- 2. Run `npm run build` and `npm test` to validate artifacts.
65
- 3. Submit PR targeting `main`; CI validates lint/tests.
66
- 4. Publish via `changeset publish` when ready for release.
67
-
68
- Agents should adhere to these guardrails to ensure repeatable, secure automation within the Appwright codebase.
17
+ ## 3. Universal Conventions
18
+ - Share a short plan with maintainers **before editing** (hard requirement)
19
+ - Author new code in `src/**`; never modify generated `dist/**`
20
+ - Rely on `src/logger.ts` instead of raw `console` (exceptions stream subprocess output only)
21
+ - Follow Conventional Commits (`feat:`, `fix:`, `chore:`) and branch from `main`
22
+ - Keep APIs backward compatible; prefer additive options over breaking changes
23
+ - Always generate a Changeset that captures the user-facing impact of the feature you worked on
24
+
25
+ ## 4. Security & Secrets
26
+ - Never commit API keys, BrowserStack creds, or AWS secrets; load via environment variables
27
+ - BrowserStack needs `BROWSERSTACK_USERNAME` / `BROWSERSTACK_ACCESS_KEY`
28
+ - Remote build downloads require AWS credentials (`AWS_REGION` plus standard SDK env vars)
29
+ - Avoid storing PII or test artifacts in the repo; use external storage for logs/videos
30
+
31
+ ## 5. JIT Index (what to open, not what to paste)
32
+
33
+ ### Package Structure
34
+ - Core library: `src/` → [see src/AGENTS.md](src/AGENTS.md)
35
+ - Device runtime: `src/device/` → [see src/device/AGENTS.md](src/device/AGENTS.md)
36
+ - Providers (BrowserStack, LambdaTest, emulator, local): `src/providers/` → [see src/providers/AGENTS.md](src/providers/AGENTS.md)
37
+ - Vision utilities: `src/vision/` [see src/vision/AGENTS.md](src/vision/AGENTS.md)
38
+ - Visual trace capture: `src/visualTrace/` [see src/visualTrace/AGENTS.md](src/visualTrace/AGENTS.md)
39
+ - Test suite: `src/tests/` → [see src/tests/AGENTS.md](src/tests/AGENTS.md)
40
+ - Example consumer app: `example/` → [see example/AGENTS.md](example/AGENTS.md)
41
+
42
+ ### Quick Find Commands
43
+ - Locate a public export: `rg -n "export .*" src/index.ts src/types` (fallback: `grep -rn "export .*" src/index.ts src/types`)
44
+ - Discover provider hooks: `rg -n "DeviceProvider" src/providers` (fallback: `grep -rn "DeviceProvider" src/providers`)
45
+ - Inspect Playwright fixtures: `rg -n "extend<TestLevelFixtures" src/fixture` (fallback: `grep -rn "extend<TestLevelFixtures" src/fixture`)
46
+ - Track vision helpers: `rg -n "AppwrightVision" src/vision` (fallback: `grep -rn "AppwrightVision" src/vision`)
47
+ - Find targeted tests: `rg -n "\\.spec\\.ts" src/tests` (fallback: `grep -rn "\.spec\.ts" src/tests`)
48
+ - Review BrowserStack permission prompts wiring: `rg -n "permissionPrompts" src/providers src/types`
49
+
50
+ ## 6. Definition of Done
51
+ - `npm run lint && npm run build && npm test -- --run` must all pass locally
52
+ - Add a Changeset entry for user-facing changes
53
+ - Ensure documentation in relevant `AGENTS.md` files reflects the update
54
+ - Confirm the plan was shared and acknowledged before merging
package/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  # appwright
2
2
 
3
+ ## 0.7.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3f00c08: Add device-level controls for BrowserStack permission prompts, including Android auto-grant toggles and iOS alert behaviour that respects the iOS 13 capability flip.
8
+
9
+ ## 0.6.1
10
+
11
+ ### Patch Changes
12
+
13
+ - 40979ac: - add BrowserStack S3 buildPath support with local download helper and coverage
14
+ - restructure hierarchical AGENTS.md guidance and reminders for contributors
15
+
3
16
  ## 0.6.0
4
17
 
5
18
  ### Minor Changes
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/browserstack/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,eAAe,EACf,cAAc,EAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAqDtC,qBAAa,0BAA2B,YAAW,cAAc;IAC/D,OAAO,CAAC,cAAc,CAAC,CAA6B;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,OAAO,CAA+B;gBAG5C,OAAO,EAAE,WAAW,CAAC,eAAe,CAAC,EACrC,WAAW,EAAE,MAAM,GAAG,SAAS;IAU3B,WAAW;IAwDX,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAMlC,OAAO,CAAC,cAAc;YASR,YAAY;YAiBZ,iBAAiB;YAKjB,yBAAyB;WAK1B,aAAa,CACxB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAuFlD,eAAe,CAAC,OAAO,EAAE;QAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf;IA2BD,OAAO,CAAC,YAAY;CAuFrB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/providers/browserstack/index.ts"],"names":[],"mappings":"AAIA,OAAO,EACL,eAAe,EACf,cAAc,EAGf,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAsDtC,qBAAa,0BAA2B,YAAW,cAAc;IAC/D,OAAO,CAAC,cAAc,CAAC,CAA6B;IACpD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,OAAO,CAA+B;gBAG5C,OAAO,EAAE,WAAW,CAAC,eAAe,CAAC,EACrC,WAAW,EAAE,MAAM,GAAG,SAAS;IAU3B,WAAW;IA0EX,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC;IAMlC,OAAO,CAAC,cAAc;YASR,YAAY;YAiBZ,iBAAiB;YAKjB,yBAAyB;WAK1B,aAAa,CACxB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAuFlD,eAAe,CAAC,OAAO,EAAE;QAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf;IA2BD,OAAO,CAAC,YAAY;CAuHrB"}
@@ -11,6 +11,7 @@ const path_1 = __importDefault(require("path"));
11
11
  const types_1 = require("../../types");
12
12
  const device_1 = require("../../device");
13
13
  const logger_1 = require("../../logger");
14
+ const s3_1 = require("./s3");
14
15
  const API_BASE_URL = "https://api-cloud.browserstack.com/app-automate";
15
16
  const envVarKeyForBuild = (projectName) => `BROWSERSTACK_APP_URL_${projectName.toUpperCase()}`;
16
17
  function getAuthHeader() {
@@ -50,45 +51,59 @@ class BrowserStackDeviceProvider {
50
51
  throw new Error("BROWSERSTACK_USERNAME and BROWSERSTACK_ACCESS_KEY are required environment variables for this device provider.");
51
52
  }
52
53
  const buildPath = this.project.use.buildPath;
53
- const isHttpUrl = buildPath.startsWith("http");
54
+ const isS3Url = (0, s3_1.isS3Uri)(buildPath);
55
+ const isHttpUrl = !isS3Url && buildPath.startsWith("http");
54
56
  const isBrowserStackUrl = buildPath.startsWith("bs://");
55
57
  let appUrl = undefined;
56
- if (isBrowserStackUrl) {
57
- appUrl = buildPath;
58
- }
59
- else {
60
- // Upload the file to BrowserStack and get the appUrl
61
- let body;
62
- let headers = {
63
- Authorization: getAuthHeader(),
64
- };
65
- if (isHttpUrl) {
66
- body = new URLSearchParams({
67
- url: buildPath,
68
- });
58
+ let downloadedArtifact;
59
+ try {
60
+ if (isBrowserStackUrl) {
61
+ appUrl = buildPath;
69
62
  }
70
63
  else {
71
- if (!fs_1.default.existsSync(buildPath)) {
72
- throw new Error(`Build file not found: ${buildPath}`);
64
+ // Upload the file to BrowserStack and get the appUrl
65
+ let body;
66
+ let headers = {
67
+ Authorization: getAuthHeader(),
68
+ };
69
+ let uploadSource = buildPath;
70
+ if (isS3Url) {
71
+ logger_1.logger.log(`Downloading build from S3: ${buildPath}`);
72
+ downloadedArtifact = await (0, s3_1.downloadS3Artifact)(buildPath);
73
+ uploadSource = downloadedArtifact.filePath;
74
+ }
75
+ if (isHttpUrl) {
76
+ body = new URLSearchParams({
77
+ url: buildPath,
78
+ });
79
+ }
80
+ else {
81
+ if (!fs_1.default.existsSync(uploadSource)) {
82
+ throw new Error(`Build file not found: ${uploadSource}`);
83
+ }
84
+ const form = new form_data_1.default();
85
+ form.append("file", fs_1.default.createReadStream(uploadSource));
86
+ headers = { ...headers, ...form.getHeaders() };
87
+ body = form;
88
+ }
89
+ const fetch = (await import("node-fetch")).default;
90
+ logger_1.logger.log(`Uploading build to BrowserStack: ${uploadSource}`);
91
+ const response = await fetch(`${API_BASE_URL}/upload`, {
92
+ method: "POST",
93
+ headers,
94
+ body,
95
+ });
96
+ const data = await response.json();
97
+ appUrl = data.app_url;
98
+ if (!appUrl) {
99
+ logger_1.logger.error("Uploading the build failed:", data);
100
+ throw new Error(`Failed to upload build to BrowserStack: ${JSON.stringify(data)}`);
73
101
  }
74
- const form = new form_data_1.default();
75
- form.append("file", fs_1.default.createReadStream(buildPath));
76
- headers = { ...headers, ...form.getHeaders() };
77
- body = form;
78
- }
79
- const fetch = (await import("node-fetch")).default;
80
- logger_1.logger.log(`Uploading: ${buildPath}`);
81
- const response = await fetch(`${API_BASE_URL}/upload`, {
82
- method: "POST",
83
- headers,
84
- body,
85
- });
86
- const data = await response.json();
87
- appUrl = data.app_url;
88
- if (!appUrl) {
89
- logger_1.logger.error("Uploading the build failed:", data);
90
102
  }
91
103
  }
104
+ finally {
105
+ await downloadedArtifact?.cleanup();
106
+ }
92
107
  process.env[envVarKeyForBuild(this.project.name)] = appUrl;
93
108
  }
94
109
  async getDevice() {
@@ -235,6 +250,7 @@ class BrowserStackDeviceProvider {
235
250
  if (!process.env[envVarKey]) {
236
251
  throw new Error(`process.env.${envVarKey} is not set. Did the file upload work?`);
237
252
  }
253
+ const permissionPrompts = deviceConfig?.permissionPrompts;
238
254
  const bstackOptions = {
239
255
  debug: true,
240
256
  interactiveDebugging: true,
@@ -281,6 +297,38 @@ class BrowserStackDeviceProvider {
281
297
  }
282
298
  }
283
299
  }
300
+ const capabilities = {
301
+ "bstack:options": bstackOptions,
302
+ "appium:app": process.env[envVarKey],
303
+ "appium:fullReset": true,
304
+ "appium:settings[snapshotMaxDepth]": 62,
305
+ };
306
+ if (platformName === types_1.Platform.ANDROID) {
307
+ const grantPreference = permissionPrompts?.android?.grantPermissions;
308
+ if (grantPreference !== "manual") {
309
+ capabilities["appium:autoGrantPermissions"] =
310
+ typeof grantPreference === "boolean" ? grantPreference : true;
311
+ }
312
+ }
313
+ if (platformName === types_1.Platform.IOS) {
314
+ const iosBehavior = permissionPrompts?.ios?.behavior ?? "accept";
315
+ if (iosBehavior !== "manual") {
316
+ const osVersionNumeric = parseFloat(deviceConfig.osVersion);
317
+ const isFlipped = !Number.isNaN(osVersionNumeric) && osVersionNumeric >= 13;
318
+ const acceptKey = isFlipped
319
+ ? "appium:autoDismissAlerts"
320
+ : "appium:autoAcceptAlerts";
321
+ const dismissKey = isFlipped
322
+ ? "appium:autoAcceptAlerts"
323
+ : "appium:autoDismissAlerts";
324
+ if (iosBehavior === "accept") {
325
+ capabilities[acceptKey] = true;
326
+ }
327
+ if (iosBehavior === "dismiss") {
328
+ capabilities[dismissKey] = true;
329
+ }
330
+ }
331
+ }
284
332
  return {
285
333
  port: 443,
286
334
  path: "/wd/hub",
@@ -289,14 +337,7 @@ class BrowserStackDeviceProvider {
289
337
  user: process.env.BROWSERSTACK_USERNAME,
290
338
  key: process.env.BROWSERSTACK_ACCESS_KEY,
291
339
  hostname: "hub.browserstack.com",
292
- capabilities: {
293
- "bstack:options": bstackOptions,
294
- "appium:autoGrantPermissions": true,
295
- "appium:app": process.env[envVarKey],
296
- "appium:autoAcceptAlerts": true,
297
- "appium:fullReset": true,
298
- "appium:settings[snapshotMaxDepth]": 62,
299
- },
340
+ capabilities,
300
341
  };
301
342
  }
302
343
  }
@@ -0,0 +1,11 @@
1
+ export type DownloadedS3Artifact = {
2
+ filePath: string;
3
+ cleanup: () => Promise<void>;
4
+ };
5
+ export declare function isS3Uri(value: string): boolean;
6
+ export declare function parseS3Uri(uri: string): {
7
+ bucket: string;
8
+ key: string;
9
+ };
10
+ export declare function downloadS3Artifact(uri: string): Promise<DownloadedS3Artifact>;
11
+ //# sourceMappingURL=s3.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"s3.d.ts","sourceRoot":"","sources":["../../../src/providers/browserstack/s3.ts"],"names":[],"mappings":"AAeA,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAAC;AAEF,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE9C;AAED,wBAAgB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAoBvE;AAgDD,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,oBAAoB,CAAC,CA8B/B"}
@@ -0,0 +1,102 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.isS3Uri = isS3Uri;
7
+ exports.parseS3Uri = parseS3Uri;
8
+ exports.downloadS3Artifact = downloadS3Artifact;
9
+ const fs_1 = __importDefault(require("fs"));
10
+ const promises_1 = __importDefault(require("fs/promises"));
11
+ const os_1 = __importDefault(require("os"));
12
+ const path_1 = __importDefault(require("path"));
13
+ const promises_2 = require("stream/promises");
14
+ const stream_1 = require("stream");
15
+ const client_s3_1 = require("@aws-sdk/client-s3");
16
+ const S3_SCHEME = "s3://";
17
+ const TEMP_PREFIX = "appwright-s3-";
18
+ function isS3Uri(value) {
19
+ return value.startsWith(S3_SCHEME);
20
+ }
21
+ function parseS3Uri(uri) {
22
+ if (!isS3Uri(uri)) {
23
+ throw new Error(`Invalid S3 URI: ${uri}`);
24
+ }
25
+ const remainder = uri.slice(S3_SCHEME.length);
26
+ const firstSlash = remainder.indexOf("/");
27
+ if (firstSlash === -1) {
28
+ throw new Error(`S3 URI must be in the format s3://bucket/key. Received: ${uri}`);
29
+ }
30
+ const bucket = remainder.slice(0, firstSlash);
31
+ const key = remainder.slice(firstSlash + 1);
32
+ if (!bucket || !key) {
33
+ throw new Error(`S3 URI must include both bucket and key: ${uri}`);
34
+ }
35
+ return {
36
+ bucket,
37
+ key: decodeURIComponent(key),
38
+ };
39
+ }
40
+ async function writeBodyToFile(body, destination) {
41
+ if (!body) {
42
+ throw new Error("Received empty S3 object body");
43
+ }
44
+ if (body instanceof stream_1.Readable) {
45
+ await (0, promises_2.pipeline)(body, fs_1.default.createWriteStream(destination));
46
+ return;
47
+ }
48
+ if (typeof body.transformToByteArray === "function") {
49
+ const bytes = await body.transformToByteArray();
50
+ await promises_1.default.writeFile(destination, Buffer.from(bytes));
51
+ return;
52
+ }
53
+ if (typeof body.arrayBuffer === "function") {
54
+ const buffer = Buffer.from(await body.arrayBuffer());
55
+ await promises_1.default.writeFile(destination, buffer);
56
+ return;
57
+ }
58
+ if (Symbol.asyncIterator in Object(body)) {
59
+ const chunks = [];
60
+ const iterable = body;
61
+ for await (const chunk of iterable) {
62
+ if (typeof chunk === "string") {
63
+ chunks.push(Buffer.from(chunk));
64
+ }
65
+ else if (chunk instanceof Uint8Array) {
66
+ chunks.push(Buffer.from(chunk));
67
+ }
68
+ else {
69
+ chunks.push(Buffer.from(String(chunk)));
70
+ }
71
+ }
72
+ await promises_1.default.writeFile(destination, Buffer.concat(chunks));
73
+ return;
74
+ }
75
+ throw new Error("Unsupported S3 response body type");
76
+ }
77
+ async function downloadS3Artifact(uri) {
78
+ const { bucket, key } = parseS3Uri(uri);
79
+ const region = process.env.AWS_REGION ?? process.env.AWS_DEFAULT_REGION;
80
+ if (!region) {
81
+ throw new Error("Set AWS_REGION or AWS_DEFAULT_REGION to download builds from S3.");
82
+ }
83
+ const client = new client_s3_1.S3Client({ region });
84
+ const command = new client_s3_1.GetObjectCommand({ Bucket: bucket, Key: key });
85
+ const response = await client.send(command);
86
+ const tmpDir = await promises_1.default.mkdtemp(path_1.default.join(os_1.default.tmpdir(), TEMP_PREFIX));
87
+ const fileName = key.split("/").filter(Boolean).pop() ?? "artifact";
88
+ const destination = path_1.default.join(tmpDir, fileName);
89
+ try {
90
+ await writeBodyToFile(response.Body, destination);
91
+ }
92
+ catch (error) {
93
+ await promises_1.default.rm(tmpDir, { recursive: true, force: true });
94
+ throw error;
95
+ }
96
+ return {
97
+ filePath: destination,
98
+ cleanup: async () => {
99
+ await promises_1.default.rm(tmpDir, { recursive: true, force: true });
100
+ },
101
+ };
102
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=browserstack.permissions.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browserstack.permissions.spec.d.ts","sourceRoot":"","sources":["../../src/tests/browserstack.permissions.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const vitest_1 = require("vitest");
4
+ const browserstack_1 = require("../providers/browserstack");
5
+ const types_1 = require("../types");
6
+ const makeEnvKey = (projectName) => `BROWSERSTACK_APP_URL_${projectName.toUpperCase()}`;
7
+ const baseDevice = {
8
+ provider: "browserstack",
9
+ name: "Test Device",
10
+ osVersion: "14",
11
+ };
12
+ const makeProvider = (platform, deviceOverrides = {}) => {
13
+ const projectName = "mobile";
14
+ const project = {
15
+ name: projectName,
16
+ use: {
17
+ buildPath: "bs://app-url",
18
+ platform,
19
+ expectTimeout: 5_000,
20
+ device: {
21
+ ...baseDevice,
22
+ ...deviceOverrides,
23
+ },
24
+ },
25
+ };
26
+ const provider = new browserstack_1.BrowserStackDeviceProvider(project, undefined);
27
+ process.env[makeEnvKey(projectName)] = "bs://app-url";
28
+ return { provider, projectName };
29
+ };
30
+ (0, vitest_1.afterEach)(() => {
31
+ delete process.env.BROWSERSTACK_USERNAME;
32
+ delete process.env.BROWSERSTACK_ACCESS_KEY;
33
+ Object.keys(process.env)
34
+ .filter((key) => key.startsWith("BROWSERSTACK_APP_URL_"))
35
+ .forEach((key) => delete process.env[key]);
36
+ });
37
+ (0, vitest_1.describe)("BrowserStack permission prompt capabilities", () => {
38
+ (0, vitest_1.test)("android defaults to auto-granting permissions", () => {
39
+ const { provider } = makeProvider(types_1.Platform.ANDROID);
40
+ const config = provider.createConfig();
41
+ (0, vitest_1.expect)(config.capabilities["appium:autoGrantPermissions"]).toBe(true);
42
+ });
43
+ (0, vitest_1.test)("android manual mode omits autoGrantPermissions capability", () => {
44
+ const { provider } = makeProvider(types_1.Platform.ANDROID, {
45
+ permissionPrompts: { android: { grantPermissions: "manual" } },
46
+ });
47
+ const config = provider.createConfig();
48
+ (0, vitest_1.expect)(config.capabilities).not.toHaveProperty("appium:autoGrantPermissions");
49
+ });
50
+ (0, vitest_1.test)("ios defaults to accepting alerts below iOS 13", () => {
51
+ const { provider } = makeProvider(types_1.Platform.IOS, {
52
+ osVersion: "12.4",
53
+ });
54
+ const config = provider.createConfig();
55
+ (0, vitest_1.expect)(config.capabilities["appium:autoAcceptAlerts"]).toBe(true);
56
+ (0, vitest_1.expect)(config.capabilities["appium:autoDismissAlerts"]).toBeUndefined();
57
+ });
58
+ (0, vitest_1.test)("ios defaults to accepting alerts on iOS 13+ using flipped capability", () => {
59
+ const { provider } = makeProvider(types_1.Platform.IOS, {
60
+ osVersion: "14.0",
61
+ });
62
+ const config = provider.createConfig();
63
+ (0, vitest_1.expect)(config.capabilities["appium:autoDismissAlerts"]).toBe(true);
64
+ (0, vitest_1.expect)(config.capabilities["appium:autoAcceptAlerts"]).toBeUndefined();
65
+ });
66
+ (0, vitest_1.test)("ios manual mode omits alert capabilities", () => {
67
+ const { provider } = makeProvider(types_1.Platform.IOS, {
68
+ osVersion: "15",
69
+ permissionPrompts: { ios: { behavior: "manual" } },
70
+ });
71
+ const config = provider.createConfig();
72
+ (0, vitest_1.expect)(config.capabilities).not.toHaveProperty("appium:autoAcceptAlerts");
73
+ (0, vitest_1.expect)(config.capabilities).not.toHaveProperty("appium:autoDismissAlerts");
74
+ });
75
+ (0, vitest_1.test)("ios dismiss behavior sets appropriate capability", () => {
76
+ const { provider } = makeProvider(types_1.Platform.IOS, {
77
+ osVersion: "12.0",
78
+ permissionPrompts: { ios: { behavior: "dismiss" } },
79
+ });
80
+ const config = provider.createConfig();
81
+ (0, vitest_1.expect)(config.capabilities["appium:autoDismissAlerts"]).toBe(true);
82
+ (0, vitest_1.expect)(config.capabilities["appium:autoAcceptAlerts"]).toBeUndefined();
83
+ });
84
+ (0, vitest_1.test)("ios dismiss on 13+ flips to autoAcceptAlerts capability", () => {
85
+ const { provider } = makeProvider(types_1.Platform.IOS, {
86
+ osVersion: "16.0",
87
+ permissionPrompts: { ios: { behavior: "dismiss" } },
88
+ });
89
+ const config = provider.createConfig();
90
+ (0, vitest_1.expect)(config.capabilities["appium:autoAcceptAlerts"]).toBe(true);
91
+ (0, vitest_1.expect)(config.capabilities["appium:autoDismissAlerts"]).toBeUndefined();
92
+ });
93
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=browserstack.s3.spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browserstack.s3.spec.d.ts","sourceRoot":"","sources":["../../src/tests/browserstack.s3.spec.ts"],"names":[],"mappings":""}
@@ -0,0 +1,78 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const vitest_1 = require("vitest");
7
+ const stream_1 = require("stream");
8
+ const promises_1 = __importDefault(require("fs/promises"));
9
+ const sendMock = vitest_1.vi.fn();
10
+ const capturedConfigs = [];
11
+ vitest_1.vi.mock("@aws-sdk/client-s3", () => {
12
+ class MockS3Client {
13
+ constructor(options) {
14
+ capturedConfigs.push(options);
15
+ }
16
+ send = sendMock;
17
+ }
18
+ class MockGetObjectCommand {
19
+ input;
20
+ constructor(input) {
21
+ this.input = input;
22
+ }
23
+ }
24
+ return {
25
+ S3Client: MockS3Client,
26
+ GetObjectCommand: MockGetObjectCommand,
27
+ __esModule: true,
28
+ };
29
+ });
30
+ let browserstackS3;
31
+ (0, vitest_1.beforeAll)(async () => {
32
+ browserstackS3 = await import("../providers/browserstack/s3.js");
33
+ });
34
+ (0, vitest_1.afterEach)(async () => {
35
+ sendMock.mockReset();
36
+ capturedConfigs.length = 0;
37
+ delete process.env.AWS_REGION;
38
+ delete process.env.AWS_DEFAULT_REGION;
39
+ });
40
+ (0, vitest_1.describe)("browserstack S3 helpers", () => {
41
+ (0, vitest_1.test)("parseS3Uri extracts bucket and decoded key", () => {
42
+ const result = browserstackS3.parseS3Uri("s3://my-bucket/builds/app%20v2.ipa");
43
+ (0, vitest_1.expect)(result).toEqual({
44
+ bucket: "my-bucket",
45
+ key: "builds/app v2.ipa",
46
+ });
47
+ });
48
+ (0, vitest_1.test)("downloadS3Artifact saves file locally and cleans up", async () => {
49
+ process.env.AWS_REGION = "us-west-2";
50
+ sendMock.mockResolvedValueOnce({
51
+ Body: stream_1.Readable.from(["test-binary"]),
52
+ });
53
+ const artifact = await browserstackS3.downloadS3Artifact("s3://test-bucket/apps/mobile.apk");
54
+ const fileContents = await promises_1.default.readFile(artifact.filePath, "utf-8");
55
+ (0, vitest_1.expect)(fileContents).toBe("test-binary");
56
+ (0, vitest_1.expect)(sendMock).toHaveBeenCalledTimes(1);
57
+ (0, vitest_1.expect)(sendMock.mock.calls[0]?.[0]).toMatchObject({
58
+ input: { Bucket: "test-bucket", Key: "apps/mobile.apk" },
59
+ });
60
+ (0, vitest_1.expect)(capturedConfigs[0]).toMatchObject({ region: "us-west-2" });
61
+ await artifact.cleanup();
62
+ await (0, vitest_1.expect)(promises_1.default.stat(artifact.filePath)).rejects.toMatchObject({
63
+ code: "ENOENT",
64
+ });
65
+ });
66
+ (0, vitest_1.test)("downloadS3Artifact throws when region is missing", async () => {
67
+ sendMock.mockResolvedValueOnce({
68
+ Body: stream_1.Readable.from(["unused"]),
69
+ });
70
+ await (0, vitest_1.expect)(browserstackS3.downloadS3Artifact("s3://bucket/key")).rejects.toThrow(/AWS_REGION/);
71
+ (0, vitest_1.expect)(sendMock).not.toHaveBeenCalled();
72
+ });
73
+ (0, vitest_1.test)("isS3Uri differentiates schemes", () => {
74
+ (0, vitest_1.expect)(browserstackS3.isS3Uri("s3://bucket/key")).toBe(true);
75
+ (0, vitest_1.expect)(browserstackS3.isS3Uri("https://example.com/app.apk")).toBe(false);
76
+ (0, vitest_1.expect)(browserstackS3.isS3Uri("bs://sample-app")).toBe(false);
77
+ });
78
+ });
@@ -126,6 +126,25 @@ export type BrowserStackConfig = {
126
126
  * ```
127
127
  */
128
128
  updateAppSettings?: IosAppSettings;
129
+ /**
130
+ * Controls how BrowserStack handles system permission prompts for this device.
131
+ * Defaults to automatically granting Android permissions and accepting iOS alerts.
132
+ */
133
+ permissionPrompts?: BrowserStackPermissionPrompts;
134
+ };
135
+ export type BrowserStackPermissionPrompts = {
136
+ android?: {
137
+ /**
138
+ * When true, auto-grant Android app permissions on install. Set to false to disable, or "manual" to omit the capability entirely.
139
+ */
140
+ grantPermissions?: boolean | "manual";
141
+ };
142
+ ios?: {
143
+ /**
144
+ * Desired handling for iOS permission alerts. Defaults to "accept" and accounts for the iOS 13+ capability flip.
145
+ */
146
+ behavior?: "accept" | "dismiss" | "manual";
147
+ };
129
148
  };
130
149
  export type LambdaTestConfig = {
131
150
  provider: "lambdatest";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samsara-dev/appwright",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "publishConfig": {
5
5
  "registry": "https://registry.npmjs.org/",
6
6
  "access": "public"
@@ -36,6 +36,7 @@
36
36
  "license": "Apache-2.0",
37
37
  "description": "E2E mobile app testing done right, with the Playwright test runner",
38
38
  "dependencies": {
39
+ "@aws-sdk/client-s3": "^3.932.0",
39
40
  "@empiricalrun/llm": "^0.9.25",
40
41
  "@ffmpeg-installer/ffmpeg": "^1.1.0",
41
42
  "@playwright/test": "^1.56.1",