Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download
80635 views
1
/**
2
* Copyright (c) 2014, Facebook, Inc. All rights reserved.
3
*
4
* This source code is licensed under the BSD-style license found in the
5
* LICENSE file in the root directory of this source tree. An additional grant
6
* of patent rights can be found in the PATENTS file in the same directory.
7
*/
8
'use strict';
9
10
var colors = require('../lib/colors');
11
var diff = require('diff');
12
var jasmine = require('../../vendor/jasmine/jasmine-1.3.0').jasmine;
13
var Q = require('q');
14
15
var ERROR_TITLE_COLOR = colors.RED + colors.BOLD + colors.UNDERLINE;
16
var DIFFABLE_MATCHERS = {
17
toBe: true,
18
toNotBe: true,
19
toEqual: true,
20
toNotEqual: true
21
};
22
var LINEBREAK_REGEX = /[\r\n]/;
23
24
function JasmineReporter(config) {
25
jasmine.Reporter.call(this);
26
this._config = config || {};
27
this._logs = [];
28
this._resultsDeferred = Q.defer();
29
}
30
31
JasmineReporter.prototype = Object.create(jasmine.Reporter.prototype);
32
33
// All describe() suites have finished
34
JasmineReporter.prototype.reportRunnerResults = function(runner) {
35
var testResults = [];
36
37
// Find the top-level suite in order to flatten test results from there
38
if (runner.suites().length) {
39
runner.suites().forEach(function(suite) {
40
if (suite.parentSuite === null) {
41
this._extractSuiteResults(testResults, [], suite);
42
}
43
}, this);
44
}
45
46
var numFailingTests = 0;
47
var numPassingTests = 0;
48
testResults.forEach(function(testResult) {
49
if (testResult.failureMessages.length > 0) {
50
numFailingTests++;
51
} else {
52
numPassingTests++;
53
}
54
});
55
56
this._resultsDeferred.resolve({
57
numFailingTests: numFailingTests,
58
numPassingTests: numPassingTests,
59
testResults: testResults
60
});
61
};
62
63
JasmineReporter.prototype.getResults = function() {
64
return this._resultsDeferred.promise;
65
};
66
67
JasmineReporter.prototype.log = function(str) {
68
console.log('logging: ', str);
69
};
70
71
JasmineReporter.prototype._extractSuiteResults =
72
function (container, ancestorTitles, suite) {
73
ancestorTitles = ancestorTitles.concat([suite.description]);
74
75
suite.specs().forEach(
76
this._extractSpecResults.bind(this, container, ancestorTitles)
77
);
78
suite.suites().forEach(
79
this._extractSuiteResults.bind(this, container, ancestorTitles)
80
);
81
};
82
83
JasmineReporter.prototype._extractSpecResults =
84
function (container, ancestorTitles, spec) {
85
var results = {
86
title: 'it ' + spec.description,
87
ancestorTitles: ancestorTitles,
88
failureMessages: [],
89
logMessages: [],
90
numPassingAsserts: 0
91
};
92
93
spec.results().getItems().forEach(function(result) {
94
switch (result.type) {
95
case 'log':
96
results.logMessages.push(result.toString());
97
break;
98
case 'expect':
99
if (result.passed()) {
100
results.numPassingAsserts++;
101
102
// Exception thrown
103
} else if (!result.matcherName && result.trace.stack) {
104
// jasmine doesn't give us access to the actual Error object, so we
105
// have to regexp out the message from the stack string in order to
106
// colorize the `message` value
107
result.trace.stack = result.trace.stack.replace(
108
/(^.*$(?=\n\s*at))/m,
109
this._formatMsg('$1', ERROR_TITLE_COLOR)
110
);
111
112
results.failureMessages.push(result.trace.stack);
113
} else {
114
var message;
115
if (DIFFABLE_MATCHERS[result.matcherName]) {
116
var ppActual = this._prettyPrint(result.actual);
117
var ppExpected = this._prettyPrint(result.expected);
118
var colorDiff = this._highlightDifferences(ppActual, ppExpected);
119
120
var matcherName = (result.isNot ? 'NOT ' : '') + result.matcherName;
121
122
message =
123
this._formatMsg('Expected:', ERROR_TITLE_COLOR) +
124
' ' + colorDiff.a +
125
' ' + this._formatMsg(matcherName + ':', ERROR_TITLE_COLOR) +
126
' ' + colorDiff.b;
127
} else {
128
message = this._formatMsg(result.message, ERROR_TITLE_COLOR);
129
}
130
131
if (result.trace.stack) {
132
// Replace the error message with a colorized version of the error
133
message = result.trace.stack.replace(result.trace.message, message);
134
135
// Remove the 'Error: ' prefix from the stack trace
136
message = message.replace(/^.*Error:\s*/, '');
137
138
// Remove jasmine jonx from the stack trace
139
message = message.split('\n').filter(function(line) {
140
return !/vendor\/jasmine\//.test(line);
141
}).join('\n');
142
}
143
144
results.failureMessages.push(message);
145
}
146
break;
147
default:
148
throw new Error(
149
'Unexpected jasmine spec result type: ', result.type
150
);
151
}
152
}, this);
153
154
container.push(results);
155
};
156
157
JasmineReporter.prototype._highlightDifferences = function (a, b) {
158
var differ;
159
if (a.match(LINEBREAK_REGEX) || b.match(LINEBREAK_REGEX)) {
160
// `diff` uses the Myers LCS diff algorithm which runs in O(n+d^2) time
161
// (where "d" is the edit distance) and can get very slow for large edit
162
// distances. Mitigate the cost by switching to a lower-resolution diff
163
// whenever linebreaks are involved.
164
differ = diff.diffLines;
165
} else {
166
differ = diff.diffChars;
167
}
168
var changes = differ(a, b);
169
var ret = {a: '', b: ''};
170
var change;
171
for (var i = 0, il = changes.length; i < il; i++) {
172
change = changes[i];
173
if (change.added) {
174
ret.b += this._formatMsg(change.value, colors.RED_BG);
175
} else if (change.removed) {
176
ret.a += this._formatMsg(change.value, colors.RED_BG);
177
} else {
178
ret.a += change.value;
179
ret.b += change.value;
180
}
181
}
182
return ret;
183
};
184
185
JasmineReporter.prototype._prettyPrint = function(obj, indent, cycleWeakMap) {
186
if (!indent) {
187
indent = '';
188
}
189
190
if (typeof obj === 'object' && obj !== null) {
191
if (jasmine.isDomNode(obj)) {
192
var attrStr = '';
193
Array.prototype.forEach.call(obj.attributes, function(attr) {
194
var attrName = attr.nodeName.trim();
195
var attrValue = attr.nodeValue.trim();
196
attrStr += ' ' + attrName + '="' + attrValue + '"';
197
});
198
return 'HTMLNode(' +
199
'<' + obj.tagName + attrStr + '>[...]</' + obj.tagName + '>' +
200
')';
201
}
202
203
/* jshint camelcase:false */
204
if (!cycleWeakMap) {
205
if (typeof WeakMap !== 'function') {
206
throw new Error(
207
'Please run node with the --harmony flag! jest requires WeakMap ' +
208
'which is only available with the --harmony flag in node < v0.12'
209
);
210
}
211
cycleWeakMap = new WeakMap();
212
}
213
214
if (cycleWeakMap.get(obj) === true) {
215
return '<circular reference>';
216
}
217
cycleWeakMap.set(obj, true);
218
219
var orderedKeys = Object.keys(obj).sort();
220
var value;
221
var keysOutput = [];
222
var keyIndent = this._formatMsg('|', colors.GRAY) + ' ';
223
for (var i = 0; i < orderedKeys.length; i++) {
224
if (orderedKeys[i] === '__jstest_pp_cycle__') {
225
continue;
226
}
227
value = obj[orderedKeys[i]];
228
keysOutput.push(
229
indent + keyIndent + orderedKeys[i] + ': ' +
230
this._prettyPrint(value, indent + keyIndent, cycleWeakMap)
231
);
232
}
233
delete obj.__jstest_pp_cycle__;
234
return '{\n' + keysOutput.join(',\n') + '\n' + indent + '}';
235
} else {
236
return jasmine.pp(obj);
237
}
238
};
239
240
JasmineReporter.prototype._formatMsg = function(msg, color) {
241
if (this._config.noHighlight) {
242
return msg;
243
}
244
return colors.colorize(msg, color);
245
};
246
247
module.exports = JasmineReporter;
248
249