Skip to content

Commit

Permalink
sinon mod improvements (#622)
Browse files Browse the repository at this point in the history
* sinon: equals support and consolidate mock implementer handling; use correct ast-types version

* sinon: consolidate mock impl code some more, implement resolves/rejects/throws

* sinon: restore parameterless mockImplementation when possible

* sinon: tests/fixes

* sinon: add missing global resets
  • Loading branch information
skirsdeda authored Nov 5, 2024
1 parent 4232f01 commit 33a62f9
Show file tree
Hide file tree
Showing 4 changed files with 447 additions and 217 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"@types/update-notifier": "5.1.0",
"@typescript-eslint/eslint-plugin": "5.62.0",
"@typescript-eslint/parser": "5.62.0",
"ast-types": "0.16.1",
"ast-types": "0.14.2",
"codecov": "3.8.3",
"eslint": "8.57.0",
"eslint-config-prettier": "8.10.0",
Expand Down
4 changes: 2 additions & 2 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

238 changes: 237 additions & 1 deletion src/transformers/sinon.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,58 @@ describe.each([
)
})

it('handles .resolves/.rejects', () => {
expectTransformation(
`
${sinonImport}
sinon.stub().resolves();
sinon.stub().resolves(1);
sinon.stub().rejects();
sinon.stub().rejects(new Error('error msg'));
`,
`
jest.fn().mockResolvedValue();
jest.fn().mockResolvedValue(1);
jest.fn().mockRejectedValue(new Error());
jest.fn().mockRejectedValue(new Error('error msg'));
`
)
})

it('handles .callsFake', () => {
expectTransformation(
`
${sinonImport}
sinon.stub().callsFake();
sinon.stub().callsFake((a, b) => a + b);
sinon.stub().callsFake(async () => ({ a: 1 }));
`,
`
jest.fn().mockImplementation();
jest.fn().mockImplementation((a, b) => a + b);
jest.fn().mockImplementation(async () => ({ a: 1 }));
`
)
})

it('handles .throws', () => {
expectTransformation(
`
${sinonImport}
sinon.stub().throws();
sinon.stub().throws(new Error('error msg'));
`,
`
jest.fn().mockImplementation(() => {
throw new Error();
});
jest.fn().mockImplementation(() => {
throw new Error('error msg');
});
`
)
})

it('handles .withArgs returns', () => {
expectTransformation(
`
Expand All @@ -199,6 +251,8 @@ describe.each([
const stub = sinon.stub(foo, 'bar').withArgs('foo', 1).returns('something')
sinon.stub(foo, 'bar').withArgs('foo', sinon.match.object).returns('something')
sinon.stub().withArgs('foo', sinon.match.any).returns('something')
sinon.stub().withArgs('boo', sinon.match.any).returnsArg(1)
sinon.stub().withArgs('boo').returns()
`,
`
jest.fn().mockImplementation((...args) => {
Expand Down Expand Up @@ -236,6 +290,63 @@ describe.each([
return 'something';
}
})
jest.fn().mockImplementation((...args) => {
if (args[0] === 'boo' && args.length >= 2) {
return args[1];
}
})
jest.fn().mockImplementation((...args) => {
if (args[0] === 'boo') {
return undefined;
}
})
`
)
})

it('handles .withArgs chained with .resolves/.rejects/.throws/.callsFake', () => {
expectTransformation(
`
${sinonImport}
sinon.stub().withArgs('foo').resolves('something')
sinon.stub().withArgs('foo', 'bar').rejects()
sinon.stub().withArgs('foo', 'bar', 1).rejects(new Error('something'))
sinon.stub(Api, 'get').withArgs('foo', 'bar', 1).throws()
const stub = sinon.stub(foo, 'bar').withArgs('foo', 1).throws(new Error('something'))
sinon.stub(foo, 'bar').withArgs('foo', sinon.match.object).callsFake((_, obj) => obj)
`,
`
jest.fn().mockImplementation((...args) => {
if (args[0] === 'foo') {
return Promise.resolve('something');
}
})
jest.fn().mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 'bar') {
return Promise.reject(new Error());
}
})
jest.fn().mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 'bar' && args[2] === 1) {
return Promise.reject(new Error('something'));
}
})
jest.spyOn(Api, 'get').mockClear().mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 'bar' && args[2] === 1) {
throw new Error();
}
})
const stub = jest.spyOn(foo, 'bar').mockClear().mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 1) {
throw new Error('something');
}
})
jest.spyOn(foo, 'bar').mockClear().mockImplementation((...args) => {
if (args[0] === 'foo' && typeof args[1] === 'object') {
return ((_, obj) => obj)(...args);
}
})
`
)
})
Expand All @@ -258,7 +369,7 @@ describe.each([
)
})

