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
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { isNextDev, isNextDeploy, createNext } from 'e2e-utils'
import type * as Playwright from 'playwright'
import { isNextDev, isNextDeploy, createNext } from 'e2e-utils'
import { createRouterAct } from 'router-act'
import { createTestDataServer } from 'test-data-service/writer'
import { createTestLog } from 'test-log'
Expand Down Expand Up @@ -52,9 +52,11 @@ describe('segment cache (revalidation)', () => {

it('evict client cache when Server Action calls revalidatePath', async () => {
let act: ReturnType<typeof createRouterAct>
let page: Playwright.Page
const browser = await next.browser('/', {
beforePageLoad(page) {
act = createRouterAct(page)
beforePageLoad(p: Playwright.Page) {
page = p
act = createRouterAct(p)
},
})

Expand All @@ -72,13 +74,21 @@ describe('segment cache (revalidation)', () => {
}
)

// Install fake timers — freezes the 300ms cooldown setTimeout
await page.clock.install()

// Perform an action that calls revalidatePath. This should cause the
// corresponding entry to be evicted from the client cache, and a new
// prefetch to be requested.
await act(async () => {
const revalidateByPath = await browser.elementById('revalidate-by-path')
await revalidateByPath.click()
})

// Advance past cooldown inside act() to intercept the re-prefetch
await act(
async () => {
const revalidateByPath = await browser.elementById('revalidate-by-path')
await revalidateByPath.click()
await page.clock.fastForward(300)
},
{
includes: 'random-greeting [1]',
Expand All @@ -103,9 +113,11 @@ describe('segment cache (revalidation)', () => {
// bother to test every feature using both Link and Form; this test should
// be sufficient.
let act: ReturnType<typeof createRouterAct>
let page: Playwright.Page
const browser = await next.browser('/', {
beforePageLoad(page) {
act = createRouterAct(page)
beforePageLoad(p: Playwright.Page) {
page = p
act = createRouterAct(p)
},
})

Expand All @@ -123,13 +135,21 @@ describe('segment cache (revalidation)', () => {
}
)

// Install fake timers — freezes the 300ms cooldown setTimeout
await page.clock.install()

// Perform an action that calls revalidatePath. This should cause the
// corresponding entry to be evicted from the client cache, and a new
// prefetch to be requested.
await act(async () => {
const revalidateByPath = await browser.elementById('revalidate-by-path')
await revalidateByPath.click()
})

// Advance past cooldown inside act() to intercept the re-prefetch
await act(
async () => {
const revalidateByPath = await browser.elementById('revalidate-by-path')
await revalidateByPath.click()
await page.clock.fastForward(300)
},
{
includes: 'random-greeting [1]',
Expand All @@ -156,9 +176,11 @@ describe('segment cache (revalidation)', () => {
// possible to simulate the revalidating behavior of Link using the manual
// prefetch API.
let act: ReturnType<typeof createRouterAct>
let page: Playwright.Page
const browser = await next.browser('/', {
beforePageLoad(page) {
act = createRouterAct(page)
beforePageLoad(p: Playwright.Page) {
page = p
act = createRouterAct(p)
},
})

Expand All @@ -176,13 +198,21 @@ describe('segment cache (revalidation)', () => {
}
)

// Install fake timers — freezes the 300ms cooldown setTimeout
await page.clock.install()

// Perform an action that calls revalidatePath. This should cause the
// corresponding entry to be evicted from the client cache, and a new
// prefetch to be requested.
await act(async () => {
const revalidateByPath = await browser.elementById('revalidate-by-path')
await revalidateByPath.click()
})

// Advance past cooldown inside act() to intercept the re-prefetch
await act(
async () => {
const revalidateByPath = await browser.elementById('revalidate-by-path')
await revalidateByPath.click()
await page.clock.fastForward(300)
},
{
includes: 'random-greeting [1]',
Expand All @@ -203,9 +233,11 @@ describe('segment cache (revalidation)', () => {

it('evict client cache when Server Action calls revalidateTag', async () => {
let act: ReturnType<typeof createRouterAct>
let page: Playwright.Page
const browser = await next.browser('/', {
beforePageLoad(page) {
act = createRouterAct(page)
beforePageLoad(p: Playwright.Page) {
page = p
act = createRouterAct(p)
},
})

Expand All @@ -223,13 +255,21 @@ describe('segment cache (revalidation)', () => {
}
)

// Install fake timers — freezes the 300ms cooldown setTimeout
await page.clock.install()

// Perform an action that calls revalidateTag. This should cause the
// corresponding entry to be evicted from the client cache, and a new
// prefetch to be requested.
await act(async () => {
const revalidateByTag = await browser.elementById('revalidate-by-tag')
await revalidateByTag.click()
})

// Advance past cooldown inside act() to intercept the re-prefetch
await act(
async () => {
const revalidateByTag = await browser.elementById('revalidate-by-tag')
await revalidateByTag.click()
await page.clock.fastForward(300)
},
{
includes: 'random-greeting [1]',
Expand Down Expand Up @@ -354,15 +394,10 @@ describe('segment cache (revalidation)', () => {

// Perform an action that calls revalidatePath. This triggers a 300ms
// cooldown before any new prefetch requests can be made.
// Don't use act() here — act()'s settling loop calls
// waitForIdleCallback({ timeout: 100 }) repeatedly, which advances
// the fake clock and would cause the cooldown to fire prematurely.
const revalidateByPath = await browser.elementById('revalidate-by-path')
const actionResponse = page.waitForResponse(
(response) => response.request().headers()['next-action'] !== undefined
)
await revalidateByPath.click()
await actionResponse
await act(async () => {
const revalidateByPath = await browser.elementById('revalidate-by-path')
await revalidateByPath.click()
})

// The cooldown timer is frozen, so no prefetch should have occurred.
TestLog.assert([])
Expand Down
26 changes: 1 addition & 25 deletions test/lib/router-act.ts
Original file line number Diff line number Diff line change
Expand Up @@ -341,31 +341,7 @@ export function createRouterAct(

let claimedExpectations = new Set<ExpectedResponseConfig>()

// Track when the queue was last empty to implement a settling period
let queueEmptyStartTime: number | null = null
const SETTLING_PERIOD_MS = 500 // Wait 500ms after queue empties

while (
batch.pendingRequests.size > 0 ||
queueEmptyStartTime === null ||
Date.now() - queueEmptyStartTime < SETTLING_PERIOD_MS
) {
if (batch.pendingRequests.size > 0) {
// Queue has requests, reset settling timer
queueEmptyStartTime = null
} else if (queueEmptyStartTime === null) {
// Queue just became empty, start settling timer
queueEmptyStartTime = Date.now()
}

if (batch.pendingRequests.size === 0) {
// Queue is empty during settling period, wait a bit and check again
await new Promise((resolve) => setTimeout(resolve, 50))
await waitForIdleCallback()
await waitForPendingRequestChecks()
continue
}

while (batch.pendingRequests.size > 0) {
const pending = batch.pendingRequests
batch.pendingRequests = new Set()
for (const item of pending) {
Expand Down
Loading