mathiasbroekelmann / js-test-driver

Automatically exported from code.google.com/p/js-test-driver
0 stars 0 forks source link

Throwing a string results in never-ending test run #338

Open GoogleCodeExporter opened 8 years ago

GoogleCodeExporter commented 8 years ago
What steps will reproduce the problem?
1. Create a test case that results in a throw "foo", eg:
var TestTest = TestCase("TestTest");
TestTest.prototype.test = function(queue) {
    throw "foo";
}

2. Run that test case
java -jar JsTestDriver-1.3.4-a.jar --config jsTestDriver.conf --tests test

3. Wait (forever)

What is the expected output? What do you see instead?

One would expect the test runner to end (and provide results), preferable in 
less than an hour ;-)

What version of the product are you using? On what operating system?

1.3.4-a, Linux, Firefox

Please provide any additional information below.

"throw 'foo'" may not be *proper* JS, but it is *acceptable* JS, in the sense 
that you can plug it in to the Firebug console and see a "foo" error (and not a 
"your syntax sucks" error).  As such, it shouldn't break anything.  It does 
though, and it's REALLY hard to track down the bug when it happens (I spent a 
good hour+ writing LOTS of console.log statements).

Original issue reported on code.google.com by jer...@syapse.com on 16 Feb 2012 at 9:49

GoogleCodeExporter commented 8 years ago
Oh, and I should have mentioned (just to make matters worse): once this error 
occurs, your whole JS Test Driver setup is hosed; it won't work again until you 
CTRL+C the server and/or refresh the browser window.  Hopefully this helps 
explain more why it took me an hour just to figure out what was going on ...

Original comment by jer...@syapse.com on 16 Feb 2012 at 9:52

GoogleCodeExporter commented 8 years ago
Console in Google Chrome says:

