Skip to main content
Glama
wizard-page.test.ts10.4 kB
/** * Wizard Page Tests * * @package WP_Navigator_Pro * @since 2.5.0 */ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { Writable } from 'stream'; import { createWizardPage, formatStepTitle, formatStepComplete, formatStepError, createStepProgressBar, type WizardPage, } from './wizard-page.js'; import { CLEAR_SCREEN, CURSOR_HOME } from './ansi.js'; import { symbols } from './components.js'; /** * Create a mock output stream that captures writes */ function createMockOutput(): { stream: NodeJS.WriteStream; output: string[]; clear: () => void } { const output: string[] = []; const stream = new Writable({ write(chunk, encoding, callback) { output.push(chunk.toString()); callback(); }, }) as unknown as NodeJS.WriteStream; // Mock isTTY for testing TTY behavior Object.defineProperty(stream, 'isTTY', { value: true, writable: true }); return { stream, output, clear: () => { output.length = 0; }, }; } describe('Wizard Page', () => { const originalIsTTY = process.stdout.isTTY; const originalColumns = process.stdout.columns; const originalRows = process.stdout.rows; const originalTerm = process.env.TERM; beforeEach(() => { // Set up TTY environment Object.defineProperty(process.stdout, 'isTTY', { value: true, writable: true }); Object.defineProperty(process.stdout, 'columns', { value: 80, writable: true }); Object.defineProperty(process.stdout, 'rows', { value: 24, writable: true }); process.env.TERM = 'xterm-256color'; }); afterEach(() => { Object.defineProperty(process.stdout, 'isTTY', { value: originalIsTTY, writable: true }); Object.defineProperty(process.stdout, 'columns', { value: originalColumns, writable: true }); Object.defineProperty(process.stdout, 'rows', { value: originalRows, writable: true }); if (originalTerm === undefined) { delete process.env.TERM; } else { process.env.TERM = originalTerm; } }); describe('createWizardPage', () => { it('should create a wizard page instance', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Test Wizard', currentStep: 1, totalSteps: 5, output: mock.stream, }); expect(wizardPage).toBeDefined(); expect(typeof wizardPage.clear).toBe('function'); expect(typeof wizardPage.render).toBe('function'); expect(typeof wizardPage.renderStep).toBe('function'); expect(typeof wizardPage.updateStep).toBe('function'); expect(typeof wizardPage.showNavigationHints).toBe('function'); expect(typeof wizardPage.isSupported).toBe('function'); expect(typeof wizardPage.getSize).toBe('function'); expect(typeof wizardPage.getPage).toBe('function'); }); it('should return the underlying page instance', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Test Wizard', currentStep: 1, totalSteps: 3, output: mock.stream, }); const page = wizardPage.getPage(); expect(page).toBeDefined(); expect(typeof page.clear).toBe('function'); }); }); describe('renderStep', () => { it('should render a complete step with header and footer', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'WP Navigator Setup', currentStep: 2, totalSteps: 5, output: mock.stream, }); wizardPage.renderStep('Step 2 content here'); const combined = mock.output.join(''); expect(combined).toContain('WP Navigator Setup'); expect(combined).toContain('2/5'); expect(combined).toContain('Step 2 content here'); }); it('should include back hint for step > 1', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 2, totalSteps: 3, showBackHint: true, output: mock.stream, }); wizardPage.renderStep('Content'); const combined = mock.output.join(''); expect(combined).toContain('[B]ack'); }); it('should not include back hint for step 1', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 1, totalSteps: 3, output: mock.stream, }); wizardPage.renderStep('Content'); const combined = mock.output.join(''); expect(combined).not.toContain('[B]ack'); }); it('should include help and quit hints by default', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 1, totalSteps: 3, output: mock.stream, }); wizardPage.renderStep('Content'); const combined = mock.output.join(''); expect(combined).toContain('[H]elp'); expect(combined).toContain('[Q]uit'); }); it('should use custom footer when provided', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 1, totalSteps: 3, customFooter: 'Custom footer text', output: mock.stream, }); wizardPage.renderStep('Content'); const combined = mock.output.join(''); expect(combined).toContain('Custom footer text'); expect(combined).not.toContain('[H]elp'); }); }); describe('updateStep', () => { it('should update current step', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 1, totalSteps: 5, output: mock.stream, }); wizardPage.updateStep(3); wizardPage.renderStep('Step 3'); const combined = mock.output.join(''); expect(combined).toContain('3/5'); }); it('should update total steps if provided', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 1, totalSteps: 3, output: mock.stream, }); wizardPage.updateStep(2, 6); wizardPage.renderStep('Step 2'); const combined = mock.output.join(''); expect(combined).toContain('2/6'); }); it('should enable back hint after updating to step > 1', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 1, totalSteps: 3, output: mock.stream, }); // Initially step 1 - no back hint wizardPage.renderStep('Step 1'); expect(mock.output.join('')).not.toContain('[B]ack'); mock.clear(); wizardPage.updateStep(2); wizardPage.renderStep('Step 2'); expect(mock.output.join('')).toContain('[B]ack'); }); }); describe('isSupported', () => { it('should return true in TTY environment', () => { const mock = createMockOutput(); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 1, totalSteps: 3, output: mock.stream, }); expect(wizardPage.isSupported()).toBe(true); }); it('should return false in non-TTY environment', () => { const mock = createMockOutput(); Object.defineProperty(mock.stream, 'isTTY', { value: false, writable: true }); const wizardPage = createWizardPage({ title: 'Wizard', currentStep: 1, totalSteps: 3, output: mock.stream, }); expect(wizardPage.isSupported()).toBe(false); }); }); }); describe('formatStepTitle', () => { it('should format step title with icon', () => { const title = formatStepTitle(1, 'Configure WordPress'); expect(title).toContain('Step 1'); expect(title).toContain('Configure WordPress'); expect(title).toContain(symbols.arrow); }); it('should format step title without icon', () => { const title = formatStepTitle(2, 'Install Plugin', { showIcon: false }); expect(title).toBe('Step 2: Install Plugin'); expect(title).not.toContain(symbols.arrow); }); it('should use custom icon', () => { const title = formatStepTitle(3, 'Verify', { icon: '★' }); expect(title).toContain('★'); expect(title).toContain('Step 3'); }); }); describe('formatStepComplete', () => { it('should format completion message', () => { const message = formatStepComplete(1); expect(message).toContain(symbols.success); expect(message).toContain('Step 1 complete'); }); it('should use custom message', () => { const message = formatStepComplete(2, 'Configuration saved!'); expect(message).toContain(symbols.success); expect(message).toContain('Configuration saved!'); }); }); describe('formatStepError', () => { it('should format error message', () => { const message = formatStepError(1, 'Connection failed'); expect(message).toContain(symbols.error); expect(message).toContain('Step 1 failed'); expect(message).toContain('Connection failed'); }); }); describe('createStepProgressBar', () => { it('should create progress bar with step count', () => { const bar = createStepProgressBar(2, 5); expect(bar).toContain('█'); expect(bar).toContain('░'); expect(bar).toContain('2/5'); }); it('should show percentage when requested', () => { const bar = createStepProgressBar(2, 5, { showPercentage: true }); expect(bar).toContain('40%'); expect(bar).not.toContain('2/5'); }); it('should respect custom width', () => { const bar = createStepProgressBar(5, 10, { width: 10 }); // 5/10 = 50%, so 5 filled + 5 empty + suffix const barPart = bar.split(' ')[0]; expect(barPart.length).toBeLessThanOrEqual(10); }); it('should handle 100% progress', () => { const bar = createStepProgressBar(5, 5); expect(bar).toContain('5/5'); expect(bar).toContain('█'); expect(bar).not.toContain('░'); }); it('should handle 0% progress', () => { const bar = createStepProgressBar(0, 5); expect(bar).toContain('0/5'); expect(bar).not.toContain('█'); expect(bar).toContain('░'); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/littlebearapps/wp-navigator-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server