resource-manager-js
Version:
Loads and caches all resources needed for a web app including css, templates, and scripts
243 lines (223 loc) • 14.4 kB
JavaScript
import sinon from 'sinon';
import ResourceManager from '../src/resource-manager';
import assert from 'assert';
describe('Resource Manager', function () {
let origWindowFetch;
beforeEach(function () {
origWindowFetch = window.fetch;
window.fetch = sinon.stub();
});
afterEach(function () {
window.fetch = origWindowFetch;
});
it('should load and unload multiple css files', function () {
let cssPaths = ['test/path/to/css/one', 'test/path/to/second/css'];
return ResourceManager.loadCss(cssPaths).then(function () {
let head = document.getElementsByTagName('head')[0];
assert.equal(head.querySelectorAll('link[href="' + cssPaths[0] + '"]').length, 1, 'calling loadCss() with new css files loads first css path in the head of the document');
assert.equal(head.querySelectorAll('link[href="' + cssPaths[1] + '"]').length, 1, 'second css path was loaded into the head of the document');
return ResourceManager.unloadCss(cssPaths).then(function (){
assert.equal(head.querySelectorAll('link[href="' + cssPaths[0] + '"]').length, 0, 'calling unloadCss() removes first css path from the head of the document');
assert.equal(head.querySelectorAll('link[href="' + cssPaths[1] + '"]').length, 0, 'second css path is removed from the head of the document');
ResourceManager.flush();
});
});
});
it('should add and remove css file from DOM appropriately', function () {
let path = 'test/path/to/css/single.css';
return ResourceManager.loadCss(path).then(function () {
let head = document.getElementsByTagName('head')[0];
assert.equal(head.querySelectorAll('link[href="' + path + '"]').length, 1, 'calling loadCss(), adds css in the head of the document');
return ResourceManager.unloadCss(path).then(function () {
assert.equal(head.querySelectorAll('link[href="' + path + '"]').length, 0, 'calling unloadCss() removes css from head of document');
ResourceManager.flush();
});
});
});
it('should NOT load css files that have already been loaded', function () {
let cssPaths = ['test/path/to/css/one', 'test/path/to/second/css'];
let head = document.getElementsByTagName('head')[0];
return ResourceManager.loadCss(cssPaths).then(function () {
assert.equal(head.querySelectorAll('link[href="' + cssPaths[0] + '"]').length, 1, 'on first loadCss() call, first file gets added to the head of the document once');
assert.equal(head.querySelectorAll('link[href="' + cssPaths[1] + '"]').length, 1, 'second file gets added to the head of the document once');
let newPath = 'test/new/third/file';
cssPaths.push(newPath); // add new path
return ResourceManager.loadCss(cssPaths).then(function () {
assert.equal(head.querySelectorAll('link[href="' + cssPaths[0] + '"]').length, 1, 'on second loadCss() call, first file does NOT get added to the head of the document a second time');
assert.equal(head.querySelectorAll('link[href="' + cssPaths[1] + '"]').length, 1, 'second file does NOT get added to the head of the document a second time');
assert.equal(head.querySelectorAll('link[href="' + newPath + '"]').length, 1, 'third file gets added to the head of the document because it was the only one of the files that hasnt been added yet');
ResourceManager.flush();
});
});
});
it('does not crash when nothing is passed to loadCss()', function () {
return ResourceManager.loadCss().then(function () {
assert.ok(true, 'no crash');
ResourceManager.flush();
});
});
it('loadTemplate() should make fetch request with correct options and return proper text response', function () {
let path = 'test/path/to/css/single';
let templateHtml = '<div>mytext</div>';
let serverResp = {text: sinon.stub().returns(Promise.resolve(templateHtml))};
window.fetch.returns(Promise.resolve(serverResp));
return ResourceManager.loadTemplate(path).then(function (html) {
assert.equal(window.fetch.args[0][0], path);
assert.equal(html, templateHtml);
ResourceManager.flush();
});
});
it('does not crash when nothing is passed to loadTemplate()', function () {
return ResourceManager.loadTemplate().then(function () {
assert.ok(true, 'no crash');
ResourceManager.flush();
});
});
it('should inject a javascript file correctly into the DOM when loaded and remove when unloaded', function () {
let path = 'path/to/my.js';
let head = document.getElementsByTagName('head')[0];
let scriptEl = document.createElement('script');
let createScriptElementStub = sinon.stub(ResourceManager, 'createScriptElement').returns(scriptEl);
sinon.stub(scriptEl, 'addEventListener').callsArg(1);
return ResourceManager.loadScript(path).then(function () {
assert.equal(head.querySelectorAll('script[src="' + path + '"]').length, 1, 'on first loadScript() call, file gets added to the head of the document once');
return ResourceManager.loadScript(path).then(function () {
assert.equal(head.querySelectorAll('script[src="' + path + '"]').length, 1, 'attempting to call loadScript() with the same path does not add it to the document a second time');
return ResourceManager.unloadScript(path).then(function () {
assert.equal(head.querySelectorAll('script[src="' + path + '"]').length, 0, 'calling unloadScript() removes the path from the document head');
ResourceManager.flush();
createScriptElementStub.restore();
});
});
});
});
it('should inject multiple javascript files into the DOM when loaded via same load call', function () {
let paths = ['path/to/my/first/file.js', 'path/to/my/second/file.js'];
let head = document.getElementsByTagName('head')[0];
let firstScriptEl = document.createElement('script');
let secondScriptEl = document.createElement('script');
let createScriptElementStub = sinon.stub(ResourceManager, 'createScriptElement');
createScriptElementStub.onFirstCall().returns(firstScriptEl);
createScriptElementStub.onSecondCall().returns(secondScriptEl);
sinon.stub(firstScriptEl, 'addEventListener').callsArg(1);
sinon.stub(secondScriptEl, 'addEventListener').callsArg(1);
return ResourceManager.loadScript(paths).then(function () {
assert.equal(head.querySelectorAll('script[src="' + paths[0] + '"]').length, 1, 'on first loadScript() call, first file gets added to the head of the document once');
assert.equal(head.querySelectorAll('script[src="' + paths[1] + '"]').length, 1, 'second file gets added to the head of the document once');
return ResourceManager.loadScript(paths[1]).then(function () {
assert.equal(head.querySelectorAll('script[src="' + paths[0] + '"]').length, 1, 'attempting to call loadScript() for second file again does not add it to the document a second time');
return ResourceManager.loadScript(paths[0]).then(function () {
assert.equal(head.querySelectorAll('script[src="' + paths[1] + '"]').length, 1, 'attempting to call loadScript() for first file again does not add it to the document a second time');
return ResourceManager.loadScript(paths).then(function () {
assert.equal(head.querySelectorAll('script[src="' + paths[0] + '"]').length, 1, 'attempting to call loadScript() with an array of first and second files again does not add first file to the document a second time');
assert.equal(head.querySelectorAll('script[src="' + paths[1] + '"]').length, 1, 'second file was not added to the document a second time');
return ResourceManager.unloadScript(paths).then(function () {
assert.equal(head.querySelectorAll('script[src="' + paths[0] + '"]').length, 0, 'calling unloadScript() removes the first file from the document head');
assert.equal(head.querySelectorAll('script[src="' + paths[1] + '"]').length, 0, 'second file is removed from the document head');
ResourceManager.flush();
createScriptElementStub.restore();
});
});
});
});
});
});
it('fetchData should make fetch request with correct options and return fetched data object', function () {
let path = 'test/path/to/css/single';
let serverResp = {json: sinon.stub().returns(Promise.resolve()), body: true};
window.fetch.returns(Promise.resolve(serverResp));
let options = {my: 'opts'};
return ResourceManager.fetchData(path, options).then(function (resp) {
assert.deepEqual(window.fetch.args[0], [path, options]);
assert.deepEqual(resp, serverResp);
ResourceManager.flush();
});
});
it('setting cache option to true will return the same response as previous requests and will not make additional fetch call', function () {
let path = 'test/path/to/css/single';
let resp = {json: sinon.stub().returns(Promise.resolve()), body: true};
window.fetch.returns(Promise.resolve(resp));
let options = {opts: 'same', cache: true};
return ResourceManager.fetchData(path, options).then(function (data) {
assert.deepEqual(data, resp, 'correct mock data was returned on first call');
return ResourceManager.fetchData(path, options).then(function (data) {
assert.deepEqual(window.fetch.callCount, 1, 'fetch called was only made once');
assert.deepEqual(data, resp, 'second call returned correct data');
ResourceManager.flush();
});
});
});
it('setting cache option to false will make fetch call always, even if the request is the same as previous ones', function () {
let path = 'test/path/to/css/single';
let mockData = {heres: 'my data'};
let resp = {json: sinon.stub().returns(mockData), body: true};
window.fetch.returns(Promise.resolve(resp));
let options = {opts: 'same', cache: false};
let fetchCallCount = 0;
return ResourceManager.fetchData(path, options).then(function (data) {
fetchCallCount++;
return ResourceManager.fetchData(path, options).then(function (data) {
fetchCallCount++;
assert.equal(window.fetch.callCount, fetchCallCount);
ResourceManager.flush();
});
});
});
it('passing no parameters to fetch data will not make an fetch call and resolve promise immediately', function () {
window.fetch.returns(Promise.resolve());
return ResourceManager.fetchData().then(function () {
assert.equal(window.fetch.callCount, 0, 'fetch was NOT called');
ResourceManager.flush();
});
});
it('calling fetchData() for a second time with the same options as previous after the first failure should perform an fetch request again', function () {
let path = 'test/path/to/css/single';
window.fetch.returns(Promise.reject());
let options = {opts: 'same'};
return ResourceManager.fetchData(path, options).catch(function () {
assert.equal(window.fetch.callCount, 1, 'fetch was called');
return ResourceManager.fetchData(path, options).catch(function () {
assert.equal(window.fetch.callCount, 2, 'fetch was called again after first request failed');
ResourceManager.flush();
});
});
});
it('making a consecutive script request before previous one finishes loads correctly', function () {
let firstPath = 'path/to/my/first/file.js';
let secondPath = 'path/to/my/second/file.js';
let head = document.getElementsByTagName('head')[0];
let firstScriptEl = document.createElement('script');
let secondScriptEl = document.createElement('script');
let createScriptElementStub = sinon.stub(ResourceManager, 'createScriptElement');
createScriptElementStub.onFirstCall().returns(firstScriptEl);
createScriptElementStub.onSecondCall().returns(secondScriptEl);
ResourceManager.loadScript(firstPath);
ResourceManager.loadScript(secondPath);
assert.equal(head.querySelectorAll('script[src="' + firstPath + '"]').length, 1, 'on first loadScript() call, first file gets added to the head of the document once');
assert.equal(head.querySelectorAll('script[src="' + secondPath + '"]').length, 1, 'second file gets added to the head of the document once');
ResourceManager.flush();
createScriptElementStub.restore();
});
it('should reject fetchData() when there is an request failure', function () {
let path = 'test/path/to/css/single';
let errorObj = {my: 'error'};
window.fetch.returns(Promise.reject(errorObj));
let options = {opts: 'same'};
return ResourceManager.fetchData(path, options)
.catch(function (e) {
assert.deepEqual(e, errorObj);
ResourceManager.flush();
});
});
it('fetchTemplate() should make fetch request with correct options and return proper text response', function () {
var path = 'test/path/to/css/single';
var templateHtml = '<div>mytext</div>';
var serverResp = {text: sinon.stub().returns(Promise.resolve(templateHtml))};
window.fetch.returns(Promise.resolve(serverResp));
return ResourceManager.fetchTemplate(path).then(function (html) {
assert.equal(window.fetch.args[0][0], path);
assert.equal(html, templateHtml);
ResourceManager.flush();
});
});
});