@e22m4u/js-service
Version:
Реализация сервис-локатора для JavaScript
1,105 lines (1,024 loc) • 43.2 kB
JavaScript
import {expect} from 'chai';
import {Service} from './service.js';
import {format} from '@e22m4u/js-format';
import {createSpy} from '@e22m4u/js-spy';
import {ServiceContainer} from './service-container.js';
import {SERVICE_CONTAINER_CLASS_NAME} from './service-container.js';
describe('ServiceContainer', function () {
it('exposes static property "kinds"', function () {
expect(ServiceContainer.kinds).to.be.eql([SERVICE_CONTAINER_CLASS_NAME]);
const MyContainer = class extends ServiceContainer {};
expect(MyContainer.kinds).to.be.eql([SERVICE_CONTAINER_CLASS_NAME]);
});
describe('constructor', function () {
it('does not require any arguments', function () {
const res = new ServiceContainer();
expect(res).to.be.instanceof(ServiceContainer);
});
it('sets the first argument as the parent container', function () {
const parent = new ServiceContainer();
const container = new ServiceContainer(parent);
const res = container['_parent'];
expect(res).to.be.eq(parent);
});
it('requires the first argument to be an instance of ServiceContainer', function () {
const throwable = v => () => new ServiceContainer(v);
const error = v =>
format(
'The provided parameter "parent" of ServicesContainer.constructor ' +
'must be an instance ServiceContainer, but %s given.',
v,
);
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable('')).to.throw(error('""'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(0)).to.throw(error('0'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
throwable(undefined)();
throwable(null)();
throwable(new ServiceContainer())();
});
});
describe('getParent', function () {
it('throws an error if no parent container', function () {
const container = new ServiceContainer();
const throwable = () => container.getParent();
expect(throwable).to.throw('The service container has no parent.');
});
it('returns parent container', function () {
const parent = new ServiceContainer();
const container = new ServiceContainer(parent);
const res = container.getParent();
expect(res).to.be.eq(parent);
});
});
describe('hasParent', function () {
it('returns true if a parent container exists and false otherwise', function () {
const container1 = new ServiceContainer();
const parent = new ServiceContainer();
const container2 = new ServiceContainer(parent);
expect(container1.hasParent()).to.be.false;
expect(container2.hasParent()).to.be.true;
});
});
describe('get', function () {
it('throws an error if no constructor given', function () {
const container = new ServiceContainer();
const throwable = v => () => container.get(v);
const error = v =>
format(
'The first argument of ServicesContainer.get must be ' +
'a class constructor, but %s given.',
v,
);
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(undefined)).to.throw(error('undefined'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
});
describe('Service', function () {
it('passes itself and given arguments to the given constructor', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
const service = container.get(MyService, 'foo', 'bar');
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('instantiates from an existing factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('overrides an existing factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService, 'baz', 'qux');
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
it('caches a new instance', function () {
let executed = 0;
class MyService extends Service {
constructor(container) {
super(container);
++executed;
}
}
const container = new ServiceContainer();
const service1 = container.get(MyService);
const service2 = container.get(MyService);
expect(service1).to.be.instanceof(MyService);
expect(service2).to.be.instanceof(MyService);
expect(service1).to.be.eq(service2);
expect(executed).to.be.eq(1);
});
it('overrides the cached instance', function () {
let executed = 0;
const givenArgs = [];
class MyService extends Service {
constructor(container, arg) {
super(container);
++executed;
givenArgs.push(arg);
}
}
const container = new ServiceContainer();
const service1 = container.get(MyService, 'foo');
const service2 = container.get(MyService);
const service3 = container.get(MyService, 'bar');
const service4 = container.get(MyService);
expect(service1).to.be.instanceof(MyService);
expect(service2).to.be.instanceof(MyService);
expect(service3).to.be.instanceof(MyService);
expect(service4).to.be.instanceof(MyService);
expect(service1).to.be.eq(service2);
expect(service2).to.be.not.eq(service3);
expect(service3).to.be.eq(service4);
expect(executed).to.be.eq(2);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
describe('class inheritance', function () {
it('should create an instance of the child class when the parent class is requested', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ChildService);
const childService = container.get(ParentService);
expect(childService).to.be.instanceof(ChildService);
});
it('should return the existing instance of the child class when the parent class is requested', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
const childService1 = container.get(ChildService);
expect(childService1).to.be.instanceof(ChildService);
const childService2 = container.get(ParentService);
expect(childService1).to.be.instanceof(ChildService);
expect(childService2).to.be.eq(childService1);
});
it('should create an instance of the requested child class, even if its parent class is registered', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
const childService = container.get(ChildService);
expect(childService).to.be.instanceof(ChildService);
});
it('should return an instance of the requested child class, not the existing instance of its parent class', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
const parentService = container.get(ParentService);
expect(parentService).to.be.instanceof(ParentService);
const childService = container.get(ChildService);
expect(childService).to.be.instanceof(ChildService);
expect(childService).to.be.not.eq(parentService);
});
});
describe('in case of a parent container', function () {
it('instantiates in the parent container', function () {
class MyService extends Service {}
const parent = new ServiceContainer();
parent.add(MyService);
const child = new ServiceContainer(parent);
const res = child.get(MyService);
expect(res).to.be.instanceof(MyService);
});
it('returns an instance from the parent container', function () {
class MyService extends Service {}
const parent = new ServiceContainer();
const service = parent.get(MyService);
const child = new ServiceContainer(parent);
const res = child.get(MyService);
expect(res).to.be.eq(service);
});
it('does not adds the given constructor to the parent container', function () {
class MyService extends Service {}
const parent = new ServiceContainer();
const child = new ServiceContainer(parent);
const service = child.get(MyService);
expect(service).to.be.instanceof(MyService);
const res1 = child.has(MyService);
const res2 = parent.has(MyService);
expect(res1).to.be.true;
expect(res2).to.be.false;
});
describe('class inheritance', function () {
it('should resolve a parent class to an instance of its child class registered in a parent container', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const parentContainer = new ServiceContainer();
const container = new ServiceContainer(parentContainer);
parentContainer.add(ChildService);
const childService = container.get(ParentService);
expect(childService).to.be.instanceof(ChildService);
});
it('should return the existing instance of the child class from a parent container when the parent class is requested', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const parentContainer = new ServiceContainer();
const container = new ServiceContainer(parentContainer);
const childService1 = parentContainer.get(ChildService);
expect(childService1).to.be.instanceof(ChildService);
const childService2 = container.get(ParentService);
expect(childService1).to.be.instanceof(ChildService);
expect(childService2).to.be.eq(childService1);
});
it('should create an instance of the requested child class, even if its parent class is registered in a parent container', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const parentContainer = new ServiceContainer();
const container = new ServiceContainer(parentContainer);
parentContainer.add(ParentService);
const childService = container.get(ChildService);
expect(childService).to.be.instanceof(ChildService);
});
it('should create an instance of the requested child class, even if a parent container has an instance of its parent class', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const parentContainer = new ServiceContainer();
const container = new ServiceContainer(parentContainer);
const parentService = parentContainer.get(ParentService);
expect(parentService).to.be.instanceof(ParentService);
const childService = container.get(ChildService);
expect(childService).to.be.instanceof(ChildService);
expect(childService).to.be.not.eq(parentService);
});
});
});
});
describe('non-Service', function () {
it('passes given arguments to the given constructor', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
const service = container.get(MyService, 'foo', 'bar');
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('instantiates from an existing factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('overrides an existing factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService, 'baz', 'qux');
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
it('caches a new instance', function () {
let executed = 0;
class MyService {
constructor() {
++executed;
}
}
const container = new ServiceContainer();
const service1 = container.get(MyService);
const service2 = container.get(MyService);
expect(service1).to.be.instanceof(MyService);
expect(service2).to.be.instanceof(MyService);
expect(service1).to.be.eq(service2);
expect(executed).to.be.eq(1);
});
it('overrides the cached instance', function () {
let executed = 0;
const givenArgs = [];
class MyService {
constructor(arg) {
++executed;
givenArgs.push(arg);
}
}
const container = new ServiceContainer();
const service1 = container.get(MyService, 'foo');
const service2 = container.get(MyService);
const service3 = container.get(MyService, 'bar');
const service4 = container.get(MyService);
expect(service1).to.be.instanceof(MyService);
expect(service2).to.be.instanceof(MyService);
expect(service3).to.be.instanceof(MyService);
expect(service4).to.be.instanceof(MyService);
expect(service1).to.be.eq(service2);
expect(service2).to.be.not.eq(service3);
expect(service3).to.be.eq(service4);
expect(executed).to.be.eq(2);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
describe('class inheritance', function () {
it('should create an instance of the child class when the parent class is requested', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ChildService);
const childService = container.get(ParentService);
expect(childService).to.be.instanceof(ChildService);
});
it('should return the existing instance of the child class when the parent class is requested', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
const childService1 = container.get(ChildService);
expect(childService1).to.be.instanceof(ChildService);
const childService2 = container.get(ParentService);
expect(childService1).to.be.instanceof(ChildService);
expect(childService2).to.be.eq(childService1);
});
it('should create an instance of the requested child class, even if its parent class is registered', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
const childService = container.get(ChildService);
expect(childService).to.be.instanceof(ChildService);
});
it('should return an instance of the requested child class, not the existing instance of its parent class', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
const parentService = container.get(ParentService);
expect(parentService).to.be.instanceof(ParentService);
const childService = container.get(ChildService);
expect(childService).to.be.instanceof(ChildService);
expect(childService).to.be.not.eq(parentService);
});
});
describe('in case of a parent container', function () {
it('instantiates in the parent container', function () {
class MyService {}
const parent = new ServiceContainer();
parent.add(MyService);
const child = new ServiceContainer(parent);
const res = child.get(MyService);
expect(res).to.be.instanceof(MyService);
});
it('returns an instance from the parent container', function () {
class MyService {}
const parent = new ServiceContainer();
const service = parent.get(MyService);
const child = new ServiceContainer(parent);
const res = child.get(MyService);
expect(res).to.be.eq(service);
});
it('does not adds the given constructor to the parent container', function () {
class MyService {}
const parent = new ServiceContainer();
const child = new ServiceContainer(parent);
const service = child.get(MyService);
expect(service).to.be.instanceof(MyService);
const res1 = child.has(MyService);
const res2 = parent.has(MyService);
expect(res1).to.be.true;
expect(res2).to.be.false;
});
describe('class inheritance', function () {
it('should resolve a parent class to an instance of its child class registered in a parent container', function () {
class ParentService {}
class ChildService extends ParentService {}
const parentContainer = new ServiceContainer();
const container = new ServiceContainer(parentContainer);
parentContainer.add(ChildService);
const childService = container.get(ParentService);
expect(childService).to.be.instanceof(ChildService);
});
it('should return the existing instance of the child class from a parent container when the parent class is requested', function () {
class ParentService {}
class ChildService extends ParentService {}
const parentContainer = new ServiceContainer();
const container = new ServiceContainer(parentContainer);
const childService1 = parentContainer.get(ChildService);
expect(childService1).to.be.instanceof(ChildService);
const childService2 = container.get(ParentService);
expect(childService1).to.be.instanceof(ChildService);
expect(childService2).to.be.eq(childService1);
});
it('should create an instance of the requested child class, even if its parent class is registered in a parent container', function () {
class ParentService {}
class ChildService extends ParentService {}
const parentContainer = new ServiceContainer();
const container = new ServiceContainer(parentContainer);
parentContainer.add(ParentService);
const childService = container.get(ChildService);
expect(childService).to.be.instanceof(ChildService);
});
it('should create an instance of the requested child class, even if a parent container has an instance of its parent class', function () {
class ParentService {}
class ChildService extends ParentService {}
const parentContainer = new ServiceContainer();
const container = new ServiceContainer(parentContainer);
const parentService = parentContainer.get(ParentService);
expect(parentService).to.be.instanceof(ParentService);
const childService = container.get(ChildService);
expect(childService).to.be.instanceof(ChildService);
expect(childService).to.be.not.eq(parentService);
});
});
});
});
});
describe('getRegistered', function () {
it('throws Error if the given constructor is not registered', function () {
const container = new ServiceContainer();
class MyService extends Service {}
const throwable = () => container.getRegistered(MyService);
expect(throwable).to.throw(
'The constructor MyService is not registered.',
);
});
it('should pass arguments to the "get" method and return a result', function () {
const container = new ServiceContainer();
class MyService extends Service {}
const spy = createSpy(container, 'get', () => 'result');
container.add(MyService);
const res = container.getRegistered(MyService, 'arg1', 'arg2');
expect(spy.callCount).to.be.eq(1);
expect(spy.getCall(0).args).to.be.eql([MyService, 'arg1', 'arg2']);
expect(res).to.be.eql('result');
});
});
describe('has', function () {
describe('Service', function () {
it('returns true when a given constructor has its cached instance or false', function () {
const container = new ServiceContainer();
class MyService extends Service {}
expect(container.has(MyService)).to.be.false;
container.get(MyService);
expect(container.has(MyService)).to.be.true;
});
it('returns true when a given constructor has its factory function or false', function () {
const container = new ServiceContainer();
class MyService extends Service {}
expect(container.has(MyService)).to.be.false;
container.add(MyService);
expect(container.has(MyService)).to.be.true;
});
describe('class inheritance', function () {
it('returns true if the child class is registered', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ChildService);
const res = container.has(ParentService);
expect(res).to.be.true;
});
it('returns false if the parent class is registered', function () {
class ParentService extends Service {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
const res = container.has(ChildService);
expect(res).to.be.false;
});
});
describe('in case of a parent container', function () {
it('returns true if the parent container has the given constructor', function () {
class MyService extends Service {}
const parent = new ServiceContainer();
parent.add(MyService);
const child = new ServiceContainer(parent);
const res = child.has(MyService);
expect(res).to.be.true;
});
});
});
describe('non-Service', function () {
it('returns true when a given constructor has its cached instance or false', function () {
const container = new ServiceContainer();
class MyService {}
expect(container.has(MyService)).to.be.false;
container.get(MyService);
expect(container.has(MyService)).to.be.true;
});
it('returns true when a given constructor has its factory function or false', function () {
const container = new ServiceContainer();
class MyService {}
expect(container.has(MyService)).to.be.false;
container.add(MyService);
expect(container.has(MyService)).to.be.true;
});
describe('class inheritance', function () {
it('returns true if the child class is registered', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ChildService);
const res = container.has(ParentService);
expect(res).to.be.true;
});
it('returns false if the parent class is registered', function () {
class ParentService {}
class ChildService extends ParentService {}
const container = new ServiceContainer();
container.add(ParentService);
const res = container.has(ChildService);
expect(res).to.be.false;
});
});
describe('in case of a parent container', function () {
it('returns true if the parent container has the given constructor', function () {
class MyService {}
const parent = new ServiceContainer();
parent.add(MyService);
const child = new ServiceContainer(parent);
const res = child.has(MyService);
expect(res).to.be.true;
});
});
});
});
describe('add', function () {
it('throws an error if no constructor given', function () {
const container = new ServiceContainer();
const throwable = v => () => container.add(v);
const error = v =>
format(
'The first argument of ServicesContainer.add must be ' +
'a class constructor, but %s given.',
v,
);
expect(throwable()).to.throw(error('undefined'));
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
});
describe('Service', function () {
it('returns itself', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const res = container.add(MyService);
expect(res).to.be.eq(container);
});
it('provides given arguments to the factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('overrides a cached instance of the given constructor', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const service1 = container.get(MyService);
const service2 = container.get(MyService);
expect(service1).to.be.eq(service2);
container.add(MyService);
const service3 = container.get(MyService);
const service4 = container.get(MyService);
expect(service3).to.be.eq(service4);
expect(service3).to.be.not.eq(service1);
});
it('overrides constructor arguments of the factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
container.add(MyService, 'baz', 'qux');
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
});
describe('non-Service', function () {
it('returns itself', function () {
class MyService {}
const container = new ServiceContainer();
const res = container.add(MyService);
expect(res).to.be.eq(container);
});
it('provides given arguments to the factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('overrides a cached instance of the given constructor', function () {
class MyService {}
const container = new ServiceContainer();
const service1 = container.get(MyService);
const service2 = container.get(MyService);
expect(service1).to.be.eq(service2);
container.add(MyService);
const service3 = container.get(MyService);
const service4 = container.get(MyService);
expect(service3).to.be.eq(service4);
expect(service3).to.be.not.eq(service1);
});
it('overrides constructor arguments of the factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
container.add(MyService, 'baz', 'qux');
const service = container.get(MyService);
expect(service).to.be.instanceof(MyService);
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
});
});
describe('use', function () {
it('throws an error if no constructor given', function () {
const container = new ServiceContainer();
const throwable = v => () => container.use(v);
const error = v =>
format(
'The first argument of ServicesContainer.use must be ' +
'a class constructor, but %s given.',
v,
);
expect(throwable()).to.throw(error('undefined'));
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
});
describe('Service', function () {
it('returns itself', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const res = container.use(MyService);
expect(res).to.be.eq(container);
});
it('passes itself and given arguments to the given constructor', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.use(MyService, 'foo', 'bar');
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('overrides an existing factory function', function () {
let executed = 0;
let givenContainer;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
executed++;
givenContainer = container;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
container.use(MyService, 'baz', 'qux');
expect(executed).to.be.eq(1);
expect(givenContainer).to.be.eq(container);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
it('caches a new instance', function () {
let executed = 0;
let service;
class MyService extends Service {
constructor(container) {
super(container);
++executed;
service = this;
}
}
const container = new ServiceContainer();
container.use(MyService);
const cachedService = container.get(MyService);
expect(cachedService).to.be.instanceof(MyService);
expect(cachedService).to.be.eq(service);
expect(executed).to.be.eq(1);
});
it('overrides the cached instance', function () {
let executed = 0;
let service;
let givenArgs;
class MyService extends Service {
constructor(container, ...args) {
super(container);
++executed;
service = this;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.use(MyService, 'foo');
expect(executed).to.be.eq(1);
expect(service).to.be.instanceof(MyService);
expect(givenArgs).to.be.eql(['foo']);
const service1 = service;
container.use(MyService, 'bar');
expect(executed).to.be.eq(2);
expect(service).to.be.instanceof(MyService);
expect(givenArgs).to.be.eql(['bar']);
const service2 = service;
expect(service2).to.be.not.eq(service1);
});
});
describe('non-Service', function () {
it('returns itself', function () {
class MyService {}
const container = new ServiceContainer();
const res = container.use(MyService);
expect(res).to.be.eq(container);
});
it('passes given arguments to the given constructor', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.use(MyService, 'foo', 'bar');
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['foo', 'bar']);
});
it('overrides an existing factory function', function () {
let executed = 0;
let givenArgs;
class MyService {
constructor(...args) {
executed++;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.add(MyService, 'foo', 'bar');
expect(executed).to.be.eq(0);
container.use(MyService, 'baz', 'qux');
expect(executed).to.be.eq(1);
expect(givenArgs).to.be.eql(['baz', 'qux']);
});
it('caches a new instance', function () {
let executed = 0;
let service;
class MyService {
constructor() {
++executed;
service = this;
}
}
const container = new ServiceContainer();
container.use(MyService);
const cachedService = container.get(MyService);
expect(cachedService).to.be.instanceof(MyService);
expect(cachedService).to.be.eq(service);
expect(executed).to.be.eq(1);
});
it('overrides the cached instance', function () {
let executed = 0;
let service;
let givenArgs;
class MyService {
constructor(...args) {
++executed;
service = this;
givenArgs = args;
}
}
const container = new ServiceContainer();
container.use(MyService, 'foo');
expect(executed).to.be.eq(1);
expect(service).to.be.instanceof(MyService);
expect(givenArgs).to.be.eql(['foo']);
const service1 = service;
container.use(MyService, 'bar');
expect(executed).to.be.eq(2);
expect(service).to.be.instanceof(MyService);
expect(givenArgs).to.be.eql(['bar']);
const service2 = service;
expect(service2).to.be.not.eq(service1);
});
});
});
describe('set', function () {
it('requires the "ctor" argument to be a class constructor', function () {
const container = new ServiceContainer();
const throwable = v => () => container.set(v, {});
const error = v =>
format(
'The first argument of ServicesContainer.set must be ' +
'a class constructor, but %s given.',
v,
);
expect(throwable()).to.throw(error('undefined'));
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
expect(throwable({})).to.throw(error('Object'));
throwable(String)();
});
it('requires the "service" argument to be an Object', function () {
const container = new ServiceContainer();
const throwable = v => () => container.set(String, v);
const error = v =>
format(
'The second argument of ServicesContainer.set must be ' +
'an Object, but %s given.',
v,
);
expect(throwable()).to.throw(error('undefined'));
expect(throwable('str')).to.throw(error('"str"'));
expect(throwable(10)).to.throw(error('10'));
expect(throwable(true)).to.throw(error('true'));
expect(throwable(false)).to.throw(error('false'));
expect(throwable(null)).to.throw(error('null'));
expect(throwable([])).to.throw(error('Array'));
throwable({})();
});
describe('Service', function () {
it('returns itself', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const res = container.set(MyService, {});
expect(res).to.be.eq(container);
});
it('sets the given service', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const service = {};
container.set(MyService, service);
const res = container.get(MyService);
expect(res).to.be.eq(service);
});
it('overrides by the given service', function () {
class MyService extends Service {}
const container = new ServiceContainer();
const service1 = {foo: 'bar'};
const service2 = {bar: 'baz'};
container.set(MyService, service1);
container.set(MyService, service2);
const res = container.get(MyService);
expect(res).to.be.eq(service2);
});
});
describe('non-Service', function () {
it('returns itself', function () {
class MyService {}
const container = new ServiceContainer();
const res = container.set(MyService, {});
expect(res).to.be.eq(container);
});
it('sets the given service', function () {
class MyService {}
const container = new ServiceContainer();
const service = {};
container.set(MyService, service);
const res = container.get(MyService);
expect(res).to.be.eq(service);
});
it('overrides by the given service', function () {
class MyService {}
const container = new ServiceContainer();
const service1 = {foo: 'bar'};
const service2 = {bar: 'baz'};
container.set(MyService, service1);
container.set(MyService, service2);
const res = container.get(MyService);
expect(res).to.be.eq(service2);
});
});
});
});