Skip to content

Commit a2a1c19

Browse files
clydinalan-agius4
authored andcommitted
refactor(@schematics/angular): flatten transformSpyCallInspection logic
Refactors the `transformSpyCallInspection` function in the Jasmine to Vitest schematic to improve readability and reduce nesting. The previous implementation used a deeply nested `if` condition to handle various spy call inspection patterns. This change inverts the primary `if` condition into a guard clause, allowing the function to exit early if the node does not match the expected type. This flattens the overall structure of the function, making the main logic more prominent and easier to follow. (cherry picked from commit 7e724d7)
1 parent fcce806 commit a2a1c19

File tree

1 file changed

+107
-87
lines changed
  • packages/schematics/angular/refactor/jasmine-vitest/transformers

1 file changed

+107
-87
lines changed

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

Lines changed: 107 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -338,104 +338,124 @@ function createMockedSpyMockProperty(spyIdentifier: ts.Expression): ts.PropertyA
338338
return createPropertyAccess(mockedSpy, 'mock');
339339
}
340340

341-
export function transformSpyCallInspection(
341+
function transformMostRecentArgs(
342342
node: ts.Node,
343343
{ sourceFile, reporter }: RefactorContext,
344344
): ts.Node {
345-
// mySpy.calls.mostRecent().args -> vi.mocked(mySpy).mock.lastCall
345+
// Check 1: Is it a property access for `.args`?
346346
if (
347-
ts.isPropertyAccessExpression(node) &&
348-
ts.isIdentifier(node.name) &&
349-
node.name.text === 'args'
347+
!ts.isPropertyAccessExpression(node) ||
348+
!ts.isIdentifier(node.name) ||
349+
node.name.text !== 'args'
350350
) {
351-
const mostRecentCall = node.expression;
352-
if (
353-
ts.isCallExpression(mostRecentCall) &&
354-
ts.isPropertyAccessExpression(mostRecentCall.expression)
355-
) {
356-
const mostRecentPae = mostRecentCall.expression; // mySpy.calls.mostRecent
357-
if (
358-
ts.isIdentifier(mostRecentPae.name) &&
359-
mostRecentPae.name.text === 'mostRecent' &&
360-
ts.isPropertyAccessExpression(mostRecentPae.expression)
361-
) {
362-
const spyIdentifier = getSpyIdentifierFromCalls(mostRecentPae.expression);
363-
if (spyIdentifier) {
364-
reporter.reportTransformation(
365-
sourceFile,
366-
node,
367-
'Transformed `spy.calls.mostRecent().args` to `vi.mocked(spy).mock.lastCall`.',
368-
);
369-
const mockProperty = createMockedSpyMockProperty(spyIdentifier);
351+
return node;
352+
}
370353

371-
return createPropertyAccess(mockProperty, 'lastCall');
372-
}
373-
}
374-
}
354+
// Check 2: Is the preceding expression a call expression?
355+
const mostRecentCall = node.expression;
356+
if (
357+
!ts.isCallExpression(mostRecentCall) ||
358+
!ts.isPropertyAccessExpression(mostRecentCall.expression)
359+
) {
360+
return node;
375361
}
376362

377-
if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
378-
const pae = node.expression; // e.g., mySpy.calls.count
379-
const spyIdentifier = ts.isPropertyAccessExpression(pae.expression)
380-
? getSpyIdentifierFromCalls(pae.expression)
381-
: undefined;
382-
383-
if (spyIdentifier) {
384-
const mockProperty = createMockedSpyMockProperty(spyIdentifier);
385-
const callsProperty = createPropertyAccess(mockProperty, 'calls');
386-
387-
const callName = pae.name.text;
388-
let newExpression: ts.Node | undefined;
389-
let message: string | undefined;
390-
391-
switch (callName) {
392-
case 'any':
393-
message = 'Transformed `spy.calls.any()` to a check on `mock.calls.length`.';
394-
newExpression = ts.factory.createBinaryExpression(
395-
createPropertyAccess(callsProperty, 'length'),
396-
ts.SyntaxKind.GreaterThanToken,
397-
ts.factory.createNumericLiteral(0),
398-
);
399-
break;
400-
case 'count':
401-
message = 'Transformed `spy.calls.count()` to `mock.calls.length`.';
402-
newExpression = createPropertyAccess(callsProperty, 'length');
403-
break;
404-
case 'first':
405-
message = 'Transformed `spy.calls.first()` to `mock.calls[0]`.';
406-
newExpression = ts.factory.createElementAccessExpression(callsProperty, 0);
407-
break;
408-
case 'all':
409-
case 'allArgs':
410-
message = `Transformed \`spy.calls.${callName}()\` to \`mock.calls\`.`;
411-
newExpression = callsProperty;
412-
break;
413-
case 'argsFor':
414-
message = 'Transformed `spy.calls.argsFor()` to `mock.calls[i]`.';
415-
newExpression = ts.factory.createElementAccessExpression(
416-
callsProperty,
417-
node.arguments[0],
418-
);
419-
break;
420-
case 'mostRecent':
421-
if (
422-
!ts.isPropertyAccessExpression(node.parent) ||
423-
!ts.isIdentifier(node.parent.name) ||
424-
node.parent.name.text !== 'args'
425-
) {
426-
const category = 'mostRecent-without-args';
427-
reporter.recordTodo(category);
428-
addTodoComment(node, category);
429-
}
363+
// Check 3: Is it a call to `.mostRecent`?
364+
const mostRecentPae = mostRecentCall.expression;
365+
if (
366+
!ts.isIdentifier(mostRecentPae.name) ||
367+
mostRecentPae.name.text !== 'mostRecent' ||
368+
!ts.isPropertyAccessExpression(mostRecentPae.expression)
369+
) {
370+
return node;
371+
}
430372

431-
return node;
432-
}
373+
// Check 4: Can we get the spy identifier from `spy.calls`?
374+
const spyIdentifier = getSpyIdentifierFromCalls(mostRecentPae.expression);
375+
if (!spyIdentifier) {
376+
return node;
377+
}
433378

434-
if (newExpression && message) {
435-
reporter.reportTransformation(sourceFile, node, message);
379+
// If all checks pass, perform the transformation.
380+
reporter.reportTransformation(
381+
sourceFile,
382+
node,
383+
'Transformed `spy.calls.mostRecent().args` to `vi.mocked(spy).mock.lastCall`.',
384+
);
385+
const mockProperty = createMockedSpyMockProperty(spyIdentifier);
436386

437-
return newExpression;
438-
}
387+
return createPropertyAccess(mockProperty, 'lastCall');
388+
}
389+
390+
export function transformSpyCallInspection(node: ts.Node, refactorCtx: RefactorContext): ts.Node {
391+
const mostRecentArgsTransformed = transformMostRecentArgs(node, refactorCtx);
392+
if (mostRecentArgsTransformed !== node) {
393+
return mostRecentArgsTransformed;
394+
}
395+
396+
if (!ts.isCallExpression(node) || !ts.isPropertyAccessExpression(node.expression)) {
397+
return node;
398+
}
399+
400+
const { sourceFile, reporter } = refactorCtx;
401+
402+
const pae = node.expression; // e.g., mySpy.calls.count
403+
const spyIdentifier = ts.isPropertyAccessExpression(pae.expression)
404+
? getSpyIdentifierFromCalls(pae.expression)
405+
: undefined;
406+
407+
if (spyIdentifier) {
408+
const mockProperty = createMockedSpyMockProperty(spyIdentifier);
409+
const callsProperty = createPropertyAccess(mockProperty, 'calls');
410+
411+
const callName = pae.name.text;
412+
let newExpression: ts.Node | undefined;
413+
let message: string | undefined;
414+
415+
switch (callName) {
416+
case 'any':
417+
message = 'Transformed `spy.calls.any()` to a check on `mock.calls.length`.';
418+
newExpression = ts.factory.createBinaryExpression(
419+
createPropertyAccess(callsProperty, 'length'),
420+
ts.SyntaxKind.GreaterThanToken,
421+
ts.factory.createNumericLiteral(0),
422+
);
423+
break;
424+
case 'count':
425+
message = 'Transformed `spy.calls.count()` to `mock.calls.length`.';
426+
newExpression = createPropertyAccess(callsProperty, 'length');
427+
break;
428+
case 'first':
429+
message = 'Transformed `spy.calls.first()` to `mock.calls[0]`.';
430+
newExpression = ts.factory.createElementAccessExpression(callsProperty, 0);
431+
break;
432+
case 'all':
433+
case 'allArgs':
434+
message = `Transformed \`spy.calls.${callName}()\` to \`mock.calls\`.`;
435+
newExpression = callsProperty;
436+
break;
437+
case 'argsFor':
438+
message = 'Transformed `spy.calls.argsFor()` to `mock.calls[i]`.';
439+
newExpression = ts.factory.createElementAccessExpression(callsProperty, node.arguments[0]);
440+
break;
441+
case 'mostRecent':
442+
if (
443+
!ts.isPropertyAccessExpression(node.parent) ||
444+
!ts.isIdentifier(node.parent.name) ||
445+
node.parent.name.text !== 'args'
446+
) {
447+
const category = 'mostRecent-without-args';
448+
reporter.recordTodo(category);
449+
addTodoComment(node, category);
450+
}
451+
452+
return node;
453+
}
454+
455+
if (newExpression && message) {
456+
reporter.reportTransformation(sourceFile, node, message);
457+
458+
return newExpression;
439459
}
440460
}
441461

0 commit comments

Comments
 (0)