I'm trying to figure out how to correctly use JS Test Driver's assertException method. From google's documentation it should be: assertException([msg], callback, error). However, no matter what I do, I always get an [ERROR] for this test (instead of a [PASS] since there was an exception):
ComponentTest.prototype.testEnforceUniqueComponentIds = function() {
assertException(function(){
this.component = new myNamespace.component(this.testComponentSettings);
});
}
And in my src JS:
myNamespace.component = function(params){
if (typeof params.selector == 'undefined'){
throw new Error('no selector provided');
}
this.settings = jQuery.extend({}, {
id : 'defaultComponentId',
type : 'defaultComponentType',
selector : 'body',
isUnique : false
}, params);
if(typeof(myNamespace.componentRegistry[params.id]) === "undefined"){
myNamespace.registerComponent(this);
} else {
throw new Error('non-unique component id provided');
}
}
Here is the output from js-test-driver:
C:\xampp2\htdocs\myNamespace\v2>java -jar JsTestDriver.jar --tests all --verbose
[PASSED] ComponentTest.testInit
[LOG] setUp
[LOG] tearDown
[PASSED] ComponentTest.testDestroy
[LOG] setUp
[LOG] tearDown
[ERROR] ComponentTest.testEnforceUniqueComponentIds
[LOG] setUp
Total 3 tests (Passed: 2; Fails: 0; Errors: 1) (1.00 ms)
Firefox 7.0.1: Run 3 tests (Passed: 2; Fails: 0; Errors 1) (1.00 ms)
ComponentTest.testEnforceUniqueComponentIds error (1.00 ms):
For the life of me, I can't figure out how to have the code within the assertException callback actually throw an exception without it causing an ERROR in the test - isn't this what is supposed to happen?
Any help would be appreciated.
assertException(function(){
this.component = new myNamespace.component(this.testComponentSettings);
}, "Error");
error argument should be Error.name of thrown error(such as "ReferenceError", "TypeError", etc.).
Related
I have application where some critical issues are reported with console.error but are not thrown so application might continue to run - possibly in crippled state.
It's necessary to report also console.error issues, but Sentry (Raven) library send to server only thrown exceptions.
Does someone knows how to solve this nicely ?
(ideally without need to rewrite all console.error calls, cause also some vendor libraries might still write output just into console)
As user #kumar303 mentioned in his comment to the question ... you can use the JS console integration Sentry.Integrations.CaptureConsole.
See https://docs.sentry.io/platforms/javascript/configuration/integrations/plugin/#captureconsole for documentation.
At the end you JS code to setup Sentry looks as follows:
import * as Sentry from '#sentry/browser';
import { CaptureConsole } from '#sentry/integrations';
Sentry.init({
dsn: 'https://your-sentry-server-dsn',
integrations: [
new CaptureConsole({
levels: ['error']
})
],
release: '1.0.0',
environment: 'prod',
maxBreadcrumbs: 50
})
If then someone calls console.error a new event will sent to sentry.
Here's a more robust override solution
// creating function declarations for better stacktraces (otherwise they'd be anonymous function expressions)
var oldConsoleError = console.error;
console.error = reportingConsoleError; // defined via function hoisting
function reportingConsoleError() {
var args = Array.prototype.slice.call(arguments);
Sentry.captureException(reduceConsoleArgs(args), { level: 'error' });
return oldConsoleError.apply(console, args);
};
var oldConsoleWarn = console.warn;
console.warn = reportingConsoleWarn; // defined via function hoisting
function reportingConsoleWarn() {
var args = Array.prototype.slice.call(arguments);
Sentry.captureMessage(reduceConsoleArgs(args), { level: 'warning' });
return oldConsoleWarn.apply(console, args);
}
function reduceConsoleArgs(args) {
let errorMsg = args[0];
// Make sure errorMsg is either an error or string.
// It's therefore best to pass in new Error('msg') instead of just 'msg' since
// that'll give you a stack trace leading up to the creation of that new Error
// whereas if you just pass in a plain string 'msg', the stack trace will include
// reportingConsoleError and reportingConsoleCall
if (!(errorMsg instanceof Error)) {
// stringify all args as a new Error (which creates a stack trace)
errorMsg = new Error(
args.reduce(function(accumulator, currentValue) {
return accumulator.toString() + ' ' + currentValue.toString();
}, '')
);
}
return errorMsg;
}
Based on #Marc Schmid's solution I came up with the following working example, if you link to the Sentry CDN files.
<script src="https://browser.sentry-cdn.com/5.11.1/bundle.min.js" integrity="sha384-r7/ZcDRYpWjCNXLUKk3iuyyyEcDJ+o+3M5CqXP5GUGODYbolXewNHAZLYSJ3ZHcV" crossorigin="anonymous"></script>
<!-- https://github.com/getsentry/sentry-javascript/issues/1976#issuecomment-492260648 -->
<script src="https://browser.sentry-cdn.com/5.11.1/captureconsole.min.js"></script>
<script>
Sentry.init({
dsn: 'https://abcdef1234567890#sentry.io/012345',
debug: false,
integrations: [
new Sentry.Integrations.CaptureConsole({
levels: ['error']
})
],
});
</script>
Found a little hacky solution:
const consoleError = console.error;
console.error = function(firstParam) {
const response = consoleError.apply(console, arguments);
Raven.captureException(firstParam, { level: 'error' });
return response;
};
It just wraps console.error and report each of error logs in console to Raven (Sentry).
If someone have nicer approach (maybe some hidden feature of Sentry) please feel free to share!
I wrote a library that is going this using your Sentry instance.
https://github.com/aneldev/dyna-sentry
I'm tyring to build and minify my JS using grunt, When i build i didn't get any errors but while minifying i'm getting an error like
../source/js/browse-container.js
1013 | var cards = listCards;
^ 'listCards' is not defined.
>> 1 error in 1 file
Warning: Task "jshint:all" failed. Use --force to continue.
Aborted due to warnings.
Below is the code,I don't see any errors in console, i just get that above error while trying to minify. (Minification is done using uglify)
Please let me know what i'm doing wrong ?
fetchListStatic: function(){
var cards = listCards;
return new Promise(function (resolve) {
if (typeof (cards) !== 'undefined') {
//var cards = listCards;
resolve(cards);
} else {
resolve([]);
}
});
},
I've declared the listCards globally. Hence resolved my issue
var moment, hbs, promise, listCards;
function BrowseContainer() {
}
Snippet for taking screenshot on Test Failure
afterEach(function() {
var spec = jasmine.getEnv().currentSpec;
var passed = spec.results().passed();
if (!passed) {
browser.takeScreenshot().then(function(png) {
writeScreenShot(png, "screenshot.png");
});
}
function writeScreenShot(data, filename) {
var stream = fs.createWriteStream(filename);
stream.write(new Buffer(data, 'base64'));
stream.end();
}
});
Console error
Message:
Failed: Cannot read property 'results' of undefined
Stack:
TypeError: Cannot read property 'results' of undefined
at Object.<anonymous>
it returns undefined here,
var spec = jasmine.getEnv().currentSpec;
console.log(spec);
Looks like issues due to Jasmine 2; any alternate solutions also welcome! I use Jasmine 2
As of Jasmine 2.0, env.currentSpec is no longer supported.
https://github.com/jasmine/jasmine/issues/1212
"var spec" is not being set to anything. That means "jasmine.getEnv().currentSpec" is not working for whatever reason.
I have an Angular module, validation, which is declared like so:
(function(ns){
ns.validation = angular.module("validation", []);
})(blog);
The module contains two services, validator and validationRulesProvider, which look like:
(function(module){
module
.factory("validationRulesProvider", function(){
var _getRules = function() {
return [{
isValid: function(post) {
return post.length > 0;
}
}];
};
return {
getRules: _getRules
};
});
})(blog.validation);
and
(function(module){
module
.factory("validator", ["validationRulesProvider", function(validationRulesProvider){
var _validate = function(post) {
var rules = validationRulesProvider.getRules();
for (var rule in rules) {
if (!rule.isValid(post)) {
return false;
}
}
return true;
};
return {
validate: _validate
};
}]);
})(blog.validation);
I am attempting to test (using Jasmine) that
The getRules method on validationRulesProvider is actually called from the validate method
The post parameter is run through each rule returned from said method
I have the following Jasmine test script:
describe("Validator: ", function(){
var _validator;
var _mockedValidationRulesProvider;
var _mockRule;
beforeEach(function(){
module("validation");
inject(function(validationRulesProvider){
_mockedValidationRulesProvider = validationRulesProvider;
});
_mockRule = jasmine.createSpy();
spyOn(_mockedValidationRulesProvider, "getRules")
.and
.returnValue([{
isValid: _mockRule
}]);
inject(function(validator){
_validator = validator;
});
});
describe("getRules - ", function(){
it("gets a collection of rules from the rules provider", function(){
_validator.validate("");
expect(_mockedValidationRulesProvider.getRules).toHaveBeenCalled();
});
it("should pass the post through each rule received from the rules provider", function(){
expect(_mockRule.calls.count()).toEqual(_mockedValidationRulesProvider.getRules().length);
});
});
});
So, I'm simply trying to create a fake implementation of validationRulesProvider.getRules. My trouble is that both of these tests fail. If I alter the line:
spyOn(_mockedValidationRulesProvider, "getRules")
.and
.returnValue([{
isValid: _mockRule
}]);
to simply be
spyOn(_mockedValidationRulesProvider, "getRules")
.and
.returnValue([]);
then the first of the two tests pass, as the loop in validator.validate will never be entered.
Karma gives the following output:
PhantomJS 1.9.8 (Windows 7) Validator: getRules - gets a collection of rules from the rules provider FAILED
TypeError: 'undefined' is not a function (evaluating 'rule.isValid(post)')
at C:/Users/User/JS/Angular/Learning/blogsite/scripts/validation/validator.js:8
at C:/Users/User/JS/Angular/Learning/blogsite/scripts/tests/validator.test.js:32
PhantomJS 1.9.8 (Windows 7) Validator: getRules - should pass the post through each rule received from the rules provider FAILED
Expected 0 to equal 1.
at C:/Users/User/JS/Angular/Learning/blogsite/scripts/tests/validator.test.js:37
PhantomJS 1.9.8 (Windows 7): Executed 5 of 5 (2 FAILED) (0 secs / 0.039 secs)
I'm a bit of a loss as to why the tests are failing in the first instance because it seems like what I should be returning from the spy is an array of one object which contains an "isValid" function - which is exactly what is returned from the actual implementation of that function.
What am I doing wrong?
When you want to loop an array in javascript, you need to use javascript basic 'for loop' instead of 'for-in loop'.
The code in your validator factory should be like this.
for (var i = 0; i < rules.length; i++) {
var rule = rules[i];
if (!rule.isValid(post)) {
return false;
}
}
In my NodeJS program, I parse some user JSON file.
So I use :
this.config = JSON.parse(fs.readFileSync(path));
The problem is that if the json file is not correctly formated, the error thrown is like:
undefined:55
},
^
SyntaxError: Unexpected token }
at Object.parse (native)
at new MyApp (/path/to/docker/lib/node_modules/myApp/lib/my-app.js:30:28)
...
As it is not really user friendly I would like to throw an Error specifying some user friendly message (like "your config file is not well formated") but I want to keep the stacktrace in order to point to the problematic line.
In the Java world I used throw new Exception("My user friendly message", catchedException) in order to have the original exception which caused that one.
How is it possible in the JS world?
What I finally did is:
try {
this.config = JSON.parse(fs.readFileSync(path));
} catch(err) {
var newErr = new Error('Problem while reading the JSON file');
newErr.stack += '\nCaused by: '+err.stack;
throw newErr;
}
There is an new Error Cause proposal for ECMAScript, and it reached stage-4 at TC34!
It means it will be in the next ECMAScript version!
https://github.com/tc39/proposal-error-cause
You would provide the cause as an error option:
throw new Error(`Couldn't parse file at path ${filePath}`, { cause: err });
The ES proposal only formalize it on the language level, but browsers/NodeJS should normally agree to log the full causal chain in practice (see https://github.com/nodejs/node/issues/38725)
As of today (end of 2021), Firefox Devtools are already able to log nested stacktraces!
Joyent released a Node.js package that can be used exactly for that. It is called VError. I paste an example of how you would use the pacakge:
var fs = require('fs');
var filename = '/nonexistent';
fs.stat(filename, function (err1) {
var err2 = new VError(err1, 'stat "%s"', filename);
console.error(err2.message);
});
would print the following:
stat "/nonexistent": ENOENT, stat '/nonexistent'
2021 Update: To chain exceptions in JS:
class MyAppError extends Error {
constructor(...params) {
super(...params)
if (Error.captureStackTrace) {
// This is the key line!
Error.captureStackTrace(this, this.constructor);
}
this.name = this.constructor.name
}
}
See the Mozilla docs on Error.captureStackTrace
Use a try / catch block:
try {
this.config = JSON.parse("}}junkJSON}");
//...etc
}
catch (e) {
//console.log(e.message);//the original error message
e.message = "Your config file is not well formatted.";//replace with new custom message
console.error(e);//raise the exception in the console
//or re-throw it without catching
throw e;
}
http://jsfiddle.net/0ogf1jxs/5/
UPDATE: If you really feel the need for a custom error you can define your own:
function BadConfig(message) {
this.message = message;
this.name = "BadConfig";
}
BadConfig.prototype = new Error();
BadConfig.prototype.constructor = BadConfig;
try {
this.config = JSON.parse("}}badJson}");
} catch(e) {
throw new BadConfig("Your JSON is wack!");
}
http://jsfiddle.net/kL394boo/
Lots of useful info at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error