20

How do I read in a page from localhost into a headless Jasmine spec so test cases can work on the DOM elements?

My Gulp task is successfully running Jasmine specs for unit testing, and now I need to build integration tests to verify full web pages served from localhost. I'm using the gulp-jasmine-browser plugin to run PhantomJS.

Example:

gulpfile.js

var gulp =           require('gulp');
var jasmineBrowser = require('gulp-jasmine-browser');

function specRunner() {
   gulp.src(['node_modules/jquery/dist/jquery.js', 'src/js/*.js', 'spec/*.js'])
      .pipe(jasmineBrowser.specRunner({ console: true }))
      .pipe(jasmineBrowser.headless());
   }

gulp.task('spec', specRunner);


spec/cart-spec.js

describe('Cart component', function() {

   it('displays on the gateway page', function() {
      var page = loadWebPage('http://localhost/');  //DOES NOT WORK
      var cart = page.find('#cart');
      expect(cart.length).toBe(1);
      });

   });

There is no loadWebPage() function. It's just to illustrate the functionality I believe is needed.

Dem Pilafian
  • 5,625
  • 6
  • 39
  • 67
  • Are you looking for PhantomJS's [page.open](http://phantomjs.org/api/webpage/method/open.html) method? – Gerrit0 Nov 24 '16 at 04:52
  • 4
    If what you are doing is trying to use Jasmine to navigate around and follow links, you might want to think about moving to a secondary framework, like Zombie and/or Selenium. – Bob_Gneu Nov 24 '16 at 06:18
  • @Gerrit0 Yeah, I've seen `page.open` and it does the right thing, but it's a PhantomJS module with no obvious way to call it from a Jasmine spec. If you know how, please tell. – Dem Pilafian Nov 24 '16 at 07:46
  • @Bob_Gneu Selenium requires Java, so it's definitely out. PhantomJS is generally considered to be able to do everything Zombie.js can do (but the browser emulation in Zombie.js is supposed to be faster than the headless WebKit in PhantomJS). – Dem Pilafian Nov 24 '16 at 08:03
  • http://webdriver.io is a selenium driver for node. Pretty sure you don't need to bother with any Java code for this to be used. – Bob_Gneu Nov 24 '16 at 08:14
  • Because of the jasmine dependency on the browser to execute this is going to be tough. I typically use mocha and zombie when I'm diving into automated integration testing. That way I only have to start up the server and navigate around. Doing it in a contest where the tests are already run in the browser is not going to work well. One saving grace is that jasmine tests can be run via the cli. That might give you some latitude to load external libraries and get phantoms to work as you need. – Bob_Gneu Nov 24 '16 at 08:16

3 Answers3

3

End-to-End testing frameworks like a Selenium, WebdriverIO, Nightwatch.js, Protractor and so on are more suitable in such case.

The gulp-jasmine-browser plugin still is about the Unit testing in the browser environment. It is not possible to navigate between pages.

Evgeniy Generalov
  • 1,296
  • 14
  • 26
2

I put together the following code that appears to work. Please feel free to check out my repo and confirm in your own environment.

package.json

{
  "name": "40646680",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "test": "gulp jasmine"
  },
  "devDependencies": {
    "gulp": "^3.9.1",
    "gulp-jasmine-browser": "^1.7.1",
    "jasmine": "^2.5.2",
    "phantomjs": "^2.1.7"
  }
}

gulpfile.js

(() => {
    "use strict";

    var gulp = require("gulp"),
        jasmineBrowser = require("gulp-jasmine-browser");

    gulp.task("jasmine", () => {
        return gulp.src("test/*.js")
            .pipe(jasmineBrowser.specRunner({
                console: true
            }))
            .pipe(jasmineBrowser.headless());
    });
})();

test/sampleJasmine.js

describe("A suite", function() {
    it("contains spec with an expectation", function() {
        expect(true).toBe(true);
    });
    it("contains failing spec with an expectation", function() {
        expect(true).toBe(false);
    });
});

Execution

Bob Chatman@CHATBAG42 F:\Development\StackOverflow\40646680
> npm test

> 40646680@1.0.0 test F:\Development\StackOverflow\40646680
> gulp jasmine

[21:56:44] Using gulpfile F:\Development\StackOverflow\40646680\gulpfile.js
[21:56:44] Starting 'jasmine'...
[21:56:44] Jasmine server listening on port 8000
.F
Failures:
1) A suite contains failing spec with an expectation
1.1) Expected true to be false.

2 specs, 1 failure
Finished in 0 seconds
[21:56:49] 'jasmine' errored after 4.26 s
[21:56:49] Error in plugin 'gulp-jasmine-browser'
Message:
    1 failure
npm ERR! Test failed.  See above for more details.

Dependencies

node 7.2
npm 3.9.3
jasmine 2.5.2
phantomjs 2.1.7 
gulp 3.9.1
Bob_Gneu
  • 1,591
  • 1
  • 18
  • 30
  • 2
    Can you add an explanation as to how this solution answers the question? – Dem Pilafian Nov 24 '16 at 08:06
  • As our discussion above has evolved, I don't think it addresses the later aspect. Please disregard this. I may look into it later this weekend if I get a chance and make it more accurately address the integration aspect. – Bob_Gneu Nov 24 '16 at 08:19
0

jsdom to the rescue!

It turns out it's pretty easy to load a web page into a headless Jasmine spec... but you need to swap out PhantomJS for jsdom.

Strategy:

  1. Use Jasmine's beforeAll() to call a function that will run JSDOM.fromURL() to request the web page.
  2. Once the web page has been loaded into the DOM, expose window and jQuery for use in your test cases.
  3. Finally, call done() to indicate the tests are now ready to run.

Make sure to close the window after the tests have run.

spec.js

const url  = 'http://dnajs.org/';
const { JSDOM } = require('jsdom');
let window, $;
function loadWebPage(done) {
   function handleWebPage(dom) {
      function waitForScripts() {
         window = dom.window;
         $ = dom.window.jQuery;
         done();
         }
      dom.window.onload = waitForScripts;
      }
   const options = { resources: 'usable', runScripts: 'dangerously' };
   JSDOM.fromURL(url, options).then(handleWebPage);
   }
function closeWebPage() { window.close(); }

describe('The web page', () => {

   beforeAll(loadWebPage);
   afterAll(closeWebPage);

   it('has the correct URL', () => {
      expect(window.location.href).toBe(url);
      });

   it('has exactly one header, main, and footer', () => {
      const actual =   {
          header: $('body >header').length,
          main:   $('body >main').length,
          footer: $('body >footer').length
          };
      const expected = { header: 1, main: 1, footer: 1 };
      expect(actual).toEqual(expected);
      });

   });


Test output

screenshot
Note: Above screenshot is from a similar Mocha spec since Mocha has a nice default reporter.

Project

It's on GitHub if you want try it out yourself:
https://github.com/dnajs/load-web-page-jsdom-jasmine


EDITED: Updated for jsdom 11

Dem Pilafian
  • 5,625
  • 6
  • 39
  • 67