yeoman-test 8.2.0 → 9.0.0

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/README.md CHANGED
@@ -22,17 +22,16 @@ $ npm install --save-dev yeoman-generator@xxx yeoman-environment@xxx
22
22
 
23
23
  ## Usage
24
24
 
25
- Usage:
25
+ Usage using the convenience last RunResult instance:
26
26
 
27
27
  ```js
28
- import helpers from 'yeoman-test';
28
+ import helpers, { result } from 'yeoman-test';
29
29
 
30
30
  describe('generator test', () => {
31
31
  describe('test', () => {
32
- let runResult;
33
32
  beforeEach(async () => {
34
- runResult = await helpers
35
- .create( // instantiates RunContext
33
+ await helpers
34
+ .run( // instantiates RunContext
36
35
  'namespace', // namespace or generator
37
36
  {}, // test options
38
37
  {} // environment options
@@ -53,47 +52,25 @@ describe('generator test', () => {
53
52
  [.withYoRcConfig('generator-foo.bar', { : {} })] // same as above
54
53
  [.commitFiles()] // commit mem-fs files to disk
55
54
  [.onGenerator(gen => {})] // do something with the generator
56
- [.onEnvironment(env => {})] // do something with the environment
57
- [.build(runContext => { // instantiates Environment/Generator
58
- [runContext.env...] // does something with the environment
59
- [runContext.generator...] // does something with the generator
60
- })]
61
- .run(); // runs the environment, promises a RunResult
62
- [result.create().run()] // instantiates a new RunContext at the same directory
55
+ [.onEnvironment(env => {})]; // do something with the environment
56
+
57
+ [await result.create('another-generator').run();] // instantiates a new RunContext at the same directory
63
58
  );
64
- afterEach(() => {
65
- if (runResult) {
66
- // Optional if context is executed at a temporary folder
67
- runResult.restore();
68
- }
69
- });
59
+
70
60
  it('runs correctly', () => {
71
61
  // runs assertions using mem-fs.
72
- [runResult.assertFile('file.txt');]
73
- [runResult.assertNoFile('file.txt');]
74
- [runResult.assertFileContent('file.txt', 'content');]
75
- [runResult.assertEqualsFileContent('file.txt', 'content');]
76
- [runResult.assertNoFileContent('file.txt', 'content');]
77
- [runResult.assertJsonFileContent('file.txt', {});]
78
- [runResult.assertNoJsonFileContent('file.txt', {});]
62
+ [result.assertFile('file.txt');]
63
+ [result.assertNoFile('file.txt');]
64
+ [result.assertFileContent('file.txt', 'content');]
65
+ [result.assertEqualsFileContent('file.txt', 'content');]
66
+ [result.assertNoFileContent('file.txt', 'content');]
67
+ [result.assertJsonFileContent('file.txt', {});]
68
+ [result.assertNoJsonFileContent('file.txt', {});]
79
69
  });
80
70
  });
81
71
  });
82
72
  ```
83
73
 
84
- Convenience last RunResult instance:
85
-
86
- ```js
87
- import helpers, { result } from 'yeoman-test';
88
-
89
- describe('generator test', () => {
90
- before(() => helpers.run('namespace'));
91
- it('test', () => {
92
- result.assert...;
93
- });
94
- });
95
- ```
96
-
97
74
  Generator compose:
98
75
 
99
76
  ```js
package/dist/adapter.d.ts CHANGED
@@ -3,3 +3,7 @@ export declare class TestAdapter extends BaseTestAdapter {
3
3
  constructor(options?: TestAdapterOptions);
4
4
  }
5
5
  export { DummyPrompt, type DummyPromptOptions, type DummyPromptCallback, type TestAdapterOptions } from '@yeoman/adapter/testing';
6
+ export type AskedQuestions = Array<{
7
+ name: string;
8
+ answer: any;
9
+ }>;
package/dist/adapter.js CHANGED
@@ -1,13 +1,13 @@
1
- // eslint-disable-next-line n/file-extension-in-import
1
+ import { mock } from 'node:test';
2
2
  import { TestAdapter as BaseTestAdapter } from '@yeoman/adapter/testing';
3
- import { spy as sinonSpy, stub as sinonStub } from 'sinon';
4
3
  export class TestAdapter extends BaseTestAdapter {
5
4
  constructor(options = {}) {
6
5
  super({
7
- spyFactory: ({ returns }) => (returns ? sinonStub().returns(returns) : sinonSpy()),
6
+ spyFactory: ({ returns }) => returns
7
+ ? mock.fn(() => { }, () => returns)
8
+ : mock.fn(),
8
9
  ...options,
9
10
  });
10
11
  }
11
12
  }
12
- // eslint-disable-next-line n/file-extension-in-import
13
13
  export { DummyPrompt } from '@yeoman/adapter/testing';
@@ -1 +1 @@
1
- export function createEnv(options: import('@yeoman/types').BaseEnvironmentOptions): import('@yeoman/types').BaseEnvironment;
1
+ export function createEnv(options: import("@yeoman/types").BaseEnvironmentOptions): import("@yeoman/types").BaseEnvironment;
@@ -3,13 +3,12 @@
3
3
  * @returns {import('@yeoman/types').BaseEnvironment}
4
4
  */
5
5
  export const createEnv = async (options) => {
6
- const DynamicEnv = await import('yeoman-environment');
7
- if (typeof DynamicEnv === 'function') {
8
- return new DynamicEnv(options);
6
+ const DynamicEnvironment = await import('yeoman-environment');
7
+ if (typeof DynamicEnvironment === 'function') {
8
+ return new DynamicEnvironment(options);
9
9
  }
10
- if (typeof DynamicEnv.default === 'function') {
11
- // eslint-disable-next-line new-cap
12
- return new DynamicEnv.default(options);
10
+ if (typeof DynamicEnvironment.default === 'function') {
11
+ return new DynamicEnvironment.default(options);
13
12
  }
14
13
  throw new Error(`'yeoman-environment' didn't returned a constructor`);
15
14
  };
package/dist/helpers.d.ts CHANGED
@@ -1,5 +1,5 @@
1
+ import { mock } from 'node:test';
1
2
  import type { BaseEnvironment, BaseEnvironmentOptions, BaseGenerator, BaseGeneratorOptions, GetGeneratorConstructor, InstantiateOptions, PromptAnswers } from '@yeoman/types';
2
- import type { SinonSpiedInstance } from 'sinon';
3
3
  import type { DefaultEnvironmentApi, DefaultGeneratorApi } from '../types/type-helpers.js';
4
4
  import { type DummyPromptOptions, TestAdapter, type TestAdapterOptions } from './adapter.js';
5
5
  import RunContext, { BasicRunContext, type RunContextSettings } from './run-context.js';
@@ -38,7 +38,7 @@ export declare class YeomanTest {
38
38
  * fs.writeFileSync('testfile', 'Roses are red.');
39
39
  * });
40
40
  */
41
- testDirectory(dir: string, cb?: (error?: any) => unknown): unknown;
41
+ testDirectory(dir: string, callback?: (error?: any) => unknown): unknown;
42
42
  /**
43
43
  * @deprecated
44
44
  * Answer prompt questions for the passed-in generator
@@ -50,13 +50,13 @@ export declare class YeomanTest {
50
50
  * @example
51
51
  * mockPrompt(angular, {'bootstrap': 'Y', 'compassBoostrap': 'Y'});
52
52
  */
