Merge branch 'main' into copilot/vscode1758504985723
This commit is contained in:
commit
8f9437646a
@ -137,13 +137,17 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clean up files that are no longer enabled (requirement #3: Toggling instructions off will remove them)
|
||||||
|
const cleanupSummary = cleanupDisabledFiles(outputDir, effectivelyEnabledSets, rootDir);
|
||||||
|
|
||||||
// Process prompts using effective states
|
// Process prompts using effective states
|
||||||
for (const promptName of effectivelyEnabledSets.prompts) {
|
for (const promptName of effectivelyEnabledSets.prompts) {
|
||||||
const sourcePath = path.join(rootDir, "prompts", `${promptName}.prompt.md`);
|
const sourcePath = path.join(rootDir, "prompts", `${promptName}.prompt.md`);
|
||||||
if (fs.existsSync(sourcePath)) {
|
if (fs.existsSync(sourcePath)) {
|
||||||
const destPath = path.join(outputDir, "prompts", `${promptName}.prompt.md`);
|
const destPath = path.join(outputDir, "prompts", `${promptName}.prompt.md`);
|
||||||
copyFile(sourcePath, destPath);
|
if (copyFile(sourcePath, destPath)) {
|
||||||
copiedCount++;
|
copiedCount++;
|
||||||
|
}
|
||||||
summary.prompts++;
|
summary.prompts++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -153,8 +157,9 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
const sourcePath = path.join(rootDir, "instructions", `${instructionName}.instructions.md`);
|
const sourcePath = path.join(rootDir, "instructions", `${instructionName}.instructions.md`);
|
||||||
if (fs.existsSync(sourcePath)) {
|
if (fs.existsSync(sourcePath)) {
|
||||||
const destPath = path.join(outputDir, "instructions", `${instructionName}.instructions.md`);
|
const destPath = path.join(outputDir, "instructions", `${instructionName}.instructions.md`);
|
||||||
copyFile(sourcePath, destPath);
|
if (copyFile(sourcePath, destPath)) {
|
||||||
copiedCount++;
|
copiedCount++;
|
||||||
|
}
|
||||||
summary.instructions++;
|
summary.instructions++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -164,8 +169,9 @@ async function applyConfig(configPath = "awesome-copilot.config.yml") {
|
|||||||
const sourcePath = path.join(rootDir, "chatmodes", `${chatmodeName}.chatmode.md`);
|
const sourcePath = path.join(rootDir, "chatmodes", `${chatmodeName}.chatmode.md`);
|
||||||
if (fs.existsSync(sourcePath)) {
|
if (fs.existsSync(sourcePath)) {
|
||||||
const destPath = path.join(outputDir, "chatmodes", `${chatmodeName}.chatmode.md`);
|
const destPath = path.join(outputDir, "chatmodes", `${chatmodeName}.chatmode.md`);
|
||||||
copyFile(sourcePath, destPath);
|
if (copyFile(sourcePath, destPath)) {
|
||||||
copiedCount++;
|
copiedCount++;
|
||||||
|
}
|
||||||
summary.chatmodes++;
|
summary.chatmodes++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -203,11 +209,66 @@ function ensureDirectoryExists(dirPath) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Copy file from source to destination
|
* Copy file from source to destination with idempotency check
|
||||||
*/
|
*/
|
||||||
function copyFile(sourcePath, destPath) {
|
function copyFile(sourcePath, destPath) {
|
||||||
|
// Check if destination exists and has same content (idempotency)
|
||||||
|
if (fs.existsSync(destPath)) {
|
||||||
|
const sourceContent = fs.readFileSync(sourcePath, 'utf8');
|
||||||
|
const destContent = fs.readFileSync(destPath, 'utf8');
|
||||||
|
|
||||||
|
if (sourceContent === destContent) {
|
||||||
|
console.log(`✓ Already exists and up-to-date: ${path.basename(sourcePath)}`);
|
||||||
|
return false; // No copy needed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fs.copyFileSync(sourcePath, destPath);
|
fs.copyFileSync(sourcePath, destPath);
|
||||||
console.log(`✓ Copied: ${path.basename(sourcePath)}`);
|
console.log(`✓ Copied: ${path.basename(sourcePath)}`);
|
||||||
|
return true; // File was copied
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up files in the output directory that are no longer enabled.
|
||||||
|
*
|
||||||
|
* @param {string} outputDir - The root directory where generated files are stored.
|
||||||
|
* @param {Object} effectivelyEnabledSets - An object mapping section names to Sets of enabled item names.
|
||||||
|
* @param {string} rootDir - The root directory of the project (used for path resolution).
|
||||||
|
* @returns {Object} Summary of the number of files removed per section.
|
||||||
|
*/
|
||||||
|
function cleanupDisabledFiles(outputDir, effectivelyEnabledSets, rootDir) {
|
||||||
|
prompts: 0,
|
||||||
|
instructions: 0,
|
||||||
|
chatmodes: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
const sections = [
|
||||||
|
{ name: "prompts", ext: ".prompt.md" },
|
||||||
|
{ name: "instructions", ext: ".instructions.md" },
|
||||||
|
{ name: "chatmodes", ext: ".chatmode.md" }
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const section of sections) {
|
||||||
|
const sectionDir = path.join(outputDir, section.name);
|
||||||
|
if (!fs.existsSync(sectionDir)) continue;
|
||||||
|
|
||||||
|
const existingFiles = fs.readdirSync(sectionDir);
|
||||||
|
for (const fileName of existingFiles) {
|
||||||
|
if (!fileName.endsWith(section.ext)) continue;
|
||||||
|
|
||||||
|
const itemName = fileName.replace(section.ext, '');
|
||||||
|
|
||||||
|
// Check if this item is still enabled
|
||||||
|
if (!effectivelyEnabledSets[section.name].has(itemName)) {
|
||||||
|
const filePath = path.join(sectionDir, fileName);
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
cleanupSummary[section.name]++;
|
||||||
|
console.log(`🗑️ Removed: ${section.name}/${fileName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return cleanupSummary;
|
||||||
}
|
}
|
||||||
|
|
||||||
// CLI usage
|
// CLI usage
|
||||||
|
|||||||
@ -54,9 +54,17 @@ const commands = {
|
|||||||
|
|
||||||
toggle: {
|
toggle: {
|
||||||
description: "Enable or disable prompts, instructions, chat modes, or collections",
|
description: "Enable or disable prompts, instructions, chat modes, or collections",
|
||||||
usage: "awesome-copilot toggle <section> <name|all> [on|off] [--config <file>]",
|
usage: "awesome-copilot toggle <section> <name|all> [on|off] [--all] [--apply] [--config <file>]",
|
||||||
|
action: async (args) => {
|
||||||
|
await handleToggleCommand(args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
reset: {
|
||||||
|
description: "Reset .awesome-copilot directory (remove all files but keep structure)",
|
||||||
|
usage: "awesome-copilot reset [config-file]",
|
||||||
action: (args) => {
|
action: (args) => {
|
||||||
handleToggleCommand(args);
|
handleResetCommand(args);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -87,9 +95,12 @@ function showHelp() {
|
|||||||
console.log(" awesome-copilot init # Create default config file");
|
console.log(" awesome-copilot init # Create default config file");
|
||||||
console.log(" awesome-copilot init my-config.yml # Create named config file");
|
console.log(" awesome-copilot init my-config.yml # Create named config file");
|
||||||
console.log(" awesome-copilot apply # Apply default config");
|
console.log(" awesome-copilot apply # Apply default config");
|
||||||
|
console.log(" awesome-copilot reset # Clear .awesome-copilot directory");
|
||||||
console.log(" awesome-copilot list instructions # See which instructions are enabled");
|
console.log(" awesome-copilot list instructions # See which instructions are enabled");
|
||||||
console.log(" awesome-copilot toggle prompts create-readme on # Enable a specific prompt");
|
console.log(" awesome-copilot toggle prompts create-readme on # Enable a specific prompt");
|
||||||
console.log(" awesome-copilot toggle instructions all off --config team.yml # Disable all instructions");
|
console.log(" awesome-copilot toggle instructions all off --config team.yml # Disable all instructions");
|
||||||
|
console.log(" awesome-copilot toggle prompts all on --all # Force enable ALL prompts (override explicit settings)");
|
||||||
|
console.log(" awesome-copilot toggle collections testing-automation on --apply # Enable collection and apply");
|
||||||
console.log("");
|
console.log("");
|
||||||
console.log("Workflow:");
|
console.log("Workflow:");
|
||||||
console.log(" 1. Run 'awesome-copilot init' to create a configuration file");
|
console.log(" 1. Run 'awesome-copilot init' to create a configuration file");
|
||||||
@ -158,9 +169,9 @@ function handleListCommand(rawArgs) {
|
|||||||
const effectiveState = effectiveStates[section]?.[itemName];
|
const effectiveState = effectiveStates[section]?.[itemName];
|
||||||
if (effectiveState) {
|
if (effectiveState) {
|
||||||
const symbol = effectiveState.enabled ? "✓" : " ";
|
const symbol = effectiveState.enabled ? "✓" : " ";
|
||||||
const reasonText = effectiveState.enabled
|
const reasonText = effectiveState.reason === "explicit"
|
||||||
? ` (${effectiveState.reason})`
|
? ` (${effectiveState.reason})`
|
||||||
: "";
|
: effectiveState.enabled ? ` (${effectiveState.reason})` : "";
|
||||||
console.log(` [${symbol}] ${itemName}${reasonText}`);
|
console.log(` [${symbol}] ${itemName}${reasonText}`);
|
||||||
} else {
|
} else {
|
||||||
console.log(` [ ] ${itemName}`);
|
console.log(` [ ] ${itemName}`);
|
||||||
@ -172,18 +183,23 @@ function handleListCommand(rawArgs) {
|
|||||||
console.log("\nUse 'awesome-copilot toggle' to enable or disable specific items.");
|
console.log("\nUse 'awesome-copilot toggle' to enable or disable specific items.");
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleToggleCommand(rawArgs) {
|
async function handleToggleCommand(rawArgs) {
|
||||||
const { args, configPath } = extractConfigOption(rawArgs);
|
const { args, configPath, flags } = extractToggleOptions(rawArgs);
|
||||||
|
|
||||||
if (args.length < 2) {
|
if (args.length < 2) {
|
||||||
throw new Error("Usage: awesome-copilot toggle <section> <name|all> [on|off] [--config <file>]");
|
throw new Error("Usage: awesome-copilot toggle <section> <name|all> [on|off] [--all] [--apply] [--config <file>]");
|
||||||
}
|
}
|
||||||
|
|
||||||
const section = validateSectionType(args[0]);
|
const section = validateSectionType(args[0]);
|
||||||
const itemName = args[1];
|
let itemName = args[1];
|
||||||
const stateArg = args[2];
|
const stateArg = args[2];
|
||||||
const desiredState = stateArg ? parseStateToken(stateArg) : null;
|
const desiredState = stateArg ? parseStateToken(stateArg) : null;
|
||||||
|
|
||||||
|
// Handle --all flag
|
||||||
|
if (flags.all) {
|
||||||
|
itemName = "all";
|
||||||
|
}
|
||||||
|
|
||||||
const availableItems = getAllAvailableItems(section);
|
const availableItems = getAllAvailableItems(section);
|
||||||
const availableSet = new Set(availableItems);
|
const availableSet = new Set(availableItems);
|
||||||
if (!availableItems.length) {
|
if (!availableItems.length) {
|
||||||
@ -260,10 +276,20 @@ function handleToggleCommand(rawArgs) {
|
|||||||
if (desiredState === null) {
|
if (desiredState === null) {
|
||||||
throw new Error("Specify 'on' or 'off' when toggling all items.");
|
throw new Error("Specify 'on' or 'off' when toggling all items.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enhanced --all behavior: override ALL items, even explicit ones
|
||||||
|
if (flags.all) {
|
||||||
|
console.log(`${desiredState ? "Force-enabling" : "Force-disabling"} ALL ${SECTION_METADATA[section].label.toLowerCase()} (including explicit overrides).`);
|
||||||
|
availableItems.forEach(item => {
|
||||||
|
sectionState[item] = desiredState;
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Regular "all" behavior: set all items explicitly but respect that they are now explicit
|
||||||
availableItems.forEach(item => {
|
availableItems.forEach(item => {
|
||||||
sectionState[item] = desiredState;
|
sectionState[item] = desiredState;
|
||||||
});
|
});
|
||||||
console.log(`${desiredState ? "Enabled" : "Disabled"} all ${SECTION_METADATA[section].label.toLowerCase()}.`);
|
console.log(`${desiredState ? "Enabled" : "Disabled"} all ${SECTION_METADATA[section].label.toLowerCase()}.`);
|
||||||
|
}
|
||||||
|
|
||||||
if (section === "instructions" && desiredState) {
|
if (section === "instructions" && desiredState) {
|
||||||
console.log("⚠️ Enabling every instruction can exceed Copilot Agent's context window. Consider enabling only what you need.");
|
console.log("⚠️ Enabling every instruction can exceed Copilot Agent's context window. Consider enabling only what you need.");
|
||||||
@ -295,8 +321,54 @@ function handleToggleCommand(rawArgs) {
|
|||||||
console.log(`Estimated ${SECTION_METADATA[section].label.toLowerCase()} context size: ${formatNumber(totalCharacters)} characters.`);
|
console.log(`Estimated ${SECTION_METADATA[section].label.toLowerCase()} context size: ${formatNumber(totalCharacters)} characters.`);
|
||||||
}
|
}
|
||||||
maybeWarnAboutContext(section, totalCharacters);
|
maybeWarnAboutContext(section, totalCharacters);
|
||||||
|
|
||||||
|
// Handle automatic application
|
||||||
|
if (flags.apply) {
|
||||||
|
console.log("\n🔄 Applying configuration automatically...");
|
||||||
|
await applyConfig(configPath);
|
||||||
|
} else {
|
||||||
console.log("Run 'awesome-copilot apply' to copy updated selections into your project.");
|
console.log("Run 'awesome-copilot apply' to copy updated selections into your project.");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractToggleOptions(rawArgs) {
|
||||||
|
const args = [...rawArgs];
|
||||||
|
let configPath = DEFAULT_CONFIG_PATH;
|
||||||
|
const flags = {
|
||||||
|
all: false,
|
||||||
|
apply: false
|
||||||
|
};
|
||||||
|
|
||||||
|
// Process flags
|
||||||
|
for (let i = args.length - 1; i >= 0; i--) {
|
||||||
|
const arg = args[i];
|
||||||
|
|
||||||
|
if (arg === "--all") {
|
||||||
|
flags.all = true;
|
||||||
|
args.splice(i, 1);
|
||||||
|
} else if (arg === "--apply") {
|
||||||
|
flags.apply = true;
|
||||||
|
args.splice(i, 1);
|
||||||
|
} else if (CONFIG_FLAG_ALIASES.includes(arg)) {
|
||||||
|
if (i === args.length - 1) {
|
||||||
|
throw new Error("Missing configuration file after --config flag.");
|
||||||
|
}
|
||||||
|
configPath = args[i + 1];
|
||||||
|
args.splice(i, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for config file as last argument
|
||||||
|
if (args.length > 0) {
|
||||||
|
const potentialPath = args[args.length - 1];
|
||||||
|
if (isConfigFilePath(potentialPath)) {
|
||||||
|
configPath = potentialPath;
|
||||||
|
args.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return { args, configPath, flags };
|
||||||
|
}
|
||||||
|
|
||||||
function extractConfigOption(rawArgs) {
|
function extractConfigOption(rawArgs) {
|
||||||
const args = [...rawArgs];
|
const args = [...rawArgs];
|
||||||
@ -396,6 +468,51 @@ function findClosestMatch(target, candidates) {
|
|||||||
return candidates.find(candidate => candidate.toLowerCase().includes(normalizedTarget));
|
return candidates.find(candidate => candidate.toLowerCase().includes(normalizedTarget));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleResetCommand(rawArgs) {
|
||||||
|
const { args, configPath } = extractConfigOption(rawArgs);
|
||||||
|
|
||||||
|
const { config } = loadConfig(configPath);
|
||||||
|
const outputDir = config.project?.output_directory || ".awesome-copilot";
|
||||||
|
|
||||||
|
if (!fs.existsSync(outputDir)) {
|
||||||
|
console.log(`📁 Directory ${outputDir} does not exist - nothing to reset.`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`🔄 Resetting ${outputDir} directory...`);
|
||||||
|
|
||||||
|
let removedCount = 0;
|
||||||
|
|
||||||
|
// Remove all files from subdirectories but keep the directory structure
|
||||||
|
const subdirs = ["prompts", "instructions", "chatmodes"];
|
||||||
|
for (const subdir of subdirs) {
|
||||||
|
const subdirPath = path.join(outputDir, subdir);
|
||||||
|
if (fs.existsSync(subdirPath)) {
|
||||||
|
const files = fs.readdirSync(subdirPath);
|
||||||
|
for (const file of files) {
|
||||||
|
const filePath = path.join(subdirPath, file);
|
||||||
|
if (fs.statSync(filePath).isFile()) {
|
||||||
|
fs.unlinkSync(filePath);
|
||||||
|
removedCount++;
|
||||||
|
console.log(`🗑️ Removed: ${subdir}/${file}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove README.md if it exists
|
||||||
|
const readmePath = path.join(outputDir, "README.md");
|
||||||
|
if (fs.existsSync(readmePath)) {
|
||||||
|
fs.unlinkSync(readmePath);
|
||||||
|
removedCount++;
|
||||||
|
console.log(`🗑️ Removed: README.md`);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(`\n✅ Reset complete! Removed ${removedCount} files.`);
|
||||||
|
console.log(`📁 Directory structure preserved: ${outputDir}/`);
|
||||||
|
console.log("Run 'awesome-copilot apply' to repopulate with current configuration.");
|
||||||
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const args = process.argv.slice(2);
|
const args = process.argv.slice(2);
|
||||||
|
|
||||||
|
|||||||
@ -105,8 +105,8 @@ async function runTests() {
|
|||||||
assert(result.success, 'Individual toggle should succeed');
|
assert(result.success, 'Individual toggle should succeed');
|
||||||
|
|
||||||
const listResult = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
|
const listResult = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
|
||||||
assert(listResult.stdout.includes('playwright-generate-test') && !listResult.stdout.includes('playwright-generate-test ('),
|
assert(listResult.stdout.includes('playwright-generate-test (explicit)'),
|
||||||
'Explicitly disabled item should not show reason');
|
'Explicitly disabled item should show explicit reason');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Test 6: Error handling for invalid commands
|
// Test 6: Error handling for invalid commands
|
||||||
|
|||||||
210
test-new-features.js
Executable file
210
test-new-features.js
Executable file
@ -0,0 +1,210 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for new features added to awesome-copilot
|
||||||
|
*/
|
||||||
|
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const { exec } = require('child_process');
|
||||||
|
const { promisify } = require('util');
|
||||||
|
|
||||||
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
|
// Change to project directory for tests
|
||||||
|
process.chdir(__dirname);
|
||||||
|
|
||||||
|
const TEST_CONFIG = 'test-new-features.yml';
|
||||||
|
const TEST_OUTPUT_DIR = '.test-new-features-awesome-copilot';
|
||||||
|
|
||||||
|
function assert(condition, message) {
|
||||||
|
if (!condition) {
|
||||||
|
throw new Error(`Assertion failed: ${message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function cleanup() {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(TEST_CONFIG)) {
|
||||||
|
fs.unlinkSync(TEST_CONFIG);
|
||||||
|
}
|
||||||
|
if (fs.existsSync(TEST_OUTPUT_DIR)) {
|
||||||
|
fs.rmSync(TEST_OUTPUT_DIR, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
// Ignore cleanup errors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runCommand(command) {
|
||||||
|
try {
|
||||||
|
const { stdout, stderr } = await execAsync(command);
|
||||||
|
return { success: true, stdout, stderr };
|
||||||
|
} catch (error) {
|
||||||
|
return { success: false, stdout: error.stdout || '', stderr: error.stderr || '', error };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setTestOutputDir(configFile) {
|
||||||
|
if (fs.existsSync(configFile)) {
|
||||||
|
let content = fs.readFileSync(configFile, 'utf8');
|
||||||
|
content = content.replace(/output_directory: "\.awesome-copilot"/, `output_directory: "${TEST_OUTPUT_DIR}"`);
|
||||||
|
fs.writeFileSync(configFile, content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runTests() {
|
||||||
|
console.log('Running tests for new awesome-copilot features...\n');
|
||||||
|
|
||||||
|
let passedTests = 0;
|
||||||
|
let totalTests = 0;
|
||||||
|
|
||||||
|
async function test(name, testFn) {
|
||||||
|
totalTests++;
|
||||||
|
try {
|
||||||
|
cleanup(); // Clean up before each test
|
||||||
|
await testFn();
|
||||||
|
console.log(`✅ ${name}`);
|
||||||
|
passedTests++;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`❌ ${name}: ${error.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test 1: Reset command
|
||||||
|
await test("Reset command clears .awesome-copilot directory", async () => {
|
||||||
|
await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
|
||||||
|
setTestOutputDir(TEST_CONFIG);
|
||||||
|
|
||||||
|
// Enable something and apply
|
||||||
|
await runCommand(`node awesome-copilot.js toggle collections testing-automation on --config ${TEST_CONFIG}`);
|
||||||
|
await runCommand(`node awesome-copilot.js apply ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Check files exist
|
||||||
|
const filesBefore = fs.readdirSync(TEST_OUTPUT_DIR, { recursive: true });
|
||||||
|
assert(filesBefore.some(f => f.includes('.md')), 'Should have some files before reset');
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
const resetResult = await runCommand(`node awesome-copilot.js reset ${TEST_CONFIG}`);
|
||||||
|
assert(resetResult.success, 'Reset should succeed');
|
||||||
|
|
||||||
|
// Check files are gone but directories remain
|
||||||
|
assert(fs.existsSync(TEST_OUTPUT_DIR), 'Output directory should still exist');
|
||||||
|
assert(fs.existsSync(path.join(TEST_OUTPUT_DIR, 'prompts')), 'Prompts directory should still exist');
|
||||||
|
const filesAfter = fs.readdirSync(TEST_OUTPUT_DIR, { recursive: true });
|
||||||
|
assert(!filesAfter.some(f => f.includes('.md')), 'Should have no .md files after reset');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 2: --apply flag
|
||||||
|
await test("--apply flag automatically applies after toggle", async () => {
|
||||||
|
await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
|
||||||
|
setTestOutputDir(TEST_CONFIG);
|
||||||
|
|
||||||
|
// Toggle with --apply flag
|
||||||
|
const result = await runCommand(`node awesome-copilot.js toggle collections testing-automation on --apply --config ${TEST_CONFIG}`);
|
||||||
|
assert(result.success, 'Toggle with --apply should succeed');
|
||||||
|
assert(result.stdout.includes('Applying configuration automatically'), 'Should show auto-apply message');
|
||||||
|
|
||||||
|
// Check files were applied
|
||||||
|
const files = fs.readdirSync(TEST_OUTPUT_DIR, { recursive: true });
|
||||||
|
assert(files.some(f => f.includes('.md')), 'Files should be automatically applied');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 3: --all flag force overrides
|
||||||
|
await test("--all flag overrides explicit settings", async () => {
|
||||||
|
await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Set explicit false
|
||||||
|
await runCommand(`node awesome-copilot.js toggle prompts create-readme off --config ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Check it's explicitly disabled
|
||||||
|
const listResult1 = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
|
||||||
|
assert(listResult1.stdout.includes('create-readme (explicit)'), 'Should show explicit disabled');
|
||||||
|
|
||||||
|
// Force enable all with --all flag
|
||||||
|
await runCommand(`node awesome-copilot.js toggle prompts all on --all --config ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Check it's now explicitly enabled
|
||||||
|
const listResult2 = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
|
||||||
|
assert(listResult2.stdout.includes('create-readme (explicit)') && listResult2.stdout.includes('[✓]'), 'Should be explicitly enabled');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 4: File cleanup on disable
|
||||||
|
await test("Files are removed when items are disabled", async () => {
|
||||||
|
await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
|
||||||
|
setTestOutputDir(TEST_CONFIG);
|
||||||
|
|
||||||
|
// Enable and apply
|
||||||
|
await runCommand(`node awesome-copilot.js toggle collections testing-automation on --apply --config ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
const filesBefore = fs.readdirSync(TEST_OUTPUT_DIR, { recursive: true });
|
||||||
|
assert(filesBefore.some(f => f.includes('.md')), 'Should have files after enable');
|
||||||
|
|
||||||
|
// Disable and apply
|
||||||
|
await runCommand(`node awesome-copilot.js toggle collections testing-automation off --apply --config ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
const filesAfter = fs.readdirSync(TEST_OUTPUT_DIR, { recursive: true });
|
||||||
|
assert(!filesAfter.some(f => f.includes('.md')), 'Should have no files after disable');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 5: Idempotency with file content checking
|
||||||
|
await test("Idempotency checks file content correctly", async () => {
|
||||||
|
await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
|
||||||
|
setTestOutputDir(TEST_CONFIG);
|
||||||
|
|
||||||
|
// Enable and apply
|
||||||
|
await runCommand(`node awesome-copilot.js toggle collections testing-automation on --apply --config ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Apply again (should show already up-to-date)
|
||||||
|
const result = await runCommand(`node awesome-copilot.js apply ${TEST_CONFIG}`);
|
||||||
|
assert(result.stdout.includes('Already exists and up-to-date'), 'Should show idempotency message');
|
||||||
|
assert(!result.stdout.includes('Copied:'), 'Should not copy any files');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Test 6: Explicit overrides preserved across collection toggles
|
||||||
|
await test("Explicit overrides preserved across collection toggles", async () => {
|
||||||
|
await runCommand(`node awesome-copilot.js init ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Enable collection
|
||||||
|
await runCommand(`node awesome-copilot.js toggle collections testing-automation on --config ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Set explicit override
|
||||||
|
await runCommand(`node awesome-copilot.js toggle prompts playwright-generate-test off --config ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Toggle collection off and on
|
||||||
|
await runCommand(`node awesome-copilot.js toggle collections testing-automation off --config ${TEST_CONFIG}`);
|
||||||
|
const toggleResult = await runCommand(`node awesome-copilot.js toggle collections testing-automation on --config ${TEST_CONFIG}`);
|
||||||
|
|
||||||
|
// Check that the explicit override is preserved (playwright-generate-test should NOT be in enabled list)
|
||||||
|
assert(!toggleResult.stdout.includes('playwright-generate-test'), 'Explicit override should be preserved');
|
||||||
|
|
||||||
|
// Double-check with list command
|
||||||
|
const listResult = await runCommand(`node awesome-copilot.js list prompts --config ${TEST_CONFIG}`);
|
||||||
|
assert(listResult.stdout.includes('playwright-generate-test (explicit)'), "'playwright-generate-test (explicit)' should be present in the list output");
|
||||||
|
assert(!listResult.stdout.includes('[✓] playwright-generate-test'), "'[✓] playwright-generate-test' should NOT be present in the list output (should remain explicitly disabled)");
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`\nNew Features Test Results: ${passedTests}/${totalTests} passed`);
|
||||||
|
|
||||||
|
cleanup(); // Final cleanup
|
||||||
|
|
||||||
|
if (passedTests === totalTests) {
|
||||||
|
console.log('🎉 All new features tests passed!');
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
console.log('💥 Some new features tests failed!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) {
|
||||||
|
runTests().then(success => {
|
||||||
|
process.exit(success ? 0 : 1);
|
||||||
|
}).catch(error => {
|
||||||
|
console.error('Test runner error:', error);
|
||||||
|
cleanup();
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { runTests };
|
||||||
Loading…
x
Reference in New Issue
Block a user