/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ import escapeStringRegexp from 'escape-string-regexp'; import {validateBlogPostFrontMatter} from '../frontMatter'; import type {BlogPostFrontMatter} from '@docusaurus/plugin-content-blog'; // TODO this abstraction reduce verbosity but it makes it harder to debug // It would be preferable to just expose helper methods function testField(params: { fieldName: keyof BlogPostFrontMatter; validFrontMatters: BlogPostFrontMatter[]; convertibleFrontMatter?: [ ConvertibleFrontMatter: {[key: string]: unknown}, ConvertedFrontMatter: BlogPostFrontMatter, ][]; invalidFrontMatters?: [ InvalidFrontMatter: {[key: string]: unknown}, ErrorMessage: string, ][]; }) { describe(`"${params.fieldName}" field`, () => { it('accept valid values', () => { params.validFrontMatters.forEach((frontMatter) => { expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter); }); }); it('convert valid values', () => { params.convertibleFrontMatter?.forEach( ([convertibleFrontMatter, convertedFrontMatter]) => { expect(validateBlogPostFrontMatter(convertibleFrontMatter)).toEqual( convertedFrontMatter, ); }, ); }); it('throw error for values', () => { params.invalidFrontMatters?.forEach(([frontMatter, message]) => { try { validateBlogPostFrontMatter(frontMatter); // eslint-disable-next-line jest/no-jasmine-globals fail( new Error( `Blog front matter is expected to be rejected, but was accepted successfully:\n ${JSON.stringify( frontMatter, null, 2, )}`, ), ); } catch (err) { // eslint-disable-next-line jest/no-conditional-expect expect((err as Error).message).toMatch( new RegExp(escapeStringRegexp(message)), ); } }); }); }); } describe('validateBlogPostFrontMatter', () => { it('accept empty object', () => { const frontMatter = {}; expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter); }); it('accept unknown field', () => { const frontMatter = {abc: '1'}; expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter); }); }); describe('validateBlogPostFrontMatter description', () => { testField({ fieldName: 'description', validFrontMatters: [ // See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398 {description: ''}, {description: 'description'}, ], }); }); describe('validateBlogPostFrontMatter title', () => { testField({ fieldName: 'title', validFrontMatters: [ // See https://github.com/facebook/docusaurus/issues/4591#issuecomment-822372398 {title: ''}, {title: 'title'}, ], }); }); describe('validateBlogPostFrontMatter id', () => { testField({ fieldName: 'id', validFrontMatters: [{id: '123'}, {id: 'id'}], invalidFrontMatters: [[{id: ''}, 'not allowed to be empty']], }); }); describe('validateBlogPostFrontMatter handles legacy/new author front matter', () => { it('allow legacy author front matter', () => { const frontMatter: BlogPostFrontMatter = { author: 'Sebastien', author_url: 'https://sebastienlorber.com', author_title: 'maintainer', author_image_url: 'https://github.com/slorber.png', }; expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter); }); it('allow new authors front matter', () => { const frontMatter: BlogPostFrontMatter = { authors: [ 'slorber', {name: 'Yangshun'}, {key: 'JMarcey', title: 'creator', random: '42'}, ], }; expect(validateBlogPostFrontMatter(frontMatter)).toEqual(frontMatter); }); }); describe('validateBlogPostFrontMatter author', () => { testField({ fieldName: 'author', validFrontMatters: [{author: '123'}, {author: 'author'}], invalidFrontMatters: [[{author: ''}, 'not allowed to be empty']], }); }); describe('validateBlogPostFrontMatter author_title', () => { testField({ fieldName: 'author_title', validFrontMatters: [ {author: '123', author_title: '123'}, {author: '123', author_title: 'author_title'}, ], invalidFrontMatters: [[{author_title: ''}, 'not allowed to be empty']], }); testField({ fieldName: 'authorTitle', validFrontMatters: [{authorTitle: '123'}, {authorTitle: 'authorTitle'}], invalidFrontMatters: [[{authorTitle: ''}, 'not allowed to be empty']], }); }); describe('validateBlogPostFrontMatter author_url', () => { testField({ fieldName: 'author_url', validFrontMatters: [ {author_url: 'https://docusaurus.io'}, {author_url: '../../relative'}, {author_url: '/absolute'}, ], invalidFrontMatters: [ [ {author_url: ''}, '"author_url" does not look like a valid url (value=\'\')', ], ], }); testField({ fieldName: 'authorURL', validFrontMatters: [ {authorURL: 'https://docusaurus.io'}, {authorURL: '../../relative'}, {authorURL: '/absolute'}, ], invalidFrontMatters: [ [ {authorURL: ''}, '"authorURL" does not look like a valid url (value=\'\')', ], ], }); }); describe('validateBlogPostFrontMatter author_image_url', () => { testField({ fieldName: 'author_image_url', validFrontMatters: [ {author_image_url: 'https://docusaurus.io/asset/image.png'}, {author_image_url: '../../relative'}, {author_image_url: '/absolute'}, ], invalidFrontMatters: [ [ {author_image_url: ''}, '"author_image_url" does not look like a valid url (value=\'\')', ], ], }); testField({ fieldName: 'authorImageURL', validFrontMatters: [ {authorImageURL: 'https://docusaurus.io/asset/image.png'}, {authorImageURL: '../../relative'}, {authorImageURL: '/absolute'}, ], invalidFrontMatters: [ [ {authorImageURL: ''}, '"authorImageURL" does not look like a valid url (value=\'\')', ], ], }); }); describe('validateBlogPostFrontMatter authors', () => { testField({ fieldName: 'author', validFrontMatters: [ {authors: []}, {authors: 'authorKey'}, {authors: ['authorKey1', 'authorKey2']}, { authors: { name: 'Author Name', imageURL: '/absolute', }, }, { authors: { key: 'authorKey', title: 'Author title', }, }, { authors: [ 'authorKey1', {key: 'authorKey3'}, 'authorKey3', {name: 'Author Name 4'}, {key: 'authorKey5'}, ], }, ], invalidFrontMatters: [ [{authors: ''}, '"authors" is not allowed to be empty'], [ {authors: [undefined]}, '"authors[0]" does not look like a valid blog post author. Please use an author key or an author object (with a key and/or name).', ], [ {authors: [null]}, '"authors[0]" does not look like a valid blog post author. Please use an author key or an author object (with a key and/or name).', ], [ {authors: [{}]}, '"authors[0]" does not look like a valid blog post author. Please use an author key or an author object (with a key and/or name).', ], ], }); }); describe('validateBlogPostFrontMatter slug', () => { testField({ fieldName: 'slug', validFrontMatters: [ {slug: 'blog/'}, {slug: '/blog'}, {slug: '/blog/'}, {slug: './blog'}, {slug: '../blog'}, {slug: '../../blog'}, {slug: '/api/plugins/@docusaurus/plugin-debug'}, {slug: '@site/api/asset/image.png'}, ], invalidFrontMatters: [[{slug: ''}, 'not allowed to be empty']], }); }); describe('validateBlogPostFrontMatter image', () => { testField({ fieldName: 'image', validFrontMatters: [ {image: 'https://docusaurus.io/image.png'}, {image: 'blog/'}, {image: '/blog'}, {image: '/blog/'}, {image: './blog'}, {image: '../blog'}, {image: '../../blog'}, {image: '/api/plugins/@docusaurus/plugin-debug'}, {image: '@site/api/asset/image.png'}, ], invalidFrontMatters: [ [{image: ''}, '"image" does not look like a valid url (value=\'\')'], ], }); }); describe('validateBlogPostFrontMatter tags', () => { testField({ fieldName: 'tags', validFrontMatters: [ {tags: []}, {tags: ['hello']}, {tags: ['hello', 'world']}, {tags: ['hello', 'world']}, {tags: ['hello', {label: 'tagLabel', permalink: '/tagPermalink'}]}, ], invalidFrontMatters: [ [ {tags: ''}, '"tags" does not look like a valid front matter Yaml array.', ], [{tags: ['']}, 'not allowed to be empty'], ], // See https://github.com/facebook/docusaurus/issues/4642 convertibleFrontMatter: [ [{tags: [42]}, {tags: ['42']}], [ {tags: [{label: 84, permalink: '/tagPermalink'}]}, {tags: [{label: '84', permalink: '/tagPermalink'}]}, ], ], }); }); describe('validateBlogPostFrontMatter keywords', () => { testField({ fieldName: 'keywords', validFrontMatters: [ {keywords: ['hello']}, {keywords: ['hello', 'world']}, {keywords: ['hello', 'world']}, {keywords: ['hello']}, ], invalidFrontMatters: [ [{keywords: ''}, 'must be an array'], [{keywords: ['']}, 'not allowed to be empty'], [{keywords: []}, 'does not contain 1 required value(s)'], ], }); }); describe('validateBlogPostFrontMatter draft', () => { testField({ fieldName: 'draft', validFrontMatters: [{draft: true}, {draft: false}], convertibleFrontMatter: [ [{draft: 'true'}, {draft: true}], [{draft: 'false'}, {draft: false}], ], invalidFrontMatters: [ [{draft: 'yes'}, 'must be a boolean'], [{draft: 'no'}, 'must be a boolean'], ], }); }); describe('validateBlogPostFrontMatter hide_table_of_contents', () => { testField({ fieldName: 'hide_table_of_contents', validFrontMatters: [ {hide_table_of_contents: true}, {hide_table_of_contents: false}, ], convertibleFrontMatter: [ [{hide_table_of_contents: 'true'}, {hide_table_of_contents: true}], [{hide_table_of_contents: 'false'}, {hide_table_of_contents: false}], ], invalidFrontMatters: [ [{hide_table_of_contents: 'yes'}, 'must be a boolean'], [{hide_table_of_contents: 'no'}, 'must be a boolean'], ], }); }); describe('validateBlogPostFrontMatter date', () => { testField({ fieldName: 'date', validFrontMatters: [ {date: new Date('2020-01-01')}, {date: '2020-01-01'}, {date: '2020'}, ], invalidFrontMatters: [ [{date: 'abc'}, 'must be a valid date'], [{date: ''}, 'must be a valid date'], ], }); });