test: break down metadata test suite into smaller ones (#67018)
### What app-dir `metadata.test.ts` is pretty big and includes a lot of erroring tests and navigation tests. Breaking them into smaller suites to avoid the erroring on effect on others. - metadata - metadata-navigation - metadata-thrown Moved the metadata testing utils into `next-test-utils` for sharing purpose. Moved the hmr test to the bottom to avoid flakyness.
This commit is contained in:
parent
9ff5c44989
commit
54f54423d8
15 changed files with 317 additions and 246 deletions
14
test/e2e/app-dir/metadata-navigation/app/layout.tsx
Normal file
14
test/e2e/app-dir/metadata-navigation/app/layout.tsx
Normal file
|
@ -0,0 +1,14 @@
|
|||
export default function Layout({ children }) {
|
||||
return (
|
||||
<html>
|
||||
<head></head>
|
||||
<body>{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
|
||||
export const metadata = {
|
||||
title: 'this is the layout title',
|
||||
description: 'this is the layout description',
|
||||
keywords: ['nextjs', 'react'],
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
import { nextTestSetup } from 'e2e-utils'
|
||||
import {
|
||||
createMultiDomMatcher,
|
||||
createMultiHtmlMatcher,
|
||||
getTitle,
|
||||
} 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')
|
||||
expect(res.status).toBe(404)
|
||||
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.
|
||||
let hasRootNotFoundFlight = false
|
||||
for (const el of $('script').toArray()) {
|
||||
const text = $(el).text()
|
||||
if (text.includes('Local found boundary')) {
|
||||
hasRootNotFoundFlight = true
|
||||
}
|
||||
}
|
||||
expect(hasRootNotFoundFlight).toBe(true)
|
||||
|
||||
// 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',
|
||||
})
|
||||
expect(res.status).toBe(307)
|
||||
})
|
||||
})
|
||||
})
|
12
test/e2e/app-dir/metadata-thrown/metadata-thrown.test.ts
Normal file
12
test/e2e/app-dir/metadata-thrown/metadata-thrown.test.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
import { nextTestSetup } from 'e2e-utils'
|
||||
|
||||
describe('app dir - metadata thrown', () => {
|
||||
const { next } = nextTestSetup({
|
||||
files: __dirname,
|
||||
})
|
||||
|
||||
it('should not crash from error thrown during preloading nested generateMetadata', async () => {
|
||||
const res = await next.fetch('/dynamic-meta')
|
||||
expect(res.status).toBe(404)
|
||||
})
|
||||
})
|
19
test/e2e/app-dir/metadata/app/dynamic/[slug]/page.tsx
Normal file
19
test/e2e/app-dir/metadata/app/dynamic/[slug]/page.tsx
Normal file
|
@ -0,0 +1,19 @@
|
|||
function format({ params, searchParams }) {
|
||||
const { slug } = params
|
||||
const { q } = searchParams
|
||||
return `params - ${slug}${q ? ` query - ${q}` : ''}`
|
||||
}
|
||||
|
||||
export default function page(props) {
|
||||
return <p>{format(props)}</p>
|
||||
}
|
||||
|
||||
export async function generateMetadata(props, parent) {
|
||||
const parentMetadata = await parent
|
||||
/* mutating */
|
||||
return {
|
||||
...parentMetadata,
|
||||
title: format(props),
|
||||
keywords: parentMetadata.keywords.concat(['child']),
|
||||
}
|
||||
}
|
11
test/e2e/app-dir/metadata/app/dynamic/layout.tsx
Normal file
11
test/e2e/app-dir/metadata/app/dynamic/layout.tsx
Normal file
|
@ -0,0 +1,11 @@
|
|||
export default function layout({ children }) {
|
||||
return children
|
||||
}
|
||||
|
||||
export async function generateMetadata() {
|
||||
return {
|
||||
keywords: 'parent',
|
||||
}
|
||||
}
|
||||
|
||||
export const revalidate = 0
|
|
@ -1,162 +1,21 @@
|
|||
import type { BrowserInterface } from 'next-webdriver'
|
||||
import { nextTestSetup } from 'e2e-utils'
|
||||
import { check } from 'next-test-utils'
|
||||
import {
|
||||
check,
|
||||
getTitle,
|
||||
createDomMatcher,
|
||||
createMultiHtmlMatcher,
|
||||
createMultiDomMatcher,
|
||||
checkMetaNameContentPair,
|
||||
checkLink,
|
||||
} from 'next-test-utils'
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
import cheerio from 'cheerio'
|
||||
|
||||
describe('app dir - metadata', () => {
|
||||
const { next, isNextDev, isNextStart, isNextDeploy } = nextTestSetup({
|
||||
files: __dirname,
|
||||
})
|
||||
|
||||
const getTitle = (browser: BrowserInterface) =>
|
||||
browser.elementByCss('title').text()
|
||||
|
||||
async function checkMeta(
|
||||
browser: BrowserInterface,
|
||||
queryValue: string,
|
||||
expected: RegExp | string | string[] | undefined | null,
|
||||
queryKey: string = 'property',
|
||||
tag: string = 'meta',
|
||||
domAttributeField: string = 'content'
|
||||
) {
|
||||
const values = await browser.eval(
|
||||
`[...document.querySelectorAll('${tag}[${queryKey}="${queryValue}"]')].map((el) => el.getAttribute("${domAttributeField}"))`
|
||||
)
|
||||
if (expected instanceof RegExp) {
|
||||
expect(values[0]).toMatch(expected)
|
||||
} else {
|
||||
if (Array.isArray(expected)) {
|
||||
expect(values).toEqual(expected)
|
||||
} else {
|
||||
// If expected is undefined, then it should not exist.
|
||||
// Otherwise, it should exist in the matched values.
|
||||
if (expected === undefined) {
|
||||
expect(values).not.toContain(undefined)
|
||||
} else {
|
||||
expect(values).toContain(expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createDomMatcher(browser: BrowserInterface) {
|
||||
/**
|
||||
* @param tag - tag name, e.g. 'meta'
|
||||
* @param query - query string, e.g. 'name="description"'
|
||||
* @param expectedObject - expected object, e.g. { content: 'my description' }
|
||||
* @returns {Promise<void>} - promise that resolves when the check is done
|
||||
*
|
||||
* @example
|
||||
* const matchDom = createDomMatcher(browser)
|
||||
* await matchDom('meta', 'name="description"', { content: 'description' })
|
||||
*/
|
||||
return async (
|
||||
tag: string,
|
||||
query: string,
|
||||
expectedObject: Record<string, string | null | undefined>
|
||||
) => {
|
||||
const props = await browser.eval(`
|
||||
const el = document.querySelector('${tag}[${query}]');
|
||||
const res = {}
|
||||
const keys = ${JSON.stringify(Object.keys(expectedObject))}
|
||||
for (const k of keys) {
|
||||
res[k] = el?.getAttribute(k)
|
||||
}
|
||||
res
|
||||
`)
|
||||
expect(props).toEqual(expectedObject)
|
||||
}
|
||||
}
|
||||
|
||||
function createMultiHtmlMatcher($: ReturnType<typeof cheerio.load>) {
|
||||
/**
|
||||
* @param tag - tag name, e.g. 'meta'
|
||||
* @param queryKey - query key, e.g. 'property'
|
||||
* @param domAttributeField - dom attribute field, e.g. 'content'
|
||||
* @param expected - expected object, e.g. { description: 'my description' }
|
||||
* @returns {void} - void when the check is done
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* const $ = await next.render$('html')
|
||||
* const matchHtml = createMultiHtmlMatcher($)
|
||||
* matchHtml('meta', 'name', 'property', {
|
||||
* description: 'description',
|
||||
* og: 'og:description'
|
||||
* })
|
||||
*
|
||||
*/
|
||||
return (
|
||||
tag: string,
|
||||
queryKey: string,
|
||||
domAttributeField: string,
|
||||
expected: Record<string, string | string[] | undefined>
|
||||
) => {
|
||||
const res = {}
|
||||
for (const key of Object.keys(expected)) {
|
||||
const el = $(`${tag}[${queryKey}="${key}"]`)
|
||||
if (el.length > 1) {
|
||||
res[key] = el.toArray().map((el) => el.attribs[domAttributeField])
|
||||
} else {
|
||||
res[key] = el.attr(domAttributeField)
|
||||
}
|
||||
}
|
||||
expect(res).toEqual(expected)
|
||||
}
|
||||
}
|
||||
|
||||
function createMultiDomMatcher(browser: BrowserInterface) {
|
||||
/**
|
||||
* @param tag - tag name, e.g. 'meta'
|
||||
* @param queryKey - query key, e.g. 'property'
|
||||
* @param domAttributeField - dom attribute field, e.g. 'content'
|
||||
* @param expected - expected object, e.g. { description: 'my description' }
|
||||
* @returns {Promise<void>} - promise that resolves when the check is done
|
||||
*
|
||||
* @example
|
||||
* const matchMultiDom = createMultiDomMatcher(browser)
|
||||
* await matchMultiDom('meta', 'property', 'content', {
|
||||
* description: 'description',
|
||||
* 'og:title': 'title',
|
||||
* 'twitter:title': 'title'
|
||||
* })
|
||||
*
|
||||
*/
|
||||
return async (
|
||||
tag: string,
|
||||
queryKey: string,
|
||||
domAttributeField: string,
|
||||
expected: Record<string, string | string[] | undefined | null>
|
||||
) => {
|
||||
await Promise.all(
|
||||
Object.keys(expected).map(async (key) => {
|
||||
return checkMeta(
|
||||
browser,
|
||||
key,
|
||||
expected[key],
|
||||
queryKey,
|
||||
tag,
|
||||
domAttributeField
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const checkMetaNameContentPair = (
|
||||
browser: BrowserInterface,
|
||||
name: string,
|
||||
content: string | string[]
|
||||
) => checkMeta(browser, name, content, 'name')
|
||||
|
||||
const checkLink = (
|
||||
browser: BrowserInterface,
|
||||
rel: string,
|
||||
content: string | string[]
|
||||
) => checkMeta(browser, rel, content, 'rel', 'link', 'href')
|
||||
|
||||
describe('basic', () => {
|
||||
it('should support title and description', async () => {
|
||||
const browser = await next.browser('/title')
|
||||
|
@ -423,13 +282,13 @@ describe('app dir - metadata', () => {
|
|||
expect(await getTitle(browser)).toBe('this is the page title')
|
||||
})
|
||||
|
||||
it('should support generateMetadata export', async () => {
|
||||
const browser = await next.browser('/async/slug')
|
||||
it('should support generateMetadata dynamic props', async () => {
|
||||
const browser = await next.browser('/dynamic/slug')
|
||||
expect(await getTitle(browser)).toBe('params - slug')
|
||||
|
||||
await checkMetaNameContentPair(browser, 'keywords', 'parent,child')
|
||||
|
||||
await browser.loadPage(next.url + '/async/blog?q=xxx')
|
||||
await browser.loadPage(next.url + '/dynamic/blog?q=xxx')
|
||||
await check(
|
||||
() => browser.elementByCss('p').text(),
|
||||
/params - blog query - xxx/
|
||||
|
@ -566,71 +425,6 @@ describe('app dir - metadata', () => {
|
|||
})
|
||||
})
|
||||
|
||||
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')
|
||||
expect(res.status).toBe(404)
|
||||
const html = await res.text()
|
||||
const $ = cheerio.load(html)
|
||||
|
||||
// TODO-APP: support render custom not-found in SSR for generateMetadata.
|
||||
// Check contains root not-found payload in flight response for now.
|
||||
let hasRootNotFoundFlight = false
|
||||
for (const el of $('script').toArray()) {
|
||||
const text = $(el).text()
|
||||
if (text.includes('Local found boundary')) {
|
||||
hasRootNotFoundFlight = true
|
||||
}
|
||||
}
|
||||
expect(hasRootNotFoundFlight).toBe(true)
|
||||
|
||||
// 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',
|
||||
})
|
||||
expect(res.status).toBe(307)
|
||||
})
|
||||
})
|
||||
|
||||
describe('icons', () => {
|
||||
it('should support basic object icons field', async () => {
|
||||
const browser = await next.browser('/icons')
|
||||
|
@ -757,31 +551,6 @@ describe('app dir - metadata', () => {
|
|||
const dynamicIconRes = await next.fetch(dynamicIconHref)
|
||||
expect(dynamicIconRes.status).toBe(200)
|
||||
})
|
||||
|
||||
if (isNextDev) {
|
||||
// This test frequently causes a compilation error when run in Turbopack
|
||||
// which also causes all subsequent tests to fail. Disabled while we investigate to reduce flakes.
|
||||
;(process.env.TURBOPACK ? it.skip : it)(
|
||||
'should handle updates to the file icon name and order',
|
||||
async () => {
|
||||
await next.renameFile(
|
||||
'app/icons/static/icon.png',
|
||||
'app/icons/static/icon2.png'
|
||||
)
|
||||
|
||||
await check(async () => {
|
||||
const $ = await next.render$('/icons/static')
|
||||
const $icon = $('head > link[rel="icon"][type!="image/x-icon"]')
|
||||
return $icon.attr('href')
|
||||
}, /\/icons\/static\/icon2/)
|
||||
|
||||
await next.renameFile(
|
||||
'app/icons/static/icon2.png',
|
||||
'app/icons/static/icon.png'
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
describe('twitter', () => {
|
||||
|
@ -1020,8 +789,30 @@ describe('app dir - metadata', () => {
|
|||
expect(ogHtml).toContain('pages-opengraph-image-page')
|
||||
})
|
||||
|
||||
it('should not crash from error thrown during preloading nested generateMetadata', async () => {
|
||||
const res = await next.fetch('/dynamic-meta')
|
||||
expect(res.status).toBe(404)
|
||||
describe('hmr', () => {
|
||||
if (isNextDev) {
|
||||
// This test frequently causes a compilation error when run in Turbopack
|
||||
// which also causes all subsequent tests to fail. Disabled while we investigate to reduce flakes.
|
||||
;(process.env.TURBOPACK ? it.skip : it)(
|
||||
'should handle updates to the file icon name and order',
|
||||
async () => {
|
||||
await next.renameFile(
|
||||
'app/icons/static/icon.png',
|
||||
'app/icons/static/icon2.png'
|
||||
)
|
||||
|
||||
await check(async () => {
|
||||
const $ = await next.render$('/icons/static')
|
||||
const $icon = $('head > link[rel="icon"][type!="image/x-icon"]')
|
||||
return $icon.attr('href')
|
||||
}, /\/icons\/static\/icon2/)
|
||||
|
||||
await next.renameFile(
|
||||
'app/icons/static/icon2.png',
|
||||
'app/icons/static/icon.png'
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
|
|
@ -10,6 +10,7 @@ import { promisify } from 'util'
|
|||
import http from 'http'
|
||||
import path from 'path'
|
||||
|
||||
import cheerio from 'cheerio'
|
||||
import spawn from 'cross-spawn'
|
||||
import { writeFile } from 'fs-extra'
|
||||
import getPort from 'get-port'
|
||||
|
@ -1232,3 +1233,150 @@ export const describeVariants = {
|
|||
}
|
||||
},
|
||||
}
|
||||
|
||||
export const getTitle = (browser: BrowserInterface) =>
|
||||
browser.elementByCss('title').text()
|
||||
|
||||
async function checkMeta(
|
||||
browser: BrowserInterface,
|
||||
queryValue: string,
|
||||
expected: RegExp | string | string[] | undefined | null,
|
||||
queryKey: string = 'property',
|
||||
tag: string = 'meta',
|
||||
domAttributeField: string = 'content'
|
||||
) {
|
||||
const values = await browser.eval(
|
||||
`[...document.querySelectorAll('${tag}[${queryKey}="${queryValue}"]')].map((el) => el.getAttribute("${domAttributeField}"))`
|
||||
)
|
||||
if (expected instanceof RegExp) {
|
||||
expect(values[0]).toMatch(expected)
|
||||
} else {
|
||||
if (Array.isArray(expected)) {
|
||||
expect(values).toEqual(expected)
|
||||
} else {
|
||||
// If expected is undefined, then it should not exist.
|
||||
// Otherwise, it should exist in the matched values.
|
||||
if (expected === undefined) {
|
||||
expect(values).not.toContain(undefined)
|
||||
} else {
|
||||
expect(values).toContain(expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function createDomMatcher(browser: BrowserInterface) {
|
||||
/**
|
||||
* @param tag - tag name, e.g. 'meta'
|
||||
* @param query - query string, e.g. 'name="description"'
|
||||
* @param expectedObject - expected object, e.g. { content: 'my description' }
|
||||
* @returns {Promise<void>} - promise that resolves when the check is done
|
||||
*
|
||||
* @example
|
||||
* const matchDom = createDomMatcher(browser)
|
||||
* await matchDom('meta', 'name="description"', { content: 'description' })
|
||||
*/
|
||||
return async (
|
||||
tag: string,
|
||||
query: string,
|
||||
expectedObject: Record<string, string | null | undefined>
|
||||
) => {
|
||||
const props = await browser.eval(`
|
||||
const el = document.querySelector('${tag}[${query}]');
|
||||
const res = {}
|
||||
const keys = ${JSON.stringify(Object.keys(expectedObject))}
|
||||
for (const k of keys) {
|
||||
res[k] = el?.getAttribute(k)
|
||||
}
|
||||
res
|
||||
`)
|
||||
expect(props).toEqual(expectedObject)
|
||||
}
|
||||
}
|
||||
|
||||
export function createMultiHtmlMatcher($: ReturnType<typeof cheerio.load>) {
|
||||
/**
|
||||
* @param tag - tag name, e.g. 'meta'
|
||||
* @param queryKey - query key, e.g. 'property'
|
||||
* @param domAttributeField - dom attribute field, e.g. 'content'
|
||||
* @param expected - expected object, e.g. { description: 'my description' }
|
||||
* @returns {void} - void when the check is done
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* const $ = await next.render$('html')
|
||||
* const matchHtml = createMultiHtmlMatcher($)
|
||||
* matchHtml('meta', 'name', 'property', {
|
||||
* description: 'description',
|
||||
* og: 'og:description'
|
||||
* })
|
||||
*
|
||||
*/
|
||||
return (
|
||||
tag: string,
|
||||
queryKey: string,
|
||||
domAttributeField: string,
|
||||
expected: Record<string, string | string[] | undefined>
|
||||
) => {
|
||||
const res = {}
|
||||
for (const key of Object.keys(expected)) {
|
||||
const el = $(`${tag}[${queryKey}="${key}"]`)
|
||||
if (el.length > 1) {
|
||||
res[key] = el.toArray().map((el) => el.attribs[domAttributeField])
|
||||
} else {
|
||||
res[key] = el.attr(domAttributeField)
|
||||
}
|
||||
}
|
||||
expect(res).toEqual(expected)
|
||||
}
|
||||
}
|
||||
|
||||
export function createMultiDomMatcher(browser: BrowserInterface) {
|
||||
/**
|
||||
* @param tag - tag name, e.g. 'meta'
|
||||
* @param queryKey - query key, e.g. 'property'
|
||||
* @param domAttributeField - dom attribute field, e.g. 'content'
|
||||
* @param expected - expected object, e.g. { description: 'my description' }
|
||||
* @returns {Promise<void>} - promise that resolves when the check is done
|
||||
*
|
||||
* @example
|
||||
* const matchMultiDom = createMultiDomMatcher(browser)
|
||||
* await matchMultiDom('meta', 'property', 'content', {
|
||||
* description: 'description',
|
||||
* 'og:title': 'title',
|
||||
* 'twitter:title': 'title'
|
||||
* })
|
||||
*
|
||||
*/
|
||||
return async (
|
||||
tag: string,
|
||||
queryKey: string,
|
||||
domAttributeField: string,
|
||||
expected: Record<string, string | string[] | undefined | null>
|
||||
) => {
|
||||
await Promise.all(
|
||||
Object.keys(expected).map(async (key) => {
|
||||
return checkMeta(
|
||||
browser,
|
||||
key,
|
||||
expected[key],
|
||||
queryKey,
|
||||
tag,
|
||||
domAttributeField
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export const checkMetaNameContentPair = (
|
||||
browser: BrowserInterface,
|
||||
name: string,
|
||||
content: string | string[]
|
||||
) => checkMeta(browser, name, content, 'name')
|
||||
|
||||
export const checkLink = (
|
||||
browser: BrowserInterface,
|
||||
rel: string,
|
||||
content: string | string[]
|
||||
) => checkMeta(browser, rel, content, 'rel', 'link', 'href')
|
||||
|
|
Loading…
Reference in a new issue