prowl-tools 0.1.5 → 0.1.7
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 +578 -185
- package/dist/{chunk-ITOSUJCN.js → chunk-O3OUTZ2P.js} +22 -2
- package/dist/chunk-O3OUTZ2P.js.map +1 -0
- package/dist/{chunk-2KD2XCTH.js → chunk-R7NUH44M.js} +1690 -298
- package/dist/chunk-R7NUH44M.js.map +1 -0
- package/dist/index.cjs +1839 -365
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +277 -19
- package/dist/index.js.map +1 -1
- package/dist/lib.cjs +1864 -403
- package/dist/lib.cjs.map +1 -1
- package/dist/lib.d.cts +702 -58
- package/dist/lib.d.ts +702 -58
- package/dist/lib.js +130 -8
- package/dist/{loader-FCXPARP7.js → loader-X37URHUV.js} +2 -2
- package/examples/hunts/hello.yml +1 -1
- package/examples/hunts/login-flow.yml +58 -0
- package/package.json +6 -3
- package/dist/chunk-2KD2XCTH.js.map +0 -1
- package/dist/chunk-ITOSUJCN.js.map +0 -1
- /package/dist/{loader-FCXPARP7.js.map → loader-X37URHUV.js.map} +0 -0
package/dist/lib.d.ts
CHANGED
|
@@ -296,7 +296,10 @@ type AssertScreenshotStep = {
|
|
|
296
296
|
threshold?: number;
|
|
297
297
|
};
|
|
298
298
|
};
|
|
299
|
-
type
|
|
299
|
+
type AssertWithAiStep = {
|
|
300
|
+
assertWithAI: string;
|
|
301
|
+
};
|
|
302
|
+
type Step = NavigateStep | ClickStep | FillStep | TypeStep | PressStep | WaitStep | SelectOptionStep | SelectStep | OnDialogStep | SetInputFilesStep | InlineAssertStep | RunHuntStep | WaitForSelectorStep | WaitForUrlStep | WaitForNetworkIdleStep | HoverStep | ScrollStep | ScrollToStep | ScreenshotStep | IfStep | RepeatStep | MockRouteStep | UnmockRouteStep | EvalScriptStep | RunScriptStep | AssertScreenshotStep | AssertWithAiStep | CopyTextStep | WaitForDownloadStep;
|
|
300
303
|
type Assertion = {
|
|
301
304
|
selectorExists: string;
|
|
302
305
|
} | {
|
|
@@ -312,7 +315,7 @@ type Assertion = {
|
|
|
312
315
|
};
|
|
313
316
|
type StepResult = {
|
|
314
317
|
type: string;
|
|
315
|
-
status: "pass" | "fail";
|
|
318
|
+
status: "pass" | "fail" | "warn";
|
|
316
319
|
durationMs: number;
|
|
317
320
|
selector?: string;
|
|
318
321
|
value?: string;
|
|
@@ -324,7 +327,7 @@ type StepResult = {
|
|
|
324
327
|
type AssertionResult = {
|
|
325
328
|
type: string;
|
|
326
329
|
value?: string | boolean;
|
|
327
|
-
status: "pass" | "fail";
|
|
330
|
+
status: "pass" | "fail" | "skipped";
|
|
328
331
|
error?: string;
|
|
329
332
|
};
|
|
330
333
|
type RunArtifacts = {
|
|
@@ -541,13 +544,57 @@ declare function parseMacSelector(selector: string): MacQuery;
|
|
|
541
544
|
/** Wrap a live {@link MacHelperClient} as a {@link SessionDriver}. */
|
|
542
545
|
declare function createMacDriver(client: MacHelperClient, options?: MacDriverOptions): SessionDriver;
|
|
543
546
|
|
|
547
|
+
/** Basename of the helper executable, in the tarball and on disk. */
|
|
548
|
+
declare const HELPER_BINARY = "prowl-macdriver";
|
|
549
|
+
/** Pinned helper version. A CLI release always installs this exact version. */
|
|
550
|
+
declare const MACDRIVER_VERSION = "0.1.0";
|
|
551
|
+
/** GitHub `owner/repo` that hosts the helper releases. */
|
|
552
|
+
declare const MACDRIVER_REPO = "prowl-tools/prowl";
|
|
553
|
+
/** Code-signing identifier assigned to release builds. */
|
|
554
|
+
declare const MACDRIVER_SIGNING_IDENTIFIER = "tools.prowl.macdriver";
|
|
555
|
+
/** Developer ID Application common-name prefix expected on release builds. */
|
|
556
|
+
declare const MACDRIVER_SIGNING_AUTHORITY_PREFIX = "Developer ID Application: Genkei Labs";
|
|
557
|
+
/** Accepted helper release version token. */
|
|
558
|
+
declare const MACDRIVER_VERSION_PATTERN: RegExp;
|
|
559
|
+
/** Validate the helper release version before using it in URLs or paths. */
|
|
560
|
+
declare function validateMacdriverVersion(version: string): string;
|
|
561
|
+
/** Git tag for a helper version — the release the workflow builds. */
|
|
562
|
+
declare function macdriverReleaseTag(version?: string): string;
|
|
563
|
+
/** Release asset file name for the universal (arm64 + x86_64) binary zip. */
|
|
564
|
+
declare function macdriverAssetName(version?: string): string;
|
|
565
|
+
/** Release asset file name for the SHA-256 checksum sidecar. */
|
|
566
|
+
declare function macdriverChecksumName(version?: string): string;
|
|
567
|
+
/** Download URL for a named asset of the pinned release (follows redirects). */
|
|
568
|
+
declare function macdriverAssetUrl(assetName: string, version?: string): string;
|
|
569
|
+
/** Root of all user-level installs: `~/.prowl/macdriver`. */
|
|
570
|
+
declare function macdriverInstallRoot(homedir?: string): string;
|
|
571
|
+
/** Directory that holds a specific installed version. */
|
|
572
|
+
declare function macdriverVersionDir(version?: string, homedir?: string): string;
|
|
573
|
+
/** Absolute path to the installed helper binary for a version. */
|
|
574
|
+
declare function macdriverInstalledBinary(version?: string, homedir?: string): string;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Guidance shown when the helper can't be resolved. Leads with the
|
|
578
|
+
* two-minute `prowl macdriver install` path; source build and the
|
|
579
|
+
* `PROWL_MACDRIVER_BIN` override are the contributor fallbacks.
|
|
580
|
+
*/
|
|
544
581
|
declare function macdriverBuildInstructions(): string;
|
|
582
|
+
type ResolveHelperOptions = {
|
|
583
|
+
/** Home directory for the user-level install lookup (defaults to `os.homedir()`). */
|
|
584
|
+
homedir?: string;
|
|
585
|
+
};
|
|
545
586
|
/**
|
|
546
|
-
* Resolve the helper binary path.
|
|
547
|
-
*
|
|
548
|
-
*
|
|
587
|
+
* Resolve the helper binary path. Search order (documented in the README's
|
|
588
|
+
* macOS Target section):
|
|
589
|
+
* 1. `PROWL_MACDRIVER_BIN` env override (absolute path to the binary);
|
|
590
|
+
* 2. the user-level install of the pinned version at
|
|
591
|
+
* `~/.prowl/macdriver/<MACDRIVER_VERSION>/prowl-macdriver`
|
|
592
|
+
* (what `prowl macdriver install` writes);
|
|
593
|
+
* 3. the contributor's repo-local source build under `macdriver/.build/`
|
|
594
|
+
* (`release` then `debug`).
|
|
595
|
+
* Throws with install-first guidance when none is found.
|
|
549
596
|
*/
|
|
550
|
-
declare function resolveHelperBinary(env?: NodeJS.ProcessEnv): string;
|
|
597
|
+
declare function resolveHelperBinary(env?: NodeJS.ProcessEnv, options?: ResolveHelperOptions): string;
|
|
551
598
|
/** Default per-request deadline for the helper transport. */
|
|
552
599
|
declare const DEFAULT_REQUEST_TIMEOUT_MS = 30000;
|
|
553
600
|
type SpawnMacHelperOptions = {
|
|
@@ -591,6 +638,291 @@ declare function launchMacSession(options: LaunchMacOptions): Promise<MacSession
|
|
|
591
638
|
/** Quit the target app (best effort) and shut the helper down. */
|
|
592
639
|
declare function closeMacSession(session: MacSession): Promise<void>;
|
|
593
640
|
|
|
641
|
+
/** Minimal shape of the `fetch` responses this module consumes. */
|
|
642
|
+
interface FetchResponseLike {
|
|
643
|
+
ok: boolean;
|
|
644
|
+
status: number;
|
|
645
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
646
|
+
text(): Promise<string>;
|
|
647
|
+
}
|
|
648
|
+
/** A `fetch`-like function; the real global `fetch` satisfies it. */
|
|
649
|
+
type FetchLike$2 = (url: string, init?: {
|
|
650
|
+
redirect?: "follow" | "error" | "manual";
|
|
651
|
+
}) => Promise<FetchResponseLike>;
|
|
652
|
+
/** Extracts the binary out of a downloaded zip into `destDir`. */
|
|
653
|
+
type Extractor = (zipPath: string, destDir: string) => Promise<void>;
|
|
654
|
+
/** Lists the raw path entries in a downloaded zip before extraction. */
|
|
655
|
+
type ArchiveLister = (zipPath: string) => Promise<string[]>;
|
|
656
|
+
/** Verifies the code signature of an installed binary; throws if invalid. */
|
|
657
|
+
type SignatureVerifier = (binaryPath: string) => Promise<void>;
|
|
658
|
+
interface CommandResult {
|
|
659
|
+
stdout?: string | Buffer;
|
|
660
|
+
stderr?: string | Buffer;
|
|
661
|
+
}
|
|
662
|
+
type CommandRunner = (file: string, args: string[]) => Promise<CommandResult>;
|
|
663
|
+
/** Parse the hex digest out of a `shasum`-style `.sha256` file. */
|
|
664
|
+
declare function parseChecksumFile(text: string): string;
|
|
665
|
+
/** SHA-256 of a buffer, lowercase hex. */
|
|
666
|
+
declare function sha256Hex(bytes: Buffer): string;
|
|
667
|
+
/** Default archive lister: `zipinfo -1`, used before any extraction happens. */
|
|
668
|
+
declare const zipinfoArchiveLister: ArchiveLister;
|
|
669
|
+
/** Validate the zip member list before extraction. */
|
|
670
|
+
declare function validateMacdriverArchiveEntries(entries: string[]): void;
|
|
671
|
+
/** Default extractor: Apple's `ditto`, which preserves the code signature. */
|
|
672
|
+
declare const dittoExtractor: Extractor;
|
|
673
|
+
interface CodesignDetails {
|
|
674
|
+
identifier: string | null;
|
|
675
|
+
authorities: string[];
|
|
676
|
+
teamIdentifier: string | null;
|
|
677
|
+
}
|
|
678
|
+
declare function parseCodesignDetails(text: string): CodesignDetails;
|
|
679
|
+
declare function verifyMacdriverSignature(binaryPath: string, run?: CommandRunner): Promise<void>;
|
|
680
|
+
/**
|
|
681
|
+
* Default signature verifier: fail closed unless `codesign` verifies the
|
|
682
|
+
* signature, the identity matches the release contract, and Gatekeeper accepts
|
|
683
|
+
* the executable through `spctl --assess --type execute`.
|
|
684
|
+
*/
|
|
685
|
+
declare const codesignVerifier: SignatureVerifier;
|
|
686
|
+
interface DownloadOptions {
|
|
687
|
+
version?: string;
|
|
688
|
+
fetchImpl?: FetchLike$2;
|
|
689
|
+
}
|
|
690
|
+
/**
|
|
691
|
+
* Download the pinned release's universal-binary zip and verify its SHA-256
|
|
692
|
+
* against the released `.sha256` sidecar. Returns the verified zip bytes.
|
|
693
|
+
* Throws a clear, actionable error on a 404 (no release cut yet) or a checksum
|
|
694
|
+
* mismatch.
|
|
695
|
+
*/
|
|
696
|
+
declare function downloadAndVerify(options?: DownloadOptions): Promise<Buffer>;
|
|
697
|
+
interface InstallOptions {
|
|
698
|
+
version?: string;
|
|
699
|
+
force?: boolean;
|
|
700
|
+
homedir?: string;
|
|
701
|
+
fetchImpl?: FetchLike$2;
|
|
702
|
+
listArchiveEntries?: ArchiveLister;
|
|
703
|
+
extract?: Extractor;
|
|
704
|
+
verifySignature?: SignatureVerifier;
|
|
705
|
+
}
|
|
706
|
+
interface InstallResult {
|
|
707
|
+
version: string;
|
|
708
|
+
binaryPath: string;
|
|
709
|
+
alreadyInstalled: boolean;
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Install the pinned helper to `~/.prowl/macdriver/<version>/prowl-macdriver`
|
|
713
|
+
* (0755). Returns `alreadyInstalled: true` (a no-op) when the binary is already
|
|
714
|
+
* present and `force` is not set. The downloaded archive is inspected before
|
|
715
|
+
* extraction, extracted into a temporary staging directory, and moved into place
|
|
716
|
+
* only after the staged helper passes file and signature verification.
|
|
717
|
+
*/
|
|
718
|
+
declare function installMacdriver(options?: InstallOptions): Promise<InstallResult>;
|
|
719
|
+
interface InstalledVersion {
|
|
720
|
+
version: string;
|
|
721
|
+
binaryPath: string;
|
|
722
|
+
}
|
|
723
|
+
interface MacdriverStatus {
|
|
724
|
+
/** The binary Prowl would use now, and how it was found. */
|
|
725
|
+
resolved: {
|
|
726
|
+
path: string;
|
|
727
|
+
source: "env" | "user-install" | "source-build";
|
|
728
|
+
} | null;
|
|
729
|
+
/** The pinned version the CLI targets. */
|
|
730
|
+
pinnedVersion: string;
|
|
731
|
+
/** All versions found under `~/.prowl/macdriver/`. */
|
|
732
|
+
installed: InstalledVersion[];
|
|
733
|
+
/** Version string the resolved binary reports, or null if it couldn't run. */
|
|
734
|
+
probedVersion: string | null;
|
|
735
|
+
}
|
|
736
|
+
/** Probe a helper binary's `version` output; null if it can't be run/parsed. */
|
|
737
|
+
type VersionProbe = (binaryPath: string) => Promise<string | null>;
|
|
738
|
+
/** Default probe: run `<binary> version` and parse the `prowl-macdriver X.Y.Z` line. */
|
|
739
|
+
declare const runVersionProbe: VersionProbe;
|
|
740
|
+
interface StatusOptions {
|
|
741
|
+
env?: NodeJS.ProcessEnv;
|
|
742
|
+
homedir?: string;
|
|
743
|
+
probe?: VersionProbe;
|
|
744
|
+
}
|
|
745
|
+
/**
|
|
746
|
+
* Gather the state `prowl macdriver status` reports: the resolved binary and
|
|
747
|
+
* how it was found, every installed version, and the resolved binary's probed
|
|
748
|
+
* version. Pure aside from filesystem reads and the (injectable) probe.
|
|
749
|
+
*/
|
|
750
|
+
declare function collectMacdriverStatus(options?: StatusOptions): Promise<MacdriverStatus>;
|
|
751
|
+
/** Static TCC-permission guidance printed after install / in status. */
|
|
752
|
+
declare function tccGuidance(): string;
|
|
753
|
+
|
|
754
|
+
/** The selector kinds the native dialect understands. */
|
|
755
|
+
type NativeSelectorKind = "id" | "label" | "text" | "role" | "focused";
|
|
756
|
+
/**
|
|
757
|
+
* A Prowl native selector parsed into a neutral, platform-independent shape.
|
|
758
|
+
* `role` optionally carries a `name` (the `role=Type[name="…"]` form); `name` is
|
|
759
|
+
* present only when the bracket was supplied and non-empty.
|
|
760
|
+
*/
|
|
761
|
+
type NativeSelector = {
|
|
762
|
+
kind: "id";
|
|
763
|
+
value: string;
|
|
764
|
+
} | {
|
|
765
|
+
kind: "label";
|
|
766
|
+
value: string;
|
|
767
|
+
} | {
|
|
768
|
+
kind: "text";
|
|
769
|
+
value: string;
|
|
770
|
+
} | {
|
|
771
|
+
kind: "role";
|
|
772
|
+
role: string;
|
|
773
|
+
name?: string;
|
|
774
|
+
} | {
|
|
775
|
+
kind: "focused";
|
|
776
|
+
};
|
|
777
|
+
/**
|
|
778
|
+
* Strip one layer of matching single/double quotes from a selector value. Mirrors
|
|
779
|
+
* the historical per-driver `unquote`, kept here so every native target unquotes
|
|
780
|
+
* identically.
|
|
781
|
+
*/
|
|
782
|
+
declare function unquoteSelectorValue(value: string): string;
|
|
783
|
+
/** Wrap a selector value in double quotes (the ranker's canonical emitted form). */
|
|
784
|
+
declare function quoteSelectorValue(value: string): string;
|
|
785
|
+
/**
|
|
786
|
+
* Parse a Prowl selector string into a neutral {@link NativeSelector}. This is
|
|
787
|
+
* the single grammar shared by both mobile drivers (which then map the neutral
|
|
788
|
+
* kind onto their own wire query). Precedence — `:focus`, then `id=`, `role=`,
|
|
789
|
+
* `label=`, `text=`, and finally a bare string treated as `text=` — matches the
|
|
790
|
+
* behavior the per-driver parsers had before consolidation.
|
|
791
|
+
*/
|
|
792
|
+
declare function parseNativeSelector(selector: string): NativeSelector;
|
|
793
|
+
/**
|
|
794
|
+
* The literal a `text=` selector matches, or null when `selector` is not an
|
|
795
|
+
* explicit `text=` form (a bare string is intentionally excluded — it mirrors the
|
|
796
|
+
* web driver's `parseTextSelector`, which `forbiddenSelectors` relies on). Shared
|
|
797
|
+
* so Android and iOS unwrap text selectors identically.
|
|
798
|
+
*/
|
|
799
|
+
declare function unwrapNativeTextSelector(selector: string): string | null;
|
|
800
|
+
/**
|
|
801
|
+
* Qualify a bare Android `resource-id` with the app's package. The raw
|
|
802
|
+
* uiautomator2 server matches resource-ids exactly (bare names return no
|
|
803
|
+
* elements), so `id=save` becomes `<appPackage>:id/save`. Values that already
|
|
804
|
+
* contain a `:` (e.g. `android:id/title`) — or calls without a package — pass
|
|
805
|
+
* through untouched. Both the Android driver's locator translation and the
|
|
806
|
+
* host-side matcher qualify through this one function.
|
|
807
|
+
*/
|
|
808
|
+
declare function qualifyResourceId(value: string, appPackage?: string): string;
|
|
809
|
+
/** Prefix `XCUIElementType` onto an iOS role shorthand (e.g. `Button`) when missing. */
|
|
810
|
+
declare function normalizeXcuiClassName(role: string): string;
|
|
811
|
+
/** Strip the `XCUIElementType` prefix for a friendlier `role=` shorthand. */
|
|
812
|
+
declare function shortIosType(type: string): string;
|
|
813
|
+
/**
|
|
814
|
+
* The per-node ingredients the ranker needs, already projected out of a
|
|
815
|
+
* platform's own node shape:
|
|
816
|
+
* - `id` the native identifier (Android resource-id, iOS accessibility id,
|
|
817
|
+
* macOS AXIdentifier) → emitted as `id=`.
|
|
818
|
+
* - `label` the exact accessibility label (Android content-desc, iOS label,
|
|
819
|
+
* macOS title/description) → emitted as `label="…"`.
|
|
820
|
+
* - `role` the class/type/role string to emit verbatim (already shortened for
|
|
821
|
+
* iOS) → emitted as `role=` and in `role=…[name="…"]`.
|
|
822
|
+
* - `name` the representative visible-text name used for `role=…[name]` and
|
|
823
|
+
* the `text=` fallback.
|
|
824
|
+
*/
|
|
825
|
+
type NativeRankFields = {
|
|
826
|
+
id?: string;
|
|
827
|
+
label?: string;
|
|
828
|
+
role?: string;
|
|
829
|
+
name?: string;
|
|
830
|
+
};
|
|
831
|
+
/**
|
|
832
|
+
* Ranked selector candidates for a node, best → last resort:
|
|
833
|
+
* `id=` > `label="…"` > `role=…[name="…"]` > `text="…"`, with a bare `role=`
|
|
834
|
+
* fallback so any node carrying a role stays addressable. This is the one ranking
|
|
835
|
+
* algorithm every native analyzer emits through, so a change to selector priority
|
|
836
|
+
* happens in exactly one place. Returns an empty array when a node exposes nothing
|
|
837
|
+
* addressable.
|
|
838
|
+
*/
|
|
839
|
+
declare function rankNativeSelectors(fields: NativeRankFields): string[];
|
|
840
|
+
/** The native targets whose selector dialect this module defines. */
|
|
841
|
+
type NativePlatform = "android" | "ios" | "macos";
|
|
842
|
+
/** How a selector kind resolves on one platform (mirrors the matrix above). */
|
|
843
|
+
type SelectorKindMapping = {
|
|
844
|
+
/** The native attribute(s) the kind targets. */
|
|
845
|
+
attribute: string;
|
|
846
|
+
/** The comparison mode against that attribute. */
|
|
847
|
+
match: "exact" | "exact (package-qualified)" | "substring";
|
|
848
|
+
};
|
|
849
|
+
/**
|
|
850
|
+
* The per-platform attribute mapping tables — the machine-readable form of the
|
|
851
|
+
* compatibility matrix, so the mapping is documented once and can be asserted in
|
|
852
|
+
* tests. Keyed by platform, then by the four addressable selector kinds. (`role`
|
|
853
|
+
* describes the bare `role=` form; the `role=…[name]` composite pairs an exact
|
|
854
|
+
* role with a substring name, per the matrix.)
|
|
855
|
+
*/
|
|
856
|
+
declare const NATIVE_ATTRIBUTE_MAP: Readonly<Record<NativePlatform, Readonly<Record<"id" | "label" | "text" | "role", SelectorKindMapping>>>>;
|
|
857
|
+
/**
|
|
858
|
+
* A hierarchy node projected into the neutral attributes the matcher compares
|
|
859
|
+
* against. Platform code (analyzers, and later the runners/macdriver) projects
|
|
860
|
+
* its own node shape into this:
|
|
861
|
+
* - `id` native identifier for exact `id=` matching.
|
|
862
|
+
* - `label` exact accessibility label for exact `label=` matching.
|
|
863
|
+
* - `role` class/type/role in the platform's canonical (full) form; the
|
|
864
|
+
* dialect's {@link NativeMatchDialect.normalizeRole} reconciles a
|
|
865
|
+
* shorthand selector (`Button`) with a full node type.
|
|
866
|
+
* - `textValues` every string a `text=` / `[name]` substring should test
|
|
867
|
+
* (Android: `[text]`; iOS: `[label, value]`; macOS:
|
|
868
|
+
* `[title, description, value]`).
|
|
869
|
+
* - `focused` whether the node currently holds keyboard focus (`:focus`).
|
|
870
|
+
*/
|
|
871
|
+
type NativeNode = {
|
|
872
|
+
id?: string;
|
|
873
|
+
label?: string;
|
|
874
|
+
role?: string;
|
|
875
|
+
textValues: string[];
|
|
876
|
+
focused?: boolean;
|
|
877
|
+
};
|
|
878
|
+
/** Options threaded into matching (currently only Android id package-qualification). */
|
|
879
|
+
type NativeMatchOptions = {
|
|
880
|
+
/** App package used to qualify a bare Android `id=` before an exact compare. */
|
|
881
|
+
appPackage?: string;
|
|
882
|
+
};
|
|
883
|
+
/**
|
|
884
|
+
* The platform-specific part of matching: how to normalize a role/type string for
|
|
885
|
+
* comparison, and how to normalize an `id=` value before an exact id compare.
|
|
886
|
+
* Everything else (exact id/label, substring text, focus) is platform-independent.
|
|
887
|
+
*/
|
|
888
|
+
type NativeMatchDialect = {
|
|
889
|
+
platform: NativePlatform;
|
|
890
|
+
/** Canonicalize a role/type string so a shorthand selector matches a full node type. */
|
|
891
|
+
normalizeRole: (role: string) => string;
|
|
892
|
+
/** Canonicalize an `id=` value (Android package-qualifies bare ids; others identity). */
|
|
893
|
+
normalizeId: (value: string, options: NativeMatchOptions) => string;
|
|
894
|
+
};
|
|
895
|
+
/** Android matching dialect: identity role compare, package-qualified ids. */
|
|
896
|
+
declare const ANDROID_MATCH_DIALECT: NativeMatchDialect;
|
|
897
|
+
/** iOS matching dialect: `XCUIElementType…`-normalized role compare, identity ids. */
|
|
898
|
+
declare const IOS_MATCH_DIALECT: NativeMatchDialect;
|
|
899
|
+
/** macOS matching dialect (exposed for the future macdriver migration). */
|
|
900
|
+
declare const MACOS_MATCH_DIALECT: NativeMatchDialect;
|
|
901
|
+
/**
|
|
902
|
+
* Decide whether a single projected {@link NativeNode} satisfies a parsed
|
|
903
|
+
* {@link NativeSelector}, using the platform's attribute + match-mode semantics:
|
|
904
|
+
* - `id` exact match on `node.id` (Android value package-qualified first).
|
|
905
|
+
* - `label` exact match on `node.label`.
|
|
906
|
+
* - `text` substring match against any of `node.textValues`.
|
|
907
|
+
* - `role` exact (normalized) role match; with `name`, additionally a
|
|
908
|
+
* substring match against any of `node.textValues`.
|
|
909
|
+
* - `focused` node currently holds keyboard focus.
|
|
910
|
+
*/
|
|
911
|
+
declare function nodeMatchesSelector(dialect: NativeMatchDialect, selector: NativeSelector, node: NativeNode, options?: NativeMatchOptions): boolean;
|
|
912
|
+
/**
|
|
913
|
+
* Walk a hierarchy of platform nodes depth-first and collect every node matching
|
|
914
|
+
* `selector`, in document order. Generic over the caller's own node type so the
|
|
915
|
+
* analyzers get their original, fully-typed nodes back: pass a `project` that maps
|
|
916
|
+
* a node to its {@link NativeNode} attributes and a `children` accessor.
|
|
917
|
+
*/
|
|
918
|
+
declare function matchNativeTree<T>(dialect: NativeMatchDialect, selector: NativeSelector, root: T, project: (node: T) => NativeNode, children: (node: T) => readonly T[], options?: NativeMatchOptions): T[];
|
|
919
|
+
/**
|
|
920
|
+
* Parse a raw agent `/source` XML dump into an element tree (best-effort, via the
|
|
921
|
+
* dependency-free {@link parseXml}) so a caller can match against a snapshot
|
|
922
|
+
* without a device. Returns null when the payload holds no element.
|
|
923
|
+
*/
|
|
924
|
+
declare function parseSnapshot(xml: string): XmlElement | null;
|
|
925
|
+
|
|
594
926
|
/** A structured query the {@link AndroidAgentClient} resolves against the device. */
|
|
595
927
|
type AndroidQuery = {
|
|
596
928
|
by: "id";
|
|
@@ -638,6 +970,12 @@ interface AndroidAgentClient {
|
|
|
638
970
|
pressKeyCode(keyCode: number): Promise<void>;
|
|
639
971
|
/** Capture the current screen as PNG bytes. */
|
|
640
972
|
screenshotPng(): Promise<Buffer>;
|
|
973
|
+
/**
|
|
974
|
+
* Return the current UI hierarchy as uiautomator2 `/source` XML. Present on
|
|
975
|
+
* live clients and consumed by the analyzer (PROWL-061); optional so lighter
|
|
976
|
+
* fakes that only drive/query need not implement it.
|
|
977
|
+
*/
|
|
978
|
+
source?(): Promise<string>;
|
|
641
979
|
close(): Promise<void>;
|
|
642
980
|
}
|
|
643
981
|
type AndroidDriverOptions = {
|
|
@@ -651,7 +989,12 @@ type AndroidDriverOptions = {
|
|
|
651
989
|
declare const ANDROID_KEYCODES: Readonly<Record<string, number>>;
|
|
652
990
|
/** Escape a string for embedding inside a `new UiSelector()...("...")` argument. */
|
|
653
991
|
declare function escapeUiSelectorArg(value: string): string;
|
|
654
|
-
/**
|
|
992
|
+
/**
|
|
993
|
+
* Parse a Prowl selector string into an {@link AndroidQuery}. Bare text matches by
|
|
994
|
+
* text. The grammar (and its `label=`-in-assertions trap) is defined once in the
|
|
995
|
+
* shared native selector engine ({@link parseNativeSelector}, PROWL-060); this
|
|
996
|
+
* only maps the neutral result onto Android's on-device query.
|
|
997
|
+
*/
|
|
655
998
|
declare function parseAndroidSelector(selector: string): AndroidQuery;
|
|
656
999
|
/**
|
|
657
1000
|
* Translate an {@link AndroidQuery} into the uiautomator2 server's native
|
|
@@ -832,33 +1175,6 @@ declare function createUia2AgentClient(transport: Uia2Transport, sessionId: stri
|
|
|
832
1175
|
appPackage?: string;
|
|
833
1176
|
}): AndroidAgentClient;
|
|
834
1177
|
|
|
835
|
-
/**
|
|
836
|
-
* PROWL-059 / ARCH-010 — iOS simulator implementation of {@link SessionDriver}.
|
|
837
|
-
*
|
|
838
|
-
* `IosDriver` drives a native iOS app through a prebuilt WebDriverAgent (WDA)
|
|
839
|
-
* runner over its W3C-shaped HTTP/JSON API ({@link IosAgentClient}). Like the
|
|
840
|
-
* macOS and Android drivers it implements only the portable subset of the driver
|
|
841
|
-
* surface — its `capabilities` set is honest: `query`, `interact`, `wait`,
|
|
842
|
-
* `screenshot`. Web-only verbs (navigation, network, dialogs, files, downloads,
|
|
843
|
-
* script evaluation) are unsupported stubs; the runner never reaches them because
|
|
844
|
-
* both the target step-compatibility check and the runtime capability gate reject
|
|
845
|
-
* web-only steps for native targets.
|
|
846
|
-
*
|
|
847
|
-
* Screenshots are captured via `simctl` (injected as `captureScreenshot`), not
|
|
848
|
-
* WDA, so artifacts still work even if the agent wedges.
|
|
849
|
-
*
|
|
850
|
-
* Selector dialect (parsed here into an {@link IosQuery}; the agent then matches
|
|
851
|
-
* on the simulator). Semantics mirror the macOS/Android drivers so `id=`/`label=`/
|
|
852
|
-
* `text=`/`role=` mean the same thing across native targets (PROWL-060 will unify
|
|
853
|
-
* the engines later):
|
|
854
|
-
* id=save → accessibility id (accessibilityIdentifier / name)
|
|
855
|
-
* label="Submit" → predicate `label == "Submit"` (exact)
|
|
856
|
-
* role=XCUIElementTypeButton → element class (shorthand `Button` is accepted too)
|
|
857
|
-
* role=Button[name="Save"] → class + visible text (label/value substring)
|
|
858
|
-
* text="Save" | Save → label/value substring
|
|
859
|
-
* :focus → predicate `hasKeyboardFocus == 1`
|
|
860
|
-
*/
|
|
861
|
-
|
|
862
1178
|
/** A structured query the {@link IosAgentClient} resolves against the simulator. */
|
|
863
1179
|
type IosQuery = {
|
|
864
1180
|
by: "accessibilityId";
|
|
@@ -899,6 +1215,12 @@ interface IosAgentClient {
|
|
|
899
1215
|
sendKeys(keys: string[]): Promise<void>;
|
|
900
1216
|
/** Return to the springboard home screen (WDA `/wda/homescreen`). */
|
|
901
1217
|
homescreen(): Promise<void>;
|
|
1218
|
+
/**
|
|
1219
|
+
* Return the current UI hierarchy as WebDriverAgent `/source` XML. Present on
|
|
1220
|
+
* live clients and consumed by the analyzer (PROWL-061); optional so lighter
|
|
1221
|
+
* fakes that only drive/query need not implement it.
|
|
1222
|
+
*/
|
|
1223
|
+
source?(): Promise<string>;
|
|
902
1224
|
close(): Promise<void>;
|
|
903
1225
|
}
|
|
904
1226
|
type IosDriverOptions = {
|
|
@@ -915,9 +1237,12 @@ type IosDriverOptions = {
|
|
|
915
1237
|
declare const IOS_PRESS_KEYS: readonly string[];
|
|
916
1238
|
/** Escape a string for embedding inside a double-quoted NSPredicate string literal. */
|
|
917
1239
|
declare function escapePredicateArg(value: string): string;
|
|
918
|
-
/**
|
|
919
|
-
|
|
920
|
-
|
|
1240
|
+
/**
|
|
1241
|
+
* Parse a Prowl selector string into an {@link IosQuery}. Bare text matches by
|
|
1242
|
+
* text. The grammar (and its `label=`-in-assertions trap) is defined once in the
|
|
1243
|
+
* shared native selector engine ({@link parseNativeSelector}, PROWL-060); this
|
|
1244
|
+
* only maps the neutral result onto iOS's WDA query.
|
|
1245
|
+
*/
|
|
921
1246
|
declare function parseIosSelector(selector: string): IosQuery;
|
|
922
1247
|
/**
|
|
923
1248
|
* Translate an {@link IosQuery} into a WDA locator strategy. `id` uses the native
|
|
@@ -941,6 +1266,19 @@ type SimctlRunner = (args: string[], options?: {
|
|
|
941
1266
|
timeoutMs?: number;
|
|
942
1267
|
env?: NodeJS.ProcessEnv;
|
|
943
1268
|
}) => Promise<SimctlResult>;
|
|
1269
|
+
/** A handle to a spawned long-running `xcrun` process (the WDA xcodebuild test host). */
|
|
1270
|
+
type XcrunProcessHandle = {
|
|
1271
|
+
kill(): void;
|
|
1272
|
+
};
|
|
1273
|
+
/**
|
|
1274
|
+
* Spawns a long-running `xcrun` command in the background — used for the
|
|
1275
|
+
* `xcodebuild test-without-building` run that hosts WebDriverAgent (which never
|
|
1276
|
+
* exits on its own; it serves HTTP until killed). Mirrors the Android
|
|
1277
|
+
* `AdbSpawner` so the launch flow stays unit-testable with a fake.
|
|
1278
|
+
*/
|
|
1279
|
+
type XcrunSpawner = (args: string[], options?: {
|
|
1280
|
+
env?: NodeJS.ProcessEnv;
|
|
1281
|
+
}) => XcrunProcessHandle;
|
|
944
1282
|
/** One simulator device from `simctl list devices --json`. */
|
|
945
1283
|
type SimDevice = {
|
|
946
1284
|
udid: string;
|
|
@@ -990,8 +1328,8 @@ declare function parseXcodeVersion(stdout: string): string | null;
|
|
|
990
1328
|
|
|
991
1329
|
/** Bundle id of the prebuilt WebDriverAgent runner (its xctest host app). */
|
|
992
1330
|
declare const WDA_RUNNER_BUNDLE_ID = "com.facebook.WebDriverAgentRunner.xctrunner";
|
|
993
|
-
/**
|
|
994
|
-
declare const
|
|
1331
|
+
/** Env var WDA reads to pick its HTTP port (injected into the runner's xctestrun env). */
|
|
1332
|
+
declare const WDA_USE_PORT_ENV = "USE_PORT";
|
|
995
1333
|
/** Establishes a live {@link IosAgentClient} against a running WDA HTTP server. */
|
|
996
1334
|
type IosAgentConnector = (options: {
|
|
997
1335
|
host: string;
|
|
@@ -1009,7 +1347,7 @@ declare function resolveWdaProject(requireFn?: NodeRequire): {
|
|
|
1009
1347
|
};
|
|
1010
1348
|
/** Cache directory for a built WDA runner, keyed on the WDA + Xcode versions. */
|
|
1011
1349
|
declare function wdaCacheDir(wdaVersion: string, xcode: string, homeDir?: string): string;
|
|
1012
|
-
type
|
|
1350
|
+
type ResolveWdaTestRunOptions = {
|
|
1013
1351
|
runner?: SimctlRunner;
|
|
1014
1352
|
env?: NodeJS.ProcessEnv;
|
|
1015
1353
|
homeDir?: string;
|
|
@@ -1018,13 +1356,50 @@ type ResolveWdaRunnerOptions = {
|
|
|
1018
1356
|
logger?: (message: string) => void;
|
|
1019
1357
|
};
|
|
1020
1358
|
/**
|
|
1021
|
-
* Resolve a WebDriverAgent
|
|
1022
|
-
*
|
|
1023
|
-
*
|
|
1024
|
-
*
|
|
1025
|
-
* build fails.
|
|
1359
|
+
* Resolve a WebDriverAgent `.xctestrun` file (the input to the XCTest host
|
|
1360
|
+
* launch). Order: (a) the `PROWL_WDA_RUNNER` env override; (b) a previously built
|
|
1361
|
+
* xctestrun in the version-keyed cache; (c) a one-time
|
|
1362
|
+
* `xcodebuild build-for-testing` that populates the cache. Simulators need no
|
|
1363
|
+
* code signing. Throws actionable errors when Xcode is missing or the build fails.
|
|
1364
|
+
*/
|
|
1365
|
+
declare function resolveWdaTestRun(options?: ResolveWdaTestRunOptions): Promise<string>;
|
|
1366
|
+
/**
|
|
1367
|
+
* Return a deep copy of a parsed `.xctestrun` plist with the WDA HTTP port
|
|
1368
|
+
* (`USE_PORT`) injected into every test target's `EnvironmentVariables`. WDA
|
|
1369
|
+
* reads `USE_PORT` from its process environment; under `xcodebuild test` that
|
|
1370
|
+
* environment comes from the xctestrun, so the dynamic port must be written here.
|
|
1371
|
+
*
|
|
1372
|
+
* Handles both xctestrun layouts: format 1 (top-level dict keyed by test-target
|
|
1373
|
+
* name) and format 2 (a `TestConfigurations[].TestTargets[]` tree). A test target
|
|
1374
|
+
* is recognized by a `TestBundlePath`/`TestHostPath` string; any object that
|
|
1375
|
+
* already carries an `EnvironmentVariables` dict is updated too. Throws when no
|
|
1376
|
+
* target is found, so a future format change fails loudly rather than launching
|
|
1377
|
+
* WDA on the wrong port.
|
|
1378
|
+
*/
|
|
1379
|
+
declare function injectUsePortIntoXctestrun(plist: unknown, port: number): unknown;
|
|
1380
|
+
/**
|
|
1381
|
+
* Produce a launch-specific `.xctestrun` with `USE_PORT` set to `port`, returning
|
|
1382
|
+
* its path. The base xctestrun (built/cached) is never mutated in place.
|
|
1026
1383
|
*/
|
|
1027
|
-
|
|
1384
|
+
type WdaTestRunPreparer = (options: {
|
|
1385
|
+
xctestrunPath: string;
|
|
1386
|
+
port: number;
|
|
1387
|
+
}) => Promise<string>;
|
|
1388
|
+
/**
|
|
1389
|
+
* Default preparer: reads the base xctestrun with `plutil` (JSON), injects
|
|
1390
|
+
* `USE_PORT`, and writes a fresh, uniquely-named xctestrun **next to the base
|
|
1391
|
+
* one**. This placement is required, not incidental: an xctestrun's product
|
|
1392
|
+
* paths are relative to `__TESTROOT__`, which xcodebuild resolves to the
|
|
1393
|
+
* directory containing the xctestrun file — so a copy written to a temp dir
|
|
1394
|
+
* makes `xcodebuild test-without-building` fail with "Missing test product".
|
|
1395
|
+
* Writing the sibling into the build's Products dir keeps `__TESTROOT__` pointing
|
|
1396
|
+
* at the real products. The intermediate JSON goes to a temp dir; only the
|
|
1397
|
+
* `.xctestrun` lands beside the products and is removed on teardown. `plutil`
|
|
1398
|
+
* ships with macOS, so no extra dependency is added.
|
|
1399
|
+
*/
|
|
1400
|
+
declare const defaultWdaTestRunPreparer: WdaTestRunPreparer;
|
|
1401
|
+
/** The `xcodebuild test-without-building` args that host WDA against `udid`. */
|
|
1402
|
+
declare function wdaTestRunArgs(xctestrunPath: string, udid: string): string[];
|
|
1028
1403
|
type IosSession = {
|
|
1029
1404
|
client: IosAgentClient;
|
|
1030
1405
|
driver: SessionDriver;
|
|
@@ -1044,10 +1419,14 @@ type LaunchIosOptions = {
|
|
|
1044
1419
|
coldStart?: boolean;
|
|
1045
1420
|
timeoutMs?: number;
|
|
1046
1421
|
runner?: SimctlRunner;
|
|
1422
|
+
/** Spawner for the long-running `xcodebuild test-without-building` WDA host. */
|
|
1423
|
+
spawner?: XcrunSpawner;
|
|
1424
|
+
/** Builds the per-launch port-injected xctestrun. */
|
|
1425
|
+
testRunPreparer?: WdaTestRunPreparer;
|
|
1047
1426
|
portAllocator?: () => Promise<number>;
|
|
1048
1427
|
agentConnector?: IosAgentConnector;
|
|
1049
|
-
/** Skip WDA build/resolution by supplying the
|
|
1050
|
-
|
|
1428
|
+
/** Skip WDA build/resolution by supplying the base `.xctestrun` path directly. */
|
|
1429
|
+
wdaTestRun?: string;
|
|
1051
1430
|
/** Optional app scope guardrail from config.guardrails.allowedApps. */
|
|
1052
1431
|
allowedApps?: string[];
|
|
1053
1432
|
/** Override the simulator lock root; intended for tests. */
|
|
@@ -1055,9 +1434,10 @@ type LaunchIosOptions = {
|
|
|
1055
1434
|
logger?: (message: string) => void;
|
|
1056
1435
|
};
|
|
1057
1436
|
/**
|
|
1058
|
-
* Preflight,
|
|
1059
|
-
*
|
|
1060
|
-
*
|
|
1437
|
+
* Preflight, host WDA via `xcodebuild test-without-building`, launch the target
|
|
1438
|
+
* app, and attach, returning a live {@link IosSession}. Actionable errors cover
|
|
1439
|
+
* each failure mode: Xcode/simctl missing, no booted simulator (device
|
|
1440
|
+
* selection), WDA build failures, agent unreachable (readiness).
|
|
1061
1441
|
*/
|
|
1062
1442
|
declare function launchIosSession(options: LaunchIosOptions): Promise<IosSession>;
|
|
1063
1443
|
/** Tear down an iOS session (agent session, WDA runner, target app). */
|
|
@@ -1183,6 +1563,20 @@ declare function androidAppAllowedIdentities(app: string, resolvedPackage?: stri
|
|
|
1183
1563
|
*/
|
|
1184
1564
|
declare function assertAndroidAppAllowed(allowedApps: string[], app: string, resolvedPackage?: string): void;
|
|
1185
1565
|
|
|
1566
|
+
type AiProvider = "anthropic" | "openai";
|
|
1567
|
+
type AiConfig = {
|
|
1568
|
+
provider: AiProvider;
|
|
1569
|
+
model: string;
|
|
1570
|
+
apiKey: string;
|
|
1571
|
+
/**
|
|
1572
|
+
* API root the request is sent to (no trailing slash, no path). When omitted,
|
|
1573
|
+
* falls back to the provider's public API. Set via `PROWL_AI_BASE_URL` so a
|
|
1574
|
+
* self-hosted gateway or (later) a managed Prowl proxy can slot in without
|
|
1575
|
+
* code changes.
|
|
1576
|
+
*/
|
|
1577
|
+
baseUrl?: string;
|
|
1578
|
+
};
|
|
1579
|
+
|
|
1186
1580
|
type StepCallback = (result: StepResult, step: Step, index: number) => void;
|
|
1187
1581
|
|
|
1188
1582
|
type RunOptions = {
|
|
@@ -1950,12 +2344,262 @@ type AnalyzeMacOptions = {
|
|
|
1950
2344
|
*/
|
|
1951
2345
|
declare function analyzeMacApp(client: MacHelperClient, options: AnalyzeMacOptions): Promise<MacAnalysisResult>;
|
|
1952
2346
|
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
2347
|
+
/**
|
|
2348
|
+
* PROWL-061 — a tiny, dependency-free XML parser for on-device UI hierarchies.
|
|
2349
|
+
*
|
|
2350
|
+
* Both native mobile analyzers read an XML page source: Android via the
|
|
2351
|
+
* uiautomator2 agent's `GET /source` (a `<hierarchy>` of `<node>` elements) and
|
|
2352
|
+
* iOS via WebDriverAgent's `GET /source` (a tree of `<XCUIElementType…>`
|
|
2353
|
+
* elements). Both dialects share the same shape — a tree of elements whose data
|
|
2354
|
+
* lives entirely in double-quoted attributes, with no meaningful text between
|
|
2355
|
+
* tags — so one small scanner serves both, keeping with the repo's "no heavy
|
|
2356
|
+
* SDK" ethos (there is no XML parser in our own dependency set, only transitive
|
|
2357
|
+
* ones we must not rely on).
|
|
2358
|
+
*
|
|
2359
|
+
* The scanner is deliberately narrow: it understands element start/end/self-close
|
|
2360
|
+
* tags, quoted attributes (single or double), XML declarations, comments, and the
|
|
2361
|
+
* five predefined entities plus numeric character references. It ignores text
|
|
2362
|
+
* nodes and CDATA (the UI dumps carry none). It never throws on malformed input —
|
|
2363
|
+
* it returns the best-effort root element, or null when there is no element at
|
|
2364
|
+
* all — so a surprising payload degrades to an empty analysis rather than a crash.
|
|
2365
|
+
*/
|
|
2366
|
+
/** A parsed XML element: its tag name, attributes, and child elements. */
|
|
2367
|
+
type XmlElement = {
|
|
2368
|
+
tag: string;
|
|
2369
|
+
attrs: Record<string, string>;
|
|
2370
|
+
children: XmlElement[];
|
|
2371
|
+
};
|
|
2372
|
+
/** Decode the five predefined XML entities and numeric character references. */
|
|
2373
|
+
declare function decodeXmlEntities(value: string): string;
|
|
2374
|
+
/**
|
|
2375
|
+
* Parse an XML document into its root {@link XmlElement} (best-effort). Returns
|
|
2376
|
+
* null when the input contains no element. Malformed markup is tolerated: unknown
|
|
2377
|
+
* constructs are skipped and mismatched end tags simply pop the stack.
|
|
2378
|
+
*/
|
|
2379
|
+
declare function parseXml(input: string): XmlElement | null;
|
|
2380
|
+
|
|
2381
|
+
/**
|
|
2382
|
+
* PROWL-061 — Android analyzer.
|
|
2383
|
+
*
|
|
2384
|
+
* The Android analog of {@link analyzeMacApp}: dumps a running Android app's
|
|
2385
|
+
* interactive elements with ranked selector candidates so hunt authors don't have
|
|
2386
|
+
* to guess resource-ids. It reads the on-device UI hierarchy through the same
|
|
2387
|
+
* uiautomator2 agent the runner uses (`GET /source`, the standard `uiautomator
|
|
2388
|
+
* dump` XML) and shapes the result to mirror the macOS analyzer's feel.
|
|
2389
|
+
*
|
|
2390
|
+
* Selector ranking (best → last resort), matching the Android driver's selector
|
|
2391
|
+
* dialect so the emitted selectors are directly usable in hunts:
|
|
2392
|
+
* id=<resource-id> (best — the native `data-testid`; already
|
|
2393
|
+
* package-qualified in the dump, e.g.
|
|
2394
|
+
* `com.android.settings:id/title`)
|
|
2395
|
+
* label="<content-desc>" (exact content-description)
|
|
2396
|
+
* role=<class>[name="<text>"] (widget class + visible text, substring)
|
|
2397
|
+
* text="<text>" (last resort — visible-text substring)
|
|
2398
|
+
*
|
|
2399
|
+
* Read-only: this never taps, types, or otherwise mutates the app — it only reads
|
|
2400
|
+
* the page source.
|
|
2401
|
+
*/
|
|
2402
|
+
|
|
2403
|
+
/**
|
|
2404
|
+
* Widget classes treated as interactive on their own (in addition to any node
|
|
2405
|
+
* flagged clickable / long-clickable / checkable / scrollable). Tuned to the
|
|
2406
|
+
* common `android.widget` / AndroidX input and control classes; text/containers
|
|
2407
|
+
* without an interactive flag are surfaced only when clickable.
|
|
2408
|
+
*/
|
|
2409
|
+
declare const ANDROID_INTERACTIVE_CLASSES: ReadonlySet<string>;
|
|
2410
|
+
/** A single node in the uiautomator hierarchy (`<node>` element attributes). */
|
|
2411
|
+
type AndroidUiNode = {
|
|
2412
|
+
className?: string;
|
|
2413
|
+
resourceId?: string;
|
|
2414
|
+
contentDesc?: string;
|
|
2415
|
+
text?: string;
|
|
2416
|
+
package?: string;
|
|
2417
|
+
clickable?: boolean;
|
|
2418
|
+
longClickable?: boolean;
|
|
2419
|
+
checkable?: boolean;
|
|
2420
|
+
checked?: boolean;
|
|
2421
|
+
scrollable?: boolean;
|
|
2422
|
+
focusable?: boolean;
|
|
2423
|
+
/** Whether the node currently holds input focus (`:focus`; from the dump's `focused` attr). */
|
|
2424
|
+
focused?: boolean;
|
|
2425
|
+
enabled?: boolean;
|
|
2426
|
+
children: AndroidUiNode[];
|
|
2427
|
+
};
|
|
2428
|
+
/** An interactive element with ranked selector candidates (best first). */
|
|
2429
|
+
type AndroidAnalysisElement = {
|
|
2430
|
+
className: string;
|
|
2431
|
+
resourceId?: string;
|
|
2432
|
+
contentDesc?: string;
|
|
2433
|
+
text?: string;
|
|
2434
|
+
clickable?: boolean;
|
|
2435
|
+
checkable?: boolean;
|
|
2436
|
+
scrollable?: boolean;
|
|
2437
|
+
enabled?: boolean;
|
|
2438
|
+
/** Ranked selector candidates, best first. Always at least one entry. */
|
|
2439
|
+
selectors: string[];
|
|
1958
2440
|
};
|
|
2441
|
+
type AndroidAnalysisResult = {
|
|
2442
|
+
/** Package name of the analyzed app. */
|
|
2443
|
+
app: string;
|
|
2444
|
+
elements: AndroidAnalysisElement[];
|
|
2445
|
+
};
|
|
2446
|
+
/** Parse a uiautomator2 `/source` XML dump into a tree of {@link AndroidUiNode}. */
|
|
2447
|
+
declare function parseAndroidHierarchy(xml: string): AndroidUiNode | null;
|
|
2448
|
+
/** Whether a node is worth surfacing as an interactive element. */
|
|
2449
|
+
declare function isAndroidInteractive(node: AndroidUiNode): boolean;
|
|
2450
|
+
/**
|
|
2451
|
+
* Ranked selector candidates for a node, best first, via the shared native
|
|
2452
|
+
* selector engine (PROWL-060). The Android attribute mapping is applied here —
|
|
2453
|
+
* `id=`←resource-id (already package-qualified in the dump, emitted verbatim),
|
|
2454
|
+
* `label=`←content-desc, `role=`←class, name←visible text — then
|
|
2455
|
+
* {@link rankNativeSelectors} imposes the shared `id=` > `label=` >
|
|
2456
|
+
* `role=…[name]` > `text=` order (with a bare `role=` fallback).
|
|
2457
|
+
*/
|
|
2458
|
+
declare function rankAndroidSelectors(node: AndroidUiNode): string[];
|
|
2459
|
+
/**
|
|
2460
|
+
* Project an {@link AndroidUiNode} into the neutral {@link NativeNode} the shared
|
|
2461
|
+
* matcher compares against: `id`←resource-id, `label`←content-desc, `role`←class,
|
|
2462
|
+
* `text=` substring source ← visible text.
|
|
2463
|
+
*/
|
|
2464
|
+
declare function androidNodeToNative(node: AndroidUiNode): NativeNode;
|
|
2465
|
+
/**
|
|
2466
|
+
* Host-side "snapshot-then-match": parse a uiautomator2 `/source` dump and return
|
|
2467
|
+
* every node the Prowl `selector` resolves to, in document order, using Android's
|
|
2468
|
+
* shared dialect semantics. Read-only and device-free. Exposed for tooling and a
|
|
2469
|
+
* future runner/macdriver migration; the runner still matches on-device today.
|
|
2470
|
+
*/
|
|
2471
|
+
declare function matchAndroidSelector(xml: string, selector: string, options?: NativeMatchOptions): AndroidUiNode[];
|
|
2472
|
+
/** The minimal transport the Android analyzer needs: read the UI hierarchy XML. */
|
|
2473
|
+
interface AndroidUiSource {
|
|
2474
|
+
/** Return the current UI hierarchy as uiautomator2 `/source` XML. */
|
|
2475
|
+
source(): Promise<string>;
|
|
2476
|
+
}
|
|
2477
|
+
type AnalyzeAndroidOptions = {
|
|
2478
|
+
/** Package name to report in the result. */
|
|
2479
|
+
app: string;
|
|
2480
|
+
};
|
|
2481
|
+
/**
|
|
2482
|
+
* Analyze an already-launched Android app through `client`, returning its
|
|
2483
|
+
* interactive elements with ranked selectors.
|
|
2484
|
+
*
|
|
2485
|
+
* The caller owns the session lifecycle (launch + guardrails + teardown); this
|
|
2486
|
+
* function is strictly read-only — it only reads the page source.
|
|
2487
|
+
*/
|
|
2488
|
+
declare function analyzeAndroidApp(client: AndroidUiSource, options: AnalyzeAndroidOptions): Promise<AndroidAnalysisResult>;
|
|
2489
|
+
|
|
2490
|
+
/**
|
|
2491
|
+
* PROWL-061 — iOS analyzer.
|
|
2492
|
+
*
|
|
2493
|
+
* The iOS analog of {@link analyzeMacApp}: dumps a running iOS app's interactive
|
|
2494
|
+
* elements (and its windows) with ranked selector candidates so hunt authors
|
|
2495
|
+
* don't have to guess accessibility identifiers. It reads the on-simulator UI
|
|
2496
|
+
* hierarchy through the same WebDriverAgent the runner uses (`GET /source`, WDA's
|
|
2497
|
+
* XML page source of `<XCUIElementType…>` elements) and shapes the result to
|
|
2498
|
+
* mirror the macOS analyzer's feel.
|
|
2499
|
+
*
|
|
2500
|
+
* Selector ranking (best → last resort), matching the iOS driver's selector
|
|
2501
|
+
* dialect so the emitted selectors are directly usable in hunts:
|
|
2502
|
+
* id=<accessibility id> (best — the native `data-testid`)
|
|
2503
|
+
* label="<label>" (exact accessibility label)
|
|
2504
|
+
* role=<Type>[name="<text>"] (element type + visible text, substring)
|
|
2505
|
+
* text="<text>" (last resort — label/value substring)
|
|
2506
|
+
*
|
|
2507
|
+
* Identifier caveat: WDA's page source exposes a single `name` attribute that is
|
|
2508
|
+
* the element's `accessibilityIdentifier` when one is set, otherwise its label.
|
|
2509
|
+
* We therefore rank `id=` only when `name` differs from `label`, so the analyzer
|
|
2510
|
+
* does not recommend label-shaped ids. Host-side matching still follows WDA's
|
|
2511
|
+
* `accessibility id` strategy and resolves `id=` against `name`.
|
|
2512
|
+
*
|
|
2513
|
+
* Read-only: this never taps, types, or otherwise mutates the app — it only reads
|
|
2514
|
+
* the page source.
|
|
2515
|
+
*/
|
|
2516
|
+
|
|
2517
|
+
/**
|
|
2518
|
+
* Element types treated as interactive. Tuned to the common `XCUIElementType…`
|
|
2519
|
+
* controls; static text and layout containers are intentionally excluded.
|
|
2520
|
+
*/
|
|
2521
|
+
declare const IOS_INTERACTIVE_TYPES: ReadonlySet<string>;
|
|
2522
|
+
/** The element type that represents a navigable window/screen surface. */
|
|
2523
|
+
declare const IOS_WINDOW_TYPE = "XCUIElementTypeWindow";
|
|
2524
|
+
/** A single node in the WDA hierarchy (`<XCUIElementType…>` element attributes). */
|
|
2525
|
+
type IosUiNode = {
|
|
2526
|
+
type?: string;
|
|
2527
|
+
name?: string;
|
|
2528
|
+
label?: string;
|
|
2529
|
+
value?: string;
|
|
2530
|
+
enabled?: boolean;
|
|
2531
|
+
visible?: boolean;
|
|
2532
|
+
children: IosUiNode[];
|
|
2533
|
+
};
|
|
2534
|
+
/** An interactive element with ranked selector candidates (best first). */
|
|
2535
|
+
type IosAnalysisElement = {
|
|
2536
|
+
type: string;
|
|
2537
|
+
name?: string;
|
|
2538
|
+
label?: string;
|
|
2539
|
+
value?: string;
|
|
2540
|
+
enabled?: boolean;
|
|
2541
|
+
visible?: boolean;
|
|
2542
|
+
/** Ranked selector candidates, best first. Always at least one entry. */
|
|
2543
|
+
selectors: string[];
|
|
2544
|
+
};
|
|
2545
|
+
/** A top-level window, exposed as a navigable surface with its best selector. */
|
|
2546
|
+
type IosAnalysisWindow = {
|
|
2547
|
+
name?: string;
|
|
2548
|
+
label?: string;
|
|
2549
|
+
/** Best selector candidate for the window. */
|
|
2550
|
+
selector: string;
|
|
2551
|
+
};
|
|
2552
|
+
type IosAnalysisResult = {
|
|
2553
|
+
/** Bundle id of the analyzed app. */
|
|
2554
|
+
app: string;
|
|
2555
|
+
elements: IosAnalysisElement[];
|
|
2556
|
+
windows: IosAnalysisWindow[];
|
|
2557
|
+
};
|
|
2558
|
+
/** Parse a WDA `/source` XML dump into a tree of {@link IosUiNode}. */
|
|
2559
|
+
declare function parseIosHierarchy(xml: string): IosUiNode | null;
|
|
2560
|
+
/**
|
|
2561
|
+
* Ranked selector candidates for a node, best first, via the shared native
|
|
2562
|
+
* selector engine (PROWL-060). The iOS attribute mapping is applied here — `id=`←
|
|
2563
|
+
* accessibility id (the `name` attribute, but only when it differs from the label;
|
|
2564
|
+
* see the caveat above), `label=`←label, `role=`←the short element type, name←
|
|
2565
|
+
* `label ?? value` — then {@link rankNativeSelectors} imposes the shared `id=` >
|
|
2566
|
+
* `label=` > `role=…[name]` > `text=` order (with a bare `role=` fallback).
|
|
2567
|
+
*/
|
|
2568
|
+
declare function rankIosSelectors(node: IosUiNode): string[];
|
|
2569
|
+
/**
|
|
2570
|
+
* Project an {@link IosUiNode} into the neutral {@link NativeNode} the shared
|
|
2571
|
+
* matcher compares against: `id`←name (matching WDA's `accessibility id`
|
|
2572
|
+
* strategy even when it equals the label), `label`←label, `role`←the full
|
|
2573
|
+
* element type (the dialect normalizes a `Button` shorthand against it), and both
|
|
2574
|
+
* label and value as `text=` substring sources.
|
|
2575
|
+
*/
|
|
2576
|
+
declare function iosNodeToNative(node: IosUiNode): NativeNode;
|
|
2577
|
+
/**
|
|
2578
|
+
* Host-side "snapshot-then-match": parse a WebDriverAgent `/source` dump and
|
|
2579
|
+
* return every node the Prowl `selector` resolves to, in document order, using
|
|
2580
|
+
* iOS's shared dialect semantics. Read-only and device-free. Exposed for tooling
|
|
2581
|
+
* and a future runner/macdriver migration; the runner still matches on-device.
|
|
2582
|
+
*/
|
|
2583
|
+
declare function matchIosSelector(xml: string, selector: string): IosUiNode[];
|
|
2584
|
+
/** Whether a node is worth surfacing as an interactive element. */
|
|
2585
|
+
declare function isIosInteractive(node: IosUiNode): boolean;
|
|
2586
|
+
/** The minimal transport the iOS analyzer needs: read the UI hierarchy XML. */
|
|
2587
|
+
interface IosUiSource {
|
|
2588
|
+
/** Return the current UI hierarchy as WebDriverAgent `/source` XML. */
|
|
2589
|
+
source(): Promise<string>;
|
|
2590
|
+
}
|
|
2591
|
+
type AnalyzeIosOptions = {
|
|
2592
|
+
/** Bundle id to report in the result. */
|
|
2593
|
+
app: string;
|
|
2594
|
+
};
|
|
2595
|
+
/**
|
|
2596
|
+
* Analyze an already-launched iOS app through `client`, returning its interactive
|
|
2597
|
+
* elements and windows with ranked selectors.
|
|
2598
|
+
*
|
|
2599
|
+
* The caller owns the session lifecycle (launch + guardrails + teardown); this
|
|
2600
|
+
* function is strictly read-only — it only reads the page source.
|
|
2601
|
+
*/
|
|
2602
|
+
declare function analyzeIosApp(client: IosUiSource, options: AnalyzeIosOptions): Promise<IosAnalysisResult>;
|
|
1959
2603
|
|
|
1960
2604
|
type GenerateOptions = {
|
|
1961
2605
|
url?: string;
|
|
@@ -1967,4 +2611,4 @@ type GenerateOptions = {
|
|
|
1967
2611
|
};
|
|
1968
2612
|
declare function generateHunt(options: GenerateOptions): Promise<string>;
|
|
1969
2613
|
|
|
1970
|
-
export { ANDROID_KEYCODES, type AdbDevice, type AdbResult, type AdbRunner, type AdbSpawner, type AgentApks, type AgentConnector, type AnalysisResult, type AnalyzeMacOptions, type AndroidAgentClient, type AndroidDriverOptions, type AndroidLocator, type AndroidQuery, type AndroidSession, type AndroidTarget, type AssertScreenshotStep, type Assertion, type AssertionResult, type BrowserChannel, type BrowserEngine, type BugFailure, type BugLogSummary, type CiFailureCluster, type CiFlakyHunt, type CiHuntResult, type CiResult, type CiStatus, type Config, DEFAULT_AGENT_REQUEST_TIMEOUT_MS, DEFAULT_ANALYZE_TREE_DEPTH, DEFAULT_FLAKY_THRESHOLD, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_SIMULATOR_LOCK_ROOT, DEFAULT_WDA_REQUEST_TIMEOUT_MS, type EvalScriptStep, type FailureCluster, type FetchLike$1 as FetchLike, type FlakyScore, type GenerateOptions, type HealResult, type HistoryEntry, type HistoryFile, type Hunt, INTERACTIVE_ROLES, IOS_PRESS_KEYS, type IfStep, type IosAgentClient, type IosAgentConnector, type IosDriverOptions, type IosLocator, type IosQuery, type IosSession, type IosTarget, type LaunchAndroidOptions, type LaunchIosOptions, type LaunchMacOptions, type MacAnalysisElement, type MacAnalysisResult, type MacAnalysisWindow, type MacAxNode, type MacDriverOptions, type MacHelperClient, type MacQuery, type MacSession, type MacosTarget, type MockRouteStep, type PageElement, type PageForm, type PageLink, type RankFlakyOptions, type ReliabilityConfig, type RepeatStep, type ReserveSimulatorOptions, type
|
|
2614
|
+
export { ANDROID_INTERACTIVE_CLASSES, ANDROID_KEYCODES, ANDROID_MATCH_DIALECT, type AdbDevice, type AdbResult, type AdbRunner, type AdbSpawner, type AgentApks, type AgentConnector, type AnalysisResult, type AnalyzeAndroidOptions, type AnalyzeIosOptions, type AnalyzeMacOptions, type AndroidAgentClient, type AndroidAnalysisElement, type AndroidAnalysisResult, type AndroidDriverOptions, type AndroidLocator, type AndroidQuery, type AndroidSession, type AndroidTarget, type AndroidUiNode, type AndroidUiSource, type ArchiveLister, type AssertScreenshotStep, type Assertion, type AssertionResult, type BrowserChannel, type BrowserEngine, type BugFailure, type BugLogSummary, type CiFailureCluster, type CiFlakyHunt, type CiHuntResult, type CiResult, type CiStatus, type CodesignDetails, type CommandResult, type CommandRunner, type Config, DEFAULT_AGENT_REQUEST_TIMEOUT_MS, DEFAULT_ANALYZE_TREE_DEPTH, DEFAULT_FLAKY_THRESHOLD, DEFAULT_REQUEST_TIMEOUT_MS, DEFAULT_SIMULATOR_LOCK_ROOT, DEFAULT_WDA_REQUEST_TIMEOUT_MS, type DownloadOptions, type EvalScriptStep, type Extractor, type FailureCluster, type FetchLike$1 as FetchLike, type FlakyScore, type GenerateOptions, HELPER_BINARY, type HealResult, type HistoryEntry, type HistoryFile, type Hunt, INTERACTIVE_ROLES, IOS_INTERACTIVE_TYPES, IOS_MATCH_DIALECT, IOS_PRESS_KEYS, IOS_WINDOW_TYPE, type IfStep, type InstallOptions, type InstallResult, type InstalledVersion, type IosAgentClient, type IosAgentConnector, type IosAnalysisElement, type IosAnalysisResult, type IosAnalysisWindow, type IosDriverOptions, type IosLocator, type IosQuery, type IosSession, type IosTarget, type IosUiNode, type IosUiSource, type LaunchAndroidOptions, type LaunchIosOptions, type LaunchMacOptions, MACDRIVER_REPO, MACDRIVER_SIGNING_AUTHORITY_PREFIX, MACDRIVER_SIGNING_IDENTIFIER, MACDRIVER_VERSION, MACDRIVER_VERSION_PATTERN, MACOS_MATCH_DIALECT, type MacAnalysisElement, type MacAnalysisResult, type MacAnalysisWindow, type MacAxNode, type MacDriverOptions, type MacHelperClient, type MacQuery, type MacSession, type MacdriverStatus, type MacosTarget, type MockRouteStep, NATIVE_ATTRIBUTE_MAP, type NativeMatchDialect, type NativeMatchOptions, type NativeNode, type NativePlatform, type NativeRankFields, type NativeSelector, type NativeSelectorKind, type PageElement, type PageForm, type PageLink, type RankFlakyOptions, type ReliabilityConfig, type RepeatStep, type ReserveSimulatorOptions, type ResolveHelperOptions, type ResolveWdaTestRunOptions, type RunArtifacts, type RunOptions, type RunResult, type RunScriptStep, type RunSuiteHooks, type RunSuiteOptions, type RunSuiteResult, type SelectorKindMapping, type SignatureVerifier, type SimDevice, type SimctlResult, type SimctlRunner, type SimulatorReservation, SpawnMacHelperClient, type SpawnMacHelperOptions, type StatusOptions, type Step, type StepResult, type Target, UIA2_REMOTE_PORT, Uia2HttpError, Uia2Transport, type Uia2TransportOptions, type UnmockRouteStep, type UpdateBacklogOptions, type VersionProbe, type Viewport, WDA_RUNNER_BUNDLE_ID, WDA_USE_PORT_ENV, WEB_ONLY_STEP_TYPES, WdaHttpError, type WdaTestRunPreparer, WdaTransport, type WdaTransportOptions, type WebTarget, type XmlElement, analyzeAndroidApp, analyzeIosApp, analyzeMacApp, analyzePage, androidAppAllowedIdentities, androidNodeToNative, androidQueryToLocator, assertAndroidAppAllowed, assertIosAppAllowed, assertStepsSupportedByTarget, assertTargetAppAllowed, bootedDevices, bootedSimulators, buildHealCandidates, closeAndroidSession, closeIosSession, closeMacSession, clusterFailures, codesignVerifier, collectMacdriverStatus, computeFlakeScore, configSchema, createAndroidDriver, createIosDriver, createMacDriver, createUia2AgentClient, createUia2Session, createWdaAgentClient, createWdaSession, decodeXmlEntities, defaultAgentConnector, defaultIosAgentConnector, defaultWdaTestRunPreparer, dittoExtractor, downloadAndVerify, escapePredicateArg, escapeUiSelectorArg, extractElementId, extractFailures, extractSelectorIntent, findFreePort, generateHunt, healSelector, huntSchema, injectUsePortIntoXctestrun, installMacdriver, interpolateHunt, iosAppAllowedIdentities, iosNodeToNative, iosQueryToLocator, isAndroidInteractive, isIosInteractive, launchAndroidSession, launchIosSession, launchMacSession, listDevices, listHunts, listSimulators, loadConfig, loadHunt, loadHuntMeta, loadHuntTags, macdriverAssetName, macdriverAssetUrl, macdriverBuildInstructions, macdriverChecksumName, macdriverInstallRoot, macdriverInstalledBinary, macdriverReleaseTag, macdriverVersionDir, matchAndroidSelector, matchIosSelector, matchNativeTree, nodeMatchesSelector, normalizeXcuiClassName, parseAaptPackage, parseAdbDevices, parseAndroidHierarchy, parseAndroidSelector, parseChecksumFile, parseCodesignDetails, parseForwardPort, parseIosHierarchy, parseIosSelector, parseMacSelector, parseNativeSelector, parseSimctlDevices, parseSnapshot, parseXcodeVersion, parseXml, qualifyResourceId, quoteSelectorValue, rankAndroidSelectors, rankFlaky, rankIosSelectors, rankMacSelectors, rankNativeSelectors, readHistory, readHuntHistory, readIosBundleIdentifier, reserveSimulatorUdid, resolveAgentApks, resolveHelperBinary, resolveWdaProject, resolveWdaTestRun, runHunt, runSuite, runVersionProbe, selectDeviceSerial, selectSimulatorUdid, sha256Hex, shortIosType, stepSchema, tccGuidance, unquoteSelectorValue, unwrapAndroidTextSelector, unwrapIosTextSelector, unwrapNativeTextSelector, updateBacklogFromSuite, validateMacdriverArchiveEntries, validateMacdriverVersion, verifyMacdriverSignature, waitForAgentReady, waitForWdaReady, wdaCacheDir, wdaTestRunArgs, webOnlyReason, zipinfoArchiveLister };
|