or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdindex.mdlanguage-service-api.mdtesting-framework.mdtypescript-plugin.md
tile.json

testing-framework.mddocs/

Testing Framework

Comprehensive testing utilities for creating in-memory Angular projects and testing language service integrations. Provides tools for unit testing Angular Language Service features and IDE integrations.

Capabilities

Language Service Test Environment

Main testing environment class for creating isolated, in-memory TypeScript server instances with Angular Language Service support.

/**
 * Main testing environment for Angular Language Service integration tests
 * Creates isolated in-memory tsserver instances for testing
 */
class LanguageServiceTestEnv {
  /**
   * Creates and initializes a new test environment
   * @returns Configured test environment instance
   */
  static setup(): LanguageServiceTestEnv;
  
  /**
   * Creates a new test project with Angular Language Service support
   * @param name - Project name
   * @param files - Project files as name/content mapping
   * @param angularCompilerOptions - Angular-specific compiler options
   * @param tsCompilerOptions - TypeScript compiler options
   * @returns Configured test project
   */
  addProject(
    name: string, 
    files: ProjectFiles, 
    angularCompilerOptions?: TestableOptions, 
    tsCompilerOptions?: ts.CompilerOptions
  ): Project;
  
  /**
   * Extracts text content from a TypeScript text span
   * @param fileName - File name containing the span
   * @param span - Text span to extract
   * @returns Extracted text or null if not found
   */
  getTextFromTsSpan(fileName: string, span: ts.TextSpan): string | null;
  
  /**
   * Asserts that there are no TypeScript source diagnostics across all projects
   * Throws if any source file has diagnostic errors
   */
  expectNoSourceDiagnostics(): void;
}

Test Project

Represents an individual Angular project within the test environment with full language service capabilities.

/**
 * Test project with Angular Language Service integration
 * Provides methods for testing language service features
 */
class Project {
  /**
   * Initializes a new test project
   * @param projectName - Name of the project
   * @param projectService - TypeScript project service instance
   * @param files - Project files
   * @param angularCompilerOptions - Angular compiler options
   * @param tsCompilerOptions - TypeScript compiler options
   * @returns Initialized project instance
   */
  static initialize(
    projectName: string,
    projectService: ts.server.ProjectService,
    files: ProjectFiles,
    angularCompilerOptions?: TestableOptions,
    tsCompilerOptions?: ts.CompilerOptions
  ): Project;
  
  /** Project name */
  readonly name: string;
  
  /** Angular Language Service instance for this project */
  readonly ngLS: LanguageService;
  
  /**
   * Opens a file for editing and returns a buffer interface
   * @param projectFileName - File name to open
   * @returns OpenBuffer instance for the file
   */
  openFile(projectFileName: string): OpenBuffer;
  
  /**
   * Gets the TypeScript SourceFile for a project file
   * @param projectFileName - File name
   * @returns SourceFile instance or undefined if not found
   */
  getSourceFile(projectFileName: string): ts.SourceFile | undefined;
  
  /**
   * Gets the TypeScript type checker for this project
   * @returns TypeChecker instance
   */
  getTypeChecker(): ts.TypeChecker;
  
  /**
   * Gets semantic diagnostics for a specific file
   * @param projectFileName - File name to check
   * @returns Array of diagnostic messages
   */
  getDiagnosticsForFile(projectFileName: string): ts.Diagnostic[];
  
  /**
   * Gets suggestion diagnostics for a specific file
   * @param projectFileName - File name to check
   * @returns Array of suggestion diagnostics
   */
  getSuggestionDiagnosticsForFile(projectFileName: string): ts.Diagnostic[];
  
  /**
   * Gets available code fixes at a specific position
   * @param projectFileName - File name
   * @param start - Start position
   * @param end - End position
   * @param errorCodes - Error codes to get fixes for
   * @returns Array of code fix actions
   */
  getCodeFixesAtPosition(
    projectFileName: string,
    start: number,
    end: number,
    errorCodes: readonly number[]
  ): readonly ts.CodeFixAction[];
  
