@@ -4,13 +4,15 @@ const rehydratedDriver = {rehydrated: true};
44
55const mockResolveDriver = jest . fn < ( sessionId ?: string ) => Promise < any > > ( ) ;
66const mockListAppsFromDevice = jest . fn < ( ...args : any [ ] ) => Promise < { packageName : string ; appName : string } [ ] > > ( ) ;
7+ const mockGetPlatformName = jest . fn < ( ) => string > ( ) ;
8+ const mockIsXCUITestDriverSession = jest . fn < ( ) => boolean > ( ) ;
79
810jest . unstable_mockModule ( '../../../session-store' , ( ) => ( {
911 // The in-memory cache is empty after an MCP process recycle.
1012 getDriver : jest . fn ( ( ) => null ) ,
1113 getSessionId : jest . fn ( ( ) => undefined ) ,
12- getPlatformName : jest . fn ( ( ) => 'Android' ) ,
13- isXCUITestDriverSession : jest . fn ( ( ) => false ) ,
14+ getPlatformName : mockGetPlatformName ,
15+ isXCUITestDriverSession : mockIsXCUITestDriverSession ,
1416 PLATFORM : { ios : 'iOS' , android : 'Android' } ,
1517} ) ) ;
1618
@@ -26,12 +28,14 @@ jest.unstable_mockModule('../../../tools/app-management/list-apps.js', () => ({
2628
2729const { resolveAppId} = await import ( '../../../tools/app-management/resolve-app-id.js' ) ;
2830
29- describe ( 'resolveAppId on a persisted session' , ( ) => {
30- beforeEach ( ( ) => {
31- jest . clearAllMocks ( ) ;
32- mockListAppsFromDevice . mockResolvedValue ( [ { packageName : 'com.example.calc' , appName : 'Calculator' } ] ) ;
33- } ) ;
31+ beforeEach ( ( ) => {
32+ jest . clearAllMocks ( ) ;
33+ mockGetPlatformName . mockReturnValue ( 'Android' ) ;
34+ mockIsXCUITestDriverSession . mockReturnValue ( false ) ;
35+ mockListAppsFromDevice . mockResolvedValue ( [ { packageName : 'com.example.calc' , appName : 'Calculator' } ] ) ;
36+ } ) ;
3437
38+ describe ( 'resolveAppId on a persisted session' , ( ) => {
3539 test ( 'rehydrates the session instead of failing on an empty driver cache' , async ( ) => {
3640 mockResolveDriver . mockResolvedValue ( { ok : true , driver : rehydratedDriver } ) ;
3741
@@ -48,3 +52,30 @@ describe('resolveAppId on a persisted session', () => {
4852 expect ( mockListAppsFromDevice ) . not . toHaveBeenCalled ( ) ;
4953 } ) ;
5054} ) ;
55+
56+ describe ( 'resolveAppId on a real iOS device' , ( ) => {
57+ const deviceDriver = { isSimulator : ( ) => false } ;
58+
59+ beforeEach ( ( ) => {
60+ mockGetPlatformName . mockReturnValue ( 'iOS' ) ;
61+ mockIsXCUITestDriverSession . mockReturnValue ( true ) ;
62+ mockResolveDriver . mockResolvedValue ( { ok : true , driver : deviceDriver } ) ;
63+ } ) ;
64+
65+ test ( 'keeps the apps that were listed when only one app type fails' , async ( ) => {
66+ mockListAppsFromDevice
67+ . mockResolvedValueOnce ( [ { packageName : 'com.example.calc' , appName : 'Calculator' } ] )
68+ . mockRejectedValueOnce ( new Error ( 'System app list unavailable' ) ) ;
69+
70+ await expect ( resolveAppId ( 'Calculator' , 'device-1' ) ) . resolves . toBe ( 'com.example.calc' ) ;
71+ } ) ;
72+
73+ test ( 'reports the device failure instead of caching an empty app list' , async ( ) => {
74+ mockListAppsFromDevice . mockRejectedValue ( new Error ( 'Could not list applications on the device' ) ) ;
75+
76+ await expect ( resolveAppId ( 'Calculator' , 'device-2' ) ) . rejects . toThrow ( / C o u l d n o t l i s t a p p l i c a t i o n s / ) ;
77+
78+ mockListAppsFromDevice . mockResolvedValue ( [ { packageName : 'com.example.calc' , appName : 'Calculator' } ] ) ;
79+ await expect ( resolveAppId ( 'Calculator' , 'device-2' ) ) . resolves . toBe ( 'com.example.calc' ) ;
80+ } ) ;
81+ } ) ;
0 commit comments