Exception TypeError: Cannot use 'in' operator to search for 'name' in foo
undefined(undefined):
TypeError: Cannot use 'in' operator to search for 'name' in foo
    at Object.serializePropertyOnObject (http://127.0.0.1:42442/static/jstestdrivernamespace.js:1:3584)
    at Object.serializeErrorToArray (http://127.0.0.1:42442/static/jstestdrivernamespace.js:1:2734)
    at Object.serializeErrors (http://127.0.0.1:42442/static/jstestdrivernamespace.js:1:2509)
    at [object Object].serializeError (http://127.0.0.1:42442/static/runner.js:1925:29)
    at [object Object].runTest (http://127.0.0.1:42442/static/runner.js:1916:57)
    at b (http://127.0.0.1:42442/static/jstestdrivernamespace.js:1:420)
    at nextTest (http://127.0.0.1:42442/static/runner.js:1783:16)
    at [object Object].pausingRunTestLoop [as runTestLoop_] (http://127.0.0.1:42442/static/runner.js:1795:5)
    at [object Object].runTestConfiguration (http://127.0.0.1:42442/static/runner.js:1839:8)
    at [object Object].runTestConfiguration (http://127.0.0.1:42442/static/runner.js:2226:33)
runner.js:356 Uncaught TypeError: Cannot use 'in' operator to search for 'name' 
in foo

Original comment by vkhomyackov on 20 Feb 2012 at 2:58

GoogleCodeExporter commented 8 years ago
From console of Chrome:

> typeof (function(){try{throw "foo"}catch(ex){return ex}})()
"string"
> "name" in (function(){try{throw "foo"}catch(ex){return ex}})()
TypeError: Cannot use 'in' operator to search for 'name' in foo

throw "foo" returns string (not Object), which cannot be properly tested in 
jstestdriver.utils.serializePropertyOnObject()

Original comment by vkhomyackov on 20 Feb 2012 at 3:29

GoogleCodeExporter commented 8 years ago
Maybe this will work (at least in console it causes no errors):

jstestdriver.utils.serializePropertyOnObject = function(b, c, a) {
  if(c && c[b]) {
    a.push(",");
    a.push('"' + b + '":');
    this.serializeObjectToArray(c[b], a)
  }
};

Original comment by vkhomyackov on 20 Feb 2012 at 3:32

GoogleCodeExporter commented 8 years ago
I have a fix for this.

Your code expects all exceptions to be thrown as an Error object like this:

throw new Error("error message");

But there are many places that throw just a string, or some other type of 
object like this:

throw "error message"

This will cause problems in jsTestDriver.

But the code below will check to see if the exception that was caught has a 
name property. If not it created an Error object and continues on. This has not 
been fully tested for other types of thrown data. We only tested it with 
throwing strings.

But with a little more work this can be corrected to allow anything to be 
thrown and your code will convert it into what it is expecting internally.

There are three places we do this each marked with the comment:
 // FIX: Convert the exception into an error if it does not have e.name

We had changed our code to always throw a new Error(message) but the we noticed 
that jQuery and other libraries just throw strings. So we figured the best 
place to put the fix was in the RunTest function.

Here is the code:

jstestdriver.plugins.TestRunnerPlugin.prototype.runTest =
    function (testCaseName, testCase, testName) {
      var testCaseInstance;
      var errors = [];
      try {
        try {
          testCaseInstance = new testCase();
        } catch (e) {
          return new jstestdriver.TestResult(
          testCaseName,
          testName,
          jstestdriver.TestResult.RESULT.ERROR,
          testCaseName + ' is not a test case',
          '',
          0);
        }
        var start = new this.dateObj_().getTime();

        jstestdriver.expectedAssertCount = -1;
        jstestdriver.assertCount = 0;
        var res = jstestdriver.TestResult.RESULT.PASSED;
        try {
          if (testCaseInstance.setUp) {
            testCaseInstance.setUp();
          }
          if (!(testName in testCaseInstance)) {
            var err = new Error(testName + ' not found in ' + testCaseName);
            err.name = 'AssertError';
            throw err;
          }
          testCaseInstance[testName]();
          if (jstestdriver.expectedAssertCount != -1 &&
          jstestdriver.expectedAssertCount != jstestdriver.assertCount) {
            var err = new Error("Expected '" +
            jstestdriver.expectedAssertCount +
            "' asserts but '" +
            jstestdriver.assertCount +
            "' encountered.");

            err.name = 'AssertError';
            throw err;
          }
        } catch (e) {
          // We use the global here because of a circular dependency. The isFailure plugin should be refactored.
          var ex = e.name ? e : new Error(e); // FIX: Convert the exception into an error if it does not have e.name
          res = jstestdriver.pluginRegistrar.isFailure(ex) ?
          jstestdriver.TestResult.RESULT.FAILED :
            jstestdriver.TestResult.RESULT.ERROR;
          errors.push(ex);
        }
        try {
          if (testCaseInstance.tearDown) {
            testCaseInstance.tearDown();
          }
          this.clearBody_();
        } catch (e) {
          var ex = e.name ? e : new Error(e); // FIX: Convert the exception into an error if it does not have e.name
          if (res == jstestdriver.TestResult.RESULT.PASSED) {
            res = jstestdriver.TestResult.RESULT.ERROR;
          }
          errors.push(ex);
        }
        var end = new this.dateObj_().getTime();
        var msg = this.serializeError(errors);
        return new jstestdriver.TestResult(testCaseName, testName, res, msg,
            jstestdriver.console.getAndResetLog(), end - start);
      } catch (e) {
        var ex = e.name ? e : new Error(e); // FIX: Convert the exception into an error if it does not have e.name
        errors.push(ex);
        return new jstestdriver.TestResult(testCaseName, testName,
            'error', 'Unexpected runner error: ' + this.serializeError(errors),
            jstestdriver.console.getAndResetLog(), 0);
      }
    };

Original comment by Intervalia on 22 Mar 2012 at 11:39

GoogleCodeExporter commented 8 years ago
FYI, this doesn't happen in 1.3.3d, which just fails with ["String"], so it's a 
regression in both 1.3.4a and 1.3.4b;

Original comment by lrekucki on 25 Apr 2012 at 9:51