-
-
Notifications
You must be signed in to change notification settings - Fork 78
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
First class support for CRA #102
Changes from all commits
873101f
bf0022b
b77f928
2e89229
dc72664
da23a81
625f4fb
ec099c9
2b754bf
d00f12f
7db9b84
42f94af
8f52c4a
9a0235a
7cab2e2
304a64a
8de7b09
a0cdc42
fd79c7d
4deda01
1783529
d829df0
bfdd733
cd38dfc
dc923dd
45ff575
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
#!/usr/bin/env node | ||
// @ts-check | ||
const path = require('path'); | ||
const fs = require('fs'); | ||
// Append current node_modules to the module search path, so require('react-scripts') can work. | ||
module.paths.push(path.resolve(process.cwd(), './node_modules')); | ||
|
||
// 1. Create `jest.config.js` | ||
try { | ||
// @ts-expect-error This is meant to run where react-scripts is installed | ||
const createJestConfig = require('react-scripts/scripts/utils/createJestConfig.js'); | ||
const jestConfig = createJestConfig( | ||
(/** @type {string} */ filePath) => path.posix.join('<rootDir>', filePath), | ||
null, | ||
true, | ||
); | ||
jestConfig.transform = { | ||
'^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': | ||
'react-scripts/config/jest/babelTransform.js', | ||
'^.+\\.(css|scss|sass)$': 'jest-preview/transforms/css', | ||
'^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': | ||
'jest-preview/transforms/fileCRA', | ||
}; | ||
jestConfig.transformIgnorePatterns = | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we do a check first to ensure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need since we are targeting un-ejected CRA apps. See other comments for more details |
||
jestConfig.transformIgnorePatterns.filter( | ||
(/** @type {string} */ pattern) => | ||
pattern !== '^.+\\.module\\.(css|sass|scss)$', | ||
); | ||
delete jestConfig.moduleNameMapper['^.+\\.module\\.(css|sass|scss)$']; | ||
|
||
const jestConfigFileContent = `module.exports = ${JSON.stringify( | ||
jestConfig, | ||
null, | ||
2, | ||
)}\n`; | ||
fs.writeFileSync('jest.config.js', jestConfigFileContent); | ||
console.log(`Added jest.config.js to the project.`); | ||
|
||
// Try to prettier `jest.config.js` | ||
const execSync = require('child_process').execSync; | ||
try { | ||
execSync('prettier jest.config.js --write'); | ||
} catch (error) { | ||
// Just ignore if user doesn't have prettier installed | ||
} | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
|
||
// 2. Create `scripts/test.js` | ||
// https://github.com/facebook/create-react-app/blob/f99167c014a728ec856bda14f87181d90b050813/packages/react-scripts/scripts/eject.js#L158-L162 | ||
|
||
try { | ||
const testFile = path.resolve( | ||
process.cwd(), | ||
'./node_modules/react-scripts/scripts/test.js', | ||
); | ||
let content = fs.readFileSync(testFile, 'utf8'); | ||
|
||
content = | ||
content | ||
// Remove dead code from .js files on eject | ||
.replace( | ||
/\/\/ @remove-on-eject-begin([\s\S]*?)\/\/ @remove-on-eject-end/gm, | ||
'', | ||
) | ||
// Require `env` direct from `react-scripts` | ||
.replace( | ||
`require('../config/env');`, | ||
`require('react-scripts/config/env');`, | ||
) | ||
.trim() + '\n'; | ||
console.log(`Added scripts/test.js to the project.`); | ||
if (!fs.existsSync('scripts')) { | ||
fs.mkdirSync('scripts'); | ||
} | ||
fs.writeFileSync(path.resolve(process.cwd(), 'scripts/test.js'), content); | ||
|
||
// 3. Update `package.json` | ||
const packageJson = require(path.resolve(process.cwd(), 'package.json')); | ||
packageJson.scripts.test = 'node scripts/test.js'; | ||
if (!packageJson.scripts['jest-preview']) { | ||
packageJson.scripts['jest-preview'] = 'jest-preview'; | ||
} | ||
fs.writeFileSync( | ||
path.resolve(process.cwd(), 'package.json'), | ||
JSON.stringify(packageJson, null, 2) + '\n', | ||
); | ||
console.log(`Update test script in package.json.`); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
|
||
// 4. Configure Jest Preview using jestPreviewConfigure in `src/setupTests.ts` or `src/setupTests.js` | ||
/** | ||
* @param {string} filePath | ||
* @param {string} content | ||
*/ | ||
function injectToFileIfExisted(filePath, content) { | ||
if (fs.existsSync(filePath)) { | ||
fs.appendFileSync(filePath, content); | ||
} | ||
} | ||
|
||
try { | ||
const configToInject = `import { jestPreviewConfigure } from 'jest-preview' | ||
|
||
jestPreviewConfigure({ | ||
// Opt-in to automatic mode to preview failed test case. | ||
autoPreview: true, | ||
// TODO: To add your global css here | ||
externalCss: ['src/index.css'], | ||
}) | ||
`; | ||
injectToFileIfExisted( | ||
path.resolve(process.cwd(), 'src/setupTests.ts'), | ||
configToInject, | ||
); | ||
injectToFileIfExisted( | ||
path.resolve(process.cwd(), 'src/setupTests.js'), | ||
configToInject, | ||
); | ||
console.log(`Configured Jest Preview in src/setupTests.(ts|js).`); | ||
} catch (error) { | ||
console.error(error); | ||
} | ||
|
||
console.log( | ||
'\nTo continue, run `npm run jest-preview` to open Jest Preview server, then `npm run test` to run Jest. It will preview any failed test in your browser.', | ||
); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
#!/usr/bin/env node | ||
const { program } = require('commander'); | ||
|
||
program | ||
.command('config-cra') | ||
.description('Integrate Jest Preview with CRA.') | ||
.action(() => { | ||
require('./configCra'); | ||
}); | ||
|
||
program.description('Start Jest Preview server.').action(() => { | ||
require('./server/previewServer'); | ||
}); | ||
|
||
program.parse(process.argv); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,27 +1,4 @@ | ||
# Use with create-react-app | ||
|
||
This example demonstrates how to use `jest-preview` with `create-react-app`. | ||
|
||
## Setup jest with create-react-app | ||
|
||
jest is setup with create-react-app by default, we don't need to do anything more | ||
|
||
## Installation and Usage | ||
|
||
Please refer to [Installation](../../README.md#installation) and [Usage](../../README.md#installation). | ||
Except for step 2 of installation: **Configure jest's transform to transform CSS and files** | ||
|
||
- Because `create-react-app` allows user to [use svg files as React components](https://create-react-app.dev/docs/adding-images-fonts-and-files/#adding-svgs), `jest-preview` therefore needs to support that, we update Jest's configuration in `package.json` as follow: | ||
|
||
```json | ||
{ | ||
"transform": { | ||
// Other transforms | ||
"^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "jest-preview/transforms/fileCRA" | ||
} | ||
} | ||
``` | ||
|
||
## Caveats | ||
|
||
Even though `jest-preview` itself supports CSS Modules, it doesn't support `create-react-app` without ejecting yet. The support will land in the next version. | ||
See the documentation at [Create React App | ||
example](https://www.jest-preview.com/docs/examples/create-react-app) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
module.exports = { | ||
roots: ['<rootDir>/src'], | ||
collectCoverageFrom: ['src/**/*.{js,jsx,ts,tsx}', '!src/**/*.d.ts'], | ||
setupFiles: ['react-app-polyfill/jsdom'], | ||
// Use `setupTests.js` if you use javascript | ||
setupFilesAfterEnv: ['<rootDir>/src/setupTests.ts'], | ||
testMatch: [ | ||
'<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}', | ||
'<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}', | ||
], | ||
testEnvironment: 'jsdom', | ||
testRunner: 'jest-circus/runner.js', | ||
transform: { | ||
'^.+\\.(js|jsx|mjs|cjs|ts|tsx)$': | ||
'react-scripts/config/jest/babelTransform.js', | ||
// Update the regex to support css and sass | ||
'^.+\\.(css|scss|sass)$': 'jest-preview/transforms/css', | ||
'^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)': | ||
'jest-preview/transforms/fileCRA', | ||
}, | ||
transformIgnorePatterns: [ | ||
'[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$', | ||
// Remove to support CSS Modules | ||
// '^.+\\.module\\.(css|sass|scss)$', | ||
], | ||
modulePaths: [], | ||
moduleNameMapper: { | ||
'^react-native$': 'react-native-web', | ||
// Remove to support CSS Modules | ||
// '^.+\\.module\\.(css|sass|scss)$': 'identity-obj-proxy', | ||
}, | ||
moduleFileExtensions: [ | ||
'web.js', | ||
'js', | ||
'web.ts', | ||
'ts', | ||
'web.tsx', | ||
'tsx', | ||
'json', | ||
'web.jsx', | ||
'jsx', | ||
'node', | ||
], | ||
watchPlugins: [ | ||
'jest-watch-typeahead/filename', | ||
'jest-watch-typeahead/testname', | ||
], | ||
resetMocks: true, | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
'use strict'; | ||
|
||
// Do this as the first thing so that any code reading it knows the right env. | ||
process.env.BABEL_ENV = 'test'; | ||
process.env.NODE_ENV = 'test'; | ||
process.env.PUBLIC_URL = ''; | ||
|
||
// Makes the script crash on unhandled rejections instead of silently | ||
// ignoring them. In the future, promise rejections that are not handled will | ||
// terminate the Node.js process with a non-zero exit code. | ||
process.on('unhandledRejection', (err) => { | ||
throw err; | ||
}); | ||
|
||
// Ensure environment variables are read. | ||
// Read config from react-scripts directly | ||
require('react-scripts/config/env'); | ||
|
||
const jest = require('jest'); | ||
const execSync = require('child_process').execSync; | ||
let argv = process.argv.slice(2); | ||
|
||
function isInGitRepository() { | ||
try { | ||
execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' }); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
|
||
function isInMercurialRepository() { | ||
try { | ||
execSync('hg --cwd . root', { stdio: 'ignore' }); | ||
return true; | ||
} catch (e) { | ||
return false; | ||
} | ||
} | ||
|
||
// Watch unless on CI or explicitly running all tests | ||
if ( | ||
!process.env.CI && | ||
argv.indexOf('--watchAll') === -1 && | ||
argv.indexOf('--watchAll=false') === -1 | ||
) { | ||
// https://github.com/facebook/create-react-app/issues/5210 | ||
const hasSourceControl = isInGitRepository() || isInMercurialRepository(); | ||
argv.push(hasSourceControl ? '--watch' : '--watchAll'); | ||
} | ||
|
||
jest.run(argv); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.textOrange { | ||
color: orange; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we add these keys to the existing
transform
instead of overwritingtransform
like this?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. This is intentional. We would like to remove
'^.+\\.css$'
. Without removing this, it causes some issues that makes Sass not work in #101