105 lines
3.6 KiB
TypeScript
105 lines
3.6 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2026 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
import { TestRig } from './test-helper.js';
|
|
import * as fs from 'node:fs';
|
|
import * as path from 'node:path';
|
|
import { execSync } from 'node:child_process';
|
|
|
|
describe('skill-creator scripts e2e', () => {
|
|
let rig: TestRig;
|
|
const initScript = path.resolve(
|
|
'packages/core/src/skills/builtin/skill-creator/scripts/init_skill.cjs',
|
|
);
|
|
const validateScript = path.resolve(
|
|
'packages/core/src/skills/builtin/skill-creator/scripts/validate_skill.cjs',
|
|
);
|
|
const packageScript = path.resolve(
|
|
'packages/core/src/skills/builtin/skill-creator/scripts/package_skill.cjs',
|
|
);
|
|
|
|
beforeEach(() => {
|
|
rig = new TestRig();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await rig.cleanup();
|
|
});
|
|
|
|
it('should initialize, validate, and package a skill', async () => {
|
|
await rig.setup('skill-creator scripts e2e');
|
|
const skillName = 'e2e-test-skill';
|
|
const tempDir = rig.testDir!;
|
|
|
|
// 1. Initialize
|
|
execSync(`node "${initScript}" ${skillName} --path "${tempDir}"`, {
|
|
stdio: 'inherit',
|
|
});
|
|
const skillDir = path.join(tempDir, skillName);
|
|
|
|
expect(fs.existsSync(skillDir)).toBe(true);
|
|
expect(fs.existsSync(path.join(skillDir, 'SKILL.md'))).toBe(true);
|
|
expect(
|
|
fs.existsSync(path.join(skillDir, 'scripts/example_script.cjs')),
|
|
).toBe(true);
|
|
|
|
// 2. Validate (should have warning initially due to TODOs)
|
|
const validateOutputInitial = execSync(
|
|
`node "${validateScript}" "${skillDir}" 2>&1`,
|
|
{ encoding: 'utf8' },
|
|
);
|
|
expect(validateOutputInitial).toContain('⚠️ Found unresolved TODO');
|
|
|
|
// 3. Package (should fail due to TODOs)
|
|
try {
|
|
execSync(`node "${packageScript}" "${skillDir}" "${tempDir}"`, {
|
|
stdio: 'pipe',
|
|
});
|
|
throw new Error('Packaging should have failed due to TODOs');
|
|
} catch (err: unknown) {
|
|
expect((err as Error).message).toContain('Command failed');
|
|
}
|
|
|
|
// 4. Fix SKILL.md (remove TODOs)
|
|
let content = fs.readFileSync(path.join(skillDir, 'SKILL.md'), 'utf8');
|
|
// More aggressive global replace for all TODO patterns
|
|
content = content.replace(/TODO:[^\n]*/g, 'Fixed');
|
|
content = content.replace(/\[TODO:[^\]]*\]/g, 'Fixed');
|
|
fs.writeFileSync(path.join(skillDir, 'SKILL.md'), content);
|
|
|
|
// Also remove TODOs from example scripts
|
|
const exampleScriptPath = path.join(skillDir, 'scripts/example_script.cjs');
|
|
let scriptContent = fs.readFileSync(exampleScriptPath, 'utf8');
|
|
scriptContent = scriptContent.replace(/TODO:[^\n]*/g, 'Fixed');
|
|
fs.writeFileSync(exampleScriptPath, scriptContent);
|
|
|
|
// 4. Validate again (should pass now)
|
|
const validateOutput = execSync(`node "${validateScript}" "${skillDir}"`, {
|
|
encoding: 'utf8',
|
|
});
|
|
expect(validateOutput).toContain('Skill is valid!');
|
|
|
|
// 5. Package
|
|
execSync(`node "${packageScript}" "${skillDir}" "${tempDir}"`, {
|
|
stdio: 'inherit',
|
|
});
|
|
const skillFile = path.join(tempDir, `${skillName}.skill`);
|
|
expect(fs.existsSync(skillFile)).toBe(true);
|
|
|
|
// 6. Verify zip content (should NOT have nested directory)
|
|
// Use unzip -l if available, otherwise fallback to tar -tf (common on Windows)
|
|
let zipList: string;
|
|
try {
|
|
zipList = execSync(`unzip -l "${skillFile}"`, { encoding: 'utf8' });
|
|
} catch {
|
|
zipList = execSync(`tar -tf "${skillFile}"`, { encoding: 'utf8' });
|
|
}
|
|
expect(zipList).toContain('SKILL.md');
|
|
expect(zipList).not.toContain(`${skillName}/SKILL.md`);
|
|
});
|
|
});
|