errors-spec.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. /* global expect: false */
  2. /* global it: false */
  3. /* global describe: false */
  4. describe("Parser:", function() {
  5. describe("#handleInfixNodes", function() {
  6. it("rejects repeated infix operators", function() {
  7. expect`1\over 2\over 3`.toFailWithParseError(
  8. "only one infix operator per group at position 9: " +
  9. "1\\over 2\\̲o̲v̲e̲r̲ ̲3");
  10. });
  11. it("rejects conflicting infix operators", function() {
  12. expect`1\over 2\choose 3`.toFailWithParseError(
  13. "only one infix operator per group at position 9: " +
  14. "1\\over 2\\̲c̲h̲o̲o̲s̲e̲ ̲3");
  15. });
  16. });
  17. describe("#handleSupSubscript", function() {
  18. it("rejects ^ at end of group", function() {
  19. expect`{1^}`.toFailWithParseError(
  20. "Expected group after '^' at position 3: {1^̲}");
  21. });
  22. it("rejects _ at end of input", function() {
  23. expect`1_`.toFailWithParseError(
  24. "Expected group after '_' at position 2: 1_̲");
  25. });
  26. it("rejects \\sqrt as argument to ^", function() {
  27. expect`1^\sqrt{2}`.toFailWithParseError(
  28. "Got function '\\sqrt' with no arguments as superscript" +
  29. " at position 3: 1^\\̲s̲q̲r̲t̲{2}");
  30. });
  31. });
  32. describe("#parseAtom", function() {
  33. it("rejects \\limits without operator", function() {
  34. expect`\alpha\limits\omega`.toFailWithParseError(
  35. "Limit controls must follow a math operator" +
  36. " at position 7: \\alpha\\̲l̲i̲m̲i̲t̲s̲\\omega");
  37. });
  38. it("rejects \\limits at the beginning of the input", function() {
  39. expect`\limits\omega`.toFailWithParseError(
  40. "Limit controls must follow a math operator" +
  41. " at position 1: \\̲l̲i̲m̲i̲t̲s̲\\omega");
  42. });
  43. it("rejects double superscripts", function() {
  44. expect`1^2^3`.toFailWithParseError(
  45. "Double superscript at position 4: 1^2^̲3");
  46. expect`1^{2+3}_4^5`.toFailWithParseError(
  47. "Double superscript at position 10: 1^{2+3}_4^̲5");
  48. });
  49. it("rejects double superscripts involving primes", function() {
  50. expect`1'_2^3`.toFailWithParseError(
  51. "Double superscript at position 5: 1'_2^̲3");
  52. expect`1^2'`.toFailWithParseError(
  53. "Double superscript at position 4: 1^2'̲");
  54. expect`1^2_3'`.toFailWithParseError(
  55. "Double superscript at position 6: 1^2_3'̲");
  56. expect`1'_2'`.toFailWithParseError(
  57. "Double superscript at position 5: 1'_2'̲");
  58. });
  59. it("rejects double subscripts", function() {
  60. expect`1_2_3`.toFailWithParseError(
  61. "Double subscript at position 4: 1_2_̲3");
  62. expect`1_{2+3}^4_5`.toFailWithParseError(
  63. "Double subscript at position 10: 1_{2+3}^4_̲5");
  64. });
  65. });
  66. describe("#parseImplicitGroup", function() {
  67. it("reports unknown environments", function() {
  68. expect`\begin{foo}bar\end{foo}`.toFailWithParseError(
  69. "No such environment: foo at position 7:" +
  70. " \\begin{̲f̲o̲o̲}̲bar\\end{foo}");
  71. });
  72. it("reports mismatched environments", function() {
  73. expect`\begin{pmatrix}1&2\\3&4\end{bmatrix}+5`
  74. .toFailWithParseError(
  75. "Mismatch: \\begin{pmatrix} matched by \\end{bmatrix}" +
  76. " at position 24: …matrix}1&2\\\\3&4\\̲e̲n̲d̲{bmatrix}+5");
  77. });
  78. });
  79. describe("#parseFunction", function() {
  80. it("rejects math-mode functions in text mode", function() {
  81. expect`\text{\sqrt2 is irrational}`.toFailWithParseError(
  82. "Can't use function '\\sqrt' in text mode" +
  83. " at position 7: \\text{\\̲s̲q̲r̲t̲2 is irrational…");
  84. });
  85. it("rejects text-mode-only functions in math mode", function() {
  86. expect`\'echec`.toFailWithParseError(
  87. "Can't use function '\\'' in math mode" +
  88. " at position 1: \\̲'̲echec");
  89. });
  90. });
  91. describe("#parseArguments", function() {
  92. it("complains about missing argument at end of input", function() {
  93. expect`2\sqrt`.toFailWithParseError(
  94. "Expected group after '\\sqrt' at end of input: 2\\sqrt");
  95. });
  96. it("complains about missing argument at end of group", function() {
  97. expect`1^{2\sqrt}`.toFailWithParseError(
  98. "Expected group after '\\sqrt'" +
  99. " at position 10: 1^{2\\sqrt}̲");
  100. });
  101. it("complains about functions as arguments to others", function() {
  102. expect`\sqrt\over2`.toFailWithParseError(
  103. "Got function '\\over' with no arguments as argument to" +
  104. " '\\sqrt' at position 6: \\sqrt\\̲o̲v̲e̲r̲2");
  105. });
  106. });
  107. describe("#parseGroup", function() {
  108. it("complains about undefined control sequence", function() {
  109. expect`\xyz`.toFailWithParseError(
  110. "Undefined control sequence: \\xyz" +
  111. " at position 1: \\̲x̲y̲z̲");
  112. });
  113. });
  114. describe("#verb", function() {
  115. it("complains about mismatched \\verb with end of string", function() {
  116. expect`\verb|hello`.toFailWithParseError(
  117. "\\verb ended by end of line instead of matching delimiter");
  118. });
  119. it("complains about mismatched \\verb with end of line", function() {
  120. expect("\\verb|hello\nworld|").toFailWithParseError(
  121. "\\verb ended by end of line instead of matching delimiter");
  122. });
  123. });
  124. });
  125. describe("Parser.expect calls:", function() {
  126. describe("#parseInput expecting EOF", function() {
  127. it("complains about extra }", function() {
  128. expect`{1+2}}`.toFailWithParseError(
  129. "Expected 'EOF', got '}' at position 6: {1+2}}̲");
  130. });
  131. it("complains about extra \\end", function() {
  132. expect`x\end{matrix}`.toFailWithParseError(
  133. "Expected 'EOF', got '\\end' at position 2:" +
  134. " x\\̲e̲n̲d̲{matrix}");
  135. });
  136. it("complains about top-level &", function() {
  137. expect`1&2`.toFailWithParseError(
  138. "Expected 'EOF', got '&' at position 2: 1&̲2");
  139. });
  140. });
  141. describe("#parseImplicitGroup expecting \\right", function() {
  142. it("rejects missing \\right", function() {
  143. expect`\left(1+2)`.toFailWithParseError(
  144. "Expected '\\right', got 'EOF' at end of input:" +
  145. " \\left(1+2)");
  146. });
  147. it("rejects incorrectly scoped \\right", function() {
  148. expect`{\left(1+2}\right)`.toFailWithParseError(
  149. "Expected '\\right', got '}' at position 11:" +
  150. " {\\left(1+2}̲\\right)");
  151. });
  152. });
  153. // Can't test the expectation for \end after an environment
  154. // since all existing arrays use parseArray which has its own expectation.
  155. describe("#parseSpecialGroup expecting braces", function() {
  156. it("complains about missing { for color", function() {
  157. expect`\textcolor#ffffff{text}`.toFailWithParseError(
  158. "Expected '{', got '#' at position 11:" +
  159. " \\textcolor#̲ffffff{text}");
  160. });
  161. it("complains about missing { for size", function() {
  162. expect`\rule{1em}[2em]`.toFailWithParseError(
  163. "Invalid size: '[' at position 11: \\rule{1em}[̲2em]");
  164. });
  165. // Can't test for the [ of an optional group since it's optional
  166. it("complains about missing } for color", function() {
  167. expect`\textcolor{#ffffff{text}`.toFailWithParseError(
  168. "Invalid color: '#ffffff{text' at position 12:" +
  169. " \\textcolor{#̲f̲f̲f̲f̲f̲f̲{̲t̲e̲x̲t̲}");
  170. });
  171. it("complains about missing ] for size", function() {
  172. expect`\rule[1em{2em}{3em}`.toFailWithParseError(
  173. "Unexpected end of input in size" +
  174. " at position 7: \\rule[1̲e̲m̲{̲2̲e̲m̲}̲{̲3̲e̲m̲}̲");
  175. });
  176. it("complains about missing ] for size at end of input", function() {
  177. expect`\rule[1em`.toFailWithParseError(
  178. "Unexpected end of input in size" +
  179. " at position 7: \\rule[1̲e̲m̲");
  180. });
  181. it("complains about missing } for color at end of input", function() {
  182. expect`\textcolor{#123456`.toFailWithParseError(
  183. "Unexpected end of input in color" +
  184. " at position 12: \\textcolor{#̲1̲2̲3̲4̲5̲6̲");
  185. });
  186. });
  187. describe("#parseGroup expecting }", function() {
  188. it("at end of file", function() {
  189. expect`\sqrt{2`.toFailWithParseError(
  190. "Expected '}', got 'EOF' at end of input: \\sqrt{2");
  191. });
  192. });
  193. describe("#parseOptionalGroup expecting ]", function() {
  194. it("at end of file", function() {
  195. expect`\sqrt[3`.toFailWithParseError(
  196. "Expected ']', got 'EOF' at end of input: \\sqrt[3");
  197. });
  198. it("before group", function() {
  199. expect`\sqrt[3{2}`.toFailWithParseError(
  200. "Expected ']', got 'EOF' at end of input: \\sqrt[3{2}");
  201. });
  202. });
  203. });
  204. describe("environments.js:", function() {
  205. describe("parseArray", function() {
  206. it("rejects missing \\end", function() {
  207. expect`\begin{matrix}1`.toFailWithParseError(
  208. "Expected & or \\\\ or \\cr or \\end at end of input:" +
  209. " \\begin{matrix}1");
  210. });
  211. it("rejects incorrectly scoped \\end", function() {
  212. expect`{\begin{matrix}1}\end{matrix}`.toFailWithParseError(
  213. "Expected & or \\\\ or \\cr or \\end at position 17:" +
  214. " …\\begin{matrix}1}̲\\end{matrix}");
  215. });
  216. });
  217. describe("array environment", function() {
  218. it("rejects unknown column types", function() {
  219. expect`\begin{array}{cba}\end{array}`.toFailWithParseError(
  220. "Unknown column alignment: b at position 16:" +
  221. " \\begin{array}{cb̲a}\\end{array}");
  222. });
  223. });
  224. });
  225. describe("functions.js:", function() {
  226. describe("delimiter functions", function() {
  227. it("reject invalid opening delimiters", function() {
  228. expect`\bigl 1 + 2 \bigr`.toFailWithParseError(
  229. "Invalid delimiter: '1' after '\\bigl' at position 7:" +
  230. " \\bigl 1̲ + 2 \\bigr");
  231. });
  232. it("reject invalid closing delimiters", function() {
  233. expect`\bigl(1+2\bigr=3`.toFailWithParseError(
  234. "Invalid delimiter: '=' after '\\bigr' at position 15:" +
  235. " \\bigl(1+2\\bigr=̲3");
  236. });
  237. });
  238. describe("\\begin and \\end", function() {
  239. it("reject invalid environment names", function() {
  240. expect`\begin x\end y`.toFailWithParseError(
  241. "Invalid environment name at position 8: \\begin x̲\\end y");
  242. });
  243. });
  244. });
  245. describe("Lexer:", function() {
  246. describe("#_innerLex", function() {
  247. it("rejects lone surrogate char", function() {
  248. expect("\udcba ").toFailWithParseError(
  249. "Unexpected character: '\udcba' at position 1:" +
  250. " \udcba\u0332 ");
  251. });
  252. it("rejects lone backslash at end of input", function() {
  253. expect("\\").toFailWithParseError(
  254. "Unexpected character: '\\' at position 1: \\̲");
  255. });
  256. });
  257. describe("#_innerLexColor", function() {
  258. it("reject 3-digit hex notation without #", function() {
  259. expect`\textcolor{1a2}{foo}`.toFailWithParseError(
  260. "Invalid color: '1a2'" +
  261. " at position 12: \\textcolor{1̲a̲2̲}{foo}");
  262. });
  263. });
  264. describe("#_innerLexSize", function() {
  265. it("reject size without unit", function() {
  266. expect`\rule{0}{2em}`.toFailWithParseError(
  267. "Invalid size: '0' at position 7: \\rule{0̲}{2em}");
  268. });
  269. it("reject size with bogus unit", function() {
  270. expect`\rule{1au}{2em}`.toFailWithParseError(
  271. "Invalid unit: 'au' at position 7: \\rule{1̲a̲u̲}{2em}");
  272. });
  273. it("reject size without number", function() {
  274. expect`\rule{em}{2em}`.toFailWithParseError(
  275. "Invalid size: 'em' at position 7: \\rule{e̲m̲}{2em}");
  276. });
  277. });
  278. });
  279. describe("Unicode accents", function() {
  280. it("should return error for invalid combining characters", function() {
  281. expect("A\u0328").toFailWithParseError(
  282. "Unknown accent ' ̨' at position 1: Ą̲̲");
  283. });
  284. });