errorreporter_test.js 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397
  1. // Copyright 2009 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. goog.provide('goog.debug.ErrorReporterTest');
  15. goog.setTestOnly('goog.debug.ErrorReporterTest');
  16. goog.require('goog.debug.Error');
  17. goog.require('goog.debug.ErrorReporter');
  18. goog.require('goog.events');
  19. goog.require('goog.functions');
  20. goog.require('goog.testing.PropertyReplacer');
  21. goog.require('goog.testing.TestCase');
  22. goog.require('goog.testing.jsunit');
  23. goog.require('goog.userAgent');
  24. goog.require('goog.userAgent.product');
  25. MockXhrIo = function() {};
  26. MockXhrIo.prototype.onReadyStateChangeEntryPoint_ = function() {};
  27. MockXhrIo.protectEntryPoints = function() {};
  28. MockXhrIo.lastUrl = null;
  29. MockXhrIo.send = function(
  30. url, opt_callback, opt_method, opt_content, opt_headers, opt_timeInterval) {
  31. MockXhrIo.lastUrl = url;
  32. MockXhrIo.lastContent = opt_content;
  33. MockXhrIo.lastHeaders = opt_headers;
  34. };
  35. var errorReporter;
  36. var originalSetTimeout = window.setTimeout;
  37. var stubs = new goog.testing.PropertyReplacer();
  38. var url = 'http://www.your.tst/more/bogus.js';
  39. var encodedUrl = 'http%3A%2F%2Fwww.your.tst%2Fmore%2Fbogus.js';
  40. function setUp() {
  41. // TODO(b/25875505): Fix unreported assertions (go/failonunreportedasserts).
  42. goog.testing.TestCase.getActiveTestCase().failOnUnreportedAsserts = false;
  43. stubs.set(goog.net, 'XhrIo', MockXhrIo);
  44. goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT = true;
  45. }
  46. function tearDown() {
  47. goog.dispose(errorReporter);
  48. stubs.reset();
  49. MockXhrIo.lastUrl = null;
  50. }
  51. function throwAnErrorWith(script, line, message, opt_stack) {
  52. var error = {message: message, fileName: script, lineNumber: line};
  53. if (opt_stack) {
  54. error['stack'] = opt_stack;
  55. }
  56. throw error;
  57. }
  58. function testsendErrorReport() {
  59. errorReporter = new goog.debug.ErrorReporter('/log');
  60. errorReporter.sendErrorReport('message', 'filename.js', 123, 'trace');
  61. assertEquals(
  62. '/log?script=filename.js&error=message&line=123', MockXhrIo.lastUrl);
  63. assertEquals('trace=trace', MockXhrIo.lastContent);
  64. }
  65. function testsendErrorReportWithCustomSender() {
  66. var uri = null;
  67. var method = null;
  68. var content = null;
  69. var headers = null;
  70. function mockXhrSender(uriIn, methodIn, contentIn, headersIn) {
  71. uri = uriIn;
  72. method = methodIn;
  73. content = contentIn;
  74. headers = headersIn;
  75. }
  76. errorReporter = new goog.debug.ErrorReporter('/log');
  77. errorReporter.setXhrSender(mockXhrSender);
  78. errorReporter.sendErrorReport('message', 'filename.js', 123, 'trace');
  79. assertEquals('/log?script=filename.js&error=message&line=123', uri);
  80. assertEquals('POST', method);
  81. assertEquals('trace=trace', content);
  82. assertUndefined(headers);
  83. }
  84. function testsendErrorReport_noTrace() {
  85. errorReporter = new goog.debug.ErrorReporter('/log');
  86. errorReporter.sendErrorReport('message', 'filename.js', 123);
  87. assertEquals(
  88. '/log?script=filename.js&error=message&line=123', MockXhrIo.lastUrl);
  89. assertEquals('', MockXhrIo.lastContent);
  90. }
  91. function test_nonInternetExplorerSendErrorReport() {
  92. if (goog.userAgent.product.SAFARI) {
  93. // TODO(b/20733468): Disabled so we can get the rest of the Closure test
  94. // suite running in a continuous build. Will investigate later.
  95. return;
  96. }
  97. stubs.set(goog.userAgent, 'IE', false);
  98. stubs.set(goog.global, 'setTimeout', function(fcn, time) { fcn.call(); });
  99. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  100. var errorFunction = goog.partial(throwAnErrorWith, url, 5, 'Hello :)');
  101. try {
  102. goog.global.setTimeout(errorFunction, 0);
  103. } catch (e) {
  104. // Expected. The error is rethrown after sending.
  105. }
  106. assertEquals(
  107. '/errorreporter?script=' + encodedUrl + '&error=Hello%20%3A)&line=5',
  108. MockXhrIo.lastUrl);
  109. assertEquals('trace=Not%20available', MockXhrIo.lastContent);
  110. }
  111. function test_internetExplorerSendErrorReport() {
  112. stubs.set(goog.userAgent, 'IE', true);
  113. stubs.set(goog.userAgent, 'isVersionOrHigher', goog.functions.FALSE);
  114. // Remove test runner's onerror handler so the test doesn't fail.
  115. stubs.set(goog.global, 'onerror', null);
  116. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  117. goog.global.onerror('Goodbye :(', url, 22);
  118. assertEquals(
  119. '/errorreporter?script=' + encodedUrl + '&error=Goodbye%20%3A(&line=22',
  120. MockXhrIo.lastUrl);
  121. assertEquals('trace=Not%20available', MockXhrIo.lastContent);
  122. }
  123. function test_setLoggingHeaders() {
  124. stubs.set(goog.userAgent, 'IE', true);
  125. stubs.set(goog.userAgent, 'isVersionOrHigher', goog.functions.FALSE);
  126. // Remove test runner's onerror handler so the test doesn't fail.
  127. stubs.set(goog.global, 'onerror', null);
  128. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  129. errorReporter.setLoggingHeaders('header!');
  130. goog.global.onerror('Goodbye :(', 'http://www.your.tst/more/bogus.js', 22);
  131. assertEquals('header!', MockXhrIo.lastHeaders);
  132. }
  133. function test_nonInternetExplorerSendErrorReportWithTrace() {
  134. stubs.set(goog.userAgent, 'IE', false);
  135. stubs.set(goog.global, 'setTimeout', function(fcn, time) { fcn.call(); });
  136. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  137. var trace = 'Error(\"Something Wrong\")@:0\n' +
  138. '$MF$E$Nx$([object Object])@http://a.b.c:83/a/f.js:901\n' +
  139. '([object Object])@http://a.b.c:813/a/f.js:37';
  140. var errorFunction = goog.partial(throwAnErrorWith, url, 5, 'Hello :)', trace);
  141. try {
  142. goog.global.setTimeout(errorFunction, 0);
  143. } catch (e) {
  144. // Expected. The error is rethrown after sending.
  145. }
  146. assertEquals(
  147. '/errorreporter?script=' + encodedUrl + '&error=Hello%20%3A)&line=5',
  148. MockXhrIo.lastUrl);
  149. assertEquals(
  150. 'trace=' +
  151. 'Error(%22Something%20Wrong%22)%40%3A0%0A' +
  152. '%24MF%24E%24Nx%24(%5Bobject%20Object%5D)%40' +
  153. 'http%3A%2F%2Fa.b.c%3A83%2Fa%2Ff.js%3A901%0A' +
  154. '(%5Bobject%20Object%5D)%40http%3A%2F%2Fa.b.c%3A813%2Fa%2Ff.js%3A37',
  155. MockXhrIo.lastContent);
  156. }
  157. function testProtectAdditionalEntryPoint_nonIE() {
  158. stubs.set(goog.userAgent, 'IE', false);
  159. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  160. var fn = function() {};
  161. var protectedFn = errorReporter.protectAdditionalEntryPoint(fn);
  162. assertNotNull(protectedFn);
  163. assertNotEquals(fn, protectedFn);
  164. }
  165. function testProtectAdditionalEntryPoint_IE() {
  166. stubs.set(goog.userAgent, 'IE', true);
  167. stubs.set(goog.userAgent, 'isVersionOrHigher', goog.functions.FALSE);
  168. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  169. var fn = function() {};
  170. var protectedFn = errorReporter.protectAdditionalEntryPoint(fn);
  171. assertNull(protectedFn);
  172. }
  173. function testHandleException_dispatchesEvent() {
  174. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  175. var loggedErrors = 0;
  176. goog.events.listen(
  177. errorReporter, goog.debug.ErrorReporter.ExceptionEvent.TYPE,
  178. function(event) {
  179. assertNotNullNorUndefined(event.error);
  180. loggedErrors++;
  181. });
  182. errorReporter.handleException(new Error());
  183. errorReporter.handleException(new Error());
  184. assertEquals(
  185. 'Expected 2 errors. ' +
  186. '(Ensure an exception was not swallowed.)',
  187. 2, loggedErrors);
  188. }
  189. function testHandleException_includesContext() {
  190. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  191. var loggedErrors = 0;
  192. var testError = new Error('test error');
  193. var testContext = {'contextParam': 'contextValue'};
  194. goog.events.listen(
  195. errorReporter, goog.debug.ErrorReporter.ExceptionEvent.TYPE,
  196. function(event) {
  197. assertNotNullNorUndefined(event.error);
  198. assertObjectEquals({contextParam: 'contextValue'}, event.context);
  199. loggedErrors++;
  200. });
  201. errorReporter.handleException(testError, testContext);
  202. assertEquals(
  203. 'Expected 1 error. ' +
  204. '(Ensure an exception was not swallowed.)',
  205. 1, loggedErrors);
  206. }
  207. function testContextProvider() {
  208. errorReporter = goog.debug.ErrorReporter.install(
  209. '/errorreporter',
  210. function(error, context) { context.providedContext = 'value'; });
  211. var loggedErrors = 0;
  212. var testError = new Error('test error');
  213. goog.events.listen(
  214. errorReporter, goog.debug.ErrorReporter.ExceptionEvent.TYPE,
  215. function(event) {
  216. assertNotNullNorUndefined(event.error);
  217. assertObjectEquals({providedContext: 'value'}, event.context);
  218. loggedErrors++;
  219. });
  220. errorReporter.handleException(testError);
  221. assertEquals(
  222. 'Expected 1 error. ' +
  223. '(Ensure an exception was not swallowed.)',
  224. 1, loggedErrors);
  225. }
  226. function testContextProvider_withOtherContext() {
  227. errorReporter = goog.debug.ErrorReporter.install(
  228. '/errorreporter',
  229. function(error, context) { context.providedContext = 'value'; });
  230. var loggedErrors = 0;
  231. var testError = new Error('test error');
  232. goog.events.listen(
  233. errorReporter, goog.debug.ErrorReporter.ExceptionEvent.TYPE,
  234. function(event) {
  235. assertNotNullNorUndefined(event.error);
  236. assertObjectEquals(
  237. {providedContext: 'value', otherContext: 'value'}, event.context);
  238. loggedErrors++;
  239. });
  240. errorReporter.handleException(testError, {'otherContext': 'value'});
  241. assertEquals(
  242. 'Expected 1 error. ' +
  243. '(Ensure an exception was not swallowed.)',
  244. 1, loggedErrors);
  245. }
  246. function testHandleException_ignoresExceptionsDuringEventDispatch() {
  247. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  248. goog.events.listen(
  249. errorReporter, goog.debug.ErrorReporter.ExceptionEvent.TYPE,
  250. function(event) { fail('This exception should be swallowed.'); });
  251. errorReporter.handleException(new Error());
  252. }
  253. function testHandleException_doNotReportErrorToServer() {
  254. var error = new goog.debug.Error();
  255. error.reportErrorToServer = false;
  256. errorReporter.handleException(error);
  257. assertNull(MockXhrIo.lastUrl);
  258. }
  259. function testDisposal() {
  260. errorReporter = goog.debug.ErrorReporter.install('/errorreporter');
  261. if (!goog.userAgent.IE) {
  262. assertNotEquals(originalSetTimeout, window.setTimeout);
  263. }
  264. goog.dispose(errorReporter);
  265. assertEquals(originalSetTimeout, window.setTimeout);
  266. }
  267. function testSetContextPrefix() {
  268. errorReporter = new goog.debug.ErrorReporter('/log');
  269. errorReporter.setContextPrefix('baz.');
  270. errorReporter.sendErrorReport(
  271. 'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
  272. assertEquals('trace=trace&baz.foo=bar', MockXhrIo.lastContent);
  273. }
  274. function testTruncationLimit() {
  275. errorReporter = new goog.debug.ErrorReporter('/log');
  276. errorReporter.setTruncationLimit(6);
  277. errorReporter.sendErrorReport(
  278. 'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
  279. assertEquals('trace=', MockXhrIo.lastContent);
  280. }
  281. function testZeroTruncationLimit() {
  282. errorReporter = new goog.debug.ErrorReporter('/log');
  283. errorReporter.setTruncationLimit(0);
  284. errorReporter.sendErrorReport(
  285. 'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
  286. assertEquals('', MockXhrIo.lastContent);
  287. }
  288. function testTruncationLimitLargerThanBody() {
  289. errorReporter = new goog.debug.ErrorReporter('/log');
  290. errorReporter.setTruncationLimit(9999);
  291. errorReporter.sendErrorReport(
  292. 'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
  293. assertEquals('trace=trace&context.foo=bar', MockXhrIo.lastContent);
  294. }
  295. function testSetNegativeTruncationLimit() {
  296. errorReporter = new goog.debug.ErrorReporter('/log');
  297. assertThrows(function() { errorReporter.setTruncationLimit(-10); });
  298. }
  299. function testSetTruncationLimitNull() {
  300. errorReporter = new goog.debug.ErrorReporter('/log');
  301. errorReporter.setTruncationLimit(null);
  302. errorReporter.sendErrorReport(
  303. 'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
  304. assertEquals('trace=trace&context.foo=bar', MockXhrIo.lastContent);
  305. }
  306. function testAttemptAutoProtectWithAllowAutoProtectOff() {
  307. goog.debug.ErrorReporter.ALLOW_AUTO_PROTECT = false;
  308. assertThrows(function() {
  309. errorReporter =
  310. new goog.debug.ErrorReporter('/log', function(e, context) {}, false);
  311. });
  312. }
  313. function testSetAdditionalArgumentsArgsEmptyObject() {
  314. errorReporter = new goog.debug.ErrorReporter('/log');
  315. errorReporter.setAdditionalArguments({});
  316. errorReporter.sendErrorReport(
  317. 'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
  318. assertEquals(
  319. '/log?script=filename.js&error=message&line=123', MockXhrIo.lastUrl);
  320. }
  321. function testSetAdditionalArgumentsSingleArgument() {
  322. errorReporter = new goog.debug.ErrorReporter('/log');
  323. errorReporter.setAdditionalArguments({'extra': 'arg'});
  324. errorReporter.sendErrorReport(
  325. 'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
  326. assertEquals(
  327. '/log?script=filename.js&error=message&line=123&extra=arg',
  328. MockXhrIo.lastUrl);
  329. }
  330. function testSetAdditionalArgumentsMultipleArguments() {
  331. errorReporter = new goog.debug.ErrorReporter('/log');
  332. errorReporter.setAdditionalArguments({'extra': 'arg', 'cat': 'dog'});
  333. errorReporter.sendErrorReport(
  334. 'message', 'filename.js', 123, 'trace', {'foo': 'bar'});
  335. assertEquals(
  336. '/log?script=filename.js&error=message&line=123&extra=arg&cat=dog',
  337. MockXhrIo.lastUrl);
  338. }