Skip to content

Commit 16df6c3

Browse files
committed
feat: add vitest-utils
1 parent 60dcb9e commit 16df6c3

File tree

6 files changed

+137
-0
lines changed

6 files changed

+137
-0
lines changed
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import { Component, inject } from '@angular/core';
2+
import { TestBed } from '@angular/core/testing';
3+
import { test, expect, vi } from 'vitest';
4+
import { fireEvent, render, screen } from '../../public_api';
5+
import { createMock, provideMock, provideMockWithValues } from '../../../vitest-utils';
6+
7+
class FixtureService {
8+
constructor(private foo: string, public bar: string) {}
9+
10+
print() {
11+
console.log(this.foo, this.bar);
12+
}
13+
14+
concat() {
15+
return this.foo + this.bar;
16+
}
17+
}
18+
19+
@Component({
20+
selector: 'atl-fixture',
21+
template: ` <button (click)="print()">Print</button> `,
22+
})
23+
class FixtureComponent {
24+
private service = inject(FixtureService);
25+
26+
print() {
27+
this.service.print();
28+
}
29+
}
30+
31+
test('mocks all functions', () => {
32+
const mock = createMock(FixtureService);
33+
expect(mock.print.mock).toBeDefined();
34+
});
35+
36+
test('provides a mock service', async () => {
37+
await render(FixtureComponent, {
38+
providers: [provideMock(FixtureService)],
39+
});
40+
const service = TestBed.inject(FixtureService);
41+
42+
fireEvent.click(screen.getByText('Print'));
43+
expect(service.print).toHaveBeenCalledTimes(1);
44+
});
45+
46+
test('provides a mock service with values', async () => {
47+
await render(FixtureComponent, {
48+
providers: [
49+
provideMockWithValues(FixtureService, {
50+
bar: 'value',
51+
concat: vi.fn(() => 'a concatenated value'),
52+
}),
53+
],
54+
});
55+
56+
const service = TestBed.inject(FixtureService);
57+
58+
fireEvent.click(screen.getByText('Print'));
59+
60+
expect(service.bar).toEqual('value');
61+
expect(service.concat()).toEqual('a concatenated value');
62+
expect(service.print).toHaveBeenCalled();
63+
});
64+
65+
test('is possible to write a mock implementation', async () => {
66+
await render(FixtureComponent, {
67+
providers: [provideMock(FixtureService)],
68+
});
69+
70+
const service = TestBed.inject(FixtureService);
71+
72+
fireEvent.click(screen.getByText('Print'));
73+
expect(service.print).toHaveBeenCalled();
74+
});
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './src/public_api';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
import { Type, Provider } from '@angular/core';
2+
import { vi, type Mock as VitestMock } from 'vitest';
3+
4+
export type Mock<T> = T & { [K in keyof T]: T[K] & VitestMock };
5+
6+
export function createMock<T>(type: Type<T>): Mock<T> {
7+
const mock: any = {};
8+
9+
function mockFunctions(proto: any) {
10+
if (!proto) {
11+
return;
12+
}
13+
14+
for (const prop of Object.getOwnPropertyNames(proto)) {
15+
if (prop === 'constructor') {
16+
continue;
17+
}
18+
19+
const descriptor = Object.getOwnPropertyDescriptor(proto, prop);
20+
if (typeof descriptor?.value === 'function') {
21+
mock[prop] = vi.fn();
22+
}
23+
}
24+
25+
mockFunctions(Object.getPrototypeOf(proto));
26+
}
27+
28+
mockFunctions(type.prototype);
29+
30+
return mock;
31+
}
32+
33+
export function createMockWithValues<T, K extends keyof T>(type: Type<T>, values: Partial<Record<K, T[K]>>): Mock<T> {
34+
const mock = createMock(type);
35+
36+
Object.entries(values).forEach(([field, value]) => {
37+
(mock as any)[field] = value;
38+
});
39+
40+
return mock;
41+
}
42+
43+
export function provideMock<T>(type: Type<T>): Provider {
44+
return {
45+
provide: type,
46+
useValue: createMock(type),
47+
};
48+
}
49+
50+
export function provideMockWithValues<T, K extends keyof T>(type: Type<T>, values: Partial<Record<K, T[K]>>): Provider {
51+
return {
52+
provide: type,
53+
useValue: createMockWithValues(type, values),
54+
};
55+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './create-mock';
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
/*
2+
* Public API Surface of testing-library
3+
*/
4+
5+
export * from './lib';

0 commit comments

Comments
 (0)