Skip to content

Commit dd0413e

Browse files
committed
upgrade angular 13 - use vitest
1 parent 18bbdda commit dd0413e

File tree

10 files changed

+562
-56
lines changed

10 files changed

+562
-56
lines changed

packages/angular/jest.config.js

Lines changed: 0 additions & 7 deletions
This file was deleted.

packages/angular/ng-package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@
44
"lib": {
55
"entryFile": "src/index.ts"
66
},
7-
"whitelistedNonPeerDependencies": ["@sentry/browser", "@sentry/core", "@sentry/utils", "@sentry/types", "tslib"],
7+
"allowedNonPeerDependencies": ["@sentry/browser", "@sentry/core", "@sentry/utils", "@sentry/types", "tslib"],
88
"assets": ["README.md", "LICENSE"]
99
}

packages/angular/package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"engines": {
1010
"node": ">=14.18"
1111
},
12-
"main": "build/bundles/sentry-angular.umd.js",
12+
"type": "module",
1313
"module": "build/fesm2015/sentry-angular.js",
1414
"publishConfig": {
1515
"access": "public"
@@ -35,9 +35,10 @@
3535
"@angular/platform-browser-dynamic": "~13.4.0",
3636
"@angular/router": "~13.4.0",
3737
"ng-packagr": "^13.3.1",
38-
"rxjs": "6.5.5",
38+
"rxjs": "7.8.1",
3939
"typescript": "4.6.4",
40-
"zone.js": "~0.11.4"
40+
"vitest": "^1.4.0",
41+
"zone.js": "~0.14.4"
4142
},
4243
"scripts": {
4344
"build": "yarn build:transpile",
@@ -52,7 +53,7 @@
5253
"fix": "eslint . --format stylish --fix",
5354
"lint": "eslint . --format stylish",
5455
"test": "yarn test:unit",
55-
"test:unit": "jest",
56+
"test:unit": "vitest",
5657
"test2": "yarn node --experimental-vm-modules $(yarn bin jest)",
5758
"test:unit:watch": "jest --watch",
5859
"yalc:publish": "yalc publish build --push --sig"

packages/angular/patch-vitest.ts

Lines changed: 298 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,298 @@
1+
/**
2+
* We are using this snippet from '@analogjs/vite-plugin-angular' to make vitest work with Angular.
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2022 Brandon Roberts
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in all
16+
* copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24+
* SOFTWARE.
25+
*/
26+
import 'zone.js';
27+
import 'zone.js/plugins/proxy';
28+
import 'zone.js/plugins/sync-test';
29+
import 'zone.js/testing';
30+
31+
/**
32+
* Patch Vitest's describe/test/beforeEach/afterEach functions so test code
33+
* always runs in a testZone (ProxyZone).
34+
*/
35+
/* global Zone */
36+
const Zone = (globalThis as any)['Zone'];
37+
38+
if (Zone === undefined) {
39+
throw new Error('Missing: Zone (zone.js)');
40+
}
41+
42+
if ((globalThis as any)['__vitest_zone_patch__'] === true) {
43+
throw new Error("'vitest' has already been patched with 'Zone'.");
44+
}
45+
46+
(globalThis as any)['__vitest_zone_patch__'] = true;
47+
const SyncTestZoneSpec = Zone['SyncTestZoneSpec'];
48+
const ProxyZoneSpec = Zone['ProxyZoneSpec'];
49+
50+
if (SyncTestZoneSpec === undefined) {
51+
throw new Error('Missing: SyncTestZoneSpec (zone.js/plugins/sync-test)');
52+
}
53+
if (ProxyZoneSpec === undefined) {
54+
throw new Error('Missing: ProxyZoneSpec (zone.js/plugins/proxy.js)');
55+
}
56+
57+
const env = globalThis as any;
58+
const ambientZone = Zone.current;
59+
60+
// Create a synchronous-only zone in which to run `describe` blocks in order to
61+
// raise an error if any asynchronous operations are attempted
62+
// inside of a `describe` but outside of a `beforeEach` or `it`.
63+
const syncZone = ambientZone.fork(new SyncTestZoneSpec('vitest.describe'));
64+
function wrapDescribeInZone(describeBody: any) {
65+
return function (...args: any) {
66+
return syncZone.run(describeBody, null, args);
67+
};
68+
}
69+
70+
// Create a proxy zone in which to run `test` blocks so that the tests function
71+
// can retroactively install different zones.
72+
const testProxyZone = ambientZone.fork(new ProxyZoneSpec());
73+
function wrapTestInZone(testBody: string | any[] | undefined) {
74+
if (testBody === undefined) {
75+
return;
76+
}
77+
78+
const wrappedFunc = function () {
79+
return testProxyZone.run(testBody, null, arguments);
80+
};
81+
try {
82+
Object.defineProperty(wrappedFunc, 'length', {
83+
configurable: true,
84+
writable: true,
85+
enumerable: false,
86+
});
87+
wrappedFunc.length = testBody.length;
88+
} catch (e) {
89+
return testBody.length === 0
90+
? () => testProxyZone.run(testBody, null)
91+
: (done: any) => testProxyZone.run(testBody, null, [done]);
92+
}
93+
94+
return wrappedFunc;
95+
}
96+
97+
/**
98+
* Allows Vitest to handle Angular test fixtures
99+
*
100+
* Vitest Snapshot guide ==> https://vitest.dev/guide/snapshot.html
101+
*
102+
* @returns customSnapshotSerializer for Angular Fixture Component
103+
*/
104+
const customSnapshotSerializer = () => {
105+
function serialize(val: any, config: any, indentation: any, depth: any, refs: any, printer: any): string {
106+
// `printer` is a function that serializes a value using existing plugins.
107+
return `${printer(fixtureVitestSerializer(val), config, indentation, depth, refs)}`;
108+
}
109+
function test(val: any): boolean {
110+
// * If it's a ComponentFixture we apply the transformation rules
111+
return val && isAngularFixture(val);
112+
}
113+
return {
114+
serialize,
115+
test,
116+
};
117+
};
118+
119+
/**
120+
* Check if is an Angular fixture
121+
*
122+
* @param val Angular fixture
123+
* @returns boolean who check if is an angular fixture
124+
*/
125+
function isAngularFixture(val: any): boolean {
126+
if (typeof val !== 'object') {
127+
return false;
128+
}
129+
130+
if (val['componentRef'] || val['componentInstance']) {
131+
return true;
132+
}
133+
134+
if (val['componentType']) {
135+
return true;
136+
}
137+
138+
// * Angular fixture keys in Fixture component Object
139+
const fixtureKeys = [
140+
'componentRef',
141+
'ngZone',
142+
'effectRunner',
143+
'_autoDetect',
144+
'_isStable',
145+
'_isDestroyed',
146+
'_resolve',
147+
'_promise',
148+
'_onUnstableSubscription',
149+
'_onStableSubscription',
150+
'_onMicrotaskEmptySubscription',
151+
'_onErrorSubscription',
152+
'changeDetectorRef',
153+
'elementRef',
154+
'debugElement',
155+
'componentInstance',
156+
'nativeElement',
157+
];
158+
159+
// * Angular fixture keys in Fixture componentRef Object
160+
const fixtureComponentRefKeys = [
161+
'location',
162+
'_rootLView',
163+
'_tNode',
164+
'previousInputValues',
165+
'instance',
166+
'changeDetectorRef',
167+
'hostView',
168+
'componentType',
169+
];
170+
171+
return (
172+
JSON.stringify(Object.keys(val)) === JSON.stringify(fixtureKeys) ||
173+
JSON.stringify(Object.keys(val)) === JSON.stringify(fixtureComponentRefKeys)
174+
);
175+
}
176+
177+
/**
178+
* Serialize Angular fixture for Vitest
179+
*
180+
* @param fixture Angular Fixture Component
181+
* @returns HTML Child Node
182+
*/
183+
function fixtureVitestSerializer(fixture: any) {
184+
// * Get Component meta data
185+
const componentType = (
186+
fixture && fixture.componentType ? fixture.componentType : fixture.componentRef.componentType
187+
) as any;
188+
189+
let inputsData: string = '';
190+
191+
const selector = Reflect.getOwnPropertyDescriptor(componentType, '__annotations__')?.value[0].selector;
192+
193+
if (componentType && componentType.propDecorators) {
194+
inputsData = Object.entries(componentType.propDecorators)
195+
.map(([key, value]) => `${key}="${value}"`)
196+
.join('');
197+
}
198+
199+
// * Get DOM Elements
200+
const divElement = fixture && fixture.nativeElement ? fixture.nativeElement : fixture.location.nativeElement;
201+
202+
// * Convert string data to HTML data
203+
const doc = new DOMParser().parseFromString(
204+
`<${selector} ${inputsData}>${divElement.innerHTML}</${selector}>`,
205+
'text/html',
206+
);
207+
208+
return doc.body.childNodes[0];
209+
}
210+
211+
/**
212+
* bind describe method to wrap describe.each function
213+
*/
214+
const bindDescribe = (originalVitestFn: {
215+
apply: (
216+
arg0: any,
217+
arg1: any[],
218+
) => {
219+
(): any;
220+
new (): any;
221+
apply: { (arg0: any, arg1: any[]): any; new (): any };
222+
};
223+
}) =>
224+
function (...eachArgs: any) {
225+
return function (...args: any[]) {
226+
args[1] = wrapDescribeInZone(args[1]);
227+
228+
// @ts-ignore
229+
return originalVitestFn.apply(this, eachArgs).apply(this, args);
230+
};
231+
};
232+
233+
/**
234+
* bind test method to wrap test.each function
235+
*/
236+
const bindTest = (originalVitestFn: {
237+
apply: (
238+
arg0: any,
239+
arg1: any[],
240+
) => {
241+
(): any;
242+
new (): any;
243+
apply: { (arg0: any, arg1: any[]): any; new (): any };
244+
};
245+
}) =>
246+
function (...eachArgs: any) {
247+
return function (...args: any[]) {
248+
args[1] = wrapTestInZone(args[1]);
249+
250+
// @ts-ignore
251+
return originalVitestFn.apply(this, eachArgs).apply(this, args);
252+
};
253+
};
254+
255+
['describe'].forEach(methodName => {
256+
const originalvitestFn = env[methodName];
257+
env[methodName] = function (...args: any[]) {
258+
args[1] = wrapDescribeInZone(args[1]);
259+
260+
return originalvitestFn.apply(this, args);
261+
};
262+
env[methodName].each = bindDescribe(originalvitestFn.each);
263+
if (methodName === 'describe') {
264+
env[methodName].only = env['fdescribe'];
265+
env[methodName].skip = env['xdescribe'];
266+
}
267+
});
268+
269+
['test', 'it'].forEach(methodName => {
270+
const originalvitestFn = env[methodName];
271+
env[methodName] = function (...args: any[]) {
272+
args[1] = wrapTestInZone(args[1]);
273+
274+
return originalvitestFn.apply(this, args);
275+
};
276+
env[methodName].each = bindTest(originalvitestFn.each);
277+
278+
if (methodName === 'test' || methodName === 'it') {
279+
env[methodName].todo = function (...args: any) {
280+
return originalvitestFn.todo.apply(this, args);
281+
};
282+
}
283+
});
284+
285+
['beforeEach', 'afterEach', 'beforeAll', 'afterAll'].forEach(methodName => {
286+
const originalvitestFn = env[methodName];
287+
288+
env[methodName] = function (...args: any[]) {
289+
args[0] = wrapTestInZone(args[0]);
290+
291+
return originalvitestFn.apply(this, args);
292+
};
293+
});
294+
295+
['expect'].forEach(methodName => {
296+
const originalvitestFn = env[methodName];
297+
return originalvitestFn.addSnapshotSerializer(customSnapshotSerializer());
298+
});

packages/angular/setup-jest.ts renamed to packages/angular/setup-test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
import 'zone.js';
2-
import 'zone.js/testing';
1+
import './patch-vitest';
32

43
import { getTestBed } from '@angular/core/testing';
54
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';

0 commit comments

Comments
 (0)