1
0
mirror of https://github.com/kremalicious/portfolio.git synced 2024-12-23 01:29:41 +01:00

Merge pull request #131 from kremalicious/feature/tests

More tests
This commit is contained in:
Matthias Kretschmann 2019-04-28 22:44:01 +02:00 committed by GitHub
commit 07c7641ff6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 515 additions and 84 deletions

View File

@ -1,7 +1,10 @@
import './src/styles/global.scss'
import React from 'react'
import AppProvider from './src/store/Provider'
import wrapPageElementWithTransition from './src/helpers/wrapPageElement'
import PropTypes from 'prop-types'
import AppProvider from './src/store/AppProvider'
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
// Global styles
import './src/styles/global.scss'
// IntersectionObserver polyfill for gatsby-image (Safari, IE)
if (typeof window !== 'undefined' && !window.IntersectionObserver) {
@ -9,10 +12,13 @@ if (typeof window !== 'undefined' && !window.IntersectionObserver) {
}
// React Context in Browser
// eslint-disable-next-line
export const wrapRootElement = ({ element }) => {
return <AppProvider>{element}</AppProvider>
}
// Page Transitions & Layout
export const wrapPageElement = wrapPageElementWithTransition
wrapRootElement.propTypes = {
element: PropTypes.any
}
// Layout with Page Transitions
export const wrapPageElement = wrapPageElementWithLayout

View File

@ -1,7 +1,7 @@
import React from 'react'
import { renderToString } from 'react-dom/server'
import AppProvider from './src/store/Provider'
import wrapPageElementWithTransition from './src/helpers/wrapPageElement'
import AppProvider from './src/store/AppProvider'
import wrapPageElementWithLayout from './src/helpers/wrapPageElement'
export const replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
// React Context in SSR/build
@ -9,5 +9,5 @@ export const replaceRenderer = ({ bodyComponent, replaceBodyHTMLString }) => {
replaceBodyHTMLString(renderToString(<ConnectedBody />))
}
// Page Transitions & Layout
export const wrapPageElement = wrapPageElementWithTransition
// Layout with Page Transitions
export const wrapPageElement = wrapPageElementWithLayout

View File

@ -0,0 +1,361 @@
{
"projectImageFiles": {
"edges": [
{
"node": {
"name": "meta"
}
},
{
"node": {
"name": "projects"
}
},
{
"node": {
"name": "chartmogul-05"
}
},
{
"node": {
"name": "ezeep-05"
}
},
{
"node": {
"name": "ezeep-06"
}
},
{
"node": {
"name": "ipixelpad-01"
}
},
{
"node": {
"name": "allinnia-02"
}
},
{
"node": {
"name": "ezeep-03"
}
},
{
"node": {
"name": "ipdb-04"
}
},
{
"node": {
"name": "allinnia-03"
}
},
{
"node": {
"name": "ezeep-07"
}
},
{
"node": {
"name": "unihalle-03"
}
},
{
"node": {
"name": "oceanprotocol-05"
}
},
{
"node": {
"name": "apertureloupe-01"
}
},
{
"node": {
"name": "coffeecup-01"
}
},
{
"node": {
"name": "ipdb-01"
}
},
{
"node": {
"name": "ezeep-04"
}
},
{
"node": {
"name": "mrreader-01"
}
},
{
"node": {
"name": "mrreader-02"
}
},
{
"node": {
"name": "unihalle-02"
}
},
{
"node": {
"name": "chartmogul-02"
}
},
{
"node": {
"name": "ipdb-03"
}
},
{
"node": {
"name": "bigchaindb-02"
}
},
{
"node": {
"name": "chartmogul-03"
}
},
{
"node": {
"name": "ipdb-02"
}
},
{
"node": {
"name": "oceanprotocol-02"
}
},
{
"node": {
"name": "bigchaindb-03"
}
},
{
"node": {
"name": "ezeep-08"
}
},
{
"node": {
"name": "ezeep-02"
}
},
{
"node": {
"name": "oceanprotocol-03"
}
},
{
"node": {
"name": "outofwhaleoil-02"
}
},
{
"node": {
"name": "bigchaindb-05"
}
},
{
"node": {
"name": "oceanprotocol-04"
}
},
{
"node": {
"name": "outofwhaleoil-01"
}
},
{
"node": {
"name": "9984-02"
}
},
{
"node": {
"name": "niepces-camera-obscura-01"
}
},
{
"node": {
"name": "niepces-camera-obscura-02"
}
},
{
"node": {
"name": "adiumeetie-01"
}
},
{
"node": {
"name": "oceanprotocol-01"
}
},
{
"node": {
"name": "outofwhaleoil-03"
}
},
{
"node": {
"name": "unihalle-01"
}
},
{
"node": {
"name": "ezeep-01"
}
},
{
"node": {
"name": "chartmogul-01"
}
},
{
"node": {
"name": "bigchaindb-04"
}
},
{
"node": {
"name": "9984-01"
}
},
{
"node": {
"name": "chartmogul-04"
}
},
{
"node": {
"name": "mrreader-03"
}
},
{
"node": {
"name": "bigchaindb-01"
}
},
{
"node": {
"name": "projectpurple-01"
}
},
{
"node": {
"name": "biv-01"
}
},
{
"node": {
"name": "sharethemeal-01"
}
},
{
"node": {
"name": "allinnia-01"
}
},
{
"node": {
"name": "package"
}
},
{
"node": {
"name": "day"
}
},
{
"node": {
"name": "download"
}
},
{
"node": {
"name": "blog"
}
},
{
"node": {
"name": "dribbble"
}
},
{
"node": {
"name": "email"
}
},
{
"node": {
"name": "favicon"
}
},
{
"node": {
"name": "github"
}
},
{
"node": {
"name": "index"
}
},
{
"node": {
"name": "info"
}
},
{
"node": {
"name": "logo"
}
},
{
"node": {
"name": "images"
}
},
{
"node": {
"name": "night"
}
},
{
"node": {
"name": "link"
}
},
{
"node": {
"name": "twitter-card"
}
},
{
"node": {
"name": "twitter"
}
},
{
"node": {
"name": "styleguide"
}
},
{
"node": {
"name": "github-header"
}
},
{
"node": {
"name": "avatar"
}
}
]
}
}

View File

@ -25,25 +25,25 @@
"dependencies": {
"classnames": "^2.2.6",
"file-saver": "^2.0.1",
"gatsby": "^2.3.22",
"gatsby-image": "^2.0.38",
"gatsby": "^2.3.31",
"gatsby-image": "^2.0.40",
"gatsby-plugin-favicon": "^3.1.6",
"gatsby-plugin-matomo": "^0.7.0",
"gatsby-plugin-offline": "^2.0.25",
"gatsby-plugin-react-helmet": "^3.0.12",
"gatsby-plugin-sass": "^2.0.11",
"gatsby-plugin-sharp": "^2.0.34",
"gatsby-plugin-sharp": "^2.0.35",
"gatsby-plugin-sitemap": "^2.0.12",
"gatsby-plugin-svgr": "^2.0.2",
"gatsby-source-filesystem": "^2.0.29",
"gatsby-source-filesystem": "^2.0.32",
"gatsby-transformer-json": "^2.1.11",
"gatsby-transformer-sharp": "^2.1.18",
"gatsby-transformer-yaml": "^2.1.11",
"gatsby-transformer-yaml": "^2.1.12",
"giphy-js-sdk-core": "^1.0.6",
"graphql": "^14.2.1",
"intersection-observer": "^0.6.0",
"js-yaml": "^3.13.1",
"node-sass": "^4.11.0",
"node-sass": "^4.12.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-helmet": "^5.2.0",
@ -56,22 +56,22 @@
"vcf": "^2.0.4"
},
"devDependencies": {
"@babel/core": "^7.4.3",
"@babel/core": "^7.4.4",
"@babel/node": "^7.2.2",
"@babel/preset-env": "^7.4.3",
"@babel/preset-env": "^7.4.4",
"@svgr/webpack": "^4.2.0",
"babel-eslint": "^10.0.1",
"babel-jest": "^24.7.1",
"babel-preset-gatsby": "^0.1.11",
"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-prettier": "^3.0.1",
"eslint-plugin-react": "^7.12.4",
"identity-obj-proxy": "^3.0.0",
"jest": "^24.7.1",
"jest-canvas-mock": "^2.0.0-beta.1",
"jest-canvas-mock": "^2.0.0",
"jest-dom": "^3.1.3",
"ora": "^3.4.0",
"prepend": "^1.0.2",
@ -79,7 +79,7 @@
"prettier-stylelint": "^0.4.2",
"react-testing-library": "^7.0.0",
"slugify": "^1.3.4",
"stylelint": "^10.0.0",
"stylelint": "^10.0.1",
"stylelint-config-css-modules": "^1.3.0",
"stylelint-config-standard": "^18.3.0",
"why-did-you-update": "^1.0.6"

View File

@ -14,9 +14,6 @@ import styles from './Layout.module.scss'
// whyDidYouUpdate(React)
// }
const timeout = 250
const RoutesContainer = posed.div(fadeIn)
const query = graphql`
query {
contentYaml {
@ -25,42 +22,8 @@ const query = graphql`
}
`
const LayoutMarkup = ({ children, data, location }) => {
const { allowedHosts } = data.contentYaml
const isHomepage = location.pathname === '/'
return (
<>
<Typekit />
<HostnameCheck allowedHosts={allowedHosts} />
<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,
data: PropTypes.shape({
contentYaml: PropTypes.shape({
allowedHosts: PropTypes.array.isRequired
}).isRequired
}).isRequired,
location: PropTypes.shape({
pathname: PropTypes.string.isRequired
}).isRequired
}
const timeout = 200
const RoutesContainer = posed.div(fadeIn)
export default class Layout extends PureComponent {
static propTypes = {
@ -72,15 +35,34 @@ export default class Layout extends PureComponent {
render() {
const { children, location } = this.props
const isHomepage = location.pathname === '/'
return (
<StaticQuery
query={query}
render={data => (
<LayoutMarkup data={data} location={location}>
{children}
</LayoutMarkup>
)}
render={data => {
const { allowedHosts } = data.contentYaml
return (
<>
<Typekit />
<HostnameCheck allowedHosts={allowedHosts} />
<PoseGroup animateOnMount={true}>
<RoutesContainer
key={location.pathname}
delay={timeout}
delayChildren={timeout}
>
<Header minimal={!isHomepage} />
<main className={styles.screen}>{children}</main>
</RoutesContainer>
</PoseGroup>
<Footer />
</>
)
}}
/>
)
}

View File

@ -1,5 +1,5 @@
import React from 'react'
import { render } from 'react-testing-library'
import { render, fireEvent, waitForElement } from 'react-testing-library'
import { StaticQuery } from 'gatsby'
import Vcard, { constructVcard, toDataURL, init } from './Vcard'
import data from '../../../jest/__fixtures__/meta.json'
@ -16,6 +16,13 @@ describe('Vcard', () => {
expect(container.firstChild).toBeInTheDocument()
})
it('Button click starts download', async () => {
const { container } = render(<Vcard />)
fireEvent.click(container.firstChild)
await waitForElement(() => global.URL.createObjectURL)
expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1)
})
it('combined vCard download process finishes', async () => {
await init(data.contentYaml)
expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1)

View File

@ -0,0 +1,14 @@
import React from 'react'
import { render } from 'react-testing-library'
import ProjectTechstack from './ProjectTechstack'
describe('ProjectTechstack', () => {
const techstack = ['CSS']
it('renders correctly', () => {
const { container } = render(<ProjectTechstack techstack={techstack} />)
expect(container.firstChild).toBeInTheDocument()
expect(container.querySelector('li').textContent).toBe('CSS')
})
})

View File

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

View File

@ -1,17 +1,45 @@
import React from 'react'
import { render } from 'react-testing-library'
import AppProvider from '../../store/Provider'
import { render, fireEvent, cleanup } from 'react-testing-library'
import { Provider } from '../../store/createContext'
import ThemeSwitch from './ThemeSwitch'
describe('ThemeSwitch', () => {
afterEach(cleanup)
const toggleDark = jest.fn()
it('renders correctly', () => {
const { getByTestId } = render(
<AppProvider>
const { container } = render(
<Provider value={{ dark: false, toggleDark: () => toggleDark }}>
<ThemeSwitch />
</AppProvider>
</Provider>
)
expect(getByTestId('theme-switch')).toBeInTheDocument()
expect(getByTestId('theme-switch').nodeName).toBe('ASIDE')
const switchContainer = container.querySelector('aside')
expect(switchContainer).toBeInTheDocument()
})
it('switches when it is dark', () => {
const { container } = render(
<Provider value={{ dark: true, toggleDark: () => toggleDark }}>
<ThemeSwitch />
</Provider>
)
const toggle = container.querySelector('input')
expect(toggle).toHaveAttribute('checked')
})
it('checkbox can be changed', () => {
const { container } = render(
<Provider value={{ dark: false, toggleDark: () => toggleDark }}>
<ThemeSwitch />
</Provider>
)
const toggle = container.querySelector('input')
expect(toggle.checked).toBeFalsy()
fireEvent.change(toggle, { target: { checked: true } })
expect(toggle.checked).toBeTruthy()
})
})

View File

@ -19,4 +19,13 @@ describe('Header', () => {
)
expect(container.firstChild).toBeInTheDocument()
})
it('Availability can be hidden', () => {
const { container } = render(
<Provider value={{ dark: false, toggleDark: () => null }}>
<Header minimal={true} />
</Provider>
)
expect(container.querySelector('.availability')).not.toBeInTheDocument()
})
})

View File

@ -0,0 +1,23 @@
import React from 'react'
import { render } from 'react-testing-library'
import { StaticQuery } from 'gatsby'
import Home from '../index'
import meta from '../../../jest/__fixtures__/meta.json'
import projects from '../../../jest/__fixtures__/projects.json'
import projectImageFiles from '../../../jest/__fixtures__/projectImageFiles.json'
beforeEach(() => {
StaticQuery.mockImplementation(({ render }) => render({ ...meta }))
})
describe('Home', () => {
const data = {
...projects,
...projectImageFiles
}
it('renders correctly from data file values', () => {
const { container } = render(<Home data={data} />)
expect(container.firstChild).toBeInTheDocument()
})
})

View File

@ -1,6 +1,6 @@
import React from 'react'
import { render } from 'react-testing-library'
import AppProvider from './Provider.jsx'
import AppProvider from './AppProvider.jsx'
describe('AppProvider', () => {
it('renders correctly', () => {

View File

@ -1,7 +1,5 @@
import { getCountry } from './getCountry'
const responseMock = 'loc=DE'
const mockFetch = data =>
jest.fn().mockImplementationOnce(() =>
Promise.resolve({
@ -13,15 +11,21 @@ const mockFetch = data =>
)
describe('getCountry', () => {
beforeEach(() => {
global.fetch = mockFetch(responseMock)
})
it('fetches and returns correct value', async () => {
global.fetch = mockFetch('loc=DE')
const country = await getCountry()
expect(global.fetch).toHaveBeenCalledTimes(1)
expect(global.fetch).toHaveBeenCalledWith('/cdn-cgi/trace?no-cache=1')
expect(country).toBe('DE')
})
it('returns nothing when XX country', async () => {
global.fetch = mockFetch('loc=XX')
const country = await getCountry()
expect(global.fetch).toHaveBeenCalledTimes(1)
expect(global.fetch).toHaveBeenCalledWith('/cdn-cgi/trace?no-cache=1')
expect(country).toBe(undefined)
})
})