@@ -167,6 +167,156 @@ func TestCompressSelectedFiles(t *testing.T) {
167
167
})
168
168
}
169
169
170
+ // Helper function to find item index in panel by name
171
+ func findItemIndexInPanel (panel * filePanel , itemName string ) int {
172
+ for i , elem := range panel .element {
173
+ if elem .name == itemName {
174
+ return i
175
+ }
176
+ }
177
+ return - 1
178
+ }
179
+
180
+ // Helper function to setup panel mode and selection
181
+ func setupPanelModeAndSelection (t * testing.T , m * model , useSelectMode bool , itemName string , selectedItems []string ) {
182
+ t .Helper ()
183
+ panel := m .getFocusedFilePanel ()
184
+
185
+ if useSelectMode {
186
+ // Switch to select mode and set selected items
187
+ m .changeFilePanelMode ()
188
+ require .Equal (t , selectMode , panel .panelMode )
189
+ panel .selected = selectedItems
190
+ } else {
191
+ // Find the item in browser mode
192
+ itemIndex := findItemIndexInPanel (panel , itemName )
193
+ require .NotEqual (t , - 1 , itemIndex , "%s should be found in panel" , itemName )
194
+ panel .cursor = itemIndex
195
+ }
196
+ }
197
+
198
+ // Helper function to perform copy or cut operation
199
+ func performCopyOrCutOperation (t * testing.T , m * model , isCut bool ) {
200
+ t .Helper ()
201
+ if isCut {
202
+ TeaUpdateWithErrCheck (t , m , utils .TeaRuneKeyMsg (common .Hotkeys .CutItems [0 ]))
203
+ } else {
204
+ TeaUpdateWithErrCheck (t , m , utils .TeaRuneKeyMsg (common .Hotkeys .CopyItems [0 ]))
205
+ }
206
+ }
207
+
208
+ // Helper function to verify clipboard state after copy/cut
209
+ func verifyClipboardState (t * testing.T , m * model , isCut bool , useSelectMode bool , selectedItemsCount int ) {
210
+ t .Helper ()
211
+ assert .Equal (t , isCut , m .copyItems .cut , "Clipboard cut state should match operation" )
212
+ if useSelectMode {
213
+ assert .Len (t , m .copyItems .items , selectedItemsCount , "Clipboard should contain all selected items" )
214
+ } else {
215
+ assert .Len (t , m .copyItems .items , 1 , "Clipboard should contain one item" )
216
+ }
217
+ }
218
+
219
+ // Helper function to navigate to target directory if different from start
220
+ func navigateToTargetDir (t * testing.T , m * model , startDir , targetDir string ) {
221
+ t .Helper ()
222
+ if targetDir != startDir {
223
+ m .updateCurrentFilePanelDir (targetDir )
224
+ TeaUpdateWithErrCheck (t , m , nil )
225
+ }
226
+ }
227
+
228
+ // Helper function to get original path for existence check
229
+ func getOriginalPath (useSelectMode bool , itemName , startDir string ) string {
230
+ if ! useSelectMode && itemName != "" {
231
+ return filepath .Join (startDir , itemName )
232
+ }
233
+ return ""
234
+ }
235
+
236
+ // Helper function to verify file or directory exists
237
+ func verifyPathExists (t * testing.T , path , message string ) {
238
+ t .Helper ()
239
+ if strings .Contains (path , ".txt" ) {
240
+ assert .FileExists (t , path , message )
241
+ } else {
242
+ assert .DirExists (t , path , message )
243
+ }
244
+ }
245
+
246
+ // Helper function to verify file or directory doesn't exist after cut
247
+ func verifyPathNotExistsEventually (t * testing.T , path , message string ) {
248
+ t .Helper ()
249
+ assert .Eventually (t , func () bool {
250
+ _ , err := os .Stat (path )
251
+ return os .IsNotExist (err )
252
+ }, time .Second , 10 * time .Millisecond , message )
253
+ }
254
+
255
+ // Helper function to verify expected destination files exist
256
+ func verifyDestinationFiles (t * testing.T , targetDir string , expectedDestFiles []string ) {
257
+ t .Helper ()
258
+ for _ , expectedFile := range expectedDestFiles {
259
+ destPath := filepath .Join (targetDir , expectedFile )
260
+ assert .Eventually (t , func () bool {
261
+ _ , err := os .Stat (destPath )
262
+ return err == nil
263
+ }, time .Second , 10 * time .Millisecond , "%s should exist in destination" , expectedFile )
264
+ }
265
+ }
266
+
267
+ // Helper function to verify prevented paste results
268
+ func verifyPreventedPasteResults (t * testing.T , m * model , originalPath string ) {
269
+ t .Helper ()
270
+ if originalPath != "" {
271
+ verifyPathExists (t , originalPath , "Original file should still exist when paste is prevented" )
272
+ }
273
+ // Clipboard should not be cleared when paste is prevented
274
+ assert .NotEqual (t , 0 , len (m .copyItems .items ), "Clipboard should not be cleared when paste is prevented" )
275
+ }
276
+
277
+ // Helper function to verify successful paste results
278
+ func verifySuccessfulPasteResults (t * testing.T , targetDir string , expectedDestFiles []string , originalPath string , shouldOriginalExist bool , shouldClipboardClear bool , m * model ) {
279
+ t .Helper ()
280
+ // Verify expected files were created in destination
281
+ verifyDestinationFiles (t , targetDir , expectedDestFiles )
282
+
283
+ // Verify original file existence based on operation type
284
+ if originalPath != "" {
285
+ if shouldOriginalExist {
286
+ verifyPathExists (t , originalPath , "Original file should exist after copy operation" )
287
+ } else {
288
+ verifyPathNotExistsEventually (t , originalPath , "Original file should not exist after cut operation" )
289
+ }
290
+ }
291
+
292
+ // Verify clipboard state after paste
293
+ if shouldClipboardClear {
294
+ assert .Eventually (t , func () bool {
295
+ return len (m .copyItems .items ) == 0
296
+ }, time .Second , 10 * time .Millisecond , "Clipboard should be cleared after cut operation" )
297
+ } else {
298
+ assert .NotEqual (t , 0 , len (m .copyItems .items ), "Clipboard should not be cleared after copy operation" )
299
+ }
300
+ }
301
+
302
+ // Helper function to setup model and perform copy/cut operation
303
+ func setupModelAndPerformOperation (t * testing.T , startDir string , useSelectMode bool , itemName string , selectedItems []string , isCut bool ) * model {
304
+ t .Helper ()
305
+ m := defaultTestModel (startDir )
306
+ TeaUpdateWithErrCheck (t , & m , nil )
307
+
308
+ setupPanelModeAndSelection (t , & m , useSelectMode , itemName , selectedItems )
309
+ performCopyOrCutOperation (t , & m , isCut )
310
+
311
+ selectedItemsCount := len (selectedItems )
312
+ if ! useSelectMode {
313
+ selectedItemsCount = 1
314
+ }
315
+ verifyClipboardState (t , & m , isCut , useSelectMode , selectedItemsCount )
316
+
317
+ return & m
318
+ }
319
+
170
320
func TestPasteItem (t * testing.T ) {
171
321
curTestDir := t .TempDir ()
172
322
sourceDir := filepath .Join (curTestDir , "source" )
@@ -239,95 +389,22 @@ func TestPasteItem(t *testing.T) {
239
389
240
390
for _ , tt := range testdata {
241
391
t .Run (tt .name , func (t * testing.T ) {
242
- m := defaultTestModel (tt .startDir )
243
- TeaUpdateWithErrCheck (t , & m , nil )
244
-
245
- panel := m .getFocusedFilePanel ()
246
-
247
- if tt .selectMode {
248
- // Switch to select mode and set selected items
249
- m .changeFilePanelMode ()
250
- require .Equal (t , selectMode , panel .panelMode )
251
- panel .selected = tt .selectedItems
252
- } else {
253
- // Find the item in browser mode
254
- itemIndex := - 1
255
- for i , elem := range panel .element {
256
- if elem .name == tt .itemName {
257
- itemIndex = i
258
- break
259
- }
260
- }
261
- require .NotEqual (t , - 1 , itemIndex , "%s should be found in panel" , tt .itemName )
262
- panel .cursor = itemIndex
263
- }
264
-
265
- // Perform copy or cut operation
266
- if tt .isCut {
267
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .CutItems [0 ]))
268
- } else {
269
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .CopyItems [0 ]))
270
- }
271
-
272
- // Verify clipboard state after copy/cut
273
- assert .Equal (t , tt .isCut , m .copyItems .cut , "Clipboard cut state should match operation" )
274
- if tt .selectMode {
275
- assert .Len (t , m .copyItems .items , len (tt .selectedItems ), "Clipboard should contain all selected items" )
276
- } else {
277
- assert .Len (t , m .copyItems .items , 1 , "Clipboard should contain one item" )
278
- }
392
+ m := setupModelAndPerformOperation (t , tt .startDir , tt .selectMode , tt .itemName , tt .selectedItems , tt .isCut )
279
393
280
394
// Navigate to target directory
281
- if tt .targetDir != tt .startDir {
282
- m .updateCurrentFilePanelDir (tt .targetDir )
283
- TeaUpdateWithErrCheck (t , & m , nil )
284
- }
395
+ navigateToTargetDir (t , m , tt .startDir , tt .targetDir )
285
396
286
397
// Get original file path for existence check
287
- var originalPath string
288
- if ! tt .selectMode && tt .itemName != "" {
289
- originalPath = filepath .Join (tt .startDir , tt .itemName )
290
- }
398
+ originalPath := getOriginalPath (tt .selectMode , tt .itemName , tt .startDir )
291
399
292
400
// Perform paste operation
293
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
401
+ TeaUpdateWithErrCheck (t , m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
294
402
403
+ // Verify results based on whether paste should be prevented
295
404
if tt .shouldPreventPaste {
296
- // Verify that paste was prevented (original should still exist, clipboard not cleared)
297
- if originalPath != "" {
298
- if strings .Contains (originalPath , ".txt" ) {
299
- assert .FileExists (t , originalPath , "Original file should still exist when paste is prevented" )
300
- } else {
301
- assert .DirExists (t , originalPath , "Original directory should still exist when paste is prevented" )
302
- }
303
- }
304
- // Clipboard should not be cleared when paste is prevented
305
- assert .NotEqual (t , 0 , len (m .copyItems .items ), "Clipboard should not be cleared when paste is prevented" )
405
+ verifyPreventedPasteResults (t , m , originalPath )
306
406
} else {
307
- // Verify expected files were created in destination
308
- for _ , expectedFile := range tt .expectedDestFiles {
309
- destPath := filepath .Join (tt .targetDir , expectedFile )
310
- assert .Eventually (t , func () bool {
311
- _ , err := os .Stat (destPath )
312
- return err == nil
313
- }, time .Second , 10 * time .Millisecond , "%s should exist in destination" , expectedFile )
314
- }
315
-
316
- // Verify original file existence based on operation type
317
- if originalPath != "" {
318
- if tt .shouldOriginalExist {
319
- if strings .Contains (originalPath , ".txt" ) {
320
- assert .FileExists (t , originalPath , "Original file should exist after copy operation" )
321
- } else {
322
- assert .DirExists (t , originalPath , "Original directory should exist after copy operation" )
323
- }
324
- } else {
325
- assert .Eventually (t , func () bool {
326
- _ , err := os .Stat (originalPath )
327
- return os .IsNotExist (err )
328
- }, time .Second , 10 * time .Millisecond , "Original file should not exist after cut operation" )
329
- }
330
- }
407
+ verifySuccessfulPasteResults (t , tt .targetDir , tt .expectedDestFiles , originalPath , tt .shouldOriginalExist , tt .shouldClipboardClear , m )
331
408
}
332
409
})
333
410
}
@@ -363,40 +440,18 @@ func TestPasteItem(t *testing.T) {
363
440
multiFile2 := filepath .Join (sourceDir , "multi2.txt" )
364
441
setupFiles (t , multiFile1 , multiFile2 )
365
442
366
- m := defaultTestModel (sourceDir )
367
- TeaUpdateWithErrCheck (t , & m , nil )
368
-
369
- // Switch to select mode
370
- m .changeFilePanelMode ()
371
- require .Equal (t , selectMode , m .getFocusedFilePanel ().panelMode )
372
-
373
- // Select multiple files
374
- panel := m .getFocusedFilePanel ()
375
- panel .selected = []string {multiFile1 , multiFile2 }
376
-
377
- // Copy selected items
378
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .CopyItems [0 ]))
379
-
380
- // Verify clipboard
381
- assert .False (t , m .copyItems .cut , "Should be copy operation" )
382
- assert .Len (t , m .copyItems .items , 2 , "Should have two items in clipboard" )
443
+ selectedItems := []string {multiFile1 , multiFile2 }
444
+ m := setupModelAndPerformOperation (t , sourceDir , true , "" , selectedItems , false )
383
445
384
446
// Navigate to destination
385
- m .updateCurrentFilePanelDir (destDir )
386
- TeaUpdateWithErrCheck (t , & m , nil )
447
+ navigateToTargetDir (t , m , sourceDir , destDir )
387
448
388
449
// Paste items
389
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
450
+ TeaUpdateWithErrCheck (t , m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
390
451
391
452
// Verify both files were copied
392
- destMulti1 := filepath .Join (destDir , "multi1.txt" )
393
- destMulti2 := filepath .Join (destDir , "multi2.txt" )
394
-
395
- assert .Eventually (t , func () bool {
396
- _ , err1 := os .Stat (destMulti1 )
397
- _ , err2 := os .Stat (destMulti2 )
398
- return err1 == nil && err2 == nil
399
- }, time .Second , 10 * time .Millisecond , "Both files should be copied to destination" )
453
+ expectedDestFiles := []string {"multi1.txt" , "multi2.txt" }
454
+ verifyDestinationFiles (t , destDir , expectedDestFiles )
400
455
})
401
456
402
457
t .Run ("Cut into Subdirectory Prevention" , func (t * testing.T ) {
@@ -407,28 +462,11 @@ func TestPasteItem(t *testing.T) {
407
462
setupFiles (t , testDirFile )
408
463
409
464
// Test the logic that prevents cutting a directory into its subdirectory
410
- m := defaultTestModel (sourceDir )
411
- TeaUpdateWithErrCheck (t , & m , nil )
412
-
413
- // Find testsubdir and cut it
414
- panel := m .getFocusedFilePanel ()
415
- subdirIndex := - 1
416
- for i , elem := range panel .element {
417
- if elem .name == "testsubdir" {
418
- subdirIndex = i
419
- break
420
- }
421
- }
422
- require .NotEqual (t , - 1 , subdirIndex , "testsubdir should be found in panel" )
423
- panel .cursor = subdirIndex
424
-
425
- // Cut the directory
426
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .CutItems [0 ]))
465
+ m := setupModelAndPerformOperation (t , sourceDir , false , "testsubdir" , nil , true )
427
466
428
467
// Navigate into the subdirectory and try to paste there (should be prevented)
429
- m .updateCurrentFilePanelDir (testSubDir )
430
- TeaUpdateWithErrCheck (t , & m , nil )
431
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
468
+ navigateToTargetDir (t , m , sourceDir , testSubDir )
469
+ TeaUpdateWithErrCheck (t , m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
432
470
433
471
// Directory should still exist in original location after prevention
434
472
assert .DirExists (t , testSubDir , "Directory should still exist after failed paste into subdirectory" )
@@ -439,43 +477,19 @@ func TestPasteItem(t *testing.T) {
439
477
dupFile := filepath .Join (sourceDir , "duplicate.txt" )
440
478
setupFiles (t , dupFile )
441
479
442
- m := defaultTestModel (sourceDir )
443
- TeaUpdateWithErrCheck (t , & m , nil )
444
-
445
- // Find and copy the file
446
- panel := m .getFocusedFilePanel ()
447
- dupIndex := - 1
448
- for i , elem := range panel .element {
449
- if elem .name == "duplicate.txt" {
450
- dupIndex = i
451
- break
452
- }
453
- }
454
- require .NotEqual (t , - 1 , dupIndex , "duplicate.txt should be found in panel" )
455
- panel .cursor = dupIndex
456
-
457
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .CopyItems [0 ]))
480
+ m := setupModelAndPerformOperation (t , sourceDir , false , "duplicate.txt" , nil , false )
458
481
459
482
// Navigate to destination and paste
460
- m .updateCurrentFilePanelDir (destDir )
461
- TeaUpdateWithErrCheck (t , & m , nil )
462
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
483
+ navigateToTargetDir (t , m , sourceDir , destDir )
484
+ TeaUpdateWithErrCheck (t , m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
463
485
464
486
// Verify first copy
465
- destDup1 := filepath .Join (destDir , "duplicate.txt" )
466
- assert .Eventually (t , func () bool {
467
- _ , err := os .Stat (destDup1 )
468
- return err == nil
469
- }, time .Second , 10 * time .Millisecond , "First copy should succeed" )
487
+ verifyDestinationFiles (t , destDir , []string {"duplicate.txt" })
470
488
471
489
// Paste again to test duplicate handling
472
- TeaUpdateWithErrCheck (t , & m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
490
+ TeaUpdateWithErrCheck (t , m , utils .TeaRuneKeyMsg (common .Hotkeys .PasteItems [0 ]))
473
491
474
492
// Verify duplicate file with different name
475
- destDup2 := filepath .Join (destDir , "duplicate(1).txt" )
476
- assert .Eventually (t , func () bool {
477
- _ , err := os .Stat (destDup2 )
478
- return err == nil
479
- }, time .Second , 10 * time .Millisecond , "Duplicate file should be created with (1) suffix" )
493
+ verifyDestinationFiles (t , destDir , []string {"duplicate(1).txt" })
480
494
})
481
495
}
0 commit comments