Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(gatsby): add support for image-cdn #34825

Merged
merged 46 commits into from
Mar 1, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
53fac80
fix(gatsby-core-utils): fix caching when using remote-file
wardpeet Feb 28, 2022
3ff3a78
feat: add support for image-cdn
wardpeet Feb 15, 2022
765183b
fix circular dep
wardpeet Feb 17, 2022
7918dfc
add support to check imagecdn is available
wardpeet Feb 17, 2022
a6ce84f
remove unused code
wardpeet Feb 17, 2022
ba5630f
fix cache
wardpeet Feb 17, 2022
1f066a0
update default values when not using GraphQL
wardpeet Feb 18, 2022
854c890
fix placeholder
wardpeet Feb 18, 2022
7c4f628
add basic unit tests
wardpeet Feb 20, 2022
9532481
feat: preserve original filenames for image cdn compat
veryspry Feb 22, 2022
679cd5a
feat: gatsbyImageData: add support for "quality" argument and default…
veryspry Feb 22, 2022
7d8346c
add tests & fix linting
wardpeet Feb 23, 2022
92f51a5
remove unnecessary stuff
wardpeet Feb 23, 2022
85d74fb
fix tests
wardpeet Feb 24, 2022
1b87181
fix tests
wardpeet Feb 24, 2022
840007a
fix jest.config
wardpeet Feb 24, 2022
37c8e53
fix tests
wardpeet Feb 24, 2022
9115c88
fix: destructure actions import in remote job dispatcher
veryspry Feb 24, 2022
6c00a4f
rename image service gql field
TylerBarnes Feb 25, 2022
4a51226
fix buss errors
wardpeet Feb 25, 2022
18d8ee2
overall fixes
wardpeet Feb 25, 2022
ea94099
fix e2e-tests
wardpeet Feb 25, 2022
d1faf53
fix test
wardpeet Feb 25, 2022
f7271e3
fix production e2e?
wardpeet Feb 25, 2022
457b758
fix bootstrap
wardpeet Feb 25, 2022
96ff8a1
fix: incorrect error string
veryspry Feb 25, 2022
be6ecaa
add npm-run-all?
wardpeet Feb 25, 2022
18846dc
Add gatsbyImage field description
TylerBarnes Feb 26, 2022
d90fff7
fix typings
wardpeet Feb 27, 2022
309f866
fix e2e tests
wardpeet Feb 27, 2022
51188af
update stuff
wardpeet Feb 28, 2022
8096870
fix test
wardpeet Feb 28, 2022
2d96d1b
add test for gatsby-worker
wardpeet Feb 28, 2022
0e89dd0
revert e2e-test package.json
wardpeet Feb 28, 2022
cbc8d5d
add quality replacement for placeholder
wardpeet Feb 28, 2022
419e56f
fix production test
wardpeet Mar 1, 2022
363f558
review changes
wardpeet Mar 1, 2022
4ad62e7
Update packages/gatsby-plugin-utils/src/polyfill-remote-file/__tests_…
wardpeet Mar 1, 2022
4855566
encode image filename to not break srcset
TylerBarnes Mar 1, 2022
0b11aed
Update README.md
TylerBarnes Mar 1, 2022
56a5e25
add encoding test
TylerBarnes Mar 1, 2022
7a8d75a
cleanup test
wardpeet Mar 1, 2022
6267eb1
fix production runtime
wardpeet Mar 1, 2022
b338fc5
use actions & fix tests
wardpeet Mar 1, 2022
3aa8f61
use actions
wardpeet Mar 1, 2022
a6651c0
fix rebase
wardpeet Mar 1, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/docs/how-to/testing/unit-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ module.exports = {
".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": `<rootDir>/__mocks__/file-mock.js`,
"^gatsby-page-utils/(.*)$": `gatsby-page-utils/dist/$1`, // Workaround for https://github.com/facebook/jest/issues/9771
"^gatsby-core-utils/(.*)$": `gatsby-core-utils/dist/$1`, // Workaround for https://github.com/facebook/jest/issues/9771
"^gatsby-plugin-utils/(.*)$": [
`gatsby-plugin-utils/dist/$1`,
`gatsby-plugin-utils/$1`,
], // Workaround for https://github.com/facebook/jest/issues/9771
},
testPathIgnorePatterns: [`node_modules`, `\\.cache`, `<rootDir>.*/public`],
transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`],
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
before(() => {
cy.exec(`npm run reset`)
})

describe(`remote-file`, () => {
beforeEach(() => {
cy.visit(`/remote-file/`).waitForRouteChange()

// trigger intersection observer
cy.scrollTo("top")
cy.wait(100)
cy.scrollTo("bottom")
})

it(`should render correct dimensions`, () => {
cy.get('[data-testid="public"]').then($urls => {
const urls = Array.from($urls.map((_, $url) => $url.getAttribute("href")))

expect(urls[0].endsWith(".jpg")).to.be.true
expect(urls[1].endsWith(".jpg")).to.be.true
expect(urls[2].endsWith(".jpg")).to.be.true
})

cy.get(".resize").then($imgs => {
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())

expect(imgDimensions[0].width).to.be.equal(100)
expect(imgDimensions[0].height).to.be.equal(133)
expect(imgDimensions[1].width).to.be.equal(100)
expect(imgDimensions[1].height).to.be.equal(160)
expect(imgDimensions[2].width).to.be.equal(100)
expect(imgDimensions[2].height).to.be.equal(67)
})

cy.get(".fixed").then($imgs => {
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())

expect(imgDimensions[0].width).to.be.equal(100)
expect(imgDimensions[0].height).to.be.equal(133)
expect(imgDimensions[1].width).to.be.equal(100)
expect(imgDimensions[1].height).to.be.equal(160)
expect(imgDimensions[2].width).to.be.equal(100)
expect(imgDimensions[2].height).to.be.equal(67)
})

cy.get(".constrained").then($imgs => {
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())

expect(imgDimensions[0].width).to.be.equal(300)
expect(imgDimensions[0].height).to.be.equal(400)
expect(imgDimensions[1].width).to.be.equal(300)
expect(imgDimensions[1].height).to.be.equal(481)
expect(imgDimensions[2].width).to.be.equal(300)
expect(imgDimensions[2].height).to.be.equal(200)
})

cy.get(".full").then($imgs => {
const parentWidth = $imgs[0].parentElement.getBoundingClientRect().width
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())

expect(imgDimensions[0].width).to.be.equal(parentWidth)
expect(Math.ceil(imgDimensions[0].height)).to.be.equal(1229)
expect(imgDimensions[1].width).to.be.equal(parentWidth)
expect(Math.ceil(imgDimensions[1].height)).to.be.equal(1478)
expect(imgDimensions[2].width).to.be.equal(parentWidth)
expect(Math.ceil(imgDimensions[2].height)).to.be.equal(614)
})
})

it(`should render a placeholder`, () => {
cy.get(".fixed [data-placeholder-image]")
.first()
.should("have.css", "background-color", "rgb(232, 184, 8)")
cy.get(".constrained [data-placeholder-image]")
.first()
.should($el => {
expect($el.prop("tagName")).to.be.equal("IMG")
expect($el.prop("src")).to.contain("data:image/jpg;base64")
})
cy.get(".full [data-placeholder-image]")
.first()
.should($el => {
expect($el.prop("tagName")).to.be.equal("DIV")
expect($el).to.be.empty
})
})
})
86 changes: 86 additions & 0 deletions e2e-tests/development-runtime/gatsby-node.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,78 @@
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
const {
addRemoteFilePolyfillInterface,
polyfillImageServiceDevRoutes,
} = require("gatsby-plugin-utils/polyfill-remote-file")

/** @type{import('gatsby').createSchemaCustomization} */
exports.createSchemaCustomization = ({ actions, schema, store }) => {
actions.createTypes(
addRemoteFilePolyfillInterface(
schema.buildObjectType({
name: "MyRemoteFile",
fields: {},
interfaces: ["Node", "RemoteFile"],
}),
{
store,
schema,
}
)
)
}

/** @type {import('gatsby').sourceNodes} */
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
const items = [
{
name: "photoA.jpg",
url:
"https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
placeholderUrl:
"https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=%width%&h=%height%",
mimeType: "image/jpg",
filename: "photo-1517849845537.jpg",
width: 2000,
height: 2667,
},
{
name: "photoB.jpg",
url:
"https://images.unsplash.com/photo-1552053831-71594a27632d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&h=2000&q=10",
mimeType: "image/jpg",
filename: "photo-1552053831.jpg",
width: 1247,
height: 2000,
},
{
name: "photoC.jpg",
url:
"https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
placeholderUrl:
"https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=%width%&h=%height%",
mimeType: "image/jpg",
filename: "photo-1561037404.jpg",
width: 2000,
height: 1333,
},
]

items.forEach((item, index) => {
actions.createNode({
id: createNodeId(`remote-file-${index}`),
...item,
internal: {
type: "MyRemoteFile",
contentDigest: createContentDigest(item.url),
},
})
})
}

/**
* @type {import('gatsby').onCreateNode}
*/
exports.onCreateNode = function onCreateNode({
actions: { createNodeField },
node,
Expand All @@ -27,6 +99,9 @@ exports.onCreateNode = function onCreateNode({
}
}

/**
* @type {import('gatsby').createPages}
*/
exports.createPages = async function createPages({
actions: { createPage, createRedirect },
graphql,
Expand Down Expand Up @@ -115,6 +190,9 @@ exports.createPages = async function createPages({
})
}

/**
* @type {import('gatsby').onCreatePage}
*/
exports.onCreatePage = async ({ page, actions }) => {
const { createPage, createRedirect, deletePage } = actions

Expand Down Expand Up @@ -169,6 +247,9 @@ exports.onCreatePage = async ({ page, actions }) => {
}
}

/**
* @type {import('gatsby').createResolvers}
*/
exports.createResolvers = ({ createResolvers }) => {
const resolvers = {
QueryDataCachesJson: {
Expand All @@ -192,3 +273,8 @@ exports.createResolvers = ({ createResolvers }) => {
}
createResolvers(resolvers)
}

/** @type{import('gatsby').onCreateDevServer} */
exports.onCreateDevServer = ({ app }) => {
polyfillImageServiceDevRoutes(app)
}
73 changes: 73 additions & 0 deletions e2e-tests/development-runtime/src/pages/remote-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { graphql } from "gatsby"
import React from "react"

import { GatsbyImage } from "gatsby-plugin-image"
import Layout from "../components/layout"
import SEO from "../components/seo"

const RemoteFile = ({ data }) => {
return (
<Layout>
<SEO title="Remote file" />

{data.allMyRemoteFile.nodes.map(node => {
return (
<div key={node.id}>
<h2>
<a href={node.publicUrl} data-testid="public">
{node.filename}
</a>
</h2>
<img
src={node.resize.src}
width={node.resize.width}
height={node.resize.height}
alt=""
className="resize"
/>
<div>
<GatsbyImage className="fixed" image={node.fixed} alt="" />
<GatsbyImage
className="constrained"
image={node.constrained}
alt=""
/>
<GatsbyImage className="full" image={node.full} alt="" />
</div>
</div>
)
})}
</Layout>
)
}

export const pageQuery = graphql`
{
allMyRemoteFile {
nodes {
id
url
filename
publicUrl
resize(width: 100) {
height
width
src
}
fixed: gatsbyImage(
layout: FIXED
width: 100
placeholder: DOMINANT_COLOR
)
constrained: gatsbyImage(
layout: CONSTRAINED
width: 300
placeholder: BLURRED
)
full: gatsbyImage(layout: FULL_WIDTH, width: 500, placeholder: NONE)
}
}
}
`

export default RemoteFile
84 changes: 84 additions & 0 deletions e2e-tests/production-runtime/cypress/integration/remote-file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
describe(`remote-file`, () => {
beforeEach(() => {
cy.visit(`/remote-file/`).waitForRouteChange()

// trigger intersection observer
cy.scrollTo("top")
cy.scrollTo("bottom", {
duration: 500,
})
})

it(`should render correct dimensions`, () => {
cy.get('[data-testid="public"]').then($urls => {
const urls = Array.from($urls.map((_, $url) => $url.getAttribute("href")))

expect(urls[0].endsWith(".jpg")).to.be.true
expect(urls[1].endsWith(".jpg")).to.be.true
expect(urls[2].endsWith(".jpg")).to.be.true
})

cy.get(".resize").then($imgs => {
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())

expect(imgDimensions[0].width).to.be.equal(100)
expect(imgDimensions[0].height).to.be.equal(133)
expect(imgDimensions[1].width).to.be.equal(100)
expect(imgDimensions[1].height).to.be.equal(160)
expect(imgDimensions[2].width).to.be.equal(100)
expect(imgDimensions[2].height).to.be.equal(67)
})

cy.get(".fixed").then($imgs => {
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())

expect(imgDimensions[0].width).to.be.equal(100)
expect(imgDimensions[0].height).to.be.equal(133)
expect(imgDimensions[1].width).to.be.equal(100)
expect(imgDimensions[1].height).to.be.equal(160)
expect(imgDimensions[2].width).to.be.equal(100)
expect(imgDimensions[2].height).to.be.equal(67)
})

cy.get(".constrained").then($imgs => {
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())

expect(imgDimensions[0].width).to.be.equal(300)
expect(imgDimensions[0].height).to.be.equal(400)
expect(imgDimensions[1].width).to.be.equal(300)
expect(imgDimensions[1].height).to.be.equal(481)
expect(imgDimensions[2].width).to.be.equal(300)
expect(imgDimensions[2].height).to.be.equal(200)
})

cy.get(".full").then($imgs => {
const parentWidth = $imgs[0].parentElement.getBoundingClientRect().width
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())

expect(imgDimensions[0].width).to.be.equal(parentWidth)
expect(Math.ceil(imgDimensions[0].height)).to.be.equal(1229)
expect(imgDimensions[1].width).to.be.equal(parentWidth)
expect(Math.ceil(imgDimensions[1].height)).to.be.equal(1478)
expect(imgDimensions[2].width).to.be.equal(parentWidth)
expect(Math.ceil(imgDimensions[2].height)).to.be.equal(614)
})
})

it(`should render a placeholder`, () => {
cy.get(".fixed [data-placeholder-image]")
.first()
.should("have.css", "background-color", "rgb(232, 184, 8)")
cy.get(".constrained [data-placeholder-image]")
.first()
.should($el => {
expect($el.prop("tagName")).to.be.equal("IMG")
expect($el.prop("src")).to.contain("data:image/jpg;base64")
})
cy.get(".full [data-placeholder-image]")
.first()
.should($el => {
expect($el.prop("tagName")).to.be.equal("DIV")
expect($el).to.be.empty
})
})
})
Loading