@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 +49 -63
- package/CHANGELOG.md +13 -0
- package/dist/providers/browserstack/index.d.ts.map +1 -1
- package/dist/providers/browserstack/index.js +81 -40
- package/dist/providers/browserstack/s3.d.ts +11 -0
- package/dist/providers/browserstack/s3.d.ts.map +1 -0
- package/dist/providers/browserstack/s3.js +102 -0
- package/dist/tests/browserstack.permissions.spec.d.ts +2 -0
- package/dist/tests/browserstack.permissions.spec.d.ts.map +1 -0
- package/dist/tests/browserstack.permissions.spec.js +93 -0
- package/dist/tests/browserstack.s3.spec.d.ts +2 -0
- package/dist/tests/browserstack.s3.spec.d.ts.map +1 -0
- package/dist/tests/browserstack.s3.spec.js +78 -0
- package/dist/types/index.d.ts +19 -0
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +2 -1
package/AGENTS.md
CHANGED
|
@@ -1,68 +1,54 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Appwright Agent Guide
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
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
|
|
32
|
-
npm
|
|
33
|
-
|
|
34
|
-
npm run
|
|
35
|
-
npm run changeset
|
|
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
|
-
##
|
|
39
|
-
|
|
40
|
-
-
|
|
41
|
-
- `
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
-
|
|
48
|
-
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
-
|
|
59
|
-
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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;
|
|
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
|
|
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
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
72
|
-
|
|
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 @@
|
|
|
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 @@
|
|
|
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
|
+
});
|
package/dist/types/index.d.ts
CHANGED
|
@@ -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":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,MAAM,WAAW,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC;AAEtE,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,WAAW,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9B;;OAEG;IACH,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAE7B;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE;QAC1B,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;KACf,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,YAAY,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IAEpB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,mBAAmB,CAAC;IAEjE;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;;OAIG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,YAAY,GACpB,kBAAkB,GAClB,gBAAgB,GAChB,iBAAiB,GACjB,cAAc,CAAC;AAEnB;;GAEG;AACH,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,cAAc,CAAC;IAEzB;;;;OAIG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;OAIG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;;OAGG;IACH,WAAW,CAAC,EAAE,iBAAiB,CAAC;IAEhC;;;OAGG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB;;;;;;;;;;;;;;OAcG;IACH,iBAAiB,CAAC,EAAE,cAAc,CAAC;
|
|
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.
|
|
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",
|