mirror of
https://github.com/renovatebot/renovate.git
synced 2025-02-04 02:46:25 +00:00
test: Write test for bad pattern: .md/# (#23609)
This commit is contained in:
parent
6001f97b56
commit
8aad74f8cb
5 changed files with 91 additions and 123 deletions
4
.github/workflows/build.yml
vendored
4
.github/workflows/build.yml
vendored
|
@ -269,8 +269,8 @@ jobs:
|
||||||
- name: Lint fenced code blocks
|
- name: Lint fenced code blocks
|
||||||
run: yarn -s doc-fence-check
|
run: yarn -s doc-fence-check
|
||||||
|
|
||||||
- name: Lint website docs
|
- name: Lint documentation
|
||||||
run: yarn -s lint-website-docs
|
run: yarn -s lint-documentation
|
||||||
|
|
||||||
lint-other:
|
lint-other:
|
||||||
needs: [setup]
|
needs: [setup]
|
||||||
|
|
|
@ -396,9 +396,6 @@ export interface RenovateOptionBase {
|
||||||
| 'postUpgradeTasks'
|
| 'postUpgradeTasks'
|
||||||
| 'regexManagers';
|
| 'regexManagers';
|
||||||
|
|
||||||
// used by tests
|
|
||||||
relatedOptions?: string[];
|
|
||||||
|
|
||||||
stage?: RenovateConfigStage;
|
stage?: RenovateConfigStage;
|
||||||
|
|
||||||
experimental?: boolean;
|
experimental?: boolean;
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
"debug": "cross-env NODE_OPTIONS=--inspect-brk ts-node lib/renovate.ts",
|
"debug": "cross-env NODE_OPTIONS=--inspect-brk ts-node lib/renovate.ts",
|
||||||
"doc-fix": "run-s markdown-lint-fix prettier-fix",
|
"doc-fix": "run-s markdown-lint-fix prettier-fix",
|
||||||
"doc-fence-check": "node tools/check-fenced-code.mjs",
|
"doc-fence-check": "node tools/check-fenced-code.mjs",
|
||||||
"lint-website-docs": "jest --coverage false test/website-docs.spec.ts",
|
"lint-documentation": "jest --coverage false test/documentation.spec.ts",
|
||||||
"eslint": "eslint . --cache --cache-location .cache/eslint --report-unused-disable-directives",
|
"eslint": "eslint . --cache --cache-location .cache/eslint --report-unused-disable-directives",
|
||||||
"eslint-fix": "eslint --cache --cache-location .cache/eslint --fix . --report-unused-disable-directives",
|
"eslint-fix": "eslint --cache --cache-location .cache/eslint --fix . --report-unused-disable-directives",
|
||||||
"eslint-ci": "eslint . --cache --cache-strategy content --cache-location .cache/eslint --format gha",
|
"eslint-ci": "eslint . --cache --cache-strategy content --cache-location .cache/eslint --format gha",
|
||||||
|
|
88
test/documentation.spec.ts
Normal file
88
test/documentation.spec.ts
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
import fs from 'fs-extra';
|
||||||
|
import { glob } from 'glob';
|
||||||
|
import { getOptions } from '../lib/config/options';
|
||||||
|
import { regEx } from '../lib/util/regex';
|
||||||
|
|
||||||
|
const options = getOptions();
|
||||||
|
const markdownGlob = '{docs,lib}/**/*.md';
|
||||||
|
|
||||||
|
describe('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', () => {
|
||||||
|
describe('configuration-options', () => {
|
||||||
|
const doc = fs.readFileSync(
|
||||||
|
'docs/usage/configuration-options.md',
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
|
||||||
|
const headers = doc
|
||||||
|
.match(/\n## (.*?)\n/g)
|
||||||
|
?.map((match) => match.substring(4, match.length - 1));
|
||||||
|
|
||||||
|
const expectedOptions = options
|
||||||
|
.filter((option) => !option.globalOnly)
|
||||||
|
.filter((option) => !option.parent)
|
||||||
|
.filter((option) => !option.autogenerated)
|
||||||
|
.map((option) => option.name)
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
it('has doc headers sorted alphabetically', () => {
|
||||||
|
expect(headers).toEqual([...headers!].sort());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has headers for every required option', () => {
|
||||||
|
expect(headers).toEqual(expectedOptions);
|
||||||
|
});
|
||||||
|
|
||||||
|
const subHeaders = doc
|
||||||
|
.match(/\n### (.*?)\n/g)
|
||||||
|
?.map((match) => match.substring(5, match.length - 1));
|
||||||
|
subHeaders!.sort();
|
||||||
|
const expectedSubOptions = options
|
||||||
|
.filter((option) => option.stage !== 'global')
|
||||||
|
.filter((option) => !option.globalOnly)
|
||||||
|
.filter((option) => option.parent)
|
||||||
|
.map((option) => option.name)
|
||||||
|
.sort();
|
||||||
|
expectedSubOptions.sort();
|
||||||
|
|
||||||
|
it('has headers for every required sub-option', () => {
|
||||||
|
expect(subHeaders).toEqual(expectedSubOptions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('self-hosted-configuration', () => {
|
||||||
|
const doc = fs.readFileSync(
|
||||||
|
'docs/usage/self-hosted-configuration.md',
|
||||||
|
'utf8'
|
||||||
|
);
|
||||||
|
|
||||||
|
const headers = doc
|
||||||
|
.match(/\n## (.*?)\n/g)
|
||||||
|
?.map((match) => match.substring(4, match.length - 1));
|
||||||
|
|
||||||
|
const expectedOptions = options
|
||||||
|
.filter((option) => !!option.globalOnly)
|
||||||
|
.map((option) => option.name)
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
it('has headers sorted alphabetically', () => {
|
||||||
|
expect(headers).toEqual([...headers!].sort());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('has headers for every required option', () => {
|
||||||
|
expect(headers).toEqual(expectedOptions);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -1,117 +0,0 @@
|
||||||
/* eslint-disable @typescript-eslint/no-unnecessary-type-assertion */
|
|
||||||
import fs from 'node:fs';
|
|
||||||
import is from '@sindresorhus/is';
|
|
||||||
import { getOptions } from '../lib/config/options';
|
|
||||||
|
|
||||||
declare global {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
|
||||||
namespace jest {
|
|
||||||
type ContainsOption<T> = T extends ArrayLike<unknown> ? T[number] : unknown;
|
|
||||||
|
|
||||||
interface Matchers<R> {
|
|
||||||
/**
|
|
||||||
* only available in `test/website-docs.spec.js`
|
|
||||||
* @param arg Value which current values should contain
|
|
||||||
*/
|
|
||||||
toContainOption(arg: ContainsOption<R>): void;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const options = getOptions();
|
|
||||||
|
|
||||||
describe('website-docs', () => {
|
|
||||||
const doc = fs.readFileSync('docs/usage/configuration-options.md', 'utf8');
|
|
||||||
const selfHostDoc = fs.readFileSync(
|
|
||||||
'docs/usage/self-hosted-configuration.md',
|
|
||||||
'utf8'
|
|
||||||
);
|
|
||||||
const headers = doc
|
|
||||||
.match(/\n## (.*?)\n/g)
|
|
||||||
?.map((match) => match.substring(4, match.length - 1));
|
|
||||||
const selfHostHeaders = selfHostDoc
|
|
||||||
.match(/\n## (.*?)\n/g)
|
|
||||||
?.map((match) => match.substring(4, match.length - 1));
|
|
||||||
const expectedOptions = options
|
|
||||||
.filter((option) => !option.globalOnly)
|
|
||||||
.filter((option) => !option.parent)
|
|
||||||
.filter((option) => !option.autogenerated)
|
|
||||||
.map((option) => option.name)
|
|
||||||
.sort();
|
|
||||||
|
|
||||||
const selfHostExpectedOptions = options
|
|
||||||
.filter((option) => !!option.globalOnly)
|
|
||||||
.map((option) => option.name)
|
|
||||||
.sort();
|
|
||||||
|
|
||||||
it('has doc headers sorted alphabetically', () => {
|
|
||||||
expect(headers).toEqual([...headers!].sort());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has headers for every required option', () => {
|
|
||||||
expect(headers).toEqual(expectedOptions);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has self hosted doc headers sorted alphabetically', () => {
|
|
||||||
expect(selfHostHeaders).toEqual([...selfHostHeaders!].sort());
|
|
||||||
});
|
|
||||||
|
|
||||||
it('has headers (self hosted) for every required option', () => {
|
|
||||||
expect(selfHostHeaders).toEqual(selfHostExpectedOptions);
|
|
||||||
});
|
|
||||||
|
|
||||||
const headers3 = doc
|
|
||||||
.match(/\n### (.*?)\n/g)
|
|
||||||
?.map((match) => match.substring(5, match.length - 1));
|
|
||||||
headers3!.sort();
|
|
||||||
const expectedOptions3 = options
|
|
||||||
.filter((option) => option.stage !== 'global')
|
|
||||||
.filter((option) => !option.globalOnly)
|
|
||||||
.filter((option) => option.parent)
|
|
||||||
.map((option) => option.name)
|
|
||||||
.sort();
|
|
||||||
expectedOptions3.sort();
|
|
||||||
|
|
||||||
it('has headers for every required sub-option', () => {
|
|
||||||
expect(headers3).toEqual(expectedOptions3);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Checking relatedOptions field in options
|
|
||||||
const relatedOptionsMatrix = options
|
|
||||||
.map((option) => option.relatedOptions)
|
|
||||||
.filter(is.truthy)
|
|
||||||
.sort();
|
|
||||||
|
|
||||||
let relatedOptions = ([] as string[]).concat(...relatedOptionsMatrix!); // Converts the matrix to an 1D array
|
|
||||||
relatedOptions = [...new Set(relatedOptions)]; // Makes all options unique
|
|
||||||
|
|
||||||
/*
|
|
||||||
Matcher which checks if the argument is within the received array (or string)
|
|
||||||
on an error, it throws a custom message.
|
|
||||||
*/
|
|
||||||
expect.extend({
|
|
||||||
toContainOption<T extends string>(received: T[], argument: T) {
|
|
||||||
if (received.includes(argument)) {
|
|
||||||
return {
|
|
||||||
message: (): string =>
|
|
||||||
`Option "${argument}" should be within options`,
|
|
||||||
pass: true,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
message: (): string =>
|
|
||||||
`Option "${argument}" doesn't exist within options`,
|
|
||||||
pass: false,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
const allOptionNames = options.map((option) => option.name).sort();
|
|
||||||
|
|
||||||
// Lists through each option in the relatedOptions array to be able to locate the exact element which causes error, in case of one
|
|
||||||
it('has valid relateOptions values', () => {
|
|
||||||
relatedOptions.forEach((relOption) => {
|
|
||||||
expect(allOptionNames).toContainOption(relOption);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in a new issue