  /**
   * Gets available refactorings at a position or range
   * @param projectFileName - File name
   * @param positionOrRange - Position or text range
   * @returns Array of applicable refactoring info
   */
  getRefactoringsAtPosition(
    projectFileName: string,
    positionOrRange: number | ts.TextRange
  ): readonly ts.ApplicableRefactorInfo[];
  
  /**
   * Applies a refactoring operation
   * @param projectFileName - File name
   * @param positionOrRange - Position or text range
   * @param refactorName - Name of refactoring to apply
   * @param reportProgress - Progress callback
   * @returns Promise resolving to refactoring result
   */
  applyRefactoring(
    projectFileName: string,
    positionOrRange: number | ts.TextRange,
    refactorName: string,
    reportProgress: ApplyRefactoringProgressFn
  ): Promise<ApplyRefactoringResult | undefined>;
  
  /**
   * Gets combined code fix for a specific fix ID
   * @param projectFileName - File name
   * @param fixId - Fix identifier
   * @returns Combined code actions
   */
  getCombinedCodeFix(projectFileName: string, fixId: string): ts.CombinedCodeActions;
  
  /**
   * Asserts that there are no source diagnostics in any TypeScript files
   * Throws if diagnostics are found
   */
  expectNoSourceDiagnostics(): void;
  
  /**
   * Asserts that there are no template diagnostics for a specific component
   * @param projectFileName - Component file name
   * @param className - Component class name
   */
  expectNoTemplateDiagnostics(projectFileName: string, className: string): void;
  
  /**
   * Gets the Angular template type checker for this project
   * @returns TemplateTypeChecker instance
   */
  getTemplateTypeChecker(): TemplateTypeChecker;
  
  /**
   * Gets the logger instance for this project
   * @returns TypeScript server logger
   */
  getLogger(): ts.server.Logger;
}

Open Buffer

Represents an open file in a test project with cursor positioning and language service operations.

/**
 * Represents an open file buffer with cursor positioning and language service operations
 */
class OpenBuffer {
  /**
   * Creates a new open buffer instance
   * @param ngLS - Angular Language Service instance
   * @param project - TypeScript server project
   * @param projectFileName - File name
   * @param scriptInfo - Script info for the file
   */
  constructor(
    ngLS: LanguageService,
    project: ts.server.Project,
    projectFileName: string,
    scriptInfo: ts.server.ScriptInfo
  );
  
  /** Current cursor position in the file */
  get cursor(): number;
  
  /** File contents */
  get contents(): string;
  
  /** Updates file contents */
  set contents(newContents: string);
  
  /**
   * Moves cursor to a position marked in text snippet
   * @param snippetWithCursor - Text with cursor marker (¦ or |)
   */
  moveCursorToText(snippetWithCursor: string): void;
  
  // Language Service Operations
  
  /**
   * Gets definition and bound span at cursor position
   * @returns Definition info with bound span or undefined
   */
  getDefinitionAndBoundSpan(): ts.DefinitionInfoAndBoundSpan | undefined;
  
  /**
   * Gets encoded semantic classifications for the file
   * @param span - Optional text span to classify
   * @param format - Classification format
   * @returns Semantic classifications
   */
  getEncodedSemanticClassifications(
    span?: ts.TextSpan,
    format?: ts.SemanticClassificationFormat
  ): ts.Classifications;
  
  /**
   * Gets completions at cursor position
   * @param options - Completion options
   * @returns Completion info with metadata or undefined
   */
  getCompletionsAtPosition(
    options?: ts.GetCompletionsAtPositionOptions
  ): ts.WithMetadata<ts.CompletionInfo> | undefined;
  
  /**
   * Gets completion entry details
   * @param entryName - Completion entry name
   * @param formatOptions - Format options
   * @param preferences - User preferences
   * @param data - Completion entry data
   * @returns Completion entry details or undefined
   */
  getCompletionEntryDetails(
    entryName: string,
    formatOptions?: ts.FormatCodeOptions | ts.FormatCodeSettings,
    preferences?: ts.UserPreferences,
    data?: ts.CompletionEntryData
  ): ts.CompletionEntryDetails | undefined;
  
