or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

angular-testing.mdcli-integration.mdconfiguration.mdcustom-frameworks.mdindex.mdinteractive-testing.mdprogrammatic-execution.mdreact-testing.mdvue-testing.md
tile.json

angular-testing.mddocs/

Angular Component Testing

Mount and test Angular components with TestBed integration and Cypress testing commands. The @cypress/angular package provides seamless integration between Angular components and Cypress's powerful testing capabilities.

Capabilities

Mount Angular Components

Mount Angular components for isolated testing with inputs, providers, and full Cypress command support.

// From cypress/angular
import { mount } from '@cypress/angular';

/**
 * Mount an Angular component for testing
 * @param component - Angular component class to mount
 * @param options - Angular TestBed configuration options
 * @returns Cypress chainable with fixture and component access
 */
function mount<T>(
  component: Type<T>,
  options?: AngularMountOptions<T>
): Cypress.Chainable<AngularMountReturn<T>>;

interface AngularMountOptions<T> extends TestModuleMetadata {
  /** Component inputs (properties) */
  componentProperties?: Partial<T>;
  /** Flag to automatically create a cy.spy() for every component @Output() property */
  autoSpyOutputs?: boolean;
  /** Whether to log the mount command */
  log?: boolean;
}

interface AngularMountReturn<T> {
  /** Angular ComponentFixture for advanced testing */
  fixture: ComponentFixture<T>;
  /** Direct access to the component instance */
  component: T;
}

Usage Examples:

// cypress/component/button.component.cy.ts
import { mount } from '@cypress/angular';
import { ButtonComponent } from './button.component';

describe('ButtonComponent', () => {
  it('renders with text', () => {
    mount(ButtonComponent, {
      componentProperties: {
        text: 'Click me',
        type: 'primary'
      }
    });
    
    cy.contains('Click me').should('be.visible');
    cy.get('button').should('have.class', 'btn-primary');
  });

  it('emits click events with manual spy', () => {
    mount(ButtonComponent, {
      componentProperties: {
        text: 'Click me'
      }
    }).then(({ component }) => {
      const clickSpy = cy.spy(component.clicked, 'emit');
      
      cy.contains('Click me').click();
      cy.wrap(clickSpy).should('have.been.called');
    });
  });

  it('emits click events with autoSpyOutputs', () => {
    mount(ButtonComponent, {
      componentProperties: {
        text: 'Click me'
      },
      autoSpyOutputs: true
    });
    
    cy.contains('Click me').click();
    cy.get('@clickedSpy').should('have.been.called');
  });

  it('handles disabled state', () => {
    mount(ButtonComponent, {
      componentProperties: {
        text: 'Disabled Button',
        disabled: true
      }
    });
    
    cy.get('button').should('be.disabled');
    cy.get('button').should('have.class', 'btn-disabled');
  });
});

Mount with Dependency Injection

Test components that depend on Angular services through dependency injection.

import { mount } from '@cypress/angular';
import { UserService } from './user.service';
import { UserProfileComponent } from './user-profile.component';

describe('UserProfileComponent', () => {
  it('displays user data from service', () => {
    const mockUser = {
      id: 1,
      name: 'John Doe',
      email: 'john@example.com'
    };

    const mockUserService = {
      getCurrentUser: () => of(mockUser),
      updateUser: (user: any) => of(user)
    };

    mount(UserProfileComponent, {
      providers: [
        { provide: UserService, useValue: mockUserService }
      ]
    });

    cy.contains('John Doe').should('be.visible');
    cy.contains('john@example.com').should('be.visible');
  });

  it('handles service errors', () => {
    const mockUserService = {
      getCurrentUser: () => throwError('User not found'),
      updateUser: (user: any) => of(user)
    };

    mount(UserProfileComponent, {
      providers: [
        { provide: UserService, useValue: mockUserService }
      ]
    });

    cy.contains('Error loading user').should('be.visible');
  });
});

Mount with Angular Forms

Test components that use Angular reactive forms or template-driven forms.

import { mount } from '@cypress/angular';
import { ReactiveFormsModule } from '@angular/forms';
import { LoginFormComponent } from './login-form.component';

