mirror of
https://github.com/kremalicious/blog.git
synced 2024-11-23 02:10:01 +01:00
commit
b997f366c3
@ -17,7 +17,8 @@
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": true,
|
||||
"es6": true
|
||||
"es6": true,
|
||||
"jest": true
|
||||
},
|
||||
"rules": {
|
||||
"quotes": ["error", "single"],
|
||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -6,3 +6,4 @@ public
|
||||
.cache
|
||||
src/components/svg
|
||||
plugins/gatsby-redirect-from
|
||||
coverage
|
||||
|
@ -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
|
||||
|
21
README.md
21
README.md
@ -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
19
jest.config.js
Normal 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']
|
||||
}
|
1
jest/__mocks__/file-mock.js
Normal file
1
jest/__mocks__/file-mock.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = 'test-file-stub'
|
28
jest/__mocks__/gatsby.js
Normal file
28
jest/__mocks__/gatsby.js
Normal 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()
|
||||
}
|
1
jest/__mocks__/svgr-mock.js
Normal file
1
jest/__mocks__/svgr-mock.js
Normal file
@ -0,0 +1 @@
|
||||
module.exports = { ReactComponent: 'svg' }
|
5
jest/jest-preprocess.js
Normal file
5
jest/jest-preprocess.js
Normal file
@ -0,0 +1,5 @@
|
||||
const babelOptions = {
|
||||
presets: ['babel-preset-gatsby']
|
||||
}
|
||||
|
||||
module.exports = require('babel-jest').createTransformer(babelOptions)
|
3
jest/loadershim.js
Normal file
3
jest/loadershim.js
Normal file
@ -0,0 +1,3 @@
|
||||
global.___loader = {
|
||||
enqueue: jest.fn()
|
||||
}
|
4
jest/setup-test-env.js
Normal file
4
jest/setup-test-env.js
Normal 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
11
jest/testRender.js
Normal 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
|
58
package.json
58
package.json
@ -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": {
|
||||
|
9
src/components/atoms/Container.test.jsx
Normal file
9
src/components/atoms/Container.test.jsx
Normal 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>)
|
||||
})
|
@ -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>
|
||||
</>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
19
src/components/atoms/Exif.test.jsx
Normal file
19
src/components/atoms/Exif.test.jsx
Normal 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} />)
|
||||
})
|
65
src/components/atoms/ExifMap.jsx
Normal file
65
src/components/atoms/ExifMap.jsx
Normal 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>
|
||||
)
|
||||
}
|
||||
}
|
9
src/components/atoms/Hamburger.test.jsx
Normal file
9
src/components/atoms/Hamburger.test.jsx
Normal 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 />)
|
||||
})
|
9
src/components/atoms/Input.test.jsx
Normal file
9
src/components/atoms/Input.test.jsx
Normal 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 />)
|
||||
})
|
Loading…
Reference in New Issue
Block a user