Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 629f5cb

Browse filesBrowse files
committed
feat(@schematics/angular): add misc transformations to jasmine-to-vitest schematic
This commit adds transformers for miscellaneous Jasmine APIs and handles unsupported features. Coverage includes: - Timer mocks (jasmine.clock) - The fail() function - jasmine.DEFAULT_TIMEOUT_INTERVAL It also includes the logic to identify and add TODO comments for any unsupported Jasmine APIs, ensuring a safe migration.
1 parent d6830d2 commit 629f5cb
Copy full SHA for 629f5cb

File tree

Expand file treeCollapse file tree

3 files changed

+501
-1
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

3 files changed

+501
-1
lines changed
Open diff view settings
Collapse file

‎packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.ts‎

Copy file name to clipboardExpand all lines: packages/schematics/angular/refactor/jasmine-vitest/test-file-transformer.ts
+21-1Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,14 @@ import {
2424
transformWithContext,
2525
transformtoHaveBeenCalledBefore,
2626
} from './transformers/jasmine-matcher';
27+
import {
28+
transformDefaultTimeoutInterval,
29+
transformFail,
30+
transformGlobalFunctions,
31+
transformTimerMocks,
32+
transformUnknownJasmineProperties,
33+
transformUnsupportedJasmineCalls,
34+
} from './transformers/jasmine-misc';
2735
import {
2836
transformCreateSpyObj,
2937
transformSpies,
@@ -80,13 +88,23 @@ export function transformJasmineToVitest(
8088
transformDoneCallback,
8189
transformtoHaveBeenCalledBefore,
8290
transformToHaveClass,
91+
transformTimerMocks,
92+
transformFocusedAndSkippedTests,
93+
transformPending,
94+
transformDoneCallback,
95+
transformGlobalFunctions,
96+
transformUnsupportedJasmineCalls,
8397
];
8498

8599
for (const transformer of transformations) {
86100
transformedNode = transformer(transformedNode, refactorCtx);
87101
}
88102
} else if (ts.isPropertyAccessExpression(transformedNode)) {
89-
const transformations = [transformAsymmetricMatchers, transformSpyCallInspection];
103+
const transformations = [
104+
transformAsymmetricMatchers,
105+
transformSpyCallInspection,
106+
transformUnknownJasmineProperties,
107+
];
90108
for (const transformer of transformations) {
91109
transformedNode = transformer(transformedNode, refactorCtx);
92110
}
@@ -95,6 +113,8 @@ export function transformJasmineToVitest(
95113
transformCalledOnceWith,
96114
transformArrayWithExactContents,
97115
transformExpectNothing,
116+
transformFail,
117+
transformDefaultTimeoutInterval,
98118
];
99119

100120
for (const transformer of statementTransformers) {
Collapse file
+250Lines changed: 250 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,250 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.dev/license
7+
*/
8+
9+
/**
10+
* @fileoverview This file contains transformers for miscellaneous Jasmine APIs that don't
11+
* fit into other categories. This includes timer mocks (`jasmine.clock`), the `fail()`
12+
* function, and configuration settings like `jasmine.DEFAULT_TIMEOUT_INTERVAL`. It also
13+
* includes logic to identify and add TODO comments for unsupported Jasmine features.
14+
*/
15+
16+
import ts from '../../../third_party/github.com/Microsoft/TypeScript/lib/typescript';
17+
import { createViCallExpression } from '../utils/ast-helpers';
18+
import { getJasmineMethodName, isJasmineCallExpression } from '../utils/ast-validation';
19+
import { addTodoComment } from '../utils/comment-helpers';
20+
import { RefactorContext } from '../utils/refactor-context';
21+
22+
export function transformTimerMocks(
23+
node: ts.Node,
24+
{ sourceFile, reporter }: RefactorContext,
25+
): ts.Node {
26+
if (
27+
!ts.isCallExpression(node) ||
28+
!ts.isPropertyAccessExpression(node.expression) ||
29+
!ts.isIdentifier(node.expression.name)
30+
) {
31+
return node;
32+
}
33+
34+
const pae = node.expression;
35+
const clockCall = pae.expression;
36+
if (!isJasmineCallExpression(clockCall, 'clock')) {
37+
return node;
38+
}
39+
40+
let newMethodName: string | undefined;
41+
switch (pae.name.text) {
42+
case 'install':
43+
newMethodName = 'useFakeTimers';
44+
break;
45+
case 'tick':
46+
newMethodName = 'advanceTimersByTime';
47+
break;
48+
case 'uninstall':
49+
newMethodName = 'useRealTimers';
50+
break;
51+
case 'mockDate':
52+
newMethodName = 'setSystemTime';
53+
break;
54+
}
55+
56+
if (newMethodName) {
57+
reporter.reportTransformation(
58+
sourceFile,
59+
node,
60+
`Transformed \`jasmine.clock().${pae.name.text}\` to \`vi.${newMethodName}\`.`,
61+
);
62+
const newArgs = newMethodName === 'useFakeTimers' ? [] : node.arguments;
63+
64+
return createViCallExpression(newMethodName, newArgs);
65+
}
66+
67+
return node;
68+
}
69+
70+
export function transformFail(node: ts.Node, { sourceFile, reporter }: RefactorContext): ts.Node {
71+
if (
72+
ts.isExpressionStatement(node) &&
73+
ts.isCallExpression(node.expression) &&
74+
ts.isIdentifier(node.expression.expression) &&
75+
node.expression.expression.text === 'fail'
76+
) {
77+
reporter.reportTransformation(sourceFile, node, 'Transformed `fail()` to `throw new Error()`.');
78+
const reason = node.expression.arguments[0];
79+
80+
return ts.factory.createThrowStatement(
81+
ts.factory.createNewExpression(
82+
ts.factory.createIdentifier('Error'),
83+
undefined,
84+
reason ? [reason] : [],
85+
),
86+
);
87+
}
88+
89+
return node;
90+
}
91+
92+
export function transformDefaultTimeoutInterval(
93+
node: ts.Node,
94+
{ sourceFile, reporter }: RefactorContext,
95+
): ts.Node {
96+
if (
97+
ts.isExpressionStatement(node) &&
98+
ts.isBinaryExpression(node.expression) &&
99+
node.expression.operatorToken.kind === ts.SyntaxKind.EqualsToken
100+
) {
101+
const assignment = node.expression;
102+
if (
103+
ts.isPropertyAccessExpression(assignment.left) &&
104+
ts.isIdentifier(assignment.left.expression) &&
105+
assignment.left.expression.text === 'jasmine' &&
106+
assignment.left.name.text === 'DEFAULT_TIMEOUT_INTERVAL'
107+
) {
108+
reporter.reportTransformation(
109+
sourceFile,
110+
node,
111+
'Transformed `jasmine.DEFAULT_TIMEOUT_INTERVAL` to `vi.setConfig()`.',
112+
);
113+
const timeoutValue = assignment.right;
114+
const setConfigCall = createViCallExpression('setConfig', [
115+
ts.factory.createObjectLiteralExpression(
116+
[ts.factory.createPropertyAssignment('testTimeout', timeoutValue)],
117+
false,
118+
),
119+
]);
120+
121+
return ts.factory.createExpressionStatement(setConfigCall);
122+
}
123+
}
124+
125+
return node;
126+
}
127+
128+
export function transformGlobalFunctions(
129+
node: ts.Node,
130+
{ sourceFile, reporter }: RefactorContext,
131+
): ts.Node {
132+
if (
133+
ts.isCallExpression(node) &&
134+
ts.isIdentifier(node.expression) &&
135+
(node.expression.text === 'setSpecProperty' || node.expression.text === 'setSuiteProperty')
136+
) {
137+
const functionName = node.expression.text;
138+
reporter.reportTransformation(
139+
sourceFile,
140+
node,
141+
`Found unsupported global function \`${functionName}\`.`,
142+
);
143+
reporter.recordTodo(functionName);
144+
addTodoComment(
145+
node,
146+
`Unsupported global function \`${functionName}\` found. This function is used for custom reporters in Jasmine ` +
147+
'and has no direct equivalent in Vitest.',
148+
);
149+
}
150+
151+
return node;
152+
}
153+
154+
const JASMINE_UNSUPPORTED_CALLS = new Map<string, string>([
155+
[
156+
'addMatchers',
157+
'jasmine.addMatchers is not supported. Please manually migrate to expect.extend().',
158+
],
159+
[
160+
'addCustomEqualityTester',
161+
'jasmine.addCustomEqualityTester is not supported. Please manually migrate to expect.addEqualityTesters().',
162+
],
163+
[
164+
'mapContaining',
165+
'jasmine.mapContaining is not supported. Vitest does not have a built-in matcher for Maps.' +
166+
' Please manually assert the contents of the Map.',
167+
],
168+
[
169+
'setContaining',
170+
'jasmine.setContaining is not supported. Vitest does not have a built-in matcher for Sets.' +
171+
' Please manually assert the contents of the Set.',
172+
],
173+
]);
174+
175+
export function transformUnsupportedJasmineCalls(
176+
node: ts.Node,
177+
{ sourceFile, reporter }: RefactorContext,
178+
): ts.Node {
179+
const methodName = getJasmineMethodName(node);
180+
if (!methodName) {
181+
return node;
182+
}
183+
184+
const message = JASMINE_UNSUPPORTED_CALLS.get(methodName);
185+
if (message) {
186+
reporter.reportTransformation(
187+
sourceFile,
188+
node,
189+
`Found unsupported call \`jasmine.${methodName}\`.`,
190+
);
191+
reporter.recordTodo(methodName);
192+
addTodoComment(node, message);
193+
}
194+
195+
return node;
196+
}
197+
198+
// If any additional properties are added to transforms, they should also be added to this list.
199+
const HANDLED_JASMINE_PROPERTIES = new Set([
200+
// Spies
201+
'createSpy',
202+
'createSpyObj',
203+
'spyOnAllFunctions',
204+
// Clock
205+
'clock',
206+
// Matchers
207+
'any',
208+
'anything',
209+
'stringMatching',
210+
'objectContaining',
211+
'arrayContaining',
212+
'arrayWithExactContents',
213+
'truthy',
214+
'falsy',
215+
'empty',
216+
'notEmpty',
217+
'mapContaining',
218+
'setContaining',
219+
// Other
220+
'DEFAULT_TIMEOUT_INTERVAL',
221+
'addMatchers',
222+
'addCustomEqualityTester',
223+
]);
224+
225+
export function transformUnknownJasmineProperties(
226+
node: ts.Node,
227+
{ sourceFile, reporter }: RefactorContext,
228+
): ts.Node {
229+
if (
230+
ts.isPropertyAccessExpression(node) &&
231+
ts.isIdentifier(node.expression) &&
232+
node.expression.text === 'jasmine'
233+
) {
234+
const propName = node.name.text;
235+
if (!HANDLED_JASMINE_PROPERTIES.has(propName)) {
236+
reporter.reportTransformation(
237+
sourceFile,
238+
node,
239+
`Found unknown jasmine property \`jasmine.${propName}\`.`,
240+
);
241+
reporter.recordTodo(`unknown-jasmine-property: ${propName}`);
242+
addTodoComment(
243+
node,
244+
`Unsupported jasmine property "${propName}" found. Please migrate this manually.`,
245+
);
246+
}
247+
}
248+
249+
return node;
250+
}

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.