Now that we’ve got Spectron working, let’s add some more to it to help with our testing. One way of doing end-to-end testing is to have a separate file that contains our page objects and any helper functions specific to that page. This will help the readability and maintainability of our tests. The latest code to this demo is located on GitHub.

Accessing Page File From Main Test File

Let’s create our page file. Since we’re in vanilla JavaScript, I’ll be using module.exports to export our file so we can access it in our main test file.

But first we need to declare a function and properties to export from a separate file.

var TestPage = function () {
  // Properties and functions relevant to the page here
}

module.exports = TestPage;

Here we have a file named test.page.js that will contain our page objects – properties and functions associated with our page that we will test against. We just create a main object, TestPage, and add all that we’ll need. To have it able to be accessed in our main test file we add the module.exports = TestPage line at the bottom.

From there we can now reference this file in our main test file:

const testPage = require('./test.page.js');

And then we can new it up to use it:

var page = new testPage();

Page File Properties

Now that we can access our page file from our test file, let’s add some value to it. Let’s refer back to our main test file. It has test cases like the following:

it('opens a window', function () {
  return app.client.waitUntilWindowLoaded()
    .getWindowCount().should.eventually.equal(1);
});

it('tests the title', function () {
  return app.client.waitUntilWindowLoaded()
    .getTitle().should.eventually.equal('Hello World!');
});

What can we take from there to add to our page file? At first glance we see the items that we are expecting to test against, such as 1 for the window count and Hello World! for our title. We can definitely break these out into our page file.

var TestPage = function () {
    this.windowCount = 1;
    this.pageTitle = 'Hello World!';
}

module.exports = TestPage;

And we can change our main test file to reference these properties.

it('opens a window', function () {
  return app.client.waitUntilWindowLoaded()
    .getWindowCount().should.eventually.equal(page.windowCount);
});

it('tests the title', function () {
  return app.client.waitUntilWindowLoaded()
    .getTitle().should.eventually.equal(page.pageTitle);
});

Now our tests are much more readable to see exactly what we expect to test against and remove the magic numbers and strings.

Page File functions

We can definitely add properties to our page file, but is there more we can do? Earlier in this post I mentioned that we can add functions to our page file, as well. How does that help our tests out, though? What functions can we move from our main test file to our page file? Let’s take another look at what we have so far in our test file.

it('opens a window', function () {
  return app.client.waitUntilWindowLoaded()
    .getWindowCount().should.eventually.equal(page.windowCount);
});

it('tests the title', function () {
  return app.client.waitUntilWindowLoaded()
    .getTitle().should.eventually.equal(page.pageTitle);
});

Can we take the app.client.waitUntilWindowLoaded().getWindowCount() function and move it to our page file? We can! We can create a new function in our page file and just call the function in there.

this.getWindowCount = function() {
  return this.app.client.waitUntilWindowLoaded().getWindowCount();
}

However, this won’t exactly work as it currently is. We have a bit of a dependency on our app variable that Spectron gives us and I use a local variable, this.app. To solve this I added another function where I can pass in that app variable into our page file to populate the local variable.

const app;

this.setApp = function(app) {
  this.app = app;
}

And in our main test file I call this function in the global before function.

global.before(function () {
  // elided
  page.setApp(app);
});

And we can do the same with the app.client.waitUntilWindowLoaded().getTitle() function.

this.getApplicationTitle = function() {
  return this.app.client.waitUntilWindowLoaded().getTitle();
}

With those changes we have a different and better looking test file.

it('opens a window', function () {
  page.getWindowCount().should.eventually.equal(page.windowCount);
});

it('tests the title', function () {
  page.getApplicationTitle().should.eventually.equal(page.pageTitle);
});

Now our tests look much more readable and can be more maintained. Of course, there are multiple ways to write tests and I’ve found that this helps me out.