mirror of
https://github.com/renovatebot/renovate.git
synced 2026-10-26 15:31:53 +00:00
e0ce9dabd6
This also makes sure that our URL fragments should be more consistent over the years, as duplicate names (across parent and child options) cannot clash. This also corrects links from the code to include the new fragment. Co-authored-by: Claude Sonnet 4.6 <jamie.tanna+claude-code@mend.io>
261 lines
8.5 KiB
TypeScript
261 lines
8.5 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))
|
|
.filter(
|
|
(header) =>
|
|
header !== 'managerFilePatterns' && header !== 'enabled',
|
|
);
|
|
}
|
|
|
|
function getRequiredConfigOptions(): string[] {
|
|
return options
|
|
.filter((option) => !option.globalOnly)
|
|
.filter((option) => !option.parents)
|
|
.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))
|
|
.sort();
|
|
}
|
|
|
|
function getRequiredConfigSubOptions(): string[] {
|
|
return options
|
|
.filter((option) => option.stage !== 'global')
|
|
.filter((option) => !option.globalOnly)
|
|
.filter((option) => option.parents)
|
|
.filter(
|
|
(option) =>
|
|
option.name !== 'managerFilePatterns' &&
|
|
option.name !== 'enabled',
|
|
)
|
|
.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));
|
|
}
|
|
|
|
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(),
|
|
);
|
|
});
|
|
});
|
|
});
|
|
});
|