From e7ec93b0b9cb2242b0717cc81b37bdddad063c3a Mon Sep 17 00:00:00 2001 From: Teik Jun Date: Wed, 29 Jul 2020 22:23:11 +0800 Subject: [PATCH] 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 --- .../pluginOptionSchema.test.ts.snap | 4 +- .../src/__tests__/pluginOptionSchema.test.ts | 47 +++++--- .../src/pluginOptionSchema.ts | 18 +++- .../src/__tests__/pluginOptionSchema.test.ts | 17 ++- .../configValidation.test.ts.snap | 19 ++-- .../server/__tests__/configValidation.test.ts | 102 +++++++++++------- .../docusaurus/src/server/configValidation.ts | 6 +- 7 files changed, 144 insertions(+), 69 deletions(-) diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap index 13f5cee4b5..8f7646bf8c 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/__snapshots__/pluginOptionSchema.test.ts.snap @@ -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]`; diff --git a/packages/docusaurus-plugin-content-blog/src/__tests__/pluginOptionSchema.test.ts b/packages/docusaurus-plugin-content-blog/src/__tests__/pluginOptionSchema.test.ts index a7889db6d1..3f85ded47c 100644 --- a/packages/docusaurus-plugin-content-blog/src/__tests__/pluginOptionSchema.test.ts +++ b/packages/docusaurus-plugin-content-blog/src/__tests__/pluginOptionSchema.test.ts @@ -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'}, }); diff --git a/packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts b/packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts index 6fd4c3581c..ae20e8ad38 100644 --- a/packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts +++ b/packages/docusaurus-plugin-content-blog/src/pluginOptionSchema.ts @@ -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), diff --git a/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts b/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts index b9cd0e41a9..1a05be086e 100644 --- a/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts +++ b/packages/docusaurus-plugin-content-docs/src/__tests__/pluginOptionSchema.test.ts @@ -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); }); diff --git a/packages/docusaurus/src/server/__tests__/__snapshots__/configValidation.test.ts.snap b/packages/docusaurus/src/server/__tests__/__snapshots__/configValidation.test.ts.snap index 8278e1c151..d43466c246 100644 --- a/packages/docusaurus/src/server/__tests__/__snapshots__/configValidation.test.ts.snap +++ b/packages/docusaurus/src/server/__tests__/__snapshots__/configValidation.test.ts.snap @@ -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 " `; diff --git a/packages/docusaurus/src/server/__tests__/configValidation.test.ts b/packages/docusaurus/src/server/__tests__/configValidation.test.ts index 87ec726913..23bf04fb74 100644 --- a/packages/docusaurus/src/server/__tests__/configValidation.test.ts +++ b/packages/docusaurus/src/server/__tests__/configValidation.test.ts @@ -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: {}, diff --git a/packages/docusaurus/src/server/configValidation.ts b/packages/docusaurus/src/server/configValidation.ts index ffc8392949..aae824ccf0 100644 --- a/packages/docusaurus/src/server/configValidation.ts +++ b/packages/docusaurus/src/server/configValidation.ts @@ -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(