Repository: vercel/next.js
Pull request: [test] Avoid flaky assertions due to dynamic metadata

Dynamic metadata streams in async and can therefore commit separately from the page. We need to wait for it to stream in instead of asserting on it immediately once the page committed.

Should fix the top 3 flaky tests in `test/e2e/app-dir/metadata/metadata.test.ts`:
![CleanShot 2025-07-07 at 16 02 24@2x](https://github.com/user-attachments/assets/aadf7579-c0cb-495e-a9cd-ae853322fb99)

-- https://app.datadoghq.com/ci/test/runs?query=test_level%3Atest%20%40test.source.file%3A%22test%2Fe2e%2Fapp-dir%2Fmetadata%2Fmetadata.test.ts%22%20%40test.status%3Afail&agg_m=count&agg_m_source=base&agg_q=%40test.name&agg_q_source=base&agg_t=count&citest_explorer_sort=timestamp%2Cdesc&cols=timestamp%2C%40test.status%2C%40test.name%3A210&currentTab=overview&eventStack=&fromUser=true&index=citest&top_n=10&top_o=top&viz=toplist&x_missing=true&start=1751292057792&end=1751896857792&paused=true

Changed files:
filename: test/e2e/app-dir/metadata/app/cache-deduping/page.tsx
patch: @@ -1,6 +1,6 @@
 import React, { cache } from 'react'
 
-const getRandomMemoized = cache(() => Math.random())
+const getRandomMemoized = cache(() => Math.random().toString())
 
 async function getRandomMemoizedByFetch() {
   const res = await fetch(
@@ -26,7 +26,7 @@ export async function generateMetadata(props, parent) {
 
   return {
     title: {
-      default: JSON.stringify({ val, val2 }),
+      default: JSON.stringify({ page: 'cache-deduping', val, val2 }),
     },
   }
 }

filename: test/e2e/app-dir/metadata/metadata.test.ts
patch: @@ -7,6 +7,7 @@ import {
   createMultiDomMatcher,
   checkMetaNameContentPair,
   checkLink,
+  retry,
 } from 'next-test-utils'
 import fs from 'fs/promises'
 import path from 'path'
@@ -227,16 +228,17 @@ describe('app dir - metadata', () => {
       const browser = await next.browser('/')
       await browser.waitForElementByCss('p#index')
       await browser.eval(`next.router.push('/alternates')`)
-      // wait for /alternates page is loaded
-      await browser.waitForElementByCss('p#alternates')
 
       const matchDom = createDomMatcher(browser)
-      await matchDom('link', 'rel="canonical"', {
-        href: 'https://example.com/alternates',
-      })
-      await matchDom('link', 'title="js title"', {
-        type: 'application/rss+xml',
-        href: 'https://example.com/blog/js.rss',
+      // Dynamic metadata streams in async
+      await retry(async () => {
+        await matchDom('link', 'rel="canonical"', {
+          href: 'https://example.com/alternates',
+        })
+        await matchDom('link', 'title="js title"', {
+          type: 'application/rss+xml',
+          href: 'https://example.com/blog/js.rss',
+        })
       })
     })
 
@@ -283,18 +285,24 @@ describe('app dir - metadata', () => {
         .click()
         .waitForElementByCss('#basic')
 
-      await checkMetaNameContentPair(
-        browser,
-        'referrer',
-        'origin-when-cross-origin'
-      )
+      await retry(async () => {
+        await checkMetaNameContentPair(
+          browser,
+          'referrer',
+          'origin-when-cross-origin'
+        )
+      })
+
       await browser.back().waitForElementByCss('#index')
       expect(await getTitle(browser)).toBe('index page')
       await browser
         .elementByCss('#to-title')
         .click()
         .waitForElementByCss('#title')
-      expect(await getTitle(browser)).toBe('this is the page title')
+
+      await retry(async () => {
+        expect(await getTitle(browser)).toBe('this is the page title')
+      })
     })
 
     it('should support generateMetadata dynamic props', async () => {
@@ -809,6 +817,12 @@ describe('app dir - metadata', () => {
         .waitForElementByCss('#value')
       const value = await browser.elementByCss('#value').text()
       const value2 = await browser.elementByCss('#value2').text()
+      // Dynamic metadata streams in async
+      await retry(async () => {
+        expect(await browser.eval(`document.title`)).toContain(
+          '"page":"cache-deduping"'
+        )
+      })
       // Value in the title should match what's shown on the page component
       const title = await browser.eval(`document.title`)
       const obj = JSON.parse(title)