Skip to content

Commit 29b236b

Browse files
wardpeetTylerBarnesveryspry
authored
feat(gatsby): add support for image-cdn (#34825)
Co-authored-by: Tyler Barnes <[email protected]> Co-authored-by: veryspry <[email protected]>
1 parent 613a8f4 commit 29b236b

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+4600
-91
lines changed

docs/docs/how-to/testing/unit-testing.md

+4
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@ module.exports = {
4444
".+\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": `<rootDir>/__mocks__/file-mock.js`,
4545
"^gatsby-page-utils/(.*)$": `gatsby-page-utils/dist/$1`, // Workaround for https://github.com/facebook/jest/issues/9771
4646
"^gatsby-core-utils/(.*)$": `gatsby-core-utils/dist/$1`, // Workaround for https://github.com/facebook/jest/issues/9771
47+
"^gatsby-plugin-utils/(.*)$": [
48+
`gatsby-plugin-utils/dist/$1`,
49+
`gatsby-plugin-utils/$1`,
50+
], // Workaround for https://github.com/facebook/jest/issues/9771
4751
},
4852
testPathIgnorePatterns: [`node_modules`, `\\.cache`, `<rootDir>.*/public`],
4953
transformIgnorePatterns: [`node_modules/(?!(gatsby)/)`],
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
before(() => {
2+
cy.exec(`npm run reset`)
3+
})
4+
5+
describe(`remote-file`, () => {
6+
beforeEach(() => {
7+
cy.visit(`/remote-file/`).waitForRouteChange()
8+
9+
// trigger intersection observer
10+
cy.scrollTo("top")
11+
cy.wait(100)
12+
cy.scrollTo("bottom")
13+
})
14+
15+
it(`should render correct dimensions`, () => {
16+
cy.get('[data-testid="public"]').then($urls => {
17+
const urls = Array.from($urls.map((_, $url) => $url.getAttribute("href")))
18+
19+
expect(urls[0].endsWith(".jpg")).to.be.true
20+
expect(urls[1].endsWith(".jpg")).to.be.true
21+
expect(urls[2].endsWith(".jpg")).to.be.true
22+
})
23+
24+
cy.get(".resize").then($imgs => {
25+
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())
26+
27+
expect(imgDimensions[0].width).to.be.equal(100)
28+
expect(imgDimensions[0].height).to.be.equal(133)
29+
expect(imgDimensions[1].width).to.be.equal(100)
30+
expect(imgDimensions[1].height).to.be.equal(160)
31+
expect(imgDimensions[2].width).to.be.equal(100)
32+
expect(imgDimensions[2].height).to.be.equal(67)
33+
})
34+
35+
cy.get(".fixed").then($imgs => {
36+
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())
37+
38+
expect(imgDimensions[0].width).to.be.equal(100)
39+
expect(imgDimensions[0].height).to.be.equal(133)
40+
expect(imgDimensions[1].width).to.be.equal(100)
41+
expect(imgDimensions[1].height).to.be.equal(160)
42+
expect(imgDimensions[2].width).to.be.equal(100)
43+
expect(imgDimensions[2].height).to.be.equal(67)
44+
})
45+
46+
cy.get(".constrained").then($imgs => {
47+
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())
48+
49+
expect(imgDimensions[0].width).to.be.equal(300)
50+
expect(imgDimensions[0].height).to.be.equal(400)
51+
expect(imgDimensions[1].width).to.be.equal(300)
52+
expect(imgDimensions[1].height).to.be.equal(481)
53+
expect(imgDimensions[2].width).to.be.equal(300)
54+
expect(imgDimensions[2].height).to.be.equal(200)
55+
})
56+
57+
cy.get(".full").then($imgs => {
58+
const parentWidth = $imgs[0].parentElement.getBoundingClientRect().width
59+
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())
60+
61+
expect(imgDimensions[0].width).to.be.equal(parentWidth)
62+
expect(Math.ceil(imgDimensions[0].height)).to.be.equal(1229)
63+
expect(imgDimensions[1].width).to.be.equal(parentWidth)
64+
expect(Math.ceil(imgDimensions[1].height)).to.be.equal(1478)
65+
expect(imgDimensions[2].width).to.be.equal(parentWidth)
66+
expect(Math.ceil(imgDimensions[2].height)).to.be.equal(614)
67+
})
68+
})
69+
70+
it(`should render a placeholder`, () => {
71+
cy.get(".fixed [data-placeholder-image]")
72+
.first()
73+
.should("have.css", "background-color", "rgb(232, 184, 8)")
74+
cy.get(".constrained [data-placeholder-image]")
75+
.first()
76+
.should($el => {
77+
expect($el.prop("tagName")).to.be.equal("IMG")
78+
expect($el.prop("src")).to.contain("data:image/jpg;base64")
79+
})
80+
cy.get(".full [data-placeholder-image]")
81+
.first()
82+
.should($el => {
83+
expect($el.prop("tagName")).to.be.equal("DIV")
84+
expect($el).to.be.empty
85+
})
86+
})
87+
})

