yeoman-test 6.2.0 → 7.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 +2 -1
- package/dist/adapter.d.ts +24 -0
- package/dist/adapter.js +104 -0
- package/dist/helpers.d.ts +151 -0
- package/dist/helpers.js +258 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +4 -0
- package/dist/run-context.d.ts +279 -0
- package/dist/run-context.js +541 -0
- package/dist/run-result.d.ts +214 -0
- package/dist/run-result.js +258 -0
- package/dist/test-context.d.ts +8 -0
- package/dist/test-context.js +9 -0
- package/package.json +37 -21
- package/lib/adapter.js +0 -107
- package/lib/index.js +0 -339
- package/lib/run-context.js +0 -552
- package/lib/run-result.js +0 -372
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
import crypto from 'node:crypto';
|
|
2
|
+
import { existsSync, rmSync } from 'node:fs';
|
|
3
|
+
import path, { resolve, isAbsolute, join as pathJoin } from 'node:path';
|
|
4
|
+
import assert from 'node:assert';
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
6
|
+
import process from 'node:process';
|
|
7
|
+
import _ from 'lodash';
|
|
8
|
+
import tempDirectory from 'temp-dir';
|
|
9
|
+
import MemFsEditor from 'mem-fs-editor';
|
|
10
|
+
import RunResult from './run-result.js';
|
|
11
|
+
import defaultHelpers from './helpers.js';
|
|
12
|
+
export class RunContextBase extends EventEmitter {
|
|
13
|
+
mockedGenerators = {};
|
|
14
|
+
env;
|
|
15
|
+
generator;
|
|
16
|
+
settings;
|
|
17
|
+
envOptions;
|
|
18
|
+
completed = false;
|
|
19
|
+
targetDirectory;
|
|
20
|
+
editor;
|
|
21
|
+
environmentPromise;
|
|
22
|
+
args = [];
|
|
23
|
+
options = {};
|
|
24
|
+
answers;
|
|
25
|
+
onGeneratorCallbacks = [];
|
|
26
|
+
onTargetDirectoryCallbacks = [];
|
|
27
|
+
onEnvironmentCallbacks = [];
|
|
28
|
+
inDirCallbacks = [];
|
|
29
|
+
Generator;
|
|
30
|
+
helpers;
|
|
31
|
+
temporaryDir = path.join(tempDirectory, crypto.randomBytes(20).toString('hex'));
|
|
32
|
+
oldCwd;
|
|
33
|
+
eventListenersSet = false;
|
|
34
|
+
envCB;
|
|
35
|
+
ran = false;
|
|
36
|
+
errored = false;
|
|
37
|
+
/**
|
|
38
|
+
* This class provide a run context object to façade the complexity involved in setting
|
|
39
|
+
* up a generator for testing
|
|
40
|
+
* @constructor
|
|
41
|
+
* @param Generator - Namespace or generator constructor. If the later
|
|
42
|
+
* is provided, then namespace is assumed to be
|
|
43
|
+
* 'gen:test' in all cases
|
|
44
|
+
* @param settings
|
|
45
|
+
* @return {this}
|
|
46
|
+
*/
|
|
47
|
+
constructor(generatorType, settings, envOptions = {}, helpers = defaultHelpers) {
|
|
48
|
+
super();
|
|
49
|
+
this.settings = {
|
|
50
|
+
namespace: 'gen:test',
|
|
51
|
+
...settings,
|
|
52
|
+
};
|
|
53
|
+
this.Generator = generatorType;
|
|
54
|
+
if (typeof generatorType !== 'string') {
|
|
55
|
+
const { namespace, resolved } = this.settings;
|
|
56
|
+
this.withGenerators([[generatorType, namespace, resolved]]);
|
|
57
|
+
}
|
|
58
|
+
this.envOptions = envOptions;
|
|
59
|
+
this.withOptions({
|
|
60
|
+
force: true,
|
|
61
|
+
skipCache: true,
|
|
62
|
+
skipInstall: true,
|
|
63
|
+
});
|
|
64
|
+
this.oldCwd = this.settings.oldCwd;
|
|
65
|
+
if (this.settings.cwd) {
|
|
66
|
+
this.cd(this.settings.cwd);
|
|
67
|
+
}
|
|
68
|
+
this.helpers = helpers;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Run the generator on the environment and promises a RunResult instance.
|
|
72
|
+
* @return {PromiseRunResult} Promise a RunResult instance.
|
|
73
|
+
*/
|
|
74
|
+
async run() {
|
|
75
|
+
if (!this.ran) {
|
|
76
|
+
await this.build();
|
|
77
|
+
}
|
|
78
|
+
try {
|
|
79
|
+
await this.env.runGenerator(this.generator);
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
this.helpers.restorePrompt(this.env);
|
|
83
|
+
this.completed = true;
|
|
84
|
+
}
|
|
85
|
+
return new RunResult(this._createRunResultOptions());
|
|
86
|
+
}
|
|
87
|
+
// If any event listeners is added, setup event listeners emitters
|
|
88
|
+
on(eventName, listener) {
|
|
89
|
+
super.on(eventName, listener);
|
|
90
|
+
// Don't setup emitters if on generator envent.
|
|
91
|
+
if (eventName !== 'generator') {
|
|
92
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
93
|
+
this.setupEventListeners();
|
|
94
|
+
}
|
|
95
|
+
return this;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* @deprecated
|
|
99
|
+
* Clean the provided directory, then change directory into it
|
|
100
|
+
* @param dirPath - Directory path (relative to CWD). Prefer passing an absolute
|
|
101
|
+
* file path for predictable results
|
|
102
|
+
* @param [cb] - callback who'll receive the folder path as argument
|
|
103
|
+
* @return run context instance
|
|
104
|
+
*/
|
|
105
|
+
inDir(dirPath, cb) {
|
|
106
|
+
this.setDir(dirPath, true);
|
|
107
|
+
this.helpers.testDirectory(dirPath, () => cb?.call(this, path.resolve(dirPath)));
|
|
108
|
+
return this;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Register an callback to prepare the destination folder.
|
|
112
|
+
* @param [cb] - callback who'll receive the folder path as argument
|
|
113
|
+
* @return this - run context instance
|
|
114
|
+
*/
|
|
115
|
+
doInDir(cb) {
|
|
116
|
+
this.inDirCallbacks.push(cb);
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* @deprecated
|
|
121
|
+
* Change directory without deleting directory content.
|
|
122
|
+
* @param dirPath - Directory path (relative to CWD). Prefer passing an absolute
|
|
123
|
+
* file path for predictable results
|
|
124
|
+
* @return run context instance
|
|
125
|
+
*/
|
|
126
|
+
cd(dirPath) {
|
|
127
|
+
dirPath = path.resolve(dirPath);
|
|
128
|
+
this.setDir(dirPath, false);
|
|
129
|
+
try {
|
|
130
|
+
process.chdir(dirPath);
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
this.completed = true;
|
|
134
|
+
throw new Error(`${error.message} ${dirPath}`);
|
|
135
|
+
}
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Cleanup a temporary directory and change the CWD into it
|
|
140
|
+
*
|
|
141
|
+
* This method is called automatically when creating a RunContext. Only use it if you need
|
|
142
|
+
* to use the callback.
|
|
143
|
+
*
|
|
144
|
+
* @param [cb] - callback who'll receive the folder path as argument
|
|
145
|
+
* @return this - run context instance
|
|
146
|
+
*/
|
|
147
|
+
inTmpDir(cb) {
|
|
148
|
+
return this.inDir(this.temporaryDir, cb);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Restore cwd to initial cwd.
|
|
152
|
+
* @return {this} run context instance
|
|
153
|
+
*/
|
|
154
|
+
restore() {
|
|
155
|
+
if (this.oldCwd) {
|
|
156
|
+
process.chdir(this.oldCwd);
|
|
157
|
+
}
|
|
158
|
+
return this;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Clean the directory used for tests inside inDir/inTmpDir
|
|
162
|
+
* @param {Boolean} force - force directory cleanup for not tmpdir
|
|
163
|
+
*/
|
|
164
|
+
cleanup() {
|
|
165
|
+
this.restore();
|
|
166
|
+
if (this.settings.tmpdir !== false) {
|
|
167
|
+
this.cleanTestDirectory();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Clean the directory used for tests inside inDir/inTmpDir
|
|
172
|
+
* @param {Boolean} force - force directory cleanup for not tmpdir
|
|
173
|
+
*/
|
|
174
|
+
cleanupTemporaryDir() {
|
|
175
|
+
this.restore();
|
|
176
|
+
if (this.temporaryDir && existsSync(this.temporaryDir)) {
|
|
177
|
+
rmSync(this.temporaryDir, { recursive: true });
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Clean the directory used for tests inside inDir/inTmpDir
|
|
182
|
+
* @param force - force directory cleanup for not tmpdir
|
|
183
|
+
*/
|
|
184
|
+
cleanTestDirectory(force = false) {
|
|
185
|
+
if (!force && this.settings.tmpdir === false) {
|
|
186
|
+
throw new Error('Cleanup test dir called with false tmpdir option.');
|
|
187
|
+
}
|
|
188
|
+
if (this.targetDirectory && existsSync(this.targetDirectory)) {
|
|
189
|
+
rmSync(this.targetDirectory, { recursive: true });
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Create an environment
|
|
194
|
+
*
|
|
195
|
+
* This method is called automatically when creating a RunContext. Only use it if you need
|
|
196
|
+
* to use the callback.
|
|
197
|
+
*
|
|
198
|
+
* @param {Function} [cb] - callback who'll receive the folder path as argument
|
|
199
|
+
* @return {this} run context instance
|
|
200
|
+
*/
|
|
201
|
+
withEnvironment(cb) {
|
|
202
|
+
this.envCB = cb;
|
|
203
|
+
return this;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Run lookup on the environment.
|
|
207
|
+
*
|
|
208
|
+
* @param lookups - lookup to run.
|
|
209
|
+
*/
|
|
210
|
+
withLookups(lookups) {
|
|
211
|
+
return this.onEnvironment(env => {
|
|
212
|
+
lookups = Array.isArray(lookups) ? lookups : [lookups];
|
|
213
|
+
for (const lookup of lookups) {
|
|
214
|
+
env.lookup(lookup);
|
|
215
|
+
}
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Provide arguments to the run context
|
|
220
|
+
* @param args - command line arguments as Array or space separated string
|
|
221
|
+
*/
|
|
222
|
+
withArguments(args) {
|
|
223
|
+
const argsArray = typeof args === 'string' ? args.split(' ') : args;
|
|
224
|
+
assert(Array.isArray(argsArray), 'args should be either a string separated by spaces or an array');
|
|
225
|
+
this.args = this.args.concat(argsArray);
|
|
226
|
+
return this;
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Provide options to the run context
|
|
230
|
+
* @param {Object} options - command line options (e.g. `--opt-one=foo`)
|
|
231
|
+
* @return {this}
|
|
232
|
+
*/
|
|
233
|
+
withOptions(options) {
|
|
234
|
+
if (!options) {
|
|
235
|
+
return this;
|
|
236
|
+
}
|
|
237
|
+
// Add options as both kebab and camel case. This is to stay backward compatibles with
|
|
238
|
+
// the switch we made to meow for options parsing.
|
|
239
|
+
for (const key of Object.keys(options)) {
|
|
240
|
+
options[_.camelCase(key)] = options[key];
|
|
241
|
+
options[_.kebabCase(key)] = options[key];
|
|
242
|
+
}
|
|
243
|
+
this.options = { ...this.options, ...options };
|
|
244
|
+
return this;
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* @deprecated
|
|
248
|
+
* Mock the prompt with dummy answers
|
|
249
|
+
* @param answers - Answers to the prompt questions
|
|
250
|
+
* @param options - Options or callback.
|
|
251
|
+
* @param {Function} [options.callback] - Callback.
|
|
252
|
+
* @param {Boolean} [options.throwOnMissingAnswer] - Throw if a answer is missing.
|
|
253
|
+
* @return {this}
|
|
254
|
+
*/
|
|
255
|
+
withPrompts(answers, options) {
|
|
256
|
+
return this.withAnswers(answers, options);
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Mock answers for prompts
|
|
260
|
+
* @param answers - Answers to the prompt questions
|
|
261
|
+
* @param options - Options or callback.
|
|
262
|
+
* @return {this}
|
|
263
|
+
*/
|
|
264
|
+
withAnswers(answers, options) {
|
|
265
|
+
const callbackSet = Boolean(this.answers);
|
|
266
|
+
this.answers = { ...this.answers, ...answers };
|
|
267
|
+
if (callbackSet)
|
|
268
|
+
return this;
|
|
269
|
+
return this.onEnvironment(env => {
|
|
270
|
+
this.helpers.mockPrompt(env, this.answers, options);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Provide dependent generators
|
|
275
|
+
* @param {Array} dependencies - paths to the generators dependencies
|
|
276
|
+
* @return {this}
|
|
277
|
+
* @example
|
|
278
|
+
* var angular = new RunContext('../../app');
|
|
279
|
+
* angular.withGenerators([
|
|
280
|
+
* '../../common',
|
|
281
|
+
* '../../controller',
|
|
282
|
+
* '../../main',
|
|
283
|
+
* [helpers.createDummyGenerator(), 'testacular:app']
|
|
284
|
+
* ]);
|
|
285
|
+
* angular.on('end', function () {
|
|
286
|
+
* // assert something
|
|
287
|
+
* });
|
|
288
|
+
*/
|
|
289
|
+
withGenerators(dependencies) {
|
|
290
|
+
assert(Array.isArray(dependencies), 'dependencies should be an array');
|
|
291
|
+
return this.onEnvironment(env => {
|
|
292
|
+
for (const dependency of dependencies) {
|
|
293
|
+
if (Array.isArray(dependency)) {
|
|
294
|
+
env.registerStub(...dependency);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
env.register(dependency);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Create mocked generators
|
|
304
|
+
* @param namespaces - namespaces of mocked generators
|
|
305
|
+
* @return this
|
|
306
|
+
* @example
|
|
307
|
+
* var angular = helpers
|
|
308
|
+
* .create('../../app')
|
|
309
|
+
* .withMockedGenerators([
|
|
310
|
+
* 'foo:app',
|
|
311
|
+
* 'foo:bar',
|
|
312
|
+
* ])
|
|
313
|
+
* .run()
|
|
314
|
+
* .then(runResult => assert(runResult
|
|
315
|
+
* .mockedGenerators['foo:app']
|
|
316
|
+
.calledOnce));
|
|
317
|
+
*/
|
|
318
|
+
withMockedGenerators(namespaces) {
|
|
319
|
+
assert(Array.isArray(namespaces), 'namespaces should be an array');
|
|
320
|
+
const dependencies = namespaces.map(namespace => [this.helpers.createMockedGenerator(), namespace]);
|
|
321
|
+
const entries = dependencies.map(([generator, namespace]) => [namespace, generator]);
|
|
322
|
+
Object.assign(this.mockedGenerators, Object.fromEntries(entries));
|
|
323
|
+
return this.withGenerators(dependencies);
|
|
324
|
+
}
|
|
325
|
+
/**
|
|
326
|
+
* Mock the local configuration with the provided config
|
|
327
|
+
* @param localConfig - should look just like if called config.getAll()
|
|
328
|
+
*/
|
|
329
|
+
withLocalConfig(localConfig) {
|
|
330
|
+
assert(typeof localConfig === 'object', 'config should be an object');
|
|
331
|
+
return this.onGenerator(generator => generator.config.defaults(localConfig));
|
|
332
|
+
}
|
|
333
|
+
withFiles(relativePath, files) {
|
|
334
|
+
return this.onTargetDirectory(function () {
|
|
335
|
+
const targetDirectory = typeof relativePath === 'string' ? pathJoin(this.targetDirectory, relativePath) : this.targetDirectory;
|
|
336
|
+
if (typeof relativePath !== 'string') {
|
|
337
|
+
files = relativePath;
|
|
338
|
+
}
|
|
339
|
+
for (const [file, content] of Object.entries(files)) {
|
|
340
|
+
const resolvedFile = isAbsolute(file) ? file : resolve(targetDirectory, file);
|
|
341
|
+
if (typeof content === 'string') {
|
|
342
|
+
this.editor.write(resolvedFile, content);
|
|
343
|
+
}
|
|
344
|
+
else {
|
|
345
|
+
this.editor.writeJSON(resolvedFile, content);
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Add .yo-rc.json to mem-fs.
|
|
352
|
+
*
|
|
353
|
+
* @param content
|
|
354
|
+
* @returns
|
|
355
|
+
*/
|
|
356
|
+
withYoRc(content) {
|
|
357
|
+
return this.withFiles({
|
|
358
|
+
'.yo-rc.json': content,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Commit mem-fs files.
|
|
363
|
+
*/
|
|
364
|
+
commitFiles() {
|
|
365
|
+
return this.onTargetDirectory(async function () {
|
|
366
|
+
await this.editor.commit();
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Execute callback after targetDirectory is set
|
|
371
|
+
* @param callback
|
|
372
|
+
* @returns
|
|
373
|
+
*/
|
|
374
|
+
onTargetDirectory(callback) {
|
|
375
|
+
this.assertNotBuild();
|
|
376
|
+
this.onTargetDirectoryCallbacks.push(callback);
|
|
377
|
+
return this;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Execute callback after generator is ready
|
|
381
|
+
* @param callback
|
|
382
|
+
* @returns
|
|
383
|
+
*/
|
|
384
|
+
onGenerator(callback) {
|
|
385
|
+
this.assertNotBuild();
|
|
386
|
+
this.onGeneratorCallbacks.push(callback);
|
|
387
|
+
return this;
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Execute callback after environment is ready
|
|
391
|
+
* @param callback
|
|
392
|
+
* @returns
|
|
393
|
+
*/
|
|
394
|
+
onEnvironment(callback) {
|
|
395
|
+
this.assertNotBuild();
|
|
396
|
+
this.onEnvironmentCallbacks.push(callback);
|
|
397
|
+
return this;
|
|
398
|
+
}
|
|
399
|
+
assertNotBuild() {
|
|
400
|
+
if (this.ran || this.completed) {
|
|
401
|
+
throw new Error('The context is already built');
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* Build the generator and the environment.
|
|
406
|
+
* @return {RunContext|false} this
|
|
407
|
+
*/
|
|
408
|
+
async build() {
|
|
409
|
+
this.assertNotBuild();
|
|
410
|
+
this.ran = true;
|
|
411
|
+
if (!this.targetDirectory && this.settings.tmpdir !== false) {
|
|
412
|
+
this.inTmpDir();
|
|
413
|
+
}
|
|
414
|
+
else if (!this.targetDirectory) {
|
|
415
|
+
throw new Error('If not a temporary dir, pass the test cwd');
|
|
416
|
+
}
|
|
417
|
+
if (this.inDirCallbacks.length > 0) {
|
|
418
|
+
const targetDirectory = path.resolve(this.targetDirectory);
|
|
419
|
+
for (const cb of this.inDirCallbacks) {
|
|
420
|
+
// eslint-disable-next-line no-await-in-loop
|
|
421
|
+
await cb(targetDirectory);
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
if (!this.targetDirectory) {
|
|
425
|
+
throw new Error('targetDirectory is required');
|
|
426
|
+
}
|
|
427
|
+
const testEnv = this.helpers.createTestEnv(this.envOptions.createEnv, {
|
|
428
|
+
cwd: this.settings.forwardCwd ? this.targetDirectory : undefined,
|
|
429
|
+
...this.options,
|
|
430
|
+
...this.envOptions,
|
|
431
|
+
});
|
|
432
|
+
this.env = this.envCB ? (await this.envCB(testEnv)) ?? testEnv : testEnv;
|
|
433
|
+
this.editor = MemFsEditor.create(this.env.sharedFs);
|
|
434
|
+
for (const onTargetDirectory of this.onTargetDirectoryCallbacks) {
|
|
435
|
+
// eslint-disable-next-line no-await-in-loop
|
|
436
|
+
await onTargetDirectory.call(this, this.targetDirectory);
|
|
437
|
+
}
|
|
438
|
+
for (const onEnvironmentCallback of this.onEnvironmentCallbacks) {
|
|
439
|
+
// eslint-disable-next-line no-await-in-loop
|
|
440
|
+
await onEnvironmentCallback.call(this, this.env);
|
|
441
|
+
}
|
|
442
|
+
let { namespace } = this.settings;
|
|
443
|
+
if (typeof this.Generator === 'string') {
|
|
444
|
+
namespace = this.env.namespace(this.Generator);
|
|
445
|
+
if (namespace !== this.Generator) {
|
|
446
|
+
// Generator is a file path, it should be registered.
|
|
447
|
+
this.env.register(this.Generator);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
// eslint-disable-next-line @typescript-eslint/await-thenable
|
|
451
|
+
this.generator = (await this.env.create(namespace, this.args, this.options));
|
|
452
|
+
for (const onGeneratorCallback of this.onGeneratorCallbacks) {
|
|
453
|
+
// eslint-disable-next-line no-await-in-loop
|
|
454
|
+
await onGeneratorCallback.call(this, this.generator);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Return a promise representing the generator run process
|
|
459
|
+
* @return Promise resolved on end or rejected on error
|
|
460
|
+
*/
|
|
461
|
+
async toPromise() {
|
|
462
|
+
return this.environmentPromise ?? this.run();
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Keeps compatibility with events
|
|
466
|
+
*/
|
|
467
|
+
setupEventListeners() {
|
|
468
|
+
if (this.eventListenersSet) {
|
|
469
|
+
return undefined;
|
|
470
|
+
}
|
|
471
|
+
this.eventListenersSet = true;
|
|
472
|
+
this.onGenerator(generator => this.emit('ready', generator));
|
|
473
|
+
this.onGenerator(generator => this.emit('generator', generator));
|
|
474
|
+
return this.build().then(async () => this.run()
|
|
475
|
+
.catch(error => {
|
|
476
|
+
if (this.listenerCount('end') === 0 && this.listenerCount('error') === 0) {
|
|
477
|
+
// When there is no listeners throw a unhandled rejection.
|
|
478
|
+
setImmediate(async function () {
|
|
479
|
+
// eslint-disable-next-line @typescript-eslint/no-throw-literal
|
|
480
|
+
throw error;
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
this.errored = true;
|
|
485
|
+
this.emit('error', error);
|
|
486
|
+
}
|
|
487
|
+
})
|
|
488
|
+
.finally(() => {
|
|
489
|
+
this.emit('end');
|
|
490
|
+
}));
|
|
491
|
+
}
|
|
492
|
+
/**
|
|
493
|
+
* Set the target directory.
|
|
494
|
+
* @private
|
|
495
|
+
* @param {String} dirPath - Directory path (relative to CWD). Prefer passing an absolute
|
|
496
|
+
* file path for predictable results
|
|
497
|
+
* @return {this} run context instance
|
|
498
|
+
*/
|
|
499
|
+
setDir(dirPath, tmpdir) {
|
|
500
|
+
if (this.targetDirectory) {
|
|
501
|
+
this.completed = true;
|
|
502
|
+
throw new Error('Test directory has already been set.');
|
|
503
|
+
}
|
|
504
|
+
if (tmpdir !== undefined) {
|
|
505
|
+
this.settings.tmpdir = tmpdir;
|
|
506
|
+
}
|
|
507
|
+
this.oldCwd = this.oldCwd ?? process.cwd();
|
|
508
|
+
this.targetDirectory = dirPath;
|
|
509
|
+
return this;
|
|
510
|
+
}
|
|
511
|
+
_createRunResultOptions() {
|
|
512
|
+
return {
|
|
513
|
+
env: this.env,
|
|
514
|
+
generator: this.generator,
|
|
515
|
+
memFs: this.env.sharedFs,
|
|
516
|
+
settings: {
|
|
517
|
+
...this.settings,
|
|
518
|
+
},
|
|
519
|
+
oldCwd: this.oldCwd,
|
|
520
|
+
cwd: this.targetDirectory,
|
|
521
|
+
envOptions: this.envOptions,
|
|
522
|
+
mockedGenerators: this.mockedGenerators,
|
|
523
|
+
helpers: this.helpers,
|
|
524
|
+
};
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
export default class RunContext extends RunContextBase {
|
|
528
|
+
// eslint-disable-next-line unicorn/no-thenable
|
|
529
|
+
async then(onfulfilled, onrejected) {
|
|
530
|
+
return this.toPromise().then(onfulfilled, onrejected);
|
|
531
|
+
}
|
|
532
|
+
async catch(onrejected) {
|
|
533
|
+
return this.toPromise().catch(onrejected);
|
|
534
|
+
}
|
|
535
|
+
async finally(onfinally) {
|
|
536
|
+
return this.toPromise().finally(onfinally);
|
|
537
|
+
}
|
|
538
|
+
get [Symbol.toStringTag]() {
|
|
539
|
+
return `RunContext`;
|
|
540
|
+
}
|
|
541
|
+
}
|