Skip to content

Instantly share code, notes, and snippets.

@wojtekmaj
Last active November 7, 2024 13:57
Show Gist options
  • Save wojtekmaj/6defa1f358daae28bd52b7b6dbeb7ab6 to your computer and use it in GitHub Desktop.
Save wojtekmaj/6defa1f358daae28bd52b7b6dbeb7ab6 to your computer and use it in GitHub Desktop.
Automatically migrate Jest project to Vitest
#!/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 ""
@hinogi
Copy link

hinogi commented Jan 16, 2024

really nice

@valneras
Copy link

thanks a lot for this script.

On my side I wanted to keep temporarily both frameworks so quicky move from one to another.
FYI here are the scripts I made to switch from Jest vo Vitest or Vitest to Jest: https://github.com/valneras/jest-vs-vitest/blob/main/scripts/
For the moment I have a blocking issue regarding debugging that is not working in Webstorm or VSCode :( (see vitest-dev/vitest#4643)

@dev-zuo
Copy link

dev-zuo commented Sep 27, 2024

nice +1

@prescottprue
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment