Commit 7bacc24c authored by Romain Courteaud's avatar Romain Courteaud

Add external component's loading functionnality

Allow to fetch HTML gadget and all its requirements.
parent 09687067
...@@ -10,9 +10,10 @@ all: lint test build doc ...@@ -10,9 +10,10 @@ all: lint test build doc
$(RENDERJS_MIN): $(RENDERJS) $(RENDERJS_MIN): $(RENDERJS)
$(UGLIFY_CMD) "$<" > "$@" $(UGLIFY_CMD) "$<" > "$@"
${BUILDDIR}/$(RENDERJS).lint: $(RENDERJS) ${BUILDDIR}/$(RENDERJS).lint: $(RENDERJS) test/renderjs_test2.js
@mkdir -p $(@D) @mkdir -p $(@D)
$(LINT_CMD) "$<" $(LINT_CMD) "$(RENDERJS)"
$(LINT_CMD) "test/renderjs_test2.js"
touch $@ touch $@
${BUILDDIR}/index.html.ok: test/index.html ${BUILDDIR}/index.html.ok: test/index.html
......
handle relative url #parseGadgetHTML TODO
how to manage local script tag #parseGadgetHTML TODO
check that gadget/dom context is kept in promise TODO
keep css file media query #declareCSS TODO
/*! RenderJs v0.2 */ /*! RenderJs v0.2 */
/*global $, localStorage, jIO */ /*global $, jQuery, localStorage, jIO, window, document */
/*jslint evil: true, indent: 2, maxerr: 3, maxlen: 79 */ /*jslint evil: true, indent: 2, maxerr: 3, maxlen: 79 */
"use strict"; "use strict";
/* /*
* RenderJs - Generic Gadget library renderer. * renderJs - Generic Gadget library renderer.
* http://www.renderjs.org/documentation * http://www.renderjs.org/documentation
*/ */
(function (document, window, $) {
var gadget_model_dict = {},
gadget_scope_dict = {},
javascript_registration_dict = {},
stylesheet_registration_dict = {},
root_gadget,
rootrenderJS,
renderJS,
declareGadget,
declareJavascript,
methods;
function RenderJSGadget() {
this.title = "";
this.interface_list = [];
this.path = "";
this.html = "";
this.required_css_list = [];
this.required_js_list = [];
}
// Returns the list of gadget prototype
RenderJSGadget.prototype.getInterfaceList = function () {
var dfr = $.Deferred(),
gadget = this,
context = $(this);
setTimeout(function () {
dfr.resolveWith(context, [gadget.interface_list]);
});
return dfr.promise();
};
// Returns a list of CSS required by the gadget
RenderJSGadget.prototype.getRequiredCSSList = function () {
var dfr = $.Deferred(),
gadget = this,
context = $(this);
setTimeout(function () {
dfr.resolveWith(context, [gadget.required_css_list]);
});
return dfr.promise();
};
// Returns a list of JS required by the gadget
RenderJSGadget.prototype.getRequiredJSList = function () {
var dfr = $.Deferred(),
gadget = this,
context = $(this);
setTimeout(function () {
dfr.resolveWith(context, [gadget.required_js_list]);
});
return dfr.promise();
};
// Returns the path of the code of a gadget
RenderJSGadget.prototype.getPath = function () {
var dfr = $.Deferred(),
gadget = this,
context = $(this);
setTimeout(function () {
dfr.resolveWith(context, [gadget.path]);
});
return dfr.promise();
};
// Returns the title of a gadget
RenderJSGadget.prototype.getTitle = function () {
var dfr = $.Deferred(),
gadget = this,
context = $(this);
setTimeout(function () {
dfr.resolveWith(context, [gadget.title]);
});
return dfr.promise();
};
// Returns the HTML of a gadget
RenderJSGadget.prototype.getHTML = function () {
var dfr = $.Deferred(),
gadget = this,
context = $(this);
setTimeout(function () {
dfr.resolveWith(context, [gadget.html]);
});
return dfr.promise();
};
// RenderJSGadget.prototype.declareMethod = function (name, callback) {
// // Register the potentially loading javascript
// var script_element = $('script').last(),
// src = script_element.attr('src');
// if (src !== undefined) {
// if (javascript_registration_dict[src] === undefined) {
// // First time loading the JS file.
// // Remember all declareMethod calls
// javascript_registration_dict[src] = {
// loaded: false,
// method_list: [[name, callback]],
// };
// script_element.load(function () {
// javascript_registration_dict[src].loaded = true;
// });
// } else if (!javascript_registration_dict[src].loaded) {
// javascript_registration_dict[src].method_list.push([name, callback]);
// }
// }
//
// // Add the method on the gadget prototype
// RenderJSGadget.prototype[name] = callback;
// return this;
// };
//
// $.parseGadgetHTML = function (data) {
// // var xml = $.parseXML(data);
// // var xml = $(data);
// // console.log(xml);
// return data;
// // return new RenderJSGadget();
// };
//
// function RenderJS() {
// }
methods = {
loadGadgetFromDom: function () {
$(this).find('[data-gadget-path]').each(function (index, value) {
$(this).renderJS('declareGadget', $(this).attr('data-gadget-path'), {
scope: $(this).attr('data-gadget-scope'),
})
.done(function (value) {
var parsed_xml;
// Check that context is still attached to the DOM
// XXX Usefull?
if ($(this).closest(document.body).length) {
parsed_xml = $($.parseXML(value));
// Inject the css
// XXX Manage relative URL
$.each(parsed_xml.find('link[rel=stylesheet]'),
function (i, link) {
$('head').append(
'<link rel="stylesheet" href="' +
$(link).attr('href') +
'" type="text/css" />'
);
});
// Inject the js
// XXX Manage relative URL
$.each(parsed_xml.find('script[type="text/javascript"]'),
function (i, script) {
// $('head').append(
// '<script type="text/javascript" href="' +
// $(script).attr('src') +
// '" />'
// );
// Prevent infinite recursion if loading render.js
// more than once
if ($('head').find('script[src="' + $(script).attr('src')
+ '"]').length === 0) {
var headID = document.getElementsByTagName("head")[0],
newScript = document.createElement('script');
newScript.type = 'text/javascript';
newScript.src = $(script).attr('src');
headID.appendChild(newScript);
}
});
// Inject the html
// XXX parseXML does not support <div /> (without 2 tags)
$(this).html(parsed_xml.find('body').clone());
// XXX No idea why it is required to make it work
// Probably because of parseXML
$(this).html($(this).html())
.renderJS('loadGadgetFromDom');
}
});
});
},
};
$.fn.renderJS = function (method) {
var result;
if (methods.hasOwnProperty(method)) {
result = methods[method].apply(
this,
Array.prototype.slice.call(arguments, 1)
);
} else {
$.error('Method ' + method +
' does not exist on jQuery.renderJS');
}
return result;
};
// // Define a local copy of renderJS
// renderJS = function (selector) {
// // The renderJS object is actually just the init constructor 'enhanced'
// return new renderJS.fn.init(selector, rootrenderJS);
// };
// renderJS.fn = renderJS.prototype = {
// constructor: renderJS,
// init: function (selector, rootrenderJS) {
// var result;
// // HANDLE: $(""), $(null), $(undefined), $(false)
// if (!selector) {
// console.log("no selector");
// result = this;
// // // HANDLE: $(DOMElement)
// // } else if (selector.nodeType) {
// // this.context = this[0] = selector;
// // this.length = 1;
// // result = this;
// // } else if (selector === this) {
// // result = this.constructor();
// } else {
// // throw new Error("Not implemented selector " + selector);
// result = this.constructor();
// }
// return result;
// },
// };
// // Give the init function the renderJS prototype for later instantiation
// renderJS.fn.init.prototype = renderJS.fn;
//
// jQuery.fn.extend({
// attr: function (name, value) {
// return jQuery.access(this, jQuery.attr, name, value,
// arguments.length > 1);
// },
// });
renderJS = function (selector) {
var result;
if (typeof selector === "string") {
result = gadget_scope_dict[selector];
} else {
result = root_gadget;
}
return result;
};
renderJS.declareJS = function (url) {
// // Prevent infinite recursion if loading render.js
// // more than once
// if ($('head').find('script[src="' + $(script).attr('src')
// + '"]').length === 0) {
// var headID = document.getElementsByTagName("head")[0],
// newScript = document.createElement('script');
// newScript.type = 'text/javascript';
// newScript.src = $(script).attr('src');
// headID.appendChild(newScript);
// }
var dfr,
origin_dfr;
if (javascript_registration_dict.hasOwnProperty(url)) {
origin_dfr = $.Deferred();
setTimeout(function () {
origin_dfr.resolveWith($(this), []);
});
dfr = origin_dfr.promise();
} else {
dfr = $.ajax({
url: url,
dataType: "script",
cache: true,
}).done(function (script, textStatus) {
javascript_registration_dict[url] = null;
});
}
return dfr;
};
renderJS.declareCSS = function (url) {
// https://github.com/furf/jquery-getCSS/blob/master/jquery.getCSS.js
var origin_dfr = $.Deferred(),
head,
link;
if (stylesheet_registration_dict.hasOwnProperty(url)) {
setTimeout(function () {
origin_dfr.resolveWith($(this), []);
});
} else {
head = document.getElementsByTagName('head')[0];
link = document.createElement('link');
link.rel = 'stylesheet';
link.type = 'text/css';
link.href = url;
origin_dfr.done(function () {
stylesheet_registration_dict[url] = null;
});
head.appendChild(link);
setTimeout(function () {
origin_dfr.resolveWith($(this), []);
});
}
return origin_dfr.promise();
};
renderJS.declareGadgetKlass = function (url) {
var dfr = $.Deferred(),
parsed_html;
if (gadget_model_dict.hasOwnProperty(url)) {
dfr.resolveWith($(this), [gadget_model_dict[url]]);
} else {
$.ajax(url)
.done(function (value, textStatus, jqXHR) {
var klass, tmp_constructor, key;
if ((jqXHR.getResponseHeader("Content-Type") || "")
=== 'text/html') {
try {
if (!gadget_model_dict.hasOwnProperty(url)) {
// Class inheritance
tmp_constructor = function () {
RenderJSGadget.call(this);
};
tmp_constructor.prototype = new RenderJSGadget();
tmp_constructor.prototype.constructor = tmp_constructor;
tmp_constructor.prototype.path = url;
parsed_html = renderJS.parseGadgetHTML(value);
for (key in parsed_html) {
if (parsed_html.hasOwnProperty(key)) {
tmp_constructor.prototype[key] = parsed_html[key];
}
}
gadget_model_dict[url] = tmp_constructor;
}
dfr.resolveWith($(this), [gadget_model_dict[url]]);
} catch (e) {
dfr.rejectWith($(this), [jqXHR, "HTML Parsing failed"]);
}
} else {
dfr.rejectWith($(this), [jqXHR, "Unexpected content type"]);
}
})
.fail(function (jqXHR, textStatus, errorThrown) {
dfr.rejectWith($(this), [jqXHR, textStatus, errorThrown]);
});
}
return dfr.promise();
};
// For test purpose only
renderJS.clearGadgetKlassList = function () {
gadget_model_dict = {};
};
renderJS.parseGadgetHTML = function (html) {
var parsed_xml,
result,
settings = {
title: "",
interface_list: [],
html: "",
required_css_list: [],
required_js_list: [],
};
if (html.constructor === String) {
parsed_xml = $($.parseXML(html));
settings.title = parsed_xml.find('head > title').first().text();
// XXX Manage relative URL during extraction of URLs
$.each(parsed_xml.find('head > link[rel=stylesheet]'),
function (i, link) {
settings.required_css_list.push($(link).attr('href'));
});
$.each(parsed_xml.find('head > script[type="text/javascript"]'),
function (i, script) {
settings.required_js_list.push($(script).attr('src'));
});
$.each(parsed_xml.find(
'head > link[rel="http://www.renderjs.org/rel/interface"]'
), function (i, link) {
settings.interface_list.push($(link).attr('href'));
});
settings.html = parsed_xml.find('html > body').first().html() || "";
result = settings;
} else {
throw new Error(html + " is not a string");
}
return result;
};
window.rJS = window.renderJS = renderJS;
window.RenderJSGadget = RenderJSGadget;
///////////////////////////////////////////////////
// Internal functions
///////////////////////////////////////////////////
// declareGadget = function (url, settings) {
// // XXX Return promise
// var dfr = $.Deferred(),
// jqxhr = $.ajax(url, {context: $(this)})
// .done(function (value, textStatus, jqXHR) {
// if ((jqXHR.getResponseHeader("Content-Type") || "")
// === 'text/html') {
// dfr.resolveWith($(this),
// [$.parseGadgetHTML(value), textStatus, jqXHR]);
// } else {
// dfr.rejectWith($(this), [jqXHR, "Unexpected content type"]);
// }
// })
// .fail(function (jqXHR, textStatus, errorThrown) {
// dfr.rejectWith($(this), [jqXHR, textStatus, errorThrown]);
// });
// console.log("Declaring gadget " + url);
// // console.log(settings.context.html());
// return dfr.promise();
// };
// declareJavascript = function () {
// console.log($(this).attr('src') + " JS loaded");
// };
///////////////////////////////////////////////////
// jQuery plugin registration
///////////////////////////////////////////////////
$.fn.declareGadget = function (url, settings) {
settings.context = $(this);
return declareGadget(url, settings);
};
///////////////////////////////////////////////////
// Bootstrap process. Register the self gadget.
///////////////////////////////////////////////////
// XXX Parse HTML, remember loaded JS, title, css
// XXX Create root gadget
// gadget_model_dict = {window.location: RenderJSGadgetFactory()},
// javascript_registration_dict = {},
// Do not wait for document.ready, as the JS file loading have to be checked
// $.each(document.getElementsByTagName("script"), function (i, elmt) {
// console.log("Set onload " + i);
// elmt.onload = declareGadget;
// });
// newScript = document.createElement('script');
// newScript.type = 'text/javascript';
// newScript.src = $(script).attr('src');
// newScript.onreadystatechange = function () {
// $(document).on('load', declareJavascript).each(function () {
// console.log($(this).attr('src') + " prepared for loading");
// });
// $('body').declareGadget(window.location, {
// scope: "root",
// });
// root_gadget = new RenderJSGadget();
// $(document).ready(function () {
// $('link[rel=stylesheet]').each(function (i, link) {
// root_gadget.required_css_list.push($(link).attr('href'));
// });
// $('script[type="text/javascript"]').each(function (i, script) {
// root_gadget.required_js_list.push($(script).attr('src'));
// });
// root_gadget.html = $("body").html();
// root_gadget.path = window.location.href;
// root_gadget.title = $(document).attr('title');
// gadget_scope_dict.root = root_gadget;
//
// $("body").renderJS('loadGadgetFromDom');
//
// setTimeout(function () {
// root_gadget.getTitle().done(function (title) {
// console.log("Root: " + title);
// });
//
// renderJS("slider").getTitle().done(function (title) {
// console.log("Slider: " + title);
// });
//
// renderJS("first").getTitle().done(function (title) {
// console.log("First: " + title);
// });
//
// renderJS("second").getTitle().done(function (title) {
// console.log("Second: " + title);
// });
// }, 500);
//
//
// // XXX Display sub gadgets title
// });
// $(document).ready(function () {
// root_gadget = $('body').declareGadget(window.location, {
// scope: "root",
// });
// });
// // XXX Load gadgets defined in the html
// $('body').renderJS('loadGadgetFromDom');
}(document, window, jQuery));
/** /**
* By default RenderJs will render all gadgets when page is loaded * By default RenderJs will render all gadgets when page is loaded
* still it's possible to override this and use explicit gadget rendering. * still it's possible to override this and use explicit gadget rendering.
* *
* @property RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING * @property RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING
* @type {Boolean} * @type {Boolean}
* @default "true" * @default "true"
...@@ -22,7 +532,7 @@ var RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING = true; ...@@ -22,7 +532,7 @@ var RENDERJS_ENABLE_IMPLICIT_GADGET_RENDERING = true;
/** /**
* By default RenderJs will examine and bind all interaction gadgets * By default RenderJs will examine and bind all interaction gadgets
* available. * available.
* *
* @property RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND * @property RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND
* @type {Boolean} * @type {Boolean}
* @default "true" * @default "true"
...@@ -31,7 +541,7 @@ var RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND = true; ...@@ -31,7 +541,7 @@ var RENDERJS_ENABLE_IMPLICIT_INTERACTION_BIND = true;
/** /**
* By default RenderJs will examine and create all routes * By default RenderJs will examine and create all routes
* *
* @property RENDERJS_ENABLE_IMPLICIT_ROUTE_CREATE * @property RENDERJS_ENABLE_IMPLICIT_ROUTE_CREATE
* @type {Boolean} * @type {Boolean}
* @default "true" * @default "true"
......
...@@ -2,17 +2,22 @@ ...@@ -2,17 +2,22 @@
"http://www.w3.org/TR/html4/loose.dtd"> "http://www.w3.org/TR/html4/loose.dtd">
<html> <html>
<head> <head>
<link rel="stylesheet" href="../lib/qunit/qunit.css" type="text/css"/> <title>Test renderJS</title>
<meta name="viewport" content="width=device-width, height=device-height"/>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="../lib/qunit/qunit.css" type="text/css" media="screen"/>
<script src="../lib/jquery/jquery.js" type="text/javascript"></script>
<script src="../lib/qunit/qunit.js" type="text/javascript"></script> <script src="../lib/qunit/qunit.js" type="text/javascript"></script>
<script data-main="require-renderjs_test.js" <script src="../lib/sinon/sinon.js" type="text/javascript"></script>
type="text/javascript" <script src="../renderjs.js" type="text/javascript"></script>
src="../lib/require/require.js"></script> <script src="renderjs_test2.js" type="text/javascript"></script>
</head> </head>
<body> <body>
<h1 id="qunit-header">QUnit RenderJS test suite</h1> <h1 id="qunit-header">QUnit renderJS test suite</h1>
<h2 id="qunit-banner"></h2> <h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2> <h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol> <ol id="qunit-tests"></ol>
<div id="qunit-fixture"> </div> <div id="qunit-fixture">test markup, will be hidden</div>
</body> </body>
</html> </html>
/*global window, document, QUnit, jQuery, renderJS, RenderJSGadget, sinon */
/*jslint indent: 2, maxerr: 3, maxlen: 79 */
"use strict";
(function (document, $, renderJS, QUnit, sinon) {
var test = QUnit.test,
stop = QUnit.stop,
start = QUnit.start,
ok = QUnit.ok,
equal = QUnit.equal,
expect = QUnit.expect,
throws = QUnit.throws,
deepEqual = QUnit.deepEqual;
/////////////////////////////////////////////////////////////////
// parseGadgetHTML
/////////////////////////////////////////////////////////////////
module("renderJS.parseGadgetHTML");
test('Not valid HTML string', function () {
// Check that parseGadgetHTML throws an error if the string is
// not a valid xml
throws(function () {
renderJS.parseGadgetHTML("<ht");
});
});
test('Not string', function () {
// Check that parseGadgetHTML throws an error if the parameter is not a
// string
throws(function () {
renderJS.parseGadgetHTML({});
});
});
test('Default result value', function () {
// Check default value returned by parseGadgetHTML
deepEqual(renderJS.parseGadgetHTML(""), {
title: "",
interface_list: [],
required_css_list: [],
required_js_list: [],
html: "",
});
});
test('Extract title', function () {
// Check that parseGadgetHTML correctly extract the title
var settings,
html = "<html>" +
"<head>" +
"<title>Great title</title>" +
"</head></html>";
settings = renderJS.parseGadgetHTML(html);
equal(settings.title, 'Great title', 'Title extracted');
});
test('Extract only one title', function () {
// Check that parseGadgetHTML correctly extract the first title
var settings,
html = "<html>" +
"<head>" +
"<title>Great title</title>" +
"<title>Great title 2</title>" +
"</head></html>";
settings = renderJS.parseGadgetHTML(html);
equal(settings.title, 'Great title', 'First title extracted');
});
test('Extract title only from head', function () {
// Check that parseGadgetHTML only extract title from head
var settings,
html = "<html>" +
"<body>" +
"<title>Great title</title>" +
"</body></html>";
settings = renderJS.parseGadgetHTML(html);
equal(settings.title, '', 'Title not found');
});
test('Extract body', function () {
// Check that parseGadgetHTML correctly extract the body
var settings,
html = "<html>" +
"<body>" +
"<p>Foo</p>" +
"</body></html>";
settings = renderJS.parseGadgetHTML(html);
equal(settings.html, "<p>Foo</p>", "HTML extracted");
});
test('Extract only one body', function () {
// Check that parseGadgetHTML correctly extract the first title
var settings,
html = "<html>" +
"<body>" +
"<p>Foo</p>" +
"</body><body>" +
"<p>Bar</p>" +
"</body></html>";
settings = renderJS.parseGadgetHTML(html);
equal(settings.html, '<p>Foo</p>', 'First body extracted');
});
test('Extract body only from html', function () {
// Check that parseGadgetHTML only extract title from html
var settings,
html = "<html>" +
"<head><body><p>Bar</p></body></head>" +
"</html>";
settings = renderJS.parseGadgetHTML(html);
equal(settings.html, "", "Body not found");
});
test('Extract CSS', function () {
// Check that parseGadgetHTML correctly extract the CSS
var settings,
html = "<html>" +
"<head>" +
"<link rel='stylesheet' href='../lib/qunit/qunit.css' " +
"type='text/css'/>" +
"</head></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.required_css_list,
['../lib/qunit/qunit.css'],
"CSS extracted");
});
test('Extract CSS order', function () {
// Check that parseGadgetHTML correctly keep CSS order
var settings,
html = "<html>" +
"<head>" +
"<link rel='stylesheet' href='../lib/qunit/qunit.css' " +
"type='text/css'/>" +
"<link rel='stylesheet' href='../lib/qunit/qunit2.css' " +
"type='text/css'/>" +
"</head></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.required_css_list,
['../lib/qunit/qunit.css', '../lib/qunit/qunit2.css'],
"CSS order kept");
});
test('Extract CSS only from head', function () {
// Check that parseGadgetHTML only extract css from head
var settings,
html = "<html>" +
"<body>" +
"<link rel='stylesheet' href='../lib/qunit/qunit.css' " +
"type='text/css'/>" +
"</body></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.required_css_list, [], "CSS not found");
});
test('Extract interface', function () {
// Check that parseGadgetHTML correctly extract the interface
var settings,
html = "<html>" +
"<head>" +
"<link rel='http://www.renderjs.org/rel/interface'" +
" href='./interface/renderable'/>" +
"</head></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.interface_list,
['./interface/renderable'],
"interface extracted");
});
test('Extract interface order', function () {
// Check that parseGadgetHTML correctly keep interface order
var settings,
html = "<html>" +
"<head>" +
"<link rel='http://www.renderjs.org/rel/interface'" +
" href='./interface/renderable'/>" +
"<link rel='http://www.renderjs.org/rel/interface'" +
" href='./interface/field'/>" +
"</head></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.interface_list,
['./interface/renderable',
'./interface/field'],
"interface order kept");
});
test('Extract interface only from head', function () {
// Check that parseGadgetHTML only extract interface from head
var settings,
html = "<html>" +
"<body>" +
"<link rel='http://www.renderjs.org/rel/interface'" +
" href='./interface/renderable'/>" +
"</body></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.interface_list, [], "interface not found");
});
test('Extract JS', function () {
// Check that parseGadgetHTML correctly extract the JS
var settings,
html = "<html>" +
"<head>" +
"<script src='../lib/qunit/qunit.js' " +
"type='text/javascript'></script>" +
"</head></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.required_js_list,
['../lib/qunit/qunit.js'],
"JS extracted");
});
test('Extract JS order', function () {
// Check that parseGadgetHTML correctly keep JS order
var settings,
html = "<html>" +
"<head>" +
"<script src='../lib/qunit/qunit.js' " +
"type='text/javascript'></script>" +
"<script src='../lib/qunit/qunit2.js' " +
"type='text/javascript'></script>" +
"</head></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.required_js_list,
['../lib/qunit/qunit.js', '../lib/qunit/qunit2.js'],
"JS order kept");
});
test('Extract JS only from head', function () {
// Check that parseGadgetHTML only extract js from head
var settings,
html = "<html>" +
"<body>" +
"<script src='../lib/qunit/qunit.js' " +
"type='text/javascript'></script>" +
"</body></html>";
settings = renderJS.parseGadgetHTML(html);
deepEqual(settings.required_js_list, [], "JS not found");
});
/////////////////////////////////////////////////////////////////
// declareGadgetKlass
/////////////////////////////////////////////////////////////////
module("renderJS.declareGadgetKlass");
test('Ajax error reject the promise', function () {
// Check that declareGadgetKlass fails if ajax fails
renderJS.clearGadgetKlassList();
var server = sinon.fakeServer.create(),
url = 'https://example.org/files/qunittest/test';
server.autoRespond = true;
server.autoRespondAfter = 1;
server.respondWith("GET", url, [404, {
"Content-Type": "text/html",
}, "foo"]);
stop();
renderJS.declareGadgetKlass(url)
.done(function () {
ok(false, "404 should fail");
})
.fail(function (jqXHR, textStatus) {
equal("404", jqXHR.status);
})
.always(function () {
start();
});
});
test('Non HTML reject the promise', function () {
// Check that declareGadgetKlass fails if non html is retrieved
renderJS.clearGadgetKlassList();
var server = sinon.fakeServer.create(),
url = 'https://example.org/files/qunittest/test';
server.autoRespond = true;
server.autoRespondAfter = 1;
server.respondWith("GET", url, [200, {
"Content-Type": "text/plain",
}, "foo"]);
stop();
renderJS.declareGadgetKlass(url)
.done(function () {
ok(false, "text/plain should fail");
})
.fail(function (jqXHR, textStatus) {
equal("200", jqXHR.status);
})
.always(function () {
start();
});
});
test('HTML parsing failure reject the promise', function () {
// Check that declareGadgetKlass fails if the html can not be parsed
renderJS.clearGadgetKlassList();
var server = sinon.fakeServer.create(),
url = 'https://example.org/files/qunittest/test',
mock;
server.autoRespond = true;
server.autoRespondAfter = 1;
server.respondWith("GET", url, [200, {
"Content-Type": "text/html",
}, ""]);
mock = sinon.mock(renderJS, "parseGadgetHTML", function () {
throw new Error();
});
mock.expects("parseGadgetHTML").once().throws();
stop();
renderJS.declareGadgetKlass(url)
.done(function () {
ok(false, "text/plain should fail");
})
.fail(function (jqXHR, textStatus) {
equal("200", jqXHR.status);
})
.always(function () {
mock.verify();
start();
mock.restore();
});
});
test('Klass creation', function () {
// Check that declareGadgetKlass returns a subclass of RenderJSGadget
// and contains all extracted properties on the prototype
renderJS.clearGadgetKlassList();
var server = sinon.fakeServer.create(),
url = 'https://example.org/files/qunittest/test',
mock;
server.autoRespond = true;
server.autoRespondAfter = 1;
server.respondWith("GET", url, [200, {
"Content-Type": "text/html",
}, "foo"]);
mock = sinon.mock(renderJS, "parseGadgetHTML");
mock.expects("parseGadgetHTML").once().withArgs("foo").returns(
{foo: 'bar'}
);
stop();
renderJS.declareGadgetKlass(url)
.done(function (Klass) {
var instance;
equal(Klass.prototype.path, url);
equal(Klass.prototype.foo, 'bar');
instance = new Klass();
ok(instance instanceof RenderJSGadget);
ok(instance instanceof Klass);
ok(Klass !== RenderJSGadget);
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
mock.verify();
start();
mock.restore();
});
});
test('Klass is not reloaded if called twice', function () {
// Check that declareGadgetKlass does not reload the gadget
// if it has already been loaded
renderJS.clearGadgetKlassList();
var server = sinon.fakeServer.create(),
url = 'https://example.org/files/qunittest/test',
mock;
server.autoRespond = true;
server.autoRespondAfter = 1;
server.respondWith("GET", url, [200, {
"Content-Type": "text/html",
}, "foo"]);
mock = sinon.mock(renderJS, "parseGadgetHTML");
mock.expects("parseGadgetHTML").once().withArgs("foo").returns(
{foo: 'bar'}
);
stop();
renderJS.declareGadgetKlass(url)
.done(function (Klass1) {
var spy;
mock.restore();
server.restore();
spy = sinon.spy($, "ajax");
renderJS.declareGadgetKlass(url)
.done(function (Klass2) {
equal(Klass1, Klass2);
ok(!spy.called);
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
spy.restore();
});
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
start();
});
});
/////////////////////////////////////////////////////////////////
// declareJS
/////////////////////////////////////////////////////////////////
module("renderJS.declareJS");
test('Ajax error reject the promise', function () {
// Check that declareJS fails if ajax fails
renderJS.clearGadgetKlassList();
var url = 'foo://bar';
stop();
renderJS.declareJS(url)
.done(function () {
ok(false, "404 should fail");
})
.fail(function (jqXHR, textStatus) {
equal("404", jqXHR.status);
})
.always(function () {
start();
});
});
test('Non JS reject the promise', function () {
// Check that declareJS fails if mime type is wrong
renderJS.clearGadgetKlassList();
var url = "data:image/png;base64," +
window.btoa("= = =");
stop();
renderJS.declareJS(url)
.done(function (value, textStatus, jqXHR) {
ok(true, "Non JS mime type should load");
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
});
});
test('JS cleanly loaded', function () {
// Check that declareJS is fetched and loaded
renderJS.clearGadgetKlassList();
var url = "data:application/javascript;base64," +
window.btoa("$('#qunit-fixture').text('JS fetched and loaded');");
stop();
renderJS.declareJS(url)
.done(function () {
equal($("#qunit-fixture").text(), "JS fetched and loaded");
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
});
});
test('JS with errors cleanly loaded', function () {
// Check that declareJS is fetched and loaded even if JS contains an error
renderJS.clearGadgetKlassList();
var url = "data:application/javascript;base64," +
window.btoa("throw new Error('foo');");
stop();
renderJS.declareJS(url)
.done(function () {
ok(true, "JS with error cleanly loaded");
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
});
});
test('JS is not fetched twice', function () {
// Check that declareJS does not load the JS twice
renderJS.clearGadgetKlassList();
var url = "data:application/javascript;base64," +
window.btoa("$('#qunit-fixture').text('JS not fetched twice');");
stop();
renderJS.declareJS(url)
.done(function () {
equal($("#qunit-fixture").text(), "JS not fetched twice");
$("#qunit-fixture").text("");
renderJS.declareJS(url)
.done(function () {
equal($("#qunit-fixture").text(), "");
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
});
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
start();
});
});
/////////////////////////////////////////////////////////////////
// declareCSS
/////////////////////////////////////////////////////////////////
module("renderJS.declareCSS");
test('Ajax error reject the promise', function () {
// Check that declareCSS fails if ajax fails
renderJS.clearGadgetKlassList();
var url = 'foo://bar';
stop();
renderJS.declareCSS(url)
.done(function () {
ok(false, "404 should fail");
})
.fail(function (jqXHR, textStatus) {
equal("404", jqXHR.status);
})
.always(function () {
start();
});
});
test('Non CSS reject the promise', function () {
// Check that declareCSS fails if mime type is wrong
renderJS.clearGadgetKlassList();
var url = "data:image/png;base64," +
window.btoa("= = =");
stop();
renderJS.declareCSS(url)
.done(function (value, textStatus, jqXHR) {
ok(true, "Non CSS mime type should load");
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
});
});
test('CSS cleanly loaded', function () {
// Check that declareCSS is fetched and loaded
renderJS.clearGadgetKlassList();
var url = "data:text/css;base64," +
window.btoa("#qunit-fixture {background-color: red;}");
stop();
renderJS.declareCSS(url)
.done(function () {
var found = false;
$('head').find('link[rel=stylesheet]').each(function (i, style) {
if (style.href === url) {
found = true;
}
});
ok(found, "CSS in the head");
equal($("#qunit-fixture").css("background-color"), "rgb(255, 0, 0)");
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
});
});
test('CSS with errors cleanly loaded', function () {
// Check that declareCSS is fetched and
// loaded even if CSS contains an error
renderJS.clearGadgetKlassList();
var url = "data:application/javascript;base64," +
window.btoa("throw new Error('foo');");
stop();
renderJS.declareCSS(url)
.done(function () {
ok(true, "CSS with error cleanly loaded");
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
});
});
test('CSS is not fetched twice', function () {
// Check that declareCSS does not load the CSS twice
renderJS.clearGadgetKlassList();
var url = "data:text/css;base64," +
window.btoa("#qunit-fixture {background-color: blue;}");
stop();
renderJS.declareCSS(url)
.done(function () {
equal($("#qunit-fixture").css("background-color"), "rgb(0, 0, 255)");
$('head').find('link[rel=stylesheet]').each(function (i, style) {
if (style.href === url) {
$(style).remove();
}
});
ok($("#qunit-fixture").css("background-color") !== "rgb(0, 0, 255)");
renderJS.declareCSS(url)
.done(function () {
var found = false;
$('head').find('link[rel=stylesheet]').each(function (i, style) {
if (style.href === url) {
found = true;
}
});
ok($("#qunit-fixture").css("background-color") !==
"rgb(0, 0, 255)", $("#qunit-fixture").css("background-color"));
ok(!found);
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
});
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
start();
});
});
/////////////////////////////////////////////////////////////////
// clearGadgetKlassList
/////////////////////////////////////////////////////////////////
module("renderJS.clearGadgetKlassList");
test('clearGadgetKlassList leads to gadget reload', function () {
// Check that declareGadgetKlass reload the gadget
// after clearGadgetKlassList is called
renderJS.clearGadgetKlassList();
var server = sinon.fakeServer.create(),
url = 'https://example.org/files/qunittest/test',
mock;
server.autoRespond = true;
server.autoRespondAfter = 1;
server.respondWith("GET", url, [200, {
"Content-Type": "text/html",
}, "foo"]);
mock = sinon.mock(renderJS, "parseGadgetHTML");
mock.expects("parseGadgetHTML").once().withArgs("foo").returns(
{foo: 'bar'}
);
stop();
renderJS.declareGadgetKlass(url)
.done(function (Klass1) {
mock.restore();
renderJS.clearGadgetKlassList();
mock = sinon.mock(renderJS, "parseGadgetHTML");
mock.expects("parseGadgetHTML").once().withArgs("foo").returns(
{foo: 'bar'}
);
renderJS.declareGadgetKlass(url)
.done(function (Klass2) {
mock.verify();
ok(Klass1 !== Klass2);
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
})
.always(function () {
start();
server.restore();
});
})
.fail(function (jqXHR, textStatus) {
ok(false, "Failed to load " + textStatus + " " + jqXHR.status);
start();
});
});
/////////////////////////////////////////////////////////////////
// RenderJSGadget.getInterfaceList
/////////////////////////////////////////////////////////////////
module("RenderJSGadget.getInterfaceList");
test('returns interface_list', function () {
// Check that getInterfaceList return a Promise
var gadget = new RenderJSGadget();
gadget.interface_list = "foo";
stop();
gadget.getInterfaceList()
.done(function (result) {
equal(result, "foo");
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// RenderJSGadget.getRequiredCSSList
/////////////////////////////////////////////////////////////////
module("RenderJSGadget.getRequiredCSSList");
test('returns interface_list', function () {
// Check that getRequiredCSSList return a Promise
var gadget = new RenderJSGadget();
gadget.required_css_list = "foo";
stop();
gadget.getRequiredCSSList()
.done(function (result) {
equal(result, "foo");
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// RenderJSGadget.getRequiredJSList
/////////////////////////////////////////////////////////////////
module("RenderJSGadget.getRequiredJSList");
test('returns interface_list', function () {
// Check that getRequiredJSList return a Promise
var gadget = new RenderJSGadget();
gadget.required_js_list = "foo";
stop();
gadget.getRequiredJSList()
.done(function (result) {
equal(result, "foo");
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// RenderJSGadget.getPath
/////////////////////////////////////////////////////////////////
module("RenderJSGadget.getPath");
test('returns interface_list', function () {
// Check that getPath return a Promise
var gadget = new RenderJSGadget();
gadget.path = "foo";
stop();
gadget.getPath()
.done(function (result) {
equal(result, "foo");
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// RenderJSGadget.getTitle
/////////////////////////////////////////////////////////////////
module("RenderJSGadget.getTitle");
test('returns interface_list', function () {
// Check that getTitle return a Promise
var gadget = new RenderJSGadget();
gadget.title = "foo";
stop();
gadget.getTitle()
.done(function (result) {
equal(result, "foo");
})
.always(function () {
start();
});
});
/////////////////////////////////////////////////////////////////
// RenderJSGadget.getHTML
/////////////////////////////////////////////////////////////////
module("RenderJSGadget.getHTML");
test('returns interface_list', function () {
// Check that getHTML return a Promise
var gadget = new RenderJSGadget();
gadget.html = "foo";
stop();
gadget.getHTML()
.done(function (result) {
equal(result, "foo");
})
.always(function () {
start();
});
});
}(document, jQuery, renderJS, QUnit, sinon));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment