Test utilities for Yeoman generators providing a fluent API for setting up test environments and asserting generated content
The RunResult class provides comprehensive assertion utilities for verifying file creation, content validation, JSON structure testing, and generator behavior. It supports both individual and batch operations with detailed error reporting and file system snapshot capabilities.
The RunResult exposes several properties for advanced test inspection:
interface RunResult<GeneratorType extends BaseGenerator> {
/** The generator instance that was tested */
generator: GeneratorType;
/** The Yeoman environment instance */
env: DefaultEnvironmentApi;
/** Current working directory after test */
cwd: string;
/** Original working directory before test */
oldCwd: string;
/** Memory file system store */
memFs: Store<MemFsEditorFile>;
/** Memory file system editor */
fs: MemFsEditor;
/** Mocked generators from the test context */
mockedGenerators: Record<string, BaseGenerator>;
/** Mock spawn stub if spawn mocking was used */
spawnStub?: any;
/** Questions asked during prompt simulation */
readonly askedQuestions: AskedQuestions;
/** Complete run options used for this result */
options: RunResultOptions<GeneratorType>;
}Verify that files and directories exist or don't exist after generator execution.
/**
* Assert that a file exists
* @param path - Path to a file or array of paths
* @throws AssertionError if file doesn't exist
*/
assertFile(path: string | string[]): void;
/**
* Assert that a file doesn't exist
* @param files - Path to a file or array of paths
* @throws AssertionError if file exists
*/
assertNoFile(files: string | string[]): void;Usage Examples:
import { result } from "yeoman-test";
// Single file
result.assertFile("package.json");
result.assertNoFile("temp-file.txt");
// Multiple files
result.assertFile([
"src/index.ts",
"test/index.test.ts",
"README.md"
]);
result.assertNoFile([
"temp.log",
"debug.tmp"
]);Verify file contents match expected patterns, strings, or exact content.
/**
* Assert that a file's content matches a regex or string
* @param file - Path to a file
* @param reg - Regex or string to search for
* @throws AssertionError if content doesn't match
*/
assertFileContent(file: string, reg: string | RegExp): void;
/**
* Assert multiple file-regex pairs
* @param pairs - Array of [file, regex] pairs
*/
assertFileContent(pairs: Array<[string, string | RegExp]>): void;
/**
* Assert that a file's content is exactly equal to the given string
* @param file - Path to a file
* @param expectedContent - Expected exact content
*/
assertEqualsFileContent(file: string, expectedContent: string): void;
/**
* Assert multiple file-content pairs for exact equality
* @param pairs - Array of [file, expectedContent] pairs
*/
assertEqualsFileContent(pairs: Array<[string, string]>): void;
/**
* Assert that a file's content does not match a regex or string
* @param file - Path to a file
* @param reg - Regex or string that should not be found
*/
assertNoFileContent(file: string, reg: RegExp | string): void;
/**
* Assert multiple file-regex pairs do not match
* @param pairs - Array of [file, regex] pairs
*/
assertNoFileContent(pairs: Array<[string, string | RegExp]>): void;Usage Examples:
// Pattern matching
result.assertFileContent("package.json", /"name": "my-app"/);
result.assertFileContent("src/index.ts", /export class MyApp/);
// String matching
result.assertFileContent("README.md", "# My Application");
// Multiple files
result.assertFileContent([
["package.json", /"version": "1\.0\.0"/],
["src/index.ts", /import.*express/]
]);
// Exact content matching
result.assertEqualsFileContent(
"config.json",
'{\n "env": "development"\n}'
);
// Negative assertions
result.assertNoFileContent("package.json", /debug/);
result.assertNoFileContent("src/index.ts", "console.log");Specialized assertions for JSON file structure and content validation.
/**
* Assert a JSON file contains the provided keys and values
* @param filename - Path to JSON file
* @param content - Object with expected key/value pairs
* @throws AssertionError if JSON doesn't contain expected content
*/
assertJsonFileContent(filename: string, content: Record<string, any>): void;
/**
* Assert a JSON file does not contain the provided keys and values
* @param filename - Path to JSON file
* @param content - Object with key/value pairs that should not exist
*/
assertNoJsonFileContent(filename: string, content: Record<string, any>): void;Usage Examples:
// Assert JSON structure
result.assertJsonFileContent("package.json", {
name: "my-app",
version: "1.0.0",
dependencies: {
express: "^4.18.0"
}
});
// Nested object assertions
result.assertJsonFileContent("config.json", {
database: {
host: "localhost",
port: 5432
}
});
// Negative JSON assertions
result.assertNoJsonFileContent("package.json", {
devDependencies: {
nodemon: "^2.0.0"
}
});General object structure validation utilities used internally by JSON assertions.
/**
* Assert an object contains the provided keys and values
* @param object - Object to check
* @param content - Expected key/value pairs
* @throws AssertionError if object doesn't contain expected content
*/
assertObjectContent(object: Record<string, unknown>, content: Record<string, any>): void;
/**
* Assert an object does not contain the provided keys and values
* @param object - Object to check
* @param content - Key/value pairs that should not exist
*/
assertNoObjectContent(object: Record<string, unknown>, content: Record<string, any>): void;Utility for comparing text with normalized newlines.
/**
* Assert that two strings are equal after standardization of newlines
* @param value - Actual string value
* @param expected - Expected string value
* @throws AssertionError if strings don't match after normalization
*/
assertTextEqual(value: string, expected: string): void;Usage Examples:
const actualContent = "Line 1\r\nLine 2\nLine 3";
const expectedContent = "Line 1\nLine 2\nLine 3";
result.assertTextEqual(actualContent, expectedContent); // PassesVerify generator composition behavior when using mocked generators.
/**
* Assert that a generator was composed (called) during execution
* @param generator - Namespace of the mocked generator
* @throws AssertionError if generator was not composed
*/
assertGeneratorComposed(generator: string): void;
/**
* Assert that a generator was not composed during execution
* @param generator - Namespace of the mocked generator
* @throws AssertionError if generator was composed
*/
assertGeneratorNotComposed(generator: string): void;
/**
* Assert that a generator was composed exactly once
* @param generator - Namespace of the mocked generator
* @throws AssertionError if generator was not composed exactly once
*/
assertGeneratorComposedOnce(generator: string): void;
/**
* Get the number of times a mocked generator was composed
* @param generator - Namespace of the mocked generator
* @returns Number of times the generator was composed
* @throws Error if generator is not mocked
*/
getGeneratorComposeCount(generator: string): number;
/**
* Get the generator mock object for detailed inspection
* @param generator - Namespace of the mocked generator
* @returns Mock object with call information
* @throws Error if generator is not mocked
*/
getGeneratorMock(generator: string): ReturnType<typeof mock.fn>['mock'];
/**
* Get array of all composed generator names
* @returns Array of generator namespaces that were composed
*/
getComposedGenerators(): string[];Usage Examples:
// Setup mocked generators in test
await helpers.run("my-generator")
.withMockedGenerators([
"sub:app",
"common:util",
"optional:feature"
]);
// Test composition behavior
result.assertGeneratorComposed("sub:app");
result.assertGeneratorComposedOnce("common:util");
result.assertGeneratorNotComposed("optional:feature");
// Detailed mock inspection
const composeCount = result.getGeneratorComposeCount("sub:app");
expect(composeCount).toBe(2);
const mock = result.getGeneratorMock("sub:app");
expect(mock.callCount()).toBe(2);
expect(mock.calls[0].arguments).toEqual([/* expected args */]);
// Get all composed generators
const composed = result.getComposedGenerators();
expect(composed).toEqual(["sub:app", "common:util"]);Capture and inspect the complete file system state after generator execution.
/**
* Return an object with filesystem changes
* @param filter - Optional filter function passed to mem-fs-editor dump
* @returns Object mapping file paths to content and state information
*/
getSnapshot(filter?: Function): Record<string, { contents: string; stateCleared: string }>;
/**
* Return an object with filenames and their state information
* @param filter - Optional filter function
* @returns Object mapping file paths to state information only
*/
getStateSnapshot(filter?: Function): Record<string, { stateCleared?: string; state?: string }>;Usage Examples:
// Get complete file system snapshot
const snapshot = result.getSnapshot();
// Inspect specific files
expect(snapshot["package.json"].contents).toContain('"name": "my-app"');
expect(snapshot["src/index.ts"].stateCleared).toBe("modified");
// Filtered snapshot
const jsFiles = result.getSnapshot(file => file.path.endsWith('.js'));
// State-only snapshot
const stateSnapshot = result.getStateSnapshot();
expect(stateSnapshot["package.json"].state).toBe("modified");Utilities for debugging and inspecting file system state during test development.
/**
* Dump file contents to console for debugging
* @param files - Optional file paths to dump (dumps all files if none specified)
* @returns this for method chaining
*/
dumpFiles(...files: string[]): this;
/**
* Dump all filenames to console
* @returns this for method chaining
*/
dumpFilenames(): this;Usage Examples:
// Dump specific files
result.dumpFiles("package.json", "src/index.ts");
// Dump all files
result.dumpFiles();
// Dump only filenames
result.dumpFilenames();Create new test contexts and manage working directory state.
/**
* Create another RunContext reusing the current settings
* @param GeneratorOrNamespace - Generator constructor or namespace
* @param settings - Optional settings override
* @param environmentOptions - Optional environment options override
* @returns New RunContext instance
*/
create<G extends BaseGenerator = GeneratorType>(
GeneratorOrNamespace: string | GetGeneratorConstructor<G>,
settings?: RunContextSettings,
environmentOptions?: BaseEnvironmentOptions
): RunContext<G>;
/**
* Deletes the test directory recursively
* @returns this for method chaining
*/
cleanup(): this;
/**
* Restore original working directory
* @returns this for method chaining
*/
restore(): this;Usage Examples:
// Create follow-up test in same directory
const secondResult = await result
.create("another-generator")
.withOptions({ extend: true })
.run();
// Cleanup after manual testing
result.cleanup();
// Restore working directory
result.restore();Access spawn call information when using spawn mocking.
/**
* Get spawn call arguments when using default spawn implementation
* @returns Array of call arguments for each spawn invocation
* @throws Error if spawn stub was not configured
*/
getSpawnArgsUsingDefaultImplementation(): any[][];Usage Examples:
// Setup spawn mocking in test context
await helpers.run("my-generator")
.withSpawnMock();
// Access spawn calls
const spawnCalls = result.getSpawnArgsUsingDefaultImplementation();
// Verify expected commands were run
expect(spawnCalls).toHaveLength(2);
expect(spawnCalls[0]).toEqual(["spawnCommand", "npm", ["install"]]);
expect(spawnCalls[1]).toEqual(["spawnCommand", "git", ["init"]]);The result export provides convenient access to the last executed test result:
/**
* Proxy object providing access to the last RunResult instance
* All RunResult methods are available directly on this object
*/
export const result: RunResult;Usage Examples:
import helpers, { result } from "yeoman-test";
describe("generator test", () => {
beforeEach(async () => {
await helpers.run("my-generator")
.withOptions({ skipInstall: true });
});
it("creates expected files", () => {
// result automatically refers to the last execution
result.assertFile("package.json");
result.assertFileContent("package.json", /"name"/);
});
});Install with Tessl CLI
npx tessl i tessl/npm-yeoman-test