summaryrefslogtreecommitdiff
path: root/preact/test/polyfills.js
diff options
context:
space:
mode:
Diffstat (limited to 'preact/test/polyfills.js')
-rw-r--r--preact/test/polyfills.js260
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'));