Files
sanderling/pkg/spec/src/actions.ts
T

120 lines
3.7 KiB
TypeScript

// Author-facing action factories. Each returns plain GeneratorNode /
// ActionDescriptor DATA (see action-tree.ts); the shared picker (pick.ts) walks
// the tree and the runtime entry (runtime-entry.ts) serializes the result. No
// factory forwards to globalThis.__sanderling__ anymore: the same data tree
// drives both the goja verifier and the V8 web runtime.
import type {
AccessibilityElement,
Action,
Direction,
DoubleTapAction,
InputTextAction,
Key,
LongPressAction,
Point,
PressKeyAction,
Sampler,
ScrollAction,
SwipeAction,
TapAction,
WaitAction,
WeightedEntry,
} from "./types.ts";
import type { ActionDescriptor, BuiltinVerb, GeneratorNode } from "./action-tree.ts";
import { getSamplerRng } from "./sampler-rng.ts";
export { setSamplerRng } from "./sampler-rng.ts";
function builtinNode(verb: BuiltinVerb): GeneratorNode {
return { kind: "builtin", verb };
}
export function actions(generator: () => Action[]): GeneratorNode {
return { kind: "actions", generate: generator as () => ActionDescriptor[] };
}
export function whenRoute(
routeExtractor: { readonly current: string | null },
routes: string | readonly string[],
body: () => Action[],
): GeneratorNode {
const allowed = typeof routes === "string" ? [routes] : routes;
return actions(() => {
const current = routeExtractor.current;
if (current === null || !allowed.includes(current)) return [];
return body();
});
}
export function weighted(...entries: WeightedEntry[]): GeneratorNode {
return { kind: "weighted", branches: entries };
}
export function from<T>(items: readonly T[]): Sampler<T> {
return {
generate(): T {
if (items.length <= 1) return items[0] as T;
const rng = getSamplerRng();
const index = rng ? rng.intN(items.length) : 0;
return items[index] as T;
},
};
}
export function Tap(parameters: { on: string | AccessibilityElement }): TapAction {
return { kind: "Tap", on: parameters.on };
}
export function DoubleTap(parameters: { on: string | AccessibilityElement }): DoubleTapAction {
return { kind: "DoubleTap", on: parameters.on };
}
export function LongPress(parameters: { on: string | AccessibilityElement }): LongPressAction {
return { kind: "LongPress", on: parameters.on };
}
export function Scroll(parameters: {
direction: Direction;
in?: string | AccessibilityElement;
}): ScrollAction {
return { kind: "Scroll", direction: parameters.direction, in: parameters.in };
}
export function InputText(parameters: {
into: string | AccessibilityElement;
text: string;
}): InputTextAction {
return { kind: "InputText", into: parameters.into, text: parameters.text };
}
export function Swipe(parameters: {
from: Point | AccessibilityElement;
to: Point | AccessibilityElement;
durationMillis?: number;
}): SwipeAction {
return {
kind: "Swipe",
from: parameters.from,
to: parameters.to,
durationMillis: parameters.durationMillis,
};
}
export function PressKey(parameters: { key: Key }): PressKeyAction {
return { kind: "PressKey", key: parameters.key };
}
export function Wait(parameters: { durationMillis: number }): WaitAction {
return { kind: "Wait", durationMillis: parameters.durationMillis };
}
export const taps: GeneratorNode = builtinNode("taps");
export const doubleTaps: GeneratorNode = builtinNode("doubleTaps");
export const longPresses: GeneratorNode = builtinNode("longPresses");
export const scrolls: GeneratorNode = builtinNode("scrolls");
export const typing: GeneratorNode = builtinNode("typing");
export const swipes: GeneratorNode = builtinNode("swipes");
export const waitOnce: GeneratorNode = builtinNode("waitOnce");
export const pressKeys: GeneratorNode = builtinNode("pressKeys");