1

Running the tests via a web browser works fine, but using grunt gives me errors. I struggle to understand what I'm doing wrong here. grunt tests fails with

$ grunt tests
Running "jsonlint:sample" (jsonlint) task
>> 4 files lint free.

Running "jshint:all" (jshint) task
>> 4 files lint free.

Running "connect:server" (connect) task
Started connect web server on http://localhost:5000

Running "qunit:all" (qunit) task
Testing http://localhost:5000/tests/tests.html F
>> Settings.json tests - Fetching settings file
>> Message: InvalidStateError: DOM Exception 11: An attempt was made to use an object that is not, or is no longer, usable.
>> Actual: null
>> Expected: undefined
>> http://localhost:5000/tests/tests.js:7

>> Settings.json tests - Fetching settings file
>> Message: Check settings JSON file
>> http://localhost:5000/tests/tests.js:25:24
>> onreadystatechange@http://localhost:5000/tests/tests.js:8:25

>> Settings.json tests - Fetching settings file
>> Message: Assertion after the final `assert.async` was resolved
>> Actual: null
>> Expected: undefined
>> notEqual@http://localhost:5000/tests/qunit/qunit-1.22.0.js:1512:18
>> http://localhost:5000/tests/tests.js:25:24
>> onreadystatechange@http://localhost:5000/tests/tests.js:8:25

>> Settings.json tests - Fetching settings file
>> Message: Check settings JSON file
>> http://localhost:5000/tests/tests.js:25:24
>> onreadystatechange@http://localhost:5000/tests/tests.js:8:25

>> Settings.json tests - Fetching settings file
>> Message: Too many calls to the `assert.async` callback
>> Actual: null
>> Expected: undefined
>> http://localhost:5000/tests/tests.js:26:13
>> onreadystatechange@http://localhost:5000/tests/tests.js:8:25

>> Settings.json tests - Fetching settings file
>> Message: Assertion after the final `assert.async` was resolved
>> Actual: null
>> Expected: undefined
>> notEqual@http://localhost:5000/tests/qunit/qunit-1.22.0.js:1512:18
>> http://localhost:5000/tests/tests.js:25:24
>> onreadystatechange@http://localhost:5000/tests/tests.js:8:25

>> Settings.json tests - Fetching settings file
>> Message: Check settings JSON file
>> http://localhost:5000/tests/tests.js:25:24
>> onreadystatechange@http://localhost:5000/tests/tests.js:8:25

>> Settings.json tests - Fetching settings
>> Message: Too many calls to the `assert.async` callback
>> Actual: null
>> Expected: undefined
>> http://localhost:5000/tests/tests.js:26:13
>> onreadystatechange@http://localhost:5000/tests/tests.js:8:25

Warning: 8/8 assertions failed (54ms) Use --force to continue.

Aborted due to warnings.

Gruntfile.js

module.exports = function(grunt) {
  grunt.initConfig({
    jsonlint: {
      sample: {
        src: ['json/*.json', 'api_files/*.json'],
        options: {
          formatter: 'prose'
        }
      }
    },
    jshint: {
      all: ['*.js', 'tests/*.js', 'server/*.js']
    },
    connect: {
      server: {
        options: {
          port: 5000,
          base: '.'
        }
      }
    },
    qunit: {
      all: {
        options: {
          urls: [
            'http://localhost:5000/tests/tests.html'
          ]
        }
      }
    }
  });

  grunt.loadNpmTasks('grunt-jsonlint');
  grunt.loadNpmTasks('grunt-contrib-jshint');
  grunt.loadNpmTasks('grunt-contrib-connect');
  grunt.loadNpmTasks('grunt-contrib-qunit');

  grunt.registerTask('tests', ['jsonlint', 'jshint', 'connect', 'qunit']);
};

tests.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width">
    <title>Backend Tests</title>
    <link rel="stylesheet" href="qunit/qunit-1.22.0.css">
    <script src="qunit/qunit-1.22.0.js"></script>
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="tests.js"></script>
</body>
</html>

tests.js

var HttpClient = function () {
    this.get = function (requestUrl, callback) {
        var anHttpRequest = new XMLHttpRequest();
        anHttpRequest.onreadystatechange = function () {
            if (anHttpRequest.readyState == 4 && anHttpRequest.status == 200) {
                callback(anHttpRequest.responseText);
            } else if (anHttpRequest.status == 404) {
                callback(null);
            }
        };

        anHttpRequest.open("GET", requestUrl, true);
        anHttpRequest.send(null);
    };
};

