Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,19 @@ function isChainedWithAnd(node: ts.Node): boolean {
return false;
}

function isSpyCreation(node: ts.Expression): boolean {
const expression = unwrapExpression(node);
if (!ts.isCallExpression(expression)) {
return false;
}

if (ts.isIdentifier(expression.expression)) {
return expression.expression.text === 'spyOn' || expression.expression.text === 'spyOnProperty';
}

return isJasmineCallExpression(expression, 'createSpy');
}

function transformPrimarySpy(node: ts.CallExpression, refactorCtx: RefactorContext): ts.Node {
const { sourceFile, reporter, pendingVitestValueImports } = refactorCtx;
if (
Expand Down Expand Up @@ -224,6 +237,16 @@ function transformSpyStrategy(node: ts.CallExpression, refactorCtx: RefactorCont
newMethodName = 'mockImplementation';
break;
case 'callThrough':
if (!isSpyCreation(spyCall)) {
// On an existing spy, `.and.callThrough()` restores the original implementation,
// so it is not redundant and cannot be dropped.
const category = 'callThrough-on-existing-spy';
reporter.recordTodo(category, sourceFile, node);
addTodoComment(node, category);

return node;
}

reporter.reportTransformation(
sourceFile,
node,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,23 @@ describe('Jasmine to Vitest Transformer - transformSpies', () => {
input: `spyOn(service, 'myMethod').and.callThrough();`,
expected: `vi.spyOn(service, 'myMethod');`,
},
{
description: 'should remove .and.callThrough() on a wrapped spyOn call',
input: `(spyOn(service, 'myMethod')!).and.callThrough();`,
expected: `(vi.spyOn(service, 'myMethod')!);`,
},
{
description: 'should add a TODO for .and.callThrough() on an existing spy',
input: `mySpy.and.callThrough();`,
expected: `// TODO: vitest-migration: Vitest does not have a direct equivalent for .and.callThrough() on an existing spy. Please migrate this manually, for example with spy.mockReset(), which restores the original implementation but also clears the recorded calls. See: https://vitest.dev/api/mock.html#mockreset
mySpy.and.callThrough();`,
},
Comment thread
thekhegay marked this conversation as resolved.
Resolved
{
description: 'should add a TODO for .and.callThrough() on a spy returned by a call',
input: `getSpy().and.callThrough();`,
expected: `// TODO: vitest-migration: Vitest does not have a direct equivalent for .and.callThrough() on an existing spy. Please migrate this manually, for example with spy.mockReset(), which restores the original implementation but also clears the recorded calls. See: https://vitest.dev/api/mock.html#mockreset
getSpy().and.callThrough();`,
},
{
description: 'should transform jasmine.createSpy("name") to vi.fn().mockName("name")',
input: `const mySpy = jasmine.createSpy('mySpy');`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,13 @@ export const TODO_NOTES = {
`Unsupported spy strategy ".and.${context.name}()" found. Please migrate this manually.`,
url: 'https://vitest.dev/api/mocked.html#mock',
},
'callThrough-on-existing-spy': {
message:
'Vitest does not have a direct equivalent for .and.callThrough() on an existing spy.' +
' Please migrate this manually, for example with spy.mockReset(), which restores' +
' the original implementation but also clears the recorded calls.',
url: 'https://vitest.dev/api/mock.html#mockreset',
},
'mostRecent-without-args': {
message:
'Direct usage of mostRecent() is not supported.' +
Expand Down
Loading