Skip to content
This repository has been archived by the owner on Nov 29, 2023. It is now read-only.

Commit

Permalink
Test applicaiton-cache.js
Browse files Browse the repository at this point in the history
  • Loading branch information
eyelidlessness committed Oct 6, 2022
1 parent 8449758 commit f2cf5c3
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 4 deletions.
32 changes: 28 additions & 4 deletions public/js/src/module/application-cache.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,28 @@
import events from './event';
import settings from './settings';

/**
* @private
*
* Used only for mocking `window.reload` in tests.
*/
const location = {
get protocol() {
return window.location.protocol;
},

reload() {
window.location.reload();
},
};

/**
* @private
*
* Exported only for testing.
*/
const UPDATE_REGISTRATION_INTERVAL = 60 * 60 * 1000;

/**
* @typedef {import('../../../../app/models/survey-model').SurveyObject} Survey
*/
Expand All @@ -14,7 +36,7 @@ import settings from './settings';
*/
const init = async (survey) => {
try {
if ('serviceWorker' in navigator) {
if (navigator.serviceWorker != null) {
const registration = await navigator.serviceWorker.register(
`${settings.basePath}/x/offline-app-worker.js`
);
Expand All @@ -29,7 +51,7 @@ const init = async (survey) => {
'Checking for offline application cache service worker update'
);
registration.update();
}, 60 * 60 * 1000);
}, UPDATE_REGISTRATION_INTERVAL);

const currentActive = registration.active;

Expand All @@ -41,15 +63,15 @@ const init = async (survey) => {
navigator.serviceWorker.addEventListener(
'controllerchange',
() => {
window.location.reload();
location.reload();
}
);
}

await registration.update();

if (currentActive == null) {
window.location.reload();
location.reload();
} else {
_reportOfflineLaunchCapable(true);
}
Expand Down Expand Up @@ -88,6 +110,8 @@ function _reportOfflineLaunchCapable(capable = true) {

export default {
init,
location,
UPDATE_REGISTRATION_INTERVAL,
get serviceWorkerScriptUrl() {
if (
'serviceWorker' in navigator &&
Expand Down
168 changes: 168 additions & 0 deletions test/client/application-cache.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
import applicationCache from '../../public/js/src/module/application-cache';
import events from '../../public/js/src/module/event';
import settings from '../../public/js/src/module/settings';

describe('Application Cache', () => {
const basePath = '-';
const offlineLaunchCapableType = events.OfflineLaunchCapable().type;

/** @type {ServiceWorker | null} */
let activeServiceWorker;

/** @type {sinon.SinonSandbox} */
let sandbox;

/** @type {sinon.SinonFakeTimers} */
let timers;

/** @type {sinon.SinonFake} */
let offlineLaunchCapableListener;

/** @type {sinon.SinonStub} */
let reloadStub;

/** @type {sinon.SinonStub} */
let registrationStub;

/** @type {sinon.SinonFake} */
let registrationUpdateFake;

beforeEach(() => {
sandbox = sinon.createSandbox();
timers = sandbox.useFakeTimers(Date.now());

offlineLaunchCapableListener = sinon.fake();

document.addEventListener(
offlineLaunchCapableType,
offlineLaunchCapableListener
);

activeServiceWorker = null;

registrationUpdateFake = sandbox.fake(() => Promise.resolve());

registrationStub = sandbox
.stub(navigator.serviceWorker, 'register')
.callsFake(() =>
Promise.resolve({
addEventListener() {},
active: activeServiceWorker,
update: registrationUpdateFake,
})
);
reloadStub = sandbox
.stub(applicationCache.location, 'reload')
.callsFake(() => {});

if (!('basePath' in settings)) {
settings.basePath = undefined;
}

sandbox.stub(settings, 'basePath').value(basePath);
});

afterEach(() => {
document.removeEventListener(
offlineLaunchCapableType,
offlineLaunchCapableListener
);
timers.restore();
sandbox.restore();
});

it('registers the service worker script', async () => {
await applicationCache.init();

expect(registrationStub).to.have.been.calledWith(
`${basePath}/x/offline-app-worker.js`
);
});

it('reloads immediately after registering the service worker for the first time', async () => {
await applicationCache.init();

expect(reloadStub).to.have.been.called;
});

it('does not reload immediately after registering the service worker for subsequent times', async () => {
activeServiceWorker = {};

await applicationCache.init();

expect(reloadStub).not.to.have.been.called;
});

it('reports offline capability after registering the service worker for subsequent times', async () => {
activeServiceWorker = {};

await applicationCache.init();

expect(offlineLaunchCapableListener).to.have.been.calledWith(
events.OfflineLaunchCapable({ capable: true })
);
});

it('reports offline capability is not available when service workers are not available', async () => {
activeServiceWorker = {};

sandbox.stub(navigator, 'serviceWorker').value(null);

await applicationCache.init();

expect(offlineLaunchCapableListener).to.have.been.calledWith(
events.OfflineLaunchCapable({ capable: false })
);
});

it('reports offline capability is not available when registration throws an error', async () => {
activeServiceWorker = {};

const error = new Error('Something bad');

registrationStub.callsFake(() => Promise.reject(error));

/** @type {Error} */
let caught;

try {
await applicationCache.init();
} catch (error) {
caught = error;
}

expect(offlineLaunchCapableListener).to.have.been.calledWith(
events.OfflineLaunchCapable({ capable: false })
);
expect(caught instanceof Error).to.equal(true);
expect(caught.message).to.include(error.message);
expect(caught.stack).to.equal(error.stack);
});

it('reloads when an updated service worker becomes active', async () => {
activeServiceWorker = {};
await applicationCache.init();

expect(applicationCache.location.reload).not.to.have.been.called;

navigator.serviceWorker.dispatchEvent(new Event('controllerchange'));

expect(applicationCache.location.reload).to.have.been.called;
});

it('checks for updates immediately after registration', async () => {
await applicationCache.init();

expect(registrationUpdateFake).to.have.been.calledOnce;
});

it('checks for updates immediately after registration', async () => {
await applicationCache.init();

expect(registrationUpdateFake).to.have.been.calledOnce;

timers.tick(applicationCache.UPDATE_REGISTRATION_INTERVAL);

expect(registrationUpdateFake).to.have.been.calledTwice;
});
});

0 comments on commit f2cf5c3

Please sign in to comment.