@testingbot/mobilewright-driver 0.1.0 → 0.3.1
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 +87 -6
- package/dist/capabilities.d.ts +4 -3
- package/dist/capabilities.d.ts.map +1 -1
- package/dist/capabilities.js +21 -10
- package/dist/capabilities.js.map +1 -1
- package/dist/driver.d.ts +29 -0
- package/dist/driver.d.ts.map +1 -1
- package/dist/driver.js +163 -49
- package/dist/driver.js.map +1 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +6 -1
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +3 -1
- package/dist/init.js.map +1 -1
- package/dist/observer.d.ts +84 -7
- package/dist/observer.d.ts.map +1 -1
- package/dist/observer.js +229 -27
- package/dist/observer.js.map +1 -1
- package/dist/options.d.ts +13 -2
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +2 -0
- package/dist/options.js.map +1 -1
- package/dist/rest-api.d.ts +9 -1
- package/dist/rest-api.d.ts.map +1 -1
- package/dist/rest-api.js.map +1 -1
- package/dist/run-log.d.ts +12 -2
- package/dist/run-log.d.ts.map +1 -1
- package/dist/run-log.js +13 -2
- package/dist/run-log.js.map +1 -1
- package/dist/session-commands.d.ts +75 -0
- package/dist/session-commands.d.ts.map +1 -0
- package/dist/session-commands.js +119 -0
- package/dist/session-commands.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @testingbot/mobilewright-driver
|
|
2
2
|
|
|
3
|
-
Run [Mobilewright](https://github.com/mobile-next/mobilewright) mobile tests on [TestingBot](https://testingbot.com)'s device cloud
|
|
3
|
+
Run [Mobilewright](https://github.com/mobile-next/mobilewright) mobile tests on [TestingBot](https://testingbot.com)'s device cloud: real iOS/Android devices, iOS simulators and Android emulators.
|
|
4
4
|
|
|
5
5
|
Requires `mobilewright >= 0.0.53` (the first release that accepts driver instances) and Node >= 18.
|
|
6
6
|
|
|
@@ -41,6 +41,8 @@ export default defineConfig({
|
|
|
41
41
|
// TestingBot sessions start with the app pre-installed — declare it here.
|
|
42
42
|
// Local paths (.apk / .ipa / simulator .zip) upload automatically.
|
|
43
43
|
apps: {
|
|
44
|
+
// A single path, or an array whose first entry is the app under test
|
|
45
|
+
// and the rest install alongside it (e.g. a mock server or test harness).
|
|
44
46
|
android: './build/app.apk',
|
|
45
47
|
'ios-simulator': './build/app-sim.zip',
|
|
46
48
|
'ios-real': './build/app.ipa', // must be test-signed for real devices
|
|
@@ -59,7 +61,7 @@ export default defineConfig({
|
|
|
59
61
|
import { test, expect } from '@mobilewright/test';
|
|
60
62
|
|
|
61
63
|
test('shows the welcome screen', async ({ device, screen, bundleId }) => {
|
|
62
|
-
await device.launchApp(bundleId);
|
|
64
|
+
await device.launchApp(bundleId!);
|
|
63
65
|
await expect(screen.getByText('Welcome')).toBeVisible();
|
|
64
66
|
});
|
|
65
67
|
```
|
|
@@ -90,6 +92,9 @@ Regexes and version ranges work on both real and virtual devices: real devices r
|
|
|
90
92
|
|
|
91
93
|
- By default a device slot's TestingBot session is reused across tests (fast: you pay device startup once). Sessions that hosted exactly one test are named after that test with its own pass/fail; sessions that hosted several get a run summary (e.g. `12/12 tests passed`).
|
|
92
94
|
- Set **`sessionPerTest: true`** for one session, video, and named verdict **per test** — the clearest dashboard, at the cost of device startup on every test.
|
|
95
|
+
- Tests are matched to sessions by **worker identity first**: the driver stamps Playwright's worker index on each session as it records it, and the run report reports the same index per test, so a test that ran in another worker process is never a candidate. Timing separates the tests within one worker, using the worker's own timestamps from the report when it is available.
|
|
96
|
+
- A session is only ever given a verdict the driver can stand behind: its own test's, or the run summary when it genuinely hosted several tests (or was the run's only session). When a session cannot be tied to a test — an ambiguous overlap under heavy parallelism, or a worker that died before recording its span — it is reported with build, extra and tags but **no** pass/fail, rather than inheriting a failure from a test that ran somewhere else. `DEBUG=testingbot:observer` names the session and the reason.
|
|
97
|
+
- Git metadata is reported automatically when mobilewright captures it: the commit hash, branch, author and subject land in the session's **extra** field (alongside your own `extra`, never replacing it), and the branch is added as a **tag** so the dashboard can filter by it. Enable it with `captureGitInfo: { commit: true }` in `mobilewright.config.ts` — Playwright turns it on by itself in CI.
|
|
93
98
|
- Failure messages are reported to the session's status message; `build` groups sessions on the dashboard and **defaults automatically from CI environments** (GitHub Actions, GitLab, CircleCI, Buildkite, Bitrise, Travis, Azure DevOps, Jenkins, TeamCity → e.g. `owner/repo #123`), or from a `TESTINGBOT_BUILD` variable.
|
|
94
99
|
|
|
95
100
|
## All options
|
|
@@ -97,7 +102,9 @@ Regexes and version ranges work on both real and virtual devices: real devices r
|
|
|
97
102
|
```ts
|
|
98
103
|
new TestingBotDriver({
|
|
99
104
|
apps: { android: './build/app.apk' }, // app under test, most specific key wins
|
|
100
|
-
// ('ios-real' beats 'ios'); tb:// URLs allowed
|
|
105
|
+
// ('ios-real' beats 'ios'); tb:// URLs allowed.
|
|
106
|
+
// Array = app under test first, helper apps after:
|
|
107
|
+
// android: ['./app.apk', './mock-server.apk']
|
|
101
108
|
key: '...', // default: TESTINGBOT_KEY env
|
|
102
109
|
secret: '...', // default: TESTINGBOT_SECRET env
|
|
103
110
|
sessionPerTest: false, // true = fresh session (and video) per test
|
|
@@ -113,6 +120,8 @@ new TestingBotDriver({
|
|
|
113
120
|
timeZone: 'Europe/Brussels', // device timezone (tz database name)
|
|
114
121
|
geoCountryCode: 'DE', // route device traffic via a proxy in this country
|
|
115
122
|
throttleNetwork: '3G', // or { downloadSpeed, uploadSpeed, latency, loss }
|
|
123
|
+
autoGrantPermissions: true, // Android: auto-grant app permission dialogs
|
|
124
|
+
autoAcceptAlerts: true, // iOS: auto-accept system permission alerts
|
|
116
125
|
screenshots: true, // screenshot at every step (default false)
|
|
117
126
|
video: true, // session video (default true)
|
|
118
127
|
recordLogs: 'strip-parameters',// command logs: true | false | 'strip-parameters'
|
|
@@ -126,6 +135,65 @@ new TestingBotDriver({
|
|
|
126
135
|
});
|
|
127
136
|
```
|
|
128
137
|
|
|
138
|
+
## TestingBot commands inside tests
|
|
139
|
+
|
|
140
|
+
Some TestingBot features are runtime commands rather than session
|
|
141
|
+
capabilities. Import the `testingbot` helper and call them from a test body —
|
|
142
|
+
it targets the device session mobilewright connected for that test:
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { test, expect } from '@mobilewright/test';
|
|
146
|
+
import { testingbot } from '@testingbot/mobilewright-driver';
|
|
147
|
+
|
|
148
|
+
test('checkout survives a slow network', async ({ device, screen, bundleId }) => {
|
|
149
|
+
await device.launchApp(bundleId!);
|
|
150
|
+
|
|
151
|
+
await testingbot.annotate('starting checkout'); // shows in the session timeline
|
|
152
|
+
await testingbot.throttle('3G');
|
|
153
|
+
await expect(screen.getByText('Order placed')).toBeVisible();
|
|
154
|
+
|
|
155
|
+
console.log(testingbot.dashboardUrl());
|
|
156
|
+
});
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
### Available commands
|
|
160
|
+
|
|
161
|
+
| Command | TestingBot command | What it does |
|
|
162
|
+
| --- | --- | --- |
|
|
163
|
+
| `throttle(conditions)` | `tb:throttle` | Network conditions mid-test: `'Edge'`, `'3G'`, `'4G'`, `'airplane'`, `'disable'`, or `{ downloadSpeed, uploadSpeed, latency, loss }` (kb/s, ms). Reset automatically at the end of the test. |
|
|
164
|
+
| `annotate(text)` | `tb:test-context` | Logs a step into the session's command list, so the timeline shows what the test was doing. |
|
|
165
|
+
| `setName(name)` | `tb:test-name` | Names the session on the dashboard. |
|
|
166
|
+
| `setBuild(build)` | `tb:test-build` | Groups the session under a build. |
|
|
167
|
+
| `setTags([...])` | `tb:test-tags` | Tags the session. |
|
|
168
|
+
| `setResult(passed)` | `tb:test-result` | Overrides the pass/fail verdict (the driver reports it automatically — use this only for outcomes mobilewright cannot see). |
|
|
169
|
+
| `updateInfo({...})` | `tb:test-info` | Bulk update: `name`, `build`, `public`, `statusMessage`, `extra`. |
|
|
170
|
+
| `breakpoint()` | `tb:break` | Pauses the session for manual inspection with a live view. Debugging only — never leave it in CI. |
|
|
171
|
+
| `shell(command, args)` | `mobile: shell` | ADB shell on Android (whitelisted subset on physical devices). |
|
|
172
|
+
| `execute(script, ...args)` | any | Escape hatch for any Appium or TestingBot execute-script command. |
|
|
173
|
+
| `sessionId()` / `dashboardUrl()` | — | This test's TestingBot session id and dashboard link. |
|
|
174
|
+
|
|
175
|
+
Notes:
|
|
176
|
+
|
|
177
|
+
- **Throttling is reset automatically** when the test's device session is
|
|
178
|
+
released, so slow-network conditions never leak into the next test that
|
|
179
|
+
reuses the device.
|
|
180
|
+
- Calling a command outside a test body raises an explanatory error rather
|
|
181
|
+
than a null-reference.
|
|
182
|
+
|
|
183
|
+
### Recipe: name every session after its test
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
test.beforeEach(async ({ device }, testInfo) => {
|
|
187
|
+
await testingbot.setName(testInfo.title);
|
|
188
|
+
await testingbot.setTags([testInfo.project.name]);
|
|
189
|
+
});
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Requesting the `device` fixture is what makes the session exist before the
|
|
193
|
+
hook runs. With `sessionPerTest: true` each session gets exactly one name;
|
|
194
|
+
in pooled mode the last test to run on a session wins (the driver still
|
|
195
|
+
reports per-test verdicts afterwards — see above).
|
|
196
|
+
|
|
129
197
|
## Testing localhost / staging servers (TestingBot Tunnel)
|
|
130
198
|
|
|
131
199
|
Either run a [TestingBot Tunnel](https://testingbot.com/support/tunnel) yourself and pass its identifier:
|
|
@@ -169,10 +237,13 @@ jobs:
|
|
|
169
237
|
| `TestingBot sessions must start with an app` | Declare the app in the driver's `apps` option (per platform / device type). |
|
|
170
238
|
| `No TestingBot real device matches ...` / `No TestingBot simulator matches ...` | The criteria matched nothing in the catalog — check [available devices](https://testingbot.com/support/devices), loosen `osVersion`/`deviceName`. |
|
|
171
239
|
| Allocation waits for minutes | The matching device is busy; TestingBot queues the session until it frees up (bounded by `allocationTimeout`). |
|
|
240
|
+
| `Test timeout ... while setting up "device"` | More `workers` than your plan's parallel limit: TestingBot queues up to 2× the limit server-side and the driver waits out the rest, all inside the test-scoped device fixture. The driver warns with your plan's number — set `workers` to at most that, or raise the mobilewright `timeout`. |
|
|
172
241
|
| `... is a simulator-only build and cannot be installed on a real device` | Build a test-signed `.ipa` for `deviceType: 'real'` projects. |
|
|
173
|
-
| `
|
|
242
|
+
| `"..." is not installed on this device` | The config's `bundleId` is not the package id of the build in `apps` (check with `aapt2 dump packagename app.apk`), or a stale `bundleId`/env override is in play. |
|
|
243
|
+
| `cannot install "..." mid-session` | TestingBot has no mid-session installs — every app in `installApps` must also be listed in the driver's `apps` option (first entry = app under test, the rest install as helper apps). |
|
|
174
244
|
| Modifier chords fail on iOS | XCUITest cannot hold modifier keys; chords are Android-only (`clearText()` handles iOS differently). |
|
|
175
|
-
| Sessions show the run summary instead of a test name | The session hosted several tests;
|
|
245
|
+
| Sessions show the run summary instead of a test name | The session hosted several tests; `sessionPerTest: true` gives each test its own session. |
|
|
246
|
+
| A session shows no pass/fail at all | The driver could not tell which test ran in it and declined to guess. Run with `DEBUG=testingbot:observer` — it logs the session id and why. |
|
|
176
247
|
| `DEBUG=testingbot:*` | Prints every allocation, hub command, upload, and report the driver performs. |
|
|
177
248
|
|
|
178
249
|
## How it maps to TestingBot
|
|
@@ -180,16 +251,18 @@ jobs:
|
|
|
180
251
|
| mobilewright concept | TestingBot implementation |
|
|
181
252
|
| --- | --- |
|
|
182
253
|
| device allocation | Appium session on `hub.testingbot.com` (the session UUID is the device id); busy devices queue server-side |
|
|
183
|
-
| app install | uploaded to TestingBot storage at allocation time and passed as `appium:app` — no mid-session installs |
|
|
254
|
+
| app install | uploaded to TestingBot storage at allocation time and passed as `appium:app`, helper apps as `appium:otherApps` (re-signed automatically for real iOS) — no mid-session installs |
|
|
184
255
|
| taps/swipes/gestures | W3C pointer actions |
|
|
185
256
|
| view hierarchy | Appium page source, mapped to mobilewright's `ViewNode` tree |
|
|
186
257
|
| webviews | Appium contexts (`webViewBridge`) |
|
|
187
258
|
| pass/fail reporting | `PUT /v1/tests/:session` via the `TestObserver` hooks |
|
|
259
|
+
| git metadata | commit/branch/author/subject from the run report → session `extra` + branch tag |
|
|
188
260
|
| video | recorded by TestingBot; `startRecording({ output })` downloads the MP4 locally at run end |
|
|
189
261
|
|
|
190
262
|
## Current limitations
|
|
191
263
|
|
|
192
264
|
- One TestingBot session may host several mobilewright tests unless `sessionPerTest: true`; multi-test sessions report the run-level verdict.
|
|
265
|
+
- The worker process that owns a session is never told which test it is running, so the session-to-test join is reconstructed after the run from Playwright's worker index plus timing. Without the run report (`jsonReport()`) it degrades to timing alone. Either way it is deliberately conservative and leaves a session unjudged rather than risk naming it after the wrong test.
|
|
193
266
|
- Modifier key chords (`pressKeys(['ctrl+a'])`) work on Android only — XCUITest cannot hold modifier keys.
|
|
194
267
|
- `pressButton` on iOS supports `HOME`, `VOLUME_UP`, `VOLUME_DOWN`; `listApps()` reports the foreground app only.
|
|
195
268
|
- Screenshots are always PNG. `applyDeviceSettings` turns Android animations off best-effort via `mobile: shell` (a no-op on iOS).
|
|
@@ -206,6 +279,14 @@ npm run build
|
|
|
206
279
|
|
|
207
280
|
Live e2e tests against a real TestingBot account live in `e2e/` (see `e2e/mobilewright.config.ts`).
|
|
208
281
|
|
|
282
|
+
### Releasing
|
|
283
|
+
|
|
284
|
+
```bash
|
|
285
|
+
npm version minor -m "Release v%s" # patch | minor | major
|
|
286
|
+
git push --follow-tags
|
|
287
|
+
gh release create "v$(node -p "require('./package.json').version")" --generate-notes
|
|
288
|
+
```
|
|
289
|
+
|
|
209
290
|
## License
|
|
210
291
|
|
|
211
292
|
Apache-2.0
|
package/dist/capabilities.d.ts
CHANGED
|
@@ -16,12 +16,13 @@ export interface PinnedDevice {
|
|
|
16
16
|
* ('ios-real' beats 'ios'). Returns the raw configured value: a local path
|
|
17
17
|
* or a tb:// / https URL (the caller uploads local paths).
|
|
18
18
|
*/
|
|
19
|
-
export declare function
|
|
19
|
+
export declare function appsForCriteria(criteria: Pick<AllocationCriteria, 'platform' | 'deviceType'>, apps: Partial<Record<AppSlot, string | string[]>>): string[];
|
|
20
20
|
/**
|
|
21
21
|
* Build the W3C new-session payload for a set of allocation criteria.
|
|
22
22
|
* Pure — catalog lookups and app uploads happen in the caller; a pinned
|
|
23
23
|
* device (real-device allocations) overrides name/version wildcards, and
|
|
24
|
-
* `
|
|
24
|
+
* `appUrls` are the already-uploaded apps (tb:// or https): the first is the
|
|
25
|
+
* app under test, any others ride along as `appium:otherApps`.
|
|
25
26
|
*/
|
|
26
|
-
export declare function buildCapabilities(criteria: AllocationCriteria, options: ResolvedOptions, pinned?: PinnedDevice,
|
|
27
|
+
export declare function buildCapabilities(criteria: AllocationCriteria, options: ResolvedOptions, pinned?: PinnedDevice, appUrls?: string[]): W3CCapabilities;
|
|
27
28
|
//# sourceMappingURL=capabilities.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACvC;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAKD;;;;GAIG;AACH,wBAAgB,
|
|
1
|
+
{"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE7D,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACrC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;CACvC;AAED,sDAAsD;AACtD,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,+DAA+D;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC;CAChB;AAKD;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,IAAI,CAAC,kBAAkB,EAAE,UAAU,GAAG,YAAY,CAAC,EAC7D,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,GAChD,MAAM,EAAE,CAOV;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,kBAAkB,EAC5B,OAAO,EAAE,eAAe,EACxB,MAAM,CAAC,EAAE,YAAY,EACrB,OAAO,GAAE,MAAM,EAAO,GACrB,eAAe,CAwGjB"}
|
package/dist/capabilities.js
CHANGED
|
@@ -5,24 +5,24 @@ const REGEX_METACHARS = /[.*+?^${}()|[\]\\]/;
|
|
|
5
5
|
* ('ios-real' beats 'ios'). Returns the raw configured value: a local path
|
|
6
6
|
* or a tb:// / https URL (the caller uploads local paths).
|
|
7
7
|
*/
|
|
8
|
-
export function
|
|
8
|
+
export function appsForCriteria(criteria, apps) {
|
|
9
9
|
const platform = criteria.platform;
|
|
10
10
|
if (!platform)
|
|
11
|
-
return
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
return apps[platform];
|
|
11
|
+
return [];
|
|
12
|
+
const specific = criteria.deviceType ? apps[`${platform}-${criteria.deviceType}`] : undefined;
|
|
13
|
+
const configured = specific ?? apps[platform];
|
|
14
|
+
if (!configured)
|
|
15
|
+
return [];
|
|
16
|
+
return Array.isArray(configured) ? configured.filter(Boolean) : [configured];
|
|
18
17
|
}
|
|
19
18
|
/**
|
|
20
19
|
* Build the W3C new-session payload for a set of allocation criteria.
|
|
21
20
|
* Pure — catalog lookups and app uploads happen in the caller; a pinned
|
|
22
21
|
* device (real-device allocations) overrides name/version wildcards, and
|
|
23
|
-
* `
|
|
22
|
+
* `appUrls` are the already-uploaded apps (tb:// or https): the first is the
|
|
23
|
+
* app under test, any others ride along as `appium:otherApps`.
|
|
24
24
|
*/
|
|
25
|
-
export function buildCapabilities(criteria, options, pinned,
|
|
25
|
+
export function buildCapabilities(criteria, options, pinned, appUrls = []) {
|
|
26
26
|
const platform = criteria.platform;
|
|
27
27
|
if (!platform) {
|
|
28
28
|
throw new Error('TestingBotDriver.allocate requires a platform ("ios" or "android"). ' +
|
|
@@ -35,8 +35,13 @@ export function buildCapabilities(criteria, options, pinned, appUrl) {
|
|
|
35
35
|
platformName: platform === 'ios' ? 'iOS' : 'Android',
|
|
36
36
|
'appium:automationName': platform === 'ios' ? 'XCUITest' : 'UiAutomator2',
|
|
37
37
|
};
|
|
38
|
+
const [appUrl, ...otherApps] = appUrls;
|
|
38
39
|
if (appUrl)
|
|
39
40
|
caps['appium:app'] = appUrl;
|
|
41
|
+
// TestingBot installs these alongside the app under test (and re-signs them
|
|
42
|
+
// for real iOS devices).
|
|
43
|
+
if (otherApps.length)
|
|
44
|
+
caps['appium:otherApps'] = otherApps;
|
|
40
45
|
// TestingBot has no app-less sessions: every session must start with an
|
|
41
46
|
// app or a browser. Fail here with guidance instead of a cryptic hub error.
|
|
42
47
|
const escape = options.capabilities;
|
|
@@ -76,6 +81,12 @@ export function buildCapabilities(criteria, options, pinned, appUrl) {
|
|
|
76
81
|
caps['appium:platformVersion'] = criteria.osVersion;
|
|
77
82
|
}
|
|
78
83
|
}
|
|
84
|
+
if (platform === 'android' && options.autoGrantPermissions !== undefined) {
|
|
85
|
+
caps['appium:autoGrantPermissions'] = options.autoGrantPermissions;
|
|
86
|
+
}
|
|
87
|
+
if (platform === 'ios' && options.autoAcceptAlerts !== undefined) {
|
|
88
|
+
caps['appium:autoAcceptAlerts'] = options.autoAcceptAlerts;
|
|
89
|
+
}
|
|
79
90
|
const tbOptions = {
|
|
80
91
|
key: options.key,
|
|
81
92
|
secret: options.secret,
|
package/dist/capabilities.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"AAgBA,kFAAkF;AAClF,MAAM,eAAe,GAAG,oBAAoB,CAAC;AAE7C;;;;GAIG;AACH,MAAM,UAAU,
|
|
1
|
+
{"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../src/capabilities.ts"],"names":[],"mappings":"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"}
|
package/dist/driver.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type AllocatedDevice, type AllocationCriteria, type AppInfo, type ConnectionConfig, type DeviceInfo, type DeviceSettings, type GestureSequence, type HardwareButton, type LaunchOptions, type ListDevicesOptions, type MobilewrightDriver, type Orientation, type RecordingOptions, type RecordingResult, type ScreenSize, type ScreenshotOptions, type Session, type SwipeDirection, type SwipeOptions, type TestObserver, type ViewNode } from '@mobilewright/protocol';
|
|
2
2
|
import { type TestingBotDriverOptions } from './options.js';
|
|
3
|
+
import { type ThrottlePreset, type ThrottleProfile } from './session-commands.js';
|
|
3
4
|
import { AppiumWebViewBridge } from './webview.js';
|
|
4
5
|
/**
|
|
5
6
|
* Mobilewright driver for TestingBot's device cloud.
|
|
@@ -35,6 +36,11 @@ export declare class TestingBotDriver implements MobilewrightDriver {
|
|
|
35
36
|
private active;
|
|
36
37
|
/** sessionPerTest: pool slots whose original session this instance has already probed/used. */
|
|
37
38
|
private readonly rotatedDeviceIds;
|
|
39
|
+
/** Plan limits from GET /v1/user, for the over-parallelism warning. */
|
|
40
|
+
private userInfo;
|
|
41
|
+
/** Allocations currently waiting on POST /session. */
|
|
42
|
+
private inFlightAllocations;
|
|
43
|
+
private warnedAboutParallelism;
|
|
38
44
|
constructor(options?: TestingBotDriverOptions);
|
|
39
45
|
prepare(): Promise<void>;
|
|
40
46
|
allocate(criteria: AllocationCriteria, _takenDeviceIds: ReadonlySet<string>, signal?: AbortSignal): Promise<AllocatedDevice>;
|
|
@@ -44,6 +50,12 @@ export declare class TestingBotDriver implements MobilewrightDriver {
|
|
|
44
50
|
disconnect(): Promise<void>;
|
|
45
51
|
/** Create a new hub session for this slot from the connect() config. */
|
|
46
52
|
private startFreshSession;
|
|
53
|
+
/** Run any Appium/TestingBot execute-script command on the live session. */
|
|
54
|
+
executeScript<T = unknown>(script: string, ...args: unknown[]): Promise<T>;
|
|
55
|
+
/** WebDriver session id of the device this instance is connected to. */
|
|
56
|
+
currentSessionId(): string;
|
|
57
|
+
/** `tb:throttle` — change network conditions mid-test. */
|
|
58
|
+
throttle(conditions: ThrottlePreset | ThrottleProfile): Promise<void>;
|
|
47
59
|
/**
|
|
48
60
|
* Best-effort, fire-and-forget per the protocol: Android animation scales
|
|
49
61
|
* via `mobile: shell` (may be unavailable depending on TestingBot's Appium
|
|
@@ -65,6 +77,15 @@ export declare class TestingBotDriver implements MobilewrightDriver {
|
|
|
65
77
|
getOrientation(): Promise<Orientation>;
|
|
66
78
|
setOrientation(orientation: Orientation): Promise<void>;
|
|
67
79
|
launchApp(bundleId: string, opts?: LaunchOptions): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Appium reports a missing package and a missing launcher activity with the
|
|
82
|
+
* same "Unable to resolve the launchable activity" text. Ask the device
|
|
83
|
+
* which case it is: a bundleId that is simply not installed (the usual
|
|
84
|
+
* cause — the config's `bundleId` does not match the package id of the
|
|
85
|
+
* build in `apps`) deserves a completely different fix than a real
|
|
86
|
+
* launcher-activity problem.
|
|
87
|
+
*/
|
|
88
|
+
private explainLaunchFailure;
|
|
68
89
|
terminateApp(bundleId: string): Promise<void>;
|
|
69
90
|
listApps(): Promise<AppInfo[]>;
|
|
70
91
|
getForegroundApp(): Promise<AppInfo>;
|
|
@@ -75,6 +96,14 @@ export declare class TestingBotDriver implements MobilewrightDriver {
|
|
|
75
96
|
startRecording(opts: RecordingOptions): Promise<void>;
|
|
76
97
|
stopRecording(): Promise<RecordingResult>;
|
|
77
98
|
private session;
|
|
99
|
+
/**
|
|
100
|
+
* Requesting more parallel sessions than the plan allows is not fatal —
|
|
101
|
+
* TestingBot queues the extras — but the wait happens inside mobilewright's
|
|
102
|
+
* test-scoped device fixture, so queued tests can hit the Playwright test
|
|
103
|
+
* timeout with a confusing "timed out setting up device" failure. Warn once
|
|
104
|
+
* with the actual numbers instead.
|
|
105
|
+
*/
|
|
106
|
+
private warnIfOverParallel;
|
|
78
107
|
private unpin;
|
|
79
108
|
}
|
|
80
109
|
//# sourceMappingURL=driver.d.ts.map
|
package/dist/driver.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,eAAe,EACpB,KAAK,kBAAkB,EACvB,KAAK,OAAO,EACZ,KAAK,gBAAgB,EACrB,KAAK,UAAU,EACf,KAAK,cAAc,EAEnB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAEhB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,OAAO,EACZ,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,QAAQ,EACd,MAAM,wBAAwB,CAAC;AAehC,OAAO,EAA4D,KAAK,uBAAuB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"driver.d.ts","sourceRoot":"","sources":["../src/driver.ts"],"names":[],"mappings":"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"}
|