describe('LoginFormComponent', () => {
  it('validates required fields', () => {
    mount(LoginFormComponent, {
      imports: [ReactiveFormsModule]
    });

    cy.get('button[type=submit]').click();
    
    cy.contains('Email is required').should('be.visible');
    cy.contains('Password is required').should('be.visible');
    cy.get('form').should('have.class', 'ng-invalid');
  });

  it('submits valid form', () => {
    mount(LoginFormComponent, {
      imports: [ReactiveFormsModule]
    }).then(({ component }) => {
      const submitSpy = cy.spy(component.formSubmit, 'emit');

      cy.get('[data-cy=email-input]').type('user@example.com');
      cy.get('[data-cy=password-input]').type('password123');
      cy.get('button[type=submit]').click();

      cy.wrap(submitSpy).should('have.been.calledWith', {
        email: 'user@example.com',
        password: 'password123'
      });
    });
  });

  it('displays validation errors', () => {
    mount(LoginFormComponent, {
      imports: [ReactiveFormsModule]
    });

    cy.get('[data-cy=email-input]').type('invalid-email');
    cy.get('[data-cy=email-input]').blur();
    
    cy.contains('Please enter a valid email').should('be.visible');
  });
});

Mount with Angular Router

Test components that use Angular Router for navigation.

import { mount } from '@cypress/angular';
import { Router } from '@angular/router';
import { Location } from '@angular/common';
import { NavigationComponent } from './navigation.component';

describe('NavigationComponent', () => {
  const routes = [
    { path: '', component: {} },
    { path: 'dashboard', component: {} },
    { path: 'profile', component: {} }
  ];

  it('highlights active route', () => {
    mount(NavigationComponent, {
      providers: [
        { provide: Router, useValue: { url: '/dashboard' } },
        { provide: Location, useValue: { path: () => '/dashboard' } }
      ]
    });

    cy.get('[data-cy=nav-dashboard]').should('have.class', 'active');
    cy.get('[data-cy=nav-profile]').should('not.have.class', 'active');
  });

  it('navigates to different routes', () => {
    const mockRouter = {
      navigate: cy.stub(),
      url: '/'
    };

    mount(NavigationComponent, {
      providers: [
        { provide: Router, useValue: mockRouter }
      ]
    });

    cy.get('[data-cy=nav-profile]').click();
    cy.wrap(mockRouter.navigate).should('have.been.calledWith', ['/profile']);
  });
});

Mount with Angular Material

Test components that use Angular Material components.

import { mount } from '@cypress/angular';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { ActionButtonComponent } from './action-button.component';

describe('ActionButtonComponent', () => {
  it('renders Material Design button', () => {
    mount(ActionButtonComponent, {
      imports: [
        BrowserAnimationsModule,
        MatButtonModule,
        MatIconModule
      ],
      componentProperties: {
        label: 'Save',
        icon: 'save',
        color: 'primary'
      }
    });

    cy.get('button[mat-raised-button]').should('be.visible');
    cy.get('mat-icon').should('contain', 'save');
    cy.get('button').should('have.class', 'mat-primary');
  });

  it('handles different button variants', () => {
    mount(ActionButtonComponent, {
      imports: [BrowserAnimationsModule, MatButtonModule],
      componentProperties: {
        label: 'Delete',
        variant: 'warn',
        disabled: false
      }
    });

    cy.get('button').should('have.class', 'mat-warn');
    cy.get('button').should('not.be.disabled');
  });
});

Testing with HTTP Client

Test components that make HTTP requests using Angular's HttpClient.

import { mount } from '@cypress/angular';
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { DataService } from './data.service';
import { DataListComponent } from './data-list.component';

describe('DataListComponent', () => {
  it('loads and displays data', () => {
    const mockData = [
      { id: 1, name: 'Item 1' },
      { id: 2, name: 'Item 2' }
    ];

    mount(DataListComponent, {
      imports: [HttpClientTestingModule],
      providers: [DataService]
    }).then(({ fixture }) => {
      const httpTestingController = fixture.debugElement.injector.get(HttpTestingController);
      
      // Verify loading state
      cy.contains('Loading...').should('be.visible');
      
      // Mock HTTP response
      const req = httpTestingController.expectOne('/api/data');
      req.flush(mockData);
      
      // Verify data is displayed
      cy.contains('Item 1').should('be.visible');
      cy.contains('Item 2').should('be.visible');
      cy.get('[data-cy=data-item]').should('have.length', 2);
    });
  });

  it('handles HTTP errors', () => {
    mount(DataListComponent, {
      imports: [HttpClientTestingModule],
      providers: [DataService]
    }).then(({ fixture }) => {
      const httpTestingController = fixture.debugElement.injector.get(HttpTestingController);
      
      // Mock HTTP error
      const req = httpTestingController.expectOne('/api/data');
      req.error(new ErrorEvent('Network error'));
      
      // Verify error message
      cy.contains('Failed to load data').should('be.visible');
    });
  });
});

