@@ -4,6 +4,7 @@ import os from 'node:os';
44import path from 'node:path' ;
55import { PNG } from '../../../utils/png.ts' ;
66import { handleSnapshotCommands } from '../snapshot.ts' ;
7+ import { withSessionlessRunnerCleanup } from '../snapshot-session.ts' ;
78import { captureSnapshot } from '../snapshot-capture.ts' ;
89import { SessionStore } from '../../session-store.ts' ;
910import type { SessionState } from '../../types.ts' ;
@@ -29,11 +30,25 @@ vi.mock('../../../platforms/ios/runner-client.ts', async (importOriginal) => {
2930 } ;
3031} ) ;
3132
33+ vi . mock ( '../../../platforms/ios/apps.ts' , async ( importOriginal ) => {
34+ const actual = await importOriginal < typeof import ( '../../../platforms/ios/apps.ts' ) > ( ) ;
35+ return {
36+ ...actual ,
37+ closeIosApp : vi . fn ( async ( ) => { } ) ,
38+ } ;
39+ } ) ;
40+
3241import { dispatchCommand } from '../../../core/dispatch.ts' ;
33- import { runIosRunnerCommand } from '../../../platforms/ios/runner-client.ts' ;
42+ import {
43+ runIosRunnerCommand ,
44+ stopIosRunnerSession ,
45+ } from '../../../platforms/ios/runner-client.ts' ;
46+ import { closeIosApp } from '../../../platforms/ios/apps.ts' ;
3447
3548const mockDispatch = vi . mocked ( dispatchCommand ) ;
3649const mockRunnerCommand = vi . mocked ( runIosRunnerCommand ) ;
50+ const mockStopIosRunnerSession = vi . mocked ( stopIosRunnerSession ) ;
51+ const mockCloseIosApp = vi . mocked ( closeIosApp ) ;
3752
3853function makeSessionStore ( ) : SessionStore {
3954 const root = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'agent-device-snapshot-handler-' ) ) ;
@@ -80,6 +95,10 @@ beforeEach(() => {
8095 mockDispatch . mockResolvedValue ( { } ) ;
8196 mockRunnerCommand . mockReset ( ) ;
8297 mockRunnerCommand . mockResolvedValue ( { } ) ;
98+ mockStopIosRunnerSession . mockReset ( ) ;
99+ mockStopIosRunnerSession . mockResolvedValue ( ) ;
100+ mockCloseIosApp . mockReset ( ) ;
101+ mockCloseIosApp . mockResolvedValue ( ) ;
83102} ) ;
84103
85104function writeSolidPng ( filePath : string , width = 390 , height = 844 ) : void {
@@ -1848,3 +1867,31 @@ test('wait sleep bypasses sessionless runner cleanup wrapper', async () => {
18481867 expect ( response ) . toBeTruthy ( ) ;
18491868 expect ( response ?. ok ) . toBe ( true ) ;
18501869} ) ;
1870+
1871+ test ( 'sessionless iOS runner cleanup stops the runner host app' , async ( ) => {
1872+ const result = await withSessionlessRunnerCleanup ( undefined , iosSimulatorDevice , async ( ) => {
1873+ return 'ok' ;
1874+ } ) ;
1875+
1876+ expect ( result ) . toBe ( 'ok' ) ;
1877+ expect ( mockStopIosRunnerSession ) . toHaveBeenCalledWith ( iosSimulatorDevice . id ) ;
1878+ expect ( mockCloseIosApp ) . toHaveBeenCalledWith (
1879+ iosSimulatorDevice ,
1880+ 'com.callstack.agentdevice.runner' ,
1881+ ) ;
1882+ } ) ;
1883+
1884+ test ( 'sessionless iOS runner host close is best effort' , async ( ) => {
1885+ mockCloseIosApp . mockRejectedValueOnce ( new Error ( 'terminate failed' ) ) ;
1886+
1887+ const result = await withSessionlessRunnerCleanup ( undefined , iosSimulatorDevice , async ( ) => {
1888+ return 'ok' ;
1889+ } ) ;
1890+
1891+ expect ( result ) . toBe ( 'ok' ) ;
1892+ expect ( mockStopIosRunnerSession ) . toHaveBeenCalledWith ( iosSimulatorDevice . id ) ;
1893+ expect ( mockCloseIosApp ) . toHaveBeenCalledWith (
1894+ iosSimulatorDevice ,
1895+ 'com.callstack.agentdevice.runner' ,
1896+ ) ;
1897+ } ) ;
0 commit comments