vitest-browser-angular 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,38 +1,188 @@
1
1
  # vitest-browser-angular
2
2
 
3
- Render Angular components in VItest Browser Mode.
3
+ This community package renders Angular components in [Vitest Browser Mode](https://vitest.dev/guide/browser).
4
4
 
5
- ## Installation
5
+ ```ts
6
+ import { Component, input } from '@angular/core';
7
+ import { expect, test } from 'vitest';
8
+ import { render } from 'vitest-browser-angular';
9
+
10
+ @Component({
11
+ selector: 'app-hello-world',
12
+ template: '<h1>Hello, {{ name() }}!</h1>',
13
+ })
14
+ export class HelloWorld {
15
+ name = input.required<string>();
16
+ }
17
+
18
+ test('renders name', async () => {
19
+ const { locator } = await render(HelloWorld, {
20
+ inputs: {
21
+ name: 'World',
22
+ },
23
+ });
24
+
25
+ await expect.element(locator).toHaveTextContent('Hello, World!');
26
+ });
27
+ ```
28
+
29
+ ## Setup
30
+
31
+ There are currently two ways to set up Vitest for Angular:
32
+
33
+ - Analog's [`vitest-angular` plugin](https://analogjs.org/docs/features/testing/vitest) _(community)_.
34
+ - Angular CLI's [`unit-test` builder](https://angular.dev/guide/testing#configuration) _(official)_.
35
+
36
+ While Angular CLI's `unit-test` builder is the official way to set up Vitest for Angular, it has some [limitations](https://analogjs.org/docs/features/testing/overview#angular-support-for-vitest). Analog's `vitest-angular` plugin provides more Vitest features and greater flexibility.
37
+
38
+ ### Setup with Analog Plugin
39
+
40
+ 1. Set up Vitest
41
+
42
+ ```sh
43
+ npm add -D @analogjs/platform vitest-browser-angular
44
+
45
+ ng g @analogjs/platform:setup-vitest
46
+ ```
47
+
48
+ 2. Activate browser mode in the generated Vitest configuration by following the [browser mode configuration instructions](https://vitest.dev/guide/browser/#configuration).
49
+
50
+ ### Setup with Angular CLI
51
+
52
+ 1. Configure your Angular project to use the `@angular/build:unit-test` builder, and add the browsers of your choice.
53
+
54
+ ```json
55
+ {
56
+ ...,
57
+ "projects": {
58
+ "my-app": {
59
+ ...,
60
+ "architect": {
61
+ "test": {
62
+ "builder": "@angular/build:unit-test",
63
+ "options": {
64
+ "browsers": ["Chromium", "Firefox", "Webkit"]
65
+ }
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ ```
72
+
73
+ _Since Angular v21, Vitest is the default runner so you don't need to set the `runner` option._
74
+
75
+ 2. Install the browser provider of your choice using `ng add`
6
76
 
7
77
  ```sh
8
- pnpm add -D vitest-browser-angular
78
+ # With Playwright
79
+ ng add @vitest/browser-playwright
80
+
81
+ # or with WebdriverIO
82
+ ng add @vitest/browser-webdriverio
9
83
  ```
10
84
 
11
- ## Setup Test Environment
85
+ 3. Add the `vitest-browser-angular` package to your project.
12
86
 
13
- To set up your test environment (with Zone.js or Zoneless), use `@analogjs/vitest-angular`'s `setupTestBed()` function.
87
+ ```sh
88
+ npm add -D vitest-browser-angular
89
+ ```
14
90
 
15
- **Important:** Make sure to use `{ browserMode: true }` when calling `setupTestBed()` to enable Vitest browser mode's visual test preview functionality.
91
+ ## Zone.js VS Zoneless Setup
92
+
93
+ Angular CLI will automatically set up the test environment for you depending on the presence of `zone.js` in your project's polyfills.
94
+
95
+ When using the Analog plugin, you can control the behavior using the `zoneless` option of `setupTestBed()` in `test-setup.ts`:
96
+
97
+ ```ts
98
+ import { setupTestBed } from '@analogjs/vitest-angular/setup-testbed';
99
+
100
+ setupTestBed({
101
+ zoneless: true,
102
+ });
103
+ ```
16
104
 
17
105
  For detailed setup instructions for both Zone.js and Zoneless configurations, please refer to the [Analog Vitest documentation](https://analogjs.org/docs/features/testing/vitest).
18
106
 
107
+ ## Component Preview
108
+
109
+ To preview, debug and interact with a component in the browser after the test, you can prevent Angular from destroying it.
110
+
111
+ In Angular CLI, enable this using the `--debug` option.
112
+
113
+ With the Analog plugin, enable this using the `browserMode` option of `setupTestBed()` in `test-setup.ts`:
114
+
115
+ ```ts
116
+ import { setupTestBed } from '@analogjs/vitest-angular/setup-testbed';
117
+
118
+ setupTestBed({
119
+ browserMode: true,
120
+ });
121
+ ```
122
+
19
123
  ## Usage
20
124
 
125
+ ### Basic Example
126
+
127
+ The `render` function supports two query patterns:
128
+
21
129
  ```ts
22
130
  import { test, expect } from 'vitest';
23
131
  import { render } from 'vitest-browser-angular';
24
132
 
25
133
  @Component({
26
- template: '<h1>{{ title }}</h1>',
134
+ template: ` <h1>Welcome</h1> `,
27
135
  })
28
- export class HelloWorldComponent {
29
- title = 'Hello World';
30
- }
136
+ export class MyComponent {}
137
+
138
+ test('query elements', async () => {
139
+ // Pattern 1: Use locator to query within the component element
140
+ const { locator } = await render(MyComponent);
141
+ await expect.element(locator.getByText('Welcome')).toBeVisible();
142
+
143
+ // Pattern 2: Use screen to query from document.body (useful for portals/overlays)
144
+ const screen = await render(MyComponent);
145
+ await expect.element(screen.getByText('Welcome')).toBeVisible();
146
+ await expect.element(screen.getByText('Some Popover Content')).toBeVisible();
147
+ });
148
+ ```
149
+
150
+ ### Query Methods
151
+
152
+ Both `locator` and `screen` provide the following query methods:
153
+
154
+ - `getByRole` - Locate by ARIA role and accessible name
155
+ - `getByText` - Locate by text content
156
+ - `getByLabelText` - Locate by associated label text
157
+ - `getByPlaceholder` - Locate by placeholder text
158
+ - `getByAltText` - Locate by alt text (images)
159
+ - `getByTitle` - Locate by title attribute
160
+ - `getByTestId` - Locate by data-testid attribute
161
+
162
+ **When to use which pattern:**
163
+
164
+ - **`locator`**: (full name: "Component Locator") - queries are scoped to the component's host element. Best for most component tests.
165
+ - **`screen`**: Queries start from `baseElement` (defaults to `document.body`). Use when testing components that render content outside their host element (modals, tooltips, portals).
31
166
 
32
- test('render', async () => {
33
- const { component } = await render(HelloWorldComponent);
34
- await expect.element(component).toHaveTextContent('Hello World');
167
+ ### Container Element
168
+
169
+ Access the component's host element directly via `container` (shortcut for `fixture.nativeElement`):
170
+
171
+ ```ts
172
+ const { container, locator } = await render(MyComponent);
173
+ expect(container).toBe(locator.element());
174
+ ```
175
+
176
+ ### Base Element
177
+
178
+ Customize the root element for screen queries (useful for portal/overlay testing):
179
+
180
+ ```ts
181
+ const customContainer = document.querySelector('#modal-root');
182
+ const screen = await render(ModalComponent, {
183
+ baseElement: customContainer,
35
184
  });
185
+ // screen queries now start from customContainer instead of document.body
36
186
  ```
37
187
 
38
188
  ## Inputs
@@ -52,15 +202,15 @@ export class ProductComponent {
52
202
  }
53
203
 
54
204
  test('render with inputs', async () => {
55
- const { component } = await render(ProductComponent, {
205
+ const screen = await render(ProductComponent, {
56
206
  inputs: {
57
207
  name: 'Laptop',
58
208
  price: 1299.99,
59
209
  },
60
210
  });
61
211
 
62
- await expect.element(component).toHaveTextContent('Laptop');
63
- await expect.element(component).toHaveTextContent('$1299.99');
212
+ await expect.element(screen.getByText('Laptop')).toBeVisible();
213
+ await expect.element(screen.getByText(/Price: \$1299\.99/)).toBeVisible();
64
214
  });
65
215
  ```
66
216
 
@@ -91,12 +241,12 @@ import { RouterLink, RouterOutlet } from '@angular/router';
91
241
  export class RoutedComponent {}
92
242
 
93
243
  test('render with simple routing', async () => {
94
- const { component } = await render(RoutedComponent, {
244
+ const screen = await render(RoutedComponent, {
95
245
  withRouting: true,
96
246
  });
97
247
 
98
- await expect.element(component).toHaveTextContent('Home');
99
- await expect.element(component).toHaveTextContent('About');
248
+ await expect.element(screen.getByText('Home')).toBeVisible();
249
+ await expect.element(screen.getByText('About')).toBeVisible();
100
250
  });
101
251
  ```
102
252
 
@@ -143,18 +293,128 @@ const routes: Routes = [
143
293
  ];
144
294
 
145
295
  test('render with route configuration', async () => {
146
- const { component, router } = await render(AppComponent, {
296
+ const { locator, routerHarness, router } = await render(AppComponent, {
147
297
  withRouting: {
148
298
  routes,
149
299
  initialRoute: '/home',
150
300
  },
151
301
  });
152
302
 
153
- await expect.element(component).toHaveTextContent('Home Page');
303
+ await expect.element(locator).toHaveTextContent('Home Page');
304
+
305
+ // Navigate programmatically (prefer routerHarness over router)
306
+ await routerHarness.navigateByUrl('/about');
307
+ await expect.element(locator).toHaveTextContent('About Page');
308
+
309
+ // Use router to inspect state
310
+ expect(router.url).toBe('/about');
311
+ });
312
+ ```
313
+
314
+ ### Route Params
315
+
316
+ When rendering a routed component, `componentClassInstance` provides access to the actual component instance with full routing context:
317
+
318
+ ```ts
319
+ import { Component, inject } from '@angular/core';
320
+ import { ActivatedRoute, Routes } from '@angular/router';
321
+
322
+ @Component({
323
+ template: '<h1>User: {{ userId }}</h1>',
324
+ })
325
+ export class UserComponent {
326
+ private route = inject(ActivatedRoute);
327
+ userId = this.route.snapshot.params['id'];
328
+ }
329
+
330
+ test('access route params', async () => {
331
+ const routes: Routes = [{ path: 'user/:id', component: UserComponent }];
332
+
333
+ const { componentClassInstance } = await render(UserComponent, {
334
+ withRouting: {
335
+ routes,
336
+ initialRoute: '/user/42',
337
+ },
338
+ });
154
339
 
155
- // Navigate programmatically
156
- await router.navigate(['/about']);
157
- await expect.element(component).toHaveTextContent('About Page');
340
+ expect(componentClassInstance.userId).toBe('42');
341
+ });
342
+ ```
343
+
344
+ ### Passing Inputs via Route Data
345
+
346
+ By default, `withComponentInputBinding()` is enabled, which automatically binds route `data`, route params, and query params to matching component inputs. This works with both signal inputs (`input()`) and `@Input()` decorators:
347
+
348
+ ```ts
349
+ import { Component, input } from '@angular/core';
350
+ import { Routes } from '@angular/router';
351
+
352
+ @Component({
353
+ template: `
354
+ <h2>{{ name() }}</h2>
355
+ <p>Age: {{ age() }}</p>
356
+ <p>Role: {{ role() }}</p>
357
+ `,
358
+ })
359
+ export class ProfileComponent {
360
+ name = input('Guest');
361
+ age = input(0);
362
+ role = input('user');
363
+ }
364
+
365
+ test('pass inputs via route data', async () => {
366
+ const routes: Routes = [
367
+ {
368
+ path: 'profile',
369
+ component: ProfileComponent,
370
+ data: {
371
+ name: 'Jane Doe',
372
+ age: 30,
373
+ role: 'admin',
374
+ },
375
+ },
376
+ ];
377
+
378
+ const { locator, componentClassInstance } = await render(ProfileComponent, {
379
+ withRouting: {
380
+ routes,
381
+ initialRoute: '/profile',
382
+ },
383
+ });
384
+
385
+ // Inputs are automatically bound from route data
386
+ expect(componentClassInstance.name()).toBe('Jane Doe');
387
+ expect(componentClassInstance.age()).toBe(30);
388
+ expect(componentClassInstance.role()).toBe('admin');
389
+
390
+ await expect.element(locator.getByText('Jane Doe')).toBeVisible();
391
+ });
392
+ ```
393
+
394
+ ### Disabling Input Binding
395
+
396
+ If you need to manually handle route data via `ActivatedRoute` instead of automatic input binding, use `disableInputBinding`:
397
+
398
+ ```ts
399
+ test('disable automatic input binding', async () => {
400
+ const routes: Routes = [
401
+ {
402
+ path: 'profile',
403
+ component: ProfileComponent,
404
+ data: { name: 'Jane Doe' },
405
+ },
406
+ ];
407
+
408
+ const { componentClassInstance } = await render(ProfileComponent, {
409
+ withRouting: {
410
+ routes,
411
+ initialRoute: '/profile',
412
+ disableInputBinding: true, // Inputs will NOT be bound from route data
413
+ },
414
+ });
415
+
416
+ // Inputs retain their default values
417
+ expect(componentClassInstance.name()).toBe('Guest');
158
418
  });
159
419
  ```
160
420
 
@@ -172,11 +432,13 @@ export class HelloWorldComponent {
172
432
  }
173
433
 
174
434
  test('renders component with service provider', async () => {
175
- const { component } = await render(ServiceConsumerComponent, {
435
+ const screen = await render(ServiceConsumerComponent, {
176
436
  componentProviders: [
177
437
  { provide: GreetingService, useClass: FakeGreetingService },
178
438
  ],
179
439
  });
440
+
441
+ await expect.element(screen.getByText('Fake Greeting')).toBeVisible();
180
442
  });
181
443
  ```
182
444
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { i as RenderResult, n as RenderConfig, o as cleanup, r as RenderFn, s as render, t as Inputs } from "./pure-WnJuXVlP.mjs";
1
+ import { a as RenderResult, c as render, i as RenderFn, n as Inputs, r as RenderConfig, s as cleanup } from "./pure-iXV3gO_T.mjs";
2
2
 
3
3
  //#region src/index.d.ts
4
4
  declare module "vitest/browser" {
package/dist/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { n as render, t as cleanup } from "./pure-B-Ur9eBk.mjs";
1
+ import { n as render, t as cleanup } from "./pure-dwpG9XN0.mjs";
2
2
  import { beforeEach } from "vitest";
3
3
  import { page } from "vitest/browser";
4
4
 
@@ -0,0 +1,93 @@
1
+ import { page, utils } from "vitest/browser";
2
+ import { inputBinding } from "@angular/core";
3
+ import { TestBed, ɵgetCleanupHook } from "@angular/core/testing";
4
+ import { Router, provideRouter, withComponentInputBinding } from "@angular/router";
5
+ import { RouterTestingHarness } from "@angular/router/testing";
6
+
7
+ //#region src/pure.ts
8
+ const { debug, getElementLocatorSelectors } = utils;
9
+ /**
10
+ * Renders an Angular component for testing with Vitest Browser Mode.
11
+ *
12
+ * @param componentClass - The component class to render
13
+ * @param options - Configuration options for rendering
14
+ * @returns A promise that resolves to the render result with locators and component access
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * // Basic render
19
+ * const { locator } = await render(MyComponent);
20
+ * await expect.element(locator.getByText('Hello')).toBeVisible();
21
+ *
22
+ * // With inputs
23
+ * const { componentClassInstance } = await render(UserComponent, {
24
+ * inputs: { name: 'John', age: 30 },
25
+ * });
26
+ *
27
+ * // With routing and route data as inputs
28
+ * const { router } = await render(ProfileComponent, {
29
+ * withRouting: {
30
+ * routes: [{ path: 'profile', component: ProfileComponent, data: { userId: '42' } }],
31
+ * initialRoute: '/profile',
32
+ * },
33
+ * });
34
+ * ```
35
+ */
36
+ async function render(componentClass, options) {
37
+ const imports = [componentClass, ...options?.imports || []];
38
+ const providers = [...options?.providers || []];
39
+ const baseElement = options?.baseElement || document.body;
40
+ if (options?.withRouting && options?.inputs) console.warn("[vitest-browser-angular] Using `inputs` with `withRouting` is not supported. Inputs cannot be passed directly to routed components. Consider passing data via route params, query params, or route data instead.");
41
+ const routingConfig = options?.withRouting ? typeof options.withRouting === "boolean" ? {
42
+ routes: [{
43
+ path: "**",
44
+ component: componentClass
45
+ }],
46
+ initialRoute: "/"
47
+ } : options.withRouting : void 0;
48
+ if (routingConfig) if (routingConfig.disableInputBinding) providers.push(provideRouter(routingConfig.routes));
49
+ else providers.push(provideRouter(routingConfig.routes, withComponentInputBinding()));
50
+ TestBed.configureTestingModule({
51
+ imports,
52
+ providers
53
+ });
54
+ if (options?.componentProviders) TestBed.overrideComponent(componentClass, { add: { providers: options.componentProviders } });
55
+ let fixture;
56
+ let container;
57
+ let componentClassInstance;
58
+ let routerHarness;
59
+ let router;
60
+ if (routingConfig) {
61
+ routerHarness = await RouterTestingHarness.create(routingConfig.initialRoute);
62
+ router = TestBed.inject(Router);
63
+ fixture = routerHarness.fixture;
64
+ container = routerHarness.routeNativeElement;
65
+ componentClassInstance = routerHarness.routeDebugElement?.componentInstance;
66
+ } else {
67
+ const bindings = Object.entries(options?.inputs ?? {}).map(([key, value]) => inputBinding(key, () => value));
68
+ fixture = TestBed.createComponent(componentClass, { bindings });
69
+ container = fixture.nativeElement;
70
+ componentClassInstance = fixture.componentInstance;
71
+ }
72
+ fixture.autoDetectChanges();
73
+ await fixture.whenStable();
74
+ const locator = page.elementLocator(container);
75
+ return {
76
+ baseElement,
77
+ container,
78
+ fixture,
79
+ debug: (el = baseElement, maxLength, opts) => debug(el, maxLength, opts),
80
+ componentClassInstance,
81
+ component: locator,
82
+ locator,
83
+ routerHarness,
84
+ router,
85
+ ...getElementLocatorSelectors(baseElement)
86
+ };
87
+ }
88
+ function cleanup(shouldTeardown = false) {
89
+ return ɵgetCleanupHook(shouldTeardown)();
90
+ }
91
+
92
+ //#endregion
93
+ export { render as n, cleanup as t };
@@ -0,0 +1,190 @@
1
+ import { Locator, LocatorSelectors, PrettyDOMOptions } from "vitest/browser";
2
+ import { EnvironmentProviders, InputSignal, Provider, Type } from "@angular/core";
3
+ import { ComponentFixture } from "@angular/core/testing";
4
+ import { Router, Routes } from "@angular/router";
5
+ import { RouterTestingHarness } from "@angular/router/testing";
6
+
7
+ //#region src/pure.d.ts
8
+
9
+ /**
10
+ * Configuration options for rendering components with Angular Router support.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * // Basic routing with route params
15
+ * await render(UserComponent, {
16
+ * withRouting: {
17
+ * routes: [{ path: 'user/:id', component: UserComponent }],
18
+ * initialRoute: '/user/42',
19
+ * },
20
+ * });
21
+ *
22
+ * // Passing inputs via route data (uses withComponentInputBinding)
23
+ * await render(ProfileComponent, {
24
+ * withRouting: {
25
+ * routes: [{
26
+ * path: 'profile',
27
+ * component: ProfileComponent,
28
+ * data: { name: 'John', age: 30 },
29
+ * }],
30
+ * initialRoute: '/profile',
31
+ * },
32
+ * });
33
+ * ```
34
+ */
35
+ interface RoutingConfig {
36
+ /**
37
+ * The route configuration to use. These routes are passed to `provideRouter()`.
38
+ */
39
+ routes: Routes;
40
+ /**
41
+ * The initial route to navigate to after setting up the router.
42
+ * This triggers navigation and activates the matching route's component.
43
+ *
44
+ * @example '/user/42' or '/profile?tab=settings'
45
+ */
46
+ initialRoute?: string;
47
+ /**
48
+ * When `true`, disables Angular's `withComponentInputBinding()` feature.
49
+ *
50
+ * By default, `withComponentInputBinding()` is enabled, which automatically
51
+ * binds route params, query params, and route data to matching component inputs.
52
+ *
53
+ * Set this to `true` if you want to manually handle route data via `ActivatedRoute`.
54
+ *
55
+ * @default false
56
+ */
57
+ disableInputBinding?: boolean;
58
+ }
59
+ type Inputs<CMP_TYPE extends Type<unknown>> = Partial<{ [PROP in keyof InstanceType<CMP_TYPE> as InstanceType<CMP_TYPE>[PROP] extends InputSignal<unknown> ? PROP : never]: InstanceType<CMP_TYPE>[PROP] extends InputSignal<infer VALUE> ? VALUE : never }>;
60
+ /**
61
+ * Options for rendering a component with `render()`.
62
+ */
63
+ interface ComponentRenderOptions<CMP_TYPE extends Type<unknown> = Type<unknown>> {
64
+ /** The base element to render into. Defaults to `document.body`. */
65
+ baseElement?: HTMLElement;
66
+ /**
67
+ * Input values to pass to the component.
68
+ *
69
+ * Note: When using `withRouting`, inputs cannot be passed directly.
70
+ * Use route `data` instead.
71
+ */
72
+ inputs?: Inputs<CMP_TYPE>;
73
+ /**
74
+ * Enable Angular Router support for the component.
75
+ *
76
+ * - When `true`: Creates a wildcard route for the component and navigates to `/`.
77
+ * - When `RoutingConfig`: Uses the provided routes and initial route.
78
+ *
79
+ * By default, `withComponentInputBinding()` is enabled, allowing you to pass
80
+ * inputs via route `data`, route params, or query params.
81
+ *
82
+ * @example
83
+ * ```typescript
84
+ * // Simple routing (component matches any route)
85
+ * await render(MyComponent, { withRouting: true });
86
+ *
87
+ * // Full routing configuration
88
+ * await render(UserComponent, {
89
+ * withRouting: {
90
+ * routes: [
91
+ * { path: 'user/:id', component: UserComponent, data: { role: 'admin' } }
92
+ * ],
93
+ * initialRoute: '/user/42',
94
+ * },
95
+ * });
96
+ * ```
97
+ */
98
+ withRouting?: RoutingConfig | boolean;
99
+ /** Additional providers to configure in the testing module. */
100
+ providers?: Array<Provider | EnvironmentProviders>;
101
+ /** Providers to add specifically to the component being rendered. */
102
+ componentProviders?: Array<Provider>;
103
+ /** Additional imports for the testing module. */
104
+ imports?: unknown[];
105
+ }
106
+ /**
107
+ * @deprecated Use ComponentRenderOptions instead
108
+ */
109
+ type RenderConfig<CMP_TYPE extends Type<unknown> = Type<unknown>> = ComponentRenderOptions<CMP_TYPE>;
110
+ interface RenderResult<T> extends LocatorSelectors {
111
+ baseElement: HTMLElement;
112
+ container: HTMLElement;
113
+ /**
114
+ * The ComponentFixture for the rendered component.
115
+ * When using `withRouting`, this is the RouterTestingHarness's internal fixture
116
+ * not a fixture of `T` directly.
117
+ */
118
+ fixture: ComponentFixture<T> | InstanceType<typeof RouterTestingHarness>["fixture"];
119
+ debug(el?: HTMLElement | HTMLElement[] | Locator | Locator[], maxLength?: number, options?: PrettyDOMOptions): void;
120
+ /**
121
+ * @deprecated Use locator instead
122
+ */
123
+ component: Locator;
124
+ /** Vitest browser locator scoped to the rendered component's container. */
125
+ locator: Locator;
126
+ /** The instance of the rendered component's class. */
127
+ componentClassInstance: T;
128
+ /**
129
+ * The RouterTestingHarness instance. Only available when `withRouting` is used.
130
+ *
131
+ * **Preferred for navigation in tests.** Use `navigateByUrl()` which:
132
+ * - Waits for all redirects to complete
133
+ * - Automatically runs change detection
134
+ * - Returns the activated component instance
135
+ * - Handles guard rejections gracefully
136
+ *
137
+ * @example
138
+ * ```typescript
139
+ * // Navigate and get the activated component
140
+ * const userComponent = await routerHarness.navigateByUrl('/user/42', UserComponent);
141
+ *
142
+ * // Simple navigation
143
+ * await routerHarness.navigateByUrl('/about');
144
+ * ```
145
+ */
146
+ routerHarness?: RouterTestingHarness;
147
+ /**
148
+ * The Angular Router instance. Only available when `withRouting` is used.
149
+ *
150
+ * Useful for inspecting router state. For navigation, prefer `routerHarness.navigateByUrl()`.
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * expect(router.url).toBe('/user/42');
155
+ * ```
156
+ */
157
+ router?: Router;
158
+ }
159
+ type RenderFn = <T>(component: Type<T>, options?: ComponentRenderOptions<Type<T>>) => Promise<RenderResult<T>>;
160
+ /**
161
+ * Renders an Angular component for testing with Vitest Browser Mode.
162
+ *
163
+ * @param componentClass - The component class to render
164
+ * @param options - Configuration options for rendering
165
+ * @returns A promise that resolves to the render result with locators and component access
166
+ *
167
+ * @example
168
+ * ```typescript
169
+ * // Basic render
170
+ * const { locator } = await render(MyComponent);
171
+ * await expect.element(locator.getByText('Hello')).toBeVisible();
172
+ *
173
+ * // With inputs
174
+ * const { componentClassInstance } = await render(UserComponent, {
175
+ * inputs: { name: 'John', age: 30 },
176
+ * });
177
+ *
178
+ * // With routing and route data as inputs
179
+ * const { router } = await render(ProfileComponent, {
180
+ * withRouting: {
181
+ * routes: [{ path: 'profile', component: ProfileComponent, data: { userId: '42' } }],
182
+ * initialRoute: '/profile',
183
+ * },
184
+ * });
185
+ * ```
186
+ */
187
+ declare function render<T>(componentClass: Type<T>, options?: ComponentRenderOptions<Type<T>>): Promise<RenderResult<T>>;
188
+ declare function cleanup(shouldTeardown?: boolean): void;
189
+ //#endregion
190
+ export { RenderResult as a, render as c, RenderFn as i, Inputs as n, RoutingConfig as o, RenderConfig as r, cleanup as s, ComponentRenderOptions as t };
package/dist/pure.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { a as RoutingConfig, i as RenderResult, n as RenderConfig, o as cleanup, r as RenderFn, s as render, t as Inputs } from "./pure-WnJuXVlP.mjs";
2
- export { Inputs, RenderConfig, RenderFn, RenderResult, RoutingConfig, cleanup, render };
1
+ import { a as RenderResult, c as render, i as RenderFn, n as Inputs, o as RoutingConfig, r as RenderConfig, s as cleanup, t as ComponentRenderOptions } from "./pure-iXV3gO_T.mjs";
2
+ export { ComponentRenderOptions, Inputs, RenderConfig, RenderFn, RenderResult, RoutingConfig, cleanup, render };
package/dist/pure.mjs CHANGED
@@ -1,3 +1,3 @@
1
- import { n as render, t as cleanup } from "./pure-B-Ur9eBk.mjs";
1
+ import { n as render, t as cleanup } from "./pure-dwpG9XN0.mjs";
2
2
 
3
3
  export { cleanup, render };