mirror of
https://github.com/oceanprotocol/market.git
synced 2024-12-02 05:57:29 +01:00
Storybook and unit testing setup (#1354)
* install and config Storybook
* create basic component for initial setup
* added testing-library / jest for testing
* restore coverage for testing
* downgrade codeclimate-actions to fix viable formatter issue
* clean up - remove unnecessary components in stories
* update readme with storybook and testing
* remove unnecessary file extensions on stories config
* remove babel and moved jest-dom to devDependencies
* delete introduction stories
* change test to .tsx
* change testing description on docs
* added interface to button story
* added build phase and update test phase
* restore build in ci
* added storyshots (automate testing) to Storybook
* simplify and update codeclimate-action
* restore workable version of codeclimate-action
* test unified test action for CI
* Revert "test unified test action for CI"
This reverts commit 039cbf3485
.
* test documented solutions for CI
* fix error on coverage CI
* added codeCoverage on jest setup
* upload coverage report from jest
* added download artifact to coverage CI
* added upload artifact to coverage CI
* remove collectCoverageFrom
* moved test step on CI
* remove coverageLocations to allow default
* load Ocean typography into storybook
* skip all PRs coming from dependabot
* improve docs (Storybook)
Co-authored-by: Matthias Kretschmann <m@kretschmann.io>
This commit is contained in:
parent
6324e85b0f
commit
89f2521025
34
.github/workflows/ci.yml
vendored
34
.github/workflows/ci.yml
vendored
@ -37,19 +37,25 @@ jobs:
|
||||
restore-keys: ${{ runner.os }}-${{ matrix.node }}-build-${{ env.cache-name }}-
|
||||
|
||||
- run: npm ci
|
||||
- run: npm run codegen:apollo
|
||||
- run: npm run lint
|
||||
# - run: npm test
|
||||
- run: npm run build
|
||||
- run: npm test
|
||||
|
||||
# coverage:
|
||||
# runs-on: ubuntu-latest
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - uses: actions/setup-node@v2
|
||||
# - run: npm ci
|
||||
# - uses: paambaati/codeclimate-action@v2.7.5
|
||||
# env:
|
||||
# CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
||||
# with:
|
||||
# coverageCommand: npm test
|
||||
- name: Upload coverage
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: coverage
|
||||
path: coverage/
|
||||
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test]
|
||||
if: ${{ success() && github.actor != 'dependabot[bot]' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/download-artifact@v2
|
||||
with:
|
||||
name: coverage
|
||||
|
||||
- uses: paambaati/codeclimate-action@v2.7.1 # using 2.7.1 for issue: https://github.com/paambaati/codeclimate-action/issues/316
|
||||
env:
|
||||
CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }}
|
||||
|
20
.storybook/main.js
Normal file
20
.storybook/main.js
Normal file
@ -0,0 +1,20 @@
|
||||
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
stories: ['../src/**/*.stories.mdx', '../src/**/*.stories.tsx'],
|
||||
addons: [
|
||||
'@storybook/addon-links',
|
||||
'@storybook/addon-essentials',
|
||||
'@storybook/addon-interactions'
|
||||
],
|
||||
framework: '@storybook/react',
|
||||
webpackFinal: async (config) => {
|
||||
config.resolve.plugins = [
|
||||
...(config.resolve.plugins || []),
|
||||
new TsconfigPathsPlugin({
|
||||
extensions: config.resolve.extensions
|
||||
})
|
||||
]
|
||||
return config
|
||||
}
|
||||
}
|
12
.storybook/preview.js
Normal file
12
.storybook/preview.js
Normal file
@ -0,0 +1,12 @@
|
||||
import '@oceanprotocol/typographies/css/ocean-typo.css'
|
||||
import '../src/stylesGlobal/styles.css'
|
||||
|
||||
export const parameters = {
|
||||
actions: { argTypesRegex: '^on[A-Z].*' },
|
||||
controls: {
|
||||
matchers: {
|
||||
color: /(background|color)$/i,
|
||||
date: /Date$/
|
||||
}
|
||||
}
|
||||
}
|
14
.storybook/storyshots.test.tsx
Normal file
14
.storybook/storyshots.test.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
import initStoryshots from '@storybook/addon-storyshots'
|
||||
import { render, waitFor } from '@testing-library/react'
|
||||
|
||||
// Stories are render-tested with @testing-library/react,
|
||||
// overwriting default snapshot testing behavior
|
||||
initStoryshots({
|
||||
asyncJest: true,
|
||||
test: async ({ story, done }) => {
|
||||
const storyElement = story.render()
|
||||
// render the story with @testing-library/react
|
||||
render(storyElement)
|
||||
await waitFor(() => done())
|
||||
}
|
||||
})
|
41
README.md
41
README.md
@ -294,6 +294,47 @@ npm run lint
|
||||
npm run format
|
||||
```
|
||||
|
||||
## 👩🎤 Storybook
|
||||
|
||||
Storybook helps us build UI components in isolation from our app's business logic, data, and context. That makes it easy to develop hard-to-reach states and save these UI states as stories to revisit during development, testing, or QA.
|
||||
|
||||
To start adding stories, create a `index.stories.tsx` inside the component's folder:
|
||||
|
||||
<pre>
|
||||
src
|
||||
└─── components
|
||||
│ └─── @shared
|
||||
│ └─── <your component>
|
||||
│ │ index.tsx
|
||||
│ │ index.module.css
|
||||
│ │ <b>index.stories.tsx</b>
|
||||
│ │ index.test.tsx
|
||||
</pre>
|
||||
|
||||
You can also write a [test](https://storybook.js.org/docs/react/writing-tests/importing-stories-in-tests#example-with-testing-library) against your story by creating a `index.test.tsx` file.
|
||||
|
||||
Starting up the Storybook server with this command will make it accessible under `http://localhost:6006`:
|
||||
|
||||
```bash
|
||||
npm run storybook
|
||||
```
|
||||
|
||||
## 🤖 Testing
|
||||
|
||||
Interaction tests are setup with Storybook's Addon for [Testing Library](https://storybook.js.org/docs/react/writing-tests/importing-stories-in-tests#example-with-testing-library), which utilizes [Jest](https://jestjs.io/) as test runner. A combined coverage report is sent to CodeClimate via the coverage GitHub Actions job.
|
||||
|
||||
Executing linting, type checking, and interaction tests:
|
||||
|
||||
```bash
|
||||
npm run test
|
||||
```
|
||||
|
||||
Executing only interaction tests:
|
||||
|
||||
```bash
|
||||
npm run jest
|
||||
```
|
||||
|
||||
## 🛳 Production
|
||||
|
||||
To create a production build, run from the root of the project:
|
||||
|
29
jest.config.js
Normal file
29
jest.config.js
Normal file
@ -0,0 +1,29 @@
|
||||
const nextJest = require('next/jest')
|
||||
|
||||
const createJestConfig = nextJest({
|
||||
// Provide the path to your Next.js app to load next.config.js and .env files in your test environment
|
||||
dir: './'
|
||||
})
|
||||
|
||||
// Add any custom config to be passed to Jest
|
||||
const customJestConfig = {
|
||||
// Add more setup options before each test is run
|
||||
setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
|
||||
// if using TypeScript with a baseUrl set to the root directory then you need the below for alias' to work
|
||||
moduleDirectories: ['node_modules', '<rootDir>/'],
|
||||
testEnvironment: 'jest-environment-jsdom',
|
||||
moduleNameMapper: {
|
||||
// '^@/components/(.*)$': '<rootDir>/components/$1',
|
||||
'@shared(.*)$': '<rootDir>/src/components/@shared/$1',
|
||||
'@hooks/(.*)$': '<rootDir>/src/@hooks/$1',
|
||||
'@context/(.*)$': '<rootDir>/src/@context/$1',
|
||||
'@images/(.*)$': '<rootDir>/src/@images/$1',
|
||||
'@utils/(.*)$': '<rootDir>/src/@utils/$1',
|
||||
'@content/(.*)$': '<rootDir>/@content/$1'
|
||||
},
|
||||
collectCoverage: true,
|
||||
coverageReporters: ['lcov']
|
||||
}
|
||||
|
||||
// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
|
||||
module.exports = createJestConfig(customJestConfig)
|
1
jest.setup.js
Normal file
1
jest.setup.js
Normal file
@ -0,0 +1 @@
|
||||
import '@testing-library/jest-dom/extend-expect'
|
34451
package-lock.json
generated
34451
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
23
package.json
23
package.json
@ -9,13 +9,16 @@
|
||||
"build": "npm run pregenerate && next build",
|
||||
"serve": "serve -s public/",
|
||||
"pregenerate": "bash scripts/pregenerate.sh",
|
||||
"test": "npm run pregenerate && npm run lint && npm run type-check",
|
||||
"test": "npm run pregenerate && npm run lint && npm run type-check && npm run jest",
|
||||
"jest": "jest",
|
||||
"lint": "eslint --ignore-path .gitignore --ext .js --ext .ts --ext .tsx .",
|
||||
"format": "prettier --ignore-path .gitignore './**/*.{css,yml,js,ts,tsx,json}' --write",
|
||||
"type-check": "tsc --noEmit",
|
||||
"deploy:s3": "bash scripts/deploy-s3.sh",
|
||||
"postinstall": "husky install",
|
||||
"codegen:apollo": "apollo client:codegen --endpoint=https://v4.subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph --target typescript --tsFileExtension=d.ts --outputFlat src/@types/subgraph/"
|
||||
"codegen:apollo": "apollo client:codegen --endpoint=https://v4.subgraph.rinkeby.oceanprotocol.com/subgraphs/name/oceanprotocol/ocean-subgraph --target typescript --tsFileExtension=d.ts --outputFlat src/@types/subgraph/",
|
||||
"storybook": "start-storybook -p 6006",
|
||||
"build-storybook": "build-storybook"
|
||||
},
|
||||
"dependencies": {
|
||||
"@coingecko/cryptoformat": "^0.4.4",
|
||||
@ -24,6 +27,7 @@
|
||||
"@oceanprotocol/lib": "^1.0.0-next.37",
|
||||
"@oceanprotocol/typographies": "^0.1.0",
|
||||
"@portis/web3": "^4.0.7",
|
||||
"@storybook/addon-storyshots": "^6.4.22",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"@urql/exchange-refocus": "^0.2.5",
|
||||
"@walletconnect/web3-provider": "^1.7.7",
|
||||
@ -69,7 +73,17 @@
|
||||
"yup": "^0.32.11"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@storybook/addon-actions": "^6.4.22",
|
||||
"@storybook/addon-essentials": "^6.4.22",
|
||||
"@storybook/addon-interactions": "^6.4.22",
|
||||
"@storybook/addon-links": "^6.4.22",
|
||||
"@storybook/react": "^6.4.22",
|
||||
"@storybook/testing-library": "^0.0.9",
|
||||
"@storybook/testing-react": "^1.2.4",
|
||||
"@svgr/webpack": "^6.2.1",
|
||||
"@testing-library/jest-dom": "^5.16.4",
|
||||
"@testing-library/react": "^12.1.5",
|
||||
"@testing-library/user-event": "^14.1.1",
|
||||
"@types/chart.js": "^2.9.37",
|
||||
"@types/d3": "^7.1.0",
|
||||
"@types/js-cookie": "^3.0.1",
|
||||
@ -96,12 +110,15 @@
|
||||
"file-loader": "^6.2.0",
|
||||
"https-browserify": "^1.0.0",
|
||||
"husky": "^7.0.4",
|
||||
"jest": "^27.5.1",
|
||||
"prettier": "^2.6.0",
|
||||
"pretty-quick": "^3.1.3",
|
||||
"process": "^0.11.10",
|
||||
"serve": "^13.0.2",
|
||||
"stream-http": "^3.2.0",
|
||||
"typescript": "^4.6.3"
|
||||
"ts-jest": "^27.1.4",
|
||||
"tsconfig-paths-webpack-plugin": "^3.5.2",
|
||||
"typescript": "^4.5.4"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
35
src/components/@shared/atoms/Button/index.stories.tsx
Normal file
35
src/components/@shared/atoms/Button/index.stories.tsx
Normal file
@ -0,0 +1,35 @@
|
||||
import React from 'react'
|
||||
import { ComponentStory, ComponentMeta } from '@storybook/react'
|
||||
|
||||
import Button, { ButtonProps } from '@shared/atoms/Button'
|
||||
|
||||
// More on default export: https://storybook.js.org/docs/react/writing-stories/introduction#default-export
|
||||
export default {
|
||||
title: 'Component/@shared/atoms/Button',
|
||||
component: Button
|
||||
} as ComponentMeta<typeof Button>
|
||||
|
||||
// More on component templates: https://storybook.js.org/docs/react/writing-stories/introduction#using-args
|
||||
const Template: ComponentStory<typeof Button> = (args: ButtonProps) => (
|
||||
<Button {...args} />
|
||||
)
|
||||
|
||||
interface Props {
|
||||
args: {
|
||||
children: string
|
||||
style: string
|
||||
size: string
|
||||
onClick: any
|
||||
}
|
||||
}
|
||||
|
||||
export const Primary: Props = Template.bind({})
|
||||
// More on args: https://storybook.js.org/docs/react/writing-stories/args
|
||||
Primary.args = {
|
||||
children: 'Button',
|
||||
style: 'primary',
|
||||
size: 'small',
|
||||
onClick: () => {
|
||||
console.log('Button pressed!')
|
||||
}
|
||||
}
|
20
src/components/@shared/atoms/Button/index.test.tsx
Normal file
20
src/components/@shared/atoms/Button/index.test.tsx
Normal file
@ -0,0 +1,20 @@
|
||||
import React from 'react'
|
||||
import { render, screen } from '@testing-library/react'
|
||||
import { composeStory } from '@storybook/testing-react'
|
||||
import Meta, { Primary as PrimaryStory } from './index.stories'
|
||||
|
||||
// Returns a component that already contain all decorators from story level, meta level and global level.
|
||||
const Primary = composeStory(PrimaryStory, Meta)
|
||||
|
||||
test('onclick handler is called', () => {
|
||||
render(<Primary />)
|
||||
const buttonElement = screen.getByRole('button')
|
||||
buttonElement.click()
|
||||
})
|
||||
|
||||
test('test against args', () => {
|
||||
render(<Primary />)
|
||||
const buttonElement = screen.getByRole('button')
|
||||
// Testing against values coming from the story itself! No need for duplication
|
||||
expect(buttonElement.textContent).toEqual(Primary.args.children)
|
||||
})
|
@ -1,7 +1,7 @@
|
||||
import React, { ReactNode, FormEvent, ReactElement } from 'react'
|
||||
import Link from 'next/link'
|
||||
import classNames from 'classnames/bind'
|
||||
import styles from './Button.module.css'
|
||||
import styles from './index.module.css'
|
||||
|
||||
const cx = classNames.bind(styles)
|
||||
|
Loading…
Reference in New Issue
Block a user