Skip to content

Commit 33a62f9

Browse files
authored
sinon mod improvements (#622)
* 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
1 parent 4232f01 commit 33a62f9

File tree

4 files changed

+447
-217
lines changed

4 files changed

+447
-217
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@
6969
"@types/update-notifier": "5.1.0",
7070
"@typescript-eslint/eslint-plugin": "5.62.0",
7171
"@typescript-eslint/parser": "5.62.0",
72-
"ast-types": "0.16.1",
72+
"ast-types": "0.14.2",
7373
"codecov": "3.8.3",
7474
"eslint": "8.57.0",
7575
"eslint-config-prettier": "8.10.0",

pnpm-lock.yaml

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/transformers/sinon.test.ts

Lines changed: 237 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,58 @@ describe.each([
187187
)
188188
})
189189

190+
it('handles .resolves/.rejects', () => {
191+
expectTransformation(
192+
`
193+
${sinonImport}
194+
sinon.stub().resolves();
195+
sinon.stub().resolves(1);
196+
sinon.stub().rejects();
197+
sinon.stub().rejects(new Error('error msg'));
198+
`,
199+
`
200+
jest.fn().mockResolvedValue();
201+
jest.fn().mockResolvedValue(1);
202+
jest.fn().mockRejectedValue(new Error());
203+
jest.fn().mockRejectedValue(new Error('error msg'));
204+
`
205+
)
206+
})
207+
208+
it('handles .callsFake', () => {
209+
expectTransformation(
210+
`
211+
${sinonImport}
212+
sinon.stub().callsFake();
213+
sinon.stub().callsFake((a, b) => a + b);
214+
sinon.stub().callsFake(async () => ({ a: 1 }));
215+
`,
216+
`
217+
jest.fn().mockImplementation();
218+
jest.fn().mockImplementation((a, b) => a + b);
219+
jest.fn().mockImplementation(async () => ({ a: 1 }));
220+
`
221+
)
222+
})
223+
224+
it('handles .throws', () => {
225+
expectTransformation(
226+
`
227+
${sinonImport}
228+
sinon.stub().throws();
229+
sinon.stub().throws(new Error('error msg'));
230+
`,
231+
`
232+
jest.fn().mockImplementation(() => {
233+
throw new Error();
234+
});
235+
jest.fn().mockImplementation(() => {
236+
throw new Error('error msg');
237+
});
238+
`
239+
)
240+
})
241+
190242
it('handles .withArgs returns', () => {
191243
expectTransformation(
192244
`
@@ -199,6 +251,8 @@ describe.each([
199251
const stub = sinon.stub(foo, 'bar').withArgs('foo', 1).returns('something')
200252
sinon.stub(foo, 'bar').withArgs('foo', sinon.match.object).returns('something')
201253
sinon.stub().withArgs('foo', sinon.match.any).returns('something')
254+
sinon.stub().withArgs('boo', sinon.match.any).returnsArg(1)
255+
sinon.stub().withArgs('boo').returns()
202256
`,
203257
`
204258
jest.fn().mockImplementation((...args) => {
@@ -236,6 +290,63 @@ describe.each([
236290
return 'something';
237291
}
238292
})
293+
jest.fn().mockImplementation((...args) => {
294+
if (args[0] === 'boo' && args.length >= 2) {
295+
return args[1];
296+
}
297+
})
298+
jest.fn().mockImplementation((...args) => {
299+
if (args[0] === 'boo') {
300+
return undefined;
301+
}
302+
})
303+
`
304+
)
305+
})
306+
307+
it('handles .withArgs chained with .resolves/.rejects/.throws/.callsFake', () => {
308+
expectTransformation(
309+
`
310+
${sinonImport}
311+
312+
sinon.stub().withArgs('foo').resolves('something')
313+
sinon.stub().withArgs('foo', 'bar').rejects()
314+
sinon.stub().withArgs('foo', 'bar', 1).rejects(new Error('something'))
315+
sinon.stub(Api, 'get').withArgs('foo', 'bar', 1).throws()
316+
const stub = sinon.stub(foo, 'bar').withArgs('foo', 1).throws(new Error('something'))
317+
sinon.stub(foo, 'bar').withArgs('foo', sinon.match.object).callsFake((_, obj) => obj)
318+
`,
319+
`
320+
jest.fn().mockImplementation((...args) => {
321+
if (args[0] === 'foo') {
322+
return Promise.resolve('something');
323+
}
324+
})
325+
jest.fn().mockImplementation((...args) => {
326+
if (args[0] === 'foo' && args[1] === 'bar') {
327+
return Promise.reject(new Error());
328+
}
329+
})
330+
jest.fn().mockImplementation((...args) => {
331+
if (args[0] === 'foo' && args[1] === 'bar' && args[2] === 1) {
332+
return Promise.reject(new Error('something'));
333+
}
334+
})
335+
jest.spyOn(Api, 'get').mockClear().mockImplementation((...args) => {
336+
if (args[0] === 'foo' && args[1] === 'bar' && args[2] === 1) {
337+
throw new Error();
338+
}
339+
})
340+
const stub = jest.spyOn(foo, 'bar').mockClear().mockImplementation((...args) => {
341+
if (args[0] === 'foo' && args[1] === 1) {
342+
throw new Error('something');
343+
}
344+
})
345+
jest.spyOn(foo, 'bar').mockClear().mockImplementation((...args) => {
346+
if (args[0] === 'foo' && typeof args[1] === 'object') {
347+
return ((_, obj) => obj)(...args);
348+
}
349+
})
239350
`
240351
)
241352
})
@@ -258,7 +369,7 @@ describe.each([
258369
)
259370
})
260371

