import { expect } from 'chai' import { matchOutline, nestOutline } from '../../../../frontend/js/features/outline/outline-parser' describe('OutlineParser', function () { describe('matchOutline', function () { it('matches all levels', function () { const content = ` \\book{Book} \\part{Part} \\addpart{Part 2} \\chapter{Chapter} \\addchap{Chapter 2} \\section{Section 1} \\addsect{Section 1b} \\subsection{Subsection} \\subsubsection{Subsubsection} \\section{Section 2} \\subsubsection{Subsubsection without subsection} \\paragraph{a paragraph} Here is some text. \\subparagraph{a subparagraph} Here is some more text. ` const outline = matchOutline(content) expect(outline).to.deep.equal([ { line: 2, title: 'Book', level: 10 }, { line: 3, title: 'Part', level: 20 }, { line: 4, title: 'Part 2', level: 20 }, { line: 5, title: 'Chapter', level: 30 }, { line: 6, title: 'Chapter 2', level: 30 }, { line: 7, title: 'Section 1', level: 40 }, { line: 8, title: 'Section 1b', level: 40 }, { line: 9, title: 'Subsection', level: 50 }, { line: 10, title: 'Subsubsection', level: 60 }, { line: 11, title: 'Section 2', level: 40 }, { line: 12, title: 'Subsubsection without subsection', level: 60 }, { line: 13, title: 'a paragraph', level: 70 }, { line: 14, title: 'a subparagraph', level: 80 } ]) }) it('matches display titles', function () { const content = ` \\section{\\label{foo} Label before} \\section{Label after \\label{foo}} \\section{Label \\label{foo} between} \\section{TT \\texttt{Bar}} \\section{plain title} ` const outline = matchOutline(content) expect(outline).to.deep.equal([ { line: 2, title: ' Label before', level: 40 }, { line: 3, title: 'Label after ', level: 40 }, { line: 4, title: 'Label between', level: 40 }, { line: 5, title: 'TT Bar', level: 40 }, { line: 6, title: 'plain title', level: 40 } ]) }) it('removes spurious commands after title definition', function () { const content = ` \\section{Plain title} more text \\href{link}{link} \\section{\\label{foo} Label before} more text \\href{link}{link} ` const outline = matchOutline(content) expect(outline).to.deep.equal([ { line: 2, title: 'Plain title', level: 40 }, { line: 3, title: ' Label before', level: 40 } ]) }) it('matches empty sections', function () { const outline = matchOutline('\\section{}') expect(outline).to.deep.equal([{ line: 1, title: '', level: 40 }]) }) it('matches indented sections', function () { const outline = matchOutline('\t\\section{Indented}') expect(outline).to.deep.equal([{ line: 1, title: 'Indented', level: 40 }]) }) it('matches unnumbered sections', function () { const outline = matchOutline('\\section*{Unnumbered}') expect(outline).to.deep.equal([ { line: 1, title: 'Unnumbered', level: 40 } ]) }) it('matches short titles', function () { const outline = matchOutline( '\\chapter[Short Title For TOC]{Very Long Title for Text}' ) expect(outline).to.deep.equal([ { line: 1, title: 'Short Title For TOC', level: 30 } ]) }) it('handles spacing', function () { const content = ` \\section {Weird Spacing} \\section * {Weird Spacing Unnumbered} \\section [Weird Spacing for TOC] {Weird Spacing} ` const outline = matchOutline(content) expect(outline).to.deep.equal([ { line: 2, title: 'Weird Spacing', level: 40 }, { line: 3, title: 'Weird Spacing Unnumbered', level: 40 }, { line: 4, title: 'Weird Spacing for TOC', level: 40 } ]) }) it("doesn't match commented lines", function () { const content = ` % \\section{I should not appear in the outline} ` const outline = matchOutline(content) expect(outline).to.deep.equal([]) }) it("doesn't match inline sections", function () { const content = ` I like to write \\section{inline} on one line. ` const outline = matchOutline(content) expect(outline).to.deep.equal([]) }) }) describe('nestOutline', function () { it('matches all levels', function () { const flatOutline = [ { line: 10, title: 'Book', level: 10 }, { line: 20, title: 'Part A', level: 20 }, { line: 30, title: 'Section A 1', level: 40 }, { line: 40, title: 'Subsection A 1 1', level: 50 }, { line: 50, title: 'Subsection A 1 2', level: 50 }, { line: 60, title: 'Section A 2', level: 40 }, { line: 70, title: 'Section A 3', level: 40 }, { line: 80, title: 'Subsection A 3 1', level: 50 }, { line: 90, title: 'Chapter', level: 30 }, { line: 100, title: 'Part B', level: 20 }, { line: 110, title: 'Section 2', level: 40 }, { line: 120, title: 'Subsubsection without subsection', level: 60 } ] const nestedOutline = nestOutline(flatOutline) expect(nestedOutline).to.deep.equal([ { line: 10, title: 'Book', level: 10, children: [ { line: 20, title: 'Part A', level: 20, children: [ { line: 30, title: 'Section A 1', level: 40, children: [ { line: 40, title: 'Subsection A 1 1', level: 50 }, { line: 50, title: 'Subsection A 1 2', level: 50 } ] }, { line: 60, title: 'Section A 2', level: 40 }, { line: 70, title: 'Section A 3', level: 40, children: [{ line: 80, title: 'Subsection A 3 1', level: 50 }] }, { line: 90, title: 'Chapter', level: 30 } ] }, { line: 100, title: 'Part B', level: 20, children: [ { line: 110, title: 'Section 2', level: 40, children: [ { line: 120, title: 'Subsubsection without subsection', level: 60 } ] } ] } ] } ]) }) }) })