/** * 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 { createSidebarsUtils, collectSidebarDocItems, collectSidebarCategories, collectSidebarLinks, transformSidebarItems, collectSidebarsDocIds, toDocNavigationLink, toNavigationLink, } from '../utils'; import type {Sidebar, Sidebars} from '../types'; import type { DocMetadataBase, PropNavigationLink, } from '@docusaurus/plugin-content-docs'; describe('createSidebarsUtils', () => { const sidebar1: Sidebar = [ { type: 'category', collapsed: false, collapsible: true, label: 'S1 Category', items: [ { type: 'category', collapsed: false, collapsible: true, label: 'S1 Subcategory', items: [{type: 'doc', id: 'doc1'}], }, {type: 'doc', id: 'doc2'}, ], }, ]; const sidebar2: Sidebar = [ { type: 'category', collapsed: false, collapsible: true, label: 'S2 Category', items: [ {type: 'doc', id: 'doc3', label: 'Doc 3'}, {type: 'doc', id: 'doc4'}, ], }, ]; const sidebar3: Sidebar = [ { type: 'category', collapsed: false, collapsible: true, label: 'S3 Category', link: { type: 'doc', id: 'doc5', }, items: [ { type: 'category', collapsed: false, collapsible: true, label: 'S3 SubCategory', link: { type: 'generated-index', slug: '/s3-subcategory-index-slug', permalink: '/s3-subcategory-index-permalink', }, items: [ { type: 'category', collapsed: false, collapsible: true, label: 'S3 SubSubCategory', link: { type: 'generated-index', slug: '/s3-subsubcategory-slug', permalink: '/s3-subsubcategory-index-permalink', }, items: [ {type: 'doc', id: 'doc6'}, {type: 'doc', id: 'doc7'}, ], }, ], }, ], }, ]; const sidebar4: Sidebar = [ { type: 'category', items: [ {type: 'link', href: 'https://facebook.com'}, {type: 'link', href: 'https://reactjs.org'}, {type: 'link', href: 'https://docusaurus.io'}, ], }, { type: 'category', collapsed: false, collapsible: true, label: 'S4 Category', link: { type: 'generated-index', slug: '/s4-category-slug', permalink: '/s4-category-slug', }, items: [ {type: 'doc', id: 'doc8'}, {type: 'doc', id: 'doc9'}, ], }, ]; const sidebars: Sidebars = {sidebar1, sidebar2, sidebar3, sidebar4}; const { getFirstDocIdOfFirstSidebar, getSidebarNameByDocId, getDocNavigation, getCategoryGeneratedIndexNavigation, getCategoryGeneratedIndexList, getFirstLink, } = createSidebarsUtils(sidebars); it('getFirstDocIdOfFirstSidebar', async () => { expect(getFirstDocIdOfFirstSidebar()).toBe('doc1'); }); it('getSidebarNameByDocId', async () => { expect(getSidebarNameByDocId('doc1')).toBe('sidebar1'); expect(getSidebarNameByDocId('doc2')).toBe('sidebar1'); expect(getSidebarNameByDocId('doc3')).toBe('sidebar2'); expect(getSidebarNameByDocId('doc4')).toBe('sidebar2'); expect(getSidebarNameByDocId('doc5')).toBe('sidebar3'); expect(getSidebarNameByDocId('doc6')).toBe('sidebar3'); expect(getSidebarNameByDocId('doc7')).toBe('sidebar3'); expect(getSidebarNameByDocId('unknown_id')).toBeUndefined(); }); it('getDocNavigation', async () => { expect(getDocNavigation('doc1', 'doc1', undefined)).toEqual({ sidebarName: 'sidebar1', previous: undefined, next: { type: 'doc', id: 'doc2', }, }); expect(getDocNavigation('doc2', 'doc2', undefined)).toEqual({ sidebarName: 'sidebar1', previous: { type: 'doc', id: 'doc1', }, next: undefined, }); expect(getDocNavigation('doc3', 'doc3', undefined)).toEqual({ sidebarName: 'sidebar2', previous: undefined, next: { type: 'doc', id: 'doc4', }, }); expect(getDocNavigation('doc4', 'doc4', undefined)).toEqual({ sidebarName: 'sidebar2', previous: { type: 'doc', id: 'doc3', label: 'Doc 3', }, next: undefined, }); expect(getDocNavigation('doc5', 'doc5', undefined)).toMatchObject({ sidebarName: 'sidebar3', previous: undefined, next: { type: 'category', label: 'S3 SubCategory', }, }); expect(getDocNavigation('doc6', 'doc6', undefined)).toMatchObject({ sidebarName: 'sidebar3', previous: { type: 'category', label: 'S3 SubSubCategory', }, next: { type: 'doc', id: 'doc7', }, }); expect(getDocNavigation('doc7', 'doc7', undefined)).toEqual({ sidebarName: 'sidebar3', previous: { type: 'doc', id: 'doc6', }, next: undefined, }); expect(getDocNavigation('doc3', 'doc3', null)).toEqual({ sidebarName: undefined, previous: undefined, next: undefined, }); expect(() => getDocNavigation('doc3', 'doc3', 'foo'), ).toThrowErrorMatchingInlineSnapshot( `"Doc with ID doc3 wants to display sidebar foo but a sidebar with this name doesn't exist"`, ); expect(getDocNavigation('doc3', 'doc3', 'sidebar1')).toEqual({ sidebarName: 'sidebar1', previous: undefined, next: undefined, }); }); it('getCategoryGeneratedIndexNavigation', async () => { expect( getCategoryGeneratedIndexNavigation('/s3-subcategory-index-permalink'), ).toMatchObject({ sidebarName: 'sidebar3', previous: { type: 'category', label: 'S3 Category', }, next: { type: 'category', label: 'S3 SubSubCategory', }, }); expect( getCategoryGeneratedIndexNavigation('/s3-subsubcategory-index-permalink'), ).toMatchObject({ sidebarName: 'sidebar3', previous: { type: 'category', label: 'S3 SubCategory', }, next: { type: 'doc', id: 'doc6', }, }); }); it('getCategoryGeneratedIndexList', async () => { expect(getCategoryGeneratedIndexList()).toMatchObject([ { type: 'category', label: 'S3 SubCategory', }, { type: 'category', label: 'S3 SubSubCategory', }, { type: 'category', label: 'S4 Category', }, ]); }); it('getFirstLink', () => { expect(getFirstLink('sidebar1')).toEqual({ id: 'doc1', type: 'doc', label: 'doc1', }); expect(getFirstLink('sidebar2')).toEqual({ id: 'doc3', type: 'doc', label: 'Doc 3', }); expect(getFirstLink('sidebar3')).toEqual({ id: 'doc5', type: 'doc', label: 'S3 Category', }); expect(getFirstLink('sidebar4')).toEqual({ type: 'generated-index', permalink: '/s4-category-slug', label: 'S4 Category', }); }); }); describe('collectSidebarDocItems', () => { it('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', () => { it('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', () => { it('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', () => { it('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', () => { it('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('toDocNavigationLink', () => { type TestDoc = Pick; function testDoc(data: TestDoc) { return data as DocMetadataBase; } it('with no front matter', () => { expect( toDocNavigationLink( testDoc({ title: 'Doc Title', permalink: '/docPermalink', frontMatter: {}, }), ), ).toEqual({ title: 'Doc Title', permalink: '/docPermalink', } as PropNavigationLink); }); it('with pagination_label front matter', () => { expect( toDocNavigationLink( testDoc({ title: 'Doc Title', permalink: '/docPermalink', frontMatter: { pagination_label: 'pagination_label', }, }), ), ).toEqual({ title: 'pagination_label', permalink: '/docPermalink', } as PropNavigationLink); }); it('with sidebar_label front matter', () => { expect( toDocNavigationLink( testDoc({ title: 'Doc Title', permalink: '/docPermalink', frontMatter: { sidebar_label: 'sidebar_label', }, }), ), ).toEqual({ title: 'sidebar_label', permalink: '/docPermalink', } as PropNavigationLink); }); it('with pagination_label + sidebar_label front matter', () => { expect( toDocNavigationLink( testDoc({ title: 'Doc Title', permalink: '/docPermalink', frontMatter: { pagination_label: 'pagination_label', sidebar_label: 'sidebar_label', }, }), ), ).toEqual({ title: 'pagination_label', permalink: '/docPermalink', } as PropNavigationLink); }); }); describe('toNavigationLink', () => { type TestDoc = Pick; function testDoc(data: TestDoc) { return {...data, frontMatter: {}} as DocMetadataBase; } const docsById: {[docId: string]: DocMetadataBase} = { doc1: testDoc({ title: 'Doc 1', permalink: '/doc1', }), doc2: testDoc({ title: 'Doc 1', permalink: '/doc1', }), }; it('with doc items', () => { expect(toNavigationLink({type: 'doc', id: 'doc1'}, docsById)).toEqual( toDocNavigationLink(docsById.doc1), ); expect(toNavigationLink({type: 'doc', id: 'doc2'}, docsById)).toEqual( toDocNavigationLink(docsById.doc2), ); expect(() => toNavigationLink({type: 'doc', id: 'doc3'}, docsById), ).toThrowErrorMatchingInlineSnapshot( `"Can't create navigation link: no doc found with id=doc3"`, ); }); it('with category item and doc link', () => { expect( toNavigationLink( { type: 'category', label: 'Category', items: [], link: { type: 'doc', id: 'doc1', }, collapsed: true, collapsible: true, }, docsById, ), ).toEqual(toDocNavigationLink(docsById.doc1)); expect(() => toNavigationLink( { type: 'category', label: 'Category', items: [], link: { type: 'doc', id: 'doc3', }, collapsed: true, collapsible: true, }, docsById, ), ).toThrowErrorMatchingInlineSnapshot( `"Can't create navigation link: no doc found with id=doc3"`, ); }); it('with category item and generated-index link', () => { expect( toNavigationLink( { type: 'category', label: 'Category', items: [], link: { type: 'generated-index', slug: 'slug', permalink: 'generated-index-permalink', }, collapsed: true, collapsible: true, }, docsById, ), ).toEqual({title: 'Category', permalink: 'generated-index-permalink'}); }); });