KaTeX/test/errors-spec.js
Kevin Barabash 14a58adb90 Migrate to eslint
Summary
We'd like contributors to use the same linter and lint rules that we use
internally.  This diff swaps out eslint for jshint and fixes all lint failures
except for the max-len failures in the test suites.

Test Plan:
- ka-lint src
- make lint
- make test

Reviewers: emily
2015-12-01 10:02:08 -08:00

346 lines
14 KiB
JavaScript

/* global beforeEach: false */
/* global jasmine: false */
/* global expect: false */
/* global it: false */
/* global describe: false */
var parseTree = require("../src/parseTree");
var Settings = require("../src/Settings");
var defaultSettings = new Settings({});
beforeEach(function() {
jasmine.addMatchers({
toFailWithParseError: function(util, customEqualityTesters) {
var prefix = "KaTeX parse error: ";
return {
compare: function(actual, expected) {
try {
parseTree(actual, defaultSettings);
return {
pass: false,
message: "'" + actual + "' parsed without error",
};
} catch (e) {
if (expected === undefined) {
return {
pass: true,
message: "'" + actual + "' parsed with error",
};
}
var msg = e.message;
var exp = prefix + expected;
if (msg === exp) {
return {
pass: true,
message: "'" + actual + "'" +
" parsed with error '" + expected + "'",
};
} else if (msg.slice(0, 19) === prefix) {
return {
pass: false,
message: "'" + actual + "'" +
" parsed with error '" + msg.slice(19) +
"' but expected '" + expected + "'",
};
} else {
return {
pass: false,
message: "'" + actual + "'" +
" caused error '" + msg +
"' but expected '" + exp + "'",
};
}
}
},
};
},
});
});
describe("Parser:", function() {
describe("#handleInfixNodes", function() {
// TODO: The position information here is broken, should be fixed.
it("rejects repeated infix operators", function() {
expect("1\\over 2\\over 3").toFailWithParseError(
"only one infix operator per group at position -1: " +
"1\\over 2\\over ");
});
it("rejects conflicting infix operators", function() {
expect("1\\over 2\\choose 3").toFailWithParseError(
"only one infix operator per group at position -1: " +
"1\\over 2\\choos");
});
});
describe("#handleSupSubscript", function() {
it("rejects ^ at end of group", function() {
expect("{1^}").toFailWithParseError(
"Expected group after '^' at position 3: {1^̲}");
});
it("rejects _ at end of input", function() {
expect("1_").toFailWithParseError(
"Expected group after '_' at position 2: 1_̲");
});
it("rejects \\sqrt as argument to ^", function() {
expect("1^\\sqrt{2}").toFailWithParseError(
"Got function '\\sqrt' with no arguments as superscript" +
" at position 2: 1^̲\\sqrt{2}");
});
});
describe("#parseAtom", function() {
// TODO: The positions in the following error messages appear to be
// off by one, i.e. they should be one character later.
it("rejects \\limits without operator", function() {
expect("\\alpha\\limits\\omega").toFailWithParseError(
"Limit controls must follow a math operator" +
" at position 6: \\alpha̲\\limits\\omega");
});
it("rejects \\limits at the beginning of the input", function() {
expect("\\limits\\omega").toFailWithParseError(
"Limit controls must follow a math operator" +
" at position 0: ̲\\limits\\omega");
});
it("rejects double superscripts", function() {
expect("1^2^3").toFailWithParseError(
"Double superscript at position 3: 1^2̲^3");
expect("1^{2+3}_4^5").toFailWithParseError(
"Double superscript at position 9: 1^{2+3}_4̲^5");
});
it("rejects double subscripts", function() {
expect("1_2_3").toFailWithParseError(
"Double subscript at position 3: 1_2̲_3");
expect("1_{2+3}^4_5").toFailWithParseError(
"Double subscript at position 9: 1_{2+3}^4̲_5");
});
});
describe("#parseImplicitGroup", function() {
it("reports unknown environments", function() {
expect("\\begin{foo}bar\\end{foo}").toFailWithParseError(
"No such environment: foo at position 11:" +
" \\begin{foo}̲bar\\end{foo}");
});
it("reports mismatched environments", function() {
expect("\\begin{pmatrix}1&2\\\\3&4\\end{bmatrix}+5")
.toFailWithParseError(
"Mismatch: \\begin{pmatrix} matched by \\end{bmatrix}");
});
});
describe("#parseFunction", function() {
it("rejects math-mode functions in text mode", function() {
// TODO: The position info is missing here
expect("\\text{\\sqrt2 is irrational}").toFailWithParseError(
"Can't use function '\\sqrt' in text mode");
});
});
describe("#parseArguments", function() {
it("complains about missing argument at end of input", function() {
expect("2\\sqrt").toFailWithParseError(
"Expected group after '\\sqrt' at position 6: 2\\sqrt̲");
});
it("complains about missing argument at end of group", function() {
expect("1^{2\\sqrt}").toFailWithParseError(
"Expected group after '\\sqrt' at position 9: 1^{2\\sqrt̲}");
});
it("complains about functions as arguments to others", function() {
// TODO: The position looks pretty wrong here
expect("\\sqrt\\over2").toFailWithParseError(
"Got function '\\over' as argument to '\\sqrt'" +
" at position 9: \\sqrt\\ove̲r2");
});
});
describe("#parseArguments", function() {
it("complains about missing argument at end of input", function() {
expect("2\\sqrt").toFailWithParseError(
"Expected group after '\\sqrt' at position 6: 2\\sqrt̲");
});
it("complains about missing argument at end of group", function() {
expect("1^{2\\sqrt}").toFailWithParseError(
"Expected group after '\\sqrt' at position 9: 1^{2\\sqrt̲}");
});
it("complains about functions as arguments to others", function() {
// TODO: The position looks pretty wrong here
expect("\\sqrt\\over2").toFailWithParseError(
"Got function '\\over' as argument to '\\sqrt'" +
" at position 9: \\sqrt\\ove̲r2");
});
});
});
describe("Parser.expect calls:", function() {
describe("#parseInput expecting EOF", function() {
it("complains about extra }", function() {
expect("{1+2}}").toFailWithParseError(
"Expected 'EOF', got '}' at position 6: {1+2}}̲");
});
it("complains about extra \\end", function() {
expect("x\\end{matrix}").toFailWithParseError(
"Expected 'EOF', got '\\end' at position 5:" +
" x\\end̲{matrix}");
});
it("complains about top-level \\\\", function() {
expect("1\\\\2").toFailWithParseError(
"Expected 'EOF', got '\\\\' at position 3: 1\\\\̲2");
});
it("complains about top-level &", function() {
expect("1&2").toFailWithParseError(
"Expected 'EOF', got '&' at position 2: 1&̲2");
});
});
describe("#parseImplicitGroup expecting \\right", function() {
it("rejects missing \\right", function() {
expect("\\left(1+2)").toFailWithParseError(
"Expected '\\right', got 'EOF' at position 10:" +
" \\left(1+2)̲");
});
it("rejects incorrectly scoped \\right", function() {
expect("{\\left(1+2}\\right)").toFailWithParseError(
"Expected '\\right', got '}' at position 11:" +
" {\\left(1+2}̲\\right)");
});
});
// Can't test the expectation for \end after an environment
// since all existing arrays use parseArray which has its own expectation.
describe("#parseSpecialGroup expecting braces", function() {
it("complains about missing { for color", function() {
expect("\\color#ffffff{text}").toFailWithParseError(
"Expected '{', got '#' at position 7:" +
" \\color#̲ffffff{text}");
});
it("complains about missing { for size", function() {
expect("\\rule{1em}[2em]").toFailWithParseError(
"Expected '{', got '[' at position 11: \\rule{1em}[̲2em]");
});
// Can't test for the [ of an optional group since it's optional
it("complains about missing } for color", function() {
expect("\\color{#ffffff {text}").toFailWithParseError(
"Expected '}', got '{' at position 16:" +
" color{#ffffff {̲text}");
});
it("complains about missing ] for size", function() {
expect("\\rule[1em{2em}{3em}").toFailWithParseError(
"Expected ']', got '{' at position 10:" +
" \\rule[1em{̲2em}{3em}");
});
});
describe("#parseGroup expecting }", function() {
it("at end of file", function() {
expect("\\sqrt{2").toFailWithParseError(
"Expected '}', got 'EOF' at position 7: \\sqrt{2̲");
});
});
describe("#parseOptionalGroup expecting ]", function() {
it("at end of file", function() {
expect("\\sqrt[3").toFailWithParseError(
"Expected ']', got 'EOF' at position 7: \\sqrt[3̲");
});
it("before group", function() {
expect("\\sqrt[3{2}").toFailWithParseError(
"Expected ']', got 'EOF' at position 10: \\sqrt[3{2}̲");
});
});
});
describe("environments.js:", function() {
describe("parseArray", function() {
it("rejects missing \\end", function() {
expect("\\begin{matrix}1").toFailWithParseError(
"Expected & or \\\\ or \\end at position 15:" +
" \\begin{matrix}1̲");
});
it("rejects incorrectly scoped \\end", function() {
expect("{\\begin{matrix}1}\\end{matrix}").toFailWithParseError(
"Expected & or \\\\\ or \\end at position 17:" +
" begin{matrix}1}̲\\end{matrix}");
});
});
describe("array environment", function() {
it("rejects unknown column types", function() {
// TODO: The error position here looks strange
expect("\\begin{array}{cba}\\end{array}").toFailWithParseError(
"Unknown column alignment: b at position 18:" +
" gin{array}{cba}̲\\end{array}");
});
});
});
describe("functions.js:", function() {
describe("delimiter functions", function() {
it("reject invalid opening delimiters", function() {
expect("\\bigl 1 + 2 \\bigr").toFailWithParseError(
"Invalid delimiter: '1' after '\\bigl' at position 7:" +
" \\bigl 1̲ + 2 \\bigr");
});
it("reject invalid closing delimiters", function() {
expect("\\bigl(1+2\\bigr=3").toFailWithParseError(
"Invalid delimiter: '=' after '\\bigr' at position 15:" +
" \\bigl(1+2\\bigr=̲3");
});
});
describe("\\begin and \\end", function() {
it("reject invalid environment names", function() {
expect("\\begin{foobar}\\end{foobar}").toFailWithParseError(
"No such environment: foobar at position 14:" +
" \\begin{foobar}̲\\end{foobar}");
});
});
});
describe("Lexer:", function() {
describe("#_innerLex", function() {
it("rejects lone surrogate char", function() {
expect("\udcba").toFailWithParseError(
"Unexpected character: '\udcba' at position 0:" +
" \u0332\udcba");
});
it("rejects lone backslash at end of input", function() {
expect("\\").toFailWithParseError(
"Unexpected character: '\\' at position 0: ̲\\");
});
});
describe("#_innerLexColor", function() {
it("reject hex notation without #", function() {
expect("\\color{1a2b3c}{foo}").toFailWithParseError(
"Invalid color at position 7: \\color{̲1a2b3c}{foo}");
});
});
describe("#_innerLexSize", function() {
it("reject size without unit", function() {
expect("\\rule{0}{2em}").toFailWithParseError(
"Invalid size at position 6: \\rule{̲0}{2em}");
});
it("reject size with bogus unit", function() {
expect("\\rule{1au}{2em}").toFailWithParseError(
"Invalid unit: 'au' at position 6: \\rule{̲1au}{2em}");
});
it("reject size without number", function() {
expect("\\rule{em}{2em}").toFailWithParseError(
"Invalid size at position 6: \\rule{̲em}{2em}");
});
});
});