katex-spec.js (69779B)
1 /* eslint max-len:0 */ 2 /* global beforeEach: false */ 3 /* global jasmine: false */ 4 /* global expect: false */ 5 /* global it: false */ 6 /* global describe: false */ 7 8 const buildMathML = require("../src/buildMathML"); 9 const buildTree = require("../src/buildTree"); 10 const katex = require("../katex"); 11 const ParseError = require("../src/ParseError"); 12 const parseTree = require("../src/parseTree"); 13 const Options = require("../src/Options"); 14 const Settings = require("../src/Settings"); 15 const Style = require("../src/Style"); 16 17 const defaultSettings = new Settings({}); 18 const defaultOptions = new Options({ 19 style: Style.TEXT, 20 size: "size5", 21 }); 22 23 const _getBuilt = function(expr, settings) { 24 const usedSettings = settings ? settings : defaultSettings; 25 const parsedTree = parseTree(expr, usedSettings); 26 const rootNode = buildTree(parsedTree, expr, usedSettings); 27 28 // grab the root node of the HTML rendering 29 const builtHTML = rootNode.children[1]; 30 31 // Remove the outer .katex and .katex-inner layers 32 return builtHTML.children[2].children; 33 }; 34 35 /** 36 * Return the root node of the rendered HTML. 37 * @param expr 38 * @param settings 39 * @returns {Object} 40 */ 41 const getBuilt = function(expr, settings) { 42 const usedSettings = settings ? settings : defaultSettings; 43 expect(expr).toBuild(usedSettings); 44 return _getBuilt(expr, settings); 45 }; 46 47 /** 48 * Return the root node of the parse tree. 49 * @param expr 50 * @param settings 51 * @returns {Object} 52 */ 53 const getParsed = function(expr, settings) { 54 const usedSettings = settings ? settings : defaultSettings; 55 56 expect(expr).toParse(usedSettings); 57 return parseTree(expr, usedSettings); 58 }; 59 60 const stripPositions = function(expr) { 61 if (typeof expr !== "object" || expr === null) { 62 return expr; 63 } 64 if (expr.lexer && typeof expr.start === "number") { 65 delete expr.lexer; 66 delete expr.start; 67 delete expr.end; 68 } 69 Object.keys(expr).forEach(function(key) { 70 stripPositions(expr[key]); 71 }); 72 return expr; 73 }; 74 75 const parseAndSetResult = function(expr, result, settings) { 76 try { 77 return parseTree(expr, settings || defaultSettings); 78 } catch (e) { 79 result.pass = false; 80 if (e instanceof ParseError) { 81 result.message = "'" + expr + "' failed " + 82 "parsing with error: " + e.message; 83 } else { 84 result.message = "'" + expr + "' failed " + 85 "parsing with unknown error: " + e.message; 86 } 87 } 88 }; 89 90 beforeEach(function() { 91 jasmine.addMatchers({ 92 93 toParse: function() { 94 return { 95 compare: function(actual, settings) { 96 const usedSettings = settings ? settings : defaultSettings; 97 98 const result = { 99 pass: true, 100 message: "'" + actual + "' succeeded parsing", 101 }; 102 parseAndSetResult(actual, result, usedSettings); 103 return result; 104 }, 105 }; 106 }, 107 108 toNotParse: function() { 109 return { 110 compare: function(actual, settings) { 111 const usedSettings = settings ? settings : defaultSettings; 112 113 const result = { 114 pass: false, 115 message: "Expected '" + actual + "' to fail " + 116 "parsing, but it succeeded", 117 }; 118 119 try { 120 parseTree(actual, usedSettings); 121 } catch (e) { 122 if (e instanceof ParseError) { 123 result.pass = true; 124 result.message = "'" + actual + "' correctly " + 125 "didn't parse with error: " + e.message; 126 } else { 127 result.message = "'" + actual + "' failed " + 128 "parsing with unknown error: " + e.message; 129 } 130 } 131 132 return result; 133 }, 134 }; 135 }, 136 137 toBuild: function() { 138 return { 139 compare: function(actual, settings) { 140 const usedSettings = settings ? settings : defaultSettings; 141 142 const result = { 143 pass: true, 144 message: "'" + actual + "' succeeded in building", 145 }; 146 147 expect(actual).toParse(usedSettings); 148 149 try { 150 _getBuilt(actual, settings); 151 } catch (e) { 152 result.pass = false; 153 if (e instanceof ParseError) { 154 result.message = "'" + actual + "' failed to " + 155 "build with error: " + e.message; 156 } else { 157 result.message = "'" + actual + "' failed " + 158 "building with unknown error: " + e.message; 159 } 160 } 161 162 return result; 163 }, 164 }; 165 }, 166 167 toParseLike: function(util, baton) { 168 return { 169 compare: function(actual, expected) { 170 const result = { 171 pass: true, 172 message: "Parse trees of '" + actual + 173 "' and '" + expected + "' are equivalent", 174 }; 175 176 const actualTree = parseAndSetResult(actual, result); 177 if (!actualTree) { 178 return result; 179 } 180 const expectedTree = parseAndSetResult(expected, result); 181 if (!expectedTree) { 182 return result; 183 } 184 stripPositions(actualTree); 185 stripPositions(expectedTree); 186 if (!util.equals(actualTree, expectedTree, baton)) { 187 result.pass = false; 188 result.message = "Parse trees of '" + actual + 189 "' and '" + expected + "' are not equivalent"; 190 } 191 return result; 192 }, 193 }; 194 }, 195 196 }); 197 }); 198 199 describe("A parser", function() { 200 it("should not fail on an empty string", function() { 201 expect("").toParse(); 202 }); 203 204 it("should ignore whitespace", function() { 205 const parseA = stripPositions(getParsed(" x y ")); 206 const parseB = stripPositions(getParsed("xy")); 207 expect(parseA).toEqual(parseB); 208 }); 209 }); 210 211 describe("An ord parser", function() { 212 const expression = "1234|/@.\"`abcdefgzABCDEFGZ"; 213 214 it("should not fail", function() { 215 expect(expression).toParse(); 216 }); 217 218 it("should build a list of ords", function() { 219 const parse = getParsed(expression); 220 221 expect(parse).toBeTruthy(); 222 223 for (let i = 0; i < parse.length; i++) { 224 const group = parse[i]; 225 expect(group.type).toMatch("ord"); 226 } 227 }); 228 229 it("should parse the right number of ords", function() { 230 const parse = getParsed(expression); 231 232 expect(parse.length).toBe(expression.length); 233 }); 234 }); 235 236 describe("A bin parser", function() { 237 const expression = "+-*\\cdot\\pm\\div"; 238 239 it("should not fail", function() { 240 expect(expression).toParse(); 241 }); 242 243 it("should build a list of bins", function() { 244 const parse = getParsed(expression); 245 expect(parse).toBeTruthy(); 246 247 for (let i = 0; i < parse.length; i++) { 248 const group = parse[i]; 249 expect(group.type).toEqual("bin"); 250 } 251 }); 252 }); 253 254 describe("A rel parser", function() { 255 const expression = "=<>\\leq\\geq\\neq\\nleq\\ngeq\\cong"; 256 257 it("should not fail", function() { 258 expect(expression).toParse(); 259 }); 260 261 it("should build a list of rels", function() { 262 const parse = getParsed(expression); 263 expect(parse).toBeTruthy(); 264 265 for (let i = 0; i < parse.length; i++) { 266 const group = parse[i]; 267 expect(group.type).toEqual("rel"); 268 } 269 }); 270 }); 271 272 describe("A punct parser", function() { 273 const expression = ",;\\colon"; 274 275 it("should not fail", function() { 276 expect(expression).toParse(); 277 }); 278 279 it("should build a list of puncts", function() { 280 const parse = getParsed(expression); 281 expect(parse).toBeTruthy(); 282 283 for (let i = 0; i < parse.length; i++) { 284 const group = parse[i]; 285 expect(group.type).toEqual("punct"); 286 } 287 }); 288 }); 289 290 describe("An open parser", function() { 291 const expression = "(["; 292 293 it("should not fail", function() { 294 expect(expression).toParse(); 295 }); 296 297 it("should build a list of opens", function() { 298 const parse = getParsed(expression); 299 expect(parse).toBeTruthy(); 300 301 for (let i = 0; i < parse.length; i++) { 302 const group = parse[i]; 303 expect(group.type).toEqual("open"); 304 } 305 }); 306 }); 307 308 describe("A close parser", function() { 309 const expression = ")]?!"; 310 311 it("should not fail", function() { 312 expect(expression).toParse(); 313 }); 314 315 it("should build a list of closes", function() { 316 const parse = getParsed(expression); 317 expect(parse).toBeTruthy(); 318 319 for (let i = 0; i < parse.length; i++) { 320 const group = parse[i]; 321 expect(group.type).toEqual("close"); 322 } 323 }); 324 }); 325 326 describe("A \\KaTeX parser", function() { 327 it("should not fail", function() { 328 expect("\\KaTeX").toParse(); 329 }); 330 }); 331 332 describe("A subscript and superscript parser", function() { 333 it("should not fail on superscripts", function() { 334 expect("x^2").toParse(); 335 }); 336 337 it("should not fail on subscripts", function() { 338 expect("x_3").toParse(); 339 }); 340 341 it("should not fail on both subscripts and superscripts", function() { 342 expect("x^2_3").toParse(); 343 344 expect("x_2^3").toParse(); 345 }); 346 347 it("should not fail when there is no nucleus", function() { 348 expect("^3").toParse(); 349 expect("_2").toParse(); 350 expect("^3_2").toParse(); 351 expect("_2^3").toParse(); 352 }); 353 354 it("should produce supsubs for superscript", function() { 355 const parse = getParsed("x^2")[0]; 356 357 expect(parse.type).toBe("supsub"); 358 expect(parse.value.base).toBeDefined(); 359 expect(parse.value.sup).toBeDefined(); 360 expect(parse.value.sub).toBeUndefined(); 361 }); 362 363 it("should produce supsubs for subscript", function() { 364 const parse = getParsed("x_3")[0]; 365 366 expect(parse.type).toBe("supsub"); 367 expect(parse.value.base).toBeDefined(); 368 expect(parse.value.sub).toBeDefined(); 369 expect(parse.value.sup).toBeUndefined(); 370 }); 371 372 it("should produce supsubs for ^_", function() { 373 const parse = getParsed("x^2_3")[0]; 374 375 expect(parse.type).toBe("supsub"); 376 expect(parse.value.base).toBeDefined(); 377 expect(parse.value.sup).toBeDefined(); 378 expect(parse.value.sub).toBeDefined(); 379 }); 380 381 it("should produce supsubs for _^", function() { 382 const parse = getParsed("x_3^2")[0]; 383 384 expect(parse.type).toBe("supsub"); 385 expect(parse.value.base).toBeDefined(); 386 expect(parse.value.sup).toBeDefined(); 387 expect(parse.value.sub).toBeDefined(); 388 }); 389 390 it("should produce the same thing regardless of order", function() { 391 const parseA = stripPositions(getParsed("x^2_3")); 392 const parseB = stripPositions(getParsed("x_3^2")); 393 394 expect(parseA).toEqual(parseB); 395 }); 396 397 it("should not parse double subscripts or superscripts", function() { 398 expect("x^x^x").toNotParse(); 399 400 expect("x_x_x").toNotParse(); 401 402 expect("x_x^x_x").toNotParse(); 403 404 expect("x_x^x^x").toNotParse(); 405 406 expect("x^x_x_x").toNotParse(); 407 408 expect("x^x_x^x").toNotParse(); 409 }); 410 411 it("should work correctly with {}s", function() { 412 expect("x^{2+3}").toParse(); 413 414 expect("x_{3-2}").toParse(); 415 416 expect("x^{2+3}_3").toParse(); 417 418 expect("x^2_{3-2}").toParse(); 419 420 expect("x^{2+3}_{3-2}").toParse(); 421 422 expect("x_{3-2}^{2+3}").toParse(); 423 424 expect("x_3^{2+3}").toParse(); 425 426 expect("x_{3-2}^2").toParse(); 427 }); 428 429 it("should work with nested super/subscripts", function() { 430 expect("x^{x^x}").toParse(); 431 expect("x^{x_x}").toParse(); 432 expect("x_{x^x}").toParse(); 433 expect("x_{x_x}").toParse(); 434 }); 435 }); 436 437 describe("A subscript and superscript tree-builder", function() { 438 it("should not fail when there is no nucleus", function() { 439 expect("^3").toBuild(); 440 expect("_2").toBuild(); 441 expect("^3_2").toBuild(); 442 expect("_2^3").toBuild(); 443 }); 444 }); 445 446 describe("A parser with limit controls", function() { 447 it("should fail when the limit control is not preceded by an op node", function() { 448 expect("3\\nolimits_2^2").toNotParse(); 449 expect("\\sqrt\\limits_2^2").toNotParse(); 450 expect("45 +\\nolimits 45").toNotParse(); 451 }); 452 453 it("should parse when the limit control directly follows an op node", function() { 454 expect("\\int\\limits_2^2 3").toParse(); 455 expect("\\sum\\nolimits_3^4 4").toParse(); 456 }); 457 458 it("should parse when the limit control is in the sup/sub area of an op node", function() { 459 expect("\\int_2^2\\limits").toParse(); 460 expect("\\int^2\\nolimits_2").toParse(); 461 expect("\\int_2\\limits^2").toParse(); 462 }); 463 464 it("should allow multiple limit controls in the sup/sub area of an op node", function() { 465 expect("\\int_2\\nolimits^2\\limits 3").toParse(); 466 expect("\\int\\nolimits\\limits_2^2").toParse(); 467 expect("\\int\\limits\\limits\\limits_2^2").toParse(); 468 }); 469 470 it("should have the rightmost limit control determine the limits property " + 471 "of the preceding op node", function() { 472 473 let parsedInput = getParsed("\\int\\nolimits\\limits_2^2"); 474 expect(parsedInput[0].value.base.value.limits).toBe(true); 475 476 parsedInput = getParsed("\\int\\limits_2\\nolimits^2"); 477 expect(parsedInput[0].value.base.value.limits).toBe(false); 478 }); 479 }); 480 481 describe("A group parser", function() { 482 it("should not fail", function() { 483 expect("{xy}").toParse(); 484 }); 485 486 it("should produce a single ord", function() { 487 const parse = getParsed("{xy}"); 488 489 expect(parse.length).toBe(1); 490 491 const ord = parse[0]; 492 493 expect(ord.type).toMatch("ord"); 494 expect(ord.value).toBeTruthy(); 495 }); 496 }); 497 498 describe("An implicit group parser", function() { 499 it("should not fail", function() { 500 expect("\\Large x").toParse(); 501 expect("abc {abc \\Large xyz} abc").toParse(); 502 }); 503 504 it("should produce a single object", function() { 505 const parse = getParsed("\\Large abc"); 506 507 expect(parse.length).toBe(1); 508 509 const sizing = parse[0]; 510 511 expect(sizing.type).toEqual("sizing"); 512 expect(sizing.value).toBeTruthy(); 513 }); 514 515 it("should apply only after the function", function() { 516 const parse = getParsed("a \\Large abc"); 517 518 expect(parse.length).toBe(2); 519 520 const sizing = parse[1]; 521 522 expect(sizing.type).toEqual("sizing"); 523 expect(sizing.value.value.length).toBe(3); 524 }); 525 526 it("should stop at the ends of groups", function() { 527 const parse = getParsed("a { b \\Large c } d"); 528 529 const group = parse[1]; 530 const sizing = group.value[1]; 531 532 expect(sizing.type).toEqual("sizing"); 533 expect(sizing.value.value.length).toBe(1); 534 }); 535 }); 536 537 describe("A function parser", function() { 538 it("should parse no argument functions", function() { 539 expect("\\div").toParse(); 540 }); 541 542 it("should parse 1 argument functions", function() { 543 expect("\\blue x").toParse(); 544 }); 545 546 it("should parse 2 argument functions", function() { 547 expect("\\frac 1 2").toParse(); 548 }); 549 550 it("should not parse 1 argument functions with no arguments", function() { 551 expect("\\blue").toNotParse(); 552 }); 553 554 it("should not parse 2 argument functions with 0 or 1 arguments", function() { 555 expect("\\frac").toNotParse(); 556 557 expect("\\frac 1").toNotParse(); 558 }); 559 560 it("should not parse a function with text right after it", function() { 561 expect("\\redx").toNotParse(); 562 }); 563 564 it("should parse a function with a number right after it", function() { 565 expect("\\frac12").toParse(); 566 }); 567 568 it("should parse some functions with text right after it", function() { 569 expect("\\;x").toParse(); 570 }); 571 }); 572 573 describe("A frac parser", function() { 574 const expression = "\\frac{x}{y}"; 575 const dfracExpression = "\\dfrac{x}{y}"; 576 const tfracExpression = "\\tfrac{x}{y}"; 577 578 it("should not fail", function() { 579 expect(expression).toParse(); 580 }); 581 582 it("should produce a frac", function() { 583 const parse = getParsed(expression)[0]; 584 585 expect(parse.type).toEqual("genfrac"); 586 expect(parse.value.numer).toBeDefined(); 587 expect(parse.value.denom).toBeDefined(); 588 }); 589 590 it("should also parse dfrac and tfrac", function() { 591 expect(dfracExpression).toParse(); 592 593 expect(tfracExpression).toParse(); 594 }); 595 596 it("should parse dfrac and tfrac as fracs", function() { 597 const dfracParse = getParsed(dfracExpression)[0]; 598 599 expect(dfracParse.type).toEqual("genfrac"); 600 expect(dfracParse.value.numer).toBeDefined(); 601 expect(dfracParse.value.denom).toBeDefined(); 602 603 const tfracParse = getParsed(tfracExpression)[0]; 604 605 expect(tfracParse.type).toEqual("genfrac"); 606 expect(tfracParse.value.numer).toBeDefined(); 607 expect(tfracParse.value.denom).toBeDefined(); 608 }); 609 610 it("should parse atop", function() { 611 const parse = getParsed("x \\atop y")[0]; 612 613 expect(parse.type).toEqual("genfrac"); 614 expect(parse.value.numer).toBeDefined(); 615 expect(parse.value.denom).toBeDefined(); 616 expect(parse.value.hasBarLine).toEqual(false); 617 }); 618 }); 619 620 describe("An over parser", function() { 621 const simpleOver = "1 \\over x"; 622 const complexOver = "1+2i \\over 3+4i"; 623 624 it("should not fail", function() { 625 expect(simpleOver).toParse(); 626 expect(complexOver).toParse(); 627 }); 628 629 it("should produce a frac", function() { 630 let parse; 631 632 parse = getParsed(simpleOver)[0]; 633 634 expect(parse.type).toEqual("genfrac"); 635 expect(parse.value.numer).toBeDefined(); 636 expect(parse.value.denom).toBeDefined(); 637 638 parse = getParsed(complexOver)[0]; 639 640 expect(parse.type).toEqual("genfrac"); 641 expect(parse.value.numer).toBeDefined(); 642 expect(parse.value.denom).toBeDefined(); 643 }); 644 645 it("should create a numerator from the atoms before \\over", function() { 646 const parse = getParsed(complexOver)[0]; 647 648 const numer = parse.value.numer; 649 expect(numer.value.length).toEqual(4); 650 }); 651 652 it("should create a demonimator from the atoms after \\over", function() { 653 const parse = getParsed(complexOver)[0]; 654 655 const denom = parse.value.numer; 656 expect(denom.value.length).toEqual(4); 657 }); 658 659 it("should handle empty numerators", function() { 660 const emptyNumerator = "\\over x"; 661 const parse = getParsed(emptyNumerator)[0]; 662 expect(parse.type).toEqual("genfrac"); 663 expect(parse.value.numer).toBeDefined(); 664 expect(parse.value.denom).toBeDefined(); 665 }); 666 667 it("should handle empty denominators", function() { 668 const emptyDenominator = "1 \\over"; 669 const parse = getParsed(emptyDenominator)[0]; 670 expect(parse.type).toEqual("genfrac"); 671 expect(parse.value.numer).toBeDefined(); 672 expect(parse.value.denom).toBeDefined(); 673 }); 674 675 it("should handle \\displaystyle correctly", function() { 676 const displaystyleExpression = "\\displaystyle 1 \\over 2"; 677 const parse = getParsed(displaystyleExpression)[0]; 678 expect(parse.type).toEqual("genfrac"); 679 expect(parse.value.numer.value[0].type).toEqual("styling"); 680 expect(parse.value.denom).toBeDefined(); 681 }); 682 683 it("should handle \\textstyle correctly", function() { 684 expect("\\textstyle 1 \\over 2") 685 .toParseLike("\\frac{\\textstyle 1}{2}"); 686 expect("{\\textstyle 1} \\over 2") 687 .toParseLike("\\frac{\\textstyle 1}{2}"); 688 }); 689 690 it("should handle nested factions", function() { 691 const nestedOverExpression = "{1 \\over 2} \\over 3"; 692 const parse = getParsed(nestedOverExpression)[0]; 693 expect(parse.type).toEqual("genfrac"); 694 expect(parse.value.numer.value[0].type).toEqual("genfrac"); 695 expect(parse.value.numer.value[0].value.numer.value[0].value).toEqual("1"); 696 expect(parse.value.numer.value[0].value.denom.value[0].value).toEqual("2"); 697 expect(parse.value.denom).toBeDefined(); 698 expect(parse.value.denom.value[0].value).toEqual("3"); 699 }); 700 701 it("should fail with multiple overs in the same group", function() { 702 const badMultipleOvers = "1 \\over 2 + 3 \\over 4"; 703 expect(badMultipleOvers).toNotParse(); 704 705 const badOverChoose = "1 \\over 2 \\choose 3"; 706 expect(badOverChoose).toNotParse(); 707 }); 708 }); 709 710 describe("A sizing parser", function() { 711 const sizeExpression = "\\Huge{x}\\small{x}"; 712 713 it("should not fail", function() { 714 expect(sizeExpression).toParse(); 715 }); 716 717 it("should produce a sizing node", function() { 718 const parse = getParsed(sizeExpression)[0]; 719 720 expect(parse.type).toEqual("sizing"); 721 expect(parse.value).toBeDefined(); 722 }); 723 }); 724 725 describe("A text parser", function() { 726 const textExpression = "\\text{a b}"; 727 const noBraceTextExpression = "\\text x"; 728 const nestedTextExpression = 729 "\\text{a {b} \\blue{c} \\color{#fff}{x} \\llap{x}}"; 730 const spaceTextExpression = "\\text{ a \\ }"; 731 const leadingSpaceTextExpression = "\\text {moo}"; 732 const badTextExpression = "\\text{a b%}"; 733 const badFunctionExpression = "\\text{\\sqrt{x}}"; 734 const mathTokenAfterText = "\\text{sin}^2"; 735 736 it("should not fail", function() { 737 expect(textExpression).toParse(); 738 }); 739 740 it("should produce a text", function() { 741 const parse = getParsed(textExpression)[0]; 742 743 expect(parse.type).toEqual("text"); 744 expect(parse.value).toBeDefined(); 745 }); 746 747 it("should produce textords instead of mathords", function() { 748 const parse = getParsed(textExpression)[0]; 749 const group = parse.value.body; 750 751 expect(group[0].type).toEqual("textord"); 752 }); 753 754 it("should not parse bad text", function() { 755 expect(badTextExpression).toNotParse(); 756 }); 757 758 it("should not parse bad functions inside text", function() { 759 expect(badFunctionExpression).toNotParse(); 760 }); 761 762 it("should parse text with no braces around it", function() { 763 expect(noBraceTextExpression).toParse(); 764 }); 765 766 it("should parse nested expressions", function() { 767 expect(nestedTextExpression).toParse(); 768 }); 769 770 it("should contract spaces", function() { 771 const parse = getParsed(spaceTextExpression)[0]; 772 const group = parse.value.body; 773 774 expect(group[0].type).toEqual("spacing"); 775 expect(group[1].type).toEqual("textord"); 776 expect(group[2].type).toEqual("spacing"); 777 expect(group[3].type).toEqual("spacing"); 778 }); 779 780 it("should accept math mode tokens after its argument", function() { 781 expect(mathTokenAfterText).toParse(); 782 }); 783 784 it("should ignore a space before the text group", function() { 785 const parse = getParsed(leadingSpaceTextExpression)[0]; 786 // [m, o, o] 787 expect(parse.value.body.length).toBe(3); 788 expect( 789 parse.value.body.map(function(n) { return n.value; }).join("") 790 ).toBe("moo"); 791 }); 792 }); 793 794 describe("A color parser", function() { 795 const colorExpression = "\\blue{x}"; 796 const newColorExpression = "\\redA{x}"; 797 const customColorExpression = "\\color{#fA6}{x}"; 798 const badCustomColorExpression = "\\color{bad-color}{x}"; 799 800 it("should not fail", function() { 801 expect(colorExpression).toParse(); 802 }); 803 804 it("should build a color node", function() { 805 const parse = getParsed(colorExpression)[0]; 806 807 expect(parse.type).toEqual("color"); 808 expect(parse.value.color).toBeDefined(); 809 expect(parse.value.value).toBeDefined(); 810 }); 811 812 it("should parse a custom color", function() { 813 expect(customColorExpression).toParse(); 814 }); 815 816 it("should correctly extract the custom color", function() { 817 const parse = getParsed(customColorExpression)[0]; 818 819 expect(parse.value.color).toEqual("#fA6"); 820 }); 821 822 it("should not parse a bad custom color", function() { 823 expect(badCustomColorExpression).toNotParse(); 824 }); 825 826 it("should parse new colors from the branding guide", function() { 827 expect(newColorExpression).toParse(); 828 }); 829 830 it("should have correct greediness", function() { 831 expect("\\color{red}a").toParse(); 832 expect("\\color{red}{\\text{a}}").toParse(); 833 expect("\\color{red}\\text{a}").toNotParse(); 834 expect("\\color{red}\\frac12").toNotParse(); 835 }); 836 }); 837 838 describe("A tie parser", function() { 839 const mathTie = "a~b"; 840 const textTie = "\\text{a~ b}"; 841 842 it("should parse ties in math mode", function() { 843 expect(mathTie).toParse(); 844 }); 845 846 it("should parse ties in text mode", function() { 847 expect(textTie).toParse(); 848 }); 849 850 it("should produce spacing in math mode", function() { 851 const parse = getParsed(mathTie); 852 853 expect(parse[1].type).toEqual("spacing"); 854 }); 855 856 it("should produce spacing in text mode", function() { 857 const text = getParsed(textTie)[0]; 858 const parse = text.value.body; 859 860 expect(parse[1].type).toEqual("spacing"); 861 }); 862 863 it("should not contract with spaces in text mode", function() { 864 const text = getParsed(textTie)[0]; 865 const parse = text.value.body; 866 867 expect(parse[2].type).toEqual("spacing"); 868 }); 869 }); 870 871 describe("A delimiter sizing parser", function() { 872 const normalDelim = "\\bigl |"; 873 const notDelim = "\\bigl x"; 874 const bigDelim = "\\Biggr \\langle"; 875 876 it("should parse normal delimiters", function() { 877 expect(normalDelim).toParse(); 878 expect(bigDelim).toParse(); 879 }); 880 881 it("should not parse not-delimiters", function() { 882 expect(notDelim).toNotParse(); 883 }); 884 885 it("should produce a delimsizing", function() { 886 const parse = getParsed(normalDelim)[0]; 887 888 expect(parse.type).toEqual("delimsizing"); 889 }); 890 891 it("should produce the correct direction delimiter", function() { 892 const leftParse = getParsed(normalDelim)[0]; 893 const rightParse = getParsed(bigDelim)[0]; 894 895 expect(leftParse.value.mclass).toEqual("mopen"); 896 expect(rightParse.value.mclass).toEqual("mclose"); 897 }); 898 899 it("should parse the correct size delimiter", function() { 900 const smallParse = getParsed(normalDelim)[0]; 901 const bigParse = getParsed(bigDelim)[0]; 902 903 expect(smallParse.value.size).toEqual(1); 904 expect(bigParse.value.size).toEqual(4); 905 }); 906 }); 907 908 describe("An overline parser", function() { 909 const overline = "\\overline{x}"; 910 911 it("should not fail", function() { 912 expect(overline).toParse(); 913 }); 914 915 it("should produce an overline", function() { 916 const parse = getParsed(overline)[0]; 917 918 expect(parse.type).toEqual("overline"); 919 }); 920 }); 921 922 describe("A rule parser", function() { 923 const emRule = "\\rule{1em}{2em}"; 924 const exRule = "\\rule{1ex}{2em}"; 925 const badUnitRule = "\\rule{1px}{2em}"; 926 const noNumberRule = "\\rule{1em}{em}"; 927 const incompleteRule = "\\rule{1em}"; 928 const hardNumberRule = "\\rule{ 01.24ex}{2.450 em }"; 929 930 it("should not fail", function() { 931 expect(emRule).toParse(); 932 expect(exRule).toParse(); 933 }); 934 935 it("should not parse invalid units", function() { 936 expect(badUnitRule).toNotParse(); 937 938 expect(noNumberRule).toNotParse(); 939 }); 940 941 it("should not parse incomplete rules", function() { 942 expect(incompleteRule).toNotParse(); 943 }); 944 945 it("should produce a rule", function() { 946 const parse = getParsed(emRule)[0]; 947 948 expect(parse.type).toEqual("rule"); 949 }); 950 951 it("should list the correct units", function() { 952 const emParse = getParsed(emRule)[0]; 953 const exParse = getParsed(exRule)[0]; 954 955 expect(emParse.value.width.unit).toEqual("em"); 956 expect(emParse.value.height.unit).toEqual("em"); 957 958 expect(exParse.value.width.unit).toEqual("ex"); 959 expect(exParse.value.height.unit).toEqual("em"); 960 }); 961 962 it("should parse the number correctly", function() { 963 const hardNumberParse = getParsed(hardNumberRule)[0]; 964 965 expect(hardNumberParse.value.width.number).toBeCloseTo(1.24); 966 expect(hardNumberParse.value.height.number).toBeCloseTo(2.45); 967 }); 968 969 it("should parse negative sizes", function() { 970 const parse = getParsed("\\rule{-1em}{- 0.2em}")[0]; 971 972 expect(parse.value.width.number).toBeCloseTo(-1); 973 expect(parse.value.height.number).toBeCloseTo(-0.2); 974 }); 975 }); 976 977 describe("A kern parser", function() { 978 const emKern = "\\kern{1em}"; 979 const exKern = "\\kern{1ex}"; 980 const muKern = "\\kern{1mu}"; 981 const badUnitRule = "\\kern{1px}"; 982 const noNumberRule = "\\kern{em}"; 983 984 it("should list the correct units", function() { 985 const emParse = getParsed(emKern)[0]; 986 const exParse = getParsed(exKern)[0]; 987 const muParse = getParsed(muKern)[0]; 988 989 expect(emParse.value.dimension.unit).toEqual("em"); 990 expect(exParse.value.dimension.unit).toEqual("ex"); 991 expect(muParse.value.dimension.unit).toEqual("mu"); 992 }); 993 994 it("should not parse invalid units", function() { 995 expect(badUnitRule).toNotParse(); 996 expect(noNumberRule).toNotParse(); 997 }); 998 999 it("should parse negative sizes", function() { 1000 const parse = getParsed("\\kern{-1em}")[0]; 1001 expect(parse.value.dimension.number).toBeCloseTo(-1); 1002 }); 1003 1004 it("should parse positive sizes", function() { 1005 const parse = getParsed("\\kern{+1em}")[0]; 1006 expect(parse.value.dimension.number).toBeCloseTo(1); 1007 }); 1008 }); 1009 1010 describe("A non-braced kern parser", function() { 1011 const emKern = "\\kern1em"; 1012 const exKern = "\\kern 1 ex"; 1013 const muKern = "\\kern 1mu"; 1014 const badUnitRule = "\\kern1px"; 1015 const noNumberRule = "\\kern em"; 1016 1017 it("should list the correct units", function() { 1018 const emParse = getParsed(emKern)[0]; 1019 const exParse = getParsed(exKern)[0]; 1020 const muParse = getParsed(muKern)[0]; 1021 1022 expect(emParse.value.dimension.unit).toEqual("em"); 1023 expect(exParse.value.dimension.unit).toEqual("ex"); 1024 expect(muParse.value.dimension.unit).toEqual("mu"); 1025 }); 1026 1027 it("should not parse invalid units", function() { 1028 expect(badUnitRule).toNotParse(); 1029 expect(noNumberRule).toNotParse(); 1030 }); 1031 1032 it("should parse negative sizes", function() { 1033 const parse = getParsed("\\kern-1em")[0]; 1034 expect(parse.value.dimension.number).toBeCloseTo(-1); 1035 }); 1036 1037 it("should parse positive sizes", function() { 1038 const parse = getParsed("\\kern+1em")[0]; 1039 expect(parse.value.dimension.number).toBeCloseTo(1); 1040 }); 1041 }); 1042 1043 describe("A left/right parser", function() { 1044 const normalLeftRight = "\\left( \\dfrac{x}{y} \\right)"; 1045 const emptyRight = "\\left( \\dfrac{x}{y} \\right."; 1046 1047 it("should not fail", function() { 1048 expect(normalLeftRight).toParse(); 1049 }); 1050 1051 it("should produce a leftright", function() { 1052 const parse = getParsed(normalLeftRight)[0]; 1053 1054 expect(parse.type).toEqual("leftright"); 1055 expect(parse.value.left).toEqual("("); 1056 expect(parse.value.right).toEqual(")"); 1057 }); 1058 1059 it("should error when it is mismatched", function() { 1060 const unmatchedLeft = "\\left( \\dfrac{x}{y}"; 1061 const unmatchedRight = "\\dfrac{x}{y} \\right)"; 1062 1063 expect(unmatchedLeft).toNotParse(); 1064 1065 expect(unmatchedRight).toNotParse(); 1066 }); 1067 1068 it("should error when braces are mismatched", function() { 1069 const unmatched = "{ \\left( \\dfrac{x}{y} } \\right)"; 1070 expect(unmatched).toNotParse(); 1071 }); 1072 1073 it("should error when non-delimiters are provided", function() { 1074 const nonDelimiter = "\\left$ \\dfrac{x}{y} \\right)"; 1075 expect(nonDelimiter).toNotParse(); 1076 }); 1077 1078 it("should parse the empty '.' delimiter", function() { 1079 expect(emptyRight).toParse(); 1080 }); 1081 1082 it("should parse the '.' delimiter with normal sizes", function() { 1083 const normalEmpty = "\\Bigl ."; 1084 expect(normalEmpty).toParse(); 1085 }); 1086 1087 it("should handle \\middle", function() { 1088 const normalMiddle = "\\left( \\dfrac{x}{y} \\middle| \\dfrac{y}{z} \\right)"; 1089 expect(normalMiddle).toParse(); 1090 }); 1091 1092 it("should handle multiple \\middles", function() { 1093 const multiMiddle = "\\left( \\dfrac{x}{y} \\middle| \\dfrac{y}{z} \\middle/ \\dfrac{z}{q} \\right)"; 1094 expect(multiMiddle).toParse(); 1095 }); 1096 1097 it("should handle nested \\middles", function() { 1098 const nestedMiddle = "\\left( a^2 \\middle| \\left( b \\middle/ c \\right) \\right)"; 1099 expect(nestedMiddle).toParse(); 1100 }); 1101 1102 it("should error when \\middle is not in \\left...\\right", function() { 1103 const unmatchedMiddle = "(\\middle|\\dfrac{x}{y})"; 1104 expect(unmatchedMiddle).toNotParse(); 1105 }); 1106 }); 1107 1108 describe("A begin/end parser", function() { 1109 1110 it("should parse a simple environment", function() { 1111 expect("\\begin{matrix}a&b\\\\c&d\\end{matrix}").toParse(); 1112 }); 1113 1114 it("should parse an environment with argument", function() { 1115 expect("\\begin{array}{cc}a&b\\\\c&d\\end{array}").toParse(); 1116 }); 1117 1118 it("should error when name is mismatched", function() { 1119 expect("\\begin{matrix}a&b\\\\c&d\\end{pmatrix}").toNotParse(); 1120 }); 1121 1122 it("should error when commands are mismatched", function() { 1123 expect("\\begin{matrix}a&b\\\\c&d\\right{pmatrix}").toNotParse(); 1124 }); 1125 1126 it("should error when end is missing", function() { 1127 expect("\\begin{matrix}a&b\\\\c&d").toNotParse(); 1128 }); 1129 1130 it("should error when braces are mismatched", function() { 1131 expect("{\\begin{matrix}a&b\\\\c&d}\\end{matrix}").toNotParse(); 1132 }); 1133 1134 it("should cooperate with infix notation", function() { 1135 expect("\\begin{matrix}0&1\\over2&3\\\\4&5&6\\end{matrix}").toParse(); 1136 }); 1137 1138 it("should nest", function() { 1139 const m1 = "\\begin{pmatrix}1&2\\\\3&4\\end{pmatrix}"; 1140 const m2 = "\\begin{array}{rl}" + m1 + "&0\\\\0&" + m1 + "\\end{array}"; 1141 expect(m2).toParse(); 1142 }); 1143 1144 it("should allow \\cr as a line terminator", function() { 1145 expect("\\begin{matrix}a&b\\cr c&d\\end{matrix}").toParse(); 1146 }); 1147 }); 1148 1149 describe("A sqrt parser", function() { 1150 const sqrt = "\\sqrt{x}"; 1151 const missingGroup = "\\sqrt"; 1152 1153 it("should parse square roots", function() { 1154 expect(sqrt).toParse(); 1155 }); 1156 1157 it("should error when there is no group", function() { 1158 expect(missingGroup).toNotParse(); 1159 }); 1160 1161 it("should produce sqrts", function() { 1162 const parse = getParsed(sqrt)[0]; 1163 1164 expect(parse.type).toEqual("sqrt"); 1165 }); 1166 }); 1167 1168 describe("A TeX-compliant parser", function() { 1169 it("should work", function() { 1170 expect("\\frac 2 3").toParse(); 1171 }); 1172 1173 it("should fail if there are not enough arguments", function() { 1174 const missingGroups = [ 1175 "\\frac{x}", 1176 "\\color{#fff}", 1177 "\\rule{1em}", 1178 "\\llap", 1179 "\\bigl", 1180 "\\text", 1181 ]; 1182 1183 for (let i = 0; i < missingGroups.length; i++) { 1184 expect(missingGroups[i]).toNotParse(); 1185 } 1186 }); 1187 1188 it("should fail when there are missing sup/subscripts", function() { 1189 expect("x^").toNotParse(); 1190 expect("x_").toNotParse(); 1191 }); 1192 1193 it("should fail when arguments require arguments", function() { 1194 const badArguments = [ 1195 "\\frac \\frac x y z", 1196 "\\frac x \\frac y z", 1197 "\\frac \\sqrt x y", 1198 "\\frac x \\sqrt y", 1199 "\\frac \\llap x y", 1200 "\\frac x \\llap y", 1201 // This actually doesn't work in real TeX, but it is suprisingly 1202 // hard to get this to correctly work. So, we take hit of very small 1203 // amounts of non-compatiblity in order for the rest of the tests to 1204 // work 1205 // "\\llap \\frac x y", 1206 "\\llap \\llap x", 1207 "\\sqrt \\llap x", 1208 ]; 1209 1210 for (let i = 0; i < badArguments.length; i++) { 1211 expect(badArguments[i]).toNotParse(); 1212 } 1213 }); 1214 1215 it("should work when the arguments have braces", function() { 1216 const goodArguments = [ 1217 "\\frac {\\frac x y} z", 1218 "\\frac x {\\frac y z}", 1219 "\\frac {\\sqrt x} y", 1220 "\\frac x {\\sqrt y}", 1221 "\\frac {\\llap x} y", 1222 "\\frac x {\\llap y}", 1223 "\\llap {\\frac x y}", 1224 "\\llap {\\llap x}", 1225 "\\sqrt {\\llap x}", 1226 ]; 1227 1228 for (let i = 0; i < goodArguments.length; i++) { 1229 expect(goodArguments[i]).toParse(); 1230 } 1231 }); 1232 1233 it("should fail when sup/subscripts require arguments", function() { 1234 const badSupSubscripts = [ 1235 "x^\\sqrt x", 1236 "x^\\llap x", 1237 "x_\\sqrt x", 1238 "x_\\llap x", 1239 ]; 1240 1241 for (let i = 0; i < badSupSubscripts.length; i++) { 1242 expect(badSupSubscripts[i]).toNotParse(); 1243 } 1244 }); 1245 1246 it("should work when sup/subscripts arguments have braces", function() { 1247 const goodSupSubscripts = [ 1248 "x^{\\sqrt x}", 1249 "x^{\\llap x}", 1250 "x_{\\sqrt x}", 1251 "x_{\\llap x}", 1252 ]; 1253 1254 for (let i = 0; i < goodSupSubscripts.length; i++) { 1255 expect(goodSupSubscripts[i]).toParse(); 1256 } 1257 }); 1258 1259 it("should parse multiple primes correctly", function() { 1260 expect("x''''").toParse(); 1261 expect("x_2''").toParse(); 1262 expect("x''_2").toParse(); 1263 }); 1264 1265 it("should fail when sup/subscripts are interspersed with arguments", function() { 1266 expect("\\sqrt^23").toNotParse(); 1267 expect("\\frac^234").toNotParse(); 1268 expect("\\frac2^34").toNotParse(); 1269 }); 1270 1271 it("should succeed when sup/subscripts come after whole functions", function() { 1272 expect("\\sqrt2^3").toParse(); 1273 expect("\\frac23^4").toParse(); 1274 }); 1275 1276 it("should succeed with a sqrt around a text/frac", function() { 1277 expect("\\sqrt \\frac x y").toParse(); 1278 expect("\\sqrt \\text x").toParse(); 1279 expect("x^\\frac x y").toParse(); 1280 expect("x_\\text x").toParse(); 1281 }); 1282 1283 it("should fail when arguments are \\left", function() { 1284 const badLeftArguments = [ 1285 "\\frac \\left( x \\right) y", 1286 "\\frac x \\left( y \\right)", 1287 "\\llap \\left( x \\right)", 1288 "\\sqrt \\left( x \\right)", 1289 "x^\\left( x \\right)", 1290 ]; 1291 1292 for (let i = 0; i < badLeftArguments.length; i++) { 1293 expect(badLeftArguments[i]).toNotParse(); 1294 } 1295 }); 1296 1297 it("should succeed when there are braces around the \\left/\\right", function() { 1298 const goodLeftArguments = [ 1299 "\\frac {\\left( x \\right)} y", 1300 "\\frac x {\\left( y \\right)}", 1301 "\\llap {\\left( x \\right)}", 1302 "\\sqrt {\\left( x \\right)}", 1303 "x^{\\left( x \\right)}", 1304 ]; 1305 1306 for (let i = 0; i < goodLeftArguments.length; i++) { 1307 expect(goodLeftArguments[i]).toParse(); 1308 } 1309 }); 1310 }); 1311 1312 describe("A style change parser", function() { 1313 it("should not fail", function() { 1314 expect("\\displaystyle x").toParse(); 1315 expect("\\textstyle x").toParse(); 1316 expect("\\scriptstyle x").toParse(); 1317 expect("\\scriptscriptstyle x").toParse(); 1318 }); 1319 1320 it("should produce the correct style", function() { 1321 const displayParse = getParsed("\\displaystyle x")[0]; 1322 expect(displayParse.value.style).toEqual("display"); 1323 1324 const scriptscriptParse = getParsed("\\scriptscriptstyle x")[0]; 1325 expect(scriptscriptParse.value.style).toEqual("scriptscript"); 1326 }); 1327 1328 it("should only change the style within its group", function() { 1329 const text = "a b { c d \\displaystyle e f } g h"; 1330 const parse = getParsed(text); 1331 1332 const displayNode = parse[2].value[2]; 1333 1334 expect(displayNode.type).toEqual("styling"); 1335 1336 const displayBody = displayNode.value.value; 1337 1338 expect(displayBody.length).toEqual(2); 1339 expect(displayBody[0].value).toEqual("e"); 1340 }); 1341 }); 1342 1343 describe("A font parser", function() { 1344 it("should parse \\mathrm, \\mathbb, and \\mathit", function() { 1345 expect("\\mathrm x").toParse(); 1346 expect("\\mathbb x").toParse(); 1347 expect("\\mathit x").toParse(); 1348 expect("\\mathrm {x + 1}").toParse(); 1349 expect("\\mathbb {x + 1}").toParse(); 1350 expect("\\mathit {x + 1}").toParse(); 1351 }); 1352 1353 it("should parse \\mathcal and \\mathfrak", function() { 1354 expect("\\mathcal{ABC123}").toParse(); 1355 expect("\\mathfrak{abcABC123}").toParse(); 1356 }); 1357 1358 it("should produce the correct fonts", function() { 1359 const mathbbParse = getParsed("\\mathbb x")[0]; 1360 expect(mathbbParse.value.font).toEqual("mathbb"); 1361 expect(mathbbParse.value.type).toEqual("font"); 1362 1363 const mathrmParse = getParsed("\\mathrm x")[0]; 1364 expect(mathrmParse.value.font).toEqual("mathrm"); 1365 expect(mathrmParse.value.type).toEqual("font"); 1366 1367 const mathitParse = getParsed("\\mathit x")[0]; 1368 expect(mathitParse.value.font).toEqual("mathit"); 1369 expect(mathitParse.value.type).toEqual("font"); 1370 1371 const mathcalParse = getParsed("\\mathcal C")[0]; 1372 expect(mathcalParse.value.font).toEqual("mathcal"); 1373 expect(mathcalParse.value.type).toEqual("font"); 1374 1375 const mathfrakParse = getParsed("\\mathfrak C")[0]; 1376 expect(mathfrakParse.value.font).toEqual("mathfrak"); 1377 expect(mathfrakParse.value.type).toEqual("font"); 1378 }); 1379 1380 it("should parse nested font commands", function() { 1381 const nestedParse = getParsed("\\mathbb{R \\neq \\mathrm{R}}")[0]; 1382 expect(nestedParse.value.font).toEqual("mathbb"); 1383 expect(nestedParse.value.type).toEqual("font"); 1384 1385 expect(nestedParse.value.body.value.length).toEqual(3); 1386 const bbBody = nestedParse.value.body.value; 1387 expect(bbBody[0].type).toEqual("mathord"); 1388 expect(bbBody[1].type).toEqual("rel"); 1389 expect(bbBody[2].type).toEqual("font"); 1390 expect(bbBody[2].value.font).toEqual("mathrm"); 1391 expect(bbBody[2].value.type).toEqual("font"); 1392 }); 1393 1394 it("should work with \\color", function() { 1395 const colorMathbbParse = getParsed("\\color{blue}{\\mathbb R}")[0]; 1396 expect(colorMathbbParse.value.type).toEqual("color"); 1397 expect(colorMathbbParse.value.color).toEqual("blue"); 1398 const body = colorMathbbParse.value.value; 1399 expect(body.length).toEqual(1); 1400 expect(body[0].value.type).toEqual("font"); 1401 expect(body[0].value.font).toEqual("mathbb"); 1402 }); 1403 1404 it("should not parse a series of font commands", function() { 1405 expect("\\mathbb \\mathrm R").toNotParse(); 1406 }); 1407 1408 it("should nest fonts correctly", function() { 1409 const bf = getParsed("\\mathbf{a\\mathrm{b}c}")[0]; 1410 expect(bf.value.type).toEqual("font"); 1411 expect(bf.value.font).toEqual("mathbf"); 1412 expect(bf.value.body.value.length).toEqual(3); 1413 expect(bf.value.body.value[0].value).toEqual("a"); 1414 expect(bf.value.body.value[1].value.type).toEqual("font"); 1415 expect(bf.value.body.value[1].value.font).toEqual("mathrm"); 1416 expect(bf.value.body.value[2].value).toEqual("c"); 1417 }); 1418 1419 it("should have the correct greediness", function() { 1420 expect("e^\\mathbf{x}").toParse(); 1421 }); 1422 }); 1423 1424 describe("An HTML font tree-builder", function() { 1425 it("should render \\mathbb{R} with the correct font", function() { 1426 const markup = katex.renderToString("\\mathbb{R}"); 1427 expect(markup).toContain("<span class=\"mord mathbb\">R</span>"); 1428 }); 1429 1430 it("should render \\mathrm{R} with the correct font", function() { 1431 const markup = katex.renderToString("\\mathrm{R}"); 1432 expect(markup).toContain("<span class=\"mord mathrm\">R</span>"); 1433 }); 1434 1435 it("should render \\mathcal{R} with the correct font", function() { 1436 const markup = katex.renderToString("\\mathcal{R}"); 1437 expect(markup).toContain("<span class=\"mord mathcal\">R</span>"); 1438 }); 1439 1440 it("should render \\mathfrak{R} with the correct font", function() { 1441 const markup = katex.renderToString("\\mathfrak{R}"); 1442 expect(markup).toContain("<span class=\"mord mathfrak\">R</span>"); 1443 }); 1444 1445 it("should render \\text{R} with the correct font", function() { 1446 const markup = katex.renderToString("\\text{R}"); 1447 expect(markup).toContain("<span class=\"mord mathrm\">R</span>"); 1448 }); 1449 1450 it("should render \\textit{R} with the correct font", function() { 1451 const markup = katex.renderToString("\\textit{R}"); 1452 expect(markup).toContain("<span class=\"mord textit\">R</span>"); 1453 }); 1454 1455 it("should render \\text{\\textit{R}} with the correct font", function() { 1456 const markup = katex.renderToString("\\text{\\textit{R}}"); 1457 expect(markup).toContain("<span class=\"mord textit\">R</span>"); 1458 }); 1459 1460 it("should render \\text{R\\textit{S}T} with the correct fonts", function() { 1461 const markup = katex.renderToString("\\text{R\\textit{S}T}"); 1462 expect(markup).toContain("<span class=\"mord mathrm\">R</span>"); 1463 expect(markup).toContain("<span class=\"mord textit\">S</span>"); 1464 expect(markup).toContain("<span class=\"mord mathrm\">T</span>"); 1465 }); 1466 1467 it("should render \\textbf{R} with the correct font", function() { 1468 const markup = katex.renderToString("\\textbf{R}"); 1469 expect(markup).toContain("<span class=\"mord mathbf\">R</span>"); 1470 }); 1471 1472 it("should render \\textsf{R} with the correct font", function() { 1473 const markup = katex.renderToString("\\textsf{R}"); 1474 expect(markup).toContain("<span class=\"mord mathsf\">R</span>"); 1475 }); 1476 1477 it("should render \\texttt{R} with the correct font", function() { 1478 const markup = katex.renderToString("\\texttt{R}"); 1479 expect(markup).toContain("<span class=\"mord mathtt\">R</span>"); 1480 }); 1481 1482 it("should render a combination of font and color changes", function() { 1483 let markup = katex.renderToString("\\color{blue}{\\mathbb R}"); 1484 let span = "<span class=\"mord mathbb\" style=\"color:blue;\">R</span>"; 1485 expect(markup).toContain(span); 1486 1487 markup = katex.renderToString("\\mathbb{\\color{blue}{R}}"); 1488 span = "<span class=\"mord mathbb\" style=\"color:blue;\">R</span>"; 1489 expect(markup).toContain(span); 1490 }); 1491 1492 it("should throw TypeError when the expression is of the wrong type", function() { 1493 expect(function() { 1494 katex.renderToString({badInputType: "yes"}); 1495 }).toThrowError(TypeError); 1496 expect(function() { 1497 katex.renderToString([1, 2]); 1498 }).toThrowError(TypeError); 1499 expect(function() { 1500 katex.renderToString(undefined); 1501 }).toThrowError(TypeError); 1502 expect(function() { 1503 katex.renderToString(null); 1504 }).toThrowError(TypeError); 1505 expect(function() { 1506 katex.renderToString(1.234); 1507 }).toThrowError(TypeError); 1508 }); 1509 1510 it("should not throw TypeError when the expression is a supported type", function() { 1511 expect(function() { 1512 katex.renderToString("\\sqrt{123}"); 1513 }).not.toThrowError(TypeError); 1514 expect(function() { 1515 katex.renderToString(new String("\\sqrt{123}")); 1516 }).not.toThrowError(TypeError); 1517 }); 1518 }); 1519 1520 1521 describe("A MathML font tree-builder", function() { 1522 const contents = "Ax2k\\omega\\Omega\\imath+"; 1523 1524 it("should render " + contents + " with the correct mathvariants", function() { 1525 const tree = getParsed(contents); 1526 const markup = buildMathML(tree, contents, defaultOptions).toMarkup(); 1527 expect(markup).toContain("<mi>A</mi>"); 1528 expect(markup).toContain("<mi>x</mi>"); 1529 expect(markup).toContain("<mn>2</mn>"); 1530 expect(markup).toContain("<mi>\u03c9</mi>"); // \omega 1531 expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega 1532 expect(markup).toContain("<mi>\u0131</mi>"); // \imath 1533 expect(markup).toContain("<mo>+</mo>"); 1534 }); 1535 1536 it("should render \\mathbb{" + contents + "} with the correct mathvariants", function() { 1537 const tex = "\\mathbb{" + contents + "}"; 1538 const tree = getParsed(tex); 1539 const markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1540 expect(markup).toContain("<mi mathvariant=\"double-struck\">A</mi>"); 1541 expect(markup).toContain("<mi>x</mi>"); 1542 expect(markup).toContain("<mn mathvariant=\"normal\">2</mn>"); 1543 expect(markup).toContain("<mi>\u03c9</mi>"); // \omega 1544 expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega 1545 expect(markup).toContain("<mi>\u0131</mi>"); // \imath 1546 expect(markup).toContain("<mo>+</mo>"); 1547 }); 1548 1549 it("should render \\mathrm{" + contents + "} with the correct mathvariants", function() { 1550 const tex = "\\mathrm{" + contents + "}"; 1551 const tree = getParsed(tex); 1552 const markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1553 expect(markup).toContain("<mi mathvariant=\"normal\">A</mi>"); 1554 expect(markup).toContain("<mi mathvariant=\"normal\">x</mi>"); 1555 expect(markup).toContain("<mn mathvariant=\"normal\">2</mn>"); 1556 expect(markup).toContain("<mi>\u03c9</mi>"); // \omega 1557 expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega 1558 expect(markup).toContain("<mi>\u0131</mi>"); // \imath 1559 expect(markup).toContain("<mo>+</mo>"); 1560 }); 1561 1562 it("should render \\mathit{" + contents + "} with the correct mathvariants", function() { 1563 const tex = "\\mathit{" + contents + "}"; 1564 const tree = getParsed(tex); 1565 const markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1566 expect(markup).toContain("<mi mathvariant=\"italic\">A</mi>"); 1567 expect(markup).toContain("<mi mathvariant=\"italic\">x</mi>"); 1568 expect(markup).toContain("<mn mathvariant=\"italic\">2</mn>"); 1569 expect(markup).toContain("<mi mathvariant=\"italic\">\u03c9</mi>"); // \omega 1570 expect(markup).toContain("<mi mathvariant=\"italic\">\u03A9</mi>"); // \Omega 1571 expect(markup).toContain("<mi mathvariant=\"italic\">\u0131</mi>"); // \imath 1572 expect(markup).toContain("<mo>+</mo>"); 1573 }); 1574 1575 it("should render \\mathbf{" + contents + "} with the correct mathvariants", function() { 1576 const tex = "\\mathbf{" + contents + "}"; 1577 const tree = getParsed(tex); 1578 const markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1579 expect(markup).toContain("<mi mathvariant=\"bold\">A</mi>"); 1580 expect(markup).toContain("<mi mathvariant=\"bold\">x</mi>"); 1581 expect(markup).toContain("<mn mathvariant=\"bold\">2</mn>"); 1582 expect(markup).toContain("<mi>\u03c9</mi>"); // \omega 1583 expect(markup).toContain("<mi mathvariant=\"bold\">\u03A9</mi>"); // \Omega 1584 expect(markup).toContain("<mi>\u0131</mi>"); // \imath 1585 expect(markup).toContain("<mo>+</mo>"); 1586 }); 1587 1588 it("should render \\mathcal{" + contents + "} with the correct mathvariants", function() { 1589 const tex = "\\mathcal{" + contents + "}"; 1590 const tree = getParsed(tex); 1591 const markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1592 expect(markup).toContain("<mi mathvariant=\"script\">A</mi>"); 1593 expect(markup).toContain("<mi>x</mi>"); // script is caps only 1594 expect(markup).toContain("<mn mathvariant=\"script\">2</mn>"); 1595 // MathJax marks everything below as "script" except \omega 1596 // We don't have these glyphs in "caligraphic" and neither does MathJax 1597 expect(markup).toContain("<mi>\u03c9</mi>"); // \omega 1598 expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega 1599 expect(markup).toContain("<mi>\u0131</mi>"); // \imath 1600 expect(markup).toContain("<mo>+</mo>"); 1601 }); 1602 1603 it("should render \\mathfrak{" + contents + "} with the correct mathvariants", function() { 1604 const tex = "\\mathfrak{" + contents + "}"; 1605 const tree = getParsed(tex); 1606 const markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1607 expect(markup).toContain("<mi mathvariant=\"fraktur\">A</mi>"); 1608 expect(markup).toContain("<mi mathvariant=\"fraktur\">x</mi>"); 1609 expect(markup).toContain("<mn mathvariant=\"fraktur\">2</mn>"); 1610 // MathJax marks everything below as "fraktur" except \omega 1611 // We don't have these glyphs in "fraktur" and neither does MathJax 1612 expect(markup).toContain("<mi>\u03c9</mi>"); // \omega 1613 expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega 1614 expect(markup).toContain("<mi>\u0131</mi>"); // \imath 1615 expect(markup).toContain("<mo>+</mo>"); 1616 }); 1617 1618 it("should render \\mathscr{" + contents + "} with the correct mathvariants", function() { 1619 const tex = "\\mathscr{" + contents + "}"; 1620 const tree = getParsed(tex); 1621 const markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1622 expect(markup).toContain("<mi mathvariant=\"script\">A</mi>"); 1623 // MathJax marks everything below as "script" except \omega 1624 // We don't have these glyphs in "script" and neither does MathJax 1625 expect(markup).toContain("<mi>x</mi>"); 1626 expect(markup).toContain("<mn mathvariant=\"normal\">2</mn>"); 1627 expect(markup).toContain("<mi>\u03c9</mi>"); // \omega 1628 expect(markup).toContain("<mi mathvariant=\"normal\">\u03A9</mi>"); // \Omega 1629 expect(markup).toContain("<mi>\u0131</mi>"); // \imath 1630 expect(markup).toContain("<mo>+</mo>"); 1631 }); 1632 1633 it("should render \\mathsf{" + contents + "} with the correct mathvariants", function() { 1634 const tex = "\\mathsf{" + contents + "}"; 1635 const tree = getParsed(tex); 1636 const markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1637 expect(markup).toContain("<mi mathvariant=\"sans-serif\">A</mi>"); 1638 expect(markup).toContain("<mi mathvariant=\"sans-serif\">x</mi>"); 1639 expect(markup).toContain("<mn mathvariant=\"sans-serif\">2</mn>"); 1640 expect(markup).toContain("<mi>\u03c9</mi>"); // \omega 1641 expect(markup).toContain("<mi mathvariant=\"sans-serif\">\u03A9</mi>"); // \Omega 1642 expect(markup).toContain("<mi>\u0131</mi>"); // \imath 1643 expect(markup).toContain("<mo>+</mo>"); 1644 }); 1645 1646 it("should render a combination of font and color changes", function() { 1647 let tex = "\\color{blue}{\\mathbb R}"; 1648 let tree = getParsed(tex); 1649 let markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1650 let node = "<mstyle mathcolor=\"blue\">" + 1651 "<mi mathvariant=\"double-struck\">R</mi>" + 1652 "</mstyle>"; 1653 expect(markup).toContain(node); 1654 1655 // reverse the order of the commands 1656 tex = "\\mathbb{\\color{blue}{R}}"; 1657 tree = getParsed(tex); 1658 markup = buildMathML(tree, tex, defaultOptions).toMarkup(); 1659 node = "<mstyle mathcolor=\"blue\">" + 1660 "<mi mathvariant=\"double-struck\">R</mi>" + 1661 "</mstyle>"; 1662 expect(markup).toContain(node); 1663 }); 1664 }); 1665 1666 describe("A bin builder", function() { 1667 it("should create mbins normally", function() { 1668 const built = getBuilt("x + y"); 1669 1670 expect(built[1].classes).toContain("mbin"); 1671 }); 1672 1673 it("should create ords when at the beginning of lists", function() { 1674 const built = getBuilt("+ x"); 1675 1676 expect(built[0].classes).toContain("mord"); 1677 expect(built[0].classes).not.toContain("mbin"); 1678 }); 1679 1680 it("should create ords after some other objects", function() { 1681 expect(getBuilt("x + + 2")[2].classes).toContain("mord"); 1682 expect(getBuilt("( + 2")[1].classes).toContain("mord"); 1683 expect(getBuilt("= + 2")[1].classes).toContain("mord"); 1684 expect(getBuilt("\\sin + 2")[1].classes).toContain("mord"); 1685 expect(getBuilt(", + 2")[1].classes).toContain("mord"); 1686 }); 1687 1688 it("should correctly interact with color objects", function() { 1689 expect(getBuilt("\\blue{x}+y")[1].classes).toContain("mbin"); 1690 expect(getBuilt("\\blue{x+}+y")[1].classes).toContain("mbin"); 1691 expect(getBuilt("\\blue{x+}+y")[2].classes).toContain("mord"); 1692 }); 1693 }); 1694 1695 describe("A markup generator", function() { 1696 it("marks trees up", function() { 1697 // Just a few quick sanity checks here... 1698 const markup = katex.renderToString("\\sigma^2"); 1699 expect(markup.indexOf("<span")).toBe(0); 1700 expect(markup).toContain("\u03c3"); // sigma 1701 expect(markup).toContain("margin-right"); 1702 expect(markup).not.toContain("marginRight"); 1703 }); 1704 1705 it("generates both MathML and HTML", function() { 1706 const markup = katex.renderToString("a"); 1707 1708 expect(markup).toContain("<span"); 1709 expect(markup).toContain("<math"); 1710 }); 1711 }); 1712 1713 describe("A parse tree generator", function() { 1714 it("generates a tree", function() { 1715 const tree = stripPositions(katex.__parse("\\sigma^2")); 1716 expect(JSON.stringify(tree)).toEqual(JSON.stringify([ 1717 { 1718 "type": "supsub", 1719 "value": { 1720 "base": { 1721 "type": "mathord", 1722 "value": "\\sigma", 1723 "mode": "math", 1724 }, 1725 "sup": { 1726 "type": "textord", 1727 "value": "2", 1728 "mode": "math", 1729 }, 1730 "sub": undefined, 1731 }, 1732 "mode": "math", 1733 }, 1734 ])); 1735 }); 1736 }); 1737 1738 describe("An accent parser", function() { 1739 it("should not fail", function() { 1740 expect("\\vec{x}").toParse(); 1741 expect("\\vec{x^2}").toParse(); 1742 expect("\\vec{x}^2").toParse(); 1743 expect("\\vec x").toParse(); 1744 }); 1745 1746 it("should produce accents", function() { 1747 const parse = getParsed("\\vec x")[0]; 1748 1749 expect(parse.type).toEqual("accent"); 1750 }); 1751 1752 it("should be grouped more tightly than supsubs", function() { 1753 const parse = getParsed("\\vec x^2")[0]; 1754 1755 expect(parse.type).toEqual("supsub"); 1756 }); 1757 1758 it("should not parse expanding accents", function() { 1759 expect("\\widehat{x}").toNotParse(); 1760 }); 1761 }); 1762 1763 describe("An accent builder", function() { 1764 it("should not fail", function() { 1765 expect("\\vec{x}").toBuild(); 1766 expect("\\vec{x}^2").toBuild(); 1767 expect("\\vec{x}_2").toBuild(); 1768 expect("\\vec{x}_2^2").toBuild(); 1769 }); 1770 1771 it("should produce mords", function() { 1772 expect(getBuilt("\\vec x")[0].classes).toContain("mord"); 1773 expect(getBuilt("\\vec +")[0].classes).toContain("mord"); 1774 expect(getBuilt("\\vec +")[0].classes).not.toContain("mbin"); 1775 expect(getBuilt("\\vec )^2")[0].classes).toContain("mord"); 1776 expect(getBuilt("\\vec )^2")[0].classes).not.toContain("mclose"); 1777 }); 1778 }); 1779 1780 describe("A phantom parser", function() { 1781 it("should not fail", function() { 1782 expect("\\phantom{x}").toParse(); 1783 expect("\\phantom{x^2}").toParse(); 1784 expect("\\phantom{x}^2").toParse(); 1785 expect("\\phantom x").toParse(); 1786 }); 1787 1788 it("should build a phantom node", function() { 1789 const parse = getParsed("\\phantom{x}")[0]; 1790 1791 expect(parse.type).toEqual("phantom"); 1792 expect(parse.value.value).toBeDefined(); 1793 }); 1794 }); 1795 1796 describe("A phantom builder", function() { 1797 it("should not fail", function() { 1798 expect("\\phantom{x}").toBuild(); 1799 expect("\\phantom{x^2}").toBuild(); 1800 expect("\\phantom{x}^2").toBuild(); 1801 expect("\\phantom x").toBuild(); 1802 }); 1803 1804 it("should make the children transparent", function() { 1805 const children = getBuilt("\\phantom{x+1}"); 1806 expect(children[0].style.color).toBe("transparent"); 1807 expect(children[1].style.color).toBe("transparent"); 1808 expect(children[2].style.color).toBe("transparent"); 1809 }); 1810 1811 it("should make all descendants transparent", function() { 1812 const children = getBuilt("\\phantom{x+\\blue{1}}"); 1813 expect(children[0].style.color).toBe("transparent"); 1814 expect(children[1].style.color).toBe("transparent"); 1815 expect(children[2].style.color).toBe("transparent"); 1816 }); 1817 }); 1818 1819 describe("A parser error", function() { 1820 it("should report the position of an error", function() { 1821 try { 1822 parseTree("\\sqrt}", defaultSettings); 1823 } catch (e) { 1824 expect(e.position).toEqual(5); 1825 } 1826 }); 1827 }); 1828 1829 describe("An optional argument parser", function() { 1830 it("should not fail", function() { 1831 // Note this doesn't actually make an optional argument, but still 1832 // should work 1833 expect("\\frac[1]{2}{3}").toParse(); 1834 1835 expect("\\rule[0.2em]{1em}{1em}").toParse(); 1836 }); 1837 1838 it("should work with sqrts with optional arguments", function() { 1839 expect("\\sqrt[3]{2}").toParse(); 1840 }); 1841 1842 it("should work when the optional argument is missing", function() { 1843 expect("\\sqrt{2}").toParse(); 1844 expect("\\rule{1em}{2em}").toParse(); 1845 }); 1846 1847 it("should fail when the optional argument is malformed", function() { 1848 expect("\\rule[1]{2em}{3em}").toNotParse(); 1849 }); 1850 1851 it("should not work if the optional argument isn't closed", function() { 1852 expect("\\sqrt[").toNotParse(); 1853 }); 1854 }); 1855 1856 describe("An array environment", function() { 1857 1858 it("should accept a single alignment character", function() { 1859 const parse = getParsed("\\begin{array}r1\\\\20\\end{array}"); 1860 expect(parse[0].type).toBe("array"); 1861 expect(parse[0].value.cols).toEqual([ 1862 { type: "align", align: "r" }, 1863 ]); 1864 }); 1865 1866 it("should accept vertical separators", function() { 1867 const parse = getParsed("\\begin{array}{|l||c|}\\end{array}"); 1868 expect(parse[0].type).toBe("array"); 1869 expect(parse[0].value.cols).toEqual([ 1870 { type: "separator", separator: "|" }, 1871 { type: "align", align: "l" }, 1872 { type: "separator", separator: "|" }, 1873 { type: "separator", separator: "|" }, 1874 { type: "align", align: "c" }, 1875 { type: "separator", separator: "|" }, 1876 ]); 1877 }); 1878 1879 }); 1880 1881 describe("A cases environment", function() { 1882 1883 it("should parse its input", function() { 1884 expect("f(a,b)=\\begin{cases}a+1&\\text{if }b\\text{ is odd}\\\\" + 1885 "a&\\text{if }b=0\\\\a-1&\\text{otherwise}\\end{cases}") 1886 .toParse(); 1887 }); 1888 1889 }); 1890 1891 describe("An aligned environment", function() { 1892 1893 it("should parse its input", function() { 1894 expect("\\begin{aligned}a&=b&c&=d\\\\e&=f\\end{aligned}") 1895 .toParse(); 1896 }); 1897 1898 }); 1899 1900 const getMathML = function(expr, settings) { 1901 const usedSettings = settings ? settings : defaultSettings; 1902 1903 expect(expr).toParse(usedSettings); 1904 1905 const built = buildMathML(parseTree(expr, usedSettings), expr, usedSettings); 1906 1907 // Strip off the surrounding <span> 1908 return built.children[0]; 1909 }; 1910 1911 describe("A MathML builder", function() { 1912 it("should generate math nodes", function() { 1913 const node = getMathML("x^2"); 1914 1915 expect(node.type).toEqual("math"); 1916 }); 1917 1918 it("should generate appropriate MathML types", function() { 1919 const identifier = getMathML("x").children[0].children[0]; 1920 expect(identifier.children[0].type).toEqual("mi"); 1921 1922 const number = getMathML("1").children[0].children[0]; 1923 expect(number.children[0].type).toEqual("mn"); 1924 1925 const operator = getMathML("+").children[0].children[0]; 1926 expect(operator.children[0].type).toEqual("mo"); 1927 1928 const space = getMathML("\\;").children[0].children[0]; 1929 expect(space.children[0].type).toEqual("mspace"); 1930 1931 const text = getMathML("\\text{a}").children[0].children[0]; 1932 expect(text.children[0].type).toEqual("mtext"); 1933 1934 const textop = getMathML("\\sin").children[0].children[0]; 1935 expect(textop.children[0].type).toEqual("mi"); 1936 }); 1937 1938 it("should generate a <mphantom> node for \\phantom", function() { 1939 const phantom = getMathML("\\phantom{x}").children[0].children[0]; 1940 expect(phantom.children[0].type).toEqual("mphantom"); 1941 }); 1942 }); 1943 1944 describe("A parser that does not throw on unsupported commands", function() { 1945 // The parser breaks on unsupported commands unless it is explicitly 1946 // told not to 1947 const errorColor = "#933"; 1948 const noThrowSettings = new Settings({ 1949 throwOnError: false, 1950 errorColor: errorColor, 1951 }); 1952 1953 it("should still parse on unrecognized control sequences", function() { 1954 expect("\\error").toParse(noThrowSettings); 1955 }); 1956 1957 describe("should allow unrecognized controls sequences anywhere, including", function() { 1958 it("in superscripts and subscripts", function() { 1959 expect("2_\\error").toBuild(noThrowSettings); 1960 expect("3^{\\error}_\\error").toBuild(noThrowSettings); 1961 expect("\\int\\nolimits^\\error_\\error").toBuild(noThrowSettings); 1962 }); 1963 1964 it("in fractions", function() { 1965 expect("\\frac{345}{\\error}").toBuild(noThrowSettings); 1966 expect("\\frac\\error{\\error}").toBuild(noThrowSettings); 1967 }); 1968 1969 it("in square roots", function() { 1970 expect("\\sqrt\\error").toBuild(noThrowSettings); 1971 expect("\\sqrt{234\\error}").toBuild(noThrowSettings); 1972 }); 1973 1974 it("in text boxes", function() { 1975 expect("\\text{\\error}").toBuild(noThrowSettings); 1976 }); 1977 }); 1978 1979 it("should produce color nodes with a color value given by errorColor", function() { 1980 const parsedInput = getParsed("\\error", noThrowSettings); 1981 expect(parsedInput[0].type).toBe("color"); 1982 expect(parsedInput[0].value.color).toBe(errorColor); 1983 }); 1984 }); 1985 1986 describe("The symbol table integraty", function() { 1987 it("should treat certain symbols as synonyms", function() { 1988 expect(getBuilt("<")).toEqual(getBuilt("\\lt")); 1989 expect(getBuilt(">")).toEqual(getBuilt("\\gt")); 1990 expect(getBuilt("\\left<\\frac{1}{x}\\right>")) 1991 .toEqual(getBuilt("\\left\\lt\\frac{1}{x}\\right\\gt")); 1992 }); 1993 }); 1994 1995 describe("A macro expander", function() { 1996 1997 const compareParseTree = function(actual, expected, macros) { 1998 const settings = new Settings({macros: macros}); 1999 actual = stripPositions(parseTree(actual, settings)); 2000 expected = stripPositions(parseTree(expected, defaultSettings)); 2001 expect(actual).toEqual(expected); 2002 }; 2003 2004 it("should produce individual tokens", function() { 2005 compareParseTree("e^\\foo", "e^1 23", {"\\foo": "123"}); 2006 }); 2007 2008 it("should allow for multiple expansion", function() { 2009 compareParseTree("1\\foo2", "1aa2", { 2010 "\\foo": "\\bar\\bar", 2011 "\\bar": "a", 2012 }); 2013 }); 2014 2015 it("should expand the \overset macro as expected", function() { 2016 expect("\\overset?=").toParseLike("\\mathop{=}\\limits^{?}"); 2017 expect("\\overset{x=y}{\sqrt{ab}}") 2018 .toParseLike("\\mathop{\sqrt{ab}}\\limits^{x=y}"); 2019 expect("\\overset {?} =").toParseLike("\\mathop{=}\\limits^{?}"); 2020 }); 2021 }); 2022 2023 describe("A parser taking String objects", function() { 2024 it("should not fail on an empty String object", function() { 2025 expect(new String("")).toParse(); 2026 }); 2027 2028 it("should parse the same as a regular string", function() { 2029 expect(new String("xy")).toParseLike("xy"); 2030 expect(new String("\\div")).toParseLike("\\div"); 2031 expect(new String("\\frac 1 2")).toParseLike("\\frac 1 2"); 2032 }); 2033 });