@yeoman/adapter
Version:
Input Output adapter for yeoman's generator/environment stack
120 lines • 4.97 kB
JavaScript
import events from 'node:events';
import { Duplex } from 'node:stream';
import { createPrompt } from '@inquirer/core';
import { createLogger } from '../log.js';
import { createAdapterPromptModule } from '../inquirer.js';
const defaultConfig = {};
const isValueSet = (type, answer) => {
if (type === 'list' || type === 'select') {
// List prompt accepts any answer value including null
return answer !== undefined;
}
if (type === 'confirm') {
// Ensure that we don't replace `false` with default `true`
return answer || answer === false;
}
// Other prompts treat all falsy values to default
return Boolean(answer);
};
const createDummyPrompt = ({ calls }, options = {}) => {
const { mockedAnswers = {}, callback = answer => answer, throwOnMissingAnswer = false } = options;
return createPrompt((config, done) => {
let answer = mockedAnswers[config.name];
if (!isValueSet(config.type, answer)) {
if (answer === undefined && config.default === undefined) {
const missingAnswerMessage = `yeoman-test: question ${config.name ?? ''} was asked but answer was not provided`;
console.warn(missingAnswerMessage);
if (throwOnMissingAnswer) {
throw new Error(missingAnswerMessage);
}
}
answer = config.default;
if (answer === undefined && config.type === 'confirm') {
// Confirm prompt defaults to true
answer = true;
}
}
calls.push({ question: config, answer });
done(callback(answer, { question: config, answers: { [config.name]: answer } }));
return config.message;
});
};
export const defineConfig = (config) => Object.assign(defaultConfig, config);
export const getConfig = () => ({ ...defaultConfig });
/**
* @experimental
*/
export class TestAdapter {
promptModule;
diff;
log;
registerDummyPrompt;
mockedAnswers;
calls = [];
abortController = new AbortController();
signal = this.abortController.signal;
spyFactory;
constructor(options = {}) {
const { spyFactory = defaultConfig.spyFactory ?? (spyOptions => () => spyOptions.returns), log = defaultConfig.log ?? createLogger(), mockedAnswers, callback, throwOnMissingAnswer, } = options;
this.spyFactory = spyFactory;
this.promptModule = createAdapterPromptModule({
input: Duplex.from('should not read from input'),
output: Duplex.from(async () => { }),
skipTTYChecks: true,
signal: this.abortController.signal,
});
this.mockedAnswers = {};
this.addAnswers(mockedAnswers ?? {});
const actualRegisterPrompt = this.promptModule.registerPrompt.bind(this.promptModule);
this.registerDummyPrompt = (promptModuleName, customPromptOptions) => actualRegisterPrompt(promptModuleName, createDummyPrompt({ calls: this.calls }, { callback, mockedAnswers: this.mockedAnswers, throwOnMissingAnswer, ...customPromptOptions }));
this.promptModule.registerPrompt = (name) => this.registerDummyPrompt(name);
for (const promptName of Object.keys(this.promptModule.prompts)) {
this.promptModule.registerPrompt(promptName, undefined);
}
this.diff = this.spyFactory({});
this.log = this.spyFactory({});
Object.assign(this.log, events.EventEmitter.prototype);
const descriptors = Object.getOwnPropertyDescriptors(log);
// Make sure all log methods are defined
const logMethods = Object.entries(descriptors)
.filter(([method, desc]) => typeof desc.value === 'function' && !Object.getOwnPropertyDescriptor(this.log, method))
.map(([method]) => method);
for (const methodName of logMethods) {
this.log[methodName] = this.spyFactory({ returns: this.log });
}
}
async queue(function_) {
return function_(this);
}
async progress(function_, _options) {
return function_({ step() { } });
}
close() {
this.abort();
}
abort(reason) {
this.abortController.abort(reason);
}
async prompt(questions, initialAnswers) {
try {
return (await this.promptModule(questions, initialAnswers));
}
catch (error) {
this.abortController.abort(error);
throw error;
}
}
/**
* Add answers to the mocked answers.
*/
addAnswers(answers) {
// Copy properties using Object.getOwnPropertyDescriptor to preserve getters and setters
for (const key in answers) {
const descriptors = Object.getOwnPropertyDescriptor(answers, key);
if (descriptors) {
Object.defineProperty(this.mockedAnswers, key, descriptors);
}
}
}
}
//# sourceMappingURL=test-adapter.js.map