Repository: skovhus/jest-codemods
Pull request #359 (2022-08-19 22:52:12)

[sinon][ts] convert sinon.SinonStub -> jest.Mock, sinon.SinonSpy -> jest.SpyInstance
Reviewers (other than @skovhus): @catc @lencioni @jayrobin

codecov[bot]: # [Codecov](https://codecov.io/gh/skovhus/jest-codemods/pull/359?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus) Report
> Merging [#359](https://codecov.io/gh/skovhus/jest-codemods/pull/359?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus) (174f6ac) into [main](https://codecov.io/gh/skovhus/jest-codemods/commit/1a26e298022e7956bc5954cc75816cd6ca58260a?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus) (1a26e29) will **increase** coverage by `0.08%`.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##             main     #359      +/-   ##
==========================================
+ Coverage   92.09%   92.17%   +0.08%     
==========================================
  Files          26       26              
  Lines        1872     1879       +7     
  Branches      393      393              
==========================================
+ Hits         1724     1732       +8     
+ Misses        102      101       -1     
  Partials       46       46              
```


| [Impacted Files](https://codecov.io/gh/skovhus/jest-codemods/pull/359?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus) | Coverage Δ | |
|---|---|---|
| [src/transformers/sinon.ts](https://codecov.io/gh/skovhus/jest-codemods/pull/359/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus#diff-c3JjL3RyYW5zZm9ybWVycy9zaW5vbi50cw==) | `88.34% <100.00%> (+0.84%)` | :arrow_up: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus)

danbeam: thanks @jayrobin and @catc! on to @skovhus 

Patch:
diff --git a/src/transformers/sinon.ts b/src/transformers/sinon.ts
index 370f1ba..0f3a8f7 100644
--- a/src/transformers/sinon.ts
+++ b/src/transformers/sinon.ts
@@ -58,6 +58,8 @@ const SINON_ON_NTH_CALLS = new Set(['onFirstCall', 'onSecondCall', 'onThirdCall'
 const EXPECT_PREFIXES = new Set(['to'])
 const isPrefix = (name) => EXPECT_PREFIXES.has(name)
 
+const isTypescript = (parser: string) => parser === 'tsx' || parser === 'ts'
+
 const SINON_CALLS_ARG = new Set([
   'callsArg',
   'callsArgOn',
@@ -88,11 +90,10 @@ function transformCallsArg(j, ast, parser) {
 
       const argName = j.memberExpression(j.identifier('args'), node.arguments[0], true)
 
-      const isTypescript = parser === 'tsx' || parser === 'ts'
       const mockImplementationArg = j.spreadPropertyPattern(
         j.identifier.from({
           name: 'args',
-          typeAnnotation: isTypescript
+          typeAnnotation: isTypescript(parser)
             ? j.typeAnnotation(j.arrayTypeAnnotation(j.anyTypeAnnotation()))
             : null,
         })
@@ -552,8 +553,6 @@ function transformStubGetCalls(j: core.JSCodeshift, ast) {
     .returnsArg
 */
 function transformMock(j: core.JSCodeshift, ast, parser: string) {
-  const isTypescript = parser === 'tsx' || parser === 'ts'
-
   // stub.withArgs(111).returns('foo') => stub.mockImplementation((...args) => { if (args[0] === '111') return 'foo' })
   ast
     .find(j.CallExpression, {
@@ -621,7 +620,7 @@ function transformMock(j: core.JSCodeshift, ast, parser: string) {
       const mockImplementationArg = j.spreadPropertyPattern(
         j.identifier.from({
           name: 'args',
-          typeAnnotation: isTypescript
+          typeAnnotation: isTypescript(parser)
             ? j.typeAnnotation(j.arrayTypeAnnotation(j.anyTypeAnnotation()))
             : null,
         })
@@ -671,7 +670,7 @@ function transformMock(j: core.JSCodeshift, ast, parser: string) {
 
       const argsVar = j.identifier.from({
         name: 'args',
-        typeAnnotation: isTypescript
+        typeAnnotation: isTypescript(parser)
           ? j.typeAnnotation(j.arrayTypeAnnotation(j.anyTypeAnnotation()))
           : null,
       })
@@ -871,6 +870,44 @@ function transformMockTimers(j, ast) {
     })
 }
 
+// let stub: sinon.SinonStub -> let stub: jest.Mock
+// let spy: sinon.SinonSpy -> let spy: jest.SpyInstance
+function transformTypes(j, ast, parser) {
+  if (!isTypescript(parser)) return
+
+  ast
+    .find(j.TSTypeReference, {
+      typeName: {
+        left: {
+          name: 'sinon',
+        },
+        right: {
+          name: 'SinonStub',
+        },
+      },
+    })
+    .forEach((np) => {
+      np.node.typeName.left.name = 'jest'
+      np.node.typeName.right.name = 'Mock'
+    })
+
+  ast
+    .find(j.TSTypeReference, {
+      typeName: {
+        left: {
+          name: 'sinon',
+        },
+        right: {
+          name: 'SinonSpy',
+        },
+      },
+    })
+    .forEach((np) => {
+      np.node.typeName.left.name = 'jest'
+      np.node.typeName.right.name = 'SpyInstance'
+    })
+}
+
 export default function transformer(fileInfo: FileInfo, api: API, options) {
   const j = api.jscodeshift
   const ast = j(fileInfo.source)
@@ -879,7 +916,6 @@ export default function transformer(fileInfo: FileInfo, api: API, options) {
     removeDefaultImport(j, ast, 'sinon-sandbox') || removeDefaultImport(j, ast, 'sinon')
 
   if (!sinonExpression) {
-    console.warn(`no sinon for "${fileInfo.path}"`)
     if (!options.skipImportDetection) {
       return fileInfo.source
     }
@@ -898,6 +934,7 @@ export default function transformer(fileInfo: FileInfo, api: API, options) {
   transformCalledWithAssertions(j, ast)
   transformMatch(j, ast)
   transformStubGetCalls(j, ast)
+  transformTypes(j, ast, options.parser)
 
   return finale(fileInfo, j, ast, options)
 }


Test patch:
diff --git a/src/transformers/sinon.test.ts b/src/transformers/sinon.test.ts
index 297e73c..08dfdde 100644
--- a/src/transformers/sinon.test.ts
+++ b/src/transformers/sinon.test.ts
@@ -690,3 +690,39 @@ describe('mock timers', () => {
     )
   })
 })
+
+describe('updates types', () => {
+  it('rewrites types for mocks & stubs', () => {
+    expectTransformation(
+      `
+      import sinon from 'sinon-sandbox'
+
+      let stub1: sinon.SinonStub
+      let stub2: sinon.SinonStub = getStub()
+      let spy1: sinon.SinonSpy
+      let spy2: sinon.SinonSpy = getSpy()
+      let doStubThing = (stub: sinon.SinonStub) => stub
+      let doSpyThing = (spy: sinon.SinonSpy) => spy
+
+      // These should be ignored
+      let num: number = 5
+      let str: string = 'asdf'
+      let keys = (obj: object) => Object.keys(obj)
+`,
+      `
+      let stub1: jest.Mock
+      let stub2: jest.Mock = getStub()
+      let spy1: jest.SpyInstance
+      let spy2: jest.SpyInstance = getSpy()
+      let doStubThing = (stub: jest.Mock) => stub
+      let doSpyThing = (spy: jest.SpyInstance) => spy
+
+      // These should be ignored
+      let num: number = 5
+      let str: string = 'asdf'
+      let keys = (obj: object) => Object.keys(obj)
+`,
+      { parser: 'ts' }
+    )
+  })
+})
diff --git a/src/utils/test-helpers.ts b/src/utils/test-helpers.ts
index 9fab150..1321c43 100644
--- a/src/utils/test-helpers.ts
+++ b/src/utils/test-helpers.ts
@@ -1,17 +1,23 @@
 import jscodeshift from 'jscodeshift'
 
 // simulate the jscodeshift api
-export function api(): jscodeshift.API {
+export function api(options): jscodeshift.API {
+  let j = jscodeshift
+
+  if (options.parser) {
+    j = j.withParser(options.parser)
+  }
+
   return {
-    jscodeshift,
-    j: jscodeshift,
+    jscodeshift: j,
+    j,
     stats: () => {},
     report: () => {},
   }
 }
 
 export function runPlugin(plugin: jscodeshift.Transform, source: string, options = {}) {
-  return plugin({ source, path: 'test.js' }, api(), options)
+  return plugin({ source, path: 'test.js' }, api(options), options)
 }
 
 export function wrapPlugin(plugin: jscodeshift.Transform) {