diff --git a/docs/install.rst b/docs/install.rst
index 21bc032813b4..b7473f0ca223 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -8,10 +8,10 @@ So for example:
.. sourcecode:: html
-
-
-
-
+
+
+
+
This allows the ability for Raven's integrations to instrument themselves. If
included before something like Angular, it'd be impossible to use for
@@ -28,7 +28,7 @@ Our CDN distributes builds with and without :doc:`integrations
+
This version does not include any plugins. See `ravenjs.com
`_ for more information about plugins and getting
@@ -45,11 +45,11 @@ add it to ``bower.json``).
.. code-block:: sh
- $ bower install raven-js --save
+ $ bower install raven-js --save
.. code-block:: html
-
+
Also note that the file is uncompresed but is ready to pass to any decent
JavaScript compressor like `UglifyJS
@@ -63,7 +63,7 @@ Raven is also available as an npm package, `raven-js
.. code-block:: sh
- $ npm install raven-js --save
+ $ npm install raven-js --save
.. code-block:: html
@@ -96,6 +96,37 @@ To use Raven with ES2015 (ES6) imports:
.config('___PUBLIC_DSN___')
.install();
+Async Loading
+~~~~~~~~~~~~~
+
+To load Sentry JS SDK asynchronously, you need to do two things.
+
+Provide global ``SENTRY_SDK`` variable with SDK's URL (for example from our CDN), your DSN and SDK's configuration.
+And place the snippet below as soon as possible in your HTML code. For example:
+
+.. code-block:: html
+
+
+
+Or you can place those two things in a separate script tags. This will queue all errors (and promises if the environment supports ``unhandledrejection`` handler) that happened before SDK was loaded and send them once it's configured and installed.
+
+Be aware however, that there are some trade-offs to this solution, as errors might provide less information due to them being "retriggered" instead of being caught from the original source.
+
+NOTE: This won't work when opening ``index.html`` or any other html file from the file system, as it doesn't support anonymous cross-origin scripts.
+The same thing can happen for any cross-origin scripts as well. To read more about it, see `What the heck is "Script error"?`_.
+
+To read un-minified source code for this loader, see `loader.js`_
+
Requirements
~~~~~~~~~~~~
diff --git a/karma.sauce.config.js b/karma.sauce.config.js
deleted file mode 100644
index bf363bb359c5..000000000000
--- a/karma.sauce.config.js
+++ /dev/null
@@ -1,117 +0,0 @@
-var commonConfig = require('./karma.config');
-
-var customLaunchers = {
- sl_chrome: {
- base: 'SauceLabs',
- browserName: 'chrome',
- platform: 'Windows 10',
- version: 'latest'
- },
- sl_firefox: {
- base: 'SauceLabs',
- browserName: 'firefox',
- platform: 'Windows 10',
- version: 'latest'
- },
- sl_edge: {
- base: 'SauceLabs',
- browserName: 'microsoftedge',
- version: 'latest',
- platform: 'Windows 10'
- },
- sl_ie_11: {
- base: 'SauceLabs',
- browserName: 'internet explorer',
- platform: 'Windows 7',
- version: '11'
- },
- sl_ie_10: {
- base: 'SauceLabs',
- browserName: 'internet explorer',
- platform: 'Windows 7',
- version: '10'
- },
- sl_safari: {
- base: 'SauceLabs',
- browserName: 'safari',
- platform: 'OS X 10.12',
- version: '11.0'
- },
- sl_ios: {
- base: 'SauceLabs',
- browserName: 'iphone',
- platform: 'OS X 10.12',
- version: '11.0'
- },
- sl_android_7: {
- base: 'SauceLabs',
- browserName: 'Chrome',
- platform: 'Android',
- version: '7.1',
- device: 'Android GoogleAPI Emulator'
- },
- sl_android_6: {
- base: 'SauceLabs',
- browserName: 'Chrome',
- platform: 'Android',
- version: '6.0',
- device: 'Android Emulator'
- },
- sl_android_5: {
- base: 'SauceLabs',
- browserName: 'android',
- platform: 'Linux',
- version: '5.1'
- },
- sl_android_4: {
- base: 'SauceLabs',
- browserName: 'android',
- platform: 'Linux',
- version: '4.4'
- }
-};
-
-var testFiles = [
- {pattern: 'node_modules/es6-promise/dist/es6-promise.auto.js', included: false},
- {pattern: 'node_modules/whatwg-fetch/fetch.js', included: false},
- {pattern: 'test/integration/123', included: false},
- {pattern: 'test/integration/throw-string.js', included: false},
- {pattern: 'test/integration/throw-error.js', included: false},
- {pattern: 'test/integration/throw-object.js', included: false},
- {pattern: 'test/integration/example.json', included: false},
- {pattern: 'test/integration/frame.html', included: false},
- 'test/integration/test.js',
- 'test/globals.js',
- 'build/raven.js',
- 'build/raven.test.js'
-];
-
-module.exports = function(config) {
- var testConfig = Object.assign({}, commonConfig, {
- files: testFiles,
- logLevel: config.LOG_INFO,
- customLaunchers: customLaunchers,
- browsers: Object.keys(customLaunchers),
- reporters: ['failed', 'saucelabs'],
- singleRun: true,
- plugins: commonConfig.plugins.concat(['karma-sauce-launcher']),
- build: process.env.TRAVIS_BUILD_NUMBER,
- // SauceLabs allows for 2 tunnels only, therefore some browsers will have to wait
- // rather long time. Plus mobile emulators tend to require a lot of time to start up.
- // 10 minutes should be more than enough to run all of them.
- browserNoActivityTimeout: 600000,
- captureTimeout: 600000,
- sauceLabs: {
- startConnect: false,
- // Just something "random" so we don't have to provide additional ENV var when running locally
- tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER || Math.ceil(Math.random() * 1337),
- recordScreenshots: false,
- recordVideo: false,
- testName:
- 'Raven.js' +
- (process.env.TRAVIS_JOB_NUMBER ? ' #' + process.env.TRAVIS_JOB_NUMBER : ''),
- public: 'public'
- }
- });
- config.set(testConfig);
-};
diff --git a/karma.unit.config.js b/karma.unit.config.js
deleted file mode 100644
index d6f468af2f3a..000000000000
--- a/karma.unit.config.js
+++ /dev/null
@@ -1,13 +0,0 @@
-var commonConfig = require('./karma.config');
-
-var testFiles = ['test/globals.js', 'build/raven.test.js'];
-
-module.exports = function(config) {
- var testConfig = Object.assign(
- {},
- commonConfig,
- {files: testFiles},
- {logLevel: config.LOG_INFO}
- );
- config.set(testConfig);
-};
diff --git a/karma.config.js b/karma/karma.config.js
similarity index 91%
rename from karma.config.js
rename to karma/karma.config.js
index f1500ba0e4e3..40cbf4cb7901 100644
--- a/karma.config.js
+++ b/karma/karma.config.js
@@ -3,7 +3,7 @@
module.exports = {
// base path that will be used to resolve all patterns (eg. files, exclude)
- basePath: '',
+ basePath: '../',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
@@ -58,9 +58,12 @@ module.exports = {
}
},
+ // https://docs.travis-ci.com/user/gui-and-headless-browsers/#Karma-and-Firefox-inactivity-timeouts
+ browserNoActivityTimeout: 30000,
+
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
- singleRun: false,
+ singleRun: true,
// Concurrency level
// how many browser should be started simultaneous
diff --git a/karma/karma.integration-sauce.config.js b/karma/karma.integration-sauce.config.js
new file mode 100644
index 000000000000..19611e51fe6c
--- /dev/null
+++ b/karma/karma.integration-sauce.config.js
@@ -0,0 +1,10 @@
+var commonSauceConfig = require('./karma.sauce.config');
+var files = require('./karma.integration.config').files;
+
+module.exports = function(config) {
+ var testConfig = Object.assign({}, commonSauceConfig, {
+ logLevel: config.LOG_INFO,
+ files: files.concat(['build/raven.test.js'])
+ });
+ config.set(testConfig);
+};
diff --git a/karma.integration.config.js b/karma/karma.integration.config.js
similarity index 86%
rename from karma.integration.config.js
rename to karma/karma.integration.config.js
index ee544fbce0ca..1ef104390cdf 100644
--- a/karma.integration.config.js
+++ b/karma/karma.integration.config.js
@@ -9,12 +9,13 @@ var testFiles = [
{pattern: 'test/integration/throw-object.js', included: false},
{pattern: 'test/integration/example.json', included: false},
{pattern: 'test/integration/frame.html', included: false},
- 'test/integration/test.js',
- 'test/globals.js',
- 'build/raven.js',
+ {pattern: 'build/raven.js', included: false},
+ 'test/integration/test.js'
];
module.exports = function(config) {
var testConfig = Object.assign({}, commonConfig, {files: testFiles});
config.set(testConfig);
};
+
+module.exports.files = testFiles;
diff --git a/karma/karma.loader-sauce.config.js b/karma/karma.loader-sauce.config.js
new file mode 100644
index 000000000000..8b8133e7fac9
--- /dev/null
+++ b/karma/karma.loader-sauce.config.js
@@ -0,0 +1,10 @@
+var commonSauceConfig = require('./karma.sauce.config');
+var files = require('./karma.loader.config').files;
+
+module.exports = function(config) {
+ var testConfig = Object.assign({}, commonSauceConfig, {
+ logLevel: config.LOG_INFO,
+ files: files
+ });
+ config.set(testConfig);
+};
diff --git a/karma/karma.loader.config.js b/karma/karma.loader.config.js
new file mode 100644
index 000000000000..735c341b4e98
--- /dev/null
+++ b/karma/karma.loader.config.js
@@ -0,0 +1,15 @@
+var commonConfig = require('./karma.config');
+
+var testFiles = [
+ {pattern: 'node_modules/es6-promise/dist/es6-promise.auto.js', included: false},
+ {pattern: 'test/integration/loader.html', included: false},
+ {pattern: 'build/raven.js', included: false},
+ {pattern: 'src/loader.js', included: false},
+ 'test/integration/loader-test.js'
+];
+
+module.exports = function(config) {
+ var testConfig = Object.assign({}, commonConfig, {files: testFiles});
+ config.set(testConfig);
+};
+module.exports.files = testFiles;
diff --git a/karma/karma.sauce.config.js b/karma/karma.sauce.config.js
new file mode 100644
index 000000000000..4ac381fd6f97
--- /dev/null
+++ b/karma/karma.sauce.config.js
@@ -0,0 +1,97 @@
+var commonConfig = require('./karma.config');
+
+var customLaunchers = {
+ sl_chrome: {
+ base: 'SauceLabs',
+ browserName: 'chrome',
+ platform: 'Windows 10',
+ version: 'latest'
+ },
+ sl_firefox: {
+ base: 'SauceLabs',
+ browserName: 'firefox',
+ platform: 'Windows 10',
+ version: 'latest'
+ },
+ sl_edge: {
+ base: 'SauceLabs',
+ browserName: 'microsoftedge',
+ version: 'latest',
+ platform: 'Windows 10'
+ },
+ sl_ie_11: {
+ base: 'SauceLabs',
+ browserName: 'internet explorer',
+ platform: 'Windows 7',
+ version: '11'
+ },
+ sl_ie_10: {
+ base: 'SauceLabs',
+ browserName: 'internet explorer',
+ platform: 'Windows 7',
+ version: '10'
+ },
+ sl_safari: {
+ base: 'SauceLabs',
+ browserName: 'safari',
+ platform: 'OS X 10.12',
+ version: '11.0'
+ },
+ sl_ios: {
+ base: 'SauceLabs',
+ browserName: 'iphone',
+ platform: 'OS X 10.12',
+ version: '11.0'
+ },
+ sl_android_7: {
+ base: 'SauceLabs',
+ browserName: 'Chrome',
+ platform: 'Android',
+ version: '7.1',
+ device: 'Android GoogleAPI Emulator'
+ },
+ sl_android_6: {
+ base: 'SauceLabs',
+ browserName: 'Chrome',
+ platform: 'Android',
+ version: '6.0',
+ device: 'Android Emulator'
+ },
+ sl_android_5: {
+ base: 'SauceLabs',
+ browserName: 'android',
+ platform: 'Linux',
+ version: '5.1'
+ },
+ sl_android_4: {
+ base: 'SauceLabs',
+ browserName: 'android',
+ platform: 'Linux',
+ version: '4.4'
+ }
+};
+
+module.exports = Object.assign({}, commonConfig, {
+ customLaunchers: customLaunchers,
+ browsers: Object.keys(customLaunchers),
+ reporters: ['failed', 'saucelabs'],
+ singleRun: true,
+ plugins: commonConfig.plugins.concat(['karma-sauce-launcher']),
+ build: process.env.TRAVIS_BUILD_NUMBER,
+ // SauceLabs allows for 2 tunnels only, therefore some browsers will have to wait
+ // rather long time. Plus mobile emulators tend to require a lot of time to start up.
+ // 10 minutes should be more than enough to run all of them.
+ browserNoActivityTimeout: 600000,
+ captureTimeout: 600000,
+ sauceLabs: {
+ startConnect: false,
+ // Just something "random" so we don't have to provide additional ENV var when running locally
+ tunnelIdentifier: process.env.TRAVIS_JOB_NUMBER || Math.ceil(Math.random() * 1337),
+ recordScreenshots: false,
+ recordVideo: false,
+ testName:
+ 'Raven.js' +
+ (process.env.TRAVIS_JOB_NUMBER ? ' #' + process.env.TRAVIS_JOB_NUMBER : ''),
+ public: 'public'
+ }
+});
diff --git a/karma/karma.unit.config.js b/karma/karma.unit.config.js
new file mode 100644
index 000000000000..9349c48a1b9f
--- /dev/null
+++ b/karma/karma.unit.config.js
@@ -0,0 +1,9 @@
+var commonConfig = require('./karma.config');
+
+module.exports = function(config) {
+ var testConfig = Object.assign({}, commonConfig, {
+ logLevel: config.LOG_INFO,
+ files: ['build/raven.test.js']
+ });
+ config.set(testConfig);
+};
diff --git a/package-lock.json b/package-lock.json
index a57b8993306a..6095a95b0b27 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
{
"name": "raven-js",
- "version": "3.22.3",
+ "version": "3.25.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -20,16 +20,6 @@
"integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
"dev": true
},
- "accepts": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.4.tgz",
- "integrity": "sha1-hiRnWMfdbSGmR0/whKR0DsBesh8=",
- "dev": true,
- "requires": {
- "mime-types": "2.1.17",
- "negotiator": "0.6.1"
- }
- },
"acorn": {
"version": "4.0.13",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz",
@@ -494,21 +484,6 @@
"integrity": "sha1-R2iMuZu2gE8OBtPnY7HDLlfY5rY=",
"dev": true
},
- "basic-auth": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.0.tgz",
- "integrity": "sha1-AV2z81PgLlY3d1X5YnQuiYHnu7o=",
- "dev": true,
- "requires": {
- "safe-buffer": "5.1.1"
- }
- },
- "batch": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
- "integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=",
- "dev": true
- },
"bcrypt-pbkdf": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
@@ -1542,12 +1517,6 @@
}
}
},
- "connect-livereload": {
- "version": "0.5.4",
- "resolved": "https://registry.npmjs.org/connect-livereload/-/connect-livereload-0.5.4.tgz",
- "integrity": "sha1-gBV9E3HJ83zBQDmrGJWXDRGdw7w=",
- "dev": true
- },
"console-browserify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
@@ -1938,12 +1907,6 @@
"minimalistic-assert": "1.0.0"
}
},
- "destroy": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
- "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=",
- "dev": true
- },
"detective": {
"version": "4.7.1",
"resolved": "https://registry.npmjs.org/detective/-/detective-4.7.1.tgz",
@@ -2572,12 +2535,6 @@
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
"dev": true
},
- "etag": {
- "version": "1.8.1",
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
- "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
- "dev": true
- },
"event-emitter": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/event-emitter/-/event-emitter-0.3.5.tgz",
@@ -3002,12 +2959,6 @@
"integrity": "sha1-71SRSQ+UM7cF+qdyScmQKa40hVk=",
"dev": true
},
- "fresh": {
- "version": "0.5.2",
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
- "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=",
- "dev": true
- },
"fs-access": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fs-access/-/fs-access-1.0.1.tgz",
@@ -4314,36 +4265,6 @@
"rimraf": "2.6.2"
}
},
- "grunt-contrib-connect": {
- "version": "0.11.2",
- "resolved": "https://registry.npmjs.org/grunt-contrib-connect/-/grunt-contrib-connect-0.11.2.tgz",
- "integrity": "sha1-HAoHB9OzKNnPO0tJDrhMSV2Tau0=",
- "dev": true,
- "requires": {
- "async": "0.9.2",
- "connect": "3.6.5",
- "connect-livereload": "0.5.4",
- "morgan": "1.9.0",
- "opn": "1.0.2",
- "portscanner": "1.2.0",
- "serve-index": "1.9.1",
- "serve-static": "1.13.1"
- },
- "dependencies": {
- "async": {
- "version": "0.9.2",
- "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz",
- "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=",
- "dev": true
- },
- "opn": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/opn/-/opn-1.0.2.tgz",
- "integrity": "sha1-uQlkM0bQChq8l3qLlvPOPFPVz18=",
- "dev": true
- }
- }
- },
"grunt-contrib-copy": {
"version": "0.8.2",
"resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-0.8.2.tgz",
@@ -6440,30 +6361,6 @@
"integrity": "sha1-XceVaVZaENbv7VQ5SR5p0jkuWPE=",
"dev": true
},
- "morgan": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.9.0.tgz",
- "integrity": "sha1-0B+mxlhZt2/PMbPLU6OCGjEdgFE=",
- "dev": true,
- "requires": {
- "basic-auth": "2.0.0",
- "debug": "2.6.9",
- "depd": "1.1.2",
- "on-finished": "2.3.0",
- "on-headers": "1.0.1"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
- },
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -6646,12 +6543,6 @@
"ee-first": "1.1.1"
}
},
- "on-headers": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.1.tgz",
- "integrity": "sha1-ko9dD0cNSTQmUepnlLCFfBAGk/c=",
- "dev": true
- },
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@@ -7094,23 +6985,6 @@
"integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
"dev": true
},
- "portscanner": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/portscanner/-/portscanner-1.2.0.tgz",
- "integrity": "sha1-sUu9olfRTDEPqcwJaCrwLUCWGAI=",
- "dev": true,
- "requires": {
- "async": "1.5.2"
- },
- "dependencies": {
- "async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=",
- "dev": true
- }
- }
- },
"prelude-ls": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
@@ -7840,76 +7714,6 @@
"integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==",
"dev": true
},
- "send": {
- "version": "0.16.1",
- "resolved": "https://registry.npmjs.org/send/-/send-0.16.1.tgz",
- "integrity": "sha512-ElCLJdJIKPk6ux/Hocwhk7NFHpI3pVm/IZOYWqUmoxcgeyM+MpxHHKhb8QmlJDX1pU6WrgaHBkVNm73Sv7uc2A==",
- "dev": true,
- "requires": {
- "debug": "2.6.9",
- "depd": "1.1.2",
- "destroy": "1.0.4",
- "encodeurl": "1.0.1",
- "escape-html": "1.0.3",
- "etag": "1.8.1",
- "fresh": "0.5.2",
- "http-errors": "1.6.2",
- "mime": "1.4.1",
- "ms": "2.0.0",
- "on-finished": "2.3.0",
- "range-parser": "1.2.0",
- "statuses": "1.3.1"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
- },
- "serve-index": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
- "integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=",
- "dev": true,
- "requires": {
- "accepts": "1.3.4",
- "batch": "0.6.1",
- "debug": "2.6.9",
- "escape-html": "1.0.3",
- "http-errors": "1.6.2",
- "mime-types": "2.1.17",
- "parseurl": "1.3.2"
- },
- "dependencies": {
- "debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "requires": {
- "ms": "2.0.0"
- }
- }
- }
- },
- "serve-static": {
- "version": "1.13.1",
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.13.1.tgz",
- "integrity": "sha512-hSMUZrsPa/I09VYFJwa627JJkNs0NrfL1Uzuup+GqHfToR2KcsXFymXSV90hoyw3M+msjFuQly+YzIH/q0MGlQ==",
- "dev": true,
- "requires": {
- "encodeurl": "1.0.1",
- "escape-html": "1.0.3",
- "parseurl": "1.3.2",
- "send": "0.16.1"
- }
- },
"set-immediate-shim": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
diff --git a/package.json b/package.json
index 5a9b20ba8ed7..c3d813d1ee82 100644
--- a/package.json
+++ b/package.json
@@ -19,15 +19,16 @@
"lint": "eslint .",
"precommit": "lint-staged",
"publish": "grunt publish",
- "test": "npm run lint && grunt build.test && npm run test:unit && npm run test:integration && npm run test:typescript",
- "test:karma:unit": "karma start karma.unit.config.js",
- "test:karma:integration": "karma start karma.integration.config.js ",
- "test:karma:sauce": "karma start karma.sauce.config.js ",
- "test:unit": "npm run test:karma:unit -- --single-run",
- "test:integration": "npm run test:karma:integration -- --single-run",
+ "test": "npm run lint && grunt build.test && npm run test:unit && npm run test:loader && npm run test:integration && npm run test:typescript",
+ "test:unit": "karma start karma/karma.unit.config.js",
+ "test:integration": "karma start karma/karma.integration.config.js",
+ "test:integration-sauce": "karma start karma/karma.integration-sauce.config.js",
+ "test:loader": "karma start karma/karma.loader.config.js",
+ "test:loader-sauce": "karma start karma/karma.loader-sauce.config.js",
"test:typescript": "tsc --noEmit --noImplicitAny typescript/raven-tests.ts",
- "test:ci": "npm run lint && grunt test:ci && npm run test:karma:sauce",
- "test:size": "grunt dist && bundlesize && git checkout -- dist/"
+ "test:ci": "npm run lint && grunt test:ci && npm run test:loader-sauce && npm run test:integration-sauce",
+ "test:size": "grunt dist && bundlesize && git checkout -- dist/",
+ "loader": "cat src/loader.js | sed '/build_marker/{N;d;}' | npx google-closure-compiler-js | perl -e \"print ';'; print ;\""
},
"devDependencies": {
"bluebird": "^3.4.1",
diff --git a/src/loader.js b/src/loader.js
new file mode 100644
index 000000000000..21a0952a5eef
--- /dev/null
+++ b/src/loader.js
@@ -0,0 +1,79 @@
+(function(_window, _document, _script, _onerror, _onunhandledrejection) {
+ var SENTRY_SDK = _window.SENTRY_SDK;
+
+ // Create a namespace and attach function that will store captured exception
+ // Because functions are also objects, we can attach the queue itself straight to it and save some bytes
+ var queue = function(exception) {
+ queue.data.push(exception);
+ };
+ queue.data = [];
+
+ // Store reference to the old `onerror` handler and override it with our own function
+ // that will just push exceptions to the queue and call through old handler if we found one
+ var _oldOnerror = _window[_onerror];
+ _window[_onerror] = function(message, source, lineno, colno, exception) {
+ // Use keys as "data type" to save some characters"
+ queue({
+ e: [].slice.call(arguments)
+ });
+
+ if (_oldOnerror) _oldOnerror.apply(_window, arguments);
+ };
+
+ // Do the same store/queue/call operations for `onunhandledrejection` event
+ var _oldOnunhandledrejection = _window[_onunhandledrejection];
+ _window[_onunhandledrejection] = function(exception) {
+ queue({
+ p: exception.reason
+ });
+ if (_oldOnunhandledrejection) _oldOnunhandledrejection.apply(_window, arguments);
+ };
+
+ // Create a `script` tag with provided SDK `url` and attach it just before the first, already existing `script` tag
+ // Scripts that are dynamically created and added to the document are async by default,
+ // they don't block rendering and execute as soon as they download, meaning they could
+ // come out in the wrong order. Because of that we don't need async=1 as GA does.
+ // it was probably(?) a legacy behavior that they left to not modify few years old snippet
+ // https://www.html5rocks.com/en/tutorials/speed/script-loading/
+ var _currentScriptTag = _document.getElementsByTagName(_script)[0];
+ var _newScriptTag = _document.createElement(_script);
+ _newScriptTag.src = SENTRY_SDK.url;
+ _newScriptTag.crossorigin = 'anonymous';
+
+ // Once our SDK is loaded
+ _newScriptTag.addEventListener('load', function() {
+ try {
+ // Restore onerror/onunhandledrejection handlers
+ _window[_onerror] = _oldOnerror;
+ _window[_onunhandledrejection] = _oldOnunhandledrejection;
+
+ var data = queue.data;
+ var SDK = _window.Raven;
+ // Configure it using provided DSN and config object
+ SDK.config(SENTRY_SDK.dsn, SENTRY_SDK.options).install();
+ // Because we installed the SDK, at this point we have an access to TraceKit's handler,
+ // which can take care of browser differences (eg. missing exception argument in onerror)
+ var tracekitErrorHandler = _window[_onerror];
+
+ // And capture all previously caught exceptions
+ if (data.length) {
+ for (var i = 0; i < data.length; i++) {
+ if (data[i].e) {
+ tracekitErrorHandler.apply(SDK.TraceKit, data[i].e);
+ } else if (data[i].p) {
+ SDK.captureException(data[i].p);
+ }
+ }
+ }
+ } catch (o_O) {
+ console.log(o_O);
+ }
+ });
+
+ // Markers used to remove setTimeout (used for testing) from prod build, DO NOT REMOVE
+ // build_marker
+ setTimeout(function() {
+ _currentScriptTag.parentNode.insertBefore(_newScriptTag, _currentScriptTag);
+ // build_marker
+ }, 500);
+})(window, document, 'script', 'onerror', 'onunhandledrejection');
diff --git a/test/globals.js b/test/globals.js
deleted file mode 100644
index bd0ed5d5a484..000000000000
--- a/test/globals.js
+++ /dev/null
@@ -1,2 +0,0 @@
-window.__DEV__ = true;
-__DEV__ = true;
diff --git a/test/integration/loader-test.js b/test/integration/loader-test.js
new file mode 100644
index 000000000000..fe133b27781d
--- /dev/null
+++ b/test/integration/loader-test.js
@@ -0,0 +1,82 @@
+/*global assert*/
+function iframeExecute(iframe, done, execute, assertCallback) {
+ iframe.contentWindow.done = function() {
+ try {
+ assertCallback(iframe);
+ done();
+ } catch (e) {
+ done(e);
+ }
+ };
+ // use setTimeout so stack trace doesn't go all the way back to mocha test runner
+ iframe.contentWindow.eval(
+ 'window.originalBuiltIns.setTimeout.call(window, ' + execute.toString() + ');'
+ );
+}
+
+function createIframe(done) {
+ var iframe = document.createElement('iframe');
+ iframe.style.display = 'none';
+ iframe.src = './base/test/integration/loader.html';
+ iframe.onload = function() {
+ done();
+ };
+ document.body.appendChild(iframe);
+ return iframe;
+}
+
+describe('Async SDK Loader', function() {
+ this.timeout(30000);
+
+ beforeEach(function(done) {
+ this.iframe = createIframe(done);
+ });
+
+ afterEach(function() {
+ document.body.removeChild(this.iframe);
+ });
+
+ it('should capture errors before and after SDK is loaded', function(done) {
+ var iframe = this.iframe;
+
+ iframeExecute(
+ iframe,
+ done,
+ function() {
+ // This is to ensure that we are indeed queuing the errors
+ if (typeof window.Raven !== 'undefined') {
+ window.loadedPreTest = true;
+ } else {
+ window.loadedPreTest = false;
+ }
+
+ // Because we pre-load loader.js through karma, we don't have to worry about XHR delays etc.
+ setTimeout(function() {
+ Raven.captureException(new Error('post load exception'));
+ done();
+ }, 1000);
+ },
+ function() {
+ // Raven shouldn't be loaded synchronously, so it shouldnt be available at the beginning
+ assert.equal(iframe.contentWindow.loadedPreTest, false, 'A');
+ // but it should be available later on
+ assert.equal(typeof iframe.contentWindow.Raven !== 'undefined', true, 'B');
+ // and it should be configured
+ assert.equal(iframe.contentWindow.Raven.isSetup(), true, 'C');
+
+ var ravenData = iframe.contentWindow.ravenData;
+
+ if (iframe.contentWindow.supportsOnunhandledrejection) {
+ assert.equal(ravenData.length, 3);
+ assert.equal(ravenData[0].exception.values[0].value, 'pre load exception');
+ assert.equal(ravenData[1].exception.values[0].value, 'pre load rejection');
+ assert.equal(ravenData[2].exception.values[0].value, 'post load exception');
+ } else {
+ assert.equal(ravenData.length, 2);
+ assert.equal(ravenData[0].exception.values[0].value, 'pre load exception');
+ assert.equal(ravenData[1].exception.values[0].value, 'post load exception');
+ }
+ }
+ );
+ });
+});
diff --git a/test/integration/loader.html b/test/integration/loader.html
new file mode 100644
index 000000000000..015eef3413fb
--- /dev/null
+++ b/test/integration/loader.html
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/test/integration/test.js b/test/integration/test.js
index 5552ed84b052..d06dce1a3c8e 100644
--- a/test/integration/test.js
+++ b/test/integration/test.js
@@ -44,7 +44,7 @@ function isEdge14() {
}
describe('integration', function() {
- this.timeout(10000);
+ this.timeout(30000);
beforeEach(function(done) {
this.iframe = createIframe(done);