@webqit/util
Version:
Utility functions used accross multiple JS libraries.
166 lines (133 loc) • 5.02 kB
JavaScript
/**
* @imports
*/
import { expect } from 'chai';
import * as Js from '../js/index.js';
describe(`JS Processing`, function() {
describe(`mixin()`, function() {
var methodACalled = 0, methodBCalled = 0, methodCCalled = 0;
class A {
methodA() {
methodACalled ++;
}
}
class B {
methodB() {
methodBCalled ++;
}
}
class Bx extends B {}
class By extends B {}
class C extends Js._mixin(A, B) {
methodC(callSuper = false) {
if (callSuper) {
super.methodA();
super.methodB();
}
methodCCalled ++;
}
}
const _C = new C;
it(`Should ensure own and inheritted methods are called.`, function() {
_C.methodA();
_C.methodB();
_C.methodC();
expect(methodACalled).to.be.a('number').that.equals(1);
expect(methodBCalled).to.be.a('number').that.equals(1);
expect(methodCCalled).to.be.a('number').that.equals(1);
});
it(`Should ensure all ancestor methods are called on super[methodName]().`, function() {
_C.methodC(true);
expect(methodACalled).to.be.a('number').that.equals(2);
expect(methodBCalled).to.be.a('number').that.equals(2);
expect(methodCCalled).to.be.a('number').that.equals(2);
});
it(`Should ensure the native instanceof syntax works.`, function() {
expect(_C instanceof A).to.be.true;
expect(_C instanceof B).to.be.true;
expect(_C instanceof C).to.be.true;
expect(_C instanceof By).to.be.false;
expect((new Bx) instanceof By).to.be.false;
});
});
describe(`internals()`, function() {
it(`Should ensure zero or more namespaces work.`, function() {
var obj = {};
expect(Js._internals(obj)).to.be.instanceOf(Map);
expect(Js._internals(obj, 'a', 'b')).to.be.instanceOf(Map);
expect(Js._internals(obj, 'a', 'c')).to.be.instanceOf(Map);
expect(Js._internals(obj, 'a').size).to.eq(2);
expect(Js._internals(obj).size).to.eq(1);
});
});
/**
Object.prototype.toString.call({});
const obj = {
[Symbol.toPrimitive](hint) {
switch (hint) {
case 'number':
return 123;
case 'string':
return 'str';
case 'default':
return 'default';
default:
throw new Error();
}
}
};
console.log(2 * obj); // 246
console.log(3 + obj); // '3default'
console.log(obj == 'default'); // true
console.log(String(obj)); // 'str'
class Bar {
get [Symbol.toStringTag]() {
return 'Bar';
}
}
console.log(new Bar().toString()); // [object Bar]
function myIndexOf(arr, elem) {
return arr.findIndex(x => Object.is(x, elem));
}
//> Object.is(NaN, NaN)
true
//> Object.is(-0, +0)
false
//If you want the clone to have the same prototype as the original, you can use Object.getPrototypeOf() and Object.create():
function clone(orig) {
const origProto = Object.getPrototypeOf(orig);
return Object.assign(Object.create(origProto), orig);
}
const DEFAULTS = {
logLevel: 0,
outputFormat: 'html'
};
function processContent(options) {
options = Object.assign({}, DEFAULTS, options); // (A)
//···
}
//In line A, we created a fresh object, copied the defaults into it and then copied options into it, overriding the defaults. Object.assign() returns the result of these operations, which we assign to options.
//This is how you would copy all own properties (not just enumerable ones), while correctly transferring getters and setters and without invoking setters on the target:
function copyAllOwnProperties(target, ...sources) {
for (const source of sources) {
for (const key of Reflect.ownKeys(source)) {
const desc = Object.getOwnPropertyDescriptor(source, key);
Object.defineProperty(target, key, desc);
}
}
return target;
}
const Storage = Sup => class extends Sup {
save(database) { }
};
const Validation = Sup => class extends Sup {
validate(schema) { }
};
//Here, we profit from the operand of the extends clause not being a fixed identifier, but an arbitrary expression. With these mixins, Employee is created like this:
class Employee extends Storage(Validation(Person)) { }
get notifier() {
delete this.notifier;
return this.notifier = document.getElementById('bookmarked-notification-anchor');
},
*/
});