Skip to content
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

Add commit hooks #170

Merged
merged 2 commits into from Aug 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/nightly.yml
Expand Up @@ -123,21 +123,21 @@ jobs:
- name: Fix local @storybook/csf version
run: |
yarn add @storybook/csf@0.0.2--canary.4566f4d.1

- name: Run test runner and expect failure
uses: mathiasvr/command-output@v1
with:
run: |
yarn build
yarn test-storybook:ci-failures

- name: Process test results
if: ${{ always() }}
id: tests
uses: sergeysova/jq-action@v2
with:
cmd: 'jq .numPassedTests test-results.json -r'

- name: Set failure check to env
if: ${{ always() }}
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Expand Up @@ -25,4 +25,4 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: |
yarn release
yarn release
2 changes: 1 addition & 1 deletion .github/workflows/stress-test.yml
Expand Up @@ -2,7 +2,7 @@ name: Stress Tests

on:
pull_request:
types: [ labeled ]
types: [labeled]

jobs:
test:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Expand Up @@ -24,7 +24,7 @@ jobs:
run: |
yarn build
yarn test-storybook:ci-coverage

- name: Generate code coverage
uses: codecov/codecov-action@v2
with:
Expand Down
4 changes: 4 additions & 0 deletions .husky/pre-commit
@@ -0,0 +1,4 @@
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
7 changes: 7 additions & 0 deletions .prettierignore
@@ -1 +1,8 @@
dist
node_modules
storybook-static
.cache
.env
*.snap
__snapshots__
CHANGELOG.md
16 changes: 8 additions & 8 deletions .storybook/main.js
Expand Up @@ -18,18 +18,18 @@ if (process.env.STRESS_TEST) {
stories.push('../stories/stress-test/*.stories.@(js|jsx|ts|tsx)');
}

