From f64a8c7b6d8262f4b6ed14581a09192f9203d5b9 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Wed, 4 Nov 2015 13:08:57 -0800 Subject: [PATCH 01/14] Convert raven.js to CommonJS; use browserify for UMD build --- .jshintrc | 3 ++- Gruntfile.js | 39 ++++++++++++++++++++----------------- package.json | 1 + src/raven.js | 14 ++++++++----- vendor/TraceKit/tracekit.js | 2 ++ 5 files changed, 35 insertions(+), 24 deletions(-) diff --git a/.jshintrc b/.jshintrc index 6a123502afdb..0049909dbfe8 100644 --- a/.jshintrc +++ b/.jshintrc @@ -3,8 +3,9 @@ "globalstrict": true, "browser": true, "predef": [ - "TraceKit", "console", + "module", + "require", "_slice" ] } diff --git a/Gruntfile.js b/Gruntfile.js index 54c18d450e4b..bded8096522e 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -4,13 +4,6 @@ module.exports = function(grunt) { var _ = require('lodash'); var path = require('path'); - var coreFiles = [ - 'template/_header.js', - 'vendor/**/*.js', - 'src/**/*.js', - 'template/_footer.js' - ]; - var excludedPlugins = [ 'react-native' ]; @@ -65,7 +58,7 @@ module.exports = function(grunt) { key.sort(); var dest = path.join('build/', key.join(','), '/raven.js'); - dict[dest] = coreFiles.concat(comb); + dict[dest] = ['build/raven.js'].concat(comb); return dict; }, {}); @@ -78,18 +71,26 @@ module.exports = function(grunt) { concat: { options: { separator: '\n', - banner: grunt.file.read('template/_copyright.js'), process: true }, - core: { - src: coreFiles.concat(plugins), - dest: 'build/raven.js' - }, - all: { + plugins: { files: pluginConcatFiles } }, + browserify: { + core: { + src: 'src/raven.js', + dest: 'build/raven.js', + options: { + banner: grunt.file.read('template/_copyright.js'), + browserifyOptions: { + standalone: 'Raven' // umd + } + } + } + }, + uglify: { options: { sourceMap: function (dest) { @@ -258,6 +259,8 @@ module.exports = function(grunt) { grunt.loadNpmTasks('grunt-contrib-copy'); // 3rd party Grunt tasks + grunt.loadNpmTasks('grunt-browserify'); + grunt.loadNpmTasks('grunt-exorcise'); grunt.loadNpmTasks('grunt-mocha'); grunt.loadNpmTasks('grunt-release'); grunt.loadNpmTasks('grunt-s3'); @@ -266,10 +269,10 @@ module.exports = function(grunt) { // Build tasks grunt.registerTask('_prep', ['clean', 'gitinfo', 'version']); - grunt.registerTask('concat.core', ['_prep', 'concat:core']); - grunt.registerTask('concat.all', ['_prep', 'concat:all']); - grunt.registerTask('build.core', ['concat.core', 'uglify', 'fixSourceMaps', 'sri:dist']); - grunt.registerTask('build.all', ['concat.all', 'uglify', 'fixSourceMaps', 'sri:dist', 'sri:build']); + grunt.registerTask('concat.core', ['browserify', 'exorcise']); + grunt.registerTask('concat.plugins', ['concat:plugins']); + grunt.registerTask('build.core', ['_prep', 'concat.core', 'uglify', 'fixSourceMaps', 'sri:dist']); + grunt.registerTask('build.all', ['_prep', 'concat.core', 'concat.plugins', 'uglify', 'fixSourceMaps', 'sri:dist', 'sri:build']); grunt.registerTask('build', ['build.all']); grunt.registerTask('dist', ['build.core', 'copy:dist']); diff --git a/package.json b/package.json index 7fa452898888..b9d6dd69786c 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,7 @@ "devDependencies": { "chai": "~1.8.1", "grunt": "^0.4.5", + "grunt-browserify": "^4.0.1", "grunt-cli": "~0.1.9", "grunt-contrib-clean": "~0.4.0", "grunt-contrib-concat": "~0.3.0", diff --git a/src/raven.js b/src/raven.js index 0f1d5d21f2be..6965d485ee55 100644 --- a/src/raven.js +++ b/src/raven.js @@ -1,6 +1,8 @@ /*global XDomainRequest:false*/ 'use strict'; +var TraceKit = require('../vendor/TraceKit/tracekit'); + // First, check for JSON support // If there is no JSON, we no-op the core features of Raven // since JSON is required to encode the payload @@ -44,7 +46,9 @@ for (var method in originalConsole) { var Raven = { VERSION: '<%= pkg.version %>', - debug: false, + TraceKit: TraceKit, // temporarily exported for tests + + debug: true, /* * Allow multiple versions of Raven to be installed. @@ -977,16 +981,14 @@ function uuid4() { function logDebug(level) { if (originalConsoleMethods[level] && Raven.debug) { - // _slice is coming from vendor/TraceKit/tracekit.js - // so it's accessible globally - originalConsoleMethods[level].apply(originalConsole, _slice.call(arguments, 1)); + originalConsoleMethods[level].apply(originalConsole, [].slice.call(arguments, 1)); } } function afterLoad() { // Attempt to initialize Raven on load var RavenConfig = window.RavenConfig; - if (RavenConfig) { + if (RavenConfig) { Raven.config(RavenConfig.dsn, RavenConfig.config).install(); } } @@ -1008,3 +1010,5 @@ function mergeContext(key, context) { } afterLoad(); + +module.exports = Raven; \ No newline at end of file diff --git a/vendor/TraceKit/tracekit.js b/vendor/TraceKit/tracekit.js index 208c912b1b31..e9e12a564f7b 100644 --- a/vendor/TraceKit/tracekit.js +++ b/vendor/TraceKit/tracekit.js @@ -1052,3 +1052,5 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return computeStackTrace; }()); + +module.exports = TraceKit; \ No newline at end of file From 906b224dbde4ddc8bd7b4f88fa346a9b733dced8 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Wed, 4 Nov 2015 15:29:20 -0800 Subject: [PATCH 02/14] Use browserify to generate plugin versions of raven --- Gruntfile.js | 56 +++++++++++++++++++++++++++------------------ package.json | 3 ++- plugins/angular.js | 53 +++++++++++++++++++++--------------------- template/_footer.js | 19 --------------- template/_header.js | 2 -- 5 files changed, 62 insertions(+), 71 deletions(-) delete mode 100644 template/_footer.js delete mode 100644 template/_header.js diff --git a/Gruntfile.js b/Gruntfile.js index bded8096522e..8ab01b877636 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -1,8 +1,10 @@ + module.exports = function(grunt) { "use strict"; var _ = require('lodash'); var path = require('path'); + var through = require('through2'); var excludedPlugins = [ 'react-native' @@ -58,7 +60,7 @@ module.exports = function(grunt) { key.sort(); var dest = path.join('build/', key.join(','), '/raven.js'); - dict[dest] = ['build/raven.js'].concat(comb); + dict[dest] = ['src/raven.js'].concat(comb); return dict; }, {}); @@ -68,26 +70,38 @@ module.exports = function(grunt) { aws: grunt.file.exists('aws.json') ? grunt.file.readJSON('aws.json'): {}, clean: ['build'], - concat: { - options: { - separator: '\n', - process: true - }, - plugins: { - files: pluginConcatFiles - } - }, browserify: { + options: { + browserifyOptions: { + banner: grunt.file.read('template/_copyright.js'), + standalone: 'Raven', // umd + }, + transform: [ + [ + // custom transformer to re-write plugins to self-register + // with Raven + new function () { + return function (file, options) { + return through(function (buf, enc, next) { + var buf = buf.toString('utf8'); + if (/plugins/.test(file)) { + buf += "\nRaven.addPlugin(module.exports.install);"; + } + this.push(buf); + next(); + }); + } + } + ] + ] + }, core: { src: 'src/raven.js', dest: 'build/raven.js', - options: { - banner: grunt.file.read('template/_copyright.js'), - browserifyOptions: { - standalone: 'Raven' // umd - } - } + }, + plugins: { + files: pluginConcatFiles } }, @@ -252,7 +266,6 @@ module.exports = function(grunt) { // Grunt contrib tasks grunt.loadNpmTasks('grunt-contrib-uglify'); - grunt.loadNpmTasks('grunt-contrib-concat'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-connect'); @@ -260,7 +273,6 @@ module.exports = function(grunt) { // 3rd party Grunt tasks grunt.loadNpmTasks('grunt-browserify'); - grunt.loadNpmTasks('grunt-exorcise'); grunt.loadNpmTasks('grunt-mocha'); grunt.loadNpmTasks('grunt-release'); grunt.loadNpmTasks('grunt-s3'); @@ -269,10 +281,10 @@ module.exports = function(grunt) { // Build tasks grunt.registerTask('_prep', ['clean', 'gitinfo', 'version']); - grunt.registerTask('concat.core', ['browserify', 'exorcise']); - grunt.registerTask('concat.plugins', ['concat:plugins']); - grunt.registerTask('build.core', ['_prep', 'concat.core', 'uglify', 'fixSourceMaps', 'sri:dist']); - grunt.registerTask('build.all', ['_prep', 'concat.core', 'concat.plugins', 'uglify', 'fixSourceMaps', 'sri:dist', 'sri:build']); + grunt.registerTask('browserify.core', ['_prep', 'browserify:core']); + grunt.registerTask('browserify.plugins', ['_prep', 'browserify:plugins']); + grunt.registerTask('build.core', ['browserify.core', 'uglify', 'fixSourceMaps', 'sri:dist']); + grunt.registerTask('build.all', ['browserify.plugins', 'uglify', 'fixSourceMaps', 'sri:dist', 'sri:build']); grunt.registerTask('build', ['build.all']); grunt.registerTask('dist', ['build.core', 'copy:dist']); diff --git a/package.json b/package.json index b9d6dd69786c..ada0b0107765 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "grunt-sri": "mattrobenolt/grunt-sri#pretty", "jquery": "^2.1.4", "lodash": "~2.4.0", - "sinon": "~1.7.3" + "sinon": "~1.7.3", + "through2": "^2.0.0" }, "keywords": [ "exceptions", diff --git a/plugins/angular.js b/plugins/angular.js index dd929f5cddeb..aaf68e4907a8 100644 --- a/plugins/angular.js +++ b/plugins/angular.js @@ -3,14 +3,8 @@ * * Provides an $exceptionHandler for Angular.js */ -;(function(window) { -'use strict'; -var angular = window.angular, - Raven = window.Raven; - -// quit if angular isn't on the page -if (!(angular && Raven)) return; +var Raven = require('../src/raven'); function RavenProvider() { this.$get = ['$window', function($window, $log) { @@ -35,28 +29,33 @@ function exceptionHandler(Raven, $delegate) { // See https://github.com/angular/angular.js/blob/v1.4.7/src/minErr.js var angularPattern = /^\[((?:[$a-zA-Z0-9]+:)?(?:[$a-zA-Z0-9]+))\] (.+?)\n(\S+)$/; -Raven.addPlugin(function () { +function install() { + var angular = window.angular; + if (!angular) return; + angular.module('ngRaven', []) .provider('Raven', RavenProvider) .config(['$provide', ExceptionHandlerProvider]); -}); - -Raven.setDataCallback(function(data) { - // We only care about mutating an exception - var exception = data.exception; - if (exception) { - exception = exception.values[0]; - var matches = angularPattern.exec(exception.value); - - if (matches) { - // This type now becomes something like: $rootScope:inprog - exception.type = matches[1]; - exception.value = matches[2]; - data.message = exception.type + ': ' + exception.value; - // auto set a new tag specifically for the angular error url - data.extra.angularDocs = matches[3].substr(0, 250); + + Raven.setDataCallback(function(data) { + // We only care about mutating an exception + var exception = data.exception; + if (exception) { + exception = exception.values[0]; + var matches = angularPattern.exec(exception.value); + + if (matches) { + // This type now becomes something like: $rootScope:inprog + exception.type = matches[1]; + exception.value = matches[2]; + data.message = exception.type + ': ' + exception.value; + // auto set a new tag specifically for the angular error url + data.extra.angularDocs = matches[3].substr(0, 250); + } } - } -}); + }); +} -}(typeof window !== 'undefined' ? window : this)); +module.exports = { + install: install +}; diff --git a/template/_footer.js b/template/_footer.js deleted file mode 100644 index b6583ef31388..000000000000 --- a/template/_footer.js +++ /dev/null @@ -1,19 +0,0 @@ -// This is being exposed no matter what because there are too many weird -// usecases for how people use Raven. If this is really a problem, I'm sorry. -window.Raven = Raven; - -// Expose Raven to the world -if (typeof define === 'function' && define.amd) { - // AMD - define('raven', [], function() { - return Raven; - }); -} else if (typeof module === 'object') { - // browserify - module.exports = Raven; -} else if (typeof exports === 'object') { - // CommonJS - exports = Raven; -} - -})(typeof window !== 'undefined' ? window : this); diff --git a/template/_header.js b/template/_header.js deleted file mode 100644 index 27595a61a2db..000000000000 --- a/template/_header.js +++ /dev/null @@ -1,2 +0,0 @@ -;(function(window, undefined){ -'use strict'; From 3941ce8ff938584274b1ea825c8a48cbbe5540c6 Mon Sep 17 00:00:00 2001 From: Ben Vinegar Date: Thu, 5 Nov 2015 16:27:38 -0800 Subject: [PATCH 03/14] Fix raven, ulils, tracekit tests --- .jshintrc | 3 +- Gruntfile.js | 26 +- src/raven.js | 261 +++++----- src/utils.js | 89 ++++ test/index.html | 11 +- test/plugins/jquery.test.js | 1 + test/raven.test.js | 892 +++++++++++++++-------------------- test/utils.test.js | 88 ++++ test/vendor/tracekit.test.js | 142 ++++++ transports/xhr.js | 35 ++ vendor/TraceKit/tracekit.js | 11 +- 11 files changed, 903 insertions(+), 656 deletions(-) create mode 100644 src/utils.js create mode 100644 test/utils.test.js create mode 100644 test/vendor/tracekit.test.js create mode 100644 transports/xhr.js diff --git a/.jshintrc b/.jshintrc index 0049909dbfe8..79444bf25963 100644 --- a/.jshintrc +++ b/.jshintrc @@ -5,7 +5,6 @@ "predef": [ "console", "module", - "require", - "_slice" + "require" ] } diff --git a/Gruntfile.js b/Gruntfile.js index 8ab01b877636..bfef9edca981 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -76,7 +76,14 @@ module.exports = function(grunt) { browserifyOptions: { banner: grunt.file.read('template/_copyright.js'), standalone: 'Raven', // umd - }, + } + }, + core: { + src: 'src/raven.js', + dest: 'build/raven.js', + }, + plugins: { + files: pluginConcatFiles, transform: [ [ // custom transformer to re-write plugins to self-register @@ -96,12 +103,14 @@ module.exports = function(grunt) { ] ] }, - core: { - src: 'src/raven.js', - dest: 'build/raven.js', - }, - plugins: { - files: pluginConcatFiles + test: { + src: 'test/**/*.test.js', + dest: 'build/raven.test.js', + options: { + browserifyOptions: { + debug: true // source maps + } + } } }, @@ -283,13 +292,14 @@ module.exports = function(grunt) { grunt.registerTask('_prep', ['clean', 'gitinfo', 'version']); grunt.registerTask('browserify.core', ['_prep', 'browserify:core']); grunt.registerTask('browserify.plugins', ['_prep', 'browserify:plugins']); + grunt.registerTask('build.test', ['_prep', 'browserify:test']); grunt.registerTask('build.core', ['browserify.core', 'uglify', 'fixSourceMaps', 'sri:dist']); grunt.registerTask('build.all', ['browserify.plugins', 'uglify', 'fixSourceMaps', 'sri:dist', 'sri:build']); grunt.registerTask('build', ['build.all']); grunt.registerTask('dist', ['build.core', 'copy:dist']); // Test task - grunt.registerTask('test', ['jshint', 'mocha']); + grunt.registerTask('test', ['jshint', 'mocha', 'browserify.core', 'browserify:test']); // Webserver tasks grunt.registerTask('run:test', ['connect:test']); diff --git a/src/raven.js b/src/raven.js index 6965d485ee55..f7988719b2e9 100644 --- a/src/raven.js +++ b/src/raven.js @@ -2,6 +2,17 @@ 'use strict'; var TraceKit = require('../vendor/TraceKit/tracekit'); +var utils = require('./utils'); + +var isFunction = utils.isFunction; +var isUndefined = utils.isUndefined; +var isError = utils.isError; +var isString = utils.isString; +var isEmptyObject = utils.isEmptyObject; +var hasKey = utils.hasKey; +var each = utils.each; +var objectMerge = utils.objectMerge; +var truncate = utils.truncate; // First, check for JSON support // If there is no JSON, we no-op the core features of Raven @@ -27,7 +38,6 @@ var _Raven = window.Raven, maxMessageLength: 100 }, isRavenInstalled = false, - objectPrototype = Object.prototype, // capture references to window.console *and* all its methods first // before the console plugin has a chance to monkey patch originalConsole = window.console || {}, @@ -70,12 +80,12 @@ var Raven = { */ config: function(dsn, options) { if (globalServer) { - logDebug('error', 'Error: Raven has already been configured'); + Raven._logDebug('error', 'Error: Raven has already been configured'); return Raven; } if (!dsn) return Raven; - var uri = parseDSN(dsn), + var uri = Raven._parseDSN(dsn), lastSlash = uri.path.lastIndexOf('/'), path = uri.path.substr(1, lastSlash); @@ -139,8 +149,8 @@ var Raven = { * @return {Raven} */ install: function() { - if (isSetup() && !isRavenInstalled) { - TraceKit.report.subscribe(handleStackInfo); + if (Raven.isSetup() && !isRavenInstalled) { + TraceKit.report.subscribe(Raven._handleStackInfo); // Install all of the plugins each(plugins, function(_, plugin) { @@ -268,7 +278,7 @@ var Raven = { // report on. try { var stack = TraceKit.computeStackTrace(ex); - handleStackInfo(stack, options); + Raven._handleStackInfo(stack, options); } catch(ex1) { if(ex !== ex1) { throw ex1; @@ -294,7 +304,7 @@ var Raven = { } // Fire away! - send( + Raven._send( objectMerge({ message: msg + '' // Make sure it's actually a string }, options) @@ -444,7 +454,14 @@ var Raven = { * @return {boolean} */ isSetup: function() { - return isSetup(); + if (!hasJSON) return false; // needs JSON support + if (!globalServer) { + if (!ravenNotConfiguredError) + Raven._logDebug('error', 'Error: Raven has not been configured.'); + ravenNotConfiguredError = true; + return false; + } + return true; } }; @@ -498,7 +515,7 @@ RavenConfigError.prototype = new Error(); RavenConfigError.prototype.constructor = RavenConfigError; /**** Private functions ****/ -function parseDSN(str) { +Raven._parseDSN = function(str) { var m = dsnPattern.exec(str), dsn = {}, i = 7; @@ -513,73 +530,14 @@ function parseDSN(str) { throw new RavenConfigError('Do not specify your private key in the DSN!'); return dsn; -} - -function isUndefined(what) { - return what === void 0; -} - -function isFunction(what) { - return typeof what === 'function'; -} - -function isString(what) { - return objectPrototype.toString.call(what) === '[object String]'; -} - -function isObject(what) { - return typeof what === 'object' && what !== null; -} - -function isEmptyObject(what) { - for (var k in what) return false; - return true; -} - -// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560 -// with some tiny modifications -function isError(what) { - return isObject(what) && - objectPrototype.toString.call(what) === '[object Error]' || - what instanceof Error; -} - -/** - * hasKey, a better form of hasOwnProperty - * Example: hasKey(MainHostObject, property) === true/false - * - * @param {Object} host object to check property - * @param {string} key to check - */ -function hasKey(object, key) { - return objectPrototype.hasOwnProperty.call(object, key); -} - -function each(obj, callback) { - var i, j; - - if (isUndefined(obj.length)) { - for (i in obj) { - if (hasKey(obj, i)) { - callback.call(null, i, obj[i]); - } - } - } else { - j = obj.length; - if (j) { - for (i = 0; i < j; i++) { - callback.call(null, i, obj[i]); - } - } - } -} +}; -function handleStackInfo(stackInfo, options) { +Raven._handleStackInfo = function(stackInfo, options) { var frames = []; if (stackInfo.stack && stackInfo.stack.length) { each(stackInfo.stack, function(i, stack) { - var frame = normalizeFrame(stack); + var frame = Raven._normalizeFrame(stack); if (frame) { frames.push(frame); } @@ -591,7 +549,7 @@ function handleStackInfo(stackInfo, options) { options: options }); - processException( + Raven._processException( stackInfo.name, stackInfo.message, stackInfo.url, @@ -599,9 +557,9 @@ function handleStackInfo(stackInfo, options) { frames, options ); -} +}; -function normalizeFrame(frame) { +Raven._normalizeFrame = function(frame) { if (!frame.url) return; // normalize the frames data @@ -610,7 +568,7 @@ function normalizeFrame(frame) { lineno: frame.line, colno: frame.column, 'function': frame.func || '?' - }, context = extractContextFromFrame(frame), i; + }, context = Raven._extractContextFromFrame(frame), i; if (context) { var keys = ['pre_context', 'context_line', 'post_context']; @@ -628,9 +586,9 @@ function normalizeFrame(frame) { ); return normalized; -} +}; -function extractContextFromFrame(frame) { +Raven._extractContextFromFrame = function(frame) { // immediately check if we should even attempt to parse a context if (!frame.context || !globalOptions.fetchContext) return; @@ -667,9 +625,9 @@ function extractContextFromFrame(frame) { context[pivot], // context_line context.slice(pivot + 1) // post_context ]; -} +}; -function processException(type, message, fileurl, lineno, frames, options) { +Raven._processException = function(type, message, fileurl, lineno, frames, options) { var stacktrace, i, fullMessage; if (!!globalOptions.ignoreErrors.test && globalOptions.ignoreErrors.test(message)) return; @@ -697,7 +655,7 @@ function processException(type, message, fileurl, lineno, frames, options) { if (!!globalOptions.whitelistUrls.test && !globalOptions.whitelistUrls.test(fileurl)) return; // Fire away! - send( + Raven._send( objectMerge({ // sentry.interfaces.Exception exception: { @@ -711,21 +669,7 @@ function processException(type, message, fileurl, lineno, frames, options) { message: fullMessage }, options) ); -} - -function objectMerge(obj1, obj2) { - if (!obj2) { - return obj1; - } - each(obj2, function(key, value){ - obj1[key] = value; - }); - return obj1; -} - -function truncate(str, max) { - return str.length <= max ? str : str.substr(0, max) + '\u2026'; -} +}; function trimPacket(data) { // For now, we only want to truncate the two different messages @@ -744,7 +688,7 @@ function now() { return +new Date(); } -function getHttpData() { +Raven._getHttpData = function() { if (!hasDocument || !document.location || !document.location.href) { return; } @@ -762,14 +706,14 @@ function getHttpData() { } return httpData; -} +}; -function send(data) { +Raven._send = function(data) { var baseData = { project: globalProject, logger: globalOptions.logger, platform: 'javascript' - }, httpData = getHttpData(); + }, httpData = Raven._getHttpData(); if (httpData) { baseData.request = httpData; @@ -814,16 +758,16 @@ function send(data) { // Send along an event_id if not explicitly passed. // This event_id can be used to reference the error within Sentry itself. // Set lastEventId after we know the error should actually be sent - lastEventId = data.event_id || (data.event_id = uuid4()); + lastEventId = data.event_id || (data.event_id = Raven._uuid4()); // Try and clean up the packet before sending by truncating long values data = trimPacket(data); - logDebug('debug', 'Raven about to send:', data); + Raven._logDebug('debug', 'Raven about to send:', data); - if (!isSetup()) return; + if (!Raven.isSetup()) return; - (globalOptions.transport || makeRequest)({ + (globalOptions.transport || Raven._makeRequest)({ url: globalServer, auth: { sentry_version: '7', @@ -845,13 +789,13 @@ function send(data) { }); } }); -} +}; -function makeImageRequest(opts) { +Raven._makeImageRequest = function(opts) { // Tack on sentry_data to auth options, which get urlencoded opts.auth.sentry_data = JSON.stringify(opts.data); - var img = newImage(), + var img = Raven._newImage(), src = opts.url + '?' + urlencode(opts.auth), crossOrigin = opts.options.crossOrigin; @@ -861,9 +805,9 @@ function makeImageRequest(opts) { img.onload = opts.onSuccess; img.onerror = img.onabort = opts.onError; img.src = src; -} +}; -function makeXhrRequest(opts) { +Raven._makeXhrRequest = function(opts) { var request; function handler() { @@ -896,34 +840,23 @@ function makeXhrRequest(opts) { request.send(JSON.stringify(opts.data)); } -function makeRequest(opts) { +Raven._makeRequest = function(opts) { var hasCORS = 'withCredentials' in new XMLHttpRequest() || typeof XDomainRequest !== 'undefined'; - return (hasCORS ? makeXhrRequest : makeImageRequest)(opts); + return (hasCORS ? Raven._makeXhrRequest : Raven._makeImageRequest)(opts); } // Note: this is shitty, but I can't figure out how to get // sinon to stub document.createElement without breaking everything // so this wrapper is just so I can stub it for tests. -function newImage() { +Raven._newImage = function() { return document.createElement('img'); -} +}; var ravenNotConfiguredError; -function isSetup() { - if (!hasJSON) return false; // needs JSON support - if (!globalServer) { - if (!ravenNotConfiguredError) - logDebug('error', 'Error: Raven has not been configured.'); - ravenNotConfiguredError = true; - return false; - } - return true; -} - function joinRegExp(patterns) { // Combine an array of regular expressions and strings into one large regexp // Be mad. @@ -946,7 +879,7 @@ function joinRegExp(patterns) { return new RegExp(sources.join('|'), 'i'); } -function uuid4() { +Raven._uuid4 = function() { var crypto = window.crypto || window.msCrypto; if (!isUndefined(crypto) && crypto.getRandomValues) { @@ -979,11 +912,11 @@ function uuid4() { } } -function logDebug(level) { +Raven._logDebug = function(level) { if (originalConsoleMethods[level] && Raven.debug) { originalConsoleMethods[level].apply(originalConsole, [].slice.call(arguments, 1)); } -} +}; function afterLoad() { // Attempt to initialize Raven on load @@ -1011,4 +944,78 @@ function mergeContext(key, context) { afterLoad(); -module.exports = Raven; \ No newline at end of file +module.exports = Raven; + +if (typeof TEST !== 'undefined' && TEST) { + Raven._test = { + // methods + afterLoad: afterLoad, + joinRegExp: joinRegExp, + urlencode: urlencode, + + // objects / primitives + globalOptions: globalOptions, + originalConsoleMethods: originalConsoleMethods, + + // classes + RavenConfigError: RavenConfigError, + + setGlobalState: function (state) { + if ('isRavenInstalled' in state) + isRavenInstalled = state.isRavenInstalled; + if ('hasJSON' in state) + hasJSON = state.hasJSON; + if ('globalProject' in state) + globalProject = state.globalProject; + if ('globalOptions' in state) + globalOptions = state.globalOptions; + if ('globalServer' in state) + globalServer = state.globalServer; + if ('globalContext' in state) + globalContext = state.globalContext; + }, + + getGlobalState: function () { + return { + isRavenInstalled: isRavenInstalled, + globalProject: globalProject, + globalOptions: globalOptions, + globalServer: globalServer, + globalContext: globalContext, + globalKey: globalKey, + originalConsoleMethods: originalConsoleMethods + }; + }, + + flushState: function () { + hasJSON = !isUndefined(window.JSON); + hasDocument = !isUndefined(document); + lastCapturedException = undefined; + lastEventId = undefined; + globalServer = undefined; + globalProject = undefined; + globalContext = {}; + globalOptions = { + logger: 'javascript', + release: undefined, + ignoreErrors: [], + ignoreUrls: [], + whitelistUrls: [], + includePaths: [], + crossOrigin: 'anonymous', + collectWindowErrors: true, + maxMessageLength: 100 + }; + startTime = 0; + ravenNotConfiguredError = undefined; + originalConsole = window.console || {}; + originalConsoleMethods = {}; + + for (var method in originalConsole) { + originalConsoleMethods[method] = originalConsole[method]; + } + + Raven.uninstall(); + } + }; +} diff --git a/src/utils.js b/src/utils.js new file mode 100644 index 000000000000..ff157dbfb507 --- /dev/null +++ b/src/utils.js @@ -0,0 +1,89 @@ +'use strict'; + +var objectPrototype = Object.prototype; + +function isUndefined(what) { + return what === void 0; +} + +function isFunction(what) { + return typeof what === 'function'; +} + +function isString(what) { + return objectPrototype.toString.call(what) === '[object String]'; +} + +function isObject(what) { + return typeof what === 'object' && what !== null; +} + +function isEmptyObject(what) { + for (var k in what) return false; + return true; +} + +// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560 +// with some tiny modifications +function isError(what) { + return isObject(what) && + objectPrototype.toString.call(what) === '[object Error]' || + what instanceof Error; +} + +function each(obj, callback) { + var i, j; + + if (isUndefined(obj.length)) { + for (i in obj) { + if (hasKey(obj, i)) { + callback.call(null, i, obj[i]); + } + } + } else { + j = obj.length; + if (j) { + for (i = 0; i < j; i++) { + callback.call(null, i, obj[i]); + } + } + } +} + +function objectMerge(obj1, obj2) { + if (!obj2) { + return obj1; + } + each(obj2, function(key, value){ + obj1[key] = value; + }); + return obj1; +} + +function truncate(str, max) { + return str.length <= max ? str : str.substr(0, max) + '\u2026'; +} + +/** + * hasKey, a better form of hasOwnProperty + * Example: hasKey(MainHostObject, property) === true/false + * + * @param {Object} host object to check property + * @param {string} key to check + */ +function hasKey(object, key) { + return objectPrototype.hasOwnProperty.call(object, key); +} + +module.exports = { + isUndefined: isUndefined, + isFunction: isFunction, + isString: isString, + isObject: isObject, + isEmptyObject: isEmptyObject, + isError: isError, + each: each, + objectMerge: objectMerge, + truncate: truncate, + hasKey: hasKey +}; diff --git a/test/index.html b/test/index.html index d988e2313b9c..6f6abe2277fa 100644 --- a/test/index.html +++ b/test/index.html @@ -19,6 +19,7 @@ - - - + + + - - + + - - - - - -