@@ -6,6 +6,7 @@ import fs from 'fs';
66import { normalize } from 'path' ;
77import * as Sentry from '@sentry/electron/main' ;
88import { bumpStat } from '@studio/common/lib/bump-stat' ;
9+ import { recursiveCopyDirectory } from '@studio/common/lib/fs-utils' ;
910import { StatsGroup , StatsMetric } from '@studio/common/types/stats' ;
1011import { readFile } from 'atomically' ;
1112import { vi } from 'vitest' ;
@@ -14,12 +15,16 @@ import {
1415 isFullscreen ,
1516 importSite ,
1617 getXdebugEnabledSite ,
18+ copySite ,
1719 loadThemeDetails ,
1820} from 'src/ipc-handlers' ;
1921import { importBackup , defaultImporterOptions } from 'src/lib/import-export/import/import-manager' ;
2022import { BackupArchiveInfo } from 'src/lib/import-export/import/types' ;
2123import { getMainWindow } from 'src/main-window' ;
2224import { SiteServer } from 'src/site-server' ;
25+ import { resolveDefaultSiteDirectory } from 'src/storage/paths' ;
26+ import { loadUserData } from 'src/storage/user-data' ;
27+ import type { UserData } from 'src/storage/storage-types' ;
2328
2429vi . mock ( 'fs' ) ;
2530vi . mock ( 'fs-extra' ) ;
@@ -37,7 +42,14 @@ vi.mock( 'src/storage/paths', () => ( {
3742 getCliPath : vi . fn ( ) . mockReturnValue ( '/mock/cli/path' ) ,
3843 getBundledNodeBinaryPath : vi . fn ( ) . mockReturnValue ( '/mock/node/binary' ) ,
3944 getSiteThumbnailPath : vi . fn ( ) . mockReturnValue ( '/mock/thumbnail.png' ) ,
40- DEFAULT_SITE_PATH : '/mock/default/site/path' ,
45+ resolveDefaultSiteDirectory : vi . fn ( ) . mockResolvedValue ( '/mock/default/site/path' ) ,
46+ } ) ) ;
47+ vi . mock ( 'src/storage/user-data' , ( ) => ( {
48+ loadUserData : vi . fn ( ) . mockResolvedValue ( { sites : [ ] } ) ,
49+ saveUserData : vi . fn ( ) . mockResolvedValue ( undefined ) ,
50+ lockAppdata : vi . fn ( ) . mockResolvedValue ( undefined ) ,
51+ unlockAppdata : vi . fn ( ) . mockResolvedValue ( undefined ) ,
52+ updateAppdata : vi . fn ( ) . mockResolvedValue ( undefined ) ,
4153} ) ) ;
4254vi . mock ( 'src/site-server' ) ;
4355vi . mock ( 'src/lib/wordpress-setup' , ( ) => ( {
@@ -266,17 +278,69 @@ describe( 'importSite', () => {
266278 } ) ;
267279} ) ;
268280
281+ describe ( 'copySite' , ( ) => {
282+ it ( 'uses the resolved default site directory for the new path' , async ( ) => {
283+ const sourceSite = {
284+ details : {
285+ id : 'source-site-id' ,
286+ name : 'Source Site' ,
287+ path : '/source/path' ,
288+ port : 9998 ,
289+ phpVersion : '8.3' ,
290+ running : false ,
291+ adminPassword : 'source-password' ,
292+ themeDetails : { name : 'Theme' , path : '/themes/theme' } ,
293+ } ,
294+ } ;
295+
296+ vi . mocked ( SiteServer . get , { partial : true } ) . mockReturnValue (
297+ sourceSite as unknown as SiteServer
298+ ) ;
299+ vi . mocked ( recursiveCopyDirectory ) . mockResolvedValue ( undefined ) ;
300+ vi . mocked ( resolveDefaultSiteDirectory ) . mockResolvedValue ( '/mock/default/site/path' ) ;
301+ vi . mocked ( fs . existsSync ) . mockReturnValue ( false ) ;
302+
303+ const result = await copySite (
304+ mockIpcMainInvokeEvent ,
305+ 'source-site-id' ,
306+ 'new-site-id' ,
307+ 'MySite'
308+ ) ;
309+
310+ expect ( resolveDefaultSiteDirectory ) . toHaveBeenCalled ( ) ;
311+ expect ( recursiveCopyDirectory ) . toHaveBeenCalledWith (
312+ sourceSite . details . path ,
313+ '/mock/default/site/path/mysite'
314+ ) ;
315+ expect ( result . path ) . toBe ( '/mock/default/site/path/mysite' ) ;
316+ } ) ;
317+ } ) ;
318+
269319describe ( 'getXdebugEnabledSite' , ( ) => {
270320 it ( 'should return null when no site has Xdebug enabled' , async ( ) => {
271- const mockUserDataWithoutXdebug = {
321+ const mockUserDataWithoutXdebug : UserData = {
272322 sites : [
273- { id : 'site-1' , name : 'Site 1' , path : '/path/to/site-1' , enableXdebug : false } ,
274- { id : 'site-2' , name : 'Site 2' , path : '/path/to/site-2' } ,
323+ {
324+ id : 'site-1' ,
325+ name : 'Site 1' ,
326+ path : '/path/to/site-1' ,
327+ enableXdebug : false ,
328+ running : false as const ,
329+ port : 9999 ,
330+ phpVersion : '8.3' ,
331+ } ,
332+ {
333+ id : 'site-2' ,
334+ name : 'Site 2' ,
335+ path : '/path/to/site-2' ,
336+ running : false as const ,
337+ port : 9999 ,
338+ phpVersion : '8.3' ,
339+ } ,
275340 ] ,
341+ snapshots : [ ] ,
276342 } ;
277- vi . mocked ( readFile ) . mockResolvedValue (
278- Buffer . from ( JSON . stringify ( mockUserDataWithoutXdebug ) )
279- ) ;
343+ vi . mocked ( loadUserData ) . mockResolvedValue ( mockUserDataWithoutXdebug ) ;
280344 vi . mocked ( fs . existsSync ) . mockReturnValue ( true ) ;
281345
282346 const result = await getXdebugEnabledSite ( mockIpcMainInvokeEvent ) ;
@@ -285,15 +349,31 @@ describe( 'getXdebugEnabledSite', () => {
285349 } ) ;
286350
287351 it ( 'should return the site that has Xdebug enabled' , async ( ) => {
288- const mockUserDataWithXdebug = {
352+ const mockUserDataWithXdebug : UserData = {
289353 sites : [
290- { id : 'site-1' , name : 'Site 1' , path : '/path/to/site-1' , enableXdebug : false } ,
291- { id : 'site-2' , name : 'Site 2' , path : '/path/to/site-2' , enableXdebug : true } ,
354+ {
355+ id : 'site-1' ,
356+ name : 'Site 1' ,
357+ path : '/path/to/site-1' ,
358+ enableXdebug : false ,
359+ running : false as const ,
360+ port : 9999 ,
361+ phpVersion : '8.3' ,
362+ } ,
363+ {
364+ id : 'site-2' ,
365+ name : 'Site 2' ,
366+ path : '/path/to/site-2' ,
367+ enableXdebug : true ,
368+ running : true as const ,
369+ port : 9999 ,
370+ phpVersion : '8.3' ,
371+ url : 'https://site-2.test' ,
372+ } ,
292373 ] ,
374+ snapshots : [ ] ,
293375 } ;
294- vi . mocked ( readFile ) . mockResolvedValue (
295- Buffer . from ( JSON . stringify ( mockUserDataWithXdebug ) )
296- ) ;
376+ vi . mocked ( loadUserData ) . mockResolvedValue ( mockUserDataWithXdebug ) ;
297377 vi . mocked ( fs . existsSync ) . mockReturnValue ( true ) ;
298378 vi . mocked ( SiteServer . get , { partial : true } ) . mockReturnValue ( {
299379 details : {
@@ -323,15 +403,30 @@ describe( 'getXdebugEnabledSite', () => {
323403 } ) ;
324404
325405 it ( 'should return the first site when multiple have Xdebug enabled' , async ( ) => {
326- const mockUserDataWithMultipleXdebug = {
406+ const mockUserDataWithMultipleXdebug : UserData = {
327407 sites : [
328- { id : 'site-1' , name : 'Site 1' , path : '/path/to/site-1' , enableXdebug : true } ,
329- { id : 'site-2' , name : 'Site 2' , path : '/path/to/site-2' , enableXdebug : true } ,
408+ {
409+ id : 'site-1' ,
410+ name : 'Site 1' ,
411+ path : '/path/to/site-1' ,
412+ enableXdebug : true ,
413+ running : false as const ,
414+ port : 9999 ,
415+ phpVersion : '8.3' ,
416+ } ,
417+ {
418+ id : 'site-2' ,
419+ name : 'Site 2' ,
420+ path : '/path/to/site-2' ,
421+ enableXdebug : true ,
422+ running : false as const ,
423+ port : 9999 ,
424+ phpVersion : '8.3' ,
425+ } ,
330426 ] ,
427+ snapshots : [ ] ,
331428 } ;
332- vi . mocked ( readFile ) . mockResolvedValue (
333- Buffer . from ( JSON . stringify ( mockUserDataWithMultipleXdebug ) )
334- ) ;
429+ vi . mocked ( loadUserData ) . mockResolvedValue ( mockUserDataWithMultipleXdebug ) ;
335430 vi . mocked ( fs . existsSync ) . mockReturnValue ( true ) ;
336431 vi . mocked ( SiteServer . get , { partial : true } ) . mockReturnValue ( {
337432 details : {
0 commit comments