utils.js 6.1 KB

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