53
- mockPrompt(envOrGenerator: BaseGenerator | DefaultEnvironmentApi, mockedAnswers?: PromptAnswers, options?: DummyPromptOptions): void;
53
+ mockPrompt(environmentOrGenerator: BaseGenerator | DefaultEnvironmentApi, mockedAnswers?: PromptAnswers, options?: DummyPromptOptions): void;
54
54
  /**
55
55
  * @deprecated
56
56
  * Restore defaults prompts on a generator.
57
57
  * @param generator or environment
58
58
  */
59
- restorePrompt(envOrGenerator: BaseGenerator | DefaultEnvironmentApi): void;
59
+ restorePrompt(environmentOrGenerator: BaseGenerator | DefaultEnvironmentApi): void;
60
60
  /**
61
61
  * @deprecated
62
62
  * Provide mocked values to the config
@@ -67,11 +67,11 @@ export declare class YeomanTest {
67
67
  /**
68
68
  * Create a mocked generator
69
69
  */
70
- createMockedGenerator(GeneratorClass?: any): SinonSpiedInstance<DefaultGeneratorApi>;
70
+ createMockedGenerator(GeneratorClass?: any): ReturnType<typeof mock.fn>;
71
71
  /**
72
72
  * Create a simple, dummy generator
73
73
  */
74
- createDummyGenerator<GenParameter extends BaseGenerator = DefaultGeneratorApi>(Generator?: GetGeneratorConstructor<GenParameter>, contents?: Record<string, (...args: any[]) => void>): new (...args: any[]) => GenParameter;
74
+ createDummyGenerator<GenParameter extends BaseGenerator = DefaultGeneratorApi>(Generator?: GetGeneratorConstructor<GenParameter>, contents?: Record<string, (...arguments_: any[]) => void>): new (...arguments_: any[]) => GenParameter;
75
75
  /**
76
76
  * Create a generator, using the given dependencies and controller arguments
77
77
  * Dependecies can be path (autodiscovery) or an array [{generator}, {name}]
@@ -118,7 +118,7 @@ export declare class YeomanTest {
118
118
  * @param {Object} - Options to be passed to the environment
119
119
  * const env = createTestEnv(require('yeoman-environment').createEnv);
120
120
  */
121
- createTestEnv(envContructor?: CreateEnv, options?: BaseEnvironmentOptions): Promise<BaseEnvironment>;
121
+ createTestEnv(environmentContructor?: CreateEnv, options?: BaseEnvironmentOptions): Promise<BaseEnvironment>;
122
122
  /**
123
123
  * Creates a TestAdapter using helpers default options.
124
124
  */