if(process.env.TEST_FAILURES) {
if (process.env.TEST_FAILURES) {
stories = ['../stories/expected-failures/*.stories.@(js|jsx|ts|tsx)'];
}

const addons = [
process.env.WITHOUT_DOCS
? {
name: '@storybook/addon-essentials',
options: {
docs: false,
},
}
name: '@storybook/addon-essentials',
options: {
docs: false,
},
}
: '@storybook/addon-essentials',
'@storybook/addon-interactions',
'@storybook/addon-coverage',
Expand All @@ -43,6 +43,6 @@ module.exports = {
buildStoriesJson: true,
},
core: {
disableTelemetry: true
}
disableTelemetry: true,
},
};
2 changes: 1 addition & 1 deletion .vscode/settings.json
Expand Up @@ -5,4 +5,4 @@
"titleBar.activeBackground": "#004752",
"titleBar.activeForeground": "#ECFCFF"
}
}
}
56 changes: 29 additions & 27 deletions README.md
Expand Up @@ -50,6 +50,7 @@ See the announcement of Interaction Testing with Storybook in detail in [this bl
The Storybook test runner uses Jest as a runner, and Playwright as a testing framework. Each one of your `.stories` files is transformed into a spec file, and each story becomes a test, which is run in a headless browser.

The test runner is simple in design – it just visits each story from a running Storybook instance and makes sure the component is not failing:

- For stories without a `play` function, it verifies whether the story rendered without any errors. This is essentially a smoke test.
- For those with a `play` function, it also checks for errors in the `play` function and that all assertions passed. This is essentially an [interaction test](https://storybook.js.org/docs/react/writing-tests/interaction-testing#write-an-interaction-test).

Expand Down Expand Up @@ -299,7 +300,7 @@ The test runner supports code coverage with the `--coverage` flag or `STORYBOOK_

### 1 - Instrument the code

Given that your components' code runs in the context of a real browser, they have to be instrumented so that the test runner is able to collect coverage. This is done by configuring [istanbul](https://istanbul.js.org/) in your Storybook. You can achieve that in two different ways:
Given that your components' code runs in the context of a real browser, they have to be instrumented so that the test runner is able to collect coverage. This is done by configuring [istanbul](https://istanbul.js.org/) in your Storybook. You can achieve that in two different ways:

#### Using @storybook/addon-coverage

Expand All @@ -317,9 +318,7 @@ And register it in your `.storybook/main.js` file:
// .storybook/main.js
module.exports = {
// ...rest of your code here
addons: [
"@storybook/addon-coverage",
]
addons: ['@storybook/addon-coverage'],
};
```

Expand Down Expand Up @@ -359,7 +358,7 @@ If you want certain parts of your code to be deliberately ignored, you can use i

### 3 - Merging code coverage with coverage from other tools

The test runner reports coverage related to the `coverage/storybook/coverage-storybook.json` file. This is by design, showing you the coverage which is tested while running Storybook.
The test runner reports coverage related to the `coverage/storybook/coverage-storybook.json` file. This is by design, showing you the coverage which is tested while running Storybook.

Now, you might have other tests (e.g. unit tests) which are _not_ covered in Storybook but are covered when running tests with Jest, which you might also generate coverage files from, for instance. In such cases, if you are using tools like [Codecov](https://codecov.io/) to automate reporting, the coverage files will be detected automatically and if there are multiple files in the coverage folder, they will be merged automatically.

Expand Down Expand Up @@ -487,40 +486,43 @@ You can use it for multiple use cases, and here's an example that combines the s
// .storybook/test-runner.js
const { getStoryContext } = require('@storybook/test-runner');
const { injectAxe, checkA11y } = require('axe-playwright');

module.exports = {
async preRender(page, context) {
await injectAxe(page);
},
async postRender(page, context) {
// Get entire context of a story, including parameters, args, argTypes, etc.
const storyContext = await getStoryContext(page, context);

// Do not test a11y for stories that disable a11y
if (storyContext.parameters?.a11y?.disable) {
return;
}
await checkA11y(page, '#root', {
detailedReport: true,
detailedReportOptions: {
html: true,
},
// pass axe options defined in @storybook/addon-a11y
axeOptions: storyContext.parameters?.a11y?.options
})
},
async preRender(page, context) {
await injectAxe(page);
},
async postRender(page, context) {
// Get entire context of a story, including parameters, args, argTypes, etc.
const storyContext = await getStoryContext(page, context);

// Do not test a11y for stories that disable a11y
if (storyContext.parameters?.a11y?.disable) {
return;
}

await checkA11y(page, '#root', {
detailedReport: true,
detailedReportOptions: {
html: true,
},
// pass axe options defined in @storybook/addon-a11y
axeOptions: storyContext.parameters?.a11y?.options,
});
},
};
```

## Troubleshooting

#### Errors with Jest 28

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:

```sh
TypeError: Jest: Got error running globalSetup
reason: Class extends value #<Object> is not a constructor or null
```

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.

#### The error output in the CLI is too short
Expand Down
8 changes: 7 additions & 1 deletion package.json
Expand Up @@ -49,7 +49,8 @@
"test-storybook:ci-failures": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"TEST_FAILURES=1 yarn build-storybook --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && yarn test-storybook:failures\"",
"test-storybook:ci-coverage": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && yarn test-storybook --coverage\"",
"test-storybook:ci-json": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"yarn build-storybook --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && yarn test-storybook:json\"",
"generate-dynamic-stories": "node scripts/generate-dynamic-stories.js"
"generate-dynamic-stories": "node scripts/generate-dynamic-stories.js",
"prepare": "husky install"
},
"bin": {
"test-storybook": "./bin/test-storybook.js"
Expand Down Expand Up @@ -81,8 +82,10 @@
"babel-loader": "^8.1.0",
"babel-plugin-istanbul": "^6.1.1",
"concurrently": "^7.0.0",
"husky": "^8.0.0",
"jest": "^27.0.6",
"jest-image-snapshot": "^4.5.1",
"lint-staged": "^13.0.3",
"prettier": "^2.3.1",
"prop-types": "^15.7.2",
"react": "^17.0.1",
Expand All @@ -92,6 +95,9 @@
"typescript": "^4.2.4",
"wait-on": "^6.0.0"
},
"lint-staged": {
"*.{ts,js,tsx,jsx,css,md}": "prettier --write"
},
"publishConfig": {
"access": "public"
},
Expand Down
2 changes: 1 addition & 1 deletion playwright/custom-environment.js
Expand Up @@ -6,7 +6,7 @@ const PlaywrightEnvironment = require('jest-playwright-preset/lib/PlaywrightEnvi
class CustomEnvironment extends PlaywrightEnvironment {
async setup() {
await super.setup();
await setupPage(this.global.page)
await setupPage(this.global.page);
}

async teardown() {
Expand Down
2 changes: 1 addition & 1 deletion playwright/jest-setup.js
Expand Up @@ -13,4 +13,4 @@ if (testRunnerConfig) {
}
}

global.__sbCollectCoverage = process.env.STORYBOOK_COLLECT_COVERAGE === 'true'
global.__sbCollectCoverage = process.env.STORYBOOK_COLLECT_COVERAGE === 'true';
2 changes: 1 addition & 1 deletion playwright/test-runner-jest.config.js
Expand Up @@ -6,4 +6,4 @@ module.exports = {
/** Add your own overrides below
* @see https://jestjs.io/docs/configuration
*/
}
};
2 changes: 1 addition & 1 deletion playwright/transform.js
Expand Up @@ -15,7 +15,7 @@ module.exports = {
'@babel/preset-react',
],
});

return result ? result.code : src;
},
};
18 changes: 7 additions & 11 deletions stories/atoms/Button.jsx
@@ -1,20 +1,16 @@
import React from "react";
import PropTypes from "prop-types";
import "./button.css";
import React from 'react';
import PropTypes from 'prop-types';
import './button.css';

/**
* Primary UI component for user interaction
*/
export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
const mode = primary
? "storybook-button--primary"
: "storybook-button--secondary";
const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
return (
<button
type="button"
className={["storybook-button", `storybook-button--${size}`, mode].join(
" "
)}
className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
style={backgroundColor && { backgroundColor }}
{...props}
>
Expand All @@ -35,7 +31,7 @@ Button.propTypes = {
/**
* How large should the button be?
*/
size: PropTypes.oneOf(["small", "medium", "large"]),
size: PropTypes.oneOf(['small', 'medium', 'large']),
/**
* Button contents
*/
Expand All @@ -49,6 +45,6 @@ Button.propTypes = {
Button.defaultProps = {
backgroundColor: null,
primary: false,
size: "medium",
size: 'medium',
onClick: undefined,
};
15 changes: 5 additions & 10 deletions stories/atoms/Button.stories.js
@@ -1,11 +1,6 @@
import React from 'react';
import { expect } from '@storybook/jest';
import {
within,
waitFor,
userEvent,
waitForElementToBeRemoved,
} from '@storybook/testing-library';
import { within, waitFor, userEvent, waitForElementToBeRemoved } from '@storybook/testing-library';

import { Button } from './Button';

Expand All @@ -31,9 +26,9 @@ Secondary.args = {
};
Secondary.parameters = {
tests: {
disableSnapshots: true
}
}
disableSnapshots: true,
},
};

export const Demo = (args) => (
<button type="button" onClick={() => args.onSubmit('clicked')}>
Expand Down Expand Up @@ -113,4 +108,4 @@ WithLoaders.play = async ({ args, canvasElement }) => {
const todoItem = await canvas.findByText('Todo: delectus aut autem');
await userEvent.click(todoItem);
await expect(args.onSubmit).toHaveBeenCalledWith('delectus aut autem');
};
};
4 changes: 2 additions & 2 deletions stories/expected-failures/Failure.stories.jsx
Expand Up @@ -12,7 +12,7 @@ const Template = (args) => <Page {...args} />;

export const ComponentThrowsErrors = () => {
throw new Error('Component has a failure');
}
};

export const PlayFnThrowsErrors = Template.bind({});
PlayFnThrowsErrors.play = () => {
Expand All @@ -24,4 +24,4 @@ PlayFnAssertionFails.play = async ({ canvasElement }) => {
const canvas = within(canvasElement);
const unexistentButton = await canvas.getByRole('button', { name: /I do not exist/i });
await userEvent.click(unexistentButton);
};
};
2 changes: 1 addition & 1 deletion test-runner-jest.config.js
Expand Up @@ -16,6 +16,6 @@ module.exports = {
setupFilesAfterEnv: ['./playwright/jest-setup.js'],
// use local build when the package is referred
moduleNameMapper: {
'@storybook/test-runner': '<rootDir>/dist/cjs/index.js'
'@storybook/test-runner': '<rootDir>/dist/cjs/index.js',
},
};
6 changes: 2 additions & 4 deletions tsconfig.json
Expand Up @@ -17,8 +17,6 @@
"types": ["jest", "node"],
"moduleResolution": "node"
},
"include": [
"src/**/*.ts"
],
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}
}