  /**
   * Gets Type Check Block information at cursor position
   * @returns TCB response or undefined
   */
  getTcb(): GetTcbResponse | undefined;
  
  /**
   * Gets outlining spans for the file
   * @returns Array of outlining spans
   */
  getOutliningSpans(): ts.OutliningSpan[];
  
  /**
   * Gets template location for component at cursor position
   * @returns Document span or undefined
   */
  getTemplateLocationForComponent(): ts.DocumentSpan | undefined;
  
  /**
   * Gets quick info at cursor position
   * @returns Quick info or undefined
   */
  getQuickInfoAtPosition(): ts.QuickInfo | undefined;
  
  /**
   * Gets type definition at cursor position
   * @returns Array of definition info or undefined
   */
  getTypeDefinitionAtPosition(): readonly ts.DefinitionInfo[] | undefined;
  
  /**
   * Gets references at cursor position
   * @returns Array of reference entries or undefined
   */
  getReferencesAtPosition(): ts.ReferenceEntry[] | undefined;
  
  /**
   * Finds rename locations for symbol at cursor position
   * @returns Array of rename locations or undefined
   */
  findRenameLocations(): readonly ts.RenameLocation[] | undefined;
  
  /**
   * Gets rename info for symbol at cursor position
   * @returns Rename info
   */
  getRenameInfo(): ts.RenameInfo;
  
  /**
   * Gets signature help at cursor position
   * @returns Signature help items or undefined
   */
  getSignatureHelpItems(): ts.SignatureHelpItems | undefined;
}

Mock Server Host

Mock implementation of TypeScript's ServerHost for controlled testing environments.

/**
 * Mock implementation of TypeScript ServerHost for testing
 * Provides controlled file system and environment simulation
 */
class MockServerHost implements ts.server.ServerHost {
  /**
   * Creates a mock server host with a mock file system
   * @param fs - Mock file system instance
   */
  constructor(fs: MockFileSystem);
  
  /** Line ending character for this host */
  get newLine(): string;
  
  /** Whether file names are case sensitive */
  get useCaseSensitiveFileNames(): boolean;
  
  /**
   * Reads file content
   * @param path - File path
   * @param encoding - Text encoding
   * @returns File content or undefined if not found
   */
  readFile(path: string, encoding?: string): string | undefined;
  
  /**
   * Resolves a path to absolute form
   * @param path - Path to resolve
   * @returns Resolved absolute path
   */
  resolvePath(path: string): string;
  
  /**
   * Checks if file exists
   * @param path - File path
   * @returns True if file exists
   */
  fileExists(path: string): boolean;
  
  /**
   * Checks if directory exists
   * @param path - Directory path
   * @returns True if directory exists
   */
  directoryExists(path: string): boolean;
  
  /**
   * Creates a directory
   * @param path - Directory path to create
   */
  createDirectory(path: string): void;
  
  /**
   * Gets the executing file path
   * @returns Path to the executing file
   */
  getExecutingFilePath(): string;
  
  /**
   * Gets the current working directory
   * @returns Current directory path
   */
  getCurrentDirectory(): string;
  
  /**
   * Creates a hash of data
   * @param data - Data to hash
   * @returns Hash string
   */
  createHash(data: string): string;
}

Type Definitions

Project Files Type

/**
 * Mapping of file names to their content for test projects
 */
type ProjectFiles = { [fileName: string]: string };

Testable Options Type

/**
 * Combined type checking and compiler options for testing
 * Includes Angular-specific options and TypeScript compatibility options
 */
type TestableOptions = TypeCheckingOptions & 
                      InternalOptions & 
                      Pick<LegacyNgcOptions, 'fullTemplateTypeCheck'> & {
  /** Enable selectorless components (internal testing option) */
  _enableSelectorless?: boolean;
};

Testing Utility Functions

Mock Server Host

Mock implementation of TypeScript's ServerHost for controlled testing environments.

/**
 * Mock implementation of TypeScript ServerHost for testing
 * Provides controlled file system and environment simulation
 */
