or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

buttons-indicators.mddata-display.mddate-time.mdform-controls.mdindex.mdlayout.mdnavigation.mdpopups-modals.mdtesting.mdtheming.md
tile.json

testing.mddocs/

Testing

Comprehensive testing utilities and harnesses for all Angular Material components, enabling reliable component testing with Angular Testing Library and Jasmine/Jest.

Capabilities

Test Harnesses

Component test harnesses for automated testing of Material components.

/**
 * Base class for component test harnesses
 */
abstract class ComponentHarness {
  protected constructor(elementRef: TestElement);
  
  protected locatorFor<T extends ComponentHarness>(selector: string): AsyncFactoryFn<T>;
  protected locatorForOptional<T extends ComponentHarness>(selector: string): AsyncFactoryFn<T | null>;
  protected locatorForAll<T extends ComponentHarness>(selector: string): AsyncFactoryFn<T[]>;
  protected async waitForTasksOutsideAngular(): Promise<void>;
  protected async forceStabilize(): Promise<void>;
  
  async host(): Promise<TestElement>;
}

/**
 * Button harness for testing button components
 */
class MatButtonHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-button';
  
  static with(options: ButtonHarnessFilters): HarnessPredicate<MatButtonHarness>;
  
  async click(): Promise<void>;
  async isDisabled(): Promise<boolean>;
  async getText(): Promise<string>;
  async focus(): Promise<void>;
  async blur(): Promise<void>;
  async isFocused(): Promise<boolean>;
  async getVariant(): Promise<'text' | 'raised' | 'stroked' | 'flat'>;
}

/**
 * Input harness for testing input components
 */
class MatInputHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-input-element';
  
  static with(options: InputHarnessFilters): HarnessPredicate<MatInputHarness>;
  
  async getValue(): Promise<string>;
  async setValue(newValue: string): Promise<void>;
  async isDisabled(): Promise<boolean>;
  async isRequired(): Promise<boolean>;
  async isReadonly(): Promise<boolean>;
  async getType(): Promise<string>;
  async getName(): Promise<string>;
  async getId(): Promise<string>;
  async getPlaceholder(): Promise<string>;
  async focus(): Promise<void>;
  async blur(): Promise<void>;
  async isFocused(): Promise<boolean>;
  async sendKeys(...keys: (string | TestKey)[]): Promise<void>;
}

/**
 * Form field harness for testing form field containers
 */
class MatFormFieldHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-form-field';
  
  static with(options: FormFieldHarnessFilters): HarnessPredicate<MatFormFieldHarness>;
  
  async getControl(): Promise<MatInputHarness | MatSelectHarness | MatDatepickerInputHarness | null>;
  async getLabel(): Promise<string | null>;
  async hasLabel(): Promise<boolean>;
  async getErrors(): Promise<string[]>;
  async getHints(): Promise<string[]>;
  async isRequired(): Promise<boolean>;
  async isDisabled(): Promise<boolean>;
  async isValid(): Promise<boolean>;
  async hasErrors(): Promise<boolean>;
  async getAppearance(): Promise<'fill' | 'outline'>;
  async getThemeColor(): Promise<'primary' | 'accent' | 'warn'>;
  async isLabelFloating(): Promise<boolean>;
}

/**
 * Select harness for testing select components
 */
class MatSelectHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-select';
  
  static with(options: SelectHarnessFilters): HarnessPredicate<MatSelectHarness>;
  
  async open(): Promise<void>;
  async close(): Promise<void>;
  async isOpen(): Promise<boolean>;
  async getOptions(filter?: OptionHarnessFilters): Promise<MatOptionHarness[]>;
  async clickOptions(filter?: OptionHarnessFilters): Promise<void>;
  async getValue(): Promise<string>;
  async getValueText(): Promise<string>;
  async isDisabled(): Promise<boolean>;
  async isRequired(): Promise<boolean>;
  async isMultiple(): Promise<boolean>;
  async focus(): Promise<void>;
  async blur(): Promise<void>;
  async isFocused(): Promise<boolean>;
}