var rootUrl = "http://localhost:5000";
var client = new HttpClient();

QUnit.module("Fetching settings.json");

QUnit.test('Fetching settings file', function (assert) {
    var done = assert.async();
    client.get(rootUrl + '/api/settings', function (response) {
        assert.notEqual(response, null, 'Check settings JSON file');
        done();
    });
});

main.js

function serveStaticFiles(router) {
    var app = express();
    app.use('/api', router);                // all of our routes will be prefixed with /api
    app.use('/', express.static(__dirname + '/')); // Makes it possible for html files to fetch images from img
    app.use(express.static('./html'));      // folder 'html' is now considered public and accessible
    app.use(express.static('./xml'));       // folder 'xml' is now considered public and accessible
    app.use(express.static('./img'));       // folder 'img' is now considered public and accessible
    app.use(express.static('./json'));      // folder 'json' is now considered public and accessible
    app.use(express.static('./tests'));     // folder 'tests' is now considered public and accessible

    return app;
}

var bugsnag = require("bugsnag");
bugsnag.register(process.env.BUGSNAG_API_KEY);
bugsnag.releaseStage = process.env.BUGSNAG_RELEASE_STAGE;
bugsnag.notifyReleaseStages = ["stage", "production"];

var express = require('express');
var router = express.Router();
var app = serveStaticFiles(router);
var port = process.env.PORT || 5000;
var server = app.listen(port);
var apiHandler = require("./server/ApiHandler");
console.log("Application created and it listens on port " + port);

apiHandler.initRestApi(router);
MdaG
  • 2,680
  • 2
  • 34
  • 46
  • What if the status code is not 200 or 404? In that case the `callback` would not get fired. I would ensure that you have a catchall in your `onreadystatechange` event: `else if (anHttpRequest.readyState >= 4) { callback(null) }` – Jordan Kasper Jan 05 '17 at 20:03
  • @jakerella You're right of course, thank you. However, that's not the cause of the errors I'm getting. Do you understand the errors and their cause? – MdaG Jan 05 '17 at 22:23
  • Since it's a 404 error I assume that the grunt-contrib-connect istn't working as expected? – MdaG Jan 06 '17 at 11:20
  • Or the node app is never started. I think that's it. The server is running fine, but it's not the same as running `heroku local` – MdaG Jan 06 '17 at 12:11
  • Yeah, i think that the issue might be that `http://localhost:5000/api/settings` is not an actual URL... where is that server? Because that's the _same_ port as your grunt connect server... should it be a different port/url/app? – Jordan Kasper Jan 06 '17 at 15:04
  • @jakerella I have a server which is started and which provides static files as well as a REST API. It's usually run on Heroku with a Procfile `web: node main.js`. I need to figure out how to run the node app via Grunt for the duration of the tests. I've added main.js to the description. – MdaG Jan 06 '17 at 15:08
  • Yeah, you'll need to run that node app first, but even more importantly, you can't load that app AND the `connect` server both on port 5000. – Jordan Kasper Jan 06 '17 at 18:17

1 Answers1

1

You'll need to run your Node server as another step in your grunt process. There is a grunt task for specifically running Express servers, I'd recommend starting there. Here's what the grunt config might look like:

grunt.initConfig({
    // ...
    express: {  // this is the new task...
      dev: {
        options: {
          script: 'path/to/main.js'
        }
      }
    },
    connect: {
      server: {
        options: {
          port: 3000,  // I changed this so it doesn't conflict with your express app
          base: '.'
        }
      }
    },
    qunit: {
      all: {
        options: {
          urls: [
            'http://localhost:3000/tests/tests.html'  // changed this as well
          ]
        }
      }
    }
});

Then you'll want to run all three tasks as your "test" run. You can create an alias like so:

grunt.registerTask('tests', ['jsonlint', 'jshint', 'express', 'connect', 'qunit']);

Jordan Kasper
  • 13,153
  • 3
  • 36
  • 55
  • Ah, thanks. I had to explicitly set the port to 5000 in express dev options or it would be assigned 3000 and that mismatches with the rootUrl in the tests. I also had to change the qunit url port to the same as express (5000) or it would just time out. However, I'm still receiving 404 when trying to fetch the data. It look like it cleared up all the error except `InvalidStateError: DOM Exception 11: An attempt was made to use an object that is not, or is no longer, usable.`. Huge step in the right direction. Thank you. :) – MdaG Jan 07 '17 at 11:19