Leonid Blinov
Leonid Blinov

Reputation: 126

Toggle navigator.online

I am running integration tests with karma using phantomjs. How can I simulate offline mode?

It seems that I cannot change 'navigator.online' and I couldn't find anything on offline mode for phantomjs.

EDIT:

Application is sending messages to external location. When browser goes offline it should stops sending messages and store them in queue. Once connection is restored it should send all messages from queue.

I am simply checking either 'navigator.online' returns true of false.

Maybe there is a better way to implement and test this.

Any advice would be appreciated.

Upvotes: 4

Views: 1218

Answers (2)

Louis
Louis

Reputation: 151501

This is the code I use to be able to control the navigator.onLine method in testing. I use this in tests that are run with Karma to launch the browser and start the tests. Mocha is the actual test runner. The following is run in a before (aka beforeAll) hook. The whole thing (including let onLine) is scoped to the describe block that needs it.

I use two methods because unfortunately, there is no way to modify navigator in a way that works everywhere. The first method works in Chrome, Firefox, IE, Edge and Opera. The 2nd method works in Safari. Conversely, the 2nd method does not work in Chrome. So we cannot use just one method or the other.

let onLine = true;

function mockNavigatorOnline() {
  const descriptor = {
    get: function getOnline() {
      return onLine;
    },
  };

  // 1st method.
  Object.defineProperty(navigator.constructor.prototype, "onLine",
                        descriptor);

  // Check whether the code above "took". We check both for `true` 
  // and `false`.
  onLine = false;
  let passes = navigator.onLine === onLine;
  onLine = true;
  passes = passes && (navigator.onLine === onLine);

  // 2nd method.
  if (!passes) {
    navigator = Object.create(navigator, { onLine: descriptor });
  }
}

Upvotes: 0

Ruan Mendes
Ruan Mendes

Reputation: 92324

navigator.online is a readonly property. Your components should have a separate property so you can set it to false or true in tests (instead of always checking navigator.online directly)

function Storer() {}
Storer.prototype.isOnline = true;


Storer.prototype.store = function() {
    // Instead of reading navigator.isOnline
    if (this.isOnline) {
        this.sendAjax();
    } else {
        this.storeLocally();
    }
}

// In your tests, you can modify isOnline
var storer = new Storer();
storer.isOnline = false;
storer.setSomething();
storer.store();
// Pseudo code here
expect(store.getLocalCache()).notToBeEmpty();

storer.isOnline = false;
store.setSomethingElse();
store.store();
// Pseudo code here
expect(storer.sendAjax).toHaveBeenCalledWith("some", "arg")

Lesson: Don't use global objects in your code if you can, it makes it harder to mock. Instead, allow your global objects to be mocked/stubbed by callers.

Upvotes: 1

Related Questions