/**
 * Option harness for testing option components
 */
class MatOptionHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-option';
  
  static with(options: OptionHarnessFilters): HarnessPredicate<MatOptionHarness>;
  
  async click(): Promise<void>;
  async getText(): Promise<string>;
  async getValue(): Promise<string>;
  async isSelected(): Promise<boolean>;
  async isActive(): Promise<boolean>;
  async isDisabled(): Promise<boolean>;
  async isMultiple(): Promise<boolean>;
}

/**
 * Checkbox harness for testing checkbox components
 */
class MatCheckboxHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-checkbox';
  
  static with(options: CheckboxHarnessFilters): HarnessPredicate<MatCheckboxHarness>;
  
  async isChecked(): Promise<boolean>;
  async isIndeterminate(): Promise<boolean>;
  async isDisabled(): Promise<boolean>;
  async isRequired(): Promise<boolean>;
  async isValid(): Promise<boolean>;
  async getName(): Promise<string | null>;
  async getValue(): Promise<string | null>;
  async getAriaLabel(): Promise<string | null>;
  async getAriaLabelledby(): Promise<string | null>;
  async getLabelText(): Promise<string>;
  async check(): Promise<void>;
  async uncheck(): Promise<void>;
  async toggle(): Promise<void>;
  async focus(): Promise<void>;
  async blur(): Promise<void>;
  async isFocused(): Promise<boolean>;
}

/**
 * Dialog harness for testing dialog components
 */
class MatDialogHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-dialog-container';
  
  static with(options: DialogHarnessFilters): HarnessPredicate<MatDialogHarness>;
  
  async getId(): Promise<string | null>;
  async getRole(): Promise<DialogRole | null>;
  async getAriaLabel(): Promise<string | null>;
  async getAriaLabelledby(): Promise<string | null>;
  async getAriaDescribedby(): Promise<string | null>;
  async close(): Promise<void>;
}

/**
 * Snack bar harness for testing snack bar components
 */
class MatSnackBarHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-snack-bar-container';
  
  static with(options: SnackBarHarnessFilters): HarnessPredicate<MatSnackBarHarness>;
  
  async getMessage(): Promise<string>;
  async getActionDescription(): Promise<string>;
  async hasAction(): Promise<boolean>;
  async dismissWithAction(): Promise<void>;
  async dismiss(): Promise<void>;
  async isDismissed(): Promise<boolean>;
}

/**
 * Table harness for testing table components
 */
class MatTableHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-table';
  
  static with(options: TableHarnessFilters): HarnessPredicate<MatTableHarness>;
  
  async getHeaderRows(filter?: RowHarnessFilters): Promise<MatHeaderRowHarness[]>;
  async getRows(filter?: RowHarnessFilters): Promise<MatRowHarness[]>;
  async getFooterRows(filter?: RowHarnessFilters): Promise<MatFooterRowHarness[]>;
  async getCellTextByIndex(filter: RowHarnessFilters, columnIndex: number): Promise<string[]>;
  async getCellTextByColumnName(filter: RowHarnessFilters, columnName: string): Promise<string[]>;
}

/**
 * Tabs harness for testing tab components
 */
class MatTabGroupHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-tab-group';
  
  static with(options: TabGroupHarnessFilters): HarnessPredicate<MatTabGroupHarness>;
  
  async getTabs(filter?: TabHarnessFilters): Promise<MatTabHarness[]>;
  async getSelectedTab(): Promise<MatTabHarness | null>;
  async selectTab(filter: TabHarnessFilters): Promise<void>;
}

/**
 * Tab harness for testing individual tabs
 */
class MatTabHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-tab';
  
  static with(options: TabHarnessFilters): HarnessPredicate<MatTabHarness>;
  
  async getLabel(): Promise<string>;
  async getAriaLabel(): Promise<string | null>;
  async getAriaLabelledby(): Promise<string | null>;
  async isSelected(): Promise<boolean>;
  async isDisabled(): Promise<boolean>;
  async select(): Promise<void>;
  async getTextContent(): Promise<string>;
}

