BMAD-METHOD/.patch/477/test-unit-prompt-skipping.t...

204 lines
6.2 KiB
JavaScript

/**
* Question Skipping Unit Tests
* Tests for skipping questions during update installations
* File: test/unit/prompt-skipping.test.js
*/
const { PromptHandler } = require('../../tools/cli/lib/ui');
const { ManifestConfigLoader } = require('../../tools/cli/lib/config-loader');
describe('Question Skipping', () => {
let promptHandler;
let configLoader;
beforeEach(() => {
promptHandler = new PromptHandler();
configLoader = new ManifestConfigLoader();
});
describe('skipQuestion', () => {
// Test 4.1: Skip Question When Update with Config
it('should skip question and return config value when isUpdate=true and config exists', async () => {
const mockConfig = {
prd_sharding: true,
getConfig: jest.fn(() => true),
hasConfig: jest.fn(() => true),
};
const result = await promptHandler.askPrdSharding({ isUpdate: true, config: mockConfig });
expect(result).toBe(true);
expect(mockConfig.hasConfig).toHaveBeenCalledWith('prd_sharding');
});
// Test 4.2: Ask Question When Fresh Install
it('should ask question on fresh install (isUpdate=false)', async () => {
const mockInquirer = jest.spyOn(promptHandler, 'prompt').mockResolvedValueOnce({
prd_sharding: true,
});
const result = await promptHandler.askPrdSharding({
isUpdate: false,
config: {},
});
expect(mockInquirer).toHaveBeenCalled();
expect(result).toBe(true);
mockInquirer.mockRestore();
});
// Test 4.3: Ask Question When Config Missing
it('should ask question if config missing on update', async () => {
const mockConfig = {
hasConfig: jest.fn(() => false),
};
const mockInquirer = jest.spyOn(promptHandler, 'prompt').mockResolvedValueOnce({
architecture_sharding: false,
});
const result = await promptHandler.askArchitectureSharding({ isUpdate: true, config: mockConfig });
expect(mockInquirer).toHaveBeenCalled();
expect(result).toBe(false);
mockInquirer.mockRestore();
});
// Test 4.4: Log Skipped Questions
it('should log when question is skipped', async () => {
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
const mockConfig = {
getConfig: jest.fn(() => 'full'),
hasConfig: jest.fn(() => true),
};
await promptHandler.askInstallType({ isUpdate: true, config: mockConfig });
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Skipping question'));
consoleLogSpy.mockRestore();
});
// Test 4.5: Multiple Questions Skipped
it('should skip all applicable questions on update', async () => {
const mockConfig = {
getConfig: jest.fn((key, fallback) => {
const values = {
prd_sharding: true,
architecture_sharding: false,
doc_organization: 'by-module',
install_type: 'full',
};
return values[key] || fallback;
}),
hasConfig: jest.fn(() => true),
};
const results = await Promise.all([
promptHandler.askPrdSharding({ isUpdate: true, config: mockConfig }),
promptHandler.askArchitectureSharding({ isUpdate: true, config: mockConfig }),
promptHandler.askDocOrganization({ isUpdate: true, config: mockConfig }),
promptHandler.askInstallType({ isUpdate: true, config: mockConfig }),
]);
expect(results).toEqual([true, false, 'by-module', 'full']);
// Each should have checked hasConfig
expect(mockConfig.hasConfig.mock.calls.length).toBe(4);
});
});
describe('prompt behavior during updates', () => {
it('should not display UI when skipping question', async () => {
const mockConfig = {
getConfig: jest.fn(() => 'value'),
hasConfig: jest.fn(() => true),
};
const consoleLogSpy = jest.spyOn(console, 'log').mockImplementation();
await promptHandler.askConfigQuestion('test_key', {
isUpdate: true,
config: mockConfig,
});
// Should log skip message but not the question itself
expect(consoleLogSpy).toHaveBeenCalledWith(expect.stringContaining('Skipping'));
consoleLogSpy.mockRestore();
});
it('should handle null/undefined defaults gracefully', async () => {
const mockConfig = {
getConfig: jest.fn(() => {}),
hasConfig: jest.fn(() => true),
};
const mockInquirer = jest.spyOn(promptHandler, 'prompt').mockResolvedValueOnce({
answer: 'user-provided',
});
const result = await promptHandler.askConfigQuestion('missing_key', {
isUpdate: true,
config: mockConfig,
});
expect(result).toBe('user-provided');
mockInquirer.mockRestore();
});
});
describe('isUpdate flag propagation', () => {
it('should pass isUpdate flag through prompt pipeline', () => {
const flags = {
isUpdate: true,
config: {},
};
expect(flags.isUpdate).toBe(true);
expect(flags.config).toBeDefined();
});
it('should distinguish fresh install from update', () => {
const freshInstallFlags = { isUpdate: false };
const updateFlags = { isUpdate: true };
expect(freshInstallFlags.isUpdate).toBe(false);
expect(updateFlags.isUpdate).toBe(true);
});
});
describe('backward compatibility', () => {
it('should handle missing isUpdate flag (default to fresh install)', async () => {
const mockInquirer = jest.spyOn(promptHandler, 'prompt').mockResolvedValueOnce({
answer: 'default-behavior',
});
// When isUpdate is not provided, should ask question
const result = await promptHandler.askConfigQuestion('key', {});
expect(mockInquirer).toHaveBeenCalled();
mockInquirer.mockRestore();
});
it('should handle missing config object', async () => {
const mockInquirer = jest.spyOn(promptHandler, 'prompt').mockResolvedValueOnce({
answer: 'fallback',
});
const result = await promptHandler.askConfigQuestion('key', {
isUpdate: true,
// config intentionally missing
});
expect(mockInquirer).toHaveBeenCalled();
mockInquirer.mockRestore();
});
});
});