Testing Directives

Test custom Angular directives in isolation.

import { mount } from '@cypress/angular';
import { Component } from '@angular/core';
import { HighlightDirective } from './highlight.directive';

@Component({
  template: `
    <div [appHighlight]="highlightColor" data-cy="highlighted-div">
      Highlighted content
    </div>
  `
})
class TestComponent {
  highlightColor = 'yellow';
}

describe('HighlightDirective', () => {
  it('applies highlight color', () => {
    mount(TestComponent, {
      declarations: [HighlightDirective],
      componentProperties: {
        highlightColor: 'red'
      }
    });

    cy.get('[data-cy=highlighted-div]')
      .should('have.css', 'background-color', 'rgb(255, 0, 0)'); // red
  });

  it('responds to color changes', () => {
    mount(TestComponent, {
      declarations: [HighlightDirective]
    }).then(({ component, fixture }) => {
      // Initial color
      cy.get('[data-cy=highlighted-div]')
        .should('have.css', 'background-color', 'rgb(255, 255, 0)'); // yellow

      // Change color
      component.highlightColor = 'blue';
      fixture.detectChanges();

      cy.get('[data-cy=highlighted-div]')
        .should('have.css', 'background-color', 'rgb(0, 0, 255)'); // blue
    });
  });
});

Testing Pipes

Test custom Angular pipes in isolation.

import { mount } from '@cypress/angular';
import { Component } from '@angular/core';
import { TruncatePipe } from './truncate.pipe';

@Component({
  template: `
    <div data-cy="truncated-text">
      {{ longText | truncate:maxLength }}
    </div>
  `
})
class TestComponent {
  longText = 'This is a very long text that should be truncated';
  maxLength = 20;
}

describe('TruncatePipe', () => {
  it('truncates long text', () => {
    mount(TestComponent, {
      declarations: [TruncatePipe],
      componentProperties: {
        longText: 'This is a very long text that should be truncated',
        maxLength: 20
      }
    });

    cy.get('[data-cy=truncated-text]')
      .should('contain', 'This is a very long...');
  });

  it('leaves short text unchanged', () => {
    mount(TestComponent, {
      declarations: [TruncatePipe],
      componentProperties: {
        longText: 'Short text',
        maxLength: 20
      }
    });

    cy.get('[data-cy=truncated-text]')
      .should('contain', 'Short text');
  });
});

Component Testing Configuration

Configure Angular component testing in your Cypress configuration:

// cypress.config.ts
import { defineConfig } from 'cypress';

export default defineConfig({
  component: {
    devServer: {
      framework: 'angular',
      bundler: 'webpack',
    },
    specPattern: 'src/**/*.cy.ts',
    supportFile: 'cypress/support/component.ts'
  }
});

Advanced Component Testing Patterns

// Testing with complex dependency injection
describe('Complex Component', () => {
  it('handles multiple injected services', () => {
    const mockAuthService = {
      isAuthenticated: () => true,
      getCurrentUser: () => of({ name: 'Test User' })
    };

    const mockNotificationService = {
      showSuccess: cy.stub(),
      showError: cy.stub()
    };

    mount(ComplexComponent, {
      providers: [
        { provide: AuthService, useValue: mockAuthService },
        { provide: NotificationService, useValue: mockNotificationService }
      ]
    }).then(({ component }) => {
      cy.get('[data-cy=save-button]').click();
      cy.wrap(mockNotificationService.showSuccess)
        .should('have.been.calledWith', 'Saved successfully');
    });
  });
});

The Angular component testing integration provides comprehensive support for testing Angular components in isolation while maintaining access to Angular's dependency injection system, change detection, and ecosystem tools like Angular Material and reactive forms.