@@ -5,7 +5,7 @@ import { SNAP_SHOT_PERMISSION_HELPER_CHANNEL } from "../ipc/channels.ts";
55
66const mocks = vi . hoisted ( ( ) => ( {
77 granted : false ,
8- getFileIcon : vi . fn ( ) ,
8+ createFromPath : vi . fn ( ) ,
99 startDrag : vi . fn ( ) ,
1010 showItemInFolder : vi . fn ( ) ,
1111 send : vi . fn ( ) ,
@@ -51,9 +51,8 @@ vi.mock("electron", async () => {
5151 return {
5252 app : {
5353 getPath : ( ) => "/Applications/T3 Code (Nightly).app/Contents/MacOS/T3 Code" ,
54- getName : ( ) => "T3 Code (Nightly)" ,
55- getFileIcon : mocks . getFileIcon ,
5654 } ,
55+ nativeImage : { createFromPath : mocks . createFromPath } ,
5756 BrowserWindow : class extends MockWindow { } ,
5857 ipcMain : new EventEmitter ( ) ,
5958 screen : {
@@ -73,7 +72,8 @@ beforeEach(() => {
7372 vi . useFakeTimers ( ) ;
7473 vi . clearAllMocks ( ) ;
7574 mocks . granted = false ;
76- mocks . getFileIcon . mockResolvedValue ( { toDataURL : ( ) => "data:image/png;base64,abc" } ) ;
75+ const icon = { toDataURL : ( ) => "data:image/png;base64,abc" } ;
76+ mocks . createFromPath . mockReturnValue ( { isEmpty : ( ) => false , resize : ( ) => icon } ) ;
7777 mocks . loadURL . mockResolvedValue ( undefined ) ;
7878 windows . length = 0 ;
7979 helper = new MacPermissionHelper ( ) ;
@@ -82,7 +82,9 @@ afterEach(() => {
8282 helper . close ( ) ;
8383 vi . useRealTimers ( ) ;
8484} ) ;
85- const open = ( ) => helper . show ( "accessibility" , "/bundle/snapshot-permission-preload.cjs" , null ) ;
85+ const iconPaths = [ "/bundle/prod-resources/icon.png" ] ;
86+ const open = ( ) =>
87+ helper . show ( "accessibility" , "/bundle/snapshot-permission-preload.cjs" , null , iconPaths ) ;
8688function send ( action : string , trusted = true ) {
8789 const window = windows . at ( - 1 ) ! ;
8890 Electron . ipcMain . emit (
@@ -109,20 +111,16 @@ it("drags the running app bundle only for the helper's own renderer", async () =
109111 send ( "drag" , false ) ;
110112 expect ( mocks . startDrag ) . not . toHaveBeenCalled ( ) ;
111113 send ( "drag" ) ;
114+ expect ( mocks . createFromPath ) . toHaveBeenCalledWith ( "/bundle/prod-resources/icon.png" ) ;
112115 expect ( mocks . startDrag ) . toHaveBeenCalledWith ( {
113116 file : "/Applications/T3 Code (Nightly).app" ,
114- icon : await mocks . getFileIcon . mock . results [ 0 ] ! . value ,
117+ icon : mocks . createFromPath . mock . results [ 0 ] ! . value . resize ( ) ,
115118 } ) ;
116119 send ( "finder" ) ;
117120 expect ( mocks . showItemInFolder ) . toHaveBeenCalledWith ( "/Applications/T3 Code (Nightly).app" ) ;
118121} ) ;
119- it ( "rechecks permissions, reports restart guidance, and releases resources when granted" , async ( ) => {
122+ it ( "rechecks permissions and releases resources when granted" , async ( ) => {
120123 await open ( ) ;
121- send ( "check" ) ;
122- expect ( mocks . send ) . toHaveBeenCalledWith (
123- SNAP_SHOT_PERMISSION_HELPER_CHANNEL ,
124- expect . stringContaining ( "quit and reopen" ) ,
125- ) ;
126124 mocks . granted = true ;
127125 vi . advanceTimersByTime ( 1000 ) ;
128126 expect ( windows [ 0 ] ! . destroyed ) . toBe ( true ) ;
@@ -131,7 +129,7 @@ it("rechecks permissions, reports restart guidance, and releases resources when
131129} ) ;
132130it ( "keeps only one helper and cleans up on dismissal" , async ( ) => {
133131 await open ( ) ;
134- await helper . show ( "screen-recording" , "/preload.cjs" , null ) ;
132+ await helper . show ( "screen-recording" , "/preload.cjs" , null , iconPaths ) ;
135133 expect ( windows [ 0 ] ! . destroyed ) . toBe ( true ) ;
136134 expect ( Electron . ipcMain . listenerCount ( SNAP_SHOT_PERMISSION_HELPER_CHANNEL ) ) . toBe ( 1 ) ;
137135 send ( "close" ) ;
@@ -143,10 +141,9 @@ it("does not open for a permission already granted", async () => {
143141 await open ( ) ;
144142 expect ( windows ) . toHaveLength ( 0 ) ;
145143} ) ;
146- it ( "does not reopen after disposal while the icon is loading" , async ( ) => {
147- const loading = open ( ) ;
148- helper . close ( ) ;
149- await loading ;
144+ it ( "does not show a helper with a missing packaged icon" , async ( ) => {
145+ mocks . createFromPath . mockReturnValueOnce ( { isEmpty : ( ) => true } ) ;
146+ await expect ( open ( ) ) . rejects . toThrow ( "packaged T3 Code icon is missing" ) ;
150147 expect ( windows ) . toHaveLength ( 0 ) ;
151148} ) ;
152149it ( "cleans up when the helper page fails to load" , async ( ) => {
@@ -162,16 +159,13 @@ it("offers the Finder fallback when native dragging fails", async () => {
162159 throw new Error ( "drag failed" ) ;
163160 } ) ;
164161 send ( "drag" ) ;
165- expect ( mocks . send ) . toHaveBeenCalledWith (
166- SNAP_SHOT_PERMISSION_HELPER_CHANNEL ,
167- expect . stringContaining ( "Show in Finder" ) ,
168- ) ;
162+ expect ( mocks . showItemInFolder ) . toHaveBeenCalledWith ( "/Applications/T3 Code (Nightly).app" ) ;
169163 expect ( windows [ 0 ] ! . destroyed ) . toBe ( false ) ;
170164} ) ;
171165
172166it ( "returns focus to onboarding when the permission is granted" , async ( ) => {
173167 const owner = new Electron . BrowserWindow ( { } ) ;
174- await helper . show ( "screen-recording" , "/preload.cjs" , owner ) ;
168+ await helper . show ( "screen-recording" , "/preload.cjs" , owner , iconPaths ) ;
175169 mocks . granted = true ;
176170 vi . advanceTimersByTime ( 1000 ) ;
177171 expect ( owner . show ) . toHaveBeenCalledOnce ( ) ;
@@ -181,9 +175,17 @@ it("returns focus to onboarding when the permission is granted", async () => {
181175} ) ;
182176it ( "closes the helper and stops checking when onboarding's window closes" , async ( ) => {
183177 const owner = new Electron . BrowserWindow ( { } ) ;
184- await helper . show ( "accessibility" , "/preload.cjs" , owner ) ;
178+ await helper . show ( "accessibility" , "/preload.cjs" , owner , iconPaths ) ;
185179 owner . destroy ( ) ;
186180 expect ( windows [ 1 ] ! . destroyed ) . toBe ( true ) ;
187181 expect ( Electron . ipcMain . listenerCount ( SNAP_SHOT_PERMISSION_HELPER_CHANNEL ) ) . toBe ( 0 ) ;
188182 expect ( vi . getTimerCount ( ) ) . toBe ( 0 ) ;
189183} ) ;
184+
185+ it ( "uses the packaged PNG when an earlier resource candidate is absent" , async ( ) => {
186+ mocks . createFromPath . mockReturnValueOnce ( { isEmpty : ( ) => true } ) ;
187+ await helper . show ( "accessibility" , "/preload.cjs" , null , [ "/missing/icon.png" , ...iconPaths ] ) ;
188+ send ( "drag" ) ;
189+ expect ( mocks . startDrag ) . toHaveBeenCalled ( ) ;
190+ expect ( mocks . createFromPath ) . toHaveBeenLastCalledWith ( iconPaths [ 0 ] ) ;
191+ } ) ;
0 commit comments