diff --git a/build.gradle b/build.gradle --- a/build.gradle +++ b/build.gradle @@ -37,7 +37,7 @@ task _legacyJs(type:Copy) { task _buildTs(dependsOn: _npmInstall, type:Exec) { inputs.dir('src/ts') - inputs.file('tsc.json') + inputs.file('tsconfig.json') outputs.dir(distDir) commandLine 'node_modules/.bin/tsc', '-p', 'tsconfig.json' @@ -77,7 +77,7 @@ task copyJsTests(type: Copy) { task buildTests(dependsOn: _localInstall, type: Exec) { inputs.dir('test/ts') - inputs.file('tsc.test.json') + inputs.file('tsconfig.test.json') outputs.dir(testDir) commandLine 'node_modules/.bin/tsc', '-p', 'tsconfig.test.json' diff --git a/gradle.properties b/gradle.properties --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ -version=1.1.1 +version=1.1.2 release=rtm \ No newline at end of file diff --git a/package-lock.json b/package-lock.json --- a/package-lock.json +++ b/package-lock.json @@ -5,9 +5,9 @@ "requires": true, "dependencies": { "@types/node": { - "version": "10.5.1", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.5.1.tgz", - "integrity": "sha512-AFLl1IALIuyt6oK4AYZsgWVJ/5rnyzQWud7IebaZWWV3YmgtPZkQmYio9R5Ze/2pdd7XfqF5bP+hWS11mAKoOQ==", + "version": "10.12.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.12.tgz", + "integrity": "sha512-Pr+6JRiKkfsFvmU/LK68oBRCQeEg36TyAbPhc2xpez24OOZZCuoIhWGTd39VZy6nGafSbxzGouFPTFD/rR1A0A==", "dev": true }, "@types/tape": { @@ -48,25 +48,32 @@ "dev": true }, "deep-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz", + "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=", "dev": true }, "define-properties": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.2.tgz", - "integrity": "sha1-g6c/L+pWmJj7c3GTyPhzyvbUXJQ=", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", "dev": true, "requires": { - "foreach": "^2.0.5", - "object-keys": "^1.0.8" + "object-keys": "^1.0.12" + }, + "dependencies": { + "object-keys": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", + "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", + "dev": true + } } }, "defined": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", - "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", + "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=", "dev": true }, "dojo": { @@ -76,7 +83,7 @@ }, "duplexer": { "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", + "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", "dev": true }, @@ -94,14 +101,14 @@ } }, "es-to-primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.1.1.tgz", - "integrity": "sha1-RTVSSKiJeQNLZ5Lhm7gfK3l13Q0=", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha512-qZryBOJjV//LaxLTV6UC//WewneB3LcXOL9NP++ozKVXsIIIpm/2c13UDiD9Jp2eThsecw9m3jPqDwTyobcdbg==", "dev": true, "requires": { - "is-callable": "^1.1.1", + "is-callable": "^1.1.4", "is-date-object": "^1.0.1", - "is-symbol": "^1.0.1" + "is-symbol": "^1.0.2" } }, "faucet": { @@ -119,27 +126,9 @@ "through2": "~0.2.3" }, "dependencies": { - "deep-equal": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-0.1.2.tgz", - "integrity": "sha1-skbCuApXCkfBG+HZvRBw7IeLh84=", - "dev": true - }, - "defined": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/defined/-/defined-0.0.0.tgz", - "integrity": "sha1-817qfXBekzuvE7LwOz+D2SFAOz4=", - "dev": true - }, - "minimist": { - "version": "0.0.5", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", - "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=", - "dev": true - }, "tape": { "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tape/-/tape-2.3.3.tgz", + "resolved": "http://registry.npmjs.org/tape/-/tape-2.3.3.tgz", "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=", "dev": true, "requires": { @@ -162,12 +151,6 @@ "is-callable": "^1.1.3" } }, - "foreach": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/foreach/-/foreach-2.0.5.tgz", - "integrity": "sha1-C+4AUBiusmDQo6865ljdATbsG5k=", - "dev": true - }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -181,9 +164,9 @@ "dev": true }, "glob": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", - "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", @@ -203,6 +186,12 @@ "function-bind": "^1.1.1" } }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -220,9 +209,9 @@ "dev": true }, "is-callable": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.3.tgz", - "integrity": "sha1-hut1OSgF3cM69xySoO7fdO52BLI=", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, "is-date-object": { @@ -241,10 +230,13 @@ } }, "is-symbol": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.1.tgz", - "integrity": "sha1-PMWfAAJRlLarLjjbrmaJJWtmBXI=", - "dev": true + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha512-HS8bZ9ox60yCJLH9snBpIwv9pYUAkcuLhSA1oero1UB5y9aiQpRA8y2ex945AOtCZL1lJDeIk3G5LthswI46Lw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } }, "isarray": { "version": "0.0.1", @@ -268,9 +260,9 @@ } }, "minimist": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", - "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "version": "0.0.5", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", + "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=", "dev": true }, "object-inspect": { @@ -280,9 +272,9 @@ "dev": true }, "object-keys": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", - "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", "dev": true }, "once": { @@ -296,19 +288,19 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "path-parse": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", - "integrity": "sha1-PBrfhx6pzWyUMbbqK9dKD/BVxME=", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", "dev": true }, "readable-stream": { "version": "1.1.14", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", "dev": true, "requires": { @@ -361,7 +353,7 @@ }, "string_decoder": { "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", "dev": true }, @@ -394,17 +386,37 @@ "resumer": "~0.0.0", "string.prototype.trim": "~1.1.2", "through": "~2.3.8" + }, + "dependencies": { + "deep-equal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", + "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU=", + "dev": true + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=", + "dev": true + }, + "minimist": { + "version": "1.2.0", + "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", + "dev": true + } } }, "through": { "version": "2.3.8", - "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", "dev": true }, "through2": { "version": "0.2.3", - "resolved": "https://registry.npmjs.org/through2/-/through2-0.2.3.tgz", + "resolved": "http://registry.npmjs.org/through2/-/through2-0.2.3.tgz", "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", "dev": true, "requires": { @@ -413,9 +425,9 @@ } }, "typescript": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.1.6.tgz", - "integrity": "sha512-tDMYfVtvpb96msS1lDX9MEdHrW4yOuZ4Kdc4Him9oU796XldPYF/t2+uKoX0BBa0hXXwDlqYQbXY5Rzjzc5hBA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.2.1.tgz", + "integrity": "sha512-jw7P2z/h6aPT4AENXDGjcfHTu5CSqzsbZc6YlUIebTyBAq8XaKp78x7VcSh30xwSCcsu5irZkYZUSFP1MrAMbg==", "dev": true }, "wrappy": { @@ -431,14 +443,6 @@ "dev": true, "requires": { "object-keys": "~0.4.0" - }, - "dependencies": { - "object-keys": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", - "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", - "dev": true - } } } } diff --git a/src/ts/di/ActivationContext.ts b/src/ts/di/ActivationContext.ts --- a/src/ts/di/ActivationContext.ts +++ b/src/ts/di/ActivationContext.ts @@ -44,7 +44,7 @@ export class ActivationContext { else throw new Error(`Service ${name} not found`); - return d.activate(this, name); + return isDescriptor(d) ? d.activate(this, name) : d; } /** @@ -80,7 +80,7 @@ export class ActivationContext { return (this._cache[id] = value); } - parse(data: object, name: string) { + parse(data, name: string) { if (isPrimitive(data)) return data; diff --git a/src/ts/di/Container.ts b/src/ts/di/Container.ts --- a/src/ts/di/Container.ts +++ b/src/ts/di/Container.ts @@ -39,7 +39,7 @@ export class Container { return this._parent; } - getService(name: string, def?: T) { + getService(name: string, def?) { const d = this._services[name]; if (!d) if (arguments.length > 1) @@ -47,13 +47,16 @@ export class Container { else throw new Error("Service '" + name + "' isn't found"); + if (!isDescriptor(d)) + return d; + if (d.isInstanceCreated()) - return d.getInstance() as T; + return d.getInstance(); const context = new ActivationContext(this, this._services); try { - return d.activate(context, name) as T; + return d.activate(context, name); } catch (error) { throw new ActivationError(name, context.getStack(), error); } @@ -65,8 +68,6 @@ export class Container { for (const name in data) this.register(name, data[name]); } else { - if (!(isDescriptor(service))) - service = new ValueDescriptor(service); this._services[nameOrCollection] = service; } return this; @@ -148,13 +149,13 @@ export class Container { if (isDependencyRegistration(registration)) { - return new ReferenceDescriptor( - registration.$dependency, - registration.lazy, - registration.optional, - registration["default"], - registration.services && this._parseObject(registration.services, resolver) - ); + return new ReferenceDescriptor({ + name: registration.$dependency, + lazy: registration.lazy, + optional: registration.optional, + default: registration.default, + services: registration.services && this._parseObject(registration.services, resolver) + }); } else if (isValueRegistration(registration)) { diff --git a/src/ts/di/ReferenceDescriptor.ts b/src/ts/di/ReferenceDescriptor.ts --- a/src/ts/di/ReferenceDescriptor.ts +++ b/src/ts/di/ReferenceDescriptor.ts @@ -3,6 +3,14 @@ import { ActivationContext } from "./Act import { ServiceMap, Descriptor } from "./interfaces"; import { ActivationError } from "./ActivationError"; +export interface ReferenceDescriptorParams { + name: string; + lazy?: boolean; + optional?: boolean; + default?; + services?: ServiceMap; +} + export class ReferenceDescriptor implements Descriptor { _name: string; @@ -14,13 +22,13 @@ export class ReferenceDescriptor impleme _services: ServiceMap; - constructor(name: string, lazy: boolean, optional: boolean, def, services: ServiceMap) { - argumentNotEmptyString(name, "name"); - this._name = name; - this._lazy = Boolean(lazy); - this._optional = Boolean(optional); - this._default = def; - this._services = services; + constructor(opts: ReferenceDescriptorParams) { + argumentNotEmptyString(opts && opts.name, "opts.name"); + this._name = opts.name; + this._lazy = !!opts.lazy; + this._optional = !!opts.optional; + this._default = !!opts.default; + this._services = opts.services; } activate(context: ActivationContext, name: string) { diff --git a/src/ts/di/interfaces.ts b/src/ts/di/interfaces.ts --- a/src/ts/di/interfaces.ts +++ b/src/ts/di/interfaces.ts @@ -1,4 +1,4 @@ -import { isNull } from "../safe"; +import { isNull, isPrimitive } from "../safe"; import { ActivationContext } from "./ActivationContext"; import { Constructor, Factory } from "../interfaces"; @@ -8,13 +8,13 @@ export interface Descriptor { getInstance(); } -export function isDescriptor(instance): instance is Descriptor { - return (!isNull(instance)) && - ("activate" in instance); +export function isDescriptor(x): x is Descriptor { + return (!isPrimitive(x)) && + (x.activate instanceof Function); } export interface ServiceMap { - [s: string]: Descriptor; + [s: string]: any; } export enum ActivationType { @@ -56,13 +56,13 @@ export interface DependencyRegistration } export function isServiceRegistration(x): x is ServiceRegistration { - return x && ("$type" in x || "$factory" in x); + return (!isPrimitive(x)) && ("$type" in x || "$factory" in x); } export function isValueRegistration(x): x is ValueRegistration { - return x && "$value" in x; + return (!isPrimitive(x)) && ("$value" in x); } export function isDependencyRegistration(x): x is DependencyRegistration { - return x && "$depdendency" in x; + return (!isPrimitive(x)) && ("$depdendency" in x); } diff --git a/test/js/plan.js b/test/js/plan.js --- a/test/js/plan.js +++ b/test/js/plan.js @@ -1,3 +1,4 @@ -define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]); +//define(["./ActivatableTests", "./trace-test", "./TraceSourceTests", "./CancellationTests"]); //define(["./CancellationTests"]); -//define(["./ObservableTests"]); \ No newline at end of file +//define(["./ObservableTests"]); +define(["./ContainerTests"]); \ No newline at end of file diff --git a/test/ts/ActivatableTests.ts b/test/ts/ActivatableTests.ts --- a/test/ts/ActivatableTests.ts +++ b/test/ts/ActivatableTests.ts @@ -1,73 +1,25 @@ -import * as tape from 'tape'; -import { ActivatableMixin} from '@implab/core/components/ActivatableMixin'; -import { AsyncComponent } from '@implab/core/components/AsyncComponent'; -import { IActivationController, IActivatable, ICancellation } from '@implab/core/interfaces'; -import { Cancellation } from '@implab/core/Cancellation'; - -class SimpleActivatable extends ActivatableMixin(AsyncComponent) { - -} - -class MockActivationController implements IActivationController { - - _active: IActivatable = null; - - - getActive() : IActivatable { - return this._active; - } +import * as tape from "tape"; +import { MockActivationController } from "./mock/MockActivationController"; +import { SimpleActivatable } from "./mock/SimpleActivatable"; - async deactivate() { - if (this._active) - await this._active.deactivate(); - this._active = null; - } - - async activate(component: IActivatable) { - if (!component || component.isActive()) - return; - component.setActivationController(this); - - await component.activate(); - } - - async activating(component: IActivatable, ct: ICancellation = Cancellation.none) { - if (component != this._active) - await this.deactivate(); - } +tape("simple activation", async t => { - async activated(component: IActivatable, ct: ICancellation = Cancellation.none) { - this._active = component; - } - - async deactivating(component: IActivatable, ct: ICancellation = Cancellation.none) { - - } + const a = new SimpleActivatable(); + t.false(a.isActive()); - async deactivated(component: IActivatable, ct: ICancellation = Cancellation.none) { - if (this._active == component) - this._active = null; - } -} - -tape('simple activation',async function(t){ - - let a = new SimpleActivatable(); - t.false(a.isActive()); - await a.activate(); t.true(a.isActive()); - + await a.deactivate(); t.false(a.isActive()); t.end(); }); -tape('controller activation', async function(t) { +tape("controller activation", async t => { - let a = new SimpleActivatable(); - let c = new MockActivationController(); + const a = new SimpleActivatable(); + const c = new MockActivationController(); t.false(a.isActive(), "the component is not active by default"); t.assert(c.getActive() == null, "the activation controller doesn't have an active component by default"); @@ -89,11 +41,11 @@ tape('controller activation', async func t.end(); }); -tape('handle error in onActivating', async function(t) { - let a = new SimpleActivatable(); +tape("handle error in onActivating", async t => { + const a = new SimpleActivatable(); - a.onActivating = async function() { - throw "Should fail"; + a.onActivating = async () => { + throw new Error("Should fail"); }; try { @@ -105,4 +57,4 @@ tape('handle error in onActivating', asy t.false(a.isActive(), "the component should remain inactive"); t.end(); -}); \ No newline at end of file +}); diff --git a/test/ts/CancellationTests.ts b/test/ts/CancellationTests.ts --- a/test/ts/CancellationTests.ts +++ b/test/ts/CancellationTests.ts @@ -1,18 +1,18 @@ -import * as tape from 'tape'; -import { Cancellation } from '@implab/core/Cancellation'; -import { ICancellation } from '@implab/core/interfaces'; -import { delay } from './TestTraits'; +import * as tape from "tape"; +import { Cancellation } from "@implab/core/Cancellation"; +import { ICancellation } from "@implab/core/interfaces"; +import { delay } from "./TestTraits"; -tape('standalone cancellation', async t => { +tape("standalone cancellation", async t => { let doCancel: (e) => void; - let ct = new Cancellation(cancel => { + const ct = new Cancellation(cancel => { doCancel = cancel; }); let counter = 0; - let reason = "BILL"; + const reason = "BILL"; t.true(ct.isSupported(), "Cancellation must be supported"); t.false(ct.isRequested(), "Cancellation shouldn't be requested"); @@ -33,7 +33,7 @@ tape('standalone cancellation', async t t.equals(counter, 2, "The callback should be triggered immediately"); let msg; - ct.register((e) => msg = e); + ct.register(e => msg = e); t.equals(msg, reason, "The cancellation reason should be passed to callback"); try { @@ -48,9 +48,9 @@ tape('standalone cancellation', async t t.end(); }); -tape('async cancellation', async t => { +tape("async cancellation", async t => { - let ct = new Cancellation(cancel => { + const ct = new Cancellation(cancel => { cancel("STOP!"); }); @@ -64,10 +64,10 @@ tape('async cancellation', async t => { t.end(); }); -tape('cancel with external event', async t => { - let ct = new Cancellation((cancel) => { - setTimeout(x => cancel('STOP!'), 0); - }) +tape("cancel with external event", async t => { + const ct = new Cancellation(cancel => { + setTimeout(x => cancel("STOP!"), 0); + }); try { await delay(10000, ct); @@ -79,10 +79,10 @@ tape('cancel with external event', async t.end(); }); -tape('operation normal flow', async t => { +tape("operation normal flow", async t => { let htimeout; - let ct = new Cancellation((cancel) => { + const ct = new Cancellation(cancel => { htimeout = setTimeout(() => cancel("STOP!"), 1000); }); @@ -94,4 +94,4 @@ tape('operation normal flow', async t => } t.end(); -}); \ No newline at end of file +}); diff --git a/test/ts/ContainerTests.ts b/test/ts/ContainerTests.ts new file mode 100644 --- /dev/null +++ b/test/ts/ContainerTests.ts @@ -0,0 +1,26 @@ +import { test } from "./TestTraits"; +import { Container } from "@implab/core/di/Container"; +import { ReferenceDescriptor } from "@implab/core/di/ReferenceDescriptor"; +import { AggregateDescriptor } from "@implab/core/di/AggregateDescriptor"; + +test("Container register/getService tests", async t => { + const container = new Container(); + + const connection1 = "db://localhost"; + + container.register("connection", connection1); + + t.equals(container.getService("connection"), connection1); + + container.register( + "dbParams", + new AggregateDescriptor({ + timeout: 10, + connection: new ReferenceDescriptor({name: "connection"}) + }) + ); + + const dbParams = container.getService("dbParams"); + t.equals(dbParams.connection, connection1); + +}); diff --git a/test/ts/ObservableTests.ts b/test/ts/ObservableTests.ts --- a/test/ts/ObservableTests.ts +++ b/test/ts/ObservableTests.ts @@ -1,23 +1,22 @@ -import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource' -import * as tape from 'tape'; -import { TapeWriter, delay } from './TestTraits'; -import { Observable } from '@implab/core/Observable'; -import { IObservable } from '@implab/core/interfaces'; - -let trace = TraceSource.get("ObservableTests"); +import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource"; +import * as tape from "tape"; +import { TapeWriter, delay } from "./TestTraits"; +import { Observable } from "@implab/core/Observable"; +import { IObservable } from "@implab/core/interfaces"; -tape('events sequence example', async t => { +const trace = TraceSource.get("ObservableTests"); +tape("events sequence example", async t => { - let events: IObservable + let events: IObservable; - let done = new Promise((resolve) => { - events = new Observable(async (notify, fail, complete) => { + const done = new Promise(resolve => { + events = new Observable(async (notify, fail, finish) => { for (let i = 0; i < 10; i++) { await delay(0); notify(i); } - complete(); + finish(); resolve(); }); }); @@ -26,7 +25,7 @@ tape('events sequence example', async t let complete = false; events.on(x => count = count + x, null, () => complete = true); - let first = await events.next(); + const first = await events.next(); t.equals(first, 0, "the first event"); t.false(complete, "the sequence is not complete"); @@ -39,10 +38,10 @@ tape('events sequence example', async t t.end(); }); -tape('event sequence termination', async t => { - let events: IObservable +tape("event sequence termination", async t => { + let events: IObservable; - let done = new Promise((resolve) => { + const done = new Promise(resolve => { events = new Observable(async (notify, fail, complete) => { await delay(0); notify(1); @@ -55,14 +54,14 @@ tape('event sequence termination', async }); let count = 0; - events.on(() => {}, (e) => count++, () => count++); + events.on(() => {}, e => count++, () => count++); - let first = await events.next(); + const first = await events.next(); t.equals(first, 1, "the first message"); try { await events.next(); t.fail("shoud throw an exception"); - } catch(e) { + } catch (e) { t.pass("the sequence is terminated"); } @@ -71,4 +70,4 @@ tape('event sequence termination', async t.equals(count, 1, "the sequence must be terminated once"); t.end(); -}); \ No newline at end of file +}); diff --git a/test/ts/TestTraits.ts b/test/ts/TestTraits.ts --- a/test/ts/TestTraits.ts +++ b/test/ts/TestTraits.ts @@ -1,21 +1,21 @@ import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces"; import { Cancellation } from "@implab/core/Cancellation"; import { TraceEvent, LogLevel, WarnLevel } from "@implab/core/log/TraceSource"; -import * as tape from 'tape'; +import * as tape from "tape"; import { argumentNotNull } from "@implab/core/safe"; export class TapeWriter implements IDestroyable { - readonly _tape: tape.Test + readonly _tape: tape.Test; _subscriptions = new Array(); - constructor(tape: tape.Test) { - argumentNotNull(tape, "tape"); - this._tape = tape; + constructor(t: tape.Test) { + argumentNotNull(t, "tape"); + this._tape = t; } writeEvents(source: IObservable, ct: ICancellation = Cancellation.none) { - let subscription = source.on(this.writeEvent.bind(this)); + const subscription = source.on(this.writeEvent.bind(this)); if (ct.isSupported()) { ct.register(subscription.destroy.bind(subscription)); } @@ -39,24 +39,37 @@ export class TapeWriter implements IDest export async function delay(timeout: number, ct: ICancellation = Cancellation.none) { let un: IDestroyable; - + try { - await new Promise((resolve, reject) => { + await new Promise((resolve, reject) => { if (ct.isRequested()) { - un = ct.register(reject); - } else { - let ht = setTimeout(() => { - resolve(); - }, timeout); - - un = ct.register(e => { - clearTimeout(ht); - reject(e); - }); - } - }); + un = ct.register(reject); + } else { + const ht = setTimeout(() => { + resolve(); + }, timeout); + + un = ct.register(e => { + clearTimeout(ht); + reject(e); + }); + } + }); } finally { - if(un) - un.destroy(); - }; -} \ No newline at end of file + if (un) + un.destroy(); + } +} + +export function test(name: string, cb: (t: tape.Test) => any) { + tape(name, async t => { + try { + await cb(t); + } catch (e) { + console.error(e); + t.fail(e); + } finally { + t.end(); + } + }); +} diff --git a/test/ts/TraceSourceTests.ts b/test/ts/TraceSourceTests.ts --- a/test/ts/TraceSourceTests.ts +++ b/test/ts/TraceSourceTests.ts @@ -1,15 +1,15 @@ -import { TraceSource, DebugLevel } from '@implab/core/log/TraceSource' -import * as tape from 'tape'; -import { TapeWriter } from './TestTraits'; +import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource"; +import * as tape from "tape"; +import { TapeWriter } from "./TestTraits"; -const sourceId = 'test/TraceSourceTests'; +const sourceId = "test/TraceSourceTests"; -tape('trace message', t => { - let trace = TraceSource.get(sourceId); +tape("trace message", t => { + const trace = TraceSource.get(sourceId); trace.level = DebugLevel; - let h = trace.events.on((ev) => { + const h = trace.events.on(ev => { t.equal(ev.source, trace, "sender should be the current trace source"); t.equal(ev.level, DebugLevel, "level should be debug level"); t.equal(ev.arg, "Hello, World!", "The message should be a formatted message"); @@ -22,16 +22,16 @@ tape('trace message', t => { h.destroy(); }); -tape('trace event', t => { - let trace = TraceSource.get(sourceId); +tape("trace event", t => { + const trace = TraceSource.get(sourceId); trace.level = DebugLevel; - let event = { + const event = { name: "custom event" }; - let h = trace.events.on((ev) => { + const h = trace.events.on(ev => { t.equal(ev.source, trace, "sender should be the current trace source"); t.equal(ev.level, DebugLevel, "level should be debug level"); t.equal(ev.arg, event, "The message should be the specified object"); @@ -44,17 +44,17 @@ tape('trace event', t => { h.destroy(); }); -tape('tape comment writer', async t => { - let writer = new TapeWriter(t); +tape("tape comment writer", async t => { + const writer = new TapeWriter(t); TraceSource.on(ts => { writer.writeEvents(ts.events); }); - let trace = TraceSource.get(sourceId); + const trace = TraceSource.get(sourceId); trace.level = DebugLevel; - trace.log("Hello, {0}!", 'World'); + trace.log("Hello, {0}!", "World"); trace.log("Multi\n line"); trace.warn("Look at me!"); trace.error("DIE!"); @@ -66,4 +66,4 @@ tape('tape comment writer', async t => { t.comment("DONE"); t.end(); -}); \ No newline at end of file +}); diff --git a/test/ts/mock/MockActivationController.ts b/test/ts/mock/MockActivationController.ts new file mode 100644 --- /dev/null +++ b/test/ts/mock/MockActivationController.ts @@ -0,0 +1,43 @@ +import { IActivatable, ICancellation, IActivationController } from "@implab/core/interfaces"; +import { Cancellation } from "@implab/core/Cancellation"; + +export class MockActivationController implements IActivationController { + + _active: IActivatable = null; + + getActive(): IActivatable { + return this._active; + } + + async deactivate() { + if (this._active) + await this._active.deactivate(); + this._active = null; + } + + async activate(component: IActivatable) { + if (!component || component.isActive()) + return; + component.setActivationController(this); + + await component.activate(); + } + + async activating(component: IActivatable, ct: ICancellation = Cancellation.none) { + if (component !== this._active) + await this.deactivate(); + } + + async activated(component: IActivatable, ct: ICancellation = Cancellation.none) { + this._active = component; + } + + async deactivating(component: IActivatable, ct: ICancellation = Cancellation.none) { + + } + + async deactivated(component: IActivatable, ct: ICancellation = Cancellation.none) { + if (this._active === component) + this._active = null; + } +} diff --git a/test/ts/mock/SimpleActivatable.ts b/test/ts/mock/SimpleActivatable.ts new file mode 100644 --- /dev/null +++ b/test/ts/mock/SimpleActivatable.ts @@ -0,0 +1,6 @@ +import { AsyncComponent } from "@implab/core/components/AsyncComponent"; +import { ActivatableMixin } from "@implab/core/components/ActivatableMixin"; + +export class SimpleActivatable extends ActivatableMixin(AsyncComponent) { + +}