next.js/test/e2e/app-dir/node-worker-threads/node-worker-threads.test.ts
node-worker-threads.test.ts104 lines3.8 KB
import { isNextDev, nextTestSetup } from 'e2e-utils'

describe('node-worker-threads', () => {
  const { next, skipped, isTurbopack } = nextTestSetup({
    files: __dirname,
    skipDeployment: true,
    dependencies: {
      pino: '9.6.0',
      jspdf: '4.2.1',
    },
  })

  if (skipped) {
    return
  }

  // These tests are Turbopack-specific since they rely on Turbopack's worker bundling
  if (!isTurbopack) {
    it.skip('webpack doesnt support bundling worker-threads', () => {})
    return
  }

  it('should handle simple worker with relative path', async () => {
    const res = await next.fetch('/api/simple-worker-test')
    const data = await res.json()
    expect(res.status).toBe(200)
    expect(data.success).toBe(true)
    expect(data.message).toBe('pong from simple worker')
  })

  it('should handle self-referencing worker with __filename', async () => {
    const res = await next.fetch('/api/worker-test')
    const data = await res.json()
    expect(res.status).toBe(200)
    expect(data.success).toBe(true)
    expect(data.message).toBe('pong')
  })

  it('should handle pino logger with transport (thread-stream)', async () => {
    // Pino with transports uses thread-stream internally, which creates worker_threads
    // with a broad pattern like join(__dirname, 'lib', 'worker.js') that can match
    // non-evaluatable files like package.json. This tests that we properly downgrade
    // those errors to warnings via loose_errors.
    const res = await next.fetch('/api/pino-test')
    const data = await res.json()
    expect(res.status).toBe(200)
    expect(data.success).toBe(true)
  })

  it('should not expose __turbopack internal workerData to user code', async () => {
    // Verify that internal __turbopack_globals__ data used to forward globals
    // is not visible to user code accessing workerData
    const res = await next.fetch('/api/workerdata-check')
    const data = await res.json()
    expect(res.status).toBe(200)
    expect(data.success).toBe(true)
    // The __turbopack_globals__ key should NOT be visible to user code
    expect(data.hasTurbopackKeys).toBe(false)
    expect(data.turbopackKeys).toEqual([])
  })

  it('should handle jsPDF which uses Worker with eval: true (issue #91642)', async () => {
    // jsPDF internally creates Worker threads with { eval: true }, passing
    // inline JS code instead of a file path. Turbopack should not try to
    // resolve the first argument as a module reference in this case.
    const res = await next.fetch('/api/jspdf-test')
    const data = await res.json()
    expect(res.status).toBe(200)
    expect(data.success).toBe(true)
    expect(data.size).toBeGreaterThan(0)
  })

  it('should handle PNG file import in worker', async () => {
    // Test that static assets (like PNG images) can be imported and used in workers
    // The server worker returns the PNG URL, then we fetch it from the client
    // to verify the URL is correctly formed and accessible
    const res = await next.fetch('/api/png-worker-test')
    const data = await res.json()
    expect(res.status).toBe(200)
    expect(data.success).toBe(true)
    expect(data.pngInfo).toBeDefined()
    expect(data.pngInfo.width).toBe(1)
    expect(data.pngInfo.height).toBe(1)

    const url = new URL(data.pngInfo.url, 'http://localhost')
    expect(url.pathname).toMatch(
      /\/_next\/static.*\/test-image\.[0-9a-z_-]+\.png/
    )
    if (!isNextDev) {
      if (next.assetToken) {
        expect(url.searchParams.get('dpl')).toBe(next.assetToken)
      } else {
        expect(url.searchParams.get('dpl')).toBeNull()
      }
    }

    // Now fetch the PNG URL from the client to verify it's accessible
    // This tests that the URL generated by the server worker is correct
    const pngRes = await next.fetch(data.pngInfo.url)
    expect(pngRes.status).toBe(200)
    expect(pngRes.headers.get('content-type')).toBe('image/png')
  })
})
Quest for Codev2.0.0
/
SIGN IN