refactor(content-docs): refactor sidebars, Joi validation, generator rework, expose config types (#5678)

This commit is contained in:
Joshua Chen 2021-10-14 20:38:26 +08:00 committed by GitHub
parent 543011c9d2
commit 8d92e9bcf5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 1806 additions and 1880 deletions

View file

@ -1,34 +0,0 @@
/**
* 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.
*/
module.exports = {
docs: [
{
'level 1': [
'a',
{
'level 2': [
{
'level 3': [
'c',
{
'level 4': [
'd',
{
'deeper more more': ['e'],
},
],
},
],
},
'f',
],
},
],
},
],
};

View file

@ -1,11 +0,0 @@
{
"docs": {
"Test": [
{
"type": "category",
"label": "Category Label",
"items": "doc1"
}
]
}
}

View file

@ -1,11 +0,0 @@
{
"docs": {
"Test": [
{
"type": "category",
"label": true,
"items": ["doc1"]
}
]
}
}

View file

@ -1,44 +0,0 @@
/*
* 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.
*/
module.exports = {
docs: [
{
type: 'category',
label: 'level 1',
items: [
'a',
{
type: 'category',
label: 'level 2',
items: [
{
type: 'category',
label: 'level 3',
items: [
'c',
{
type: 'category',
label: 'level 4',
items: [
'd',
{
type: 'category',
label: 'deeper more more',
items: ['e'],
},
],
},
],
},
'f',
],
},
],
},
],
};

View file

@ -1,20 +0,0 @@
{
"docs": [
{
"type": "category",
"label": "Introduction",
"items": [
"doc1"
],
"collapsed": false
},
{
"type": "category",
"label": "Powering MDX",
"items": [
"doc2"
],
"collapsed": false
}
]
}

View file

@ -1,21 +0,0 @@
{
"docs": {
"Test": [
{
"type": "category",
"label": "Introduction",
"items": ["doc1"],
"collapsed": false
}
],
"Reference": [
{
"type": "category",
"label": "Powering MDX",
"items": ["doc2"],
"collapsed": false
}
]
}
}

View file

@ -1,10 +0,0 @@
{
"docs": {
"Test": [
{
"type": "doc",
"id": ["doc1"]
}
]
}
}

View file

@ -1,20 +0,0 @@
/**
* 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.
*/
module.exports = {
docs: [
{
type: 'category',
label: 'Getting Started',
items: ['greeting'],
},
{
type: 'doc',
id: 'api',
},
],
};

View file

@ -1,11 +0,0 @@
{
"docs": {
"Test": [
{
"type": "link",
"label": "GitHub",
"href": ["example.com"]
}
]
}
}

View file

@ -1,11 +0,0 @@
{
"docs": {
"Test": [
{
"type": "link",
"label": false,
"href": "https://github.com"
}
]
}
}

View file

@ -1,11 +0,0 @@
{
"docs": {
"Test": [
{
"type": "link",
"label": "category",
"href": "https://github.com"
}
]
}
}

View file

@ -1,14 +0,0 @@
{
"docs": {
"Test": [
"foo/bar",
"foo/baz",
{
"type": "superman"
}
],
"Guides": [
"hello"
]
}
}

View file

@ -1,20 +0,0 @@
{
"docs": {
"Test": [
"foo/bar",
"foo/baz",
{
"type": "category",
"label": "category",
"href": "https://github.com"
},
{
"type": "ref",
"id": "hello"
}
],
"Guides": [
"hello"
]
}
}

View file

@ -1,20 +0,0 @@
{
"docs": {
"Test": [
"foo/bar",
"foo/baz",
{
"type": "link",
"label": "Github",
"href": "https://github.com"
},
{
"type": "ref",
"id": "hello"
}
],
"Guides": [
"hello"
]
}
}

View file

@ -1,233 +0,0 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`loadSidebars sidebars link 1`] = `
Object {
"docs": Array [
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"href": "https://github.com",
"label": "category",
"type": "link",
},
],
"label": "Test",
"type": "category",
},
],
}
`;
exports[`loadSidebars sidebars with category.collapsed property 1`] = `
Object {
"docs": Array [
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"collapsed": false,
"collapsible": true,
"items": Array [
Object {
"id": "doc1",
"type": "doc",
},
],
"label": "Introduction",
"type": "category",
},
],
"label": "Test",
"type": "category",
},
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"collapsed": false,
"collapsible": true,
"items": Array [
Object {
"id": "doc2",
"type": "doc",
},
],
"label": "Powering MDX",
"type": "category",
},
],
"label": "Reference",
"type": "category",
},
],
}
`;
exports[`loadSidebars sidebars with category.collapsed property at first level 1`] = `
Object {
"docs": Array [
Object {
"collapsed": false,
"collapsible": true,
"items": Array [
Object {
"id": "doc1",
"type": "doc",
},
],
"label": "Introduction",
"type": "category",
},
Object {
"collapsed": false,
"collapsible": true,
"items": Array [
Object {
"id": "doc2",
"type": "doc",
},
],
"label": "Powering MDX",
"type": "category",
},
],
}
`;
exports[`loadSidebars sidebars with deep level of category 1`] = `
Object {
"docs": Array [
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"id": "a",
"type": "doc",
},
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"id": "c",
"type": "doc",
},
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"id": "d",
"type": "doc",
},
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"id": "e",
"type": "doc",
},
],
"label": "deeper more more",
"type": "category",
},
],
"label": "level 4",
"type": "category",
},
],
"label": "level 3",
"type": "category",
},
Object {
"id": "f",
"type": "doc",
},
],
"label": "level 2",
"type": "category",
},
],
"label": "level 1",
"type": "category",
},
],
}
`;
exports[`loadSidebars sidebars with first level not a category 1`] = `
Object {
"docs": Array [
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"id": "greeting",
"type": "doc",
},
],
"label": "Getting Started",
"type": "category",
},
Object {
"id": "api",
"type": "doc",
},
],
}
`;
exports[`loadSidebars sidebars with known sidebar item type 1`] = `
Object {
"docs": Array [
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"id": "foo/bar",
"type": "doc",
},
Object {
"id": "foo/baz",
"type": "doc",
},
Object {
"href": "https://github.com",
"label": "Github",
"type": "link",
},
Object {
"id": "hello",
"type": "ref",
},
],
"label": "Test",
"type": "category",
},
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"id": "hello",
"type": "doc",
},
],
"label": "Guides",
"type": "category",
},
],
}
`;

