/** * 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 {DefaultCategoryCollapsedValue} from '../sidebars'; import {Sidebar, SidebarItemsGenerator} from '../types'; import fs from 'fs-extra'; import {DefaultNumberPrefixParser} from '../numberPrefix'; describe('DefaultSidebarItemsGenerator', () => { function testDefaultSidebarItemsGenerator( options: Partial[0]>, ) { return DefaultSidebarItemsGenerator({ numberPrefixParser: DefaultNumberPrefixParser, item: { type: 'autogenerated', dirName: '.', }, version: { versionName: 'current', contentPath: 'docs', }, docs: [], ...options, }); } function mockCategoryMetadataFiles( categoryMetadataFiles: Record>, ) { 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: {}, }, ], }); 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: {}, }, ], }); expect(sidebarSlice).toEqual([ {type: 'doc', id: 'intro'}, { type: 'category', label: 'Tutorials', collapsed: DefaultCategoryCollapsedValue, items: [ {type: 'doc', id: 'tutorial1'}, {type: 'doc', id: 'tutorial2'}, ], }, { type: 'category', label: 'Guides', collapsed: false, items: [ {type: 'doc', id: 'guide1'}, { type: 'category', label: 'SubGuides (metadata file label)', collapsed: DefaultCategoryCollapsedValue, 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)', 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: {}, }, ], }); expect(sidebarSlice).toEqual([ { type: 'category', label: 'subsubsubfolder3 (_category_.json label)', collapsed: false, items: [ {type: 'doc', id: 'doc8'}, {type: 'doc', id: 'doc7'}, ], }, { type: 'category', label: 'subsubsubfolder2 (_category_.yml label)', collapsed: true, items: [{type: 'doc', id: 'doc6'}], }, {type: 'doc', id: 'doc1'}, {type: 'doc', id: 'doc4'}, { type: 'category', label: 'subsubsubfolder', collapsed: true, items: [{type: 'doc', id: 'doc5'}], }, ] as Sidebar); }); });