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
36 changes: 36 additions & 0 deletions pkg/driver/wda/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -726,6 +726,15 @@ func (d *Driver) launchApp(step *flow.LaunchAppStep) *core.CommandResult {
wg.Wait()
}

// Reset simulator keychain if requested — after clearState so a reinstall
// can't race, before launch so the app starts with a clean keychain.
// No-op with a warning on real devices (keychain can't be reset there).
if step.ClearKeychain {
if r := d.resetKeychain(); !r.Success {
logger.Warn("launchApp: clearKeychain skipped: %s", r.Message)
}
}

if d.udid != "" {
d.alertAction = resolveAlertAction(permissions)
}
Expand Down Expand Up @@ -901,6 +910,33 @@ func (d *Driver) clearAppStateSimulator(bundleID string) *core.CommandResult {
return successResult(fmt.Sprintf("Cleared state for %s (uninstall+reinstall)", bundleID), nil)
}

// clearKeychain handles the standalone clearKeychain step. Unsupported on
// real devices (iOS keychain is sandboxed and can't be reset via public API).
func (d *Driver) clearKeychain(_ *flow.ClearKeychainStep) *core.CommandResult {
return d.resetKeychain()
}

// resetKeychain runs `xcrun simctl keychain <udid> reset` on the simulator.
// Shared by the standalone step and the launchApp clearKeychain: true option.
func (d *Driver) resetKeychain() *core.CommandResult {
if !d.info.IsSimulator {
return &core.CommandResult{
Success: false,
Error: fmt.Errorf("clearKeychain is not supported on real iOS devices"),
Message: "clearKeychain requires an iOS Simulator — the iOS keychain is sandboxed on real devices and cannot be reset programmatically. Use clearState to reinstall the app, which drops its keychain entries.",
}
}
if d.udid == "" {
return errorResult(fmt.Errorf("simulator UDID required"), "clearKeychain requires a booted simulator")
}
cmd := exec.Command("xcrun", "simctl", "keychain", d.udid, "reset")
if output, err := cmd.CombinedOutput(); err != nil {
return errorResult(fmt.Errorf("simctl keychain reset failed: %w: %s", err, string(output)),
"Failed to reset simulator keychain")
}
return successResult("Simulator keychain reset", nil)
}

func (d *Driver) clearAppStateDevice(bundleID string) *core.CommandResult {
// Uninstall via xcrun devicectl (uses remoted, doesn't disrupt usbmuxd port forwarding)
cmd := exec.Command("xcrun", "devicectl", "device", "uninstall", "app",
Expand Down
4 changes: 4 additions & 0 deletions pkg/driver/wda/driver.go
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,10 @@ func (d *Driver) Execute(step flow.Step) *core.CommandResult {
case *flow.SetPermissionsStep:
result = d.setPermissions(s)

// Keychain
case *flow.ClearKeychainStep:
result = d.clearKeychain(s)

default:
result = &core.CommandResult{
Success: false,
Expand Down
Loading