blob: 0e49e7ceac2ad0f1d5e434835c61274eca844cb7 [file] [log] [blame]
// Copyright 2009 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
goog.provide('goog.debug.ErrorReporterTest');
goog.setTestOnly('goog.debug.ErrorReporterTest');
goog.require('goog.debug.ErrorReporter');
goog.require('goog.events');
goog.require('goog.functions');
goog.require('goog.testing.PropertyReplacer');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
MockXhrIo = function() {};
MockXhrIo.prototype.onReadyStateChangeEntryPoint_ = function() {};
MockXhrIo.protectEntryPoints = function() {};
MockXhrIo.lastUrl = null;
MockXhrIo.send = function(url, opt_callback, opt_method, opt_content,
opt_headers, opt_timeInterval) {
MockXhrIo.lastUrl = url;
MockXhrIo.lastContent = opt_content;
MockXhrIo.lastHeaders = opt_headers;
};
var errorReporter;
var originalSetTimeout = window.setTimeout;
var stubs = new goog.testing.PropertyReplacer();
var url = 'http://www.your.tst/more/bogus.js';
var encodedUrl = 'http%3A%2F%2Fwww.your.tst%2Fmore%2Fbogus.js';
function setUp() {
stubs.set(goog.net, 'XhrIo', MockXhrIo);
goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT = true;
}
function tearDown() {
goog.dispose(errorReporter);
stubs.reset();
MockXhrIo.lastUrl = null;
}
function throwAnErrorWith(script, line, message, opt_stack) {
var error = {
message: message,
fileName: script,
lineNumber: line};
if (opt_stack) {
error['stack'] = opt_stack;
}
throw error;
}
function testsendErrorReport() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace');
assertEquals('/log?script=filename.js&error=message&line=123',
MockXhrIo.lastUrl);
assertEquals('trace=trace', MockXhrIo.lastContent);
}
function testsendErrorReportWithCustomSender() {
var uri = null;
var method = null;
var content = null;
var headers = null;
function mockXhrSender(uriIn, methodIn, contentIn, headersIn) {
uri = uriIn;
method = methodIn;
content = contentIn;
headers = headersIn;
}
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setXhrSender(mockXhrSender);
errorReporter.sendErrorReport('message', 'filename.js', 123, 'trace');
assertEquals('/log?script=filename.js&error=message&line=123', uri);
assertEquals('POST', method);
assertEquals('trace=trace', content);
assertUndefined(headers);
}
function testsendErrorReport_noTrace() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.sendErrorReport(
'message', 'filename.js', 123);
assertEquals('/log?script=filename.js&error=message&line=123',
MockXhrIo.lastUrl);
assertEquals('', MockXhrIo.lastContent);
}
function test_nonInternetExplorerSendErrorReport() {
stubs.set(goog.userAgent, 'IE', false);
stubs.set(goog.global, 'setTimeout',
function(fcn, time) {
fcn.call();
});
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
var errorFunction = goog.partial(throwAnErrorWith, url, 5, 'Hello :)');
try {
goog.global.setTimeout(errorFunction, 0);
} catch (e) {
// Expected. The error is rethrown after sending.
}
assertEquals(
'/errorreporter?script=' + encodedUrl + '&error=Hello%20%3A)&line=5',
MockXhrIo.lastUrl);
assertEquals('trace=Not%20available', MockXhrIo.lastContent);
}
function test_internetExplorerSendErrorReport() {
stubs.set(goog.userAgent, 'IE', true);
stubs.set(goog.userAgent, 'isVersionOrHigher', goog.functions.FALSE);
// Remove test runner's onerror handler so the test doesn't fail.
stubs.set(goog.global, 'onerror', null);
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
goog.global.onerror('Goodbye :(', url, 22);
assertEquals(
'/errorreporter?script=' + encodedUrl + '&error=Goodbye%20%3A(&line=22',
MockXhrIo.lastUrl);
assertEquals('trace=Not%20available', MockXhrIo.lastContent);
}
function test_setLoggingHeaders() {
stubs.set(goog.userAgent, 'IE', true);
stubs.set(goog.userAgent, 'isVersionOrHigher', goog.functions.FALSE);
// Remove test runner's onerror handler so the test doesn't fail.
stubs.set(goog.global, 'onerror', null);
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
errorReporter.setLoggingHeaders('header!');
goog.global.onerror('Goodbye :(', 'http://www.your.tst/more/bogus.js', 22);
assertEquals('header!', MockXhrIo.lastHeaders);
}
function test_nonInternetExplorerSendErrorReportWithTrace() {
stubs.set(goog.userAgent, 'IE', false);
stubs.set(goog.global, 'setTimeout',
function(fcn, time) {
fcn.call();
});
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
var trace =
'Error(\"Something Wrong\")@:0\n' +
'$MF$E$Nx$([object Object])@http://a.b.c:83/a/f.js:901\n' +
'([object Object])@http://a.b.c:813/a/f.js:37';
var errorFunction = goog.partial(throwAnErrorWith, url, 5, 'Hello :)',
trace);
try {
goog.global.setTimeout(errorFunction, 0);
} catch (e) {
// Expected. The error is rethrown after sending.
}
assertEquals(
'/errorreporter?script=' + encodedUrl + '&error=Hello%20%3A)&line=5',
MockXhrIo.lastUrl);
assertEquals('trace=' +
'Error(%22Something%20Wrong%22)%40%3A0%0A' +
'%24MF%24E%24Nx%24(%5Bobject%20Object%5D)%40' +
'http%3A%2F%2Fa.b.c%3A83%2Fa%2Ff.js%3A901%0A' +
'(%5Bobject%20Object%5D)%40http%3A%2F%2Fa.b.c%3A813%2Fa%2Ff.js%3A37',
MockXhrIo.lastContent);
}
function testProtectAdditionalEntryPoint_nonIE() {
stubs.set(goog.userAgent, 'IE', false);
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
var fn = function() {};
var protectedFn = errorReporter.protectAdditionalEntryPoint(fn);
assertNotNull(protectedFn);
assertNotEquals(fn, protectedFn);
}
function testProtectAdditionalEntryPoint_IE() {
stubs.set(goog.userAgent, 'IE', true);
stubs.set(goog.userAgent, 'isVersionOrHigher', goog.functions.FALSE);
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
var fn = function() {};
var protectedFn = errorReporter.protectAdditionalEntryPoint(fn);
assertNull(protectedFn);
}
function testHandleException_dispatchesEvent() {
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
var loggedErrors = 0;
goog.events.listen(errorReporter,
goog.debug.ErrorReporter.ExceptionEvent.TYPE,
function(event) {
assertNotNullNorUndefined(event.error);
loggedErrors++;
});
errorReporter.handleException(new Error());
errorReporter.handleException(new Error());
assertEquals('Expected 2 errors. ' +
'(Ensure an exception was not swallowed.)', 2, loggedErrors);
}
function testHandleException_includesContext() {
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
var loggedErrors = 0;
var testError = new Error('test error');
var testContext = { 'contextParam' : 'contextValue' };
goog.events.listen(errorReporter,
goog.debug.ErrorReporter.ExceptionEvent.TYPE,
function(event) {
assertNotNullNorUndefined(event.error);
assertObjectEquals({ contextParam: 'contextValue' }, event.context);
loggedErrors++;
});
errorReporter.handleException(testError, testContext);
assertEquals('Expected 1 error. ' +
'(Ensure an exception was not swallowed.)', 1, loggedErrors);
}
function testContextProvider() {
errorReporter = goog.debug.ErrorReporter.install('/errorreporter',
function(error, context) {
context.providedContext = 'value';
});
var loggedErrors = 0;
var testError = new Error('test error');
goog.events.listen(errorReporter,
goog.debug.ErrorReporter.ExceptionEvent.TYPE,
function(event) {
assertNotNullNorUndefined(event.error);
assertObjectEquals({ providedContext: 'value' } , event.context);
loggedErrors++;
});
errorReporter.handleException(testError);
assertEquals('Expected 1 error. ' +
'(Ensure an exception was not swallowed.)', 1, loggedErrors);
}
function testContextProvider_withOtherContext() {
errorReporter = goog.debug.ErrorReporter.install('/errorreporter',
function(error, context) {
context.providedContext = 'value';
});
var loggedErrors = 0;
var testError = new Error('test error');
goog.events.listen(errorReporter,
goog.debug.ErrorReporter.ExceptionEvent.TYPE,
function(event) {
assertNotNullNorUndefined(event.error);
assertObjectEquals(
{ providedContext: 'value', otherContext: 'value' },
event.context);
loggedErrors++;
});
errorReporter.handleException(testError, { 'otherContext' : 'value' });
assertEquals('Expected 1 error. ' +
'(Ensure an exception was not swallowed.)', 1, loggedErrors);
}
function testHandleException_ignoresExceptionsDuringEventDispatch() {
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
goog.events.listen(errorReporter,
goog.debug.ErrorReporter.ExceptionEvent.TYPE,
function(event) {
fail('This exception should be swallowed.');
});
errorReporter.handleException(new Error());
}
function testDisposal() {
errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
if (!goog.userAgent.IE) {
assertNotEquals(originalSetTimeout, window.setTimeout);
}
goog.dispose(errorReporter);
assertEquals(originalSetTimeout, window.setTimeout);
}
function testSetContextPrefix() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setContextPrefix('baz.');
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
assertEquals('trace=trace&baz.foo=bar', MockXhrIo.lastContent);
}
function testTruncationLimit() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setTruncationLimit(6);
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
assertEquals('trace=', MockXhrIo.lastContent);
}
function testZeroTruncationLimit() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setTruncationLimit(0);
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
assertEquals('', MockXhrIo.lastContent);
}
function testTruncationLimitLargerThanBody() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setTruncationLimit(9999);
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
assertEquals('trace=trace&context.foo=bar', MockXhrIo.lastContent);
}
function testSetNegativeTruncationLimit() {
errorReporter = new goog.debug.ErrorReporter('/log');
assertThrows(function() {
errorReporter.setTruncationLimit(-10);
});
}
function testSetTruncationLimitNull() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setTruncationLimit(null);
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
assertEquals('trace=trace&context.foo=bar', MockXhrIo.lastContent);
}
function testAttemptAutoProtectWithAllowAutoProtectOff() {
goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT = false;
assertThrows(function() {
errorReporter = new goog.debug.ErrorReporter(
'/log', function(e, context) {}, false);
});
}
function testSetAdditionalArgumentsArgsEmptyObject() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setAdditionalArguments({});
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
assertEquals('/log?script=filename.js&error=message&line=123',
MockXhrIo.lastUrl);
}
function testSetAdditionalArgumentsSingleArgument() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setAdditionalArguments({'extra': 'arg'});
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
assertEquals('/log?script=filename.js&error=message&line=123&extra=arg',
MockXhrIo.lastUrl);
}
function testSetAdditionalArgumentsMultipleArguments() {
errorReporter = new goog.debug.ErrorReporter('/log');
errorReporter.setAdditionalArguments({'extra': 'arg', 'cat': 'dog'});
errorReporter.sendErrorReport(
'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
assertEquals(
'/log?script=filename.js&error=message&line=123&extra=arg&cat=dog',
MockXhrIo.lastUrl);
}