Last active
November 7, 2024 13:57
-
-
Save wojtekmaj/6defa1f358daae28bd52b7b6dbeb7ab6 to your computer and use it in GitHub Desktop.
Automatically migrate Jest project to Vitest
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
#!/bin/bash | |
# Ensure we're working on the latest version of the main branch | |
git switch main | |
git fetch | |
git pull | |
# Create a new branch | |
git switch -c vitest | |
# Define functions | |
add_vitest_config() { | |
# Add Vitest config | |
echo "import { defineConfig } from 'vitest/config'; | |
export default defineConfig({ | |
test: { | |
environment: 'happy-dom', | |
setupFiles: 'vitest.setup.ts', | |
}, | |
});" > vitest.config.ts | |
} | |
add_vitest_setup() { | |
# Add Vitest setup | |
echo "import { afterEach } from 'vitest'; | |
import { cleanup } from '@testing-library/react'; | |
import '@testing-library/jest-dom/vitest'; | |
afterEach(() => { | |
cleanup(); | |
});" > vitest.setup.ts | |
} | |
add_vitest_script() { | |
# Remove Jest script | |
npm pkg delete scripts.jest | |
# Add Vitest scripts | |
npm pkg set scripts.unit="vitest run" | |
# Replace `yarn jest`` with `yarn unit` in test script | |
val=$(npm pkg get scripts.test) | |
replaced_val=${val//jest/unit} | |
# Get rid of double quotes | |
replaced_val=${replaced_val//\"/} | |
npm pkg set scripts.test=$replaced_val | |
} | |
join_by() { | |
local d=${1-} f=${2-} | |
if shift 2; then | |
printf %s "$f" "${@/#/$d}" | |
fi | |
} | |
# Migrate | |
# Add Vitest files | |
yarn add vitest happy-dom --dev | |
# Remove Babel and Jest files | |
rm -rf .babelrc jest.config.js | |
yarn remove @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript @types/jest jest jest-environment-jsdom | |
# Dedupe dependencies | |
yarn dedupe | |
# Add Vitest config | |
add_vitest_config | |
# Add Vitest setup | |
add_vitest_setup | |
# Add Vitest script | |
add_vitest_script | |
# Go to src directory (assuming all test files are there) | |
cd src | |
# Find all *.spec.js, *.spec.ts, *.spec.jsx, and *.spec.tsx files. For each of them: | |
# - Replace "jest.*" with "vi.*" | |
# - Attempt to replace "jest.mock" and "jest.requireActual" with "vi.mock" and "vi.importActual", respectively. | |
# - Add "import { describe, expect, it, … } from 'vitest';" to the top of the file | |
files=$(find . -type f -name "*.spec.js" -o -name "*.spec.ts" -o -name "*.spec.jsx" -o -name "*.spec.tsx" -o -name "*.test.js" -o -name "*.test.ts" -o -name "*.test.jsx" -o -name "*.test.tsx" | grep -v "node_modules") | |
for file in $files; do | |
echo "Processing $file" | |
# Detect if file contains "from ["']vitest["']". If so, skip it. | |
if grep -q "from ['\"]vitest['\"]" $file; then | |
echo " Test file appears to have already been migrated to Vitest. Skipping" | |
continue | |
fi | |
# Detect if file contains "from ["']@playwright/test["']". If so, skip it. | |
if grep -q "from ['\"]@playwright\/test['\"]" $file; then | |
echo " Test file appears to be using Playwright. Skipping" | |
continue | |
fi | |
# Replace "jest.clearAllMocks" with "vi.clearAllMocks" | |
sed -i '' 's/jest.clearAllMocks/vi.clearAllMocks/g' $file | |
# Replace "jest.fn" with "vi.fn" | |
sed -i '' 's/jest.fn/vi.fn/g' $file | |
# Replace "jest.mocked" with "vi.mocked" | |
sed -i '' 's/jest.mocked/vi.mocked/g' $file | |
# Replace "jest.resetAllMocks" with "vi.resetAllMocks" | |
sed -i '' 's/jest.resetAllMocks/vi.resetAllMocks/g' $file | |
# Replace "jest.resetModules" with "vi.resetModules" | |
sed -i '' 's/jest.resetModules/vi.resetModules/g' $file | |
# Replace "jest.spyOn" with "vi.spyOn" | |
sed -i '' 's/jest.spyOn/vi.spyOn/g' $file | |
# Replace "jest.useFakeTimers" with "vi.useFakeTimers" | |
sed -i '' 's/jest.useFakeTimers/vi.useFakeTimers/g' $file | |
# Replace "jest.useRealTimers" with "vi.useRealTimers" | |
sed -i '' 's/jest.useRealTimers/vi.useRealTimers/g' $file | |
# Replace "advanceTimers: jest.advanceTimersByTime" with "advanceTimers: vi.advanceTimersByTime.bind(vi)" | |
sed -i '' 's/advanceTimers: jest.advanceTimersByTime/advanceTimers: vi.advanceTimersByTime.bind(vi)/g' $file | |
# Detect jest.mock(). Since vi.mock() uses ESM modules, chances are manual changes | |
# are going to be necessary. So, we'll print a warning. | |
if grep -q "jest.mock(" $file; then | |
echo " Warning: $file contained jest.mock(). You'll likely need to manually fix vi.mock() implementation." | |
sed -i '' 's/jest.mock/vi.mock/g' $file | |
fi | |
# Detect jest.requireActual(). Since vi.importActual() uses ESM modules, chances are | |
# manual changes are going to be necessary. So, we'll print a warning. | |
if grep -q "jest.requireActual(" $file; then | |
echo " Warning: $file contained jest.requireActual(). You'll likely need to manually fix vi.importActual() implementation." | |
sed -i '' 's/jest.requireActual/vi.importActual/g' $file | |
fi | |
# Replace jest.setTimeout() with vi.setConfig({ testTimeout: N }) | |
if grep -q "jest.setTimeout(" $file; then | |
echo " TODO" | |
fi | |
# Define list of required imports for each file | |
# Clear the array of imports | |
imports=() | |
# If file contains "afterAll", add it to the list of required imports | |
if grep -q "afterAll" $file; then | |
imports[0]="afterAll" | |
fi | |
# If file contains "afterEach", add it to the list of required imports | |
if grep -q "afterEach" $file; then | |
imports[0]="afterEach" | |
fi | |
# If file contains "beforeAll", add it to the list of required imports | |
if grep -q "beforeAll" $file; then | |
imports[1]="beforeAll" | |
fi | |
# If file contains "beforeEach", add it to the list of required imports | |
if grep -q "beforeEach" $file; then | |
imports[1]="beforeEach" | |
fi | |
imports[2]="describe" | |
imports[3]="expect" | |
imports[4]="it" | |
# If file contains "vi.", add it to the list of required imports | |
if grep -q "vi\." $file; then | |
imports[5]="vi" | |
fi | |
# Join list of required imports into a string | |
imports_string=$(join_by ", " "${imports[@]}") | |
# Add "import { … } from 'vitest';" to the top of the file. | |
sed -i '' '1i\ | |
import { '"$imports_string"' } from "vitest"; | |
' $file | |
echo " Done" | |
done | |
# Return to root directory | |
cd ../ | |
# In .github/workflows/ci.yml, replace "yarn jest" with "yarn unit" | |
sed -i '' 's/yarn jest/yarn unit/g' .github/workflows/ci.yml | |
yarn prettier --write . | |
# Run tests to be sure everything is working, exit if not | |
yarn unit || exit 1 | |
# Run lint to be sure everything is working, exit if not | |
yarn lint || exit 1 | |
# Commit all updates | |
git add . | |
git commit -m "Migrate from Jest to Vitest" | |
# Push changes to remote | |
git push | |
gh pr create --title "Migrate from Jest to Vitest" --body "" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Awesome thanks for writing this - used this for the basis of my own script - there were some things I added like beforeAll, afterAll, test, testing for deps to remove etc
Here it is if anyone is interested: https://gist.github.com/prescottprue/e8f2a437e647285340dea50a495a811b#file-jest-to-vitest-sh