class MockServerHost implements ts.server.ServerHost {
  /**
   * Creates a mock server host with a mock file system
   * @param fs - Mock file system instance
   */
  constructor(fs: MockFileSystem);
  
  /** Line ending character for this host */
  get newLine(): string;
  
  /** Whether file names are case sensitive */
  get useCaseSensitiveFileNames(): boolean;
  
  /**
   * Reads file content
   * @param path - File path
   * @param encoding - Text encoding
   * @returns File content or undefined if not found
   */
  readFile(path: string, encoding?: string): string | undefined;
  
  /**
   * Resolves a path to absolute form
   * @param path - Path to resolve
   * @returns Resolved absolute path
   */
  resolvePath(path: string): string;
  
  /**
   * Checks if file exists
   * @param path - File path
   * @returns True if file exists
   */
  fileExists(path: string): boolean;
  
  /**
   * Checks if directory exists
   * @param path - Directory path
   * @returns True if directory exists
   */
  directoryExists(path: string): boolean;
  
  /**
   * Creates a directory
   * @param path - Directory path to create
   */
  createDirectory(path: string): void;
  
  /**
   * Gets the executing file path
   * @returns Path to the executing file
   */
  getExecutingFilePath(): string;
  
  /**
   * Gets the current working directory
   * @returns Current directory path
   */
  getCurrentDirectory(): string;
  
  /**
   * Creates a hash of data
   * @param data - Data to hash
   * @returns Hash string
   */
  createHash(data: string): string;
}

Assertion Helpers

/**
 * Asserts that array of objects with fileName property matches expected file names
 * @param refs - Array of objects with fileName property
 * @param expectedFileNames - Expected file names
 */
function assertFileNames(
  refs: Array<{fileName: string}>, 
  expectedFileNames: string[]
): void;

/**
 * Asserts that array of objects with fileName property matches expected paths (regex)
 * @param refs - Array of objects with fileName property
 * @param expectedPaths - Expected path patterns as regex
 */
function assertFilePaths(
  refs: Array<{fileName: string}>, 
  expectedPaths: RegExp[]
): void;

/**
 * Asserts that array of objects with textSpan property matches expected text spans
 * @param items - Array of objects with textSpan property
 * @param expectedTextSpans - Expected text span content
 */
function assertTextSpans(
  items: Array<{textSpan: string}>, 
  expectedTextSpans: string[]
): void;

Diagnostic Helpers

/**
 * Checks if a diagnostic is Angular-specific (not from TypeScript)
 * @param diag - TypeScript diagnostic
 * @returns True if diagnostic originates from Angular
 */
function isNgSpecificDiagnostic(diag: ts.Diagnostic): boolean;

Project Creation Helpers

/**
 * Creates test project with both module-based and standalone declarations
 * @param env - Test environment
 * @param projectName - Project name
 * @param projectFiles - Main project files
 * @param angularCompilerOptions - Angular options
 * @param standaloneFiles - Additional standalone files
 * @param tsCompilerOptions - TypeScript options
 * @returns Configured project
 */
function createModuleAndProjectWithDeclarations(
  env: LanguageServiceTestEnv,
  projectName: string,
  projectFiles: ProjectFiles,
  angularCompilerOptions?: TestableOptions,
  standaloneFiles?: ProjectFiles,
  tsCompilerOptions?: ts.CompilerOptions
): Project;

/**
 * Creates test project with standalone declarations only
 * @param env - Test environment
 * @param projectName - Project name
 * @param projectFiles - Project files
 * @param angularCompilerOptions - Angular options
 * @param standaloneFiles - Standalone files
 * @returns Configured project
 */
function createProjectWithStandaloneDeclarations(
  env: LanguageServiceTestEnv,
  projectName: string,
  projectFiles: ProjectFiles,
  angularCompilerOptions?: TestableOptions,
  standaloneFiles?: ProjectFiles
): Project;

Utility Functions

/**
 * Humanizes document span objects for better test output
 * @param item - Document span-like object
 * @param env - Test environment for context
 * @returns Humanized object with string representations
 */
