mirror of
https://github.com/hanzoai/next.js.git
synced 2026-10-30 14:46:38 +00:00
The `staticPathKey` condition added in #91231 inadvertently applies to server action requests on dynamic SSG routes when `cacheComponents` is enabled. Server action fetch requests from the client do not send the `RSC` header (`rsc: 1`). They only send `Accept: text/x-component` and the `Next-Action` header. This means `isRSCRequest` and `isDynamicRSCRequest` are both `false` for action requests. The new `staticPathKey` condition (`isSSG && pageIsDynamic && prerenderInfo?.fallbackRouteParams`) evaluates to `true` for dynamic PPR routes, setting `staticPathKey` even though `ssgCacheKey` is `null` for actions. With `staticPathKey` set, the request enters the fallback rendering block, which serves the cached fallback HTML shell with the action result appended to it, instead of responding with just the RSC action result. The fix excludes server action requests from the `staticPathKey` computation by adding `!isPossibleServerAction` to the condition, restoring the pre-#91231 behavior where `staticPathKey` was always `null` for server actions in production. fixes #91662 closes #91677 closes #91669
77 lines
1.9 KiB
TypeScript
77 lines
1.9 KiB
TypeScript
import { ChildProcess } from 'child_process'
|
|
import { NextInstance, createNext } from 'e2e-utils'
|
|
import fs from 'fs-extra'
|
|
import { findPort, initNextServerScript, killApp } from 'next-test-utils'
|
|
import { join } from 'path'
|
|
import webdriver from 'next-webdriver'
|
|
|
|
describe('standalone mode: server actions', () => {
|
|
let next: NextInstance
|
|
let server: ChildProcess
|
|
let appPort: number
|
|
|
|
beforeAll(async () => {
|
|
next = await createNext({
|
|
files: __dirname,
|
|
skipStart: true,
|
|
})
|
|
await next.build()
|
|
|
|
await fs.move(
|
|
join(next.testDir, '.next/standalone'),
|
|
join(next.testDir, 'standalone')
|
|
)
|
|
|
|
await fs.copy(
|
|
join(next.testDir, 'public'),
|
|
join(next.testDir, 'standalone/public')
|
|
)
|
|
|
|
await fs.copy(
|
|
join(next.testDir, '.next/static'),
|
|
join(next.testDir, 'standalone/.next/static')
|
|
)
|
|
|
|
for (const file of await fs.readdir(next.testDir)) {
|
|
if (file !== 'standalone') {
|
|
await fs.remove(join(next.testDir, file))
|
|
console.log('removed', file)
|
|
}
|
|
}
|
|
|
|
const testServer = join(next.testDir, 'standalone/server.js')
|
|
appPort = await findPort()
|
|
server = await initNextServerScript(testServer, /- Local:/, {
|
|
...process.env,
|
|
...next.env,
|
|
HOSTNAME: '::',
|
|
PORT: '' + appPort,
|
|
})
|
|
})
|
|
|
|
afterAll(async () => {
|
|
await next.destroy()
|
|
|
|
if (server) {
|
|
await killApp(server)
|
|
}
|
|
})
|
|
|
|
it('should be able to execute server actions', async () => {
|
|
const browser = await webdriver(appPort, `/world`)
|
|
await browser.elementByCss('button').click()
|
|
|
|
expect(await browser.elementByCss('#result').text()).toBe('hello world')
|
|
})
|
|
|
|
it('should be able to execute MPA server actions', async () => {
|
|
const browser = await webdriver(appPort, `/world`, {
|
|
disableJavaScript: true,
|
|
})
|
|
|
|
await browser.elementByCss('button').click()
|
|
|
|
expect(await browser.elementByCss('#result').text()).toBe('hello world')
|
|
})
|
|
})
|