1
0
mirror of https://github.com/kremalicious/blog.git synced 2024-12-22 09:13:35 +01:00

setup testing

This commit is contained in:
Matthias Kretschmann 2019-05-02 21:11:52 +02:00
parent fde55614a0
commit 1a0a5d5b24
Signed by: m
GPG Key ID: 606EEEF3C479A91F
19 changed files with 251 additions and 94 deletions

View File

@ -17,7 +17,8 @@
"env": {
"browser": true,
"node": true,
"es6": true
"es6": true,
"jest": true
},
"rules": {
"quotes": ["error", "single"],

1
.gitignore vendored
View File

@ -6,3 +6,4 @@ public
.cache
src/components/svg
plugins/gatsby-redirect-from
coverage

View File

@ -18,10 +18,19 @@ before_install:
- sudo apt-get install python3-pip
- sudo -H pip3 install --upgrade pip
before_script:
# https://docs.codeclimate.com/docs/travis-ci-test-coverage
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
- chmod +x ./cc-test-reporter
- ./cc-test-reporter before-build
script:
- npm test
- travis_wait 60 npm run build
after_script:
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
after_success:
- pip install --user awscli
- export PATH=$PATH:$HOME/.local/bin

View File

@ -10,6 +10,7 @@
<p align="center">
<a href="https://travis-ci.com/kremalicious/blog"><img src="https://travis-ci.com/kremalicious/blog.svg?branch=master" /></a>
<a href="https://codeclimate.com/github/kremalicious/blog/maintainability"><img src="https://api.codeclimate.com/v1/badges/4e86c791349cd12368cd/maintainability" /></a>
<a href="https://codeclimate.com/github/kremalicious/blog/test_coverage"><img src="https://api.codeclimate.com/v1/badges/4e86c791349cd12368cd/test_coverage" /></a>
<a href="https://greenkeeper.io/"><img src="https://badges.greenkeeper.io/kremalicious/blog.svg" /></a>
</p>
@ -28,6 +29,7 @@
- [🍬 Typekit component](#-typekit-component)
- [✨ Development](#-development)
- [🔮 Linting](#-linting)
- [👩‍🔬 Testing](#-testing)
- [🎈 Add a new post](#-add-a-new-post)
- [🚚 Deployment](#-deployment)
- [🏛 Licenses](#-licenses)
@ -139,7 +141,6 @@ All SVG assets under `src/images/` will be converted to React components with th
```jsx
import { ReactComponent as Logo } from './components/svg/Logo'
;<Logo />
```
@ -185,6 +186,24 @@ npm run format
npm run format:css
```
### 👩‍🔬 Testing
Test suite is setup with [Jest](https://jestjs.io) and [react-testing-library](https://github.com/kentcdodds/react-testing-library).
To run all tests, including all linting tests:
```bash
npm test
```
All test files live beside the respective component. Testing setup, fixtures, and mocks can be found in `./jest.config.js` and `./jest` folder.
For local development, run the test watcher:
```bash
npm run test:watch
```
### 🎈 Add a new post
```bash

19
jest.config.js Normal file
View File

@ -0,0 +1,19 @@
module.exports = {
transform: {
'^.+\\.jsx?$': '<rootDir>/jest/jest-preprocess.js'
},
moduleNameMapper: {
'.+\\.(css|styl|less|sass|scss)$': 'identity-obj-proxy',
'.+\\.(jpg|jpeg|png|gif|eot|otf|webp|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$':
'<rootDir>/jest/__mocks__/file-mock.js',
'\\.svg': '<rootDir>/jest/__mocks__/svgr-mock.js'
},
testPathIgnorePatterns: ['node_modules', '.cache', 'public', 'coverage'],
transformIgnorePatterns: ['node_modules/(?!(gatsby)/)'],
globals: {
__PATH_PREFIX__: ''
},
testURL: 'http://localhost',
setupFiles: ['<rootDir>/jest/loadershim.js'],
setupFilesAfterEnv: ['<rootDir>/jest/setup-test-env.js']
}

View File

@ -0,0 +1 @@
module.exports = 'test-file-stub'

28
jest/__mocks__/gatsby.js Normal file
View File

@ -0,0 +1,28 @@
const React = require('react')
const gatsby = jest.requireActual('gatsby')
module.exports = {
...gatsby,
graphql: jest.fn(),
Link: jest.fn().mockImplementation(
// these props are invalid for an `a` tag
({
/* eslint-disable no-unused-vars */
activeClassName,
activeStyle,
getProps,
innerRef,
ref,
replace,
to,
/* eslint-enable no-unused-vars */
...rest
}) =>
React.createElement('a', {
...rest,
href: to
})
),
StaticQuery: jest.fn(),
useStaticQuery: jest.fn()
}

View File

@ -0,0 +1 @@
module.exports = { ReactComponent: 'svg' }

5
jest/jest-preprocess.js Normal file
View File

@ -0,0 +1,5 @@
const babelOptions = {
presets: ['babel-preset-gatsby']
}
module.exports = require('babel-jest').createTransformer(babelOptions)

3
jest/loadershim.js Normal file
View File

@ -0,0 +1,3 @@
global.___loader = {
enqueue: jest.fn()
}

4
jest/setup-test-env.js Normal file
View File

@ -0,0 +1,4 @@
import 'jest-dom/extend-expect'
// this is basically: afterEach(cleanup)
import 'react-testing-library/cleanup-after-each'

11
jest/testRender.js Normal file
View File

@ -0,0 +1,11 @@
import { render } from 'react-testing-library'
const testRender = component => {
it('renders without crashing', () => {
const { container } = render(component)
expect(container.firstChild).toBeInTheDocument()
})
}
export default testRender

View File

@ -22,7 +22,8 @@
"lint:md": "markdownlint './**/*.{md,markdown}' --ignore './{node_modules,public,.cache,.git}/**/*'",
"lint:yaml": "prettier '**/*.{yml,yaml}' --list-different",
"lint": "run-p --continue-on-error lint:js lint:css lint:yaml lint:md",
"test": "npm run lint",
"test": "npm run lint && jest --coverage",
"test:watch": "npm run lint && jest --coverage --watch",
"deploy": "./scripts/deploy.sh",
"new": "babel-node ./scripts/new.js"
},
@ -33,36 +34,36 @@
"dms2dec": "^1.1.0",
"fast-exif": "^1.0.1",
"fraction.js": "^4.0.12",
"gatsby": "^2.3.22",
"gatsby-image": "^2.0.38",
"gatsby": "^2.4.2",
"gatsby-image": "^2.0.41",
"gatsby-plugin-catch-links": "^2.0.13",
"gatsby-plugin-favicon": "^3.1.5",
"gatsby-plugin-feed": "^2.1.0",
"gatsby-plugin-lunr": "^1.4.0",
"gatsby-plugin-favicon": "^3.1.6",
"gatsby-plugin-feed": "^2.2.0",
"gatsby-plugin-lunr": "^1.5.0",
"gatsby-plugin-matomo": "^0.7.0",
"gatsby-plugin-meta-redirect": "^1.1.1",
"gatsby-plugin-offline": "^2.0.25",
"gatsby-plugin-react-helmet": "^3.0.11",
"gatsby-plugin-offline": "^2.1.0",
"gatsby-plugin-react-helmet": "^3.0.12",
"gatsby-plugin-sass": "^2.0.11",
"gatsby-plugin-sharp": "^2.0.34",
"gatsby-plugin-sitemap": "^2.0.12",
"gatsby-plugin-sharp": "^2.0.36",
"gatsby-plugin-sitemap": "^2.1.0",
"gatsby-plugin-svgr": "^2.0.2",
"gatsby-plugin-webpack-size": "^0.0.3",
"gatsby-redirect-from": "^0.1.1",
"gatsby-remark-autolink-headers": "^2.0.16",
"gatsby-remark-copy-linked-files": "^2.0.11",
"gatsby-remark-copy-linked-files": "^2.0.12",
"gatsby-remark-highlights": "^1.3.4",
"gatsby-remark-images": "^3.0.10",
"gatsby-remark-images": "^3.0.11",
"gatsby-remark-smartypants": "^2.0.9",
"gatsby-source-filesystem": "^2.0.29",
"gatsby-source-filesystem": "^2.0.33",
"gatsby-source-graphql": "^2.0.18",
"gatsby-transformer-remark": "^2.3.8",
"gatsby-transformer-sharp": "^2.1.18",
"graphql": "^14.2.0",
"gatsby-transformer-remark": "^2.3.12",
"gatsby-transformer-sharp": "^2.1.19",
"graphql": "^14.2.1",
"intersection-observer": "^0.6.0",
"js-scrypt": "^0.2.0",
"load-script": "^1.0.0",
"node-sass": "^4.11.0",
"node-sass": "^4.12.0",
"nord": "^0.2.1",
"pigeon-maps": "^0.12.1",
"pigeon-marker": "^0.3.4",
@ -70,7 +71,7 @@
"react-blockies": "^1.4.1",
"react-clipboard.js": "^2.0.7",
"react-dom": "^16.8.6",
"react-helmet": "^5.2.0",
"react-helmet": "^5.2.1",
"react-modal": "^3.8.1",
"react-pose": "^4.0.8",
"react-qr-svg": "^2.2.1",
@ -80,31 +81,36 @@
"remark-react": "^5.0.1",
"slugify": "^1.3.4",
"terser": "^3.17.0",
"web3": "^1.0.0-beta.51"
"web3": "^1.0.0-beta.54"
},
"devDependencies": {
"@babel/node": "^7.2.2",
"@babel/preset-env": "^7.4.2",
"@babel/preset-env": "^7.4.4",
"@svgr/webpack": "^4.2.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.7.1",
"eslint": "^5.16.0",
"eslint-config-prettier": "^4.1.0",
"eslint-config-prettier": "^4.2.0",
"eslint-loader": "^2.1.2",
"eslint-plugin-graphql": "^3.0.3",
"eslint-plugin-jsx-a11y": "^6.2.1",
"eslint-plugin-prettier": "^3.0.1",
"eslint-plugin-react": "^7.12.4",
"eslint-plugin-react": "^7.13.0",
"fs-extra": "^7.0.1",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.7.1",
"jest-dom": "^3.1.4",
"markdownlint-cli": "^0.15.0",
"npm-run-all": "^4.1.5",
"ora": "^3.2.0",
"ora": "^3.4.0",
"pify": "^4.0.1",
"prettier": "^1.17.0",
"prettier-stylelint": "^0.4.2",
"stylelint": "^10.0.0",
"stylelint-config-css-modules": "^1.3.0",
"react-testing-library": "^7.0.0",
"stylelint": "^10.0.1",
"stylelint-config-css-modules": "^1.4.0",
"stylelint-config-standard": "^18.3.0",
"stylelint-scss": "^3.5.4",
"stylelint-scss": "^3.6.1",
"why-did-you-update": "^1.0.6"
},
"engines": {

View File

@ -0,0 +1,9 @@
import React from 'react'
// import { render } from 'react-testing-library'
import testRender from '../../../jest/testRender'
import Container from './Container'
describe('Container', () => {
testRender(<Container>Hello</Container>)
})

View File

@ -1,70 +1,8 @@
import React, { Fragment, PureComponent } from 'react'
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Map from 'pigeon-maps'
import Marker from 'pigeon-marker'
import ExifMap from './ExifMap'
import styles from './Exif.module.scss'
const MAPBOX_ACCESS_TOKEN =
'pk.eyJ1Ijoia3JlbWFsaWNpb3VzIiwiYSI6ImNqbTE2NHpkYjJmNm8zcHF4eDVqZzk3ejEifQ.1uwPzM6MSTgL2e1Hxcmuqw'
const retina =
typeof window !== 'undefined' && window.devicePixelRatio >= 2 ? '@2x' : ''
const mapbox = (mapboxId, accessToken) => (x, y, z) =>
`https://api.mapbox.com/styles/v1/mapbox/${mapboxId}/tiles/256/${z}/${x}/${y}${retina}?access_token=${accessToken}`
const providers = {
osm: (x, y, z) => {
const s = String.fromCharCode(97 + ((x + y + z) % 3))
return `https://${s}.tile.openstreetmap.org/${z}/${x}/${y}.png`
},
wikimedia: (x, y, z) =>
`https://maps.wikimedia.org/osm-intl/${z}/${x}/${y}${retina}.png`,
stamen: (x, y, z) =>
`https://stamen-tiles.a.ssl.fastly.net/terrain/${z}/${x}/${y}${retina}.jpg`,
streets: mapbox('streets-v10', MAPBOX_ACCESS_TOKEN),
satellite: mapbox('satellite-streets-v10', MAPBOX_ACCESS_TOKEN),
outdoors: mapbox('outdoors-v10', MAPBOX_ACCESS_TOKEN),
light: mapbox('light-v9', MAPBOX_ACCESS_TOKEN),
dark: mapbox('dark-v9', MAPBOX_ACCESS_TOKEN)
}
class ExifMap extends PureComponent {
state = { zoom: 12 }
static propTypes = {
gps: PropTypes.object
}
zoomIn = () => {
this.setState({
zoom: Math.min(this.state.zoom + 4, 20)
})
}
render() {
const { latitude, longitude } = this.props.gps
return (
<Map
center={[latitude, longitude]}
zoom={this.state.zoom}
height={160}
attribution={false}
provider={providers['light']}
metaWheelZoom={true}
metaWheelZoomWarning={'META+wheel to zoom'}
>
<Marker
anchor={[latitude, longitude]}
payload={1}
onClick={this.zoomIn}
/>
</Map>
)
}
}
export default class Exif extends PureComponent {
static propTypes = {
exif: PropTypes.object
@ -82,7 +20,7 @@ export default class Exif extends PureComponent {
} = this.props.exif
return (
<Fragment>
<>
<aside className={styles.exif}>
<div className={styles.data}>
{model && <span title="Camera model">{model}</span>}
@ -94,7 +32,7 @@ export default class Exif extends PureComponent {
</div>
<div className={styles.map}>{gps && <ExifMap gps={gps} />}</div>
</aside>
</Fragment>
</>
)
}
}

View File

@ -0,0 +1,19 @@
import React from 'react'
// import { render } from 'react-testing-library'
import testRender from '../../../jest/testRender'
import Exif from './Exif'
const exif = {
iso: '500',
model: 'Canon',
fstop: '7.2',
shutterspeed: '200',
focalLength: '200',
exposure: '200',
gps: { latitude: '52.4792516', longitude: '13.431609' }
}
describe('Exif', () => {
testRender(<Exif exif={exif} />)
})

View File

@ -0,0 +1,65 @@
import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import Map from 'pigeon-maps'
import Marker from 'pigeon-marker'
const MAPBOX_ACCESS_TOKEN =
'pk.eyJ1Ijoia3JlbWFsaWNpb3VzIiwiYSI6ImNqbTE2NHpkYjJmNm8zcHF4eDVqZzk3ejEifQ.1uwPzM6MSTgL2e1Hxcmuqw'
const retina =
typeof window !== 'undefined' && window.devicePixelRatio >= 2 ? '@2x' : ''
const mapbox = (mapboxId, accessToken) => (x, y, z) =>
`https://api.mapbox.com/styles/v1/mapbox/${mapboxId}/tiles/256/${z}/${x}/${y}${retina}?access_token=${accessToken}`
const providers = {
// osm: (x, y, z) => {
// const s = String.fromCharCode(97 + ((x + y + z) % 3))
// return `https://${s}.tile.openstreetmap.org/${z}/${x}/${y}.png`
// },
// wikimedia: (x, y, z) =>
// `https://maps.wikimedia.org/osm-intl/${z}/${x}/${y}${retina}.png`,
// stamen: (x, y, z) =>
// `https://stamen-tiles.a.ssl.fastly.net/terrain/${z}/${x}/${y}${retina}.jpg`,
// streets: mapbox('streets-v10', MAPBOX_ACCESS_TOKEN),
// satellite: mapbox('satellite-streets-v10', MAPBOX_ACCESS_TOKEN),
// outdoors: mapbox('outdoors-v10', MAPBOX_ACCESS_TOKEN),
light: mapbox('light-v9', MAPBOX_ACCESS_TOKEN),
dark: mapbox('dark-v9', MAPBOX_ACCESS_TOKEN)
}
export default class ExifMap extends PureComponent {
state = { zoom: 12 }
static propTypes = {
gps: PropTypes.object
}
zoomIn = () => {
this.setState({
zoom: Math.min(this.state.zoom + 4, 20)
})
}
render() {
const { latitude, longitude } = this.props.gps
return (
<Map
center={[latitude, longitude]}
zoom={this.state.zoom}
height={160}
attribution={false}
provider={providers['light']}
metaWheelZoom={true}
metaWheelZoomWarning={'META+wheel to zoom'}
>
<Marker
anchor={[latitude, longitude]}
payload={1}
onClick={this.zoomIn}
/>
</Map>
)
}
}

View File

@ -0,0 +1,9 @@
import React from 'react'
// import { render } from 'react-testing-library'
import testRender from '../../../jest/testRender'
import Hamburger from './Hamburger'
describe('Hamburger', () => {
testRender(<Hamburger />)
})

View File

@ -0,0 +1,9 @@
import React from 'react'
// import { render } from 'react-testing-library'
import testRender from '../../../jest/testRender'
import Input from './Input'
describe('Input', () => {
testRender(<Input />)
})