mirror of
https://github.com/Smaug123/KaTeX
synced 2025-10-04 18:58:39 +00:00
Switch from jasmine to jest (#747)
Summary: The reasons for switching to jest: - easy snapshot testing so that we can easily verify the structure of the parse tree and MathML tree - easy compilation of ES6 features for tests as we continue to expand our use of ES6 Test Plan: - npm test
This commit is contained in:
committed by
Erik Demaine
parent
4480f2c987
commit
361c500a7f
1
.gitignore
vendored
1
.gitignore
vendored
@@ -12,3 +12,4 @@ diff.png
|
||||
/test/symgroups.log
|
||||
/test/symgroups.pdf
|
||||
/test/screenshotter/unicode-fonts
|
||||
coverage/
|
||||
|
@@ -9,9 +9,9 @@ help solving a problem, feel free to stop by our [gitter channel](https://gitter
|
||||
If you'd like to contribute, try contributing new symbols or functions that
|
||||
KaTeX doesn't currently support. The wiki has a page which lists [all of the
|
||||
supported
|
||||
functions](https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX) as
|
||||
well as a page that describes how to [examine TeX commands and where to find
|
||||
rules](https://github.com/Khan/KaTeX/wiki/Examining-TeX) which can be quite
|
||||
functions](https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX) as
|
||||
well as a page that describes how to [examine TeX commands and where to find
|
||||
rules](https://github.com/Khan/KaTeX/wiki/Examining-TeX) which can be quite
|
||||
useful when adding new commands. There's also a user-contributed [preview page]
|
||||
(http://utensil-site.github.io/available-in-katex/)
|
||||
showing how KaTeX would render a series of symbols/functions (including the ones
|
||||
@@ -60,18 +60,22 @@ This will host an interactive editor at
|
||||
[http://localhost:7936/](http://localhost:7936/) to play around with and test
|
||||
changes.
|
||||
|
||||
#### Jasmine tests
|
||||
#### Jest tests
|
||||
|
||||
The JavaScript parser and some of the tree
|
||||
builder is tested with Jasmine. These tests can be run either using node with
|
||||
`make test`, or in the browser by visiting
|
||||
[http://localhost:7936/test/test.html](http://localhost:7936/test/test.html).
|
||||
The JavaScript parser and some of the HTML and MathML tree
|
||||
builders are tested with Jest. These tests can be run using node with
|
||||
`make test`. If you need to debug the tests see
|
||||
[https://facebook.github.io/jest/docs/troubleshooting.html](https://facebook.github.io/jest/docs/troubleshooting.html)
|
||||
|
||||
The Jasmine tests should be run after every change, even the addition of small
|
||||
The interactive editor can also be used for debugging tests in the browser by
|
||||
copy/pasting the test case to be debugged into the editor. The permalink option
|
||||
can come in really useful when doing repeated runs of the same test case.
|
||||
|
||||
The Jest tests should be run after every change, even the addition of small
|
||||
symbols. However, [Travis](https://travis-ci.org/Khan/KaTeX/) will run these
|
||||
tests when you submit a pull request, in case you forget.
|
||||
|
||||
If you make any changes to Parser.js, add Jasmine tests to ensure they work.
|
||||
If you make any changes to Parser.js, add Jest tests to ensure they work.
|
||||
|
||||
#### Screenshot tests
|
||||
|
||||
@@ -117,7 +121,7 @@ Code
|
||||
In general, try to make your code blend in with the surrounding code.
|
||||
|
||||
## Pull Requests
|
||||
|
||||
|
||||
- link back to the original issue(s) whenever possible
|
||||
- new commands should be added to the [wiki](https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX)
|
||||
- commits should be squashed before merging
|
||||
|
7
Makefile
7
Makefile
@@ -1,4 +1,4 @@
|
||||
.PHONY: build dist lint setup copy serve clean metrics test zip contrib
|
||||
.PHONY: build dist lint setup copy serve clean metrics test coverage zip contrib
|
||||
build: lint build/katex.min.js build/katex.min.css contrib zip compress
|
||||
|
||||
ifeq ($(KATEX_DIST),skip)
|
||||
@@ -101,7 +101,10 @@ serve: $(NIS)
|
||||
$(NODE) server.js
|
||||
|
||||
test: $(NIS)
|
||||
JASMINE_CONFIG_PATH=test/jasmine.json node_modules/.bin/jasmine
|
||||
node_modules/.bin/jest
|
||||
|
||||
coverage: $(NIS)
|
||||
node_modules/.bin/jest --coverage
|
||||
|
||||
PERL=perl
|
||||
PYTHON=$(shell python2 --version >/dev/null 2>&1 && echo python2 || echo python)
|
||||
|
@@ -1,5 +1,4 @@
|
||||
/* global beforeEach: false */
|
||||
/* global jasmine: false */
|
||||
/* global expect: false */
|
||||
/* global it: false */
|
||||
/* global describe: false */
|
||||
@@ -7,60 +6,56 @@
|
||||
const splitAtDelimiters = require("./splitAtDelimiters");
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.addMatchers({
|
||||
toSplitInto: function() {
|
||||
return {
|
||||
compare: function(actual, left, right, result) {
|
||||
const message = {
|
||||
pass: true,
|
||||
message: "'" + actual + "' split correctly",
|
||||
};
|
||||
|
||||
const startData = [{type: "text", data: actual}];
|
||||
|
||||
const split =
|
||||
splitAtDelimiters(startData, left, right, false);
|
||||
|
||||
if (split.length !== result.length) {
|
||||
message.pass = false;
|
||||
message.message = "Different number of splits: " +
|
||||
split.length + " vs. " + result.length + " (" +
|
||||
JSON.stringify(split) + " vs. " +
|
||||
JSON.stringify(result) + ")";
|
||||
return message;
|
||||
}
|
||||
|
||||
for (let i = 0; i < split.length; i++) {
|
||||
const real = split[i];
|
||||
const correct = result[i];
|
||||
|
||||
let good = true;
|
||||
let diff;
|
||||
|
||||
if (real.type !== correct.type) {
|
||||
good = false;
|
||||
diff = "type";
|
||||
} else if (real.data !== correct.data) {
|
||||
good = false;
|
||||
diff = "data";
|
||||
} else if (real.display !== correct.display) {
|
||||
good = false;
|
||||
diff = "display";
|
||||
}
|
||||
|
||||
if (!good) {
|
||||
message.pass = false;
|
||||
message.message = "Difference at split " +
|
||||
(i + 1) + ": " + JSON.stringify(real) +
|
||||
" vs. " + JSON.stringify(correct) +
|
||||
" (" + diff + " differs)";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
},
|
||||
expect.extend({
|
||||
toSplitInto: function(actual, left, right, result) {
|
||||
const message = {
|
||||
pass: true,
|
||||
message: "'" + actual + "' split correctly",
|
||||
};
|
||||
|
||||
const startData = [{type: "text", data: actual}];
|
||||
|
||||
const split =
|
||||
splitAtDelimiters(startData, left, right, false);
|
||||
|
||||
if (split.length !== result.length) {
|
||||
message.pass = false;
|
||||
message.message = "Different number of splits: " +
|
||||
split.length + " vs. " + result.length + " (" +
|
||||
JSON.stringify(split) + " vs. " +
|
||||
JSON.stringify(result) + ")";
|
||||
return message;
|
||||
}
|
||||
|
||||
for (let i = 0; i < split.length; i++) {
|
||||
const real = split[i];
|
||||
const correct = result[i];
|
||||
|
||||
let good = true;
|
||||
let diff;
|
||||
|
||||
if (real.type !== correct.type) {
|
||||
good = false;
|
||||
diff = "type";
|
||||
} else if (real.data !== correct.data) {
|
||||
good = false;
|
||||
diff = "data";
|
||||
} else if (real.display !== correct.display) {
|
||||
good = false;
|
||||
diff = "display";
|
||||
}
|
||||
|
||||
if (!good) {
|
||||
message.pass = false;
|
||||
message.message = "Difference at split " +
|
||||
(i + 1) + ": " + JSON.stringify(real) +
|
||||
" vs. " + JSON.stringify(correct) +
|
||||
" (" + diff + " differs)";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return message;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@@ -4,6 +4,3 @@
|
||||
build/**
|
||||
node_modules/**
|
||||
dist/**
|
||||
|
||||
# Third party code
|
||||
test/jasmine/**
|
||||
|
12
package.json
12
package.json
@@ -16,6 +16,7 @@
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"babel-eslint": "^7.2.0",
|
||||
"babel-jest": "^20.0.3",
|
||||
"babel-plugin-transform-class-properties": "^6.23.0",
|
||||
"babel-plugin-transform-runtime": "^6.15.0",
|
||||
"babel-preset-es2015": "^6.18.0",
|
||||
@@ -26,8 +27,7 @@
|
||||
"eslint": "^3.13.0",
|
||||
"express": "^4.14.0",
|
||||
"glob": "^7.1.1",
|
||||
"jasmine": "^2.3.2",
|
||||
"jasmine-core": "^2.3.4",
|
||||
"jest": "^20.0.4",
|
||||
"js-yaml": "^3.3.1",
|
||||
"jspngopt": "^0.2.0",
|
||||
"less": "~2.7.1",
|
||||
@@ -46,5 +46,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"match-at": "^0.1.0"
|
||||
},
|
||||
"jest": {
|
||||
"testMatch": [
|
||||
"**/test/*-spec.js"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.js$": "babel-jest"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -54,8 +54,6 @@ function getStatic(url, file) {
|
||||
}
|
||||
|
||||
browserified("/katex.js", "katex", "katex");
|
||||
app.use("/test/jasmine", express.static(path.dirname(
|
||||
require.resolve("jasmine-core/lib/jasmine-core/jasmine.js"))));
|
||||
browserified("/test/katex-spec.js", "test/*[Ss]pec.js");
|
||||
browserified(
|
||||
"/contrib/auto-render/auto-render.js",
|
||||
|
@@ -1,5 +1,4 @@
|
||||
/* global beforeEach: false */
|
||||
/* global jasmine: false */
|
||||
/* global expect: false */
|
||||
/* global it: false */
|
||||
/* global describe: false */
|
||||
@@ -10,50 +9,47 @@ import Settings from "../src/Settings";
|
||||
const defaultSettings = new Settings({});
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.addMatchers({
|
||||
toFailWithParseError: function(util, customEqualityTesters) {
|
||||
const 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",
|
||||
};
|
||||
}
|
||||
const msg = e.message;
|
||||
const 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 + "'",
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
const prefix = "KaTeX parse error: ";
|
||||
|
||||
expect.extend({
|
||||
toFailWithParseError: 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",
|
||||
};
|
||||
}
|
||||
const msg = e.message;
|
||||
const 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 + "'",
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
@@ -1,11 +0,0 @@
|
||||
{
|
||||
"spec_dir": ".",
|
||||
"spec_files": [
|
||||
"test/**/*[sS]pec.js",
|
||||
"contrib/**/*[sS]pec.js"
|
||||
],
|
||||
"helpers": [
|
||||
"helpers/**/*.js",
|
||||
"node_modules/babel-core/register.js"
|
||||
]
|
||||
}
|
@@ -1,6 +1,5 @@
|
||||
/* eslint max-len:0 */
|
||||
/* global beforeEach: false */
|
||||
/* global jasmine: false */
|
||||
/* global expect: false */
|
||||
/* global it: false */
|
||||
/* global describe: false */
|
||||
@@ -88,115 +87,99 @@ const parseAndSetResult = function(expr, result, settings) {
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
jasmine.addMatchers({
|
||||
expect.extend({
|
||||
toParse: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
|
||||
toParse: function() {
|
||||
return {
|
||||
compare: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
|
||||
const result = {
|
||||
pass: true,
|
||||
message: "'" + actual + "' succeeded parsing",
|
||||
};
|
||||
parseAndSetResult(actual, result, usedSettings);
|
||||
return result;
|
||||
},
|
||||
const result = {
|
||||
pass: true,
|
||||
message: "'" + actual + "' succeeded parsing",
|
||||
};
|
||||
parseAndSetResult(actual, result, usedSettings);
|
||||
return result;
|
||||
},
|
||||
|
||||
toNotParse: function() {
|
||||
return {
|
||||
compare: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
toNotParse: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
|
||||
const 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;
|
||||
},
|
||||
const 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) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
toBuild: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
|
||||
const 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;
|
||||
},
|
||||
const 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, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
toParseLike: function(actual, expected, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
|
||||
const result = {
|
||||
pass: true,
|
||||
message: "Parse trees of '" + actual +
|
||||
"' and '" + expected + "' are equivalent",
|
||||
};
|
||||
|
||||
const actualTree = parseAndSetResult(actual, result,
|
||||
usedSettings);
|
||||
if (!actualTree) {
|
||||
return result;
|
||||
}
|
||||
const expectedTree = parseAndSetResult(expected, result,
|
||||
usedSettings);
|
||||
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;
|
||||
},
|
||||
const result = {
|
||||
pass: true,
|
||||
message: "Parse trees of '" + actual +
|
||||
"' and '" + expected + "' are equivalent",
|
||||
};
|
||||
},
|
||||
|
||||
const actualTree = parseAndSetResult(actual, result,
|
||||
usedSettings);
|
||||
if (!actualTree) {
|
||||
return result;
|
||||
}
|
||||
const expectedTree = parseAndSetResult(expected, result,
|
||||
usedSettings);
|
||||
if (!expectedTree) {
|
||||
return result;
|
||||
}
|
||||
|
||||
stripPositions(actualTree);
|
||||
stripPositions(expectedTree);
|
||||
|
||||
if (JSON.stringify(actualTree) !== JSON.stringify(expectedTree)) {
|
||||
result.pass = false;
|
||||
result.message = "Parse trees of '" + actual +
|
||||
"' and '" + expected + "' are not equivalent";
|
||||
}
|
||||
return result;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
@@ -1,12 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script src="jasmine/jasmine.js"></script>
|
||||
<script src="jasmine/jasmine-html.js"></script>
|
||||
<script src="jasmine/boot.js"></script>
|
||||
<link rel="stylesheet" href="jasmine/jasmine.css">
|
||||
<script src="katex-spec.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
@@ -1,6 +1,5 @@
|
||||
/* eslint max-len:0 */
|
||||
/* global beforeEach: false */
|
||||
/* global jasmine: false */
|
||||
/* global expect: false */
|
||||
/* global it: false */
|
||||
/* global describe: false */
|
||||
@@ -27,50 +26,42 @@ const parseAndSetResult = function(expr, result, settings) {
|
||||
|
||||
describe("unicode", function() {
|
||||
beforeEach(function() {
|
||||
jasmine.addMatchers({
|
||||
expect.extend({
|
||||
|
||||
toParse: function() {
|
||||
return {
|
||||
compare: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
toParse: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
|
||||
const result = {
|
||||
pass: true,
|
||||
message: "'" + actual + "' succeeded parsing",
|
||||
};
|
||||
parseAndSetResult(actual, result, usedSettings);
|
||||
return result;
|
||||
},
|
||||
const result = {
|
||||
pass: true,
|
||||
message: "'" + actual + "' succeeded parsing",
|
||||
};
|
||||
parseAndSetResult(actual, result, usedSettings);
|
||||
return result;
|
||||
},
|
||||
|
||||
toNotParse: function() {
|
||||
return {
|
||||
compare: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
toNotParse: function(actual, settings) {
|
||||
const usedSettings = settings ? settings : defaultSettings;
|
||||
|
||||
const 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;
|
||||
},
|
||||
const 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;
|
||||
},
|
||||
});
|
||||
});
|
||||
|
Reference in New Issue
Block a user