Skip to content

Commit

Permalink
Merge branch 'main' into sinon-jest-types
Browse files Browse the repository at this point in the history
  • Loading branch information
skovhus authored Aug 23, 2022
2 parents 3d699fc + b4ca35b commit 6a2712b
Show file tree
Hide file tree
Showing 2 changed files with 183 additions and 9 deletions.
104 changes: 96 additions & 8 deletions src/transformers/sinon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,32 +180,39 @@ describe('spies and stubs', () => {
`,
`
jest.fn().mockImplementation((...args) => {
if (args[0] === 'foo')
if (args[0] === 'foo') {
return 'something';
}
})
jest.fn().mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 'bar')
if (args[0] === 'foo' && args[1] === 'bar') {
return 'something';
}
})
jest.fn().mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 'bar' && args[2] === 1)
if (args[0] === 'foo' && args[1] === 'bar' && args[2] === 1) {
return 'something';
}
})
jest.spyOn(Api, 'get').mockClear().mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 'bar' && args[2] === 1)
if (args[0] === 'foo' && args[1] === 'bar' && args[2] === 1) {
return 'something';
}
})
const stub = jest.spyOn(foo, 'bar').mockClear().mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 1)
if (args[0] === 'foo' && args[1] === 1) {
return 'something';
}
})
jest.spyOn(foo, 'bar').mockClear().mockImplementation((...args) => {
if (args[0] === 'foo' && typeof args[1] === 'object')
if (args[0] === 'foo' && typeof args[1] === 'object') {
return 'something';
}
})
jest.fn().mockImplementation((...args) => {
if (args[0] === 'foo' && args.length >= 2)
if (args[0] === 'foo' && args.length >= 2) {
return 'something';
}
})
`
)
Expand All @@ -220,8 +227,9 @@ describe('spies and stubs', () => {
`,
`
jest.fn().mockImplementation((...args: any[]) => {
if (args[0] === 'foo')
if (args[0] === 'foo') {
return 'something';
}
})
`,
{ parser: 'tsx' }
Expand Down Expand Up @@ -356,6 +364,86 @@ describe('spies and stubs', () => {
{ parser: 'ts' }
)
})

it('handles on*Call', () => {
expectTransformation(
`
import sinon from 'sinon-sandbox'
stub.onFirstCall().returns(5)
stub.onSecondCall().returns(6, 7)
stub.onFirstCall().returnsArg(0)
stub.onSecondCall().returnsArg(1)
// invalid onCall() params
stub.onCall().returns('invalid')
`,
`
stub.mockImplementation(() => {
if (stub.mock.calls.length === 0) {
return 5;
}
})
stub.mockImplementation(() => {
if (stub.mock.calls.length === 1) {
return 6;
}
})
stub.mockImplementation((...args) => {
if (stub.mock.calls.length === 0) {
return args[0];
}
})
stub.mockImplementation((...args) => {
if (stub.mock.calls.length === 1) {
return args[1];
}
})
// invalid onCall() params
stub.onCall().mockReturnValue('invalid')
`
)
})
it('handles on*Call (parser: ts)', () => {
expectTransformation(
`
import sinon from 'sinon-sandbox'
stub.onThirdCall().returns([8, 9, 10])
stub.onCall(3).returns(biscuits)
stub.onThirdCall().returnsArg(2)
stub.onCall(3).returnsArg(biscuits)
`,
`
stub.mockImplementation(() => {
if (stub.mock.calls.length === 2) {
return [8, 9, 10];
}
})
stub.mockImplementation(() => {
if (stub.mock.calls.length === 3) {
return biscuits;
}
})
stub.mockImplementation((...args: any[]) => {
if (stub.mock.calls.length === 2) {
return args[2];
}
})
stub.mockImplementation((...args: any[]) => {
if (stub.mock.calls.length === 3) {
return args[biscuits];
}
})
`,
{ parser: 'tsx' }
)
})
})

