From 3f996904a0a7320a2af4b30acaedf36b4ef6083a Mon Sep 17 00:00:00 2001 From: neverland Date: Thu, 4 Dec 2025 14:51:33 +0800 Subject: [PATCH] test: fix unstable test cases --- .gitignore | 1 + package.json | 1 + pnpm-lock.yaml | 57 ++++++++++++++++++++++++++++++++++++++ src/index.ts | 15 ++++++---- test/agents.test.ts | 53 ++++++++++++++++------------------- test/custom-tools.test.ts | 58 ++++++++++++++++++--------------------- test/index.test.ts | 12 ++++---- 7 files changed, 126 insertions(+), 71 deletions(-) diff --git a/.gitignore b/.gitignore index 37660a9..921d8e8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ node_modules dist/ test-results +test-temp-* # IDE .vscode/* diff --git a/package.json b/package.json index 4a25686..ce78e4d 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "fs-extra": "^11.3.2", "minimist": "^1.2.8", "picocolors": "^1.1.1", + "rimraf": "^6.1.2", "rslog": "^1.3.2", "simple-git-hooks": "^2.13.1", "typescript": "^5.9.3" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c097e07..12a9ded 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: picocolors: specifier: ^1.1.1 version: 1.1.1 + rimraf: + specifier: ^6.1.2 + version: 6.1.2 rslog: specifier: ^1.3.2 version: 1.3.2 @@ -546,6 +549,10 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + glob@13.0.0: + resolution: {integrity: sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==} + engines: {node: 20 || >=22} + graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} @@ -584,6 +591,10 @@ packages: lodash@4.17.21: resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + lru-cache@11.2.4: + resolution: {integrity: sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==} + engines: {node: 20 || >=22} + lru-cache@6.0.0: resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} engines: {node: '>=10'} @@ -592,9 +603,20 @@ packages: resolution: {integrity: sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==} engines: {node: 20 || >=22} + minimatch@10.1.1: + resolution: {integrity: sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==} + engines: {node: 20 || >=22} + minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} @@ -602,6 +624,10 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + path-scurry@2.0.1: + resolution: {integrity: sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==} + engines: {node: 20 || >=22} + picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -617,6 +643,11 @@ packages: resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} hasBin: true + rimraf@6.1.2: + resolution: {integrity: sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==} + engines: {node: 20 || >=22} + hasBin: true + rsbuild-plugin-dts@0.18.2: resolution: {integrity: sha512-gUZ6MXjp7PQtBWlyCkcNp34scgc7qSQRc6Rw4YHVeuBnLvVAXsToYJHn32ImYPBJzRGFA9dz5D1r7ZZKurD7Vg==} engines: {node: '>=18.12.0'} @@ -1186,6 +1217,12 @@ snapshots: function-bind@1.1.2: {} + glob@13.0.0: + dependencies: + minimatch: 10.1.1 + minipass: 7.1.2 + path-scurry: 2.0.1 + graceful-fs@4.2.11: {} has-flag@4.0.0: {} @@ -1216,6 +1253,8 @@ snapshots: lodash@4.17.21: {} + lru-cache@11.2.4: {} + lru-cache@6.0.0: dependencies: yallist: 4.0.0 @@ -1224,12 +1263,25 @@ snapshots: dependencies: '@isaacs/brace-expansion': 5.0.0 + minimatch@10.1.1: + dependencies: + '@isaacs/brace-expansion': 5.0.0 + minimist@1.2.8: {} + minipass@7.1.2: {} + + package-json-from-dist@1.0.1: {} + path-key@3.1.1: {} path-parse@1.0.7: {} + path-scurry@2.0.1: + dependencies: + lru-cache: 11.2.4 + minipass: 7.1.2 + picocolors@1.1.1: {} punycode@2.3.1: {} @@ -1242,6 +1294,11 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 + rimraf@6.1.2: + dependencies: + glob: 13.0.0 + package-json-from-dist: 1.0.1 + rsbuild-plugin-dts@0.18.2(@microsoft/api-extractor@7.55.1(@types/node@24.10.1))(@rsbuild/core@1.6.10)(typescript@5.9.3): dependencies: '@ast-grep/napi': 0.37.0 diff --git a/src/index.ts b/src/index.ts index 9631933..99a4662 100644 --- a/src/index.ts +++ b/src/index.ts @@ -174,8 +174,8 @@ const readJSON = async (path: string) => const readPackageJson = async (filePath: string) => readJSON(path.join(filePath, 'package.json')); -const parseArgv = () => { - const argv = minimist(process.argv.slice(2), { +const parseArgv = (processArgv: string[]) => { + const argv = minimist(processArgv.slice(2), { alias: { h: 'help', d: 'dir', t: 'template' }, }); @@ -210,7 +210,7 @@ type ExtraTool = { command?: string; }; -async function runCommand(command: string, cwd: string) { +function runCommand(command: string, cwd: string) { const [bin, ...args] = command.split(' '); spawn.sync(bin, args, { stdio: 'inherit', @@ -228,6 +228,7 @@ export async function create({ version, noteInformation, extraTools, + argv: processArgv = process.argv, }: { name: string; root: string; @@ -248,11 +249,15 @@ export async function create({ * Specify additional tools. */ extraTools?: ExtraTool[]; + /** + * For test purpose, override the default argv (process.argv). + */ + argv?: string[]; }) { console.log(''); logger.greet(`◆ Create ${upperFirst(name)} Project`); - const argv = parseArgv(); + const argv = parseArgv(processArgv); if (argv.help) { logHelpMessage(name, templates); @@ -346,7 +351,7 @@ export async function create({ await matchedTool.action(); } if (matchedTool?.command) { - await runCommand(matchedTool.command, distFolder); + runCommand(matchedTool.command, distFolder); } continue; } diff --git a/test/agents.test.ts b/test/agents.test.ts index 745305a..f6ff2eb 100644 --- a/test/agents.test.ts +++ b/test/agents.test.ts @@ -2,11 +2,11 @@ import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { assert, beforeEach, expect, test } from '@rstest/core'; -import { create } from '../dist/index.js'; +import { create } from '../src'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const testDir = path.join(__dirname, 'temp'); const fixturesDir = path.join(__dirname, 'fixtures', 'agents-md'); +const testDir = path.join(fixturesDir, 'test-temp-output'); beforeEach(() => { // Clean up test directory before each test @@ -15,13 +15,8 @@ beforeEach(() => { } fs.mkdirSync(testDir, { recursive: true }); - // Store original argv - const originalArgv = process.argv; - // Return cleanup function return () => { - // Restore original argv and clean up - process.argv = originalArgv; if (fs.existsSync(testDir)) { fs.rmSync(testDir, { recursive: true }); } @@ -30,13 +25,13 @@ beforeEach(() => { test('should generate AGENTS.md with no tools selected', async () => { const projectDir = path.join(testDir, 'no-tools'); - process.argv = ['node', 'test', '--dir', projectDir, '--template', 'vanilla']; await create({ name: 'test', root: fixturesDir, templates: ['vanilla'], getTemplateName: async () => 'vanilla', + argv: ['node', 'test', '--dir', projectDir, '--template', 'vanilla'], }); const agentsPath = path.join(projectDir, 'AGENTS.md'); @@ -75,22 +70,22 @@ test('should generate AGENTS.md with no tools selected', async () => { test('should generate AGENTS.md with single tool selected', async () => { const projectDir = path.join(testDir, 'single-tool'); - process.argv = [ - 'node', - 'test', - '--dir', - projectDir, - '--template', - 'vanilla', - '--tools', - 'biome', - ]; await create({ name: 'test', root: fixturesDir, templates: ['vanilla'], getTemplateName: async () => 'vanilla', + argv: [ + 'node', + 'test', + '--dir', + projectDir, + '--template', + 'vanilla', + '--tools', + 'biome', + ], }); const agentsPath = path.join(projectDir, 'AGENTS.md'); @@ -134,16 +129,6 @@ test('should generate AGENTS.md with single tool selected', async () => { test('should generate AGENTS.md with eslint tool and template mapping', async () => { const projectDir = path.join(testDir, 'eslint-tool'); - process.argv = [ - 'node', - 'test', - '--dir', - projectDir, - '--template', - 'vanilla', - '--tools', - 'eslint', - ]; await create({ name: 'test', @@ -154,6 +139,16 @@ test('should generate AGENTS.md with eslint tool and template mapping', async () if (templateName === 'vanilla') return 'vanilla-ts'; return null; }, + argv: [ + 'node', + 'test', + '--dir', + projectDir, + '--template', + 'vanilla', + '--tools', + 'eslint', + ], }); const agentsPath = path.join(projectDir, 'AGENTS.md'); @@ -196,13 +191,13 @@ test('should generate AGENTS.md with eslint tool and template mapping', async () test('should merge top-level sections from AGENTS.md files', async () => { const projectDir = path.join(testDir, 'h1-support'); - process.argv = ['node', 'test', '--dir', projectDir, '--template', 'vanilla']; await create({ name: 'test', root: fixturesDir, templates: ['vanilla'], getTemplateName: async () => 'vanilla', + argv: ['node', 'test', '--dir', projectDir, '--template', 'vanilla'], }); const agentsPath = path.join(projectDir, 'AGENTS.md'); diff --git a/test/custom-tools.test.ts b/test/custom-tools.test.ts index 6f2376c..50103dd 100644 --- a/test/custom-tools.test.ts +++ b/test/custom-tools.test.ts @@ -3,11 +3,11 @@ import path from 'node:path'; import { fileURLToPath } from 'node:url'; import { beforeEach, expect, test } from '@rstest/core'; import fse from 'fs-extra'; -import { create } from '../dist/index.js'; +import { create } from '../src'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const testDir = path.join(__dirname, 'temp'); const fixturesDir = path.join(__dirname, 'fixtures', 'basic'); +const testDir = path.join(fixturesDir, 'test-temp-output'); beforeEach(() => { if (fs.existsSync(testDir)) { @@ -15,10 +15,7 @@ beforeEach(() => { } fs.mkdirSync(testDir, { recursive: true }); - const originalArgv = process.argv; - return () => { - process.argv = originalArgv; if (fs.existsSync(testDir)) { fs.rmSync(testDir, { recursive: true }); } @@ -29,17 +26,6 @@ test('should run extra tool action', async () => { const projectDir = path.join(testDir, 'extra-tool-action'); let actionCalled = false; - process.argv = [ - 'node', - 'test', - '--dir', - projectDir, - '--template', - 'vanilla', - '--tools', - 'custom-action', - ]; - await create({ name: 'test', root: fixturesDir, @@ -54,6 +40,16 @@ test('should run extra tool action', async () => { }, }, ], + argv: [ + 'node', + 'test', + '--dir', + projectDir, + '--template', + 'vanilla', + '--tools', + 'custom-action', + ], }); expect(actionCalled).toBe(true); @@ -61,20 +57,10 @@ test('should run extra tool action', async () => { test('should run extra tool command', async () => { const projectDir = path.join(testDir, 'extra-tool-command'); - const touchedFile = path.join(projectDir, 'touched-by-command.txt'); + const testFile = path.join(__dirname, 'node_modules', 'test.txt'); - await fse.remove(touchedFile); - - process.argv = [ - 'node', - 'test', - '--dir', - projectDir, - '--template', - 'vanilla', - '--tools', - 'custom-command', - ]; + await fse.outputFile(testFile, ''); + expect(fs.existsSync(testFile)).toBe(true); await create({ name: 'test', @@ -85,10 +71,20 @@ test('should run extra tool command', async () => { { value: 'custom-command', label: 'Custom Command', - command: 'touch touched-by-command.txt', + command: `npx rimraf ${testFile}`, }, ], + argv: [ + 'node', + 'test', + '--dir', + projectDir, + '--template', + 'vanilla', + '--tools', + 'custom-command', + ], }); - expect(fs.existsSync(touchedFile)).toBe(true); + expect(fs.existsSync(testFile)).toBe(false); }); diff --git a/test/index.test.ts b/test/index.test.ts index 02c3c91..a029de3 100644 --- a/test/index.test.ts +++ b/test/index.test.ts @@ -1,4 +1,4 @@ -import { assert, test } from '@rstest/core'; +import { expect, test } from '@rstest/core'; import { checkCancel, create, @@ -8,9 +8,9 @@ import { } from '../dist/index.js'; test('should export public APIs', () => { - assert.deepStrictEqual(typeof checkCancel, 'function'); - assert.deepStrictEqual(typeof create, 'function'); - assert.deepStrictEqual(typeof multiselect, 'function'); - assert.deepStrictEqual(typeof select, 'function'); - assert.deepStrictEqual(typeof text, 'function'); + expect(typeof checkCancel).toBe('function'); + expect(typeof create).toBe('function'); + expect(typeof multiselect).toBe('function'); + expect(typeof select).toBe('function'); + expect(typeof text).toBe('function'); });