/*
/*
apiStub.getCall(0).args[1].data
apistub.args[1][1]
*/
Expand Down Expand Up @@ -386,6 +497,60 @@ describe.each([
{ parser: 'ts' }
)
})
it('handles .callsArg* after .on*Call/.withArgs', () => {
expectTransformation(
`
${sinonImport}
apiStub.onFirstCall().callsArg(0)
apiStub.onSecondCall().callsArgOn(1, thisArg)
apiStub.onThirdCall().callsArgWith(2, 'a', 'b')
apiStub.onCall(2).callsArgOnWith(3, thisArg, 'c', 'd')
apiStub.withArgs('foo', 'bar').callsArg(0)
apiStub.withArgs(sinon.match.any, sinon.match.func).callsArgOn(1, thisArg)
apiStub.withArgs(sinon.match.any).callsArgWith(0, 'a', 'b')
`,
`
apiStub.mockImplementation((...args) => {
if (apiStub.mock.calls.length === 0) {
return args[0]();
}
})
apiStub.mockImplementation((...args) => {
if (apiStub.mock.calls.length === 1) {
return args[1].call(thisArg);
}
})
apiStub.mockImplementation((...args) => {
if (apiStub.mock.calls.length === 2) {
return args[2]('a', 'b');
}
})
apiStub.mockImplementation((...args) => {
if (apiStub.mock.calls.length === 2) {
return args[3].call(thisArg, 'c', 'd');
}
})
apiStub.mockImplementation((...args) => {
if (args[0] === 'foo' && args[1] === 'bar') {
return args[0]();
}
})
apiStub.mockImplementation((...args) => {
if (args.length >= 2 && typeof args[1] === 'function') {
return args[1].call(thisArg);
}
})
apiStub.mockImplementation((...args) => {
if (args.length >= 1) {
return args[0]('a', 'b');
}
})
`
)
})

it('handles on*Call', () => {
expectTransformation(
Expand Down Expand Up @@ -466,6 +631,65 @@ describe.each([
{ parser: 'tsx' }
)
})

it('handles on*Call chained with .resolves/.rejects/.throws/.callsFake', () => {
expectTransformation(
`
${sinonSandboxImport}
stub.onFirstCall().resolves()
stub.onSecondCall().resolves(1)
stub.onFirstCall().rejects()
stub.onSecondCall().rejects(new Error('msg'))
stub.onThirdCall().throws()
stub.onThirdCall().throws(new Error('msg'))
stub.onCall(1).callsFake(() => 2)
`,
`
stub.mockImplementation(() => {
if (stub.mock.calls.length === 0) {
return Promise.resolve();
}
})
stub.mockImplementation(() => {
if (stub.mock.calls.length === 1) {
return Promise.resolve(1);
}
})
stub.mockImplementation(() => {
if (stub.mock.calls.length === 0) {
return Promise.reject(new Error());
}
})
stub.mockImplementation(() => {
if (stub.mock.calls.length === 1) {
return Promise.reject(new Error('msg'));
}
})
stub.mockImplementation(() => {
if (stub.mock.calls.length === 2) {
throw new Error();
}
})
stub.mockImplementation(() => {
if (stub.mock.calls.length === 2) {
throw new Error('msg');
}
})
stub.mockImplementation((...args) => {
if (stub.mock.calls.length === 1) {
return (() => 2)(...args);
}
})
`
)
})
})

describe('mocks', () => {
Expand All @@ -489,6 +713,9 @@ describe.each([
Api.get.restore()
Api.get.reset()
sinon.restore()
sinon.reset()
sinon.resetBehavior()
sinon.resetHistory()
stub.resetBehavior()
stub.resetHistory()
`,
Expand All @@ -497,6 +724,9 @@ describe.each([
Api.get.mockRestore()
Api.get.mockReset()
jest.restoreAllMocks()
jest.resetAllMocks()
jest.resetAllMocks()
jest.resetAllMocks()
stub.mockReset()
stub.mockReset()
`
Expand Down Expand Up @@ -598,6 +828,9 @@ describe.each([
expect(spy.calledThrice).to.equal(true)
expect(spy.called).to.equal(true)
expect(spy.calledOnce).equals(true)
expect(spy.called).equals(true)
// .to.be
expect(Api.get.callCount).to.be(1)
expect(Api.get.called).to.be(true)
Expand Down Expand Up @@ -629,6 +862,9 @@ describe.each([
expect(spy).toHaveBeenCalledTimes(3)
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenCalledTimes(1)
expect(spy).toHaveBeenCalled()
// .to.be
expect(Api.get).toHaveBeenCalledTimes(1)
expect(Api.get).toHaveBeenCalled()
Expand Down
Loading

0 comments on commit 33a62f9

Please sign in to comment.