mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-25 15:01:49 +00:00
5c4f65c86b
A follow-up missed from 982a0219bf.
277 lines
9.1 KiB
TypeScript
277 lines
9.1 KiB
TypeScript
import fs from 'fs-extra';
|
|
import { glob } from 'glob';
|
|
import { getOptions } from '../../lib/config/options/index.ts';
|
|
import { packageCacheNamespaces } from '../../lib/util/cache/package/namespaces.ts';
|
|
import { regEx } from '../../lib/util/regex.ts';
|
|
import { templateHelperNames } from '../../lib/util/template/index.ts';
|
|
|
|
const options = getOptions();
|
|
const markdownGlob = '{docs,lib}/**/*.md';
|
|
|
|
describe('docs/documentation', () => {
|
|
it('has no invalid links', async () => {
|
|
const markdownFiles = await glob(markdownGlob);
|
|
|
|
await Promise.all(
|
|
markdownFiles.map(async (markdownFile) => {
|
|
const markdownText = await fs.readFile(markdownFile, 'utf8');
|
|
expect(markdownText).not.toMatch(regEx(/\.md\/#/));
|
|
}),
|
|
);
|
|
});
|
|
|
|
describe('website-documentation', () => {
|
|
async function getConfigOptionSubHeaders(
|
|
file: string,
|
|
configOption: string,
|
|
): Promise<string[]> {
|
|
const subHeadings = [];
|
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
|
const reg = regEx(`##\\s${configOption}[\\s\\S]+?\n##\\s`);
|
|
const match = reg.exec(content);
|
|
const subHeadersMatch = match?.[0]?.matchAll(
|
|
/\n###\s(?<child>[\w.]+)\n/g,
|
|
);
|
|
if (subHeadersMatch) {
|
|
for (const subHeaderStr of subHeadersMatch) {
|
|
if (subHeaderStr?.groups?.child) {
|
|
subHeadings.push(subHeaderStr.groups.child);
|
|
}
|
|
}
|
|
}
|
|
return subHeadings;
|
|
}
|
|
|
|
describe('docs/usage/configuration-options.md', () => {
|
|
async function getConfigHeaders(file: string): Promise<string[]> {
|
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
|
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
|
return matches
|
|
.map((match) =>
|
|
match.substring(4, match.length - 1).replace(/^`|`$/g, ''),
|
|
)
|
|
.filter((header) => header !== 'managerFilePatterns');
|
|
}
|
|
|
|
function getRequiredConfigOptions(): string[] {
|
|
return (
|
|
options
|
|
.filter((option) => !option.globalOnly)
|
|
// Only include top-level options, which have no parents (implicit root) or explicitly define the
|
|
// root ('.') as their parent.
|
|
.filter(
|
|
(option) =>
|
|
!option.parents ||
|
|
option.parents.length === 0 ||
|
|
option.parents.includes('.'),
|
|
)
|
|
.filter((option) => !option.autogenerated)
|
|
.map((option) => option.name)
|
|
.sort()
|
|
);
|
|
}
|
|
|
|
it('has doc headers sorted alphabetically', async () => {
|
|
expect(await getConfigHeaders('configuration-options.md')).toEqual(
|
|
(await getConfigHeaders('configuration-options.md')).sort(),
|
|
);
|
|
});
|
|
|
|
it('has headers for every required option', async () => {
|
|
expect(await getConfigHeaders('configuration-options.md')).toEqual(
|
|
getRequiredConfigOptions(),
|
|
);
|
|
});
|
|
|
|
async function getConfigSubHeaders(file: string): Promise<string[]> {
|
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
|
const matches = content.match(/\n### (.*?)\n/g) ?? [];
|
|
return matches
|
|
.map((match) =>
|
|
match.substring(5, match.length - 1).replace(/^`|`$/g, ''),
|
|
)
|
|
.sort();
|
|
}
|
|
|
|
function getRequiredConfigSubOptions(): string[] {
|
|
return (
|
|
options
|
|
.filter((option) => option.stage !== 'global')
|
|
.filter((option) => !option.globalOnly)
|
|
// Only include true sub-options: options which have parents but none of those are explicitly the root ('.').
|
|
.filter(
|
|
(option) =>
|
|
option.parents &&
|
|
option.parents.length > 0 &&
|
|
!option.parents.includes('.'),
|
|
)
|
|
.filter((option) => option.name !== 'managerFilePatterns')
|
|
.flatMap((option) =>
|
|
(option.parents ?? [])
|
|
.filter((parent) => parent !== '.')
|
|
.map((parent) => `${parent}.${option.name}`),
|
|
)
|
|
.sort()
|
|
);
|
|
}
|
|
|
|
function getParentNames(): Set<string> {
|
|
const childrens = options
|
|
.filter((option) => option.stage !== 'global')
|
|
.filter((option) => !option.globalOnly)
|
|
.filter((option) => option.parents);
|
|
|
|
const parentNames = new Set<string>();
|
|
for (const children of childrens) {
|
|
const parents = children.parents ?? [];
|
|
for (const parent of parents) {
|
|
parentNames.add(parent);
|
|
}
|
|
}
|
|
|
|
return parentNames;
|
|
}
|
|
|
|
it('has headers for every required sub-option', async () => {
|
|
expect(await getConfigSubHeaders('configuration-options.md')).toEqual(
|
|
getRequiredConfigSubOptions(),
|
|
);
|
|
});
|
|
|
|
it.each([...getParentNames()])(
|
|
'%s has sub-headers sorted alphabetically',
|
|
async (parentName: string) => {
|
|
expect(
|
|
await getConfigOptionSubHeaders(
|
|
'configuration-options.md',
|
|
parentName,
|
|
),
|
|
).toEqual(
|
|
(
|
|
await getConfigOptionSubHeaders(
|
|
'configuration-options.md',
|
|
parentName,
|
|
)
|
|
).sort(),
|
|
);
|
|
},
|
|
);
|
|
});
|
|
|
|
describe('docs/usage/self-hosted-configuration.md', () => {
|
|
async function getSelfHostedHeaders(file: string): Promise<string[]> {
|
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
|
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
|
return matches.map((match) =>
|
|
match.substring(4, match.length - 1).replace(/^`|`$/g, ''),
|
|
);
|
|
}
|
|
|
|
function getRequiredSelfHostedOptions(): string[] {
|
|
return options
|
|
.filter((option) => option.globalOnly)
|
|
.map((option) => option.name)
|
|
.sort();
|
|
}
|
|
|
|
it('has headers sorted alphabetically', async () => {
|
|
expect(
|
|
await getSelfHostedHeaders('self-hosted-configuration.md'),
|
|
).toEqual(
|
|
(await getSelfHostedHeaders('self-hosted-configuration.md')).sort(),
|
|
);
|
|
});
|
|
|
|
it('has headers for every required option', async () => {
|
|
expect(
|
|
await getSelfHostedHeaders('self-hosted-configuration.md'),
|
|
).toEqual(getRequiredSelfHostedOptions());
|
|
});
|
|
|
|
function getCacheNamespacesFromDocs(file: string): string[] {
|
|
const content = fs.readFileSync(`docs/usage/${file}`, 'utf8');
|
|
const beginMarker = '<!-- cache-namespaces-begin -->';
|
|
const endMarker = '<!-- cache-namespaces-end -->';
|
|
|
|
const beginIndex = content.indexOf(beginMarker);
|
|
const endIndex = content.indexOf(endMarker);
|
|
|
|
if (beginIndex === -1 || endIndex === -1) {
|
|
return [];
|
|
}
|
|
|
|
const cacheNamespacesSection = content.substring(
|
|
beginIndex + beginMarker.length,
|
|
endIndex,
|
|
);
|
|
const matches = cacheNamespacesSection.match(/- `([^`]+)`/g) ?? [];
|
|
|
|
return matches
|
|
.map((match) => match.substring(3, match.length - 1))
|
|
.sort();
|
|
}
|
|
|
|
function getExpectedCacheNamespaces(): string[] {
|
|
return packageCacheNamespaces
|
|
.filter((namespace) => namespace !== '_test-namespace')
|
|
.sort();
|
|
}
|
|
|
|
it('has complete cache namespaces list', () => {
|
|
expect(
|
|
getCacheNamespacesFromDocs('self-hosted-configuration.md'),
|
|
).toEqual(getExpectedCacheNamespaces());
|
|
});
|
|
});
|
|
|
|
describe('docs/usage/self-hosted-experimental.md', () => {
|
|
async function getSelfHostedExperimentalConfigHeaders(
|
|
file: string,
|
|
): Promise<string[]> {
|
|
const content = await fs.readFile(`docs/usage/${file}`, 'utf8');
|
|
const matches = content.match(/\n## (.*?)\n/g) ?? [];
|
|
return matches.map((match) => match.substring(4, match.length - 1));
|
|
}
|
|
|
|
it('has headers sorted alphabetically', async () => {
|
|
expect(
|
|
await getSelfHostedExperimentalConfigHeaders(
|
|
'self-hosted-experimental.md',
|
|
),
|
|
).toEqual(
|
|
(
|
|
await getSelfHostedExperimentalConfigHeaders(
|
|
'self-hosted-experimental.md',
|
|
)
|
|
).sort(),
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('docs/usage/templates.md', async () => {
|
|
async function getAdditionalHandlebarsHelpersHeaders(): Promise<
|
|
string[]
|
|
> {
|
|
const content = await fs.readFile(`docs/usage/templates.md`, 'utf8');
|
|
const matches = content.match(/\n### (.*?)\n/g) ?? [];
|
|
return matches.map((match) => match.substring(5, match.length - 1));
|
|
}
|
|
|
|
const additionalHandlebarsHelpers =
|
|
await getAdditionalHandlebarsHelpersHeaders();
|
|
|
|
it('has headers sorted alphabetically', () => {
|
|
expect(additionalHandlebarsHelpers).toEqual(
|
|
additionalHandlebarsHelpers.toSorted(),
|
|
);
|
|
});
|
|
|
|
it('documents all added Handlebars helpers', () => {
|
|
expect(additionalHandlebarsHelpers).toEqual(
|
|
templateHelperNames.toSorted(),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|