Some checks failed
Test examples / Test Examples (20) (push) Has been cancelled
Test examples / Test Examples (22) (push) Has been cancelled
Lock Threads / action (push) Has been cancelled
Trigger Release / start (push) Has been cancelled
Stale issue handler / stale (push) Has been cancelled
Update Font Data / create-pull-request (push) Has been cancelled
build-and-deploy / deploy-target (push) Has been cancelled
build-and-deploy / build (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-musl - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-unknown-linux-gnu - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-pc-windows-msvc - node@16 (push) Has been cancelled
build-and-deploy / stable - aarch64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / stable - x86_64-apple-darwin - node@16 (push) Has been cancelled
build-and-deploy / build-wasm (nodejs) (push) Has been cancelled
build-and-deploy / build-wasm (web) (push) Has been cancelled
build-and-deploy / Deploy preview tarball (push) Has been cancelled
build-and-deploy / Potentially publish release (push) Has been cancelled
build-and-deploy / publish-turbopack-npm-packages (push) Has been cancelled
build-and-deploy / Deploy examples (push) Has been cancelled
build-and-deploy / thank you, build (push) Has been cancelled
build-and-deploy / Upload Turbopack Bytesize metrics to Datadog (push) Has been cancelled
Rspack Next.js development integration tests / Rspack integration tests (push) Has been cancelled
Rspack Next.js production integration tests / Rspack integration tests (push) Has been cancelled
Turbopack Next.js development integration tests / Next.js integration tests (push) Has been cancelled
Turbopack Next.js production integration tests / Next.js integration tests (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack development test manifest (push) Has been cancelled
Update Rspack test manifest / Update and upload Rspack production test manifest (push) Has been cancelled
Upload bundler test manifests to areweturboyet.com / Upload test results (push) Has been cancelled
Update React / create-pull-request (push) Has been cancelled
test-e2e-project-reset-cron / reset-test-project (push) Has been cancelled
Notify about the top 15 issues/PRs/feature requests (most reacted) in the last 90 days / run (push) Has been cancelled
202 lines
6.6 KiB
TypeScript
202 lines
6.6 KiB
TypeScript
import { nextTestSetup } from 'e2e-utils'
|
|
import { retry, createMultiDomMatcher } from 'next-test-utils'
|
|
|
|
describe('app-dir - metadata-streaming', () => {
|
|
const { next } = nextTestSetup({
|
|
files: __dirname,
|
|
})
|
|
|
|
it('should delay the metadata render to body', async () => {
|
|
const $ = await next.render$('/')
|
|
expect($('head title').length).toBe(0)
|
|
expect($('body title').length).toBe(1)
|
|
})
|
|
|
|
it('should still load viewport meta tags even if metadata is delayed', async () => {
|
|
const $ = await next.render$('/slow')
|
|
|
|
expect($('meta[name="viewport"]').attr('content')).toBe(
|
|
'width=device-width, initial-scale=1'
|
|
)
|
|
expect($('meta[charset]').attr('charset')).toBe('utf-8')
|
|
})
|
|
|
|
it('should render the metadata in the browser', async () => {
|
|
const browser = await next.browser('/')
|
|
await retry(async () => {
|
|
expect(await browser.elementByCss('title').text()).toBe('index page')
|
|
})
|
|
})
|
|
|
|
it('should load the initial html without slow metadata during navigation', async () => {
|
|
// navigate from / to /slow, the metadata should be empty first, e.g. no title.
|
|
// then the metadata should be loaded after few seconds.
|
|
const browser = await next.browser('/')
|
|
await browser.elementByCss('#to-slow').click()
|
|
|
|
await retry(async () => {
|
|
expect(await browser.elementByCss('title').text()).toBe('slow page')
|
|
const matchMultiDom = createMultiDomMatcher(browser)
|
|
|
|
await matchMultiDom('meta', 'name', 'content', {
|
|
description: 'slow page description',
|
|
generator: 'next.js',
|
|
'application-name': 'test',
|
|
referrer: 'origin-when-cross-origin',
|
|
keywords: 'next.js,react,javascript',
|
|
author: ['huozhi'],
|
|
viewport: 'width=device-width, initial-scale=1',
|
|
creator: 'huozhi',
|
|
publisher: 'vercel',
|
|
robots: 'index, follow',
|
|
})
|
|
})
|
|
})
|
|
|
|
it('should send the blocking response for html limited bots', async () => {
|
|
const $ = await next.render$(
|
|
'/',
|
|
undefined, // no query
|
|
{
|
|
headers: {
|
|
'user-agent': 'Twitterbot',
|
|
},
|
|
}
|
|
)
|
|
expect(await $('title').text()).toBe('index page')
|
|
})
|
|
|
|
it('should send streaming response for headless browser bots', async () => {
|
|
const browser = await next.browser('/')
|
|
await retry(async () => {
|
|
expect(await browser.elementByCss('title').text()).toBe('index page')
|
|
})
|
|
})
|
|
|
|
it('should only insert metadata once into head or body', async () => {
|
|
const browser = await next.browser('/slow')
|
|
|
|
// each metadata should be inserted only once
|
|
|
|
expect(await browser.hasElementByCssSelector('head title')).toBe(false)
|
|
|
|
// only charset and viewport are rendered in head
|
|
expect((await browser.elementsByCss('head meta')).length).toBe(2)
|
|
expect((await browser.elementsByCss('body title')).length).toBe(1)
|
|
|
|
// all metadata should be rendered in body
|
|
expect((await browser.elementsByCss('body meta')).length).toBe(9)
|
|
})
|
|
|
|
describe('dynamic api', () => {
|
|
it('should render metadata to body', async () => {
|
|
const $ = await next.render$('/dynamic-api')
|
|
expect($('head title').length).toBe(0)
|
|
expect($('body title').length).toBe(1)
|
|
})
|
|
|
|
it('should load the metadata in browser', async () => {
|
|
const browser = await next.browser('/dynamic-api')
|
|
await retry(async () => {
|
|
expect(
|
|
await browser.elementByCss('body title', { state: 'attached' }).text()
|
|
).toMatch(/Dynamic api \d+/)
|
|
})
|
|
})
|
|
})
|
|
|
|
describe('navigation API', () => {
|
|
it('should trigger not-found boundary when call notFound', async () => {
|
|
const browser = await next.browser('/notfound')
|
|
|
|
// Show 404 page
|
|
await retry(async () => {
|
|
expect(await browser.elementByCss('h1').text()).toBe('404')
|
|
})
|
|
})
|
|
|
|
it('should trigger redirection when call redirect', async () => {
|
|
const browser = await next.browser('/redirect')
|
|
// Redirect to home page
|
|
expect(await browser.elementByCss('p').text()).toBe('index page')
|
|
})
|
|
|
|
it('should trigger custom not-found in the boundary', async () => {
|
|
const browser = await next.browser('/notfound/boundary')
|
|
|
|
expect(await browser.elementByCss('h1').text()).toBe('Custom Not Found')
|
|
})
|
|
|
|
it('should not duplicate metadata with navigation API', async () => {
|
|
const browser = await next.browser('/notfound/boundary')
|
|
|
|
const titleTags = await browser.elementsByCss('title')
|
|
expect(titleTags.length).toBe(1)
|
|
})
|
|
|
|
it('should render blocking 404 response status when html limited bots access notFound', async () => {
|
|
const { status } = await next.fetch('/notfound', {
|
|
headers: {
|
|
'user-agent': 'Twitterbot',
|
|
},
|
|
})
|
|
expect(status).toBe(404)
|
|
})
|
|
|
|
it('should render blocking 307 response status when html limited bots access redirect', async () => {
|
|
const { status } = await next.fetch('/redirect', {
|
|
headers: {
|
|
'user-agent': 'Twitterbot',
|
|
},
|
|
redirect: 'manual',
|
|
})
|
|
expect(status).toBe(307)
|
|
})
|
|
})
|
|
|
|
describe('static', () => {
|
|
it('should render static metadata in the head', async () => {
|
|
const $ = await next.render$('/static/full')
|
|
// We can't ensure if it's inserted into head or body since it's a race condition,
|
|
// where sometimes the metadata can be suspended.
|
|
expect($('title').length).toBe(1)
|
|
expect($('title').text()).toBe('static page')
|
|
})
|
|
|
|
it('should determine dynamic metadata in build and render in the body', async () => {
|
|
const $ = await next.render$('/static/partial')
|
|
expect($('title').length).toBe(1)
|
|
expect($('body title').text()).toBe('partial static page')
|
|
})
|
|
|
|
it('should still render dynamic metadata in the head for html bots', async () => {
|
|
const $ = await next.render$(
|
|
'/static/partial',
|
|
{},
|
|
{
|
|
headers: {
|
|
'user-agent': 'Twitterbot',
|
|
},
|
|
}
|
|
)
|
|
expect($('title').length).toBe(1)
|
|
expect($('head title').text()).toBe('partial static page')
|
|
})
|
|
|
|
it('should still render blocking metadata for Google speed insights bot (special case)', async () => {
|
|
const $ = await next.render$(
|
|
'/static/partial',
|
|
{},
|
|
{
|
|
headers: {
|
|
'user-agent':
|
|
'UA Mozilla/5.0 (Linux; Android 7.0; Moto G (4)) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4590.2 Mobile Safari/537.36 Chrome-Lighthouse',
|
|
},
|
|
}
|
|
)
|
|
expect($('title').length).toBe(1)
|
|
expect($('head title').text()).toBe('partial static page')
|
|
})
|
|
})
|
|
})
|