diff options
Diffstat (limited to 'preact/test/polyfills.js')
-rw-r--r-- | preact/test/polyfills.js | 260 |
1 files changed, 260 insertions, 0 deletions
diff --git a/preact/test/polyfills.js b/preact/test/polyfills.js new file mode 100644 index 0000000..e2f542b --- /dev/null +++ b/preact/test/polyfills.js @@ -0,0 +1,260 @@ +// ES2015 APIs used by developer tools integration +import 'core-js/es6/map'; +import 'core-js/es6/promise'; +import 'core-js/fn/array/fill'; +import 'core-js/fn/array/from'; +import 'core-js/fn/array/find'; +import 'core-js/fn/array/includes'; +import 'core-js/fn/string/includes'; +import 'core-js/fn/object/assign'; +import 'core-js/fn/string/starts-with'; +import 'core-js/fn/string/code-point-at'; +import 'core-js/fn/string/from-code-point'; +import 'core-js/fn/string/repeat'; +import * as kl from 'kolorist'; + +// Something that's loaded before this file polyfills Symbol object. +// We need to verify that it works in IE without that. +if (/Trident/.test(window.navigator.userAgent)) { + window.Symbol = undefined; +} + +// Fix Function#name on browsers that do not support it (IE). +// Taken from: https://stackoverflow.com/a/17056530/755391 +if (!function f() {}.name) { + Object.defineProperty(Function.prototype, 'name', { + get() { + let name = (this.toString().match(/^function\s*([^\s(]+)/) || [])[1]; + // For better performance only parse once, and then cache the + // result through a new accessor for repeated access. + Object.defineProperty(this, 'name', { value: name }); + return name; + } + }); +} + +/* global chai */ +chai.use((chai, util) => { + const Assertion = chai.Assertion; + + Assertion.addMethod('equalNode', function(expectedNode, message) { + const obj = this._obj; + message = message || 'equalNode'; + + if (expectedNode == null) { + this.assert( + obj == null, + `${message}: expected node to "== null" but got #{act} instead.`, + `${message}: expected node to not "!= null".`, + expectedNode, + obj + ); + } else { + new Assertion(obj).to.be.instanceof(Node, message); + this.assert( + obj.tagName === expectedNode.tagName, + `${message}: expected node to have tagName #{exp} but got #{act} instead.`, + `${message}: expected node to not have tagName #{act}.`, + expectedNode.tagName, + obj.tagName + ); + this.assert( + obj === expectedNode, + `${message}: expected #{this} to be #{exp} but got #{act}`, + `${message}: expected # {this} not to be #{exp}`, + expectedNode, + obj + ); + } + }); +}); + +// +// The following code overwrites karma's internal logging feature to +// support a much prettier and humand readable represantation of +// console logs in our terminal. This includes indentation, coloring +// and support for Map and Set objects. +// +function patchConsole(method) { + const original = window.console[method]; + window.console[method] = (...args) => { + // @ts-ignore + // eslint-disable-next-line no-undef + __karma__.log(method, ['__LOG_CUSTOM:' + serializeConsoleArgs(args)]); + original.apply(window.console, args); + }; +} + +patchConsole('log'); +patchConsole('warn'); +patchConsole('error'); +patchConsole('info'); + +/** + * @param {any[]} args + * @returns {[string]} + */ +function serializeConsoleArgs(args) { + const flat = args.map(arg => serialize(arg, 'flat', 0, new Set())); + // We don't have access to the users terminal width, so we'll try to + // format everything into one line if possible and assume a terminal + // width of 80 chars + if (kl.stripColors(flat.join(', ')).length <= 80) { + return [flat.join(', ')]; + } + + const serialized = args.map(arg => serialize(arg, 'default', 0, new Set())); + return ['\n' + serialized.join(',\n') + '\n']; +} + +/** + * @param {number} n + * @returns {string} + */ +function applyIndent(n) { + if (n <= 0) return ''; + return ' '.repeat(n); +} + +/** + * @param {any} value + * @param {"flat" | "default"} mode + * @param {number} indent + * @param {Set<any>} seen + * @returns {string} + */ +function serialize(value, mode, indent, seen) { + if (seen.has(value)) { + return kl.cyan('[Circular]'); + } + + if (value === null) { + return kl.bold('null'); + } else if (Array.isArray(value)) { + seen.add(value); + const values = value.map(v => serialize(v, mode, indent + 1, seen)); + if (mode === 'flat') { + return `[ ${values.join(', ')} ]`; + } + + const space = applyIndent(indent); + const pretty = values.map(v => applyIndent(indent + 1) + v).join(',\n'); + return `[\n${pretty}\n${space}]`; + } else if (value instanceof Set) { + const values = []; + value.forEach(v => { + values.push(serialize(v, mode, indent, seen)); + }); + + if (mode === 'flat') { + return `Set(${value.size}) { ${values.join(', ')} }`; + } + + const pretty = values.map(v => applyIndent(indent + 1) + v).join(',\n'); + return `Set(${value.size}) {\n${pretty}\n${applyIndent(indent)}}`; + } else if (value instanceof Map) { + const values = []; + value.forEach((v, k) => { + values.push([ + serialize(v, 'flat', indent, seen), + serialize(k, 'flat', indent, seen) + ]); + }); + + if (mode === 'flat') { + const pretty = values.map(v => `${v[0]} => ${v[1]}`).join(', '); + return `Map(${value.size}) { ${pretty} }`; + } + + const pretty = values + .map(v => { + return applyIndent(indent + 1) + `${v[0]} => ${v[1]}`; + }) + .join(', '); + return `Map(${value.size}) {\n${pretty}\n${applyIndent(indent)}}`; + } + + switch (typeof value) { + case 'undefined': + return kl.dim('undefined'); + + case 'bigint': + case 'number': + case 'boolean': + return kl.yellow(String(value)); + case 'string': { + // By default node's built in logging doesn't wrap top level + // strings with quotes + if (indent === 0) { + return String(value); + } + const quote = /[^\\]"/.test(value) ? '"' : "'"; + return kl.green(String(quote + value + quote)); + } + case 'symbol': + return kl.green(value.toString()); + case 'function': + return kl.cyan(`[Function: ${value.name || 'anonymous'}]`); + } + + if (value instanceof Element) { + return value.outerHTML; + } + + seen.add(value); + + const props = Object.keys(value).map(key => { + const v = serialize(value[key], mode, indent + 1, seen); + return `${key}: ${v}`; + }); + + if (props.length === 0) { + return '{}'; + } else if (mode === 'flat') { + const pretty = props.join(', '); + return `{ ${pretty} }`; + } + + const pretty = props.map(p => applyIndent(indent + 1) + p).join(',\n'); + return `{\n${pretty}\n${applyIndent(indent)}}`; +} + +// Use these lines to test pretty formatting: +// +// const obj = { foo: 123 }; +// obj.obj = obj; +// console.log(obj); +// console.log([1, 2]); +// console.log(new Set([1, 2])); +// console.log(new Map([[1, 2]])); +// console.log({ +// foo: { bar: 123, bob: { a: 1 } } +// }); +// console.log( +// 'hey', +// null, +// undefined, +// [1, 2, ['a']], +// () => {}, +// { +// type: 'div', +// props: {}, +// key: undefined, +// ref: undefined, +// __k: null, +// __: null, +// __b: 0, +// __e: null, +// __d: undefined, +// __c: null, +// __h: null, +// constructor: undefined, +// __v: 1 +// }, +// { +// foo: { bar: 123, bob: { a: 1, b: new Set([1, 2]), c: new Map([[1, 2]]) } } +// }, +// new Set([1, 2]), +// new Map([[1, 2]]) +// ); +// console.log(document.createElement('div')); |