| | |
| |
|
| | import cheerio from 'cheerio' |
| | import { nextTestSetup } from 'e2e-utils' |
| | import { renderViaHTTP } from 'next-test-utils' |
| | import path from 'path' |
| |
|
| | const isReact18 = parseInt(process.env.NEXT_TEST_REACT_VERSION) === 18 |
| |
|
| | describe('Client Navigation rendering <Head />', () => { |
| | const { next } = nextTestSetup({ |
| | files: path.join(__dirname, 'fixture'), |
| | }) |
| |
|
| | function render( |
| | pathname: Parameters<typeof renderViaHTTP>[1], |
| | query?: Parameters<typeof renderViaHTTP>[2] |
| | ) { |
| | return renderViaHTTP(next.appPort, pathname, query) |
| | } |
| |
|
| | it('should handle undefined prop in head server-side', async () => { |
| | const html = await render('/head') |
| | const $ = cheerio.load(html) |
| | const value = 'content' in $('meta[name="empty-content"]').attr() |
| |
|
| | expect(value).toBe(false) |
| | }) |
| |
|
| | |
| | test('header renders default charset', async () => { |
| | const html = await render('/default-head') |
| | expect(html).toContain('<meta charSet="utf-8" data-next-head=""/>') |
| | expect(html).toContain('next-head, but only once.') |
| | }) |
| |
|
| | test('header renders default viewport', async () => { |
| | const html = await render('/default-head') |
| | expect(html).toContain( |
| | '<meta name="viewport" content="width=device-width" data-next-head=""/>' |
| | ) |
| | }) |
| |
|
| | test('header helper renders header information', async () => { |
| | const html = await render('/head') |
| | expect(html).toContain('<meta charSet="iso-8859-5" data-next-head=""/>') |
| | expect(html).toContain('<meta content="my meta" data-next-head=""/>') |
| | expect(html).toContain( |
| | '<meta name="viewport" content="width=device-width,initial-scale=1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain('I can have meta tags') |
| | }) |
| |
|
| | test('header helper dedupes tags', async () => { |
| | const html = await render('/head') |
| | expect(html).toContain('<meta charSet="iso-8859-5" data-next-head=""/>') |
| | expect(html).not.toContain('<meta charSet="utf-8" data-next-head=""/>') |
| | expect(html).toContain( |
| | '<meta name="viewport" content="width=device-width,initial-scale=1" data-next-head=""/>' |
| | ) |
| | |
| | expect(html.match(/<meta name="viewport" /g).length).toBe(1) |
| | expect(html).not.toContain( |
| | '<meta name="viewport" content="width=device-width"' |
| | ) |
| | expect(html).toContain('<meta content="my meta" data-next-head=""/>') |
| | expect(html).toContain( |
| | '<link rel="stylesheet" href="/dup-style.css" data-next-head=""/><link rel="stylesheet" href="/dup-style.css" data-next-head=""/>' |
| | ) |
| | const dedupeLink = |
| | '<link rel="stylesheet" href="dedupe-style.css" data-next-head=""/>' |
| | expect(html).toContain(dedupeLink) |
| | expect( |
| | html.substring(html.indexOf(dedupeLink) + dedupeLink.length) |
| | ).not.toContain('<link rel="stylesheet" href="dedupe-style.css"') |
| | expect(html).toContain( |
| | '<link rel="alternate" hrefLang="en" href="/last/en" data-next-head=""/>' |
| | ) |
| | expect(html).not.toContain( |
| | '<link rel="alternate" hrefLang="en" href="/first/en"' |
| | ) |
| | }) |
| |
|
| | test('header helper dedupes tags with the same key as the default', async () => { |
| | const html = await render('/head-duplicate-default-keys') |
| | |
| | expect((html.match(/charSet=/g) || []).length).toBe(1) |
| | |
| | expect((html.match(/name="viewport"/g) || []).length).toBe(1) |
| | expect(html).toContain('<meta charSet="iso-8859-1" data-next-head=""/>') |
| | expect(html).toContain( |
| | '<meta name="viewport" content="width=500" data-next-head=""/>' |
| | ) |
| | }) |
| |
|
| | test('header helper avoids dedupe of specific tags', async () => { |
| | const html = await render('/head') |
| | console.log(html) |
| | expect(html).toContain( |
| | '<meta property="article:tag" content="tag1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="article:tag" content="tag2" data-next-head=""/>' |
| | ) |
| | expect(html).not.toContain('<meta property="dedupe:tag" content="tag3"') |
| | expect(html).toContain( |
| | '<meta property="dedupe:tag" content="tag4" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image" content="ogImageTag1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image" content="ogImageTag2" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:alt" content="ogImageAltTag1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:alt" content="ogImageAltTag2" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:width" content="ogImageWidthTag1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:width" content="ogImageWidthTag2" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:height" content="ogImageHeightTag1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:height" content="ogImageHeightTag2" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:type" content="ogImageTypeTag1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:type" content="ogImageTypeTag2" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:secure_url" content="ogImageSecureUrlTag1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:secure_url" content="ogImageSecureUrlTag2" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:url" content="ogImageUrlTag1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="og:image:url" content="ogImageUrlTag2" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="fb:pages" content="fbpages1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta property="fb:pages" content="fbpages2" data-next-head=""/>' |
| | ) |
| | }) |
| |
|
| | test('header helper avoids dedupe of meta tags with the same name if they use unique keys', async () => { |
| | const html = await render('/head') |
| | expect(html).toContain( |
| | '<meta name="citation_author" content="authorName1" data-next-head=""/>' |
| | ) |
| | expect(html).toContain( |
| | '<meta name="citation_author" content="authorName2" data-next-head=""/>' |
| | ) |
| | }) |
| |
|
| | test('header helper renders Fragment children', async () => { |
| | const html = await render('/head') |
| | expect(html).toContain('<title data-next-head="">Fragment title</title>') |
| | expect(html).toContain('<meta content="meta fragment" data-next-head=""/>') |
| | }) |
| |
|
| | test('header helper renders boolean attributes correctly children', async () => { |
| | const html = await render('/head') |
| | expect(html).toContain( |
| | '<script src="/test-async-false.js" data-next-head="">' |
| | ) |
| | expect(html).toContain( |
| | '<script src="/test-async-true.js" async="" data-next-head="">' |
| | ) |
| | expect(html).toContain( |
| | '<script src="/test-defer.js" defer="" data-next-head="">' |
| | ) |
| | }) |
| |
|
| | it('should place charset element at the top of <head>', async () => { |
| | const html = await render('/head-priority') |
| | const nextHeadElement = |
| | '<meta charSet="iso-8859-5" data-next-head=""/><meta name="viewport" content="width=device-width,initial-scale=1" data-next-head=""/><meta name="title" content="head title" data-next-head=""/>' |
| | const documentHeadElement = |
| | '<meta name="keywords" content="document head test"/>' |
| |
|
| | expect(html).toContain( |
| | isReact18 |
| | ? |
| | |
| | `</style></noscript>${nextHeadElement}${documentHeadElement}` |
| | : `<head>${nextHeadElement}${documentHeadElement}` |
| | ) |
| | }) |
| |
|
| | test('custom meta properties are rendered only once', async () => { |
| | const browser = await next.browser('/head-with-custom-metadata') |
| |
|
| | |
| | const titleElements = await browser.elementsByCss('title') |
| | expect(titleElements).toHaveLength(1) |
| | const titleText = await browser.elementByCss('title').text() |
| | expect(titleText).toBe('Title Page') |
| |
|
| | |
| | const ogTitleElements = await browser.elementsByCss( |
| | 'meta[property="og:title"]' |
| | ) |
| | expect(ogTitleElements).toHaveLength(1) |
| | const ogTitleContent = await browser |
| | .elementByCss('meta[property="og:title"]') |
| | .getAttribute('content') |
| | expect(ogTitleContent).toBe('Title Content') |
| |
|
| | const descriptionElements = await browser.elementsByCss( |
| | 'meta[name="description"]' |
| | ) |
| | expect(descriptionElements).toHaveLength(1) |
| | const descriptionContent = await browser |
| | .elementByCss('meta[name="description"]') |
| | .getAttribute('content') |
| | expect(descriptionContent).toBe('Description Content') |
| | }) |
| | }) |
| |
|