/**
 * Menu harness for testing menu components
 */
class MatMenuHarness extends ComponentHarness {
  static hostSelector = '.mat-mdc-menu-panel';
  
  static with(options: MenuHarnessFilters): HarnessPredicate<MatMenuHarness>;
  
  async isOpen(): Promise<boolean>;
  async getTriggerText(): Promise<string>;
  async focus(): Promise<void>;
  async blur(): Promise<void>;
  async isFocused(): Promise<boolean>;
  async open(): Promise<void>;
  async close(): Promise<void>;
  async getItems(filters?: MenuItemHarnessFilters): Promise<MatMenuItemHarness[]>;
  async clickItem(itemFilter: MenuItemHarnessFilters, ...subItemFilters: MenuItemHarnessFilters[]): Promise<void>;
}

/**
 * Datepicker harness for testing datepicker components
 */
class MatDatepickerInputHarness extends ComponentHarness {
  static hostSelector = '.mat-datepicker-input';
  
  static with(options: DatepickerInputHarnessFilters): HarnessPredicate<MatDatepickerInputHarness>;
  
  async getValue(): Promise<string>;
  async setValue(newValue: string): Promise<void>;
  async openCalendar(): Promise<void>;
  async closeCalendar(): Promise<void>;
  async isCalendarOpen(): Promise<boolean>;
  async isDisabled(): Promise<boolean>;
  async isRequired(): Promise<boolean>;
  async getMin(): Promise<string | null>;
  async getMax(): Promise<string | null>;
}

Filter Interfaces

Type definitions for filtering test harnesses.

/**
 * Base harness filters
 */
interface BaseHarnessFilters {
  selector?: string;
  ancestor?: string;
}

/**
 * Button harness filters
 */
interface ButtonHarnessFilters extends BaseHarnessFilters {
  text?: string | RegExp;
  variant?: 'text' | 'raised' | 'stroked' | 'flat';
  disabled?: boolean;
}

/**
 * Input harness filters
 */
interface InputHarnessFilters extends BaseHarnessFilters {
  value?: string | RegExp;
  placeholder?: string | RegExp;
  type?: string;
  disabled?: boolean;
}

/**
 * Form field harness filters
 */
interface FormFieldHarnessFilters extends BaseHarnessFilters {
  floatingLabelText?: string | RegExp;
  hasErrors?: boolean;
}

/**
 * Select harness filters
 */
interface SelectHarnessFilters extends BaseHarnessFilters {
  disabled?: boolean;
  required?: boolean;
  multiple?: boolean;
}

/**
 * Option harness filters
 */
interface OptionHarnessFilters extends BaseHarnessFilters {
  text?: string | RegExp;
  isSelected?: boolean;
  disabled?: boolean;
}

/**
 * Checkbox harness filters
 */
interface CheckboxHarnessFilters extends BaseHarnessFilters {
  label?: string | RegExp;
  name?: string;
  checked?: boolean;
  disabled?: boolean;
}

/**
 * Dialog harness filters
 */
interface DialogHarnessFilters extends BaseHarnessFilters {
  // No additional filters for dialog
}

/**
 * Snack bar harness filters
 */
interface SnackBarHarnessFilters extends BaseHarnessFilters {
  // No additional filters for snack bar
}

/**
 * Table harness filters
 */
interface TableHarnessFilters extends BaseHarnessFilters {
  // No additional filters for table
}

/**
 * Row harness filters
 */
interface RowHarnessFilters extends BaseHarnessFilters {
  // No additional filters for row
}

/**
 * Tab group harness filters
 */
interface TabGroupHarnessFilters extends BaseHarnessFilters {
  selectedTabLabel?: string | RegExp;
}

/**
 * Tab harness filters
 */
