vitest-browser-angular 0.2.0 → 0.3.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 +65 -17
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +1 -1
- package/dist/pure-CSDeMQ9B.mjs +50 -0
- package/dist/pure-DIxLzF0p.d.mts +43 -0
- package/dist/pure.d.mts +2 -2
- package/dist/pure.mjs +1 -1
- package/package.json +1 -1
- package/dist/pure-B-Ur9eBk.mjs +0 -42
- package/dist/pure-WnJuXVlP.d.mts +0 -31
package/README.md
CHANGED
|
@@ -18,21 +18,67 @@ For detailed setup instructions for both Zone.js and Zoneless configurations, pl
|
|
|
18
18
|
|
|
19
19
|
## Usage
|
|
20
20
|
|
|
21
|
+
### Basic Example
|
|
22
|
+
|
|
23
|
+
The `render` function supports two query patterns:
|
|
24
|
+
|
|
21
25
|
```ts
|
|
22
26
|
import { test, expect } from 'vitest';
|
|
23
27
|
import { render } from 'vitest-browser-angular';
|
|
24
28
|
|
|
25
29
|
@Component({
|
|
26
|
-
template:
|
|
30
|
+
template: ` <h1>Welcome</h1> `,
|
|
27
31
|
})
|
|
28
|
-
export class
|
|
29
|
-
|
|
30
|
-
|
|
32
|
+
export class MyComponent {}
|
|
33
|
+
|
|
34
|
+
test('query elements', async () => {
|
|
35
|
+
// Pattern 1: Use locator to query within the component element
|
|
36
|
+
const { locator } = await render(MyComponent);
|
|
37
|
+
await expect.element(locator.getByText('Welcome')).toBeVisible();
|
|
38
|
+
|
|
39
|
+
// Pattern 2: Use screen to query from document.body (useful for portals/overlays)
|
|
40
|
+
const screen = await render(MyComponent);
|
|
41
|
+
await expect.element(screen.getByText('Welcome')).toBeVisible();
|
|
42
|
+
await expect.element(screen.getByText('Some Popover Content')).toBeVisible();
|
|
43
|
+
});
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### Query Methods
|
|
47
|
+
|
|
48
|
+
Both `locator` and `screen` provide the following query methods:
|
|
31
49
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
50
|
+
- `getByRole` - Locate by ARIA role and accessible name
|
|
51
|
+
- `getByText` - Locate by text content
|
|
52
|
+
- `getByLabelText` - Locate by associated label text
|
|
53
|
+
- `getByPlaceholder` - Locate by placeholder text
|
|
54
|
+
- `getByAltText` - Locate by alt text (images)
|
|
55
|
+
- `getByTitle` - Locate by title attribute
|
|
56
|
+
- `getByTestId` - Locate by data-testid attribute
|
|
57
|
+
|
|
58
|
+
**When to use which pattern:**
|
|
59
|
+
|
|
60
|
+
- **`locator`**: (full name: "Component Locator") - queries are scoped to the component's host element. Best for most component tests.
|
|
61
|
+
- **`screen`**: Queries start from `baseElement` (defaults to `document.body`). Use when testing components that render content outside their host element (modals, tooltips, portals).
|
|
62
|
+
|
|
63
|
+
### Container Element
|
|
64
|
+
|
|
65
|
+
Access the component's host element directly via `container` (shortcut for `fixture.nativeElement`):
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const { container, locator } = await render(MyComponent);
|
|
69
|
+
expect(container).toBe(locator.element());
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Base Element
|
|
73
|
+
|
|
74
|
+
Customize the root element for screen queries (useful for portal/overlay testing):
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
const customContainer = document.querySelector('#modal-root');
|
|
78
|
+
const screen = await render(ModalComponent, {
|
|
79
|
+
baseElement: customContainer,
|
|
35
80
|
});
|
|
81
|
+
// screen queries now start from customContainer instead of document.body
|
|
36
82
|
```
|
|
37
83
|
|
|
38
84
|
## Inputs
|
|
@@ -52,15 +98,15 @@ export class ProductComponent {
|
|
|
52
98
|
}
|
|
53
99
|
|
|
54
100
|
test('render with inputs', async () => {
|
|
55
|
-
const
|
|
101
|
+
const screen = await render(ProductComponent, {
|
|
56
102
|
inputs: {
|
|
57
103
|
name: 'Laptop',
|
|
58
104
|
price: 1299.99,
|
|
59
105
|
},
|
|
60
106
|
});
|
|
61
107
|
|
|
62
|
-
await expect.element(
|
|
63
|
-
await expect.element(
|
|
108
|
+
await expect.element(screen.getByText('Laptop')).toBeVisible();
|
|
109
|
+
await expect.element(screen.getByText(/Price: \$1299\.99/)).toBeVisible();
|
|
64
110
|
});
|
|
65
111
|
```
|
|
66
112
|
|
|
@@ -91,12 +137,12 @@ import { RouterLink, RouterOutlet } from '@angular/router';
|
|
|
91
137
|
export class RoutedComponent {}
|
|
92
138
|
|
|
93
139
|
test('render with simple routing', async () => {
|
|
94
|
-
const
|
|
140
|
+
const screen = await render(RoutedComponent, {
|
|
95
141
|
withRouting: true,
|
|
96
142
|
});
|
|
97
143
|
|
|
98
|
-
await expect.element(
|
|
99
|
-
await expect.element(
|
|
144
|
+
await expect.element(screen.getByText('Home')).toBeVisible();
|
|
145
|
+
await expect.element(screen.getByText('About')).toBeVisible();
|
|
100
146
|
});
|
|
101
147
|
```
|
|
102
148
|
|
|
@@ -143,18 +189,18 @@ const routes: Routes = [
|
|
|
143
189
|
];
|
|
144
190
|
|
|
145
191
|
test('render with route configuration', async () => {
|
|
146
|
-
const {
|
|
192
|
+
const { locator, router } = await render(AppComponent, {
|
|
147
193
|
withRouting: {
|
|
148
194
|
routes,
|
|
149
195
|
initialRoute: '/home',
|
|
150
196
|
},
|
|
151
197
|
});
|
|
152
198
|
|
|
153
|
-
await expect.element(
|
|
199
|
+
await expect.element(locator).toHaveTextContent('Home Page');
|
|
154
200
|
|
|
155
201
|
// Navigate programmatically
|
|
156
202
|
await router.navigate(['/about']);
|
|
157
|
-
await expect.element(
|
|
203
|
+
await expect.element(locator).toHaveTextContent('About Page');
|
|
158
204
|
});
|
|
159
205
|
```
|
|
160
206
|
|
|
@@ -172,11 +218,13 @@ export class HelloWorldComponent {
|
|
|
172
218
|
}
|
|
173
219
|
|
|
174
220
|
test('renders component with service provider', async () => {
|
|
175
|
-
const
|
|
221
|
+
const screen = await render(ServiceConsumerComponent, {
|
|
176
222
|
componentProviders: [
|
|
177
223
|
{ provide: GreetingService, useClass: FakeGreetingService },
|
|
178
224
|
],
|
|
179
225
|
});
|
|
226
|
+
|
|
227
|
+
await expect.element(screen.getByText('Fake Greeting')).toBeVisible();
|
|
180
228
|
});
|
|
181
229
|
```
|
|
182
230
|
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as RenderResult, c as render, i as RenderFn, n as Inputs, r as RenderConfig, s as cleanup } from "./pure-DIxLzF0p.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/index.d.ts
|
|
4
4
|
declare module "vitest/browser" {
|
package/dist/index.mjs
CHANGED
|
@@ -0,0 +1,50 @@
|
|
|
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 } from "@angular/router";
|
|
5
|
+
import { RouterTestingHarness } from "@angular/router/testing";
|
|
6
|
+
|
|
7
|
+
//#region src/pure.ts
|
|
8
|
+
const { debug, getElementLocatorSelectors } = utils;
|
|
9
|
+
async function render(componentClass, options) {
|
|
10
|
+
const imports = [componentClass, ...options?.imports || []];
|
|
11
|
+
const providers = [...options?.providers || []];
|
|
12
|
+
const renderResult = {};
|
|
13
|
+
const baseElement = options?.baseElement || document.body;
|
|
14
|
+
if (options?.withRouting) {
|
|
15
|
+
const routes = typeof options.withRouting === "boolean" ? [] : options.withRouting.routes;
|
|
16
|
+
providers.push(provideRouter(routes));
|
|
17
|
+
}
|
|
18
|
+
TestBed.configureTestingModule({
|
|
19
|
+
imports,
|
|
20
|
+
providers
|
|
21
|
+
});
|
|
22
|
+
if (options?.componentProviders) TestBed.overrideComponent(componentClass, { add: { providers: options.componentProviders } });
|
|
23
|
+
if (options?.withRouting) {
|
|
24
|
+
renderResult.routerHarness = await RouterTestingHarness.create(typeof options.withRouting === "boolean" ? void 0 : options.withRouting.initialRoute);
|
|
25
|
+
renderResult.router = TestBed.inject(Router);
|
|
26
|
+
}
|
|
27
|
+
const bindings = Object.entries(options?.inputs ?? {}).map(([key, value]) => inputBinding(key, () => value));
|
|
28
|
+
const fixture = TestBed.createComponent(componentClass, { bindings });
|
|
29
|
+
fixture.autoDetectChanges();
|
|
30
|
+
await fixture.whenStable();
|
|
31
|
+
const container = fixture.nativeElement;
|
|
32
|
+
const locator = page.elementLocator(container);
|
|
33
|
+
return {
|
|
34
|
+
...renderResult,
|
|
35
|
+
baseElement,
|
|
36
|
+
container,
|
|
37
|
+
fixture,
|
|
38
|
+
debug: (el = baseElement, maxLength, options$1) => debug(el, maxLength, options$1),
|
|
39
|
+
componentClassInstance: fixture.componentInstance,
|
|
40
|
+
component: locator,
|
|
41
|
+
locator,
|
|
42
|
+
...getElementLocatorSelectors(baseElement)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
function cleanup(shouldTeardown = false) {
|
|
46
|
+
return ɵgetCleanupHook(shouldTeardown)();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
//#endregion
|
|
50
|
+
export { render as n, cleanup as t };
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
interface RoutingConfig {
|
|
9
|
+
routes: Routes;
|
|
10
|
+
initialRoute?: string;
|
|
11
|
+
}
|
|
12
|
+
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 }>;
|
|
13
|
+
interface ComponentRenderOptions<CMP_TYPE extends Type<unknown> = Type<unknown>> {
|
|
14
|
+
baseElement?: HTMLElement;
|
|
15
|
+
inputs?: Inputs<CMP_TYPE>;
|
|
16
|
+
withRouting?: RoutingConfig | boolean;
|
|
17
|
+
providers?: Array<Provider | EnvironmentProviders>;
|
|
18
|
+
componentProviders?: Array<Provider>;
|
|
19
|
+
imports?: unknown[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* @deprecated Use ComponentRenderOptions instead
|
|
23
|
+
*/
|
|
24
|
+
type RenderConfig<CMP_TYPE extends Type<unknown> = Type<unknown>> = ComponentRenderOptions<CMP_TYPE>;
|
|
25
|
+
interface RenderResult<T> extends LocatorSelectors {
|
|
26
|
+
baseElement: HTMLElement;
|
|
27
|
+
container: HTMLElement;
|
|
28
|
+
fixture: ComponentFixture<T>;
|
|
29
|
+
debug(el?: HTMLElement | HTMLElement[] | Locator | Locator[], maxLength?: number, options?: PrettyDOMOptions): void;
|
|
30
|
+
/**
|
|
31
|
+
* @deprecated Use locator instead
|
|
32
|
+
*/
|
|
33
|
+
component: Locator;
|
|
34
|
+
locator: Locator;
|
|
35
|
+
componentClassInstance: T;
|
|
36
|
+
routerHarness?: RouterTestingHarness;
|
|
37
|
+
router?: Router;
|
|
38
|
+
}
|
|
39
|
+
type RenderFn = <T>(component: Type<T>, options?: ComponentRenderOptions<Type<T>>) => Promise<RenderResult<T>>;
|
|
40
|
+
declare function render<T>(componentClass: Type<T>, options?: ComponentRenderOptions<Type<T>>): Promise<RenderResult<T>>;
|
|
41
|
+
declare function cleanup(shouldTeardown?: boolean): void;
|
|
42
|
+
//#endregion
|
|
43
|
+
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
|
|
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-DIxLzF0p.mjs";
|
|
2
|
+
export { ComponentRenderOptions, Inputs, RenderConfig, RenderFn, RenderResult, RoutingConfig, cleanup, render };
|
package/dist/pure.mjs
CHANGED
package/package.json
CHANGED
package/dist/pure-B-Ur9eBk.mjs
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { page } from "vitest/browser";
|
|
2
|
-
import { inputBinding } from "@angular/core";
|
|
3
|
-
import { TestBed, ɵgetCleanupHook } from "@angular/core/testing";
|
|
4
|
-
import { Router, provideRouter } from "@angular/router";
|
|
5
|
-
import { RouterTestingHarness } from "@angular/router/testing";
|
|
6
|
-
|
|
7
|
-
//#region src/pure.ts
|
|
8
|
-
async function render(componentClass, config) {
|
|
9
|
-
const imports = [componentClass, ...config?.imports || []];
|
|
10
|
-
const providers = [...config?.providers || []];
|
|
11
|
-
const renderResult = {};
|
|
12
|
-
if (config?.withRouting) {
|
|
13
|
-
const routes = typeof config.withRouting === "boolean" ? [] : config.withRouting.routes;
|
|
14
|
-
providers.push(provideRouter(routes));
|
|
15
|
-
}
|
|
16
|
-
TestBed.configureTestingModule({
|
|
17
|
-
imports,
|
|
18
|
-
providers
|
|
19
|
-
});
|
|
20
|
-
if (config?.componentProviders) TestBed.overrideComponent(componentClass, { add: { providers: config.componentProviders } });
|
|
21
|
-
if (config?.withRouting) {
|
|
22
|
-
renderResult.routerHarness = await RouterTestingHarness.create(typeof config.withRouting === "boolean" ? void 0 : config.withRouting.initialRoute);
|
|
23
|
-
renderResult.router = TestBed.inject(Router);
|
|
24
|
-
}
|
|
25
|
-
const bindings = Object.entries(config?.inputs ?? {}).map(([key, value]) => inputBinding(key, () => value));
|
|
26
|
-
const fixture = TestBed.createComponent(componentClass, { bindings });
|
|
27
|
-
fixture.autoDetectChanges();
|
|
28
|
-
await fixture.whenStable();
|
|
29
|
-
const component = page.elementLocator(fixture.nativeElement);
|
|
30
|
-
return {
|
|
31
|
-
...renderResult,
|
|
32
|
-
fixture,
|
|
33
|
-
componentClassInstance: fixture.componentInstance,
|
|
34
|
-
component
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
function cleanup(shouldTeardown = false) {
|
|
38
|
-
return ɵgetCleanupHook(shouldTeardown)();
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
//#endregion
|
|
42
|
-
export { render as n, cleanup as t };
|
package/dist/pure-WnJuXVlP.d.mts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { Locator } 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
|
-
interface RoutingConfig {
|
|
9
|
-
routes: Routes;
|
|
10
|
-
initialRoute?: string;
|
|
11
|
-
}
|
|
12
|
-
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 }>;
|
|
13
|
-
interface RenderConfig<CMP_TYPE extends Type<unknown> = Type<unknown>> {
|
|
14
|
-
inputs?: Inputs<CMP_TYPE>;
|
|
15
|
-
withRouting?: RoutingConfig | boolean;
|
|
16
|
-
providers?: Array<Provider | EnvironmentProviders>;
|
|
17
|
-
componentProviders?: Array<Provider>;
|
|
18
|
-
imports?: unknown[];
|
|
19
|
-
}
|
|
20
|
-
interface RenderResult<T> {
|
|
21
|
-
fixture: ComponentFixture<T>;
|
|
22
|
-
component: Locator;
|
|
23
|
-
componentClassInstance: T;
|
|
24
|
-
routerHarness?: RouterTestingHarness;
|
|
25
|
-
router?: Router;
|
|
26
|
-
}
|
|
27
|
-
type RenderFn = <T>(component: Type<T>, config?: RenderConfig<Type<T>>) => Promise<RenderResult<T>>;
|
|
28
|
-
declare function render<T>(componentClass: Type<T>, config?: RenderConfig<Type<T>>): Promise<RenderResult<T>>;
|
|
29
|
-
declare function cleanup(shouldTeardown?: boolean): void;
|
|
30
|
-
//#endregion
|
|
31
|
-
export { RoutingConfig as a, RenderResult as i, RenderConfig as n, cleanup as o, RenderFn as r, render as s, Inputs as t };
|