Refactor test helpers (#1336)

* Refactor test helpers

* Combine common codes from `parse` and `build`, and dispatch using
`Mode`
* Remove `toNotBuild` and `toNotParse` and use `expect.not`
* Remove `Warning` and instead mock `console.warn`
* Add `expect.toHavePassed` and check whether parse/build succeeded
lazily
* Improve failed test `message`:
  - Use color
  - Print stack traces(excluding internals)
  - Print diff
  - Follow jest matcher output style

* Update helpers.js

* Remove toBeTruthy checks

getParsed throws an error if parsing fails.

* Used tagged literals

* Use .toHaveLength

* Use tagged literals

* Use to{Build,Parse}Like where possible

* Remove compareParseTree

* Use snapshot where possible

* Join into one line where <88 chars

* Revert console.warn() to throw an error

Merge `expectToWarn` to `expectKaTeX`, like
`expect.toFailWithParseError`

* Remove call to console.warn from stack traces

* Fix merge errors

* Remove `getTree`

* Move `_getBuilt` into `getBuilt`
* Move default settings and tagging literal support in to `getParsed` 
and `getBuilt`
* Remove stack traces clean-up
* Remove `toHavePassed` matcher

* Extract `expected` string construction into `printExpectedResult`
This commit is contained in:
ylemkimon
2018-07-22 11:06:07 +09:00
committed by Kevin Barabash
parent c9947220b6
commit 71035c7111
7 changed files with 1054 additions and 1200 deletions

View File

@@ -1,14 +1,73 @@
/* global expect: false */
import katex from "../katex";
import ParseError from "../src/ParseError";
import parseTree from "../src/parseTree";
import Settings from "../src/Settings";
import diff from 'jest-diff';
import {RECEIVED_COLOR, printReceived, printExpected} from 'jest-matcher-utils';
import {formatStackTrace, separateMessageFromStack} from 'jest-message-util';
export function ConsoleWarning(message) {
Error.captureStackTrace(this, global.console.warn);
this.name = this.constructor.name;
this.message = message;
}
Object.setPrototypeOf(ConsoleWarning.prototype, Error.prototype);
/**
* Return the first raw string if x is tagged literal. Otherwise return x.
*/
export const r = x => x != null && x.hasOwnProperty('raw') ? x.raw[0] : x;
/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* This function is from https://github.com/facebook/jest/blob/9867e16e518d50c79
* 492f7f0d2bc1ef8dff37db4/packages/expect/src/to_throw_matchers.js and licensed
* under the MIT license found in the https://github.com/facebook/jest/blob/master/LICENSE.
*/
const printActualErrorMessage = error => {
if (error) {
const {message, stack} = separateMessageFromStack(error.stack);
return (
'Instead, it threw:\n' +
RECEIVED_COLOR(
` ${message}` +
formatStackTrace(
// remove KaTeX internal stack entries
stack.split('\n')
.filter(line => line.indexOf('new ParseError') === -1)
.join('\n'),
{
rootDir: process.cwd(),
testMatch: [],
},
{
noStackTrace: false,
},
),
)
);
}
return 'But it didn\'t throw anything.';
};
const printExpectedResult = (mode, isNot, expectedError) => expectedError == null
? (isNot ? 'fail ' : 'success ') + mode
: (isNot ? 'not throw a ' : `fail ${mode} with a `) +
(expectedError.name || `ParseError matching "${expectedError}"`);
export const nonstrictSettings = new Settings({strict: false});
export const strictSettings = new Settings({strict: true});
export const _getBuilt = function(expr, settings = new Settings()) {
/**
* Return the root node of the rendered HTML.
* @param expr
* @param settings
* @returns {Object}
*/
export function getBuilt(expr, settings = new Settings()) {
expr = r(expr); // support tagging literals
let rootNode = katex.__renderToDomTree(expr, settings);
if (rootNode.classes.indexOf('katex-error') >= 0) {
@@ -30,18 +89,7 @@ export const _getBuilt = function(expr, settings = new Settings()) {
(node) => node.classes.indexOf("strut") < 0));
}
return children;
};
/**
* Return the root node of the rendered HTML.
* @param expr
* @param settings
* @returns {Object}
*/
export const getBuilt = function(expr, settings = new Settings()) {
expect(expr).toBuild(settings);
return _getBuilt(expr, settings);
};
}
/**
* Return the root node of the parse tree.
@@ -49,12 +97,12 @@ export const getBuilt = function(expr, settings = new Settings()) {
* @param settings
* @returns {Object}
*/
export const getParsed = function(expr, settings = new Settings()) {
expect(expr).toParse(settings);
export function getParsed(expr, settings = new Settings()) {
expr = r(expr); // support tagging literals
return parseTree(expr, settings);
};
}
export const stripPositions = function(expr) {
export const stripPositions = expr => {
if (typeof expr !== "object" || expr === null) {
return expr;
}
@@ -67,34 +115,63 @@ export const stripPositions = function(expr) {
return expr;
};
export const parseAndSetResult = function(expr, result,
settings = new Settings()) {
try {
return parseTree(expr, settings);
} 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}`;
}
}
export const Mode = {
PARSE: {
apply: getParsed,
noun: 'parsing',
Verb: 'Parse',
},
BUILD: {
apply: getBuilt,
noun: 'building',
Verb: 'Build',
},
};
export const buildAndSetResult = function(expr, result,
settings = new Settings()) {
export const expectKaTeX = (expr, settings, mode, isNot, expectedError) => {
let pass = expectedError == null;
let error;
try {
return _getBuilt(expr, settings);
mode.apply(expr, settings);
} catch (e) {
result.pass = false;
error = e;
if (e instanceof ParseError) {
result.message = () =>
`'${expr}' failed building with error: ${e.message}`;
pass = expectedError === ParseError || (typeof expectedError ===
"string" && e.message === `KaTeX parse error: ${expectedError}`);
} else if (e instanceof ConsoleWarning) {
pass = expectedError === ConsoleWarning;
} else {
result.message = () =>
`'${expr}' failed building with unknown error: ${e.message}`;
pass = !!isNot; // always fail
}
}
return {
pass,
message: () => 'Expected the expression to ' +
printExpectedResult(mode.noun, isNot, expectedError) +
`:\n ${printReceived(expr)}\n` +
printActualErrorMessage(error),
};
};
export const expectEquivalent = (actual, expected, settings, mode, expand) => {
const actualTree = stripPositions(mode.apply(actual, settings));
const expectedTree = stripPositions(mode.apply(expected, settings));
const pass = JSON.stringify(actualTree) === JSON.stringify(expectedTree);
return {
pass,
message: pass
? () =>
`${mode.Verb} trees of ${printReceived(actual)} and ` +
`${printExpected(expected)} are equivalent`
: () => {
const diffString = diff(expectedTree, actualTree, {
expand,
});
return `${mode.Verb} trees of ${printReceived(actual)} and ` +
`${printExpected(expected)} are not equivalent` +
(diffString ? `:\n\n${diffString}` : '');
},
};
};