Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Simulating going online/offline with JSDOM

We are working on a tutorial for the basics of offline first apps, and are using JSDOM with Tape to test our code. In our code we update the DOM so that a text node changes from saying "online" to "offline" and vice versa by attaching an event listener to the window and listening for the "online"/"offline" events, and navigator.onLine to initialise the text to online/offline. Like so:

// get the online status element from the DOM
var onlineStatusDom = document.querySelector('.online-status');
// navigator.onLine will be true when online and false when offline. We update the text in the online status element in the dom to reflect the online status from navigator.onLine
if (navigator.onLine) {
  onlineStatusDom.innerText = 'online';
} else {
  onlineStatusDom.innerText = 'offline';
}

// we use the 'online' and 'offline' events to update the online/offline notification to the user
// in IE8 the offline/online events exist on document.body rather than window, so make sure to reflect that in your code!
window.addEventListener('offline', function(e) {
  onlineStatusDom.innerText = 'offline';
});

window.addEventListener('online', function(e) {
  onlineStatusDom.innerText = 'online';
});

We want to use JSDOM to test that when offline the offline event fires and our text node updates to say "offline".

JSDOM has a window.navigator.onLine property, but it is read only, and we can't find a way to change it (always true). It seems that it has the online/offline events as well, but I cannot see how to get them to fire.

How can we simulate going online/offline when testing with node?

like image 869
Zooey Miller Avatar asked Jun 21 '17 14:06

Zooey Miller


1 Answers

There is no provision in JSDOM 11.0.0 (which is the current version as I write this answer) for changing navigator.onLine or for generating the online and offline events.

However, it is possible to take over navigator.onLine to control it and to generate the events yourself. Here is a proof of concept:

const { JSDOM } = require("jsdom");
const { window } = new JSDOM();

class OnlineController {
    constructor(win) {
        this.win = win;
        this.onLine = win.navigator.onLine;

        // Replace the default onLine implementation with our own.
        Object.defineProperty(win.navigator.constructor.prototype,
                              "onLine",
                              {
                                  get: () => {
                                      return this.onLine;
                                  },
                              });
    }

    goOnline() {
        const was = this.onLine;
        this.onLine = true;

        // Fire only on transitions.
        if (!was) {
            this.fire("online");
        }
    }

    goOffline() {
        const was = this.onLine;
        this.onLine = false;

        // Fire only on transitions.
        if (was) {
            this.fire("offline");
        }
    }

    fire(event) {
        this.win.dispatchEvent(new this.win.Event(event));
    }
}

window.addEventListener("offline", function () {
    console.log("gone offline");
});

window.addEventListener("online", function () {
    console.log("gone online");
});

const cont = new OnlineController(window);
console.log("online?", window.navigator.onLine);
cont.goOffline();
console.log("online?", window.navigator.onLine);
cont.goOnline();
console.log("online?", window.navigator.onLine);

If you run the file, you should get:

online? true
gone offline
online? false
gone online
online? true
like image 76
Louis Avatar answered Oct 23 '22 18:10

Louis