describe('mocks', () => {
Expand Down
88 changes: 87 additions & 1 deletion src/transformers/sinon.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ const SINON_MATCHERS_WITH_ARGS = {
string: 'string',
}
const SINON_NTH_CALLS = new Set(['firstCall', 'secondCall', 'thirdCall', 'lastCall'])
const SINON_ON_NTH_CALLS = new Set(['onFirstCall', 'onSecondCall', 'onThirdCall'])
const EXPECT_PREFIXES = new Set(['to'])
const isPrefix = (name) => EXPECT_PREFIXES.has(name)

Expand Down Expand Up @@ -371,6 +372,90 @@ function transformStub(j, ast, sinonExpression, logWarning) {
})
}

/*
transform .onCall(0), .on{First,Second,Third}Call()
stub.onCall(4).return(biscuits) -> stub.mockImplementation(() => { if (stub.mock.calls.length === 3) return biscuits; })
stub.onFirstCall().returnArg(2) -> stub.mockImplementation((...args: any[]) => { if (stub.mock.calls.length === 0) return args[2]; })
*/
function transformStubOnCalls(j, ast, parser) {
ast
.find(j.CallExpression, {
callee: {
object: {
callee: {
property: {
name: (n) => n === 'onCall' || SINON_ON_NTH_CALLS.has(n),
},
},
},
property: {
name: (n) => ['returns', 'returnsArg'].includes(n),
},
},
})
.replaceWith(({ node }) => {
let index
switch (node.callee.object.callee.property.name) {
case 'onCall':
index = node.callee.object.arguments[0]
break
case 'onFirstCall':
index = j.numericLiteral(0)
break
case 'onSecondCall':
index = j.numericLiteral(1)
break
case 'onThirdCall':
index = j.numericLiteral(2)
break
}
if (!index) return node

// `jest.spyOn` or `jest.fn`
const mockFn = node.callee.object.callee.object
const callLengthConditionalExpression = j.binaryExpression(
'===',
j.memberExpression(mockFn, j.identifier('mock.calls.length')),
index
)

const isReturns = node.callee.property.name === 'returns'
const isTypescript = parser === 'ts' || parser === 'tsx'

const mockImplementationArgs = isReturns
? []
: [
j.spreadPropertyPattern(
j.identifier.from({
name: 'args',
typeAnnotation: isTypescript
? j.typeAnnotation(j.arrayTypeAnnotation(j.anyTypeAnnotation()))
: null,
})
),
]
const mockImplementationReturn = isReturns
? node.arguments[0]
: j.memberExpression(j.identifier('args'), node.arguments[0], true)

const mockImplementationFn = j.arrowFunctionExpression(
mockImplementationArgs,
j.blockStatement([
j.ifStatement(
callLengthConditionalExpression,
j.blockStatement([j.returnStatement(mockImplementationReturn)])
),
])
)

return j.callExpression(
j.memberExpression(mockFn, j.identifier('mockImplementation')),
[mockImplementationFn]
)
})
}

/*
stub.getCall(0) -> stub.mock.calls[0]
stub.getCall(0).args[1] -> stub.mock.calls[0][1]
Expand Down Expand Up @@ -546,7 +631,7 @@ function transformMock(j: core.JSCodeshift, ast, parser: string) {
j.blockStatement([
j.ifStatement(
mockImplementationConditionalExpression,
j.returnStatement(mockImplementationReturn[0])
j.blockStatement([j.returnStatement(mockImplementationReturn[0])])
),
])
)
Expand Down Expand Up @@ -840,6 +925,7 @@ export default function transformer(fileInfo: FileInfo, api: API, options) {
const logWarning = (msg, node) => logger(fileInfo, msg, node)

transformStub(j, ast, sinonExpression, logWarning)
transformStubOnCalls(j, ast, options.parser)
transformMockTimers(j, ast)
transformMock(j, ast, options.parser)
transformMockResets(j, ast)
Expand Down

0 comments on commit 6a2712b

Please sign in to comment.