1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2025-01-03 18:35:00 +01:00

add more tests

This commit is contained in:
Matthias Kretschmann 2019-04-16 03:59:21 +02:00
parent 05aa2ffa84
commit 0e78f34d15
Signed by: m
GPG Key ID: 606EEEF3C479A91F
9 changed files with 139 additions and 68 deletions

View File

@ -9,6 +9,9 @@
"childImageSharp": { "childImageSharp": {
"fluid": { "fluid": {
"src": "/static/b45f45aa8d98d4e4019a242d38f2f248/bc3a8/avatar.jpg" "src": "/static/b45f45aa8d98d4e4019a242d38f2f248/bc3a8/avatar.jpg"
},
"resize": {
"src": "/static/b45f45aa8d98d4e4019a242d38f2f248/bc3a8/avatar.jpg"
} }
} }
}, },

View File

@ -18,6 +18,7 @@
"format": "prettier --write 'src/**/*.{js,jsx}'", "format": "prettier --write 'src/**/*.{js,jsx}'",
"format:css": "prettier-stylelint --write --quiet 'src/**/*.{css,scss}'", "format:css": "prettier-stylelint --write --quiet 'src/**/*.{css,scss}'",
"test": "npm run lint && jest --coverage", "test": "npm run lint && jest --coverage",
"test:watch": "npm run lint && jest --coverage --watch",
"deploy": "./scripts/deploy.sh", "deploy": "./scripts/deploy.sh",
"new": "babel-node ./scripts/new.js" "new": "babel-node ./scripts/new.js"
}, },

View File

