@garyr/pt-cli 0.30.1 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/dist/commands/learnCommand.js +66 -51
- package/doc/testing.md +48 -0
- package/package.json +2 -1
- package/src/commands/learnCommand.ts +65 -53
- package/tests/config-utils.test.ts +473 -0
- package/tests/config.test.ts +36 -6
- package/tests/init.test.ts +428 -14
- package/tests/learn.test.ts +838 -0
- package/tests/substitute.test.ts +479 -0
package/tests/init.test.ts
CHANGED
|
@@ -8,18 +8,34 @@ const testHome = path.join(process.cwd(), '.test-home-init');
|
|
|
8
8
|
process.env.HOME = testHome;
|
|
9
9
|
|
|
10
10
|
import { init } from '../src/commands/initCommand.js';
|
|
11
|
+
import { loadConfig, saveConfig, PtConfig, CONFIG_PATH } from '../src/config.js';
|
|
12
|
+
|
|
13
|
+
// Helper to clean up test directories
|
|
14
|
+
function cleanup(...paths: string[]) {
|
|
15
|
+
for (const p of paths) {
|
|
16
|
+
if (fs.existsSync(p)) {
|
|
17
|
+
fs.rmSync(p, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Helper to set up a config with a template for testing
|
|
23
|
+
function setupTestConfig(templateName: string, template: any): PtConfig {
|
|
24
|
+
const config: PtConfig = {
|
|
25
|
+
version: '3.0',
|
|
26
|
+
templates: {
|
|
27
|
+
[templateName]: template
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
saveConfig(config);
|
|
31
|
+
return config;
|
|
32
|
+
}
|
|
11
33
|
|
|
12
34
|
test('direct JSON template initialization via --file', async () => {
|
|
13
35
|
const jsonFilePath = path.join(process.cwd(), 'test-direct-template.json');
|
|
14
36
|
const projectDest = path.join(process.cwd(), 'test-scaffolded-project');
|
|
15
37
|
|
|
16
|
-
|
|
17
|
-
if (fs.existsSync(jsonFilePath)) {
|
|
18
|
-
fs.unlinkSync(jsonFilePath);
|
|
19
|
-
}
|
|
20
|
-
if (fs.existsSync(projectDest)) {
|
|
21
|
-
fs.rmSync(projectDest, { recursive: true, force: true });
|
|
22
|
-
}
|
|
38
|
+
cleanup(projectDest);
|
|
23
39
|
|
|
24
40
|
// Create a mock template JSON configuration
|
|
25
41
|
const mockTemplate = {
|
|
@@ -44,7 +60,6 @@ test('direct JSON template initialization via --file', async () => {
|
|
|
44
60
|
fs.writeFileSync(jsonFilePath, JSON.stringify(mockTemplate, null, 2));
|
|
45
61
|
|
|
46
62
|
// Run the init command with the --file option
|
|
47
|
-
// targetName (1st arg) is omitted/undefined, destPath (2nd arg) is our projectDest, file option is provided
|
|
48
63
|
await init(undefined, projectDest, {
|
|
49
64
|
file: jsonFilePath,
|
|
50
65
|
yes: true,
|
|
@@ -65,13 +80,412 @@ test('direct JSON template initialization via --file', async () => {
|
|
|
65
80
|
assert.ok(infoContent.includes('mock template'), 'Should contain description');
|
|
66
81
|
|
|
67
82
|
// Clean up
|
|
68
|
-
|
|
69
|
-
|
|
83
|
+
cleanup(projectDest, testHome);
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test('init with --file and typeName as destPath shortcut', async () => {
|
|
87
|
+
const jsonFilePath = path.join(process.cwd(), 'test-file-shortcut.json');
|
|
88
|
+
const projectDest = path.join(process.cwd(), 'test-shortcut-project');
|
|
89
|
+
|
|
90
|
+
cleanup(projectDest);
|
|
91
|
+
|
|
92
|
+
const mockTemplate = {
|
|
93
|
+
name: 'shortcut-test',
|
|
94
|
+
description: 'Testing typeName-as-dest shortcut',
|
|
95
|
+
folders: [
|
|
96
|
+
{ name: 'lib', info: 'library code' }
|
|
97
|
+
]
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
fs.writeFileSync(jsonFilePath, JSON.stringify(mockTemplate, null, 2));
|
|
101
|
+
|
|
102
|
+
// When --file is provided and only typeName is given (no destPath),
|
|
103
|
+
// typeName becomes the destination path
|
|
104
|
+
await init(projectDest, undefined, {
|
|
105
|
+
file: jsonFilePath,
|
|
106
|
+
yes: true,
|
|
107
|
+
skipPostConfig: true
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
assert.ok(fs.existsSync(projectDest), 'Project should be created at typeName path');
|
|
111
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'lib')), 'lib directory should exist');
|
|
112
|
+
|
|
113
|
+
// Clean up
|
|
114
|
+
fs.unlinkSync(jsonFilePath);
|
|
115
|
+
cleanup(projectDest, testHome);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('init creates nested folder structure with .info.md files', async () => {
|
|
119
|
+
const projectDest = path.join(process.cwd(), 'test-nested-structure');
|
|
120
|
+
cleanup(projectDest);
|
|
121
|
+
|
|
122
|
+
const jsonFilePath = path.join(process.cwd(), 'test-nested-template.json');
|
|
123
|
+
const mockTemplate = {
|
|
124
|
+
name: 'nested-test',
|
|
125
|
+
description: 'Testing nested structure creation',
|
|
126
|
+
folders: [
|
|
127
|
+
{
|
|
128
|
+
name: 'src',
|
|
129
|
+
info: 'Source code directory',
|
|
130
|
+
children: [
|
|
131
|
+
{
|
|
132
|
+
name: 'models',
|
|
133
|
+
info: 'Data models',
|
|
134
|
+
children: [
|
|
135
|
+
{ name: 'base', info: 'Base model classes' }
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
{ name: 'views', info: 'View templates' }
|
|
139
|
+
]
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: 'tests',
|
|
143
|
+
info: 'Test directory'
|
|
144
|
+
}
|
|
145
|
+
]
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
fs.writeFileSync(jsonFilePath, JSON.stringify(mockTemplate, null, 2));
|
|
149
|
+
|
|
150
|
+
await init(undefined, projectDest, {
|
|
151
|
+
file: jsonFilePath,
|
|
152
|
+
yes: true,
|
|
153
|
+
skipPostConfig: true
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Verify deeply nested structure
|
|
157
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'src')), 'src should exist');
|
|
158
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'src/models')), 'src/models should exist');
|
|
159
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'src/models/base')), 'src/models/base should exist');
|
|
160
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'src/views')), 'src/views should exist');
|
|
161
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'tests')), 'tests should exist');
|
|
162
|
+
|
|
163
|
+
// Verify .info.md files were created for folders with info
|
|
164
|
+
const srcInfo = fs.readFileSync(path.join(projectDest, 'src/.info.md'), 'utf-8');
|
|
165
|
+
assert.ok(srcInfo.includes('Source code directory'), 'src .info.md should have correct content');
|
|
166
|
+
|
|
167
|
+
const modelsInfo = fs.readFileSync(path.join(projectDest, 'src/models/.info.md'), 'utf-8');
|
|
168
|
+
assert.ok(modelsInfo.includes('Data models'), 'models .info.md should have correct content');
|
|
169
|
+
|
|
170
|
+
const baseInfo = fs.readFileSync(path.join(projectDest, 'src/models/base/.info.md'), 'utf-8');
|
|
171
|
+
assert.ok(baseInfo.includes('Base model classes'), 'base .info.md should have correct content');
|
|
172
|
+
|
|
173
|
+
// Clean up
|
|
174
|
+
fs.unlinkSync(jsonFilePath);
|
|
175
|
+
cleanup(projectDest, testHome);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test('init dry run does not create files', async () => {
|
|
179
|
+
const projectDest = path.join(process.cwd(), 'test-dry-run-project');
|
|
180
|
+
cleanup(projectDest);
|
|
181
|
+
|
|
182
|
+
const jsonFilePath = path.join(process.cwd(), 'test-dryrun-template.json');
|
|
183
|
+
const mockTemplate = {
|
|
184
|
+
name: 'dryrun-test',
|
|
185
|
+
description: 'Testing dry run mode',
|
|
186
|
+
folders: [
|
|
187
|
+
{ name: 'src', info: 'source' },
|
|
188
|
+
{ name: 'docs', info: 'documentation' }
|
|
189
|
+
]
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
fs.writeFileSync(jsonFilePath, JSON.stringify(mockTemplate, null, 2));
|
|
193
|
+
|
|
194
|
+
await init(undefined, projectDest, {
|
|
195
|
+
file: jsonFilePath,
|
|
196
|
+
yes: true,
|
|
197
|
+
skipPostConfig: true,
|
|
198
|
+
dryRun: true
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Dry run should NOT create the project directory
|
|
202
|
+
assert.ok(!fs.existsSync(projectDest), 'Project directory should NOT exist in dry run');
|
|
203
|
+
|
|
204
|
+
// Clean up
|
|
205
|
+
fs.unlinkSync(jsonFilePath);
|
|
206
|
+
cleanup(testHome);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
test('init with saved template from config', async () => {
|
|
210
|
+
const projectDest = path.join(process.cwd(), 'test-saved-template-project');
|
|
211
|
+
cleanup(projectDest);
|
|
212
|
+
|
|
213
|
+
// Set up a config with a template
|
|
214
|
+
setupTestConfig('test-saved-tpl', {
|
|
215
|
+
description: 'A saved template for testing',
|
|
216
|
+
folders: [
|
|
217
|
+
{ name: 'app', info: 'application code' },
|
|
218
|
+
{ name: 'config', info: 'configuration files' }
|
|
219
|
+
]
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
await init('test-saved-tpl', projectDest, {
|
|
223
|
+
yes: true,
|
|
224
|
+
skipPostConfig: true
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
assert.ok(fs.existsSync(projectDest), 'Project should be created');
|
|
228
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'app')), 'app directory should exist');
|
|
229
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'config')), 'config directory should exist');
|
|
230
|
+
|
|
231
|
+
// Verify .info.md at project root
|
|
232
|
+
const infoContent = fs.readFileSync(path.join(projectDest, '.info.md'), 'utf-8');
|
|
233
|
+
assert.ok(infoContent.includes('test-saved-tpl'), 'Root .info.md should have template name');
|
|
234
|
+
assert.ok(infoContent.includes('A saved template for testing'), 'Root .info.md should have description');
|
|
235
|
+
|
|
236
|
+
// Clean up
|
|
237
|
+
cleanup(projectDest, testHome);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('init with variables via --vars option', async () => {
|
|
241
|
+
const projectDest = path.join(process.cwd(), 'test-vars-project');
|
|
242
|
+
const templateRoot = path.join(process.cwd(), 'test-vars-template-root');
|
|
243
|
+
cleanup(projectDest, templateRoot);
|
|
244
|
+
|
|
245
|
+
// Create template root with a file containing variables
|
|
246
|
+
fs.mkdirSync(templateRoot, { recursive: true });
|
|
247
|
+
fs.writeFileSync(
|
|
248
|
+
path.join(templateRoot, 'README.md'),
|
|
249
|
+
'# {{ project_name }}\n\nBy {{ author }}\n'
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
// Set up config with template that has variables and copy_files
|
|
253
|
+
setupTestConfig('vars-tpl', {
|
|
254
|
+
description: 'Template with variables',
|
|
255
|
+
templateRoot: templateRoot,
|
|
256
|
+
folders: [],
|
|
257
|
+
variables: [
|
|
258
|
+
{ name: 'project_name', prompt: 'Project name:', required: true },
|
|
259
|
+
{ name: 'author', prompt: 'Author:', default: 'Unknown' }
|
|
260
|
+
],
|
|
261
|
+
copy_files: [
|
|
262
|
+
{ src: 'README.md', dest: 'README.md', substitute_variables: true }
|
|
263
|
+
]
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
await init('vars-tpl', projectDest, {
|
|
267
|
+
yes: true,
|
|
268
|
+
skipPostConfig: true,
|
|
269
|
+
vars: 'project_name=MyProject,author=TestAuthor'
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
assert.ok(fs.existsSync(projectDest), 'Project should be created');
|
|
273
|
+
|
|
274
|
+
// Verify variable substitution in copied file
|
|
275
|
+
const readme = fs.readFileSync(path.join(projectDest, 'README.md'), 'utf-8');
|
|
276
|
+
assert.ok(readme.includes('MyProject'), 'README should contain substituted project_name');
|
|
277
|
+
assert.ok(readme.includes('TestAuthor'), 'README should contain substituted author');
|
|
278
|
+
assert.ok(!readme.includes('{{ project_name }}'), 'README should NOT contain variable placeholder');
|
|
279
|
+
|
|
280
|
+
// Clean up
|
|
281
|
+
cleanup(projectDest, templateRoot, testHome);
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
test('init with variables uses defaults in --yes mode', async () => {
|
|
285
|
+
const projectDest = path.join(process.cwd(), 'test-vars-default-project');
|
|
286
|
+
const templateRoot = path.join(process.cwd(), 'test-vars-default-tpl-root');
|
|
287
|
+
cleanup(projectDest, templateRoot);
|
|
288
|
+
|
|
289
|
+
fs.mkdirSync(templateRoot, { recursive: true });
|
|
290
|
+
fs.writeFileSync(
|
|
291
|
+
path.join(templateRoot, 'config.txt'),
|
|
292
|
+
'env={{ environment }}\n'
|
|
293
|
+
);
|
|
294
|
+
|
|
295
|
+
setupTestConfig('vars-default-tpl', {
|
|
296
|
+
description: 'Template with default variables',
|
|
297
|
+
templateRoot: templateRoot,
|
|
298
|
+
folders: [],
|
|
299
|
+
variables: [
|
|
300
|
+
{ name: 'environment', prompt: 'Environment:', default: 'development', required: false }
|
|
301
|
+
],
|
|
302
|
+
copy_files: [
|
|
303
|
+
{ src: 'config.txt', dest: 'config.txt', substitute_variables: true }
|
|
304
|
+
]
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
// Run without providing --vars; in --yes mode, defaults should be used
|
|
308
|
+
await init('vars-default-tpl', projectDest, {
|
|
309
|
+
yes: true,
|
|
310
|
+
skipPostConfig: true
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
const configContent = fs.readFileSync(path.join(projectDest, 'config.txt'), 'utf-8');
|
|
314
|
+
assert.ok(configContent.includes('env=development'), 'Should use default variable value');
|
|
315
|
+
|
|
316
|
+
cleanup(projectDest, templateRoot, testHome);
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test('init with post_copy handles executables', async () => {
|
|
320
|
+
const projectDest = path.join(process.cwd(), 'test-postcopy-project');
|
|
321
|
+
const templateRoot = path.join(process.cwd(), 'test-postcopy-tpl-root');
|
|
322
|
+
cleanup(projectDest, templateRoot);
|
|
323
|
+
|
|
324
|
+
fs.mkdirSync(templateRoot, { recursive: true });
|
|
325
|
+
fs.writeFileSync(path.join(templateRoot, 'setup.sh'), '#!/bin/bash\necho "setup"\n');
|
|
326
|
+
|
|
327
|
+
setupTestConfig('postcopy-tpl', {
|
|
328
|
+
description: 'Template with post_copy',
|
|
329
|
+
templateRoot: templateRoot,
|
|
330
|
+
folders: [
|
|
331
|
+
{ name: 'bin', info: 'executables' }
|
|
332
|
+
],
|
|
333
|
+
post_copy: [
|
|
334
|
+
{ src: 'setup.sh', dest: 'setup.sh' }
|
|
335
|
+
]
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
await init('postcopy-tpl', projectDest, {
|
|
339
|
+
yes: true,
|
|
340
|
+
skipPostConfig: true
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
assert.ok(fs.existsSync(projectDest), 'Project should be created');
|
|
344
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'setup.sh')), 'setup.sh should be copied');
|
|
345
|
+
|
|
346
|
+
// Verify content was copied
|
|
347
|
+
const content = fs.readFileSync(path.join(projectDest, 'setup.sh'), 'utf-8');
|
|
348
|
+
assert.ok(content.includes('echo "setup"'), 'setup.sh should have correct content');
|
|
349
|
+
|
|
350
|
+
// Verify it was made executable (on Linux/macOS)
|
|
351
|
+
if (process.platform !== 'win32') {
|
|
352
|
+
const stat = fs.statSync(path.join(projectDest, 'setup.sh'));
|
|
353
|
+
assert.ok(stat.mode & 0o111, 'setup.sh should be executable');
|
|
70
354
|
}
|
|
71
|
-
|
|
72
|
-
|
|
355
|
+
|
|
356
|
+
cleanup(projectDest, templateRoot, testHome);
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
test('init with missing templateRoot warns but creates structure', async () => {
|
|
360
|
+
const projectDest = path.join(process.cwd(), 'test-missing-root-project');
|
|
361
|
+
cleanup(projectDest);
|
|
362
|
+
|
|
363
|
+
setupTestConfig('missing-root-tpl', {
|
|
364
|
+
description: 'Template with missing root',
|
|
365
|
+
templateRoot: '/tmp/nonexistent-pt-test-dir-' + Date.now(),
|
|
366
|
+
folders: [
|
|
367
|
+
{ name: 'src', info: 'source code' },
|
|
368
|
+
{ name: 'lib', info: 'library code' }
|
|
369
|
+
],
|
|
370
|
+
copy_files: [
|
|
371
|
+
{ src: 'README.md', dest: 'README.md' }
|
|
372
|
+
]
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Should still create the folder structure even though templateRoot is missing
|
|
376
|
+
await init('missing-root-tpl', projectDest, {
|
|
377
|
+
yes: true,
|
|
378
|
+
skipPostConfig: true
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
assert.ok(fs.existsSync(projectDest), 'Project should still be created');
|
|
382
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'src')), 'src directory should exist');
|
|
383
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'lib')), 'lib directory should exist');
|
|
384
|
+
// But README.md should NOT exist since templateRoot doesn't exist
|
|
385
|
+
assert.ok(!fs.existsSync(path.join(projectDest, 'README.md')), 'README.md should NOT exist');
|
|
386
|
+
|
|
387
|
+
cleanup(projectDest, testHome);
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
test('init fails for non-existent template name', async () => {
|
|
391
|
+
const projectDest = path.join(process.cwd(), 'test-nonexistent-tpl');
|
|
392
|
+
cleanup(projectDest);
|
|
393
|
+
|
|
394
|
+
// Set up empty config
|
|
395
|
+
setupTestConfig('existing-tpl', {
|
|
396
|
+
description: 'Some template',
|
|
397
|
+
folders: []
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Mock process.exit so the test doesn't die
|
|
401
|
+
let exitCalled = false;
|
|
402
|
+
let exitCode: number | undefined;
|
|
403
|
+
const originalExit = process.exit;
|
|
404
|
+
process.exit = ((code?: number) => {
|
|
405
|
+
exitCalled = true;
|
|
406
|
+
exitCode = code;
|
|
407
|
+
throw new Error('process.exit called');
|
|
408
|
+
}) as any;
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
await init('nonexistent-template', projectDest, {
|
|
412
|
+
yes: true,
|
|
413
|
+
skipPostConfig: true
|
|
414
|
+
});
|
|
415
|
+
assert.fail('Should have called process.exit');
|
|
416
|
+
} catch (e) {
|
|
417
|
+
assert.ok(exitCalled, 'process.exit should have been called');
|
|
418
|
+
assert.strictEqual(exitCode, 1, 'Should exit with code 1');
|
|
419
|
+
} finally {
|
|
420
|
+
process.exit = originalExit;
|
|
73
421
|
}
|
|
74
|
-
|
|
75
|
-
|
|
422
|
+
|
|
423
|
+
cleanup(projectDest, testHome);
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
test('init fails when destination already exists', async () => {
|
|
427
|
+
const projectDest = path.join(process.cwd(), 'test-existing-dest');
|
|
428
|
+
|
|
429
|
+
// Create the destination first
|
|
430
|
+
fs.mkdirSync(projectDest, { recursive: true });
|
|
431
|
+
|
|
432
|
+
setupTestConfig('exists-tpl', {
|
|
433
|
+
description: 'A template',
|
|
434
|
+
folders: [{ name: 'src', info: '' }]
|
|
435
|
+
});
|
|
436
|
+
|
|
437
|
+
let exitCalled = false;
|
|
438
|
+
const originalExit = process.exit;
|
|
439
|
+
process.exit = ((code?: number) => {
|
|
440
|
+
exitCalled = true;
|
|
441
|
+
throw new Error('process.exit called');
|
|
442
|
+
}) as any;
|
|
443
|
+
|
|
444
|
+
try {
|
|
445
|
+
await init('exists-tpl', projectDest, {
|
|
446
|
+
yes: true,
|
|
447
|
+
skipPostConfig: true
|
|
448
|
+
});
|
|
449
|
+
assert.fail('Should have called process.exit');
|
|
450
|
+
} catch (e) {
|
|
451
|
+
assert.ok(exitCalled, 'process.exit should have been called for existing destination');
|
|
452
|
+
} finally {
|
|
453
|
+
process.exit = originalExit;
|
|
76
454
|
}
|
|
455
|
+
|
|
456
|
+
cleanup(projectDest, testHome);
|
|
457
|
+
});
|
|
458
|
+
|
|
459
|
+
test('init with copy_files copies directory recursively', async () => {
|
|
460
|
+
const projectDest = path.join(process.cwd(), 'test-recursive-copy-project');
|
|
461
|
+
const templateRoot = path.join(process.cwd(), 'test-recursive-copy-tpl-root');
|
|
462
|
+
cleanup(projectDest, templateRoot);
|
|
463
|
+
|
|
464
|
+
// Create template root with a nested directory structure
|
|
465
|
+
fs.mkdirSync(path.join(templateRoot, 'scripts', 'helpers'), { recursive: true });
|
|
466
|
+
fs.writeFileSync(path.join(templateRoot, 'scripts', 'build.sh'), '#!/bin/bash\necho "building"');
|
|
467
|
+
fs.writeFileSync(path.join(templateRoot, 'scripts', 'helpers', 'utils.sh'), '#!/bin/bash\necho "utils"');
|
|
468
|
+
|
|
469
|
+
setupTestConfig('recursive-tpl', {
|
|
470
|
+
description: 'Template with recursive copy',
|
|
471
|
+
templateRoot: templateRoot,
|
|
472
|
+
folders: [],
|
|
473
|
+
copy_files: [
|
|
474
|
+
{ src: 'scripts', dest: 'scripts', substitute_variables: false }
|
|
475
|
+
]
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
await init('recursive-tpl', projectDest, {
|
|
479
|
+
yes: true,
|
|
480
|
+
skipPostConfig: true
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'scripts')), 'scripts dir should exist');
|
|
484
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'scripts', 'build.sh')), 'build.sh should exist');
|
|
485
|
+
assert.ok(fs.existsSync(path.join(projectDest, 'scripts', 'helpers', 'utils.sh')), 'nested utils.sh should exist');
|
|
486
|
+
|
|
487
|
+
const content = fs.readFileSync(path.join(projectDest, 'scripts', 'build.sh'), 'utf-8');
|
|
488
|
+
assert.ok(content.includes('echo "building"'), 'build.sh should have correct content');
|
|
489
|
+
|
|
490
|
+
cleanup(projectDest, templateRoot, testHome);
|
|
77
491
|
});
|