Convert 12 JavaScript files to TypeScript (#57575)
This commit is contained in:
@@ -4,8 +4,8 @@ import { runRule } from '../../lib/init-test'
|
||||
import { githubOwnedActionReferences } from '../../lib/linting-rules/github-owned-action-references'
|
||||
|
||||
describe(githubOwnedActionReferences.names.join(' - '), () => {
|
||||
test('Using hardcoded GitHub-owned actions causes error', async () => {
|
||||
const markdown = [
|
||||
test('Using hardcoded GitHub-owned actions causes error', async (): Promise<void> => {
|
||||
const markdown: string = [
|
||||
'Hello actions/checkout@v2 apps.',
|
||||
'A actions/delete-package-versions@v2 for apps.',
|
||||
'Hello actions/download-artifact@v2.',
|
||||
@@ -13,7 +13,11 @@ describe(githubOwnedActionReferences.names.join(' - '), () => {
|
||||
'actions/cache@',
|
||||
'[link title](/actions/cache)',
|
||||
].join('\n')
|
||||
const result = await runRule(githubOwnedActionReferences, { strings: { markdown } })
|
||||
const result = await runRule(githubOwnedActionReferences, {
|
||||
strings: { markdown },
|
||||
files: undefined,
|
||||
ruleConfig: true,
|
||||
})
|
||||
const errors = result.markdown
|
||||
expect(errors.length).toBe(3)
|
||||
})
|
||||
@@ -6,12 +6,16 @@ import { tableLiquidVersioning } from '../../lib/linting-rules/table-liquid-vers
|
||||
const FIXTURE_FILEPATH = 'src/content-linter/tests/fixtures/tables.md'
|
||||
|
||||
describe(tableLiquidVersioning.names.join(' - '), () => {
|
||||
test('non-early access file with early access references fails', async () => {
|
||||
const result = await runRule(tableLiquidVersioning, { files: [FIXTURE_FILEPATH] })
|
||||
test('non-early access file with early access references fails', async (): Promise<void> => {
|
||||
const result = await runRule(tableLiquidVersioning, {
|
||||
strings: undefined,
|
||||
files: [FIXTURE_FILEPATH],
|
||||
ruleConfig: true,
|
||||
})
|
||||
const errors = result[FIXTURE_FILEPATH]
|
||||
expect(errors.length).toBe(11)
|
||||
const lineNumbers = errors.map((error) => error.lineNumber)
|
||||
const expectedErrorLines = [38, 40, 43, 44, 51, 53, 54, 55, 57, 58, 59]
|
||||
const lineNumbers: number[] = errors.map((error) => error.lineNumber)
|
||||
const expectedErrorLines: number[] = [38, 40, 43, 44, 51, 53, 54, 55, 57, 58, 59]
|
||||
expect(JSON.stringify(lineNumbers)).toEqual(JSON.stringify(expectedErrorLines))
|
||||
})
|
||||
})
|
||||
@@ -7,11 +7,12 @@ import { decode } from 'html-entities'
|
||||
// Take advantage of the subtle fact that a lot of the times, the html value
|
||||
// we get here is a single line that starts with `<p>` and ends with `</p>`
|
||||
// and contains no longer HTML tags.
|
||||
export function fastTextOnly(html) {
|
||||
export function fastTextOnly(html: string): string {
|
||||
if (!html) return ''
|
||||
if (html.startsWith('<p>') && html.endsWith('</p>')) {
|
||||
const middle = html.slice(3, -4)
|
||||
if (!middle.includes('<')) return decode(middle.trim())
|
||||
}
|
||||
return cheerio.load(html, { xmlMode: true }).text().trim()
|
||||
const $ = cheerio.load(html, { xmlMode: true })
|
||||
return $.root().text().trim()
|
||||
}
|
||||
@@ -1,20 +1,24 @@
|
||||
import { schema } from '@/frame/lib/frontmatter'
|
||||
|
||||
// Copy the properties from the frontmatter schema.
|
||||
const featureVersions = {
|
||||
interface FeatureVersionsSchema {
|
||||
type: 'object'
|
||||
properties: {
|
||||
versions: Object.assign({}, schema.properties.versions),
|
||||
versions: any
|
||||
}
|
||||
additionalProperties: false
|
||||
}
|
||||
|
||||
// Copy the properties from the frontmatter schema.
|
||||
const featureVersions: FeatureVersionsSchema = {
|
||||
type: 'object',
|
||||
properties: {
|
||||
versions: Object.assign({}, (schema.properties as any).versions),
|
||||
},
|
||||
additionalProperties: false,
|
||||
}
|
||||
|
||||
// Remove the feature versions properties.
|
||||
// We don't want to allow features within features! We just want pure versioning.
|
||||
delete featureVersions.properties.versions.properties.feature
|
||||
|
||||
// Call it invalid if any properties other than version properties are found.
|
||||
featureVersions.additionalProperties = false
|
||||
|
||||
// avoid ajv strict warning
|
||||
featureVersions.type = 'object'
|
||||
|
||||
export default featureVersions
|
||||
@@ -1,18 +0,0 @@
|
||||
export const term = {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
pattern: '^((?!\\*).)*$', // no asterisks allowed
|
||||
}
|
||||
|
||||
export default {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['term'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
term,
|
||||
},
|
||||
},
|
||||
minItems: 21,
|
||||
}
|
||||
43
src/data-directory/lib/data-schemas/glossaries-candidates.ts
Normal file
43
src/data-directory/lib/data-schemas/glossaries-candidates.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export interface TermSchema {
|
||||
type: 'string'
|
||||
minLength: number
|
||||
pattern: string
|
||||
}
|
||||
|
||||
export const term: TermSchema = {
|
||||
type: 'string',
|
||||
minLength: 1,
|
||||
pattern: '^((?!\\*).)*$', // no asterisks allowed
|
||||
}
|
||||
|
||||
export interface GlossaryCandidateItem {
|
||||
term: string
|
||||
}
|
||||
|
||||
export interface GlossaryCandidatesSchema {
|
||||
type: 'array'
|
||||
items: {
|
||||
type: 'object'
|
||||
required: ['term']
|
||||
additionalProperties: false
|
||||
properties: {
|
||||
term: TermSchema
|
||||
}
|
||||
}
|
||||
minItems: number
|
||||
}
|
||||
|
||||
const schema: GlossaryCandidatesSchema = {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['term'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
term,
|
||||
},
|
||||
},
|
||||
minItems: 21,
|
||||
}
|
||||
|
||||
export default schema
|
||||
@@ -1,18 +0,0 @@
|
||||
import { term } from './glossaries-candidates'
|
||||
|
||||
export default {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['term', 'description'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
term,
|
||||
description: {
|
||||
type: 'string',
|
||||
lintable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
minItems: 21,
|
||||
}
|
||||
44
src/data-directory/lib/data-schemas/glossaries-external.ts
Normal file
44
src/data-directory/lib/data-schemas/glossaries-external.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { term, type TermSchema } from './glossaries-candidates'
|
||||
|
||||
export interface GlossaryExternalItem {
|
||||
term: string
|
||||
description: string
|
||||
}
|
||||
|
||||
export interface DescriptionSchema {
|
||||
type: 'string'
|
||||
lintable: boolean
|
||||
}
|
||||
|
||||
export interface GlossariesExternalSchema {
|
||||
type: 'array'
|
||||
items: {
|
||||
type: 'object'
|
||||
required: ['term', 'description']
|
||||
additionalProperties: false
|
||||
properties: {
|
||||
term: TermSchema
|
||||
description: DescriptionSchema
|
||||
}
|
||||
}
|
||||
minItems: number
|
||||
}
|
||||
|
||||
const schema: GlossariesExternalSchema = {
|
||||
type: 'array',
|
||||
items: {
|
||||
type: 'object',
|
||||
required: ['term', 'description'],
|
||||
additionalProperties: false,
|
||||
properties: {
|
||||
term,
|
||||
description: {
|
||||
type: 'string',
|
||||
lintable: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
minItems: 21,
|
||||
}
|
||||
|
||||
export default schema
|
||||
@@ -3,15 +3,15 @@ import { describe, expect, test } from 'vitest'
|
||||
import filenameToKey from '@/data-directory/lib/filename-to-key'
|
||||
|
||||
describe('filename-to-key', () => {
|
||||
test('converts filenames to object keys', () => {
|
||||
test('converts filenames to object keys', (): void => {
|
||||
expect(filenameToKey('foo/bar/baz.txt')).toBe('foo.bar.baz')
|
||||
})
|
||||
|
||||
test('ignores leading slash on filenames', () => {
|
||||
test('ignores leading slash on filenames', (): void => {
|
||||
expect(filenameToKey('/foo/bar/baz.txt')).toBe('foo.bar.baz')
|
||||
})
|
||||
|
||||
test('supports MS Windows paths', () => {
|
||||
test('supports MS Windows paths', (): void => {
|
||||
expect(filenameToKey('path\\to\\file.txt')).toBe('path.to.file')
|
||||
})
|
||||
})
|
||||
@@ -5,13 +5,13 @@ import { get } from '@/tests/helpers/e2etest'
|
||||
describe('general /api pages', () => {
|
||||
vi.setConfig({ testTimeout: 60 * 1000 })
|
||||
|
||||
test("any /api URL that isn't found should JSON", async () => {
|
||||
test("any /api URL that isn't found should JSON", async (): Promise<void> => {
|
||||
const res = await get('/api')
|
||||
expect(res.statusCode).toBe(404)
|
||||
expect(res.headers['content-type']).toMatch(/application\/json/)
|
||||
})
|
||||
|
||||
test("any /api/* URL that isn't found should be JSON", async () => {
|
||||
test("any /api/* URL that isn't found should be JSON", async (): Promise<void> => {
|
||||
const res = await get('/api/yadayada')
|
||||
expect(res.statusCode).toBe(404)
|
||||
expect(JSON.parse(res.body).error).toBe('/yadayada not found')
|
||||
@@ -1,15 +0,0 @@
|
||||
import yaml from 'js-yaml'
|
||||
import { groupBy } from 'lodash-es'
|
||||
import { renderContent } from '@/content-render/index'
|
||||
|
||||
export default async function processUpcomingChanges(upcomingChangesYml) {
|
||||
const upcomingChanges = yaml.load(upcomingChangesYml).upcoming_changes
|
||||
|
||||
for (const change of upcomingChanges) {
|
||||
change.date = change.date.slice(0, 10)
|
||||
change.reason = await renderContent(change.reason)
|
||||
change.description = await renderContent(change.description)
|
||||
}
|
||||
|
||||
return groupBy(upcomingChanges.reverse(), 'date')
|
||||
}
|
||||
28
src/graphql/scripts/utils/process-upcoming-changes.ts
Normal file
28
src/graphql/scripts/utils/process-upcoming-changes.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import yaml from 'js-yaml'
|
||||
import { groupBy } from 'lodash-es'
|
||||
import { renderContent } from '@/content-render/index'
|
||||
|
||||
interface UpcomingChange {
|
||||
date: string
|
||||
reason: string
|
||||
description: string
|
||||
[key: string]: unknown
|
||||
}
|
||||
|
||||
interface UpcomingChangesData {
|
||||
upcoming_changes: UpcomingChange[]
|
||||
}
|
||||
|
||||
export default async function processUpcomingChanges(
|
||||
upcomingChangesYml: string,
|
||||
): Promise<Record<string, UpcomingChange[]>> {
|
||||
const upcomingChanges = (yaml.load(upcomingChangesYml) as UpcomingChangesData).upcoming_changes
|
||||
|
||||
for (const change of upcomingChanges) {
|
||||
change.date = change.date.slice(0, 10)
|
||||
change.reason = await renderContent(change.reason)
|
||||
change.description = await renderContent(change.description)
|
||||
}
|
||||
|
||||
return groupBy(upcomingChanges.reverse(), 'date')
|
||||
}
|
||||
@@ -5,15 +5,15 @@
|
||||
// in the JSON file.
|
||||
|
||||
// These paths must match the paths in src/pages/[versionId]/rest
|
||||
export const nonAutomatedRestPaths = [
|
||||
export const nonAutomatedRestPaths: readonly string[] = [
|
||||
'/rest/quickstart',
|
||||
'/rest/about-the-rest-api',
|
||||
'/rest/using-the-rest-api',
|
||||
'/rest/authentication',
|
||||
'/rest/guides',
|
||||
]
|
||||
] as const
|
||||
|
||||
// This path is used to set the page in the
|
||||
// src/rest/components/ApiVersionPicker.tsx component. That component
|
||||
// has a link to the page that describes what api versioning is.
|
||||
export const apiVersionPath = '/rest/about-the-rest-api/api-versions'
|
||||
export const apiVersionPath: string = '/rest/about-the-rest-api/api-versions'
|
||||
@@ -1,16 +0,0 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'
|
||||
|
||||
export function checkCachingHeaders(res, defaultSurrogateKey = false, minMaxAge = 60 * 60) {
|
||||
expect(res.headers['set-cookie']).toBeUndefined()
|
||||
expect(res.headers['cache-control']).toContain('public')
|
||||
const maxAgeSeconds = parseInt(res.headers['cache-control'].match(/max-age=(\d+)/)[1], 10)
|
||||
// Let's not be too specific in the tests, just as long as it's testing
|
||||
// that it's a reasonably large number of seconds.
|
||||
expect(maxAgeSeconds).toBeGreaterThanOrEqual(minMaxAge)
|
||||
// Because it doesn't have a unique URL
|
||||
expect(res.headers['surrogate-key'].split(/\s/g)[0]).toBe(
|
||||
defaultSurrogateKey ? SURROGATE_ENUMS.DEFAULT : SURROGATE_ENUMS.MANUAL,
|
||||
)
|
||||
}
|
||||
34
src/tests/helpers/caching-headers.ts
Normal file
34
src/tests/helpers/caching-headers.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import { expect } from 'vitest'
|
||||
|
||||
import { SURROGATE_ENUMS } from '@/frame/middleware/set-fastly-surrogate-key'
|
||||
|
||||
interface ResponseWithHeaders {
|
||||
headers: Record<string, string | string[] | undefined>
|
||||
}
|
||||
|
||||
export function checkCachingHeaders(
|
||||
res: ResponseWithHeaders,
|
||||
defaultSurrogateKey: boolean = false,
|
||||
minMaxAge: number = 60 * 60,
|
||||
): void {
|
||||
expect(res.headers['set-cookie']).toBeUndefined()
|
||||
expect(res.headers['cache-control']).toContain('public')
|
||||
|
||||
const cacheControlHeader = res.headers['cache-control'] as string
|
||||
const maxAgeMatch = cacheControlHeader.match(/max-age=(\d+)/)
|
||||
|
||||
if (!maxAgeMatch) {
|
||||
throw new Error('Cache-Control header does not contain max-age directive')
|
||||
}
|
||||
|
||||
const maxAgeSeconds = parseInt(maxAgeMatch[1], 10)
|
||||
// Let's not be too specific in the tests, just as long as it's testing
|
||||
// that it's a reasonably large number of seconds.
|
||||
expect(maxAgeSeconds).toBeGreaterThanOrEqual(minMaxAge)
|
||||
|
||||
// Because it doesn't have a unique URL
|
||||
const surrogateKeyHeader = res.headers['surrogate-key'] as string
|
||||
expect(surrogateKeyHeader.split(/\s/g)[0]).toBe(
|
||||
defaultSurrogateKey ? SURROGATE_ENUMS.DEFAULT : SURROGATE_ENUMS.MANUAL,
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,12 @@ import semver from 'semver'
|
||||
|
||||
// Where "release" is a release number, like `3.1` for Enterprise Server,
|
||||
// and "range" is a semver range operator with another number, like `<=3.2`.
|
||||
export default function versionSatisfiesRange(release, range) {
|
||||
export default function versionSatisfiesRange(release: string | undefined, range: string): boolean {
|
||||
// Handle undefined release
|
||||
if (!release) {
|
||||
return false
|
||||
}
|
||||
|
||||
// workaround for Enterprise Server 11.10.340 because we can't use semver to
|
||||
// compare it to 2.x like we can with 2.0+
|
||||
if (release === '11.10.340') return range.startsWith('<')
|
||||
@@ -15,5 +20,10 @@ export default function versionSatisfiesRange(release, range) {
|
||||
release = '1.0'
|
||||
}
|
||||
|
||||
return semver.satisfies(semver.coerce(release), range)
|
||||
const coercedRelease = semver.coerce(release)
|
||||
if (!coercedRelease) {
|
||||
throw new Error(`Unable to coerce release version: ${release}`)
|
||||
}
|
||||
|
||||
return semver.satisfies(coercedRelease, range)
|
||||
}
|
||||
Reference in New Issue
Block a user