diff --git a/e2e-tests/gatsby-script/cypress/integration/inline-scripts.ts b/e2e-tests/gatsby-script/cypress/integration/inline-scripts.ts index 6374326b144bf..3a08056442f9b 100644 --- a/e2e-tests/gatsby-script/cypress/integration/inline-scripts.ts +++ b/e2e-tests/gatsby-script/cypress/integration/inline-scripts.ts @@ -4,10 +4,19 @@ import { ResourceRecord, MarkRecord } from "../../records" // TODO - Import from gatsby core after gatsby-script is in general availability import { ScriptStrategy } from "gatsby-script" -/** - * The two test suites below test the same thing, inline scripts via dangerouslySetInnerHTML and template literals. - * To keep the tests flat and easier to debug they are duplicated instead of iterated over. - */ +// The page that we will assert against +const page = `/inline-scripts` + +const typesOfInlineScripts = [ + { + descriptor: `dangerouslySetInnerHTML`, + inlineScriptType: InlineScript.dangerouslySet, + }, + { + descriptor: `template literals`, + inlineScriptType: InlineScript.templateLiteral, + }, +] beforeEach(() => { cy.intercept(new RegExp(`framework`), { middleware: true }, req => { @@ -17,184 +26,252 @@ beforeEach(() => { }) }) -describe(`inline scripts set via dangerouslySetInnerHTML`, () => { - describe(`using the ${ScriptStrategy.preHydrate} strategy`, () => { - it(`should execute successfully`, () => { - cy.visit(`/`) - - cy.getRecord( - `${ScriptStrategy.preHydrate}-${InlineScript.dangerouslySet}`, - `success`, - true - ).should(`equal`, `true`) - }) +/** + * Normally we would duplicate the tests so they're flatter and easier to debug, + * but since the test count grew and the cases are exactly the same we'll iterate. + */ - it(`should load before other strategies`, () => { - cy.visit(`/`) +for (const { descriptor, inlineScriptType } of typesOfInlineScripts) { + describe(`inline scripts set via ${descriptor}`, () => { + describe(`using the ${ScriptStrategy.preHydrate} strategy`, () => { + it(`should execute successfully`, () => { + cy.visit(page) - cy.getRecord( - `${ScriptStrategy.preHydrate}-${InlineScript.dangerouslySet}`, - MarkRecord.executeStart - ).then(dangerouslySetExecuteStart => { cy.getRecord( - `${ScriptStrategy.postHydrate}-${InlineScript.dangerouslySet}`, - MarkRecord.executeStart - ).should(`be.greaterThan`, dangerouslySetExecuteStart) + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, + `success`, + true + ).should(`equal`, `true`) + }) + + it(`should load before other strategies`, () => { + cy.visit(page) cy.getRecord( - `${ScriptStrategy.idle}-${InlineScript.dangerouslySet}`, + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, MarkRecord.executeStart - ).should(`be.greaterThan`, dangerouslySetExecuteStart) + ).then(dangerouslySetExecuteStart => { + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + MarkRecord.executeStart + ).should(`be.greaterThan`, dangerouslySetExecuteStart) + + cy.getRecord( + `${ScriptStrategy.idle}-${inlineScriptType}`, + MarkRecord.executeStart + ).should(`be.greaterThan`, dangerouslySetExecuteStart) + }) }) }) - }) - describe(`using the ${ScriptStrategy.postHydrate} strategy`, () => { - it(`should execute successfully`, () => { - cy.visit(`/`) + describe(`using the ${ScriptStrategy.postHydrate} strategy`, () => { + it(`should execute successfully`, () => { + cy.visit(page) - cy.getRecord( - `${ScriptStrategy.postHydrate}-${InlineScript.dangerouslySet}`, - `success`, - true - ).should(`equal`, `true`) - }) - - it(`should load after the framework bundle has loaded`, () => { - cy.visit(`/`) - - // Assert framework is loaded before inline script is executed - cy.getRecord( - `${ScriptStrategy.postHydrate}-${InlineScript.dangerouslySet}`, - MarkRecord.executeStart - ).then(dangerouslySetExecuteStart => { - cy.getRecord(`framework`, ResourceRecord.responseEnd).should( - `be.lessThan`, - dangerouslySetExecuteStart - ) + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + `success`, + true + ).should(`equal`, `true`) }) - }) - }) - describe(`using the ${ScriptStrategy.idle} strategy`, () => { - it(`should execute successfully`, () => { - cy.visit(`/`) + it(`should load after the framework bundle has loaded`, () => { + cy.visit(page) - cy.getRecord( - `${ScriptStrategy.idle}-${InlineScript.dangerouslySet}`, - `success`, - true - ).should(`equal`, `true`) + // Assert framework is loaded before inline script is executed + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + MarkRecord.executeStart + ).then(dangerouslySetExecuteStart => { + cy.getRecord(`framework`, ResourceRecord.responseEnd).should( + `be.lessThan`, + dangerouslySetExecuteStart + ) + }) + }) }) - it(`should load before other strategies`, () => { - cy.visit(`/`) + describe(`using the ${ScriptStrategy.idle} strategy`, () => { + it(`should execute successfully`, () => { + cy.visit(page) - cy.getRecord( - `${ScriptStrategy.idle}-${InlineScript.dangerouslySet}`, - MarkRecord.executeStart - ).then(dangerouslySetExecuteStart => { cy.getRecord( - `${ScriptStrategy.preHydrate}-${InlineScript.dangerouslySet}`, - MarkRecord.executeStart - ).should(`be.lessThan`, dangerouslySetExecuteStart) + `${ScriptStrategy.idle}-${inlineScriptType}`, + `success`, + true + ).should(`equal`, `true`) + }) + + it(`should load before other strategies`, () => { + cy.visit(page) cy.getRecord( - `${ScriptStrategy.postHydrate}-${InlineScript.dangerouslySet}`, + `${ScriptStrategy.idle}-${inlineScriptType}`, MarkRecord.executeStart - ).should(`be.lessThan`, dangerouslySetExecuteStart) + ).then(dangerouslySetExecuteStart => { + cy.getRecord( + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, + MarkRecord.executeStart + ).should(`be.lessThan`, dangerouslySetExecuteStart) + + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + MarkRecord.executeStart + ).should(`be.lessThan`, dangerouslySetExecuteStart) + }) }) }) - }) -}) -describe(`inline scripts set via template literals`, () => { - describe(`using the ${ScriptStrategy.preHydrate} strategy`, () => { - it(`should execute successfully`, () => { - cy.visit(`/`) + describe(`when navigation occurs`, () => { + it(`should load only once on initial page load`, () => { + cy.visit(page) - cy.getRecord( - `${ScriptStrategy.preHydrate}-${InlineScript.templateLiteral}`, - `success`, - true - ).should(`equal`, `true`) - }) + cy.get(`table[id=script-mark-records] tbody`) + .children() + .should(`have.length`, 6) + cy.getRecord( + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.preHydrate) + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.postHydrate) + cy.getRecord( + `${ScriptStrategy.idle}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.idle) + }) - it(`should load before other strategies`, () => { - cy.visit(`/`) + it(`should load only once after the page is refreshed`, () => { + cy.visit(page) + cy.reload() - cy.getRecord( - `${ScriptStrategy.preHydrate}-${InlineScript.templateLiteral}`, - MarkRecord.executeStart - ).then(templateLiteralExecuteStart => { + cy.get(`table[id=script-mark-records] tbody`) + .children() + .should(`have.length`, 6) cy.getRecord( - `${ScriptStrategy.postHydrate}-${InlineScript.templateLiteral}`, - MarkRecord.executeStart - ).should(`be.greaterThan`, templateLiteralExecuteStart) - + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.preHydrate) cy.getRecord( - `${ScriptStrategy.idle}-${InlineScript.templateLiteral}`, - MarkRecord.executeStart - ).should(`be.greaterThan`, templateLiteralExecuteStart) + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.postHydrate) + cy.getRecord( + `${ScriptStrategy.idle}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.idle) }) - }) - }) - describe(`using the ${ScriptStrategy.postHydrate} strategy`, () => { - it(`should execute successfully`, () => { - cy.visit(`/`) + it(`should load only once after anchor link navigation`, () => { + cy.visit(page) + cy.get(`a[id=anchor-link-back-to-index]`).click() + cy.get(`a[href="${page}"][id=anchor-link]`).click() - cy.getRecord( - `${ScriptStrategy.postHydrate}-${InlineScript.templateLiteral}`, - `success`, - true - ).should(`equal`, `true`) - }) - - it(`should load after the framework bundle has loaded`, () => { - cy.visit(`/`) - - // Assert framework is loaded before inline script is executed - cy.getRecord( - `${ScriptStrategy.postHydrate}-${InlineScript.templateLiteral}`, - MarkRecord.executeStart - ).then(templateLiteralExecuteStart => { - cy.getRecord(`framework`, ResourceRecord.responseEnd).should( - `be.lessThan`, - templateLiteralExecuteStart - ) + cy.get(`table[id=script-mark-records] tbody`) + .children() + .should(`have.length`, 6) + cy.getRecord( + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.preHydrate) + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.postHydrate) + cy.getRecord( + `${ScriptStrategy.idle}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.idle) }) - }) - }) - describe(`using the ${ScriptStrategy.idle} strategy`, () => { - it(`should execute successfully`, () => { - cy.visit(`/`) + it(`should load only once if the page is revisited via browser back/forward buttons after anchor link navigation`, () => { + cy.visit(`/`) + cy.get(`a[href="${page}"][id=anchor-link]`).click() + cy.go(`back`) + cy.go(`forward`) - cy.getRecord( - `${ScriptStrategy.idle}-${InlineScript.templateLiteral}`, - `success`, - true - ).should(`equal`, `true`) - }) + cy.get(`table[id=script-mark-records] tbody`) + .children() + .should(`have.length`, 6) + cy.getRecord( + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.preHydrate) + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.postHydrate) + cy.getRecord( + `${ScriptStrategy.idle}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.idle) + }) - it(`should load before other strategies`, () => { - cy.visit(`/`) + it(`should load only once after Gatsby link navigation`, () => { + cy.visit(page) + cy.get(`a[id=gatsby-link-back-to-index]`).click() + cy.get(`a[href="${page}"][id=gatsby-link]`).click() - cy.getRecord( - `${ScriptStrategy.idle}-${InlineScript.templateLiteral}`, - MarkRecord.executeStart - ).then(templateLiteralExecuteStart => { + cy.get(`table[id=script-mark-records] tbody`) + .children() + .should(`have.length`, 6) cy.getRecord( - `${ScriptStrategy.preHydrate}-${InlineScript.templateLiteral}`, - MarkRecord.executeStart - ).should(`be.lessThan`, templateLiteralExecuteStart) + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.preHydrate) + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.postHydrate) + cy.getRecord( + `${ScriptStrategy.idle}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.idle) + }) + // TODO - Fix + it.skip(`should load only once if the page is revisited via browser back/forward buttons after Gatsby link navigation`, () => { + cy.visit(`/`) + cy.get(`a[href="${page}"][id=gatsby-link]`).click() + cy.go(`back`) + cy.go(`forward`) + + cy.get(`table[id=script-mark-records] tbody`) + .children() + .should(`have.length`, 6) cy.getRecord( - `${ScriptStrategy.postHydrate}-${InlineScript.templateLiteral}`, - MarkRecord.executeStart - ).should(`be.lessThan`, templateLiteralExecuteStart) + `${ScriptStrategy.preHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.preHydrate) + cy.getRecord( + `${ScriptStrategy.postHydrate}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.postHydrate) + cy.getRecord( + `${ScriptStrategy.idle}-${inlineScriptType}`, + `strategy`, + true + ).should(`equal`, ScriptStrategy.idle) }) }) }) -}) +} diff --git a/e2e-tests/gatsby-script/cypress/integration/scripts-with-sources.ts b/e2e-tests/gatsby-script/cypress/integration/scripts-with-sources.ts index 60026ab579403..b5291122ca5bf 100644 --- a/e2e-tests/gatsby-script/cypress/integration/scripts-with-sources.ts +++ b/e2e-tests/gatsby-script/cypress/integration/scripts-with-sources.ts @@ -4,6 +4,9 @@ import { ResourceRecord } from "../../records" // TODO - Import from gatsby core after gatsby-script is in general availability import { ScriptStrategy } from "gatsby-script" +// The page that we will assert against +const page = `/scripts-with-sources` + beforeEach(() => { // @ts-ignore Object.values does exist, Cypress wants ES5 in tsconfig for (const script of [...Object.values(scripts), new RegExp(`framework`)]) { @@ -18,12 +21,12 @@ beforeEach(() => { describe(`scripts with sources`, () => { describe(`using the ${ScriptStrategy.preHydrate} strategy`, () => { it(`should load successfully`, () => { - cy.visit(`/`) + cy.visit(page) cy.getRecord(Script.dayjs, `success`, true).should(`equal`, `true`) }) it(`should load before other strategies`, () => { - cy.visit(`/`) + cy.visit(page) cy.getRecord(Script.dayjs, ResourceRecord.fetchStart).then( dayjsFetchStart => { @@ -39,16 +42,24 @@ describe(`scripts with sources`, () => { } ) }) + + it(`should call an on load callback once the script has loaded`, () => { + cy.visit(page) + + cy.getRecord(Script.dayjs, ResourceRecord.responseEnd).then(() => { + cy.get(`[data-on-load-result=${ScriptStrategy.preHydrate}]`) + }) + }) }) describe(`using the ${ScriptStrategy.postHydrate} strategy`, () => { it(`should load successfully`, () => { - cy.visit(`/`) + cy.visit(page) cy.getRecord(Script.three, `success`, true).should(`equal`, `true`) }) it(`should load after the framework bundle has loaded`, () => { - cy.visit(`/`) + cy.visit(page) // Assert framework is loaded before three starts loading cy.getRecord(Script.three, ResourceRecord.fetchStart).then( @@ -60,16 +71,23 @@ describe(`scripts with sources`, () => { } ) }) + + it(`should call an on load callback once the script has loaded`, () => { + cy.visit(page) + cy.getRecord(Script.three, ResourceRecord.responseEnd).then(() => { + cy.get(`[data-on-load-result=${ScriptStrategy.postHydrate}]`) + }) + }) }) describe(`using the ${ScriptStrategy.idle} strategy`, () => { it(`should load successfully`, () => { - cy.visit(`/`) + cy.visit(page) cy.getRecord(Script.marked, `success`, true).should(`equal`, `true`) }) it(`should load after other strategies`, () => { - cy.visit(`/`) + cy.visit(page) cy.getRecord(Script.marked, ResourceRecord.fetchStart).then( markedFetchStart => { @@ -85,5 +103,146 @@ describe(`scripts with sources`, () => { } ) }) + + it(`should call an on load callback once the script has loaded`, () => { + cy.visit(page) + cy.getRecord(Script.marked, ResourceRecord.responseEnd).then(() => { + cy.get(`[data-on-load-result=${ScriptStrategy.idle}]`) + }) + }) + }) + + describe(`when navigation occurs`, () => { + it(`should load only once on initial page load`, () => { + cy.visit(page) + + cy.get(`table[id=script-resource-records] tbody`) + .children() + .should(`have.length`, 4) + cy.getRecord(Script.dayjs, `strategy`, true).should( + `equal`, + ScriptStrategy.preHydrate + ) + cy.getRecord(Script.three, `strategy`, true).should( + `equal`, + ScriptStrategy.postHydrate + ) + cy.getRecord(Script.marked, `strategy`, true).should( + `equal`, + ScriptStrategy.idle + ) + }) + + it(`should load only once after the page is refreshed`, () => { + cy.visit(page) + cy.reload() + + cy.get(`table[id=script-resource-records] tbody`) + .children() + .should(`have.length`, 4) + cy.getRecord(Script.dayjs, `strategy`, true).should( + `equal`, + ScriptStrategy.preHydrate + ) + cy.getRecord(Script.three, `strategy`, true).should( + `equal`, + ScriptStrategy.postHydrate + ) + cy.getRecord(Script.marked, `strategy`, true).should( + `equal`, + ScriptStrategy.idle + ) + }) + + it(`should load only once after anchor link navigation`, () => { + cy.visit(page) + cy.get(`a[id=anchor-link-back-to-index]`).click() + cy.get(`a[href="${page}"][id=anchor-link]`).click() + + cy.get(`table[id=script-resource-records] tbody`) + .children() + .should(`have.length`, 4) + cy.getRecord(Script.dayjs, `strategy`, true).should( + `equal`, + ScriptStrategy.preHydrate + ) + cy.getRecord(Script.three, `strategy`, true).should( + `equal`, + ScriptStrategy.postHydrate + ) + cy.getRecord(Script.marked, `strategy`, true).should( + `equal`, + ScriptStrategy.idle + ) + }) + + it(`should load only once if the page is revisited via browser back/forward buttons after anchor link navigation`, () => { + cy.visit(`/`) + cy.get(`a[href="${page}"][id=anchor-link]`).click() + cy.go(`back`) + cy.go(`forward`) + + cy.get(`table[id=script-resource-records] tbody`) + .children() + .should(`have.length`, 4) + cy.getRecord(Script.dayjs, `strategy`, true).should( + `equal`, + ScriptStrategy.preHydrate + ) + cy.getRecord(Script.three, `strategy`, true).should( + `equal`, + ScriptStrategy.postHydrate + ) + cy.getRecord(Script.marked, `strategy`, true).should( + `equal`, + ScriptStrategy.idle + ) + }) + + it(`should load only once after Gatsby link navigation`, () => { + cy.visit(page) + cy.get(`a[id=gatsby-link-back-to-index]`).click() + cy.get(`a[href="${page}"][id=gatsby-link]`).click() + + cy.get(`table[id=script-resource-records] tbody`) + .children() + .should(`have.length`, 4) + cy.getRecord(Script.dayjs, `strategy`, true).should( + `equal`, + ScriptStrategy.preHydrate + ) + cy.getRecord(Script.three, `strategy`, true).should( + `equal`, + ScriptStrategy.postHydrate + ) + cy.getRecord(Script.marked, `strategy`, true).should( + `equal`, + ScriptStrategy.idle + ) + }) + + // TODO - Fix + it.skip(`should load only once if the page is revisited via browser back/forward buttons after Gatsby link navigation`, () => { + cy.visit(`/`) + cy.get(`a[href="${page}"][id=gatsby-link]`).click() + cy.go(`back`) + cy.go(`forward`) + + cy.get(`table[id=script-resource-records] tbody`) + .children() + .should(`have.length`, 4) + cy.getRecord(Script.dayjs, `strategy`, true).should( + `equal`, + ScriptStrategy.preHydrate + ) + cy.getRecord(Script.three, `strategy`, true).should( + `equal`, + ScriptStrategy.postHydrate + ) + cy.getRecord(Script.marked, `strategy`, true).should( + `equal`, + ScriptStrategy.idle + ) + }) }) }) diff --git a/e2e-tests/gatsby-script/cypress/support/index.ts b/e2e-tests/gatsby-script/cypress/support/index.ts index 21af0090d113c..51cc2d07d255c 100644 --- a/e2e-tests/gatsby-script/cypress/support/index.ts +++ b/e2e-tests/gatsby-script/cypress/support/index.ts @@ -13,7 +13,6 @@ declare global { metric: string, raw?: boolean ): Chainable - waitForRouteChange(): unknown } } } diff --git a/e2e-tests/gatsby-script/scripts.ts b/e2e-tests/gatsby-script/scripts.ts index 6fbfc133ddb73..4c5a963933f5f 100644 --- a/e2e-tests/gatsby-script/scripts.ts +++ b/e2e-tests/gatsby-script/scripts.ts @@ -76,13 +76,12 @@ export const inlineScripts = { } function constructInlineScript(type: string, strategy: ScriptStrategy): string { - return `(function() { + return ` performance.mark(\`inline-script\`, { detail: { strategy: \`${strategy}\`, type: \`${type}\`, executeStart: performance.now() }}) window[\`${strategy}-${type}\`] = true; - })(); ` } diff --git a/e2e-tests/gatsby-script/src/components/script-mark-records.tsx b/e2e-tests/gatsby-script/src/components/script-mark-records.tsx index 982b63470312b..8f95c6544f745 100644 --- a/e2e-tests/gatsby-script/src/components/script-mark-records.tsx +++ b/e2e-tests/gatsby-script/src/components/script-mark-records.tsx @@ -12,57 +12,51 @@ export function ScriptMarkRecords(): JSX.Element { * Poll for the mark records we care about. * We'll use this approach instead of listening for the load event to be consistent. */ - function getMarkRecords(retries: number = 0): void { - const markRecords = performance.getEntriesByType( - `mark` - ) as Array - - const scriptRecords = markRecords.filter( - record => record.name === `inline-script` - ) - - if (scriptRecords.length !== 6 && retries < 10) { - setTimeout(() => { - getMarkRecords(retries + 1) - }, 100) - } + useEffect(() => { + const interval = setInterval(() => { + const markRecords = performance.getEntriesByType( + `mark` + ) as Array - setRecords(scriptRecords) - } + const scriptRecords = markRecords.filter( + markRecord => markRecord.name === `inline-script` + ) - useEffect(() => { - getMarkRecords() + if (scriptRecords.length === 6 || performance.now() > 10000) { + setRecords(scriptRecords) + clearInterval(interval) + } + }, 100) }, []) return ( - <> - - - - - - - - - - - {records.map(record => { +
TypeStrategySuccessExecute start (ms)
+ + + + + + + + + + {records + .sort((a, b) => a.detail.executeStart - b.detail.executeStart) + .map(record => { const { strategy, type, executeStart } = record.detail const key = `${strategy}-${type}` - // @ts-ignore + // @ts-ignore Do not complain about key not being a number const success = `${typeof window[key] === `boolean`}` return ( - {/* @ts-ignore */} ) })} - -
TypeStrategySuccessExecute start (ms)
{type} {strategy}{success} {trim(executeStart)}
- + + ) } diff --git a/e2e-tests/gatsby-script/src/components/script-resource-records.tsx b/e2e-tests/gatsby-script/src/components/script-resource-records.tsx index 1af9c4baffa23..3af58252df5f5 100644 --- a/e2e-tests/gatsby-script/src/components/script-resource-records.tsx +++ b/e2e-tests/gatsby-script/src/components/script-resource-records.tsx @@ -1,6 +1,5 @@ import React, { useState, useEffect } from "react" import { - scriptUrls, scriptUrlIndex, scriptStrategyIndex, scriptSuccessIndex, @@ -9,59 +8,60 @@ import { import { ResourceRecord } from "../../records" import { trim } from "../utils/trim" +interface Props { + check: (record: PerformanceResourceTiming) => boolean + count: number +} + /** * Displays performance resource records of scripts in a table. */ -export function ScriptResourceRecords(): JSX.Element { +export function ScriptResourceRecords(props: Props): JSX.Element { + const { check, count } = props + const [records, setRecords] = useState>([]) /** * Poll for the resource records we care about. * Use this approach since `PerformanceObserver` doesn't give us preload link records (e.g. framework) */ - function getResourceRecords(retries: number = 0): void { - const resourceRecords = performance.getEntriesByType( - `resource` - ) as Array - - const scriptRecords = resourceRecords.filter( - record => scriptUrls.has(record.name) || isFrameworkRecord(record) - ) - - if (scriptRecords.length !== scriptUrls.size + 1 && retries < 10) { - setTimeout(() => { - getResourceRecords(retries + 1) - }, 100) - } + useEffect(() => { + const interval = setInterval(() => { + const resourceRecords = performance.getEntriesByType( + `resource` + ) as Array - setRecords(scriptRecords) - } + const scriptRecords = resourceRecords.filter(check) - useEffect(() => { - getResourceRecords() + if (scriptRecords.length === count || performance.now() > 10000) { + setRecords(scriptRecords) + clearInterval(interval) + } + }, 100) }, []) return ( - <> - - - - - - - - - - - - {records.map(record => { +
ScriptStrategySuccessFetch start (ms)Response end (ms)
+ + + + + + + + + + + {records + .sort((a, b) => a.fetchStart - b.fetchStart) + .map(record => { const { name: url, fetchStart, responseEnd } = record || {} let name: Script | `framework` let strategy: string let success: string - if (isFrameworkRecord(record)) { + if (record.name.includes(`framework`)) { name = `framework` strategy = `N/A` success = `N/A` @@ -81,12 +81,7 @@ export function ScriptResourceRecords(): JSX.Element { ) })} - -
ScriptStrategySuccessFetch start (ms)Response end (ms)
- + + ) } - -function isFrameworkRecord(record: PerformanceResourceTiming): boolean { - return record.name.includes(`framework`) -} diff --git a/e2e-tests/gatsby-script/src/pages/index.tsx b/e2e-tests/gatsby-script/src/pages/index.tsx index 96cfb10469b3b..e4fa3bd3878f7 100644 --- a/e2e-tests/gatsby-script/src/pages/index.tsx +++ b/e2e-tests/gatsby-script/src/pages/index.tsx @@ -1,71 +1,39 @@ import * as React from "react" -import { ScriptResourceRecords } from "../components/script-resource-records" -import { ScriptMarkRecords } from "../components/script-mark-records" -import { useOccupyMainThread } from "../hooks/use-occupy-main-thread" -import { scripts, inlineScripts, InlineScript } from "../../scripts" +import { Link } from "gatsby" import "../styles/global.css" -// TODO - Import from gatsby core after gatsby-script is in general availability -import { Script, ScriptStrategy } from "gatsby-script" +const pages = [ + { name: `Scripts with sources`, path: `/scripts-with-sources` }, + { name: `Inline scripts`, path: `/inline-scripts` }, +] function IndexPage() { - useOccupyMainThread() - return (

Script component e2e test

- -
-

Scripts with sources

- - -
-

Inline scripts

- - - - - +

Tests are on other pages, links below.

+ +

Links to pages (anchor):

+ + +

Links to pages (gatsby-link):

+
    + {pages.map(({ name, path }) => ( +
  • + + {`${name} (gatsby-link)`} + +
  • + ))} +
) } diff --git a/e2e-tests/gatsby-script/src/pages/inline-scripts.tsx b/e2e-tests/gatsby-script/src/pages/inline-scripts.tsx new file mode 100644 index 0000000000000..27d3a2e4c75a3 --- /dev/null +++ b/e2e-tests/gatsby-script/src/pages/inline-scripts.tsx @@ -0,0 +1,99 @@ +import * as React from "react" +import { Link } from "gatsby" +import { ScriptResourceRecords } from "../components/script-resource-records" +import { ScriptMarkRecords } from "../components/script-mark-records" +import { useOccupyMainThread } from "../hooks/use-occupy-main-thread" +import { inlineScripts, InlineScript } from "../../scripts" +import "../styles/global.css" + +// TODO - Import from gatsby core after gatsby-script is in general availability +import { Script, ScriptStrategy } from "gatsby-script" + +function IndexPage() { + useOccupyMainThread() + + return ( +
+

Script component e2e test

+ +
+

Framework script

+ record.name.includes(`framework`)} + count={1} + /> + +
+

Inline scripts

+ + +
+ + + + + +
+ ) +} + +export default IndexPage diff --git a/e2e-tests/gatsby-script/src/pages/scripts-with-sources.tsx b/e2e-tests/gatsby-script/src/pages/scripts-with-sources.tsx new file mode 100644 index 0000000000000..939c5c13c5efa --- /dev/null +++ b/e2e-tests/gatsby-script/src/pages/scripts-with-sources.tsx @@ -0,0 +1,61 @@ +import * as React from "react" +import { Link } from "gatsby" +import { ScriptResourceRecords } from "../components/script-resource-records" +import { useOccupyMainThread } from "../hooks/use-occupy-main-thread" +import { scripts, scriptUrls } from "../../scripts" +import { onLoad } from "../utils/on-load" +import "../styles/global.css" + +// TODO - Import from gatsby core after gatsby-script is in general availability +import { Script, ScriptStrategy } from "gatsby-script" + +function IndexPage() { + useOccupyMainThread() + + return ( +
+

Script component e2e test

+ +
+

Scripts with sources

+ + scriptUrls.has(record.name) || record.name.includes(`framework`) + } + count={4} + /> + +
+ + +