Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,12 @@ extension CommandType {
var traits: CommandTraits {
switch self {
// Interaction commands: require the foreground-guard + stabilization preflight.
case .tap, .longPress, .drag, .remotePress, .type, .swipe,
// tapSeries/dragSeries are the series forms of tap/drag; keyboardReturn is the sibling
// of keyboardDismiss — all three were missing from the historical switch (drift the
// table now prevents) and are classified as interactions here.
case .tap, .tapSeries, .longPress, .drag, .dragSeries, .remotePress, .type, .swipe,
.back, .backInApp, .backSystem, .rotate, .appSwitcher,
.keyboardDismiss, .pinch, .rotateGesture, .transformGesture:
.keyboardDismiss, .keyboardReturn, .pinch, .rotateGesture, .transformGesture:
return CommandTraits(isInteraction: true, readOnly: .never, isLifecycle: false)

// Read-only reads: eligible for the session-invalidating retry.
Expand All @@ -89,12 +92,11 @@ extension CommandType {
return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: true)

// Normal preflight, not retried.
// NOTE — pre-existing classifications preserved verbatim (candidates for a later, separate
// normalization PR, not this refactor): mouseClick / tapSeries / dragSeries are NOT interaction
// commands; keyboardReturn is NOT an interaction command (unlike its sibling keyboardDismiss);
// querySelector is NOT read-only; recordStart is NOT a lifecycle command.
case .mouseClick, .tapSeries, .dragSeries, .querySelector,
.home, .keyboardReturn, .recordStart:
// NOTE: mouseClick stays non-interaction for now — it is macOS-only and the foreground
// guard interacts with bespoke macOS activation, so classifying it needs a macOS smoke
// check first (tracked as a follow-up). Also preserved: querySelector is NOT read-only;
// recordStart is NOT a lifecycle command; home/alert remain non-interaction by design.
case .mouseClick, .querySelector, .home, .recordStart:
return CommandTraits(isInteraction: false, readOnly: .never, isLifecycle: false)
}
}
Expand Down
85 changes: 76 additions & 9 deletions scripts/perf/scenario.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,35 +53,102 @@ export function buildSettingsTour(p: ResolvedProfile, ctx: StepContext): Scenari
const textEntry: ScenarioStep[] = p.selectors.searchEditableAtRoot
? [
// iOS: editable search field exists at root; fill it directly (freshRoot resets scroll).
bat('fill search', 'fill', { command: 'fill', positionals: [s.searchFieldEditable, 'general'] }, { freshRoot: true }),
bat(
'fill search',
'fill',
{ command: 'fill', positionals: [s.searchFieldEditable, 'general'] },
{ freshRoot: true },
),
bat('type', 'type', { command: 'type', positionals: ['wifi'] }),
bat('get editable text', 'get', { command: 'get', positionals: ['text', s.searchFieldEditable] }),
bat('get editable text', 'get', {
command: 'get',
positionals: ['text', s.searchFieldEditable],
}),
bat('keyboard return', 'keyboard', { command: 'keyboard', positionals: ['return'] }),
]
: [
// Android: tap the search entry first to reveal the editable, then type/fill it.
bat('press search field', 'press', { command: 'press', positionals: [s.searchField] }, { freshRoot: true }),
bat(
'press search field',
'press',
{ command: 'press', positionals: [s.searchField] },
{ freshRoot: true },
),
bat('type', 'type', { command: 'type', positionals: ['wifi'] }),
bat('fill search', 'fill', { command: 'fill', positionals: [s.searchFieldEditable, 'general'] }),
bat('get editable text', 'get', { command: 'get', positionals: ['text', s.searchFieldEditable] }),
bat('fill search', 'fill', {
command: 'fill',
positionals: [s.searchFieldEditable, 'general'],
}),
bat('get editable text', 'get', {
command: 'get',
positionals: ['text', s.searchFieldEditable],
}),
];

// These iOS-only repeated gesture forms route to dedicated XCTest runner commands:
// press --count > 1 -> tapSeries; swipe --count > 1 -> dragSeries.
const iosRunnerSeries: ScenarioStep[] =
p.platform === 'ios'
? [
bat(
'press series (tapSeries)',
'press',
{ command: 'press', positionals: ['200', '95'], flags: { count: 2, intervalMs: 50 } },
{ freshRoot: true },
),
bat(
'swipe series (dragSeries)',
'swipe',
{
command: 'swipe',
positionals: ['200', '650', '200', '450', '120'],
flags: { count: 2, pauseMs: 50, pattern: 'ping-pong' },
},
{ freshRoot: true },
),
]
: [];

return [
// --- reset to root via relaunch ---
std('open (relaunch → root)', 'open', ['open', p.appTarget, '--relaunch']),

// --- reads on the root tree (snapshots first; anchor label is visible here) ---
bat('snapshot -i (root)', 'snapshot', { command: 'snapshot', flags: { snapshotInteractiveOnly: true } }, { isSnapshot: true }),
bat(
'snapshot -i (root)',
'snapshot',
{ command: 'snapshot', flags: { snapshotInteractiveOnly: true } },
{ isSnapshot: true },
),
bat('snapshot (root)', 'snapshot', { command: 'snapshot' }, { isSnapshot: true }),

// --- navigate into a sub-screen from a fresh root (freshRoot resets scroll so the
// deep-screen row is in view), read it, then return ---
bat('press → deep screen', 'press', { command: 'press', positionals: [s.deepScreen] }, { freshRoot: true }),
bat(
'press → deep screen',
'press',
{ command: 'press', positionals: [s.deepScreen] },
{ freshRoot: true },
),
bat('snapshot (deep)', 'snapshot', { command: 'snapshot' }, { isSnapshot: true }),
bat('snapshot -i (deep)', 'snapshot', { command: 'snapshot', flags: { snapshotInteractiveOnly: true } }, { isSnapshot: true }),
bat(
'snapshot -i (deep)',
'snapshot',
{ command: 'snapshot', flags: { snapshotInteractiveOnly: true } },
{ isSnapshot: true },
),
bat('back', 'back', { command: 'back' }),

// --- iOS runner series commands surfaced by PR #643 ---
...iosRunnerSeries,

// --- targeted reads against the visible anchor (freshRoot so the anchor is on screen) ---
bat('wait text', 'wait', { command: 'wait', positionals: ['text', s.anchorText, '3000'] }, { freshRoot: true }),
bat(
'wait text',
'wait',
{ command: 'wait', positionals: ['text', s.anchorText, '3000'] },
{ freshRoot: true },
),
bat('find', 'find', { command: 'find', positionals: [s.anchorText] }),
bat('get text', 'get', { command: 'get', positionals: ['text', s.anchorLabel] }),
bat('is visible', 'is', { command: 'is', positionals: ['visible', s.anchorLabel] }),
Expand Down
Loading