【调整】申请证书配置CA选项增加liteSSL证书

This commit is contained in:
cai
2026-01-13 16:45:05 +08:00
parent 6c15ae35a1
commit 190e250095
2108 changed files with 58 additions and 401539 deletions

View File

@@ -1,314 +0,0 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { createCompressTask, compressHelpers, batchCompress } from '../src/modules/compress.js';
import { CompressConfig } from '../src/types.js';
import fs from 'fs';
import path from 'path';
import { rimraf } from 'rimraf';
import { createReadStream } from 'fs';
import { createGunzip } from 'zlib';
import { extract } from 'tar-fs';
describe('compress module', () => {
const testDir = './test-temp/compress';
const srcDir = path.join(testDir, 'src');
const destDir = path.join(testDir, 'dest');
beforeEach(async () => {
await fs.promises.mkdir(srcDir, { recursive: true });
await fs.promises.mkdir(destDir, { recursive: true });
// 创建测试文件
await fs.promises.writeFile(path.join(srcDir, 'file1.txt'), 'This is test file 1');
await fs.promises.writeFile(path.join(srcDir, 'file2.txt'), 'This is test file 2');
// 创建子目录和文件
const subDir = path.join(srcDir, 'subdir');
await fs.promises.mkdir(subDir, { recursive: true });
await fs.promises.writeFile(path.join(subDir, 'file3.txt'), 'This is test file 3 in subdirectory');
});
afterEach(async () => {
await rimraf(testDir);
});
describe('createCompressTask', () => {
it('should create ZIP archive', async () => {
const config: CompressConfig = {
src: path.join(srcDir, '**/*'),
filename: 'test.zip',
dest: destDir,
type: 'zip'
};
const task = createCompressTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const zipPath = path.join(destDir, 'test.zip');
expect(fs.existsSync(zipPath)).toBe(true);
const stats = await fs.promises.stat(zipPath);
expect(stats.size).toBeGreaterThan(0);
});
it('should create TAR archive', async () => {
const config: CompressConfig = {
src: path.join(srcDir, '**/*'),
filename: 'test.tar',
dest: destDir,
type: 'tar'
};
const task = createCompressTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const tarPath = path.join(destDir, 'test.tar');
expect(fs.existsSync(tarPath)).toBe(true);
const stats = await fs.promises.stat(tarPath);
expect(stats.size).toBeGreaterThan(0);
});
it('should create GZIP archive', async () => {
const config: CompressConfig = {
src: path.join(srcDir, '**/*'),
filename: 'test.tar.gz',
dest: destDir,
type: 'gzip'
};
const task = createCompressTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const gzipPath = path.join(destDir, 'test.tar.gz');
expect(fs.existsSync(gzipPath)).toBe(true);
const stats = await fs.promises.stat(gzipPath);
expect(stats.size).toBeGreaterThan(0);
});
it('should handle different compression levels', async () => {
const configs: CompressConfig[] = [
{
src: path.join(srcDir, '**/*'),
filename: 'fast.zip',
dest: destDir,
type: 'zip',
level: 1
},
{
src: path.join(srcDir, '**/*'),
filename: 'best.zip',
dest: destDir,
type: 'zip',
level: 9
}
];
for (const config of configs) {
const task = createCompressTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
}
const fastStats = await fs.promises.stat(path.join(destDir, 'fast.zip'));
const bestStats = await fs.promises.stat(path.join(destDir, 'best.zip'));
expect(fastStats.size).toBeGreaterThan(0);
expect(bestStats.size).toBeGreaterThan(0);
// 通常最高压缩级别的文件应该更小,但对于小文件可能差异不大
});
});
describe('compressHelpers', () => {
it('should create config correctly', () => {
const config = compressHelpers.createConfig(
'src/**/*',
'test.zip',
'dest',
{ level: 8, type: 'zip' }
);
expect(config.src).toBe('src/**/*');
expect(config.filename).toBe('test.zip');
expect(config.dest).toBe('dest');
expect(config.level).toBe(8);
expect(config.type).toBe('zip');
});
it('should infer type from filename', () => {
expect(compressHelpers.inferType('test.zip')).toBe('zip');
expect(compressHelpers.inferType('test.tar')).toBe('tar');
expect(compressHelpers.inferType('test.tar.gz')).toBe('gzip');
expect(compressHelpers.inferType('test.tgz')).toBe('gzip');
expect(compressHelpers.inferType('unknown.ext')).toBe('zip');
});
it('should generate timestamped filename', () => {
const filename = compressHelpers.timestampedFilename('backup');
expect(filename).toMatch(/^backup-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.zip$/);
const customExt = compressHelpers.timestampedFilename('backup', 'tar.gz');
expect(customExt).toMatch(/^backup-\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.tar\.gz$/);
});
it('should get compression levels', () => {
expect(compressHelpers.getCompressionLevel('speed')).toBe(1);
expect(compressHelpers.getCompressionLevel('size')).toBe(9);
expect(compressHelpers.getCompressionLevel('balanced')).toBe(6);
expect(compressHelpers.getCompressionLevel()).toBe(6); // default
});
it('should validate config', () => {
const validConfig: CompressConfig = {
src: 'src/**/*',
filename: 'test.zip',
dest: 'dest'
};
expect(compressHelpers.validateConfig(validConfig)).toEqual([]);
const invalidConfig: CompressConfig = {
src: '',
filename: '',
dest: '',
level: 15
};
const errors = compressHelpers.validateConfig(invalidConfig);
expect(errors).toContain('缺少源文件路径');
expect(errors).toContain('缺少压缩包文件名');
expect(errors).toContain('缺少输出目录');
expect(errors).toContain('压缩级别必须在 0-9 之间');
});
});
describe('batchCompress', () => {
it('should handle multiple compress operations', async () => {
const configs: CompressConfig[] = [
{
src: path.join(srcDir, '*.txt'),
filename: 'texts.zip',
dest: destDir
},
{
src: path.join(srcDir, 'subdir/**/*'),
filename: 'subdir.zip',
dest: destDir
}
];
const results = await batchCompress(configs);
expect(results).toHaveLength(2);
expect(results[0].success).toBe(true);
expect(results[1].success).toBe(true);
expect(fs.existsSync(path.join(destDir, 'texts.zip'))).toBe(true);
expect(fs.existsSync(path.join(destDir, 'subdir.zip'))).toBe(true);
});
it('should handle errors gracefully', async () => {
const configs: CompressConfig[] = [
{
src: 'non-existent/**/*',
filename: 'test.zip',
dest: '/invalid/path'
}
];
const results = await batchCompress(configs);
expect(results).toHaveLength(1);
expect(results[0].success).toBe(false);
expect(results[0].error).toBeDefined();
});
});
describe('real-world scenarios', () => {
it('should compress build directory', async () => {
// 模拟构建目录结构
const buildDir = path.join(srcDir, 'build');
await fs.promises.mkdir(path.join(buildDir, 'js'), { recursive: true });
await fs.promises.mkdir(path.join(buildDir, 'css'), { recursive: true });
await fs.promises.mkdir(path.join(buildDir, 'assets'), { recursive: true });
await fs.promises.writeFile(path.join(buildDir, 'index.html'), '<html>...</html>');
await fs.promises.writeFile(path.join(buildDir, 'js', 'app.js'), 'console.log("app");');
await fs.promises.writeFile(path.join(buildDir, 'css', 'style.css'), '.app {}');
await fs.promises.writeFile(path.join(buildDir, 'assets', 'logo.png'), 'fake-png-data');
const config: CompressConfig = {
src: path.join(buildDir, '**/*'),
filename: compressHelpers.timestampedFilename('build'),
dest: destDir,
level: compressHelpers.getCompressionLevel('balanced')
};
const task = createCompressTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const files = await fs.promises.readdir(destDir);
const zipFile = files.find(f => f.startsWith('build-') && f.endsWith('.zip'));
expect(zipFile).toBeDefined();
if (zipFile) {
const stats = await fs.promises.stat(path.join(destDir, zipFile));
expect(stats.size).toBeGreaterThan(0);
}
});
it('should create release package with version', async () => {
const version = '1.2.3';
const config: CompressConfig = {
src: path.join(srcDir, '**/*'),
filename: `release-v${version}.tar.gz`,
dest: destDir,
type: 'gzip',
level: 9
};
const task = createCompressTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const releasePath = path.join(destDir, `release-v${version}.tar.gz`);
expect(fs.existsSync(releasePath)).toBe(true);
const stats = await fs.promises.stat(releasePath);
expect(stats.size).toBeGreaterThan(0);
});
});
});

