| @@ -0,0 +1,100 | |||||
|
|
1 | import { TextWriterBase } from "../text/TextWriterBase"; | |||
|
|
2 | import { isNull, isNullOrEmptyString, isPrimitive } from "../safe"; | |||
|
|
3 | import { NullConsole } from "./NullConsole"; | |||
|
|
4 | ||||
|
|
5 | interface LogConsole { | |||
|
|
6 | debug(...args: any[]): void; | |||
|
|
7 | log(...args: any[]): void; | |||
|
|
8 | warn(...args: any[]): void; | |||
|
|
9 | error(...args: any[]): void; | |||
|
|
10 | } | |||
|
|
11 | ||||
|
|
12 | function hasConsole() { | |||
|
|
13 | try { | |||
|
|
14 | // tslint:disable-next-line:no-console | |||
|
|
15 | return (typeof console !== "undefined" && typeof console.log === "function"); | |||
|
|
16 | } catch { | |||
|
|
17 | return false; | |||
|
|
18 | } | |||
|
|
19 | } | |||
|
|
20 | ||||
|
|
21 | function getConsole() { | |||
|
|
22 | return hasConsole() ? console : NullConsole.instance; | |||
|
|
23 | } | |||
|
|
24 | ||||
|
|
25 | export class ConsoleWriter extends TextWriterBase { | |||
|
|
26 | static readonly default = new ConsoleWriter(getConsole()); | |||
|
|
27 | ||||
|
|
28 | private _buffer: any[]; | |||
|
|
29 | ||||
|
|
30 | private _out: LogConsole; | |||
|
|
31 | private _level: keyof LogConsole; | |||
|
|
32 | ||||
|
|
33 | constructor(out?: LogConsole) { | |||
|
|
34 | super(); | |||
|
|
35 | this._out = out || NullConsole.instance; | |||
|
|
36 | this._buffer = []; | |||
|
|
37 | this._level = "log"; | |||
|
|
38 | } | |||
|
|
39 | ||||
|
|
40 | getLogLevel() { | |||
|
|
41 | return this._level; | |||
|
|
42 | } | |||
|
|
43 | ||||
|
|
44 | setLogLevel(level: keyof LogConsole) { | |||
|
|
45 | this._level = level; | |||
|
|
46 | } | |||
|
|
47 | ||||
|
|
48 | /** Flushes the buffer to the console | |||
|
|
49 | */ | |||
|
|
50 | writeNewLine() { | |||
|
|
51 | // group text chunks together, and let objects as is | |||
|
|
52 | // ['a', 'b', {foo: 'bar'}, 'c', 'd'] -> ['ab', {foo: 'bar'}, 'cd'] | |||
|
|
53 | // this will prevent from additional spaces to occur in the console | |||
|
|
54 | // ['a', 'b'] will be printed as 'a b' rather then 'ab'. | |||
|
|
55 | ||||
|
|
56 | // console.log("writeLine", this._buffer); | |||
|
|
57 | ||||
|
|
58 | let offset = 0; | |||
|
|
59 | const args = []; | |||
|
|
60 | this._buffer.forEach((v, i) => { | |||
|
|
61 | if (!isPrimitive(v)) { | |||
|
|
62 | if (offset < i) | |||
|
|
63 | args.push(i - offset > 1 ? this._buffer.slice(offset, i).join("") : this._buffer[offset]); | |||
|
|
64 | args.push(v); | |||
|
|
65 | offset = i + 1; | |||
|
|
66 | } | |||
|
|
67 | }); | |||
|
|
68 | if (offset < this._buffer.length) | |||
|
|
69 | args.push(this._buffer.slice(offset).join("")); | |||
|
|
70 | ||||
|
|
71 | // console.log("WriteLine", args); | |||
|
|
72 | ||||
|
|
73 | this._out[this._level].apply(this._out, args); | |||
|
|
74 | ||||
|
|
75 | this._buffer = []; | |||
|
|
76 | } | |||
|
|
77 | ||||
|
|
78 | /** Adds a text chunk to the buffer. Buffer contents will be flushed when | |||
|
|
79 | * the end of line will be printed. | |||
|
|
80 | * | |||
|
|
81 | * @param text The text to be added to the buffer. | |||
|
|
82 | */ | |||
|
|
83 | writeText(text: string) { | |||
|
|
84 | this._buffer.push(text); | |||
|
|
85 | } | |||
|
|
86 | ||||
|
|
87 | /** Wrotes the specified value to the buffer. | |||
|
|
88 | * | |||
|
|
89 | * @param value The value to be added to the buffer | |||
|
|
90 | * @param spec The instructions how to format the value, is this parameter | |||
|
|
91 | * is ommited the raw value will be added to the buffer and | |||
|
|
92 | * passed directly to the console out. | |||
|
|
93 | */ | |||
|
|
94 | writeValue(value: any, spec?: string) { | |||
|
|
95 | if (isNullOrEmptyString(spec)) | |||
|
|
96 | this._buffer.push(value); | |||
|
|
97 | else | |||
|
|
98 | super.writeValue(value); | |||
|
|
99 | } | |||
|
|
100 | } | |||
| @@ -0,0 +1,52 | |||||
|
|
1 | export class NullConsole { | |||
|
|
2 | static readonly instance = new NullConsole(); | |||
|
|
3 | ||||
|
|
4 | assert(condition?: boolean, message?: string, ...data: any[]): void { | |||
|
|
5 | } | |||
|
|
6 | clear(): void { | |||
|
|
7 | } | |||
|
|
8 | count(label?: string): void { | |||
|
|
9 | } | |||
|
|
10 | debug(message?: any, ...optionalParams: any[]): void { | |||
|
|
11 | } | |||
|
|
12 | dir(value?: any, ...optionalParams: any[]): void { | |||
|
|
13 | } | |||
|
|
14 | dirxml(value: any): void { | |||
|
|
15 | } | |||
|
|
16 | error(message?: any, ...optionalParams: any[]): void { | |||
|
|
17 | } | |||
|
|
18 | exception(message?: string, ...optionalParams: any[]): void { | |||
|
|
19 | } | |||
|
|
20 | group(groupTitle?: string, ...optionalParams: any[]): void { | |||
|
|
21 | } | |||
|
|
22 | groupCollapsed(groupTitle?: string, ...optionalParams: any[]): void { | |||
|
|
23 | } | |||
|
|
24 | groupEnd(): void { | |||
|
|
25 | } | |||
|
|
26 | info(message?: any, ...optionalParams: any[]): void { | |||
|
|
27 | } | |||
|
|
28 | log(message?: any, ...optionalParams: any[]): void { | |||
|
|
29 | } | |||
|
|
30 | markTimeline(label?: string): void { | |||
|
|
31 | } | |||
|
|
32 | profile(reportName?: string): void { | |||
|
|
33 | } | |||
|
|
34 | profileEnd(reportName?: string): void { | |||
|
|
35 | } | |||
|
|
36 | table(...tabularData: any[]): void { | |||
|
|
37 | } | |||
|
|
38 | time(label?: string): void { | |||
|
|
39 | } | |||
|
|
40 | timeEnd(label?: string): void { | |||
|
|
41 | } | |||
|
|
42 | timeStamp(label?: string): void { | |||
|
|
43 | } | |||
|
|
44 | timeline(label?: string): void { | |||
|
|
45 | } | |||
|
|
46 | timelineEnd(label?: string): void { | |||
|
|
47 | } | |||
|
|
48 | trace(message?: any, ...optionalParams: any[]): void { | |||
|
|
49 | } | |||
|
|
50 | warn(message?: any, ...optionalParams: any[]): void { | |||
|
|
51 | } | |||
|
|
52 | } | |||
| @@ -0,0 +1,20 | |||||
|
|
1 | import { TraceEvent, TraceSource } from "./TraceSource"; | |||
|
|
2 | import { format } from "../text/StringBuilder"; | |||
|
|
3 | ||||
|
|
4 | export class TraceEventData implements TraceEvent { | |||
|
|
5 | source: TraceSource; | |||
|
|
6 | level: number; | |||
|
|
7 | message: any; | |||
|
|
8 | args?: any[]; | |||
|
|
9 | ||||
|
|
10 | constructor(source: TraceSource, level: number, message: any, args: any[]) { | |||
|
|
11 | this.source = source; | |||
|
|
12 | this.level = level; | |||
|
|
13 | this.message = message; | |||
|
|
14 | this.args = args; | |||
|
|
15 | } | |||
|
|
16 | ||||
|
|
17 | toString() { | |||
|
|
18 | return format(this.message, ...this.args); | |||
|
|
19 | } | |||
|
|
20 | } | |||
| @@ -0,0 +1,41 | |||||
|
|
1 | import { IObservable, IDestroyable, ICancellation } from "../../interfaces"; | |||
|
|
2 | import { TraceEvent, LogLevel, WarnLevel, DebugLevel } from "../TraceSource"; | |||
|
|
3 | import { Cancellation } from "../../Cancellation"; | |||
|
|
4 | import { destroy, argumentNotNull } from "../../safe"; | |||
|
|
5 | import { ConsoleWriter } from "../ConsoleWriter"; | |||
|
|
6 | ||||
|
|
7 | export class ConsoleLogger implements IDestroyable { | |||
|
|
8 | private readonly _subscriptions = new Array<IDestroyable>(); | |||
|
|
9 | private readonly _writer: ConsoleWriter; | |||
|
|
10 | ||||
|
|
11 | constructor(writer = ConsoleWriter.default) { | |||
|
|
12 | argumentNotNull(writer, "writer"); | |||
|
|
13 | this._writer = writer; | |||
|
|
14 | } | |||
|
|
15 | ||||
|
|
16 | writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) { | |||
|
|
17 | const subscription = source.on(this.writeEvent.bind(this)); | |||
|
|
18 | if (ct.isSupported()) { | |||
|
|
19 | ct.register(subscription.destroy.bind(subscription)); | |||
|
|
20 | } | |||
|
|
21 | this._subscriptions.push(subscription); | |||
|
|
22 | } | |||
|
|
23 | ||||
|
|
24 | writeEvent(next: TraceEvent) { | |||
|
|
25 | if (next.level >= DebugLevel) { | |||
|
|
26 | this._writer.setLogLevel("debug"); | |||
|
|
27 | } else if (next.level >= LogLevel) { | |||
|
|
28 | this._writer.setLogLevel("log"); | |||
|
|
29 | } else if (next.level >= WarnLevel) { | |||
|
|
30 | this._writer.setLogLevel("warn"); | |||
|
|
31 | } else { | |||
|
|
32 | this._writer.setLogLevel("error"); | |||
|
|
33 | } | |||
|
|
34 | this._writer.write("{0}: ", next.source.id); | |||
|
|
35 | this._writer.writeLine(next.message, ...next.args); | |||
|
|
36 | } | |||
|
|
37 | ||||
|
|
38 | destroy() { | |||
|
|
39 | this._subscriptions.forEach(destroy); | |||
|
|
40 | } | |||
|
|
41 | } | |||
| @@ -0,0 +1,30 | |||||
|
|
1 | import { isPrimitive, isNull } from "../safe"; | |||
|
|
2 | ||||
|
|
3 | export class Converter { | |||
|
|
4 | static readonly default = new Converter(); | |||
|
|
5 | ||||
|
|
6 | convert(value: any, pattern: string) { | |||
|
|
7 | if (pattern && pattern.toLocaleLowerCase() === "json") { | |||
|
|
8 | const seen = []; | |||
|
|
9 | return JSON.stringify(value, (k, v) => { | |||
|
|
10 | if (!isPrimitive(v)) { | |||
|
|
11 | const id = seen.indexOf(v); | |||
|
|
12 | if (id >= 0) | |||
|
|
13 | return "@ref-" + id; | |||
|
|
14 | else { | |||
|
|
15 | seen.push(v); | |||
|
|
16 | return v; | |||
|
|
17 | } | |||
|
|
18 | } else { | |||
|
|
19 | return v; | |||
|
|
20 | } | |||
|
|
21 | }, 2); | |||
|
|
22 | } else if (isNull(value)) { | |||
|
|
23 | return ""; | |||
|
|
24 | } else if (value instanceof Date) { | |||
|
|
25 | return value.toISOString(); | |||
|
|
26 | } else { | |||
|
|
27 | return value.toString(); | |||
|
|
28 | } | |||
|
|
29 | } | |||
|
|
30 | } | |||
| @@ -0,0 +1,19 | |||||
|
|
1 | import { TextWriter } from "../interfaces"; | |||
|
|
2 | ||||
|
|
3 | export class NullTextWriter implements TextWriter { | |||
|
|
4 | static readonly instance = new NullTextWriter(); | |||
|
|
5 | ||||
|
|
6 | write(obj: any): void; | |||
|
|
7 | write(format: string, ...args: any[]): void; | |||
|
|
8 | write() { | |||
|
|
9 | } | |||
|
|
10 | ||||
|
|
11 | writeLine(obj: any): void; | |||
|
|
12 | writeLine(format: string, ...args: any[]): void; | |||
|
|
13 | writeLine() { | |||
|
|
14 | } | |||
|
|
15 | ||||
|
|
16 | writeValue(value: any, spec?: string): void; | |||
|
|
17 | writeValue() { | |||
|
|
18 | } | |||
|
|
19 | } | |||
| @@ -0,0 +1,48 | |||||
|
|
1 | import { TextWriter } from "../interfaces"; | |||
|
|
2 | import { FormatCompiler } from "./FormatCompiler"; | |||
|
|
3 | import { isString, argumentNotNull } from "../safe"; | |||
|
|
4 | import { Converter } from "./Converter"; | |||
|
|
5 | ||||
|
|
6 | const compiler = new FormatCompiler(); | |||
|
|
7 | ||||
|
|
8 | export abstract class TextWriterBase implements TextWriter { | |||
|
|
9 | private _converter: Converter; | |||
|
|
10 | ||||
|
|
11 | constructor(converter = Converter.default) { | |||
|
|
12 | argumentNotNull(converter, "converter"); | |||
|
|
13 | this._converter = converter; | |||
|
|
14 | } | |||
|
|
15 | ||||
|
|
16 | writeNewLine() { | |||
|
|
17 | this.writeValue("\n"); | |||
|
|
18 | } | |||
|
|
19 | ||||
|
|
20 | write(obj: any): void; | |||
|
|
21 | write(format: string, ...args: any[]): void; | |||
|
|
22 | write(format: any, ...args: any[]): void { | |||
|
|
23 | if (args.length) { | |||
|
|
24 | const compiled = compiler.compile(format); | |||
|
|
25 | compiled(this, args); | |||
|
|
26 | } else { | |||
|
|
27 | this.writeValue(format); | |||
|
|
28 | } | |||
|
|
29 | } | |||
|
|
30 | ||||
|
|
31 | writeLine(obj?: any): void; | |||
|
|
32 | writeLine(format: string, ...args: any[]): void; | |||
|
|
33 | writeLine(): void { | |||
|
|
34 | if (arguments.length) | |||
|
|
35 | this.write.apply(this, arguments); | |||
|
|
36 | this.writeNewLine(); | |||
|
|
37 | } | |||
|
|
38 | ||||
|
|
39 | writeValue(value: any, spec?: string): void { | |||
|
|
40 | this.writeText( | |||
|
|
41 | isString(value) ? | |||
|
|
42 | value : | |||
|
|
43 | this._converter.convert(value, spec) | |||
|
|
44 | ); | |||
|
|
45 | } | |||
|
|
46 | ||||
|
|
47 | abstract writeText(text: string); | |||
|
|
48 | } | |||
| @@ -0,0 +1,86 | |||||
|
|
1 | import { StringBuilder } from "@implab/core/text/StringBuilder"; | |||
|
|
2 | import { test } from "./TestTraits"; | |||
|
|
3 | import { MockConsole } from "./mock/MockConsole"; | |||
|
|
4 | import { ConsoleWriter } from "@implab/core/log/ConsoleWriter"; | |||
|
|
5 | ||||
|
|
6 | test("String builder", async t => { | |||
|
|
7 | const sb = new StringBuilder(); | |||
|
|
8 | ||||
|
|
9 | sb.write("hello"); | |||
|
|
10 | t.equals(sb.toString(), "hello", "Write simple text"); | |||
|
|
11 | ||||
|
|
12 | sb.write(", "); | |||
|
|
13 | sb.write("world!"); | |||
|
|
14 | t.equals(sb.toString(), "hello, world!", "Append text"); | |||
|
|
15 | ||||
|
|
16 | sb.clear(); | |||
|
|
17 | t.equals(sb.toString(), "", "Clear"); | |||
|
|
18 | ||||
|
|
19 | sb.write(1); | |||
|
|
20 | t.equals(sb.toString(), "1", "Write number"); | |||
|
|
21 | ||||
|
|
22 | sb.clear(); | |||
|
|
23 | sb.writeValue(0.123); | |||
|
|
24 | t.equals(sb.toString(), "0.123", "Format number"); | |||
|
|
25 | ||||
|
|
26 | sb.clear(); | |||
|
|
27 | sb.writeValue(new Date("2019-01-02T00:00:00.000Z")); | |||
|
|
28 | t.equals(sb.toString(), "2019-01-02T00:00:00.000Z", "Format date (ISO)"); | |||
|
|
29 | ||||
|
|
30 | sb.clear(); | |||
|
|
31 | sb.write("{0}", "hello"); | |||
|
|
32 | t.equals(sb.toString(), "hello", "Simple format text"); | |||
|
|
33 | ||||
|
|
34 | sb.write(", {0}!", "world"); | |||
|
|
35 | t.equals(sb.toString(), "hello, world!", "Append formatted text"); | |||
|
|
36 | ||||
|
|
37 | sb.clear(); | |||
|
|
38 | sb.write("abc: {0:json}; {0.length}; {0.1} {{olo}}", ["a", "b", "c"]); | |||
|
|
39 | t.equals(sb.toString(), 'abc: [\n "a",\n "b",\n "c"\n]; 3; b {olo}', "Format string with spec"); | |||
|
|
40 | ||||
|
|
41 | sb.clear(); | |||
|
|
42 | t.throws(() => sb.write("}", 0), "Should die on bad format: '}'"); | |||
|
|
43 | t.throws(() => sb.write("{", 0), "Should die on bad format: '{'"); | |||
|
|
44 | t.throws(() => sb.write("{}", 0), "Should die on bad format: '{}'"); | |||
|
|
45 | t.throws(() => sb.write("{:}", 0), "Should die on bad format: '{:}'"); | |||
|
|
46 | t.throws(() => sb.write("{{0}", 0), "Should die on bad format: '{{0}'"); | |||
|
|
47 | ||||
|
|
48 | }); | |||
|
|
49 | ||||
|
|
50 | test("ConsoleWriter", t => { | |||
|
|
51 | const mockConsole = new MockConsole(); | |||
|
|
52 | const writer = new ConsoleWriter(mockConsole); | |||
|
|
53 | ||||
|
|
54 | writer.setLogLevel("log"); | |||
|
|
55 | ||||
|
|
56 | writer.writeLine("Hello, world!"); | |||
|
|
57 | ||||
|
|
58 | t.equals(mockConsole.getBuffer().length, 1, "One line should be written"); | |||
|
|
59 | t.equals(mockConsole.getBuffer()[0].level, "log", "LogLevel should be 'log'"); | |||
|
|
60 | t.deepEqual(mockConsole.getBuffer()[0].data, ["Hello, world!"], "The buffer should contain single string"); | |||
|
|
61 | ||||
|
|
62 | mockConsole.clear(); | |||
|
|
63 | writer.setLogLevel("debug"); | |||
|
|
64 | writer.write("Bring "); | |||
|
|
65 | writer.write("the {0}!", "light"); | |||
|
|
66 | t.equals(mockConsole.getBuffer().length, 0, "No line should be written"); | |||
|
|
67 | writer.writeLine(); | |||
|
|
68 | ||||
|
|
69 | t.equals(mockConsole.getBuffer().length, 1, "One line should be written"); | |||
|
|
70 | t.equals(mockConsole.getBuffer()[0].level, "debug", "LogLevel should be 'log'"); | |||
|
|
71 | t.deepEqual(mockConsole.getBuffer()[0].data, ["Bring the light!"], "Should concatenate string parts together"); | |||
|
|
72 | ||||
|
|
73 | mockConsole.clear(); | |||
|
|
74 | writer.writeLine("It's {0} o'clock, lets have some {1}!", { h: 5}, { title: "tee" }); | |||
|
|
75 | ||||
|
|
76 | 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"); | |||
|
|
77 | ||||
|
|
78 | mockConsole.clear(); | |||
|
|
79 | writer.writeLine("{0} or {1} to {2}", {i: 25}, 6, 4); | |||
|
|
80 | t.deepEqual(mockConsole.getBuffer()[0].data, [{i: 25}, " or 6 to 4"], "25 or 6 to 4"); | |||
|
|
81 | ||||
|
|
82 | mockConsole.clear(); | |||
|
|
83 | writer.writeLine("{0} or {1} to {2}! Let's have some {3}", 25, 6, 4, { product: "tee" } ); | |||
|
|
84 | 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"); | |||
|
|
85 | ||||
|
|
86 | }); | |||
| @@ -0,0 +1,52 | |||||
|
|
1 | import {NullConsole} from "@implab/core/log/NullConsole"; | |||
|
|
2 | ||||
|
|
3 | interface ConsoleLineData { | |||
|
|
4 | level: string; | |||
|
|
5 | data: any[]; | |||
|
|
6 | } | |||
|
|
7 | ||||
|
|
8 | export class MockConsole extends NullConsole { | |||
|
|
9 | _buffer: ConsoleLineData[] = []; | |||
|
|
10 | ||||
|
|
11 | debug(...args: any[]) { | |||
|
|
12 | this._buffer.push({ | |||
|
|
13 | level: "debug", | |||
|
|
14 | data: args | |||
|
|
15 | }); | |||
|
|
16 | } | |||
|
|
17 | ||||
|
|
18 | log(...args: any[]) { | |||
|
|
19 | this._buffer.push({ | |||
|
|
20 | level: "log", | |||
|
|
21 | data: args | |||
|
|
22 | }); | |||
|
|
23 | } | |||
|
|
24 | ||||
|
|
25 | warn(...args: any[]) { | |||
|
|
26 | this._buffer.push({ | |||
|
|
27 | level: "warn", | |||
|
|
28 | data: args | |||
|
|
29 | }); | |||
|
|
30 | } | |||
|
|
31 | ||||
|
|
32 | error(...args: any[]) { | |||
|
|
33 | this._buffer.push({ | |||
|
|
34 | level: "error", | |||
|
|
35 | data: args | |||
|
|
36 | }); | |||
|
|
37 | } | |||
|
|
38 | ||||
|
|
39 | getBuffer() { | |||
|
|
40 | return this._buffer; | |||
|
|
41 | } | |||
|
|
42 | ||||
|
|
43 | getLine(i: number) { | |||
|
|
44 | if (i >= this._buffer.length) | |||
|
|
45 | throw new Error(`Line number ${i} is out of range, buffer.length = ${this._buffer.length}`); | |||
|
|
46 | return this._buffer[i].data; | |||
|
|
47 | } | |||
|
|
48 | ||||
|
|
49 | clear() { | |||
|
|
50 | this._buffer = []; | |||
|
|
51 | } | |||
|
|
52 | } | |||
| @@ -99,6 +99,8 def createSoursetTasks = { String name, | |||||
| 99 | '-t', target, |
|
99 | '-t', target, | |
| 100 | '-m', jsmodule, |
|
100 | '-m', jsmodule, | |
| 101 | '-d', |
|
101 | '-d', | |
|
|
102 | '--sourceMap', | |||
|
|
103 | '--sourceRoot', "file://$setDir/ts", | |||
| 102 | '--outDir', compileDir, |
|
104 | '--outDir', compileDir, | |
| 103 | '--declarationDir', declDir |
|
105 | '--declarationDir', declDir | |
| 104 |
|
106 | |||
| @@ -6,4 +6,4 description=Dependency injection, loggin | |||||
| 6 | license=BSD-2-Clause |
|
6 | license=BSD-2-Clause | |
| 7 | repository=https://bitbucket.org/implab/implabjs-core |
|
7 | repository=https://bitbucket.org/implab/implabjs-core | |
| 8 | npmScope=implab |
|
8 | npmScope=implab | |
| 9 | npmName=core No newline at end of file |
|
9 | npmName=core-amd No newline at end of file | |
| @@ -90,7 +90,7 | |||||
| 90 | }, |
|
90 | }, | |
| 91 | "duplexer": { |
|
91 | "duplexer": { | |
| 92 | "version": "0.1.1", |
|
92 | "version": "0.1.1", | |
| 93 |
"resolved": "http |
|
93 | "resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", | |
| 94 | "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", |
|
94 | "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=", | |
| 95 | "dev": true |
|
95 | "dev": true | |
| 96 | }, |
|
96 | }, | |
| @@ -144,7 +144,7 | |||||
| 144 | "dependencies": { |
|
144 | "dependencies": { | |
| 145 | "tape": { |
|
145 | "tape": { | |
| 146 | "version": "2.3.3", |
|
146 | "version": "2.3.3", | |
| 147 |
"resolved": "http |
|
147 | "resolved": "http://registry.npmjs.org/tape/-/tape-2.3.3.tgz", | |
| 148 | "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=", |
|
148 | "integrity": "sha1-Lnzgox3wn41oUWZKcYQuDKUFevc=", | |
| 149 | "dev": true, |
|
149 | "dev": true, | |
| 150 | "requires": { |
|
150 | "requires": { | |
| @@ -277,7 +277,7 | |||||
| 277 | }, |
|
277 | }, | |
| 278 | "minimist": { |
|
278 | "minimist": { | |
| 279 | "version": "0.0.5", |
|
279 | "version": "0.0.5", | |
| 280 |
"resolved": "http |
|
280 | "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", | |
| 281 | "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=", |
|
281 | "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=", | |
| 282 | "dev": true |
|
282 | "dev": true | |
| 283 | }, |
|
283 | }, | |
| @@ -316,7 +316,7 | |||||
| 316 | }, |
|
316 | }, | |
| 317 | "readable-stream": { |
|
317 | "readable-stream": { | |
| 318 | "version": "1.1.14", |
|
318 | "version": "1.1.14", | |
| 319 |
"resolved": "http |
|
319 | "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", | |
| 320 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", |
|
320 | "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", | |
| 321 | "dev": true, |
|
321 | "dev": true, | |
| 322 | "requires": { |
|
322 | "requires": { | |
| @@ -369,7 +369,7 | |||||
| 369 | }, |
|
369 | }, | |
| 370 | "string_decoder": { |
|
370 | "string_decoder": { | |
| 371 | "version": "0.10.31", |
|
371 | "version": "0.10.31", | |
| 372 |
"resolved": "http |
|
372 | "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", | |
| 373 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", |
|
373 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", | |
| 374 | "dev": true |
|
374 | "dev": true | |
| 375 | }, |
|
375 | }, | |
| @@ -432,7 +432,7 | |||||
| 432 | }, |
|
432 | }, | |
| 433 | "through2": { |
|
433 | "through2": { | |
| 434 | "version": "0.2.3", |
|
434 | "version": "0.2.3", | |
| 435 |
"resolved": "http |
|
435 | "resolved": "http://registry.npmjs.org/through2/-/through2-0.2.3.tgz", | |
| 436 | "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", |
|
436 | "integrity": "sha1-6zKE2k6jEbbMis42U3SKUqvyWj8=", | |
| 437 | "dev": true, |
|
437 | "dev": true, | |
| 438 | "requires": { |
|
438 | "requires": { | |
| @@ -7,6 +7,23 const trace = TraceSource.get(m.id); | |||||
| 7 |
|
7 | |||
| 8 | type TemplateFn = (obj: object) => string; |
|
8 | type TemplateFn = (obj: object) => string; | |
| 9 |
|
9 | |||
|
|
10 | const htmlEscapes = { | |||
|
|
11 | "&": "&", | |||
|
|
12 | "<": "<", | |||
|
|
13 | ">": ">", | |||
|
|
14 | '"': """, | |||
|
|
15 | "'": "'", | |||
|
|
16 | "/": "/" | |||
|
|
17 | }; | |||
|
|
18 | ||||
|
|
19 | // Regex containing the keys listed immediately above. | |||
|
|
20 | const htmlEscaper = /[&<>"'\/]/g; | |||
|
|
21 | ||||
|
|
22 | // Escape a string for HTML interpolation. | |||
|
|
23 | function escapeHtml(string: any) { | |||
|
|
24 | return ("" + string).replace(htmlEscaper, match => htmlEscapes[match]); | |||
|
|
25 | } | |||
|
|
26 | ||||
| 10 | export class TemplateCompiler { |
|
27 | export class TemplateCompiler { | |
| 11 |
|
28 | |||
| 12 | _data: string[]; |
|
29 | _data: string[]; | |
| @@ -27,14 +44,14 export class TemplateCompiler { | |||||
| 27 |
|
44 | |||
| 28 | try { |
|
45 | try { | |
| 29 | // tslint:disable-next-line:function-constructor |
|
46 | // tslint:disable-next-line:function-constructor | |
| 30 | const compiled = new Function("obj, format, $data", text); |
|
47 | const compiled = new Function("obj, format, $data, escapeHtml", text); | |
| 31 | /** |
|
48 | /** | |
| 32 | * Функция форматирования по шаблону |
|
49 | * Функция форматирования по шаблону | |
| 33 | * |
|
50 | * | |
| 34 | * @type{Function} |
|
51 | * @type{Function} | |
| 35 | * @param{Object} obj объект с параметрами для подстановки |
|
52 | * @param{Object} obj объект с параметрами для подстановки | |
| 36 | */ |
|
53 | */ | |
| 37 | return (obj: object) => compiled(obj || {}, format, this._data); |
|
54 | return (obj: object) => compiled(obj || {}, format, this._data, escapeHtml); | |
| 38 | } catch (e) { |
|
55 | } catch (e) { | |
| 39 | trace.traceEvent(DebugLevel, [e, text, this._data]); |
|
56 | trace.traceEvent(DebugLevel, [e, text, this._data]); | |
| 40 | throw e; |
|
57 | throw e; | |
| @@ -69,6 +86,9 export class TemplateCompiler { | |||||
| 69 | case TokenType.OpenInlineBlock: |
|
86 | case TokenType.OpenInlineBlock: | |
| 70 | this.visitInline(parser); |
|
87 | this.visitInline(parser); | |
| 71 | break; |
|
88 | break; | |
|
|
89 | case TokenType.OpenFilterBlock: | |||
|
|
90 | this.visitFilter(parser); | |||
|
|
91 | break; | |||
| 72 | default: |
|
92 | default: | |
| 73 | this.visitTextFragment(parser); |
|
93 | this.visitTextFragment(parser); | |
| 74 | break; |
|
94 | break; | |
| @@ -87,6 +107,17 export class TemplateCompiler { | |||||
| 87 | this._code.push(code.join("")); |
|
107 | this._code.push(code.join("")); | |
| 88 | } |
|
108 | } | |
| 89 |
|
109 | |||
|
|
110 | visitFilter(parser: ITemplateParser) { | |||
|
|
111 | const code = ["$p.push(escapeHtml("]; | |||
|
|
112 | while (parser.next()) { | |||
|
|
113 | if (parser.token() === TokenType.CloseBlock) | |||
|
|
114 | break; | |||
|
|
115 | code.push(parser.value()); | |||
|
|
116 | } | |||
|
|
117 | code.push("));"); | |||
|
|
118 | this._code.push(code.join("")); | |||
|
|
119 | } | |||
|
|
120 | ||||
| 90 | visitCode(parser: ITemplateParser) { |
|
121 | visitCode(parser: ITemplateParser) { | |
| 91 | const code = []; |
|
122 | const code = []; | |
| 92 | while (parser.next()) { |
|
123 | while (parser.next()) { | |
| @@ -5,12 +5,13 import m = require("module"); | |||||
| 5 |
|
5 | |||
| 6 | const trace = TraceSource.get(m.id); |
|
6 | const trace = TraceSource.get(m.id); | |
| 7 |
|
7 | |||
| 8 | const splitRx = /(<%=|\[%=|<%|\[%|%\]|%>)/; |
|
8 | const splitRx = /(<%=|<%~|\[%~|\[%=|<%|\[%|%\]|%>)/; | |
| 9 |
|
9 | |||
| 10 | export enum TokenType { |
|
10 | export enum TokenType { | |
| 11 | None, |
|
11 | None, | |
| 12 | Text, |
|
12 | Text, | |
| 13 | OpenInlineBlock, |
|
13 | OpenInlineBlock, | |
|
|
14 | OpenFilterBlock, | |||
| 14 | OpenBlock, |
|
15 | OpenBlock, | |
| 15 | CloseBlock |
|
16 | CloseBlock | |
| 16 | } |
|
17 | } | |
| @@ -20,6 +21,8 const tokenMap: MapOf<TokenType> = { | |||||
| 20 | "[%": TokenType.OpenBlock, |
|
21 | "[%": TokenType.OpenBlock, | |
| 21 | "<%=": TokenType.OpenInlineBlock, |
|
22 | "<%=": TokenType.OpenInlineBlock, | |
| 22 | "[%=": TokenType.OpenInlineBlock, |
|
23 | "[%=": TokenType.OpenInlineBlock, | |
|
|
24 | "<%~": TokenType.OpenFilterBlock, | |||
|
|
25 | "[%~": TokenType.OpenFilterBlock, | |||
| 23 | "%>": TokenType.CloseBlock, |
|
26 | "%>": TokenType.CloseBlock, | |
| 24 | "%]": TokenType.CloseBlock |
|
27 | "%]": TokenType.CloseBlock | |
| 25 | }; |
|
28 | }; | |
| @@ -101,11 +101,11 export interface IObserver<T> { | |||||
| 101 | } |
|
101 | } | |
| 102 |
|
102 | |||
| 103 | export interface TextWriter { |
|
103 | export interface TextWriter { | |
| 104 |
|
|
104 | write(obj: any): void; | |
| 105 |
|
|
105 | write(format: string, ...args: any[]): void; | |
| 106 |
|
106 | |||
| 107 |
|
|
107 | writeLine(obj?: any): void; | |
| 108 |
|
|
108 | writeLine(format: string, ...args: any[]): void; | |
| 109 |
|
109 | |||
| 110 |
|
|
110 | writeValue(value: any, spec?: string): void; | |
| 111 | } |
|
111 | } | |
| @@ -1,6 +1,6 | |||||
| 1 | import { Observable } from "../Observable"; |
|
1 | import { Observable } from "../Observable"; | |
| 2 | import { Registry } from "./Registry"; |
|
2 | import { Registry } from "./Registry"; | |
| 3 | import { format as _format } from "../text/StringFormat"; |
|
3 | import { TraceEventData } from "./TraceEventData"; | |
| 4 |
|
4 | |||
| 5 | export const DebugLevel = 400; |
|
5 | export const DebugLevel = 400; | |
| 6 |
|
6 | |||
| @@ -17,13 +17,9 export interface TraceEvent { | |||||
| 17 |
|
17 | |||
| 18 | readonly level: number; |
|
18 | readonly level: number; | |
| 19 |
|
19 | |||
| 20 |
readonly a |
|
20 | readonly message: any; | |
| 21 | } |
|
|||
| 22 |
|
21 | |||
| 23 | function format(msg) { |
|
22 | readonly args?: any[]; | |
| 24 | if (typeof(msg) !== "string" || arguments.length === 1) |
|
|||
| 25 | return msg; |
|
|||
| 26 | return _format.apply(null, arguments); |
|
|||
| 27 | } |
|
23 | } | |
| 28 |
|
24 | |||
| 29 | export class TraceSource { |
|
25 | export class TraceSource { | |
| @@ -43,35 +39,41 export class TraceSource { | |||||
| 43 | }); |
|
39 | }); | |
| 44 | } |
|
40 | } | |
| 45 |
|
41 | |||
| 46 |
protected emit(level: number, a |
|
42 | protected emit(level: number, message: any, args?: any[]) { | |
| 47 |
this._notifyNext( |
|
43 | this._notifyNext(new TraceEventData(this, level, message, args)); | |
| 48 | } |
|
44 | } | |
| 49 |
|
45 | |||
| 50 | isDebugEnabled() { |
|
46 | isDebugEnabled() { | |
| 51 | return this.level >= DebugLevel; |
|
47 | return this.level >= DebugLevel; | |
| 52 | } |
|
48 | } | |
| 53 |
|
49 | |||
|
|
50 | debug(data: any): void; | |||
|
|
51 | debug(msg: string, ...args: any[]): void; | |||
| 54 | debug(msg: string, ...args: any[]) { |
|
52 | debug(msg: string, ...args: any[]) { | |
| 55 | if (this.isEnabled(DebugLevel)) |
|
53 | if (this.isEnabled(DebugLevel)) | |
| 56 |
this.emit(DebugLevel, |
|
54 | this.emit(DebugLevel, msg, args); | |
| 57 | } |
|
55 | } | |
| 58 |
|
56 | |||
| 59 | isLogEnabled() { |
|
57 | isLogEnabled() { | |
| 60 | return this.level >= LogLevel; |
|
58 | return this.level >= LogLevel; | |
| 61 | } |
|
59 | } | |
| 62 |
|
60 | |||
|
|
61 | log(data: any): void; | |||
|
|
62 | log(msg: string, ...args: any[]): void; | |||
| 63 | log(msg: string, ...args: any[]) { |
|
63 | log(msg: string, ...args: any[]) { | |
| 64 | if (this.isEnabled(LogLevel)) |
|
64 | if (this.isEnabled(LogLevel)) | |
| 65 |
this.emit(LogLevel, |
|
65 | this.emit(LogLevel, msg, args); | |
| 66 | } |
|
66 | } | |
| 67 |
|
67 | |||
| 68 | isWarnEnabled() { |
|
68 | isWarnEnabled() { | |
| 69 | return this.level >= WarnLevel; |
|
69 | return this.level >= WarnLevel; | |
| 70 | } |
|
70 | } | |
| 71 |
|
71 | |||
|
|
72 | warn(data: any): void; | |||
|
|
73 | warn(msg: string, ...args: any[]): void; | |||
| 72 | warn(msg: string, ...args: any[]) { |
|
74 | warn(msg: string, ...args: any[]) { | |
| 73 | if (this.isEnabled(WarnLevel)) |
|
75 | if (this.isEnabled(WarnLevel)) | |
| 74 |
this.emit(WarnLevel, |
|
76 | this.emit(WarnLevel, msg, args); | |
| 75 | } |
|
77 | } | |
| 76 |
|
78 | |||
| 77 | /** |
|
79 | /** | |
| @@ -81,15 +83,20 export class TraceSource { | |||||
| 81 | return this.level >= ErrorLevel; |
|
83 | return this.level >= ErrorLevel; | |
| 82 | } |
|
84 | } | |
| 83 |
|
85 | |||
|
|
86 | /** Traces a error | |||
|
|
87 | * @param data The object which will be passed to the underlying listeners | |||
|
|
88 | */ | |||
|
|
89 | error(data: any): void; | |||
| 84 | /** |
|
90 | /** | |
| 85 | * Traces a error. |
|
91 | * Traces a error. | |
| 86 | * |
|
92 | * | |
| 87 | * @param msg the message. |
|
93 | * @param msg the message. | |
| 88 | * @param args parameters which will be substituted in the message. |
|
94 | * @param args parameters which will be substituted in the message. | |
| 89 | */ |
|
95 | */ | |
|
|
96 | error(msg: string, ...args: any[]): void; | |||
| 90 | error(msg: string, ...args: any[]) { |
|
97 | error(msg: string, ...args: any[]) { | |
| 91 | if (this.isEnabled(ErrorLevel)) |
|
98 | if (this.isEnabled(ErrorLevel)) | |
| 92 |
this.emit(ErrorLevel, |
|
99 | this.emit(ErrorLevel, msg, args); | |
| 93 | } |
|
100 | } | |
| 94 |
|
101 | |||
| 95 | /** |
|
102 | /** | |
| @@ -106,11 +113,11 export class TraceSource { | |||||
| 106 | * Traces a raw event, passing data as it is to the underlying listeners |
|
113 | * Traces a raw event, passing data as it is to the underlying listeners | |
| 107 | * |
|
114 | * | |
| 108 | * @param level the level of the event |
|
115 | * @param level the level of the event | |
| 109 |
* @param |
|
116 | * @param msg the data of the event, can be a simple string or any object. | |
| 110 | */ |
|
117 | */ | |
| 111 |
traceEvent(level: number, |
|
118 | traceEvent(level: number, msg: any, ...args: any[]) { | |
| 112 | if (this.isEnabled(level)) |
|
119 | if (this.isEnabled(level)) | |
| 113 | this.emit(level, arg); |
|
120 | this.emit(level, msg, args); | |
| 114 | } |
|
121 | } | |
| 115 |
|
122 | |||
| 116 | /** |
|
123 | /** | |
| @@ -1,49 +1,1 | |||||
| 1 | import { IObservable, IDestroyable, ICancellation } from "../../interfaces"; |
|
1 | export { ConsoleLogger as ConsoleWriter } from "./ConsoleLogger"; No newline at end of file | |
| 2 | import { TraceEvent, LogLevel, WarnLevel, DebugLevel } from "../TraceSource"; |
|
|||
| 3 | import { Cancellation } from "../../Cancellation"; |
|
|||
| 4 | import { destroy } from "../../safe"; |
|
|||
| 5 |
|
||||
| 6 | function hasConsole() { |
|
|||
| 7 | try { |
|
|||
| 8 | // tslint:disable-next-line:no-console |
|
|||
| 9 | return (typeof console !== "undefined" && typeof console.log === "function"); |
|
|||
| 10 | } catch { |
|
|||
| 11 | return false; |
|
|||
| 12 | } |
|
|||
| 13 | } |
|
|||
| 14 |
|
||||
| 15 | export class ConsoleWriter implements IDestroyable { |
|
|||
| 16 | readonly _subscriptions = new Array<IDestroyable>(); |
|
|||
| 17 |
|
||||
| 18 | writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) { |
|
|||
| 19 | const subscription = source.on(this.writeEvent.bind(this)); |
|
|||
| 20 | if (ct.isSupported()) { |
|
|||
| 21 | ct.register(subscription.destroy.bind(subscription)); |
|
|||
| 22 | } |
|
|||
| 23 | this._subscriptions.push(subscription); |
|
|||
| 24 | } |
|
|||
| 25 |
|
||||
| 26 | writeEvent(next: TraceEvent) { |
|
|||
| 27 | // IE will create console only when devepoler tools are activated |
|
|||
| 28 | if (!hasConsole()) |
|
|||
| 29 | return; |
|
|||
| 30 |
|
||||
| 31 | if (next.level >= DebugLevel) { |
|
|||
| 32 | // tslint:disable-next-line:no-console |
|
|||
| 33 | console.debug(next.source.id.toString(), next.arg); |
|
|||
| 34 | } else if (next.level >= LogLevel) { |
|
|||
| 35 | // tslint:disable-next-line:no-console |
|
|||
| 36 | console.log(next.source.id.toString(), next.arg); |
|
|||
| 37 | } else if (next.level >= WarnLevel) { |
|
|||
| 38 | // tslint:disable-next-line:no-console |
|
|||
| 39 | console.warn(next.source.id.toString(), next.arg); |
|
|||
| 40 | } else { |
|
|||
| 41 | // tslint:disable-next-line:no-console |
|
|||
| 42 | console.error(next.source.id.toString(), next.arg); |
|
|||
| 43 | } |
|
|||
| 44 | } |
|
|||
| 45 |
|
||||
| 46 | destroy() { |
|
|||
| 47 | this._subscriptions.forEach(destroy); |
|
|||
| 48 | } |
|
|||
| 49 | } |
|
|||
| @@ -1,20 +1,41 | |||||
| 1 | import { FormatScanner, TokeType } from "./FormatScanner"; |
|
1 | import { FormatScanner, TokeType } from "./FormatScanner"; | |
| 2 | import { isNullOrEmptyString } from "../safe"; |
|
2 | import { isNullOrEmptyString, isPrimitive, get } from "../safe"; | |
| 3 | import { TextWriter } from "../interfaces"; |
|
3 | import { TextWriter, MapOf } from "../interfaces"; | |
|
|
4 | ||||
|
|
5 | type CompiledPattern = (writer: TextWriter, args: any) => void; | |||
| 4 |
|
6 | |||
| 5 | export class FormatCompiler { |
|
7 | export class FormatCompiler { | |
| 6 | _scanner: FormatScanner; |
|
8 | _scanner: FormatScanner; | |
|
|
9 | _cache: MapOf<CompiledPattern> = {}; | |||
| 7 |
|
10 | |||
| 8 | _parts: []; |
|
11 | _parts: Array<string | { name: string; format: string; }>; | |
|
|
12 | ||||
|
|
13 | compile(pattern: string) { | |||
|
|
14 | let compiledPattern = this._cache && this._cache[pattern]; | |||
|
|
15 | if (!compiledPattern) { | |||
|
|
16 | this._scanner = new FormatScanner(pattern); | |||
|
|
17 | this._parts = []; | |||
|
|
18 | ||||
|
|
19 | this.visitText(); | |||
|
|
20 | const parts = this._parts; | |||
| 9 |
|
21 | |||
| 10 | compile() { |
|
22 | compiledPattern = (writer: TextWriter, args: any) => { | |
| 11 | return (writer: TextWriter, args: any) => { |
|
23 | parts.forEach(x => { | |
| 12 | this._parts.forEach(x => writer.WriteValue(x)) |
|
24 | if (isPrimitive(x)) | |
| 13 | }; |
|
25 | writer.writeValue(x); | |
|
|
26 | else | |||
|
|
27 | writer.writeValue(get(x.name, args), x.format); | |||
|
|
28 | }); | |||
|
|
29 | }; | |||
|
|
30 | if (this._cache) | |||
|
|
31 | this._cache[pattern] = compiledPattern; | |||
|
|
32 | } | |||
|
|
33 | return compiledPattern; | |||
| 14 | } |
|
34 | } | |
| 15 |
|
35 | |||
| 16 | visitText() { |
|
36 | visitText() { | |
| 17 | while (this._scanner.next()) { |
|
37 | while (this._scanner.next()) { | |
|
|
38 | // console.log(this._scanner.getTokenType(), this._scanner.getTokenValue()); | |||
| 18 | switch (this._scanner.getTokenType()) { |
|
39 | switch (this._scanner.getTokenType()) { | |
| 19 | case TokeType.CurlOpen: |
|
40 | case TokeType.CurlOpen: | |
| 20 | this.visitCurlOpen(); |
|
41 | this.visitCurlOpen(); | |
| @@ -25,8 +46,6 export class FormatCompiler { | |||||
| 25 | default: |
|
46 | default: | |
| 26 | this.pushText(this._scanner.getTokenValue()); |
|
47 | this.pushText(this._scanner.getTokenValue()); | |
| 27 | } |
|
48 | } | |
| 28 | if (this._scanner.getTokenType() === TokeType.CurlOpen) |
|
|||
| 29 | this.visitCurlOpen(); |
|
|||
| 30 | } |
|
49 | } | |
| 31 | } |
|
50 | } | |
| 32 |
|
51 | |||
| @@ -39,12 +58,14 export class FormatCompiler { | |||||
| 39 | } |
|
58 | } | |
| 40 |
|
59 | |||
| 41 | visitCurlOpen() { |
|
60 | visitCurlOpen() { | |
| 42 |
if (this._scanner.next()) |
|
61 | if (!this._scanner.next()) | |
| 43 | if (this._scanner.getTokenType() === TokeType.CurlOpen) |
|
62 | this.dieUnexpectedEnd("{ | TEXT"); | |
| 44 | this.pushText("{"); |
|
63 | ||
| 45 | else |
|
64 | if (this._scanner.getTokenType() === TokeType.CurlOpen) | |
| 46 |
|
|
65 | this.pushText("{"); | |
| 47 |
|
|
66 | else | |
|
|
67 | this.visitTemplateSubst(); | |||
|
|
68 | ||||
| 48 | } |
|
69 | } | |
| 49 |
|
70 | |||
| 50 | visitTemplateSubst() { |
|
71 | visitTemplateSubst() { | |
| @@ -52,20 +73,23 export class FormatCompiler { | |||||
| 52 | this.dieUnexpectedToken("TEXT"); |
|
73 | this.dieUnexpectedToken("TEXT"); | |
| 53 |
|
74 | |||
| 54 | const fieldName = this._scanner.getTokenValue(); |
|
75 | const fieldName = this._scanner.getTokenValue(); | |
| 55 |
const filedFormat = this.readColon() |
|
76 | const filedFormat = this.readColon() ? this.readFieldFormat() : null; | |
|
|
77 | ||||
|
|
78 | if (this._scanner.getTokenType() !== TokeType.CurlClose) | |||
|
|
79 | this.dieUnexpectedToken("}"); | |||
| 56 |
|
80 | |||
| 57 | this.pushSubst(fieldName, filedFormat); |
|
81 | this.pushSubst(fieldName, filedFormat); | |
| 58 | } |
|
82 | } | |
| 59 |
|
83 | |||
| 60 | readFieldFormat() { |
|
84 | readFieldFormat() { | |
| 61 | const parts = new Array<string>(); |
|
85 | const parts = new Array<string>(); | |
| 62 | while (this._scanner.next()) { |
|
86 | do { | |
| 63 | if (this._scanner.getTokenType() === TokeType.CurlClose) { |
|
87 | if (this._scanner.getTokenType() === TokeType.CurlClose) { | |
| 64 | return parts.join(""); |
|
88 | return parts.join(""); | |
| 65 | } else { |
|
89 | } else { | |
| 66 | parts.push(this._scanner.getTokenValue()); |
|
90 | parts.push(this._scanner.getTokenValue()); | |
| 67 | } |
|
91 | } | |
| 68 | } |
|
92 | } while (this._scanner.next()); | |
| 69 |
|
93 | |||
| 70 | this.dieUnexpectedEnd("}"); |
|
94 | this.dieUnexpectedEnd("}"); | |
| 71 | } |
|
95 | } | |
| @@ -81,11 +105,12 export class FormatCompiler { | |||||
| 81 | } |
|
105 | } | |
| 82 |
|
106 | |||
| 83 | pushSubst(fieldName: string, filedFormat: string) { |
|
107 | pushSubst(fieldName: string, filedFormat: string) { | |
| 84 |
|
108 | // console.log("pushSubst ", fieldName, filedFormat); | ||
|
|
109 | this._parts.push({ name: fieldName, format: filedFormat }); | |||
| 85 | } |
|
110 | } | |
| 86 |
|
111 | |||
| 87 | pushText(text: string) { |
|
112 | pushText(text: string) { | |
| 88 |
|
113 | this._parts.push(text); | ||
| 89 | } |
|
114 | } | |
| 90 |
|
115 | |||
| 91 | dieUnexpectedToken(expected?: string) { |
|
116 | dieUnexpectedToken(expected?: string) { | |
| @@ -2,10 +2,10 import { argumentNotEmptyString } from " | |||||
| 2 | import { MapOf } from "../interfaces"; |
|
2 | import { MapOf } from "../interfaces"; | |
| 3 |
|
3 | |||
| 4 | export const enum TokeType { |
|
4 | export const enum TokeType { | |
| 5 | CurlOpen, |
|
5 | CurlOpen = 1, | |
| 6 | CurlClose, |
|
6 | CurlClose = 2, | |
| 7 | Colon, |
|
7 | Colon = 3, | |
| 8 | Text |
|
8 | Text = 4 | |
| 9 | } |
|
9 | } | |
| 10 |
|
10 | |||
| 11 | const typeMap = { |
|
11 | const typeMap = { | |
| @@ -16,7 +16,6 const typeMap = { | |||||
| 16 |
|
16 | |||
| 17 | export class FormatScanner { |
|
17 | export class FormatScanner { | |
| 18 | private _text: string; |
|
18 | private _text: string; | |
| 19 | private _pos: number; |
|
|||
| 20 | private _tokenType: TokeType; |
|
19 | private _tokenType: TokeType; | |
| 21 | private _tokenValue: string; |
|
20 | private _tokenValue: string; | |
| 22 | private _rx = /[^{}:]+|(.)/g; |
|
21 | private _rx = /[^{}:]+|(.)/g; | |
| @@ -29,7 +28,6 export class FormatScanner { | |||||
| 29 | next() { |
|
28 | next() { | |
| 30 | if (this._rx.lastIndex >= this._text.length) |
|
29 | if (this._rx.lastIndex >= this._text.length) | |
| 31 | return false; |
|
30 | return false; | |
| 32 | this._pos = this._rx.lastIndex; |
|
|||
| 33 |
|
31 | |||
| 34 | const match = this._rx.exec(this._text); |
|
32 | const match = this._rx.exec(this._text); | |
| 35 | this._tokenType = typeMap[match[1]] || TokeType.Text; |
|
33 | this._tokenType = typeMap[match[1]] || TokeType.Text; | |
| @@ -1,18 +1,31 | |||||
| 1 | export class StringBuilder { |
|
1 | import { TextWriterBase } from "./TextWriterBase"; | |
| 2 | private _data: string[]; |
|
2 | import { Converter } from "./Converter"; | |
| 3 | private _newLine = "\n"; |
|
3 | ||
|
|
4 | export class StringBuilder extends TextWriterBase { | |||
|
|
5 | private _data = new Array<string>(); | |||
| 4 |
|
6 | |||
| 5 | Write(obj: any); |
|
7 | constructor(converter = Converter.default) { | |
| 6 | Write(format: string, ...args: any[]) { |
|
8 | super(converter); | |
|
|
9 | } | |||
| 7 |
|
10 | |||
|
|
11 | writeText(text: string) { | |||
|
|
12 | this._data.push(text); | |||
| 8 | } |
|
13 | } | |
| 9 |
|
14 | |||
| 10 | WriteLine(obj: any); |
|
15 | toString() { | |
| 11 | WriteLine(format: string, ...args: any[]) { |
|
16 | return this._data.join(""); | |
| 12 |
|
||||
| 13 | } |
|
17 | } | |
| 14 |
|
18 | |||
| 15 | WriteValue(value: any, spec?: string) { |
|
19 | clear() { | |
| 16 |
|
20 | this._data.length = 0; | ||
| 17 | } |
|
21 | } | |
| 18 | } |
|
22 | } | |
|
|
23 | ||||
|
|
24 | const sb = new StringBuilder(); | |||
|
|
25 | ||||
|
|
26 | export function format(format: string, ...args: any): string; | |||
|
|
27 | export function format() { | |||
|
|
28 | sb.clear(); | |||
|
|
29 | sb.write.apply(sb, arguments); | |||
|
|
30 | return sb.toString(); | |||
|
|
31 | } | |||
| @@ -1,8 +1,8 | |||||
| 1 | import * as tape from "tape"; |
|
|||
| 2 |
|
|
1 | import { MockActivationController } from "./mock/MockActivationController"; | |
| 3 | import { SimpleActivatable } from "./mock/SimpleActivatable"; |
|
2 | import { SimpleActivatable } from "./mock/SimpleActivatable"; | |
|
|
3 | import { test } from "./TestTraits"; | |||
| 4 |
|
4 | |||
| 5 |
t |
|
5 | test("simple activation", async t => { | |
| 6 |
|
6 | |||
| 7 | const a = new SimpleActivatable(); |
|
7 | const a = new SimpleActivatable(); | |
| 8 | t.false(a.isActive()); |
|
8 | t.false(a.isActive()); | |
| @@ -12,11 +12,9 tape("simple activation", async t => { | |||||
| 12 |
|
12 | |||
| 13 | await a.deactivate(); |
|
13 | await a.deactivate(); | |
| 14 | t.false(a.isActive()); |
|
14 | t.false(a.isActive()); | |
| 15 |
|
||||
| 16 | t.end(); |
|
|||
| 17 | }); |
|
15 | }); | |
| 18 |
|
16 | |||
| 19 |
t |
|
17 | test("controller activation", async t => { | |
| 20 |
|
18 | |||
| 21 | const a = new SimpleActivatable(); |
|
19 | const a = new SimpleActivatable(); | |
| 22 | const c = new MockActivationController(); |
|
20 | const c = new MockActivationController(); | |
| @@ -37,11 +35,9 tape("controller activation", async t => | |||||
| 37 | t.false(a.isActive(), "The component should successfully deactivate"); |
|
35 | t.false(a.isActive(), "The component should successfully deactivate"); | |
| 38 | t.equal(c.getActive(), null, "The controller shouldn't point to any component"); |
|
36 | t.equal(c.getActive(), null, "The controller shouldn't point to any component"); | |
| 39 | t.equal(a.getActivationController(), c, "The componet should point to it's controller"); |
|
37 | t.equal(a.getActivationController(), c, "The componet should point to it's controller"); | |
| 40 |
|
||||
| 41 | t.end(); |
|
|||
| 42 | }); |
|
38 | }); | |
| 43 |
|
39 | |||
| 44 |
t |
|
40 | test("handle error in onActivating", async t => { | |
| 45 | const a = new SimpleActivatable(); |
|
41 | const a = new SimpleActivatable(); | |
| 46 |
|
42 | |||
| 47 | a.onActivating = async () => { |
|
43 | a.onActivating = async () => { | |
| @@ -55,6 +51,4 tape("handle error in onActivating", asy | |||||
| 55 | } |
|
51 | } | |
| 56 |
|
52 | |||
| 57 | t.false(a.isActive(), "the component should remain inactive"); |
|
53 | t.false(a.isActive(), "the component should remain inactive"); | |
| 58 |
|
||||
| 59 | t.end(); |
|
|||
| 60 | }); |
|
54 | }); | |
| @@ -1,8 +1,8 | |||||
| 1 | import * as tape from "tape"; |
|
|||
| 2 |
|
|
1 | import { Cancellation } from "@implab/core/Cancellation"; | |
| 3 | import { delay } from "@implab/core/safe"; |
|
2 | import { delay } from "@implab/core/safe"; | |
|
|
3 | import { test } from "./TestTraits"; | |||
| 4 |
|
4 | |||
| 5 |
t |
|
5 | test("standalone cancellation", async t => { | |
| 6 |
|
6 | |||
| 7 | let doCancel: (e) => void; |
|
7 | let doCancel: (e) => void; | |
| 8 |
|
8 | |||
| @@ -43,11 +43,9 tape("standalone cancellation", async t | |||||
| 43 | msg = e; |
|
43 | msg = e; | |
| 44 | } |
|
44 | } | |
| 45 | t.equals(msg, reason, "The cancellation reason should be catched"); |
|
45 | t.equals(msg, reason, "The cancellation reason should be catched"); | |
| 46 |
|
||||
| 47 | t.end(); |
|
|||
| 48 | }); |
|
46 | }); | |
| 49 |
|
47 | |||
| 50 |
t |
|
48 | test("async cancellation", async t => { | |
| 51 |
|
49 | |||
| 52 | const ct = new Cancellation(cancel => { |
|
50 | const ct = new Cancellation(cancel => { | |
| 53 | cancel("STOP!"); |
|
51 | cancel("STOP!"); | |
| @@ -59,11 +57,9 tape("async cancellation", async t => { | |||||
| 59 | } catch (e) { |
|
57 | } catch (e) { | |
| 60 | t.equals(e, "STOP!", "Should throw the cancellation reason"); |
|
58 | t.equals(e, "STOP!", "Should throw the cancellation reason"); | |
| 61 | } |
|
59 | } | |
| 62 |
|
||||
| 63 | t.end(); |
|
|||
| 64 | }); |
|
60 | }); | |
| 65 |
|
61 | |||
| 66 |
t |
|
62 | test("cancel with external event", async t => { | |
| 67 | const ct = new Cancellation(cancel => { |
|
63 | const ct = new Cancellation(cancel => { | |
| 68 | setTimeout(x => cancel("STOP!"), 0); |
|
64 | setTimeout(x => cancel("STOP!"), 0); | |
| 69 | }); |
|
65 | }); | |
| @@ -74,11 +70,9 tape("cancel with external event", async | |||||
| 74 | } catch (e) { |
|
70 | } catch (e) { | |
| 75 | t.equals(e, "STOP!", "Should throw the cancellation reason"); |
|
71 | t.equals(e, "STOP!", "Should throw the cancellation reason"); | |
| 76 | } |
|
72 | } | |
| 77 |
|
||||
| 78 | t.end(); |
|
|||
| 79 | }); |
|
73 | }); | |
| 80 |
|
74 | |||
| 81 |
t |
|
75 | test("operation normal flow", async t => { | |
| 82 |
|
76 | |||
| 83 | let htimeout; |
|
77 | let htimeout; | |
| 84 | const ct = new Cancellation(cancel => { |
|
78 | const ct = new Cancellation(cancel => { | |
| @@ -91,6 +85,4 tape("operation normal flow", async t => | |||||
| 91 | } finally { |
|
85 | } finally { | |
| 92 | clearTimeout(htimeout); |
|
86 | clearTimeout(htimeout); | |
| 93 | } |
|
87 | } | |
| 94 |
|
||||
| 95 | t.end(); |
|
|||
| 96 | }); |
|
88 | }); | |
| @@ -1,12 +1,12 | |||||
| 1 | import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource"; |
|
1 | import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource"; | |
| 2 | import * as tape from "tape"; |
|
|||
| 3 | import { Observable } from "@implab/core/Observable"; |
|
2 | import { Observable } from "@implab/core/Observable"; | |
| 4 | import { IObservable } from "@implab/core/interfaces"; |
|
3 | import { IObservable } from "@implab/core/interfaces"; | |
| 5 | import { delay } from "@implab/core/safe"; |
|
4 | import { delay } from "@implab/core/safe"; | |
|
|
5 | import { test } from "./TestTraits"; | |||
| 6 |
|
6 | |||
| 7 | const trace = TraceSource.get("ObservableTests"); |
|
7 | const trace = TraceSource.get("ObservableTests"); | |
| 8 |
|
8 | |||
| 9 |
t |
|
9 | test("events sequence example", async t => { | |
| 10 |
|
10 | |||
| 11 | let events: IObservable<number>; |
|
11 | let events: IObservable<number>; | |
| 12 |
|
12 | |||
| @@ -34,11 +34,9 tape("events sequence example", async t | |||||
| 34 |
|
34 | |||
| 35 | t.equals(count, 45, "the summ of the evetns"); |
|
35 | t.equals(count, 45, "the summ of the evetns"); | |
| 36 | t.true(complete, "the sequence is complete"); |
|
36 | t.true(complete, "the sequence is complete"); | |
| 37 |
|
||||
| 38 | t.end(); |
|
|||
| 39 | }); |
|
37 | }); | |
| 40 |
|
38 | |||
| 41 |
t |
|
39 | test("event sequence termination", async t => { | |
| 42 | let events: IObservable<number>; |
|
40 | let events: IObservable<number>; | |
| 43 |
|
41 | |||
| 44 | const done = new Promise<void>(resolve => { |
|
42 | const done = new Promise<void>(resolve => { | |
| @@ -68,6 +66,4 tape("event sequence termination", async | |||||
| 68 | await done; |
|
66 | await done; | |
| 69 |
|
67 | |||
| 70 | t.equals(count, 1, "the sequence must be terminated once"); |
|
68 | t.equals(count, 1, "the sequence must be terminated once"); | |
| 71 |
|
||||
| 72 | t.end(); |
|
|||
| 73 | }); |
|
69 | }); | |
| @@ -1,8 +1,8 | |||||
| 1 | import tape = require("tape"); |
|
|||
| 2 |
|
|
1 | import { Cancellation } from "@implab/core/Cancellation"; | |
| 3 | import { first, isPromise, firstWhere, delay, nowait } from "@implab/core/safe"; |
|
2 | import { first, isPromise, firstWhere, delay, nowait } from "@implab/core/safe"; | |
|
|
3 | import { test } from "./TestTraits"; | |||
| 4 |
|
4 | |||
| 5 |
t |
|
5 | test("await delay test", async t => { | |
| 6 | // schedule delay |
|
6 | // schedule delay | |
| 7 | let resolved = false; |
|
7 | let resolved = false; | |
| 8 | let res = delay(0).then(() => resolved = true); |
|
8 | let res = delay(0).then(() => resolved = true); | |
| @@ -36,11 +36,9 tape("await delay test", async t => { | |||||
| 36 | // try schedule delay after the cancellation is requested |
|
36 | // try schedule delay after the cancellation is requested | |
| 37 | nowait(delay(0, ct)); |
|
37 | nowait(delay(0, ct)); | |
| 38 | }, "Should throw if cancelled before start"); |
|
38 | }, "Should throw if cancelled before start"); | |
| 39 |
|
||||
| 40 | t.end(); |
|
|||
| 41 | }); |
|
39 | }); | |
| 42 |
|
40 | |||
| 43 |
t |
|
41 | test("sequemce test", async t => { | |
| 44 | const sequence = ["a", "b", "c"]; |
|
42 | const sequence = ["a", "b", "c"]; | |
| 45 | const empty = []; |
|
43 | const empty = []; | |
| 46 |
|
44 | |||
| @@ -94,6 +92,4 tape("sequemce test", async t => { | |||||
| 94 |
|
92 | |||
| 95 | v = await new Promise(resolve => first(asyncSequence, resolve)); |
|
93 | v = await new Promise(resolve => first(asyncSequence, resolve)); | |
| 96 | t.equal(v, "a", "The callback should be called for the first element"); |
|
94 | t.equal(v, "a", "The callback should be called for the first element"); | |
| 97 |
|
||||
| 98 | t.end(); |
|
|||
| 99 | }); |
|
95 | }); | |
| @@ -5,9 +5,10 import * as tape from "tape"; | |||||
| 5 | import { argumentNotNull, destroy } from "@implab/core/safe"; |
|
5 | import { argumentNotNull, destroy } from "@implab/core/safe"; | |
| 6 |
|
6 | |||
| 7 | export class TapeWriter implements IDestroyable { |
|
7 | export class TapeWriter implements IDestroyable { | |
| 8 | readonly _tape: tape.Test; |
|
8 | private readonly _tape: tape.Test; | |
| 9 |
|
9 | |||
| 10 | _subscriptions = new Array<IDestroyable>(); |
|
10 | private readonly _subscriptions = new Array<IDestroyable>(); | |
|
|
11 | private _destroyed; | |||
| 11 |
|
12 | |||
| 12 | constructor(t: tape.Test) { |
|
13 | constructor(t: tape.Test) { | |
| 13 | argumentNotNull(t, "tape"); |
|
14 | argumentNotNull(t, "tape"); | |
| @@ -15,22 +16,24 export class TapeWriter implements IDest | |||||
| 15 | } |
|
16 | } | |
| 16 |
|
17 | |||
| 17 | writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) { |
|
18 | writeEvents(source: IObservable<TraceEvent>, ct: ICancellation = Cancellation.none) { | |
| 18 | const subscription = source.on(this.writeEvent.bind(this)); |
|
19 | if (!this._destroyed) { | |
| 19 | if (ct.isSupported()) { |
|
20 | const subscription = source.on(this.writeEvent.bind(this)); | |
| 20 | ct.register(subscription.destroy.bind(subscription)); |
|
21 | if (ct.isSupported()) { | |
|
|
22 | ct.register(subscription.destroy.bind(subscription)); | |||
|
|
23 | } | |||
|
|
24 | this._subscriptions.push(subscription); | |||
| 21 | } |
|
25 | } | |
| 22 | this._subscriptions.push(subscription); |
|
|||
| 23 | } |
|
26 | } | |
| 24 |
|
27 | |||
| 25 | writeEvent(next: TraceEvent) { |
|
28 | writeEvent(next: TraceEvent) { | |
| 26 | if (next.level >= DebugLevel) { |
|
29 | if (next.level >= DebugLevel) { | |
| 27 |
this._tape.comment(`DEBUG ${next.source.id} ${next |
|
30 | this._tape.comment(`DEBUG ${next.source.id} ${next}`); | |
| 28 | } else if (next.level >= LogLevel) { |
|
31 | } else if (next.level >= LogLevel) { | |
| 29 |
this._tape.comment(`LOG ${next.source.id} ${next |
|
32 | this._tape.comment(`LOG ${next.source.id} ${next}`); | |
| 30 | } else if (next.level >= WarnLevel) { |
|
33 | } else if (next.level >= WarnLevel) { | |
| 31 |
this._tape.comment(`WARN ${next.source.id} ${next |
|
34 | this._tape.comment(`WARN ${next.source.id} ${next}`); | |
| 32 | } else { |
|
35 | } else { | |
| 33 |
this._tape.comment(`ERROR ${next.source.id} ${next |
|
36 | this._tape.comment(`ERROR ${next.source.id} ${next}`); | |
| 34 | } |
|
37 | } | |
| 35 | } |
|
38 | } | |
| 36 |
|
39 | |||
| @@ -39,17 +42,22 export class TapeWriter implements IDest | |||||
| 39 | } |
|
42 | } | |
| 40 | } |
|
43 | } | |
| 41 |
|
44 | |||
| 42 | export function test(name: string, cb: (t: tape.Test) => any) { |
|
45 | export function test(name: string, cb: (t: tape.Test, trace: TraceSource) => any) { | |
| 43 | tape(name, async t => { |
|
46 | tape(name, async t => { | |
| 44 | const writer = new TapeWriter(t); |
|
47 | const writer = new TapeWriter(t); | |
| 45 |
|
48 | |||
| 46 | TraceSource.on(ts => { |
|
49 | // this trace is not announced through the TraceSource global registry | |
|
|
50 | const trace = new TraceSource(name); | |||
|
|
51 | trace.level = DebugLevel; | |||
|
|
52 | writer.writeEvents(trace.events); | |||
|
|
53 | ||||
|
|
54 | const h = TraceSource.on(ts => { | |||
| 47 | ts.level = DebugLevel; |
|
55 | ts.level = DebugLevel; | |
| 48 | writer.writeEvents(ts.events); |
|
56 | writer.writeEvents(ts.events); | |
| 49 | }); |
|
57 | }); | |
| 50 |
|
58 | |||
| 51 | try { |
|
59 | try { | |
| 52 | await cb(t); |
|
60 | await cb(t, trace); | |
| 53 | } catch (e) { |
|
61 | } catch (e) { | |
| 54 |
|
62 | |||
| 55 | // verbose error information |
|
63 | // verbose error information | |
| @@ -60,6 +68,7 export function test(name: string, cb: ( | |||||
| 60 | } finally { |
|
68 | } finally { | |
| 61 | t.end(); |
|
69 | t.end(); | |
| 62 | destroy(writer); |
|
70 | destroy(writer); | |
|
|
71 | destroy(h); | |||
| 63 | } |
|
72 | } | |
| 64 | }); |
|
73 | }); | |
| 65 | } |
|
74 | } | |
| @@ -1,6 +1,9 | |||||
| 1 | import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource"; |
|
1 | import { TraceSource, DebugLevel } from "@implab/core/log/TraceSource"; | |
| 2 | import * as tape from "tape"; |
|
2 | import * as tape from "tape"; | |
| 3 | import { TapeWriter } from "./TestTraits"; |
|
3 | import { TapeWriter, test } from "./TestTraits"; | |
|
|
4 | import { MockConsole } from "./mock/MockConsole"; | |||
|
|
5 | import { ConsoleLog } from "@implab/core/log/writers/ConsoleLog"; | |||
|
|
6 | import { ConsoleWriter } from "@implab/core/log/ConsoleWriter"; | |||
| 4 |
|
7 | |||
| 5 | const sourceId = "test/TraceSourceTests"; |
|
8 | const sourceId = "test/TraceSourceTests"; | |
| 6 |
|
9 | |||
| @@ -12,7 +15,7 tape("trace message", t => { | |||||
| 12 | const h = trace.events.on(ev => { |
|
15 | const h = trace.events.on(ev => { | |
| 13 | t.equal(ev.source, trace, "sender should be the current trace source"); |
|
16 | t.equal(ev.source, trace, "sender should be the current trace source"); | |
| 14 | t.equal(ev.level, DebugLevel, "level should be debug level"); |
|
17 | t.equal(ev.level, DebugLevel, "level should be debug level"); | |
| 15 |
t.equal(ev. |
|
18 | t.equal(ev.toString(), "Hello, World!", "The message should be a formatted message"); | |
| 16 |
|
19 | |||
| 17 | t.end(); |
|
20 | t.end(); | |
| 18 | }); |
|
21 | }); | |
| @@ -34,7 +37,7 tape("trace event", t => { | |||||
| 34 | const h = trace.events.on(ev => { |
|
37 | const h = trace.events.on(ev => { | |
| 35 | t.equal(ev.source, trace, "sender should be the current trace source"); |
|
38 | t.equal(ev.source, trace, "sender should be the current trace source"); | |
| 36 | t.equal(ev.level, DebugLevel, "level should be debug level"); |
|
39 | t.equal(ev.level, DebugLevel, "level should be debug level"); | |
| 37 |
t.equal(ev.a |
|
40 | t.equal(ev.message, event, "The message should be the specified object"); | |
| 38 |
|
41 | |||
| 39 | t.end(); |
|
42 | t.end(); | |
| 40 | }); |
|
43 | }); | |
| @@ -67,3 +70,17 tape("tape comment writer", async t => { | |||||
| 67 |
|
70 | |||
| 68 | t.end(); |
|
71 | t.end(); | |
| 69 | }); |
|
72 | }); | |
|
|
73 | ||||
|
|
74 | test("console writer", (t, trace) => { | |||
|
|
75 | ||||
|
|
76 | const mockConsole = new MockConsole(); | |||
|
|
77 | const writer = new ConsoleWriter(mockConsole); | |||
|
|
78 | const consoleLog = new ConsoleLog(writer); | |||
|
|
79 | consoleLog.writeEvents(trace.events); | |||
|
|
80 | ||||
|
|
81 | trace.log("Hello, world!"); | |||
|
|
82 | t.deepEqual(mockConsole.getLine(0), ["console writer: Hello, world!"], "Log one string"); | |||
|
|
83 | ||||
|
|
84 | trace.log({ foo: "bar" }); | |||
|
|
85 | t.deepEqual(mockConsole.getLine(1), ["console writer: ", { foo: "bar" }], "Log an object"); | |||
|
|
86 | }); | |||
General Comments 0
You need to be logged in to leave comments.
Login now
