refactor(content-docs): clean up sidebars logic; validate generator returns (#6596)

* refactor(content-docs): clean up sidebars logic; validate generator returns

* remove another TODO

* fix types

* refactors

* refactor...
This commit is contained in:
Joshua Chen 2022-02-04 09:46:25 +08:00 committed by GitHub
parent d6bdf7e804
commit e3fd3e74ce
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 750 additions and 615 deletions

View file

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

View file

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

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,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,6 +1,6 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`loadNormalizedSidebars sidebars link 1`] = `
exports[`loadSidebars sidebars link 1`] = `
Object {
"docs": Array [
Object {
@ -21,7 +21,7 @@ Object {
}
`;
exports[`loadNormalizedSidebars sidebars with category.collapsed property 1`] = `
exports[`loadSidebars sidebars with category.collapsed property 1`] = `
Object {
"docs": Array [
Object {
@ -72,7 +72,7 @@ Object {
}
`;
exports[`loadNormalizedSidebars sidebars with category.collapsed property at first level 1`] = `
exports[`loadSidebars sidebars with category.collapsed property at first level 1`] = `
Object {
"docs": Array [
Object {
@ -105,7 +105,7 @@ Object {
}
`;
exports[`loadNormalizedSidebars sidebars with deep level of category 1`] = `
exports[`loadSidebars sidebars with deep level of category 1`] = `
Object {
"docs": Array [
Object {
@ -177,7 +177,7 @@ Object {
}
`;
exports[`loadNormalizedSidebars sidebars with first level not a category 1`] = `
exports[`loadSidebars sidebars with first level not a category 1`] = `
Object {
"docs": Array [
Object {
@ -201,7 +201,7 @@ Object {
}
`;
exports[`loadNormalizedSidebars sidebars with known sidebar item type 1`] = `
exports[`loadSidebars sidebars with known sidebar item type 1`] = `
Object {
"docs": Array [
Object {
@ -246,3 +246,23 @@ Object {
],
}
`;
exports[`loadSidebars undefined path 1`] = `
Object {
"defaultSidebar": Array [
Object {
"collapsed": true,
"collapsible": true,
"items": Array [
Object {
"id": "bar",
"type": "doc",
},
],
"label": "foo",
"link": undefined,
"type": "category",
},
],
}
`;

View file

@ -214,8 +214,6 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'Tutorials',
collapsed: true,
collapsible: true,
link: {
type: 'doc',
id: 'tutorials-index',
@ -228,19 +226,16 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'Guides',
collapsed: false,
collapsible: true,
link: {
type: 'doc',
id: 'guides-index',
},
collapsed: false,
items: [
{type: 'doc', id: 'guide1', className: 'foo'},
{
type: 'category',
label: 'SubGuides (metadata file label)',
collapsed: true,
collapsible: true,
items: [{type: 'doc', id: 'nested-guide'}],
link: {
type: 'generated-index',
@ -279,8 +274,6 @@ describe('DefaultSidebarItemsGenerator', () => {
'subfolder/subsubfolder/subsubsubfolder3': {
position: 1,
label: 'subsubsubfolder3 (_category_.json label)',
collapsible: false,
collapsed: false,
link: {
type: 'doc',
id: 'doc1', // This is a "fully-qualified" ID that can't be found locally
@ -355,8 +348,6 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'subsubsubfolder3 (_category_.json label)',
collapsed: false,
collapsible: false,
link: {
id: 'doc1',
type: 'doc',
@ -369,8 +360,6 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'subsubsubfolder2 (_category_.yml label)',
collapsed: true,
collapsible: true,
className: 'bar',
items: [{type: 'doc', id: 'doc6'}],
},
@ -379,8 +368,6 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'subsubsubfolder',
collapsed: true,
collapsible: true,
items: [{type: 'doc', id: 'doc5'}],
},
] as Sidebar);
@ -458,8 +445,6 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'Category label',
collapsed: true,
collapsible: true,
link: {
id: 'parent/doc3',
type: 'doc',
@ -478,8 +463,6 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'Category 2 label',
collapsed: true,
collapsible: true,
items: [
{
id: 'parent/doc4',
@ -583,8 +566,6 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'Tutorials',
collapsed: true,
collapsible: true,
link: {
type: 'doc',
id: 'tutorials-index',
@ -597,8 +578,6 @@ describe('DefaultSidebarItemsGenerator', () => {
{
type: 'category',
label: 'Guides',
collapsed: true,
collapsible: true,
items: [
{type: 'doc', id: 'guide1', className: 'foo'},
{type: 'doc', id: 'guide2'},

View file

@ -6,32 +6,36 @@
*/
import path from 'path';
import {
loadNormalizedSidebars,
DefaultSidebars,
DisabledSidebars,
} from '../index';
import type {NormalizeSidebarsParams, VersionMetadata} from '../../types';
import {loadSidebars, DisabledSidebars} from '../index';
import type {SidebarProcessorParams} from '../types';
import {DefaultSidebarItemsGenerator} from '../generator';
describe('loadNormalizedSidebars', () => {
describe('loadSidebars', () => {
const fixtureDir = path.join(__dirname, '__fixtures__', 'sidebars');
const options: NormalizeSidebarsParams = {
sidebarCollapsed: true,
sidebarCollapsible: true,
version: {
versionName: 'version',
versionPath: 'versionPath',
} as VersionMetadata,
const params: SidebarProcessorParams = {
sidebarItemsGenerator: DefaultSidebarItemsGenerator,
numberPrefixParser: (filename) => ({filename}),
docs: [
{
source: '@site/docs/foo/bar.md',
sourceDirName: 'foo',
id: 'bar',
frontMatter: {},
},
],
version: {contentPath: 'docs/foo', contentPathLocalized: 'docs/foo'},
categoryLabelSlugger: null,
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
};
test('sidebars with known sidebar item type', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars.json');
const result = await loadNormalizedSidebars(sidebarPath, options);
const result = await loadSidebars(sidebarPath, params);
expect(result).toMatchSnapshot();
});
test('sidebars with deep level of category', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-category.js');
const result = await loadNormalizedSidebars(sidebarPath, options);
const result = await loadSidebars(sidebarPath, params);
expect(result).toMatchSnapshot();
});
@ -41,8 +45,8 @@ describe('loadNormalizedSidebars', () => {
fixtureDir,
'sidebars-category-shorthand.js',
);
const sidebar1 = await loadNormalizedSidebars(sidebarPath1, options);
const sidebar2 = await loadNormalizedSidebars(sidebarPath2, options);
const sidebar1 = await loadSidebars(sidebarPath1, params);
const sidebar2 = await loadSidebars(sidebarPath2, params);
expect(sidebar1).toEqual(sidebar2);
});
@ -51,53 +55,11 @@ describe('loadNormalizedSidebars', () => {
fixtureDir,
'sidebars-category-wrong-items.json',
);
await expect(() => loadNormalizedSidebars(sidebarPath, options)).rejects
.toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"category\\",
\\"label\\": \\"Category Label\\",
\\"items\\" [1]: \\"doc1\\"
}

[1] \\"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',
await expect(() =>
loadSidebars(sidebarPath, params),
).rejects.toThrowErrorMatchingInlineSnapshot(
`"Invalid category {\\"type\\":\\"category\\",\\"label\\":\\"Category Label\\",\\"items\\":\\"doc1\\"}: items must be an array"`,
);
await expect(() => loadNormalizedSidebars(sidebarPath, options)).rejects
.toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"category\\",
\\"items\\": [
\\"doc1\\"
],
\\"label\\" [1]: true
}

[1] \\"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',
);
await expect(() => loadNormalizedSidebars(sidebarPath, options)).rejects
.toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"doc\\",
\\"id\\" [1]: [
\\"doc1\\"
]
}

[1] \\"id\\" must be a string"
`);
});
test('sidebars with first level not a category', async () => {
@ -105,95 +67,35 @@ describe('loadNormalizedSidebars', () => {
fixtureDir,
'sidebars-first-level-not-category.js',
);
const result = await loadNormalizedSidebars(sidebarPath, options);
const result = await loadSidebars(sidebarPath, params);
expect(result).toMatchSnapshot();
});
test('sidebars link', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link.json');
const result = await loadNormalizedSidebars(sidebarPath, options);
const result = await loadSidebars(sidebarPath, params);
expect(result).toMatchSnapshot();
});
test('sidebars link wrong label', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link-wrong-label.json');
await expect(() => loadNormalizedSidebars(sidebarPath, options)).rejects
.toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"link\\",
\\"href\\": \\"https://github.com\\",
\\"label\\" [1]: false
}

[1] \\"label\\" must be a string"
`);
});
test('sidebars link wrong href', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-link-wrong-href.json');
await expect(() => loadNormalizedSidebars(sidebarPath, options)).rejects
.toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"link\\",
\\"label\\": \\"GitHub\\",
\\"href\\" [1]: [
\\"example.com\\"
]
}

[1] \\"href\\" contains an invalid value"
`);
});
test('sidebars with unknown sidebar item type', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-unknown-type.json');
await expect(() => loadNormalizedSidebars(sidebarPath, options)).rejects
.toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"superman\\",
\\"undefined\\" [1]: -- missing --
}

[1] Unknown sidebar item type \\"superman\\"."
`);
});
test('sidebars with known sidebar item type but wrong field', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-wrong-field.json');
await expect(() => loadNormalizedSidebars(sidebarPath, options)).rejects
.toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"category\\",
\\"label\\": \\"category\\",
\\"href\\": \\"https://github.com\\",
\\"items\\" [1]: -- missing --
}

[1] \\"items\\" is required"
`);
});
test('unexisting path', async () => {
await expect(loadNormalizedSidebars('badpath', options)).resolves.toEqual(
await expect(loadSidebars('badpath', params)).resolves.toEqual(
DisabledSidebars,
);
});
test('undefined path', async () => {
await expect(loadNormalizedSidebars(undefined, options)).resolves.toEqual(
DefaultSidebars,
);
await expect(loadSidebars(undefined, params)).resolves.toMatchSnapshot();
});
test('literal false path', async () => {
await expect(loadNormalizedSidebars(false, options)).resolves.toEqual(
await expect(loadSidebars(false, params)).resolves.toEqual(
DisabledSidebars,
);
});
test('sidebars with category.collapsed property', async () => {
const sidebarPath = path.join(fixtureDir, 'sidebars-collapsed.json');
const result = await loadNormalizedSidebars(sidebarPath, options);
const result = await loadSidebars(sidebarPath, params);
expect(result).toMatchSnapshot();
});
@ -202,7 +104,7 @@ describe('loadNormalizedSidebars', () => {
fixtureDir,
'sidebars-collapsed-first-level.json',
);
const result = await loadNormalizedSidebars(sidebarPath, options);
const result = await loadSidebars(sidebarPath, params);
expect(result).toMatchSnapshot();
});
});

View file

@ -0,0 +1,194 @@
/**
* 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 {postProcessSidebars} from '../postProcessor';
describe('postProcess', () => {
test('transforms category without subitems', () => {
const processedSidebar = postProcessSidebars(
{
sidebar: [
{
type: 'category',
label: 'Category',
link: {
type: 'generated-index',
slug: 'generated/permalink',
},
items: [],
},
{
type: 'category',
label: 'Category 2',
link: {
type: 'doc',
id: 'doc ID',
},
items: [],
},
],
},
{
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
version: {versionPath: 'version'},
},
);
expect(processedSidebar).toMatchInlineSnapshot(`
Object {
"sidebar": Array [
Object {
"href": "version/generated/permalink",
"label": "Category",
"type": "link",
},
Object {
"id": "doc ID",
"label": "Category 2",
"type": "doc",
},
],
}
`);
expect(() => {
postProcessSidebars(
{
sidebar: [
{
type: 'category',
label: 'Bad category',
items: [],
},
],
},
{
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
version: {versionPath: 'version'},
},
);
}).toThrowErrorMatchingInlineSnapshot(
`"Sidebar category Bad category has neither any subitem nor a link. This makes this item not able to link to anything."`,
);
});
test('corrects collapsed state inconsistencies', () => {
expect(
postProcessSidebars(
{
sidebar: [
{
type: 'category',
label: 'Category',
collapsed: true,
collapsible: false,
items: [{type: 'doc', id: 'foo'}],
},
],
},
{
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: true},
version: {versionPath: 'version'},
},
),
).toMatchInlineSnapshot(`
Object {
"sidebar": Array [
Object {
"collapsed": false,
"collapsible": false,
"items": Array [
Object {
"id": "foo",
"type": "doc",
},
],
"label": "Category",
"link": undefined,
"type": "category",
},
],
}
`);
expect(
postProcessSidebars(
{
sidebar: [
{
type: 'category',
label: 'Category',
collapsed: true,
items: [{type: 'doc', id: 'foo'}],
},
],
},
{
sidebarOptions: {sidebarCollapsed: false, sidebarCollapsible: false},
version: {versionPath: 'version'},
},
),
).toMatchInlineSnapshot(`
Object {
"sidebar": Array [
Object {
"collapsed": false,
"collapsible": false,
"items": Array [
Object {
"id": "foo",
"type": "doc",
},
],
"label": "Category",
"link": undefined,
"type": "category",
},
],
}
`);
expect(
postProcessSidebars(
{
sidebar: [
{
type: 'category',
label: 'Category',
items: [{type: 'doc', id: 'foo'}],
},
],
},
{
sidebarOptions: {sidebarCollapsed: true, sidebarCollapsible: false},
version: {versionPath: 'version'},
},
),
).toMatchInlineSnapshot(`
Object {
"sidebar": Array [
Object {
"collapsed": false,
"collapsible": false,
"items": Array [
Object {
"id": "foo",
"type": "doc",
},
],
"label": "Category",
"link": undefined,
"type": "category",
},
],
}
`);
});
});

View file

@ -5,12 +5,15 @@
* LICENSE file in the root directory of this source tree.
*/
import {processSidebars, type SidebarProcessorParams} from '../processor';
import {processSidebars} from '../processor';
import type {
SidebarItem,
SidebarItemsGenerator,
Sidebars,
NormalizedSidebar,
NormalizedSidebars,
SidebarProcessorParams,
CategoryMetadataFile,
ProcessedSidebars,
} from '../types';
import {DefaultSidebarItemsGenerator} from '../generator';
import {createSlugger} from '@docusaurus/utils';
@ -25,7 +28,7 @@ describe('processSidebars', () => {
return jest.fn(async () => sidebarSlice);
}
const StaticGeneratedSidebarSlice: SidebarItem[] = [
const StaticGeneratedSidebarSlice: NormalizedSidebar = [
{type: 'doc', id: 'doc-generated-id-1'},
{type: 'doc', id: 'doc-generated-id-2'},
];
@ -53,9 +56,10 @@ describe('processSidebars', () => {
async function testProcessSidebars(
unprocessedSidebars: NormalizedSidebars,
categoriesMetadata: Record<string, CategoryMetadataFile> = {},
paramsOverrides: Partial<SidebarProcessorParams> = {},
) {
return processSidebars(unprocessedSidebars, {
return processSidebars(unprocessedSidebars, categoriesMetadata, {
...params,
...paramsOverrides,
});
@ -101,10 +105,7 @@ describe('processSidebars', () => {
link: {
type: 'generated-index',
slug: 'category-generated-index-slug',
permalink: 'category-generated-index-permalink',
},
collapsed: true, // A suspicious bad config that will be normalized
collapsible: false,
items: [
{type: 'doc', id: 'doc2'},
{type: 'autogenerated', dirName: 'dir1'},
@ -131,6 +132,7 @@ describe('processSidebars', () => {
expect(StaticSidebarItemsGenerator).toHaveBeenCalledTimes(3);
expect(StaticSidebarItemsGenerator).toHaveBeenCalledWith({
categoriesMetadata: {},
defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
item: {type: 'autogenerated', dirName: 'dir1'},
docs: params.docs,
@ -143,6 +145,7 @@ describe('processSidebars', () => {
});
expect(StaticSidebarItemsGenerator).toHaveBeenCalledWith({
defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
categoriesMetadata: {},
item: {type: 'autogenerated', dirName: 'dir2'},
docs: params.docs,
version: {
@ -154,6 +157,7 @@ describe('processSidebars', () => {
});
expect(StaticSidebarItemsGenerator).toHaveBeenCalledWith({
defaultSidebarItemsGenerator: DefaultSidebarItemsGenerator,
categoriesMetadata: {},
item: {type: 'autogenerated', dirName: 'dir3'},
docs: params.docs,
version: {
@ -173,10 +177,7 @@ describe('processSidebars', () => {
link: {
type: 'generated-index',
slug: 'category-generated-index-slug',
permalink: 'category-generated-index-permalink',
},
collapsed: false,
collapsible: false,
items: [{type: 'doc', id: 'doc2'}, ...StaticGeneratedSidebarSlice],
},
{type: 'link', href: 'https://facebook.com', label: 'FB'},
@ -194,20 +195,17 @@ describe('processSidebars', () => {
items: [{type: 'doc', id: 'doc4'}],
},
],
} as Sidebars);
} as ProcessedSidebars);
});
test('ensure generated items are normalized', async () => {
const sidebarSliceContainingCategoryGeneratedIndex: SidebarItem[] = [
const sidebarSliceContainingCategoryGeneratedIndex: NormalizedSidebar = [
{
type: 'category',
label: 'Generated category',
link: {
type: 'generated-index',
slug: 'generated-cat-index-slug',
// @ts-expect-error: TODO undefined should be allowed here,
// typing error needing refactor
permalink: undefined,
},
items: [
{
@ -218,15 +216,19 @@ describe('processSidebars', () => {
},
];
const unprocessedSidebars: NormalizedSidebars = {
const unprocessedSidebars = {
someSidebar: [{type: 'autogenerated', dirName: 'dir2'}],
};
const processedSidebar = await testProcessSidebars(unprocessedSidebars, {
sidebarItemsGenerator: createStaticSidebarItemGenerator(
sidebarSliceContainingCategoryGeneratedIndex,
),
});
const processedSidebar = await testProcessSidebars(
unprocessedSidebars,
{},
{
sidebarItemsGenerator: createStaticSidebarItemGenerator(
sidebarSliceContainingCategoryGeneratedIndex,
),
},
);
expect(processedSidebar).toEqual({
someSidebar: [
@ -236,7 +238,6 @@ describe('processSidebars', () => {
link: {
type: 'generated-index',
slug: 'generated-cat-index-slug',
permalink: '/docs/1.0.0/generated-cat-index-slug',
},
items: [
{
@ -244,67 +245,8 @@ describe('processSidebars', () => {
id: 'foo',
},
],
collapsible: true,
collapsed: true,
},
],
} as Sidebars);
});
test('transforms category without subitems', async () => {
const sidebarSlice: SidebarItem[] = [
{
type: 'category',
label: 'Category',
link: {
type: 'generated-index',
permalink: 'generated/permalink',
},
items: [],
},
{
type: 'category',
label: 'Category 2',
link: {
type: 'doc',
id: 'doc ID',
},
items: [],
},
];
const processedSidebar = await testProcessSidebars(
{sidebar: sidebarSlice},
{},
);
expect(processedSidebar).toEqual({
sidebar: [
{
type: 'link',
label: 'Category',
href: 'generated/permalink',
},
{
type: 'doc',
label: 'Category 2',
id: 'doc ID',
},
],
} as Sidebars);
await expect(async () => {
await testProcessSidebars({
sidebar: [
{
type: 'category',
label: 'Bad category',
items: [],
},
],
});
}).rejects.toThrowErrorMatchingInlineSnapshot(
`"Sidebar category Bad category has neither any subitem nor a link. This makes this item not able to link to anything."`,
);
} as ProcessedSidebars);
});
});

View file

@ -6,14 +6,9 @@
*/
import {validateSidebars, validateCategoryMetadataFile} from '../validation';
import type {CategoryMetadataFile} from '../generator';
import type {SidebarsConfig} from '../types';
import type {SidebarsConfig, CategoryMetadataFile} from '../types';
describe('validateSidebars', () => {
// TODO add more tests
// TODO it seems many error cases are not validated properly
// and error messages are quite bad
test('throw for bad value', async () => {
expect(() => validateSidebars({sidebar: [{type: 42}]}))
.toThrowErrorMatchingInlineSnapshot(`
@ -45,10 +40,193 @@ describe('validateSidebars', () => {
};
validateSidebars(sidebars);
});
});
describe('html item type', () => {
test('requires a value', () => {
test('sidebar category wrong label', () => {
expect(() =>
validateSidebars({
docs: [
{
type: 'category',
label: true,
items: [{type: 'doc', id: 'doc1'}],
},
],
}),
).toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"category\\",
\\"items\\": [
{
\\"type\\": \\"doc\\",
\\"id\\": \\"doc1\\"
}
],
\\"label\\" [1]: true
}

[1] \\"label\\" must be a string"
`);
});
test('sidebars link wrong label', () => {
expect(() =>
validateSidebars({
docs: [
{
type: 'link',
label: false,
href: 'https://github.com',
},
],
}),
).toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"link\\",
\\"href\\": \\"https://github.com\\",
\\"label\\" [1]: false
}

[1] \\"label\\" must be a string"
`);
});
test('sidebars link wrong href', () => {
expect(() =>
validateSidebars({
docs: [
{
type: 'link',
label: 'GitHub',
href: ['example.com'],
},
],
}),
).toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"link\\",
\\"label\\": \\"GitHub\\",
\\"href\\" [1]: [
\\"example.com\\"
]
}

[1] \\"href\\" contains an invalid value"
`);
});
test('sidebars with unknown sidebar item type', () => {
expect(() =>
validateSidebars({
docs: [
{
type: 'superman',
},
],
}),
).toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"superman\\",
\\"undefined\\" [1]: -- missing --
}

[1] Unknown sidebar item type \\"superman\\"."
`);
});
test('sidebars category missing items', () => {
expect(() =>
validateSidebars({
docs: [
{
type: 'category',
label: 'category',
},
{
type: 'ref',
id: 'hello',
},
],
}),
).toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"category\\",
\\"label\\": \\"category\\",
\\"items\\" [1]: -- missing --
}

[1] \\"items\\" is required"
`);
});
test('sidebars category wrong field', () => {
expect(() =>
validateSidebars({
docs: [
{
type: 'category',
label: 'category',
items: [],
href: 'https://google.com',
},
{
type: 'ref',
id: 'hello',
},
],
}),
).toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"category\\",
\\"label\\": \\"category\\",
\\"items\\": [],
\\"href\\" [1]: \\"https://google.com\\"
}

[1] \\"href\\" is not allowed"
`);
});
test('sidebar category wrong items', () => {
expect(() =>
validateSidebars({
docs: {
Test: [
{
type: 'category',
label: 'Category Label',
items: 'doc1',
},
],
},
}),
).toThrowErrorMatchingInlineSnapshot(`"sidebar.forEach is not a function"`);
});
test('sidebars item doc but id is not a string', async () => {
expect(() =>
validateSidebars({
docs: [
{
type: 'doc',
id: ['doc1'],
},
],
}),
).toThrowErrorMatchingInlineSnapshot(`
"{
\\"type\\": \\"doc\\",
\\"id\\" [1]: [
\\"doc1\\"
]
}

[1] \\"id\\" must be a string"
`);
});
test('HTML type requires a value', () => {
const sidebars: SidebarsConfig = {
sidebar1: [
{
@ -68,7 +246,7 @@ describe('html item type', () => {
`);
});
test('accepts valid values', () => {
test('HTML type accepts valid values', () => {
const sidebars: SidebarsConfig = {
sidebar1: [
{