Skip to content

Commit

Permalink
feat(gatsby): add support for image-cdn (#34825)
Browse files Browse the repository at this point in the history
Co-authored-by: Tyler Barnes <tylerdbarnes@gmail.com>
Co-authored-by: veryspry <ehlinger.matt@gmail.com>
  • Loading branch information
3 people committed Mar 1, 2022
1 parent 613a8f4 commit 29b236b
Show file tree
Hide file tree
Showing 60 changed files with 4,600 additions and 91 deletions.
4 changes: 4 additions & 0 deletions docs/docs/how-to/testing/unit-testing.md
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
@@ -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
@@ -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
@@ -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
@@ -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
})
})
})

0 comments on commit 29b236b

Please sign in to comment.