mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 19:17:10 +00:00
fix(runner): re-check foreground at apply time, skip stale actions
ensureForeground runs before observe, but the app can leave between observe and apply (a prior gesture settling late); swipes/keys then fire stale coordinates onto whatever screen is now up. Re-check foreground immediately before applying and, when the app is gone, skip the action and log it (making the escape visible) so the next step's guard relaunches instead.
This commit is contained in:
1 parent
505367f42b
commit
3f8c6018f6
1 file changed
+30
-1
@@ -225,7 +225,17 @@ func Run(ctx context.Context, options Options) (Summary, error) {
|
||||
}
|
||||
|
||||
applySkipped := false
|
||||
if nextErr == nil {
|
||||
if nextErr == nil && !appIsForeground(ctx, options) {
|
||||
// The app left the foreground between observe and apply (a prior
|
||||
// action's gesture settling late, or an async navigation). The
|
||||
// chosen action's coordinates reference a tree that no longer
|
||||
// applies, so firing it would act on whatever screen is now up.
|
||||
// Skip it and record the escape; the next step's guard relaunches.
|
||||
logger.Warn("app not in foreground at action time; skipping (relaunch next step)",
|
||||
"step", stepIndex, "action", nextAction.Kind)
|
||||
applySkipped = true
|
||||
lastAction = nil
|
||||
} else if nextErr == nil {
|
||||
if err := applyAction(ctx, options.Driver, nextAction, tree); err != nil {
|
||||
if isWDADrop(err) {
|
||||
return summary, fmt.Errorf("step %d: the iOS XCTest runner could not be restarted - re-run the test: %w", stepIndex, err)
|
||||
@@ -411,6 +421,25 @@ func ensureForeground(ctx context.Context, options Options, logger *slog.Logger,
|
||||
return true
|
||||
}
|
||||
|
||||
// appIsForeground reports whether the app under test currently owns the
|
||||
// foreground. It is the apply-time half of the scope guard: ensureForeground
|
||||
// runs before observe, but the app can leave between observe and apply (a prior
|
||||
// gesture settling late, an async navigation), and swipes/keys carry stale
|
||||
// coordinates with no selector to re-resolve. An absent capability or an unknown
|
||||
// foreground returns true so the run is never blocked where the signal is
|
||||
// unavailable (web, iOS, a transient read).
|
||||
func appIsForeground(ctx context.Context, options Options) bool {
|
||||
checker, ok := options.Driver.(driver.ForegroundChecker)
|
||||
if !ok || options.BundleID == "" {
|
||||
return true
|
||||
}
|
||||
foreground, err := checker.ForegroundApp(ctx)
|
||||
if err != nil || foreground == "" {
|
||||
return true
|
||||
}
|
||||
return foreground == options.BundleID
|
||||
}
|
||||
|
||||
// foregroundReadyAttempts bounds how many times waitForForeground tries to
|
||||
// bring the app forward before the first step, so a stuck system dialog can
|
||||
// never hang the run.
|
||||
|
||||
Reference in new issue
Block a user