@@ -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