/* eslint max-len:0 */ /* global beforeEach: false */ /* global jasmine: false */ /* global expect: false */ /* global it: false */ /* global describe: false */ var buildMathML = require("../src/buildMathML"); var buildTree = require("../src/buildTree"); var katex = require("../katex"); var ParseError = require("../src/ParseError"); var parseTree = require("../src/parseTree"); var Options = require("../src/Options"); var Settings = require("../src/Settings"); var Style = require("../src/Style"); var defaultSettings = new Settings({}); var defaultOptions = new Options({ style: Style.TEXT, size: "size5", }); var _getBuilt = function(expr, settings) { var usedSettings = settings ? settings : defaultSettings; var parsedTree = parseTree(expr, usedSettings); var rootNode = buildTree(parsedTree, expr, usedSettings); // grab the root node of the HTML rendering var builtHTML = rootNode.children[1]; // Remove the outer .katex and .katex-inner layers return builtHTML.children[2].children; }; /** * Return the root node of the rendered HTML. * @param expr * @param settings * @returns {Object} */ var getBuilt = function(expr, settings) { var usedSettings = settings ? settings : defaultSettings; expect(expr).toBuild(usedSettings); return _getBuilt(expr, settings); }; /** * Return the root node of the parse tree. * @param expr * @param settings * @returns {Object} */ var getParsed = function(expr, settings) { var usedSettings = settings ? settings : defaultSettings; expect(expr).toParse(usedSettings); return parseTree(expr, usedSettings); }; var stripPositions = function(expr) { if (typeof expr !== "object" || expr === null) { return expr; } if (expr.lexer && typeof expr.start === "number") { delete expr.lexer; delete expr.start; delete expr.end; } Object.keys(expr).forEach(function(key) { stripPositions(expr[key]); }); return expr; }; var parseAndSetResult = function(expr, result, settings) { try { return parseTree(expr, settings || defaultSettings); } catch (e) { result.pass = false; if (e instanceof ParseError) { result.message = "'" + expr + "' failed " + "parsing with error: " + e.message; } else { result.message = "'" + expr + "' failed " + "parsing with unknown error: " + e.message; } } }; beforeEach(function() { jasmine.addMatchers({ toParse: function() { return { compare: function(actual, settings) { var usedSettings = settings ? settings : defaultSettings; var result = { pass: true, message: "'" + actual + "' succeeded parsing", }; parseAndSetResult(actual, result, usedSettings); return result; }, }; }, toNotParse: function() { return { compare: function(actual, settings) { var usedSettings = settings ? settings : defaultSettings; var result = { pass: false, message: "Expected '" + actual + "' to fail " + "parsing, but it succeeded", }; try { parseTree(actual, usedSettings); } catch (e) { if (e instanceof ParseError) { result.pass = true; result.message = "'" + actual + "' correctly " + "didn't parse with error: " + e.message; } else { result.message = "'" + actual + "' failed " + "parsing with unknown error: " + e.message; } } return result; }, }; }, toBuild: function() { return { compare: function(actual, settings) { var usedSettings = settings ? settings : defaultSettings; var result = { pass: true, message: "'" + actual + "' succeeded in building", }; expect(actual).toParse(usedSettings); try { _getBuilt(actual, settings); } catch (e) { result.pass = false; if (e instanceof ParseError) { result.message = "'" + actual + "' failed to " + "build with error: " + e.message; } else { result.message = "'" + actual + "' failed " + "building with unknown error: " + e.message; } } return result; }, }; }, toParseLike: function(util, baton) { return { compare: function(actual, expected) { var result = { pass: true, message: "Parse trees of '" + actual + "' and '" + expected + "' are equivalent", }; var actualTree = parseAndSetResult(actual, result); if (!actualTree) { return result; } var expectedTree = parseAndSetResult(expected, result); if (!expectedTree) { return result; } stripPositions(actualTree); stripPositions(expectedTree); if (!util.equals(actualTree, expectedTree, baton)) { result.pass = false; result.message = "Parse trees of '" + actual + "' and '" + expected + "' are not equivalent"; } return result; }, }; }, }); }); describe("A parser", function() { it("should not fail on an empty string", function() { expect("").toParse(); }); it("should ignore whitespace", function() { var parseA = stripPositions(getParsed(" x y ")); var parseB = stripPositions(getParsed("xy")); expect(parseA).toEqual(parseB); }); }); describe("An ord parser", function() { var expression = "1234|/@.\"`abcdefgzABCDEFGZ"; it("should not fail", function() { expect(expression).toParse(); }); it("should build a list of ords", function() { var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { var group = parse[i]; expect(group.type).toMatch("ord"); } }); it("should parse the right number of ords", function() { var parse = getParsed(expression); expect(parse.length).toBe(expression.length); }); }); describe("A bin parser", function() { var expression = "+-*\\cdot\\pm\\div"; it("should not fail", function() { expect(expression).toParse(); }); it("should build a list of bins", function() { var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { var group = parse[i]; expect(group.type).toEqual("bin"); } }); }); describe("A rel parser", function() { var expression = "=<>\\leq\\geq\\neq\\nleq\\ngeq\\cong"; it("should not fail", function() { expect(expression).toParse(); }); it("should build a list of rels", function() { var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { var group = parse[i]; expect(group.type).toEqual("rel"); } }); }); describe("A punct parser", function() { var expression = ",;\\colon"; it("should not fail", function() { expect(expression).toParse(); }); it("should build a list of puncts", function() { var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { var group = parse[i]; expect(group.type).toEqual("punct"); } }); }); describe("An open parser", function() { var expression = "(["; it("should not fail", function() { expect(expression).toParse(); }); it("should build a list of opens", function() { var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { var group = parse[i]; expect(group.type).toEqual("open"); } }); }); describe("A close parser", function() { var expression = ")]?!"; it("should not fail", function() { expect(expression).toParse(); }); it("should build a list of closes", function() { var parse = getParsed(expression); expect(parse).toBeTruthy(); for (var i = 0; i < parse.length; i++) { var group = parse[i]; expect(group.type).toEqual("close"); } }); }); describe("A \\KaTeX parser", function() { it("should not fail", function() { expect("\\KaTeX").toParse(); }); }); describe("A subscript and superscript parser", function() { it("should not fail on superscripts", function() { expect("x^2").toParse(); }); it("should not fail on subscripts", function() { expect("x_3").toParse(); }); it("should not fail on both subscripts and superscripts", function() { expect("x^2_3").toParse(); expect("x_2^3").toParse(); }); it("should not fail when there is no nucleus", function() { expect("^3").toParse(); expect("_2").toParse(); expect("^3_2").toParse(); expect("_2^3").toParse(); }); it("should produce supsubs for superscript", function() { var parse = getParsed("x^2")[0]; expect(parse.type).toBe("supsub"); expect(parse.value.base).toBeDefined(); expect(parse.value.sup).toBeDefined(); expect(parse.value.sub).toBeUndefined(); }); it("should produce supsubs for subscript", function() { var parse = getParsed("x_3")[0]; expect(parse.type).toBe("supsub"); expect(parse.value.base).toBeDefined(); expect(parse.value.sub).toBeDefined(); expect(parse.value.sup).toBeUndefined(); }); it("should produce supsubs for ^_", function() { var parse = getParsed("x^2_3")[0]; expect(parse.type).toBe("supsub"); expect(parse.value.base).toBeDefined(); expect(parse.value.sup).toBeDefined(); expect(parse.value.sub).toBeDefined(); }); it("should produce supsubs for _^", function() { var parse = getParsed("x_3^2")[0]; expect(parse.type).toBe("supsub"); expect(parse.value.base).toBeDefined(); expect(parse.value.sup).toBeDefined(); expect(parse.value.sub).toBeDefined(); }); it("should produce the same thing regardless of order", function() { var parseA = stripPositions(getParsed("x^2_3")); var parseB = stripPositions(getParsed("x_3^2")); expect(parseA).toEqual(parseB); }); it("should not parse double subscripts or superscripts", function() { expect("x^x^x").toNotParse(); expect("x_x_x").toNotParse(); expect("x_x^x_x").toNotParse(); expect("x_x^x^x").toNotParse(); expect("x^x_x_x").toNotParse(); expect("x^x_x^x").toNotParse(); }); it("should work correctly with {}s", function() { expect("x^{2+3}").toParse(); expect("x_{3-2}").toParse(); expect("x^{2+3}_3").toParse(); expect("x^2_{3-2}").toParse(); expect("x^{2+3}_{3-2}").toParse(); expect("x_{3-2}^{2+3}").toParse(); expect("x_3^{2+3}").toParse(); expect("x_{3-2}^2").toParse(); }); it("should work with nested super/subscripts", function() { expect("x^{x^x}").toParse(); expect("x^{x_x}").toParse(); expect("x_{x^x}").toParse(); expect("x_{x_x}").toParse(); }); }); describe("A subscript and superscript tree-builder", function() { it("should not fail when there is no nucleus", function() { expect("^3").toBuild(); expect("_2").toBuild(); expect("^3_2").toBuild(); expect("_2^3").toBuild(); }); }); describe("A parser with limit controls", function() { it("should fail when the limit control is not preceded by an op node", function() { expect("3\\nolimits_2^2").toNotParse(); expect("\\sqrt\\limits_2^2").toNotParse(); expect("45 +\\nolimits 45").toNotParse(); }); it("should parse when the limit control directly follows an op node", function() { expect("\\int\\limits_2^2 3").toParse(); expect("\\sum\\nolimits_3^4 4").toParse(); }); it("should parse when the limit control is in the sup/sub area of an op node", function() { expect("\\int_2^2\\limits").toParse(); expect("\\int^2\\nolimits_2").toParse(); expect("\\int_2\\limits^2").toParse(); }); it("should allow multiple limit controls in the sup/sub area of an op node", function() { expect("\\int_2\\nolimits^2\\limits 3").toParse(); expect("\\int\\nolimits\\limits_2^2").toParse(); expect("\\int\\limits\\limits\\limits_2^2").toParse(); }); it("should have the rightmost limit control determine the limits property " + "of the preceding op node", function() { var parsedInput = getParsed("\\int\\nolimits\\limits_2^2"); expect(parsedInput[0].value.base.value.limits).toBe(true); parsedInput = getParsed("\\int\\limits_2\\nolimits^2"); expect(parsedInput[0].value.base.value.limits).toBe(false); }); }); describe("A group parser", function() { it("should not fail", function() { expect("{xy}").toParse(); }); it("should produce a single ord", function() { var parse = getParsed("{xy}"); expect(parse.length).toBe(1); var ord = parse[0]; expect(ord.type).toMatch("ord"); expect(ord.value).toBeTruthy(); }); }); describe("An implicit group parser", function() { it("should not fail", function() { expect("\\Large x").toParse(); expect("abc {abc \\Large xyz} abc").toParse(); }); it("should produce a single object", function() { var parse = getParsed("\\Large abc"); expect(parse.length).toBe(1); var sizing = parse[0]; expect(sizing.type).toEqual("sizing"); expect(sizing.value).toBeTruthy(); }); it("should apply only after the function", function() { var parse = getParsed("a \\Large abc"); expect(parse.length).toBe(2); var sizing = parse[1]; expect(sizing.type).toEqual("sizing"); expect(sizing.value.value.length).toBe(3); }); it("should stop at the ends of groups", function() { var parse = getParsed("a { b \\Large c } d"); var group = parse[1]; var sizing = group.value[1]; expect(sizing.type).toEqual("sizing"); expect(sizing.value.value.length).toBe(1); }); }); describe("A function parser", function() { it("should parse no argument functions", function() { expect("\\div").toParse(); }); it("should parse 1 argument functions", function() { expect("\\blue x").toParse(); }); it("should parse 2 argument functions", function() { expect("\\frac 1 2").toParse(); }); it("should not parse 1 argument functions with no arguments", function() { expect("\\blue").toNotParse(); }); it("should not parse 2 argument functions with 0 or 1 arguments", function() { expect("\\frac").toNotParse(); expect("\\frac 1").toNotParse(); }); it("should not parse a function with text right after it", function() { expect("\\redx").toNotParse(); }); it("should parse a function with a number right after it", function() { expect("\\frac12").toParse(); }); it("should parse some functions with text right after it", function() { expect("\\;x").toParse(); }); }); describe("A frac parser", function() { var expression = "\\frac{x}{y}"; var dfracExpression = "\\dfrac{x}{y}"; var tfracExpression = "\\tfrac{x}{y}"; it("should not fail", function() { expect(expression).toParse(); }); it("should produce a frac", function() { var parse = getParsed(expression)[0]; expect(parse.type).toEqual("genfrac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); }); it("should also parse dfrac and tfrac", function() { expect(dfracExpression).toParse(); expect(tfracExpression).toParse(); }); it("should parse dfrac and tfrac as fracs", function() { var dfracParse = getParsed(dfracExpression)[0]; expect(dfracParse.type).toEqual("genfrac"); expect(dfracParse.value.numer).toBeDefined(); expect(dfracParse.value.denom).toBeDefined(); var tfracParse = getParsed(tfracExpression)[0]; expect(tfracParse.type).toEqual("genfrac"); expect(tfracParse.value.numer).toBeDefined(); expect(tfracParse.value.denom).toBeDefined(); }); it("should parse atop", function() { var parse = getParsed("x \\atop y")[0]; expect(parse.type).toEqual("genfrac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); expect(parse.value.hasBarLine).toEqual(false); }); }); describe("An over parser", function() { var simpleOver = "1 \\over x"; var complexOver = "1+2i \\over 3+4i"; it("should not fail", function() { expect(simpleOver).toParse(); expect(complexOver).toParse(); }); it("should produce a frac", function() { var parse; parse = getParsed(simpleOver)[0]; expect(parse.type).toEqual("genfrac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); parse = getParsed(complexOver)[0]; expect(parse.type).toEqual("genfrac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); }); it("should create a numerator from the atoms before \\over", function() { var parse = getParsed(complexOver)[0]; var numer = parse.value.numer; expect(numer.value.length).toEqual(4); }); it("should create a demonimator from the atoms after \\over", function() { var parse = getParsed(complexOver)[0]; var denom = parse.value.numer; expect(denom.value.length).toEqual(4); }); it("should handle empty numerators", function() { var emptyNumerator = "\\over x"; var parse = getParsed(emptyNumerator)[0]; expect(parse.type).toEqual("genfrac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); }); it("should handle empty denominators", function() { var emptyDenominator = "1 \\over"; var parse = getParsed(emptyDenominator)[0]; expect(parse.type).toEqual("genfrac"); expect(parse.value.numer).toBeDefined(); expect(parse.value.denom).toBeDefined(); }); it("should handle \\displaystyle correctly", function() { var displaystyleExpression = "\\displaystyle 1 \\over 2"; var parse = getParsed(displaystyleExpression)[0]; expect(parse.type).toEqual("genfrac"); expect(parse.value.numer.value[0].type).toEqual("styling"); expect(parse.value.denom).toBeDefined(); }); it("should handle \\textstyle correctly", function() { expect("\\textstyle 1 \\over 2") .toParseLike("\\frac{\\textstyle 1}{2}"); expect("{\\textstyle 1} \\over 2") .toParseLike("\\frac{\\textstyle 1}{2}"); }); it("should handle nested factions", function() { var nestedOverExpression = "{1 \\over 2} \\over 3"; var parse = getParsed(nestedOverExpression)[0]; expect(parse.type).toEqual("genfrac"); expect(parse.value.numer.value[0].type).toEqual("genfrac"); expect(parse.value.numer.value[0].value.numer.value[0].value).toEqual("1"); expect(parse.value.numer.value[0].value.denom.value[0].value).toEqual("2"); expect(parse.value.denom).toBeDefined(); expect(parse.value.denom.value[0].value).toEqual("3"); }); it("should fail with multiple overs in the same group", function() { var badMultipleOvers = "1 \\over 2 + 3 \\over 4"; expect(badMultipleOvers).toNotParse(); var badOverChoose = "1 \\over 2 \\choose 3"; expect(badOverChoose).toNotParse(); }); }); describe("A sizing parser", function() { var sizeExpression = "\\Huge{x}\\small{x}"; it("should not fail", function() { expect(sizeExpression).toParse(); }); it("should produce a sizing node", function() { var parse = getParsed(sizeExpression)[0]; expect(parse.type).toEqual("sizing"); expect(parse.value).toBeDefined(); }); }); describe("A text parser", function() { var textExpression = "\\text{a b}"; var noBraceTextExpression = "\\text x"; var nestedTextExpression = "\\text{a {b} \\blue{c} \\color{#fff}{x} \\llap{x}}"; var spaceTextExpression = "\\text{ a \\ }"; var leadingSpaceTextExpression = "\\text {moo}"; var badTextExpression = "\\text{a b%}"; var badFunctionExpression = "\\text{\\sqrt{x}}"; var mathTokenAfterText = "\\text{sin}^2"; it("should not fail", function() { expect(textExpression).toParse(); }); it("should produce a text", function() { var parse = getParsed(textExpression)[0]; expect(parse.type).toEqual("text"); expect(parse.value).toBeDefined(); }); it("should produce textords instead of mathords", function() { var parse = getParsed(textExpression)[0]; var group = parse.value.body; expect(group[0].type).toEqual("textord"); }); it("should not parse bad text", function() { expect(badTextExpression).toNotParse(); }); it("should not parse bad functions inside text", function() { expect(badFunctionExpression).toNotParse(); }); it("should parse text with no braces around it", function() { expect(noBraceTextExpression).toParse(); }); it("should parse nested expressions", function() { expect(nestedTextExpression).toParse(); }); it("should contract spaces", function() { var parse = getParsed(spaceTextExpression)[0]; var group = parse.value.body; expect(group[0].type).toEqual("spacing"); expect(group[1].type).toEqual("textord"); expect(group[2].type).toEqual("spacing"); expect(group[3].type).toEqual("spacing"); }); it("should accept math mode tokens after its argument", function() { expect(mathTokenAfterText).toParse(); }); it("should ignore a space before the text group", function() { var parse = getParsed(leadingSpaceTextExpression)[0]; // [m, o, o] expect(parse.value.body.length).toBe(3); expect( parse.value.body.map(function(n) { return n.value; }).join("") ).toBe("moo"); }); }); describe("A color parser", function() { var colorExpression = "\\blue{x}"; var newColorExpression = "\\redA{x}"; var customColorExpression = "\\color{#fA6}{x}"; var badCustomColorExpression = "\\color{bad-color}{x}"; it("should not fail", function() { expect(colorExpression).toParse(); }); it("should build a color node", function() { var parse = getParsed(colorExpression)[0]; expect(parse.type).toEqual("color"); expect(parse.value.color).toBeDefined(); expect(parse.value.value).toBeDefined(); }); it("should parse a custom color", function() { expect(customColorExpression).toParse(); }); it("should correctly extract the custom color", function() { var parse = getParsed(customColorExpression)[0]; expect(parse.value.color).toEqual("#fA6"); }); it("should not parse a bad custom color", function() { expect(badCustomColorExpression).toNotParse(); }); it("should parse new colors from the branding guide", function() { expect(newColorExpression).toParse(); }); it("should have correct greediness", function() { expect("\\color{red}a").toParse(); expect("\\color{red}{\\text{a}}").toParse(); expect("\\color{red}\\text{a}").toNotParse(); expect("\\color{red}\\frac12").toNotParse(); }); }); describe("A tie parser", function() { var mathTie = "a~b"; var textTie = "\\text{a~ b}"; it("should parse ties in math mode", function() { expect(mathTie).toParse(); }); it("should parse ties in text mode", function() { expect(textTie).toParse(); }); it("should produce spacing in math mode", function() { var parse = getParsed(mathTie); expect(parse[1].type).toEqual("spacing"); }); it("should produce spacing in text mode", function() { var text = getParsed(textTie)[0]; var parse = text.value.body; expect(parse[1].type).toEqual("spacing"); }); it("should not contract with spaces in text mode", function() { var text = getParsed(textTie)[0]; var parse = text.value.body; expect(parse[2].type).toEqual("spacing"); }); }); describe("A delimiter sizing parser", function() { var normalDelim = "\\bigl |"; var notDelim = "\\bigl x"; var bigDelim = "\\Biggr \\langle"; it("should parse normal delimiters", function() { expect(normalDelim).toParse(); expect(bigDelim).toParse(); }); it("should not parse not-delimiters", function() { expect(notDelim).toNotParse(); }); it("should produce a delimsizing", function() { var parse = getParsed(normalDelim)[0]; expect(parse.type).toEqual("delimsizing"); }); it("should produce the correct direction delimiter", function() { var leftParse = getParsed(normalDelim)[0]; var rightParse = getParsed(bigDelim)[0]; expect(leftParse.value.mclass).toEqual("mopen"); expect(rightParse.value.mclass).toEqual("mclose"); }); it("should parse the correct size delimiter", function() { var smallParse = getParsed(normalDelim)[0]; var bigParse = getParsed(bigDelim)[0]; expect(smallParse.value.size).toEqual(1); expect(bigParse.value.size).toEqual(4); }); }); describe("An overline parser", function() { var overline = "\\overline{x}"; it("should not fail", function() { expect(overline).toParse(); }); it("should produce an overline", function() { var parse = getParsed(overline)[0]; expect(parse.type).toEqual("overline"); }); }); describe("A rule parser", function() { var emRule = "\\rule{1em}{2em}"; var exRule = "\\rule{1ex}{2em}"; var badUnitRule = "\\rule{1px}{2em}"; var noNumberRule = "\\rule{1em}{em}"; var incompleteRule = "\\rule{1em}"; var hardNumberRule = "\\rule{ 01.24ex}{2.450 em }"; it("should not fail", function() { expect(emRule).toParse(); expect(exRule).toParse(); }); it("should not parse invalid units", function() { expect(badUnitRule).toNotParse(); expect(noNumberRule).toNotParse(); }); it("should not parse incomplete rules", function() { expect(incompleteRule).toNotParse(); }); it("should produce a rule", function() { var parse = getParsed(emRule)[0]; expect(parse.type).toEqual("rule"); }); it("should list the correct units", function() { var emParse = getParsed(emRule)[0]; var exParse = getParsed(exRule)[0]; expect(emParse.value.width.unit).toEqual("em"); expect(emParse.value.height.unit).toEqual("em"); expect(exParse.value.width.unit).toEqual("ex"); expect(exParse.value.height.unit).toEqual("em"); }); it("should parse the number correctly", function() { var hardNumberParse = getParsed(hardNumberRule)[0]; expect(hardNumberParse.value.width.number).toBeCloseTo(1.24); expect(hardNumberParse.value.height.number).toBeCloseTo(2.45); }); it("should parse negative sizes", function() { var parse = getParsed("\\rule{-1em}{- 0.2em}")[0]; expect(parse.value.width.number).toBeCloseTo(-1); expect(parse.value.height.number).toBeCloseTo(-0.2); }); }); describe("A kern parser", function() { var emKern = "\\kern{1em}"; var exKern = "\\kern{1ex}"; var muKern = "\\kern{1mu}"; var badUnitRule = "\\kern{1px}"; var noNumberRule = "\\kern{em}"; it("should list the correct units", function() { var emParse = getParsed(emKern)[0]; var exParse = getParsed(exKern)[0]; var muParse = getParsed(muKern)[0]; expect(emParse.value.dimension.unit).toEqual("em"); expect(exParse.value.dimension.unit).toEqual("ex"); expect(muParse.value.dimension.unit).toEqual("mu"); }); it("should not parse invalid units", function() { expect(badUnitRule).toNotParse(); expect(noNumberRule).toNotParse(); }); it("should parse negative sizes", function() { var parse = getParsed("\\kern{-1em}")[0]; expect(parse.value.dimension.number).toBeCloseTo(-1); }); it("should parse positive sizes", function() { var parse = getParsed("\\kern{+1em}")[0]; expect(parse.value.dimension.number).toBeCloseTo(1); }); }); describe("A non-braced kern parser", function() { var emKern = "\\kern1em"; var exKern = "\\kern 1 ex"; var muKern = "\\kern 1mu"; var badUnitRule = "\\kern1px"; var noNumberRule = "\\kern em"; it("should list the correct units", function() { var emParse = getParsed(emKern)[0]; var exParse = getParsed(exKern)[0]; var muParse = getParsed(muKern)[0]; expect(emParse.value.dimension.unit).toEqual("em"); expect(exParse.value.dimension.unit).toEqual("ex"); expect(muParse.value.dimension.unit).toEqual("mu"); }); it("should not parse invalid units", function() { expect(badUnitRule).toNotParse(); expect(noNumberRule).toNotParse(); }); it("should parse negative sizes", function() { var parse = getParsed("\\kern-1em")[0]; expect(parse.value.dimension.number).toBeCloseTo(-1); }); it("should parse positive sizes", function() { var parse = getParsed("\\kern+1em")[0]; expect(parse.value.dimension.number).toBeCloseTo(1); }); }); describe("A left/right parser", function() { var normalLeftRight = "\\left( \\dfrac{x}{y} \\right)"; var emptyRight = "\\left( \\dfrac{x}{y} \\right."; it("should not fail", function() { expect(normalLeftRight).toParse(); }); it("should produce a leftright", function() { var parse = getParsed(normalLeftRight)[0]; expect(parse.type).toEqual("leftright"); expect(parse.value.left).toEqual("("); expect(parse.value.right).toEqual(")"); }); it("should error when it is mismatched", function() { var unmatchedLeft = "\\left( \\dfrac{x}{y}"; var unmatchedRight = "\\dfrac{x}{y} \\right)"; expect(unmatchedLeft).toNotParse(); expect(unmatchedRight).toNotParse(); }); it("should error when braces are mismatched", function() { var unmatched = "{ \\left( \\dfrac{x}{y} } \\right)"; expect(unmatched).toNotParse(); }); it("should error when non-delimiters are provided", function() { var nonDelimiter = "\\left$ \\dfrac{x}{y} \\right)"; expect(nonDelimiter).toNotParse(); }); it("should parse the empty '.' delimiter", function() { expect(emptyRight).toParse(); }); it("should parse the '.' delimiter with normal sizes", function() { var normalEmpty = "\\Bigl ."; expect(normalEmpty).toParse(); }); it("should handle \\middle", function() { var normalMiddle = "\\left( \\dfrac{x}{y} \\middle| \\dfrac{y}{z} \\right)"; expect(normalMiddle).toParse(); }); it("should handle multiple \\middles", function() { var multiMiddle = "\\left( \\dfrac{x}{y} \\middle| \\dfrac{y}{z} \\middle/ \\dfrac{z}{q} \\right)"; expect(multiMiddle).toParse(); }); it("should handle nested \\middles", function() { var nestedMiddle = "\\left( a^2 \\middle| \\left( b \\middle/ c \\right) \\right)"; expect(nestedMiddle).toParse(); }); it("should error when \\middle is not in \\left...\\right", function() { var unmatchedMiddle = "(\\middle|\\dfrac{x}{y})"; expect(unmatchedMiddle).toNotParse(); }); }); describe("A begin/end parser", function() { it("should parse a simple environment", function() { expect("\\begin{matrix}a&b\\\\c&d\\end{matrix}").toParse(); }); it("should parse an environment with argument", function() { expect("\\begin{array}{cc}a&b\\\\c&d\\end{array}").toParse(); }); it("should error when name is mismatched", function() { expect("\\begin{matrix}a&b\\\\c&d\\end{pmatrix}").toNotParse(); }); it("should error when commands are mismatched", function() { expect("\\begin{matrix}a&b\\\\c&d\\right{pmatrix}").toNotParse(); }); it("should error when end is missing", function() { expect("\\begin{matrix}a&b\\\\c&d").toNotParse(); }); it("should error when braces are mismatched", function() { expect("{\\begin{matrix}a&b\\\\c&d}\\end{matrix}").toNotParse(); }); it("should cooperate with infix notation", function() { expect("\\begin{matrix}0&1\\over2&3\\\\4&5&6\\end{matrix}").toParse(); }); it("should nest", function() { var m1 = "\\begin{pmatrix}1&2\\\\3&4\\end{pmatrix}"; var m2 = "\\begin{array}{rl}" + m1 + "&0\\\\0&" + m1 + "\\end{array}"; expect(m2).toParse(); }); it("should allow \\cr as a line terminator", function() { expect("\\begin{matrix}a&b\\cr c&d\\end{matrix}").toParse(); }); }); describe("A sqrt parser", function() { var sqrt = "\\sqrt{x}"; var missingGroup = "\\sqrt"; it("should parse square roots", function() { expect(sqrt).toParse(); }); it("should error when there is no group", function() { expect(missingGroup).toNotParse(); }); it("should produce sqrts", function() { var parse = getParsed(sqrt)[0]; expect(parse.type).toEqual("sqrt"); }); }); describe("A TeX-compliant parser", function() { it("should work", function() { expect("\\frac 2 3").toParse(); }); it("should fail if there are not enough arguments", function() { var missingGroups = [ "\\frac{x}", "\\color{#fff}", "\\rule{1em}", "\\llap", "\\bigl", "\\text", ]; for (var i = 0; i < missingGroups.length; i++) { expect(missingGroups[i]).toNotParse(); } }); it("should fail when there are missing sup/subscripts", function() { expect("x^").toNotParse(); expect("x_").toNotParse(); }); it("should fail when arguments require arguments", function() { var badArguments = [ "\\frac \\frac x y z", "\\frac x \\frac y z", "\\frac \\sqrt x y", "\\frac x \\sqrt y", "\\frac \\llap x y", "\\frac x \\llap y", // This actually doesn't work in real TeX, but it is suprisingly // hard to get this to correctly work. So, we take hit of very small // amounts of non-compatiblity in order for the rest of the tests to // work // "\\llap \\frac x y", "\\llap \\llap x", "\\sqrt \\llap x", ]; for (var i = 0; i < badArguments.length; i++) { expect(badArguments[i]).toNotParse(); } }); it("should work when the arguments have braces", function() { var goodArguments = [ "\\frac {\\frac x y} z", "\\frac x {\\frac y z}", "\\frac {\\sqrt x} y", "\\frac x {\\sqrt y}", "\\frac {\\llap x} y", "\\frac x {\\llap y}", "\\llap {\\frac x y}", "\\llap {\\llap x}", "\\sqrt {\\llap x}", ]; for (var i = 0; i < goodArguments.length; i++) { expect(goodArguments[i]).toParse(); } }); it("should fail when sup/subscripts require arguments", function() { var badSupSubscripts = [ "x^\\sqrt x", "x^\\llap x", "x_\\sqrt x", "x_\\llap x", ]; for (var i = 0; i < badSupSubscripts.length; i++) { expect(badSupSubscripts[i]).toNotParse(); } }); it("should work when sup/subscripts arguments have braces", function() { var goodSupSubscripts = [ "x^{\\sqrt x}", "x^{\\llap x}", "x_{\\sqrt x}", "x_{\\llap x}", ]; for (var i = 0; i < goodSupSubscripts.length; i++) { expect(goodSupSubscripts[i]).toParse(); } }); it("should parse multiple primes correctly", function() { expect("x''''").toParse(); expect("x_2''").toParse(); expect("x''_2").toParse(); expect("x'_2'").toParse(); }); it("should fail when sup/subscripts are interspersed with arguments", function() { expect("\\sqrt^23").toNotParse(); expect("\\frac^234").toNotParse(); expect("\\frac2^34").toNotParse(); }); it("should succeed when sup/subscripts come after whole functions", function() { expect("\\sqrt2^3").toParse(); expect("\\frac23^4").toParse(); }); it("should succeed with a sqrt around a text/frac", function() { expect("\\sqrt \\frac x y").toParse(); expect("\\sqrt \\text x").toParse(); expect("x^\\frac x y").toParse(); expect("x_\\text x").toParse(); }); it("should fail when arguments are \\left", function() { var badLeftArguments = [ "\\frac \\left( x \\right) y", "\\frac x \\left( y \\right)", "\\llap \\left( x \\right)", "\\sqrt \\left( x \\right)", "x^\\left( x \\right)", ]; for (var i = 0; i < badLeftArguments.length; i++) { expect(badLeftArguments[i]).toNotParse(); } }); it("should succeed when there are braces around the \\left/\\right", function() { var goodLeftArguments = [ "\\frac {\\left( x \\right)} y", "\\frac x {\\left( y \\right)}", "\\llap {\\left( x \\right)}", "\\sqrt {\\left( x \\right)}", "x^{\\left( x \\right)}", ]; for (var i = 0; i < goodLeftArguments.length; i++) { expect(goodLeftArguments[i]).toParse(); } }); }); describe("A style change parser", function() { it("should not fail", function() { expect("\\displaystyle x").toParse(); expect("\\textstyle x").toParse(); expect("\\scriptstyle x").toParse(); expect("\\scriptscriptstyle x").toParse(); }); it("should produce the correct style", function() { var displayParse = getParsed("\\displaystyle x")[0]; expect(displayParse.value.style).toEqual("display"); var scriptscriptParse = getParsed("\\scriptscriptstyle x")[0]; expect(scriptscriptParse.value.style).toEqual("scriptscript"); }); it("should only change the style within its group", function() { var text = "a b { c d \\displaystyle e f } g h"; var parse = getParsed(text); var displayNode = parse[2].value[2]; expect(displayNode.type).toEqual("styling"); var displayBody = displayNode.value.value; expect(displayBody.length).toEqual(2); expect(displayBody[0].value).toEqual("e"); }); }); describe("A font parser", function() { it("should parse \\mathrm, \\mathbb, and \\mathit", function() { expect("\\mathrm x").toParse(); expect("\\mathbb x").toParse(); expect("\\mathit x").toParse(); expect("\\mathrm {x + 1}").toParse(); expect("\\mathbb {x + 1}").toParse(); expect("\\mathit {x + 1}").toParse(); }); it("should parse \\mathcal and \\mathfrak", function() { expect("\\mathcal{ABC123}").toParse(); expect("\\mathfrak{abcABC123}").toParse(); }); it("should produce the correct fonts", function() { var mathbbParse = getParsed("\\mathbb x")[0]; expect(mathbbParse.value.font).toEqual("mathbb"); expect(mathbbParse.value.type).toEqual("font"); var mathrmParse = getParsed("\\mathrm x")[0]; expect(mathrmParse.value.font).toEqual("mathrm"); expect(mathrmParse.value.type).toEqual("font"); var mathitParse = getParsed("\\mathit x")[0]; expect(mathitParse.value.font).toEqual("mathit"); expect(mathitParse.value.type).toEqual("font"); var mathcalParse = getParsed("\\mathcal C")[0]; expect(mathcalParse.value.font).toEqual("mathcal"); expect(mathcalParse.value.type).toEqual("font"); var mathfrakParse = getParsed("\\mathfrak C")[0]; expect(mathfrakParse.value.font).toEqual("mathfrak"); expect(mathfrakParse.value.type).toEqual("font"); }); it("should parse nested font commands", function() { var nestedParse = getParsed("\\mathbb{R \\neq \\mathrm{R}}")[0]; expect(nestedParse.value.font).toEqual("mathbb"); expect(nestedParse.value.type).toEqual("font"); expect(nestedParse.value.body.value.length).toEqual(3); var bbBody = nestedParse.value.body.value; expect(bbBody[0].type).toEqual("mathord"); expect(bbBody[1].type).toEqual("rel"); expect(bbBody[2].type).toEqual("font"); expect(bbBody[2].value.font).toEqual("mathrm"); expect(bbBody[2].value.type).toEqual("font"); }); it("should work with \\color", function() { var colorMathbbParse = getParsed("\\color{blue}{\\mathbb R}")[0]; expect(colorMathbbParse.value.type).toEqual("color"); expect(colorMathbbParse.value.color).toEqual("blue"); var body = colorMathbbParse.value.value; expect(body.length).toEqual(1); expect(body[0].value.type).toEqual("font"); expect(body[0].value.font).toEqual("mathbb"); }); it("should not parse a series of font commands", function() { expect("\\mathbb \\mathrm R").toNotParse(); }); it("should nest fonts correctly", function() { var bf = getParsed("\\mathbf{a\\mathrm{b}c}")[0]; expect(bf.value.type).toEqual("font"); expect(bf.value.font).toEqual("mathbf"); expect(bf.value.body.value.length).toEqual(3); expect(bf.value.body.value[0].value).toEqual("a"); expect(bf.value.body.value[1].value.type).toEqual("font"); expect(bf.value.body.value[1].value.font).toEqual("mathrm"); expect(bf.value.body.value[2].value).toEqual("c"); }); it("should have the correct greediness", function() { expect("e^\\mathbf{x}").toParse(); }); }); describe("An HTML font tree-builder", function() { it("should render \\mathbb{R} with the correct font", function() { var markup = katex.renderToString("\\mathbb{R}"); expect(markup).toContain("R"); }); it("should render \\mathrm{R} with the correct font", function() { var markup = katex.renderToString("\\mathrm{R}"); expect(markup).toContain("R"); }); it("should render \\mathcal{R} with the correct font", function() { var markup = katex.renderToString("\\mathcal{R}"); expect(markup).toContain("R"); }); it("should render \\mathfrak{R} with the correct font", function() { var markup = katex.renderToString("\\mathfrak{R}"); expect(markup).toContain("R"); }); it("should render \\text{R} with the correct font", function() { var markup = katex.renderToString("\\text{R}"); expect(markup).toContain("R"); }); it("should render \\textit{R} with the correct font", function() { var markup = katex.renderToString("\\textit{R}"); expect(markup).toContain("R"); }); it("should render \\text{\\textit{R}} with the correct font", function() { var markup = katex.renderToString("\\text{\\textit{R}}"); expect(markup).toContain("R"); }); it("should render \\text{R\\textit{S}T} with the correct fonts", function() { var markup = katex.renderToString("\\text{R\\textit{S}T}"); expect(markup).toContain("R"); expect(markup).toContain("S"); expect(markup).toContain("T"); }); it("should render \\textbf{R} with the correct font", function() { var markup = katex.renderToString("\\textbf{R}"); expect(markup).toContain("R"); }); it("should render \\textsf{R} with the correct font", function() { var markup = katex.renderToString("\\textsf{R}"); expect(markup).toContain("R"); }); it("should render \\texttt{R} with the correct font", function() { var markup = katex.renderToString("\\texttt{R}"); expect(markup).toContain("R"); }); it("should render a combination of font and color changes", function() { var markup = katex.renderToString("\\color{blue}{\\mathbb R}"); var span = "R"; expect(markup).toContain(span); markup = katex.renderToString("\\mathbb{\\color{blue}{R}}"); span = "R"; expect(markup).toContain(span); }); it("should throw TypeError when the expression is of the wrong type", function() { expect(function() { katex.renderToString({badInputType: "yes"}); }).toThrowError(TypeError); expect(function() { katex.renderToString([1, 2]); }).toThrowError(TypeError); expect(function() { katex.renderToString(undefined); }).toThrowError(TypeError); expect(function() { katex.renderToString(null); }).toThrowError(TypeError); expect(function() { katex.renderToString(1.234); }).toThrowError(TypeError); }); it("should not throw TypeError when the expression is a supported type", function() { expect(function() { katex.renderToString("\\sqrt{123}"); }).not.toThrowError(TypeError); expect(function() { katex.renderToString(new String("\\sqrt{123}")); }).not.toThrowError(TypeError); }); }); describe("A MathML font tree-builder", function() { var contents = "Ax2k\\omega\\Omega\\imath+"; it("should render " + contents + " with the correct mathvariants", function() { var tree = getParsed(contents); var markup = buildMathML(tree, contents, defaultOptions).toMarkup(); expect(markup).toContain("A"); expect(markup).toContain("x"); expect(markup).toContain("2"); expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render \\mathbb{" + contents + "} with the correct mathvariants", function() { var tex = "\\mathbb{" + contents + "}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); expect(markup).toContain("A"); expect(markup).toContain("x"); expect(markup).toContain("2"); expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render \\mathrm{" + contents + "} with the correct mathvariants", function() { var tex = "\\mathrm{" + contents + "}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); expect(markup).toContain("A"); expect(markup).toContain("x"); expect(markup).toContain("2"); expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render \\mathit{" + contents + "} with the correct mathvariants", function() { var tex = "\\mathit{" + contents + "}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); expect(markup).toContain("A"); expect(markup).toContain("x"); expect(markup).toContain("2"); expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render \\mathbf{" + contents + "} with the correct mathvariants", function() { var tex = "\\mathbf{" + contents + "}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); expect(markup).toContain("A"); expect(markup).toContain("x"); expect(markup).toContain("2"); expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render \\mathcal{" + contents + "} with the correct mathvariants", function() { var tex = "\\mathcal{" + contents + "}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); expect(markup).toContain("A"); expect(markup).toContain("x"); // script is caps only expect(markup).toContain("2"); // MathJax marks everything below as "script" except \omega // We don't have these glyphs in "caligraphic" and neither does MathJax expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render \\mathfrak{" + contents + "} with the correct mathvariants", function() { var tex = "\\mathfrak{" + contents + "}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); expect(markup).toContain("A"); expect(markup).toContain("x"); expect(markup).toContain("2"); // MathJax marks everything below as "fraktur" except \omega // We don't have these glyphs in "fraktur" and neither does MathJax expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render \\mathscr{" + contents + "} with the correct mathvariants", function() { var tex = "\\mathscr{" + contents + "}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); expect(markup).toContain("A"); // MathJax marks everything below as "script" except \omega // We don't have these glyphs in "script" and neither does MathJax expect(markup).toContain("x"); expect(markup).toContain("2"); expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render \\mathsf{" + contents + "} with the correct mathvariants", function() { var tex = "\\mathsf{" + contents + "}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); expect(markup).toContain("A"); expect(markup).toContain("x"); expect(markup).toContain("2"); expect(markup).toContain("\u03c9"); // \omega expect(markup).toContain("\u03A9"); // \Omega expect(markup).toContain("\u0131"); // \imath expect(markup).toContain("+"); }); it("should render a combination of font and color changes", function() { var tex = "\\color{blue}{\\mathbb R}"; var tree = getParsed(tex); var markup = buildMathML(tree, tex, defaultOptions).toMarkup(); var node = "" + "R" + ""; expect(markup).toContain(node); // reverse the order of the commands tex = "\\mathbb{\\color{blue}{R}}"; tree = getParsed(tex); markup = buildMathML(tree, tex, defaultOptions).toMarkup(); node = "" + "R" + ""; expect(markup).toContain(node); }); }); describe("A bin builder", function() { it("should create mbins normally", function() { var built = getBuilt("x + y"); expect(built[1].classes).toContain("mbin"); }); it("should create ords when at the beginning of lists", function() { var built = getBuilt("+ x"); expect(built[0].classes).toContain("mord"); expect(built[0].classes).not.toContain("mbin"); }); it("should create ords after some other objects", function() { expect(getBuilt("x + + 2")[2].classes).toContain("mord"); expect(getBuilt("( + 2")[1].classes).toContain("mord"); expect(getBuilt("= + 2")[1].classes).toContain("mord"); expect(getBuilt("\\sin + 2")[1].classes).toContain("mord"); expect(getBuilt(", + 2")[1].classes).toContain("mord"); }); it("should correctly interact with color objects", function() { expect(getBuilt("\\blue{x}+y")[1].classes).toContain("mbin"); expect(getBuilt("\\blue{x+}+y")[1].classes).toContain("mbin"); expect(getBuilt("\\blue{x+}+y")[2].classes).toContain("mord"); }); }); describe("A markup generator", function() { it("marks trees up", function() { // Just a few quick sanity checks here... var markup = katex.renderToString("\\sigma^2"); expect(markup.indexOf(" return built.children[0]; }; describe("A MathML builder", function() { it("should generate math nodes", function() { var node = getMathML("x^2"); expect(node.type).toEqual("math"); }); it("should generate appropriate MathML types", function() { var identifier = getMathML("x").children[0].children[0]; expect(identifier.children[0].type).toEqual("mi"); var number = getMathML("1").children[0].children[0]; expect(number.children[0].type).toEqual("mn"); var operator = getMathML("+").children[0].children[0]; expect(operator.children[0].type).toEqual("mo"); var space = getMathML("\\;").children[0].children[0]; expect(space.children[0].type).toEqual("mspace"); var text = getMathML("\\text{a}").children[0].children[0]; expect(text.children[0].type).toEqual("mtext"); var textop = getMathML("\\sin").children[0].children[0]; expect(textop.children[0].type).toEqual("mi"); }); it("should generate a node for \\phantom", function() { var phantom = getMathML("\\phantom{x}").children[0].children[0]; expect(phantom.children[0].type).toEqual("mphantom"); }); }); describe("A parser that does not throw on unsupported commands", function() { // The parser breaks on unsupported commands unless it is explicitly // told not to var errorColor = "#933"; var noThrowSettings = new Settings({ throwOnError: false, errorColor: errorColor, }); it("should still parse on unrecognized control sequences", function() { expect("\\error").toParse(noThrowSettings); }); describe("should allow unrecognized controls sequences anywhere, including", function() { it("in superscripts and subscripts", function() { expect("2_\\error").toBuild(noThrowSettings); expect("3^{\\error}_\\error").toBuild(noThrowSettings); expect("\\int\\nolimits^\\error_\\error").toBuild(noThrowSettings); }); it("in fractions", function() { expect("\\frac{345}{\\error}").toBuild(noThrowSettings); expect("\\frac\\error{\\error}").toBuild(noThrowSettings); }); it("in square roots", function() { expect("\\sqrt\\error").toBuild(noThrowSettings); expect("\\sqrt{234\\error}").toBuild(noThrowSettings); }); it("in text boxes", function() { expect("\\text{\\error}").toBuild(noThrowSettings); }); }); it("should produce color nodes with a color value given by errorColor", function() { var parsedInput = getParsed("\\error", noThrowSettings); expect(parsedInput[0].type).toBe("color"); expect(parsedInput[0].value.color).toBe(errorColor); }); }); describe("The symbol table integraty", function() { it("should treat certain symbols as synonyms", function() { expect(getBuilt("<")).toEqual(getBuilt("\\lt")); expect(getBuilt(">")).toEqual(getBuilt("\\gt")); expect(getBuilt("\\left<\\frac{1}{x}\\right>")) .toEqual(getBuilt("\\left\\lt\\frac{1}{x}\\right\\gt")); }); }); describe("A macro expander", function() { var compareParseTree = function(actual, expected, macros) { var settings = new Settings({macros: macros}); actual = stripPositions(parseTree(actual, settings)); expected = stripPositions(parseTree(expected, defaultSettings)); expect(actual).toEqual(expected); }; it("should produce individual tokens", function() { compareParseTree("e^\\foo", "e^1 23", {"\\foo": "123"}); }); it("should allow for multiple expansion", function() { compareParseTree("1\\foo2", "1aa2", { "\\foo": "\\bar\\bar", "\\bar": "a", }); }); }); describe("A parser taking String objects", function() { it("should not fail on an empty String object", function() { expect(new String("")).toParse(); }); it("should parse the same as a regular string", function() { expect(new String("xy")).toParseLike("xy"); expect(new String("\\div")).toParseLike("\\div"); expect(new String("\\frac 1 2")).toParseLike("\\frac 1 2"); }); });