/** * Tests for scripts/plugin.js -- plugin install, remove, list. */ import fs from 'fs'; import path from 'path'; import { execSync } from './exec.js'; import { fileURLToPath } from 'url'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const ROOT = path.join(__dirname, '..'); const SCRIPT = path.join(ROOT, 'scripts', 'plugin.js'); const PLUGINS_DIR = path.join(ROOT, 'plugins'); const CONFIG_PATH = path.join(ROOT, 'camofox.config.json'); const run = (args) => execSync(`node ${SCRIPT} ${args}`, { cwd: ROOT, encoding: 'utf-8' }); // Save/restore config around tests let originalConfig; beforeAll(() => { originalConfig = fs.readFileSync(CONFIG_PATH, 'utf-8'); }); afterAll(() => { fs.writeFileSync(CONFIG_PATH, originalConfig); }); // Clean up test plugins after each test afterEach(() => { const testDir = path.join(PLUGINS_DIR, 'test-plugin'); if (fs.existsSync(testDir)) fs.rmSync(testDir, { recursive: true }); // Restore config fs.writeFileSync(CONFIG_PATH, originalConfig); }); describe('plugin list', () => { test('lists youtube as enabled', () => { const out = run('list'); expect(out).toContain('youtube'); expect(out).toContain('[ok]'); }); }); describe('plugin install (local)', () => { const tmpDir = path.join(ROOT, '.tmp-test-plugin'); beforeEach(() => { fs.mkdirSync(tmpDir, { recursive: true }); fs.writeFileSync(path.join(tmpDir, 'index.js'), 'export function register(app, ctx) { app.get("/test", (req, res) => res.json({})); }'); }); afterEach(() => { if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true }); const installed = path.join(PLUGINS_DIR, '.tmp-test-plugin'); if (fs.existsSync(installed)) fs.rmSync(installed, { recursive: true }); }); test('copies plugin dir and updates config', () => { const out = run(`install ${tmpDir}`); expect(out).toContain('Installed'); // Plugin dir exists const installed = path.join(PLUGINS_DIR, '.tmp-test-plugin'); expect(fs.existsSync(installed)).toBe(true); expect(fs.existsSync(path.join(installed, 'index.js'))).toBe(true); // Config updated const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); if (Array.isArray(config.plugins)) { expect(config.plugins).toContain('.tmp-test-plugin'); } else { expect(config.plugins['.tmp-test-plugin']).toBeDefined(); } }); test('rejects duplicate install', () => { run(`install ${tmpDir}`); expect(() => run(`install ${tmpDir}`)).toThrow(); }); }); describe('plugin remove', () => { const tmpDir = path.join(ROOT, '.tmp-test-plugin-rm'); beforeEach(() => { fs.mkdirSync(tmpDir, { recursive: true }); fs.writeFileSync(path.join(tmpDir, 'index.js'), 'export function register(app, ctx) {}'); run(`install ${tmpDir}`); }); afterEach(() => { if (fs.existsSync(tmpDir)) fs.rmSync(tmpDir, { recursive: true }); const installed = path.join(PLUGINS_DIR, '.tmp-test-plugin-rm'); if (fs.existsSync(installed)) fs.rmSync(installed, { recursive: true }); }); test('removes plugin dir and config entry', () => { const out = run('remove .tmp-test-plugin-rm'); expect(out).toContain('Removed'); const installed = path.join(PLUGINS_DIR, '.tmp-test-plugin-rm'); expect(fs.existsSync(installed)).toBe(false); const config = JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8')); expect(config.plugins).not.toContain('.tmp-test-plugin-rm'); }); test('errors on unknown plugin', () => { expect(() => run('remove nonexistent-plugin-xyz')).toThrow(); }); }); describe('plugin help', () => { test('shows usage with no args', () => { const out = run(''); expect(out).toContain('Usage'); expect(out).toContain('install'); expect(out).toContain('remove'); }); });