next.js/test/e2e/app-dir/next-dynamic-csp-nonce/next-dynamic-csp-nonce.test.ts
next-dynamic-csp-nonce.test.ts62 lines2.0 KB
import { nextTestSetup } from 'e2e-utils'

describe('next/dynamic with CSP nonce', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  it('should include nonce attribute on preload links generated by next/dynamic', async () => {
    const $ = await next.render$('/')

    // Check that preload links have the nonce attribute
    const preloadLinks = $('link[rel="preload"]')

    const dynamicPreloadLinks = preloadLinks.filter((_, element) => {
      const $element = $(element)
      const href = $element.attr('href')
      return href && /_next\/static\/(immutable\/)?chunks\//.test(href)
    })

    // There should be at least one preload link for dynamic chunks
    expect(dynamicPreloadLinks.length).toBeGreaterThan(0)

    dynamicPreloadLinks.each((_, element) => {
      const $element = $(element)
      const href = $element.attr('href')

      // Only check preload links for dynamic chunks
      if (href && /_next\/static\/(immutable\/)?chunks\//.test(href)) {
        expect($element.attr('nonce')).toBe('test-nonce')
      }
    })
  })

  it('should not generate CSP violations when using next/dynamic with nonce', async () => {
    const browser = await next.browser('/')

    // Wait for the dynamic component to load
    await browser.waitForElementByCss('#dynamic-component-loaded')

    // OPTIMIZATION: Get both text values concurrently
    const [pageTitle, componentText] = await Promise.all([
      browser.elementByCss('#page-title').then((el) => el.text()),
      browser.elementByCss('#dynamic-component-loaded').then((el) => el.text()),
    ])

    expect(pageTitle).toBe('CSP Nonce Test Page')
    expect(componentText).toBe('Dynamic component loaded successfully')

    // Check for CSP violations in Chrome (most expensive operation)
    if (global.browserName === 'chrome') {
      const logs = await browser.log()
      const cspViolations = logs.filter(
        (log) =>
          log.source === 'security' &&
          log.message.includes('Content Security Policy')
      )

      expect(cspViolations).toEqual([])
    }
  })
})
Quest for Codev2.0.0
/
SIGN IN