-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(gatsby): Gatsby Head API (#35980)
Co-authored-by: Michal Piechowiak <misiek.piechowiak@gmail.com> Co-authored-by: pieh <misiek.piechowiak@gmail.com> Co-authored-by: tyhopp <hopp.ty.c@gmail.com> Co-authored-by: Lennart <lekoarts@gmail.com> Co-authored-by: Ty Hopp <tyhopp@users.noreply.github.com>
- Loading branch information
1 parent
b7b3f31
commit 40810c4
Showing
112 changed files
with
2,943 additions
and
204 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
e2e-tests/development-runtime/cypress/integration/head-function-export/correct-props.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
import headFunctionExportSharedData from "../../../shared-data/head-function-export.js" | ||
|
||
it(`Head function export receive correct props`, () => { | ||
cy.visit(headFunctionExportSharedData.page.correctProps).waitForRouteChange() | ||
|
||
const data = { | ||
site: { | ||
siteMetadata: { | ||
headFunctionExport: { | ||
...headFunctionExportSharedData.data.queried, | ||
}, | ||
}, | ||
}, | ||
} | ||
const location = { | ||
pathname: headFunctionExportSharedData.page.correctProps, | ||
} | ||
|
||
const pageContext = headFunctionExportSharedData.data.context | ||
|
||
cy.getTestElement(`pageContext`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, JSON.stringify(pageContext, null, 2)) | ||
|
||
cy.getTestElement(`location`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, JSON.stringify(location, null, 2)) | ||
|
||
cy.getTestElement(`data`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, JSON.stringify(data, null, 2)) | ||
}) |
6 changes: 6 additions & 0 deletions
6
e2e-tests/development-runtime/cypress/integration/head-function-export/fs-route-api.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import { page, data } from "../../../shared-data/head-function-export.js" | ||
|
||
it(`Head function export with FS Route API should work`, () => { | ||
cy.visit(page.fsRouteApi).waitForRouteChange() | ||
cy.getTestElement(`title`).should(`have.text`, data.fsRouteApi.slug) | ||
}) |
99 changes: 99 additions & 0 deletions
99
e2e-tests/development-runtime/cypress/integration/head-function-export/html-insertion.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { page, data } from "../../../shared-data/head-function-export.js" | ||
|
||
describe(`Head function export html insertion`, () => { | ||
it(`should work with static data`, () => { | ||
cy.visit(page.basic).waitForRouteChange() | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.static.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.static.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.static.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.static.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.link) | ||
}) | ||
|
||
it(`should work with data from a page query`, () => { | ||
cy.visit(page.pageQuery).waitForRouteChange() | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.queried.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.queried.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.queried.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.queried.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.queried.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.queried.link) | ||
}) | ||
|
||
it(`should work when a head function with static data is re-exported from the page`, () => { | ||
cy.visit(page.reExport).waitForRouteChange() | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.static.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.static.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.static.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.static.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.link) | ||
}) | ||
|
||
it(`should work when an imported Head component with queried data is used`, () => { | ||
cy.visit(page.staticQuery).waitForRouteChange() | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.queried.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.queried.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.queried.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.queried.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.queried.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.queried.link) | ||
}) | ||
|
||
it(`should work in a DSG page (exporting function named config)`, () => { | ||
cy.visit(page.dsg).waitForRouteChange() | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.dsg.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.dsg.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.dsg.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.dsg.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.dsg.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.dsg.link) | ||
}) | ||
|
||
it(`should work in an SSR page (exporting function named getServerData)`, () => { | ||
cy.visit(page.ssr).waitForRouteChange() | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.ssr.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.ssr.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.ssr.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.ssr.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.ssr.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.ssr.link) | ||
}) | ||
}) |
13 changes: 13 additions & 0 deletions
13
e2e-tests/development-runtime/cypress/integration/head-function-export/invalid-elements.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { page, data } from "../../../shared-data/head-function-export.js" | ||
|
||
it(`Head function export should not include invalid elements`, () => { | ||
cy.visit(page.invalidElements).waitForRouteChange() | ||
|
||
cy.get(`head > h1`).should(`not.exist`) | ||
cy.get(`head > div`).should(`not.exist`) | ||
cy.get(`head > audio`).should(`not.exist`) | ||
cy.get(`head > video`).should(`not.exist`) | ||
cy.get(`head > title`) | ||
.should(`exist`) | ||
.and(`have.text`, data.invalidElements.title) | ||
}) |
114 changes: 114 additions & 0 deletions
114
e2e-tests/development-runtime/cypress/integration/head-function-export/navigation.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { page, data } from "../../../shared-data/head-function-export.js" | ||
|
||
// No need to test SSR navigation (anchor tags) because it's effectively covered in the html insertion tests | ||
|
||
describe(`Head function export behavior during CSR navigation (Gatsby Link)`, () => { | ||
it(`should remove tags not on next page`, () => { | ||
cy.visit(page.basic).waitForRouteChange() | ||
|
||
cy.getTestElement(`extra-meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.static.extraMeta) | ||
|
||
cy.getTestElement(`gatsby-link`).click().waitForRouteChange() | ||
|
||
cy.get(`[data-testid="extra-meta"]`).should(`not.exist`) | ||
}) | ||
|
||
it(`should add tags not on next page`, () => { | ||
cy.visit(page.basic).waitForRouteChange() | ||
|
||
cy.get(`[data-testid="extra-meta-2"]`).should(`not.exist`) | ||
|
||
cy.getTestElement(`gatsby-link`).click().waitForRouteChange() | ||
|
||
cy.getTestElement(`extra-meta-2`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.queried.extraMeta2) | ||
}) | ||
|
||
it(`should not contain tags from old tags when we navigate to page without Head export`, () => { | ||
cy.visit(page.basic).waitForRouteChange() | ||
|
||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.static.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.static.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.static.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.static.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.link) | ||
|
||
cy.getTestElement(`navigate-to-page-without-head-export`) | ||
.click() | ||
.waitForRouteChange() | ||
|
||
cy.getTestElement(`base`).should(`not.exist`) | ||
cy.getTestElement(`title`).should(`not.exist`) | ||
cy.getTestElement(`meta`).should(`not.exist`) | ||
cy.getTestElement(`noscript`).should(`not.exist`) | ||
cy.getTestElement(`style`).should(`not.exist`) | ||
cy.getTestElement(`link`).should(`not.exist`) | ||
}) | ||
|
||
/** | ||
* Technically nodes are always removed from the DOM and new ones added (in other words nodes are not reused with different data), | ||
* but since this is an implementation detail we'll still test the behavior we expect as if we didn't know that. | ||
*/ | ||
it(`should change meta tag values`, () => { | ||
// Initial load | ||
cy.visit(page.basic).waitForRouteChange() | ||
|
||
// Validate data from initial load | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.static.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.static.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.static.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.static.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.link) | ||
|
||
// Navigate to a different page via Gatsby Link | ||
cy.getTestElement(`gatsby-link`).click() | ||
|
||
// Validate data on navigated-to page | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.queried.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.queried.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.queried.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.queried.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.queried.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.queried.link) | ||
|
||
// Navigate back to original page via Gatsby Link | ||
cy.getTestElement(`gatsby-link`).click().waitForRouteChange() | ||
|
||
// Validate data is same as initial load | ||
cy.getTestElement(`base`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.base) | ||
cy.getTestElement(`title`).should(`have.text`, data.static.title) | ||
cy.getTestElement(`meta`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, data.static.meta) | ||
cy.getTestElement(`noscript`).should(`have.text`, data.static.noscript) | ||
cy.getTestElement(`style`).should(`contain`, data.static.style) | ||
cy.getTestElement(`link`) | ||
.invoke(`attr`, `href`) | ||
.should(`equal`, data.static.link) | ||
}) | ||
}) |
11 changes: 11 additions & 0 deletions
11
e2e-tests/development-runtime/cypress/integration/head-function-export/typescript.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
describe(`Tsx Pages`, () => { | ||
it(`Works with Head export`, () => { | ||
cy.visit(`/head-function-export/tsx-page`) | ||
|
||
cy.getTestElement(`title`).should(`contain`, `TypeScript`) | ||
|
||
cy.getTestElement(`name`) | ||
.invoke(`attr`, `content`) | ||
.should(`equal`, `TypeScript`) | ||
}) | ||
}) |
28 changes: 28 additions & 0 deletions
28
e2e-tests/development-runtime/cypress/integration/head-function-export/warnings.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { VALID_NODE_NAMES } from "gatsby/cache-dir/head/constants" | ||
import { page } from "../../../shared-data/head-function-export.js" | ||
|
||
describe(`Head function export should warn`, () => { | ||
beforeEach(() => { | ||
cy.visit(page.warnings, { | ||
onBeforeLoad(win) { | ||
cy.stub(win.console, `warn`).as(`consoleWarn`) | ||
}, | ||
}).waitForRouteChange() | ||
}) | ||
|
||
it(`for elements that belong in the body`, () => { | ||
cy.get(`@consoleWarn`).should( | ||
`be.calledWith`, | ||
`<h1> is not a valid head element. Please use one of the following: ${VALID_NODE_NAMES.join( | ||
`, ` | ||
)}` | ||
) | ||
}) | ||
|
||
it(`for scripts that could use the script component`, () => { | ||
cy.get(`@consoleWarn`).should( | ||
`be.calledWith`, | ||
`Do not add scripts here. Please use the <Script> component in your page template instead. For more info see: https://www.gatsbyjs.com/docs/reference/built-in-components/gatsby-script/` | ||
) | ||
}) | ||
}) |
28 changes: 28 additions & 0 deletions
28
e2e-tests/development-runtime/cypress/integration/hot-reloading/head-export.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
const { data } = require("../../../shared-data/head-function-export") | ||
|
||
const TEST_ID = `extra-meta-for-hot-reloading` | ||
|
||
describe(`hot reloading Head export`, () => { | ||
beforeEach(() => { | ||
cy.visit(`/head-function-export/basic`).waitForRouteChange() | ||
}) | ||
|
||
it(`displays placeholder content on launch`, () => { | ||
cy.getTestElement(TEST_ID) | ||
.invoke(`attr`, `content`) | ||
.should(`contain.equal`, "%SOME_EXTRA_META%") | ||
}) | ||
|
||
it(`hot reloads with new content`, () => { | ||
const text = `New Title by HMR` | ||
cy.exec( | ||
`npm run update -- --file src/pages/head-function-export/basic.js --replacements "SOME_EXTRA_META:${text}"` | ||
) | ||
|
||
cy.waitForHmr() | ||
|
||
cy.getTestElement(TEST_ID) | ||
.invoke(`attr`, `content`) | ||
.should(`contain.equal`, text) | ||
}) | ||
}) |
Oops, something went wrong.