next.js/test/e2e/app-dir/metadata-navigation/metadata-navigation.test.ts
metadata-navigation.test.ts141 lines4.9 KB
import { nextTestSetup } from 'e2e-utils'
import {
  createMultiDomMatcher,
  createMultiHtmlMatcher,
  getTitle,
  retry,
} from 'next-test-utils'

describe('app dir - metadata navigation', () => {
  const { next } = nextTestSetup({
    files: __dirname,
  })

  describe('navigation', () => {
    it('should render root not-found with default metadata', async () => {
      const $ = await next.render$('/does-not-exist')

      // Should contain default metadata and noindex tag
      const matchHtml = createMultiHtmlMatcher($)
      expect($('meta[charset="utf-8"]').length).toBe(1)
      matchHtml('meta', 'name', 'content', {
        viewport: 'width=device-width, initial-scale=1',
        robots: 'noindex',
        // not found metadata
        description: 'Root not found description',
      })
      expect(await $('title').text()).toBe('Root not found')
    })

    it('should support notFound in generateMetadata', async () => {
      const res = await next.fetch('/async/not-found')
      // metadata is suspended in SSR, it won't affect the response status
      expect(res.status).toBe(200)
      const $ = await next.render$('/async/not-found')

      // TODO-APP: support render custom not-found in SSR for generateMetadata.
      // Check contains root not-found payload in flight response for now.
      const flightDataPrefix = 'self.__next_f.push([1,"'
      const flightDataSuffix = '"])'
      let flightText = ''
      for (const el of $('script').toArray()) {
        const text = $(el).text()
        if (text.startsWith(flightDataPrefix)) {
          flightText += text.slice(
            flightDataPrefix.length,
            -flightDataSuffix.length
          )
        }
      }
      expect(flightText).toContain('Local found boundary')

      // Should contain default metadata and noindex tag
      const matchHtml = createMultiHtmlMatcher($)
      expect($('meta[charset="utf-8"]').length).toBe(1)
      matchHtml('meta', 'name', 'content', {
        viewport: 'width=device-width, initial-scale=1',
        robots: 'noindex',
      })

      const browser = await next.browser('/async/not-found')
      expect(await browser.elementByCss('h2').text()).toBe(
        'Local found boundary'
      )

      const matchMultiDom = createMultiDomMatcher(browser)
      await matchMultiDom('meta', 'name', 'content', {
        viewport: 'width=device-width, initial-scale=1',
        keywords: 'parent',
        robots: 'noindex',
        // not found metadata
        description: 'Local not found description',
      })
      expect(await getTitle(browser)).toBe('Local not found')
    })

    it('should support redirect in generateMetadata', async () => {
      const res = await next.fetch('/async/redirect', {
        redirect: 'manual',
      })
      // metadata is suspended in SSR, it won't affect the response status
      expect(res.status).toBe(200)
      const browser = await next.browser('/async/redirect')
      await retry(async () => {
        expect(await browser.elementByCss('p').text()).toBe(
          'redirect dest page'
        )
      })
    })

    it('should show the index title', async () => {
      const browser = await next.browser('/parallel-route')
      expect(await getTitle(browser)).toBe('Home Layout')
    })

    it('should show target page metadata after navigation', async () => {
      const browser = await next.browser('/parallel-route')
      await browser.elementByCss('#product-link').click()
      await browser.waitForElementByCss('#product-title')
      expect(await getTitle(browser)).toBe('Product Layout')
    })

    it('should show target page metadata after navigation with back', async () => {
      const browser = await next.browser('/parallel-route')
      await browser.elementByCss('#product-link').click()
      await browser.waitForElementByCss('#product-title')
      await browser.elementByCss('#home-link').click()
      await browser.waitForElementByCss('#home-title')
      expect(await getTitle(browser)).toBe('Home Layout')
    })
  })

  describe('server action', () => {
    it('should not render fallback noindex metadata if request is initiated from server action', async () => {
      const browser = await next.browser('/server-action/not-found')
      // collect server action requests
      let isActionSent = false
      browser.on('request', (req) => {
        if (
          req.method() === 'POST' &&
          req.url().endsWith('/server-action/not-found')
        ) {
          isActionSent = true
        }
      })

      // trigger not-found action and wait until the server action is performed
      await browser.elementByCss('#trigger-not-found').click()
      await retry(async () => {
        expect(isActionSent).toBe(true)
      })

      expect(await browser.elementsByCss('meta[name="robots"]')).toHaveLength(1)
      expect(
        await browser
          .elementByCss('meta[name="robots"]')
          .getAttribute('content')
      ).toBe('noindex, nofollow')
    })
  })
})
Quest for Codev2.0.0
/
SIGN IN