Render Angular components in VItest Browser Mode
npm install vitest-browser-angularRender Angular components in VItest Browser Mode.
``sh`
pnpm add -D vitest-browser-angular
To set up your test environment (with Zone.js or Zoneless), use @analogjs/vitest-angular's setupTestBed() function.
Important: Make sure to use { browserMode: true } when calling setupTestBed() to enable Vitest browser mode's visual test preview functionality.
For detailed setup instructions for both Zone.js and Zoneless configurations, please refer to the Analog Vitest documentation.
The render function supports two query patterns:
`ts
import { test, expect } from 'vitest';
import { render } from 'vitest-browser-angular';
@Component({
template:
,
})
export class MyComponent {}test('query elements', async () => {
// Pattern 1: Use locator to query within the component element
const { locator } = await render(MyComponent);
await expect.element(locator.getByText('Welcome')).toBeVisible();
// Pattern 2: Use screen to query from document.body (useful for portals/overlays)
const screen = await render(MyComponent);
await expect.element(screen.getByText('Welcome')).toBeVisible();
await expect.element(screen.getByText('Some Popover Content')).toBeVisible();
});
`$3
Both
locator and screen provide the following query methods:-
getByRole - Locate by ARIA role and accessible name
- getByText - Locate by text content
- getByLabelText - Locate by associated label text
- getByPlaceholder - Locate by placeholder text
- getByAltText - Locate by alt text (images)
- getByTitle - Locate by title attribute
- getByTestId - Locate by data-testid attributeWhen to use which pattern:
-
locator: (full name: "Component Locator") - queries are scoped to the component's host element. Best for most component tests.
- screen: Queries start from baseElement (defaults to document.body). Use when testing components that render content outside their host element (modals, tooltips, portals).$3
Access the component's host element directly via
container (shortcut for fixture.nativeElement):`ts
const { container, locator } = await render(MyComponent);
expect(container).toBe(locator.element());
`$3
Customize the root element for screen queries (useful for portal/overlay testing):
`ts
const customContainer = document.querySelector('#modal-root');
const screen = await render(ModalComponent, {
baseElement: customContainer,
});
// screen queries now start from customContainer instead of document.body
`Inputs
Pass input values to components using the
inputs option:`ts
import { Component, input } from '@angular/core';@Component({
template: '
{{ name() }}
Price: ${{ price() }}
',
standalone: true,
})
export class ProductComponent {
name = input('Unknown Product');
price = input(0);
}test('render with inputs', async () => {
const screen = await render(ProductComponent, {
inputs: {
name: 'Laptop',
price: 1299.99,
},
});
await expect.element(screen.getByText('Laptop')).toBeVisible();
await expect.element(screen.getByText(/Price: \$1299\.99/)).toBeVisible();
});
`Works with both signal-based inputs (
input()) and @Input() decorators.Routing
$3
Enable routing with
withRouting: true for components that use routing features but don't require specific route configuration:`ts
import { test, expect } from 'vitest';
import { render } from 'vitest-browser-angular';
import { Component } from '@angular/core';
import { RouterLink, RouterOutlet } from '@angular/router';@Component({
template:
,
imports: [RouterLink, RouterOutlet],
})
export class RoutedComponent {}test('render with simple routing', async () => {
const screen = await render(RoutedComponent, {
withRouting: true,
});
await expect.element(screen.getByText('Home')).toBeVisible();
await expect.element(screen.getByText('About')).toBeVisible();
});
`$3
Configure specific routes and optionally set an initial route:
`ts
import { test, expect } from 'vitest';
import { render } from 'vitest-browser-angular';
import { Component, inject } from '@angular/core';
import { Router, RouterLink, RouterOutlet, Routes } from '@angular/router';@Component({
template: '
Home Page
',
})
export class HomeComponent {}@Component({
template: '
About Page
',
standalone: true,
})
export class AboutComponent {}@Component({
template:
,
imports: [RouterLink, RouterOutlet],
standalone: true,
})
export class AppComponent {
router = inject(Router);
}const routes: Routes = [
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
{ path: '', redirectTo: '/home', pathMatch: 'full' },
];
test('render with route configuration', async () => {
const { locator, router } = await render(AppComponent, {
withRouting: {
routes,
initialRoute: '/home',
},
});
await expect.element(locator).toHaveTextContent('Home Page');
// Navigate programmatically
await router.navigate(['/about']);
await expect.element(locator).toHaveTextContent('About Page');
});
`Component Providers
If you need to add or override component providers, you can use the
componentProviders option.`ts
@Component({
template: '{{ title }}
',
providers: [GreetingService],
})
export class HelloWorldComponent {
title = 'Hello World';
}test('renders component with service provider', async () => {
const screen = await render(ServiceConsumerComponent, {
componentProviders: [
{ provide: GreetingService, useClass: FakeGreetingService },
],
});
await expect.element(screen.getByText('Fake Greeting')).toBeVisible();
});
``Want to contribute? Yayy! 🎉
Please read and follow our Contributing Guidelines to learn what are the right steps to take before contributing your time, effort and code.
Thanks 🙏
Be kind to each other and please read our code of conduct.
This project is inspired by the following projects:
vitest-browser-vue
angular-testing-library
MIT