spy4js
Advanced tools
Comparing version 2.7.0 to 2.8.0
@@ -20,3 +20,132 @@ 'use strict'; | ||
const forEach = (arrOrObj, handler) => { | ||
const NOP = () => {}; | ||
const forEach = (o, handler) => Object.keys(o).forEach(k => handler(o[k], k)); | ||
const __typePattern = /^\[object ([^\]]+)]$/; | ||
const __getTypeOfObject = o => (__typePattern.exec(Object.prototype.toString.call(o)) || [undefined])[1]; | ||
const __serializeProp = (key, value, custom, serialized) => typeof value === 'string' ? `${key}="${value}"` : `${key}={${__serialize(value, custom, serialized)}}`; | ||
const __serializeProps = (o, custom, serialized) => { | ||
const elemProps = o.props; | ||
const props = []; | ||
forEach(elemProps, (v, k) => { | ||
if (v !== undefined && k !== 'children') props.push(__serializeProp(k, v, custom, serialized)); | ||
}); | ||
if (o.key) props.push(__serializeProp('key', o.key, custom, serialized)); | ||
if (o.ref) props.push(__serializeProp('ref', o.ref, custom, serialized)); | ||
if (!props.length) return ''; | ||
return ' ' + props.join(' '); | ||
}; | ||
const __serializeChildren = (o, custom, serialized) => { | ||
const children = o.props.children; | ||
if (!children) return ''; | ||
if (typeof children === 'string') return children; | ||
return Array.isArray(children) ? children.map(v => __serialize(v, custom, serialized)).join('') : __serialize(children, custom, serialized); | ||
}; | ||
const __reactTypeNameReader = { | ||
String: type => type, | ||
Function: type => type.name, | ||
Symbol: type => Symbol.keyFor(type) === 'react.fragment' && 'Fragment' || undefined | ||
}; | ||
const getTypeName = type => (__reactTypeNameReader[__getTypeOfObject(type)] || NOP)(type); | ||
const __serializeReactElement = (o, custom, serialized) => { | ||
const type = getTypeName(o.type) || 'UNKNOWN'; | ||
const children = __serializeChildren(o, custom, serialized); | ||
return `<${type}${__serializeProps(o, custom, serialized)}${children ? `>${children}</${type}>` : ' />'}`; | ||
}; | ||
const __serializeReact = (o, custom, serialized) => { | ||
const symbolKey = Symbol.keyFor(o.$$typeof); | ||
switch (symbolKey) { | ||
case 'react.element': | ||
return __serializeReactElement(o, custom, serialized); | ||
default: | ||
return; | ||
} | ||
}; | ||
const __serializeIfReact = (o, custom, serialized) => { | ||
if (Object.prototype.toString.call(o.$$typeof) === '[object Symbol]') { | ||
const key = Symbol.keyFor(o.$$typeof); | ||
if (key && key.indexOf('react.') === 0) return __serializeReact(o, custom, serialized); | ||
} | ||
}; | ||
const __serializerByType = { | ||
BigInt: o => `${String(o)}n`, | ||
RegExp: o => `/${String(o)}/`, | ||
String: o => o.indexOf('"') === -1 && o.indexOf("'") !== -1 ? `"${o}"` : `'${o}'`, | ||
Function: o => o.name || 'Function', | ||
AsyncFunction: o => o.name || 'AsyncFunction', | ||
Date: o => `new Date(${Number(o)})`, | ||
Number: o => String(o), | ||
Boolean: o => String(o), | ||
Set: o => `new Set(${__serializeArray([...o.keys()], undefined, [])})`, | ||
Map: o => `new Map(${__serializeArray([...o.entries()], undefined, [])})`, | ||
Symbol: o => Symbol.keyFor(o) === undefined ? o.toString() : `Symbol.for('${Symbol.keyFor(o)}')`, | ||
Error: o => `new ${o.name}('${o.message}')` | ||
}; | ||
const __serializeArray = (o, custom, serialized) => { | ||
const results = []; | ||
for (let i = 0; i < o.length; i++) { | ||
results.push(__serialize(o[i], custom, serialized)); | ||
} | ||
return `[${results.join(', ')}]`; | ||
}; | ||
const __serializeOptArray = (o, custom, serialized) => { | ||
if (Array.isArray(o)) return __serializeArray(o, custom, serialized); | ||
}; | ||
const __serializeObject = (o, custom, serialized) => { | ||
const oKeys = Object.keys(o); | ||
oKeys.sort(); | ||
const results = []; | ||
for (let i = 0; i < oKeys.length; i++) { | ||
const key = oKeys[i]; | ||
results.push(`${key}: ${__serialize(o[key], custom, serialized)}`); | ||
} | ||
const objectType = o.constructor.name; | ||
const displayedType = objectType === 'Object' ? '' : objectType; | ||
return `${displayedType}{${results.join(', ')}}`; | ||
}; | ||
const Serialize = { | ||
custom: (o, custom) => custom && custom(o), | ||
undefOrNull: o => o === undefined && 'undefined' || o === null && 'null' || undefined, | ||
flat: o => (__serializerByType[__getTypeOfObject(o)] || NOP)(o), | ||
cyclomatic: (o, _, serialized) => serialized.indexOf(o) !== -1 && '>CYCLOMATIC<' || undefined, | ||
react: __serializeIfReact, | ||
array: __serializeOptArray | ||
}; | ||
const __serialize = (o, custom, serialized) => Serialize.custom(o, custom, serialized) || Serialize.undefOrNull(o, custom, serialized) || Serialize.flat(o, custom, serialized) || Serialize.cyclomatic(o, custom, serialized) || (nextSerialized => Serialize.react(o, custom, nextSerialized) || Serialize.array(o, custom, nextSerialized) || __serializeObject(o, custom, nextSerialized))([...serialized, o]); | ||
const _serialize = (o, custom) => __serialize(o, custom, []); | ||
const Serializer = { | ||
run: o => _serialize(o), | ||
create: custom => o => _serialize(o, custom) | ||
}; | ||
const IGNORE = Symbol.for('__Spy_IGNORE__'); | ||
const serialize = Serializer.create(o => o === IGNORE && '>IGNORED<' || undefined); | ||
const forEach$1 = (arrOrObj, handler) => { | ||
for (let key in arrOrObj) { | ||
@@ -31,3 +160,3 @@ if (arrOrObj.hasOwnProperty(key)) { | ||
const keys = []; | ||
forEach(arrOrObj, key => keys.push(key)); | ||
forEach$1(arrOrObj, key => keys.push(key)); | ||
return keys; | ||
@@ -38,3 +167,3 @@ }; | ||
const result = [...arr1]; | ||
forEach(arr2, (key, val) => { | ||
forEach$1(arr2, (key, val) => { | ||
if (arr1.indexOf(val) === -1) { | ||
@@ -47,3 +176,3 @@ result.push(val); | ||
const IGNORE = Symbol.for('__Spy_IGNORE__'); | ||
const SPY_COMPARE_FAILED = 'Spy.COMPARE failed'; | ||
@@ -58,3 +187,3 @@ class SpyComparator { | ||
compare(arg) { | ||
if (!this._func(arg)) return 'custom comparison failed'; | ||
if (this._func(arg) === false) return [SPY_COMPARE_FAILED]; | ||
} | ||
@@ -66,2 +195,4 @@ | ||
const __different = type => ['different ' + type]; | ||
const __diff = (a, b, initial, useOwnEquals, alreadyComparedArray = []) => { | ||
@@ -72,7 +203,7 @@ if (a === IGNORE || b === IGNORE) return; | ||
if (a === b) return; | ||
if (a === undefined || b === undefined) return 'one was undefined'; | ||
if (a === null || b === null) return 'one was null'; | ||
if (a === undefined || b === undefined) return ['one was undefined']; | ||
if (a === null || b === null) return ['one was null']; | ||
const aClass = Object.prototype.toString.call(a); | ||
const bClass = Object.prototype.toString.call(b); | ||
if (aClass !== bClass) return `different object types: ${aClass} <-> ${bClass}`; | ||
if (aClass !== bClass) return __different(`object types: ${aClass} <-> ${bClass}`); | ||
@@ -85,12 +216,12 @@ switch (aClass) { | ||
return 'different regexp'; | ||
return __different('regexp'); | ||
case '[object String]': | ||
return 'different string'; | ||
return __different('string'); | ||
case '[object Function]': | ||
return 'different function'; | ||
return __different('function'); | ||
case '[object AsyncFunction]': | ||
return 'different async function'; | ||
return __different('async function'); | ||
@@ -102,6 +233,6 @@ case '[object Number]': | ||
return 'different number'; | ||
return __different('number'); | ||
case '[object BigInt]': | ||
return 'different BigInt'; | ||
return __different('BigInt'); | ||
@@ -113,9 +244,9 @@ case '[object Date]': | ||
return 'different date'; | ||
return __different('date'); | ||
case '[object Boolean]': | ||
return 'different bool'; | ||
return __different('bool'); | ||
case '[object Symbol]': | ||
return 'different symbols'; | ||
return __different('symbols'); | ||
@@ -127,7 +258,7 @@ case '[object Error]': | ||
return 'different error'; | ||
return __different('error'); | ||
default: | ||
if (a.constructor !== b.constructor) { | ||
return 'different constructor'; | ||
return __different('constructor'); | ||
} | ||
@@ -142,3 +273,3 @@ | ||
return 'own equals method failed <- ' + 'Maybe you want to disable the usage ' + 'of own equals implementation? ' + '[ Use: spy.configure({useOwnEquals: false}) ]'; | ||
return ['own equals method failed <- ' + 'Maybe you want to disable the usage ' + 'of own equals implementation? ' + '[ Use: spy.configure({useOwnEquals: false}) ]']; | ||
} | ||
@@ -156,6 +287,6 @@ | ||
const diffStr = __diff(a[key], b[key], false, useOwnEquals, compared); | ||
const diff = __diff(a[key], b[key], false, useOwnEquals, compared); | ||
if (diffStr !== undefined) { | ||
return `${initial ? `--> ${key}` : `${key}`} / ${diffStr}`; | ||
if (diff !== undefined) { | ||
return [key, ...diff]; | ||
} | ||
@@ -165,6 +296,30 @@ } | ||
const __serializeDifferentProp = (obj, diff) => { | ||
let toSerialize = obj; | ||
diff.slice(0, -1).forEach(key => { | ||
toSerialize = toSerialize[key]; | ||
}); | ||
return serialize(toSerialize); | ||
}; | ||
const __diffToStr = (diff, info = '') => { | ||
if (diff.length === 1) return diff[0]; | ||
return `--> ${diff[0]} / ` + diff.slice(1).join(' / ') + info; | ||
}; | ||
const differenceOf = (a, b, config = { | ||
useOwnEquals: true | ||
}) => { | ||
return __diff(a, b, true, config.useOwnEquals); | ||
const diff = __diff(a, b, true, config.useOwnEquals); | ||
if (!diff) return; | ||
const diffStr = __diffToStr(diff); | ||
if (diff.length < 2) return diffStr; | ||
const diffProp1 = __serializeDifferentProp(a, diff); | ||
const info = diff[diff.length - 1] === SPY_COMPARE_FAILED ? `called with: ${diffProp1}` : `${diffProp1} != ${__serializeDifferentProp(b, diff)}`; | ||
return `${diffStr} [${info}]`; | ||
}; | ||
@@ -197,3 +352,3 @@ | ||
SpyRegistry.prototype.restoreAll = function () { | ||
forEach(this.register, (ignored, entry) => { | ||
forEach$1(this.register, (ignored, entry) => { | ||
restoreAttributeForEntry(entry); | ||
@@ -242,130 +397,2 @@ }); | ||
const NOP = () => {}; | ||
const forEach$1 = (o, handler) => Object.keys(o).forEach(k => handler(o[k], k)); | ||
const __typePattern = /^\[object ([^\]]+)]$/; | ||
const __getTypeOfObject = o => (__typePattern.exec(Object.prototype.toString.call(o)) || [undefined])[1]; | ||
const __serializeProp = (key, value, custom, serialized) => typeof value === 'string' ? `${key}="${value}"` : `${key}={${__serialize(value, custom, serialized)}}`; | ||
const __serializeProps = (o, custom, serialized) => { | ||
const elemProps = o.props; | ||
const props = []; | ||
forEach$1(elemProps, (v, k) => { | ||
if (v !== undefined && k !== 'children') props.push(__serializeProp(k, v, custom, serialized)); | ||
}); | ||
if (o.key) props.push(__serializeProp('key', o.key, custom, serialized)); | ||
if (o.ref) props.push(__serializeProp('ref', o.ref, custom, serialized)); | ||
if (!props.length) return ''; | ||
return ' ' + props.join(' '); | ||
}; | ||
const __serializeChildren = (o, custom, serialized) => { | ||
const children = o.props.children; | ||
if (!children) return ''; | ||
if (typeof children === 'string') return children; | ||
return Array.isArray(children) ? children.map(v => __serialize(v, custom, serialized)).join('') : __serialize(children, custom, serialized); | ||
}; | ||
const __reactTypeNameReader = { | ||
String: type => type, | ||
Function: type => type.name, | ||
Symbol: type => Symbol.keyFor(type) === 'react.fragment' && 'Fragment' || undefined | ||
}; | ||
const getTypeName = type => (__reactTypeNameReader[__getTypeOfObject(type)] || NOP)(type); | ||
const __serializeReactElement = (o, custom, serialized) => { | ||
const type = getTypeName(o.type) || 'UNKNOWN'; | ||
const children = __serializeChildren(o, custom, serialized); | ||
return `<${type}${__serializeProps(o, custom, serialized)}${children ? `>${children}</${type}>` : ' />'}`; | ||
}; | ||
const __serializeReact = (o, custom, serialized) => { | ||
const symbolKey = Symbol.keyFor(o.$$typeof); | ||
switch (symbolKey) { | ||
case 'react.element': | ||
return __serializeReactElement(o, custom, serialized); | ||
default: | ||
return; | ||
} | ||
}; | ||
const __serializeIfReact = (o, custom, serialized) => { | ||
if (Object.prototype.toString.call(o.$$typeof) === '[object Symbol]') { | ||
const key = Symbol.keyFor(o.$$typeof); | ||
if (key && key.indexOf('react.') === 0) return __serializeReact(o, custom, serialized); | ||
} | ||
}; | ||
const __serializerByType = { | ||
BigInt: o => `${String(o)}n`, | ||
RegExp: o => `/${String(o)}/`, | ||
String: o => o.indexOf('"') === -1 && o.indexOf("'") !== -1 ? `"${o}"` : `'${o}'`, | ||
Function: o => o.name || 'Function', | ||
AsyncFunction: o => o.name || 'AsyncFunction', | ||
Date: o => `new Date(${Number(o)})`, | ||
Number: o => String(o), | ||
Boolean: o => String(o), | ||
Set: o => `new Set(${__serializeArray([...o.keys()], undefined, [])})`, | ||
Map: o => `new Map(${__serializeArray([...o.entries()], undefined, [])})`, | ||
Symbol: o => Symbol.keyFor(o) === undefined ? o.toString() : `Symbol.for('${Symbol.keyFor(o)}')`, | ||
Error: o => `new ${o.name}('${o.message}')` | ||
}; | ||
const __serializeArray = (o, custom, serialized) => { | ||
const results = []; | ||
for (let i = 0; i < o.length; i++) { | ||
results.push(__serialize(o[i], custom, serialized)); | ||
} | ||
return `[${results.join(', ')}]`; | ||
}; | ||
const __serializeOptArray = (o, custom, serialized) => { | ||
if (Array.isArray(o)) return __serializeArray(o, custom, serialized); | ||
}; | ||
const __serializeObject = (o, custom, serialized) => { | ||
const oKeys = Object.keys(o); | ||
oKeys.sort(); | ||
const results = []; | ||
for (let i = 0; i < oKeys.length; i++) { | ||
const key = oKeys[i]; | ||
results.push(`${key}: ${__serialize(o[key], custom, serialized)}`); | ||
} | ||
const objectType = o.constructor.name; | ||
const displayedType = objectType === 'Object' ? '' : objectType; | ||
return `${displayedType}{${results.join(', ')}}`; | ||
}; | ||
const Serialize = { | ||
custom: (o, custom) => custom && custom(o), | ||
undefOrNull: o => o === undefined && 'undefined' || o === null && 'null' || undefined, | ||
flat: o => (__serializerByType[__getTypeOfObject(o)] || NOP)(o), | ||
cyclomatic: (o, _, serialized) => serialized.indexOf(o) !== -1 && '>CYCLOMATIC<' || undefined, | ||
react: __serializeIfReact, | ||
array: __serializeOptArray | ||
}; | ||
const __serialize = (o, custom, serialized) => Serialize.custom(o, custom, serialized) || Serialize.undefOrNull(o, custom, serialized) || Serialize.flat(o, custom, serialized) || Serialize.cyclomatic(o, custom, serialized) || (nextSerialized => Serialize.react(o, custom, nextSerialized) || Serialize.array(o, custom, nextSerialized) || __serializeObject(o, custom, nextSerialized))([...serialized, o]); | ||
const _serialize = (o, custom) => __serialize(o, custom, []); | ||
const Serializer = { | ||
run: o => _serialize(o), | ||
create: custom => o => _serialize(o, custom) | ||
}; | ||
const serialize = Serializer.create(o => o === IGNORE && '>IGNORED<' || undefined); | ||
const uninitialized = method => () => { | ||
@@ -399,3 +426,3 @@ throw new Error(`Method '${method}' was not initialized on Mock.`); | ||
const mock = registerMock(obj); | ||
forEach(methods, (_, method) => { | ||
forEach$1(methods, (_, method) => { | ||
mock[method] = uninitialized(method); | ||
@@ -413,3 +440,3 @@ }); | ||
}, spyOn) => { | ||
forEach(mock, method => { | ||
forEach$1(mock, method => { | ||
try { | ||
@@ -424,3 +451,3 @@ mock[method] = spyOn(mocked, method); | ||
const initMockScope = (scoping, spyOn) => { | ||
forEach(_mocks[scoping], (_, mock) => initMock(mock, spyOn)); | ||
forEach$1(_mocks[scoping], (_, mock) => initMock(mock, spyOn)); | ||
}; | ||
@@ -433,2 +460,6 @@ | ||
const _testSuite = { | ||
isJest: !!jest, | ||
isCJS: !!require | ||
}; | ||
const runner = {}; | ||
@@ -452,3 +483,3 @@ const oldDescribe = describe; | ||
const configureTestSuite = other => { | ||
const configure = other => { | ||
if (other.afterEach) runner.afterEach = other.afterEach; | ||
@@ -458,2 +489,31 @@ if (other.beforeEach) runner.beforeEach = other.beforeEach; | ||
const addSnapshotSerializer = serializer => { | ||
_testSuite.isJest && expect && expect.addSnapshotSerializer && expect.addSnapshotSerializer(serializer); | ||
}; | ||
const __caller = stackNum => { | ||
const traceFn = Error.prepareStackTrace; | ||
Error.prepareStackTrace = (err, stack) => stack; | ||
const stack = new Error().stack; | ||
Error.prepareStackTrace = traceFn; | ||
return stack[stackNum].getFileName(); | ||
}; | ||
const __callerBasedir = stackNum => require('path').dirname(__caller(stackNum)); | ||
const STACK_NUM_CREATE_MOCK = 4; | ||
const createMock$1 = (SPY, moduleName, names) => { | ||
if (!_testSuite.isCJS) throw new Error('Spy.moduleMock works only if your test runner executes with CommonJS'); | ||
return SPY.mock(require(require('path').join(__callerBasedir(STACK_NUM_CREATE_MOCK), moduleName)), ...names); | ||
}; | ||
const TestSuite = { | ||
addSnapshotSerializer, | ||
createMock: createMock$1, | ||
configure | ||
}; | ||
const registry = new SpyRegistry(); | ||
@@ -470,3 +530,3 @@ let __LOCK__ = true; | ||
}; | ||
expect && expect.addSnapshotSerializer && expect.addSnapshotSerializer({ | ||
TestSuite.addSnapshotSerializer({ | ||
test: v => v && v[Symbols.isSpy], | ||
@@ -715,3 +775,3 @@ print: spy => spy[Symbols.snap] | ||
}; | ||
forEach(SpyFunctions, (key, value) => { | ||
forEach$1(SpyFunctions, (key, value) => { | ||
spy[key] = value; | ||
@@ -728,3 +788,3 @@ }); | ||
configureTestSuite({ | ||
TestSuite.configure({ | ||
afterEach: config.afterEach, | ||
@@ -760,2 +820,6 @@ beforeEach: config.beforeEach | ||
static mockModule(moduleName, ...methodNames) { | ||
return TestSuite.createMock(Spy, moduleName, methodNames); | ||
} | ||
static initMocks(scope) { | ||
@@ -786,4 +850,4 @@ initMocks(Spy.on, scope); | ||
}; | ||
configureTestSuite(defaultHooks); | ||
TestSuite.configure(defaultHooks); | ||
exports.Spy = Spy; |
@@ -16,3 +16,132 @@ function _defineProperty(obj, key, value) { | ||
const forEach = (arrOrObj, handler) => { | ||
const NOP = () => {}; | ||
const forEach = (o, handler) => Object.keys(o).forEach(k => handler(o[k], k)); | ||
const __typePattern = /^\[object ([^\]]+)]$/; | ||
const __getTypeOfObject = o => (__typePattern.exec(Object.prototype.toString.call(o)) || [undefined])[1]; | ||
const __serializeProp = (key, value, custom, serialized) => typeof value === 'string' ? `${key}="${value}"` : `${key}={${__serialize(value, custom, serialized)}}`; | ||
const __serializeProps = (o, custom, serialized) => { | ||
const elemProps = o.props; | ||
const props = []; | ||
forEach(elemProps, (v, k) => { | ||
if (v !== undefined && k !== 'children') props.push(__serializeProp(k, v, custom, serialized)); | ||
}); | ||
if (o.key) props.push(__serializeProp('key', o.key, custom, serialized)); | ||
if (o.ref) props.push(__serializeProp('ref', o.ref, custom, serialized)); | ||
if (!props.length) return ''; | ||
return ' ' + props.join(' '); | ||
}; | ||
const __serializeChildren = (o, custom, serialized) => { | ||
const children = o.props.children; | ||
if (!children) return ''; | ||
if (typeof children === 'string') return children; | ||
return Array.isArray(children) ? children.map(v => __serialize(v, custom, serialized)).join('') : __serialize(children, custom, serialized); | ||
}; | ||
const __reactTypeNameReader = { | ||
String: type => type, | ||
Function: type => type.name, | ||
Symbol: type => Symbol.keyFor(type) === 'react.fragment' && 'Fragment' || undefined | ||
}; | ||
const getTypeName = type => (__reactTypeNameReader[__getTypeOfObject(type)] || NOP)(type); | ||
const __serializeReactElement = (o, custom, serialized) => { | ||
const type = getTypeName(o.type) || 'UNKNOWN'; | ||
const children = __serializeChildren(o, custom, serialized); | ||
return `<${type}${__serializeProps(o, custom, serialized)}${children ? `>${children}</${type}>` : ' />'}`; | ||
}; | ||
const __serializeReact = (o, custom, serialized) => { | ||
const symbolKey = Symbol.keyFor(o.$$typeof); | ||
switch (symbolKey) { | ||
case 'react.element': | ||
return __serializeReactElement(o, custom, serialized); | ||
default: | ||
return; | ||
} | ||
}; | ||
const __serializeIfReact = (o, custom, serialized) => { | ||
if (Object.prototype.toString.call(o.$$typeof) === '[object Symbol]') { | ||
const key = Symbol.keyFor(o.$$typeof); | ||
if (key && key.indexOf('react.') === 0) return __serializeReact(o, custom, serialized); | ||
} | ||
}; | ||
const __serializerByType = { | ||
BigInt: o => `${String(o)}n`, | ||
RegExp: o => `/${String(o)}/`, | ||
String: o => o.indexOf('"') === -1 && o.indexOf("'") !== -1 ? `"${o}"` : `'${o}'`, | ||
Function: o => o.name || 'Function', | ||
AsyncFunction: o => o.name || 'AsyncFunction', | ||
Date: o => `new Date(${Number(o)})`, | ||
Number: o => String(o), | ||
Boolean: o => String(o), | ||
Set: o => `new Set(${__serializeArray([...o.keys()], undefined, [])})`, | ||
Map: o => `new Map(${__serializeArray([...o.entries()], undefined, [])})`, | ||
Symbol: o => Symbol.keyFor(o) === undefined ? o.toString() : `Symbol.for('${Symbol.keyFor(o)}')`, | ||
Error: o => `new ${o.name}('${o.message}')` | ||
}; | ||
const __serializeArray = (o, custom, serialized) => { | ||
const results = []; | ||
for (let i = 0; i < o.length; i++) { | ||
results.push(__serialize(o[i], custom, serialized)); | ||
} | ||
return `[${results.join(', ')}]`; | ||
}; | ||
const __serializeOptArray = (o, custom, serialized) => { | ||
if (Array.isArray(o)) return __serializeArray(o, custom, serialized); | ||
}; | ||
const __serializeObject = (o, custom, serialized) => { | ||
const oKeys = Object.keys(o); | ||
oKeys.sort(); | ||
const results = []; | ||
for (let i = 0; i < oKeys.length; i++) { | ||
const key = oKeys[i]; | ||
results.push(`${key}: ${__serialize(o[key], custom, serialized)}`); | ||
} | ||
const objectType = o.constructor.name; | ||
const displayedType = objectType === 'Object' ? '' : objectType; | ||
return `${displayedType}{${results.join(', ')}}`; | ||
}; | ||
const Serialize = { | ||
custom: (o, custom) => custom && custom(o), | ||
undefOrNull: o => o === undefined && 'undefined' || o === null && 'null' || undefined, | ||
flat: o => (__serializerByType[__getTypeOfObject(o)] || NOP)(o), | ||
cyclomatic: (o, _, serialized) => serialized.indexOf(o) !== -1 && '>CYCLOMATIC<' || undefined, | ||
react: __serializeIfReact, | ||
array: __serializeOptArray | ||
}; | ||
const __serialize = (o, custom, serialized) => Serialize.custom(o, custom, serialized) || Serialize.undefOrNull(o, custom, serialized) || Serialize.flat(o, custom, serialized) || Serialize.cyclomatic(o, custom, serialized) || (nextSerialized => Serialize.react(o, custom, nextSerialized) || Serialize.array(o, custom, nextSerialized) || __serializeObject(o, custom, nextSerialized))([...serialized, o]); | ||
const _serialize = (o, custom) => __serialize(o, custom, []); | ||
const Serializer = { | ||
run: o => _serialize(o), | ||
create: custom => o => _serialize(o, custom) | ||
}; | ||
const IGNORE = Symbol.for('__Spy_IGNORE__'); | ||
const serialize = Serializer.create(o => o === IGNORE && '>IGNORED<' || undefined); | ||
const forEach$1 = (arrOrObj, handler) => { | ||
for (let key in arrOrObj) { | ||
@@ -27,3 +156,3 @@ if (arrOrObj.hasOwnProperty(key)) { | ||
const keys = []; | ||
forEach(arrOrObj, key => keys.push(key)); | ||
forEach$1(arrOrObj, key => keys.push(key)); | ||
return keys; | ||
@@ -34,3 +163,3 @@ }; | ||
const result = [...arr1]; | ||
forEach(arr2, (key, val) => { | ||
forEach$1(arr2, (key, val) => { | ||
if (arr1.indexOf(val) === -1) { | ||
@@ -43,3 +172,3 @@ result.push(val); | ||
const IGNORE = Symbol.for('__Spy_IGNORE__'); | ||
const SPY_COMPARE_FAILED = 'Spy.COMPARE failed'; | ||
@@ -54,3 +183,3 @@ class SpyComparator { | ||
compare(arg) { | ||
if (!this._func(arg)) return 'custom comparison failed'; | ||
if (this._func(arg) === false) return [SPY_COMPARE_FAILED]; | ||
} | ||
@@ -62,2 +191,4 @@ | ||
const __different = type => ['different ' + type]; | ||
const __diff = (a, b, initial, useOwnEquals, alreadyComparedArray = []) => { | ||
@@ -68,7 +199,7 @@ if (a === IGNORE || b === IGNORE) return; | ||
if (a === b) return; | ||
if (a === undefined || b === undefined) return 'one was undefined'; | ||
if (a === null || b === null) return 'one was null'; | ||
if (a === undefined || b === undefined) return ['one was undefined']; | ||
if (a === null || b === null) return ['one was null']; | ||
const aClass = Object.prototype.toString.call(a); | ||
const bClass = Object.prototype.toString.call(b); | ||
if (aClass !== bClass) return `different object types: ${aClass} <-> ${bClass}`; | ||
if (aClass !== bClass) return __different(`object types: ${aClass} <-> ${bClass}`); | ||
@@ -81,12 +212,12 @@ switch (aClass) { | ||
return 'different regexp'; | ||
return __different('regexp'); | ||
case '[object String]': | ||
return 'different string'; | ||
return __different('string'); | ||
case '[object Function]': | ||
return 'different function'; | ||
return __different('function'); | ||
case '[object AsyncFunction]': | ||
return 'different async function'; | ||
return __different('async function'); | ||
@@ -98,6 +229,6 @@ case '[object Number]': | ||
return 'different number'; | ||
return __different('number'); | ||
case '[object BigInt]': | ||
return 'different BigInt'; | ||
return __different('BigInt'); | ||
@@ -109,9 +240,9 @@ case '[object Date]': | ||
return 'different date'; | ||
return __different('date'); | ||
case '[object Boolean]': | ||
return 'different bool'; | ||
return __different('bool'); | ||
case '[object Symbol]': | ||
return 'different symbols'; | ||
return __different('symbols'); | ||
@@ -123,7 +254,7 @@ case '[object Error]': | ||
return 'different error'; | ||
return __different('error'); | ||
default: | ||
if (a.constructor !== b.constructor) { | ||
return 'different constructor'; | ||
return __different('constructor'); | ||
} | ||
@@ -138,3 +269,3 @@ | ||
return 'own equals method failed <- ' + 'Maybe you want to disable the usage ' + 'of own equals implementation? ' + '[ Use: spy.configure({useOwnEquals: false}) ]'; | ||
return ['own equals method failed <- ' + 'Maybe you want to disable the usage ' + 'of own equals implementation? ' + '[ Use: spy.configure({useOwnEquals: false}) ]']; | ||
} | ||
@@ -152,6 +283,6 @@ | ||
const diffStr = __diff(a[key], b[key], false, useOwnEquals, compared); | ||
const diff = __diff(a[key], b[key], false, useOwnEquals, compared); | ||
if (diffStr !== undefined) { | ||
return `${initial ? `--> ${key}` : `${key}`} / ${diffStr}`; | ||
if (diff !== undefined) { | ||
return [key, ...diff]; | ||
} | ||
@@ -161,6 +292,30 @@ } | ||
const __serializeDifferentProp = (obj, diff) => { | ||
let toSerialize = obj; | ||
diff.slice(0, -1).forEach(key => { | ||
toSerialize = toSerialize[key]; | ||
}); | ||
return serialize(toSerialize); | ||
}; | ||
const __diffToStr = (diff, info = '') => { | ||
if (diff.length === 1) return diff[0]; | ||
return `--> ${diff[0]} / ` + diff.slice(1).join(' / ') + info; | ||
}; | ||
const differenceOf = (a, b, config = { | ||
useOwnEquals: true | ||
}) => { | ||
return __diff(a, b, true, config.useOwnEquals); | ||
const diff = __diff(a, b, true, config.useOwnEquals); | ||
if (!diff) return; | ||
const diffStr = __diffToStr(diff); | ||
if (diff.length < 2) return diffStr; | ||
const diffProp1 = __serializeDifferentProp(a, diff); | ||
const info = diff[diff.length - 1] === SPY_COMPARE_FAILED ? `called with: ${diffProp1}` : `${diffProp1} != ${__serializeDifferentProp(b, diff)}`; | ||
return `${diffStr} [${info}]`; | ||
}; | ||
@@ -193,3 +348,3 @@ | ||
SpyRegistry.prototype.restoreAll = function () { | ||
forEach(this.register, (ignored, entry) => { | ||
forEach$1(this.register, (ignored, entry) => { | ||
restoreAttributeForEntry(entry); | ||
@@ -238,130 +393,2 @@ }); | ||
const NOP = () => {}; | ||
const forEach$1 = (o, handler) => Object.keys(o).forEach(k => handler(o[k], k)); | ||
const __typePattern = /^\[object ([^\]]+)]$/; | ||
const __getTypeOfObject = o => (__typePattern.exec(Object.prototype.toString.call(o)) || [undefined])[1]; | ||
const __serializeProp = (key, value, custom, serialized) => typeof value === 'string' ? `${key}="${value}"` : `${key}={${__serialize(value, custom, serialized)}}`; | ||
const __serializeProps = (o, custom, serialized) => { | ||
const elemProps = o.props; | ||
const props = []; | ||
forEach$1(elemProps, (v, k) => { | ||
if (v !== undefined && k !== 'children') props.push(__serializeProp(k, v, custom, serialized)); | ||
}); | ||
if (o.key) props.push(__serializeProp('key', o.key, custom, serialized)); | ||
if (o.ref) props.push(__serializeProp('ref', o.ref, custom, serialized)); | ||
if (!props.length) return ''; | ||
return ' ' + props.join(' '); | ||
}; | ||
const __serializeChildren = (o, custom, serialized) => { | ||
const children = o.props.children; | ||
if (!children) return ''; | ||
if (typeof children === 'string') return children; | ||
return Array.isArray(children) ? children.map(v => __serialize(v, custom, serialized)).join('') : __serialize(children, custom, serialized); | ||
}; | ||
const __reactTypeNameReader = { | ||
String: type => type, | ||
Function: type => type.name, | ||
Symbol: type => Symbol.keyFor(type) === 'react.fragment' && 'Fragment' || undefined | ||
}; | ||
const getTypeName = type => (__reactTypeNameReader[__getTypeOfObject(type)] || NOP)(type); | ||
const __serializeReactElement = (o, custom, serialized) => { | ||
const type = getTypeName(o.type) || 'UNKNOWN'; | ||
const children = __serializeChildren(o, custom, serialized); | ||
return `<${type}${__serializeProps(o, custom, serialized)}${children ? `>${children}</${type}>` : ' />'}`; | ||
}; | ||
const __serializeReact = (o, custom, serialized) => { | ||
const symbolKey = Symbol.keyFor(o.$$typeof); | ||
switch (symbolKey) { | ||
case 'react.element': | ||
return __serializeReactElement(o, custom, serialized); | ||
default: | ||
return; | ||
} | ||
}; | ||
const __serializeIfReact = (o, custom, serialized) => { | ||
if (Object.prototype.toString.call(o.$$typeof) === '[object Symbol]') { | ||
const key = Symbol.keyFor(o.$$typeof); | ||
if (key && key.indexOf('react.') === 0) return __serializeReact(o, custom, serialized); | ||
} | ||
}; | ||
const __serializerByType = { | ||
BigInt: o => `${String(o)}n`, | ||
RegExp: o => `/${String(o)}/`, | ||
String: o => o.indexOf('"') === -1 && o.indexOf("'") !== -1 ? `"${o}"` : `'${o}'`, | ||
Function: o => o.name || 'Function', | ||
AsyncFunction: o => o.name || 'AsyncFunction', | ||
Date: o => `new Date(${Number(o)})`, | ||
Number: o => String(o), | ||
Boolean: o => String(o), | ||
Set: o => `new Set(${__serializeArray([...o.keys()], undefined, [])})`, | ||
Map: o => `new Map(${__serializeArray([...o.entries()], undefined, [])})`, | ||
Symbol: o => Symbol.keyFor(o) === undefined ? o.toString() : `Symbol.for('${Symbol.keyFor(o)}')`, | ||
Error: o => `new ${o.name}('${o.message}')` | ||
}; | ||
const __serializeArray = (o, custom, serialized) => { | ||
const results = []; | ||
for (let i = 0; i < o.length; i++) { | ||
results.push(__serialize(o[i], custom, serialized)); | ||
} | ||
return `[${results.join(', ')}]`; | ||
}; | ||
const __serializeOptArray = (o, custom, serialized) => { | ||
if (Array.isArray(o)) return __serializeArray(o, custom, serialized); | ||
}; | ||
const __serializeObject = (o, custom, serialized) => { | ||
const oKeys = Object.keys(o); | ||
oKeys.sort(); | ||
const results = []; | ||
for (let i = 0; i < oKeys.length; i++) { | ||
const key = oKeys[i]; | ||
results.push(`${key}: ${__serialize(o[key], custom, serialized)}`); | ||
} | ||
const objectType = o.constructor.name; | ||
const displayedType = objectType === 'Object' ? '' : objectType; | ||
return `${displayedType}{${results.join(', ')}}`; | ||
}; | ||
const Serialize = { | ||
custom: (o, custom) => custom && custom(o), | ||
undefOrNull: o => o === undefined && 'undefined' || o === null && 'null' || undefined, | ||
flat: o => (__serializerByType[__getTypeOfObject(o)] || NOP)(o), | ||
cyclomatic: (o, _, serialized) => serialized.indexOf(o) !== -1 && '>CYCLOMATIC<' || undefined, | ||
react: __serializeIfReact, | ||
array: __serializeOptArray | ||
}; | ||
const __serialize = (o, custom, serialized) => Serialize.custom(o, custom, serialized) || Serialize.undefOrNull(o, custom, serialized) || Serialize.flat(o, custom, serialized) || Serialize.cyclomatic(o, custom, serialized) || (nextSerialized => Serialize.react(o, custom, nextSerialized) || Serialize.array(o, custom, nextSerialized) || __serializeObject(o, custom, nextSerialized))([...serialized, o]); | ||
const _serialize = (o, custom) => __serialize(o, custom, []); | ||
const Serializer = { | ||
run: o => _serialize(o), | ||
create: custom => o => _serialize(o, custom) | ||
}; | ||
const serialize = Serializer.create(o => o === IGNORE && '>IGNORED<' || undefined); | ||
const uninitialized = method => () => { | ||
@@ -395,3 +422,3 @@ throw new Error(`Method '${method}' was not initialized on Mock.`); | ||
const mock = registerMock(obj); | ||
forEach(methods, (_, method) => { | ||
forEach$1(methods, (_, method) => { | ||
mock[method] = uninitialized(method); | ||
@@ -409,3 +436,3 @@ }); | ||
}, spyOn) => { | ||
forEach(mock, method => { | ||
forEach$1(mock, method => { | ||
try { | ||
@@ -420,3 +447,3 @@ mock[method] = spyOn(mocked, method); | ||
const initMockScope = (scoping, spyOn) => { | ||
forEach(_mocks[scoping], (_, mock) => initMock(mock, spyOn)); | ||
forEach$1(_mocks[scoping], (_, mock) => initMock(mock, spyOn)); | ||
}; | ||
@@ -429,2 +456,6 @@ | ||
const _testSuite = { | ||
isJest: !!jest, | ||
isCJS: !!require | ||
}; | ||
const runner = {}; | ||
@@ -448,3 +479,3 @@ const oldDescribe = describe; | ||
const configureTestSuite = other => { | ||
const configure = other => { | ||
if (other.afterEach) runner.afterEach = other.afterEach; | ||
@@ -454,2 +485,31 @@ if (other.beforeEach) runner.beforeEach = other.beforeEach; | ||
const addSnapshotSerializer = serializer => { | ||
_testSuite.isJest && expect && expect.addSnapshotSerializer && expect.addSnapshotSerializer(serializer); | ||
}; | ||
const __caller = stackNum => { | ||
const traceFn = Error.prepareStackTrace; | ||
Error.prepareStackTrace = (err, stack) => stack; | ||
const stack = new Error().stack; | ||
Error.prepareStackTrace = traceFn; | ||
return stack[stackNum].getFileName(); | ||
}; | ||
const __callerBasedir = stackNum => require('path').dirname(__caller(stackNum)); | ||
const STACK_NUM_CREATE_MOCK = 4; | ||
const createMock$1 = (SPY, moduleName, names) => { | ||
if (!_testSuite.isCJS) throw new Error('Spy.moduleMock works only if your test runner executes with CommonJS'); | ||
return SPY.mock(require(require('path').join(__callerBasedir(STACK_NUM_CREATE_MOCK), moduleName)), ...names); | ||
}; | ||
const TestSuite = { | ||
addSnapshotSerializer, | ||
createMock: createMock$1, | ||
configure | ||
}; | ||
const registry = new SpyRegistry(); | ||
@@ -466,3 +526,3 @@ let __LOCK__ = true; | ||
}; | ||
expect && expect.addSnapshotSerializer && expect.addSnapshotSerializer({ | ||
TestSuite.addSnapshotSerializer({ | ||
test: v => v && v[Symbols.isSpy], | ||
@@ -711,3 +771,3 @@ print: spy => spy[Symbols.snap] | ||
}; | ||
forEach(SpyFunctions, (key, value) => { | ||
forEach$1(SpyFunctions, (key, value) => { | ||
spy[key] = value; | ||
@@ -724,3 +784,3 @@ }); | ||
configureTestSuite({ | ||
TestSuite.configure({ | ||
afterEach: config.afterEach, | ||
@@ -756,2 +816,6 @@ beforeEach: config.beforeEach | ||
static mockModule(moduleName, ...methodNames) { | ||
return TestSuite.createMock(Spy, moduleName, methodNames); | ||
} | ||
static initMocks(scope) { | ||
@@ -782,4 +846,4 @@ initMocks(Spy.on, scope); | ||
}; | ||
configureTestSuite(defaultHooks); | ||
TestSuite.configure(defaultHooks); | ||
export { Spy }; |
{ | ||
"name": "spy4js", | ||
"version": "2.7.0", | ||
"version": "2.8.0", | ||
"description": "Smart, compact and powerful spy test framework", | ||
@@ -5,0 +5,0 @@ "main": "dist/cjs/spy.js", |
@@ -48,3 +48,3 @@ [![GitHub license][license-image]][license-url] | ||
```js | ||
import {Spy} from 'spy4js'; | ||
import { Spy } from 'spy4js'; | ||
@@ -65,2 +65,5 @@ // initialize directly | ||
const someObject2$Mock = Spy.mock(someObject2, 'toJSON', 'toString', 'getDate'); | ||
// mock exported functions from other modules | ||
const myModuleMocks = Spy.mockModule('./my-module', 'useMe'); | ||
``` | ||
@@ -249,3 +252,3 @@ | ||
``` | ||
Spy.mock(object:Object, ...methodNames:Array<string>) => Object (Mock) | ||
Spy.mock(object:Object, ...methodNames: string[]) => Object (Mock) | ||
``` | ||
@@ -256,2 +259,8 @@ Creating an object that references spies for all given methodNames. | ||
### mockModule (static) | ||
``` | ||
Spy.mockModule(moduleName: string, ...methodNames: string[]) => Object (Mock) | ||
``` | ||
Same as [mock](#mock) but only necessary if you want to mock exported functions. | ||
### initMocks (static) | ||
@@ -262,4 +271,4 @@ ``` | ||
Does initialize all global and scope-related mocks by applying spies. Mocks can be | ||
created with [mock](#mock). This function has not to be called manually, if you rely on | ||
the default test suite hooks. | ||
created with [mock](#mock) or [mockModule](#mockModule). This function has not to | ||
be called manually, if you rely on the default test suite hooks. | ||
@@ -266,0 +275,0 @@ ### restoreAll (static) |
@@ -47,2 +47,3 @@ | ||
mock<T, K extends keyof T>(obj: T, ...methodNames: K[]): { [P in K]: SpyInstance }; | ||
mockModule<K extends string>(moduleName: string, ...methodNames: K[]): { [P in K]: SpyInstance }; | ||
initMocks(scope?: string): undefined; | ||
@@ -49,0 +50,0 @@ restoreAll(): undefined; |
@@ -19,3 +19,3 @@ /** | ||
import { createMock, initMocks } from './mock'; | ||
import { configureTestSuite } from './test-suite'; | ||
import { TestSuite } from './test-suite'; | ||
@@ -53,8 +53,6 @@ /** | ||
*/ | ||
(expect: any) && | ||
(expect: any).addSnapshotSerializer && | ||
(expect: any).addSnapshotSerializer({ | ||
test: v => v && v[Symbols.isSpy], | ||
print: spy => spy[Symbols.snap], | ||
}); | ||
TestSuite.addSnapshotSerializer({ | ||
test: v => v && v[Symbols.isSpy], | ||
print: spy => spy[Symbols.snap], | ||
}); | ||
@@ -712,3 +710,3 @@ /** | ||
} | ||
configureTestSuite({ | ||
TestSuite.configure({ | ||
afterEach: config.afterEach, | ||
@@ -813,2 +811,32 @@ beforeEach: config.beforeEach, | ||
/** | ||
* This static method enables you to create mocks on module scope. | ||
* As long as jest will support this behaviour, the Spy will too. If | ||
* you are calling this function on other test runners you will | ||
* encounter an exception. You should favor to use "Spy.mock" but there | ||
* might be reasons that this will not work. E.g. if you want to mock | ||
* directly exported functions. | ||
* | ||
* For example: | ||
* | ||
* const Mock$MyModule = Spy.mockModule('./my-module', 'useMe'); | ||
* | ||
* Now you could do: | ||
* Mock$MyModule.useMe.returns(['foo', 'bar']); | ||
* | ||
* @param {string} moduleName -> Everything that's expected by "jest.mock" | ||
* as first parameter. Relative and absolute | ||
* module paths. | ||
* @param {string[]} methodNames -> Iterative provided attribute | ||
* names that will be mocked. | ||
* | ||
* @return {Object} Mock. | ||
*/ | ||
static mockModule<K: string>( | ||
moduleName: string, | ||
...methodNames: K[] | ||
): { [name: K]: SpyInstance } { | ||
return TestSuite.createMock(Spy, moduleName, (methodNames: any[])); | ||
} | ||
/** | ||
* This static method initializes all created | ||
@@ -869,4 +897,4 @@ * mocks (see Spy.mock). This is necessary, because | ||
configureTestSuite(defaultHooks); | ||
TestSuite.configure(defaultHooks); | ||
export { Spy }; |
@@ -11,2 +11,7 @@ /** | ||
// we have to cheat here flow since it might be the case that "jest" was not | ||
// configured correctly or the jest test runner might not even be present | ||
// $FlowFixMe | ||
export const _testSuite = { isJest: !!(jest: any), isCJS: !!(require: any) }; | ||
type Scope = string; | ||
@@ -34,5 +39,52 @@ type Runner = { afterEach?: Scope => void, beforeEach?: Scope => void }; | ||
export const configureTestSuite = (other: Runner): void => { | ||
const configure = (other: Runner): void => { | ||
if (other.afterEach) runner.afterEach = other.afterEach; | ||
if (other.beforeEach) runner.beforeEach = other.beforeEach; | ||
}; | ||
const addSnapshotSerializer = (serializer: any) => { | ||
_testSuite.isJest && | ||
(expect: any) && | ||
(expect: any).addSnapshotSerializer && | ||
(expect: any).addSnapshotSerializer(serializer); | ||
}; | ||
const __caller = (stackNum: number) => { | ||
const traceFn = Error.prepareStackTrace; | ||
Error.prepareStackTrace = (err, stack) => stack; | ||
const stack = new Error().stack; | ||
Error.prepareStackTrace = traceFn; | ||
// it highly depends from were the functionality will be called | ||
return (stack[stackNum]: any).getFileName(); | ||
}; | ||
const __callerBasedir = (stackNum: number) => | ||
require('path').dirname(__caller(stackNum)); | ||
// 1. Spy.createMock in some test | ||
// 2. _createMock from test-suite.js | ||
// 3. __callerBasedir from test-suite.js | ||
// 4. __caller from test-suite.js | ||
const STACK_NUM_CREATE_MOCK = 4; | ||
const createMock = ( | ||
SPY: Function, | ||
moduleName: string, | ||
names: string[] | ||
): Object => { | ||
if (!_testSuite.isCJS) | ||
throw new Error( | ||
'Spy.moduleMock works only if your test runner executes with CommonJS' | ||
); | ||
// now we are free to use "require('path')" to calculate the correct | ||
// module path for the mocking. | ||
return SPY.mock( | ||
require(require('path').join( | ||
__callerBasedir(STACK_NUM_CREATE_MOCK), | ||
moduleName | ||
)), | ||
...names | ||
); | ||
}; | ||
export const TestSuite = { addSnapshotSerializer, createMock, configure }; |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
118213
2716
544
3