interface TabHarnessFilters extends BaseHarnessFilters {
  label?: string | RegExp;
  selected?: boolean;
  disabled?: boolean;
}

/**
 * Menu harness filters
 */
interface MenuHarnessFilters extends BaseHarnessFilters {
  triggerText?: string | RegExp;
}

/**
 * Menu item harness filters
 */
interface MenuItemHarnessFilters extends BaseHarnessFilters {
  text?: string | RegExp;
  hasSubmenu?: boolean;
}

/**
 * Datepicker input harness filters
 */
interface DatepickerInputHarnessFilters extends BaseHarnessFilters {
  value?: string | RegExp;
  placeholder?: string | RegExp;
}

Test Utilities

Utility constants and functions for testing with dates and other common scenarios.

/**
 * Month constants for easier date testing
 */
const JAN = 0;
const FEB = 1;
const MAR = 2;
const APR = 3;
const MAY = 4;
const JUN = 5;
const JUL = 6;
const AUG = 7;
const SEP = 8;
const OCT = 9;
const NOV = 10;
const DEC = 11;

/**
 * Test key constants
 */
enum TestKey {
  BACKSPACE,
  TAB,
  ENTER,
  SHIFT,
  CONTROL,
  ALT,
  PAUSE,
  ESCAPE,
  SPACE,
  PAGE_UP,
  PAGE_DOWN,
  END,
  HOME,
  LEFT_ARROW,
  UP_ARROW,
  RIGHT_ARROW,
  DOWN_ARROW,
  INSERT,
  DELETE,
  F1,
  F2,
  F3,
  F4,
  F5,
  F6,
  F7,
  F8,
  F9,
  F10,
  F11,
  F12,
  META
}

/**
 * Test element interface
 */
interface TestElement {
  blur(): Promise<void>;
  clear(): Promise<void>;
  click(): Promise<void>;
  focus(): Promise<void>;
  getCssValue(property: string): Promise<string>;
  hover(): Promise<void>;
  mouseAway(): Promise<void>;
  sendKeys(...keys: (string | TestKey)[]): Promise<void>;
  text(): Promise<string>;
  getAttribute(name: string): Promise<string | null>;
  hasClass(name: string): Promise<boolean>;
  getDimensions(): Promise<ElementDimensions>;
  getProperty(name: string): Promise<any>;
  setInputValue(value: string): Promise<void>;
  selectOptions(...optionIndexes: number[]): Promise<void>;
  matchesSelector(selector: string): Promise<boolean>;
  isFocused(): Promise<boolean>;
  dispatchEvent(name: string, data?: Record<string, EventInit>): Promise<void>;
}

/**
 * Element dimensions interface
 */
interface ElementDimensions {
  top: number;
  left: number;
  width: number;
  height: number;
}

/**
 * Harness predicate for filtering
 */
class HarnessPredicate<T extends ComponentHarness> {
  static stringMatches(s: string | Promise<string>, pattern: string | RegExp): Promise<boolean>;
  
  add(description: string, predicate: AsyncPredicate<T>): this;
  addOption<O>(name: string, option: O | undefined, predicate: AsyncOptionPredicate<T, O>): this;
  filter(harnesses: T[]): Promise<T[]>;
  evaluate(harness: T): Promise<boolean>;
  getDescription(): string;
  getSelector(): string;
}

/**
 * Async predicate function type
 */
type AsyncPredicate<T> = (item: T) => Promise<boolean>;

/**
 * Async option predicate function type
 */
type AsyncOptionPredicate<T, O> = (item: T, option: O) => Promise<boolean>;

/**
 * Async factory function type
 */
type AsyncFactoryFn<T> = () => Promise<T>;

Usage Examples:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatButtonHarness } from '@angular/material/button/testing';
import { MatInputHarness } from '@angular/material/input/testing';
import { MatFormFieldHarness } from '@angular/material/form-field/testing';
import { MatSelectHarness } from '@angular/material/select/testing';
import { MatCheckboxHarness } from '@angular/material/checkbox/testing';
import { MatDialogHarness } from '@angular/material/dialog/testing';
import { JAN, FEB, MAR } from '@angular/material/testing';

