@keepsnappy/agent 0.1.0-alpha.24 → 0.1.0-alpha.25
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 +2 -2
- package/dist/appium-driver.js +39 -7
- package/dist/keepsnappy.js +829 -446
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ keepsnappy run -- maestro test --include-tags perf e2e/flows/ # wrap your own
|
|
|
37
37
|
keepsnappy run --upload --wait # send the results; the server judges them and the CLI prints its verdict
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
Flags: `--config` (default `keepsnappy.yml`), `--repeat N`, `--out DIR`, `--upload [--ingest-url URL]`, `--wait`, `--fail-on-worse`, `--commit SHA` / `--branch NAME` (default `GITHUB_SHA` / `GITHUB_HEAD_REF` on a pull request, else `GITHUB_REF_NAME`), `--base-commit SHA`, `--device-model` / `--device-os` (default: read from
|
|
40
|
+
Flags: `--config` (default `keepsnappy.yml`), `--repeat N`, `--out DIR`, `--upload [--ingest-url URL]`, `--wait`, `--fail-on-worse`, `--commit SHA` / `--branch NAME` (default `GITHUB_SHA` / `GITHUB_HEAD_REF` on a pull request, else `GITHUB_REF_NAME`), `--base-commit SHA`, `--device UDID` (the simulator or iPhone to run on; default `device:` in keepsnappy.yml, else the only booted simulator; with several booted and none named the run refuses and lists them; every Maestro command gets it as `maestro --device UDID`), `--device-model` / `--device-os` (default: read from that simulator), `--scenario NAME` (flow name for a command given after `--`), `--replay <out dir>[:ARM]` (repeatable: take recorded runs instead of running anything), `--no-markers`, `--clock-offset-ms`.
|
|
41
41
|
|
|
42
42
|
Commit details: when a run set opens (`keepsnappy run --upload`, `keepsnappy farm open`), the CLI reads the subject, author
|
|
43
43
|
name and committer date of `--commit` and `--base-commit` with `git log` in the checkout (argv only, 5 s timeout) and
|
|
@@ -60,7 +60,7 @@ Annotated example: `examples/keepsnappy.yml`. Every setting:
|
|
|
60
60
|
| `project.key_env` | Name of the env var with the project key (default `KEEPSNAPPY_KEY`). Only read with `--upload`. |
|
|
61
61
|
| `project.ingest_url` | KeepSnappy API base URL for `--upload` (or `--ingest-url`, or env `KEEPSNAPPY_API`). Defaults to the hosted KeepSnappy API. |
|
|
62
62
|
| `app.bundle_id` | Required. App whose keepsnappy log is read after each run. |
|
|
63
|
-
| `platform`, `device` | `ios-sim` (default; `device` = UDID or `booted`) or `ios-device` (UDID required). One device per invocation in v1. |
|
|
63
|
+
| `platform`, `device` | `ios-sim` (default; `device` = UDID, or `booted` for the only booted simulator: with several booted, name one) or `ios-device` (UDID required). One device per invocation in v1. |
|
|
64
64
|
| `repetitions` | Runs of the whole flow set (default 5). |
|
|
65
65
|
| `flows.runner` | `maestro` (build `maestro test ...`) or `command` (run `flows.command`). |
|
|
66
66
|
| `flows.tags`, `flows.exclude_tags` | `--include-tags=` / `--exclude-tags=`. |
|
package/dist/appium-driver.js
CHANGED
|
@@ -546,7 +546,6 @@ class Driver {
|
|
|
546
546
|
}
|
|
547
547
|
]
|
|
548
548
|
});
|
|
549
|
-
await this.cmd("DELETE", "/actions").catch(() => null);
|
|
550
549
|
}
|
|
551
550
|
async stroke(from, points, note) {
|
|
552
551
|
const safe = await this.safeBottomY();
|
|
@@ -662,7 +661,7 @@ async function iosCommand(driver, link, cmd, n, state, waitMs) {
|
|
|
662
661
|
}
|
|
663
662
|
var ANDROID = {
|
|
664
663
|
name: "android",
|
|
665
|
-
defaultDevice: "Google Pixel
|
|
664
|
+
defaultDevice: "Google Pixel 9 Pro-15.0",
|
|
666
665
|
caps: (device) => ({
|
|
667
666
|
platformName: "Android",
|
|
668
667
|
"appium:automationName": "UiAutomator2",
|
|
@@ -937,7 +936,7 @@ async function rowsInList(t, spec, c) {
|
|
|
937
936
|
const android = t.driver.platform.name === "android";
|
|
938
937
|
const call = spec.field === "description" ? /descriptionMatches/ : /textMatches/;
|
|
939
938
|
const own = android && spec.field ? all.filter((l) => call.test(l.value)) : all;
|
|
940
|
-
const attr = sel
|
|
939
|
+
const attr = rowAttr(t, sel, spec.field);
|
|
941
940
|
const frames = [];
|
|
942
941
|
const inside = [];
|
|
943
942
|
const parent = spec.scoped ?? true ? c.at.el : undefined;
|
|
@@ -963,6 +962,10 @@ async function rowsInList(t, spec, c) {
|
|
|
963
962
|
ids.push(String(await t.driver.attribute(el, attr).catch(() => null) ?? ""));
|
|
964
963
|
return { ids, frames, list };
|
|
965
964
|
}
|
|
965
|
+
var rowAttr = (t, sel, field) => {
|
|
966
|
+
const android = t.driver.platform.name === "android";
|
|
967
|
+
return sel.text !== undefined ? android ? field === "description" ? "content-desc" : "text" : "label" : android ? "resource-id" : "name";
|
|
968
|
+
};
|
|
966
969
|
function room(t, { frames, list }, toward) {
|
|
967
970
|
if (frames.length === 0)
|
|
968
971
|
return false;
|
|
@@ -1140,7 +1143,7 @@ import { dirname, join, relative, resolve } from "node:path";
|
|
|
1140
1143
|
import { fileURLToPath } from "node:url";
|
|
1141
1144
|
var DRIVER_NAME = "appium";
|
|
1142
1145
|
function driverVersion() {
|
|
1143
|
-
const built = "0.1.0-alpha.
|
|
1146
|
+
const built = "0.1.0-alpha.25";
|
|
1144
1147
|
if (built)
|
|
1145
1148
|
return built;
|
|
1146
1149
|
let dir = dirname(fileURLToPath(import.meta.url));
|
|
@@ -1334,6 +1337,7 @@ class Session {
|
|
|
1334
1337
|
capture;
|
|
1335
1338
|
gcN = 0;
|
|
1336
1339
|
stepDepth = 0;
|
|
1340
|
+
beforeLaunchOpen = false;
|
|
1337
1341
|
stepLog = [];
|
|
1338
1342
|
defects = { gestureRetry: 0, navStuck: 0, navCatchUp: 0, items: [] };
|
|
1339
1343
|
navigating = false;
|
|
@@ -1352,6 +1356,7 @@ class Session {
|
|
|
1352
1356
|
this.capture = o.capture;
|
|
1353
1357
|
}
|
|
1354
1358
|
async gc(atEnd = false) {
|
|
1359
|
+
this.notInBeforeLaunch("GC command");
|
|
1355
1360
|
if (this.actionGc !== "step" && !(atEnd && this.endGc && this.actionGc === "off"))
|
|
1356
1361
|
return null;
|
|
1357
1362
|
if (this.linkState.linkMode === "safari") {
|
|
@@ -1369,6 +1374,7 @@ class Session {
|
|
|
1369
1374
|
}
|
|
1370
1375
|
memReadOff = false;
|
|
1371
1376
|
async memRead(n) {
|
|
1377
|
+
this.notInBeforeLaunch("memory read");
|
|
1372
1378
|
if (n === null || this.memReadOff || this.linkState.linkMode === "safari")
|
|
1373
1379
|
return false;
|
|
1374
1380
|
const start = this.now();
|
|
@@ -1398,6 +1404,7 @@ class Session {
|
|
|
1398
1404
|
return took;
|
|
1399
1405
|
}
|
|
1400
1406
|
async navigation(fn) {
|
|
1407
|
+
this.notInBeforeLaunch("navigation pass");
|
|
1401
1408
|
if (this.stepDepth > 0)
|
|
1402
1409
|
throw new Error("a navigation pass cannot run inside a measured step");
|
|
1403
1410
|
if (this.navigating)
|
|
@@ -1419,10 +1426,16 @@ class Session {
|
|
|
1419
1426
|
declaresSteps() {
|
|
1420
1427
|
return this.markers.some((m) => m.phase === "start" && m.step !== NAV_STEP);
|
|
1421
1428
|
}
|
|
1429
|
+
notInBeforeLaunch(what) {
|
|
1430
|
+
if (this.beforeLaunchOpen)
|
|
1431
|
+
throw new Error(`no step or marker in beforeLaunch (${what}): it runs before the measured launch`);
|
|
1432
|
+
}
|
|
1422
1433
|
marker(step, phase, gcN = null) {
|
|
1434
|
+
this.notInBeforeLaunch(`marker ${step}:${phase}`);
|
|
1423
1435
|
this.markers.push({ token: this.token, step, phase, t_host: this.now(), ...gcN !== null ? { gc_n: gcN } : {} });
|
|
1424
1436
|
}
|
|
1425
1437
|
async step(name, action, checks = noop, once = noop) {
|
|
1438
|
+
this.notInBeforeLaunch(`step ${name}`);
|
|
1426
1439
|
if (this.navigating)
|
|
1427
1440
|
throw new Error("a measured step cannot start inside a navigation pass");
|
|
1428
1441
|
await this.ready();
|
|
@@ -1743,7 +1756,7 @@ class Session {
|
|
|
1743
1756
|
activate() {
|
|
1744
1757
|
return this.driver.activate();
|
|
1745
1758
|
}
|
|
1746
|
-
async beginRun({ token, args = "", home }) {
|
|
1759
|
+
async beginRun({ token, args = "", home, beforeLaunch }) {
|
|
1747
1760
|
this.token = token;
|
|
1748
1761
|
if (token)
|
|
1749
1762
|
this.opened.add(token);
|
|
@@ -1753,6 +1766,14 @@ class Session {
|
|
|
1753
1766
|
await this.waitVisible(home, HOME_READY_MS);
|
|
1754
1767
|
const alert = await this.openLink(activationLink(this.scheme, token, args));
|
|
1755
1768
|
await this.driver.sleep(ACTIVATION_SETTLE_MS);
|
|
1769
|
+
if (beforeLaunch) {
|
|
1770
|
+
this.beforeLaunchOpen = true;
|
|
1771
|
+
try {
|
|
1772
|
+
await beforeLaunch();
|
|
1773
|
+
} finally {
|
|
1774
|
+
this.beforeLaunchOpen = false;
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1756
1777
|
await this.relaunch();
|
|
1757
1778
|
await this.waitVisible(home, HOME_READY_MS);
|
|
1758
1779
|
if (token && this.linkState.linkMode !== "safari")
|
|
@@ -2160,11 +2181,22 @@ async function runIterations(t, module, ctx, iterations, progress) {
|
|
|
2160
2181
|
progress();
|
|
2161
2182
|
driver.context = { journey: it.journey, iteration: it.iteration, phase: "begin" };
|
|
2162
2183
|
const t0 = t.now();
|
|
2163
|
-
|
|
2184
|
+
const itCtx = { ...ctx, journey: it.journey, iteration: it.iteration };
|
|
2185
|
+
const hook = module.beforeLaunch;
|
|
2186
|
+
const beforeLaunch = hook && (async () => {
|
|
2187
|
+
driver.context = { ...driver.context, phase: "before-launch" };
|
|
2188
|
+
try {
|
|
2189
|
+
await hook(t, itCtx);
|
|
2190
|
+
} catch (e) {
|
|
2191
|
+
throw new Error(`beforeLaunch failed (${it.journey} #${it.iteration}): ${e instanceof Error ? e.message : String(e)}`, { cause: e });
|
|
2192
|
+
}
|
|
2193
|
+
driver.context = { ...driver.context, phase: "begin" };
|
|
2194
|
+
});
|
|
2195
|
+
rec.alert = await t.beginRun({ token: it.token, home: module.home, ...module.sdkArgs ? { args: module.sdkArgs } : {}, ...beforeLaunch ? { beforeLaunch } : {} });
|
|
2164
2196
|
const t1 = t.now();
|
|
2165
2197
|
driver.context = { ...driver.context, phase: "journey" };
|
|
2166
2198
|
const waitsBefore = t.readyWaits.length;
|
|
2167
|
-
await module.journeys[it.journey](t,
|
|
2199
|
+
await module.journeys[it.journey](t, itCtx);
|
|
2168
2200
|
await t.ready();
|
|
2169
2201
|
const t2 = t.now();
|
|
2170
2202
|
t.marker("run:complete", "end");
|