diff --git a/package-lock.json b/package-lock.json index 038193f..a5b6b0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "8.0.0-rc.0", "license": "MIT", "dependencies": { - "@yeoman/adapter": "^1.0.4", + "@yeoman/adapter": "^1.1.0", "inquirer": "^9.2.2", "lodash-es": "^4.17.21", "mem-fs": "^3.0.0", @@ -2712,9 +2712,9 @@ "peer": true }, "node_modules/@yeoman/adapter": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@yeoman/adapter/-/adapter-1.0.4.tgz", - "integrity": "sha512-m8cVYrbJLvTrSor/ByF6d3evYIohep1mrZ/EYPevu6KMz08c5r9EQb8R3o6M+Ev7SZPw2NyDqVuMcWr/z6USFA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yeoman/adapter/-/adapter-1.1.0.tgz", + "integrity": "sha512-lyzWtpfjvRdiVJ2JtPJPWGYa7wD6mEC5pcnpvGG9WQKKF5II2GS5OGIhuD7A3gnqimzwpdOuIBIWwYISFY5TPw==", "dependencies": { "@types/inquirer": "^9.0.3", "are-we-there-yet": "^4.0.0", diff --git a/package.json b/package.json index 840e8a1..5ec0b07 100644 --- a/package.json +++ b/package.json @@ -36,7 +36,7 @@ "doc_path": "../yeoman-test-doc" }, "dependencies": { - "@yeoman/adapter": "^1.0.4", + "@yeoman/adapter": "^1.1.0", "inquirer": "^9.2.2", "lodash-es": "^4.17.21", "mem-fs": "^3.0.0", diff --git a/src/adapter.ts b/src/adapter.ts index cb04d60..51de9df 100644 --- a/src/adapter.ts +++ b/src/adapter.ts @@ -1,144 +1,15 @@ -import events from 'node:events'; -import { PassThrough } from 'node:stream'; -import { createLogger, type AdapterWithProgress } from '@yeoman/adapter'; +// eslint-disable-next-line n/file-extension-in-import +import { TestAdapter as BaseTestAdapter, type TestAdapterOptions } from '@yeoman/adapter/testing'; import { spy as sinonSpy, stub as sinonStub } from 'sinon'; -import type { PromptAnswers, PromptQuestion, Logger, QueuedAdapter, PromptQuestions, Task } from '@yeoman/types'; -import { createPromptModule, type PromptModule } from 'inquirer'; - -export type DummyPromptCallback = (answer: any, { question, answers }: { question: PromptQuestion; answers: PromptAnswers }) => any; - -export type DummyPromptOptions = { - mockedAnswers?: PromptAnswers; - callback?: DummyPromptCallback; - throwOnMissingAnswer?: boolean; -}; - -export type TestAdapterOptions = DummyPromptOptions & { log?: any }; - -export class DummyPrompt { - answers: PromptAnswers; - question: PromptQuestion; - callback!: DummyPromptCallback; - throwOnMissingAnswer = false; - - constructor(question: PromptQuestion, _rl: any, answers: PromptAnswers, options: DummyPromptOptions = {}) { - const { mockedAnswers, callback, throwOnMissingAnswer } = options; - this.answers = { ...answers, ...mockedAnswers }; - this.question = question; - - this.callback = callback ?? (answers => answers); - this.throwOnMissingAnswer = throwOnMissingAnswer ?? false; - } - - async run() { - let answer = this.answers[this.question.name!]; - let isSet; - - switch (this.question.type) { - case 'list': { - // List prompt accepts any answer value including null - isSet = answer !== undefined; - break; - } - - case 'confirm': { - // Ensure that we don't replace `false` with default `true` - isSet = answer || answer === false; - break; - } - - default: { - // Other prompts treat all falsy values to default - isSet = Boolean(answer); - } - } - - if (!isSet) { - if (answer === undefined && this.question.default === undefined) { - const missingAnswerMessage = `yeoman-test: question ${this.question.name} was asked but answer was not provided`; - console.warn(missingAnswerMessage); - if (this.throwOnMissingAnswer) { - throw new Error(missingAnswerMessage); - } - } - - answer = this.question.default; - - if (answer === undefined && this.question.type === 'confirm') { - answer = true; - } - } - - return this.callback(answer, { question: this.question, answers: this.answers }); - } -} - -export class TestAdapter implements AdapterWithProgress { - promptModule: PromptModule; - diff: any; - log: Logger; - registerDummyPrompt: (promptName: string, customPromptOptions?: DummyPromptOptions) => PromptModule; +export class TestAdapter extends BaseTestAdapter { constructor(options: TestAdapterOptions = {}) { - const { log = createLogger(), ...promptOptions } = options; - this.promptModule = createPromptModule({ - input: new PassThrough() as any, - output: new PassThrough() as any, - skipTTYChecks: true, + super({ + spyFactory: ({ returns }) => (returns ? sinonStub().returns(returns) : sinonSpy()), + ...options, }); - - const actualRegisterPrompt = this.promptModule.registerPrompt.bind(this.promptModule); - - this.registerDummyPrompt = (promptModuleName: string, customPromptOptions?: DummyPromptOptions) => - actualRegisterPrompt( - promptModuleName, - class CustomDummyPrompt extends DummyPrompt { - constructor(question: PromptQuestion, rl: any, answers: PromptAnswers) { - super(question, rl, answers, customPromptOptions ?? promptOptions); - } - } as any, - ); - - this.promptModule.registerPrompt = (name: string) => this.registerDummyPrompt(name); - - for (const promptName of Object.keys(this.promptModule.prompts)) { - this.promptModule.registerPrompt(promptName, undefined as any); - } - - this.diff = sinonSpy(); - this.log = sinonSpy() as any; - 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 as any)[methodName] = sinonStub().returns(this.log); - } - } - - async queue(fn: Task): Promise { - return fn(this); - } - - async progress( - fn: (progress: { step: (prefix: string, message: string, ...args: any[]) => void }) => ReturnType, - _options?: { disabled?: boolean | undefined; name?: string | undefined } | undefined, - ): Promise { - // eslint-disable-next-line @typescript-eslint/no-empty-function - return fn({ step() {} }); - } - - close(): void { - this.promptModule.restoreDefaultPrompts(); - } - - async prompt( - questions: PromptQuestions, - initialAnswers?: Partial | undefined, - ): Promise { - return this.promptModule(questions, initialAnswers); } } + +// eslint-disable-next-line n/file-extension-in-import +export { DummyPrompt, type DummyPromptOptions, type DummyPromptCallback, type TestAdapterOptions } from '@yeoman/adapter/testing';