helpers.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. import katex from "../katex";
  2. import ParseError from "../src/ParseError";
  3. import parseTree from "../src/parseTree";
  4. import Settings from "../src/Settings";
  5. import diff from 'jest-diff';
  6. import {RECEIVED_COLOR, printReceived, printExpected} from 'jest-matcher-utils';
  7. import {formatStackTrace, separateMessageFromStack} from 'jest-message-util';
  8. export function ConsoleWarning(message) {
  9. Error.captureStackTrace(this, global.console.warn);
  10. this.name = this.constructor.name;
  11. this.message = message;
  12. }
  13. Object.setPrototypeOf(ConsoleWarning.prototype, Error.prototype);
  14. /**
  15. * Return the first raw string if x is tagged literal. Otherwise return x.
  16. */
  17. export const r = x => x != null && x.hasOwnProperty('raw') ? x.raw[0] : x;
  18. /**
  19. * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
  20. *
  21. * This function is from https://github.com/facebook/jest/blob/9867e16e518d50c79
  22. * 492f7f0d2bc1ef8dff37db4/packages/expect/src/to_throw_matchers.js and licensed
  23. * under the MIT license found in the https://github.com/facebook/jest/blob/master/LICENSE.
  24. */
  25. const printActualErrorMessage = error => {
  26. if (error) {
  27. const {message, stack} = separateMessageFromStack(error.stack);
  28. return (
  29. 'Instead, it threw:\n' +
  30. /* eslint-disable-next-line new-cap */
  31. RECEIVED_COLOR(
  32. ` ${message}` +
  33. formatStackTrace(
  34. // remove KaTeX internal stack entries
  35. stack.split('\n')
  36. .filter(line => line.indexOf('new ParseError') === -1)
  37. .join('\n'),
  38. {
  39. rootDir: process.cwd(),
  40. testMatch: [],
  41. },
  42. {
  43. noStackTrace: false,
  44. },
  45. ),
  46. )
  47. );
  48. }
  49. return 'But it didn\'t throw anything.';
  50. };
  51. const printExpectedResult = (mode, isNot, expectedError) => expectedError == null
  52. ? (isNot ? 'fail ' : 'success ') + mode
  53. : (isNot ? 'not throw a ' : `fail ${mode} with a `) +
  54. (expectedError.name || `ParseError matching "${expectedError}"`);
  55. export const nonstrictSettings = new Settings({strict: false});
  56. export const strictSettings = new Settings({strict: true});
  57. /**
  58. * Return the root node of the rendered HTML.
  59. * @param expr
  60. * @param settings
  61. * @returns {Object}
  62. */
  63. export function getBuilt(expr, settings = new Settings()) {
  64. expr = r(expr); // support tagging literals
  65. let rootNode = katex.__renderToDomTree(expr, settings);
  66. if (rootNode.classes.indexOf('katex-error') >= 0) {
  67. return rootNode;
  68. }
  69. if (rootNode.classes.indexOf('katex-display') >= 0) {
  70. rootNode = rootNode.children[0];
  71. }
  72. // grab the root node of the HTML rendering
  73. // rootNode.children[0] is the MathML rendering
  74. const builtHTML = rootNode.children[1];
  75. // combine the non-strut children of all base spans
  76. const children = [];
  77. for (let i = 0; i < builtHTML.children.length; i++) {
  78. children.push(...builtHTML.children[i].children.filter(
  79. (node) => node.classes.indexOf("strut") < 0));
  80. }
  81. return children;
  82. }
  83. /**
  84. * Return the root node of the parse tree.
  85. * @param expr
  86. * @param settings
  87. * @returns {Object}
  88. */
  89. export function getParsed(expr, settings = new Settings()) {
  90. expr = r(expr); // support tagging literals
  91. return parseTree(expr, settings);
  92. }
  93. export const stripPositions = expr => {
  94. if (typeof expr !== "object" || expr === null) {
  95. return expr;
  96. }
  97. if (expr.loc && expr.loc.lexer && typeof expr.loc.start === "number") {
  98. delete expr.loc;
  99. }
  100. Object.keys(expr).forEach(function(key) {
  101. stripPositions(expr[key]);
  102. });
  103. return expr;
  104. };
  105. export const Mode = {
  106. PARSE: {
  107. apply: getParsed,
  108. noun: 'parsing',
  109. Verb: 'Parse',
  110. },
  111. BUILD: {
  112. apply: getBuilt,
  113. noun: 'building',
  114. Verb: 'Build',
  115. },
  116. };
  117. export const expectKaTeX = (expr, settings, mode, isNot, expectedError) => {
  118. let pass = expectedError == null;
  119. let error;
  120. try {
  121. mode.apply(expr, settings);
  122. } catch (e) {
  123. error = e;
  124. if (e instanceof ParseError) {
  125. pass = expectedError === ParseError || (typeof expectedError ===
  126. "string" && e.message === `KaTeX parse error: ${expectedError}`);
  127. } else if (e instanceof ConsoleWarning) {
  128. pass = expectedError === ConsoleWarning;
  129. } else {
  130. pass = !!isNot; // always fail
  131. }
  132. }
  133. return {
  134. pass,
  135. message: () => 'Expected the expression to ' +
  136. printExpectedResult(mode.noun, isNot, expectedError) +
  137. `:\n ${printReceived(expr)}\n` +
  138. printActualErrorMessage(error),
  139. };
  140. };
  141. export const expectEquivalent = (actual, expected, settings, mode, expand) => {
  142. const actualTree = stripPositions(mode.apply(actual, settings));
  143. const expectedTree = stripPositions(mode.apply(expected, settings));
  144. const pass = JSON.stringify(actualTree) === JSON.stringify(expectedTree);
  145. return {
  146. pass,
  147. message: pass
  148. ? () =>
  149. `${mode.Verb} trees of ${printReceived(actual)} and ` +
  150. `${printExpected(expected)} are equivalent`
  151. : () => {
  152. const diffString = diff(expectedTree, actualTree, {
  153. expand,
  154. });
  155. return `${mode.Verb} trees of ${printReceived(actual)} and ` +
  156. `${printExpected(expected)} are not equivalent` +
  157. (diffString ? `:\n\n${diffString}` : '');
  158. },
  159. };
  160. };