View File

@@ -1,386 +0,0 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { GulpBuildTools, createBuildTools, helpers, presets } from '../src/index.js';
import { BuildToolsConfig } from '../src/types.js';
import fs from 'fs';
import path from 'path';
import { rimraf } from 'rimraf';
describe('index module', () => {
const testDir = './test-temp/index';
let buildTools: GulpBuildTools;
beforeEach(async () => {
await fs.promises.mkdir(testDir, { recursive: true });
buildTools = createBuildTools({
cwd: testDir,
verbose: false,
tempDir: path.join(testDir, 'temp')
});
});
afterEach(async () => {
await rimraf(testDir);
});
describe('GulpBuildTools class', () => {
it('should create instance with default config', () => {
const tools = new GulpBuildTools();
expect(tools).toBeInstanceOf(GulpBuildTools);
});
it('should create instance with custom config', () => {
const config: BuildToolsConfig = {
cwd: '/custom/path',
verbose: true,
tempDir: '/tmp'
};
const tools = new GulpBuildTools(config);
expect(tools).toBeInstanceOf(GulpBuildTools);
});
it('should have all required methods', () => {
expect(buildTools.renameFiles).toBeDefined();
expect(buildTools.replaceContent).toBeDefined();
expect(buildTools.uploadFiles).toBeDefined();
expect(buildTools.compressFiles).toBeDefined();
expect(buildTools.gitOperation).toBeDefined();
expect(buildTools.sshExecution).toBeDefined();
expect(buildTools.createBuildPipeline).toBeDefined();
expect(buildTools.createParallelTasks).toBeDefined();
expect(buildTools.createSeriesTasks).toBeDefined();
});
});
describe('createBuildTools function', () => {
it('should create build tools instance', () => {
const tools = createBuildTools();
expect(tools).toBeInstanceOf(GulpBuildTools);
});
it('should pass config to instance', () => {
const config: BuildToolsConfig = {
verbose: true,
tempDir: './custom-temp'
};
const tools = createBuildTools(config);
expect(tools).toBeInstanceOf(GulpBuildTools);
});
});
describe('createBuildPipeline', () => {
it('should create empty pipeline', () => {
const pipeline = buildTools.createBuildPipeline({});
expect(pipeline).toBeDefined();
expect(typeof pipeline).toBe('function');
});
it('should create pipeline with single task', () => {
const srcDir = path.join(testDir, 'src');
const destDir = path.join(testDir, 'dest');
const pipeline = buildTools.createBuildPipeline({
replace: [{
src: path.join(srcDir, '**/*.js'),
replacements: [
{ search: 'test', replace: 'production' }
],
dest: destDir
}]
});
expect(pipeline).toBeDefined();
expect(typeof pipeline).toBe('function');
expect(pipeline.displayName).toBe('build-pipeline');
});
it('should create pipeline with multiple task types', () => {
const srcDir = path.join(testDir, 'src');
const destDir = path.join(testDir, 'dest');
const pipeline = buildTools.createBuildPipeline({
replace: [{
src: path.join(srcDir, '**/*.js'),
replacements: [{ search: 'dev', replace: 'prod' }],
dest: destDir
}],
rename: [{
src: path.join(destDir, '**/*.js'),
rename: helpers.rename.addSuffix('.min'),
dest: destDir
}],
compress: {
src: path.join(destDir, '**/*'),
filename: 'bundle.zip',
dest: path.join(testDir, 'releases')
}
});
expect(pipeline).toBeDefined();
expect(typeof pipeline).toBe('function');
});
});
describe('createParallelTasks', () => {
it('should create parallel tasks', () => {
const srcDir = path.join(testDir, 'src');
const destDir = path.join(testDir, 'dest');
const tasks = {
'task1': buildTools.renameFiles({
src: path.join(srcDir, '*.js'),
rename: 'bundle1.js',
dest: destDir
}),
'task2': buildTools.renameFiles({
src: path.join(srcDir, '*.css'),
rename: 'bundle2.css',
dest: destDir
})
};
const parallelTask = buildTools.createParallelTasks(tasks);
expect(parallelTask).toBeDefined();
expect(typeof parallelTask).toBe('function');
expect(parallelTask.displayName).toBe('parallel-tasks');
});
});
describe('createSeriesTasks', () => {
it('should create series tasks', () => {
const srcDir = path.join(testDir, 'src');
const destDir = path.join(testDir, 'dest');
const tasks = {
'step1': buildTools.replaceContent({
src: path.join(srcDir, '**/*.js'),
replacements: [{ search: 'old', replace: 'new' }],
dest: destDir
}),
'step2': buildTools.compressFiles({
src: path.join(destDir, '**/*'),
filename: 'result.zip',
dest: path.join(testDir, 'output')
})
};
const seriesTask = buildTools.createSeriesTasks(tasks);
expect(seriesTask).toBeDefined();
expect(typeof seriesTask).toBe('function');
expect(seriesTask.displayName).toBe('series-tasks');
});
});
describe('helpers export', () => {
it('should export all helper modules', () => {
expect(helpers).toBeDefined();
expect(helpers.rename).toBeDefined();
expect(helpers.replace).toBeDefined();
expect(helpers.upload).toBeDefined();
expect(helpers.compress).toBeDefined();
expect(helpers.git).toBeDefined();
expect(helpers.ssh).toBeDefined();
});
it('should have working rename helpers', () => {
expect(helpers.rename.addPrefix).toBeDefined();
expect(helpers.rename.addSuffix).toBeDefined();
expect(helpers.rename.changeExtension).toBeDefined();
expect(helpers.rename.addTimestamp).toBeDefined();
expect(helpers.rename.toLowerCase).toBeDefined();
expect(helpers.rename.toUpperCase).toBeDefined();
expect(helpers.rename.replaceInName).toBeDefined();
});
it('should have working replace helpers', () => {
expect(helpers.replace.version).toBeDefined();
expect(helpers.replace.apiBaseUrl).toBeDefined();
expect(helpers.replace.envVariable).toBeDefined();
expect(helpers.replace.htmlTitle).toBeDefined();
expect(helpers.replace.copyright).toBeDefined();
expect(helpers.replace.timestamp).toBeDefined();
expect(helpers.replace.buildNumber).toBeDefined();
expect(helpers.replace.cssColor).toBeDefined();
expect(helpers.replace.jsConfig).toBeDefined();
});
it('should have working compress helpers', () => {
expect(helpers.compress.createConfig).toBeDefined();
expect(helpers.compress.inferType).toBeDefined();
expect(helpers.compress.timestampedFilename).toBeDefined();
expect(helpers.compress.getCompressionLevel).toBeDefined();
expect(helpers.compress.validateConfig).toBeDefined();
});
});
describe('presets export', () => {
it('should export all preset modules', () => {
expect(presets).toBeDefined();
expect(presets.frontend).toBeDefined();
expect(presets.backend).toBeDefined();
expect(presets.general).toBeDefined();
});
it('should have frontend presets', () => {
expect(presets.frontend.vueDeploy).toBeDefined();
expect(presets.frontend.optimize).toBeDefined();
});
it('should have backend presets', () => {
expect(presets.backend.nodeDeploy).toBeDefined();
});
it('should have general presets', () => {
expect(presets.general.backupAndDeploy).toBeDefined();
});
it('should create valid vue deploy config', () => {
const config = presets.frontend.vueDeploy({
buildDir: 'dist',
serverConfig: {
type: 'sftp',
host: 'example.com',
username: 'user',
password: 'pass',
remotePath: '/var/www',
src: 'dist/**/*'
}
});
expect(config).toBeDefined();
expect(config.replace).toBeDefined();
expect(config.compress).toBeDefined();
expect(config.upload).toBeDefined();
expect(Array.isArray(config.replace)).toBe(true);
});
it('should create valid optimization config', () => {
const config = presets.frontend.optimize('dist');
expect(config).toBeDefined();
expect(config.replace).toBeDefined();
expect(Array.isArray(config.replace)).toBe(true);
expect(config.replace![0].src).toContain('dist');
});
it('should create valid node deploy config', () => {
const config = presets.backend.nodeDeploy({
appPath: '/var/www/app',
serverConfig: {
type: 'sftp',
host: 'server.com',
username: 'deploy',
password: 'pass',
remotePath: '/var/www',
src: 'dist/**/*'
},
sshConfig: {
host: 'server.com',
username: 'deploy',
password: 'pass',
commands: []
}
});
expect(config).toBeDefined();
expect(config.git).toBeDefined();
expect(config.upload).toBeDefined();
expect(config.ssh).toBeDefined();
});
});
describe('integration tests', () => {
it('should work with real file operations', async () => {
const srcDir = path.join(testDir, 'src');
const destDir = path.join(testDir, 'dest');
await fs.promises.mkdir(srcDir, { recursive: true });
await fs.promises.mkdir(destDir, { recursive: true });
// 创建测试文件
await fs.promises.writeFile(
path.join(srcDir, 'test.js'),
'const version = "{{VERSION}}";'
);
// 创建简单的替换任务
const task = buildTools.replaceContent({
src: path.join(srcDir, '*.js'),
replacements: [
{ search: '{{VERSION}}', replace: '1.0.0' }
],
dest: destDir
});
// 执行任务
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
// 验证结果
const content = await fs.promises.readFile(path.join(destDir, 'test.js'), 'utf8');
expect(content).toBe('const version = "1.0.0";');
});
it('should work with build pipeline', async () => {
const srcDir = path.join(testDir, 'src');
const buildDir = path.join(testDir, 'build');
const releaseDir = path.join(testDir, 'releases');
await fs.promises.mkdir(srcDir, { recursive: true });
await fs.promises.mkdir(buildDir, { recursive: true });
await fs.promises.mkdir(releaseDir, { recursive: true });
// 创建测试文件
await fs.promises.writeFile(
path.join(srcDir, 'app.js'),
'const env = "development"; const version = "{{VERSION}}";'
);
// 创建构建流水线
const pipeline = buildTools.createBuildPipeline({
replace: [{
src: path.join(srcDir, '*.js'),
replacements: [
{ search: 'development', replace: 'production' },
{ search: '{{VERSION}}', replace: '2.0.0' }
],
dest: buildDir
}],
rename: [{
src: path.join(buildDir, '*.js'),
rename: helpers.rename.addSuffix('.min'),
dest: buildDir
}],
compress: {
src: path.join(buildDir, '**/*'),
filename: 'release.zip',
dest: releaseDir
}
});
// 执行流水线
await new Promise<void>((resolve, reject) => {
pipeline((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
// 验证结果
const buildContent = await fs.promises.readFile(path.join(buildDir, 'app.min.js'), 'utf8');
expect(buildContent).toContain('production');
expect(buildContent).toContain('2.0.0');
const releaseFiles = await fs.promises.readdir(releaseDir);
expect(releaseFiles).toContain('release.zip');
});
});
});

View File

@@ -1,165 +0,0 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { createRenameTask, renameHelpers, batchRename } from '../src/modules/rename.js';
import { RenameConfig } from '../src/types.js';
import fs from 'fs';
import path from 'path';
import { rimraf } from 'rimraf';
describe('rename module', () => {
const testDir = './test-temp/rename';
const srcDir = path.join(testDir, 'src');
const destDir = path.join(testDir, 'dest');
beforeEach(async () => {
// 创建测试目录和文件
await fs.promises.mkdir(srcDir, { recursive: true });
await fs.promises.mkdir(destDir, { recursive: true });
// 创建测试文件
await fs.promises.writeFile(path.join(srcDir, 'test1.js'), 'console.log("test1");');
await fs.promises.writeFile(path.join(srcDir, 'test2.js'), 'console.log("test2");');
await fs.promises.writeFile(path.join(srcDir, 'style.css'), '.test { color: red; }');
});
afterEach(async () => {
// 清理测试目录
await rimraf(testDir);
});
describe('createRenameTask', () => {
it('should rename files with string', async () => {
const config: RenameConfig = {
src: path.join(srcDir, '*.js'),
rename: 'bundle.js',
dest: destDir
};
const task = createRenameTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
// 检查文件是否被重命名
const files = await fs.promises.readdir(destDir);
expect(files).toContain('bundle.js');
});
it('should rename files with function', async () => {
const config: RenameConfig = {
src: path.join(srcDir, '*.js'),
rename: (path) => {
path.basename = path.basename + '.min';
},
dest: destDir
};
const task = createRenameTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
// 检查文件是否被重命名
const files = await fs.promises.readdir(destDir);
expect(files).toContain('test1.min.js');
expect(files).toContain('test2.min.js');
});
});
describe('renameHelpers', () => {
it('should add prefix correctly', () => {
const mockPath = { basename: 'test', extname: '.js' };
renameHelpers.addPrefix('prefix-')(mockPath);
expect(mockPath.basename).toBe('prefix-test');
});
it('should add suffix correctly', () => {
const mockPath = { basename: 'test', extname: '.js' };
renameHelpers.addSuffix('-suffix')(mockPath);
expect(mockPath.basename).toBe('test-suffix');
});
it('should change extension correctly', () => {
const mockPath = { basename: 'test', extname: '.js' };
renameHelpers.changeExtension('.min.js')(mockPath);
expect(mockPath.extname).toBe('.min.js');
});
it('should convert to lowercase', () => {
const mockPath = { basename: 'TEST', extname: '.js' };
renameHelpers.toLowerCase()(mockPath);
expect(mockPath.basename).toBe('test');
});
it('should convert to uppercase', () => {
const mockPath = { basename: 'test', extname: '.js' };
renameHelpers.toUpperCase()(mockPath);
expect(mockPath.basename).toBe('TEST');
});
it('should replace characters in name', () => {
const mockPath = { basename: 'old-name', extname: '.js' };
renameHelpers.replaceInName('old', 'new')(mockPath);
expect(mockPath.basename).toBe('new-name');
});
it('should add timestamp', () => {
const mockPath = { basename: 'test', extname: '.js' };
const originalBasename = mockPath.basename;
renameHelpers.addTimestamp()(mockPath);
expect(mockPath.basename).toMatch(new RegExp(`^${originalBasename}-\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}`));
});
});
describe('batchRename', () => {
it('should handle multiple rename operations', async () => {
const configs: RenameConfig[] = [
{
src: path.join(srcDir, '*.js'),
rename: renameHelpers.addPrefix('js-'),
dest: destDir
},
{
src: path.join(srcDir, '*.css'),
rename: renameHelpers.addPrefix('css-'),
dest: destDir
}
];
const results = await batchRename(configs);
expect(results).toHaveLength(2);
expect(results[0].success).toBe(true);
expect(results[1].success).toBe(true);
const files = await fs.promises.readdir(destDir);
expect(files).toContain('js-test1.js');
expect(files).toContain('js-test2.js');
expect(files).toContain('css-style.css');
});
it('should handle errors gracefully', async () => {
const configs: RenameConfig[] = [
{
src: 'non-existent/**/*.js',
rename: 'test.js',
dest: '/invalid/path'
}
];
const results = await batchRename(configs);
expect(results).toHaveLength(1);
expect(results[0].success).toBe(false);
expect(results[0].error).toBeDefined();
});
});
});

View File

@@ -1,280 +0,0 @@
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { createReplaceTask, replacePatterns, batchReplace } from '../src/modules/replace.js';
import { ReplaceConfig } from '../src/types.js';
import fs from 'fs';
import path from 'path';
import { rimraf } from 'rimraf';
describe('replace module', () => {
const testDir = './test-temp/replace';
const srcDir = path.join(testDir, 'src');
const destDir = path.join(testDir, 'dest');
beforeEach(async () => {
await fs.promises.mkdir(srcDir, { recursive: true });
await fs.promises.mkdir(destDir, { recursive: true });
// 创建测试文件
await fs.promises.writeFile(
path.join(srcDir, 'config.js'),
`const API_URL = "{{API_URL}}";
const VERSION = "{{VERSION}}";
const DEBUG = true;`
);
await fs.promises.writeFile(
path.join(srcDir, 'index.html'),
`<!DOCTYPE html>
<html>
<head>
<title>{{TITLE}}</title>
</head>
<body>
<h1>{{TITLE}}</h1>
<p>Version: {{VERSION}}</p>
</body>
</html>`
);
await fs.promises.writeFile(
path.join(srcDir, 'package.json'),
`{
"name": "test-app",
"version": "1.0.0",
"description": "Test application"
}`
);
});
afterEach(async () => {
await rimraf(testDir);
});
describe('createReplaceTask', () => {
it('should replace content with string', async () => {
const config: ReplaceConfig = {
src: path.join(srcDir, 'config.js'),
replacements: [
{
search: '{{API_URL}}',
replace: 'https://api.example.com'
},
{
search: '{{VERSION}}',
replace: '2.0.0'
}
],
dest: destDir
};
const task = createReplaceTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const content = await fs.promises.readFile(path.join(destDir, 'config.js'), 'utf8');
expect(content).toContain('https://api.example.com');
expect(content).toContain('2.0.0');
expect(content).not.toContain('{{API_URL}}');
expect(content).not.toContain('{{VERSION}}');
});
it('should replace content with regex', async () => {
const config: ReplaceConfig = {
src: path.join(srcDir, 'config.js'),
replacements: [
{
search: /const\s+DEBUG\s*=\s*true/g,
replace: 'const DEBUG = false'
}
],
dest: destDir
};
const task = createReplaceTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const content = await fs.promises.readFile(path.join(destDir, 'config.js'), 'utf8');
expect(content).toContain('const DEBUG = false');
expect(content).not.toContain('const DEBUG = true');
});
it('should replace content with function', async () => {
const config: ReplaceConfig = {
src: path.join(srcDir, 'config.js'),
replacements: [
{
search: /{{([A-Z_]+)}}/g,
replace: (match, varName) => {
const replacements: Record<string, string> = {
'API_URL': 'https://api.production.com',
'VERSION': '3.0.0'
};
return replacements[varName] || match;
}
}
],
dest: destDir
};
const task = createReplaceTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const content = await fs.promises.readFile(path.join(destDir, 'config.js'), 'utf8');
expect(content).toContain('https://api.production.com');
expect(content).toContain('3.0.0');
});
});
describe('replacePatterns', () => {
it('should create version replacement pattern', () => {
const pattern = replacePatterns.version('2.5.0');
expect(pattern.search).toBeInstanceOf(RegExp);
expect(pattern.replace).toBe('"version": "2.5.0"');
});
it('should create API base URL replacement pattern', () => {
const pattern = replacePatterns.apiBaseUrl('https://new-api.com');
expect(pattern.search).toBeInstanceOf(RegExp);
expect(pattern.replace).toBe("const API_BASE_URL = 'https://new-api.com'");
});
it('should create environment variable replacement pattern', () => {
const pattern = replacePatterns.envVariable('NODE_ENV', 'production');
expect(pattern.search).toBeInstanceOf(RegExp);
expect(pattern.replace).toBe('NODE_ENV=production');
});
it('should create HTML title replacement pattern', () => {
const pattern = replacePatterns.htmlTitle('My New App');
expect(pattern.search).toBeInstanceOf(RegExp);
expect(pattern.replace).toBe('<title>My New App</title>');
});
it('should create timestamp replacement pattern', () => {
const pattern = replacePatterns.timestamp();
expect(pattern.search).toBeInstanceOf(RegExp);
expect(pattern.replace).toMatch(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
});
});
describe('batchReplace', () => {
it('should handle multiple replace operations', async () => {
const configs: ReplaceConfig[] = [
{
src: path.join(srcDir, 'config.js'),
replacements: [
{ search: '{{API_URL}}', replace: 'https://api1.com' }
],
dest: destDir
},
{
src: path.join(srcDir, 'index.html'),
replacements: [
{ search: '{{TITLE}}', replace: 'Test App' },
{ search: '{{VERSION}}', replace: '1.5.0' }
],
dest: destDir
}
];
const results = await batchReplace(configs);
expect(results).toHaveLength(2);
expect(results[0].success).toBe(true);
expect(results[1].success).toBe(true);
const configContent = await fs.promises.readFile(path.join(destDir, 'config.js'), 'utf8');
const htmlContent = await fs.promises.readFile(path.join(destDir, 'index.html'), 'utf8');
expect(configContent).toContain('https://api1.com');
expect(htmlContent).toContain('Test App');
expect(htmlContent).toContain('1.5.0');
});
it('should handle errors gracefully', async () => {
const configs: ReplaceConfig[] = [
{
src: 'non-existent.js',
replacements: [
{ search: 'test', replace: 'replacement' }
],
dest: '/invalid/path'
}
];
const results = await batchReplace(configs);
expect(results).toHaveLength(1);
expect(results[0].success).toBe(false);
expect(results[0].error).toBeDefined();
});
});
describe('real-world scenarios', () => {
it('should handle package.json version update', async () => {
const config: ReplaceConfig = {
src: path.join(srcDir, 'package.json'),
replacements: [
replacePatterns.version('2.0.0')
],
dest: destDir
};
const task = createReplaceTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const content = await fs.promises.readFile(path.join(destDir, 'package.json'), 'utf8');
const packageJson = JSON.parse(content);
expect(packageJson.version).toBe('2.0.0');
});
it('should handle HTML template replacement', async () => {
const config: ReplaceConfig = {
src: path.join(srcDir, 'index.html'),
replacements: [
replacePatterns.htmlTitle('Production App'),
{ search: /{{VERSION}}/g, replace: '2.1.0' },
{ search: /<h1>{{TITLE}}<\/h1>/g, replace: '<h1>Production App</h1>' }
],
dest: destDir
};
const task = createReplaceTask(config);
await new Promise<void>((resolve, reject) => {
task((error?: Error) => {
if (error) reject(error);
else resolve();
});
});
const content = await fs.promises.readFile(path.join(destDir, 'index.html'), 'utf8');
expect(content).toContain('<title>Production App</title>');
expect(content).toContain('<h1>Production App</h1>');
expect(content).toContain('Version: 2.1.0');
});
});
});