diff --git a/src/raven.js b/src/raven.js index df66adb37e34..bfca257ac3ff 100644 --- a/src/raven.js +++ b/src/raven.js @@ -23,7 +23,6 @@ var _Raven = window.Raven, maxMessageLength: 100, extra: {} }, - authQueryString, isRavenInstalled = false, objectPrototype = Object.prototype, // capture references to window.console *and* all its methods first @@ -114,8 +113,6 @@ var Raven = { TraceKit.collectWindowErrors = !!globalOptions.collectWindowErrors; - setAuthQueryString(); - // return for chaining return Raven; }, @@ -508,15 +505,6 @@ function each(obj, callback) { } } - -function setAuthQueryString() { - authQueryString = - '?sentry_version=4' + - '&sentry_client=raven-js/' + Raven.VERSION + - '&sentry_key=' + globalKey; -} - - function handleStackInfo(stackInfo, options) { var frames = []; @@ -748,35 +736,46 @@ function send(data) { // Set lastEventId after we know the error should actually be sent lastEventId = data.event_id || (data.event_id = uuid4()); - makeRequest(data); -} + logDebug('debug', 'Raven about to send:', data); + if (!isSetup()) return; -function makeRequest(data) { - var img, - src; + makeRequest({ + url: globalServer, + auth: { + sentry_version: '4', + sentry_client: 'raven-js/' + Raven.VERSION, + sentry_key: globalKey + }, + data: data, + options: globalOptions, + onSuccess: function success() { + triggerEvent('success', { + data: data, + src: globalServer + }); + }, + onError: function failure() { + triggerEvent('failure', { + data: data, + src: globalServer + }); + } + }); +} - logDebug('debug', 'Raven about to send:', data); +function makeRequest(opts) { + // Tack on sentry_data to auth options, which get urlencoded + opts.auth.sentry_data = JSON.stringify(opts.data); - if (!isSetup()) return; + var img = newImage(), + src = opts.url + '?' + urlencode(opts.auth); - img = newImage(); - src = globalServer + authQueryString + '&sentry_data=' + encodeURIComponent(JSON.stringify(data)); - if (globalOptions.crossOrigin || globalOptions.crossOrigin === '') { - img.crossOrigin = globalOptions.crossOrigin; + if (opts.options.crossOrigin || opts.options.crossOrigin === '') { + img.crossOrigin = opts.options.crossOrigin; } - img.onload = function success() { - triggerEvent('success', { - data: data, - src: src - }); - }; - img.onerror = img.onabort = function failure() { - triggerEvent('failure', { - data: data, - src: src - }); - }; + img.onload = opts.onSuccess; + img.onerror = img.onabort = opts.onError; img.src = src; } @@ -870,4 +869,13 @@ function afterLoad() { Raven.config(RavenConfig.dsn, RavenConfig.config).install(); } } + +function urlencode(o) { + var pairs = []; + each(o, function(key, value) { + pairs.push(encodeURIComponent(key) + '=' + encodeURIComponent(value)); + }); + return pairs.join('&'); +} + afterLoad(); diff --git a/test/raven.test.js b/test/raven.test.js index 6c55c5f10916..f90da7aafc93 100644 --- a/test/raven.test.js +++ b/test/raven.test.js @@ -1,5 +1,4 @@ function flushRavenState() { - authQueryString = undefined; hasJSON = !isUndefined(window.JSON); lastCapturedException = undefined; lastEventId = undefined; @@ -360,14 +359,6 @@ describe('globals', function() { }); }); - describe('setAuthQueryString', function() { - it('should return a properly formatted string and cache it', function() { - var expected = '?sentry_version=4&sentry_client=raven-js/<%= pkg.version %>&sentry_key=abc'; - setAuthQueryString(); - assert.strictEqual(authQueryString, expected); - }); - }); - describe('parseDSN', function() { it('should do what it advertises', function() { var pieces = parseDSN('http://abc@example.com:80/2'); @@ -881,7 +872,7 @@ describe('globals', function() { }; send({foo: 'bar'}); - assert.deepEqual(window.makeRequest.lastCall.args[0], { + assert.deepEqual(window.makeRequest.lastCall.args[0].data, { project: '2', logger: 'javascript', platform: 'javascript', @@ -913,7 +904,7 @@ describe('globals', function() { globalUser = {name: 'Matt'}; send({foo: 'bar'}); - assert.deepEqual(window.makeRequest.lastCall.args, [{ + assert.deepEqual(window.makeRequest.lastCall.args[0].data, { project: '2', logger: 'javascript', platform: 'javascript', @@ -929,7 +920,7 @@ describe('globals', function() { }, foo: 'bar', extra: {'session:duration': 100} - }]); + }); }); it('should merge in global tags', function() { @@ -948,7 +939,7 @@ describe('globals', function() { send({tags: {tag2: 'value2'}}); - assert.deepEqual(window.makeRequest.lastCall.args, [{ + assert.deepEqual(window.makeRequest.lastCall.args[0].data, { project: '2', logger: 'javascript', platform: 'javascript', @@ -961,7 +952,7 @@ describe('globals', function() { event_id: 'abc123', tags: {tag1: 'value1', tag2: 'value2'}, extra: {'session:duration': 100} - }]); + }); assert.deepEqual(globalOptions, { logger: 'javascript', tags: {tag1: 'value1'} @@ -984,7 +975,7 @@ describe('globals', function() { send({extra: {key2: 'value2'}}); - assert.deepEqual(window.makeRequest.lastCall.args, [{ + assert.deepEqual(window.makeRequest.lastCall.args[0].data, { project: '2', logger: 'javascript', platform: 'javascript', @@ -996,7 +987,7 @@ describe('globals', function() { }, event_id: 'abc123', extra: {key1: 'value1', key2: 'value2', 'session:duration': 100} - }]); + }); assert.deepEqual(globalOptions, { logger: 'javascript', extra: {key1: 'value1'} @@ -1018,10 +1009,10 @@ describe('globals', function() { globalUser = {name: 'Matt'}; send({foo: 'bar'}); - assert.deepEqual(window.makeRequest.lastCall.args, [{ + assert.deepEqual(window.makeRequest.lastCall.args[0].data, { lol: 'ibrokeit', event_id: 'abc123', - }]); + }); }); it('should ignore dataCallback if it does not return anything', function() { @@ -1041,7 +1032,7 @@ describe('globals', function() { }; send({foo: 'bar'}); - assert.deepEqual(window.makeRequest.lastCall.args[0], { + assert.deepEqual(window.makeRequest.lastCall.args[0].data, { project: '2', logger: 'javascript', platform: 'javascript', @@ -1072,7 +1063,7 @@ describe('globals', function() { }; send({foo: 'bar', tags: {}, extra: {}}); - assert.deepEqual(window.makeRequest.lastCall.args[0], { + assert.deepEqual(window.makeRequest.lastCall.args[0].data, { project: '2', logger: 'javascript', platform: 'javascript', @@ -1103,7 +1094,7 @@ describe('globals', function() { }; send({foo: 'bar'}); - assert.deepEqual(window.makeRequest.lastCall.args[0], { + assert.deepEqual(window.makeRequest.lastCall.args[0].data, { project: '2', release: 'abc123', logger: 'javascript', @@ -1119,49 +1110,105 @@ describe('globals', function() { extra: {'session:duration': 100} }); }); - }); - describe('makeRequest', function() { - var imageCache; + it('should pass correct opts to makeRequest', function() { + this.sinon.stub(window, 'isSetup').returns(true); + this.sinon.stub(window, 'makeRequest'); + this.sinon.stub(window, 'getHttpData').returns({ + url: 'http://localhost/?a=b', + headers: {'User-Agent': 'lolbrowser'} + }); - beforeEach(function () { - imageCache = []; - this.sinon.stub(window, 'newImage', function(){ var img = {}; imageCache.push(img); return img; }); - }) + globalServer = 'http://localhost/store/'; + + globalOptions = { + projectId: 2, + logger: 'javascript', + release: 'abc123', + }; + + send({foo: 'bar'}); + var args = window.makeRequest.lastCall.args; + assert.equal(args.length, 1); + var opts = args[0]; + assert.equal(opts.url, 'http://localhost/store/'); + assert.deepEqual(opts.data, { + project: '2', + release: 'abc123', + logger: 'javascript', + platform: 'javascript', + request: { + url: 'http://localhost/?a=b', + headers: { + 'User-Agent': 'lolbrowser' + } + }, + event_id: 'abc123', + foo: 'bar', + extra: {'session:duration': 100}, + }); + assert.deepEqual(opts.auth, { + sentry_client: 'raven-js/<%= pkg.version %>', + sentry_key: 'abc', + sentry_version: '4' + }); + assert.deepEqual(opts.options, globalOptions); + assert.isFunction(opts.onSuccess); + assert.isFunction(opts.onError); + }); it('should check `isSetup`', function() { this.sinon.stub(window, 'isSetup').returns(false); - makeRequest({foo: 'bar'}); + this.sinon.stub(window, 'makeRequest'); + send({foo: 'bar'}); assert.isTrue(window.isSetup.called); }); - it('should not create the image if `isSetup` is false', function() { + it('should not makeRequest if `isSetup` is false', function() { this.sinon.stub(window, 'isSetup').returns(false); - makeRequest({foo: 'bar'}); - assert.isFalse(window.newImage.called); + this.sinon.stub(window, 'makeRequest'); + send({foo: 'bar'}); + assert.isFalse(window.makeRequest.called); }); it('should log to console', function() { this.sinon.stub(window, 'isSetup').returns(true); this.sinon.stub(window, 'logDebug'); - makeRequest({foo: 'bar'}); + this.sinon.stub(window, 'makeRequest'); + send({foo: 'bar'}); assert.isTrue(window.logDebug.called); }); + }); - it('should load an Image', function() { - authQueryString = '?lol'; - globalServer = 'http://localhost/'; + describe('makeRequest', function() { + var imageCache; + + beforeEach(function () { + imageCache = []; + this.sinon.stub(window, 'newImage', function(){ var img = {}; imageCache.push(img); return img; }); + }) - makeRequest({foo: 'bar'}); + it('should load an Image', function() { + makeRequest({ + url: 'http://localhost/', + auth: {a: '1', b: '2'}, + data: {foo: 'bar'}, + options: globalOptions + }); assert.equal(imageCache.length, 1); - assert.equal(imageCache[0].src, 'http://localhost/?lol&sentry_data=%7B%22foo%22%3A%22bar%22%7D'); + assert.equal(imageCache[0].src, 'http://localhost/?a=1&b=2&sentry_data=%7B%22foo%22%3A%22bar%22%7D'); }); it('should populate crossOrigin based on globalOptions', function() { globalOptions = { crossOrigin: 'something' }; - makeRequest({foo: 'bar'}); + makeRequest({ + url: globalServer, + auth: {lol: '1'}, + data: {foo: 'bar'}, + options: globalOptions + }); assert.equal(imageCache.length, 1); assert.equal(imageCache[0].crossOrigin, 'something'); }); @@ -1170,7 +1217,12 @@ describe('globals', function() { globalOptions = { crossOrigin: '' }; - makeRequest({foo: 'bar'}); + makeRequest({ + url: globalServer, + auth: {lol: '1'}, + data: {foo: 'bar'}, + options: globalOptions + }); assert.equal(imageCache.length, 1); assert.equal(imageCache[0].crossOrigin, ''); }); @@ -1179,7 +1231,12 @@ describe('globals', function() { globalOptions = { crossOrigin: false }; - makeRequest({foo: 'bar'}); + makeRequest({ + url: globalServer, + auth: {lol: '1'}, + data: {foo: 'bar'}, + options: globalOptions + }); assert.equal(imageCache.length, 1); assert.isUndefined(imageCache[0].crossOrigin); }); @@ -1360,6 +1417,13 @@ describe('globals', function() { ]).source, 'a|b|a\\.b|d|[0-9]'); }); }); + + describe('urlencode', function() { + it('should work', function() { + assert.equal(urlencode({}), ''); + assert.equal(urlencode({'foo': 'bar', 'baz': '1 2'}), 'foo=bar&baz=1%202'); + }); + }); }); describe('Raven (public API)', function() {