utils.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. "use strict";
  2. var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
  3. Object.defineProperty(exports, "__esModule", {
  4. value: true
  5. });
  6. exports.checkHtmlElement = checkHtmlElement;
  7. exports.checkNode = checkNode;
  8. exports.parseCSS = parseCSS;
  9. exports.deprecate = deprecate;
  10. exports.getMessage = getMessage;
  11. exports.matches = matches;
  12. exports.normalize = normalize;
  13. exports.getTag = getTag;
  14. exports.getSingleElementValue = getSingleElementValue;
  15. exports.compareArraysAsSet = compareArraysAsSet;
  16. exports.toSentence = toSentence;
  17. exports.NodeTypeError = exports.HtmlElementTypeError = void 0;
  18. var _redent = _interopRequireDefault(require("redent"));
  19. var _css = require("css");
  20. var _isEqual = _interopRequireDefault(require("lodash/isEqual"));
  21. class GenericTypeError extends Error {
  22. constructor(expectedString, received, matcherFn, context) {
  23. super();
  24. /* istanbul ignore next */
  25. if (Error.captureStackTrace) {
  26. Error.captureStackTrace(this, matcherFn);
  27. }
  28. let withType = '';
  29. try {
  30. withType = context.utils.printWithType('Received', received, context.utils.printReceived);
  31. } catch (e) {// Can throw for Document:
  32. // https://github.com/jsdom/jsdom/issues/2304
  33. }
  34. this.message = [context.utils.matcherHint(`${context.isNot ? '.not' : ''}.${matcherFn.name}`, 'received', ''), '', // eslint-disable-next-line babel/new-cap
  35. `${context.utils.RECEIVED_COLOR('received')} value must ${expectedString}.`, withType].join('\n');
  36. }
  37. }
  38. class HtmlElementTypeError extends GenericTypeError {
  39. constructor(...args) {
  40. super('be an HTMLElement or an SVGElement', ...args);
  41. }
  42. }
  43. exports.HtmlElementTypeError = HtmlElementTypeError;
  44. class NodeTypeError extends GenericTypeError {
  45. constructor(...args) {
  46. super('be a Node', ...args);
  47. }
  48. }
  49. exports.NodeTypeError = NodeTypeError;
  50. function checkHasWindow(htmlElement, ErrorClass, ...args) {
  51. if (!htmlElement || !htmlElement.ownerDocument || !htmlElement.ownerDocument.defaultView) {
  52. throw new ErrorClass(htmlElement, ...args);
  53. }
  54. }
  55. function checkNode(node, ...args) {
  56. checkHasWindow(node, NodeTypeError, ...args);
  57. const window = node.ownerDocument.defaultView;
  58. if (!(node instanceof window.Node)) {
  59. throw new NodeTypeError(node, ...args);
  60. }
  61. }
  62. function checkHtmlElement(htmlElement, ...args) {
  63. checkHasWindow(htmlElement, HtmlElementTypeError, ...args);
  64. const window = htmlElement.ownerDocument.defaultView;
  65. if (!(htmlElement instanceof window.HTMLElement) && !(htmlElement instanceof window.SVGElement)) {
  66. throw new HtmlElementTypeError(htmlElement, ...args);
  67. }
  68. }
  69. class InvalidCSSError extends Error {
  70. constructor(received, matcherFn, context) {
  71. super();
  72. /* istanbul ignore next */
  73. if (Error.captureStackTrace) {
  74. Error.captureStackTrace(this, matcherFn);
  75. }
  76. this.message = [received.message, '', // eslint-disable-next-line babel/new-cap
  77. context.utils.RECEIVED_COLOR(`Failing css:`), // eslint-disable-next-line babel/new-cap
  78. context.utils.RECEIVED_COLOR(`${received.css}`)].join('\n');
  79. }
  80. }
  81. function parseCSS(css, ...args) {
  82. const ast = (0, _css.parse)(`selector { ${css} }`, {
  83. silent: true
  84. }).stylesheet;
  85. if (ast.parsingErrors && ast.parsingErrors.length > 0) {
  86. const {
  87. reason,
  88. line
  89. } = ast.parsingErrors[0];
  90. throw new InvalidCSSError({
  91. css,
  92. message: `Syntax error parsing expected css: ${reason} on line: ${line}`
  93. }, ...args);
  94. }
  95. const parsedRules = ast.rules[0].declarations.filter(d => d.type === 'declaration').reduce((obj, {
  96. property,
  97. value
  98. }) => Object.assign(obj, {
  99. [property]: value
  100. }), {});
  101. return parsedRules;
  102. }
  103. function display(context, value) {
  104. return typeof value === 'string' ? value : context.utils.stringify(value);
  105. }
  106. function getMessage(context, matcher, expectedLabel, expectedValue, receivedLabel, receivedValue) {
  107. return [`${matcher}\n`, // eslint-disable-next-line babel/new-cap
  108. `${expectedLabel}:\n${context.utils.EXPECTED_COLOR((0, _redent.default)(display(context, expectedValue), 2))}`, // eslint-disable-next-line babel/new-cap
  109. `${receivedLabel}:\n${context.utils.RECEIVED_COLOR((0, _redent.default)(display(context, receivedValue), 2))}`].join('\n');
  110. }
  111. function matches(textToMatch, matcher) {
  112. if (matcher instanceof RegExp) {
  113. return matcher.test(textToMatch);
  114. } else {
  115. return textToMatch.includes(String(matcher));
  116. }
  117. }
  118. function deprecate(name, replacementText) {
  119. // Notify user that they are using deprecated functionality.
  120. // eslint-disable-next-line no-console
  121. console.warn(`Warning: ${name} has been deprecated and will be removed in future updates.`, replacementText);
  122. }
  123. function normalize(text) {
  124. return text.replace(/\s+/g, ' ').trim();
  125. }
  126. function getTag(element) {
  127. return element.tagName && element.tagName.toLowerCase();
  128. }
  129. function getSelectValue({
  130. multiple,
  131. options
  132. }) {
  133. const selectedOptions = [...options].filter(option => option.selected);
  134. if (multiple) {
  135. return [...selectedOptions].map(opt => opt.value);
  136. }
  137. /* istanbul ignore if */
  138. if (selectedOptions.length === 0) {
  139. return undefined; // Couldn't make this happen, but just in case
  140. }
  141. return selectedOptions[0].value;
  142. }
  143. function getInputValue(inputElement) {
  144. switch (inputElement.type) {
  145. case 'number':
  146. return inputElement.value === '' ? null : Number(inputElement.value);
  147. case 'checkbox':
  148. return inputElement.checked;
  149. default:
  150. return inputElement.value;
  151. }
  152. }
  153. function getSingleElementValue(element) {
  154. /* istanbul ignore if */
  155. if (!element) {
  156. return undefined;
  157. }
  158. switch (element.tagName.toLowerCase()) {
  159. case 'input':
  160. return getInputValue(element);
  161. case 'select':
  162. return getSelectValue(element);
  163. default:
  164. return element.value;
  165. }
  166. }
  167. function compareArraysAsSet(a, b) {
  168. if (Array.isArray(a) && Array.isArray(b)) {
  169. return (0, _isEqual.default)(new Set(a), new Set(b));
  170. }
  171. return undefined;
  172. }
  173. function toSentence(array, {
  174. wordConnector = ', ',
  175. lastWordConnector = ' and '
  176. } = {}) {
  177. return [array.slice(0, -1).join(wordConnector), array[array.length - 1]].join(array.length > 1 ? lastWordConnector : '');
  178. }