yeoman-test 8.1.1 → 8.3.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,4 +1,3 @@
1
- // eslint-disable-next-line n/file-extension-in-import
2
1
  import { TestAdapter as BaseTestAdapter } from '@yeoman/adapter/testing';
3
2
  import { spy as sinonSpy, stub as sinonStub } from 'sinon';
4
3
  export class TestAdapter extends BaseTestAdapter {
@@ -9,5 +8,4 @@ export class TestAdapter extends BaseTestAdapter {
9
8
  });
10
9
  }
11
10
  }
12
- // eslint-disable-next-line n/file-extension-in-import
13
11
  export { DummyPrompt } from '@yeoman/adapter/testing';
@@ -6,7 +6,7 @@ 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
8
  import { type CreateEnv, type Dependency, type YeomanTest } from './helpers.js';
9
- import { type DummyPromptOptions, type TestAdapterOptions } from './adapter.js';
9
+ import { type AskedQuestions, type DummyPromptOptions, type TestAdapterOptions } from './adapter.js';
10
10
  /**
11
11
  * Provides settings for creating a `RunContext`.
12
12
  */
@@ -48,6 +48,7 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
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;
@@ -203,7 +204,11 @@ export declare class RunContextBase<GeneratorType extends BaseGenerator = Defaul
203
204
  * });
204
205
  */
205
206
  withGenerators(dependencies: Dependency[]): this;
206
- withSpawnMock(stub?: import("sinon").SinonStub<any[], any>): this;
207
+ withSpawnMock(options?: ((...args: any[]) => any) | {
208
+ stub?: (...args: any[]) => any;
209
+ registerSinonDefaults?: boolean;
210
+ callback?: (stub: any) => void | Promise<void>;
211
+ }): this;
207
212
  withMockedGeneratorFactory(mockedGeneratorFactory: MockedGeneratorFactory): this;
208
213
  /**
209
214
  * Create mocked generators
@@ -5,7 +5,6 @@ import assert from 'node:assert';
5
5
  import { EventEmitter } from 'node:events';
6
6
  import process from 'node:process';
7
7
  import { camelCase, kebabCase, merge as lodashMerge, set as lodashSet } from 'lodash-es';
8
- // eslint-disable-next-line n/file-extension-in-import
9
8
  import { resetFileCommitStates } from 'mem-fs-editor/state';
10
9
  import { create as createMemFs } from 'mem-fs';
11
10
  import tempDirectory from 'temp-dir';
@@ -27,6 +26,7 @@ export class RunContextBase extends EventEmitter {
27
26
  memFs;
28
27
  spawnStub;
29
28
  mockedGeneratorFactory;
29
+ askedQuestions = [];
30
30
  environmentPromise;
31
31
  args = [];
32
32
  options = {};
@@ -311,10 +311,30 @@ export class RunContextBase extends EventEmitter {
311
311
  }
312
312
  });
313
313
  }
314
- withSpawnMock(stub = sinonStub()) {
314
+ withSpawnMock(options) {
315
315
  if (this.spawnStub) {
316
316
  throw new Error('Multiple withSpawnMock calls');
317
317
  }
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
323
+ const defaultChild = { stdout: { on() { } }, stderr: { on() { } } };
324
+ 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 }));
332
+ }
333
+ if (callback) {
334
+ this.onBeforePrepare(async () => {
335
+ await callback(stub);
336
+ });
337
+ }
318
338
  this.spawnStub = stub;
319
339
  return this.onEnvironment(env => {
320
340
  env.on('compose', (_namespace, generator) => {
@@ -502,13 +522,21 @@ export class RunContextBase extends EventEmitter {
502
522
  // eslint-disable-next-line @typescript-eslint/member-ordering
503
523
  async build() {
504
524
  await this.prepare();
525
+ const { askedQuestions, adapterOptions } = this;
526
+ const promptCallback = function (answer, options) {
527
+ const { question } = options;
528
+ if (question.name) {
529
+ askedQuestions.push({ name: question.name, answer });
530
+ }
531
+ return adapterOptions?.callback ? adapterOptions.callback.call(this, answer, options) : answer;
532
+ };
505
533
  const testEnv = await this.helpers.createTestEnv(this.envOptions.createEnv, {
506
534
  cwd: this.settings.forwardCwd ? this.targetDirectory : undefined,
507
535
  sharedFs: this.memFs,
508
536
  force: true,
509
537
  skipCache: true,
510
538
  skipInstall: true,
511
- adapter: this.helpers.createTestAdapter({ ...this.adapterOptions, mockedAnswers: this.answers }),
539
+ adapter: this.helpers.createTestAdapter({ ...this.adapterOptions, mockedAnswers: this.answers, callback: promptCallback }),
512
540
  ...this.envOptions,
513
541
  });
514
542
  this.env = this.envCB ? (await this.envCB(testEnv)) ?? testEnv : testEnv;
@@ -560,6 +588,7 @@ export class RunContextBase extends EventEmitter {
560
588
  envOptions: this.envOptions,
561
589
  mockedGenerators: this.mockedGenerators,
562
590
  helpers: this.helpers,
591
+ askedQuestions: this.askedQuestions,
563
592
  };
564
593
  }
565
594
  /**
@@ -4,6 +4,7 @@ import type { BaseEnvironmentOptions, BaseGenerator, GetGeneratorConstructor } f
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,6 +50,7 @@ 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.
@@ -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,6 +37,7 @@ 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
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yeoman-test",
3
- "version": "8.1.1",
3
+ "version": "8.3.0",
4
4
  "description": "Test utilities for Yeoman generators",
5
5
  "keywords": [
6
6
  "yeoman",