Skip to content

Commit

Permalink
test(v2): add tests for config validation (#3142)
Browse files Browse the repository at this point in the history
* test(v2): add tests for correctly defined fields

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

* test(v2): modify tests and comments
  • Loading branch information
teikjun authored Jul 29, 2020
1 parent ee2d1b4 commit e7ec93b
Show file tree
Hide file tree
Showing 7 changed files with 143 additions and 68 deletions.
Original file line number Diff line number Diff line change
@@ -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]`;
Original file line number Diff line number Diff line change
Expand Up @@ -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({
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',
});
};
const {value} = PluginOptionSchema.validate(userOptions);
expect(value).toEqual({
...DEFAULT_OPTIONS,
postsPerPage: 5,
include: ['api/*', 'docs/*'],
routeBasePath: 'not_blog',
path: 'not_blog',
...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,
Expand All @@ -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',
Expand All @@ -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'},
});
Expand Down
18 changes: 14 additions & 4 deletions packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({});
Expand All @@ -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);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -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
"
`;
102 changes: 66 additions & 36 deletions packages/docusaurus/src/server/__tests__/configValidation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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: {},
Expand Down
6 changes: 3 additions & 3 deletions packages/docusaurus/src/server/configValidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -79,7 +79,7 @@ const ConfigSchema = Joi.object({
type: Joi.string().required(),
}).unknown(),
),
tagline: Joi.string(),
tagline: Joi.string().allow(''),
});

export function validateConfig(
Expand Down

0 comments on commit e7ec93b

Please sign in to comment.