@garyr/pt-cli 0.30.1 → 0.31.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/dist/commands/learnCommand.js +66 -51
- package/doc/testing.md +48 -0
- package/package.json +1 -1
- package/src/commands/learnCommand.ts +65 -53
- package/tests/config-utils.test.ts +473 -0
- package/tests/config.test.ts +19 -4
- package/tests/init.test.ts +428 -14
- package/tests/learn.test.ts +838 -0
- package/tests/substitute.test.ts +479 -0
|
@@ -252,57 +252,72 @@ export async function learn(sourcePath, updateTemplate = null, options = {}) {
|
|
|
252
252
|
selectedFolders = rootDirs.filter(d => ['APP', 'scripts', 'bin'].some(p => d === p)); // Only copy specific ones recursively
|
|
253
253
|
}
|
|
254
254
|
else {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
255
|
+
if (rootFiles.length > 0) {
|
|
256
|
+
const filesResponse = await inquirer.prompt({
|
|
257
|
+
type: 'checkbox',
|
|
258
|
+
name: 'selectedFiles',
|
|
259
|
+
message: 'Select root files to include as boilerplate:',
|
|
260
|
+
loop: false,
|
|
261
|
+
theme: {
|
|
262
|
+
icon: {
|
|
263
|
+
checked: chalk.green('[x] '),
|
|
264
|
+
unchecked: '[ ] ',
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
choices: rootFiles.map(f => ({
|
|
268
|
+
name: f,
|
|
269
|
+
checked: ['.makerc', 'readme.md', 'README.md', '.gitattributes', '.gitignore', 'Makefile', 'makefile', 'package.json'].some(p => f.toLowerCase() === p.toLowerCase())
|
|
270
|
+
}))
|
|
271
|
+
});
|
|
272
|
+
selectedFiles = filesResponse.selectedFiles;
|
|
273
|
+
}
|
|
274
|
+
else {
|
|
275
|
+
selectedFiles = [];
|
|
276
|
+
}
|
|
277
|
+
if (rootDirs.length > 0) {
|
|
278
|
+
const foldersResponse = await inquirer.prompt({
|
|
279
|
+
type: 'checkbox',
|
|
280
|
+
name: 'selectedStructure',
|
|
281
|
+
message: 'Select folders to include in the template structure (skeleton):',
|
|
282
|
+
loop: false,
|
|
283
|
+
theme: {
|
|
284
|
+
icon: {
|
|
285
|
+
checked: chalk.green('[x] '),
|
|
286
|
+
unchecked: '[ ] ',
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
choices: rootDirs.map(d => ({
|
|
290
|
+
name: d,
|
|
291
|
+
checked: true // Include all in structure by default
|
|
292
|
+
}))
|
|
293
|
+
});
|
|
294
|
+
selectedStructure = foldersResponse.selectedStructure;
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
selectedStructure = [];
|
|
298
|
+
}
|
|
299
|
+
if (selectedStructure.length > 0) {
|
|
300
|
+
const copyFoldersResponse = await inquirer.prompt({
|
|
301
|
+
type: 'checkbox',
|
|
302
|
+
name: 'selectedFolders',
|
|
303
|
+
message: 'Select folders to copy RECURSIVELY as boilerplate (with contents):',
|
|
304
|
+
loop: false,
|
|
305
|
+
theme: {
|
|
306
|
+
icon: {
|
|
307
|
+
checked: chalk.green('[x] '),
|
|
308
|
+
unchecked: '[ ] ',
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
choices: selectedStructure.map((d) => ({
|
|
312
|
+
name: d,
|
|
313
|
+
checked: ['APP', 'scripts', 'bin'].some(p => d === p)
|
|
314
|
+
}))
|
|
315
|
+
});
|
|
316
|
+
selectedFolders = copyFoldersResponse.selectedFolders;
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
selectedFolders = [];
|
|
320
|
+
}
|
|
306
321
|
}
|
|
307
322
|
const copy_files = [];
|
|
308
323
|
if (fileTemplateConfig.copy_files && Array.isArray(fileTemplateConfig.copy_files)) {
|
package/doc/testing.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Testing pt-cli
|
|
2
|
+
|
|
3
|
+
This guide explains how to run, write, and manage the test suite for `pt-cli`.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The test suite uses Node.js's native test runner (`node:test`) and assertion library (`node:assert`). It is configured to run files ending with `.test.ts` in the `tests/` directory.
|
|
8
|
+
|
|
9
|
+
## Running Tests
|
|
10
|
+
|
|
11
|
+
### 1. Run the Entire Test Suite
|
|
12
|
+
To execute all tests:
|
|
13
|
+
```bash
|
|
14
|
+
npm test
|
|
15
|
+
```
|
|
16
|
+
This runs the underlying command:
|
|
17
|
+
```bash
|
|
18
|
+
node --import tsx --test tests/**/*.test.ts
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### 2. Run Individual Test Files
|
|
22
|
+
To run a specific test suite, use `tsx`:
|
|
23
|
+
```bash
|
|
24
|
+
npx tsx --test tests/config.test.ts
|
|
25
|
+
npx tsx --test tests/learn.test.ts
|
|
26
|
+
npx tsx --test tests/substitute.test.ts
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 3. Run with Test Coverage
|
|
30
|
+
To generate a test coverage report directly in the terminal:
|
|
31
|
+
```bash
|
|
32
|
+
node --experimental-test-coverage --import tsx --test tests/**/*.test.ts
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Writing Tests
|
|
38
|
+
|
|
39
|
+
When writing new tests, please adhere to these guidelines:
|
|
40
|
+
|
|
41
|
+
1. **Use Native imports**: Import `test` from `node:test` and `assert` from `node:assert`. Do not use external testing frameworks (like Mocha, Jest, or Vitest).
|
|
42
|
+
2. **ESM Imports**: Since this is an ESM (ECMAScript Modules) project, file imports within tests must use the `.js` extension (e.g., `import { learn } from '../src/commands/learnCommand.js';`).
|
|
43
|
+
3. **Environment Isolation**: The configuration path relies on `process.env.HOME`. To prevent tests from polluting your user config directory, override the home directory before importing any CLI files:
|
|
44
|
+
```typescript
|
|
45
|
+
const testHome = path.join(process.cwd(), '.test-home-custom');
|
|
46
|
+
process.env.HOME = testHome;
|
|
47
|
+
```
|
|
48
|
+
4. **Cleanup**: Always ensure temporary files, workspace directories, and test home directories are deleted after tests complete (e.g., in a `finally` block or `after` hook).
|
package/package.json
CHANGED
|
@@ -262,59 +262,71 @@ export async function learn(sourcePath: string, updateTemplate: string | null =
|
|
|
262
262
|
selectedStructure = rootDirs; // Include all folders in structure
|
|
263
263
|
selectedFolders = rootDirs.filter(d => ['APP', 'scripts', 'bin'].some(p => d === p)); // Only copy specific ones recursively
|
|
264
264
|
} else {
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
265
|
+
if (rootFiles.length > 0) {
|
|
266
|
+
const filesResponse = await inquirer.prompt({
|
|
267
|
+
type: 'checkbox',
|
|
268
|
+
name: 'selectedFiles',
|
|
269
|
+
message: 'Select root files to include as boilerplate:',
|
|
270
|
+
loop: false,
|
|
271
|
+
theme: {
|
|
272
|
+
icon: {
|
|
273
|
+
checked: chalk.green('[x] '),
|
|
274
|
+
unchecked: '[ ] ',
|
|
275
|
+
}
|
|
276
|
+
},
|
|
277
|
+
choices: rootFiles.map(f => ({
|
|
278
|
+
name: f,
|
|
279
|
+
checked: ['.makerc', 'readme.md', 'README.md', '.gitattributes', '.gitignore', 'Makefile', 'makefile', 'package.json'].some(p => f.toLowerCase() === p.toLowerCase())
|
|
280
|
+
}))
|
|
281
|
+
});
|
|
282
|
+
selectedFiles = filesResponse.selectedFiles;
|
|
283
|
+
} else {
|
|
284
|
+
selectedFiles = [];
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (rootDirs.length > 0) {
|
|
288
|
+
const foldersResponse = await inquirer.prompt({
|
|
289
|
+
type: 'checkbox',
|
|
290
|
+
name: 'selectedStructure',
|
|
291
|
+
message: 'Select folders to include in the template structure (skeleton):',
|
|
292
|
+
loop: false,
|
|
293
|
+
theme: {
|
|
294
|
+
icon: {
|
|
295
|
+
checked: chalk.green('[x] '),
|
|
296
|
+
unchecked: '[ ] ',
|
|
297
|
+
}
|
|
298
|
+
},
|
|
299
|
+
choices: rootDirs.map(d => ({
|
|
300
|
+
name: d,
|
|
301
|
+
checked: true // Include all in structure by default
|
|
302
|
+
}))
|
|
303
|
+
});
|
|
304
|
+
selectedStructure = foldersResponse.selectedStructure;
|
|
305
|
+
} else {
|
|
306
|
+
selectedStructure = [];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (selectedStructure.length > 0) {
|
|
310
|
+
const copyFoldersResponse = await inquirer.prompt({
|
|
311
|
+
type: 'checkbox',
|
|
312
|
+
name: 'selectedFolders',
|
|
313
|
+
message: 'Select folders to copy RECURSIVELY as boilerplate (with contents):',
|
|
314
|
+
loop: false,
|
|
315
|
+
theme: {
|
|
316
|
+
icon: {
|
|
317
|
+
checked: chalk.green('[x] '),
|
|
318
|
+
unchecked: '[ ] ',
|
|
319
|
+
}
|
|
320
|
+
},
|
|
321
|
+
choices: selectedStructure.map((d: string) => ({
|
|
322
|
+
name: d,
|
|
323
|
+
checked: ['APP', 'scripts', 'bin'].some(p => d === p)
|
|
324
|
+
}))
|
|
325
|
+
});
|
|
326
|
+
selectedFolders = copyFoldersResponse.selectedFolders;
|
|
327
|
+
} else {
|
|
328
|
+
selectedFolders = [];
|
|
329
|
+
}
|
|
318
330
|
}
|
|
319
331
|
|
|
320
332
|
const copy_files: CopyFileEntry[] = [];
|