Skip to content

Commit

Permalink
fix(gatsby-remark-images): regenerate markdown when used image changes (
Browse files Browse the repository at this point in the history
  • Loading branch information
Josh Johnson committed Jan 18, 2022
1 parent f5bb0b6 commit 3954944
Show file tree
Hide file tree
Showing 19 changed files with 399 additions and 105 deletions.
8 changes: 8 additions & 0 deletions e2e-tests/development-runtime/content/md-image.md
@@ -0,0 +1,8 @@
---
title: Hello World
date: 2018-12-14
---

This is a truly meaningful blog post

![Image](../src/images/image.png)
Expand Up @@ -3,31 +3,68 @@ const TEST_ID = `sub-title`
const message = `This is a sub-title`

describe(`hot reloading non-js file`, () => {
beforeEach(() => {
cy.exec(
`npm run update -- --file content/2018-12-14-hello-world.md --replacements "${message}:%${TEMPLATE}%" --exact`
)
cy.wait(1000)
describe(`markdown`, () => {
beforeEach(() => {
cy.exec(
`npm run update -- --file content/2018-12-14-hello-world.md --replacements "${message}:%${TEMPLATE}%" --exact`
)
cy.wait(1000)

cy.visit(`/2018-12-14-hello-world/`).waitForRouteChange()

cy.wait(1000)
})

it(`displays placeholder content on launch`, () => {
cy.getTestElement(TEST_ID).invoke(`text`).should(`contain`, TEMPLATE)
})

it(`hot reloads with new content`, () => {
cy.getTestElement(TEST_ID).invoke(`text`).should(`contain`, TEMPLATE)

cy.exec(
`npm run update -- --file content/2018-12-14-hello-world.md --replacements "${TEMPLATE}:${message}"`
)

// wait for socket.io to update
cy.wait(5000)

cy.getTestElement(TEST_ID).invoke(`text`).should(`eq`, message)
})
})

cy.visit(`/2018-12-14-hello-world/`).waitForRouteChange()
describe(`image`, () => {
beforeEach(() => {
cy.visit(`/md-image/`).waitForRouteChange()
cy.wait(1000)

cy.wait(1000)
})
cy.exec(
`npm run update -- --file src/images/image.png --copy "src/images/original.png"`
)
cy.wait(2000)
})

it(`displays placeholder content on launch`, () => {
cy.getTestElement(TEST_ID).invoke(`text`).should(`contain`, TEMPLATE)
})
const runImageSnapshot = (snapshotName) => {
cy.get(`.gatsby-resp-image-wrapper`)
.find("img")
.each(($el, i) => {
cy.wrap($el).matchImageSnapshot(`${snapshotName}-${i}`)
})
}

it(`hot reloads with new content`, () => {
cy.getTestElement(TEST_ID).invoke(`text`).should(`contain`, TEMPLATE)
it(`displays original content on launch`, () => {
runImageSnapshot(`non-js-file--image-original`)
})

cy.exec(
`npm run update -- --file content/2018-12-14-hello-world.md --replacements "${TEMPLATE}:${message}"`
)
it(`hot reloads with new content`, () => {
cy.exec(
`npm run update -- --file src/images/image.png --copy "src/images/new.png"`
)

// wati for socket.io to update
cy.wait(5000)
// wait for socket.io to update
cy.wait(5000)

cy.getTestElement(TEST_ID).invoke(`text`).should(`eq`, message)
runImageSnapshot(`non-js-file--image-new`)
})
})
})
Expand Up @@ -13,8 +13,9 @@ describe(`collection-routing`, () => {
})

it(`can navigate to a collection route and see its content rendered`, () => {
// this test depends on the alphabetical sorting of markdown files
cy.findByTestId(`collection-routing-blog-0`)
cy.should(`have.attr`, `data-testslug`, `/2018-12-14-hello-world/`)
.should(`have.attr`, `data-testslug`, `/2018-12-14-hello-world/`)
.click()
cy.waitForRouteChange()
.assertRoute(`/collection-routing/2018-12-14-hello-world/`)
Expand All @@ -25,8 +26,9 @@ describe(`collection-routing`, () => {
})

it(`can navigate to a collection route that uses unions and see its content rendered`, () => {
// this test depends on the alphabetical sorting of image files
cy.findByTestId(`collection-routing-image-0`)
cy.should(`have.attr`, `data-testimagename`, `gatsby-astronaut`)
.should(`have.attr`, `data-testimagename`, `gatsby-astronaut`)
.click()
cy.waitForRouteChange()
.assertRoute(`/collection-routing/gatsby-astronaut/`)
Expand Down
10 changes: 10 additions & 0 deletions e2e-tests/development-runtime/cypress/plugins/index.js
Expand Up @@ -12,6 +12,7 @@
// the project's config changing)
const blockResources = require(`./block-resources`)
const gatsbyConfig = require(`./gatsby-config`)
const { addMatchImageSnapshotPlugin } = require("cypress-image-snapshot/plugin")

module.exports = (on, config) => {
// `on` is used to hook into various events Cypress emits
Expand All @@ -20,4 +21,13 @@ module.exports = (on, config) => {
...blockResources,
...gatsbyConfig,
})

addMatchImageSnapshotPlugin(on, config)
on("before:browser:launch", (browser = {}, launchOptions) => {
if (browser.family === "chromium" || browser.family === "chrome") {
// Make retina screens run at 1x density so they match the versions in CI
launchOptions.args.push("--force-device-scale-factor=1")
}
return launchOptions
})
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions e2e-tests/development-runtime/cypress/support/commands.js
@@ -1,4 +1,6 @@
import "@testing-library/cypress/add-commands"
import { addMatchImageSnapshotCommand } from "cypress-image-snapshot/command"
import "gatsby-cypress"

Cypress.Commands.add(`lifecycleCallCount`, action =>
cy
Expand Down Expand Up @@ -113,3 +115,12 @@ Cypress.Commands.add(`getFastRefreshOverlay`, () => (
Cypress.Commands.add(`assertNoFastRefreshOverlay`, () => (
cy.get('gatsby-fast-refresh').should('not.exist')
))

addMatchImageSnapshotCommand({
customDiffDir: `/__diff_output__`,
customDiffConfig: {
threshold: 0.1,
},
failureThreshold: 0.08,
failureThresholdType: `percent`,
})
5 changes: 4 additions & 1 deletion e2e-tests/development-runtime/gatsby-config.js
Expand Up @@ -33,7 +33,10 @@ module.exports = {
{
resolve: `gatsby-transformer-remark`,
options: {
plugins: [`gatsby-remark-subcache`],
plugins: [
`gatsby-remark-subcache`,
`gatsby-remark-images`
],
},
},
`gatsby-plugin-sharp`,
Expand Down
6 changes: 4 additions & 2 deletions e2e-tests/development-runtime/package.json
Expand Up @@ -5,7 +5,7 @@
"author": "Dustin Schau <dustin@gatsbyjs.com>",
"dependencies": {
"babel-plugin-search-and-replace": "^1.1.0",
"gatsby": "^3.0.0-next.6",
"gatsby": "^4.6.0-next.4",
"gatsby-image": "^3.0.0-next.0",
"gatsby-plugin-image": "^1.0.0-next.5",
"gatsby-plugin-less": "^5.1.0-next.2",
Expand All @@ -15,10 +15,11 @@
"gatsby-plugin-sass": "^4.1.0-next.2",
"gatsby-plugin-sharp": "^3.0.0-next.5",
"gatsby-plugin-stylus": "^3.1.0-next.2",
"gatsby-remark-images": "^6.6.0-next.2",
"gatsby-seo": "^0.1.0",
"gatsby-source-filesystem": "^3.0.0-next.2",
"gatsby-transformer-json": "^3.0.0-next.0",
"gatsby-transformer-remark": "^3.0.0-next.0",
"gatsby-transformer-remark": "^5.6.0-next.2",
"gatsby-transformer-sharp": "^3.0.0-next.1",
"node-fetch": "^2.6.1",
"prop-types": "^15.6.2",
Expand Down Expand Up @@ -56,6 +57,7 @@
"@testing-library/cypress": "^7.0.0",
"cross-env": "^5.2.0",
"cypress": "6.1.0",
"cypress-image-snapshot": "^4.0.1",
"fs-extra": "^7.0.1",
"gatsby-core-utils": "^2.12.0",
"gatsby-cypress": "^0.1.7",
Expand Down
9 changes: 8 additions & 1 deletion e2e-tests/development-runtime/scripts/update.js
Expand Up @@ -13,6 +13,10 @@ const args = yargs
default: [],
type: `array`,
})
.option(`copy`, {
default: undefined,
type: `string`,
})
.option(`exact`, {
default: false,
type: `boolean`,
Expand Down Expand Up @@ -50,7 +54,7 @@ const args = yargs
async function update() {
const history = await getHistory()

const { file: fileArg, replacements, restore } = args
const { file: fileArg, replacements, restore, copy } = args
const filePath = path.resolve(fileArg)
if (restore) {
const original = history.get(filePath)
Expand Down Expand Up @@ -85,6 +89,9 @@ async function update() {
if (exists) {
await fs.remove(filePath)
}
} else if(args.copy) {
const copyFileContent = await fs.readFile(args.copy)
await fs.writeFile(filePath, copyFileContent)
} else {
const contents = replacements.reduce((replaced, pair) => {
const [key, value] = pair.split(`:`)
Expand Down
3 changes: 3 additions & 0 deletions e2e-tests/development-runtime/snapshots.js
@@ -0,0 +1,3 @@
module.exports = {
"__version": "6.1.0"
}
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added e2e-tests/development-runtime/src/images/new.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Expand Up @@ -5,7 +5,7 @@ import Layout from "../../components/layout"
export default function Index(props) {
return (
<Layout>
{props.data.markdown.nodes.map((node, index) => {
{props.data.markdown.nodes.sort((a, b) => a.fields.slug.localeCompare(b.fields.slug)).map((node, index) => {
return (
<Link
key={node.gatsbyPath}
Expand All @@ -17,7 +17,7 @@ export default function Index(props) {
</Link>
)
})}
{props.data.images.nodes.map((node, index) => {
{props.data.images.nodes.sort((a, b) => a.parent.name.localeCompare(b.parent.name)).map((node, index) => {
return (
<Link
key={node.gatsbyPath}
Expand Down Expand Up @@ -49,7 +49,7 @@ export const query = graphql`
}
}
images: allImageSharp(limit: 1, skip: 1) {
images: allImageSharp {
nodes {
parent {
... on File {
Expand Down
Expand Up @@ -266,6 +266,37 @@ exports[`it transforms images in markdown 1`] = `
</span>"
`;

exports[`it transforms images in markdown and uses getRemarkFileDependency when given 1`] = `
"<span
class=\\"gatsby-resp-image-wrapper\\"
style=\\"position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 300px; \\"
>
<a
class=\\"gatsby-resp-image-link\\"
href=\\"not-a-real-dir/not-a-real-dir/images/my-image.jpeg\\"
style=\\"display: block\\"
target=\\"_blank\\"
rel=\\"noopener\\"
>
<span
class=\\"gatsby-resp-image-background-image\\"
style=\\"padding-bottom: 133.33333333333331%; position: relative; bottom: 0; left: 0; background-image: url('data:image/png;base64,iVBORw'); background-size: cover; display: block;\\"
></span>
<img
class=\\"gatsby-resp-image-image\\"
alt=\\"image\\"
title=\\"image\\"
src=\\"not-a-real-dir/not-a-real-dir/images/my-image.jpeg\\"
srcset=\\"not-a-real-dir/not-a-real-dir/images/my-image.jpeg, not-a-real-dir/not-a-real-dir/images/my-image.jpeg\\"
sizes=\\"(max-width: 650px) 100vw, 650px\\"
style=\\"width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;\\"
loading=\\"lazy\\"
decoding=\\"async\\"
/>
</a>
</span>"
`;

exports[`it transforms images in markdown with query strings 1`] = `
"<span
class=\\"gatsby-resp-image-wrapper\\"
Expand Down
46 changes: 40 additions & 6 deletions packages/gatsby-remark-images/src/__tests__/index.js
Expand Up @@ -63,14 +63,27 @@ const createNode = content => {
return markdownNode
}

const createPluginOptions = (content, imagePaths = `/`) => {
const createPluginOptions = (
content,
imagePaths = `/`,
passGetRemarkFileDependency = false
) => {
const dirName = `not-a-real-dir`
const mockGetImageNode = imagePath => {
return {
absolutePath: queryString.parseUrl(`${dirName}/${imagePath}`).url,
}
}

let getRemarkFileDependency
if (passGetRemarkFileDependency) {
getRemarkFileDependency = jest.fn(fileQuery =>
mockGetImageNode(fileQuery.absolutePath.eq)
)
}

return {
files: [].concat(imagePaths).map(imagePath => {
return {
absolutePath: queryString.parseUrl(`${dirName}/${imagePath}`).url,
}
}),
files: [].concat(imagePaths).map(mockGetImageNode),
markdownNode: createNode(content),
markdownAST: remark.parse(content),
getNode: () => {
Expand All @@ -82,6 +95,7 @@ const createPluginOptions = (content, imagePaths = `/`) => {
parseString: remark.parse.bind(remark),
generateHTML: node => hastToHTML(toHAST(node)),
},
getRemarkFileDependency,
}
}

Expand Down Expand Up @@ -119,6 +133,26 @@ test(`it leaves files with unsupported file extensions alone`, async () => {
expect(result).toEqual([])
})

test(`it transforms images in markdown and uses getRemarkFileDependency when given`, async () => {
const imagePath = `images/my-image.jpeg`
const content = `
![image](./${imagePath})
`.trim()

const pluginOptions = createPluginOptions(content, imagePath, true)
const nodes = await plugin(pluginOptions)

expect(nodes.length).toBe(1)

const node = nodes.pop()
expect(node.type).toBe(`html`)
expect(node.value).toMatchSnapshot()
expect(node.value).not.toMatch(`<html>`)

expect(pluginOptions.getRemarkFileDependency.mock.calls.length).toBe(1)
})

test(`it transforms images in markdown`, async () => {
const imagePath = `images/my-image.jpeg`
const content = `
Expand Down

0 comments on commit 3954944

Please sign in to comment.