e2e-tests/development-runtime/gatsby-node.js

+86
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,78 @@
11
const path = require(`path`)
22
const { createFilePath } = require(`gatsby-source-filesystem`)
3+
const {
4+
addRemoteFilePolyfillInterface,
5+
polyfillImageServiceDevRoutes,
6+
} = require("gatsby-plugin-utils/polyfill-remote-file")
37

8+
/** @type{import('gatsby').createSchemaCustomization} */
9+
exports.createSchemaCustomization = ({ actions, schema, store }) => {
10+
actions.createTypes(
11+
addRemoteFilePolyfillInterface(
12+
schema.buildObjectType({
13+
name: "MyRemoteFile",
14+
fields: {},
15+
interfaces: ["Node", "RemoteFile"],
16+
}),
17+
{
18+
store,
19+
schema,
20+
}
21+
)
22+
)
23+
}
24+
25+
/** @type {import('gatsby').sourceNodes} */
26+
exports.sourceNodes = ({ actions, createNodeId, createContentDigest }) => {
27+
const items = [
28+
{
29+
name: "photoA.jpg",
30+
url:
31+
"https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
32+
placeholderUrl:
33+
"https://images.unsplash.com/photo-1517849845537-4d257902454a?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=%width%&h=%height%",
34+
mimeType: "image/jpg",
35+
filename: "photo-1517849845537.jpg",
36+
width: 2000,
37+
height: 2667,
38+
},
39+
{
40+
name: "photoB.jpg",
41+
url:
42+
"https://images.unsplash.com/photo-1552053831-71594a27632d?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&h=2000&q=10",
43+
mimeType: "image/jpg",
44+
filename: "photo-1552053831.jpg",
45+
width: 1247,
46+
height: 2000,
47+
},
48+
{
49+
name: "photoC.jpg",
50+
url:
51+
"https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=2000&q=80",
52+
placeholderUrl:
53+
"https://images.unsplash.com/photo-1561037404-61cd46aa615b?ixlib=rb-1.2.1&ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&auto=format&fit=crop&w=%width%&h=%height%",
54+
mimeType: "image/jpg",
55+
filename: "photo-1561037404.jpg",
56+
width: 2000,
57+
height: 1333,
58+
},
59+
]
60+
61+
items.forEach((item, index) => {
62+
actions.createNode({
63+
id: createNodeId(`remote-file-${index}`),
64+
...item,
65+
internal: {
66+
type: "MyRemoteFile",
67+
contentDigest: createContentDigest(item.url),
68+
},
69+
})
70+
})
71+
}
72+
73+
/**
74+
* @type {import('gatsby').onCreateNode}
75+
*/
476
exports.onCreateNode = function onCreateNode({
577
actions: { createNodeField },
678
node,
@@ -27,6 +99,9 @@ exports.onCreateNode = function onCreateNode({
2799
}
28100
}
29101

102+
/**
103+
* @type {import('gatsby').createPages}
104+
*/
30105
exports.createPages = async function createPages({
31106
actions: { createPage, createRedirect },
32107
graphql,
@@ -115,6 +190,9 @@ exports.createPages = async function createPages({
115190
})
116191
}
117192

193+
/**
194+
* @type {import('gatsby').onCreatePage}
195+
*/
118196
exports.onCreatePage = async ({ page, actions }) => {
119197
const { createPage, createRedirect, deletePage } = actions
120198

@@ -169,6 +247,9 @@ exports.onCreatePage = async ({ page, actions }) => {
169247
}
170248
}
171249

250+
/**
251+
* @type {import('gatsby').createResolvers}
252+
*/
172253
exports.createResolvers = ({ createResolvers }) => {
173254
const resolvers = {
174255
QueryDataCachesJson: {
@@ -192,3 +273,8 @@ exports.createResolvers = ({ createResolvers }) => {
192273
}
193274
createResolvers(resolvers)
194275
}
276+
277+
/** @type{import('gatsby').onCreateDevServer} */
278+
exports.onCreateDevServer = ({ app }) => {
279+
polyfillImageServiceDevRoutes(app)
280+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { graphql } from "gatsby"
2+
import React from "react"
3+
4+
import { GatsbyImage } from "gatsby-plugin-image"
5+
import Layout from "../components/layout"
6+
import SEO from "../components/seo"
7+
8+
const RemoteFile = ({ data }) => {
9+
return (
10+
<Layout>
11+
<SEO title="Remote file" />
12+
13+
{data.allMyRemoteFile.nodes.map(node => {
14+
return (
15+
<div key={node.id}>
16+
<h2>
17+
<a href={node.publicUrl} data-testid="public">
18+
{node.filename}
19+
</a>
20+
</h2>
21+
<img
22+
src={node.resize.src}
23+
width={node.resize.width}
24+
height={node.resize.height}
25+
alt=""
26+
className="resize"
27+
/>
28+
<div>
29+
<GatsbyImage className="fixed" image={node.fixed} alt="" />
30+
<GatsbyImage
31+
className="constrained"
32+
image={node.constrained}
33+
alt=""
34+
/>
35+
<GatsbyImage className="full" image={node.full} alt="" />
36+
</div>
37+
</div>
38+
)
39+
})}
40+
</Layout>
41+
)
42+
}
43+
44+
export const pageQuery = graphql`
45+
{
46+
allMyRemoteFile {
47+
nodes {
48+
id
49+
url
50+
filename
51+
publicUrl
52+
resize(width: 100) {
53+
height
54+
width
55+
src
56+
}
57+
fixed: gatsbyImage(
58+
layout: FIXED
59+
width: 100
60+
placeholder: DOMINANT_COLOR
61+
)
62+
constrained: gatsbyImage(
63+
layout: CONSTRAINED
64+
width: 300
65+
placeholder: BLURRED
66+
)
67+
full: gatsbyImage(layout: FULL_WIDTH, width: 500, placeholder: NONE)
68+
}
69+
}
70+
}
71+
`
72+
73+
export default RemoteFile
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
describe(`remote-file`, () => {
2+
beforeEach(() => {
3+
cy.visit(`/remote-file/`).waitForRouteChange()
4+
5+
// trigger intersection observer
6+
cy.scrollTo("top")
7+
cy.scrollTo("bottom", {
8+
duration: 500,
9+
})
10+
})
11+
12+
it(`should render correct dimensions`, () => {
13+
cy.get('[data-testid="public"]').then($urls => {
14+
const urls = Array.from($urls.map((_, $url) => $url.getAttribute("href")))
15+
16+
expect(urls[0].endsWith(".jpg")).to.be.true
17+
expect(urls[1].endsWith(".jpg")).to.be.true
18+
expect(urls[2].endsWith(".jpg")).to.be.true
19+
})
20+
21+
cy.get(".resize").then($imgs => {
22+
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())
23+
24+
expect(imgDimensions[0].width).to.be.equal(100)
25+
expect(imgDimensions[0].height).to.be.equal(133)
26+
expect(imgDimensions[1].width).to.be.equal(100)
27+
expect(imgDimensions[1].height).to.be.equal(160)
28+
expect(imgDimensions[2].width).to.be.equal(100)
29+
expect(imgDimensions[2].height).to.be.equal(67)
30+
})
31+
32+
cy.get(".fixed").then($imgs => {
33+
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())
34+
35+
expect(imgDimensions[0].width).to.be.equal(100)
36+
expect(imgDimensions[0].height).to.be.equal(133)
37+
expect(imgDimensions[1].width).to.be.equal(100)
38+
expect(imgDimensions[1].height).to.be.equal(160)
39+
expect(imgDimensions[2].width).to.be.equal(100)
40+
expect(imgDimensions[2].height).to.be.equal(67)
41+
})
42+
43+
cy.get(".constrained").then($imgs => {
44+
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())
45+
46+
expect(imgDimensions[0].width).to.be.equal(300)
47+
expect(imgDimensions[0].height).to.be.equal(400)
48+
expect(imgDimensions[1].width).to.be.equal(300)
49+
expect(imgDimensions[1].height).to.be.equal(481)
50+
expect(imgDimensions[2].width).to.be.equal(300)
51+
expect(imgDimensions[2].height).to.be.equal(200)
52+
})
53+
54+
cy.get(".full").then($imgs => {
55+
const parentWidth = $imgs[0].parentElement.getBoundingClientRect().width
56+
const imgDimensions = $imgs.map((_, $img) => $img.getBoundingClientRect())
57+
58+
expect(imgDimensions[0].width).to.be.equal(parentWidth)
59+
expect(Math.ceil(imgDimensions[0].height)).to.be.equal(1229)
60+
expect(imgDimensions[1].width).to.be.equal(parentWidth)
61+
expect(Math.ceil(imgDimensions[1].height)).to.be.equal(1478)
62+
expect(imgDimensions[2].width).to.be.equal(parentWidth)
63+
expect(Math.ceil(imgDimensions[2].height)).to.be.equal(614)
64+
})
65+
})
66+
67+
it(`should render a placeholder`, () => {
68+
cy.get(".fixed [data-placeholder-image]")
69+
.first()
70+
.should("have.css", "background-color", "rgb(232, 184, 8)")
71+
cy.get(".constrained [data-placeholder-image]")
72+
.first()
73+
.should($el => {
74+
expect($el.prop("tagName")).to.be.equal("IMG")
75+
expect($el.prop("src")).to.contain("data:image/jpg;base64")
76+
})
77+
cy.get(".full [data-placeholder-image]")
78+
.first()
79+
.should($el => {
80+
expect($el.prop("tagName")).to.be.equal("DIV")
81+
expect($el).to.be.empty
82+
})
83+
})
84+
})

0 commit comments

Comments
 (0)