function humanizeDocumentSpanLike<T extends ts.DocumentSpan>(
  item: T, 
  env: LanguageServiceTestEnv
): T & Stringy<ts.DocumentSpan>;

/**
 * Extracts text from content using a text span
 * @param contents - Full text content
 * @param textSpan - Span to extract
 * @returns Extracted text
 */
function getText(contents: string, textSpan: ts.TextSpan): string;

/**
 * Patches language service projects with test host for controlled testing
 * Internal function for test environment setup
 */
function patchLanguageServiceProjectsWithTestHost(): void;

Usage Examples

Basic Test Setup

import { 
  LanguageServiceTestEnv, 
  Project, 
  ProjectFiles 
} from "@angular/language-service/testing";

describe('Angular Language Service', () => {
  let env: LanguageServiceTestEnv;
  let project: Project;
  
  beforeEach(() => {
    env = LanguageServiceTestEnv.setup();
    
    const files: ProjectFiles = {
      'app.component.ts': `
        import { Component } from '@angular/core';
        @Component({
          selector: 'app-root',
          template: '<div>{{ title }}</div>'
        })
        export class AppComponent {
          title = 'Hello World';
        }
      `,
      'app.module.ts': `
        import { NgModule } from '@angular/core';
        import { AppComponent } from './app.component';
        @NgModule({
          declarations: [AppComponent],
          bootstrap: [AppComponent]
        })
        export class AppModule {}
      `
    };
    
    project = env.addProject('test-project', files);
  });
  
  it('should provide completions in template', () => {
    const buffer = project.openFile('app.component.ts');
    buffer.moveCursorToText('{{ title¦ }}');
    
    const completions = buffer.getCompletionsAtPosition();
    expect(completions?.entries).toBeDefined();
    expect(completions!.entries.some(e => e.name === 'title')).toBe(true);
  });
});

Testing Refactorings

import { 
  LanguageServiceTestEnv, 
  Project,
  ApplyRefactoringProgressFn 
} from "@angular/language-service/testing";

describe('Signal Input Refactoring', () => {
  let env: LanguageServiceTestEnv;
  let project: Project;
  
  beforeEach(() => {
    env = LanguageServiceTestEnv.setup();
    project = env.addProject('signal-test', {
      'component.ts': `
        import { Component, Input } from '@angular/core';
        @Component({
          selector: 'my-comp',
          template: '<div>{{ value }}</div>'
        })
        export class MyComponent {
          @Input() value: string = '';
        }
      `
    });
  });
  
  it('should convert @Input to signal input', async () => {
    const buffer = project.openFile('component.ts');
    buffer.moveCursorToText('@Input() value¦: string');
    
    const progressCallback: ApplyRefactoringProgressFn = jest.fn();
    
    const result = await buffer.applyRefactoring(
      'component.ts',
      buffer.cursor,
      'convert-to-signal-input',
      progressCallback
    );
    
    expect(result).toBeDefined();
    expect(result!.errorMessage).toBeUndefined();
    expect(progressCallback).toHaveBeenCalled();
  });
});

Testing Diagnostics

import { 
  LanguageServiceTestEnv, 
  Project,
  isNgSpecificDiagnostic 
} from "@angular/language-service/testing";

describe('Template Diagnostics', () => {
  let env: LanguageServiceTestEnv;
  let project: Project;
  
  beforeEach(() => {
    env = LanguageServiceTestEnv.setup();
    project = env.addProject('diagnostic-test', {
      'component.ts': `
        import { Component } from '@angular/core';
        @Component({
          selector: 'my-comp',
          template: '<div>{{ unknownProperty }}</div>'
        })
        export class MyComponent {
          knownProperty = 'test';
        }
      `
    }, { strictTemplates: true });
  });
  
  it('should report template errors', () => {
    const diagnostics = project.getDiagnosticsForFile('component.ts');
    const angularDiagnostics = diagnostics.filter(isNgSpecificDiagnostic);
    
    expect(angularDiagnostics.length).toBeGreaterThan(0);
    expect(angularDiagnostics[0].messageText).toContain('unknownProperty');
  });
});