describe('MyComponent', () => {
  let component: MyComponent;
  let fixture: ComponentFixture<MyComponent>;
  let loader: HarnessLoader;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [MyComponent]
    }).compileComponents();

    fixture = TestBed.createComponent(MyComponent);
    component = fixture.componentInstance;
    loader = TestbedHarnessEnvironment.loader(fixture);
  });

  it('should interact with button', async () => {
    const button = await loader.getHarness(MatButtonHarness.with({text: 'Click me'}));
    
    expect(await button.isDisabled()).toBe(false);
    expect(await button.getText()).toBe('Click me');
    
    await button.click();
    
    expect(component.clicked).toBe(true);
  });

  it('should fill out form', async () => {
    // Get form field harnesses
    const nameField = await loader.getHarness(
      MatFormFieldHarness.with({floatingLabelText: 'Name'})
    );
    const emailField = await loader.getHarness(
      MatFormFieldHarness.with({floatingLabelText: 'Email'})
    );
    
    // Get input controls
    const nameInput = await nameField.getControl() as MatInputHarness;
    const emailInput = await emailField.getControl() as MatInputHarness;
    
    // Fill out form
    await nameInput.setValue('John Doe');
    await emailInput.setValue('john@example.com');
    
    expect(await nameInput.getValue()).toBe('John Doe');
    expect(await emailInput.getValue()).toBe('john@example.com');
    
    // Check validation
    expect(await nameField.hasErrors()).toBe(false);
    expect(await emailField.hasErrors()).toBe(false);
  });

  it('should select from dropdown', async () => {
    const select = await loader.getHarness(MatSelectHarness);
    
    expect(await select.isOpen()).toBe(false);
    
    await select.open();
    expect(await select.isOpen()).toBe(true);
    
    const options = await select.getOptions();
    expect(options.length).toBe(3);
    
    await select.clickOptions({text: 'Option 2'});
    expect(await select.getValueText()).toBe('Option 2');
  });

  it('should check checkbox', async () => {
    const checkbox = await loader.getHarness(
      MatCheckboxHarness.with({label: 'Accept terms'})
    );
    
    expect(await checkbox.isChecked()).toBe(false);
    
    await checkbox.check();
    expect(await checkbox.isChecked()).toBe(true);
    
    await checkbox.uncheck();
    expect(await checkbox.isChecked()).toBe(false);
  });

  it('should test dialog', async () => {
    // Trigger dialog open
    const openButton = await loader.getHarness(
      MatButtonHarness.with({text: 'Open Dialog'})
    );
    await openButton.click();
    
    // Get dialog harness
    const dialog = await loader.getHarness(MatDialogHarness);
    
    expect(await dialog.getId()).toBe('my-dialog');
    expect(await dialog.getRole()).toBe('dialog');
    
    // Interact with dialog content
    const closeButton = await loader.getHarness(
      MatButtonHarness.with({text: 'Close'})
    );
    await closeButton.click();
  });

  it('should test date operations', () => {
    // Use month constants for clearer date testing
    const testDate = new Date(2023, JAN, 15); // January 15, 2023
    const anotherDate = new Date(2023, FEB, 28); // February 28, 2023
    
    component.setStartDate(testDate);
    component.setEndDate(anotherDate);
    
    expect(component.getDateRange()).toEqual({
      start: testDate,
      end: anotherDate
    });
  });

  it('should test async operations', async () => {
    const loadButton = await loader.getHarness(
      MatButtonHarness.with({text: 'Load Data'})
    );
    
    await loadButton.click();
    
    // Wait for async operation to complete
    await fixture.whenStable();
    
    expect(component.isLoading).toBe(false);
    expect(component.data.length).toBeGreaterThan(0);
  });
});