plugin-scaffold.test.ts
1 /** 2 * Tests for plugin scaffold: create new plugin directories. 3 */ 4 5 import { describe, it, expect, afterEach } from 'vitest'; 6 import * as fs from 'node:fs'; 7 import * as os from 'node:os'; 8 import * as path from 'node:path'; 9 import { createPluginScaffold } from './plugin-scaffold.js'; 10 11 describe('createPluginScaffold', () => { 12 const createdDirs: string[] = []; 13 14 afterEach(() => { 15 for (const dir of createdDirs) { 16 try { fs.rmSync(dir, { recursive: true, force: true }); } catch {} 17 } 18 createdDirs.length = 0; 19 }); 20 21 it('creates all expected files', () => { 22 const dir = path.join(os.tmpdir(), `opencli-scaffold-${Date.now()}`); 23 createdDirs.push(dir); 24 25 const result = createPluginScaffold('my-test', { dir }); 26 expect(result.name).toBe('my-test'); 27 expect(result.dir).toBe(dir); 28 expect(result.files).toContain('opencli-plugin.json'); 29 expect(result.files).toContain('package.json'); 30 expect(result.files).toContain('hello.ts'); 31 expect(result.files).toContain('greet.ts'); 32 expect(result.files).toContain('README.md'); 33 34 // All files exist 35 for (const f of result.files) { 36 expect(fs.existsSync(path.join(dir, f))).toBe(true); 37 } 38 }); 39 40 it('generates valid opencli-plugin.json', () => { 41 const dir = path.join(os.tmpdir(), `opencli-scaffold-${Date.now()}`); 42 createdDirs.push(dir); 43 44 createPluginScaffold('test-manifest', { dir, description: 'Test desc' }); 45 const manifest = JSON.parse(fs.readFileSync(path.join(dir, 'opencli-plugin.json'), 'utf-8')); 46 expect(manifest.name).toBe('test-manifest'); 47 expect(manifest.version).toBe('0.1.0'); 48 expect(manifest.description).toBe('Test desc'); 49 expect(manifest.opencli).toMatch(/^>=/); 50 }); 51 52 it('generates ESM package.json', () => { 53 const dir = path.join(os.tmpdir(), `opencli-scaffold-${Date.now()}`); 54 createdDirs.push(dir); 55 56 createPluginScaffold('test-pkg', { dir }); 57 const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8')); 58 expect(pkg.type).toBe('module'); 59 expect(pkg.peerDependencies?.['@jackwener/opencli']).toBeDefined(); 60 }); 61 62 it('generates a TS sample that matches the current plugin API', () => { 63 const dir = path.join(os.tmpdir(), `opencli-scaffold-${Date.now()}`); 64 createdDirs.push(dir); 65 66 createPluginScaffold('test-ts', { dir }); 67 const tsSample = fs.readFileSync(path.join(dir, 'greet.ts'), 'utf-8'); 68 69 expect(tsSample).toContain(`import { cli, Strategy } from '@jackwener/opencli/registry';`); 70 expect(tsSample).toContain(`strategy: Strategy.PUBLIC`); 71 expect(tsSample).toContain(`help: 'Name to greet'`); 72 expect(tsSample).toContain(`func: async (_page, kwargs)`); 73 expect(tsSample).not.toContain('async run('); 74 }); 75 76 it('documents a supported local install flow', () => { 77 const dir = path.join(os.tmpdir(), `opencli-scaffold-${Date.now()}`); 78 createdDirs.push(dir); 79 80 createPluginScaffold('test-readme', { dir }); 81 const readme = fs.readFileSync(path.join(dir, 'README.md'), 'utf-8'); 82 83 expect(readme).toContain(`opencli plugin install file://${dir}`); 84 }); 85 86 it('rejects invalid names', () => { 87 expect(() => createPluginScaffold('Bad_Name')).toThrow('Invalid plugin name'); 88 expect(() => createPluginScaffold('123start')).toThrow('Invalid plugin name'); 89 }); 90 91 it('rejects non-empty directory', () => { 92 const dir = path.join(os.tmpdir(), `opencli-scaffold-${Date.now()}`); 93 createdDirs.push(dir); 94 fs.mkdirSync(dir, { recursive: true }); 95 fs.writeFileSync(path.join(dir, 'existing.txt'), 'x'); 96 expect(() => createPluginScaffold('test', { dir })).toThrow('not empty'); 97 }); 98 });