diff --git a/devices/wda_foreground_app.go b/devices/wda_foreground_app.go index 9351b79..9cf1623 100644 --- a/devices/wda_foreground_app.go +++ b/devices/wda_foreground_app.go @@ -2,10 +2,16 @@ package devices import ( "fmt" + "strings" "github.com/mobile-next/mobilecli/devices/devicekit" ) +// springboardBundleID is the home screen. A real device lists it among its +// installed apps as "SpringBoard"; a simulator does not list it, and WDA names +// it with a single space. +const springboardBundleID = "com.apple.springboard" + // wdaForegroundApp asks WDA which app is in the foreground and enriches the // answer with version details from the device's installed-app list. Shared by // the physical-device and simulator implementations, which both talk to WDA. @@ -22,7 +28,12 @@ func wdaForegroundApp(client *devicekit.DeviceKitClient, listApps func(onlyLaunc return nil, fmt.Errorf("failed to list apps: %w", err) } - // find the matching app to get full details + return foregroundAppFromWDA(activeApp, apps), nil +} + +// foregroundAppFromWDA describes the app WDA reports in the foreground, using +// the installed-app entry when there is one. +func foregroundAppFromWDA(activeApp *devicekit.ActiveAppInfo, apps []InstalledAppInfo) *ForegroundAppInfo { for _, app := range apps { if app.PackageName == activeApp.BundleID { return &ForegroundAppInfo{ @@ -30,15 +41,29 @@ func wdaForegroundApp(client *devicekit.DeviceKitClient, listApps func(onlyLaunc AppName: app.AppName, Version: app.Version, Activity: activeApp.ViewController, - }, nil + } } } - // if app not found in list (e.g., system app), return info from WDA only + // not in the list (e.g., a system app): describe it from WDA alone return &ForegroundAppInfo{ PackageName: activeApp.BundleID, - AppName: activeApp.Name, + AppName: wdaAppName(activeApp), Version: "", Activity: activeApp.ViewController, - }, nil + } +} + +// wdaAppName is the name WDA reports for an app, or, when that name is blank, +// the name a real device lists for the home screen and the bundle ID for any +// other app, as the Android foreground app does for an app without a label. +func wdaAppName(activeApp *devicekit.ActiveAppInfo) string { + name := strings.TrimSpace(activeApp.Name) + if name != "" { + return name + } + if activeApp.BundleID == springboardBundleID { + return "SpringBoard" + } + return activeApp.BundleID } diff --git a/devices/wda_foreground_app_test.go b/devices/wda_foreground_app_test.go new file mode 100644 index 0000000..cbfa849 --- /dev/null +++ b/devices/wda_foreground_app_test.go @@ -0,0 +1,45 @@ +package devices + +import ( + "testing" + + "github.com/mobile-next/mobilecli/devices/devicekit" +) + +func activeApp(bundleID string, name string) *devicekit.ActiveAppInfo { + return &devicekit.ActiveAppInfo{BundleID: bundleID, Name: name, ViewController: "SomeController"} +} + +func TestForegroundAppNamesTheHomeScreenSpringBoardWhenWDAGivesABlankName(t *testing.T) { + app := foregroundAppFromWDA(activeApp("com.apple.springboard", " "), nil) + + if app.AppName != "SpringBoard" { + t.Errorf("got app name %q, want %q", app.AppName, "SpringBoard") + } +} + +func TestForegroundAppFallsBackToTheBundleIDWhenWDAGivesABlankName(t *testing.T) { + app := foregroundAppFromWDA(activeApp("com.example.system", " "), nil) + + if app.AppName != "com.example.system" { + t.Errorf("got app name %q, want the bundle ID", app.AppName) + } +} + +func TestForegroundAppKeepsTheNameWDAReports(t *testing.T) { + app := foregroundAppFromWDA(activeApp("com.example.system", "System Thing"), nil) + + if app.AppName != "System Thing" { + t.Errorf("got app name %q, want %q", app.AppName, "System Thing") + } +} + +func TestForegroundAppPrefersTheInstalledAppEntry(t *testing.T) { + installed := []InstalledAppInfo{{PackageName: "com.example.app", AppName: "Example", Version: "1.2"}} + + app := foregroundAppFromWDA(activeApp("com.example.app", " "), installed) + + if app.AppName != "Example" || app.Version != "1.2" || app.Activity != "SomeController" { + t.Errorf("got %+v, want the installed app's name and version with WDA's activity", app) + } +} diff --git a/test/simulator.spec.ts b/test/simulator.spec.ts index 196dfff..26ef1cd 100644 --- a/test/simulator.spec.ts +++ b/test/simulator.spec.ts @@ -261,6 +261,19 @@ test.describe('iOS Simulator Tests', () => { verifySpringBoardIsForeground(foregroundAfterHome); }); + test('should name the home screen SpringBoard, as a real device does', async () => { + test.skip(!simulatorId, 'simulator not found'); + + pressButton(simulatorId, 'HOME'); + await eventually( + () => getForegroundApp(simulatorId).data.packageName, + 'the home screen never came to the front', + ).toBe('com.apple.springboard'); + + // the agent reports springboard's name as a single space + expect(getForegroundApp(simulatorId).data.appName).toBe('SpringBoard'); + }); + test('should set and read back clipboard text', async () => { test.skip(!simulatorId, 'simulator not found');