View file

@ -22,17 +22,16 @@ import {DEFAULT_PLUGIN_ID} from '@docusaurus/core/lib/constants';
import * as cliDocs from '../cli';
import {OptionsSchema} from '../options';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import {
DocMetadata,
LoadedVersion,
import type {DocMetadata, LoadedVersion} from '../types';
import type {
SidebarItem,
SidebarItemsGeneratorOption,
SidebarItemsGeneratorOptionArgs,
} from '../types';
} from '../sidebars/types';
import {toSidebarsProp} from '../props';
import {validate} from 'webpack';
import {DefaultSidebarItemsGenerator} from '../sidebarItemsGenerator';
import {DefaultSidebarItemsGenerator} from '../sidebars/generator';
import {DisabledSidebars} from '../sidebars';
function findDocById(version: LoadedVersion, unversionedId: string) {

View file

@ -7,7 +7,7 @@
import {OptionsSchema, DEFAULT_OPTIONS, validateOptions} from '../options';
import {normalizePluginOptions} from '@docusaurus/utils-validation';
import {DefaultSidebarItemsGenerator} from '../sidebarItemsGenerator';
import {DefaultSidebarItemsGenerator} from '../sidebars/generator';
import {
DefaultNumberPrefixParser,
DisabledNumberPrefixParser,

View file

@ -1,358 +0,0 @@
/**
* 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 {
CategoryMetadatasFile,
DefaultSidebarItemsGenerator,
} from '../sidebarItemsGenerator';
import {Sidebar, SidebarItemsGenerator} from '../types';
import fs from 'fs-extra';
import {DefaultNumberPrefixParser} from '../numberPrefix';
describe('DefaultSidebarItemsGenerator', () => {
function testDefaultSidebarItemsGenerator(
params: Partial<Parameters<SidebarItemsGenerator>[0]>,
) {
return DefaultSidebarItemsGenerator({
numberPrefixParser: DefaultNumberPrefixParser,
item: {
type: 'autogenerated',
dirName: '.',
},
version: {
versionName: 'current',
contentPath: 'docs',
},
docs: [],
options: {
sidebarCollapsed: true,
sidebarCollapsible: true,
},
...params,
});
}
function mockCategoryMetadataFiles(
categoryMetadataFiles: Record<string, Partial<CategoryMetadatasFile>>,
) {
jest.spyOn(fs, 'pathExists').mockImplementation((metadataFilePath) => {
return typeof categoryMetadataFiles[metadataFilePath] !== 'undefined';
});
jest.spyOn(fs, 'readFile').mockImplementation(
// @ts-expect-error: annoying TS error due to overrides
async (metadataFilePath: string) => {
return JSON.stringify(categoryMetadataFiles[metadataFilePath]);
},
);
}
test('generates empty sidebar slice when no docs and emit a warning', async () => {
const consoleWarn = jest.spyOn(console, 'warn');
const sidebarSlice = await testDefaultSidebarItemsGenerator({
docs: [],
});
expect(sidebarSlice).toEqual([]);
expect(consoleWarn).toHaveBeenCalledWith(
expect.stringMatching(
/No docs found in dir .: can't auto-generate a sidebar/,
),
);
});
test('generates simple flat sidebar', async () => {
const sidebarSlice = await DefaultSidebarItemsGenerator({
numberPrefixParser: DefaultNumberPrefixParser,
item: {
type: 'autogenerated',
dirName: '.',
},
version: {
versionName: 'current',
contentPath: '',
},
docs: [
{
id: 'doc1',
source: 'doc1.md',
sourceDirName: '.',
sidebarPosition: 2,
frontMatter: {
sidebar_label: 'doc1 sidebar label',
},
},
{
id: 'doc2',
source: 'doc2.md',
sourceDirName: '.',
sidebarPosition: 3,
frontMatter: {},
},
{
id: 'doc3',
source: 'doc3.md',
sourceDirName: '.',
sidebarPosition: 1,
frontMatter: {},
},
{
id: 'doc4',
source: 'doc4.md',
sourceDirName: '.',
sidebarPosition: 1.5,
frontMatter: {},
},
{
id: 'doc5',
source: 'doc5.md',
sourceDirName: '.',
sidebarPosition: undefined,
frontMatter: {},
},
],
options: {
sidebarCollapsed: true,
sidebarCollapsible: true,
},
});
expect(sidebarSlice).toEqual([
{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 () => {
mockCategoryMetadataFiles({
'02-Guides/_category_.json': {collapsed: false},
'02-Guides/01-SubGuides/_category_.yml': {
label: 'SubGuides (metadata file label)',
},
});
const sidebarSlice = await DefaultSidebarItemsGenerator({
numberPrefixParser: DefaultNumberPrefixParser,
item: {
type: 'autogenerated',
dirName: '.',
},
version: {
versionName: 'current',
contentPath: '',
},
docs: [
{
id: 'intro',
source: 'intro.md',
sourceDirName: '.',
sidebarPosition: 1,
frontMatter: {},
},
{
id: 'tutorial2',
source: 'tutorial2.md',
sourceDirName: '01-Tutorials',
sidebarPosition: 2,
frontMatter: {},
},
{
id: 'tutorial1',
source: 'tutorial1.md',
sourceDirName: '01-Tutorials',
sidebarPosition: 1,
frontMatter: {},
},
{
id: 'guide2',
source: 'guide2.md',
sourceDirName: '02-Guides',
sidebarPosition: 2,
frontMatter: {},
},
{
id: 'guide1',
source: 'guide1.md',
sourceDirName: '02-Guides',
sidebarPosition: 1,
frontMatter: {},
},
{
id: 'nested-guide',
source: 'nested-guide.md',
sourceDirName: '02-Guides/01-SubGuides',
sidebarPosition: undefined,
frontMatter: {},
},
{
id: 'end',
source: 'end.md',
sourceDirName: '.',
sidebarPosition: 3,
frontMatter: {},
},
],
options: {
sidebarCollapsed: true,
sidebarCollapsible: true,
},
});
expect(sidebarSlice).toEqual([
{type: 'doc', id: 'intro'},
{
type: 'category',
label: 'Tutorials',
collapsed: true,
collapsible: true,
items: [
{type: 'doc', id: 'tutorial1'},
{type: 'doc', id: 'tutorial2'},
],
},
{
type: 'category',
label: 'Guides',
collapsed: false,
collapsible: true,
items: [
{type: 'doc', id: 'guide1'},
{
type: 'category',
label: 'SubGuides (metadata file label)',
collapsed: true,
collapsible: true,
items: [{type: 'doc', id: 'nested-guide'}],
},
{type: 'doc', id: 'guide2'},
],
},
{type: 'doc', id: 'end'},
] as Sidebar);
});
test('generates subfolder sidebar', async () => {
// Ensure that category metadata file is correctly read
// fix edge case found in https://github.com/facebook/docusaurus/issues/4638
mockCategoryMetadataFiles({
'subfolder/subsubfolder/subsubsubfolder2/_category_.yml': {
position: 2,
label: 'subsubsubfolder2 (_category_.yml label)',
},
'subfolder/subsubfolder/subsubsubfolder3/_category_.json': {
position: 1,
label: 'subsubsubfolder3 (_category_.json label)',
collapsible: false,
collapsed: false,
},
});
const sidebarSlice = await DefaultSidebarItemsGenerator({
numberPrefixParser: DefaultNumberPrefixParser,
item: {
type: 'autogenerated',
dirName: 'subfolder/subsubfolder',
},
version: {
versionName: 'current',
contentPath: '',
},
docs: [
{
id: 'doc1',
source: 'doc1.md',
sourceDirName: 'subfolder/subsubfolder',
sidebarPosition: undefined,
frontMatter: {},
},
{
id: 'doc2',
source: 'doc2.md',
sourceDirName: 'subfolder',
sidebarPosition: undefined,
frontMatter: {},
},
{
id: 'doc3',
source: 'doc3.md',
sourceDirName: '.',
sidebarPosition: undefined,
frontMatter: {},
},
{
id: 'doc4',
source: 'doc4.md',
sourceDirName: 'subfolder/subsubfolder',
sidebarPosition: undefined,
frontMatter: {},
},
{
id: 'doc5',
source: 'doc5.md',
sourceDirName: 'subfolder/subsubfolder/subsubsubfolder',
sidebarPosition: undefined,
frontMatter: {},
},
{
id: 'doc6',
source: 'doc6.md',
sourceDirName: 'subfolder/subsubfolder/subsubsubfolder2',
sidebarPosition: undefined,
frontMatter: {},
},
{
id: 'doc7',
source: 'doc7.md',
sourceDirName: 'subfolder/subsubfolder/subsubsubfolder3',
sidebarPosition: 2,
frontMatter: {},
},
{
id: 'doc8',
source: 'doc8.md',
sourceDirName: 'subfolder/subsubfolder/subsubsubfolder3',
sidebarPosition: 1,
frontMatter: {},
},
],
options: {
sidebarCollapsed: true,
sidebarCollapsible: true,
},
});
expect(sidebarSlice).toEqual([
{
type: 'category',
label: 'subsubsubfolder3 (_category_.json label)',
collapsed: false,
collapsible: false,
items: [
{type: 'doc', id: 'doc8'},
{type: 'doc', id: 'doc7'},
],
},
{
type: 'category',
label: 'subsubsubfolder2 (_category_.yml label)',
collapsed: true,
collapsible: true,
items: [{type: 'doc', id: 'doc6'}],
},
{type: 'doc', id: 'doc1'},
{type: 'doc', id: 'doc4'},
{
type: 'category',
label: 'subsubsubfolder',
collapsed: true,
collapsible: true,
items: [{type: 'doc', id: 'doc5'}],
},
] as Sidebar);
});
});

View file

@ -1,741 +0,0 @@
/**
* 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 path from 'path';
import {
loadSidebars,
collectSidebarDocItems,
collectSidebarsDocIds,
createSidebarsUtils,
collectSidebarCategories,
collectSidebarLinks,
transformSidebarItems,
processSidebars,
DefaultSidebars,
DisabledSidebars,
fixSidebarItemInconsistencies,
} from '../sidebars';
import {
Sidebar,
SidebarItem,
SidebarItemsGenerator,
Sidebars,
UnprocessedSidebars,
SidebarOptions,
SidebarItemCategory,
} from '../types';
import {DefaultSidebarItemsGenerator} from '../sidebarItemsGenerator';
describe('loadSidebars', () => {
const fixtureDir = path.join(__dirname, '__fixtures__', 'sidebars');
const options: SidebarOptions = {
sidebarCollapsed: true,
sidebarCollapsible: true,
};
test('sidebars with known sidebar item type', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars.json');
const result = loadSidebars(sidebarPath, options);
expect(result).toMatchSnapshot();
});
test('sidebars with deep level of category', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-category.js');
const result = loadSidebars(sidebarPath, options);
expect(result).toMatchSnapshot();
});
test('sidebars shorthand and longform lead to exact same sidebar', async () => {
const sidebarPath1 = path.join(fixtureDir, 'sidebars-category.js');
const sidebarPath2 = path.join(
fixtureDir,
'sidebars-category-shorthand.js',
);
const sidebar1 = loadSidebars(sidebarPath1, options);
const sidebar2 = loadSidebars(sidebarPath2, options);
expect(sidebar1).toEqual(sidebar2);
});
test('sidebars with category but category.items is not an array', async () => {
const sidebarPath = path.join(
fixtureDir,
'sidebars-category-wrong-items.json',
);
expect(() =>
loadSidebars(sidebarPath, options),
).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"category\\",\\"label\\":\\"Category Label\\",\\"items\\":\\"doc1\\"}: \\"items\\" must be an array."`,
);
});
test('sidebars with category but category label is not a string', async () => {
const sidebarPath = path.join(
fixtureDir,
'sidebars-category-wrong-label.json',
);
expect(() =>
loadSidebars(sidebarPath, options),
).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"category\\",\\"label\\":true,\\"items\\":[\\"doc1\\"]}: \\"label\\" must be a string."`,
);
});
test('sidebars item doc but id is not a string', async () => {
const sidebarPath = path.join(
fixtureDir,
'sidebars-doc-id-not-string.json',
);
expect(() =>
loadSidebars(sidebarPath, options),
).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"doc\\",\\"id\\":[\\"doc1\\"]}: \\"id\\" must be a string."`,
);
});
test('sidebars with first level not a category', async () => {
const sidebarPath = path.join(
fixtureDir,
'sidebars-first-level-not-category.js',
);
const result = loadSidebars(sidebarPath, options);
expect(result).toMatchSnapshot();
});
test('sidebars link', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link.json');
const result = loadSidebars(sidebarPath, options);
expect(result).toMatchSnapshot();
});
test('sidebars link wrong label', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link-wrong-label.json');
expect(() =>
loadSidebars(sidebarPath, options),
).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"link\\",\\"label\\":false,\\"href\\":\\"https://github.com\\"}: \\"label\\" must be a string."`,
);
});
test('sidebars link wrong href', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link-wrong-href.json');
expect(() =>
loadSidebars(sidebarPath, options),
).toThrowErrorMatchingInlineSnapshot(
`"Error loading {\\"type\\":\\"link\\",\\"label\\":\\"GitHub\\",\\"href\\":[\\"example.com\\"]}: \\"href\\" must be a string."`,
);
});
test('sidebars with unknown sidebar item type', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-unknown-type.json');
expect(() => loadSidebars(sidebarPath, options))
.toThrowErrorMatchingInlineSnapshot(`
"Unknown sidebar item type \\"superman\\". Sidebar item is {\\"type\\":\\"superman\\"}.
"
`);
});
test('sidebars with known sidebar item type but wrong field', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-wrong-field.json');
expect(() =>
loadSidebars(sidebarPath, options),
).toThrowErrorMatchingInlineSnapshot(
`"Unknown sidebar item keys: href. Item: {\\"type\\":\\"category\\",\\"label\\":\\"category\\",\\"href\\":\\"https://github.com\\"}"`,
);
});
test('unexisting path', () => {
expect(loadSidebars('badpath', options)).toEqual(DisabledSidebars);
});
test('undefined path', () => {
expect(loadSidebars(undefined, options)).toEqual(DefaultSidebars);
});
test('literal false path', () => {
expect(loadSidebars(false, options)).toEqual(DisabledSidebars);
});
test('sidebars with category.collapsed property', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-collapsed.json');
const result = loadSidebars(sidebarPath, options);
expect(result).toMatchSnapshot();
});
test('sidebars with category.collapsed property at first level', async () => {
const sidebarPath = path.join(
fixtureDir,
'sidebars-collapsed-first-level.json',
);
const result = loadSidebars(sidebarPath, options);
expect(result).toMatchSnapshot();
});
});
describe('collectSidebarDocItems', () => {
test('can collect docs', async () => {
const sidebar: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category1',
items: [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 2',
items: [
{type: 'doc', id: 'doc2'},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Sub sub category 1',
items: [{type: 'doc', id: 'doc3'}],
},
],
},
],
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category2',
items: [
{type: 'doc', id: 'doc4'},
{type: 'doc', id: 'doc5'},
],
},
];
expect(collectSidebarDocItems(sidebar).map((doc) => doc.id)).toEqual([
'doc1',
'doc2',
'doc3',
'doc4',
'doc5',
]);
});
});
describe('collectSidebarCategories', () => {
test('can collect categories', async () => {
const sidebar: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category1',
items: [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 2',
items: [
{type: 'doc', id: 'doc2'},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Sub sub category 1',
items: [{type: 'doc', id: 'doc3'}],
},
],
},
],
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category2',
items: [
{type: 'doc', id: 'doc4'},
{type: 'doc', id: 'doc5'},
],
},
];
expect(
collectSidebarCategories(sidebar).map((category) => category.label),
).toEqual([
'Category1',
'Subcategory 1',
'Subcategory 2',
'Sub sub category 1',
'Category2',
]);
});
});
describe('collectSidebarLinks', () => {
test('can collect links', async () => {
const sidebar: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category1',
items: [
{
type: 'link',
href: 'https://google.com',
label: 'Google',
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 2',
items: [
{
type: 'link',
href: 'https://facebook.com',
label: 'Facebook',
},
],
},
],
},
];
expect(collectSidebarLinks(sidebar).map((link) => link.href)).toEqual([
'https://google.com',
'https://facebook.com',
]);
});
});
describe('collectSidebarsDocIds', () => {
test('can collect sidebars doc items', async () => {
const sidebar1: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category1',
items: [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
},
{type: 'doc', id: 'doc2'},
],
},
];
const sidebar2: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category2',
items: [
{type: 'doc', id: 'doc3'},
{type: 'doc', id: 'doc4'},
],
},
];
const sidebar3: Sidebar = [
{type: 'doc', id: 'doc5'},
{type: 'doc', id: 'doc6'},
];
expect(collectSidebarsDocIds({sidebar1, sidebar2, sidebar3})).toEqual({
sidebar1: ['doc1', 'doc2'],
sidebar2: ['doc3', 'doc4'],
sidebar3: ['doc5', 'doc6'],
});
});
});
describe('transformSidebarItems', () => {
test('can transform sidebar items', async () => {
const sidebar: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category1',
items: [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
customProps: {fakeProp: false},
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 2',
items: [
{type: 'doc', id: 'doc2'},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Sub sub category 1',
items: [
{type: 'doc', id: 'doc3', customProps: {lorem: 'ipsum'}},
],
},
],
},
],
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category2',
items: [
{type: 'doc', id: 'doc4'},
{type: 'doc', id: 'doc5'},
],
},
];
expect(
transformSidebarItems(sidebar, (item) => {
if (item.type === 'category') {
return {...item, label: `MODIFIED LABEL: ${item.label}`};
}
return item;
}),
).toEqual([
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'MODIFIED LABEL: Category1',
items: [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'MODIFIED LABEL: Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
customProps: {fakeProp: false},
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'MODIFIED LABEL: Subcategory 2',
items: [
{type: 'doc', id: 'doc2'},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'MODIFIED LABEL: Sub sub category 1',
items: [
{type: 'doc', id: 'doc3', customProps: {lorem: 'ipsum'}},
],
},
],
},
],
},
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'MODIFIED LABEL: Category2',
items: [
{type: 'doc', id: 'doc4'},
{type: 'doc', id: 'doc5'},
],
},
]);
});
});
describe('processSidebars', () => {
const StaticGeneratedSidebarSlice: SidebarItem[] = [
{type: 'doc', id: 'doc-generated-id-1'},
{type: 'doc', id: 'doc-generated-id-2'},
];
const StaticSidebarItemsGenerator: SidebarItemsGenerator = jest.fn(
async () => {
return StaticGeneratedSidebarSlice;
},
);
async function testProcessSidebars(unprocessedSidebars: UnprocessedSidebars) {
return processSidebars({
sidebarItemsGenerator: StaticSidebarItemsGenerator,
unprocessedSidebars,
docs: [],
// @ts-expect-error: useless for this test
version: {},
});
}
test('let sidebars without autogenerated items untouched', async () => {
const unprocessedSidebars: UnprocessedSidebars = {
someSidebar: [
{type: 'doc', id: 'doc1'},
{
type: 'category',
collapsed: false,
collapsible: true,
items: [{type: 'doc', id: 'doc2'}],
label: 'Category',
},
{type: 'link', href: 'https://facebook.com', label: 'FB'},
],
secondSidebar: [
{type: 'doc', id: 'doc3'},
{type: 'link', href: 'https://instagram.com', label: 'IG'},
{
type: 'category',
collapsed: false,
collapsible: true,
items: [{type: 'doc', id: 'doc4'}],
label: 'Category',
},
],
};
const processedSidebar = await testProcessSidebars(unprocessedSidebars);
expect(processedSidebar).toEqual(unprocessedSidebars);
});
test('replace autogenerated items by generated sidebars slices', async () => {
const unprocessedSidebars: UnprocessedSidebars = {
someSidebar: [
{type: 'doc', id: 'doc1'},
{
type: 'category',
collapsed: false,
collapsible: true,
items: [
{type: 'doc', id: 'doc2'},
{type: 'autogenerated', dirName: 'dir1'},
],
label: 'Category',
},
{type: 'link', href: 'https://facebook.com', label: 'FB'},
],
secondSidebar: [
{type: 'doc', id: 'doc3'},
{type: 'autogenerated', dirName: 'dir2'},
{type: 'link', href: 'https://instagram.com', label: 'IG'},
{type: 'autogenerated', dirName: 'dir3'},
{
type: 'category',
collapsed: false,
collapsible: true,
items: [{type: 'doc', id: 'doc4'}],
label: 'Category',
},
],
};
const processedSidebar = await testProcessSidebars(unprocessedSidebars);
expect(StaticSidebarItemsGenerator).toHaveBeenCalledTimes(3);
expect(StaticSidebarItemsGenerator).toHaveBeenCalledWith({
defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
item: {type: 'autogenerated', dirName: 'dir1'},
docs: [],
version: {},
});
expect(StaticSidebarItemsGenerator).toHaveBeenCalledWith({
defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
item: {type: 'autogenerated', dirName: 'dir2'},
docs: [],
version: {},
});
expect(StaticSidebarItemsGenerator).toHaveBeenCalledWith({
defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
item: {type: 'autogenerated', dirName: 'dir3'},
docs: [],
version: {},
});
expect(processedSidebar).toEqual({
someSidebar: [
{type: 'doc', id: 'doc1'},
{
type: 'category',
collapsed: false,
collapsible: true,
items: [{type: 'doc', id: 'doc2'}, ...StaticGeneratedSidebarSlice],
label: 'Category',
},
{type: 'link', href: 'https://facebook.com', label: 'FB'},
],
secondSidebar: [
{type: 'doc', id: 'doc3'},
...StaticGeneratedSidebarSlice,
{type: 'link', href: 'https://instagram.com', label: 'IG'},
...StaticGeneratedSidebarSlice,
{
type: 'category',
collapsed: false,
collapsible: true,
items: [{type: 'doc', id: 'doc4'}],
label: 'Category',
},
],
} as Sidebars);
});
});
describe('createSidebarsUtils', () => {
const sidebar1: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category1',
items: [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Subcategory 1',
items: [{type: 'doc', id: 'doc1'}],
},
{type: 'doc', id: 'doc2'},
],
},
];
const sidebar2: Sidebar = [
{
type: 'category',
collapsed: false,
collapsible: true,
label: 'Category2',
items: [
{type: 'doc', id: 'doc3'},
{type: 'doc', id: 'doc4'},
],
},
];
const sidebars: Sidebars = {sidebar1, sidebar2};
const {getFirstDocIdOfFirstSidebar, getSidebarNameByDocId, getDocNavigation} =
createSidebarsUtils(sidebars);
test('getSidebarNameByDocId', async () => {
expect(getFirstDocIdOfFirstSidebar()).toEqual('doc1');
});
test('getSidebarNameByDocId', async () => {
expect(getSidebarNameByDocId('doc1')).toEqual('sidebar1');
expect(getSidebarNameByDocId('doc2')).toEqual('sidebar1');
expect(getSidebarNameByDocId('doc3')).toEqual('sidebar2');
expect(getSidebarNameByDocId('doc4')).toEqual('sidebar2');
expect(getSidebarNameByDocId('doc5')).toEqual(undefined);
expect(getSidebarNameByDocId('doc6')).toEqual(undefined);
});
test('getDocNavigation', async () => {
expect(getDocNavigation('doc1')).toEqual({
sidebarName: 'sidebar1',
previousId: undefined,
nextId: 'doc2',
});
expect(getDocNavigation('doc2')).toEqual({
sidebarName: 'sidebar1',
previousId: 'doc1',
nextId: undefined,
});
expect(getDocNavigation('doc3')).toEqual({
sidebarName: 'sidebar2',
previousId: undefined,
nextId: 'doc4',
});
expect(getDocNavigation('doc4')).toEqual({
sidebarName: 'sidebar2',
previousId: 'doc3',
nextId: undefined,
});
});
});
describe('fixSidebarItemInconsistencies', () => {
test('should not fix good category', () => {
const category: SidebarItemCategory = {
type: 'category',
label: 'Cat',
items: [],
collapsible: true,
collapsed: true,
};
expect(fixSidebarItemInconsistencies(category)).toEqual(category);
});
test('should fix bad category', () => {
const category: SidebarItemCategory = {
type: 'category',
label: 'Cat',
items: [],
collapsible: false,
collapsed: true, // Bad because collapsible=false
};
expect(fixSidebarItemInconsistencies(category)).toEqual({
...category,
collapsed: false,
});
});
test('should fix bad subcategory', () => {
const subCategory: SidebarItemCategory = {
type: 'category',
label: 'SubCat',
items: [],
collapsible: false,
collapsed: true, // Bad because collapsible=false
};
const category: SidebarItemCategory = {
type: 'category',
label: 'Cat',
items: [subCategory],
collapsible: true,
collapsed: true,
};
expect(fixSidebarItemInconsistencies(category)).toEqual({
...category,
items: [
{
...subCategory,
collapsed: false,
},
],
});
});
});