forked from ampproject/amphtml
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconsole-logging-setup.js
179 lines (163 loc) · 5.04 KB
/
console-logging-setup.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
'use strict';
import sinon from 'sinon'; // eslint-disable-line local/no-import
import {logger} from '#preact/logger';
/**
* @fileoverview Provides functions that track unexpected console errors during
* tests and prints warnings when they are detected.
*/
export let expectedAsyncErrors = [];
let consoleErrorSandbox;
let testRunner;
let testName;
let consoleInfoLogWarnSandbox;
const originalConsoleError = console.error;
/**
* Exposes the test name for use during error logging.
* @param {string} name
*/
export function setTestName(name) {
testName = name;
}
/**
* Exposes the test runner object so that errors can be associated with it.
* TODO(wg-infra): Remove this after karma-runner/karma-mocha#236 is fixed.
* @param {Object} runner
*/
export function setTestRunner(runner) {
testRunner = runner;
}
/**
* Resets all console logging stubs.
*/
export function restoreConsoleSandbox() {
if (consoleInfoLogWarnSandbox) {
consoleInfoLogWarnSandbox.restore();
}
}
/**
* @param {string} message Message of error that's about to be logged.
* @return {number} index or -1
*/
export function indexOfExpectedMessage(message) {
// Match equal strings.
const exact = expectedAsyncErrors.indexOf(message);
if (exact != -1) {
return exact;
}
// Match regex.
for (let i = 0; i < expectedAsyncErrors.length; i++) {
const expectedError = expectedAsyncErrors[i];
if (typeof expectedError != 'string' && expectedError.test(message)) {
return i;
}
}
return -1;
}
/**
* Prints a warning when a console error is detected during a test.
* @param {*} messages One or more error messages
*/
function printWarning(...messages) {
const message = messages.join(' ');
const index = indexOfExpectedMessage(message);
if (index != -1) {
expectedAsyncErrors.splice(index, 1);
return;
}
const errorMessage = message.split('\n', 1)[0]; // First line.
const helpMessage =
' The test "' +
testName +
'"' +
' resulted in a call to console.error. (See above line.)\n' +
' ⤷ If the error is not expected, fix the code that generated ' +
'the error.\n' +
' ⤷ If the error is expected (and synchronous), use the following ' +
'pattern to wrap the test code that generated the error:\n' +
" 'allowConsoleError(() => { <code that generated the " +
"error> });'\n" +
' ⤷ If the error is expected (and asynchronous), use the ' +
'following pattern at the top of the test:\n' +
" 'expectAsyncConsoleError(<string or regex>[, <number of" +
' times the error message repeats>]);';
originalConsoleError(errorMessage + "'\n" + helpMessage);
}
/**
* Used during normal test execution, to detect unexpected console errors.
*/
export function warnForConsoleError() {
expectedAsyncErrors = [];
consoleErrorSandbox = sinon.createSandbox();
const consoleErrorStub = consoleErrorSandbox
.stub(console, 'error')
.callsFake(printWarning);
self.expectAsyncConsoleError = function (message, repeat = 1) {
expectedAsyncErrors.push.apply(
expectedAsyncErrors,
Array(repeat).fill(message)
);
};
self.allowConsoleError = function (func) {
consoleErrorStub.reset();
consoleErrorStub.callsFake(() => {});
const result = func();
try {
expect(consoleErrorStub).to.have.been.called;
} catch (e) {
const helpMessage =
'The test "' +
testName +
'" contains an "allowConsoleError" block ' +
"that didn't result in a call to console.error.";
originalConsoleError(helpMessage);
} finally {
consoleErrorStub.callsFake(printWarning);
}
return result;
};
}
/**
* Used to restore error level logging after each test.
*/
export function restoreConsoleError() {
consoleErrorSandbox.restore();
if (expectedAsyncErrors.length > 0) {
const helpMessage =
'The test "' +
testName +
'" called "expectAsyncConsoleError", ' +
'but there were no call(s) to console.error with these message(s): ' +
'"' +
expectedAsyncErrors.join('", "') +
'"';
testRunner.test.error(new Error(helpMessage));
}
expectedAsyncErrors = [];
}
/**
* Used to silence info, log, and warn level logging during each test, unless
* verbose mode is enabled.
*/
export function maybeStubConsoleInfoLogWarn() {
const {verboseLogging} = window.__karma__.config;
if (!verboseLogging) {
consoleInfoLogWarnSandbox = sinon.createSandbox();
consoleInfoLogWarnSandbox.stub(console, 'info').callsFake(() => {});
consoleInfoLogWarnSandbox.stub(console, 'log').callsFake(() => {});
consoleInfoLogWarnSandbox.stub(console, 'warn').callsFake(() => {});
}
}
let loggerSandbox;
/**
* Used to silence "production" logs from logger
*/
export function stubLogger() {
loggerSandbox = sinon.createSandbox();
// Ensure we do not log anything (even if --verbose is enabled)
loggerSandbox.stub(logger);
// Ensure error logs still get reported properly:
logger.error.callsFake(console.error.bind(console));
}
export function restoreLogger() {
loggerSandbox?.restore();
}