Repository: vercel/next.js
Pull request: Turbopack: add test case for persistent caching

### What?

Make the test case cover more cases by testing for add and removal of pages, client components, multiple noop builds and multiple change cycles.

Closes PACK-4381

Changed files:
filename: test/e2e/persistent-caching/app/client/page.tsx
patch: @@ -0,0 +1,10 @@
+'use client'
+
+export default function Page() {
+  return (
+    <>
+      <p>hello world</p>
+      <main suppressHydrationWarning>Timestamp</main>
+    </>
+  )
+}

filename: test/e2e/persistent-caching/app/remove-me/page.tsx
patch: @@ -0,0 +1,8 @@
+export default function Page() {
+  return (
+    <>
+      <p>hello world</p>
+      <main suppressHydrationWarning>Timestamp</main>
+    </>
+  )
+}

filename: test/e2e/persistent-caching/persistent-caching.test.ts
patch: @@ -2,6 +2,7 @@ import { nextTestSetup } from 'e2e-utils'
 import { waitFor } from 'next-test-utils'
 
 describe('persistent-caching', () => {
+  process.env.NEXT_DEPLOYMENT_ID = '' + Date.now()
   const { skipped, next, isNextDev } = nextTestSetup({
     files: __dirname,
     skipDeployment: true,
@@ -26,18 +27,25 @@ describe('persistent-caching', () => {
 
   async function start() {
     if (!isNextDev) {
-      await next.build()
+      // TODO workaround for missing content hashing on output static files
+      // Browser caching would break this test case, but we want to test persistent caching.
+      process.env.NEXT_DEPLOYMENT_ID = '' + Date.now()
     }
     await next.start()
   }
 
   it('should persistent cache loaders', async () => {
-    let appTimestamp, pagesTimestamp
+    let appTimestamp, appClientTimestamp, pagesTimestamp
     {
       const browser = await next.browser('/')
       appTimestamp = await browser.elementByCss('main').text()
       await browser.close()
     }
+    {
+      const browser = await next.browser('/client')
+      appClientTimestamp = await browser.elementByCss('main').text()
+      await browser.close()
+    }
     {
       const browser = await next.browser('/pages')
       pagesTimestamp = await browser.elementByCss('main').text()
@@ -52,59 +60,119 @@ describe('persistent-caching', () => {
       await browser.close()
     }
     {
-      const browser = await next.browser('/pages')
+      const browser = await next.browser('/client')
       // TODO Persistent Caching for webpack dev server is broken
-      expect(await browser.elementByCss('main').text()).toBe(pagesTimestamp)
-      await browser.close()
-    }
-  })
-
-  it('should allow to change files while stopped', async () => {
-    {
-      const browser = await next.browser('/')
-      expect(await browser.elementByCss('p').text()).toBe('hello world')
+      expect(await browser.elementByCss('main').text()).toBe(appClientTimestamp)
       await browser.close()
     }
     {
       const browser = await next.browser('/pages')
-      expect(await browser.elementByCss('p').text()).toBe('hello world')
+      // TODO Persistent Caching for webpack dev server is broken
+      expect(await browser.elementByCss('main').text()).toBe(pagesTimestamp)
       await browser.close()
     }
+  })
 
-    await stop()
+  it.each([1, 2, 3])(
+    'should allow to change files while stopped (run %d)',
+    async (_i) => {
+      async function checkInitial() {
+        {
+          const browser = await next.browser('/')
+          expect(await browser.elementByCss('p').text()).toBe('hello world')
+          await browser.close()
+        }
+        {
+          const browser = await next.browser('/client')
+          expect(await browser.elementByCss('p').text()).toBe('hello world')
+          await browser.close()
+        }
+        {
+          const browser = await next.browser('/pages')
+          expect(await browser.elementByCss('p').text()).toBe('hello world')
+          await browser.close()
+        }
+        {
+          const browser = await next.browser('/remove-me')
+          expect(await browser.elementByCss('p').text()).toBe('hello world')
+          await browser.close()
+        }
+      }
 
-    await next.patchFile(
-      'pages/pages.tsx',
-      (content) => {
-        return content.replace('hello world', 'hello persistent caching')
-      },
-      async () => {
-        await next.patchFile(
-          'app/page.tsx',
-          (content) => {
-            return content.replace('hello world', 'hello persistent caching')
-          },
-          async () => {
-            await start()
-            {
-              const browser = await next.browser('/')
-              expect(await browser.elementByCss('p').text()).toBe(
-                'hello persistent caching'
-              )
-              await browser.close()
-            }
-            {
-              const browser = await next.browser('/pages')
-              expect(await browser.elementByCss('p').text()).toBe(
-                'hello persistent caching'
+      await checkInitial()
+
+      await stop()
+
+      async function checkChanges() {
+        {
+          const browser = await next.browser('/')
+          expect(await browser.elementByCss('p').text()).toBe(
+            'hello persistent caching'
+          )
+          await browser.close()
+        }
+        {
+          const browser = await next.browser('/client')
+          expect(await browser.elementByCss('p').text()).toBe(
+            'hello persistent caching'
+          )
+          await browser.close()
+        }
+        {
+          const browser = await next.browser('/pages')
+          expect(await browser.elementByCss('p').text()).toBe(
+            'hello persistent caching'
+          )
+          await browser.close()
+        }
+        {
+          const browser = await next.browser('/add-me')
+          expect(await browser.elementByCss('p').text()).toBe('hello world')
+          await browser.close()
+        }
+      }
+
+      await next.patchFile(
+        'pages/pages.tsx',
+        (content) => {
+          return content.replace('hello world', 'hello persistent caching')
+        },
+        async () => {
+          await next.patchFile(
+            'app/page.tsx',
+            (content) => {
+              return content.replace('hello world', 'hello persistent caching')
+            },
+            async () => {
+              await next.patchFile(
+                'app/client/page.tsx',
+                (content) => {
+                  return content.replace(
+                    'hello world',
+                    'hello persistent caching'
+                  )
+                },
+                async () => {
+                  await next.renameFolder('app/remove-me', 'app/add-me')
+                  try {
+                    await start()
+                    await checkChanges()
+                    // Some no-op change builds
+                    for (let i = 0; i < 2; i++) {
+                      await restartCycle()
+                      await checkChanges()
+                    }
+                    await stop()
+                  } finally {
+                    await next.renameFolder('app/add-me', 'app/remove-me')
+                  }
+                }
               )
-              await browser.close()
             }
-            await stop()
-          }
-        )
-      }
-    )
-    await start()
-  })
+          )
+        }
+      )
+      await start()
+    }
+  )
 })