mirror of
https://github.com/kremalicious/portfolio.git
synced 2024-11-14 17:15:15 +01:00
fix tests
This commit is contained in:
parent
83d62c109b
commit
21cb1a1ffa
22
README.md
22
README.md
@ -18,8 +18,8 @@
|
||||
- [⛵️ Lighthouse score](#️-lighthouse-score)
|
||||
- [💍 One data file to rule all pages](#-one-data-file-to-rule-all-pages)
|
||||
- [🐱 GitHub repositories](#-github-repositories)
|
||||
- 🗂 JSON Resume](#-json-resume)
|
||||
- [💅 Theme switcher](#-theme-switcher)
|
||||
- [🗂 JSON Resume](#-json-resume)
|
||||
- [🏆 SEO component](#-seo-component)
|
||||
- [📇 Client-side vCard creation](#-client-side-vcard-creation)
|
||||
- [💫 Page transitions](#-page-transitions)
|
||||
@ -66,6 +66,16 @@ If you want to know how, have a look at the respective components:
|
||||
- [`content/repos.yml`](content/repos.yml)
|
||||
- [`src/components/molecules/Repository.jsx`](src/components/molecules/Repository.jsx)
|
||||
|
||||
### 🗂 JSON Resume
|
||||
|
||||
Resume page based on [JSON Resume](https://jsonresume.org) standard. Most site metadata and social profiles are defined in [`content/resume.json`](content/resume.json) and used throughout the site.
|
||||
|
||||
If you want to know how, have a look at the respective components:
|
||||
|
||||
- [`content/resume.json`](content/resume.json)
|
||||
- [`src/pages/resume/index.jsx`](src/pages/resume/index.jsx)
|
||||
- [`src/hooks/use-resume.js`](src/hooks/use-resume.js)
|
||||
|
||||
### 💅 Theme switcher
|
||||
|
||||
Includes a theme switcher which allows user to toggle between a light and a dark theme. Switching between them also happens automatically based on user's local sunset and sunrise times. Uses Cloudflare's geo location HTTP header functionality.
|
||||
@ -77,16 +87,6 @@ If you want to know how, have a look at the respective components:
|
||||
- [`src/components/molecules/ThemeSwitch.jsx`](src/components/molecules/ThemeSwitch.jsx)
|
||||
- [`src/hooks/use-dark-mode.jsx`](src/hooks/use-dark-mode.jsx)
|
||||
|
||||
### 🗂 JSON Resume
|
||||
|
||||
Resume page based on [JSON Resume](https://jsonresume.org) standard. Most metadata and social profiles are defined in [`content/resume.json`](content/resume.json) and used throughout the site.
|
||||
|
||||
If you want to know how, have a look at the respective components:
|
||||
|
||||
- [`content/resume.json`](content/resume.json)
|
||||
- [`src/pages/resume.jsx`](src/pages/resume.jsx)
|
||||
- [`src/hooks/use-resume.js`](src/hooks/use-resume.js)
|
||||
|
||||
### 🏆 SEO component
|
||||
|
||||
Includes a SEO component which automatically switches all required `meta` tags for search engines, Twitter Cards, and Facebook OpenGraph tags based on the browsed route/page.
|
||||
|
63
package-lock.json
generated
63
package-lock.json
generated
@ -3042,6 +3042,14 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@mapbox/hast-util-table-cell-style": {
|
||||
"version": "0.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/hast-util-table-cell-style/-/hast-util-table-cell-style-0.1.3.tgz",
|
||||
"integrity": "sha512-QsEsh5YaDvHoMQ2YHdvZy2iDnU3GgKVBTcHf6cILyoWDZtPSdlG444pL/ioPYO/GpXSfODBb9sefEetfC4v9oA==",
|
||||
"requires": {
|
||||
"unist-util-visit": "^1.3.0"
|
||||
}
|
||||
},
|
||||
"@mikaelkristiansson/domready": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@mikaelkristiansson/domready/-/domready-1.0.9.tgz",
|
||||
@ -12455,6 +12463,19 @@
|
||||
"minimalistic-assert": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"hast-to-hyperscript": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/hast-to-hyperscript/-/hast-to-hyperscript-7.0.4.tgz",
|
||||
"integrity": "sha512-vmwriQ2H0RPS9ho4Kkbf3n3lY436QKLq6VaGA1pzBh36hBi3tm1DO9bR+kaJIbpT10UqaANDkMjxvjVfr+cnOA==",
|
||||
"requires": {
|
||||
"comma-separated-tokens": "^1.0.0",
|
||||
"property-information": "^5.3.0",
|
||||
"space-separated-tokens": "^1.0.0",
|
||||
"style-to-object": "^0.2.1",
|
||||
"unist-util-is": "^3.0.0",
|
||||
"web-namespaces": "^1.1.2"
|
||||
}
|
||||
},
|
||||
"hast-util-is-element": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-1.0.3.tgz",
|
||||
@ -13158,6 +13179,11 @@
|
||||
"prop-types": "^15.5.10"
|
||||
}
|
||||
},
|
||||
"inline-style-parser": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.1.1.tgz",
|
||||
"integrity": "sha512-7NXolsK4CAS5+xvdj5OMMbI962hU/wvwoxk+LWR9Ek9bVtyuuYScDN6eS0rUm6TxApFpw7CX1o4uJzcd4AyD3Q=="
|
||||
},
|
||||
"inquirer": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.0.tgz",
|
||||
@ -20679,6 +20705,14 @@
|
||||
"react-is": "^16.8.1"
|
||||
}
|
||||
},
|
||||
"property-information": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/property-information/-/property-information-5.3.0.tgz",
|
||||
"integrity": "sha512-IslotQn1hBCZDY7SaJ3zmCjVea219VTwmOk6Pu3z9haU9m4+T8GwaDubur+6NMHEU+Fjs/6/p66z6QULPkcL1w==",
|
||||
"requires": {
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"proto-list": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
|
||||
@ -21531,6 +21565,11 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"remark-breaks": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/remark-breaks/-/remark-breaks-1.0.3.tgz",
|
||||
"integrity": "sha512-ip5hvJE8vsUJCGfgHaEJbf/JfO6KTZV+NBG68AWkEMhrjHW3Qh7EorED41mCt0FFSTrUDeRiNHovKO7cqgPZmw=="
|
||||
},
|
||||
"remark-html": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-html/-/remark-html-10.0.0.tgz",
|
||||
@ -21564,6 +21603,17 @@
|
||||
"xtend": "^4.0.1"
|
||||
}
|
||||
},
|
||||
"remark-react": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/remark-react/-/remark-react-6.0.0.tgz",
|
||||
"integrity": "sha512-5g73p8ZuqKoSdKByEf6IbXtVaHnbSEV0aamhIIqpzeNvj1wWDPX0USSPs4Gf3ZAsQIehIp6QiqJIbbXpq74bug==",
|
||||
"requires": {
|
||||
"@mapbox/hast-util-table-cell-style": "^0.1.3",
|
||||
"hast-to-hyperscript": "^7.0.0",
|
||||
"hast-util-sanitize": "^2.0.0",
|
||||
"mdast-util-to-hast": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"remark-stringify": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-7.0.4.tgz",
|
||||
@ -23631,6 +23681,14 @@
|
||||
"integrity": "sha1-eVjHk+R+MuB9K1yv5cC/jhLneQI=",
|
||||
"dev": true
|
||||
},
|
||||
"style-to-object": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-0.2.3.tgz",
|
||||
"integrity": "sha512-1d/k4EY2N7jVLOqf2j04dTc37TPOv/hHxZmvpg8Pdh8UYydxeu/C1W1U4vD8alzf5V2Gt7rLsmkr4dxAlDm9ng==",
|
||||
"requires": {
|
||||
"inline-style-parser": "0.1.1"
|
||||
}
|
||||
},
|
||||
"style-value-types": {
|
||||
"version": "3.1.7",
|
||||
"resolved": "https://registry.npmjs.org/style-value-types/-/style-value-types-3.1.7.tgz",
|
||||
@ -25715,6 +25773,11 @@
|
||||
"defaults": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"web-namespaces": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.3.tgz",
|
||||
"integrity": "sha512-r8sAtNmgR0WKOKOxzuSgk09JsHlpKlB+uHi937qypOu3PZ17UxPrierFKDye/uNHjNTTEshu5PId8rojIPj/tA=="
|
||||
},
|
||||
"webidl-conversions": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz",
|
||||
|
@ -15,7 +15,12 @@ import {
|
||||
FileText,
|
||||
Key,
|
||||
Image,
|
||||
Mail
|
||||
Mail,
|
||||
MapPin,
|
||||
Globe,
|
||||
Briefcase,
|
||||
Award,
|
||||
BookOpen
|
||||
} from 'react-feather'
|
||||
import { ReactComponent as Dribbble } from '../../images/dribbble.svg'
|
||||
import styles from './Icon.module.scss'
|
||||
@ -41,7 +46,12 @@ const Icon = ({ name, ...props }) => {
|
||||
FileText,
|
||||
Key,
|
||||
Image,
|
||||
Mail
|
||||
Mail,
|
||||
MapPin,
|
||||
Globe,
|
||||
Briefcase,
|
||||
Award,
|
||||
BookOpen
|
||||
}
|
||||
|
||||
const IconMapped = components[name]
|
||||
|
@ -39,21 +39,19 @@ export default function Vcard() {
|
||||
export const init = async meta => {
|
||||
// first, convert the avatar to base64, then construct all vCard elements
|
||||
const dataUrl = await toDataURL(meta.photoSrc, 'image/jpeg')
|
||||
const vcard = await constructVcard(dataUrl, meta)
|
||||
const vcard = await constructVcard(meta, dataUrl)
|
||||
|
||||
downloadVcard(vcard, meta)
|
||||
}
|
||||
|
||||
// Construct the download from a blob of the just constructed vCard,
|
||||
// and save it to user's file system
|
||||
export const downloadVcard = (vcard, meta) => {
|
||||
// Construct the download from a blob of the just constructed vCard,
|
||||
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'
|
||||
})
|
||||
// save it to user's file system
|
||||
saveAs(blob, name)
|
||||
}
|
||||
|
||||
export const constructVcard = async (dataUrl, meta) => {
|
||||
export const constructVcard = async meta => {
|
||||
const contact = new vCard()
|
||||
const blog = meta.profiles.filter(({ network }) => network === 'Blog')[0].url
|
||||
const twitter = meta.profiles.filter(
|
||||
@ -82,7 +80,7 @@ export const constructVcard = async (dataUrl, meta) => {
|
||||
|
||||
// Helper function to create base64 string from avatar image
|
||||
// without the need to read image file from file system
|
||||
export const toDataURL = async (photoSrc, outputFormat) => {
|
||||
export async function toDataURL(photoSrc, outputFormat) {
|
||||
const img = new Image()
|
||||
img.crossOrigin = 'Anonymous'
|
||||
img.src = photoSrc
|
||||
|
@ -1,7 +1,16 @@
|
||||
import React from 'react'
|
||||
import { render, fireEvent, waitForElement } from '@testing-library/react'
|
||||
import Vcard, { constructVcard, toDataURL, init } from './Vcard'
|
||||
import data from '../../../jest/__fixtures__/meta.json'
|
||||
import meta from '../../../jest/__fixtures__/meta.json'
|
||||
import resume from '../../../jest/__fixtures__/resume.json'
|
||||
|
||||
const metaMock = {
|
||||
...meta.metaYaml,
|
||||
name: resume.contentJson.basics.name,
|
||||
label: resume.contentJson.basics.label,
|
||||
email: resume.contentJson.basics.email,
|
||||
profiles: [...resume.contentJson.basics.profiles]
|
||||
}
|
||||
|
||||
describe('Vcard', () => {
|
||||
beforeEach(() => {
|
||||
@ -21,15 +30,12 @@ describe('Vcard', () => {
|
||||
})
|
||||
|
||||
it('combined vCard download process finishes', async () => {
|
||||
await init(data.metaYaml)
|
||||
await init(metaMock)
|
||||
expect(global.URL.createObjectURL).toHaveBeenCalledTimes(1)
|
||||
})
|
||||
|
||||
it('vCard can be constructed', async () => {
|
||||
const vcard = await constructVcard(
|
||||
'data:image/jpeg;base64,00',
|
||||
data.metaYaml
|
||||
)
|
||||
const vcard = await constructVcard(metaMock, 'data:image/jpeg;base64,00')
|
||||
expect(vcard).toBeDefined()
|
||||
})
|
||||
|
||||
|
@ -49,7 +49,6 @@ const query = graphql`
|
||||
startDate
|
||||
endDate
|
||||
summary
|
||||
highlights
|
||||
}
|
||||
awards {
|
||||
title
|
||||
|
Loading…
Reference in New Issue
Block a user