@@ -21,6 +21,11 @@ vi.mock('@tauri-apps/plugin-fs', () => ({
2121 writeTextFile : vi . fn ( ) ,
2222} ) ) ;
2323
24+ const toastError = vi . fn ( ) ;
25+ vi . mock ( 'sonner' , ( ) => ( {
26+ toast : { error : ( ...args : unknown [ ] ) => toastError ( ...args ) } ,
27+ } ) ) ;
28+
2429const defaultPlaylist = ( ) =>
2530 new PlaylistBuilder ( )
2631 . withId ( 'test-playlist' )
@@ -34,6 +39,7 @@ describe('PlaylistDetail view', () => {
3439 useQueueStore . setState ( { items : [ ] , currentIndex : 0 } ) ;
3540 PlaylistDetailWrapper . seedPlaylist ( defaultPlaylist ( ) ) ;
3641 ( dialog . save as Mock ) . mockResolvedValue ( null ) ;
42+ toastError . mockClear ( ) ;
3743 } ) ;
3844
3945 it ( '(Snapshot) renders playlist detail with tracks' , async ( ) => {
@@ -182,28 +188,72 @@ describe('PlaylistDetail view', () => {
182188 expect ( queueItems [ 2 ] ?. title ) . toBe ( 'So What' ) ;
183189 } ) ;
184190
185- it ( 'exports playlist as JSON file via save dialog' , async ( ) => {
186- const expectedPath = '/downloads/Test Playlist.json' ;
187- ( dialog . save as Mock ) . mockResolvedValue ( expectedPath ) ;
191+ describe ( 'JSON export' , ( ) => {
192+ it ( 'exports playlist as JSON file via save dialog' , async ( ) => {
193+ const expectedPath = '/downloads/Test Playlist.json' ;
194+ ( dialog . save as Mock ) . mockResolvedValue ( expectedPath ) ;
195+
196+ await PlaylistDetailWrapper . mount ( 'test-playlist' ) ;
197+ await PlaylistDetailWrapper . exportJsonOption . click ( ) ;
198+
199+ expect ( dialog . save ) . toHaveBeenCalledWith (
200+ expect . objectContaining ( {
201+ defaultPath : 'Test Playlist.json' ,
202+ filters : [ { name : 'JSON Files' , extensions : [ 'json' ] } ] ,
203+ } ) ,
204+ ) ;
205+
206+ expect ( fs . writeTextFile ) . toHaveBeenCalledWith (
207+ expectedPath ,
208+ expect . any ( String ) ,
209+ ) ;
210+
211+ const writtenJson = ( fs . writeTextFile as Mock ) . mock . calls [ 0 ] [ 1 ] ;
212+ const parsed = JSON . parse ( writtenJson ) ;
213+ expect ( parsed . name ) . toBe ( 'Test Playlist' ) ;
214+ expect ( parsed . tracks ) . toHaveLength ( 2 ) ;
215+ } ) ;
188216
189- await PlaylistDetailWrapper . mount ( 'test-playlist' ) ;
190- await PlaylistDetailWrapper . exportJsonOption . click ( ) ;
217+ it ( 'does nothing when the user cancels the save dialog' , async ( ) => {
218+ ( dialog . save as Mock ) . mockResolvedValueOnce ( null ) ;
191219
192- expect ( dialog . save ) . toHaveBeenCalledWith (
193- expect . objectContaining ( {
194- defaultPath : 'Test Playlist.json' ,
195- filters : [ { name : 'JSON Files' , extensions : [ 'json' ] } ] ,
196- } ) ,
197- ) ;
220+ await PlaylistDetailWrapper . mount ( 'test-playlist' ) ;
221+ await PlaylistDetailWrapper . exportJsonOption . click ( ) ;
198222
199- expect ( fs . writeTextFile ) . toHaveBeenCalledWith (
200- expectedPath ,
201- expect . any ( String ) ,
202- ) ;
223+ expect ( fs . writeTextFile ) . not . toHaveBeenCalled ( ) ;
224+ } ) ;
225+
226+ it ( 'shows an error toast when writing the file fails' , async ( ) => {
227+ ( dialog . save as Mock ) . mockResolvedValueOnce (
228+ '/downloads/Test Playlist.json' ,
229+ ) ;
230+ ( fs . writeTextFile as Mock ) . mockRejectedValueOnce (
231+ new Error ( 'Permission denied' ) ,
232+ ) ;
233+
234+ await PlaylistDetailWrapper . mount ( 'test-playlist' ) ;
235+ await PlaylistDetailWrapper . exportJsonOption . click ( ) ;
236+
237+ await vi . waitFor ( ( ) => {
238+ expect ( toastError ) . toHaveBeenCalledWith ( 'Failed to export playlist' , {
239+ description : 'Permission denied' ,
240+ } ) ;
241+ } ) ;
242+ } ) ;
203243
204- const writtenJson = ( fs . writeTextFile as Mock ) . mock . calls [ 0 ] [ 1 ] ;
205- const parsed = JSON . parse ( writtenJson ) ;
206- expect ( parsed . name ) . toBe ( 'Test Playlist' ) ;
207- expect ( parsed . tracks ) . toHaveLength ( 2 ) ;
244+ it ( 'shows an error toast when the save dialog fails' , async ( ) => {
245+ ( dialog . save as Mock ) . mockRejectedValueOnce (
246+ new Error ( 'Dialog unavailable' ) ,
247+ ) ;
248+
249+ await PlaylistDetailWrapper . mount ( 'test-playlist' ) ;
250+ await PlaylistDetailWrapper . exportJsonOption . click ( ) ;
251+
252+ await vi . waitFor ( ( ) => {
253+ expect ( toastError ) . toHaveBeenCalledWith ( 'Failed to export playlist' , {
254+ description : 'Dialog unavailable' ,
255+ } ) ;
256+ } ) ;
257+ } ) ;
208258 } ) ;
209259} ) ;
0 commit comments