www

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README

commit 403dca64ab2f3379286032e7c7d44dd6160756e1
parent 5cca3a299e7ca00e5ee9eeb48dcfd0ff4b9b74cd
Author: Emily Eisenberg <emily@khanacademy.org>
Date:   Fri, 12 Sep 2014 13:30:30 -0700

Improve testing

Summary:
Move dom creation into katex.js so our tests can test non-dom things, and add
some buildTree tests. Add some checks make utils.js work in node. Add support
for jasmine-node, to allow for command line unit testing.

Test Plan:
- Make sure tests work, in both the browser and with `make test`
- Make sure huxley screenshots didn't change

Reviewers: alpert

Reviewed By: alpert

Differential Revision: http://phabricator.khanacademy.org/D13125

Diffstat:
MMakefile | 5++++-
MbuildTree.js | 2+-
Mkatex.js | 2+-
Mpackage.json | 6+++++-
Mserver.js | 4++--
Atest/katex-spec.js | 988+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Dtest/katex-tests.js | 951-------------------------------------------------------------------------------
Mtest/test.html | 2+-
Mutils.js | 20+++++++++++---------
9 files changed, 1013 insertions(+), 967 deletions(-)

diff --git a/Makefile b/Makefile @@ -1,4 +1,4 @@ -.PHONY: build setup copy serve clean metrics +.PHONY: build setup copy serve clean metrics test build: setup build/katex.min.js build/katex.min.css compress setup: @@ -27,6 +27,9 @@ compress: build/katex.min.js build/katex.min.css serve: node server.js +test: + ./node_modules/.bin/jasmine-node test/katex-spec.js + metrics: cd metrics && ./mapping.pl | ./extract_tfms.py | ./replace_line.py diff --git a/buildTree.js b/buildTree.js @@ -708,7 +708,7 @@ var buildTree = function(tree) { makeSpan(["katex-inner"], [topStrut, bottomStrut, span]) ]); - return katexNode.toDOM(); + return katexNode; }; module.exports = buildTree; diff --git a/katex.js b/katex.js @@ -8,7 +8,7 @@ var process = function(toParse, baseNode) { utils.clearNode(baseNode); var tree = parseTree(toParse); - var node = buildTree(tree); + var node = buildTree(tree).toDOM(); baseNode.appendChild(node); }; diff --git a/package.json b/package.json @@ -14,6 +14,10 @@ "less": "~1.4.2", "uglify-js": "~2.4.15", "clean-css": "~2.2.15", - "huxley": "~0.7.4" + "huxley": "~0.7.4", + "jasmine-node": "git://github.com/mhevery/jasmine-node.git#Jasmine2.0" + }, + "scripts": { + "test": "make test" } } diff --git a/server.js b/server.js @@ -41,9 +41,9 @@ app.get("/katex.css", function(req, res, next) { }); }); -app.get("/test/katex-tests.js", function(req, res, next) { +app.get("/test/katex-spec.js", function(req, res, next) { var b = browserify(); - b.add("./test/katex-tests"); + b.add("./test/katex-spec"); var stream = b.bundle({}); diff --git a/test/katex-spec.js b/test/katex-spec.js @@ -0,0 +1,988 @@ +var buildTree = require("../buildTree"); +var parseTree = require("../parseTree"); +var ParseError = require("../ParseError"); + +var getBuilt = function(expr) { + expect(expr).toBuild(); + + var built = buildTree(parseTree(expr)); + + // Remove the outer .katex and .katex-inner layers + return built.children[0].children[2].children; +}; + +beforeEach(function() { + jasmine.addMatchers({ + toParse: function() { + return { + compare: function(actual) { + var result = { + pass: true, + message: "'" + actual + "' succeeded parsing" + }; + + try { + parseTree(actual); + } catch (e) { + result.pass = false; + if (e instanceof ParseError) { + result.message = "'" + actual + "' failed " + + "parsing with error: " + e.message; + } else { + result.message = "'" + actual + "' failed " + + "parsing with unknown error: " + e.message; + } + } + + return result; + } + }; + }, + + toNotParse: function() { + return { + compare: function(actual) { + var result = { + pass: false, + message: "Expected '" + actual + "' to fail " + + "parsing, but it succeeded" + }; + + try { + parseTree(actual); + } 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) { + var result = { + pass: true, + message: "'" + actual + "' succeeded in building" + }; + + expect(actual).toParse(); + + try { + buildTree(parseTree(actual)); + } 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; + } + }; + } + }); +}); + +describe("A parser", function() { + it("should not fail on an empty string", function() { + expect("").toParse(); + }); + + it("should ignore whitespace", function() { + var parseA = parseTree(" x y "); + var parseB = parseTree("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 = parseTree(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 = parseTree(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 = parseTree(expression); + expect(parse).toBeTruthy(); + + for (var i = 0; i < parse.length; i++) { + var group = parse[i]; + expect(group.type).toMatch("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 = parseTree(expression); + expect(parse).toBeTruthy(); + + for (var i = 0; i < parse.length; i++) { + var group = parse[i]; + expect(group.type).toMatch("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 = parseTree(expression); + expect(parse).toBeTruthy(); + + for (var i = 0; i < parse.length; i++) { + var group = parse[i]; + expect(group.type).toMatch("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 = parseTree(expression); + expect(parse).toBeTruthy(); + + for (var i = 0; i < parse.length; i++) { + var group = parse[i]; + expect(group.type).toMatch("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 = parseTree(expression); + expect(parse).toBeTruthy(); + + for (var i = 0; i < parse.length; i++) { + var group = parse[i]; + expect(group.type).toMatch("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 = parseTree("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 = parseTree("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 = parseTree("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 = parseTree("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 = parseTree("x^2_3"); + var parseB = parseTree("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 group parser", function() { + it("should not fail", function() { + expect("{xy}").toParse(); + }); + + it("should produce a single ord", function() { + var parse = parseTree("{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 = parseTree("\\Large abc"); + + expect(parse.length).toBe(1); + + var sizing = parse[0]; + + expect(sizing.type).toMatch("sizing"); + expect(sizing.value).toBeTruthy(); + }); + + it("should apply only after the function", function() { + var parse = parseTree("a \\Large abc"); + + expect(parse.length).toBe(2); + + var sizing = parse[1]; + + expect(sizing.type).toMatch("sizing"); + expect(sizing.value.value.length).toBe(3); + }); + + it("should stop at the ends of groups", function() { + var parse = parseTree("a { b \\Large c } d"); + + var group = parse[1]; + var sizing = group.value[1]; + + expect(sizing.type).toMatch("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 = parseTree(expression)[0]; + + expect(parse.type).toMatch("frac"); + 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 = parseTree(dfracExpression)[0]; + + expect(dfracParse.type).toMatch("frac"); + expect(dfracParse.value.numer).toBeDefined(); + expect(dfracParse.value.denom).toBeDefined(); + + var tfracParse = parseTree(tfracExpression)[0]; + + expect(tfracParse.type).toMatch("frac"); + expect(tfracParse.value.numer).toBeDefined(); + expect(tfracParse.value.denom).toBeDefined(); + }); +}); + +describe("A sizing parser", function() { + var sizeExpression = "\\Huge{x}\\small{x}"; + var nestedSizeExpression = "\\Huge{\\small{x}}"; + + it("should not fail", function() { + expect(sizeExpression).toParse(); + }); + + it("should produce a sizing node", function() { + var parse = parseTree(sizeExpression)[0]; + + expect(parse.type).toMatch("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}}"; + + it("should not fail", function() { + expect(textExpression).toParse(); + }); + + it("should produce a text", function() { + var parse = parseTree(textExpression)[0]; + + expect(parse.type).toMatch("text"); + expect(parse.value).toBeDefined(); + }); + + it("should produce textords instead of mathords", function() { + var parse = parseTree(textExpression)[0]; + var group = parse.value.body; + + expect(group[0].type).toMatch("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 = parseTree(spaceTextExpression)[0]; + var group = parse.value.body; + + expect(group[0].type).toMatch("spacing"); + expect(group[1].type).toMatch("textord"); + expect(group[2].type).toMatch("spacing"); + expect(group[3].type).toMatch("spacing"); + }); + + it("should ignore a space before the text group", function() { + var parse = parseTree(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 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 = parseTree(colorExpression)[0]; + + expect(parse.type).toMatch("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 = parseTree(customColorExpression)[0]; + + expect(parse.value.color).toMatch("#fA6"); + }); + + it("should not parse a bad custom color", function() { + expect(badCustomColorExpression).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 = parseTree(mathTie); + + expect(parse[1].type).toMatch("spacing"); + }); + + it("should produce spacing in text mode", function() { + var text = parseTree(textTie)[0]; + var parse = text.value.body; + + expect(parse[1].type).toMatch("spacing"); + }); + + it("should not contract with spaces in text mode", function() { + var text = parseTree(textTie)[0]; + var parse = text.value.body; + + expect(parse[2].type).toMatch("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 = parseTree(normalDelim)[0]; + + expect(parse.type).toMatch("delimsizing"); + }); + + it("should produce the correct direction delimiter", function() { + var leftParse = parseTree(normalDelim)[0]; + var rightParse = parseTree(bigDelim)[0]; + + expect(leftParse.value.delimType).toMatch("open"); + expect(rightParse.value.delimType).toMatch("close"); + }); + + it("should parse the correct size delimiter", function() { + var smallParse = parseTree(normalDelim)[0]; + var bigParse = parseTree(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 = parseTree(overline)[0]; + + expect(parse.type).toMatch("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 = parseTree(emRule)[0]; + + expect(parse.type).toMatch("rule"); + }); + + it("should list the correct units", function() { + var emParse = parseTree(emRule)[0]; + var exParse = parseTree(exRule)[0]; + + expect(emParse.value.width.unit).toMatch("em"); + expect(emParse.value.height.unit).toMatch("em"); + + expect(exParse.value.width.unit).toMatch("ex"); + expect(exParse.value.height.unit).toMatch("em"); + }); + + it("should parse the number correctly", function() { + var hardNumberParse = parseTree(hardNumberRule)[0]; + + expect(hardNumberParse.value.width.number).toBeCloseTo(1.24); + expect(hardNumberParse.value.height.number).toBeCloseTo(2.45); + }); +}); + +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 = parseTree(normalLeftRight)[0]; + + expect(parse.type).toMatch("leftright"); + expect(parse.value.left).toMatch("\\("); + expect(parse.value.right).toMatch("\\)"); + }); + + 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(); + }); +}); + +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 = parseTree(sqrt)[0]; + + expect(parse.type).toMatch("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 = parseTree("\\displaystyle x")[0]; + expect(displayParse.value.style).toMatch("display"); + + var scriptscriptParse = parseTree("\\scriptscriptstyle x")[0]; + expect(scriptscriptParse.value.style).toMatch("scriptscript"); + }); + + it("should only change the style within its group", function() { + var text = "a b { c d \\displaystyle e f } g h"; + expect(text).toParse(); + + var parse = parseTree(text); + + var displayNode = parse[2].value[2]; + + expect(displayNode.type).toMatch("styling"); + + var displayBody = displayNode.value.value; + + expect(displayBody.length).toEqual(2); + expect(displayBody[0].value).toMatch("e"); + }); +}); + +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("mord"); + }); +}); diff --git a/test/katex-tests.js b/test/katex-tests.js @@ -1,951 +0,0 @@ -var buildTree = require("../buildTree"); -var parseTree = require("../parseTree"); -var ParseError = require("../ParseError"); - -beforeEach(function() { - jasmine.addMatchers({ - toParse: function() { - return { - compare: function(actual) { - var result = { - pass: true, - message: "'" + actual + "' succeeded parsing" - }; - - try { - parseTree(actual); - } catch (e) { - result.pass = false; - if (e instanceof ParseError) { - result.message = "'" + actual + "' failed " + - "parsing with error: " + e.message; - } else { - result.message = "'" + actual + "' failed " + - "parsing with unknown error: " + e.message; - } - } - - return result; - } - }; - }, - - toNotParse: function() { - return { - compare: function(actual) { - var result = { - pass: false, - message: "Expected '" + actual + "' to fail " + - "parsing, but it succeeded" - }; - - try { - parseTree(actual); - } 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) { - var result = { - pass: true, - message: "'" + actual + "' succeeded in building" - }; - - expect(actual).toParse(); - - try { - buildTree(parseTree(actual)); - } 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; - } - }; - } - }); -}); - -describe("A parser", function() { - it("should not fail on an empty string", function() { - expect("").toParse(); - }); - - it("should ignore whitespace", function() { - var parseA = parseTree(" x y "); - var parseB = parseTree("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 = parseTree(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 = parseTree(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 = parseTree(expression); - expect(parse).toBeTruthy(); - - for (var i = 0; i < parse.length; i++) { - var group = parse[i]; - expect(group.type).toMatch("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 = parseTree(expression); - expect(parse).toBeTruthy(); - - for (var i = 0; i < parse.length; i++) { - var group = parse[i]; - expect(group.type).toMatch("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 = parseTree(expression); - expect(parse).toBeTruthy(); - - for (var i = 0; i < parse.length; i++) { - var group = parse[i]; - expect(group.type).toMatch("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 = parseTree(expression); - expect(parse).toBeTruthy(); - - for (var i = 0; i < parse.length; i++) { - var group = parse[i]; - expect(group.type).toMatch("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 = parseTree(expression); - expect(parse).toBeTruthy(); - - for (var i = 0; i < parse.length; i++) { - var group = parse[i]; - expect(group.type).toMatch("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 = parseTree("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 = parseTree("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 = parseTree("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 = parseTree("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 = parseTree("x^2_3"); - var parseB = parseTree("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 group parser", function() { - it("should not fail", function() { - expect("{xy}").toParse(); - }); - - it("should produce a single ord", function() { - var parse = parseTree("{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 = parseTree("\\Large abc"); - - expect(parse.length).toBe(1); - - var sizing = parse[0]; - - expect(sizing.type).toMatch("sizing"); - expect(sizing.value).toBeTruthy(); - }); - - it("should apply only after the function", function() { - var parse = parseTree("a \\Large abc"); - - expect(parse.length).toBe(2); - - var sizing = parse[1]; - - expect(sizing.type).toMatch("sizing"); - expect(sizing.value.value.length).toBe(3); - }); - - it("should stop at the ends of groups", function() { - var parse = parseTree("a { b \\Large c } d"); - - var group = parse[1]; - var sizing = group.value[1]; - - expect(sizing.type).toMatch("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 = parseTree(expression)[0]; - - expect(parse.type).toMatch("frac"); - 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 = parseTree(dfracExpression)[0]; - - expect(dfracParse.type).toMatch("frac"); - expect(dfracParse.value.numer).toBeDefined(); - expect(dfracParse.value.denom).toBeDefined(); - - var tfracParse = parseTree(tfracExpression)[0]; - - expect(tfracParse.type).toMatch("frac"); - expect(tfracParse.value.numer).toBeDefined(); - expect(tfracParse.value.denom).toBeDefined(); - }); -}); - -describe("A sizing parser", function() { - var sizeExpression = "\\Huge{x}\\small{x}"; - var nestedSizeExpression = "\\Huge{\\small{x}}"; - - it("should not fail", function() { - expect(sizeExpression).toParse(); - }); - - it("should produce a sizing node", function() { - var parse = parseTree(sizeExpression)[0]; - - expect(parse.type).toMatch("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}}"; - - it("should not fail", function() { - expect(textExpression).toParse(); - }); - - it("should produce a text", function() { - var parse = parseTree(textExpression)[0]; - - expect(parse.type).toMatch("text"); - expect(parse.value).toBeDefined(); - }); - - it("should produce textords instead of mathords", function() { - var parse = parseTree(textExpression)[0]; - var group = parse.value.body; - - expect(group[0].type).toMatch("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 = parseTree(spaceTextExpression)[0]; - var group = parse.value.body; - - expect(group[0].type).toMatch("spacing"); - expect(group[1].type).toMatch("textord"); - expect(group[2].type).toMatch("spacing"); - expect(group[3].type).toMatch("spacing"); - }); - - it("should ignore a space before the text group", function() { - var parse = parseTree(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 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 = parseTree(colorExpression)[0]; - - expect(parse.type).toMatch("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 = parseTree(customColorExpression)[0]; - - expect(parse.value.color).toMatch("#fA6"); - }); - - it("should not parse a bad custom color", function() { - expect(badCustomColorExpression).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 = parseTree(mathTie); - - expect(parse[1].type).toMatch("spacing"); - }); - - it("should produce spacing in text mode", function() { - var text = parseTree(textTie)[0]; - var parse = text.value.body; - - expect(parse[1].type).toMatch("spacing"); - }); - - it("should not contract with spaces in text mode", function() { - var text = parseTree(textTie)[0]; - var parse = text.value.body; - - expect(parse[2].type).toMatch("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 = parseTree(normalDelim)[0]; - - expect(parse.type).toMatch("delimsizing"); - }); - - it("should produce the correct direction delimiter", function() { - var leftParse = parseTree(normalDelim)[0]; - var rightParse = parseTree(bigDelim)[0]; - - expect(leftParse.value.delimType).toMatch("open"); - expect(rightParse.value.delimType).toMatch("close"); - }); - - it("should parse the correct size delimiter", function() { - var smallParse = parseTree(normalDelim)[0]; - var bigParse = parseTree(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 = parseTree(overline)[0]; - - expect(parse.type).toMatch("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 = parseTree(emRule)[0]; - - expect(parse.type).toMatch("rule"); - }); - - it("should list the correct units", function() { - var emParse = parseTree(emRule)[0]; - var exParse = parseTree(exRule)[0]; - - expect(emParse.value.width.unit).toMatch("em"); - expect(emParse.value.height.unit).toMatch("em"); - - expect(exParse.value.width.unit).toMatch("ex"); - expect(exParse.value.height.unit).toMatch("em"); - }); - - it("should parse the number correctly", function() { - var hardNumberParse = parseTree(hardNumberRule)[0]; - - expect(hardNumberParse.value.width.number).toBeCloseTo(1.24); - expect(hardNumberParse.value.height.number).toBeCloseTo(2.45); - }); -}); - -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 = parseTree(normalLeftRight)[0]; - - expect(parse.type).toMatch("leftright"); - expect(parse.value.left).toMatch("\\("); - expect(parse.value.right).toMatch("\\)"); - }); - - 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(); - }); -}); - -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 = parseTree(sqrt)[0]; - - expect(parse.type).toMatch("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 = parseTree("\\displaystyle x")[0]; - expect(displayParse.value.style).toMatch("display"); - - var scriptscriptParse = parseTree("\\scriptscriptstyle x")[0]; - expect(scriptscriptParse.value.style).toMatch("scriptscript"); - }); - - it("should only change the style within its group", function() { - var text = "a b { c d \\displaystyle e f } g h"; - expect(text).toParse(); - - var parse = parseTree(text); - - var displayNode = parse[2].value[2]; - - expect(displayNode.type).toMatch("styling"); - - var displayBody = displayNode.value.value; - - expect(displayBody.length).toEqual(2); - expect(displayBody[0].value).toMatch("e"); - }); -}); diff --git a/test/test.html b/test/test.html @@ -5,7 +5,7 @@ <script src="jasmine/jasmine-html.js"></script> <script src="jasmine/boot.js"></script> <link rel="stylesheet" href="jasmine/jasmine.css"> - <script src="katex-tests.js"></script> + <script src="katex-spec.js"></script> </head> <body> </body> diff --git a/utils.js b/utils.js @@ -21,15 +21,17 @@ var contains = function(list, elem) { var setTextContent; -var testNode = document.createElement("span"); -if ("textContent" in testNode) { - setTextContent = function(node, text) { - node.textContent = text; - }; -} else { - setTextContent = function(node, text) { - node.innerText = text; - }; +if (typeof document !== "undefined") { + var testNode = document.createElement("span"); + if ("textContent" in testNode) { + setTextContent = function(node, text) { + node.textContent = text; + }; + } else { + setTextContent = function(node, text) { + node.innerText = text; + }; + } } function clearNode(node) {