11import { copySourceEntry } from './copy-source-entry.js'
22import { describe , expect , test , vi , beforeEach } from 'vitest'
3- import * as fs from '@shopify/cli-kit/node/fs'
4-
5- vi . mock ( '@shopify/cli-kit/node/fs' )
3+ import { inTemporaryDirectory , writeFile , mkdir , fileExistsSync , readFile } from '@shopify/cli-kit/node/fs'
4+ import { joinPath } from '@shopify/cli-kit/node/path'
65
76describe ( 'copySourceEntry' , ( ) => {
87 let mockStdout : any
@@ -12,149 +11,206 @@ describe('copySourceEntry', () => {
1211 } )
1312
1413 test ( 'throws when source path does not exist' , async ( ) => {
15- // Given
16- vi . mocked ( fs . fileExists ) . mockResolvedValue ( false )
14+ await inTemporaryDirectory ( async ( tmpDir ) => {
15+ // Given
16+ const baseDir = joinPath ( tmpDir , 'ext' )
17+ const outputDir = joinPath ( tmpDir , 'out' )
18+ const appDirectory = tmpDir
19+ await mkdir ( baseDir )
20+ await mkdir ( outputDir )
21+
22+ // When / Then
23+ await expect (
24+ copySourceEntry (
25+ {
26+ source : 'missing/file.js' ,
27+ destination : undefined ,
28+ baseDir,
29+ outputDir,
30+ appDirectory,
31+ } ,
32+ { stdout : mockStdout } ,
33+ ) ,
34+ ) . rejects . toThrow ( `Source does not exist: ${ joinPath ( baseDir , 'missing/file.js' ) } ` )
35+ } )
36+ } )
1737
18- // When / Then
19- await expect (
20- copySourceEntry (
38+ test ( 'copies file to explicit destination path' , async ( ) => {
39+ await inTemporaryDirectory ( async ( tmpDir ) => {
40+ // Given
41+ const baseDir = joinPath ( tmpDir , 'ext' )
42+ const outputDir = joinPath ( tmpDir , 'out' )
43+ const appDirectory = tmpDir
44+ await mkdir ( baseDir )
45+ await mkdir ( outputDir )
46+ const sourceFile = joinPath ( baseDir , 'src/icon.png' )
47+ await mkdir ( joinPath ( baseDir , 'src' ) )
48+ await writeFile ( sourceFile , 'icon-content' )
49+
50+ // When
51+ const result = await copySourceEntry (
2152 {
22- source : 'missing/file.js ' ,
23- destination : undefined ,
24- baseDir : '/ext' ,
25- outputDir : '/out' ,
26- appDirectory : '/ext' ,
53+ source : 'src/icon.png ' ,
54+ destination : 'assets/icon.png' ,
55+ baseDir,
56+ outputDir,
57+ appDirectory,
2758 } ,
2859 { stdout : mockStdout } ,
29- ) ,
30- ) . rejects . toThrow ( 'Source does not exist: /ext/missing/file.js' )
31- } )
32-
33- test ( 'copies file to explicit destination path' , async ( ) => {
34- // Given
35- vi . mocked ( fs . fileExists ) . mockResolvedValue ( true )
36- vi . mocked ( fs . isDirectory ) . mockResolvedValue ( false )
37- vi . mocked ( fs . mkdir ) . mockResolvedValue ( )
38- vi . mocked ( fs . copyFile ) . mockResolvedValue ( )
39-
40- // When
41- const result = await copySourceEntry (
42- {
43- source : 'src/icon.png' ,
44- destination : 'assets/icon.png' ,
45- baseDir : '/ext' ,
46- outputDir : '/out' ,
47- appDirectory : '/ext' ,
48- } ,
49- { stdout : mockStdout } ,
50- )
51-
52- // Then
53- expect ( fs . copyFile ) . toHaveBeenCalledWith ( '/ext/src/icon.png' , '/out/assets/icon.png' )
54- expect ( result . filesCopied ) . toBe ( 1 )
55- expect ( result . outputPaths ) . toEqual ( [ 'assets/icon.png' ] )
56- expect ( mockStdout . write ) . toHaveBeenCalledWith ( 'Included src/icon.png\n' )
60+ )
61+
62+ // Then
63+ const expectedDest = joinPath ( outputDir , 'assets/icon.png' )
64+ expect ( fileExistsSync ( expectedDest ) ) . toBe ( true )
65+ await expect ( readFile ( expectedDest ) ) . resolves . toBe ( 'icon-content' )
66+ expect ( result . filesCopied ) . toBe ( 1 )
67+ expect ( result . outputPaths ) . toEqual ( [ 'assets/icon.png' ] )
68+ expect ( mockStdout . write ) . toHaveBeenCalledWith ( 'Included src/icon.png\n' )
69+ } )
5770 } )
5871
5972 test ( 'copies directory under its own name when no destination is given' , async ( ) => {
60- // Given
61- vi . mocked ( fs . fileExists ) . mockResolvedValue ( true )
62- vi . mocked ( fs . isDirectory ) . mockResolvedValue ( true )
63- vi . mocked ( fs . copyDirectoryContents ) . mockResolvedValue ( )
64- vi . mocked ( fs . glob ) . mockResolvedValue ( [ 'index.html' , 'logo.png' ] )
65-
66- // When
67- const result = await copySourceEntry (
68- { source : 'dist' , destination : undefined , baseDir : '/ext' , outputDir : '/out' , appDirectory : '/ext' } ,
69- { stdout : mockStdout } ,
70- )
71-
72- // Then
73- expect ( fs . copyDirectoryContents ) . toHaveBeenCalledWith ( '/ext/dist' , '/out/dist' )
74- expect ( result . filesCopied ) . toBe ( 2 )
75- expect ( result . outputPaths ) . toEqual ( [ 'dist/index.html' , 'dist/logo.png' ] )
76- expect ( mockStdout . write ) . toHaveBeenCalledWith ( 'Included dist\n' )
73+ await inTemporaryDirectory ( async ( tmpDir ) => {
74+ // Given
75+ const baseDir = joinPath ( tmpDir , 'ext' )
76+ const outputDir = joinPath ( tmpDir , 'out' )
77+ const appDirectory = tmpDir
78+ await mkdir ( baseDir )
79+ await mkdir ( outputDir )
80+
81+ const distDir = joinPath ( baseDir , 'dist' )
82+ await mkdir ( distDir )
83+ await writeFile ( joinPath ( distDir , 'index.html' ) , 'html' )
84+ await writeFile ( joinPath ( distDir , 'logo.png' ) , 'logo' )
85+
86+ // When
87+ const result = await copySourceEntry (
88+ { source : 'dist' , destination : undefined , baseDir, outputDir, appDirectory} ,
89+ { stdout : mockStdout } ,
90+ )
91+
92+ // Then
93+ expect ( fileExistsSync ( joinPath ( outputDir , 'dist/index.html' ) ) ) . toBe ( true )
94+ expect ( fileExistsSync ( joinPath ( outputDir , 'dist/logo.png' ) ) ) . toBe ( true )
95+ expect ( result . filesCopied ) . toBe ( 2 )
96+ expect ( result . outputPaths ) . toHaveLength ( 2 )
97+ expect ( result . outputPaths ) . toEqual ( expect . arrayContaining ( [ 'dist/index.html' , 'dist/logo.png' ] ) )
98+ expect ( mockStdout . write ) . toHaveBeenCalledWith ( 'Included dist\n' )
99+ } )
77100 } )
78101
79102 test ( 'copies file to basename in outputDir when source is a file and no destination given' , async ( ) => {
80- // Given
81- vi . mocked ( fs . fileExists ) . mockResolvedValue ( true )
82- vi . mocked ( fs . isDirectory ) . mockResolvedValue ( false )
83- vi . mocked ( fs . mkdir ) . mockResolvedValue ( )
84- vi . mocked ( fs . copyFile ) . mockResolvedValue ( )
85-
86- // When
87- const result = await copySourceEntry (
88- { source : 'README.md' , destination : undefined , baseDir : '/ext' , outputDir : '/out' , appDirectory : '/ext' } ,
89- { stdout : mockStdout } ,
90- )
91-
92- // Then
93- expect ( fs . copyFile ) . toHaveBeenCalledWith ( '/ext/README.md' , '/out/README.md' )
94- expect ( result . filesCopied ) . toBe ( 1 )
95- expect ( result . outputPaths ) . toEqual ( [ 'README.md' ] )
96- expect ( mockStdout . write ) . toHaveBeenCalledWith ( 'Included README.md\n' )
103+ await inTemporaryDirectory ( async ( tmpDir ) => {
104+ // Given
105+ const baseDir = joinPath ( tmpDir , 'ext' )
106+ const outputDir = joinPath ( tmpDir , 'out' )
107+ const appDirectory = tmpDir
108+ await mkdir ( baseDir )
109+ await mkdir ( outputDir )
110+ await writeFile ( joinPath ( baseDir , 'README.md' ) , 'readme' )
111+
112+ // When
113+ const result = await copySourceEntry (
114+ { source : 'README.md' , destination : undefined , baseDir, outputDir, appDirectory} ,
115+ { stdout : mockStdout } ,
116+ )
117+
118+ // Then
119+ const expectedDest = joinPath ( outputDir , 'README.md' )
120+ expect ( fileExistsSync ( expectedDest ) ) . toBe ( true )
121+ await expect ( readFile ( expectedDest ) ) . resolves . toBe ( 'readme' )
122+ expect ( result . filesCopied ) . toBe ( 1 )
123+ expect ( result . outputPaths ) . toEqual ( [ 'README.md' ] )
124+ expect ( mockStdout . write ) . toHaveBeenCalledWith ( 'Included README.md\n' )
125+ } )
97126 } )
98127
99128 test ( 'copies directory to explicit destination path' , async ( ) => {
100- // Given
101- vi . mocked ( fs . fileExists ) . mockResolvedValue ( true )
102- vi . mocked ( fs . isDirectory ) . mockResolvedValue ( true )
103- vi . mocked ( fs . copyDirectoryContents ) . mockResolvedValue ( )
104- vi . mocked ( fs . glob ) . mockResolvedValue ( [ 'x.js' ] )
105-
106- // When
107- const result = await copySourceEntry (
108- { source : 'dist' , destination : 'vendor/dist' , baseDir : '/ext' , outputDir : '/out' , appDirectory : '/ext' } ,
109- { stdout : mockStdout } ,
110- )
111-
112- // Then
113- expect ( fs . copyDirectoryContents ) . toHaveBeenCalledWith ( '/ext/dist' , '/out/vendor/dist' )
114- expect ( result . filesCopied ) . toBe ( 1 )
115- expect ( result . outputPaths ) . toEqual ( [ 'vendor/dist/x.js' ] )
116- expect ( mockStdout . write ) . toHaveBeenCalledWith ( 'Included dist\n' )
129+ await inTemporaryDirectory ( async ( tmpDir ) => {
130+ // Given
131+ const baseDir = joinPath ( tmpDir , 'ext' )
132+ const outputDir = joinPath ( tmpDir , 'out' )
133+ const appDirectory = tmpDir
134+ await mkdir ( baseDir )
135+ await mkdir ( outputDir )
136+
137+ const distDir = joinPath ( baseDir , 'dist' )
138+ await mkdir ( distDir )
139+ await writeFile ( joinPath ( distDir , 'x.js' ) , 'js' )
140+
141+ // When
142+ const result = await copySourceEntry (
143+ { source : 'dist' , destination : 'vendor/dist' , baseDir, outputDir, appDirectory} ,
144+ { stdout : mockStdout } ,
145+ )
146+
147+ // Then
148+ expect ( fileExistsSync ( joinPath ( outputDir , 'vendor/dist/x.js' ) ) ) . toBe ( true )
149+ expect ( result . filesCopied ) . toBe ( 1 )
150+ expect ( result . outputPaths ) . toEqual ( [ 'vendor/dist/x.js' ] )
151+ expect ( mockStdout . write ) . toHaveBeenCalledWith ( 'Included dist\n' )
152+ } )
117153 } )
118154
119155 test ( 'returns count of files discovered in destination directory after directory copy' , async ( ) => {
120- // Given
121- vi . mocked ( fs . fileExists ) . mockResolvedValue ( true )
122- vi . mocked ( fs . isDirectory ) . mockResolvedValue ( true )
123- vi . mocked ( fs . copyDirectoryContents ) . mockResolvedValue ( )
124- // Simulate 5 files inside the copied directory
125- vi . mocked ( fs . glob ) . mockResolvedValue ( [ 'a.js' , 'b.js' , 'c.js' , 'd.js' , 'e.js' ] )
126-
127- // When
128- const result = await copySourceEntry (
129- { source : 'theme' , destination : undefined , baseDir : '/ext' , outputDir : '/out' , appDirectory : '/ext' } ,
130- { stdout : mockStdout } ,
131- )
132-
133- // Then — count comes from glob on destPath, not a constant
134- expect ( result . filesCopied ) . toBe ( 5 )
135- expect ( result . outputPaths ) . toEqual ( [ 'theme/a.js' , 'theme/b.js' , 'theme/c.js' , 'theme/d.js' , 'theme/e.js' ] )
156+ await inTemporaryDirectory ( async ( tmpDir ) => {
157+ // Given
158+ const baseDir = joinPath ( tmpDir , 'ext' )
159+ const outputDir = joinPath ( tmpDir , 'out' )
160+ const appDirectory = tmpDir
161+ await mkdir ( baseDir )
162+ await mkdir ( outputDir )
163+
164+ const themeDir = joinPath ( baseDir , 'theme' )
165+ await mkdir ( themeDir )
166+ await writeFile ( joinPath ( themeDir , 'a.js' ) , 'a' )
167+ await writeFile ( joinPath ( themeDir , 'b.js' ) , 'b' )
168+ await writeFile ( joinPath ( themeDir , 'c.js' ) , 'c' )
169+ await writeFile ( joinPath ( themeDir , 'd.js' ) , 'd' )
170+ await writeFile ( joinPath ( themeDir , 'e.js' ) , 'e' )
171+
172+ // When
173+ const result = await copySourceEntry (
174+ { source : 'theme' , destination : undefined , baseDir, outputDir, appDirectory} ,
175+ { stdout : mockStdout } ,
176+ )
177+
178+ // Then
179+ expect ( result . filesCopied ) . toBe ( 5 )
180+ expect ( result . outputPaths ) . toHaveLength ( 5 )
181+ expect ( result . outputPaths ) . toEqual (
182+ expect . arrayContaining ( [ 'theme/a.js' , 'theme/b.js' , 'theme/c.js' , 'theme/d.js' , 'theme/e.js' ] ) ,
183+ )
184+ } )
136185 } )
137186
138187 test ( 'creates parent directories before copying a file' , async ( ) => {
139- // Given
140- vi . mocked ( fs . fileExists ) . mockResolvedValue ( true )
141- vi . mocked ( fs . isDirectory ) . mockResolvedValue ( false )
142- vi . mocked ( fs . mkdir ) . mockResolvedValue ( )
143- vi . mocked ( fs . copyFile ) . mockResolvedValue ( )
144-
145- // When
146- await copySourceEntry (
147- {
148- source : 'src/deep/icon.png' ,
149- destination : 'assets/icons/icon.png' ,
150- baseDir : '/ext' ,
151- outputDir : '/out' ,
152- appDirectory : '/ext' ,
153- } ,
154- { stdout : mockStdout } ,
155- )
156-
157- // Then — parent of destination path created
158- expect ( fs . mkdir ) . toHaveBeenCalledWith ( '/out/assets/icons' )
188+ await inTemporaryDirectory ( async ( tmpDir ) => {
189+ // Given
190+ const baseDir = joinPath ( tmpDir , 'ext' )
191+ const outputDir = joinPath ( tmpDir , 'out' )
192+ const appDirectory = tmpDir
193+ await mkdir ( baseDir )
194+ await mkdir ( outputDir )
195+
196+ const deepDir = joinPath ( baseDir , 'src/deep' )
197+ await mkdir ( deepDir )
198+ await writeFile ( joinPath ( deepDir , 'icon.png' ) , 'icon' )
199+
200+ // When
201+ await copySourceEntry (
202+ {
203+ source : 'src/deep/icon.png' ,
204+ destination : 'assets/icons/icon.png' ,
205+ baseDir,
206+ outputDir,
207+ appDirectory,
208+ } ,
209+ { stdout : mockStdout } ,
210+ )
211+
212+ // Then — parent of destination path created
213+ expect ( fileExistsSync ( joinPath ( outputDir , 'assets/icons/icon.png' ) ) ) . toBe ( true )
214+ } )
159215 } )
160216} )
0 commit comments