outline-parser.test.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import { expect } from 'chai'
  2. import {
  3. matchOutline,
  4. nestOutline,
  5. } from '../../../../frontend/js/features/outline/outline-parser'
  6. describe('OutlineParser', function () {
  7. describe('matchOutline', function () {
  8. it('matches all levels', function () {
  9. const content = `
  10. \\book{Book}
  11. \\part{Part}
  12. \\addpart{Part 2}
  13. \\chapter{Chapter}
  14. \\addchap{Chapter 2}
  15. \\section{Section 1}
  16. \\addsec{Section 1b}
  17. \\subsection{Subsection}
  18. \\subsubsection{Subsubsection}
  19. \\section{Section 2}
  20. \\subsubsection{Subsubsection without subsection}
  21. \\paragraph{a paragraph} Here is some text.
  22. \\subparagraph{a subparagraph} Here is some more text.
  23. `
  24. const outline = matchOutline(content)
  25. expect(outline).to.deep.equal([
  26. { line: 2, title: 'Book', level: 10 },
  27. { line: 3, title: 'Part', level: 20 },
  28. { line: 4, title: 'Part 2', level: 20 },
  29. { line: 5, title: 'Chapter', level: 30 },
  30. { line: 6, title: 'Chapter 2', level: 30 },
  31. { line: 7, title: 'Section 1', level: 40 },
  32. { line: 8, title: 'Section 1b', level: 40 },
  33. { line: 9, title: 'Subsection', level: 50 },
  34. { line: 10, title: 'Subsubsection', level: 60 },
  35. { line: 11, title: 'Section 2', level: 40 },
  36. { line: 12, title: 'Subsubsection without subsection', level: 60 },
  37. { line: 13, title: 'a paragraph', level: 70 },
  38. { line: 14, title: 'a subparagraph', level: 80 },
  39. ])
  40. })
  41. it('matches display titles', function () {
  42. const content = `
  43. \\section{\\label{foo} Label before}
  44. \\section{Label after \\label{foo}}
  45. \\section{Label \\label{foo} between}
  46. \\section{TT \\texttt{Bar}}
  47. \\section{plain title}
  48. `
  49. const outline = matchOutline(content)
  50. expect(outline).to.deep.equal([
  51. { line: 2, title: ' Label before', level: 40 },
  52. { line: 3, title: 'Label after ', level: 40 },
  53. { line: 4, title: 'Label between', level: 40 },
  54. { line: 5, title: 'TT Bar', level: 40 },
  55. { line: 6, title: 'plain title', level: 40 },
  56. ])
  57. })
  58. it('removes spurious commands after title definition', function () {
  59. const content = `
  60. \\section{Plain title} more text \\href{link}{link}
  61. \\section{\\label{foo} Label before} more text \\href{link}{link}
  62. `
  63. const outline = matchOutline(content)
  64. expect(outline).to.deep.equal([
  65. { line: 2, title: 'Plain title', level: 40 },
  66. { line: 3, title: ' Label before', level: 40 },
  67. ])
  68. })
  69. it('matches empty sections', function () {
  70. const outline = matchOutline('\\section{}')
  71. expect(outline).to.deep.equal([{ line: 1, title: '', level: 40 }])
  72. })
  73. it('matches indented sections', function () {
  74. const outline = matchOutline('\t\\section{Indented}')
  75. expect(outline).to.deep.equal([{ line: 1, title: 'Indented', level: 40 }])
  76. })
  77. it('matches unnumbered sections', function () {
  78. const outline = matchOutline('\\section*{Unnumbered}')
  79. expect(outline).to.deep.equal([
  80. { line: 1, title: 'Unnumbered', level: 40 },
  81. ])
  82. })
  83. it('matches short titles', function () {
  84. const outline = matchOutline(
  85. '\\chapter[Short Title For TOC]{Very Long Title for Text}'
  86. )
  87. expect(outline).to.deep.equal([
  88. { line: 1, title: 'Short Title For TOC', level: 30 },
  89. ])
  90. })
  91. it('handles spacing', function () {
  92. const content = `
  93. \\section {Weird Spacing}
  94. \\section * {Weird Spacing Unnumbered}
  95. \\section [Weird Spacing for TOC] {Weird Spacing}
  96. `
  97. const outline = matchOutline(content)
  98. expect(outline).to.deep.equal([
  99. { line: 2, title: 'Weird Spacing', level: 40 },
  100. { line: 3, title: 'Weird Spacing Unnumbered', level: 40 },
  101. { line: 4, title: 'Weird Spacing for TOC', level: 40 },
  102. ])
  103. })
  104. it("doesn't match commented lines", function () {
  105. const content = `
  106. % \\section{I should not appear in the outline}
  107. `
  108. const outline = matchOutline(content)
  109. expect(outline).to.deep.equal([])
  110. })
  111. it("doesn't match inline sections", function () {
  112. const content = `
  113. I like to write \\section{inline} on one line.
  114. `
  115. const outline = matchOutline(content)
  116. expect(outline).to.deep.equal([])
  117. })
  118. })
  119. describe('nestOutline', function () {
  120. it('matches all levels', function () {
  121. const flatOutline = [
  122. { line: 10, title: 'Book', level: 10 },
  123. { line: 20, title: 'Part A', level: 20 },
  124. { line: 30, title: 'Section A 1', level: 40 },
  125. { line: 40, title: 'Subsection A 1 1', level: 50 },
  126. { line: 50, title: 'Subsection A 1 2', level: 50 },
  127. { line: 60, title: 'Section A 2', level: 40 },
  128. { line: 70, title: 'Section A 3', level: 40 },
  129. { line: 80, title: 'Subsection A 3 1', level: 50 },
  130. { line: 90, title: 'Chapter', level: 30 },
  131. { line: 100, title: 'Part B', level: 20 },
  132. { line: 110, title: 'Section 2', level: 40 },
  133. { line: 120, title: 'Subsubsection without subsection', level: 60 },
  134. ]
  135. const nestedOutline = nestOutline(flatOutline)
  136. expect(nestedOutline).to.deep.equal([
  137. {
  138. line: 10,
  139. title: 'Book',
  140. level: 10,
  141. children: [
  142. {
  143. line: 20,
  144. title: 'Part A',
  145. level: 20,
  146. children: [
  147. {
  148. line: 30,
  149. title: 'Section A 1',
  150. level: 40,
  151. children: [
  152. { line: 40, title: 'Subsection A 1 1', level: 50 },
  153. { line: 50, title: 'Subsection A 1 2', level: 50 },
  154. ],
  155. },
  156. { line: 60, title: 'Section A 2', level: 40 },
  157. {
  158. line: 70,
  159. title: 'Section A 3',
  160. level: 40,
  161. children: [
  162. { line: 80, title: 'Subsection A 3 1', level: 50 },
  163. ],
  164. },
  165. { line: 90, title: 'Chapter', level: 30 },
  166. ],
  167. },
  168. {
  169. line: 100,
  170. title: 'Part B',
  171. level: 20,
  172. children: [
  173. {
  174. line: 110,
  175. title: 'Section 2',
  176. level: 40,
  177. children: [
  178. {
  179. line: 120,
  180. title: 'Subsubsection without subsection',
  181. level: 60,
  182. },
  183. ],
  184. },
  185. ],
  186. },
  187. ],
  188. },
  189. ])
  190. })
  191. })
  192. })