1import GithubSlugger from 'github-slugger'; 2 3import { BASE_HEADING_LEVEL, HeadingManager, HeadingType } from './headingManager'; 4 5const SluggerStub: GithubSlugger = { 6 slug: str => str, 7 reset: () => {}, 8}; 9 10describe('HeadingManager tests', () => { 11 test('instantiates properly', () => { 12 const meta = { maxHeadingDepth: 2, headings: [] }; 13 const headingManager = new HeadingManager(SluggerStub, meta); 14 15 expect(headingManager.headings).toEqual([]); 16 expect(headingManager.metadata.headings).toEqual([]); 17 expect(headingManager.maxNestingLevel).toBe(BASE_HEADING_LEVEL + 2); 18 }); 19 20 test('_findMetaForTitle not returning same title twice', () => { 21 const TITLE = 'Some Title'; 22 const meta = { headings: [{ title: TITLE, depth: 1, type: 'text', _processed: true }] }; 23 const headingManager = new HeadingManager(SluggerStub, meta); 24 25 const result = headingManager['findMetaForTitle'](TITLE); 26 expect(result).toBeUndefined(); 27 }); 28 29 test('_findMetaForTitle marks meta as processed', () => { 30 const TITLE = 'Some Title'; 31 const meta = { headings: [{ title: TITLE, depth: 1, type: 'text' }] }; 32 const headingManager = new HeadingManager(SluggerStub, meta); 33 34 const result = headingManager['findMetaForTitle'](TITLE); 35 expect(result?._processed).toBeTruthy(); 36 }); 37}); 38 39describe('HeadingManager.addHeading()', () => { 40 const META_TITLE = 'Meta heading 1'; 41 const META_LEVEL = 3; 42 const meta = { 43 maxHeadingDepth: 3, 44 headings: [{ title: META_TITLE, depth: META_LEVEL, type: 'text' }], 45 }; 46 const headingManager = new HeadingManager(SluggerStub, meta); 47 48 test('finds info from meta', () => { 49 const result = headingManager.addHeading(META_TITLE); 50 51 expect(result.metadata).toBeDefined(); 52 expect(result.title).toBe(META_TITLE); 53 expect(result.level).toBe(META_LEVEL); 54 }); 55 56 test('falls back to base level if unspecified', () => { 57 const result = headingManager.addHeading('title not in meta', undefined); 58 59 expect(result.level).toBe(BASE_HEADING_LEVEL); 60 }); 61 62 test('uses argument level over meta level', () => { 63 headingManager.metadata.headings?.forEach(it => (it._processed = false)); 64 65 const result = headingManager.addHeading(META_TITLE, 4); 66 67 expect(result.level).toBe(4); 68 }); 69 70 test('additional params override anything', () => { 71 const result = headingManager.addHeading('unused', 5, { 72 sidebarType: HeadingType.InlineCode, 73 sidebarTitle: 'The Override', 74 sidebarDepth: 2, // level = 4 75 }); 76 77 expect(result.title).toBe('The Override'); 78 expect(result.level).toBe(4); 79 expect(result.type).toBe(HeadingType.InlineCode); 80 }); 81 82 test('level out of range unlisted', () => { 83 const tooLow = headingManager.addHeading('Too Low', BASE_HEADING_LEVEL - 1); 84 const ok = headingManager.addHeading('OK'); 85 const tooHigh = headingManager.addHeading('Too High', 1337); 86 87 expect(headingManager.headings).toContain(ok); 88 expect(headingManager.headings).not.toContain(tooLow); 89 expect(headingManager.headings).not.toContain(tooHigh); 90 }); 91 92 test('explicitly hidden are unlisted', () => { 93 const result = headingManager.addHeading('The Unlisted', 2, { 94 hideInSidebar: true, 95 }); 96 97 expect(headingManager.headings).not.toContain(result); 98 }); 99}); 100