Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions packages/next/src/client/app-bootstrap.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* - next/script with `beforeInteractive` strategy
*/

import { setAttributesFromProps } from './set-attributes-from-props'

const version = process.env.__NEXT_VERSION

window.next = {
Expand All @@ -27,11 +29,7 @@ function loadScriptsInSequence(
const el = document.createElement('script')

if (props) {
for (const key in props) {
if (key !== 'children') {
el.setAttribute(key, props[key])
}
}
setAttributesFromProps(el, props)
}

if (src) {
Expand Down
11 changes: 11 additions & 0 deletions test/e2e/app-dir/script-before-interactive/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<html lang="en">
<body>{children}</body>
</html>
)
}
34 changes: 34 additions & 0 deletions test/e2e/app-dir/script-before-interactive/app/multiple/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import Script from 'next/script'

export default function MultiplePage() {
return (
<div>
<h1>Multiple beforeInteractive Scripts Test</h1>
<Script
id="first-script"
strategy="beforeInteractive"
className="first-script"
dangerouslySetInnerHTML={{
__html: `
console.log('First beforeInteractive script executed');
window.firstScriptExecuted = true;
`,
}}
/>
<Script
id="second-script"
strategy="beforeInteractive"
className="second-script"
dangerouslySetInnerHTML={{
__html: `
console.log('Second beforeInteractive script executed');
window.secondScriptExecuted = true;
`,
}}
/>
<p>
This page tests multiple beforeInteractive scripts with CSS classes.
</p>
</div>
)
}
23 changes: 23 additions & 0 deletions test/e2e/app-dir/script-before-interactive/app/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import Script from 'next/script'

export default function Page() {
return (
<div>
<h1>Script beforeInteractive Test</h1>
<Script
id="example-script"
strategy="beforeInteractive"
className="example-class"
dangerouslySetInnerHTML={{
__html: `
console.log('beforeInteractive script executed');
window.beforeInteractiveExecuted = true;
`,
}}
/>
<p>
This page tests the beforeInteractive script strategy with CSS classes.
</p>
</div>
)
}
4 changes: 4 additions & 0 deletions test/e2e/app-dir/script-before-interactive/next.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
/** @type {import('next').NextConfig} */
const nextConfig = {}

module.exports = nextConfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { nextTestSetup } from 'e2e-utils'

describe('Script component with beforeInteractive strategy CSS class rendering', () => {
const { next } = nextTestSetup({
files: __dirname,
})

it('should render script tag with correct class attribute instead of classname', async () => {
const browser = await next.browser('/')

// Wait for the page to fully load
await browser.waitForElementByCss('#example-script')

// Get the HTML content to check the actual rendered attributes
const html = await browser.eval(() => document.documentElement.innerHTML)

// Check that the script tag has 'class' attribute, not 'classname'
expect(html).toContain('class="example-class"')
expect(html).not.toContain('classname="example-class"')

// Also verify the script element directly
const scriptElement = await browser.elementByCss('#example-script')
const className = await scriptElement.getAttribute('class')

expect(className).toBe('example-class')
})

it('should execute beforeInteractive script correctly', async () => {
const browser = await next.browser('/')

// Check that the script executed by looking for its side effects
const hasExecuted = await browser.eval(() => {
return (window as any).beforeInteractiveExecuted === true
})

expect(hasExecuted).toBe(true)
})

it('should render script in document head with beforeInteractive strategy', async () => {
const browser = await next.browser('/')

// Check that the script is in the head section
const scriptInHead = await browser.eval(() => {
return document.head.querySelector('#example-script') !== null
})

expect(scriptInHead).toBe(true)
})

it('should render multiple beforeInteractive scripts with correct class attributes', async () => {
const browser = await next.browser('/multiple')

const html = await browser.eval(() => document.documentElement.innerHTML)

// Check that both scripts have correct class attributes
expect(html).toContain('class="first-script"')
expect(html).toContain('class="second-script"')
expect(html).not.toContain('classname="first-script"')
expect(html).not.toContain('classname="second-script"')
})
})
Loading