@ -25,59 +25,62 @@ const query = graphql`
} }
` `
const LayoutMarkup = ({ children, isHomepage, allowedHosts, location }) => ( const LayoutMarkup = ({ children, data, location }) => {
<> const { allowedHosts } = data.contentYaml
<Typekit /> const isHomepage = location.pathname === '/'
<HostnameCheck allowedHosts={allowedHosts} />
<PoseGroup animateOnMount={true}> return (
<RoutesContainer <>
key={location.pathname} <Typekit />
delay={timeout} <HostnameCheck allowedHosts={allowedHosts} />
delayChildren={timeout}
>
<Header minimal={!isHomepage} />
<main className={styles.screen}>{children}</main>
</RoutesContainer>
</PoseGroup>
<Footer /> <PoseGroup animateOnMount={true}>
</> <RoutesContainer
) key={location.pathname}
delay={timeout}
delayChildren={timeout}
>
<Header minimal={!isHomepage} />
<main className={styles.screen}>{children}</main>
</RoutesContainer>
</PoseGroup>
<Footer />
</>
)
}
LayoutMarkup.propTypes = { LayoutMarkup.propTypes = {
children: PropTypes.any.isRequired, children: PropTypes.any.isRequired,
isHomepage: PropTypes.bool.isRequired, data: PropTypes.shape({
allowedHosts: PropTypes.array.isRequired, contentYaml: PropTypes.shape({
location: PropTypes.object.isRequired allowedHosts: PropTypes.array.isRequired
}).isRequired
}).isRequired,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired
}).isRequired
} }
export default class Layout extends PureComponent { export default class Layout extends PureComponent {
static propTypes = { static propTypes = {
children: PropTypes.any.isRequired, children: PropTypes.any.isRequired,
location: PropTypes.object.isRequired location: PropTypes.shape({
pathname: PropTypes.string.isRequired
}).isRequired
} }
render() { render() {
const { children, location } = this.props const { children, location } = this.props
const isHomepage = location.pathname === '/'
return ( return (
<StaticQuery <StaticQuery
query={query} query={query}
render={data => { render={data => (
const { allowedHosts } = data.contentYaml <LayoutMarkup data={data} location={location}>
{children}
return ( </LayoutMarkup>
<LayoutMarkup )}
isHomepage={isHomepage}
allowedHosts={allowedHosts}
location={location}
>
{children}
</LayoutMarkup>
)
}}
/> />
) )
} }

View File

@ -13,7 +13,7 @@ const query = graphql`
email email
avatar { avatar {
childImageSharp { childImageSharp {
original: resize { resize {
src src
} }
} }
@ -62,15 +62,16 @@ export default class Vcard extends PureComponent {
// Construct the download from a blob of the just constructed vCard, // Construct the download from a blob of the just constructed vCard,
// and save it to user's file system // and save it to user's file system
const downloadVcard = (vcard, meta) => { export const downloadVcard = (vcard, meta) => {
const name = meta.addressbook.split('/').join('') const { addressbook } = meta
const name = addressbook.split('/').join('')
const blob = new Blob([vcard], { type: 'text/x-vcard' }) const blob = new Blob([vcard], { type: 'text/x-vcard' })
saveAs(blob, name) saveAs(blob, name)
} }
const constructVcard = meta => { export const constructVcard = meta => {
const contact = new vCard() const contact = new vCard()
const photoSrc = meta.avatar.childImageSharp.original.src const photoSrc = meta.avatar.childImageSharp.resize.src
// first, convert the avatar to base64, then construct all vCard elements // first, convert the avatar to base64, then construct all vCard elements
toDataURL( toDataURL(
@ -99,7 +100,7 @@ const constructVcard = meta => {
// Helper function to create base64 string from avatar image // Helper function to create base64 string from avatar image
// without the need to read image file from file system // without the need to read image file from file system
const toDataURL = (src, callback, outputFormat) => { export const toDataURL = (src, callback, outputFormat) => {
const img = new Image() const img = new Image()
img.crossOrigin = 'Anonymous' img.crossOrigin = 'Anonymous'
@ -118,6 +119,7 @@ const toDataURL = (src, callback, outputFormat) => {
} }
img.src = src img.src = src
if (img.complete || img.complete === undefined) { if (img.complete || img.complete === undefined) {
img.src = img.src =
'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==' 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw=='

View File

@ -0,0 +1,37 @@
import React from 'react'
import { render } from 'react-testing-library'
import { StaticQuery } from 'gatsby'
import vCard from 'vcf'
import Vcard, { constructVcard, downloadVcard, toDataURL } from './Vcard'
import data from '../../../jest/__fixtures__/meta.json'
describe('Vcard', () => {
beforeEach(() => {
StaticQuery.mockImplementationOnce(({ render }) => render({ ...data }))
})
it('renders correctly', () => {
const { container } = render(<Vcard />)
expect(container.firstChild).toBeInTheDocument()
})
it('vCard can be constructed', async () => {
await constructVcard(data.contentYaml)
})
it('vCard can be downloaded', async () => {
const contact = new vCard()
const vcard = contact.toString('3.0')
global.URL.createObjectURL = jest.fn(() => 'details')
await downloadVcard(vcard, data.contentYaml)
expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1)
})
it('Base64 from image can be constructed', () => {
const photoSrc = data.contentYaml.avatar.childImageSharp.resize.src
toDataURL(photoSrc, () => null, 'image/jpeg')
})
})

View File

@ -48,7 +48,10 @@ export default class ThemeSwitch extends PureComponent {
<Helmet> <Helmet>
<body className={dark ? 'dark' : null} /> <body className={dark ? 'dark' : null} />
</Helmet> </Helmet>
<Animation className={styles.themeSwitch}> <Animation
className={styles.themeSwitch}
data-testid={'theme-switch'}
>
<label className={styles.checkbox}> <label className={styles.checkbox}>
<span className={styles.label}>Toggle Night Mode</span> <span className={styles.label}>Toggle Night Mode</span>
<ThemeToggleInput dark={dark} toggleDark={toggleDark} /> <ThemeToggleInput dark={dark} toggleDark={toggleDark} />

View File

@ -0,0 +1,17 @@
import React from 'react'
import { render } from 'react-testing-library'
import AppProvider from '../../store/provider'
import ThemeSwitch from './ThemeSwitch'
describe('ThemeSwitch', () => {
it('renders correctly', () => {
const { getByTestId } = render(
<AppProvider>
<ThemeSwitch />
</AppProvider>
)
expect(getByTestId('theme-switch')).toBeInTheDocument()
expect(getByTestId('theme-switch').nodeName).toBe('ASIDE')
})
})

View File

@ -1,4 +1,5 @@
import React, { PureComponent } from 'react' import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Link, StaticQuery, graphql } from 'gatsby' import { Link, StaticQuery, graphql } from 'gatsby'
import classNames from 'classnames' import classNames from 'classnames'
import Vcard from '../atoms/Vcard' import Vcard from '../atoms/Vcard'
@ -24,12 +25,10 @@ const query = graphql`
} }
` `
let classes = classNames('h-card', [styles.footer]) export const FooterMarkup = ({ pkg, meta, year }) => {
const classes = classNames('h-card', [styles.footer])
export default class Footer extends PureComponent { return (
state = { year: new Date().getFullYear() }
FooterMarkup = ({ meta, pkg, year }) => (
<footer className={classes}> <footer className={classes}>
<Link to={'/'}> <Link to={'/'}>
<LogoUnit minimal /> <LogoUnit minimal />
@ -55,6 +54,16 @@ export default class Footer extends PureComponent {
</p> </p>
</footer> </footer>
) )
}
FooterMarkup.propTypes = {
pkg: PropTypes.object.isRequired,
meta: PropTypes.object.isRequired,
year: PropTypes.number.isRequired
}
export default class Footer extends PureComponent {
state = { year: new Date().getFullYear() }
render() { render() {
return ( return (
@ -64,9 +73,7 @@ export default class Footer extends PureComponent {
const pkg = data.portfolioJson const pkg = data.portfolioJson
const meta = data.contentYaml const meta = data.contentYaml
return ( return <FooterMarkup year={this.state.year} pkg={pkg} meta={meta} />
<this.FooterMarkup year={this.state.year} pkg={pkg} meta={meta} />
)
}} }}
/> />
) )

View File

@ -23,28 +23,26 @@ export default class AppProvider extends PureComponent {
getCountry = async () => { getCountry = async () => {
let trace = [] let trace = []
await fetch('/cdn-cgi/trace?no-cache=1') try {
.then(data => { const data = await fetch('/cdn-cgi/trace?no-cache=1')
let lines const text = await data.text()
const lines = text.split('\n')
data.text().then(text => { let keyValue
lines = text.split('\n')
let keyValue lines.forEach(line => {
keyValue = line.split('=')
trace[keyValue[0]] = decodeURIComponent(keyValue[1] || '')
lines.forEach(line => { if (keyValue[0] === 'loc' && trace['loc'] !== 'XX') {
keyValue = line.split('=') this.setState({ location: trace['loc'] })
trace[keyValue[0]] = decodeURIComponent(keyValue[1] || '') } else {
return
if (keyValue[0] === 'loc' && trace['loc'] !== 'XX') { }
this.setState({ location: trace['loc'] })
} else {
return
}
})
})
}) })
.catch(() => null) // fail silently } catch (error) {
return null // fail silently
}
} }
setDark() { setDark() {