@@ -317,6 +317,95 @@ test('swipeAndroid invokes adb input swipe with duration', async () => {
317317 }
318318} ) ;
319319
320+ test ( 'openAndroidApp default launch uses -p package flag' , async ( ) => {
321+ await withMockedAdb (
322+ 'agent-device-android-open-default-' ,
323+ [
324+ '#!/bin/sh' ,
325+ 'printf "__CMD__\\n" >> "$AGENT_DEVICE_TEST_ARGS_FILE"' ,
326+ 'printf "%s\\n" "$@" >> "$AGENT_DEVICE_TEST_ARGS_FILE"' ,
327+ 'if [ "$1" = "-s" ]; then' ,
328+ ' shift' ,
329+ ' shift' ,
330+ 'fi' ,
331+ 'if [ "$1" = "shell" ] && [ "$2" = "pm" ] && [ "$3" = "list" ]; then' ,
332+ ' echo "package:com.example.app"' ,
333+ ' exit 0' ,
334+ 'fi' ,
335+ 'if [ "$1" = "shell" ] && [ "$2" = "am" ] && [ "$3" = "start" ]; then' ,
336+ ' echo "Status: ok"' ,
337+ ' exit 0' ,
338+ 'fi' ,
339+ 'exit 0' ,
340+ '' ,
341+ ] . join ( '\n' ) ,
342+ async ( { argsLogPath, device } ) => {
343+ await openAndroidApp ( device , 'com.example.app' ) ;
344+ const logged = await fs . readFile ( argsLogPath , 'utf8' ) ;
345+ assert . match ( logged , / s h e l l \n a m \n s t a r t \n - W \n - a \n a n d r o i d \. i n t e n t \. a c t i o n \. M A I N / ) ;
346+ assert . match ( logged , / - p \n c o m \. e x a m p l e \. a p p / ) ;
347+ } ,
348+ ) ;
349+ } ) ;
350+
351+ test ( 'openAndroidApp fallback resolve-activity includes MAIN/LAUNCHER flags' , async ( ) => {
352+ await withMockedAdb (
353+ 'agent-device-android-open-fallback-' ,
354+ [
355+ '#!/bin/sh' ,
356+ 'printf "__CMD__\\n" >> "$AGENT_DEVICE_TEST_ARGS_FILE"' ,
357+ 'printf "%s\\n" "$@" >> "$AGENT_DEVICE_TEST_ARGS_FILE"' ,
358+ 'if [ "$1" = "-s" ]; then' ,
359+ ' shift' ,
360+ ' shift' ,
361+ 'fi' ,
362+ 'if [ "$1" = "shell" ] && [ "$2" = "pm" ] && [ "$3" = "list" ]; then' ,
363+ ' echo "package:com.microsoft.office.outlook"' ,
364+ ' exit 0' ,
365+ 'fi' ,
366+ '# First am start (with -p) fails to simulate multi-entry app issue' ,
367+ 'if [ "$1" = "shell" ] && [ "$2" = "am" ] && [ "$3" = "start" ]; then' ,
368+ ' for arg in "$@"; do' ,
369+ ' if [ "$arg" = "-p" ]; then' ,
370+ ' echo "Error: Activity not started" >&2' ,
371+ ' exit 1' ,
372+ ' fi' ,
373+ ' done' ,
374+ ' echo "Status: ok"' ,
375+ ' exit 0' ,
376+ 'fi' ,
377+ '# resolve-activity returns correct launcher component' ,
378+ 'if [ "$1" = "shell" ] && [ "$2" = "cmd" ] && [ "$3" = "package" ] && [ "$4" = "resolve-activity" ]; then' ,
379+ ' echo "priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true"' ,
380+ ' echo "com.microsoft.office.outlook/com.microsoft.office.outlook.ui.miit.MiitLauncherActivity"' ,
381+ ' exit 0' ,
382+ 'fi' ,
383+ 'exit 0' ,
384+ '' ,
385+ ] . join ( '\n' ) ,
386+ async ( { argsLogPath, device } ) => {
387+ await openAndroidApp ( device , 'com.microsoft.office.outlook' ) ;
388+ const logged = await fs . readFile ( argsLogPath , 'utf8' ) ;
389+ // Verify resolve-activity was called with MAIN/LAUNCHER flags
390+ assert . match ( logged , / r e s o l v e - a c t i v i t y \n - - b r i e f \n - a \n a n d r o i d \. i n t e n t \. a c t i o n \. M A I N \n - c \n a n d r o i d \. i n t e n t \. c a t e g o r y \. L A U N C H E R \n c o m \. m i c r o s o f t \. o f f i c e \. o u t l o o k / ) ;
391+ // Verify fallback launch used the resolved component
392+ assert . match ( logged , / - n \n c o m \. m i c r o s o f t \. o f f i c e \. o u t l o o k \/ c o m \. m i c r o s o f t \. o f f i c e \. o u t l o o k \. u i \. m i i t \. M i i t L a u n c h e r A c t i v i t y / ) ;
393+ } ,
394+ ) ;
395+ } ) ;
396+
397+ test ( 'parseAndroidLaunchComponent handles multi-entry resolve output' , ( ) => {
398+ // Some devices return extra metadata lines before the component
399+ const stdout = [
400+ 'priority=0 preferredOrder=0 match=0x108000 specificIndex=-1 isDefault=true' ,
401+ 'com.microsoft.office.outlook/com.microsoft.office.outlook.ui.miit.MiitLauncherActivity' ,
402+ ] . join ( '\n' ) ;
403+ assert . equal (
404+ parseAndroidLaunchComponent ( stdout ) ,
405+ 'com.microsoft.office.outlook/com.microsoft.office.outlook.ui.miit.MiitLauncherActivity' ,
406+ ) ;
407+ } ) ;
408+
320409test ( 'setAndroidSetting permission grant camera uses pm grant' , async ( ) => {
321410 await withMockedAdb (
322411 'agent-device-android-permission-camera-' ,
0 commit comments