yeoman-test 8.0.0-beta.5 → 8.0.0-beta.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/adapter.d.ts CHANGED
@@ -1,22 +1,31 @@
1
1
  import type { PromptAnswers, PromptQuestion, Logger, InputOutputAdapter, PromptQuestions } from '@yeoman/types';
2
2
  import { type PromptModule } from 'inquirer';
3
+ export type DummyPromptCallback = (answer: any, { question, answers }: {
4
+ question: PromptQuestion;
5
+ answers: PromptAnswers;
6
+ }) => any;
3
7
  export type DummyPromptOptions = {
4
- callback?: (answers: PromptAnswers) => PromptAnswers;
8
+ mockedAnswers?: PromptAnswers;
9
+ callback?: DummyPromptCallback;
5
10
  throwOnMissingAnswer?: boolean;
6
11
  };
12
+ export type TestAdapterOptions = DummyPromptOptions & {
13
+ log?: any;
14
+ };
7
15
  export declare class DummyPrompt {
8
16
  answers: PromptAnswers;
9
17
  question: PromptQuestion;
10
- callback: (answers: PromptAnswers) => PromptAnswers;
18
+ callback: DummyPromptCallback;
11
19
  throwOnMissingAnswer: boolean;
12
- constructor(question: PromptQuestion, _rl: any, answers: PromptAnswers, mockedAnswers?: PromptAnswers, options?: ((answers: PromptAnswers) => PromptAnswers) | DummyPromptOptions);
13
- run(): Promise<import("inquirer").Answers>;
20
+ constructor(question: PromptQuestion, _rl: any, answers: PromptAnswers, options?: DummyPromptOptions);
21
+ run(): Promise<any>;
14
22
  }
15
23
  export declare class TestAdapter implements InputOutputAdapter {
16
24
  promptModule: PromptModule;
17
25
  diff: any;
18
26
  log: Logger;
19
- constructor(mockedAnswers?: PromptAnswers);
27
+ registerDummyPrompt: (promptName: string, customPromptOptions?: DummyPromptOptions) => PromptModule;
28
+ constructor(options?: TestAdapterOptions);
20
29
  close(): void;
21
30
  prompt<A extends PromptAnswers = PromptAnswers>(questions: PromptQuestions<A>, initialAnswers?: Partial<A> | undefined): Promise<A>;
22
31
  }
package/dist/adapter.js CHANGED
@@ -1,6 +1,6 @@
1
- /* eslint-disable max-params */
2
1
  import events from 'node:events';
3
2
  import { PassThrough } from 'node:stream';
3
+ import { createLogger } from '@yeoman/adapter';
4
4
  import { spy as sinonSpy, stub as sinonStub } from 'sinon';
5
5
  import { createPromptModule } from 'inquirer';
