@inquirer/testing
v3.2.0
Published
Inquirer testing utilities
Readme
@inquirer/testing
The @inquirer/testing package is Inquirer's answer to testing prompts built with @inquirer/core.
Installation
npm install @inquirer/testing --save-devyarn add @inquirer/testing --devUsage
This package provides two ways to test Inquirer prompts:
- Unit testing with
render()- Test individual prompts in isolation - E2E testing with
screen- Test full CLI applications that use Inquirer
Unit Testing with render()
The render() function creates and instruments a command line interface for testing a single prompt.
import { render } from '@inquirer/testing';
import input from '@inquirer/input';
describe('input prompt', () => {
it('handle simple use case', async () => {
const { answer, events, getScreen } = await render(input, {
message: 'What is your name',
});
expect(getScreen()).toMatchInlineSnapshot(`"? What is your name"`);
events.type('J');
expect(getScreen()).toMatchInlineSnapshot(`"? What is your name J"`);
events.type('ohn');
events.keypress('enter');
await expect(answer).resolves.toEqual('John');
expect(getScreen()).toMatchInlineSnapshot(`"? What is your name John"`);
});
});render() API
render takes 2 arguments:
- The Inquirer prompt to test (the return value of
createPrompt()) - The prompt configuration (the first prompt argument)
render returns a promise that resolves once the prompt is rendered. This promise returns:
answer(Promise) - Resolves when an answer is provided and validgetScreen(({ raw?: boolean }) => string) - Returns the current screen content. By default strips ANSI codesevents- Utilities to interact with the prompt:keypress(key: string | KeyObject)- Trigger a keypress eventtype(text: string)- Type text into the prompt
getFullOutput(() => Promise<string>) - Returns the full output interpreted through a virtual terminal, resolving ANSI escape sequences into the actual screen state
Unit Testing Example
You can refer to the @inquirer/input test suite for a comprehensive unit testing example using render().
E2E Testing with screen
For testing full CLI applications that use Inquirer prompts internally, use the framework-specific entry points:
Vitest
import { describe, it, expect } from 'vitest';
import { screen } from '@inquirer/testing/vitest';
// Import your CLI AFTER @inquirer/testing/vitest
import { runMyCli } from './my-cli.js';
describe('my CLI', () => {
it('asks for name and confirms', async () => {
const result = runMyCli();
// First prompt is immediately available
expect(screen.getScreen()).toContain('What is your name?');
screen.type('John');
screen.keypress('enter');
// Wait for next prompt
await screen.next();
expect(screen.getScreen()).toContain('Confirm?');
screen.keypress('enter');
await result;
});
});Jest
import { screen } from '@inquirer/testing/jest';
import { runMyCli } from './my-cli.js';
describe('my CLI', () => {
it('asks for name and confirms', async () => {
const result = runMyCli();
// First prompt is immediately available
expect(screen.getScreen()).toContain('What is your name?');
screen.type('John');
screen.keypress('enter');
// Wait for next prompt
await screen.next();
expect(screen.getScreen()).toContain('Confirm?');
screen.keypress('enter');
await result;
});
});screen API
The screen object provides:
next()- Wait for the next screen update (prompt transitions, validation errors, async updates). The initial prompt render is available immediately viagetScreen()— nonext()neededgetScreen({ raw?: boolean })- Get the current prompt screen content. By default strips ANSI codesgetFullOutput({ raw?: boolean })- Get all accumulated output interpreted through a virtual terminal (returns aPromise). By default resolves ANSI escape sequences into actual screen statetype(text)- Type text (writes to stream AND emits keypresses)keypress(key)- Send a keypress eventclear()- Reset screen state (called automatically before each test)
Mocking Third-Party Prompts
All @inquirer/* prompts are mocked automatically. To mock a third-party or custom prompt package, use wrapPrompt in your own mock call:
Vitest
import { screen, wrapPrompt } from '@inquirer/testing/vitest';
vi.mock('@my-company/custom-prompt', async (importOriginal) => {
const actual = await importOriginal<typeof import('@my-company/custom-prompt')>();
return { ...actual, default: wrapPrompt(actual.default) };
});Jest
In Jest, jest.mock() factories are hoisted before imports, so wrapPrompt must be accessed via jest.requireActual() inside the factory:
import { screen } from '@inquirer/testing/jest';
jest.mock('@my-company/custom-prompt', () => {
const { wrapPrompt } = jest.requireActual('@inquirer/testing/jest');
const actual = jest.requireActual('@my-company/custom-prompt');
return { ...actual, default: wrapPrompt(actual.default) };
});Important Notes
- Import order matters: Import
@inquirer/testing/vitestor@inquirer/testing/jestBEFORE importing modules that use Inquirer prompts - Editor prompt: The external editor is mocked —
screen.type()buffers text, andscreen.keypress('enter')submits it (same pattern as other prompts). Works with bothwaitForUserInput: trueandfalse - Sequential prompts: Multiple prompts are supported, but they must run sequentially (not concurrently)
E2E Testing Example
You can refer to the @inquirer/demo test suite for a comprehensive E2E testing example using screen.
License
Copyright (c) 2023 Simon Boudrias (twitter: @vaxilart) Licensed under the MIT license.
