mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-05 04:27:09 +00:00
feat(spec): add doubleTaps random-target builtin to defaultActions
This commit is contained in:
1 parent
f08d9119b2
commit
177b571b0a
7 files changed
+22
-8
No files matched your search
@@ -71,7 +71,7 @@ export function Wait(parameters: { durationMillis: number }): WaitAction {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function builtinGenerator(
|
function builtinGenerator(
|
||||||
name: "taps" | "typing" | "swipes" | "waitOnce" | "pressKeys",
|
name: "taps" | "doubleTaps" | "typing" | "swipes" | "waitOnce" | "pressKeys",
|
||||||
): ActionGenerator {
|
): ActionGenerator {
|
||||||
return new Proxy({} as ActionGenerator, {
|
return new Proxy({} as ActionGenerator, {
|
||||||
get(_target, property) {
|
get(_target, property) {
|
||||||
@@ -85,6 +85,7 @@ function builtinGenerator(
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const taps: ActionGenerator = builtinGenerator("taps");
|
export const taps: ActionGenerator = builtinGenerator("taps");
|
||||||
|
export const doubleTaps: ActionGenerator = builtinGenerator("doubleTaps");
|
||||||
export const typing: ActionGenerator = builtinGenerator("typing");
|
export const typing: ActionGenerator = builtinGenerator("typing");
|
||||||
export const swipes: ActionGenerator = builtinGenerator("swipes");
|
export const swipes: ActionGenerator = builtinGenerator("swipes");
|
||||||
export const waitOnce: ActionGenerator = builtinGenerator("waitOnce");
|
export const waitOnce: ActionGenerator = builtinGenerator("waitOnce");
|
||||||
|
|||||||
@@ -1,15 +1,21 @@
|
|||||||
import { swipes, taps, typing, weighted } from "../actions.ts";
|
import { doubleTaps, swipes, taps, typing, weighted } from "../actions.ts";
|
||||||
import type { ActionGenerator } from "../types.ts";
|
import type { ActionGenerator } from "../types.ts";
|
||||||
|
|
||||||
export { pressKey, swipes, taps, typing, waitOnce } from "../actions.ts";
|
export { doubleTaps, pressKey, swipes, taps, typing, waitOnce } from "../actions.ts";
|
||||||
|
|
||||||
// A broad exploration generator: tap things, type edge-case values into fields,
|
// A broad exploration generator: tap things, type edge-case values into fields,
|
||||||
// and swipe. Layer it under targeted depth flows so the fuzzer wanders the whole
|
// and swipe. Layer it under targeted depth flows so the fuzzer wanders the whole
|
||||||
// app while still driving the paths an author wrote. It deliberately omits the
|
// app while still driving the paths an author wrote. It deliberately omits the
|
||||||
// hardware back key: backing out past the app's root screen exits the app under
|
// hardware back key: backing out past the app's root screen exits the app under
|
||||||
// test, and exploration must stay within the app.
|
// test, and exploration must stay within the app.
|
||||||
|
//
|
||||||
|
// doubleTaps fires the same target twice ~50ms apart inside one step. Real users
|
||||||
|
// double-tap (image zoom, like-to-favorite, play/pause); without this the fuzzer
|
||||||
|
// can never produce sub-100ms event spacing, so race-window bugs (debounce gaps,
|
||||||
|
// in-flight guards, init races) stay unreachable.
|
||||||
export const defaultActions: ActionGenerator = weighted(
|
export const defaultActions: ActionGenerator = weighted(
|
||||||
[55, taps],
|
[45, taps],
|
||||||
[30, typing],
|
[15, doubleTaps],
|
||||||
|
[25, typing],
|
||||||
[15, swipes],
|
[15, swipes],
|
||||||
);
|
);
|
||||||
@@ -38,6 +38,7 @@ export {
|
|||||||
Tap,
|
Tap,
|
||||||
Wait,
|
Wait,
|
||||||
actions,
|
actions,
|
||||||
|
doubleTaps,
|
||||||
from,
|
from,
|
||||||
pressKey,
|
pressKey,
|
||||||
swipes,
|
swipes,
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ export interface SanderlingRuntime {
|
|||||||
pressKey: (parameters: { key: Key }) => PressKeyAction;
|
pressKey: (parameters: { key: Key }) => PressKeyAction;
|
||||||
wait: (parameters: { durationMillis: number }) => WaitAction;
|
wait: (parameters: { durationMillis: number }) => WaitAction;
|
||||||
taps: ActionGenerator;
|
taps: ActionGenerator;
|
||||||
|
doubleTaps: ActionGenerator;
|
||||||
typing: ActionGenerator;
|
typing: ActionGenerator;
|
||||||
swipes: ActionGenerator;
|
swipes: ActionGenerator;
|
||||||
waitOnce: ActionGenerator;
|
waitOnce: ActionGenerator;
|
||||||
|
|||||||
@@ -350,6 +350,7 @@ const runtime = {
|
|||||||
return { kind: "Wait", durationMillis: p.durationMillis };
|
return { kind: "Wait", durationMillis: p.durationMillis };
|
||||||
},
|
},
|
||||||
taps: { __sanderlingActionGenerator: true, __sanderlingKind: "taps" } as ActionGeneratorHandle,
|
taps: { __sanderlingActionGenerator: true, __sanderlingKind: "taps" } as ActionGeneratorHandle,
|
||||||
|
doubleTaps: { __sanderlingActionGenerator: true, __sanderlingKind: "doubleTaps" } as ActionGeneratorHandle,
|
||||||
typing: { __sanderlingActionGenerator: true, __sanderlingKind: "typing" } as ActionGeneratorHandle,
|
typing: { __sanderlingActionGenerator: true, __sanderlingKind: "typing" } as ActionGeneratorHandle,
|
||||||
swipes: { __sanderlingActionGenerator: true, __sanderlingKind: "swipes" } as ActionGeneratorHandle,
|
swipes: { __sanderlingActionGenerator: true, __sanderlingKind: "swipes" } as ActionGeneratorHandle,
|
||||||
waitOnce: { __sanderlingActionGenerator: true, __sanderlingKind: "waitOnce" } as ActionGeneratorHandle,
|
waitOnce: { __sanderlingActionGenerator: true, __sanderlingKind: "waitOnce" } as ActionGeneratorHandle,
|
||||||
@@ -444,7 +445,9 @@ function resolveGenerator(handle: ActionGeneratorHandle): unknown {
|
|||||||
return resolveGenerator(inner);
|
return resolveGenerator(inner);
|
||||||
}
|
}
|
||||||
case "taps":
|
case "taps":
|
||||||
return randomTap();
|
return randomTap("Tap");
|
||||||
|
case "doubleTaps":
|
||||||
|
return randomTap("DoubleTap");
|
||||||
case "typing":
|
case "typing":
|
||||||
return randomInput();
|
return randomInput();
|
||||||
case "swipes":
|
case "swipes":
|
||||||
@@ -462,7 +465,7 @@ function resolveGenerator(handle: ActionGeneratorHandle): unknown {
|
|||||||
// doesn't re-walk the DOM and re-flush layout on every iteration.
|
// doesn't re-walk the DOM and re-flush layout on every iteration.
|
||||||
let randomTapCandidates: HTMLElement[] | null = null;
|
let randomTapCandidates: HTMLElement[] | null = null;
|
||||||
|
|
||||||
function randomTap(): unknown {
|
function randomTap(kind: "Tap" | "DoubleTap"): unknown {
|
||||||
if (!randomTapCandidates) {
|
if (!randomTapCandidates) {
|
||||||
randomTapCandidates = Array.from(
|
randomTapCandidates = Array.from(
|
||||||
document.querySelectorAll<HTMLElement>(
|
document.querySelectorAll<HTMLElement>(
|
||||||
@@ -480,7 +483,7 @@ function randomTap(): unknown {
|
|||||||
if (!picked) return null;
|
if (!picked) return null;
|
||||||
const rect = picked.getBoundingClientRect();
|
const rect = picked.getBoundingClientRect();
|
||||||
return {
|
return {
|
||||||
kind: "Tap",
|
kind,
|
||||||
on: {
|
on: {
|
||||||
x: Math.round(rect.left + rect.width / 2),
|
x: Math.round(rect.left + rect.width / 2),
|
||||||
y: Math.round(rect.top + rect.height / 2),
|
y: Math.round(rect.top + rect.height / 2),
|
||||||
|
|||||||
@@ -150,6 +150,7 @@ function installFakeRuntime(): RecordedRuntime {
|
|||||||
pressKey: ({ key }) => ({ kind: "PressKey", key }),
|
pressKey: ({ key }) => ({ kind: "PressKey", key }),
|
||||||
wait: ({ durationMillis }) => ({ kind: "Wait", durationMillis }),
|
wait: ({ durationMillis }) => ({ kind: "Wait", durationMillis }),
|
||||||
taps: { __sanderlingActionGenerator: true, generate: () => [] },
|
taps: { __sanderlingActionGenerator: true, generate: () => [] },
|
||||||
|
doubleTaps: { __sanderlingActionGenerator: true, generate: () => [] },
|
||||||
typing: { __sanderlingActionGenerator: true, generate: () => [] },
|
typing: { __sanderlingActionGenerator: true, generate: () => [] },
|
||||||
swipes: { __sanderlingActionGenerator: true, generate: () => [] },
|
swipes: { __sanderlingActionGenerator: true, generate: () => [] },
|
||||||
waitOnce: { __sanderlingActionGenerator: true, generate: () => [] },
|
waitOnce: { __sanderlingActionGenerator: true, generate: () => [] },
|
||||||
|
|||||||
@@ -64,6 +64,7 @@ function installRuntime(initialState: State): RecordedRuntime {
|
|||||||
pressKey: ({ key }) => ({ kind: "PressKey", key }),
|
pressKey: ({ key }) => ({ kind: "PressKey", key }),
|
||||||
wait: ({ durationMillis }) => ({ kind: "Wait", durationMillis }),
|
wait: ({ durationMillis }) => ({ kind: "Wait", durationMillis }),
|
||||||
taps: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
taps: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
||||||
|
doubleTaps: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
||||||
typing: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
typing: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
||||||
swipes: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
swipes: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
||||||
waitOnce: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
waitOnce: { __sanderlingActionGenerator: true, generate: () => [] } as ActionGenerator,
|
||||||
|
|||||||
Reference in new issue
Block a user