Skip to content

Commit

Permalink
Merge pull request #154 from andykenward/feat/jest-28
Browse files Browse the repository at this point in the history
feat: jest 28 support
  • Loading branch information
yannbf committed Aug 5, 2022
2 parents 91cf672 + 2c6b041 commit 561bc7a
Show file tree
Hide file tree
Showing 5 changed files with 798 additions and 725 deletions.
30 changes: 22 additions & 8 deletions README.md
Expand Up @@ -24,7 +24,7 @@ Storybook test runner turns all of your stories into executable tests.
- [Image snapshot recipe](#image-snapshot-recipe)
- [Render lifecycle](#render-lifecycle)
- [Troubleshooting](#troubleshooting)
- [Errors with Jest 28](#errors-with-jest-28)
- [Jest 27 support](#jest-27-support)
- [The error output in the CLI is too short](#the-error-output-in-the-cli-is-too-short)
- [The test runner seems flaky and keeps timing out](#the-test-runner-seems-flaky-and-keeps-timing-out)
- [The test runner reports "No tests found" running on a Windows CI](#the-test-runner-reports-"no-tests-found"-running-on-a-windows-ci)
Expand Down Expand Up @@ -68,7 +68,7 @@ yarn add @storybook/test-runner -D
Jest is a peer dependency. If you don't have it, also install it

```jsx
yarn add jest@27 -D
yarn add jest -D
```

<details>
Expand Down Expand Up @@ -513,16 +513,30 @@ module.exports = {
## Troubleshooting
#### Errors with Jest 28
#### Jest 27 support
Jest 28 has been released, but unfortunately `jest-playwright` is not yet compatible with it, therefore the test-runner is also not compatible. You likely are having an issue that looks like this:
[`jest-playwright` 2.0.0](https://github.com/playwright-community/jest-playwright/releases/tag/v2.0.0) has a breaking change of requiring Jest 28+. To support older versions of Jest you will need to use [Yarn resolutions](https://yarnpkg.com/configuration/manifest/#resolutions) or [NPM overrides](https://docs.npmjs.com/cli/v8/configuring-npm/package-json#overrides) to downgrade `jest-playwright-preset` to `^1.7.2`.
```sh
TypeError: Jest: Got error running globalSetup
reason: Class extends value #<Object> is not a constructor or null
```json
// Yarn resolutions in your projects package.json
{
"resolutions": {
"jest-playwright-preset": "^1.7.2"
}
}
```
As soon as `jest-playwright` is compatible, so the test-runner will be too. Please follow [this issue](https://github.com/storybookjs/test-runner/issues/99) for updates.
```json
// NPM overrides in your projects package.json
{
"overrides": {
"@storybook/test-runner": {
"jest-playwright-preset": "^1.7.2"
}
}
}
```
#### The error output in the CLI is too short
Expand Down
40 changes: 16 additions & 24 deletions bin/test-storybook.js
Expand Up @@ -40,15 +40,15 @@ const cleanup = () => {
let isWatchMode = false;
async function reportCoverage() {
if (isWatchMode || process.env.STORYBOOK_COLLECT_COVERAGE !== 'true') {
return
return;
}

const coverageFolderE2E = path.resolve(process.cwd(), '.nyc_output');
const coverageFolder = path.resolve(process.cwd(), 'coverage/storybook');

// in case something goes wrong and .nyc_output does not exist, bail
if (!fs.existsSync(coverageFolderE2E)) {
return
return;
}

// if there's no coverage folder, create one
Expand All @@ -57,22 +57,21 @@ async function reportCoverage() {
}

// move the coverage files from .nyc_output folder (coming from jest-playwright) to coverage, then delete .nyc_output
fs.renameSync(
`${coverageFolderE2E}/coverage.json`,
`${coverageFolder}/coverage-storybook.json`,
);
fs.renameSync(`${coverageFolderE2E}/coverage.json`, `${coverageFolder}/coverage-storybook.json`);
fs.rmSync(coverageFolderE2E, { recursive: true });

// --skip-full in case we only want to show not fully covered code
// --check-coverage if we want to break if coverage reaches certain threshold
// .nycrc will be respected for thresholds etc. https://www.npmjs.com/package/nyc#coverage-thresholds
execSync(`npx nyc report --reporter=text -t ${coverageFolder} --report-dir ${coverageFolder}`, { stdio: 'inherit' })
execSync(`npx nyc report --reporter=text -t ${coverageFolder} --report-dir ${coverageFolder}`, {
stdio: 'inherit',
});
}

const onProcessEnd = () => {
cleanup();
reportCoverage();
}
};

process.on('SIGINT', onProcessEnd);
process.on('exit', onProcessEnd);
Expand Down Expand Up @@ -101,16 +100,12 @@ function sanitizeURL(url) {
const checkForIncompatibilities = () => {
try {
const jestVersion = require('jest/package.json').version;
if (semver.gte(jestVersion, '28.0.0')) {
error(dedent`We detected that your project is using Jest 28.0.0 or higher, which is currently incompatible with the test runner.
You can find more info at: https://github.com/storybookjs/test-runner#errors-with-jest-28
`);
process.exit(1);
if (semver.valid(jestVersion) === null) {
throw new Error('Not valid Jest version or no Jest installed');
}
} catch (err) {
error(
'We detected that Jest is not installed in your project. Please install Jest@27 and run test-storybook again.'
'We detected that Jest is not installed in your project. Please install Jest and run test-storybook again.'
);
process.exit(1);
}
Expand Down Expand Up @@ -151,23 +146,20 @@ async function getIndexJson(url) {
const indexJsonUrl = new URL('index.json', url).toString();
const storiesJsonUrl = new URL('stories.json', url).toString();

const [indexRes, storiesRes] = await Promise.all([
fetch(indexJsonUrl),
fetch(storiesJsonUrl)
]);
const [indexRes, storiesRes] = await Promise.all([fetch(indexJsonUrl), fetch(storiesJsonUrl)]);

if (indexRes.ok) {
try {
const json = await indexRes.text();
return JSON.parse(json);
} catch (err) { }
} catch (err) {}
}

if(storiesRes.ok) {
if (storiesRes.ok) {
try {
const json = await storiesRes.text();
return JSON.parse(json);
} catch (err) { }
} catch (err) {}
}

throw new Error(dedent`
Expand All @@ -187,7 +179,7 @@ async function getIndexJson(url) {
async function getIndexTempDir(url) {
let tmpDir;
try {
const indexJson = await getIndexJson(url)
const indexJson = await getIndexJson(url);
const titleIdToTest = transformPlaywrightJson(indexJson);

tmpDir = tempy.directory();
Expand Down Expand Up @@ -236,7 +228,7 @@ const main = async () => {
const rawTargetURL = process.env.TARGET_URL || runnerOptions.url || 'http://localhost:6006';
await checkStorybook(rawTargetURL);

const targetURL = sanitizeURL(rawTargetURL)
const targetURL = sanitizeURL(rawTargetURL);

process.env.TARGET_URL = targetURL;

Expand Down
18 changes: 9 additions & 9 deletions package.json
Expand Up @@ -61,7 +61,7 @@
"@babel/preset-typescript": "^7.13.0",
"@babel/template": "^7.14.5",
"@babel/types": "^7.14.8",
"@jest/types": "^27.0.6",
"@jest/types": "^28.1.3",
"@storybook/addon-coverage": "^0.0.1",
"@storybook/addon-essentials": "^6.5.0",
"@storybook/addon-interactions": "^6.5.0",
Expand All @@ -72,21 +72,21 @@
"@testing-library/dom": "^8.1.0",
"@testing-library/react": "^12.0.0",
"@testing-library/user-event": "^13.2.1",
"@types/jest": "^27.0.3",
"@types/jest": "^28.1.6",
"@types/node": "^16.4.1",
"auto": "^10.3.0",
"babel-jest": "^27.0.6",
"babel-jest": "^28.1.3",
"babel-loader": "^8.1.0",
"babel-plugin-istanbul": "^6.1.1",
"concurrently": "^7.0.0",
"jest": "^27.0.6",
"jest-image-snapshot": "^4.5.1",
"jest": "^28.1.3",
"jest-image-snapshot": "^5.1.0",
"prettier": "^2.3.1",
"prop-types": "^15.7.2",
"react": "^17.0.1",
"react-dom": "^17.0.1",
"rimraf": "^3.0.2",
"ts-jest": "^27.0.4",
"ts-jest": "^28.0.7",
"typescript": "^4.2.4",
"wait-on": "^6.0.0"
},
Expand All @@ -105,9 +105,9 @@
"can-bind-to-host": "^1.1.1",
"commander": "^9.0.0",
"global": "^4.4.0",
"jest-playwright-preset": "^1.7.2",
"jest-playwright-preset": "^1.7.2 || ^2.0.0",
"jest-serializer-html": "^7.1.0",
"jest-watch-typeahead": "^1.0.0",
"jest-watch-typeahead": "^2.0.0",
"node-fetch": "^2",
"playwright": "^1.14.0",
"semver": "^7.3.7",
Expand All @@ -118,7 +118,7 @@
"@storybook/core-common": "^6.5.0",
"@storybook/csf-tools": "^6.5.0",
"@storybook/store": "^6.5.0",
"jest": "^26.6.3 || ^27.0.0"
"jest": "^26.6.3 || ^27.0.0 || ^28.0.0"
},
"auto": {
"plugins": [
Expand Down
21 changes: 20 additions & 1 deletion playwright/transform.js
@@ -1,4 +1,5 @@
const { transform: babelTransform } = require('@babel/core');
const semver = require('semver');
const { transformPlaywright } = require('../dist/cjs/playwright/transformPlaywright');

module.exports = {
Expand All @@ -15,6 +16,24 @@ module.exports = {
'@babel/preset-react',
],
});
return result ? result.code : src;

if (result) {
/**
* To support Jest 28 we need to check the version of Jest used in the project.
* As process() and processAsync() methods of a custom transformer module cannot return a string anymore.
* They must always return an object. See https://jestjs.io/docs/upgrading-to-jest28#transformer
*/
const jestVersion = require('jest/package.json').version;

if (semver.lte(jestVersion, '28.0.0')) {
return result.code;
}

return {
code: result.code,
};
}

return src;
},
};

0 comments on commit 561bc7a

Please sign in to comment.