Skip to content
Permalink
Browse files
feat(gatsby-script): Duplicate script callbacks if no injected script…
… callbacks (#35717)

* refactor(gatsby-script): Only dedupe if id or src exists

* test(gatsby-script): Add waitForRouteChange on visits

* feat(gatsby-script): Execute duplicate script callbacks even if first script has no callbacks

* chore(gatsby-script): Add comment about listener behavior

* feat(gatsby-script): Adjust remove event listener logic
  • Loading branch information
tyhopp committed May 23, 2022
1 parent 5f0b2fe commit f91f4771e633cf0e566df4d7cae1279431d42bbe
Showing 10 changed files with 186 additions and 104 deletions.
@@ -1,6 +1,6 @@
describe(`duplicate scripts`, () => {
beforeEach(() => {
cy.visit(`/gatsby-script-duplicate-scripts/`)
cy.visit(`/gatsby-script-duplicate-scripts/`).waitForRouteChange()
})

it(`should execute load callbacks of duplicate scripts`, () => {
@@ -14,4 +14,25 @@ describe(`duplicate scripts`, () => {
cy.get(`[data-on-error-result=duplicate-2]`).should(`have.length`, 1)
cy.get(`[data-on-error-result=duplicate-3]`).should(`have.length`, 1)
})

it(`should execute load callbacks even if the first script has no declared load callback`, () => {
cy.get(`[data-on-load-result=duplicate-first-script-no-callback]`).should(
`have.length`,
1
)
cy.get(`[data-on-load-result=duplicate-first-script-no-callback-2]`).should(
`have.length`,
1
)
})

it(`should execute error callbacks even if the first script has no declared error callback`, () => {
cy.get(`[data-on-error-result=duplicate-first-script-no-callback]`).should(
`have.length`,
1
)
cy.get(
`[data-on-error-result=duplicate-first-script-no-callback-2]`
).should(`have.length`, 1)
})
})
@@ -26,7 +26,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
describe(`inline scripts set via ${descriptor}`, () => {
describe(`using the post-hydrate strategy`, () => {
it(`should execute successfully`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.getRecord(
`post-hydrate-${inlineScriptType}`,
@@ -36,7 +36,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load after the framework bundle has loaded`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

// Assert framework is loaded before inline script is executed
cy.getRecord(
@@ -53,7 +53,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {

describe(`using the idle strategy`, () => {
it(`should execute successfully`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.getRecord(`idle-${inlineScriptType}`, `success`, true).should(
`equal`,
@@ -62,7 +62,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load after other strategies`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.getRecord(`idle-${inlineScriptType}`, markRecord.executeStart).then(
dangerouslySetExecuteStart => {
@@ -77,7 +77,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {

describe(`when navigation occurs`, () => {
it(`should load only once on initial page load`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.get(`table[id=script-mark-records] tbody`)
.children()
@@ -112,7 +112,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load only once after anchor link navigation`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.get(`a[id=anchor-link-back-to-index]`).click()
cy.url().should(`contain`, page.navigation)
cy.get(`a[href="${page.target}"][id=anchor-link]`).click()
@@ -153,7 +153,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load only once after Gatsby link navigation`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.get(`a[id=gatsby-link-back-to-index]`).click()
cy.get(`a[href="${page.target}"][id=gatsby-link]`).click()

@@ -172,7 +172,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load only once if the page is revisited via browser back/forward buttons after Gatsby link navigation`, () => {
cy.visit(page.navigation)
cy.visit(page.navigation).waitForRouteChange()
cy.get(`a[href="${page.target}"][id=gatsby-link]`).click()
cy.go(`back`)
cy.go(`forward`)
@@ -9,12 +9,12 @@ const page = {
describe(`scripts with sources`, () => {
describe(`using the post-hydrate strategy`, () => {
it(`should load successfully`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.getRecord(script.three, `success`, true).should(`equal`, `true`)
})

it(`should load after the framework bundle has loaded`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

// Assert framework is loaded before three starts loading
cy.getRecord(script.three, resourceRecord.fetchStart).then(
@@ -28,26 +28,26 @@ describe(`scripts with sources`, () => {
})

it(`should call an on load callback once the script has loaded`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.getRecord(script.three, resourceRecord.responseEnd).then(() => {
cy.get(`[data-on-load-result=post-hydrate]`)
})
})

it(`should call an on error callback if an error occurred`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.get(`[data-on-error-result=post-hydrate]`)
})
})

describe(`using the idle strategy`, () => {
it(`should load successfully`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.getRecord(script.marked, `success`, true).should(`equal`, `true`)
})

it(`should load after other strategies`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.getRecord(script.marked, resourceRecord.fetchStart).then(
markedFetchStart => {
@@ -60,21 +60,21 @@ describe(`scripts with sources`, () => {
})

it(`should call an on load callback once the script has loaded`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.getRecord(script.marked, resourceRecord.responseEnd).then(() => {
cy.get(`[data-on-load-result=idle]`)
})
})

it(`should call an on error callback if an error occurred`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.get(`[data-on-error-result=idle]`)
})
})

describe(`when navigation occurs`, () => {
it(`should load only once on initial page load`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.get(`table[id=script-resource-records] tbody`)
.children()
@@ -87,7 +87,7 @@ describe(`scripts with sources`, () => {
})

it(`should load only once after the page is refreshed`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.reload()

cy.get(`table[id=script-resource-records] tbody`)
@@ -101,7 +101,7 @@ describe(`scripts with sources`, () => {
})

it(`should load only once after anchor link navigation`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.get(`a[id=anchor-link-back-to-index]`).click()
cy.url().should(`contain`, page.navigation)
cy.get(`a[href="${page.target}"][id=anchor-link]`).click()
@@ -117,7 +117,7 @@ describe(`scripts with sources`, () => {
})

it(`should load only once if the page is revisited via browser back/forward buttons after anchor link navigation`, () => {
cy.visit(page.navigation)
cy.visit(page.navigation).waitForRouteChange()
cy.get(`a[href="${page.target}"][id=anchor-link]`).click()
cy.go(`back`)
cy.go(`forward`)
@@ -133,7 +133,7 @@ describe(`scripts with sources`, () => {
})

it(`should load only once after Gatsby link navigation`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.get(`a[id=gatsby-link-back-to-index]`).click()
cy.get(`a[href="${page.target}"][id=gatsby-link]`).click()

@@ -148,7 +148,7 @@ describe(`scripts with sources`, () => {
})

it(`should load only once if the page is revisited via browser back/forward buttons after Gatsby link navigation`, () => {
cy.visit(page.navigation)
cy.visit(page.navigation).waitForRouteChange()
cy.get(`a[href="${page.target}"][id=gatsby-link]`).click()
cy.go(`back`)
cy.go(`forward`)
@@ -3,13 +3,13 @@ import { script } from "../../../gatsby-script-scripts"
const page = `/gatsby-script-ssr-browser-apis/`

it(`scripts load successfully when used via wrapPageElement`, () => {
cy.visit(page)
cy.visit(page).waitForRouteChange()
cy.getRecord(script.three, `success`, true).should(`equal`, `true`)
cy.getRecord(script.marked, `success`, true).should(`equal`, `true`)
})

it(`scripts load successfully when used via wrapRootElement`, () => {
cy.visit(page)
cy.visit(page).waitForRouteChange()
cy.getRecord(script.jQuery, `success`, true).should(`equal`, `true`)
cy.getRecord(script.popper, `success`, true).should(`equal`, `true`)
})
@@ -6,6 +6,12 @@ import { onLoad, onError } from "../utils/gatsby-script-callbacks"
const DuplicateScripts = () => {
const [onLoadScriptLoaded, setOnLoadScriptLoaded] = useState(false)
const [onErrorScriptLoaded, setOnErrorScriptLoaded] = useState(false)
const [secondOnLoadScriptLoaded, setSecondOnLoadScriptLoaded] = useState(
false
)
const [secondOnErrorScriptLoaded, setSecondOnErrorScriptLoaded] = useState(
false
)

return (
<main>
@@ -54,6 +60,40 @@ const DuplicateScripts = () => {
}}
/>
)}

<Script src={scripts.three} />
<Script
src={scripts.three}
onLoad={() => {
onLoad(`duplicate-first-script-no-callback`)
setSecondOnLoadScriptLoaded(true)
}}
/>
{secondOnLoadScriptLoaded && (
<Script
src={scripts.three}
onLoad={() => {
onLoad(`duplicate-first-script-no-callback-2`)
}}
/>
)}

<Script src="/other-non-existent-script.js" />
<Script
src="/other-non-existent-script.js"
onError={() => {
onError(`duplicate-first-script-no-callback`)
setSecondOnErrorScriptLoaded(true)
}}
/>
{secondOnErrorScriptLoaded && (
<Script
src="/other-non-existent-script.js"
onError={() => {
onError(`duplicate-first-script-no-callback-2`)
}}
/>
)}
</main>
)
}
@@ -2,7 +2,7 @@ Cypress.config(`defaultCommandTimeout`, 30000) // Since we're asserting network

describe(`duplicate scripts`, () => {
beforeEach(() => {
cy.visit(`/gatsby-script-duplicate-scripts/`)
cy.visit(`/gatsby-script-duplicate-scripts/`).waitForRouteChange()
})

it(`should execute load callbacks of duplicate scripts`, () => {
@@ -26,7 +26,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
describe(`inline scripts set via ${descriptor}`, () => {
describe(`using the post-hydrate strategy`, () => {
it(`should execute successfully`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.getRecord(
`post-hydrate-${inlineScriptType}`,
@@ -36,7 +36,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load after the framework bundle has loaded`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

// Assert framework is loaded before inline script is executed
cy.getRecord(
@@ -53,7 +53,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {

describe(`using the idle strategy`, () => {
it(`should execute successfully`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.getRecord(`idle-${inlineScriptType}`, `success`, true).should(
`equal`,
@@ -62,7 +62,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load after other strategies`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.getRecord(`idle-${inlineScriptType}`, markRecord.executeStart).then(
dangerouslySetExecuteStart => {
@@ -77,7 +77,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {

describe(`when navigation occurs`, () => {
it(`should load only once on initial page load`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()

cy.get(`table[id=script-mark-records] tbody`)
.children()
@@ -112,7 +112,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load only once after anchor link navigation`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.get(`a[id=anchor-link-back-to-index]`).click()
cy.url().should(`contain`, page.navigation)
cy.get(`a[href="${page.target}"][id=anchor-link]`).click()
@@ -153,7 +153,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load only once after Gatsby link navigation`, () => {
cy.visit(page.target)
cy.visit(page.target).waitForRouteChange()
cy.get(`a[id=gatsby-link-back-to-index]`).click()
cy.get(`a[href="${page.target}"][id=gatsby-link]`).click()

@@ -172,7 +172,7 @@ for (const { descriptor, inlineScriptType } of typesOfInlineScripts) {
})

it(`should load only once if the page is revisited via browser back/forward buttons after Gatsby link navigation`, () => {
cy.visit(page.navigation)
cy.visit(page.navigation).waitForRouteChange()
cy.get(`a[href="${page.target}"][id=gatsby-link]`).click()
cy.go(`back`)
cy.go(`forward`)

0 comments on commit f91f477

Please sign in to comment.