UNPKG

3.39 kBJavaScriptView Raw
1'use strict';
2
3const _ = require('lodash');
4const Bluebird = require('bluebird');
5
6function asSelfDescribing(value) {
7 if (!value || !_.isFunction(value.describeTo)) {
8 return {
9 describeTo(description) {
10 description.appendValue(value);
11 }
12 };
13 } else {
14 return value;
15 }
16}
17
18function makeNaNReadable(__key, value) {
19 return typeof value === 'number' && isNaN(value) ? '<NaN>' : value;
20}
21
22function Description() {
23 let value = '';
24 return {
25 useJsonForObjects: true,
26 indentation: 0,
27 append(text) {
28 if (this.indentation) {
29 text = ('' + text).replace('\n', _.padEnd('\n', this.indentation + 1, '\t'));
30 }
31 try {
32 value += text;
33 } catch (e) {
34 value += '[ ' + typeof text + ']';
35 }
36 return this;
37 },
38 indented(describingfn) {
39 this.indentation += 1;
40 const result = describingfn();
41 if (result && _.isFunction(result.then)) {
42 return Bluebird.resolve(result)
43 .finally(() => {
44 this.indentation -= 1;
45 });
46 } else {
47 this.indentation -= 1;
48 return result;
49 }
50 },
51 appendDescriptionOf(selfDescribing) {
52 if (selfDescribing && _.isFunction(selfDescribing.describeTo)) {
53 selfDescribing.describeTo(this);
54 } else {
55 this.appendValue(selfDescribing);
56 }
57 return this;
58 },
59 appendValue(value, indentLists) {
60 if (_.isUndefined(value)) {
61 this.append('undefined');
62 } else if (_.isNull(value)) {
63 this.append('null');
64 } else if (_.isString(value)) {
65 this.append('"');
66 this.append(value);
67 this.append('"');
68 } else if (_.isNumber(value)) {
69 this.append('<');
70 this.append(value);
71 this.append('>');
72 } else if (_.isArray(value)) {
73 if (indentLists && value.length > 1) {
74 this.indented(() => this.appendList('[\n', ',\n', '', value))
75 .append('\n]');
76 } else {
77 this.appendList('[', ', ', ']', value);
78 }
79 } else if (isDomNode(value)) {
80 this.append('DOM node ')
81 .appendValue(_.isFunction(value.html) ? value.html() : value.outerHTML);
82 } else if (_.isFunction(value)) {
83 this.append('Function' + (value.name ? ' ' + value.name : ''));
84 } else if (_.isRegExp(value)) {
85 this.append(value.toString());
86 } else if (this.useJsonForObjects) {
87 try {
88 this.append(JSON.stringify(value, makeNaNReadable));
89 } catch (e) {
90 const oldJsonFlag = this.useJsonForObjects;
91 this.useJsonForObjects = false;
92 this.appendNonJson(value);
93 this.useJsonForObjects = oldJsonFlag;
94 }
95 } else {
96 this.append(value);
97 }
98 return this;
99 },
100 appendNonJson(value) {
101 this.append('{');
102 let first = true;
103 _.forEach(value, (innerValue, key) => {
104 if (!first) {
105 this.append(', ');
106 }
107 first = false;
108 this.append(key)
109 .append(': ');
110 this.appendValue(innerValue);
111 }, this);
112 this.append('}');
113 },
114 appendList(start, separator, end, list) {
115 this.append(start);
116 _.forEach(list, (value, index) => {
117 if (index !== 0) {
118 this.append(separator);
119 }
120 this.appendDescriptionOf(asSelfDescribing(value));
121 }, this);
122 this.append(end);
123 return this;
124 },
125 get() {
126 return value;
127 }
128 };
129
130 function isDomNode(value) {
131 if (!value) {
132 return false;
133 }
134 return _.isFunction(value.appendChild) && _.isFunction(value.isEqualNode) && !_.isUndefined(value.outerHTML) ||
135 _.isFunction(value.html) && _.isFunction(value.text);
136 }
137}
138
139module.exports = Description;