mirror of
https://github.com/facebook/docusaurus.git
synced 2025-04-29 18:27:56 +02:00
test: improve test coverage (#6857)
This commit is contained in:
parent
edb4d00096
commit
f763ac13a9
22 changed files with 435 additions and 220 deletions
|
@ -22,6 +22,47 @@ Array [
|
||||||
]
|
]
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
exports[`translateContent should fallback when translation is incomplete 1`] = `
|
||||||
|
Object {
|
||||||
|
"blogListPaginated": Array [
|
||||||
|
Object {
|
||||||
|
"items": Array [
|
||||||
|
"hello",
|
||||||
|
],
|
||||||
|
"metadata": Object {
|
||||||
|
"blogDescription": "Someone's random blog",
|
||||||
|
"blogTitle": "My blog",
|
||||||
|
"nextPage": null,
|
||||||
|
"page": 1,
|
||||||
|
"permalink": "/",
|
||||||
|
"postsPerPage": 10,
|
||||||
|
"previousPage": null,
|
||||||
|
"totalCount": 1,
|
||||||
|
"totalPages": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"blogPosts": Array [
|
||||||
|
Object {
|
||||||
|
"id": "hello",
|
||||||
|
"metadata": Object {
|
||||||
|
"date": 2021-07-19T00:00:00.000Z,
|
||||||
|
"description": "/blog/2021/06/19/hello",
|
||||||
|
"formattedDate": "June 19, 2021",
|
||||||
|
"permalink": "/blog/2021/06/19/hello",
|
||||||
|
"source": "/blog/2021/06/19/hello",
|
||||||
|
"tags": Array [],
|
||||||
|
"title": "Hello",
|
||||||
|
"truncated": true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"blogSidebarTitle": "All my posts",
|
||||||
|
"blogTags": Object {},
|
||||||
|
"blogTagsListPath": "/tags",
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
exports[`translateContent should return translated loaded content matching snapshot 1`] = `
|
exports[`translateContent should return translated loaded content matching snapshot 1`] = `
|
||||||
Object {
|
Object {
|
||||||
"blogListPaginated": Array [
|
"blogListPaginated": Array [
|
||||||
|
|
|
@ -77,6 +77,12 @@ describe('getContentTranslationFiles', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('translateContent', () => {
|
describe('translateContent', () => {
|
||||||
|
test('should fallback when translation is incomplete', () => {
|
||||||
|
expect(
|
||||||
|
translateContent(sampleBlogContent, [{path: 'foo', content: {}}]),
|
||||||
|
).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
|
||||||
test('should not translate anything if translation files are untranslated', () => {
|
test('should not translate anything if translation files are untranslated', () => {
|
||||||
const translationFiles = getSampleTranslationFiles();
|
const translationFiles = getSampleTranslationFiles();
|
||||||
expect(translateContent(sampleBlogContent, translationFiles)).toEqual(
|
expect(translateContent(sampleBlogContent, translationFiles)).toEqual(
|
||||||
|
|
|
@ -53,9 +53,6 @@ export function translateContent(
|
||||||
content: BlogContent,
|
content: BlogContent,
|
||||||
translationFiles: TranslationFiles,
|
translationFiles: TranslationFiles,
|
||||||
): BlogContent {
|
): BlogContent {
|
||||||
if (translationFiles.length === 0) {
|
|
||||||
return content;
|
|
||||||
}
|
|
||||||
const {content: optionsTranslations} = translationFiles[0]!;
|
const {content: optionsTranslations} = translationFiles[0]!;
|
||||||
return {
|
return {
|
||||||
...content,
|
...content,
|
||||||
|
|
|
@ -21,10 +21,6 @@ function getCategoryGeneratedIndexMetadata({
|
||||||
}): CategoryGeneratedIndexMetadata {
|
}): CategoryGeneratedIndexMetadata {
|
||||||
const {sidebarName, previous, next} =
|
const {sidebarName, previous, next} =
|
||||||
sidebarsUtils.getCategoryGeneratedIndexNavigation(category.link.permalink);
|
sidebarsUtils.getCategoryGeneratedIndexNavigation(category.link.permalink);
|
||||||
if (!sidebarName) {
|
|
||||||
throw new Error('unexpected');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
title: category.link.title ?? category.label,
|
title: category.link.title ?? category.label,
|
||||||
description: category.link.description,
|
description: category.link.description,
|
||||||
|
@ -32,7 +28,7 @@ function getCategoryGeneratedIndexMetadata({
|
||||||
keywords: category.link.keywords,
|
keywords: category.link.keywords,
|
||||||
slug: category.link.slug,
|
slug: category.link.slug,
|
||||||
permalink: category.link.permalink,
|
permalink: category.link.permalink,
|
||||||
sidebar: sidebarName,
|
sidebar: sidebarName!,
|
||||||
previous: toNavigationLink(previous, docsById),
|
previous: toNavigationLink(previous, docsById),
|
||||||
next: toNavigationLink(next, docsById),
|
next: toNavigationLink(next, docsById),
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
{
|
||||||
|
"foo": "bar"
|
||||||
|
}
|
|
@ -0,0 +1,255 @@
|
||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`DefaultSidebarItemsGenerator generates complex nested sidebar 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"id": "intro",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"id": "tutorial1",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "tutorial2",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "Tutorials",
|
||||||
|
"link": Object {
|
||||||
|
"id": "tutorials-index",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"collapsed": false,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"customProps": Object {
|
||||||
|
"description": "foo",
|
||||||
|
},
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"className": "foo",
|
||||||
|
"id": "guide1",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"id": "nested-guide",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "SubGuides (metadata file label)",
|
||||||
|
"link": Object {
|
||||||
|
"description": "subguides-description",
|
||||||
|
"slug": "subguides-generated-index-slug",
|
||||||
|
"title": "subguides-title",
|
||||||
|
"type": "generated-index",
|
||||||
|
},
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "guide2",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "Guides",
|
||||||
|
"link": Object {
|
||||||
|
"id": "guides-index",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "end",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`DefaultSidebarItemsGenerator generates simple flat sidebar 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"id": "doc3",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "doc4",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "doc1",
|
||||||
|
"label": "doc1 sidebar label",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "doc2",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "doc5",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`DefaultSidebarItemsGenerator generates subfolder sidebar 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"id": "doc8",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "doc7",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "subsubsubfolder3 (_category_.json label)",
|
||||||
|
"link": Object {
|
||||||
|
"id": "doc1",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"className": "bar",
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"id": "doc6",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "subsubsubfolder2 (_category_.yml label)",
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "doc1",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "doc4",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"id": "doc5",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "subsubsubfolder",
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`DefaultSidebarItemsGenerator respects custom isCategoryIndex 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"id": "intro",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"id": "tutorial1",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "tutorial2",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "Tutorials",
|
||||||
|
"link": Object {
|
||||||
|
"id": "tutorials-index",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"className": "foo",
|
||||||
|
"id": "guide1",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "guide2",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "not-guides-index",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "Guides",
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
||||||
|
|
||||||
|
exports[`DefaultSidebarItemsGenerator uses explicit link over the index/readme.{md,mdx} naming convention 1`] = `
|
||||||
|
Array [
|
||||||
|
Object {
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"id": "parent/doc2",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "parent/doc1",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "Category label",
|
||||||
|
"link": Object {
|
||||||
|
"id": "parent/doc3",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"collapsed": undefined,
|
||||||
|
"collapsible": undefined,
|
||||||
|
"items": Array [
|
||||||
|
Object {
|
||||||
|
"id": "parent/doc4",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "parent/doc6",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
Object {
|
||||||
|
"id": "parent/doc5",
|
||||||
|
"type": "doc",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
"label": "Category 2 label",
|
||||||
|
"type": "category",
|
||||||
|
},
|
||||||
|
]
|
||||||
|
`;
|
|
@ -6,7 +6,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {DefaultSidebarItemsGenerator} from '../generator';
|
import {DefaultSidebarItemsGenerator} from '../generator';
|
||||||
import type {Sidebar, SidebarItemsGenerator} from '../types';
|
import type {SidebarItemsGenerator} from '../types';
|
||||||
import {DefaultNumberPrefixParser} from '../../numberPrefix';
|
import {DefaultNumberPrefixParser} from '../../numberPrefix';
|
||||||
import {isCategoryIndex} from '../../docs';
|
import {isCategoryIndex} from '../../docs';
|
||||||
|
|
||||||
|
@ -104,13 +104,7 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(sidebarSlice).toEqual([
|
expect(sidebarSlice).toMatchSnapshot();
|
||||||
{type: 'doc', id: 'doc3'},
|
|
||||||
{type: 'doc', id: 'doc4'},
|
|
||||||
{type: 'doc', id: 'doc1', label: 'doc1 sidebar label'},
|
|
||||||
{type: 'doc', id: 'doc2'},
|
|
||||||
{type: 'doc', id: 'doc5'},
|
|
||||||
] as Sidebar);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('generates complex nested sidebar', async () => {
|
test('generates complex nested sidebar', async () => {
|
||||||
|
@ -214,49 +208,7 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(sidebarSlice).toEqual([
|
expect(sidebarSlice).toMatchSnapshot();
|
||||||
{type: 'doc', id: 'intro'},
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
label: 'Tutorials',
|
|
||||||
link: {
|
|
||||||
type: 'doc',
|
|
||||||
id: 'tutorials-index',
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{type: 'doc', id: 'tutorial1'},
|
|
||||||
{type: 'doc', id: 'tutorial2'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
label: 'Guides',
|
|
||||||
link: {
|
|
||||||
type: 'doc',
|
|
||||||
id: 'guides-index',
|
|
||||||
},
|
|
||||||
customProps: {
|
|
||||||
description: 'foo',
|
|
||||||
},
|
|
||||||
collapsed: false,
|
|
||||||
items: [
|
|
||||||
{type: 'doc', id: 'guide1', className: 'foo'},
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
label: 'SubGuides (metadata file label)',
|
|
||||||
items: [{type: 'doc', id: 'nested-guide'}],
|
|
||||||
link: {
|
|
||||||
type: 'generated-index',
|
|
||||||
slug: 'subguides-generated-index-slug',
|
|
||||||
title: 'subguides-title',
|
|
||||||
description: 'subguides-description',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{type: 'doc', id: 'guide2'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{type: 'doc', id: 'end'},
|
|
||||||
] as Sidebar);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('generates subfolder sidebar', async () => {
|
test('generates subfolder sidebar', async () => {
|
||||||
|
@ -352,33 +304,7 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(sidebarSlice).toEqual([
|
expect(sidebarSlice).toMatchSnapshot();
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
label: 'subsubsubfolder3 (_category_.json label)',
|
|
||||||
link: {
|
|
||||||
id: 'doc1',
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{type: 'doc', id: 'doc8'},
|
|
||||||
{type: 'doc', id: 'doc7'},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
label: 'subsubsubfolder2 (_category_.yml label)',
|
|
||||||
className: 'bar',
|
|
||||||
items: [{type: 'doc', id: 'doc6'}],
|
|
||||||
},
|
|
||||||
{type: 'doc', id: 'doc1'},
|
|
||||||
{type: 'doc', id: 'doc4'},
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
label: 'subsubsubfolder',
|
|
||||||
items: [{type: 'doc', id: 'doc5'}],
|
|
||||||
},
|
|
||||||
] as Sidebar);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('uses explicit link over the index/readme.{md,mdx} naming convention', async () => {
|
test('uses explicit link over the index/readme.{md,mdx} naming convention', async () => {
|
||||||
|
@ -449,45 +375,7 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(sidebarSlice).toEqual([
|
expect(sidebarSlice).toMatchSnapshot();
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
label: 'Category label',
|
|
||||||
link: {
|
|
||||||
id: 'parent/doc3',
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 'parent/doc2',
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
// doc1 is below doc2, because its file name is index.md
|
|
||||||
{
|
|
||||||
id: 'parent/doc1',
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: 'category',
|
|
||||||
label: 'Category 2 label',
|
|
||||||
items: [
|
|
||||||
{
|
|
||||||
id: 'parent/doc4',
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'parent/doc6',
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'parent/doc5',
|
|
||||||
type: 'doc',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
] as Sidebar);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test('respects custom isCategoryIndex', async () => {
|
test('respects custom isCategoryIndex', async () => {
|
||||||
|
@ -570,32 +458,49 @@ describe('DefaultSidebarItemsGenerator', () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(sidebarSlice).toEqual([
|
expect(sidebarSlice).toMatchSnapshot();
|
||||||
{type: 'doc', id: 'intro'},
|
});
|
||||||
{
|
|
||||||
type: 'category',
|
test('throws for unknown index link', async () => {
|
||||||
label: 'Tutorials',
|
const generateSidebar = () =>
|
||||||
link: {
|
DefaultSidebarItemsGenerator({
|
||||||
type: 'doc',
|
numberPrefixParser: DefaultNumberPrefixParser,
|
||||||
id: 'tutorials-index',
|
isCategoryIndex,
|
||||||
|
item: {
|
||||||
|
type: 'autogenerated',
|
||||||
|
dirName: '.',
|
||||||
},
|
},
|
||||||
items: [
|
version: {
|
||||||
{type: 'doc', id: 'tutorial1'},
|
versionName: 'current',
|
||||||
{type: 'doc', id: 'tutorial2'},
|
contentPath: '',
|
||||||
],
|
},
|
||||||
},
|
categoriesMetadata: {
|
||||||
{
|
category: {
|
||||||
type: 'category',
|
link: {
|
||||||
label: 'Guides',
|
type: 'doc',
|
||||||
items: [
|
id: 'foo',
|
||||||
{type: 'doc', id: 'guide1', className: 'foo'},
|
},
|
||||||
{type: 'doc', id: 'guide2'},
|
},
|
||||||
|
},
|
||||||
|
docs: [
|
||||||
{
|
{
|
||||||
type: 'doc',
|
id: 'intro',
|
||||||
id: 'not-guides-index',
|
unversionedId: 'intro',
|
||||||
|
source: '@site/docs/category/intro.md',
|
||||||
|
sourceDirName: 'category',
|
||||||
|
frontMatter: {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
options: {
|
||||||
] as Sidebar);
|
sidebarCollapsed: true,
|
||||||
|
sidebarCollapsible: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(generateSidebar).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||||
|
"Can't find any doc with ID foo.
|
||||||
|
Available doc IDs:
|
||||||
|
- intro"
|
||||||
|
`);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -23,7 +23,10 @@ describe('loadSidebars', () => {
|
||||||
frontMatter: {},
|
frontMatter: {},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
version: {contentPath: 'docs/foo', contentPathLocalized: 'docs/foo'},
|
version: {
|
||||||
|
contentPath: path.join(fixtureDir, 'docs'),
|
||||||
|
contentPathLocalized: path.join(fixtureDir, 'docs'),
|
||||||
|
},
|
||||||
categoryLabelSlugger: null,
|
categoryLabelSlugger: null,
|
||||||
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
|
||||||
};
|
};
|
||||||
|
@ -107,4 +110,36 @@ describe('loadSidebars', () => {
|
||||||
const result = await loadSidebars(sidebarPath, params);
|
const result = await loadSidebars(sidebarPath, params);
|
||||||
expect(result).toMatchSnapshot();
|
expect(result).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('duplicate category metadata files', async () => {
|
||||||
|
const sidebarPath = path.join(
|
||||||
|
fixtureDir,
|
||||||
|
'sidebars-collapsed-first-level.json',
|
||||||
|
);
|
||||||
|
const consoleWarnMock = jest
|
||||||
|
.spyOn(console, 'warn')
|
||||||
|
.mockImplementation(() => {});
|
||||||
|
const consoleErrorMock = jest
|
||||||
|
.spyOn(console, 'error')
|
||||||
|
.mockImplementation(() => {});
|
||||||
|
await expect(() =>
|
||||||
|
loadSidebars(sidebarPath, {
|
||||||
|
...params,
|
||||||
|
version: {
|
||||||
|
contentPath: path.join(fixtureDir, 'invalid-docs'),
|
||||||
|
contentPathLocalized: path.join(fixtureDir, 'invalid-docs'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
).rejects.toThrowErrorMatchingInlineSnapshot(`"\\"foo\\" is not allowed"`);
|
||||||
|
expect(consoleWarnMock).toBeCalledWith(
|
||||||
|
expect.stringMatching(
|
||||||
|
/.*\[WARNING].* There are more than one category metadata files for .*foo.*: foo\/_category_.json, foo\/_category_.yml. The behavior is undetermined./,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
expect(consoleErrorMock).toBeCalledWith(
|
||||||
|
expect.stringMatching(
|
||||||
|
/.*\[ERROR].* The docs sidebar category metadata file .*foo\/_category_.json.* looks invalid!/,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -60,9 +60,9 @@ export const DefaultSidebarItemsGenerator: SidebarItemsGenerator = async ({
|
||||||
const doc = findDoc(docId);
|
const doc = findDoc(docId);
|
||||||
if (!doc) {
|
if (!doc) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
`Can't find any doc with id=${docId}.\nAvailable doc ids:\n- ${Object.keys(
|
`Can't find any doc with ID ${docId}.
|
||||||
docsById,
|
Available doc IDs:
|
||||||
).join('\n- ')}`,
|
- ${Object.keys(docsById).join('\n- ')}`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return doc;
|
return doc;
|
||||||
|
|
|
@ -258,11 +258,7 @@ export function createSidebarsUtils(sidebars: Sidebars): SidebarsUtils {
|
||||||
const sidebarName = Object.entries(sidebarNameToNavigationItems).find(
|
const sidebarName = Object.entries(sidebarNameToNavigationItems).find(
|
||||||
([, navigationItems]) =>
|
([, navigationItems]) =>
|
||||||
navigationItems.find(isCurrentCategoryGeneratedIndexItem),
|
navigationItems.find(isCurrentCategoryGeneratedIndexItem),
|
||||||
)?.[0];
|
)![0];
|
||||||
|
|
||||||
if (!sidebarName) {
|
|
||||||
return emptySidebarNavigation();
|
|
||||||
}
|
|
||||||
const navigationItems = sidebarNameToNavigationItems[sidebarName]!;
|
const navigationItems = sidebarNameToNavigationItems[sidebarName]!;
|
||||||
const currentItemIndex = navigationItems.findIndex(
|
const currentItemIndex = navigationItems.findIndex(
|
||||||
isCurrentCategoryGeneratedIndexItem,
|
isCurrentCategoryGeneratedIndexItem,
|
||||||
|
|
8
packages/docusaurus-types/src/index.d.ts
vendored
8
packages/docusaurus-types/src/index.d.ts
vendored
|
@ -161,10 +161,7 @@ export type ImportedPresetModule = PresetModule & {
|
||||||
default?: PresetModule;
|
default?: PresetModule;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PresetConfig =
|
export type PresetConfig = string | [string, Record<string, unknown>];
|
||||||
| [string, Record<string, unknown>]
|
|
||||||
| [string]
|
|
||||||
| string;
|
|
||||||
|
|
||||||
export type HostPortCLIOptions = {
|
export type HostPortCLIOptions = {
|
||||||
host?: string;
|
host?: string;
|
||||||
|
@ -356,9 +353,8 @@ export type ConfigurePostCssFn = Plugin<unknown>['configurePostCss'];
|
||||||
export type PluginOptions = {id?: string} & Record<string, unknown>;
|
export type PluginOptions = {id?: string} & Record<string, unknown>;
|
||||||
|
|
||||||
export type PluginConfig =
|
export type PluginConfig =
|
||||||
| [string, PluginOptions]
|
|
||||||
| [string]
|
|
||||||
| string
|
| string
|
||||||
|
| [string, PluginOptions]
|
||||||
| [PluginModule, PluginOptions]
|
| [PluginModule, PluginOptions]
|
||||||
| PluginModule;
|
| PluginModule;
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,9 @@ function createPluginSchema(theme: boolean = false) {
|
||||||
Joi.alternatives()
|
Joi.alternatives()
|
||||||
.try(
|
.try(
|
||||||
Joi.function(),
|
Joi.function(),
|
||||||
Joi.array().ordered(Joi.function().required(), Joi.object().required()),
|
Joi.array()
|
||||||
|
.ordered(Joi.function().required(), Joi.object().required())
|
||||||
|
.length(2),
|
||||||
Joi.string(),
|
Joi.string(),
|
||||||
Joi.array()
|
Joi.array()
|
||||||
.ordered(Joi.string().required(), Joi.object().required())
|
.ordered(Joi.string().required(), Joi.object().required())
|
||||||
|
|
|
@ -3,3 +3,7 @@ module.exports = function (context, options) {
|
||||||
name: 'third-plugin',
|
name: 'third-plugin',
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
module.exports.validateThemeConfig = function ({validate, themeConfig}) {
|
||||||
|
return {a: 1};
|
||||||
|
};
|
||||||
|
|
|
@ -38,6 +38,7 @@ describe('initPlugins', () => {
|
||||||
expect(plugins[1].name).toBe('second-plugin');
|
expect(plugins[1].name).toBe('second-plugin');
|
||||||
expect(plugins[2].name).toBe('third-plugin');
|
expect(plugins[2].name).toBe('third-plugin');
|
||||||
expect(plugins[3].name).toBe('fourth-plugin');
|
expect(plugins[3].name).toBe('fourth-plugin');
|
||||||
|
expect(context.siteConfig.themeConfig).toEqual({a: 1});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('plugins with bad values throw user-friendly error message', async () => {
|
test('plugins with bad values throw user-friendly error message', async () => {
|
||||||
|
|
|
@ -89,7 +89,7 @@ export async function loadPlugins({
|
||||||
// need to run in certain order or depend on others for data.
|
// need to run in certain order or depend on others for data.
|
||||||
const loadedPlugins: LoadedPlugin[] = await Promise.all(
|
const loadedPlugins: LoadedPlugin[] = await Promise.all(
|
||||||
plugins.map(async (plugin) => {
|
plugins.map(async (plugin) => {
|
||||||
const content = plugin.loadContent ? await plugin.loadContent() : null;
|
const content = await plugin.loadContent?.();
|
||||||
return {...plugin, content};
|
return {...plugin, content};
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -205,7 +205,7 @@ export async function loadPlugins({
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
contentLoadedTranslatedPlugins.map(async (plugin) => {
|
contentLoadedTranslatedPlugins.map(async (plugin) => {
|
||||||
if (!plugin.routesLoaded) {
|
if (!plugin.routesLoaded) {
|
||||||
return null;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO remove this deprecated lifecycle soon
|
// TODO remove this deprecated lifecycle soon
|
||||||
|
@ -213,7 +213,7 @@ export async function loadPlugins({
|
||||||
// TODO, 1 user reported usage of this lifecycle! https://github.com/facebook/docusaurus/issues/3918
|
// TODO, 1 user reported usage of this lifecycle! https://github.com/facebook/docusaurus/issues/3918
|
||||||
logger.error`Plugin code=${'routesLoaded'} lifecycle is deprecated. If you think we should keep this lifecycle, please report here: path=${'https://github.com/facebook/docusaurus/issues/3918'}`;
|
logger.error`Plugin code=${'routesLoaded'} lifecycle is deprecated. If you think we should keep this lifecycle, please report here: path=${'https://github.com/facebook/docusaurus/issues/3918'}`;
|
||||||
|
|
||||||
return plugin.routesLoaded(pluginsRouteConfigs);
|
await plugin.routesLoaded(pluginsRouteConfigs);
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -61,39 +61,29 @@ async function normalizePluginConfig(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(pluginConfig)) {
|
// plugins: [
|
||||||
// plugins: [
|
// ['./plugin',options],
|
||||||
// ['./plugin',options],
|
// ]
|
||||||
// ]
|
if (typeof pluginConfig[0] === 'string') {
|
||||||
if (typeof pluginConfig[0] === 'string') {
|
const pluginModuleImport = pluginConfig[0];
|
||||||
const pluginModuleImport = pluginConfig[0];
|
const pluginPath = pluginRequire.resolve(pluginModuleImport);
|
||||||
const pluginPath = pluginRequire.resolve(pluginModuleImport);
|
const pluginModule = importFresh<ImportedPluginModule>(pluginPath);
|
||||||
const pluginModule = importFresh<ImportedPluginModule>(pluginPath);
|
return {
|
||||||
return {
|
plugin: pluginModule?.default ?? pluginModule,
|
||||||
plugin: pluginModule?.default ?? pluginModule,
|
options: pluginConfig[1],
|
||||||
options: pluginConfig[1] ?? {},
|
pluginModule: {
|
||||||
pluginModule: {
|
path: pluginModuleImport,
|
||||||
path: pluginModuleImport,
|
module: pluginModule,
|
||||||
module: pluginModule,
|
},
|
||||||
},
|
};
|
||||||
};
|
|
||||||
}
|
|
||||||
// plugins: [
|
|
||||||
// [function plugin() { },options],
|
|
||||||
// ]
|
|
||||||
if (typeof pluginConfig[0] === 'function') {
|
|
||||||
return {
|
|
||||||
plugin: pluginConfig[0],
|
|
||||||
options: pluginConfig[1] ?? {},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// plugins: [
|
||||||
throw new Error(
|
// [function plugin() { },options],
|
||||||
`Unexpected: can't load plugin for following plugin config.\n${JSON.stringify(
|
// ]
|
||||||
pluginConfig,
|
return {
|
||||||
)}`,
|
plugin: pluginConfig[0],
|
||||||
);
|
options: pluginConfig[1],
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function normalizePluginConfigs(
|
export async function normalizePluginConfigs(
|
||||||
|
@ -219,16 +209,9 @@ export default async function initPlugins({
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const plugins: InitializedPlugin[] = (
|
const plugins: InitializedPlugin[] = await Promise.all(
|
||||||
await Promise.all(
|
pluginConfigsNormalized.map(initializePlugin),
|
||||||
pluginConfigsNormalized.map((pluginConfig) => {
|
);
|
||||||
if (!pluginConfig) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return initializePlugin(pluginConfig);
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
).filter(<T>(item: T): item is Exclude<T, null> => Boolean(item));
|
|
||||||
|
|
||||||
ensureUniquePluginInstanceIds(plugins);
|
ensureUniquePluginInstanceIds(plugins);
|
||||||
|
|
||||||
|
|
|
@ -32,10 +32,8 @@ export default async function loadPresets(context: LoadContext): Promise<{
|
||||||
let presetOptions = {};
|
let presetOptions = {};
|
||||||
if (typeof presetItem === 'string') {
|
if (typeof presetItem === 'string') {
|
||||||
presetModuleImport = presetItem;
|
presetModuleImport = presetItem;
|
||||||
} else if (Array.isArray(presetItem)) {
|
|
||||||
[presetModuleImport, presetOptions = {}] = presetItem;
|
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Invalid presets format detected in config.');
|
[presetModuleImport, presetOptions] = presetItem;
|
||||||
}
|
}
|
||||||
const presetName = resolveModuleName(
|
const presetName = resolveModuleName(
|
||||||
presetModuleImport,
|
presetModuleImport,
|
||||||
|
|
0
packages/docusaurus/src/server/versions/__tests__/__fixtures__/dummy-plugin.js
generated
Normal file
0
packages/docusaurus/src/server/versions/__tests__/__fixtures__/dummy-plugin.js
generated
Normal file
|
@ -12,7 +12,7 @@ describe('getPluginVersion', () => {
|
||||||
it('Can detect external packages plugins versions of correctly.', async () => {
|
it('Can detect external packages plugins versions of correctly.', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
getPluginVersion(
|
getPluginVersion(
|
||||||
path.join(__dirname, '..', '__fixtures__', 'dummy-plugin.js'),
|
path.join(__dirname, '__fixtures__/dummy-plugin.js'),
|
||||||
// Make the plugin appear external.
|
// Make the plugin appear external.
|
||||||
path.join(__dirname, '..', '..', '..', '..', '..', '..', 'website'),
|
path.join(__dirname, '..', '..', '..', '..', '..', '..', 'website'),
|
||||||
),
|
),
|
||||||
|
@ -22,9 +22,9 @@ describe('getPluginVersion', () => {
|
||||||
it('Can detect project plugins versions correctly.', async () => {
|
it('Can detect project plugins versions correctly.', async () => {
|
||||||
await expect(
|
await expect(
|
||||||
getPluginVersion(
|
getPluginVersion(
|
||||||
path.join(__dirname, '..', '__fixtures__', 'dummy-plugin.js'),
|
path.join(__dirname, '__fixtures__/dummy-plugin.js'),
|
||||||
// Make the plugin appear project local.
|
// Make the plugin appear project local.
|
||||||
path.join(__dirname, '..', '__fixtures__'),
|
path.join(__dirname, '__fixtures__'),
|
||||||
),
|
),
|
||||||
).resolves.toEqual({type: 'project'});
|
).resolves.toEqual({type: 'project'});
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,11 +22,8 @@ export async function getPackageJsonVersion(
|
||||||
async function getPackageJsonName(
|
async function getPackageJsonName(
|
||||||
packageJsonPath: string,
|
packageJsonPath: string,
|
||||||
): Promise<string | undefined> {
|
): Promise<string | undefined> {
|
||||||
if (await fs.pathExists(packageJsonPath)) {
|
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
|
||||||
// eslint-disable-next-line @typescript-eslint/no-var-requires, import/no-dynamic-require, global-require
|
return require(packageJsonPath).name;
|
||||||
return require(packageJsonPath).name;
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPluginVersion(
|
export async function getPluginVersion(
|
||||||
|
|
Loading…
Add table
Reference in a new issue