Legacy wrapper providing comprehensive Nx devkit utilities for creating plugins, generators, and executors
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Testing helpers for creating virtual workspaces and file systems for generator and executor testing. These utilities enable comprehensive testing of Nx generators, executors, and other workspace operations in isolated environments.
Functions for creating virtual file systems in test environments.
/**
* Create an empty virtual file system tree
* @returns Empty Tree instance for testing
*/
function createTree(): Tree;
/**
* Create a virtual tree with a basic empty workspace structure
* @returns Tree with minimal workspace files (nx.json, package.json, etc.)
*/
function createTreeWithEmptyWorkspace(): Tree;
/**
* @deprecated Use createTreeWithEmptyWorkspace instead
* Create a virtual tree with empty V1 workspace (throws error)
*/
function createTreeWithEmptyV1Workspace(): never;Usage Examples:
import {
createTree,
createTreeWithEmptyWorkspace,
Tree
} from "@nrwl/devkit/testing";
describe('My Generator', () => {
let tree: Tree;
beforeEach(() => {
// Create empty workspace for testing
tree = createTreeWithEmptyWorkspace();
});
it('should generate files correctly', async () => {
// Run your generator
await myGenerator(tree, { name: 'test-lib' });
// Assert files were created
expect(tree.exists('libs/test-lib/src/index.ts')).toBe(true);
expect(tree.exists('libs/test-lib/project.json')).toBe(true);
// Check file contents
const indexContent = tree.read('libs/test-lib/src/index.ts', 'utf-8');
expect(indexContent).toContain('export');
});
it('should update existing files', async () => {
// Pre-populate tree with existing files
tree.write('package.json', JSON.stringify({
name: 'test-workspace',
scripts: {}
}));
// Run generator that modifies existing files
await myGenerator(tree, { name: 'test-lib' });
// Check modifications
const packageJson = JSON.parse(tree.read('package.json', 'utf-8')!);
expect(packageJson.scripts).toBeDefined();
});
});Common patterns for testing generators and executors.
/**
* Helper to run generator and capture results
* @param generator - Generator function to test
* @param tree - Virtual file system
* @param options - Generator options
* @returns Promise with generator result and updated tree
*/
async function runGenerator<T>(
generator: Generator<T>,
tree: Tree,
options: T
): Promise<{
tree: Tree;
callback?: GeneratorCallback;
}>;
/**
* Helper to test executor execution
* @param executor - Executor function to test
* @param options - Executor options
* @param context - Mock executor context
* @returns Promise with execution result
*/
async function runExecutor<T>(
executor: Executor<T>,
options: T,
context: Partial<ExecutorContext>
): Promise<{ success: boolean; [key: string]: any }>;Usage Examples:
import {
Tree,
Generator,
ExecutorContext,
createTreeWithEmptyWorkspace
} from "@nrwl/devkit";
import { runGenerator, runExecutor } from "@nrwl/devkit/testing";
// Testing a generator
describe('Library Generator', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});
it('should create library files', async () => {
const { tree: resultTree, callback } = await runGenerator(
libraryGenerator,
tree,
{ name: 'my-lib', directory: 'libs' }
);
// Test file creation
expect(resultTree.exists('libs/my-lib/src/index.ts')).toBe(true);
expect(resultTree.exists('libs/my-lib/project.json')).toBe(true);
// Test project configuration
const projectJson = JSON.parse(
resultTree.read('libs/my-lib/project.json', 'utf-8')!
);
expect(projectJson.projectType).toBe('library');
// Test callback execution
if (callback) {
await callback();
// Verify callback effects
}
});
it('should handle existing files gracefully', async () => {
// Pre-create conflicting file
tree.write('libs/my-lib/src/index.ts', 'existing content');
await expect(
runGenerator(libraryGenerator, tree, { name: 'my-lib' })
).rejects.toThrow('File already exists');
});
});
// Testing an executor
describe('Build Executor', () => {
it('should build successfully', async () => {
const mockContext: Partial<ExecutorContext> = {
root: '/workspace',
projectName: 'my-app',
targetName: 'build',
workspace: {
projects: {
'my-app': {
root: 'apps/my-app'
}
}
}
};
const result = await runExecutor(
buildExecutor,
{ outputPath: 'dist/apps/my-app' },
mockContext
);
expect(result.success).toBe(true);
});
it('should handle build errors', async () => {
const mockContext: Partial<ExecutorContext> = {
root: '/workspace',
projectName: 'broken-app'
};
const result = await runExecutor(
buildExecutor,
{ outputPath: 'dist/apps/broken-app' },
mockContext
);
expect(result.success).toBe(false);
expect(result.error).toBeDefined();
});
});Functions for creating mock objects and contexts for testing.
/**
* Create a mock executor context for testing
* @param overrides - Properties to override in the context
* @returns Mock ExecutorContext
*/
function createMockExecutorContext(
overrides?: Partial<ExecutorContext>
): ExecutorContext;
/**
* Create a mock project graph for testing
* @param projects - Projects to include in the graph
* @returns Mock ProjectGraph
*/
function createMockProjectGraph(
projects: Record<string, ProjectConfiguration>
): ProjectGraph;
/**
* Create mock workspace configuration
* @param projects - Projects to include
* @returns Mock workspace configuration
*/
function createMockWorkspace(
projects: Record<string, ProjectConfiguration>
): ProjectsConfigurations;Usage Examples:
import {
createMockExecutorContext,
createMockProjectGraph,
createMockWorkspace
} from "@nrwl/devkit/testing";
describe('Workspace Operations', () => {
it('should work with mock project graph', () => {
const mockGraph = createMockProjectGraph({
'app1': {
root: 'apps/app1',
projectType: 'application'
},
'lib1': {
root: 'libs/lib1',
projectType: 'library'
}
});
// Test functions that depend on project graph
const dependencies = findProjectDependencies(mockGraph, 'app1');
expect(dependencies).toEqual(['lib1']);
});
it('should work with mock executor context', async () => {
const mockContext = createMockExecutorContext({
projectName: 'test-project',
targetName: 'build',
root: '/test-workspace'
});
const result = await myExecutor({ outputPath: 'dist' }, mockContext);
expect(result.success).toBe(true);
});
});Specialized utilities for testing common Nx scenarios.
/**
* Helper for testing file generation
* @param tree - Virtual file system
* @param generator - Generator to test
* @param options - Generator options
* @returns Test assertions helper
*/
function testFileGeneration<T>(
tree: Tree,
generator: Generator<T>,
options: T
): {
toHaveCreatedFile(path: string): void;
toHaveUpdatedFile(path: string): void;
toHaveFileContent(path: string, content: string | RegExp): void;
};
/**
* Helper for testing project configuration changes
* @param tree - Virtual file system
* @param projectName - Project to test
* @returns Project configuration assertions
*/
function testProjectConfiguration(tree: Tree, projectName: string): {
toHaveTarget(targetName: string): void;
toHaveTag(tag: string): void;
toHaveProjectType(type: 'application' | 'library'): void;
};Usage Examples:
import {
testFileGeneration,
testProjectConfiguration
} from "@nrwl/devkit/testing";
describe('Component Generator', () => {
let tree: Tree;
beforeEach(() => {
tree = createTreeWithEmptyWorkspace();
});
it('should generate component files', async () => {
const fileTest = testFileGeneration(
tree,
componentGenerator,
{ name: 'my-component', project: 'my-app' }
);
await fileTest.toHaveCreatedFile('apps/my-app/src/app/my-component/my-component.component.ts');
await fileTest.toHaveCreatedFile('apps/my-app/src/app/my-component/my-component.component.html');
await fileTest.toHaveFileContent(
'apps/my-app/src/app/my-component/my-component.component.ts',
/export class MyComponentComponent/
);
});
it('should configure project correctly', async () => {
await libraryGenerator(tree, { name: 'my-lib' });
const projectTest = testProjectConfiguration(tree, 'my-lib');
projectTest.toHaveTarget('build');
projectTest.toHaveTarget('test');
projectTest.toHaveProjectType('library');
projectTest.toHaveTag('scope:shared');
});
});Install with Tessl CLI
npx tessl i tessl/npm-nrwl--devkit