Repository: skovhus/jest-codemods
Pull request #518 (2023-04-12 11:18:58)

sinon: add support for sinon.assert
Attempts to implement most of the `sinon.assert` matchers in the `sinon` codemod. 

If you have any idea of how to implement more of the  `sinon.assert` matchers, please let me know. Also, the switch statement that I used is not the prettiest one, but at least it is relatively easy to read (suggestions welcome there too).

I used these as documentation for how `sinon.assert` is used:

- https://sinonjs.org/releases/latest/assertions/
- https://github.com/search?p=1&q=sinon.assert&type=Code

ping @skovhus 

codecov[bot]: ## [Codecov](https://codecov.io/gh/skovhus/jest-codemods/pull/518?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus) Report
Patch coverage: **`100.00`**% and project coverage change: **`+0.09`** :tada:
> Comparison is base [(`a94e2b5`)](https://codecov.io/gh/skovhus/jest-codemods/commit/a94e2b561fed619cbebf0b9c28e0a919c44aa4e3?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus) 92.38% compared to head [(`d4afac0`)](https://codecov.io/gh/skovhus/jest-codemods/pull/518?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus) 92.47%.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##             main     #518      +/-   ##
==========================================
+ Coverage   92.38%   92.47%   +0.09%     
==========================================
  Files          26       26              
  Lines        1944     1968      +24     
  Branches      405      407       +2     
==========================================
+ Hits         1796     1820      +24     
  Misses        102      102              
  Partials       46       46              
```


| [Impacted Files](https://codecov.io/gh/skovhus/jest-codemods/pull/518?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/518?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus#diff-c3JjL3RyYW5zZm9ybWVycy9zaW5vbi50cw==) | `91.47% <100.00%> (+0.72%)` | :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)

</details>

[:umbrella: View full report in Codecov by Sentry](https://codecov.io/gh/skovhus/jest-codemods/pull/518?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Kenneth+Skovhus).


Patch:
diff --git a/src/transformers/sinon.ts b/src/transformers/sinon.ts
index c93a749..3623cfd 100644
--- a/src/transformers/sinon.ts
+++ b/src/transformers/sinon.ts
@@ -31,7 +31,7 @@ const CHAI_CHAIN_MATCHERS = new Set(
     a.toLowerCase()
   )
 )
-const SINON_CALLED_WITH_METHODS = ['calledWith', 'notCalledWith']
+const SINON_CALLED_WITH_METHODS = ['calledWith', 'notCalledWith', 'neverCalledWith']
 const SINON_SPY_METHODS = ['spy', 'stub']
 const SINON_MOCK_RESETS = {
   reset: 'mockReset',
@@ -741,6 +741,87 @@ function transformMockResets(j, ast) {
     })
 }
 
+/* 
+  sinon.assert.called(spy) -> expect(spy).toHaveBeenCalled()
+  sinon.assert.calledOnce(spy) -> expect(spy).toHaveBeenCalledTimes(1)
+  sinon.assert.calledWith(spy, arg1, arg2) -> expect(spy).toHaveBeenCalledWith(arg1, arg2)
+*/
+function transformAssert(j, ast) {
+  ast
+    .find(j.CallExpression, {
+      type: 'CallExpression',
+      callee: {
+        type: 'MemberExpression',
+        object: {
+          type: 'MemberExpression',
+          object: {
+            type: 'Identifier',
+            name: 'sinon',
+          },
+          property: {
+            type: 'Identifier',
+            name: 'assert',
+          },
+        },
+        property: {
+          type: 'Identifier',
+          name: (n) =>
+            SINON_CALL_COUNT_METHODS.includes(n) || SINON_CALLED_WITH_METHODS.includes(n),
+        },
+      },
+    })
+    .replaceWith((np) => {
+      const args = np.node.arguments
+      const { name } = np.node.callee.property
+
+      let matcher
+
+      switch (name) {
+        case 'called':
+          matcher = j.callExpression(j.identifier('toHaveBeenCalled'), [])
+          break
+        case 'notCalled':
+          matcher = j.callExpression(
+            j.memberExpression(j.identifier('not'), j.identifier('toHaveBeenCalled')),
+            []
+          )
+          break
+        case 'calledOnce':
+          matcher = j.callExpression(j.identifier('toHaveBeenCalledTimes'), [
+            j.numericLiteral(1),
+          ])
+          break
+        case 'calledTwice':
+          matcher = j.callExpression(j.identifier('toHaveBeenCalledTimes'), [
+            j.numericLiteral(2),
+          ])
+          break
+        case 'calledThrice':
+          matcher = j.callExpression(j.identifier('toHaveBeenCalledTimes'), [
+            j.numericLiteral(3),
+          ])
+          break
+        case 'callCount':
+          matcher = j.callExpression(j.identifier('toHaveBeenCalledTimes'), [args[1]])
+          break
+        case 'calledWith':
+          matcher = j.callExpression(j.identifier('toHaveBeenCalledWith'), args.slice(1))
+          break
+        case 'neverCalledWith':
+          matcher = j.callExpression(
+            j.memberExpression(j.identifier('not'), j.identifier('toHaveBeenCalledWith')),
+            args.slice(1)
+          )
+          break
+      }
+
+      return j.memberExpression(
+        j.callExpression(j.identifier('expect'), [args[0]]),
+        matcher
+      )
+    })
+}
+
 /* 
   sinon.match({ ... }) -> expect.objectContaining({ ... })
   // .any. matches:
@@ -948,6 +1029,7 @@ export default function transformer(fileInfo: FileInfo, api: API, options) {
   transformCallsArg(j, ast, options.parser)
   transformCallCountAssertions(j, ast)
   transformCalledWithAssertions(j, ast)
+  transformAssert(j, ast)
   transformMatch(j, ast)
   transformStubGetCalls(j, ast)
   transformTypes(j, ast, options.parser)


Test patch:
diff --git a/src/transformers/sinon.test.ts b/src/transformers/sinon.test.ts
index 50e5611..200fcf3 100644
--- a/src/transformers/sinon.test.ts
+++ b/src/transformers/sinon.test.ts
@@ -531,6 +531,37 @@ describe('sinon.match', () => {
   })
 })
 
+describe('sinon.assert', () => {
+  it('handles assertions', () => {
+    expectTransformation(
+      `
+        import sinon from 'sinon'
+
+        sinon.assert.called(spy);
+        sinon.assert.notCalled(spy);
+        sinon.assert.calledOnce(spy);
+        sinon.assert.calledTwice(spy);
+        sinon.assert.calledThrice(spy);
+        sinon.assert.callCount(spy, 56);
+        sinon.assert.calledWith(spyOrSpyCall, arg1, arg2);
+        sinon.assert.calledWith(spy, sinon.match({ author: "cjno" }));
+        sinon.assert.neverCalledWith(spy, arg1, arg2);
+      `,
+      `
+        expect(spy).toHaveBeenCalled();
+        expect(spy).not.toHaveBeenCalled();
+        expect(spy).toHaveBeenCalledTimes(1);
+        expect(spy).toHaveBeenCalledTimes(2);
+        expect(spy).toHaveBeenCalledTimes(3);
+        expect(spy).toHaveBeenCalledTimes(56);
+        expect(spyOrSpyCall).toHaveBeenCalledWith(arg1, arg2);
+        expect(spy).toHaveBeenCalledWith(expect.objectContaining({ author: "cjno" }));
+        expect(spy).not.toHaveBeenCalledWith(arg1, arg2);
+      `
+    )
+  })
+})
+
 describe('spy count and call assertions', () => {
   it('handles call count assertions', () => {
     expectTransformation(