-
Notifications
You must be signed in to change notification settings - Fork 177
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #829 from wheresrhys/rhys/codemod
Rhys/codemod
- Loading branch information
Showing
21 changed files
with
2,640 additions
and
1,188 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
try.js |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
# @fetch-mock/codemods | ||
|
||
A tool for helping upgrade to fetch-mock@12. | ||
|
||
## Usage | ||
|
||
1. Install `npm i -D @fetch-mock/codemods jscodeshift`; | ||
2. Manually modify any code using `.sandbox()` according to the example above. | ||
3. Run `jscodeshift -t node_modules/@fetch-mock/codemods/wrc/index.jscodeshift .` to run over your entire project, or replace `.` with the directory/file paths you wish to modify. [The jscodeshift CLI has many options](https://jscodeshift.com/run/cli/) - adjust them to suit your project. **Note that the parser option should not be used as @fetch-mock/codemods forces use of the TSX parser in order to ensure compatibility with teh greatest range of projects**. | ||
4. For scenarios where the codemod is unable to detect which variable contains a fetch-mock instance (e.g. when it is required in a global set up file, or when using `jest.mock()`) you may pass in one or more variable names using the `FM_VARIABLES` environment variable e.g. `FM_VARIABLES=fm,fetch` | ||
5. After the codemods have executed, run your tests, correcting all the errors thrown. The notes on what is in/out of scope will help guide you. | ||
6. Once all your tests are fixed you should be able to uninstall the codemods: `npm uninstall @fetch-mock/codemods jscodeshift` | ||
|
||
## Features | ||
|
||
- Identifies instances of fetch-mock imported using `require` or `import` | ||
- Rewrites `.mock()` to `.route()` | ||
- Rewrites `.reset()`, `.restore()`, `.resetBehavior()` and `.resetHistory()` to their equivalents in fetch-mock@12 | ||
- Rewrites `.lastUrl()`, `.lastOptions()` and `lastResponse()` to their equivalents in fetch-mock@12 | ||
- Adds an informative error whenever `.lastCall()` or `.calls()` are used with advice on how to manually correct these | ||
- Converts `.getOnce()`, `.getAnyOnce()`, `.postOnce()` etc... - which have been removed - to calls to the underlying `.get()` method with additional options passed in. | ||
- Removes uses of the deprecated options `overwriteRoutes`, `warnOnFallback`, `sendAsJson` | ||
- Removes uses of the deprecated `fallbackToNetwork` option, and adds an informative error with details of how to replace with the `spyGlobal()` method | ||
|
||
## Limitations/Out of scope | ||
|
||
- Javascript is a language with multiple ways to do the same thing. While these codemods attempt to cover a few different patterns, it's likely that they don't cover all the ways fetch-mock is being used. Please raise an issue if you think they can be improved. | ||
- fetch-mock@12 no longer has the `.mock()` method which combines defining a route _and_ setting up global mocking. All calls to `.mock()` are replaced by `.route()`. | ||
If using global `fetch` you will also need to call `.mockGlobal()` at least once per test suite. | ||
- The `.sandbox()` method previously created a `fetch` function that also had all the fetch-mock methods attached. This is no longer the case, but pulling it apart is complex and deliberately left out of scope for this codemod. | ||
- Any uses of fetch-mock prior to assigning to a variable will not be modified e.g. `require('fetch-mock').mock('a', 'b')` will not be converted to `require('fetch-mock').route('a', 'b')` | ||
- When using a pattern such as `jest.mock('node-fetch', () => require('fetch-mock').sandbox())`, the codemod is unable to identify that `require('node-fetch')` will be an instance of fetch-mock. | ||
- On the server side fetch-mock was previously built around node-fetch's classes, but now uses native `fetch`. In most cases, even if your application code still uses node-fetch, your mocks will still work. However, if you explicitly create instances of `Request` or `Headers` using node-fetch's classes, you may need to provide these to fetch-mock. | ||
|
||
Taking the last 4 points together, this example illustrates the kind of manual modifications required: | ||
|
||
### Before | ||
|
||
```js | ||
jest.mock('node-fetch', () => require('fetch-mock').sandbox()); | ||
|
||
const nodeFetch = require('node-fetch'); | ||
|
||
it('runs a test', () => { | ||
nodeFetch.get('http://a.com', 200); | ||
myAPI.call(); | ||
expect(nodeFetch.called()).toBe(true); | ||
}); | ||
``` | ||
|
||
### After | ||
|
||
```js | ||
const fetchMock = require('fetch-mock'); | ||
|
||
jest.mock('node-fetch', () => { | ||
const nodeFetch = jest.requireActual('node-fetch'); | ||
// only needed if your application makes use of Response, Request | ||
// or Headers classes directly | ||
Object.assign(fetchMock.config, { | ||
fetch: nodeFetch, | ||
Response: nodeFetch.Response, | ||
Request: nodeFetch.Request, | ||
Headers: nodeFetch.Headers, | ||
}); | ||
return fetchMock.fetchHandler; | ||
}); | ||
const nodeFetch = require('node-fetch'); | ||
|
||
it('runs a test', () => { | ||
fetchMock.get('http://a.com', 200); | ||
myAPI.call(); | ||
expect(fetchMock.called()).toBe(true); | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
{ | ||
"name": "@fetch-mock/codemods", | ||
"description": "Codemods for upgrading fetch-mock", | ||
"version": "0.0.0", | ||
"main": "./src/index.js", | ||
"type": "commonjs", | ||
"engines": { | ||
"node": ">=18.11.0" | ||
}, | ||
"repository": { | ||
"directory": "packages/codemods", | ||
"type": "git", | ||
"url": "git+https://github.com/wheresrhys/fetch-mock.git" | ||
}, | ||
"scripts": { | ||
"build": "echo 'no build'" | ||
}, | ||
"license": "MIT", | ||
"author": "Rhys Evans", | ||
"bugs": { | ||
"url": "https://github.com/wheresrhys/fetch-mock/issues" | ||
}, | ||
"homepage": "http://www.wheresrhys.co.uk/fetch-mock", | ||
"dependencies": { | ||
"jscodeshift": "^17.0.0" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
const fetchMock = require('fetch-mock'); | ||
fetchMock.mock('blah', 200); | ||
fm1.mock('blah', 200); | ||
fm2.mock('blah', 200); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import fetchMock from 'fetch-mock'; | ||
fetchMock.mock("blah", <div>Content</div>); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import fetchMock from 'fetch-mock'; | ||
function helper (res: number): void { | ||
fetchMock.mock("blah", <div>Content</div>); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
import fetchMock from 'fetch-mock'; | ||
function helper (res: number): void { | ||
fetchMock.mock("blah", res); | ||
}; |
94 changes: 94 additions & 0 deletions
94
packages/codemods/src/__tests__/identifying-fetchmock-instances.test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
import { describe, it, expect } from 'vitest'; | ||
import { codemod } from '../index'; | ||
import jscodeshift from 'jscodeshift'; | ||
|
||
function expectCodemodResult(src, expected) { | ||
expect(codemod(src, jscodeshift)).toEqual(expected); | ||
} | ||
|
||
describe('identifying fetch-mock instances', () => { | ||
it('cjs require() named fetchMock', () => { | ||
expectCodemodResult( | ||
` | ||
const fetchMock = require('fetch-mock'); | ||
fetchMock.mock("blah", 200) | ||
`, | ||
` | ||
const fetchMock = require('fetch-mock'); | ||
fetchMock.route("blah", 200) | ||
`, | ||
); | ||
}); | ||
it('cjs require() named something else', () => { | ||
expectCodemodResult( | ||
` | ||
const fetchNot = require('fetch-mock'); | ||
fetchNot.mock("blah", 200) | ||
`, | ||
` | ||
const fetchNot = require('fetch-mock'); | ||
fetchNot.route("blah", 200) | ||
`, | ||
); | ||
}); | ||
it('esm import named fetchMock', () => { | ||
expectCodemodResult( | ||
` | ||
import fetchMock from 'fetch-mock'; | ||
fetchMock.mock("blah", 200) | ||
`, | ||
` | ||
import fetchMock from 'fetch-mock'; | ||
fetchMock.route("blah", 200) | ||
`, | ||
); | ||
}); | ||
it('esm import named something else', () => { | ||
expectCodemodResult( | ||
` | ||
import fetchNot from 'fetch-mock'; | ||
fetchNot.mock("blah", 200) | ||
`, | ||
` | ||
import fetchNot from 'fetch-mock'; | ||
fetchNot.route("blah", 200) | ||
`, | ||
); | ||
}); | ||
it.skip('unassigned instances of require("fetch-mock")', () => { | ||
expectCodemodResult( | ||
`require('fetch-mock').mock("blah", 200)`, | ||
`require('fetch-mock').route("blah", 200)`, | ||
); | ||
}); | ||
it.skip('sandbox() instances', () => { | ||
expectCodemodResult( | ||
` | ||
const fetchMock = require('fetch-mock'); | ||
const fm = fetchMock.sandbox(); | ||
fm.mock("blah", 200) | ||
`, | ||
` | ||
const fetchMock = require('fetch-mock'); | ||
const fm = fetchMock.sandbox(); | ||
fm.route("blah", 200) | ||
`, | ||
); | ||
}); | ||
it.skip('sandbox() instances used by jest / vitest.mock', () => {}); | ||
it.skip('identify multiple instances on a page e.g. fetchMock and fm=fetchMock.sandbox()', () => {}); | ||
it.skip('identify when a fm instance with lots of chained methods is assigned to a new variable', () => { | ||
expectCodemodResult( | ||
` | ||
const fetchMock = require('fetch-mock'); | ||
const fm = fetchMock.get('a', 'b').get('a', 'b'); | ||
fm.mock("blah", 200) | ||
`, | ||
` | ||
const fetchMock = require('fetch-mock'); | ||
const fm = fetchMock.get('a', 'b').get('a', 'b'); | ||
fm.route("blah", 200) | ||
`, | ||
); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { it, describe, expect } from 'vitest'; | ||
import { promisify } from 'node:util'; | ||
import { exec as callbackExec } from 'node:child_process'; | ||
const exec = promisify(callbackExec); | ||
|
||
describe('integration', () => { | ||
it('can operate on typescript', async () => { | ||
const { stdout } = await exec( | ||
'jscodeshift --parser ts -p -d -t ./packages/codemods/src/index.js ./packages/codemods/src/__tests__/fixtures/typescript.ts', | ||
); | ||
expect(stdout).toContain(`import fetchMock from 'fetch-mock'; | ||
function helper (res: number): void { | ||
fetchMock.route("blah", res); | ||
}`); | ||
}); | ||
it('can operate on jsx', async () => { | ||
const { stdout } = await exec( | ||
'jscodeshift --parser ts -p -d -t ./packages/codemods/src/index.js ./packages/codemods/src/__tests__/fixtures/jsx.jsx', | ||
); | ||
expect(stdout).toContain(`import fetchMock from 'fetch-mock'; | ||
fetchMock.route("blah", <div>Content</div>);`); | ||
}); | ||
|
||
it('can operate on tsx', async () => { | ||
const { stdout } = await exec( | ||
'jscodeshift --parser ts -p -d -t ./packages/codemods/src/index.js ./packages/codemods/src/__tests__/fixtures/tsx.tsx', | ||
); | ||
expect(stdout).toContain(`import fetchMock from 'fetch-mock'; | ||
function helper (res: number): void { | ||
fetchMock.route("blah", <div>Content</div>); | ||
}`); | ||
}); | ||
it('allow passing in one or more additional variable names for fetch-mock', async () => { | ||
const { stdout } = await exec( | ||
'FM_VARIABLES=fm1,fm2 jscodeshift --parser ts -p -d -t ./packages/codemods/src/index.js ./packages/codemods/src/__tests__/fixtures/extra-vars.js', | ||
); | ||
expect(stdout).toContain(`const fetchMock = require('fetch-mock'); | ||
fetchMock.route('blah', 200); | ||
fm1.route('blah', 200); | ||
fm2.route('blah', 200);`); | ||
}); | ||
}); |
Oops, something went wrong.