@@ -132,13 +132,13 @@ export declare class YeomanTest {
132
132
  * Run the provided Generator
133
133
  * @param GeneratorOrNamespace - Generator constructor or namespace
134
134
  */
135
- run<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(GeneratorOrNamespace: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, envOptions?: BaseEnvironmentOptions): RunContext<GeneratorType>;
135
+ run<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(GeneratorOrNamespace: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, environmentOptions?: BaseEnvironmentOptions): RunContext<GeneratorType>;
136
136
  /**
137
137
  * Prepare a run context
138
138
  * @param {String|Function} GeneratorOrNamespace - Generator constructor or namespace
139
139
  * @return {RunContext}
140
140
  */
141
- create<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(GeneratorOrNamespace: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, envOptions?: BaseEnvironmentOptions): RunContext<GeneratorType>;
141
+ create<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(GeneratorOrNamespace: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, environmentOptions?: BaseEnvironmentOptions): RunContext<GeneratorType>;
142
142
  /**
143
143
  * Prepare temporary dir without generator support.
144
144
  * Generator and environment will be undefined.
package/dist/helpers.js CHANGED
@@ -1,18 +1,20 @@
1
- import { mkdirSync, existsSync, rmSync } from 'node:fs';
1
+ import { existsSync, mkdirSync, rmSync } from 'node:fs';
2
2
  import { resolve } from 'node:path';
3
3
  import process from 'node:process';
4
+ import { mock } from 'node:test';
4
5
  import { cloneDeep } from 'lodash-es';
5
- import { spy as sinonSpy, stub as sinonStub } from 'sinon';
6
6
  import { TestAdapter } from './adapter.js';
7
7
  import RunContext, { BasicRunContext } from './run-context.js';
8
8
  import testContext from './test-context.js';
9
- import { createEnv } from './default-environment.js';
9
+ import { createEnv as createEnvironment } from './default-environment.js';
10
10
  let GeneratorImplementation;
11
11
  try {
12
12
  const GeneratorImport = await import('yeoman-generator');
13
13
  GeneratorImplementation = GeneratorImport.default ?? GeneratorImport;
14
14
  }
15
- catch { }
15
+ catch {
16
+ // Ignore error
17
+ }
16
18
  /**
17
19
  * Collection of unit test helpers. (mostly related to Mocha syntax)
18
20
  * @class YeomanTest
@@ -47,7 +49,7 @@ export class YeomanTest {
47
49
  * fs.writeFileSync('testfile', 'Roses are red.');
48
50
  * });
49
51
  */
50
- testDirectory(dir, cb) {
52
+ testDirectory(dir, callback) {
51
53
  if (!dir) {
52
54
  throw new Error('Missing directory');
53
55
  }
@@ -61,10 +63,10 @@ export class YeomanTest {
61
63
  }
62
64
  mkdirSync(dir, { recursive: true });
63
65
  process.chdir(dir);
64
- return cb?.();
66
+ return callback?.();
65
67
  }
66
68
  catch (error) {
67
- return cb?.(error);
69
+ return callback?.(error);
68
70
  }
69
71
  }
70
72
  /**
@@ -78,8 +80,8 @@ export class YeomanTest {
78
80
  * @example
79
81
  * mockPrompt(angular, {'bootstrap': 'Y', 'compassBoostrap': 'Y'});
80
82
  */
81
- mockPrompt(envOrGenerator, mockedAnswers, options) {
82
- const environment = 'env' in envOrGenerator ? envOrGenerator.env : envOrGenerator;
83
+ mockPrompt(environmentOrGenerator, mockedAnswers, options) {
84
+ const environment = 'env' in environmentOrGenerator ? environmentOrGenerator.env : environmentOrGenerator;
83
85
  if (!environment.adapter) {
84
86
  throw new Error('environment is not an Environment instance');
85
87
  }
@@ -94,8 +96,8 @@ export class YeomanTest {
94
96
  * Restore defaults prompts on a generator.
95
97
  * @param generator or environment
96
98
  */
97
- restorePrompt(envOrGenerator) {
98
- const environment = envOrGenerator.env ?? envOrGenerator;
99
+ restorePrompt(environmentOrGenerator) {
100
+ const environment = environmentOrGenerator.env ?? environmentOrGenerator;
99
101
  environment.adapter.close();
100
102
  }
101
103
  /**
@@ -113,10 +115,10 @@ export class YeomanTest {
113
115
  createMockedGenerator(GeneratorClass = GeneratorImplementation) {
114
116
  class MockedGenerator extends GeneratorClass {
115
117
  }
116
- const generator = sinonSpy(MockedGenerator);
118
+ const generator = mock.fn(MockedGenerator);
117
119
  for (const methodName of ['run', 'queueTasks', 'runWithOptions', 'queueOwnTasks']) {
118
120
  Object.defineProperty(MockedGenerator.prototype, methodName, {
119
- value: sinonStub(),
121
+ value: mock.fn(),
120
122
  });
121
123
  }
122
124
  return generator;
@@ -130,18 +132,18 @@ export class YeomanTest {
130
132
  },
131
133
  }) {
132
134
  class DummyGenerator extends Generator {
133
- constructor(...args) {
134
- const optIndex = Array.isArray(args[0]) ? 1 : 0;
135
- args[optIndex] = args[optIndex] ?? {};
136
- const options = args[optIndex];
135
+ constructor(...arguments_) {
136
+ const optIndex = Array.isArray(arguments_[0]) ? 1 : 0;
137
+ arguments_[optIndex] = arguments_[optIndex] ?? {};
138
+ const options = arguments_[optIndex];
137
139
  options.namespace = options.namespace ?? 'dummy';
138
140
  options.resolved = options.resolved ?? 'dummy';
139
- super(...args);
141
+ super(...arguments_);
140
142
  }
141
143
  }
142
- for (const [propName, propValue] of Object.entries(contents)) {
143
- Object.defineProperty(DummyGenerator.prototype, propName, {
144
- value: propValue ?? Object.create(null),
144
+ for (const [propertyName, propertyValue] of Object.entries(contents)) {
145
+ Object.defineProperty(DummyGenerator.prototype, propertyName, {
146
+ value: propertyValue ?? Object.create(null),
145
147
  writable: true,
146
148
  });
147
149
  }
@@ -168,16 +170,16 @@ export class YeomanTest {
168
170
  */
169
171
  async createGenerator(name, options = {}) {
170
172
  const { dependencies = [], localConfigOnly = true, ...instantiateOptions } = options;
171
- const env = await this.createEnv({ sharedOptions: { localConfigOnly } });
173
+ const environment = await this.createEnv({ sharedOptions: { localConfigOnly } });
172
174
  for (const dependency of dependencies) {
173
175
  if (typeof dependency === 'string') {
174
- env.register(dependency);
176
+ environment.register(dependency);
175
177
  }
176
178
  else {
177
- env.register(...dependency);
179
+ environment.register(...dependency);
178
180
  }
179
181
  }
180
- return env.create(name, instantiateOptions);
182
+ return environment.create(name, instantiateOptions);
181
183
  }
182
184
  /**
183
185
  * Shortcut to the Environment's createEnv.
@@ -195,7 +197,7 @@ export class YeomanTest {
195
197
  * });
196
198
  */
197
199
  async createEnv(options) {
198
- return createEnv(options);
200
+ return createEnvironment(options);
199
201
  }
200
202
  /**
201
203
  * Creates a test environment.
@@ -204,31 +206,31 @@ export class YeomanTest {
204
206
  * @param {Object} - Options to be passed to the environment
205
207
  * const env = createTestEnv(require('yeoman-environment').createEnv);
206
208
  */
207
- async createTestEnv(envContructor = this.createEnv, options = { localConfigOnly: true }) {
208
- let envOptions = cloneDeep(this.environmentOptions ?? {});
209
+ async createTestEnv(environmentContructor = this.createEnv, options = { localConfigOnly: true }) {
210
+ let environmentOptions = cloneDeep(this.environmentOptions ?? {});
209
211
  if (typeof options === 'boolean') {
210
- envOptions = {
212
+ environmentOptions = {
211
213
  newErrorHandler: true,
212
- ...envOptions,
214
+ ...environmentOptions,
213
215
  sharedOptions: {
214
216
  localConfigOnly: options,
215
- ...envOptions.sharedOptions,
217
+ ...environmentOptions.sharedOptions,
216
218
  },
217
219
  };
218
220
  }
219
221
  else {
220
- envOptions.sharedOptions = {
222
+ environmentOptions.sharedOptions = {
221
223
  localConfigOnly: true,
222
- ...envOptions.sharedOptions,
224
+ ...environmentOptions.sharedOptions,
223
225
  ...options.sharedOptions,
224
226
  };
225
- envOptions = {
227
+ environmentOptions = {
226
228
  newErrorHandler: true,
227
- ...envOptions,
229
+ ...environmentOptions,
228
230
  ...options,
229
231
  };
230
232
  }
231
- return envContructor({ adapter: this.createTestAdapter(), ...envOptions });
233
+ return environmentContructor({ adapter: this.createTestAdapter(), ...environmentOptions });
232
234
  }
233
235
  /**
234
236
  * Creates a TestAdapter using helpers default options.
@@ -247,11 +249,11 @@ export class YeomanTest {
247
249
  * Run the provided Generator
248
250
  * @param GeneratorOrNamespace - Generator constructor or namespace
249
251
  */
250
- run(GeneratorOrNamespace, settings, envOptions) {
252
+ run(GeneratorOrNamespace, settings, environmentOptions) {
251
253
  const contextSettings = cloneDeep(this.settings ?? {});
252
254
  const generatorOptions = cloneDeep(this.generatorOptions ?? {});
253
255
  const RunContext = this.getRunContextType();
254
- const runContext = new RunContext(GeneratorOrNamespace, { ...contextSettings, ...settings }, envOptions, this).withOptions(generatorOptions);
256
+ const runContext = new RunContext(GeneratorOrNamespace, { ...contextSettings, ...settings }, environmentOptions, this).withOptions(generatorOptions);
255
257
  if (settings?.autoCleanup !== false) {
256
258
  testContext.startNewContext(runContext);
257
259
  }
@@ -262,8 +264,8 @@ export class YeomanTest {
262
264
  * @param {String|Function} GeneratorOrNamespace - Generator constructor or namespace
263
265
  * @return {RunContext}
264
266
  */
265
- create(GeneratorOrNamespace, settings, envOptions) {
266
- return this.run(GeneratorOrNamespace, settings, envOptions);
267
+ create(GeneratorOrNamespace, settings, environmentOptions) {
268
+ return this.run(GeneratorOrNamespace, settings, environmentOptions);
267
269
  }
268
270
  /**
269
271
  * Prepare temporary dir without generator support.
@@ -0,0 +1,3 @@
1
+ export namespace mochaHooks {
2
+ function afterAll(): void;
3
+ }
@@ -0,0 +1,6 @@
1
+ import { context } from './index.js';
2
+ export const mochaHooks = {
3
+ afterAll() {
4
+ context.startNewContext();
5
+ },
6
+ };
@@ -1,12 +1,12 @@
1
- /// <reference types="node" resolution-mode="require"/>
2
1
  import { EventEmitter } from 'node:events';
2
+ import { mock } from 'node:test';
3
3
  import { type Store } from 'mem-fs';
4
- import type { BaseEnvironmentOptions, BaseGenerator, GetGeneratorConstructor, GetGeneratorOptions, PromptAnswers, LookupOptions } from '@yeoman/types';
5
- import { type MemFsEditorFile, type MemFsEditor } from 'mem-fs-editor';
6
- import type { DefaultGeneratorApi, DefaultEnvironmentApi } from '../types/type-helpers.js';
4
+ import type { BaseEnvironmentOptions, BaseGenerator, GetGeneratorConstructor, GetGeneratorOptions, LookupOptions, PromptAnswers } from '@yeoman/types';
5
+ import { type MemFsEditor, type MemFsEditorFile } from 'mem-fs-editor';
6
+ import type { DefaultEnvironmentApi, DefaultGeneratorApi } from '../types/type-helpers.js';
7
7
  import RunResult, { type RunResultOptions } from './run-result.js';
8
- import { type CreateEnv, type Dependency, type YeomanTest } from './helpers.js';
9
- import { type DummyPromptOptions, type TestAdapterOptions } from './adapter.js';
8
+ import { type CreateEnv as CreateEnvironment, type Dependency, type YeomanTest } from './helpers.js';
9
+ import { type AskedQuestions, type DummyPromptOptions, type TestAdapterOptions } from './adapter.js';
10
10
  /**
11
11
  * Provides settings for creating a `RunContext`.
12
12
  */
@@ -33,21 +33,22 @@ export type RunContextSettings = {
33
33
  };
34
34
  type PromiseRunResult<GeneratorType extends BaseGenerator> = Promise<RunResult<GeneratorType>>;
35
35
  type MockedGeneratorFactory<GenParameter extends BaseGenerator = DefaultGeneratorApi> = (GeneratorClass?: GetGeneratorConstructor<GenParameter>) => GetGeneratorConstructor<GenParameter>;
36
- type EnvOptions = BaseEnvironmentOptions & {
37
- createEnv?: CreateEnv;
36
+ type EnvironmentOptions = BaseEnvironmentOptions & {
37
+ createEnv?: CreateEnvironment;
38
38
  };
39
39
  export declare class RunContextBase<GeneratorType extends BaseGenerator = DefaultGeneratorApi> extends EventEmitter {
40
40
  readonly mockedGenerators: Record<string, BaseGenerator>;
41
41
  env: DefaultEnvironmentApi;
42
42
  generator: GeneratorType;
43
43
  readonly settings: RunContextSettings;
44
- readonly envOptions: EnvOptions;
44
+ readonly envOptions: EnvironmentOptions;
45
45
  completed: boolean;
46
46
  targetDirectory?: string;
47
47
  editor: MemFsEditor;
48
48
  memFs: Store<MemFsEditorFile>;
49
49
  spawnStub?: any;
50
50
  mockedGeneratorFactory: MockedGeneratorFactory;
51
+ readonly askedQuestions: AskedQuestions;
51
52
  protected environmentPromise?: PromiseRunResult<GeneratorType>;
52
53
  private args;
53
54
  private options;
@@ -78,13 +79,13 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
78
79
  * @param settings
79
80
  * @return {this}
80
81
  */
81
- constructor(generatorType?: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, envOptions?: EnvOptions, helpers?: YeomanTest);
82
+ constructor(generatorType?: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, environmentOptions?: EnvironmentOptions, helpers?: YeomanTest);
82
83
  /**
83
84
  * Run the generator on the environment and promises a RunResult instance.
84
85
  * @return {PromiseRunResult} Promise a RunResult instance.
85
86
  */
86
87
  run(): PromiseRunResult<GeneratorType>;
87
- on(eventName: string | symbol, listener: (...args: any[]) => void): this;
88
+ on(eventName: string | symbol, listener: (...arguments_: any[]) => void): this;
88
89
  /**
89
90
  * @deprecated
90
91
  * Clean the provided directory, then change directory into it
@@ -93,13 +94,13 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
93
94
  * @param [cb] - callback who'll receive the folder path as argument
94
95
  * @return run context instance
95
96
  */
96
- inDir(dirPath: string, cb?: (folderPath: string) => void): this;
97
+ inDir(dirPath: string, callback?: (folderPath: string) => void): this;
97
98
  /**
98
99
  * Register an callback to prepare the destination folder.
99
100
  * @param [cb] - callback who'll receive the folder path as argument
100
101
  * @return this - run context instance
101
102
  */
102
- doInDir(cb: (folderPath: string) => void): this;
103
+ doInDir(callback: (folderPath: string) => void): this;
103
104
  /**
104
105
  * @deprecated
105
106
  * Change directory without deleting directory content.
@@ -117,7 +118,7 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
117
118
  * @param [cb] - callback who'll receive the folder path as argument
118
119
  * @return this - run context instance
119
120
  */
120
- inTmpDir(cb?: (folderPath: string) => void): this;
121
+ inTmpDir(callback?: (folderPath: string) => void): this;
121
122
  /**
122
123
  * Restore cwd to initial cwd.
123
124
  * @return {this} run context instance
@@ -151,7 +152,7 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
151
152
  * @param {Function} [cb] - callback who'll receive the folder path as argument
152
153
  * @return {this} run context instance
153
154
  */
154
- withEnvironment(cb: any): this;
155
+ withEnvironment(callback: any): this;
155
156
  /**
156
157
  * Run lookup on the environment.
157
158
  *
@@ -162,7 +163,7 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
162
163
  * Provide arguments to the run context
163
164
  * @param args - command line arguments as Array or space separated string
164
165
  */
165
- withArguments(args: string | string[]): this;
166
+ withArguments(arguments_: string | string[]): this;
166
167
  /**
167
168
  * Provide options to the run context
168
169
  * @param {Object} options - command line options (e.g. `--opt-one=foo`)
@@ -203,10 +204,13 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
203
204
  * });
204
205
  */
205
206
  withGenerators(dependencies: Dependency[]): this;
206
- withSpawnMock(options?: ((...args: any[]) => any) | {
207
- stub?: (...args: any[]) => any;
208
- registerSinonDefaults?: boolean;
209
- callback?: (stub: any) => void | Promise<void>;
207
+ withSpawnMock<StubType = ReturnType<typeof mock.fn>>(options?: ((...arguments_: any[]) => any) | {
208
+ stub?: (...arguments_: any[]) => any;
209
+ registerNodeMockDefaults?: boolean;
210
+ callback?: ({ stub, implementation }: {
211
+ stub: StubType;
212
+ implementation: any;
213
+ }) => void | Promise<void>;
210
214
  }): this;
211
215
  withMockedGeneratorFactory(mockedGeneratorFactory: MockedGeneratorFactory): this;
212
216
  /**
@@ -282,7 +286,7 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
282
286
  * @param callback
283
287
  * @returns
284
288
  */
285
- onEnvironment(callback: (this: this, env: DefaultEnvironmentApi) => any): this;
289
+ onEnvironment(callback: (this: this, environment: DefaultEnvironmentApi) => any): this;
286
290
  prepare(): Promise<void>;
287
291
  protected assertNotBuild(): void;
288
292
  /**
@@ -1,20 +1,18 @@
1
1
  import crypto from 'node:crypto';
2
2
  import { existsSync, rmSync } from 'node:fs';
3
- import path, { resolve, isAbsolute, join as pathJoin } from 'node:path';
3
+ import path, { isAbsolute, join as pathJoin, resolve } from 'node:path';
4
4
  import assert from 'node:assert';
5
5
  import { EventEmitter } from 'node:events';
6
6
  import process from 'node:process';
7
+ import { mock } from 'node:test';
7
8
  import { camelCase, kebabCase, merge as lodashMerge, set as lodashSet } from 'lodash-es';
8
- // eslint-disable-next-line n/file-extension-in-import
9
9
  import { resetFileCommitStates } from 'mem-fs-editor/state';
10
10
  import { create as createMemFs } from 'mem-fs';
11
11
  import tempDirectory from 'temp-dir';
12
- import { stub as sinonStub } from 'sinon';
13
12
  import { create as createMemFsEditor } from 'mem-fs-editor';
14
13
  import RunResult from './run-result.js';
15
14
  import defaultHelpers from './helpers.js';
16
15
  import testContext from './test-context.js';
17
- // eslint-disable-next-line unicorn/prefer-event-target
18
16
  export class RunContextBase extends EventEmitter {
19
17
  mockedGenerators = {};
20
18
  env;
@@ -27,6 +25,7 @@ export class RunContextBase extends EventEmitter {
27
25
  memFs;
28
26
  spawnStub;
29
27
  mockedGeneratorFactory;
28
+ askedQuestions = [];
30
29
  environmentPromise;
31
30
  args = [];
32
31
  options = {};
@@ -57,13 +56,13 @@ export class RunContextBase extends EventEmitter {
57
56
  * @param settings
58
57
  * @return {this}
59
58
  */
60
- constructor(generatorType, settings, envOptions = {}, helpers = defaultHelpers) {
59
+ constructor(generatorType, settings, environmentOptions = {}, helpers = defaultHelpers) {
61
60
  super();
62
61
  this.settings = {
63
62
  ...settings,
64
63
  };
65
64
  this.Generator = generatorType;
66
- this.envOptions = envOptions;
65
+ this.envOptions = environmentOptions;
67
66
  this.oldCwd = this.settings.oldCwd;
68
67
  if (this.settings.cwd) {
69
68
  this.cd(this.settings.cwd);
@@ -97,7 +96,6 @@ export class RunContextBase extends EventEmitter {
97
96
  super.on(eventName, listener);
98
97
  // Don't setup emitters if on generator envent.
99
98
  if (eventName !== 'generator') {
100
- // eslint-disable-next-line @typescript-eslint/no-floating-promises
101
99
  this.setupEventListeners();
102
100
  }
103
101
  return this;
@@ -110,9 +108,9 @@ export class RunContextBase extends EventEmitter {
110
108
  * @param [cb] - callback who'll receive the folder path as argument
111
109
  * @return run context instance
112
110
  */
113
- inDir(dirPath, cb) {
111
+ inDir(dirPath, callback) {
114
112
  this.setDir(dirPath, true);
115
- this.helpers.testDirectory(dirPath, () => cb?.call(this, path.resolve(dirPath)));
113
+ this.helpers.testDirectory(dirPath, () => callback?.call(this, path.resolve(dirPath)));
116
114
  return this;
117
115
  }
118
116
  /**
@@ -120,8 +118,8 @@ export class RunContextBase extends EventEmitter {
120
118
  * @param [cb] - callback who'll receive the folder path as argument
121
119
  * @return this - run context instance
122
120
  */
123
- doInDir(cb) {
124
- this.inDirCallbacks.push(cb);
121
+ doInDir(callback) {
122
+ this.inDirCallbacks.push(callback);
125
123
  return this;
126
124
  }
127
125
  /**
@@ -152,8 +150,8 @@ export class RunContextBase extends EventEmitter {
152
150
  * @param [cb] - callback who'll receive the folder path as argument
153
151
  * @return this - run context instance
154
152
  */
155
- inTmpDir(cb) {
156
- return this.inDir(this.temporaryDir, cb);
153
+ inTmpDir(callback) {
154
+ return this.inDir(this.temporaryDir, callback);
157
155
  }
158
156
  /**
159
157
  * Restore cwd to initial cwd.
@@ -213,8 +211,8 @@ export class RunContextBase extends EventEmitter {
213
211
  * @param {Function} [cb] - callback who'll receive the folder path as argument
214
212
  * @return {this} run context instance
215
213
  */
216
- withEnvironment(cb) {
217
- this.envCB = cb;
214
+ withEnvironment(callback) {
215
+ this.envCB = callback;
218
216
  return this;
219
217
  }
220
218
  /**
@@ -223,11 +221,10 @@ export class RunContextBase extends EventEmitter {
223
221
  * @param lookups - lookup to run.
224
222
  */
225
223
  withLookups(lookups) {
226
- return this.onEnvironment(async (env) => {
224
+ return this.onEnvironment(async (environment) => {
227
225
  lookups = Array.isArray(lookups) ? lookups : [lookups];
228
226
  for (const lookup of lookups) {
229
- // eslint-disable-next-line no-await-in-loop
230
- await env.lookup(lookup);
227
+ await environment.lookup(lookup);
231
228
  }
232
229
  });
233
230
  }
@@ -235,10 +232,10 @@ export class RunContextBase extends EventEmitter {
235
232
  * Provide arguments to the run context
236
233
  * @param args - command line arguments as Array or space separated string
237
234
  */
238
- withArguments(args) {
239
- const argsArray = typeof args === 'string' ? args.split(' ') : args;
240
- assert(Array.isArray(argsArray), 'args should be either a string separated by spaces or an array');
241
- this.args = this.args.concat(argsArray);
235
+ withArguments(arguments_) {
236
+ const argumentsArray = typeof arguments_ === 'string' ? arguments_.split(' ') : arguments_;
237
+ assert(Array.isArray(argumentsArray), 'args should be either a string separated by spaces or an array');
238
+ this.args = [...this.args, ...argumentsArray];
242
239
  return this;
243
240
  }
244
241
  /**
@@ -300,13 +297,13 @@ export class RunContextBase extends EventEmitter {
300
297
  */
301
298
  withGenerators(dependencies) {
302
299
  assert(Array.isArray(dependencies), 'dependencies should be an array');
303
- return this.onEnvironment(async (env) => {
300
+ return this.onEnvironment(async (environment) => {
304
301
  for (const dependency of dependencies) {
305
302
  if (typeof dependency === 'string') {
306
- env.register(dependency);
303
+ environment.register(dependency);
307
304
  }
308
305
  else {
309
- env.register(...dependency);
306
+ environment.register(...dependency);
310
307
  }
311
308
  }
312
309
  });
@@ -315,31 +312,33 @@ export class RunContextBase extends EventEmitter {
315
312
  if (this.spawnStub) {
316
313
  throw new Error('Multiple withSpawnMock calls');
317
314
  }
318
- const stub = typeof options === 'function' ? options : options?.stub ?? sinonStub();
319
- const registerSinonDefaults = typeof options === 'function' ? false : options?.registerSinonDefaults ?? true;
320
- const callback = typeof options === 'function' ? undefined : options?.callback;
321
- if (registerSinonDefaults) {
322
- // eslint-disable-next-line @typescript-eslint/no-empty-function
315
+ const registerNodeMockDefaults = typeof options === 'function' ? false : (options?.registerNodeMockDefaults ?? true);
316
+ let implementation;
317
+ if (registerNodeMockDefaults) {
323
318
  const defaultChild = { stdout: { on() { } }, stderr: { on() { } } };
324
319
  const defaultReturn = { exitCode: 0, stdout: '', stderr: '' };
325
- const stubFn = stub;
326
- // eslint-disable-next-line @typescript-eslint/promise-function-async
327
- stubFn.withArgs('spawnCommand').callsFake(() => Object.assign(Promise.resolve({ ...defaultReturn }), defaultChild));
328
- // eslint-disable-next-line @typescript-eslint/promise-function-async
329
- stubFn.withArgs('spawn').callsFake(() => Object.assign(Promise.resolve({ ...defaultReturn }), defaultChild));
330
- stubFn.withArgs('spawnCommandSync').callsFake(() => ({ ...defaultReturn }));
331
- stubFn.withArgs('spawnSync').callsFake(() => ({ ...defaultReturn }));
320
+ implementation = (...arguments_) => {
321
+ const [methodName] = arguments_;
322
+ if (methodName === 'spawnCommand' || methodName === 'spawn') {
323
+ return Object.assign(Promise.resolve({ ...defaultReturn }), defaultChild);
324
+ }
325
+ if (methodName === 'spawnCommandSync' || methodName === 'spawnSync') {
326
+ return { ...defaultReturn };
327
+ }
328
+ };
332
329
  }
330
+ const stub = typeof options === 'function' ? options : (options?.stub ?? mock.fn(() => { }, implementation));
331
+ const callback = typeof options === 'function' ? undefined : options?.callback;
333
332
  if (callback) {
334
333
  this.onBeforePrepare(async () => {
335
- await callback(stub);
334
+ await callback({ stub, implementation });
336
335
  });
337
336
  }
338
337
  this.spawnStub = stub;
339
- return this.onEnvironment(env => {
340
- env.on('compose', (_namespace, generator) => {
341
- const createCallback = method => function (...args) {
342
- return stub.call(this, method, ...args);
338
+ return this.onEnvironment(environment => {
339
+ environment.on('compose', (_namespace, generator) => {
340
+ const createCallback = method => function (...arguments_) {
341
+ return stub.call(this, method, ...arguments_);
343
342
  };
344
343
  generator.spawnCommand = createCallback('spawnCommand');
345
344
  generator.spawnCommandSync = createCallback('spawnCommandSync');
@@ -476,9 +475,8 @@ export class RunContextBase extends EventEmitter {
476
475
  }
477
476
  async prepare() {
478
477
  if (this.beforePrepareCallbacks.length > 0) {
479
- for (const cb of this.beforePrepareCallbacks) {
480
- // eslint-disable-next-line no-await-in-loop
481
- await cb.call(this);
478
+ for (const callback of this.beforePrepareCallbacks) {
479
+ await callback.call(this);
482
480
  }
483
481
  }
484
482
  this.assertNotBuild();
@@ -491,9 +489,8 @@ export class RunContextBase extends EventEmitter {
491
489
  }
492
490
  if (this.inDirCallbacks.length > 0) {
493
491
  const targetDirectory = path.resolve(this.targetDirectory);
494
- for (const cb of this.inDirCallbacks) {
495
- // eslint-disable-next-line no-await-in-loop
496
- await cb(targetDirectory);
492
+ for (const callback of this.inDirCallbacks) {
493
+ await callback(targetDirectory);
497
494
  }
498
495
  }
499
496
  if (!this.targetDirectory) {
@@ -506,7 +503,6 @@ export class RunContextBase extends EventEmitter {
506
503
  }
507
504
  this.editor = createMemFsEditor(this.memFs);
508
505
  for (const onTargetDirectory of this.onTargetDirectoryCallbacks) {
509
- // eslint-disable-next-line no-await-in-loop
510
506
  await onTargetDirectory.call(this, this.targetDirectory);
511
507
  }
512
508
  }
@@ -519,21 +515,27 @@ export class RunContextBase extends EventEmitter {
519
515
  * Build the generator and the environment.
520
516
  * @return {RunContext|false} this
521
517
  */
522
- // eslint-disable-next-line @typescript-eslint/member-ordering
523
518
  async build() {
524
519
  await this.prepare();
525
- const testEnv = await this.helpers.createTestEnv(this.envOptions.createEnv, {
520
+ const { askedQuestions, adapterOptions } = this;
521
+ const promptCallback = function (answer, options) {
522
+ const { question } = options;
523
+ if (question.name) {
524
+ askedQuestions.push({ name: question.name, answer });
525
+ }
526
+ return adapterOptions?.callback ? adapterOptions.callback.call(this, answer, options) : answer;
527
+ };
528
+ const testEnvironment = await this.helpers.createTestEnv(this.envOptions.createEnv, {
526
529
  cwd: this.settings.forwardCwd ? this.targetDirectory : undefined,
527
530
  sharedFs: this.memFs,
528
531
  force: true,
529
532
  skipCache: true,
530
533
  skipInstall: true,
531
- adapter: this.helpers.createTestAdapter({ ...this.adapterOptions, mockedAnswers: this.answers }),
534
+ adapter: this.helpers.createTestAdapter({ ...this.adapterOptions, mockedAnswers: this.answers, callback: promptCallback }),
532
535
  ...this.envOptions,
533
536
  });
534
- this.env = this.envCB ? (await this.envCB(testEnv)) ?? testEnv : testEnv;
537
+ this.env = this.envCB ? ((await this.envCB(testEnvironment)) ?? testEnvironment) : testEnvironment;
535
538
  for (const onEnvironmentCallback of this.onEnvironmentCallbacks) {
536
- // eslint-disable-next-line no-await-in-loop
537
539
  await onEnvironmentCallback.call(this, this.env);
538
540
  }
539
541
  const { namespace = typeof this.Generator === 'string' ? this.env.namespace(this.Generator) : 'gen:test' } = this.settings;
@@ -555,7 +557,6 @@ export class RunContextBase extends EventEmitter {
555
557
  },
556
558
  });
557
559
  for (const onGeneratorCallback of this.onGeneratorCallbacks) {
558
- // eslint-disable-next-line no-await-in-loop
559
560
  await onGeneratorCallback.call(this, this.generator);
560
561
  }
561
562
  }
@@ -580,6 +581,7 @@ export class RunContextBase extends EventEmitter {
580
581
  envOptions: this.envOptions,
581
582
  mockedGenerators: this.mockedGenerators,
582
583
  helpers: this.helpers,
584
+ askedQuestions: this.askedQuestions,
583
585
  };
584
586
  }
585
587
  /**
@@ -596,8 +598,7 @@ export class RunContextBase extends EventEmitter {
596
598
  .catch(error => {
597
599
  if (this.listenerCount('end') === 0 && this.listenerCount('error') === 0) {
598
600
  // When there is no listeners throw a unhandled rejection.
599
- setImmediate(async function () {
600
- // eslint-disable-next-line @typescript-eslint/no-throw-literal
601
+ setImmediate(async () => {
601
602
  throw error;
602
603
  });
603
604
  }
@@ -631,7 +632,6 @@ export class RunContextBase extends EventEmitter {
631
632
  }
632
633
  }
633
634
  export default class RunContext extends RunContextBase {
634
- // eslint-disable-next-line unicorn/no-thenable
635
635
  async then(onfulfilled, onrejected) {
636
636
  return this.toPromise().then(onfulfilled, onrejected);
637
637
  }
@@ -1,9 +1,10 @@
1
1
  import type { Store } from 'mem-fs';
2
- import { type MemFsEditorFile, type MemFsEditor } from 'mem-fs-editor';
2
+ import { type MemFsEditor, type MemFsEditorFile } from 'mem-fs-editor';
3
3
  import type { BaseEnvironmentOptions, BaseGenerator, GetGeneratorConstructor } from '@yeoman/types';
4
4
  import type { DefaultEnvironmentApi, DefaultGeneratorApi } from '../types/type-helpers.js';
5
5
  import { type RunContextSettings } from './run-context.js';
6
6
  import { type YeomanTest } from './helpers.js';
7
+ import { type AskedQuestions } from './adapter.js';
7
8
  /**
8
9
  * Provides options for `RunResult`s.
9
10
  */
@@ -34,6 +35,7 @@ export type RunResultOptions<GeneratorType extends BaseGenerator> = {
34
35
  spawnStub?: any;
35
36
  settings: RunContextSettings;
36
37
  helpers: YeomanTest;
38
+ askedQuestions: AskedQuestions;
37
39
  };
38
40
  /**
39
41
  * This class provides utilities for testing generated content.
@@ -48,13 +50,14 @@ export default class RunResult<GeneratorType extends BaseGenerator = BaseGenerat
48
50
  mockedGenerators: any;
49
51
  options: RunResultOptions<GeneratorType>;
50
52
  spawnStub?: any;
53
+ readonly askedQuestions: AskedQuestions;
51
54
  constructor(options: RunResultOptions<GeneratorType>);
52
55
  /**
53
56
  * Create another RunContext reusing the settings.
54
57
  * See helpers.create api
55
58
  */
56
- create<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(GeneratorOrNamespace: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, envOptions?: BaseEnvironmentOptions): import("./run-context.js").default<GeneratorType>;
57
- getSpawnArgsUsingDefaultImplementation(): any;
59
+ create<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(GeneratorOrNamespace: string | GetGeneratorConstructor<GeneratorType>, settings?: RunContextSettings, environmentOptions?: BaseEnvironmentOptions): import("./run-context.js").default<GeneratorType>;
60
+ getSpawnArgsUsingDefaultImplementation(): unknown[][];
58
61
  /**
59
62
  * Return an object with fs changes.
60
63
  * @param {Function} filter - parameter forwarded to mem-fs-editor#dump
@@ -4,13 +4,13 @@ import path from 'node:path';
4
4
  import process from 'node:process';
5
5
  import { create as createMemFsEditor } from 'mem-fs-editor';
6
6
  const isObject = object => typeof object === 'object' && object !== null && object !== undefined;
7
- function convertArgs(args) {
8
- if (args.length > 1) {
9
- return [[...args]];
7
+ const convertArguments = arguments_ => {
8
+ if (arguments_.length > 1) {
9
+ return [[...arguments_]];
10
10
  }
11
- const arg = args[0];
12
- return Array.isArray(arg) ? arg : [arg];
13
- }
11
+ const [argument] = arguments_;
12
+ return Array.isArray(argument) ? argument : [argument];
13
+ };
14
14
  /**
15
15
  * This class provides utilities for testing generated content.
16
16
  */
@@ -24,6 +24,7 @@ export default class RunResult {
24
24
  mockedGenerators;
25
25
  options;
26
26
  spawnStub;
27
+ askedQuestions;
27
28
  constructor(options) {
28
29
  if (options.memFs && !options.cwd) {
29
30
  throw new Error('CWD option is required for mem-fs tests');
@@ -36,13 +37,14 @@ export default class RunResult {
36
37
  this.fs = this.memFs && createMemFsEditor(this.memFs);
37
38
  this.mockedGenerators = options.mockedGenerators || {};
38
39
  this.spawnStub = options.spawnStub;
40
+ this.askedQuestions = options.askedQuestions;
39
41
  this.options = options;
40
42
  }
41
43
  /**
42
44
  * Create another RunContext reusing the settings.
43
45
  * See helpers.create api
44
46
  */
45
- create(GeneratorOrNamespace, settings, envOptions) {
47
+ create(GeneratorOrNamespace, settings, environmentOptions) {
46
48
  return this.options.helpers.create(GeneratorOrNamespace, {
47
49
  ...this.options.settings,
48
50
  cwd: this.cwd,
@@ -50,13 +52,13 @@ export default class RunResult {
50
52
  memFs: this.memFs,
51
53
  ...settings,
52
54
  autoCleanup: false,
53
- }, { ...this.options.envOptions, ...envOptions });
55
+ }, { ...this.options.envOptions, ...environmentOptions });
54
56
  }
55
57
  getSpawnArgsUsingDefaultImplementation() {
56
58
  if (!this.spawnStub) {
57
59
  throw new Error('Spawn stub was not found');
58
60
  }
59
- return this.spawnStub.getCalls().map(call => call.args);
61
+ return this.spawnStub.mock.calls.map(call => call.arguments);
60
62
  }
61
63
  /**
62
64
  * Return an object with fs changes.
@@ -152,7 +154,7 @@ export default class RunResult {
152
154
  * result.assertFile(['templates/user.hbs', 'templates/user/edit.hbs']);
153
155
  */
154
156
  assertFile(path) {
155
- for (const file of convertArgs([path])) {
157
+ for (const file of convertArguments([path])) {
156
158
  const here = this._exists(file);
157
159
  assert.ok(here, `${file}, no such file or directory`);
158
160
  }
@@ -171,15 +173,14 @@ export default class RunResult {
171
173
  * result.assertNoFile(['templates/user.hbs', 'templates/user/edit.hbs']);
172
174
  */
173
175
  assertNoFile(files) {
174
- for (const file of convertArgs([files])) {
176
+ for (const file of convertArguments([files])) {
175
177
  const here = this._exists(file);
176
178
  assert.ok(!here, `${file} exists`);
177
179
  }
178
180
  }
179
- assertFileContent(...args) {
180
- for (const pair of convertArgs(args)) {
181
- const file = pair[0];
182
- const regex = pair[1];
181
+ assertFileContent(...arguments_) {
182
+ for (const pair of convertArguments(arguments_)) {
183
+ const [file, regex] = pair;
183
184
  this.assertFile(file);
184
185
  const body = this._readFile(file);
185
186
  let match = false;
@@ -187,18 +188,16 @@ export default class RunResult {
187
188
  assert(match, `${file} did not match '${regex}'. Contained:\n\n${body}`);
188
189
  }
189
190
  }
190
- assertEqualsFileContent(...args) {
191
- for (const pair of convertArgs(args)) {
192
- const file = pair[0];
193
- const expectedContent = pair[1];
191
+ assertEqualsFileContent(...arguments_) {
192
+ for (const pair of convertArguments(arguments_)) {
193
+ const [file, expectedContent] = pair;
194
194
  this.assertFile(file);
195
195
  this.assertTextEqual(this._readFile(file), expectedContent);
196
196
  }
197
197
  }
198
- assertNoFileContent(...args) {
199
- for (const pair of convertArgs(args)) {
200
- const file = pair[0];
201
- const regex = pair[1];
198
+ assertNoFileContent(...arguments_) {
199
+ for (const pair of convertArguments(arguments_)) {
200
+ const [file, regex] = pair;
202
201
  this.assertFile(file);
203
202
  const body = this._readFile(file);
204
203
  if (typeof regex === 'string') {
@@ -1,9 +1,11 @@
1
- import type RunContext from './run-context.js';
2
1
  import type RunResult from './run-result.js';
3
2
  declare class TestContext {
3
+ beforeCwd?: string;
4
+ autoRestore: boolean;
5
+ autoCleanup?: boolean;
4
6
  runResult?: RunResult;
5
7
  private runContext?;
6
- startNewContext(runContext?: RunContext<any>): void;
8
+ startNewContext(runContext?: any, autoCleanup?: boolean): void;
7
9
  }
8
10
  declare const testContext: TestContext;
9
11
  export default testContext;
@@ -1,27 +1,40 @@
1
1
  import process from 'node:process';
2
+ import { onExit } from 'signal-exit';
2
3
  class TestContext {
4
+ beforeCwd;
5
+ autoRestore = true;
6
+ autoCleanup;
3
7
  runResult;
4
8
  runContext;
5
- startNewContext(runContext) {
6
- this.runContext?.cleanupTemporaryDir();
9
+ startNewContext(runContext, autoCleanup = true) {
10
+ if (this.beforeCwd !== process.cwd()) {
11
+ if (this.autoCleanup) {
12
+ this.runContext?.cleanupTemporaryDir();
13
+ }
14
+ else if (this.autoRestore) {
15
+ this.runContext?.restore();
16
+ }
17
+ }
18
+ if (this.beforeCwd && this.beforeCwd !== process.cwd()) {
19
+ console.log('Test failed to restore context', this.beforeCwd, process.cwd());
20
+ }
21
+ this.autoCleanup = autoCleanup;
22
+ this.beforeCwd = runContext ? process.cwd() : undefined;
7
23
  this.runContext = runContext;
8
24
  this.runResult = undefined;
9
25
  }
10
26
  }
11
27
  const testContext = new TestContext();
12
- const cleanupTemporaryDir = () => {
28
+ onExit(() => {
13
29
  testContext.startNewContext();
14
- };
15
- process.on('exit', cleanupTemporaryDir);
16
- process.on('SIGINT', cleanupTemporaryDir);
17
- process.on('SIGTERM', cleanupTemporaryDir);
30
+ });
18
31
  export default testContext;
19
32
  const handler2 = {
20
- get(_target, prop, receiver) {
33
+ get(_target, property, receiver) {
21
34
  if (testContext.runResult === undefined) {
22
35
  throw new Error('Last result is missing.');
23
36
  }
24
- return Reflect.get(testContext.runResult, prop, receiver);
37
+ return Reflect.get(testContext.runResult, property, receiver);
25
38
  },
26
39
  };
27
40
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeoman-test",
3
- "version": "8.2.0",
3
+ "version": "9.0.0",
4
4
  "description": "Test utilities for Yeoman generators",
5
5
  "keywords": [
6
6
  "yeoman",
@@ -12,8 +12,13 @@
12
12
  "author": "The Yeoman Team",
13
13
  "type": "module",
14
14
  "exports": {
15
- "types": "./dist/index.d.ts",
16
- "import": "./dist/index.js"
15
+ "./mocha-cleanup": {
16
+ "import": "./dist/mocha-cleanup.hooks.js"
17
+ },
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js"
21
+ }
17
22
  },
18
23
  "types": "./dist/index.d.ts",
19
24
  "files": [
@@ -27,52 +32,45 @@
27
32
  "doc:fix": "sed -i -e 's:^[[:space:]]*<!--[[:space:]]*$::g' -e 's:^[[:space:]]*-->[[:space:]]*$::g' $npm_package_config_doc_path/global.html",
28
33
  "doc:generate": "jsdoc -c jsdoc.json -d $npm_package_config_doc_path",
29
34
  "doc:prettier": "prettier $npm_package_config_doc_path --write --ignore-path .prettierignore-doc",
35
+ "fix": "prettier . --write && eslint . --fix",
30
36
  "precommit": "lint-staged",
31
- "prepare": "npm run build",
32
- "pretest": "xo",
33
- "test": "c8 esmocha --forbid-only"
37
+ "prepare": "npm run build && husky",
38
+ "pretest": "eslint .",
39
+ "test": "vitest run --coverage"
34
40
  },
35
41
  "config": {
36
42
  "doc_path": "../yeoman-test-doc"
37
43
  },
38
44
  "dependencies": {
39
- "@yeoman/adapter": "^1.4.0",
40
- "inquirer": "^9.2.2",
45
+ "@yeoman/adapter": "^1.6.0",
41
46
  "lodash-es": "^4.17.21",
42
- "mem-fs-editor": "^10.0.3",
43
- "sinon": "^16.0.0",
47
+ "mem-fs-editor": "^11.1.1",
48
+ "signal-exit": "^4.1.0",
44
49
  "temp-dir": "^3.0.0",
45
- "type-fest": "^4.3.1"
50
+ "type-fest": "^4.25.0"
46
51
  },
47
52
  "devDependencies": {
48
- "@types/inquirer": "^9.0.3",
49
- "@types/lodash": "^4.14.195",
50
- "@types/node": "^16.18.19",
51
- "@types/sinon": "^10.0.13",
52
- "c8": "^8.0.0",
53
+ "@types/lodash-es": "^4.17.12",
54
+ "@types/node": "^18.19.46",
55
+ "@vitest/coverage-v8": "^2.0.5",
56
+ "@yeoman/eslint": "0.2.0",
53
57
  "coveralls": "^3.1.1",
54
- "esmocha": "^1.0.1",
55
- "husky": "^8.0.2",
58
+ "husky": "^9.1.5",
56
59
  "jsdoc": "^4.0.2",
57
- "lint-staged": "^14.0.1",
58
- "prettier": "^3.0.3",
60
+ "lint-staged": "^15.2.9",
61
+ "prettier": "^3.3.3",
59
62
  "prettier-plugin-packagejson": "^2.3.0",
60
63
  "tui-jsdoc-template": "^1.2.2",
61
- "typescript": "~5.2.2",
62
- "xo": "0.56.0",
63
- "yeoman-environment": "^3.18.3",
64
- "yeoman-generator": "^5.9.0"
64
+ "typescript": "^5.5.4",
65
+ "vitest": "^2.0.5",
66
+ "yeoman-environment": "^4.4.1",
67
+ "yeoman-generator": "^7.3.2"
65
68
  },
66
69
  "peerDependencies": {
67
- "@yeoman/types": "^1.1.0",
68
- "mem-fs": "^3.0.0",
69
- "yeoman-environment": "^3.18.3",
70
- "yeoman-generator": "^5.9.0 || >=6.0.0"
71
- },
72
- "acceptDependencies": {
73
- "mem-fs": "^4.0.0-beta.1",
74
- "yeoman-environment": ">=4.0.0-beta.6",
75
- "yeoman-generator": ">=6.0.0"
70
+ "@yeoman/types": "^1.4.0",
71
+ "mem-fs": "^4.1.0",
72
+ "yeoman-environment": "^4.0.0",
73
+ "yeoman-generator": "^7.0.0"
76
74
  },
77
75
  "peerDependenciesMeta": {
78
76
  "yeoman-environment": {
@@ -83,6 +81,6 @@
83
81
  }
84
82
  },
85
83
  "engines": {
86
- "node": "^16.17.0 || >=18.12.0"
84
+ "node": "^18.19.0 || >= 20.6.1"
87
85
  }
88
86
  }
@@ -1,6 +1,6 @@
1
- import type { BaseGenerator, BaseEnvironment } from '@yeoman/types';
1
+ import type { BaseEnvironment, BaseGenerator } from '@yeoman/types';
2
2
  import type GeneratorImplementation from 'yeoman-generator';
3
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
3
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
4
4
  // @ts-ignore
5
5
  import type EnvironmentImplementation from 'yeoman-environment';
6
6
  import type { IfAny } from 'type-fest';