import webdriver, { BrowserInterface } from 'next-webdriver' import { createNext } from 'e2e-utils' import { NextInstance } from 'e2e-utils' import { check } from 'next-test-utils' describe('beforeInteractive in document Head', () => { let next: NextInstance beforeAll(async () => { next = await createNext({ files: { 'pages/_document.js': ` import { Html, Head, Main, NextScript } from 'next/document' import Script from 'next/script' export default function Document() { return (
Home page
> ) } `, }, dependencies: { react: '19.0.0-rc-f994737d14-20240522', 'react-dom': '19.0.0-rc-f994737d14-20240522', }, }) }) afterAll(() => next.destroy()) it('Script is injected server-side', async () => { let browser: BrowserInterface try { browser = await webdriver(next.url, '/') const script = await browser.eval( `document.querySelector('script[data-nscript="beforeInteractive"]')` ) expect(script).not.toBeNull() } finally { if (browser) await browser.close() } }) }) describe('beforeInteractive in document body', () => { let next: NextInstance beforeAll(async () => { next = await createNext({ files: { 'pages/_document.js': ` import { Html, Head, Main, NextScript } from 'next/document' import Script from 'next/script' export default function Document() { return (Home page
> ) } `, }, dependencies: { react: '19.0.0-rc-f994737d14-20240522', 'react-dom': '19.0.0-rc-f994737d14-20240522', }, }) }) afterAll(() => next.destroy()) it('Script is injected server-side', async () => { let browser: BrowserInterface try { browser = await webdriver(next.url, '/') const script = await browser.eval( `document.querySelector('script[data-nscript="beforeInteractive"]')` ) expect(script).not.toBeNull() } finally { if (browser) await browser.close() } }) }) describe('empty strategy in document Head', () => { let next: NextInstance beforeAll(async () => { next = await createNext({ files: { 'pages/_document.js': ` import { Html, Head, Main, NextScript } from 'next/document' import Script from 'next/script' export default function Document() { return (Home page
> ) } `, }, dependencies: { react: '19.0.0-rc-f994737d14-20240522', 'react-dom': '19.0.0-rc-f994737d14-20240522', }, }) }) afterAll(() => next.destroy()) it('Script is injected server-side', async () => { let browser: BrowserInterface try { browser = await webdriver(next.url, '/') const script = await browser.eval( `document.querySelector('script[data-nscript="afterInteractive"]')` ) expect(script).not.toBeNull() } finally { if (browser) await browser.close() } }) }) describe('empty strategy in document body', () => { let next: NextInstance beforeAll(async () => { next = await createNext({ files: { 'pages/_document.js': ` import { Html, Head, Main, NextScript } from 'next/document' import Script from 'next/script' export default function Document() { return (Home page
> ) } `, }, dependencies: { react: '19.0.0-rc-f994737d14-20240522', 'react-dom': '19.0.0-rc-f994737d14-20240522', }, }) }) afterAll(() => next.destroy()) it('Script is injected server-side', async () => { let browser: BrowserInterface try { browser = await webdriver(next.url, '/') const script = await browser.eval( `document.querySelector('script[data-nscript="afterInteractive"]')` ) expect(script).not.toBeNull() } finally { if (browser) await browser.close() } }) }) ;(process.env.TURBOPACK ? describe.skip : describe)( 'experimental.nextScriptWorkers', () => { describe('experimental.nextScriptWorkers: false with no Partytown dependency', () => { let next: NextInstance beforeAll(async () => { next = await createNext({ files: { 'pages/index.js': ` import Script from 'next/script' export default function Page() { return ( <> > ) } `, }, // TODO: @housseindjirdeh: verify React 18 functionality dependencies: { react: '19.0.0-rc-f994737d14-20240522', 'react-dom': '19.0.0-rc-f994737d14-20240522', }, }) }) afterAll(() => next.destroy()) it('Partytown snippet is not injected to head if not enabled in configuration', async () => { let browser: BrowserInterface try { browser = await webdriver(next.url, '/') const snippetScript = await browser.eval( `document.querySelector('script[data-partytown]')` ) expect(snippetScript).toEqual(null) } finally { if (browser) await browser.close() } }) }) describe('experimental.nextScriptWorkers: true with required Partytown dependency for external script', () => { let next: NextInstance beforeAll(async () => { next = await createNext({ nextConfig: { experimental: { nextScriptWorkers: true, }, }, files: { 'pages/index.js': ` import Script from 'next/script' export default function Page() { return ( <> > ) } `, }, dependencies: { react: '19.0.0-rc-f994737d14-20240522', 'react-dom': '19.0.0-rc-f994737d14-20240522', '@builder.io/partytown': '0.4.2', }, }) }) afterAll(() => next.destroy()) it('Partytown snippets are injected to head if enabled in configuration', async () => { let browser: BrowserInterface try { browser = await webdriver(next.url, '/') const snippetScript = await browser.eval( `document.querySelector('script[data-partytown]').innerHTML` ) const configScript = await browser.eval( `document.querySelector('script[data-partytown-config]').innerHTML` ) expect(snippetScript).not.toEqual(null) // A default config is included that points to the correct folder that hosts partytown's static files expect(configScript).not.toEqual(null) expect(configScript.replace(/(?: *[\n\r])+ */g, '')).toEqual( 'partytown = {lib: "/_next/static/~partytown/"};' ) } finally { if (browser) await browser.close() } }) it('Worker scripts are modified by Partytown to execute on a worker thread', async () => { let browser: BrowserInterface try { browser = await webdriver(next.url, '/') // Partytown modifies type to "text/partytown-x" after it has been executed in the web worker await check(async () => { const processedWorkerScripts = await browser.eval( `document.querySelectorAll('script[type="text/partytown-x"]').length` ) return processedWorkerScripts > 0 ? 'success' : processedWorkerScripts }, 'success') } finally { if (browser) await browser.close() } }) }) describe('experimental.nextScriptWorkers: true with required Partytown dependency for inline script', () => { let next: NextInstance // Note: previously we were using `finally` cluase inside of test assertion. However, if the test times out // exceeding jest.setTimeout() value, the finally clause is not executed and subsequent tests will fail due to // hanging next instance. afterEach(async () => { if (next) { await next.destroy() next = undefined } }) const createNextApp = async (script) => await createNext({ nextConfig: { experimental: { nextScriptWorkers: true, }, }, files: { 'pages/index.js': ` import Script from 'next/script' export default function Page() { return ( <> ${script} > ) } `, }, dependencies: { react: '19.0.0-rc-f994737d14-20240522', 'react-dom': '19.0.0-rc-f994737d14-20240522', '@builder.io/partytown': '0.4.2', }, }) it('Inline worker script through children is modified by Partytown to execute on a worker thread', async () => { let browser: BrowserInterface next = await createNextApp( `` ) try { browser = await webdriver(next.url, '/') // Partytown modifies type to "text/partytown-x" after it has been executed in the web worker await check(async () => { const processedWorkerScripts = await browser.eval( `document.querySelectorAll('script[type="text/partytown-x"]').length` ) return processedWorkerScripts + '' }, '1') const text = await browser.elementById('text').text() expect(text).toBe('abc') } finally { if (browser) await browser.close() } }) it('Inline worker script through dangerouslySetInnerHtml is modified by Partytown to execute on a worker thread', async () => { let browser: BrowserInterface next = await createNextApp( `` ) try { browser = await webdriver(next.url, '/') // Partytown modifies type to "text/partytown-x" after it has been executed in the web worker await check(async () => { const processedWorkerScripts = await browser.eval( `document.querySelectorAll('script[type="text/partytown-x"]').length` ) return processedWorkerScripts + '' }, '1') const text = await browser.elementById('text').text() expect(text).toBe('abcd') } finally { if (browser) await browser.close() } }) }) describe('experimental.nextScriptWorkers: true with config override', () => { let next: NextInstance beforeAll(async () => { next = await createNext({ nextConfig: { experimental: { nextScriptWorkers: true, }, }, files: { 'pages/_document.js': ` import Document, { Html, Head, Main, NextScript } from "next/document"; class MyDocument extends Document { render() { return (