snapshot.js (9711B)
1 import _extends from "@babel/runtime/helpers/extends"; 2 import _defineProperty from "@babel/runtime/helpers/defineProperty"; 3 4 function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; } 5 6 function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; } 7 8 /** 9 * This file contains helper methods to create expected snapshot structures 10 * of both instance and ES6 exports. 11 * 12 * The files are located here and not under /test or /tools so it's transpiled 13 * into ES5 code under /lib and can be used straight by node.js 14 */ 15 import assert from 'assert'; 16 import * as allIsFunctions from './is.js'; 17 import { create } from '../core/create.js'; 18 import { endsWith } from './string.js'; 19 export function validateBundle(expectedBundleStructure, bundle) { 20 var originalWarn = console.warn; 21 22 console.warn = function () { 23 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { 24 args[_key] = arguments[_key]; 25 } 26 27 if (args.join(' ').indexOf('is moved to') !== -1 && args.join(' ').indexOf('Please use the new location instead') !== -1) { 28 // Ignore warnings like: 29 // Warning: math.type.isNumber is moved to math.isNumber in v6.0.0. Please use the new location instead. 30 return; 31 } 32 33 originalWarn.apply(console, args); 34 }; 35 36 try { 37 var issues = []; // see whether all expected functions and objects are there 38 39 traverse(expectedBundleStructure, (expectedType, path) => { 40 var actualValue = get(bundle, path); 41 var actualType = validateTypeOf(actualValue); 42 var message = actualType === 'undefined' ? 'Missing entry in bundle. ' + "Path: ".concat(JSON.stringify(path), ", expected type: ").concat(expectedType, ", actual type: ").concat(actualType) : 'Unexpected entry type in bundle. ' + "Path: ".concat(JSON.stringify(path), ", expected type: ").concat(expectedType, ", actual type: ").concat(actualType); 43 44 if (actualType !== expectedType) { 45 issues.push({ 46 actualType, 47 expectedType, 48 message 49 }); 50 console.warn(message); 51 } 52 }); // see whether there are any functions or objects that shouldn't be there 53 54 traverse(bundle, (actualValue, path) => { 55 var actualType = validateTypeOf(actualValue); 56 var expectedType = get(expectedBundleStructure, path) || 'undefined'; // FIXME: ugly to have these special cases 57 58 if (path.join('.').indexOf('docs.') !== -1) { 59 // ignore the contents of docs 60 return; 61 } 62 63 if (path.join('.').indexOf('all.') !== -1) { 64 // ignore the contents of all dependencies 65 return; 66 } 67 68 var message = expectedType === 'undefined' ? 'Unknown entry in bundle. ' + 'Is there a new function added which is missing in this snapshot test? ' + "Path: ".concat(JSON.stringify(path), ", expected type: ").concat(expectedType, ", actual type: ").concat(actualType) : 'Unexpected entry type in bundle. ' + "Path: ".concat(JSON.stringify(path), ", expected type: ").concat(expectedType, ", actual type: ").concat(actualType); 69 70 if (actualType !== expectedType) { 71 issues.push({ 72 actualType, 73 expectedType, 74 message 75 }); 76 console.warn(message); 77 } 78 }); // assert on the first issue (if any) 79 80 if (issues.length > 0) { 81 var { 82 actualType, 83 expectedType, 84 message 85 } = issues[0]; 86 console.warn("".concat(issues.length, " bundle issues found")); 87 assert.strictEqual(actualType, expectedType, message); 88 } 89 } finally { 90 console.warn = originalWarn; 91 } 92 } 93 /** 94 * Based on an object with factory functions, create the expected 95 * structures for ES6 export and a mathjs instance. 96 * @param {Object} factories 97 * @return {{expectedInstanceStructure: Object, expectedES6Structure: Object}} 98 */ 99 100 export function createSnapshotFromFactories(factories) { 101 var math = create(factories); 102 var allFactoryFunctions = {}; 103 var allFunctionsConstantsClasses = {}; 104 var allFunctionsConstants = {}; 105 var allTransformFunctions = {}; 106 var allDependencyCollections = {}; 107 var allClasses = {}; 108 var allNodeClasses = {}; 109 Object.keys(factories).forEach(factoryName => { 110 var factory = factories[factoryName]; 111 var name = factory.fn; 112 var isTransformFunction = factory.meta && factory.meta.isTransformFunction; 113 var isClass = !isLowerCase(name[0]) && validateTypeOf(math[name]) === 'Function'; 114 var dependenciesName = factory.fn + (isTransformFunction ? 'Transform' : '') + 'Dependencies'; 115 allFactoryFunctions[factoryName] = 'Function'; 116 allFunctionsConstantsClasses[name] = validateTypeOf(math[name]); 117 allDependencyCollections[dependenciesName] = 'Object'; 118 119 if (isTransformFunction) { 120 allTransformFunctions[name] = 'Function'; 121 } 122 123 if (isClass) { 124 if (endsWith(name, 'Node')) { 125 allNodeClasses[name] = 'Function'; 126 } else { 127 allClasses[name] = 'Function'; 128 } 129 } else { 130 allFunctionsConstants[name] = validateTypeOf(math[name]); 131 } 132 }); 133 var embeddedDocs = {}; 134 Object.keys(factories).forEach(factoryName => { 135 var factory = factories[factoryName]; 136 var name = factory.fn; 137 138 if (isLowerCase(factory.fn[0])) { 139 // ignore class names starting with upper case 140 embeddedDocs[name] = 'Object'; 141 } 142 }); 143 embeddedDocs = exclude(embeddedDocs, ['equalScalar', 'apply', 'addScalar', 'multiplyScalar', 'print', 'divideScalar', 'parse', 'compile', 'parser', 'chain', 'reviver', 'replacer']); 144 var allTypeChecks = {}; 145 Object.keys(allIsFunctions).forEach(name => { 146 if (name.indexOf('is') === 0) { 147 allTypeChecks[name] = 'Function'; 148 } 149 }); 150 var allErrorClasses = { 151 ArgumentsError: 'Function', 152 DimensionError: 'Function', 153 IndexError: 'Function' 154 }; 155 156 var expectedInstanceStructure = _objectSpread(_objectSpread(_objectSpread(_objectSpread({}, allFunctionsConstantsClasses), {}, { 157 on: 'Function', 158 off: 'Function', 159 once: 'Function', 160 emit: 'Function', 161 import: 'Function', 162 config: 'Function', 163 create: 'Function', 164 factory: 'Function' 165 }, allTypeChecks), allErrorClasses), {}, { 166 expression: { 167 transform: _objectSpread({}, allTransformFunctions), 168 mathWithTransform: _objectSpread(_objectSpread({}, exclude(allFunctionsConstants, ['chain'])), {}, { 169 config: 'Function' 170 }) 171 } 172 }); 173 174 var expectedES6Structure = _objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread(_objectSpread({}, exclude(allFunctionsConstantsClasses, ['E', 'false', 'Infinity', 'NaN', 'null', 'PI', 'true'])), {}, { 175 create: 'Function', 176 config: 'Function', 177 factory: 'Function', 178 _true: 'boolean', 179 _false: 'boolean', 180 _null: 'null', 181 _Infinity: 'number', 182 _NaN: 'number' 183 }, allTypeChecks), allErrorClasses), allDependencyCollections), allFactoryFunctions), {}, { 184 docs: embeddedDocs 185 }); 186 187 return { 188 expectedInstanceStructure, 189 expectedES6Structure 190 }; 191 } 192 export function validateTypeOf(x) { 193 if (x && x.type === 'Unit') { 194 return 'Unit'; 195 } 196 197 if (x && x.type === 'Complex') { 198 return 'Complex'; 199 } 200 201 if (Array.isArray(x)) { 202 return 'Array'; 203 } 204 205 if (x === null) { 206 return 'null'; 207 } 208 209 if (typeof x === 'function') { 210 return 'Function'; 211 } 212 213 if (typeof x === 'object') { 214 return 'Object'; 215 } 216 217 return typeof x; 218 } 219 220 function traverse(obj) { 221 var callback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : (value, path) => {}; 222 var path = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : []; 223 224 // FIXME: ugly to have these special cases 225 if (path.length > 0 && path[0].indexOf('Dependencies') !== -1) { 226 // special case for objects holding a collection of dependencies 227 callback(obj, path); 228 } else if (validateTypeOf(obj) === 'Array') { 229 obj.map((item, index) => traverse(item, callback, path.concat(index))); 230 } else if (validateTypeOf(obj) === 'Object') { 231 Object.keys(obj).forEach(key => { 232 // FIXME: ugly to have these special cases 233 // ignore special case of deprecated docs 234 if (key === 'docs' && path.join('.') === 'expression') { 235 return; 236 } 237 238 traverse(obj[key], callback, path.concat(key)); 239 }); 240 } else { 241 callback(obj, path); 242 } 243 } 244 245 function get(object, path) { 246 var child = object; 247 248 for (var i = 0; i < path.length; i++) { 249 var key = path[i]; 250 child = child ? child[key] : undefined; 251 } 252 253 return child; 254 } 255 /** 256 * Create a copy of the provided `object` and delete 257 * all properties listed in `excludedProperties` 258 * @param {Object} object 259 * @param {string[]} excludedProperties 260 * @return {Object} 261 */ 262 263 264 function exclude(object, excludedProperties) { 265 var strippedObject = _extends({}, object); 266 267 excludedProperties.forEach(excludedProperty => { 268 delete strippedObject[excludedProperty]; 269 }); 270 return strippedObject; 271 } 272 273 function isLowerCase(text) { 274 return typeof text === 'string' && text.toLowerCase() === text; 275 }