Skip to content

Commit 2d37e0a

Browse files
committed
feat(@schematics/angular): stabilize refactor-jasmine-vitest schematic
Stabilize `refactor-jasmine-vitest` schematic by covering the known remaining test patterns and cases.
1 parent 5591b72 commit 2d37e0a

File tree

11 files changed

+569
-130
lines changed

11 files changed

+569
-130
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -288,8 +288,8 @@ describe('Jasmine to Vitest Transformer - Integration Tests', () => {
288288
});
289289
290290
it('should handle spy call order', () => {
291-
const spyA = vi.fn();
292-
const spyB = vi.fn();
291+
const spyA = vi.fn().mockName('spyA');
292+
const spyB = vi.fn().mockName('spyB');
293293
spyA();
294294
spyB();
295295
expect(Math.min(...vi.mocked(spyA).mock.invocationCallOrder)).toBeLessThan(Math.min(...vi.mocked(spyB).mock.invocationCallOrder));
@@ -387,7 +387,7 @@ describe('Jasmine to Vitest Transformer - Integration Tests', () => {
387387
});
388388
389389
it('should handle spies throwing errors', () => {
390-
const spy = vi.fn().mockImplementation(() => { throw new Error('Test Error') });
390+
const spy = vi.fn().mockName('mySpy').mockImplementation(() => { throw new Error('Test Error') });
391391
expect(() => spy()).toThrowError('Test Error');
392392
});
393393
});

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

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,19 +27,21 @@ import {
2727
transformExpectAsync,
2828
transformExpectNothing,
2929
transformSyntacticSugarMatchers,
30+
transformToBeNullish,
31+
transformToHaveBeenCalledBefore,
3032
transformToHaveClass,
3133
transformWithContext,
32-
transformtoHaveBeenCalledBefore,
3334
} from './transformers/jasmine-matcher';
3435
import {
35-
transformDefaultTimeoutInterval,
3636
transformFail,
37-
transformGlobalFunctions,
37+
transformJasmineMembers,
3838
transformTimerMocks,
3939
transformUnknownJasmineProperties,
40+
transformUnsupportedGlobalFunctions,
4041
transformUnsupportedJasmineCalls,
4142
} from './transformers/jasmine-misc';
4243
import {
44+
transformCreateSpy,
4345
transformCreateSpyObj,
4446
transformSpies,
4547
transformSpyCallInspection,
@@ -116,16 +118,18 @@ const callExpressionTransformers = [
116118
transformSyntacticSugarMatchers,
117119
transformComplexMatchers,
118120
transformSpies,
121+
transformCreateSpy,
119122
transformCreateSpyObj,
120123
transformSpyReset,
121124
transformSpyCallInspection,
122-
transformtoHaveBeenCalledBefore,
125+
transformToHaveBeenCalledBefore,
123126
transformToHaveClass,
127+
transformToBeNullish,
124128

125129
// **Stage 3: Global Functions & Cleanup**
126130
// These handle global Jasmine functions and catch-alls for unsupported APIs.
127131
transformTimerMocks,
128-
transformGlobalFunctions,
132+
transformUnsupportedGlobalFunctions,
129133
transformUnsupportedJasmineCalls,
130134
];
131135

@@ -149,7 +153,7 @@ const expressionStatementTransformers = [
149153
transformArrayWithExactContents,
150154
transformExpectNothing,
151155
transformFail,
152-
transformDefaultTimeoutInterval,
156+
transformJasmineMembers,
153157
];
154158

155159
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ describe('Jasmine to Vitest Transformer - Nested Transformations', () => {
2828
await expectAsync(service.myProp).toBeResolvedTo(42);
2929
`,
3030
expected: `
31-
vi.spyOn(service, 'myProp', 'get').mockReturnValue(Promise.resolve(42));
31+
vi.spyOn(service, 'myProp', 'get').mockResolvedValue(42);
3232
await expect(service.myProp).resolves.toEqual(42);
3333
`,
3434
},

packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher.ts

Lines changed: 72 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ export function transformAsymmetricMatchers(
121121
return node;
122122
}
123123

124-
export function transformtoHaveBeenCalledBefore(
124+
export function transformToHaveBeenCalledBefore(
125125
node: ts.Node,
126126
{ sourceFile, reporter }: RefactorContext,
127127
): ts.Node {
@@ -205,7 +205,7 @@ export function transformToHaveClass(
205205
expectExpression = expectExpression.expression;
206206
}
207207

208-
if (matcherName !== 'toHaveClass') {
208+
if (matcherName !== 'toHaveClass' || !ts.isCallExpression(expectExpression)) {
209209
return node;
210210
}
211211

@@ -218,21 +218,17 @@ export function transformToHaveClass(
218218
const [className] = node.arguments;
219219
const newExpectArgs: ts.Expression[] = [];
220220

221-
if (ts.isCallExpression(expectExpression)) {
222-
const [element] = expectExpression.arguments;
223-
const classListContains = ts.factory.createCallExpression(
224-
createPropertyAccess(createPropertyAccess(element, 'classList'), 'contains'),
225-
undefined,
226-
[className],
227-
);
228-
newExpectArgs.push(classListContains);
221+
const [element] = expectExpression.arguments;
222+
const classListContains = ts.factory.createCallExpression(
223+
createPropertyAccess(createPropertyAccess(element, 'classList'), 'contains'),
224+
undefined,
225+
[className],
226+
);
227+
newExpectArgs.push(classListContains);
229228

230-
// Pass the context message from withContext to the new expect call
231-
if (expectExpression.arguments.length > 1) {
232-
newExpectArgs.push(expectExpression.arguments[1]);
233-
}
234-
} else {
235-
return node;
229+
// Pass the context message from withContext to the new expect call
230+
if (expectExpression.arguments.length > 1) {
231+
newExpectArgs.push(expectExpression.arguments[1]);
236232
}
237233

238234
const newExpect = createExpectCallExpression(newExpectArgs);
@@ -626,3 +622,63 @@ export function transformExpectNothing(
626622

627623
return replacement;
628624
}
625+
626+
export function transformToBeNullish(
627+
node: ts.Node,
628+
{ sourceFile, reporter }: RefactorContext,
629+
): ts.Node {
630+
if (
631+
!ts.isCallExpression(node) ||
632+
!ts.isPropertyAccessExpression(node.expression) ||
633+
node.arguments.length !== 0
634+
) {
635+
return node;
636+
}
637+
638+
const pae = node.expression;
639+
const matcherName = pae.name.text;
640+
let isNegated = false;
641+
642+
let expectExpression = pae.expression;
643+
if (ts.isPropertyAccessExpression(expectExpression) && expectExpression.name.text === 'not') {
644+
isNegated = true;
645+
expectExpression = expectExpression.expression;
646+
}
647+
648+
if (matcherName !== 'toBeNullish' || !ts.isCallExpression(expectExpression)) {
649+
return node;
650+
}
651+
652+
reporter.reportTransformation(
653+
sourceFile,
654+
node,
655+
'Transformed `.toBeNullish()` to a `element === null || element === undefined` check.',
656+
);
657+
658+
const element = expectExpression.arguments[0];
659+
660+
const nullCheck = ts.factory.createBinaryExpression(
661+
element,
662+
ts.SyntaxKind.EqualsEqualsEqualsToken,
663+
ts.factory.createNull(),
664+
);
665+
666+
const undefinedCheck = ts.factory.createBinaryExpression(
667+
element,
668+
ts.SyntaxKind.EqualsEqualsEqualsToken,
669+
ts.factory.createIdentifier('undefined'),
670+
);
671+
672+
const fullExpression = ts.factory.createBinaryExpression(
673+
nullCheck,
674+
ts.SyntaxKind.BarBarToken,
675+
undefinedCheck,
676+
);
677+
678+
const newExpect = createExpectCallExpression([fullExpression]);
679+
const newMatcher = isNegated ? ts.factory.createFalse() : ts.factory.createTrue();
680+
681+
return ts.factory.createCallExpression(createPropertyAccess(newExpect, 'toBe'), undefined, [
682+
newMatcher,
683+
]);
684+
}

packages/schematics/angular/refactor/jasmine-vitest/transformers/jasmine-matcher_spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -356,3 +356,24 @@ describe('transformToHaveClass', () => {
356356
});
357357
});
358358
});
359+
360+
describe('transformToBeNullish', () => {
361+
const testCases = [
362+
{
363+
description: 'should transform toBeNullish',
364+
input: `expect(element).toBeNullish();`,
365+
expected: `expect(element === null || element === undefined).toBe(true);`,
366+
},
367+
{
368+
description: 'should transform not.toBeNullish',
369+
input: `expect(element).not.toBeNullish();`,
370+
expected: `expect(element === null || element === undefined).toBe(false);`,
371+
},
372+
];
373+
374+
testCases.forEach(({ description, input, expected }) => {
375+
it(description, async () => {
376+
await expectTransformation(input, expected);
377+
});
378+
});
379+
});

0 commit comments

Comments
 (0)