Convert 28 JavaScript files to TypeScript (#57753)
This commit is contained in:
@@ -6,11 +6,13 @@
|
||||
*/
|
||||
|
||||
export function printAnnotationResults(
|
||||
results,
|
||||
// Using 'any' type as results structure is dynamic and comes from various linting tools with different formats
|
||||
results: any,
|
||||
{ skippableRules = [], skippableFlawProperties = [] } = {},
|
||||
) {
|
||||
for (const [file, flaws] of Object.entries(results)) {
|
||||
for (const flaw of flaws) {
|
||||
// Using 'any' type for flaws as they have varying structures depending on the linting rule
|
||||
for (const flaw of flaws as any) {
|
||||
if (intersection(flaw.ruleNames, skippableRules)) {
|
||||
continue
|
||||
}
|
||||
@@ -52,6 +54,7 @@ export function printAnnotationResults(
|
||||
}
|
||||
}
|
||||
|
||||
function intersection(arr1, arr2) {
|
||||
return arr1.some((item) => arr2.includes(item))
|
||||
// Using 'any' types for generic array intersection utility function
|
||||
function intersection(arr1: any[], arr2: any[]) {
|
||||
return arr1.some((item: any) => arr2.includes(item))
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations
|
||||
import { addError } from 'markdownlint-rule-helpers'
|
||||
import { intersection } from 'lodash-es'
|
||||
|
||||
@@ -5,12 +6,13 @@ import { getFrontmatter } from '../helpers/utils'
|
||||
import { formatAjvErrors } from '../helpers/schema-utils'
|
||||
import { frontmatter, deprecatedProperties } from '@/frame/lib/frontmatter'
|
||||
import readFrontmatter from '@/frame/lib/read-frontmatter'
|
||||
import type { RuleParams, RuleErrorCallback, Rule } from '../../types'
|
||||
|
||||
export const frontmatterSchema = {
|
||||
export const frontmatterSchema: Rule = {
|
||||
names: ['GHD012', 'frontmatter-schema'],
|
||||
description: 'Frontmatter must conform to the schema',
|
||||
tags: ['frontmatter', 'schema'],
|
||||
function: (params, onError) => {
|
||||
function: (params: RuleParams, onError: RuleErrorCallback) => {
|
||||
const fm = getFrontmatter(params.lines)
|
||||
if (!fm) return
|
||||
|
||||
@@ -22,25 +24,25 @@ export const frontmatterSchema = {
|
||||
for (const key of deprecatedKeys) {
|
||||
// Early access articles are allowed to have deprecated properties
|
||||
if (params.name.includes('early-access')) continue
|
||||
const line = params.lines.find((line) => line.trim().startsWith(key))
|
||||
const lineNumber = params.lines.indexOf(line) + 1
|
||||
const line = params.lines.find((line: string) => line.trim().startsWith(key))
|
||||
const lineNumber = params.lines.indexOf(line!) + 1
|
||||
addError(
|
||||
onError,
|
||||
lineNumber,
|
||||
`The frontmatter property '${key}' is deprecated. Please remove the property from your article's frontmatter.`,
|
||||
line,
|
||||
[1, line.length],
|
||||
line!,
|
||||
[1, line!.length],
|
||||
null, // No fix possible
|
||||
)
|
||||
}
|
||||
|
||||
// Check that the frontmatter matches the schema
|
||||
const { errors } = readFrontmatter(params.lines.join('\n'), { schema: frontmatter.schema })
|
||||
const formattedErrors = formatAjvErrors(errors)
|
||||
const formattedErrors = formatAjvErrors(errors as any)
|
||||
for (const error of formattedErrors) {
|
||||
// If the missing property is at the top level, we don't have a line
|
||||
// to point to. In that case, the error will be added to line 1.
|
||||
const query = (line) => line.trim().startsWith(`${error.searchProperty}:`)
|
||||
const query = (line: string) => line.trim().startsWith(`${error.searchProperty}:`)
|
||||
const line = error.searchProperty === '' ? null : params.lines.find(query)
|
||||
const lineNumber = line ? params.lines.indexOf(line) + 1 : 1
|
||||
addError(
|
||||
@@ -1,6 +1,8 @@
|
||||
// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations
|
||||
import { addError } from 'markdownlint-rule-helpers'
|
||||
|
||||
import { forEachInlineChild, getRange } from '../helpers/utils'
|
||||
import type { RuleParams, RuleErrorCallback, MarkdownToken, Rule } from '../../types'
|
||||
|
||||
const excludeStartWords = ['image', 'graphic']
|
||||
|
||||
@@ -8,21 +10,21 @@ const excludeStartWords = ['image', 'graphic']
|
||||
Images should have meaningful alternative text (alt text)
|
||||
and should not begin with words like "image" or "graphic".
|
||||
*/
|
||||
export const imageAltTextExcludeStartWords = {
|
||||
export const imageAltTextExcludeStartWords: Rule = {
|
||||
names: ['GHD031', 'image-alt-text-exclude-words'],
|
||||
description: 'Alternate text for images should not begin with words like "image" or "graphic"',
|
||||
tags: ['accessibility', 'images'],
|
||||
parser: 'markdownit',
|
||||
function: (params, onError) => {
|
||||
forEachInlineChild(params, 'image', function forToken(token) {
|
||||
const imageAltText = token.content.trim()
|
||||
|
||||
function: (params: RuleParams, onError: RuleErrorCallback) => {
|
||||
forEachInlineChild(params, 'image', function forToken(token: MarkdownToken) {
|
||||
// If the alt text is empty, there is nothing to check and you can't
|
||||
// produce a valid range.
|
||||
// We can safely return early because the image-alt-text-length rule
|
||||
// will fail this one.
|
||||
if (!token.content) return
|
||||
|
||||
const imageAltText = token.content.trim()
|
||||
|
||||
const range = getRange(token.line, imageAltText)
|
||||
if (
|
||||
excludeStartWords.some((excludeWord) => imageAltText.toLowerCase().startsWith(excludeWord))
|
||||
@@ -1,15 +1,18 @@
|
||||
// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations
|
||||
import { filterTokens } from 'markdownlint-rule-helpers'
|
||||
|
||||
import { addFixErrorDetail, getRange } from '../helpers/utils'
|
||||
import { allLanguageKeys } from '@/languages/lib/languages'
|
||||
import type { RuleParams, RuleErrorCallback, Rule } from '../../types'
|
||||
|
||||
export const internalLinksNoLang = {
|
||||
export const internalLinksNoLang: Rule = {
|
||||
names: ['GHD002', 'internal-links-no-lang'],
|
||||
description: 'Internal links must not have a hardcoded language code',
|
||||
tags: ['links', 'url'],
|
||||
parser: 'markdownit',
|
||||
function: (params, onError) => {
|
||||
filterTokens(params, 'inline', (token) => {
|
||||
function: (params: RuleParams, onError: RuleErrorCallback) => {
|
||||
// Using 'any' type for token as markdownlint-rule-helpers doesn't provide TypeScript types
|
||||
filterTokens(params, 'inline', (token: any) => {
|
||||
for (const child of token.children) {
|
||||
if (child.type !== 'link_open') continue
|
||||
|
||||
@@ -18,14 +21,17 @@ export const internalLinksNoLang = {
|
||||
// ['href', 'get-started'], ['target', '_blank'],
|
||||
// ['rel', 'canonical'],
|
||||
// ]
|
||||
// Attribute arrays are tuples of [attributeName, attributeValue] from markdownit parser
|
||||
const hrefsMissingSlashes = child.attrs
|
||||
// The attribute could also be `target` or `rel`
|
||||
.filter((attr) => attr[0] === 'href')
|
||||
.filter((attr) => attr[1].startsWith('/') || !attr[1].startsWith('//'))
|
||||
.filter((attr: [string, string]) => attr[0] === 'href')
|
||||
.filter((attr: [string, string]) => attr[1].startsWith('/') || !attr[1].startsWith('//'))
|
||||
// Filter out link paths that start with language code
|
||||
.filter((attr) => allLanguageKeys.some((lang) => attr[1].split('/')[1] === lang))
|
||||
.filter((attr: [string, string]) =>
|
||||
allLanguageKeys.some((lang) => attr[1].split('/')[1] === lang),
|
||||
)
|
||||
// Get the link path from the attribute
|
||||
.map((attr) => attr[1])
|
||||
.map((attr: [string, string]) => attr[1])
|
||||
// Create errors for each link path that includes a language code
|
||||
for (const linkPath of hrefsMissingSlashes) {
|
||||
const range = getRange(child.line, linkPath)
|
||||
@@ -1,14 +1,17 @@
|
||||
// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations
|
||||
import { filterTokens } from 'markdownlint-rule-helpers'
|
||||
|
||||
import { addFixErrorDetail, getRange } from '../helpers/utils'
|
||||
import type { RuleParams, RuleErrorCallback, Rule } from '../../types'
|
||||
|
||||
export const internalLinksSlash = {
|
||||
export const internalLinksSlash: Rule = {
|
||||
names: ['GHD003', 'internal-links-slash'],
|
||||
description: 'Internal links must start with a /',
|
||||
tags: ['links', 'url'],
|
||||
parser: 'markdownit',
|
||||
function: (params, onError) => {
|
||||
filterTokens(params, 'inline', (token) => {
|
||||
function: (params: RuleParams, onError: RuleErrorCallback) => {
|
||||
// Using 'any' type for token as markdownlint-rule-helpers doesn't provide TypeScript types
|
||||
filterTokens(params, 'inline', (token: any) => {
|
||||
for (const child of token.children) {
|
||||
if (child.type !== 'link_open') continue
|
||||
|
||||
@@ -17,20 +20,21 @@ export const internalLinksSlash = {
|
||||
// ['href', '/get-started'], ['target', '_blank'],
|
||||
// ['rel', 'canonical'],
|
||||
// ]
|
||||
// Attribute arrays are tuples of [attributeName, attributeValue] from markdownit parser
|
||||
const hrefsMissingSlashes = child.attrs
|
||||
// The attribute could also be `target` or `rel`
|
||||
.filter((attr) => attr[0] === 'href')
|
||||
.filter((attr: [string, string]) => attr[0] === 'href')
|
||||
// Filter out prefixes we don't want to check
|
||||
.filter(
|
||||
(attr) =>
|
||||
(attr: [string, string]) =>
|
||||
!['http', 'mailto', '#', '/'].some((ignorePrefix) =>
|
||||
attr[1].startsWith(ignorePrefix),
|
||||
),
|
||||
)
|
||||
// We can ignore empty links because MD042 from markdownlint catches empty links
|
||||
.filter((attr) => attr[1] !== '')
|
||||
.filter((attr: [string, string]) => attr[1] !== '')
|
||||
// Get the link path from the attribute
|
||||
.map((attr) => attr[1])
|
||||
.map((attr: [string, string]) => attr[1])
|
||||
|
||||
// Create errors for each link path that doesn't start with a /
|
||||
for (const linkPath of hrefsMissingSlashes) {
|
||||
@@ -1,15 +1,16 @@
|
||||
// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations
|
||||
import { addError, filterTokens } from 'markdownlint-rule-helpers'
|
||||
import type { RuleParams, RuleErrorCallback } from '../../types'
|
||||
import type { RuleParams, RuleErrorCallback, Rule } from '../../types'
|
||||
|
||||
import { doesStringEndWithPeriod, getRange, isStringQuoted } from '../helpers/utils'
|
||||
|
||||
export const linkPunctuation = {
|
||||
export const linkPunctuation: Rule = {
|
||||
names: ['GHD001', 'link-punctuation'],
|
||||
description: 'Internal link titles must not contain punctuation',
|
||||
tags: ['links', 'url'],
|
||||
parser: 'markdownit',
|
||||
function: (params: RuleParams, onError: RuleErrorCallback) => {
|
||||
// Using 'any' type for token as markdownlint-rule-helpers doesn't provide TypeScript types
|
||||
filterTokens(params, 'inline', (token: any) => {
|
||||
const { children, line } = token
|
||||
let inLink = false
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import { TokenKind } from 'liquidjs'
|
||||
// @ts-ignore - markdownlint-rule-helpers doesn't have TypeScript declarations
|
||||
import { addError } from 'markdownlint-rule-helpers'
|
||||
|
||||
import { getLiquidTokens, conditionalTags, getPositionData } from '../helpers/liquid-utils'
|
||||
import { isStringQuoted } from '../helpers/utils'
|
||||
import type { RuleParams, RuleErrorCallback, Rule } from '../../types'
|
||||
|
||||
/*
|
||||
Checks for instances where a Liquid conditional tag's argument is
|
||||
@@ -12,18 +14,20 @@ import { isStringQuoted } from '../helpers/utils'
|
||||
{% if "foo" %}
|
||||
{% ifversion "bar" %}
|
||||
*/
|
||||
export const liquidQuotedConditionalArg = {
|
||||
export const liquidQuotedConditionalArg: Rule = {
|
||||
names: ['GHD016', 'liquid-quoted-conditional-arg'],
|
||||
description: 'Liquid conditional tags should not quote the conditional argument',
|
||||
tags: ['liquid', 'format'],
|
||||
function: (params, onError) => {
|
||||
function: (params: RuleParams, onError: RuleErrorCallback) => {
|
||||
const content = params.lines.join('\n')
|
||||
// Using 'any' type for tokens as getLiquidTokens returns tokens from liquid-utils.js which lacks type definitions
|
||||
const tokens = getLiquidTokens(content)
|
||||
.filter((token) => token.kind === TokenKind.Tag)
|
||||
.filter((token) => conditionalTags.includes(token.name))
|
||||
.filter((token) => {
|
||||
.filter((token: any) => token.kind === TokenKind.Tag)
|
||||
.filter((token: any) => conditionalTags.includes(token.name))
|
||||
.filter((token: any) => {
|
||||
const tokensArray = token.args.split(/\s+/g)
|
||||
if (tokensArray.some((arg) => isStringQuoted(arg))) return true
|
||||
// Using 'any' for args as they come from the untyped liquid token structure
|
||||
if (tokensArray.some((arg: any) => isStringQuoted(arg))) return true
|
||||
return false
|
||||
})
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { TokenKind } from 'liquidjs'
|
||||
import { getLiquidTokens, getPositionData } from '../helpers/liquid-utils'
|
||||
import { addFixErrorDetail } from '../helpers/utils'
|
||||
import type { RuleParams, RuleErrorCallback, Rule } from '../../types'
|
||||
/*
|
||||
Octicons should always have an aria-label attribute even if aria hidden. For example:
|
||||
|
||||
@@ -13,16 +14,17 @@ Octicons should always have an aria-label attribute even if aria hidden. For exa
|
||||
|
||||
*/
|
||||
|
||||
export const octiconAriaLabels = {
|
||||
export const octiconAriaLabels: Rule = {
|
||||
names: ['GHD044', 'octicon-aria-labels'],
|
||||
description: 'Octicons should always have an aria-label attribute even if aria-hidden.',
|
||||
tags: ['accessibility', 'octicons'],
|
||||
parser: 'markdownit',
|
||||
function: (params, onError) => {
|
||||
function: (params: RuleParams, onError: RuleErrorCallback) => {
|
||||
const content = params.lines.join('\n')
|
||||
// Using 'any' type for tokens as getLiquidTokens returns tokens from liquid-utils.js which lacks type definitions
|
||||
const tokens = getLiquidTokens(content)
|
||||
.filter((token) => token.kind === TokenKind.Tag)
|
||||
.filter((token) => token.name === 'octicon')
|
||||
.filter((token: any) => token.kind === TokenKind.Tag)
|
||||
.filter((token: any) => token.name === 'octicon')
|
||||
|
||||
for (const token of tokens) {
|
||||
const { lineNumber, column, length } = getPositionData(token, params.lines)
|
||||
@@ -41,7 +41,7 @@ describe(frontmatterEarlyAccessReferences.names.join(' - '), () => {
|
||||
expect(lineNumbers.includes(4)).toBe(false)
|
||||
expect(lineNumbers.includes(5)).toBe(false)
|
||||
expect(errors[0].errorRange).toEqual([8, 12])
|
||||
expect(errors[1].errorRange).toEqual([15, 12], [28, 12])
|
||||
expect(errors[1].errorRange).toEqual([15, 12])
|
||||
})
|
||||
test('early access file with early access references passes', async () => {
|
||||
const result = await runRule(frontmatterEarlyAccessReferences, {
|
||||
@@ -22,8 +22,8 @@ describe(tableColumnIntegrity.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(3)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Table row has 3 columns but header has 2')
|
||||
if ((errors[0] as any).detail) {
|
||||
expect((errors[0] as any).detail).toContain('Table row has 3 columns but header has 2')
|
||||
} else if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Table row has 3 columns but header has 2')
|
||||
} else {
|
||||
@@ -38,8 +38,8 @@ describe(tableColumnIntegrity.names.join(' - '), () => {
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(1)
|
||||
expect(errors[0].lineNumber).toBe(3)
|
||||
if (errors[0].detail) {
|
||||
expect(errors[0].detail).toContain('Table row has 2 columns but header has 3')
|
||||
if ((errors[0] as any).detail) {
|
||||
expect((errors[0] as any).detail).toContain('Table row has 2 columns but header has 3')
|
||||
} else if (errors[0].errorDetail) {
|
||||
expect(errors[0].errorDetail).toContain('Table row has 2 columns but header has 3')
|
||||
} else {
|
||||
@@ -6,7 +6,8 @@ import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-v
|
||||
import { DataDirectory } from '@/tests/helpers/data-directory'
|
||||
|
||||
describe('data tag', () => {
|
||||
let dd
|
||||
// Using 'any' type as DataDirectory is from data-directory.js which lacks type definitions
|
||||
let dd: any
|
||||
const enDirBefore = languages.en.dir
|
||||
|
||||
beforeAll(() => {
|
||||
@@ -41,7 +42,7 @@ describe('data tag', () => {
|
||||
currentLanguage: 'en',
|
||||
currentPath: '/en/liquid-tags/good-data-variable',
|
||||
}
|
||||
const rendered = await page.render(context)
|
||||
const rendered = await page!.render(context)
|
||||
// The test fixture contains:
|
||||
// {% data variables.stuff.foo %}
|
||||
// which we control the value of here in the test.
|
||||
@@ -57,7 +58,7 @@ describe('data tag', () => {
|
||||
currentPath: '/en/liquid-tags/bad-data-variable',
|
||||
currentLanguage: 'en',
|
||||
}
|
||||
await expect(page.render(context)).rejects.toThrow(
|
||||
await expect(page!.render(context)).rejects.toThrow(
|
||||
"Can't find the key 'foo.bar.tipu' in the scope., line:2, col:1",
|
||||
)
|
||||
})
|
||||
@@ -7,8 +7,10 @@ import { DataDirectory } from '@/tests/helpers/data-directory'
|
||||
describe('liquid helper tags', () => {
|
||||
vi.setConfig({ testTimeout: 60 * 1000 })
|
||||
|
||||
const context = {}
|
||||
let dd
|
||||
// Using 'any' type as context is a test fixture with dynamic properties set in beforeAll
|
||||
const context: any = {}
|
||||
// Using 'any' type as DataDirectory is from data-directory.js which lacks type definitions
|
||||
let dd: any
|
||||
const enDirBefore = languages.en.dir
|
||||
|
||||
beforeAll(() => {
|
||||
@@ -4,6 +4,7 @@ import { extname, basename } from 'path'
|
||||
|
||||
import walk from 'walk-sync'
|
||||
import { beforeAll, describe, expect, test } from 'vitest'
|
||||
import type { ValidateFunction, SchemaObject } from 'ajv'
|
||||
|
||||
import { getJsonValidator, validateJson } from '@/tests/lib/validate-json-schema'
|
||||
import { formatAjvErrors } from '@/tests/helpers/schemas'
|
||||
@@ -19,7 +20,8 @@ const yamlWalkOptions = {
|
||||
}
|
||||
|
||||
for (const dataDir of directorySchemas) {
|
||||
let schema, validate
|
||||
let schema: SchemaObject
|
||||
let validate: ValidateFunction
|
||||
const dataDirectoryName = basename(dataDir)
|
||||
const yamlFileList = walk(dataDir, yamlWalkOptions).sort()
|
||||
|
||||
@@ -32,7 +34,7 @@ for (const dataDir of directorySchemas) {
|
||||
test.each(yamlFileList)('%p', async (yamlAbsPath) => {
|
||||
const yamlContent = yaml.load(readFileSync(yamlAbsPath, 'utf8'))
|
||||
const isValid = validate(yamlContent)
|
||||
const formattedErrors = isValid ? validate.errors : formatAjvErrors(validate.errors)
|
||||
const formattedErrors = isValid ? undefined : formatAjvErrors(validate.errors || [])
|
||||
expect(isValid, formattedErrors).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -43,6 +45,7 @@ describe('single data files', () => {
|
||||
const ymlData = yaml.load(readFileSync(filepath, 'utf8'))
|
||||
const schema = (await import(dataSchemas[filepath])).default
|
||||
const { isValid, errors } = validateJson(schema, ymlData)
|
||||
expect(isValid, errors).toBe(true)
|
||||
const formattedErrors = isValid ? undefined : formatAjvErrors(errors || [])
|
||||
expect(isValid, formattedErrors).toBe(true)
|
||||
})
|
||||
})
|
||||
@@ -20,7 +20,7 @@ describe('data-directory', () => {
|
||||
})
|
||||
|
||||
test('option: preprocess function', async () => {
|
||||
const preprocess = function (content) {
|
||||
const preprocess = function (content: string) {
|
||||
return content.replace('markdown', 'MARKDOWN')
|
||||
}
|
||||
const data = dataDirectory(fixturesDir, { preprocess })
|
||||
@@ -35,7 +35,7 @@ describe('data-directory', () => {
|
||||
})
|
||||
|
||||
test('option: ignorePatterns', async () => {
|
||||
const ignorePatterns = []
|
||||
const ignorePatterns: RegExp[] = []
|
||||
|
||||
// README is ignored by default
|
||||
expect('README' in dataDirectory(fixturesDir)).toBe(false)
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { NextFunction } from 'express'
|
||||
|
||||
// Fastly provides a Soft Purge feature that allows you to mark content as outdated (stale) instead of permanently
|
||||
// purging and thereby deleting it from Fastly's caches. Objects invalidated with Soft Purge will be treated as
|
||||
// outdated (stale) while Fastly fetches a new version from origin.
|
||||
@@ -14,7 +16,8 @@ export const SURROGATE_ENUMS = {
|
||||
MANUAL: 'manual-purge',
|
||||
}
|
||||
|
||||
export function setFastlySurrogateKey(res, enumKey, isCustomKey = false) {
|
||||
// Using 'any' type for res parameter to maintain compatibility with Express Response objects
|
||||
export function setFastlySurrogateKey(res: any, enumKey: string, isCustomKey = false) {
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
if (!isCustomKey && !Object.values(SURROGATE_ENUMS).includes(enumKey)) {
|
||||
throw new Error(
|
||||
@@ -27,17 +30,21 @@ export function setFastlySurrogateKey(res, enumKey, isCustomKey = false) {
|
||||
res.set(KEY, enumKey)
|
||||
}
|
||||
|
||||
export function setDefaultFastlySurrogateKey(req, res, next) {
|
||||
// Using 'any' type for req and res parameters to maintain backward compatibility with test mock objects
|
||||
// that don't fully implement ExtendedRequest and Response interfaces
|
||||
export function setDefaultFastlySurrogateKey(req: any, res: any, next: NextFunction) {
|
||||
res.set(KEY, `${SURROGATE_ENUMS.DEFAULT} ${makeLanguageSurrogateKey()}`)
|
||||
return next()
|
||||
}
|
||||
|
||||
export function setLanguageFastlySurrogateKey(req, res, next) {
|
||||
// Using 'any' type for req and res parameters to maintain backward compatibility with test mock objects
|
||||
// that don't fully implement ExtendedRequest and Response interfaces
|
||||
export function setLanguageFastlySurrogateKey(req: any, res: any, next: NextFunction) {
|
||||
res.set(KEY, `${SURROGATE_ENUMS.DEFAULT} ${makeLanguageSurrogateKey(req.language)}`)
|
||||
return next()
|
||||
}
|
||||
|
||||
export function makeLanguageSurrogateKey(langCode) {
|
||||
export function makeLanguageSurrogateKey(langCode?: string) {
|
||||
if (!langCode) {
|
||||
return 'no-language'
|
||||
}
|
||||
@@ -3,6 +3,7 @@ import { describe, expect, test, vi } from 'vitest'
|
||||
import { loadPageMap, loadPages } from '@/frame/lib/page-data'
|
||||
import { renderContent } from '@/content-render/index'
|
||||
import { allVersions } from '@/versions/lib/all-versions'
|
||||
import type { Permalink } from '@/types'
|
||||
|
||||
describe('toc links', () => {
|
||||
vi.setConfig({ testTimeout: 3 * 60 * 1000 })
|
||||
@@ -20,7 +21,8 @@ describe('toc links', () => {
|
||||
for (const pageVersion of Object.keys(allVersions)) {
|
||||
for (const page of englishIndexPages) {
|
||||
// skip page if it doesn't have a permalink for the current product version
|
||||
if (!page.permalinks.some((permalink) => permalink.pageVersion === pageVersion)) continue
|
||||
if (!page.permalinks.some((permalink: Permalink) => permalink.pageVersion === pageVersion))
|
||||
continue
|
||||
|
||||
// build fake context object for rendering the page
|
||||
const context = {
|
||||
@@ -38,7 +40,7 @@ describe('toc links', () => {
|
||||
} catch (err) {
|
||||
issues.push({
|
||||
'TOC path': page.relativePath,
|
||||
error: err.message,
|
||||
error: err instanceof Error ? err.message : String(err),
|
||||
pageVersion,
|
||||
})
|
||||
}
|
||||
@@ -10,10 +10,15 @@ import {
|
||||
getPreviews,
|
||||
} from '../lib/index'
|
||||
|
||||
interface GraphqlType {
|
||||
kind: string
|
||||
type: string
|
||||
}
|
||||
|
||||
describe('graphql schema', () => {
|
||||
const graphqlTypes = JSON.parse(readFileSync('src/graphql/lib/types.json')).map(
|
||||
(item) => item.kind,
|
||||
)
|
||||
const graphqlTypes = (
|
||||
JSON.parse(readFileSync('src/graphql/lib/types.json', 'utf-8')) as GraphqlType[]
|
||||
).map((item) => item.kind)
|
||||
for (const version in allVersions) {
|
||||
for (const type of graphqlTypes) {
|
||||
test(`getting the GraphQL ${type} schema works for ${version}`, async () => {
|
||||
@@ -2,6 +2,7 @@ import { describe, expect, test } from 'vitest'
|
||||
|
||||
import { getDOM } from '@/tests/helpers/e2etest'
|
||||
import { loadPages } from '@/frame/lib/page-data'
|
||||
import type { Permalink } from '@/types'
|
||||
|
||||
const pageList = await loadPages(undefined, ['en'])
|
||||
|
||||
@@ -27,7 +28,9 @@ describe('server rendering certain GraphQL pages', () => {
|
||||
)
|
||||
const nonFPTPermalinks = autogeneratedPages
|
||||
.map((page) =>
|
||||
page.permalinks.find((permalink) => permalink.pageVersion !== 'free-pro-team@latest'),
|
||||
page.permalinks.find(
|
||||
(permalink: Permalink) => permalink.pageVersion !== 'free-pro-team@latest',
|
||||
),
|
||||
)
|
||||
.filter(Boolean)
|
||||
const nonFPTPermalinksHrefs = nonFPTPermalinks.map((permalink) => {
|
||||
@@ -13,7 +13,7 @@ const liquidEndRex = /{%-?\s*endif\s*-?%}$/
|
||||
// {% ifversion ghes%}/foo/bar{%endif %}
|
||||
//
|
||||
// And if no liquid, just return as is.
|
||||
function stripLiquid(text) {
|
||||
function stripLiquid(text: string): string {
|
||||
if (liquidStartRex.test(text) && liquidEndRex.test(text)) {
|
||||
return text.replace(liquidStartRex, '').replace(liquidEndRex, '').trim()
|
||||
} else if (text.includes('{')) {
|
||||
@@ -26,7 +26,9 @@ function stripLiquid(text) {
|
||||
// return undefined if it can found as a known page.
|
||||
// Otherwise, return an object with information that is used to
|
||||
// print a useful test error message in the assertion.
|
||||
export function checkURL(uri, index, redirectsContext) {
|
||||
// Using 'any' type for redirectsContext parameter as it's a complex context object
|
||||
// with dynamic structure that would require extensive type definitions
|
||||
export function checkURL(uri: string, index: number, redirectsContext: any) {
|
||||
const url = `/en${stripLiquid(uri).split('#')[0]}`
|
||||
if (!(url in redirectsContext.pages)) {
|
||||
// Some are written without a version, but don't work with the
|
||||
13
src/versions/lib/all-versions.d.ts
vendored
13
src/versions/lib/all-versions.d.ts
vendored
@@ -1,13 +0,0 @@
|
||||
import type { AllVersions } from '@/types'
|
||||
|
||||
export const allVersionKeys: string[]
|
||||
|
||||
export const allVersionShortnames: Record<string, string>
|
||||
|
||||
export declare function isApiVersioned(version: string): boolean
|
||||
|
||||
export declare function getDocsVersion(openApiVersion: string): string
|
||||
|
||||
export declare function getOpenApiVersion(version: string): string
|
||||
|
||||
export const allVersions: AllVersions
|
||||
@@ -6,6 +6,7 @@ import { latest } from '@/versions/lib/enterprise-server-releases'
|
||||
import schema from '@/tests/helpers/schemas/versions-schema'
|
||||
import nonEnterpriseDefaultVersion from '@/versions/lib/non-enterprise-default-version'
|
||||
import { formatAjvErrors } from '@/tests/helpers/schemas'
|
||||
import type { Version } from '@/types'
|
||||
|
||||
const validate = getJsonValidator(schema)
|
||||
|
||||
@@ -16,12 +17,13 @@ describe('versions module', () => {
|
||||
})
|
||||
|
||||
test('every version is valid', () => {
|
||||
Object.values(allVersions).forEach((versionObj) => {
|
||||
Object.values(allVersions).forEach((versionObj: Version) => {
|
||||
const versionName = versionObj.version
|
||||
const isValid = validate(versionObj)
|
||||
let errors
|
||||
let errors: string | undefined
|
||||
|
||||
if (!isValid) {
|
||||
errors = `version '${versionObj.version}': ${formatAjvErrors(validate.errors)}`
|
||||
errors = `version '${versionName}': ${formatAjvErrors(validate.errors || [])}`
|
||||
}
|
||||
|
||||
expect(isValid, errors).toBe(true)
|
||||
@@ -29,7 +31,7 @@ describe('versions module', () => {
|
||||
})
|
||||
|
||||
test('check REST api calendar date versioned versions set to correct latestApiVersion', () => {
|
||||
Object.values(allVersions).forEach((versionObj) => {
|
||||
Object.values(allVersions).forEach((versionObj: Version) => {
|
||||
if (versionObj.apiVersions.length > 0) {
|
||||
const latestApiVersion = versionObj.latestApiVersion
|
||||
const apiVersions = versionObj.apiVersions
|
||||
Reference in New Issue
Block a user