6
6
  export class DummyPrompt {
@@ -8,21 +8,12 @@ export class DummyPrompt {
8
8
  question;
9
9
  callback;
10
10
  throwOnMissingAnswer = false;
11
- constructor(question, _rl, answers, mockedAnswers, options) {
11
+ constructor(question, _rl, answers, options = {}) {
12
+ const { mockedAnswers, callback, throwOnMissingAnswer } = options;
12
13
  this.answers = { ...answers, ...mockedAnswers };
13
14
  this.question = question;
14
- if (typeof options === 'function') {
15
- this.callback = options;
16
- }
17
- else if (options) {
18
- if (options.callback) {
19
- this.callback = options.callback;
20
- }
21
- if (options.throwOnMissingAnswer !== undefined) {
22
- this.throwOnMissingAnswer = options.throwOnMissingAnswer;
23
- }
24
- }
25
- this.callback = this.callback || (answers => answers);
15
+ this.callback = callback ?? (answers => answers);
16
+ this.throwOnMissingAnswer = throwOnMissingAnswer ?? false;
26
17
  }
27
18
  async run() {
28
19
  let answer = this.answers[this.question.name];
@@ -56,45 +47,40 @@ export class DummyPrompt {
56
47
  answer = true;
57
48
  }
58
49
  }
59
- return this.callback(answer);
50
+ return this.callback(answer, { question: this.question, answers: this.answers });
60
51
  }
61
52
  }
62
53
  export class TestAdapter {
63
54
  promptModule;
64
55
  diff;
65
56
  log;
66
- constructor(mockedAnswers) {
57
+ registerDummyPrompt;
58
+ constructor(options = {}) {
59
+ const { log = createLogger(), ...promptOptions } = options;
67
60
  this.promptModule = createPromptModule({
68
61
  input: new PassThrough(),
69
62
  output: new PassThrough(),
70
63
  skipTTYChecks: true,
71
64
  });
65
+ const actualRegisterPrompt = this.promptModule.registerPrompt.bind(this.promptModule);
66
+ this.registerDummyPrompt = (promptModuleName, customPromptOptions) => actualRegisterPrompt(promptModuleName, class CustomDummyPrompt extends DummyPrompt {
67
+ constructor(question, rl, answers) {
68
+ super(question, rl, answers, customPromptOptions ?? promptOptions);
69
+ }
70
+ });
71
+ this.promptModule.registerPrompt = (name) => this.registerDummyPrompt(name);
72
72
  for (const promptName of Object.keys(this.promptModule.prompts)) {
73
- this.promptModule.registerPrompt(promptName, class CustomDummyPrompt extends DummyPrompt {
74
- constructor(question, rl, answers) {
75
- super(question, rl, answers, mockedAnswers);
76
- }
77
- });
73
+ this.promptModule.registerPrompt(promptName, undefined);
78
74
  }
79
75
  this.diff = sinonSpy();
80
76
  this.log = sinonSpy();
81
77
  Object.assign(this.log, events.EventEmitter.prototype);
78
+ const descriptors = Object.getOwnPropertyDescriptors(log);
82
79
  // Make sure all log methods are defined
83
- const adapterMethods = [
84
- 'write',
85
- 'writeln',
86
- 'ok',
87
- 'error',
88
- 'skip',
89
- 'force',
90
- 'create',
91
- 'invoke',
92
- 'conflict',
93
- 'identical',
94
- 'info',
95
- 'table',
96
- ];
97
- for (const methodName of adapterMethods) {
80
+ const logMethods = Object.entries(descriptors)
81
+ .filter(([method, desc]) => typeof desc.value === 'function' && !Object.getOwnPropertyDescriptor(this.log, method))
82
+ .map(([method]) => method);
83
+ for (const methodName of logMethods) {
98
84
  this.log[methodName] = sinonStub().returns(this.log);
99
85
  }
100
86
  }
@@ -1 +1 @@
1
- export function defaultEnvironment(): Promise<any>;
1
+ export function createEnv(options: import('@yeoman/types').BaseEnvironmentOptions): import('@yeoman/types').BaseEnvironment;
@@ -1,4 +1,15 @@
1
- export const defaultEnvironment = async () => {
2
- const dynamicEnv = await import('yeoman-environment');
3
- return dynamicEnv.createEnv ?? dynamicEnv.default.createEnv;
1
+ /**
2
+ * @param {import('@yeoman/types').BaseEnvironmentOptions} options
3
+ * @returns {import('@yeoman/types').BaseEnvironment}
4
+ */
5
+ export const createEnv = async (options) => {
6
+ const DynamicEnv = await import('yeoman-environment');
7
+ if (typeof DynamicEnv === 'function') {
8
+ return new DynamicEnv(options);
9
+ }
10
+ if (typeof DynamicEnv.default === 'function') {
11
+ // eslint-disable-next-line new-cap
12
+ return new DynamicEnv.default(options);
13
+ }
14
+ throw new Error(`'yeoman-environment' didn't returned a constructor`);
4
15
  };
package/dist/helpers.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import type { BaseEnvironmentOptions, BaseGenerator, BaseGeneratorOptions, GetGeneratorConstructor, GetGeneratorOptions, PromptAnswers } from '@yeoman/types';
1
+ import type { BaseEnvironment, BaseEnvironmentOptions, BaseGenerator, BaseGeneratorOptions, GetGeneratorConstructor, InstantiateOptions, PromptAnswers } from '@yeoman/types';
2
2
  import type { SinonSpiedInstance } from 'sinon';
3
3
  import type { DefaultEnvironmentApi, DefaultGeneratorApi } from '../types/type-helpers.js';
4
4
  import { type DummyPromptOptions } from './adapter.js';
5
5
  import RunContext, { BasicRunContext, type RunContextSettings } from './run-context.js';
6
+ export type CreateEnv = (options: BaseEnvironmentOptions) => Promise<BaseEnvironment>;
6
7
  /**
7
8
  * Dependencies can be path (autodiscovery) or an array [<generator>, <name>]
8
9
  */
@@ -89,7 +90,10 @@ export declare class YeomanTest {
89
90
  * ];
90
91
  * var angular = createGenerator('angular:app', deps);
91
92
  */
92
- createGenerator<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(name: string, dependencies: Dependency[], args?: string[], options?: GetGeneratorOptions<GeneratorType>, localConfigOnly?: boolean): Promise<GeneratorType>;
93
+ createGenerator<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(name: string | GetGeneratorConstructor<GeneratorType>, options?: {
94
+ dependencies?: Dependency[];
95
+ localConfigOnly?: boolean;
96
+ } & InstantiateOptions<GeneratorType>): Promise<GeneratorType>;
93
97
  /**
94
98
  * Shortcut to the Environment's createEnv.
95
99
  *
@@ -105,7 +109,7 @@ export declare class YeomanTest {
105
109
  * createEnv.restore();
106
110
  * });
107
111
  */
108
- createEnv(...args: any[]): Promise<DefaultEnvironmentApi>;
112
+ createEnv(options: BaseEnvironmentOptions): Promise<DefaultEnvironmentApi>;
109
113
  /**
110
114
  * Creates a test environment.
111
115
  *
@@ -113,7 +117,7 @@ export declare class YeomanTest {
113
117
  * @param {Object} - Options to be passed to the environment
114
118
  * const env = createTestEnv(require('yeoman-environment').createEnv);
115
119
  */
116
- createTestEnv(envContructor?: (...args: any[]) => Promise<import("@yeoman/types").BaseEnvironment>, options?: BaseEnvironmentOptions): Promise<import("@yeoman/types").BaseEnvironment>;
120
+ createTestEnv(envContructor?: CreateEnv, options?: BaseEnvironmentOptions): Promise<BaseEnvironment>;
117
121
  /**
118
122
  * Get RunContext type
119
123
  * @return {RunContext}
package/dist/helpers.js CHANGED
@@ -1,13 +1,12 @@
1
- /* eslint-disable max-params */
2
1
  import { mkdirSync, existsSync, rmSync } from 'node:fs';
3
2
  import { resolve } from 'node:path';
4
3
  import process from 'node:process';
5
4
  import _ from 'lodash';
6
5
  import { spy as sinonSpy, stub as sinonStub } from 'sinon';
7
- import { DummyPrompt, TestAdapter } from './adapter.js';
6
+ import { TestAdapter } from './adapter.js';
8
7
  import RunContext, { BasicRunContext } from './run-context.js';
9
8
  import testContext from './test-context.js';
10
- import { defaultEnvironment } from './default-environment.js';
9
+ import { createEnv } from './default-environment.js';
11
10
  let GeneratorImplementation;
12
11
  try {
13
12
  const GeneratorImport = await import('yeoman-generator');
@@ -84,13 +83,10 @@ export class YeomanTest {
84
83
  if (!environment.adapter) {
85
84
  throw new Error('environment is not an Environment instance');
86
85
  }
87
- const { promptModule } = environment.adapter;
86
+ const testAdapter = environment.adapter;
87
+ const { promptModule } = testAdapter;
88
88
  for (const name of Object.keys(promptModule.prompts)) {
89
- promptModule.registerPrompt(name, class CustomDummyPrompt extends DummyPrompt {
90
- constructor(question, rl, answers) {
91
- super(question, rl, answers, mockedAnswers, options);
92
- }
93
- });
89
+ testAdapter.registerDummyPrompt(name, { ...options, mockedAnswers });
94
90
  }
95
91
  }
96
92
  /**
@@ -170,8 +166,9 @@ export class YeomanTest {
170
166
  * ];
171
167
  * var angular = createGenerator('angular:app', deps);
172
168
  */
173
- async createGenerator(name, dependencies, args, options, localConfigOnly = true) {
174
- const env = await this.createEnv([], { sharedOptions: { localConfigOnly } });
169
+ async createGenerator(name, options = {}) {
170
+ const { dependencies = [], localConfigOnly = true, ...instantiateOptions } = options;
171
+ const env = await this.createEnv({ sharedOptions: { localConfigOnly } });
175
172
  for (const dependency of dependencies) {
176
173
  if (typeof dependency === 'string') {
177
174
  env.register(dependency);
@@ -180,7 +177,7 @@ export class YeomanTest {
180
177
  env.register(...dependency);
181
178
  }
182
179
  }
183
- return env.create(name, { generatorArgs: args, generatorOptions: options });
180
+ return env.create(name, instantiateOptions);
184
181
  }
185
182
  /**
186
183
  * Shortcut to the Environment's createEnv.
@@ -197,8 +194,8 @@ export class YeomanTest {
197
194
  * createEnv.restore();
198
195
  * });
199
196
  */
200
- async createEnv(...args) {
201
- return (await defaultEnvironment())(...args);
197
+ async createEnv(options) {
198
+ return createEnv(options);
202
199
  }
203
200
  /**
204
201
  * Creates a test environment.
@@ -231,7 +228,7 @@ export class YeomanTest {
231
228
  ...options,
232
229
  };
233
230
  }
234
- return envContructor([], envOptions, new TestAdapter());
231
+ return envContructor({ adapter: new TestAdapter(), ...envOptions });
235
232
  }
236
233
  /**
237
234
  * Get RunContext type
@@ -5,8 +5,8 @@ import type { BaseEnvironmentOptions, BaseGenerator, GetGeneratorConstructor, Ge
5
5
  import { type MemFsEditorFile, type MemFsEditor } from 'mem-fs-editor';
6
6
  import type { DefaultGeneratorApi, DefaultEnvironmentApi } from '../types/type-helpers.js';
7
7
  import RunResult, { type RunResultOptions } from './run-result.js';
8
- import { type Dependency, type YeomanTest } from './helpers.js';
9
- import { type DummyPromptOptions } from './adapter.js';
8
+ import { type CreateEnv, type Dependency, type YeomanTest } from './helpers.js';
9
+ import { type DummyPromptOptions, type TestAdapterOptions } from './adapter.js';
10
10
  /**
11
11
  * Provides settings for creating a `RunContext`.
12
12
  */
@@ -34,7 +34,7 @@ export type RunContextSettings = {
34
34
  type PromiseRunResult<GeneratorType extends BaseGenerator> = Promise<RunResult<GeneratorType>>;
35
35
  type MockedGeneratorFactory<GenParameter extends BaseGenerator = DefaultGeneratorApi> = (GeneratorClass?: GetGeneratorConstructor<GenParameter>) => GetGeneratorConstructor<GenParameter>;
36
36
  type EnvOptions = BaseEnvironmentOptions & {
37
- createEnv?: any;
37
+ createEnv?: CreateEnv;
38
38
  };
39
39
  export declare class RunContextBase<GeneratorType extends BaseGenerator = DefaultGeneratorApi> extends EventEmitter {
40
40
  readonly mockedGenerators: Record<string, BaseGenerator>;
@@ -51,6 +51,7 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
51
51
  private args;
52
52
  private options;
53
53
  private answers?;
54
+ private readonly adapterOptions?;
54
55
  private keepFsState?;
55
56
  private readonly onGeneratorCallbacks;
56
57
  private readonly onTargetDirectoryCallbacks;
@@ -136,6 +137,10 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
136
137
  * @param force - force directory cleanup for not tmpdir
137
138
  */
138
139
  cleanTestDirectory(force?: boolean): void;
140
+ /**
141
+ * TestAdapter options.
142
+ */
143
+ withAdapterOptions(options: Omit<TestAdapterOptions, 'mockedAnswers'>): this;
139
144
  /**
140
145
  * Create an environment
141
146
  *
@@ -172,14 +177,14 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
172
177
  * @param {Boolean} [options.throwOnMissingAnswer] - Throw if a answer is missing.
173
178
  * @return {this}
174
179
  */
175
- withPrompts(answers: PromptAnswers, options?: DummyPromptOptions): this;
180
+ withPrompts(answers: PromptAnswers, options?: Omit<DummyPromptOptions, 'mockedAnswers'>): this;
176
181
  /**
177
182
  * Mock answers for prompts
178
183
  * @param answers - Answers to the prompt questions
179
184
  * @param options - Options or callback.
180
185
  * @return {this}
181
186
  */
182
- withAnswers(answers: PromptAnswers, options?: DummyPromptOptions): this;
187
+ withAnswers(answers: PromptAnswers, options?: Omit<DummyPromptOptions, 'mockedAnswers'>): this;
183
188
  /**
184
189
  * Provide dependent generators
185
190
  * @param {Array} dependencies - paths to the generators dependencies
@@ -12,6 +12,7 @@ import tempDirectory from 'temp-dir';
12
12
  import { create as createMemFsEditor } from 'mem-fs-editor';
13
13
  import RunResult from './run-result.js';
14
14
  import defaultHelpers from './helpers.js';
15
+ import { TestAdapter } from './adapter.js';
15
16
  import testContext from './test-context.js';
16
17
  const { camelCase, kebabCase, merge: lodashMerge, set: lodashSet } = _;
17
18
  export class RunContextBase extends EventEmitter {
@@ -29,6 +30,7 @@ export class RunContextBase extends EventEmitter {
29
30
  args = [];
30
31
  options = {};
31
32
  answers;
33
+ adapterOptions = {};
32
34
  keepFsState;
33
35
  onGeneratorCallbacks = [];
34
36
  onTargetDirectoryCallbacks = [];
@@ -194,6 +196,13 @@ export class RunContextBase extends EventEmitter {
194
196
  rmSync(this.targetDirectory, { recursive: true });
195
197
  }
196
198
  }
199
+ /**
200
+ * TestAdapter options.
201
+ */
202
+ withAdapterOptions(options) {
203
+ Object.assign(this.adapterOptions, options);
204
+ return this;
205
+ }
197
206
  /**
198
207
  * Create an environment
199
208
  *
@@ -268,13 +277,9 @@ export class RunContextBase extends EventEmitter {
268
277
  * @return {this}
269
278
  */
270
279
  withAnswers(answers, options) {
271
- const callbackSet = Boolean(this.answers);
272
280
  this.answers = { ...this.answers, ...answers };
273
- if (callbackSet)
274
- return this;
275
- return this.onEnvironment(env => {
276
- this.helpers.mockPrompt(env, this.answers, options);
277
- });
281
+ Object.assign(this.adapterOptions, options);
282
+ return this;
278
283
  }
279
284
  /**
280
285
  * Provide dependent generators
@@ -485,7 +490,7 @@ export class RunContextBase extends EventEmitter {
485
490
  force: true,
486
491
  skipCache: true,
487
492
  skipInstall: true,
488
- ...this.options,
493
+ adapter: new TestAdapter({ ...this.adapterOptions, mockedAnswers: this.answers }),
489
494
  ...this.envOptions,
490
495
  });
491
496
  this.env = this.envCB ? (await this.envCB(testEnv)) ?? testEnv : testEnv;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeoman-test",
3
- "version": "8.0.0-beta.5",
3
+ "version": "8.0.0-beta.7",
4
4
  "description": "Test utilities for Yeoman generators",
5
5
  "keywords": [
6
6
  "yeoman",
@@ -36,11 +36,12 @@
36
36
  "doc_path": "../yeoman-test-doc"
37
37
  },
38
38
  "dependencies": {
39
+ "@yeoman/adapter": "^1.0.2",
39
40
  "inquirer": "^9.2.2",
40
41
  "lodash": "^4.17.21",
41
42
  "mem-fs": "^3.0.0",
42
43
  "mem-fs-editor": "^10.0.2",
43
- "sinon": "^14.0.2",
44
+ "sinon": "^15.1.0",
44
45
  "temp-dir": "^3.0.0",
45
46
  "type-fest": "^3.10.0"
46
47
  },
@@ -52,20 +53,20 @@
52
53
  "coveralls": "^3.1.1",
53
54
  "esmocha": "^1.0.1",
54
55
  "husky": "^8.0.2",
55
- "jsdoc": "^3.6.10",
56
+ "jsdoc": "^4.0.2",
56
57
  "lint-staged": "^13.0.3",
57
58
  "prettier": "^2.2.1",
58
59
  "prettier-plugin-packagejson": "^2.3.0",
59
60
  "tui-jsdoc-template": "^1.2.2",
60
61
  "typescript": "^5.0.4",
61
62
  "xo": "^0.54.2",
62
- "yeoman-environment": "^3.18.0",
63
- "yeoman-generator": "^5.8.0"
63
+ "yeoman-environment": "^3.18.3",
64
+ "yeoman-generator": "^5.9.0"
64
65
  },
65
66
  "peerDependencies": {
66
- "@yeoman/types": "^0.5.0",
67
- "yeoman-environment": "^3.18.0 || ^4.0.0-alpha.5",
68
- "yeoman-generator": "^5.8.0 || ^6.0.0-beta.3"
67
+ "@yeoman/types": "^1.0.1",
68
+ "yeoman-environment": "^3.18.3 || ^4.0.0-alpha.6",
69
+ "yeoman-generator": "^5.9.0 || ^6.0.0-beta.4"
69
70
  },
70
71
  "peerDependenciesMeta": {
71
72
  "yeoman-environment": {