0
# Test Execution Context
1
2
The RunContext provides a fluent, chainable API for configuring and executing generator tests. It manages the complete test lifecycle from setup through execution to cleanup, with support for temporary directories, file system mocking, prompt simulation, and generator composition.
3
4
## Context Properties
5
6
The RunContext exposes several properties for advanced test scenarios:
7
8
```typescript { .api }
9
interface RunContext<GeneratorType> {
10
/** The generator instance being tested */
11
generator: GeneratorType;
12
13
/** The Yeoman environment instance */
14
env: DefaultEnvironmentApi;
15
16
/** Memory file system store */
17
memFs: Store<MemFsEditorFile>;
18
19
/** Memory file system editor */
20
editor: MemFsEditor;
21
22
/** Context configuration settings */
23
settings: RunContextSettings;
24
25
/** Environment options */
26
envOptions: BaseEnvironmentOptions;
27
28
/** Whether the test has completed */
29
completed: boolean;
30
31
/** Target directory for the test */
32
targetDirectory?: string;
33
34
/** Mocked generators for composition testing */
35
mockedGenerators: Record<string, BaseGenerator>;
36
37
/** Mock spawn stub when using spawn mocking */
38
spawnStub?: any;
39
40
/** Questions asked during prompt simulation */
41
askedQuestions: AskedQuestions;
42
}
43
```
44
45
## Capabilities
46
47
### Creating Run Contexts
48
49
Create and configure test execution contexts for generator testing.
50
51
```typescript { .api }
52
/**
53
* Run the provided Generator in a test context
54
* @param GeneratorOrNamespace - Generator constructor or namespace
55
* @param settings - Configuration options for the run context
56
* @param environmentOptions - Options for the Yeoman environment
57
* @returns RunContext instance for chaining configuration
58
*/
59
function run<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(
60
GeneratorOrNamespace: string | GetGeneratorConstructor<GeneratorType>,
61
settings?: RunContextSettings,
62
environmentOptions?: BaseEnvironmentOptions
63
): RunContext<GeneratorType>;
64
65
/**
66
* Alias for run() - prepare a run context
67
* @param GeneratorOrNamespace - Generator constructor or namespace
68
* @param settings - Configuration options
69
* @param environmentOptions - Environment options
70
* @returns RunContext instance
71
*/
72
function create<GeneratorType extends BaseGenerator = DefaultGeneratorApi>(
73
GeneratorOrNamespace: string | GetGeneratorConstructor<GeneratorType>,
74
settings?: RunContextSettings,
75
environmentOptions?: BaseEnvironmentOptions
76
): RunContext<GeneratorType>;
77
78
/**
79
* Prepare temporary dir without generator support
80
* @param settings - Configuration options
81
* @returns BasicRunContext for directory-only testing
82
*/
83
function prepareTemporaryDir(settings?: RunContextSettings): BasicRunContext;
84
```
85
86
**Usage Examples:**
87
88
```typescript
89
import helpers from "yeoman-test";
90
91
// Basic generator test
92
const context = helpers.run("my-generator");
93
94
// Generator from file path
95
const context = helpers.run("./path/to/generator");
96
97
// With custom settings
98
const context = helpers.run("my-generator", {
99
tmpdir: false,
100
cwd: "/custom/path"
101
});
102
103
// Directory-only testing
104
const context = helpers.prepareTemporaryDir();
105
```
106
107
### Configuration Methods
108
109
Configure generator arguments, options, and behavior before execution.
110
111
```typescript { .api }
112
/**
113
* Provide arguments to the run context
114
* @param args - Command line arguments as Array or space separated string
115
* @returns this for method chaining
116
*/
117
withArguments(args: string | string[]): this;
118
119
/**
120
* Provide options to the run context
121
* @param options - Command line options (e.g. --opt-one=foo)
122
* @returns this for method chaining
123
*/
124
withOptions(options: Partial<Omit<GetGeneratorOptions<GeneratorType>, 'env' | 'namespace' | 'resolved'>>): this;
125
126
/**
127
* Mock answers for prompts
128
* @param answers - Answers to the prompt questions
129
* @param options - Additional prompt options
130
* @returns this for method chaining
131
*/
132
withAnswers(answers: PromptAnswers, options?: Omit<DummyPromptOptions, 'mockedAnswers'>): this;
133
134
/**
135
* @deprecated Use withAnswers instead
136
* Mock the prompt with dummy answers
137
*/
138
withPrompts(answers: PromptAnswers, options?: Omit<DummyPromptOptions, 'mockedAnswers'>): this;
139
```
140
141
**Usage Examples:**
142
143
```typescript
144
await helpers.run("my-generator")
145
.withArguments(["arg1", "arg2"])
146
.withOptions({
147
skipInstall: true,
148
force: true
149
})
150
.withAnswers({
151
projectName: "test-app",
152
features: ["typescript", "testing"]
153
});
154
155
// Arguments as string
156
await helpers.run("my-generator")
157
.withArguments("init --force");
158
```
159
160
### File System Setup
161
162
Configure files, directories, and configuration before generator execution.
163
164
```typescript { .api }
165
/**
166
* Add files to mem-fs. Files will be resolved relative to targetDir.
167
* Files with Object content will be merged to existing content.
168
* @param files - Object mapping file paths to content
169
* @returns this for method chaining
170
*/
171
withFiles(files: Record<string, string | Record<string, unknown>>): this;
172
173
/**
174
* Add files to specific relative path
175
* @param relativePath - Path relative to target directory
176
* @param files - Object mapping file paths to content
177
* @returns this for method chaining
178
*/
179
withFiles(relativePath: string, files: Record<string, string | Record<string, unknown>>): this;
180
181
/**
182
* Add .yo-rc.json to mem-fs
183
* @param content - Content for .yo-rc.json as string or object
184
* @returns this for method chaining
185
*/
186
withYoRc(content: string | Record<string, unknown>): this;
187
188
/**
189
* Add a generator config to .yo-rc.json
190
* @param key - Generator key for the configuration
191
* @param content - Configuration object
192
* @returns this for method chaining
193
*/
194
withYoRcConfig(key: string, content: Record<string, unknown>): this;
195
196
/**
197
* Mock the local configuration with the provided config
198
* @param localConfig - Should look like config.getAll() output
199
* @returns this for method chaining
200
*/
201
withLocalConfig(localConfig: any): this;
202
203
/**
204
* Commit mem-fs files to disk
205
* @returns this for method chaining
206
*/
207
commitFiles(): this;
208
```
209
210
**Usage Examples:**
211
212
```typescript
213
await helpers.run("my-generator")
214
.withFiles({
215
"package.json": '{"name": "existing-project"}',
216
"config.json": { theme: "dark", debug: true },
217
"templates/base.hbs": "Hello {{name}}"
218
})
219
.withYoRcConfig("my-generator", {
220
previousRun: true,
221
settings: { typescript: true }
222
})
223
.commitFiles();
224
225
// Files in subdirectory
226
await helpers.run("my-generator")
227
.withFiles("src", {
228
"index.ts": "export * from './lib';",
229
"lib.ts": "export const VERSION = '1.0.0';"
230
});
231
```
232
233
### Generator Dependencies
234
235
Configure dependent generators and mocked generators for composition testing.
236
237
```typescript { .api }
238
/**
239
* Provide dependent generators
240
* @param dependencies - Paths to generators or [generator, options] pairs
241
* @returns this for method chaining
242
*/
243
withGenerators(dependencies: Dependency[]): this;
244
245
/**
246
* Create mocked generators for testing composition
247
* @param namespaces - Namespaces of mocked generators
248
* @returns this for method chaining
249
*/
250
withMockedGenerators(namespaces: string[]): this;
251
252
/**
253
* Provide custom mocked generator factory
254
* @param mockedGeneratorFactory - Factory function for creating mocked generators
255
* @returns this for method chaining
256
*/
257
withMockedGeneratorFactory(mockedGeneratorFactory: MockedGeneratorFactory): this;
258
259
type Dependency = string | Parameters<DefaultEnvironmentApi['register']>;
260
type MockedGeneratorFactory<GenParameter extends BaseGenerator = DefaultGeneratorApi> = (
261
GeneratorClass?: GetGeneratorConstructor<GenParameter>
262
) => GetGeneratorConstructor<GenParameter>;
263
```
264
265
**Usage Examples:**
266
267
```typescript
268
await helpers.run("my-generator")
269
.withGenerators([
270
"./path/to/dep-generator",
271
["./other-generator", { namespace: "custom:name" }]
272
])
273
.withMockedGenerators([
274
"sub-generator:app",
275
"common:util"
276
]);
277
278
// Access mocked generators in tests
279
it("composes with sub-generator", () => {
280
result.assertGeneratorComposed("sub-generator:app");
281
const mock = result.getGeneratorMock("sub-generator:app");
282
expect(mock.callCount()).toBe(1);
283
});
284
```
285
286
### Environment Configuration
287
288
Configure the Yeoman environment and lookup behavior.
289
290
```typescript { .api }
291
/**
292
* Run lookup on the environment
293
* @param lookups - Lookup options to run
294
* @returns this for method chaining
295
*/
296
withLookups(lookups: LookupOptions | LookupOptions[]): this;
297
298
/**
299
* Customize environment creation
300
* @param callback - Callback to modify environment
301
* @returns this for method chaining
302
*/
303
withEnvironment(callback: (env: DefaultEnvironmentApi) => any): this;
304
305
/**
306
* Customize environment run method
307
* @param callback - Custom run implementation
308
* @returns this for method chaining
309
*/
310
withEnvironmentRun(callback: (this: this, env: DefaultEnvironmentApi, gen: GeneratorType) => void): this;
311
312
/**
313
* TestAdapter options for prompt mocking
314
* @param options - Adapter configuration options
315
* @returns this for method chaining
316
*/
317
withAdapterOptions(options: Omit<TestAdapterOptions, 'mockedAnswers'>): this;
318
```
319
320
### Spawn Command Mocking
321
322
Mock spawn calls for testing generators that execute external commands.
323
324
```typescript { .api }
325
/**
326
* Mock spawn calls with custom stub or default implementation
327
* @param options - Stub function or configuration object
328
* @returns this for method chaining
329
*/
330
withSpawnMock<StubType = ReturnType<typeof mock.fn>>(
331
options?: ((...args) => any) | {
332
stub?: (...args) => any;
333
registerNodeMockDefaults?: boolean;
334
callback?: ({ stub, implementation }: { stub: StubType; implementation: any }) => void | Promise<void>;
335
}
336
): this;
337
```
338
339
**Usage Examples:**
340
341
```typescript
342
// Default spawn mocking
343
await helpers.run("my-generator")
344
.withSpawnMock();
345
346
// Custom spawn behavior
347
await helpers.run("my-generator")
348
.withSpawnMock({
349
stub: mock.fn(() => ({ exitCode: 0, stdout: "success" })),
350
callback: ({ stub }) => {
351
// Configure stub behavior
352
stub.mockReturnValueOnce({ exitCode: 1, stderr: "error" });
353
}
354
});
355
356
// Access spawn calls in tests
357
it("runs expected commands", () => {
358
const spawnArgs = result.getSpawnArgsUsingDefaultImplementation();
359
expect(spawnArgs[0]).toEqual(["spawnCommand", "npm", ["install"]]);
360
});
361
```
362
363
### Directory Management
364
365
Configure working directories and temporary directory behavior.
366
367
```typescript { .api }
368
/**
369
* @deprecated Clean the provided directory, then change directory into it
370
* @param dirPath - Directory path (relative to CWD)
371
* @param callback - Callback receiving the folder path
372
* @returns this for method chaining
373
*/
374
inDir(dirPath: string, callback?: (folderPath: string) => void): this;
375
376
/**
377
* @deprecated Change directory without deleting directory content
378
* @param dirPath - Directory path (relative to CWD)
379
* @returns this for method chaining
380
*/
381
cd(dirPath: string): this;
382
383
/**
384
* Cleanup a temporary directory and change the CWD into it
385
* @param callback - Callback receiving the folder path
386
* @returns this for method chaining
387
*/
388
inTmpDir(callback?: (folderPath: string) => void): this;
389
390
/**
391
* Register a callback to prepare the destination folder
392
* @param callback - Callback receiving the folder path
393
* @returns this for method chaining
394
*/
395
doInDir(callback: (folderPath: string) => void): this;
396
```
397
398
### Lifecycle Callbacks
399
400
Configure callbacks for different stages of the test execution.
401
402
```typescript { .api }
403
/**
404
* Execute callback when generator is ready
405
* @param callback - Callback receiving the generator instance
406
* @returns this for method chaining
407
*/
408
onGenerator(callback: (this: this, generator: GeneratorType) => any): this;
409
410
/**
411
* Execute callback when environment is ready
412
* @param callback - Callback receiving the environment instance
413
* @returns this for method chaining
414
*/
415
onEnvironment(callback: (this: this, environment: DefaultEnvironmentApi) => any): this;
416
417
/**
418
* Execute callback when target directory is set
419
* @param callback - Callback receiving the target directory path
420
* @returns this for method chaining
421
*/
422
onTargetDirectory(callback: (this: this, targetDirectory: string) => any): this;
423
424
/**
425
* Execute callback before preparation phase
426
* @param callback - Callback executed before test preparation
427
* @returns this for method chaining
428
*/
429
onBeforePrepare(callback: (this: this) => void | Promise<void>): this;
430
```
431
432
**Usage Examples:**
433
434
```typescript
435
await helpers.run("my-generator")
436
.onGenerator(generator => {
437
// Customize generator before execution
438
generator.options.customFlag = true;
439
})
440
.onEnvironment(env => {
441
// Configure environment
442
env.register("./custom-generator", { namespace: "custom:gen" });
443
})
444
.onTargetDirectory(targetDir => {
445
// Setup target directory
446
fs.writeFileSync(path.join(targetDir, "preset.json"), "{}");
447
});
448
```
449
450
### Execution and State Management
451
452
Execute the generator and manage test state.
453
454
```typescript { .api }
455
/**
456
* Run the generator and return results
457
* @returns Promise resolving to RunResult instance
458
*/
459
run(): Promise<RunResult<GeneratorType>>;
460
461
/**
462
* Build/prepare the context without running the generator
463
* @returns Promise resolving when context is ready
464
*/
465
build(): Promise<void>;
466
467
/**
468
* Prepare the test context without running
469
* @returns Promise resolving when preparation is complete
470
*/
471
prepare(): Promise<void>;
472
473
/**
474
* Don't reset mem-fs state to aggregate snapshots from multiple runs
475
* @returns this for method chaining
476
*/
477
withKeepFsState(): this;
478
479
/**
480
* Clean the directory used for tests and restore original working directory
481
*/
482
cleanup(): void;
483
484
/**
485
* Clean the temporary directory
486
*/
487
cleanupTemporaryDir(): void;
488
489
/**
490
* Clean the test directory with optional force flag
491
* @param force - Force cleanup even if directory is not temporary
492
* @returns this for method chaining
493
*/
494
cleanTestDirectory(force?: boolean): this;
495
496
/**
497
* Restore original working directory
498
* @returns this for method chaining
499
*/
500
restore(): this;
501
```
502
503
## RunContext Promise Interface
504
505
RunContext implements the Promise interface for convenient async/await usage:
506
507
```typescript { .api }
508
/**
509
* RunContext implements Promise<RunResult<GeneratorType>>
510
*/
511
interface RunContext<GeneratorType> extends Promise<RunResult<GeneratorType>> {
512
then<TResult1, TResult2>(
513
onfulfilled?: (value: RunResult<GeneratorType>) => TResult1 | PromiseLike<TResult1>,
514
onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>
515
): Promise<TResult1 | TResult2>;
516
517
catch<TResult>(
518
onrejected?: (reason: any) => TResult | PromiseLike<TResult>
519
): Promise<RunResult<GeneratorType> | TResult>;
520
521
finally(onfinally?: () => void): Promise<RunResult<GeneratorType>>;
522
}
523
```
524
525
**Usage Examples:**
526
527
```typescript
528
// Promise-style usage
529
const result = await helpers.run("my-generator")
530
.withOptions({ skipInstall: true });
531
532
// Traditional promise chains
533
helpers.run("my-generator")
534
.then(result => {
535
result.assertFile("package.json");
536
})
537
.catch(error => {
538
console.error("Test failed:", error);
539
});
540
```