diff --git a/.project b/.project
new file mode 100644
--- /dev/null
+++ b/.project
@@ -0,0 +1,17 @@
+
+
+ core
+ Project implabjs-core created by Buildship.
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectbuilder
+
+
+
+
+
+ org.eclipse.buildship.core.gradleprojectnature
+
+
diff --git a/build.gradle b/build.gradle
--- a/build.gradle
+++ b/build.gradle
@@ -6,7 +6,6 @@ plugins {
// результатом будет версия '{num}.{distance}' где distance - расстояние от
// текущей ревизии до ревизии с тэгом
def tagDistance = 0;
-def isRelease = false;
if (!version) {
@@ -35,25 +34,11 @@ if (hasProperty('versionSuffix') && vers
version += "-$versionSuffix"
}
-if(!npmName)
- npmName = name;
-
-if (hasProperty('release')) {
- isRelease = (release != 'false')
-} else {
- isRelease = (tagDistance == 0);
-}
-
-if(!["amd", "commonjs", "system", "umd", "es6", "esnext"].contains(jsmodule))
+if(! jsmodule in ["amd", "commonjs", "system", "umd", "es6", "esnext"])
throw new Exception("Invalid jsmodule specified: $jsmodule");
-if(!["es3", "es5", "es6", "es2016", "es2017", "esnext"].contains(target))
+if(! target in ["es3", "es5", "es6", "es2016", "es2017", "esnext"])
throw new Exception("Invalid target specified: $target")
-def targetLibs = [
- "es3" : ["es5", "es2015.promise", "es2015.symbol", "dom", "scripthost"],
- "es5" : ["es5", "es2015.promise", "es2015.symbol", "dom", "scripthost"]
-];
-
ext {
packageName = "@$npmScope/$npmName"
}
@@ -61,25 +46,50 @@ ext {
def jstarget = target;
sources {
+ amd {
+ }
+
+ cjs {
+
+ }
}
typescript {
compilerOptions {
- lib = targetLibs[target] ?: [target, "dom"]
+ lib = [target, "dom", "scripthost"]
+ if (jstarget in ["es5", "es3"])
+ lib += ["es2015.promise"]
+
target = jstarget
module = jsmodule
types = []
+ declaration = true
+ listFiles = true
+
}
tsLintCmd = "tslint"
esLintCmd = "eslint"
npmCmd = "npm"
}
+configureTsMain {
+ compilerOptions {
+ if (jstarget in ["es5", "es3"])
+ lib += ["es2015.symbol", "es2015.iterable"]
+ }
+}
+
+configureTsTest {
+ compilerOptions {
+ types += [ "node" ]
+ }
+}
+
task printVersion {
doLast {
println "version: $version";
- println "isRelease: $isRelease, tagDistance: $tagDistance";
+ println "tagDistance: $tagDistance";
println "packageName: $packageName";
println "bundle: ${npmPack.outputs.files.join(',')}";
println "target: $jstarget";
@@ -95,11 +105,6 @@ task clean {
npmPackMeta {
meta {
- name = "@$npmScope/$npmName"
+ name = packageName
}
}
-
-task markRelease(type: Exec) {
- onlyIf { tagDistance > 1 }
- commandLine "hg", "tag", "v$version";
-}
\ 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.12.18",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
- "integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
+ "version": "8.10.55",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.55.tgz",
+ "integrity": "sha512-iZeh1EgupfmAAOASk580R1SL5lWF3CsBVgVH0395qyNF8fhO16xy1UwAav2PdGxIIsYRn7RzJgMGjdsvam6YYg==",
"dev": true
},
"@types/requirejs": {
diff --git a/package.json b/package.json
--- a/package.json
+++ b/package.json
@@ -21,7 +21,7 @@
"tslib": "latest"
},
"devDependencies": {
- "@types/node": "latest",
+ "@types/node": "^8.0.0",
"@types/requirejs": "latest",
"@types/tape": "latest",
"dojo": "^1.10.0",
diff --git a/src/test/ts/ActivatableTests.ts b/src/test/ts/ActivatableTests.ts
deleted file mode 100644
--- a/src/test/ts/ActivatableTests.ts
+++ /dev/null
@@ -1,54 +0,0 @@
-import { MockActivationController } from "./mock/MockActivationController";
-import { SimpleActivatable } from "./mock/SimpleActivatable";
-import { test } from "./TestTraits";
-
-test("simple activation", async t => {
-
- const a = new SimpleActivatable();
- t.false(a.isActive());
-
- await a.activate();
- t.true(a.isActive());
-
- await a.deactivate();
- t.false(a.isActive());
-});
-
-test("controller activation", async t => {
-
- 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");
- t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default");
-
- t.comment("Active the component through the controller");
- await c.activate(a);
- t.true(a.isActive(), "The component should successfully activate");
- t.equal(c.getActive(), a, "The controller should point to the activated component");
- t.equal(a.getActivationController(), c, "The component should point to the controller");
-
- t.comment("Deactive the component throug the controller");
- await c.deactivate();
-
- t.false(a.isActive(), "The component should successfully deactivate");
- t.equal(c.getActive(), null, "The controller shouldn't point to any component");
- t.equal(a.getActivationController(), c, "The componet should point to it's controller");
-});
-
-test("handle error in onActivating", async t => {
- const a = new SimpleActivatable();
-
- a.onActivating = async () => {
- throw new Error("Should fail");
- };
-
- try {
- await a.activate();
- t.fail("activation should fail");
- } catch {
- }
-
- t.false(a.isActive(), "the component should remain inactive");
-});
diff --git a/src/test/ts/CancellationTests.ts b/src/test/ts/CancellationTests.ts
deleted file mode 100644
--- a/src/test/ts/CancellationTests.ts
+++ /dev/null
@@ -1,88 +0,0 @@
-import { Cancellation } from "@implab/core/Cancellation";
-import { delay } from "@implab/core/safe";
-import { test } from "./TestTraits";
-
-test("standalone cancellation", async t => {
-
- let doCancel: (e) => void;
-
- const ct = new Cancellation(cancel => {
- doCancel = cancel;
- });
-
- let counter = 0;
- const reason = "BILL";
-
- t.true(ct.isSupported(), "Cancellation must be supported");
- t.false(ct.isRequested(), "Cancellation shouldn't be requested");
- ct.throwIfRequested();
- t.pass("The exception shouldn't be thrown unless the cancellation is requested");
-
- ct.register(() => counter++);
- t.equals(counter, 0, "counter should be zero");
-
- ct.register(() => counter++).destroy();
-
- doCancel(reason);
-
- t.true(ct.isRequested(), "Cancellation should be requested");
- t.equals(counter, 1, "The registered callback should be triggered");
-
- ct.register(() => counter++);
- t.equals(counter, 2, "The callback should be triggered immediately");
-
- let msg;
- ct.register(e => msg = e);
- t.equals(msg, reason, "The cancellation reason should be passed to callback");
-
- try {
- msg = null;
- ct.throwIfRequested();
- t.fail("The exception should be thrown");
- } catch (e) {
- msg = e;
- }
- t.equals(msg, reason, "The cancellation reason should be catched");
-});
-
-test("async cancellation", async t => {
-
- const ct = new Cancellation(cancel => {
- cancel("STOP!");
- });
-
- try {
- await delay(0, ct);
- t.fail("Should thow the exception");
- } catch (e) {
- t.equals(e, "STOP!", "Should throw the cancellation reason");
- }
-});
-
-test("cancel with external event", async t => {
- const ct = new Cancellation(cancel => {
- setTimeout(x => cancel("STOP!"), 0);
- });
-
- try {
- await delay(10000, ct);
- t.fail("Should thow the exception");
- } catch (e) {
- t.equals(e, "STOP!", "Should throw the cancellation reason");
- }
-});
-
-test("operation normal flow", async t => {
-
- let htimeout;
- const ct = new Cancellation(cancel => {
- htimeout = setTimeout(() => cancel("STOP!"), 1000);
- });
-
- try {
- await delay(0, ct);
- t.pass("Should pass");
- } finally {
- clearTimeout(htimeout);
- }
-});
diff --git a/src/test/ts/ContainerTests.ts b/src/test/ts/ContainerTests.ts
deleted file mode 100644
--- a/src/test/ts/ContainerTests.ts
+++ /dev/null
@@ -1,93 +0,0 @@
-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";
-import { ValueDescriptor } from "@implab/core/di/ValueDescriptor";
-import { Foo } from "./mock/Foo";
-import { Bar } from "./mock/Bar";
-import { isNull } from "@implab/core/safe";
-
-test("Container register/resolve tests", async t => {
- const container = new Container();
-
- const connection1 = "db://localhost";
-
- t.throws(
- () => container.register("bla-bla", "bla-bla"),
- "Do not allow to register anything other than descriptors"
- );
-
- t.doesNotThrow(
- () => container.register("connection", new ValueDescriptor(connection1)),
- "register ValueDescriptor"
- );
-
- t.equals(container.resolve("connection"), connection1, "resolve string value");
-
- t.doesNotThrow(
- () => container.register(
- "dbParams",
- new AggregateDescriptor({
- timeout: 10,
- connection: new ReferenceDescriptor({ name: "connection" })
- })
- ),
- "register AggregateDescriptor"
- );
-
- const dbParams = container.resolve("dbParams");
- t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
-});
-
-test("Container configure/resolve tests", async t => {
-
- const container = new Container();
-
- await container.configure({
- foo: {
- $type: Foo
- },
-
- box: {
- $type: Bar,
- params: {
- $dependency: "foo"
- }
- },
-
- bar: {
- $type: Bar,
- params: {
- db: {
- provider: {
- $dependency: "db"
- }
- }
- }
- }
- });
- t.pass("should configure from js object");
-
- const f1 = container.resolve("foo");
-
- t.assert(!isNull(f1), "foo should be not null");
-
- t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
-
-});
-
-test("Load configuration from module", async t => {
- const container = new Container();
-
- await container.configure("./mock/config1", { contextRequire: require });
- t.pass("The configuration should load");
-
- const f1 = container.resolve("foo");
-
- t.assert(!isNull(f1), "foo should be not null");
-
- const b1 = container.resolve("bar") as Bar;
-
- t.assert(!isNull(b1), "bar should not be null");
- t.assert(!isNull(b1.foo), "bar.foo should not be null");
-});
diff --git a/src/test/ts/ObservableTests.ts b/src/test/ts/ObservableTests.ts
deleted file mode 100644
--- a/src/test/ts/ObservableTests.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
-import { Observable } from "@implab/core/Observable";
-import { IObservable } from "@implab/core/interfaces";
-import { delay } from "@implab/core/safe";
-import { test } from "./TestTraits";
-
-const trace = TraceSource.get("ObservableTests");
-
-test("events sequence example", async t => {
-
- let events: IObservable;
-
- const done = new Promise(resolve => {
- events = new Observable(async (notify, fail, finish) => {
- for (let i = 0; i < 10; i++) {
- await delay(0);
- notify(i);
- }
- finish();
- resolve();
- });
- });
-
- let count = 0;
- let complete = false;
- events.on(x => count = count + x, null, () => complete = true);
-
- const first = await events.next();
-
- t.equals(first, 0, "the first event");
- t.false(complete, "the sequence is not complete");
-
- await done;
-
- t.equals(count, 45, "the summ of the evetns");
- t.true(complete, "the sequence is complete");
-});
-
-test("event sequence termination", async t => {
- let events: IObservable;
-
- const done = new Promise(resolve => {
- events = new Observable(async (notify, fail, complete) => {
- await delay(0);
- notify(1);
- complete();
- notify(2);
- complete();
- fail("Sequence terminated");
- resolve();
- });
- });
-
- let count = 0;
- events.on(() => {}, e => count++, () => count++);
-
- const first = await events.next();
- t.equals(first, 1, "the first message");
- try {
- await events.next();
- t.fail("shoud throw an exception");
- } catch (e) {
- t.pass("the sequence is terminated");
- }
-
- await done;
-
- t.equals(count, 1, "the sequence must be terminated once");
-});
diff --git a/src/test/ts/SafeTests.ts b/src/test/ts/SafeTests.ts
deleted file mode 100644
--- a/src/test/ts/SafeTests.ts
+++ /dev/null
@@ -1,95 +0,0 @@
-import { Cancellation } from "@implab/core/Cancellation";
-import { first, isPromise, firstWhere, delay, nowait } from "@implab/core/safe";
-import { test } from "./TestTraits";
-
-test("await delay test", async t => {
- // schedule delay
- let resolved = false;
- let res = delay(0).then(() => resolved = true);
-
- t.false(resolved, "the delay should be async");
-
- await res;
- t.pass("await delay");
-
- // create cancellation token
- let cancel: (e?: any) => void;
- const ct = new Cancellation(c => cancel = c);
-
- // schedule delay
- resolved = false;
- res = delay(0, ct).then(() => resolved = true);
-
- t.false(resolved, "created delay with ct");
-
- // cancel
- cancel();
-
- try {
- await res;
- t.fail("the delay should fail when it is cancelled");
- } catch {
- t.pass("the delay is cancelled");
- }
-
- t.throws(() => {
- // try schedule delay after the cancellation is requested
- nowait(delay(0, ct));
- }, "Should throw if cancelled before start");
-});
-
-test("sequemce test", async t => {
- const sequence = ["a", "b", "c"];
- const empty = [];
-
- // synchronous tests
- t.equals(first(sequence), "a", "Should return the first element");
- t.equals(firstWhere(sequence, x => x === "b"), "b", "Should get the second element");
-
- let v: string;
- let e: Error;
- first(sequence, x => v = x);
- t.equal(v, "a", "The callback should be called for the first element");
- firstWhere(sequence, x => x === "b", x => v = x);
- t.equal(v, "b", "The callback should be called for the second element");
-
- t.throws(() => {
- first(empty);
- }, "Should throw when the sequence is empty");
-
- t.throws(() => {
- firstWhere(empty, x => x === "b");
- }, "Should throw when the sequence is empty");
-
- t.throws(() => {
- first(empty, x => v = x);
- }, "Should throw when the sequence is empty");
-
- t.throws(() => {
- firstWhere(empty, x => x === "b", x => v = x);
- }, "Should throw when the sequence is empty");
-
- t.throws(() => {
- firstWhere(sequence, x => x === "z");
- }, "Should throw when the element isn't found");
-
- t.throws(() => {
- firstWhere(sequence, x => x === "z", x => v = x);
- }, "Should throw when the element isn't found");
-
- first(empty, null, x => e = x);
- t.true(e, "The errorback should be called for the empty sequence");
-
- // async tests
- const asyncSequence = Promise.resolve(sequence);
- const asyncEmptySequence = Promise.resolve(empty);
-
- const promise = first(asyncSequence);
- t.true(isPromise(promise), "Should return promise");
-
- v = await promise;
- t.equal(v, "a", "Should return the first element");
-
- v = await new Promise(resolve => first(asyncSequence, resolve));
- t.equal(v, "a", "The callback should be called for the first element");
-});
diff --git a/src/test/ts/TestTraits.ts b/src/test/ts/TestTraits.ts
deleted file mode 100644
--- a/src/test/ts/TestTraits.ts
+++ /dev/null
@@ -1,74 +0,0 @@
-import { IObservable, ICancellation, IDestroyable } from "@implab/core/interfaces";
-import { Cancellation } from "@implab/core/Cancellation";
-import { TraceEvent, LogLevel, WarnLevel, DebugLevel, TraceSource } from "@implab/core/log/TraceSource";
-import * as tape from "tape";
-import { argumentNotNull, destroy } from "@implab/core/safe";
-
-export class TapeWriter implements IDestroyable {
- private readonly _tape: tape.Test;
-
- private readonly _subscriptions = new Array();
- private _destroyed;
-
- constructor(t: tape.Test) {
- argumentNotNull(t, "tape");
- this._tape = t;
- }
-
- writeEvents(source: IObservable, ct: ICancellation = Cancellation.none) {
- if (!this._destroyed) {
- const subscription = source.on(this.writeEvent.bind(this));
- if (ct.isSupported()) {
- ct.register(subscription.destroy.bind(subscription));
- }
- this._subscriptions.push(subscription);
- }
- }
-
- writeEvent(next: TraceEvent) {
- if (next.level >= DebugLevel) {
- this._tape.comment(`DEBUG ${next.source.id} ${next}`);
- } else if (next.level >= LogLevel) {
- this._tape.comment(`LOG ${next.source.id} ${next}`);
- } else if (next.level >= WarnLevel) {
- this._tape.comment(`WARN ${next.source.id} ${next}`);
- } else {
- this._tape.comment(`ERROR ${next.source.id} ${next}`);
- }
- }
-
- destroy() {
- this._subscriptions.forEach(destroy);
- }
-}
-
-export function test(name: string, cb: (t: tape.Test, trace: TraceSource) => any) {
- tape(name, async t => {
- const writer = new TapeWriter(t);
-
- // this trace is not announced through the TraceSource global registry
- const trace = new TraceSource(name);
- trace.level = DebugLevel;
- writer.writeEvents(trace.events);
-
- const h = TraceSource.on(ts => {
- ts.level = DebugLevel;
- writer.writeEvents(ts.events);
- });
-
- try {
- await cb(t, trace);
- } catch (e) {
-
- // verbose error information
- // tslint:disable-next-line
- console.error(e);
- t.fail(e);
-
- } finally {
- t.end();
- destroy(writer);
- destroy(h);
- }
- });
-}
diff --git a/src/test/ts/TextTests.ts b/src/test/ts/TextTests.ts
deleted file mode 100644
--- a/src/test/ts/TextTests.ts
+++ /dev/null
@@ -1,86 +0,0 @@
-import { StringBuilder } from "@implab/core/text/StringBuilder";
-import { test } from "./TestTraits";
-import { MockConsole } from "./mock/MockConsole";
-import { ConsoleWriter } from "@implab/core/log/ConsoleWriter";
-
-test("String builder", async t => {
- const sb = new StringBuilder();
-
- sb.write("hello");
- t.equals(sb.toString(), "hello", "Write simple text");
-
- sb.write(", ");
- sb.write("world!");
- t.equals(sb.toString(), "hello, world!", "Append text");
-
- sb.clear();
- t.equals(sb.toString(), "", "Clear");
-
- sb.write(1);
- t.equals(sb.toString(), "1", "Write number");
-
- sb.clear();
- sb.writeValue(0.123);
- t.equals(sb.toString(), "0.123", "Format number");
-
- sb.clear();
- sb.writeValue(new Date("2019-01-02T00:00:00.000Z"));
- t.equals(sb.toString(), "2019-01-02T00:00:00.000Z", "Format date (ISO)");
-
- sb.clear();
- sb.write("{0}", "hello");
- t.equals(sb.toString(), "hello", "Simple format text");
-
- sb.write(", {0}!", "world");
- t.equals(sb.toString(), "hello, world!", "Append formatted text");
-
- sb.clear();
- sb.write("abc: {0:json}; {0.length}; {0.1} {{olo}}", ["a", "b", "c"]);
- t.equals(sb.toString(), 'abc: [\n "a",\n "b",\n "c"\n]; 3; b {olo}', "Format string with spec");
-
- sb.clear();
- t.throws(() => sb.write("}", 0), "Should die on bad format: '}'");
- t.throws(() => sb.write("{", 0), "Should die on bad format: '{'");
- t.throws(() => sb.write("{}", 0), "Should die on bad format: '{}'");
- t.throws(() => sb.write("{:}", 0), "Should die on bad format: '{:}'");
- t.throws(() => sb.write("{{0}", 0), "Should die on bad format: '{{0}'");
-
-});
-
-test("ConsoleWriter", t => {
- const mockConsole = new MockConsole();
- const writer = new ConsoleWriter(mockConsole);
-
- writer.setLogLevel("log");
-
- writer.writeLine("Hello, world!");
-
- t.equals(mockConsole.getBuffer().length, 1, "One line should be written");
- t.equals(mockConsole.getBuffer()[0].level, "log", "LogLevel should be 'log'");
- t.deepEqual(mockConsole.getBuffer()[0].data, ["Hello, world!"], "The buffer should contain single string");
-
- mockConsole.clear();
- writer.setLogLevel("debug");
- writer.write("Bring ");
- writer.write("the {0}!", "light");
- t.equals(mockConsole.getBuffer().length, 0, "No line should be written");
- writer.writeLine();
-
- t.equals(mockConsole.getBuffer().length, 1, "One line should be written");
- t.equals(mockConsole.getBuffer()[0].level, "debug", "LogLevel should be 'log'");
- t.deepEqual(mockConsole.getBuffer()[0].data, ["Bring the light!"], "Should concatenate string parts together");
-
- mockConsole.clear();
- writer.writeLine("It's {0} o'clock, lets have some {1}!", { h: 5}, { title: "tee" });
-
- t.deepEqual(mockConsole.getBuffer()[0].data, ["It's ", { h: 5}, " o'clock, lets have some ", { title: "tee" }, "!"], "Non string parts should be psassed as is");
-
- mockConsole.clear();
- writer.writeLine("{0} or {1} to {2}", {i: 25}, 6, 4);
- t.deepEqual(mockConsole.getBuffer()[0].data, [{i: 25}, " or 6 to 4"], "25 or 6 to 4");
-
- mockConsole.clear();
- writer.writeLine("{0} or {1} to {2}! Let's have some {3}", 25, 6, 4, { product: "tee" } );
- t.deepEqual(mockConsole.getBuffer()[0].data, ["25 or 6 to 4! Let's have some ", { product: "tee" }], "Should handle many text chunks and object at the end");
-
-});
diff --git a/src/test/ts/TraceSourceTests.ts b/src/test/ts/TraceSourceTests.ts
deleted file mode 100644
--- a/src/test/ts/TraceSourceTests.ts
+++ /dev/null
@@ -1,89 +0,0 @@
-import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource";
-import * as tape from "tape";
-import { TapeWriter, test } from "./TestTraits";
-import { MockConsole } from "./mock/MockConsole";
-import { ConsoleLogger } from "@implab/core/log/writers/ConsoleLogger";
-import { ConsoleWriter } from "@implab/core/log/ConsoleWriter";
-
-const sourceId = "test/TraceSourceTests";
-
-tape("trace message", t => {
- const trace = TraceSource.get(sourceId);
-
- trace.level = DebugLevel;
-
- 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.toString(), "Hello, World!", "The message should be a formatted message");
-
- t.end();
- });
-
- trace.debug("Hello, {0}!", "World");
-
- h.destroy();
-});
-
-tape("trace event", t => {
- const trace = TraceSource.get(sourceId);
-
- trace.level = DebugLevel;
-
- const event = {
- name: "custom event"
- };
-
- 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.message, event, "The message should be the specified object");
-
- t.end();
- });
-
- trace.traceEvent(DebugLevel, event);
-
- h.destroy();
-});
-
-tape("tape comment writer", async t => {
- const writer = new TapeWriter(t);
-
- TraceSource.on(ts => {
- writer.writeEvents(ts.events);
- });
-
- const trace = TraceSource.get(sourceId);
- trace.level = DebugLevel;
-
- trace.log("Hello, {0}!", "World");
- trace.log("Multi\n line");
- trace.warn("Look at me!");
- trace.error("DIE!");
-
- writer.destroy();
-
- trace.log("You shouldn't see it!");
-
- t.comment("DONE");
-
- t.end();
-});
-
-test("console writer", (t, trace) => {
-
- const mockConsole = new MockConsole();
- const writer = new ConsoleWriter(mockConsole);
- const consoleLog = new ConsoleLogger(writer);
- consoleLog.writeEvents(trace.events);
-
- trace.log("Hello, world!");
- t.deepEqual(mockConsole.getLine(0), ["console writer: Hello, world!"], "Log one string");
-
- trace.log({ foo: "bar" });
- t.deepEqual(mockConsole.getLine(1), ["console writer: ", { foo: "bar" }], "Log an object");
-
- trace.log("json: {0:json}", { foo: "bar" });
- t.deepEqual(mockConsole.getLine(2), ['console writer: json: {\n "foo": "bar"\n}'], "should convert to string substitutions with spec");
-});
diff --git a/src/test/ts/dummy.ts b/src/test/ts/dummy.ts
deleted file mode 100644
--- a/src/test/ts/dummy.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import * as tape from "tape";
-import { Uuid } from "@implab/core/Uuid";
-
-tape("simple", t => {
- t.pass("sync assert");
- setTimeout(() => {
- t.pass("async assert");
- t.comment(Uuid());
- t.ok(Uuid() !== Uuid());
- // end should be called after the last assertion
- t.end();
- }, 100);
-});
diff --git a/src/test/ts/mock/MockActivationController.ts b/src/test/ts/mock/MockActivationController.ts
--- a/src/test/ts/mock/MockActivationController.ts
+++ b/src/test/ts/mock/MockActivationController.ts
@@ -1,5 +1,5 @@
-import { IActivatable, ICancellation, IActivationController } from "@implab/core/interfaces";
-import { Cancellation } from "@implab/core/Cancellation";
+import { IActivatable, ICancellation, IActivationController } from "../interfaces";
+import { Cancellation } from "../Cancellation";
export class MockActivationController implements IActivationController {
diff --git a/src/test/ts/mock/MockConsole.ts b/src/test/ts/mock/MockConsole.ts
--- a/src/test/ts/mock/MockConsole.ts
+++ b/src/test/ts/mock/MockConsole.ts
@@ -1,4 +1,4 @@
-import {NullConsole} from "@implab/core/log/NullConsole";
+import {NullConsole} from "../log/NullConsole";
interface ConsoleLineData {
level: string;
diff --git a/src/test/ts/mock/SimpleActivatable.ts b/src/test/ts/mock/SimpleActivatable.ts
--- a/src/test/ts/mock/SimpleActivatable.ts
+++ b/src/test/ts/mock/SimpleActivatable.ts
@@ -1,5 +1,5 @@
-import { AsyncComponent } from "@implab/core/components/AsyncComponent";
-import { ActivatableMixin } from "@implab/core/components/ActivatableMixin";
+import { AsyncComponent } from "../components/AsyncComponent";
+import { ActivatableMixin } from "../components/ActivatableMixin";
export class SimpleActivatable extends ActivatableMixin(AsyncComponent) {
diff --git a/src/test/ts/tests/ActivatableTests.ts b/src/test/ts/tests/ActivatableTests.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/ActivatableTests.ts
@@ -0,0 +1,54 @@
+import { MockActivationController } from "../mock/MockActivationController";
+import { SimpleActivatable } from "../mock/SimpleActivatable";
+import { test } from "./TestTraits";
+
+test("simple activation", async t => {
+
+ const a = new SimpleActivatable();
+ t.false(a.isActive());
+
+ await a.activate();
+ t.true(a.isActive());
+
+ await a.deactivate();
+ t.false(a.isActive());
+});
+
+test("controller activation", async t => {
+
+ 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");
+ t.assert(a.getActivationController() == null, "the component doesn't have an activation controller by default");
+
+ t.comment("Active the component through the controller");
+ await c.activate(a);
+ t.true(a.isActive(), "The component should successfully activate");
+ t.equal(c.getActive(), a, "The controller should point to the activated component");
+ t.equal(a.getActivationController(), c, "The component should point to the controller");
+
+ t.comment("Deactive the component throug the controller");
+ await c.deactivate();
+
+ t.false(a.isActive(), "The component should successfully deactivate");
+ t.equal(c.getActive(), null, "The controller shouldn't point to any component");
+ t.equal(a.getActivationController(), c, "The componet should point to it's controller");
+});
+
+test("handle error in onActivating", async t => {
+ const a = new SimpleActivatable();
+
+ a.onActivating = async () => {
+ throw new Error("Should fail");
+ };
+
+ try {
+ await a.activate();
+ t.fail("activation should fail");
+ } catch {
+ }
+
+ t.false(a.isActive(), "the component should remain inactive");
+});
diff --git a/src/test/ts/tests/CancellationTests.ts b/src/test/ts/tests/CancellationTests.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/CancellationTests.ts
@@ -0,0 +1,88 @@
+import { Cancellation } from "../Cancellation";
+import { delay } from "../safe";
+import { test } from "./TestTraits";
+
+test("standalone cancellation", async t => {
+
+ let doCancel: (e) => void;
+
+ const ct = new Cancellation(cancel => {
+ doCancel = cancel;
+ });
+
+ let counter = 0;
+ const reason = "BILL";
+
+ t.true(ct.isSupported(), "Cancellation must be supported");
+ t.false(ct.isRequested(), "Cancellation shouldn't be requested");
+ ct.throwIfRequested();
+ t.pass("The exception shouldn't be thrown unless the cancellation is requested");
+
+ ct.register(() => counter++);
+ t.equals(counter, 0, "counter should be zero");
+
+ ct.register(() => counter++).destroy();
+
+ doCancel(reason);
+
+ t.true(ct.isRequested(), "Cancellation should be requested");
+ t.equals(counter, 1, "The registered callback should be triggered");
+
+ ct.register(() => counter++);
+ t.equals(counter, 2, "The callback should be triggered immediately");
+
+ let msg;
+ ct.register(e => msg = e);
+ t.equals(msg, reason, "The cancellation reason should be passed to callback");
+
+ try {
+ msg = null;
+ ct.throwIfRequested();
+ t.fail("The exception should be thrown");
+ } catch (e) {
+ msg = e;
+ }
+ t.equals(msg, reason, "The cancellation reason should be catched");
+});
+
+test("async cancellation", async t => {
+
+ const ct = new Cancellation(cancel => {
+ cancel("STOP!");
+ });
+
+ try {
+ await delay(0, ct);
+ t.fail("Should thow the exception");
+ } catch (e) {
+ t.equals(e, "STOP!", "Should throw the cancellation reason");
+ }
+});
+
+test("cancel with external event", async t => {
+ const ct = new Cancellation(cancel => {
+ setTimeout(x => cancel("STOP!"), 0);
+ });
+
+ try {
+ await delay(10000, ct);
+ t.fail("Should thow the exception");
+ } catch (e) {
+ t.equals(e, "STOP!", "Should throw the cancellation reason");
+ }
+});
+
+test("operation normal flow", async t => {
+
+ let htimeout;
+ const ct = new Cancellation(cancel => {
+ htimeout = setTimeout(() => cancel("STOP!"), 1000);
+ });
+
+ try {
+ await delay(0, ct);
+ t.pass("Should pass");
+ } finally {
+ clearTimeout(htimeout);
+ }
+});
diff --git a/src/test/ts/tests/ContainerTests.ts b/src/test/ts/tests/ContainerTests.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/ContainerTests.ts
@@ -0,0 +1,93 @@
+import { test } from "./TestTraits";
+import { Container } from "../di/Container";
+import { ReferenceDescriptor } from "../di/ReferenceDescriptor";
+import { AggregateDescriptor } from "../di/AggregateDescriptor";
+import { ValueDescriptor } from "../di/ValueDescriptor";
+import { Foo } from "../mock/Foo";
+import { Bar } from "../mock/Bar";
+import { isNull } from "../safe";
+
+test("Container register/resolve tests", async t => {
+ const container = new Container();
+
+ const connection1 = "db://localhost";
+
+ t.throws(
+ () => container.register("bla-bla", "bla-bla"),
+ "Do not allow to register anything other than descriptors"
+ );
+
+ t.doesNotThrow(
+ () => container.register("connection", new ValueDescriptor(connection1)),
+ "register ValueDescriptor"
+ );
+
+ t.equals(container.resolve("connection"), connection1, "resolve string value");
+
+ t.doesNotThrow(
+ () => container.register(
+ "dbParams",
+ new AggregateDescriptor({
+ timeout: 10,
+ connection: new ReferenceDescriptor({ name: "connection" })
+ })
+ ),
+ "register AggregateDescriptor"
+ );
+
+ const dbParams = container.resolve("dbParams");
+ t.equals(dbParams.connection, connection1, "should get string value 'dbParams.connection'");
+});
+
+test("Container configure/resolve tests", async t => {
+
+ const container = new Container();
+
+ await container.configure({
+ foo: {
+ $type: Foo
+ },
+
+ box: {
+ $type: Bar,
+ params: {
+ $dependency: "foo"
+ }
+ },
+
+ bar: {
+ $type: Bar,
+ params: {
+ db: {
+ provider: {
+ $dependency: "db"
+ }
+ }
+ }
+ }
+ });
+ t.pass("should configure from js object");
+
+ const f1 = container.resolve("foo");
+
+ t.assert(!isNull(f1), "foo should be not null");
+
+ t.throws(() => container.resolve("bar"), "should not resolve dependency 'db'");
+
+});
+
+test("Load configuration from module", async t => {
+ const container = new Container();
+
+ await container.configure("./mock/config1", { contextRequire: require });
+ t.pass("The configuration should load");
+
+ const f1 = container.resolve("foo");
+
+ t.assert(!isNull(f1), "foo should be not null");
+
+ const b1 = container.resolve("bar") as Bar;
+
+ t.assert(!isNull(b1), "bar should not be null");
+ t.assert(!isNull(b1.foo), "bar.foo should not be null");
+});
diff --git a/src/test/ts/tests/ObservableTests.ts b/src/test/ts/tests/ObservableTests.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/ObservableTests.ts
@@ -0,0 +1,69 @@
+import { TraceSource } from "../log/TraceSource";
+import { Observable } from "../Observable";
+import { IObservable } from "../interfaces";
+import { delay } from "../safe";
+import { test } from "./TestTraits";
+
+const trace = TraceSource.get("ObservableTests");
+
+test("events sequence example", async t => {
+
+ let events: IObservable;
+
+ const done = new Promise(resolve => {
+ events = new Observable(async (notify, fail, finish) => {
+ for (let i = 0; i < 10; i++) {
+ await delay(0);
+ notify(i);
+ }
+ finish();
+ resolve();
+ });
+ });
+
+ let count = 0;
+ let complete = false;
+ events.on(x => count = count + x, null, () => complete = true);
+
+ const first = await events.next();
+
+ t.equals(first, 0, "the first event");
+ t.false(complete, "the sequence is not complete");
+
+ await done;
+
+ t.equals(count, 45, "the summ of the evetns");
+ t.true(complete, "the sequence is complete");
+});
+
+test("event sequence termination", async t => {
+ let events: IObservable;
+
+ const done = new Promise(resolve => {
+ events = new Observable(async (notify, fail, complete) => {
+ await delay(0);
+ notify(1);
+ complete();
+ notify(2);
+ complete();
+ fail("Sequence terminated");
+ resolve();
+ });
+ });
+
+ let count = 0;
+ events.on(() => {}, e => count++, () => count++);
+
+ const first = await events.next();
+ t.equals(first, 1, "the first message");
+ try {
+ await events.next();
+ t.fail("shoud throw an exception");
+ } catch (e) {
+ t.pass("the sequence is terminated");
+ }
+
+ await done;
+
+ t.equals(count, 1, "the sequence must be terminated once");
+});
diff --git a/src/test/ts/tests/SafeTests.ts b/src/test/ts/tests/SafeTests.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/SafeTests.ts
@@ -0,0 +1,95 @@
+import { Cancellation } from "../Cancellation";
+import { first, isPromise, firstWhere, delay, nowait } from "../safe";
+import { test } from "./TestTraits";
+
+test("await delay test", async t => {
+ // schedule delay
+ let resolved = false;
+ let res = delay(0).then(() => resolved = true);
+
+ t.false(resolved, "the delay should be async");
+
+ await res;
+ t.pass("await delay");
+
+ // create cancellation token
+ let cancel: (e?: any) => void;
+ const ct = new Cancellation(c => cancel = c);
+
+ // schedule delay
+ resolved = false;
+ res = delay(0, ct).then(() => resolved = true);
+
+ t.false(resolved, "created delay with ct");
+
+ // cancel
+ cancel();
+
+ try {
+ await res;
+ t.fail("the delay should fail when it is cancelled");
+ } catch {
+ t.pass("the delay is cancelled");
+ }
+
+ t.throws(() => {
+ // try schedule delay after the cancellation is requested
+ nowait(delay(0, ct));
+ }, "Should throw if cancelled before start");
+});
+
+test("sequemce test", async t => {
+ const sequence = ["a", "b", "c"];
+ const empty = [];
+
+ // synchronous tests
+ t.equals(first(sequence), "a", "Should return the first element");
+ t.equals(firstWhere(sequence, x => x === "b"), "b", "Should get the second element");
+
+ let v: string;
+ let e: Error;
+ first(sequence, x => v = x);
+ t.equal(v, "a", "The callback should be called for the first element");
+ firstWhere(sequence, x => x === "b", x => v = x);
+ t.equal(v, "b", "The callback should be called for the second element");
+
+ t.throws(() => {
+ first(empty);
+ }, "Should throw when the sequence is empty");
+
+ t.throws(() => {
+ firstWhere(empty, x => x === "b");
+ }, "Should throw when the sequence is empty");
+
+ t.throws(() => {
+ first(empty, x => v = x);
+ }, "Should throw when the sequence is empty");
+
+ t.throws(() => {
+ firstWhere(empty, x => x === "b", x => v = x);
+ }, "Should throw when the sequence is empty");
+
+ t.throws(() => {
+ firstWhere(sequence, x => x === "z");
+ }, "Should throw when the element isn't found");
+
+ t.throws(() => {
+ firstWhere(sequence, x => x === "z", x => v = x);
+ }, "Should throw when the element isn't found");
+
+ first(empty, null, x => e = x);
+ t.true(e, "The errorback should be called for the empty sequence");
+
+ // async tests
+ const asyncSequence = Promise.resolve(sequence);
+ const asyncEmptySequence = Promise.resolve(empty);
+
+ const promise = first(asyncSequence);
+ t.true(isPromise(promise), "Should return promise");
+
+ v = await promise;
+ t.equal(v, "a", "Should return the first element");
+
+ v = await new Promise(resolve => first(asyncSequence, resolve));
+ t.equal(v, "a", "The callback should be called for the first element");
+});
diff --git a/src/test/ts/tests/TestTraits.ts b/src/test/ts/tests/TestTraits.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/TestTraits.ts
@@ -0,0 +1,74 @@
+import { IObservable, ICancellation, IDestroyable } from "../interfaces";
+import { Cancellation } from "../Cancellation";
+import { TraceEvent, LogLevel, WarnLevel, DebugLevel, TraceSource } from "../log/TraceSource";
+import * as tape from "tape";
+import { argumentNotNull, destroy } from "../safe";
+
+export class TapeWriter implements IDestroyable {
+ private readonly _tape: tape.Test;
+
+ private readonly _subscriptions = new Array();
+ private _destroyed;
+
+ constructor(t: tape.Test) {
+ argumentNotNull(t, "tape");
+ this._tape = t;
+ }
+
+ writeEvents(source: IObservable, ct: ICancellation = Cancellation.none) {
+ if (!this._destroyed) {
+ const subscription = source.on(this.writeEvent.bind(this));
+ if (ct.isSupported()) {
+ ct.register(subscription.destroy.bind(subscription));
+ }
+ this._subscriptions.push(subscription);
+ }
+ }
+
+ writeEvent(next: TraceEvent) {
+ if (next.level >= DebugLevel) {
+ this._tape.comment(`DEBUG ${next.source.id} ${next}`);
+ } else if (next.level >= LogLevel) {
+ this._tape.comment(`LOG ${next.source.id} ${next}`);
+ } else if (next.level >= WarnLevel) {
+ this._tape.comment(`WARN ${next.source.id} ${next}`);
+ } else {
+ this._tape.comment(`ERROR ${next.source.id} ${next}`);
+ }
+ }
+
+ destroy() {
+ this._subscriptions.forEach(destroy);
+ }
+}
+
+export function test(name: string, cb: (t: tape.Test, trace: TraceSource) => any) {
+ tape(name, async t => {
+ const writer = new TapeWriter(t);
+
+ // this trace is not announced through the TraceSource global registry
+ const trace = new TraceSource(name);
+ trace.level = DebugLevel;
+ writer.writeEvents(trace.events);
+
+ const h = TraceSource.on(ts => {
+ ts.level = DebugLevel;
+ writer.writeEvents(ts.events);
+ });
+
+ try {
+ await cb(t, trace);
+ } catch (e) {
+
+ // verbose error information
+ // tslint:disable-next-line
+ console.error(e);
+ t.fail(e);
+
+ } finally {
+ t.end();
+ destroy(writer);
+ destroy(h);
+ }
+ });
+}
diff --git a/src/test/ts/tests/TextTests.ts b/src/test/ts/tests/TextTests.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/TextTests.ts
@@ -0,0 +1,86 @@
+import { StringBuilder } from "../text/StringBuilder";
+import { test } from "./TestTraits";
+import { MockConsole } from "../mock/MockConsole";
+import { ConsoleWriter } from "../log/ConsoleWriter";
+
+test("String builder", async t => {
+ const sb = new StringBuilder();
+
+ sb.write("hello");
+ t.equals(sb.toString(), "hello", "Write simple text");
+
+ sb.write(", ");
+ sb.write("world!");
+ t.equals(sb.toString(), "hello, world!", "Append text");
+
+ sb.clear();
+ t.equals(sb.toString(), "", "Clear");
+
+ sb.write(1);
+ t.equals(sb.toString(), "1", "Write number");
+
+ sb.clear();
+ sb.writeValue(0.123);
+ t.equals(sb.toString(), "0.123", "Format number");
+
+ sb.clear();
+ sb.writeValue(new Date("2019-01-02T00:00:00.000Z"));
+ t.equals(sb.toString(), "2019-01-02T00:00:00.000Z", "Format date (ISO)");
+
+ sb.clear();
+ sb.write("{0}", "hello");
+ t.equals(sb.toString(), "hello", "Simple format text");
+
+ sb.write(", {0}!", "world");
+ t.equals(sb.toString(), "hello, world!", "Append formatted text");
+
+ sb.clear();
+ sb.write("abc: {0:json}; {0.length}; {0.1} {{olo}}", ["a", "b", "c"]);
+ t.equals(sb.toString(), 'abc: [\n "a",\n "b",\n "c"\n]; 3; b {olo}', "Format string with spec");
+
+ sb.clear();
+ t.throws(() => sb.write("}", 0), "Should die on bad format: '}'");
+ t.throws(() => sb.write("{", 0), "Should die on bad format: '{'");
+ t.throws(() => sb.write("{}", 0), "Should die on bad format: '{}'");
+ t.throws(() => sb.write("{:}", 0), "Should die on bad format: '{:}'");
+ t.throws(() => sb.write("{{0}", 0), "Should die on bad format: '{{0}'");
+
+});
+
+test("ConsoleWriter", t => {
+ const mockConsole = new MockConsole();
+ const writer = new ConsoleWriter(mockConsole);
+
+ writer.setLogLevel("log");
+
+ writer.writeLine("Hello, world!");
+
+ t.equals(mockConsole.getBuffer().length, 1, "One line should be written");
+ t.equals(mockConsole.getBuffer()[0].level, "log", "LogLevel should be 'log'");
+ t.deepEqual(mockConsole.getBuffer()[0].data, ["Hello, world!"], "The buffer should contain single string");
+
+ mockConsole.clear();
+ writer.setLogLevel("debug");
+ writer.write("Bring ");
+ writer.write("the {0}!", "light");
+ t.equals(mockConsole.getBuffer().length, 0, "No line should be written");
+ writer.writeLine();
+
+ t.equals(mockConsole.getBuffer().length, 1, "One line should be written");
+ t.equals(mockConsole.getBuffer()[0].level, "debug", "LogLevel should be 'log'");
+ t.deepEqual(mockConsole.getBuffer()[0].data, ["Bring the light!"], "Should concatenate string parts together");
+
+ mockConsole.clear();
+ writer.writeLine("It's {0} o'clock, lets have some {1}!", { h: 5}, { title: "tee" });
+
+ t.deepEqual(mockConsole.getBuffer()[0].data, ["It's ", { h: 5}, " o'clock, lets have some ", { title: "tee" }, "!"], "Non string parts should be psassed as is");
+
+ mockConsole.clear();
+ writer.writeLine("{0} or {1} to {2}", {i: 25}, 6, 4);
+ t.deepEqual(mockConsole.getBuffer()[0].data, [{i: 25}, " or 6 to 4"], "25 or 6 to 4");
+
+ mockConsole.clear();
+ writer.writeLine("{0} or {1} to {2}! Let's have some {3}", 25, 6, 4, { product: "tee" } );
+ t.deepEqual(mockConsole.getBuffer()[0].data, ["25 or 6 to 4! Let's have some ", { product: "tee" }], "Should handle many text chunks and object at the end");
+
+});
diff --git a/src/test/ts/tests/TraceSourceTests.ts b/src/test/ts/tests/TraceSourceTests.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/TraceSourceTests.ts
@@ -0,0 +1,89 @@
+import { TraceSource, DebugLevel } from "../log/TraceSource";
+import * as tape from "tape";
+import { TapeWriter, test } from "./TestTraits";
+import { MockConsole } from "../mock/MockConsole";
+import { ConsoleLogger } from "../log/writers/ConsoleLogger";
+import { ConsoleWriter } from "../log/ConsoleWriter";
+
+const sourceId = "test/TraceSourceTests";
+
+tape("trace message", t => {
+ const trace = TraceSource.get(sourceId);
+
+ trace.level = DebugLevel;
+
+ 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.toString(), "Hello, World!", "The message should be a formatted message");
+
+ t.end();
+ });
+
+ trace.debug("Hello, {0}!", "World");
+
+ h.destroy();
+});
+
+tape("trace event", t => {
+ const trace = TraceSource.get(sourceId);
+
+ trace.level = DebugLevel;
+
+ const event = {
+ name: "custom event"
+ };
+
+ 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.message, event, "The message should be the specified object");
+
+ t.end();
+ });
+
+ trace.traceEvent(DebugLevel, event);
+
+ h.destroy();
+});
+
+tape("tape comment writer", async t => {
+ const writer = new TapeWriter(t);
+
+ TraceSource.on(ts => {
+ writer.writeEvents(ts.events);
+ });
+
+ const trace = TraceSource.get(sourceId);
+ trace.level = DebugLevel;
+
+ trace.log("Hello, {0}!", "World");
+ trace.log("Multi\n line");
+ trace.warn("Look at me!");
+ trace.error("DIE!");
+
+ writer.destroy();
+
+ trace.log("You shouldn't see it!");
+
+ t.comment("DONE");
+
+ t.end();
+});
+
+test("console writer", (t, trace) => {
+
+ const mockConsole = new MockConsole();
+ const writer = new ConsoleWriter(mockConsole);
+ const consoleLog = new ConsoleLogger(writer);
+ consoleLog.writeEvents(trace.events);
+
+ trace.log("Hello, world!");
+ t.deepEqual(mockConsole.getLine(0), ["console writer: Hello, world!"], "Log one string");
+
+ trace.log({ foo: "bar" });
+ t.deepEqual(mockConsole.getLine(1), ["console writer: ", { foo: "bar" }], "Log an object");
+
+ trace.log("json: {0:json}", { foo: "bar" });
+ t.deepEqual(mockConsole.getLine(2), ['console writer: json: {\n "foo": "bar"\n}'], "should convert to string substitutions with spec");
+});
diff --git a/src/test/ts/tests/dummy.ts b/src/test/ts/tests/dummy.ts
new file mode 100644
--- /dev/null
+++ b/src/test/ts/tests/dummy.ts
@@ -0,0 +1,13 @@
+import * as tape from "tape";
+import { Uuid } from "../Uuid";
+
+tape("simple", t => {
+ t.pass("sync assert");
+ setTimeout(() => {
+ t.pass("async assert");
+ t.comment(Uuid());
+ t.ok(Uuid() !== Uuid());
+ // end should be called after the last assertion
+ t.end();
+ }, 100);
+});
diff --git a/src/test/tsconfig.json b/src/test/tsconfig.json
--- a/src/test/tsconfig.json
+++ b/src/test/tsconfig.json
@@ -3,11 +3,10 @@
"compilerOptions": {
"rootDir": "ts",
"baseUrl": ".",
- "paths": {
- "@implab/core/*": [
- "../../build/dist/*"
- ]
- }
+ "rootDirs": [
+ "ts",
+ "../main/ts"
+ ]
},
"include" : [
"ts/**/*.ts"