261-
/*
372+
/*
262373
apiStub.getCall(0).args[1].data
263374
apistub.args[1][1]
264375
*/
@@ -386,6 +497,60 @@ describe.each([
386497
{ parser: 'ts' }
387498
)
388499
})
500+
it('handles .callsArg* after .on*Call/.withArgs', () => {
501+
expectTransformation(
502+
`
503+
${sinonImport}
504+
505+
apiStub.onFirstCall().callsArg(0)
506+
apiStub.onSecondCall().callsArgOn(1, thisArg)
507+
apiStub.onThirdCall().callsArgWith(2, 'a', 'b')
508+
apiStub.onCall(2).callsArgOnWith(3, thisArg, 'c', 'd')
509+
510+
apiStub.withArgs('foo', 'bar').callsArg(0)
511+
apiStub.withArgs(sinon.match.any, sinon.match.func).callsArgOn(1, thisArg)
512+
apiStub.withArgs(sinon.match.any).callsArgWith(0, 'a', 'b')
513+
`,
514+
`
515+
apiStub.mockImplementation((...args) => {
516+
if (apiStub.mock.calls.length === 0) {
517+
return args[0]();
518+
}
519+
})
520+
apiStub.mockImplementation((...args) => {
521+
if (apiStub.mock.calls.length === 1) {
522+
return args[1].call(thisArg);
523+
}
524+
})
525+
apiStub.mockImplementation((...args) => {
526+
if (apiStub.mock.calls.length === 2) {
527+
return args[2]('a', 'b');
528+
}
529+
})
530+
apiStub.mockImplementation((...args) => {
531+
if (apiStub.mock.calls.length === 2) {
532+
return args[3].call(thisArg, 'c', 'd');
533+
}
534+
})
535+
536+
apiStub.mockImplementation((...args) => {
537+
if (args[0] === 'foo' && args[1] === 'bar') {
538+
return args[0]();
539+
}
540+
})
541+
apiStub.mockImplementation((...args) => {
542+
if (args.length >= 2 && typeof args[1] === 'function') {
543+
return args[1].call(thisArg);
544+
}
545+
})
546+
apiStub.mockImplementation((...args) => {
547+
if (args.length >= 1) {
548+
return args[0]('a', 'b');
549+
}
550+
})
551+
`
552+
)
553+
})
389554

390555
it('handles on*Call', () => {
391556
expectTransformation(
@@ -466,6 +631,65 @@ describe.each([
466631
{ parser: 'tsx' }
467632
)
468633
})
634+
635+
it('handles on*Call chained with .resolves/.rejects/.throws/.callsFake', () => {
636+
expectTransformation(
637+
`
638+
${sinonSandboxImport}
639+
640+
stub.onFirstCall().resolves()
641+
stub.onSecondCall().resolves(1)
642+
643+
stub.onFirstCall().rejects()
644+
stub.onSecondCall().rejects(new Error('msg'))
645+
646+
stub.onThirdCall().throws()
647+
stub.onThirdCall().throws(new Error('msg'))
648+
649+
stub.onCall(1).callsFake(() => 2)
650+
`,
651+
`
652+
stub.mockImplementation(() => {
653+
if (stub.mock.calls.length === 0) {
654+
return Promise.resolve();
655+
}
656+
})
657+
stub.mockImplementation(() => {
658+
if (stub.mock.calls.length === 1) {
659+
return Promise.resolve(1);
660+
}
661+
})
662+
663+
stub.mockImplementation(() => {
664+
if (stub.mock.calls.length === 0) {
665+
return Promise.reject(new Error());
666+
}
667+
})
668+
stub.mockImplementation(() => {
669+
if (stub.mock.calls.length === 1) {
670+
return Promise.reject(new Error('msg'));
671+
}
672+
})
673+
674+
stub.mockImplementation(() => {
675+
if (stub.mock.calls.length === 2) {
676+
throw new Error();
677+
}
678+
})
679+
stub.mockImplementation(() => {
680+
if (stub.mock.calls.length === 2) {
681+
throw new Error('msg');
682+
}
683+
})
684+
685+
stub.mockImplementation((...args) => {
686+
if (stub.mock.calls.length === 1) {
687+
return (() => 2)(...args);
688+
}
689+
})
690+
`
691+
)
692+
})
469693
})
470694

471695
describe('mocks', () => {
@@ -489,6 +713,9 @@ describe.each([
489713
Api.get.restore()
490714
Api.get.reset()
491715
sinon.restore()
716+
sinon.reset()
717+
sinon.resetBehavior()
718+
sinon.resetHistory()
492719
stub.resetBehavior()
493720
stub.resetHistory()
494721
`,
@@ -497,6 +724,9 @@ describe.each([
497724
Api.get.mockRestore()
498725
Api.get.mockReset()
499726
jest.restoreAllMocks()
727+
jest.resetAllMocks()
728+
jest.resetAllMocks()
729+
jest.resetAllMocks()
500730
stub.mockReset()
501731
stub.mockReset()
502732
`
@@ -598,6 +828,9 @@ describe.each([
598828
expect(spy.calledThrice).to.equal(true)
599829
expect(spy.called).to.equal(true)
600830
831+
expect(spy.calledOnce).equals(true)
832+
expect(spy.called).equals(true)
833+
601834
// .to.be
602835
expect(Api.get.callCount).to.be(1)
603836
expect(Api.get.called).to.be(true)
@@ -629,6 +862,9 @@ describe.each([
629862
expect(spy).toHaveBeenCalledTimes(3)
630863
expect(spy).toHaveBeenCalled()
631864
865+
expect(spy).toHaveBeenCalledTimes(1)
866+
expect(spy).toHaveBeenCalled()
867+
632868
// .to.be
633869
expect(Api.get).toHaveBeenCalledTimes(1)
634870
expect(Api.get).toHaveBeenCalled()

0 commit comments

Comments
 (0)