1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2024-12-31 17:17:38 +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": {
"fluid": {
"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:css": "prettier-stylelint --write --quiet 'src/**/*.{css,scss}'",
"test": "npm run lint && jest --coverage",
"test:watch": "npm run lint && jest --coverage --watch",
"deploy": "./scripts/deploy.sh",
"new": "babel-node ./scripts/new.js"
},

View File

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

View File

@ -13,7 +13,7 @@ const query = graphql`
email
avatar {
childImageSharp {
original: resize {
resize {
src
}
}
@ -62,15 +62,16 @@ export default class Vcard extends PureComponent {
// Construct the download from a blob of the just constructed vCard,
// and save it to user's file system
const downloadVcard = (vcard, meta) => {
const name = meta.addressbook.split('/').join('')
export const downloadVcard = (vcard, meta) => {
const { addressbook } = meta
const name = addressbook.split('/').join('')
const blob = new Blob([vcard], { type: 'text/x-vcard' })
saveAs(blob, name)
}
const constructVcard = meta => {
export const constructVcard = meta => {
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
toDataURL(
@ -99,7 +100,7 @@ const constructVcard = meta => {
// Helper function to create base64 string from avatar image
// 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()
img.crossOrigin = 'Anonymous'
@ -118,6 +119,7 @@ const toDataURL = (src, callback, outputFormat) => {
}
img.src = src
if (img.complete || img.complete === undefined) {
img.src =
'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>
<body className={dark ? 'dark' : null} />
</Helmet>
<Animation className={styles.themeSwitch}>
<Animation
className={styles.themeSwitch}
data-testid={'theme-switch'}
>
<label className={styles.checkbox}>
<span className={styles.label}>Toggle Night Mode</span>
<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 PropTypes from 'prop-types'
import { Link, StaticQuery, graphql } from 'gatsby'
import classNames from 'classnames'
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 {
state = { year: new Date().getFullYear() }
FooterMarkup = ({ meta, pkg, year }) => (
return (
<footer className={classes}>
<Link to={'/'}>
<LogoUnit minimal />
@ -55,6 +54,16 @@ export default class Footer extends PureComponent {
</p>
</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() {
return (
@ -64,9 +73,7 @@ export default class Footer extends PureComponent {
const pkg = data.portfolioJson
const meta = data.contentYaml
return (
<this.FooterMarkup year={this.state.year} pkg={pkg} meta={meta} />
)
return <FooterMarkup year={this.state.year} pkg={pkg} meta={meta} />
}}
/>
)

View File

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