Files
leaflet/spec/suites/core/ClassSpec.js
2022-10-20 21:18:26 +02:00

274 lines
6.1 KiB
JavaScript

/* eslint no-new: 0 */
describe("Class", () => {
describe("#extend", () => {
let Klass,
props,
constructor,
method;
beforeEach(() => {
constructor = sinon.spy();
method = sinon.spy();
props = {
statics: {bla: 1},
includes: {mixin: true},
initialize: constructor,
foo: 5,
bar: method
};
Klass = L.Class.extend(props);
});
it("creates a class with the given constructor & properties", () => {
const a = new Klass();
expect(constructor.called).to.be.ok();
expect(a.foo).to.eql(5);
a.bar();
expect(method.called).to.be.ok();
});
it("inherits parent classes' constructor & properties", () => {
const Klass2 = Klass.extend({baz: 2});
const b = new Klass2();
expect(b instanceof Klass).to.be.ok();
expect(b instanceof Klass2).to.be.ok();
expect(constructor.called).to.be.ok();
expect(b.baz).to.eql(2);
b.bar();
expect(method.called).to.be.ok();
});
it("does not modify source props object", () => {
expect(props).to.eql({
statics: {bla: 1},
includes: {mixin: true},
initialize: constructor,
foo: 5,
bar: method
});
});
it("supports static properties", () => {
expect(Klass.bla).to.eql(1);
});
it("does not merge 'statics' property itself", () => {
expect('statics' in Klass.prototype).to.not.be.ok();
});
it("inherits parent static properties", () => {
const Klass2 = Klass.extend({});
expect(Klass2.bla).to.eql(1);
});
it("overrides parent static properties", () => {
const Klass2 = Klass.extend({statics: {bla: 2}});
expect(Klass2.bla).to.eql(2);
});
it("includes the given mixin", () => {
const a = new Klass();
expect(a.mixin).to.be.ok();
});
it("does not merge 'includes' property itself", () => {
expect('includes' in Klass.prototype).to.not.be.ok();
});
it("includes multiple mixins", () => {
const Klass2 = L.Class.extend({
includes: [{mixin: true}, {mixin2: true}]
});
const a = new Klass2();
expect(a.mixin).to.be.ok();
expect(a.mixin2).to.be.ok();
});
it("grants the ability to include the given mixin", () => {
Klass.include({mixin2: true});
const a = new Klass();
expect(a.mixin2).to.be.ok();
});
it("merges options instead of replacing them", () => {
const KlassWithOptions1 = L.Class.extend({
options: {
foo1: 1,
foo2: 2
}
});
const KlassWithOptions2 = KlassWithOptions1.extend({
options: {
foo2: 3,
foo3: 4
}
});
const a = new KlassWithOptions2();
expect(a.options.foo1).to.eql(1);
expect(a.options.foo2).to.eql(3);
expect(a.options.foo3).to.eql(4);
});
it("gives new classes a distinct options object", () => {
const K1 = L.Class.extend({options: {}});
const K2 = K1.extend({});
expect(K2.prototype.options).not.to.equal(K1.prototype.options);
});
it("inherits options prototypally", () => {
const K1 = L.Class.extend({options: {}});
const K2 = K1.extend({options: {}});
K1.prototype.options.foo = 'bar';
expect(K2.prototype.options.foo).to.eql('bar');
});
it("does not reuse original props.options", () => {
const props = {options: {}};
const K = L.Class.extend(props);
expect(K.prototype.options).not.to.be(props.options);
});
it("does not replace source props.options object", () => {
const K1 = L.Class.extend({options: {}});
const opts = {};
const props = {options: opts};
K1.extend(props);
expect(props.options).to.be(opts);
});
it("prevents change of prototype options", () => {
const Klass = L.Class.extend({options: {}});
const instance = new Klass();
expect(Klass.prototype.options).to.not.be(instance.options);
});
it("adds constructor hooks correctly", () => {
const spy1 = sinon.spy();
Klass.addInitHook(spy1);
Klass.addInitHook('bar', 1, 2, 3);
new Klass();
expect(spy1.called).to.be.ok();
expect(method.calledWith(1, 2, 3));
});
it("inherits constructor hooks", () => {
const spy1 = sinon.spy(),
spy2 = sinon.spy();
const Klass2 = Klass.extend({});
Klass.addInitHook(spy1);
Klass2.addInitHook(spy2);
new Klass2();
expect(spy1.called).to.be.ok();
expect(spy2.called).to.be.ok();
});
it("does not call child constructor hooks", () => {
const spy1 = sinon.spy(),
spy2 = sinon.spy();
const Klass2 = Klass.extend({});
Klass.addInitHook(spy1);
Klass2.addInitHook(spy2);
new Klass();
expect(spy1.called).to.be.ok();
expect(spy2.called).to.eql(false);
});
it("calls parent constructor hooks when child has none", () => {
const spy1 = sinon.spy();
Klass.addInitHook(spy1);
const Klass2 = Klass.extend({});
new Klass2();
expect(spy1.called).to.be.ok();
});
});
describe("#include", () => {
let Klass;
beforeEach(() => {
Klass = L.Class.extend({});
});
it("returns the class with the extra methods", () => {
const q = sinon.spy();
const Qlass = Klass.include({quux: q});
const a = new Klass();
const b = new Qlass();
a.quux();
expect(q.called).to.be.ok();
b.quux();
expect(q.called).to.be.ok();
});
it("keeps parent options", () => { // #6070
const Quux = L.Class.extend({
options: {foo: 'Foo!'}
});
Quux.include({
options: {bar: 'Bar!'}
});
const q = new Quux();
expect(q.options).to.have.property('foo');
expect(q.options).to.have.property('bar');
});
it("does not reuse original props.options", () => {
const props = {options: {}};
const K = Klass.include(props);
expect(K.prototype.options).not.to.be(props.options);
});
it("does not replace source props.options object", () => {
const K1 = Klass.include({options: {}});
const opts = {};
const props = {options: opts};
K1.extend(props);
expect(props.options).to.be(opts);
});
});
// TODO Class.mergeOptions
});