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 +15 -38
- package/dist/adapter.d.ts +4 -0
- package/dist/adapter.js +4 -4
- package/dist/default-environment.d.ts +1 -1
- package/dist/default-environment.js +5 -6
- package/dist/helpers.d.ts +9 -9
- package/dist/helpers.js +42 -40
- package/dist/mocha-cleanup.hooks.d.ts +3 -0
- package/dist/mocha-cleanup.hooks.js +6 -0
- package/dist/run-context.d.ts +25 -21
- package/dist/run-context.js +58 -58
- package/dist/run-result.d.ts +6 -3
- package/dist/run-result.js +22 -23
- package/dist/test-context.d.ts +4 -2
- package/dist/test-context.js +22 -9
- package/package.json +32 -34
- package/types/type-helpers.d.ts +2 -2
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
|
-
|
|
35
|
-
.
|
|
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
|
-
|
|
58
|
-
|
|
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
|
-
|
|
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
|
-
[
|
|
73
|
-
[
|
|
74
|
-
[
|
|
75
|
-
[
|
|
76
|
-
[
|
|
77
|
-
[
|
|
78
|
-
[
|
|
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
|
-
|
|
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 }) =>
|
|
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(
|
|
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
|
|
7
|
-
if (typeof
|
|
8
|
-
return new
|
|
6
|
+
const DynamicEnvironment = await import('yeoman-environment');
|
|
7
|
+
if (typeof DynamicEnvironment === 'function') {
|
|
8
|
+
return new DynamicEnvironment(options);
|
|
9
9
|
}
|
|
10
|
-
if (typeof
|
|
11
|
-
|
|
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,
|
|
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(
|
|
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(
|
|
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):
|
|
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, (...
|
|
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(
|
|
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,
|
|
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,
|
|
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 {
|
|
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,
|
|
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
|
|
66
|
+
return callback?.();
|
|
65
67
|
}
|
|
66
68
|
catch (error) {
|
|
67
|
-
return
|
|
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(
|
|
82
|
-
const environment = 'env' in
|
|
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(
|
|
98
|
-
const environment =
|
|
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 =
|
|
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:
|
|
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(...
|
|
134
|
-
const optIndex = Array.isArray(
|
|
135
|
-
|
|
136
|
-
const options =
|
|
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(...
|
|
141
|
+
super(...arguments_);
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
|
-
for (const [
|
|
143
|
-
Object.defineProperty(DummyGenerator.prototype,
|
|
144
|
-
value:
|
|
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
|
|
173
|
+
const environment = await this.createEnv({ sharedOptions: { localConfigOnly } });
|
|
172
174
|
for (const dependency of dependencies) {
|
|
173
175
|
if (typeof dependency === 'string') {
|
|
174
|
-
|
|
176
|
+
environment.register(dependency);
|
|
175
177
|
}
|
|
176
178
|
else {
|
|
177
|
-
|
|
179
|
+
environment.register(...dependency);
|
|
178
180
|
}
|
|
179
181
|
}
|
|
180
|
-
return
|
|
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
|
|
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(
|
|
208
|
-
let
|
|
209
|
+
async createTestEnv(environmentContructor = this.createEnv, options = { localConfigOnly: true }) {
|
|
210
|
+
let environmentOptions = cloneDeep(this.environmentOptions ?? {});
|
|
209
211
|
if (typeof options === 'boolean') {
|
|
210
|
-
|
|
212
|
+
environmentOptions = {
|
|
211
213
|
newErrorHandler: true,
|
|
212
|
-
...
|
|
214
|
+
...environmentOptions,
|
|
213
215
|
sharedOptions: {
|
|
214
216
|
localConfigOnly: options,
|
|
215
|
-
...
|
|
217
|
+
...environmentOptions.sharedOptions,
|
|
216
218
|
},
|
|
217
219
|
};
|
|
218
220
|
}
|
|
219
221
|
else {
|
|
220
|
-
|
|
222
|
+
environmentOptions.sharedOptions = {
|
|
221
223
|
localConfigOnly: true,
|
|
222
|
-
...
|
|
224
|
+
...environmentOptions.sharedOptions,
|
|
223
225
|
...options.sharedOptions,
|
|
224
226
|
};
|
|
225
|
-
|
|
227
|
+
environmentOptions = {
|
|
226
228
|
newErrorHandler: true,
|
|
227
|
-
...
|
|
229
|
+
...environmentOptions,
|
|
228
230
|
...options,
|
|
229
231
|
};
|
|
230
232
|
}
|
|
231
|
-
return
|
|
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,
|
|
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 },
|
|
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,
|
|
266
|
-
return this.run(GeneratorOrNamespace, settings,
|
|
267
|
+
create(GeneratorOrNamespace, settings, environmentOptions) {
|
|
268
|
+
return this.run(GeneratorOrNamespace, settings, environmentOptions);
|
|
267
269
|
}
|
|
268
270
|
/**
|
|
269
271
|
* Prepare temporary dir without generator support.
|
package/dist/run-context.d.ts
CHANGED
|
@@ -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,
|
|
5
|
-
import { type
|
|
6
|
-
import type {
|
|
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
|
|
37
|
-
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:
|
|
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,
|
|
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: (...
|
|
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,
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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?: ((...
|
|
207
|
-
stub?: (...
|
|
208
|
-
|
|
209
|
-
callback?: (stub
|
|
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,
|
|
289
|
+
onEnvironment(callback: (this: this, environment: DefaultEnvironmentApi) => any): this;
|
|
286
290
|
prepare(): Promise<void>;
|
|
287
291
|
protected assertNotBuild(): void;
|
|
288
292
|
/**
|
package/dist/run-context.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import crypto from 'node:crypto';
|
|
2
2
|
import { existsSync, rmSync } from 'node:fs';
|
|
3
|
-
import 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,
|
|
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 =
|
|
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,
|
|
111
|
+
inDir(dirPath, callback) {
|
|
114
112
|
this.setDir(dirPath, true);
|
|
115
|
-
this.helpers.testDirectory(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(
|
|
124
|
-
this.inDirCallbacks.push(
|
|
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(
|
|
156
|
-
return this.inDir(this.temporaryDir,
|
|
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(
|
|
217
|
-
this.envCB =
|
|
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 (
|
|
224
|
+
return this.onEnvironment(async (environment) => {
|
|
227
225
|
lookups = Array.isArray(lookups) ? lookups : [lookups];
|
|
228
226
|
for (const lookup of lookups) {
|
|
229
|
-
|
|
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(
|
|
239
|
-
const
|
|
240
|
-
assert(Array.isArray(
|
|
241
|
-
this.args = this.args
|
|
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 (
|
|
300
|
+
return this.onEnvironment(async (environment) => {
|
|
304
301
|
for (const dependency of dependencies) {
|
|
305
302
|
if (typeof dependency === 'string') {
|
|
306
|
-
|
|
303
|
+
environment.register(dependency);
|
|
307
304
|
}
|
|
308
305
|
else {
|
|
309
|
-
|
|
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
|
|
319
|
-
|
|
320
|
-
|
|
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
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
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(
|
|
340
|
-
|
|
341
|
-
const createCallback = method => function (...
|
|
342
|
-
return stub.call(this, method, ...
|
|
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
|
|
480
|
-
|
|
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
|
|
495
|
-
|
|
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
|
|
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(
|
|
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
|
|
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
|
}
|
package/dist/run-result.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Store } from 'mem-fs';
|
|
2
|
-
import { type
|
|
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,
|
|
57
|
-
getSpawnArgsUsingDefaultImplementation():
|
|
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
|
package/dist/run-result.js
CHANGED
|
@@ -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
|
-
|
|
8
|
-
if (
|
|
9
|
-
return [[...
|
|
7
|
+
const convertArguments = arguments_ => {
|
|
8
|
+
if (arguments_.length > 1) {
|
|
9
|
+
return [[...arguments_]];
|
|
10
10
|
}
|
|
11
|
-
const
|
|
12
|
-
return Array.isArray(
|
|
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,
|
|
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, ...
|
|
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.
|
|
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
|
|
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
|
|
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(...
|
|
180
|
-
for (const pair of
|
|
181
|
-
const file = pair
|
|
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(...
|
|
191
|
-
for (const pair of
|
|
192
|
-
const file = pair
|
|
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(...
|
|
199
|
-
for (const pair of
|
|
200
|
-
const file = pair
|
|
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') {
|
package/dist/test-context.d.ts
CHANGED
|
@@ -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?:
|
|
8
|
+
startNewContext(runContext?: any, autoCleanup?: boolean): void;
|
|
7
9
|
}
|
|
8
10
|
declare const testContext: TestContext;
|
|
9
11
|
export default testContext;
|
package/dist/test-context.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
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,
|
|
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,
|
|
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": "
|
|
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
|
-
"
|
|
16
|
-
|
|
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": "
|
|
33
|
-
"test": "
|
|
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.
|
|
40
|
-
"inquirer": "^9.2.2",
|
|
45
|
+
"@yeoman/adapter": "^1.6.0",
|
|
41
46
|
"lodash-es": "^4.17.21",
|
|
42
|
-
"mem-fs-editor": "^
|
|
43
|
-
"
|
|
47
|
+
"mem-fs-editor": "^11.1.1",
|
|
48
|
+
"signal-exit": "^4.1.0",
|
|
44
49
|
"temp-dir": "^3.0.0",
|
|
45
|
-
"type-fest": "^4.
|
|
50
|
+
"type-fest": "^4.25.0"
|
|
46
51
|
},
|
|
47
52
|
"devDependencies": {
|
|
48
|
-
"@types/
|
|
49
|
-
"@types/
|
|
50
|
-
"@
|
|
51
|
-
"@
|
|
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
|
-
"
|
|
55
|
-
"husky": "^8.0.2",
|
|
58
|
+
"husky": "^9.1.5",
|
|
56
59
|
"jsdoc": "^4.0.2",
|
|
57
|
-
"lint-staged": "^
|
|
58
|
-
"prettier": "^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": "
|
|
62
|
-
"
|
|
63
|
-
"yeoman-environment": "^
|
|
64
|
-
"yeoman-generator": "^
|
|
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.
|
|
68
|
-
"mem-fs": "^
|
|
69
|
-
"yeoman-environment": "^
|
|
70
|
-
"yeoman-generator": "^
|
|
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": "^
|
|
84
|
+
"node": "^18.19.0 || >= 20.6.1"
|
|
87
85
|
}
|
|
88
86
|
}
|
package/types/type-helpers.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
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
|
|
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';
|