test(v2): add tests for config validation (#3142)

* test(v2): add tests for correctly defined fields

* test(v2): add test for remarkPlugins and rehypePlugins validation

* test(v2): modify tests and comments
This commit is contained in:
Teik Jun 2020-07-29 22:23:11 +08:00 committed by GitHub
parent ee2d1b42f6
commit e7ec93b0b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 144 additions and 69 deletions

View file

@ -1,5 +1,5 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`throw Error in case of invalid feedtype 1`] = `[ValidationError: "feedOptions.type" does not match any of the allowed types]`;
exports[`should throw Error in case of invalid feedtype 1`] = `[ValidationError: "feedOptions.type" does not match any of the allowed types]`;
exports[`throw Error in case of invalid options 1`] = `[ValidationError: "postsPerPage" must be larger than or equal to 1]`;
exports[`should throw Error in case of invalid options 1`] = `[ValidationError: "postsPerPage" must be larger than or equal to 1]`;

View file

@ -7,28 +7,47 @@
import {PluginOptionSchema, DEFAULT_OPTIONS} from '../pluginOptionSchema';
test('normalize options', () => {
// the type of remark/rehype plugins is function
const remarkRehypePluginStub = () => {};
test('should normalize options', () => {
const {value} = PluginOptionSchema.validate({});
expect(value).toEqual(DEFAULT_OPTIONS);
});
test('validate options', () => {
const {value} = PluginOptionSchema.validate({
path: 'not_blog',
postsPerPage: 5,
include: ['api/*', 'docs/*'],
routeBasePath: 'not_blog',
});
expect(value).toEqual({
test('should accept correctly defined user options', () => {
const userOptions = {
...DEFAULT_OPTIONS,
feedOptions: {type: 'rss', title: 'myTitle'},
path: 'not_blog',
routeBasePath: '',
postsPerPage: 5,
include: ['api/*', 'docs/*'],
routeBasePath: 'not_blog',
path: 'not_blog',
};
const {value} = PluginOptionSchema.validate(userOptions);
expect(value).toEqual({
...userOptions,
feedOptions: {type: ['rss'], title: 'myTitle'},
});
});
test('throw Error in case of invalid options', () => {
test('should accept valid user options', async () => {
const userOptions = {
...DEFAULT_OPTIONS,
routebasePath: '',
beforeDefaultRemarkPlugins: [],
beforeDefaultRehypePlugins: [remarkRehypePluginStub],
remarkPlugins: [remarkRehypePluginStub, {option1: '42'}],
rehypePlugins: [
remarkRehypePluginStub,
[remarkRehypePluginStub, {option1: '42'}],
],
};
const {value} = await PluginOptionSchema.validate(userOptions);
expect(value).toEqual(userOptions);
});
test('should throw Error in case of invalid options', () => {
const {error} = PluginOptionSchema.validate({
path: 'not_blog',
postsPerPage: -1,
@ -39,7 +58,7 @@ test('throw Error in case of invalid options', () => {
expect(error).toMatchSnapshot();
});
test('throw Error in case of invalid feedtype', () => {
test('should throw Error in case of invalid feedtype', () => {
const {error} = PluginOptionSchema.validate({
feedOptions: {
type: 'none',
@ -49,7 +68,7 @@ test('throw Error in case of invalid feedtype', () => {
expect(error).toMatchSnapshot();
});
test('convert all feed type to array with other feed type', () => {
test('should convert all feed type to array with other feed type', () => {
const {value} = PluginOptionSchema.validate({
feedOptions: {type: 'all'},
});

View file

@ -63,10 +63,20 @@ export const PluginOptionSchema = Joi.object({
truncateMarker: Joi.object().default(DEFAULT_OPTIONS.truncateMarker),
admonitions: Joi.object().default(DEFAULT_OPTIONS.admonitions),
beforeDefaultRemarkPlugins: Joi.array()
.items(Joi.object())
.items(
Joi.array()
.items(Joi.function().required(), Joi.object().required())
.length(2),
Joi.function(),
)
.default(DEFAULT_OPTIONS.beforeDefaultRemarkPlugins),
beforeDefaultRehypePlugins: Joi.array()
.items(Joi.object())
.items(
Joi.array()
.items(Joi.function().required(), Joi.object().required())
.length(2),
Joi.function(),
)
.default(DEFAULT_OPTIONS.beforeDefaultRehypePlugins),
feedOptions: Joi.object({
type: Joi.alternatives().conditional(
@ -75,8 +85,8 @@ export const PluginOptionSchema = Joi.object({
then: Joi.custom((val) => (val === 'all' ? ['rss', 'atom'] : [val])),
},
),
title: Joi.string(),
description: Joi.string(),
title: Joi.string().allow(''),
description: Joi.string().allow(''),
copyright: Joi.string(),
language: Joi.string(),
}).default(DEFAULT_OPTIONS.feedOptions),

View file

@ -18,6 +18,9 @@ export default function normalizePluginOptions(options) {
}
}
// the type of remark/rehype plugins is function
const remarkRehypePluginStub = () => {};
describe('normalizeDocsPluginOptions', () => {
test('should return default options for undefined user options', async () => {
const {value} = await PluginOptionSchema.validate({});
@ -34,14 +37,26 @@ describe('normalizeDocsPluginOptions', () => {
docLayoutComponent: '@theme/DocPage',
docItemComponent: '@theme/DocItem',
remarkPlugins: [],
rehypePlugins: [],
rehypePlugins: [remarkRehypePluginStub],
showLastUpdateTime: true,
showLastUpdateAuthor: true,
admonitions: {},
excludeNextVersionDocs: true,
disableVersioning: true,
};
const {value} = await PluginOptionSchema.validate(userOptions);
expect(value).toEqual(userOptions);
});
test('should accept correctly defined remark and rehype plugin options', async () => {
const userOptions = {
...DEFAULT_OPTIONS,
remarkPlugins: [remarkRehypePluginStub, {option1: '42'}],
rehypePlugins: [
remarkRehypePluginStub,
[remarkRehypePluginStub, {option1: '42'}],
],
};
const {value} = await PluginOptionSchema.validate(userOptions);
expect(value).toEqual(userOptions);
});

View file

@ -1,50 +1,51 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`validateConfig throw error for baseUrl without trailing \`/\` 1`] = `
exports[`normalizeConfig should throw error for baseUrl without trailing \`/\` 1`] = `
"\\"baseUrl\\" must be a string with a trailing \`/\`
"
`;
exports[`validateConfig throw error for required fields 1`] = `
exports[`normalizeConfig should throw error for required fields 1`] = `
"\\"baseUrl\\" is required
\\"favicon\\" is required
\\"title\\" is required
\\"url\\" is required
\\"themes\\" must be an array
\\"presets\\" must be an array
\\"scripts\\" must be an array
\\"stylesheets\\" must be an array
These field(s) [\\"invalid\\",\\"preset\\",] are not recognized in docusaurus.config.js.
These field(s) [\\"invalidField\\",] are not recognized in docusaurus.config.js.
If you still want these fields to be in your configuration, put them in the 'customFields' attribute.
See https://v2.docusaurus.io/docs/docusaurus.config.js/#customfields"
`;
exports[`validateConfig throw error for unknown field 1`] = `
exports[`normalizeConfig should throw error for unknown field 1`] = `
"These field(s) [\\"invalid\\",] are not recognized in docusaurus.config.js.
If you still want these fields to be in your configuration, put them in the 'customFields' attribute.
See https://v2.docusaurus.io/docs/docusaurus.config.js/#customfields"
`;
exports[`validateConfig throw error if css doesn't have href 1`] = `
exports[`normalizeConfig should throw error if css doesn't have href 1`] = `
"\\"stylesheets[1]\\" does not match any of the allowed types
"
`;
exports[`validateConfig throw error if plugins is not array 1`] = `
exports[`normalizeConfig should throw error if plugins is not array 1`] = `
"\\"plugins\\" must be an array
"
`;
exports[`validateConfig throw error if presets is not array 1`] = `
exports[`normalizeConfig should throw error if presets is not array 1`] = `
"\\"presets\\" must be an array
"
`;
exports[`validateConfig throw error if scripts doesn't have src 1`] = `
exports[`normalizeConfig should throw error if scripts doesn't have src 1`] = `
"\\"scripts[1]\\" does not match any of the allowed types
"
`;
exports[`validateConfig throw error if themes is not array 1`] = `
exports[`normalizeConfig should throw error if themes is not array 1`] = `
"\\"themes\\" must be an array
"
`;

View file

@ -15,94 +15,124 @@ const baseConfig = {
url: 'https://mysite.com',
};
const testConfig = (config) => validateConfig({...baseConfig, ...config});
const normalizeConfig = (config) => validateConfig({...baseConfig, ...config});
describe('validateConfig', () => {
test('normalize config', () => {
const value = testConfig({});
describe('normalizeConfig', () => {
test('should normalize empty config', () => {
const value = normalizeConfig({});
expect(value).toEqual({
...DEFAULT_CONFIG,
...baseConfig,
});
});
test('throw error for unknown field', () => {
test('should accept correctly defined config options', () => {
const userConfig = {
...DEFAULT_CONFIG,
...baseConfig,
tagline: 'my awesome site',
organizationName: 'facebook',
projectName: 'docusaurus',
githubHost: 'github.com',
customFields: {
myCustomField: '42',
},
scripts: [
{
src: `/analytics.js`,
async: true,
defer: true,
},
],
stylesheets: [
{
href: '/katex/katex.min.css',
type: 'text/css',
crossorigin: 'anonymous',
},
],
};
const normalizedConfig = normalizeConfig(userConfig);
expect(normalizedConfig).toEqual(userConfig);
});
test('should accept custom field in config', () => {
const value = normalizeConfig({
customFields: {
author: 'anshul',
},
});
expect(value).toEqual({
...DEFAULT_CONFIG,
...baseConfig,
customFields: {
author: 'anshul',
},
});
});
test('should throw error for unknown field', () => {
expect(() => {
testConfig({
normalizeConfig({
invalid: true,
});
}).toThrowErrorMatchingSnapshot();
});
test('throw error for baseUrl without trailing `/`', () => {
test('should throw error for baseUrl without trailing `/`', () => {
expect(() => {
testConfig({
normalizeConfig({
baseUrl: 'noslash',
});
}).toThrowErrorMatchingSnapshot();
});
test('throw error if plugins is not array', () => {
test('should throw error if plugins is not array', () => {
expect(() => {
testConfig({
normalizeConfig({
plugins: {},
});
}).toThrowErrorMatchingSnapshot();
});
test('throw error if themes is not array', () => {
test('should throw error if themes is not array', () => {
expect(() => {
testConfig({
normalizeConfig({
themes: {},
});
}).toThrowErrorMatchingSnapshot();
});
test('throw error if presets is not array', () => {
test('should throw error if presets is not array', () => {
expect(() => {
testConfig({
normalizeConfig({
presets: {},
});
}).toThrowErrorMatchingSnapshot();
});
test("throw error if scripts doesn't have src", () => {
test("should throw error if scripts doesn't have src", () => {
expect(() => {
testConfig({
normalizeConfig({
scripts: ['https://some.com', {}],
});
}).toThrowErrorMatchingSnapshot();
});
test("throw error if css doesn't have href", () => {
test("should throw error if css doesn't have href", () => {
expect(() => {
testConfig({
normalizeConfig({
stylesheets: ['https://somescript.com', {type: 'text/css'}],
});
}).toThrowErrorMatchingSnapshot();
});
test('custom field in config', () => {
const value = testConfig({
customFields: {
author: 'anshul',
},
});
expect(value).toEqual({
...DEFAULT_CONFIG,
...baseConfig,
customFields: {
author: 'anshul',
},
});
});
test('throw error for required fields', () => {
test('should throw error for required fields', () => {
expect(
() =>
validateConfig(({
invalid: true,
preset: {},
invalidField: true,
presets: {},
stylesheets: {},
themes: {},
scripts: {},

View file

@ -56,8 +56,8 @@ const ConfigSchema = Joi.object({
onBrokenLinks: Joi.string()
.equal('ignore', 'log', 'error', 'throw')
.default(DEFAULT_CONFIG.onBrokenLinks),
organizationName: Joi.string(),
projectName: Joi.string(),
organizationName: Joi.string().allow(''),
projectName: Joi.string().allow(''),
customFields: Joi.object().unknown().default(DEFAULT_CONFIG.customFields),
githubHost: Joi.string(),
plugins: Joi.array().items(PluginSchema).default(DEFAULT_CONFIG.plugins),
@ -79,7 +79,7 @@ const ConfigSchema = Joi.object({
type: Joi.string().required(),
}).unknown(),
),
tagline: Joi.string(),
tagline: Joi.string().allow(''),
});
export function validateConfig(