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: image and file cdn url generator adapter implementation #38685

Merged
merged 33 commits into from Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
3ff1518
alternate image url construction
kathmbeck Nov 2, 2023
8c4b42a
Merge branch 'master' into alternate-image-url
kathmbeck Nov 2, 2023
aee50e6
try using image cdn in e2e site
pieh Nov 3, 2023
6b3bdbe
Update netlify.toml
kathmbeck Nov 3, 2023
3f8efd2
have separate check for dispatching image and file service
pieh Nov 7, 2023
40d2add
fix tests?
pieh Nov 7, 2023
9ebede5
try to use images from deploy (so we can avoid using ones hosted exte…
pieh Nov 8, 2023
599f34d
replicate prod-runtime imagecdn tests in adapters
pieh Nov 8, 2023
011228a
fix import
pieh Nov 8, 2023
d9dea3f
adjusting remote-file tests
pieh Nov 8, 2023
f89b551
adjusting remote-file tests 2
pieh Nov 8, 2023
57fdd3a
cleanup/test
kathmbeck Nov 8, 2023
1a8df77
assert naturalWidth/height in image-cdn tests (both adapters and prod…
pieh Nov 9, 2023
36b918a
remove unused
pieh Nov 9, 2023
5b81ecd
don't use path prefix for alternate image cdn url
pieh Nov 13, 2023
d324338
Merge remote-tracking branch 'origin/master' into alternate-image-url
pieh Nov 13, 2023
0975496
_gatsby/file is prefixed
pieh Nov 13, 2023
d31412a
feat: move custom image cdn url generator implementation to adapter (…
pieh Nov 30, 2023
ed5f3bf
Merge branch 'master' into alternate-image-url
kathmbeck Nov 30, 2023
370d2b4
use position/cover
kathmbeck Nov 30, 2023
3527298
update comment
kathmbeck Nov 30, 2023
a175d3b
update docs
kathmbeck Nov 30, 2023
4ba752e
Merge remote-tracking branch 'origin/master' into alternate-image-url
pieh Dec 4, 2023
4514117
chore: types/jsdocs shuffle
pieh Dec 4, 2023
c7b3da3
apply suggestion from https://github.com/gatsbyjs/gatsby/pull/38685\#…
pieh Dec 6, 2023
78b108c
remove docs from feature branch
pieh Dec 6, 2023
d82f88f
feat: provide custom FILE_CDN url generator from adapter (#38735)
pieh Dec 6, 2023
53ce56d
add note that generated urls ideally are relative, but can be absolut…
pieh Dec 6, 2023
7de5336
feat: allow adding remote file allowed url patterns (#38719)
pieh Dec 11, 2023
8f1bc5d
chore: update adapter README about imageCDN
pieh Dec 11, 2023
434a946
Merge remote-tracking branch 'origin/master' into alternate-image-url
pieh Dec 14, 2023
1bb2e01
use correct remote_images for adapters e2e site
pieh Dec 14, 2023
1c79840
Merge remote-tracking branch 'origin/master' into alternate-image-url
pieh Dec 14, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .circleci/config.yml
Expand Up @@ -455,6 +455,8 @@ jobs:

e2e_tests_adapters:
<<: *e2e-executor
docker:
- image: cypress/browsers:node-18.16.1-chrome-114.0.5735.133-1-ff-114.0.2-edge-114.0.1823.51-1
steps:
- run: echo 'export CYPRESS_RECORD_KEY="${CY_CLOUD_ADAPTERS}"' >> "$BASH_ENV"
- e2e-test:
Expand Down
254 changes: 254 additions & 0 deletions e2e-tests/adapters/cypress/e2e/remote-file.cy.ts
@@ -0,0 +1,254 @@
Cypress.on("uncaught:exception", err => {
if (
(err.message.includes("Minified React error #418") ||
err.message.includes("Minified React error #423") ||
err.message.includes("Minified React error #425")) &&
Cypress.env(`TEST_PLUGIN_OFFLINE`)
) {
return false
}
})

const PATH_PREFIX = Cypress.env(`PATH_PREFIX`) || ``

// there are multiple scenarios we want to test and ensure that custom image cdn url is used:
// - child build process (SSG, Page Query)
// - main build process (SSG, Page Context)
// - query engine (SSR, Page Query)
const configs = [
{
title: `remote-file (SSG, Page Query)`,
pagePath: `/routes/remote-file/`,
placeholders: true,
},
{
title: `remote-file (SSG, Page Context)`,
pagePath: `/routes/remote-file-data-from-context/`,
placeholders: true,
},
{
title: `remote-file (SSR, Page Query)`,
pagePath: `/routes/ssr/remote-file/`,
placeholders: false,
},
]

for (const config of configs) {
describe(
config.title,
{
retries: {
runMode: 4,
},
},
() => {
beforeEach(() => {
cy.visit(config.pagePath).waitForRouteChange()

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

describe(`Image CDN`, () => {
async function testImages(images, expectations) {
for (let i = 0; i < images.length; i++) {
const expectation = expectations[i]

const url = images[i].currentSrc

const { href, origin } = new URL(url)
const urlWithoutOrigin = href.replace(origin, ``)

// using Netlify Image CDN
expect(urlWithoutOrigin).to.match(/^\/.netlify\/images/)

const res = await fetch(url, {
method: "HEAD",
})
expect(res.ok).to.be.true

const expectedNaturalWidth =
expectation.naturalWidth ?? expectation.width
const expectedNaturalHeight =
expectation.naturalHeight ?? expectation.height

if (expectation.width) {
expect(
Math.ceil(images[i].getBoundingClientRect().width)
).to.be.equal(expectation.width)
}
if (expectation.height) {
expect(
Math.ceil(images[i].getBoundingClientRect().height)
).to.be.equal(expectation.height)
}
if (expectedNaturalWidth) {
expect(Math.ceil(images[i].naturalWidth)).to.be.equal(
expectedNaturalWidth
)
}
if (expectedNaturalHeight) {
expect(Math.ceil(images[i].naturalHeight)).to.be.equal(
expectedNaturalHeight
)
}
}
}

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

// urls is array of href attribute, not absolute urls, so it already is stripped of origin
for (const urlWithoutOrigin of urls) {
// using Netlify Image CDN
expect(urlWithoutOrigin).to.match(/^\/.netlify\/images/)
const res = await fetch(urlWithoutOrigin, {
method: "HEAD",
})
expect(res.ok).to.be.true
}
})

cy.get(".resize").then({ timeout: 60000 }, async $imgs => {
await testImages(Array.from($imgs), [
{
width: 100,
height: 133,
},
{
width: 100,
height: 160,
},
{
width: 100,
height: 67,
},
])
})

cy.get(".fixed img:not([aria-hidden=true])").then(
{ timeout: 60000 },
async $imgs => {
await testImages(Array.from($imgs), [
{
width: 100,
height: 133,
},
{
width: 100,
height: 160,
},
{
width: 100,
height: 67,
},
])
}
)

cy.get(".constrained img:not([aria-hidden=true])").then(
{ timeout: 60000 },
async $imgs => {
await testImages(Array.from($imgs), [
{
width: 300,
height: 400,
},
{
width: 300,
height: 481,
},
{
width: 300,
height: 200,
},
])
}
)

cy.get(".full img:not([aria-hidden=true])").then(
{ timeout: 60000 },
async $imgs => {
await testImages(Array.from($imgs), [
{
naturalHeight: 1333,
},
{
naturalHeight: 1603,
},
{
naturalHeight: 666,
},
])
}
)
})

it(`should render a placeholder`, () => {
if (config.placeholders) {
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(".constrained_traced [data-placeholder-image]")
.first()
.should($el => {
// traced falls back to DOMINANT_COLOR
expect($el.prop("tagName")).to.be.equal("DIV")
expect($el).to.be.empty
})
}
cy.get(".full [data-placeholder-image]")
.first()
.should($el => {
expect($el.prop("tagName")).to.be.equal("DIV")
expect($el).to.be.empty
})
})
})

it(`File CDN`, () => {
cy.get('[data-testid="file-public"]').then(async $urls => {
const fileCdnFixtures = Array.from(
$urls.map((_, $url) => {
return {
urlWithoutOrigin: $url.getAttribute("href"),
allowed: $url.getAttribute("data-allowed") === "true",
}
})
)

// urls is array of href attribute, not absolute urls, so it already is stripped of origin
for (const { urlWithoutOrigin, allowed } of fileCdnFixtures) {
// using Netlify Image CDN
expect(urlWithoutOrigin).to.match(
new RegExp(`^${PATH_PREFIX}/_gatsby/file`)
)
const res = await fetch(urlWithoutOrigin, {
method: "HEAD",
})
if (allowed) {
expect(res.ok).to.be.true
} else {
expect(res.ok).to.be.false
expect(res.status).to.be.equal(500)
}
}
})
})
}
)
}
6 changes: 5 additions & 1 deletion e2e-tests/adapters/gatsby-config.ts
Expand Up @@ -24,7 +24,11 @@ const config: GatsbyConfig = {
},
trailingSlash,
pathPrefix,
plugins: [],
plugins: [
`gatsby-plugin-image`,
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
],
headers: [
{
source: `/*`,
Expand Down