Unit Testing dynamic loading of javascript libraries - javascript

I am writing JavaScript tests in Jasmine for a method that dynamically load jQuery if not present or the version loaded is an earlier version that what the plugin requires. What is needed is to test a couple of different scenarios.
No library exists
An earlier version exists, so is upgraded
A new version is specified (options)
etc
The issue is that the test adds a version of jQuery to the page, so this effects/interferes with later tests. Is there any way to reload the page or remove added javascript?
Solution
I've found a work around for testing. The code to check if JQuery is loaded is:
if(typeof jQuery == 'undefined'){ [load JQuery]; }
In the Jasmin afterEach method:
jQuery = undefined;
It doesn't actually remove the dynamically loaded library, but the method being tested doesn't see it.

Related

Error: SignalR: SignalR is not loaded. Please ensure jquery.signalR-x.js is referenced before ~/signalr/js [duplicate]

I use SignalR 2.2.0 in a MVC5 project. SignalR depends of JQuery in client-side.
JQuery recently released new version, I updated it from Nuget, specifically from version 2.2.4 to 3.0.0.1, but then SignalR stopped working. Startup command "$.connection.hub" in javascript fails.
After a long time head scratching, I downgrade JQuery to 2.2.4 and everything is fine again.
Am I the only one getting this problem? There is any workaround?
Thanks.
You must edit the signalR code by yourself, In jquery 3 they removed the shortcut for load event :
Breaking change: .load(), .unload(), and .error() removed
These methods are shortcuts for event operations, but had several API
limitations. The event .load() method conflicted with the ajax .load()
method. The .error() method could not be used with window.onerror
because of the way the DOM method is defined. If you need to attach
events by these names, use the .on() method, e.g. change
$("img").load(fn) to $(img).on("load", fn).
https://jquery.com/upgrade-guide/3.0/
so in the file jquery.signalR-{version}.js :
you must update this line :
_pageWindow.load(function () { _pageLoaded = true; });
To :
_pageWindow.on("load",function () { _pageLoaded = true; });
Finally version 2.2.1 of SignalR was released, solving this problem. Thanks for all comments.
If you're still getting errors like this after updating to 2.2.1 and jQuery 3.x then read on...
TypeError: Cannot read property 'client' of undefined
Like I am you are probably using the dynamically generated proxy, and you checked your /signalr/hubs file and found you don't have any proxies defined.
var proxies = {};
Wait you may ask I didn't change anything - where did they go?
Well, like me you probably were in such a hurry to upgrade signalR to 2.2.1 that you forgot to do it in all your projects and now you are using both 2.2.1 and 2.2.0 in different assemblies. (I am defining my hubs in a different assembly than my main app).
All I needed to do was make sure I had the latest nuget package version in every project and it all worked. Should work fine after rebuilding. If not, this may also help.
Also do yourself a favor and read the jQuery 3 upgrade guide if you use much jQuery elsewhere.

How to use different version of JQuery on a JS file?

I have a Cisco JS library designed to work using JQuery 1.4.2 and I'm using latest 2.X version of JQuery on the UI page I'm developing.
Cisco Library
I'm using Jabberwerx.js file from the above library link.
The library works fine if the JQuery loaded is of 1.4 but fails to work with later versions of JQuery. If I use old version of Jquery my UI based on bootstrap doesn't work. I tried to use noConflict() but then I can not edit the entire library which is very huge. There are lots of webservice calls and functions on the library so upgrading it is very painstaking.
Is it possible to use a particular version of JQuery on this JS file and the rest of the application can use the latest version of JQuery?
The library has this code on it. Can we change this to make it work using old version of JQ.
(function(window)
{ var jQuery=function(selector,context)
{
return new jQuery.fn.init(selector,context);
},
_jQuery=window.jQuery,
_$=window.$,
}
I'm not looking at using different versions of JQuery on the same page but I'm trying to limit the usage of one version of JQuery to one of the JS files.
When you use different version of jquery then your code conflicts. So there may appear obvious errors.
To fix it, you need to use $.noConflict() passing a boolean parameter true.
jQuery.noConflict(true);
//removes jquery itself and allows you to work with different version of jquery
Example:
<script src="jquery-version-1.0"></script>
<script>//code for v-1</script>
<script>jQuery.noConflict(true);//remove jquery</script>
<script src="jquery-version-2.0"></script><!--use another version-->
<script>//code for v-2</script>
In addition to what Bhojendra Nepal's answer, You can also check the version of jQuery through the following Code :
var jq_version = $().jquery;
console.log(jq_version);
/* gives the version number like 1.7.1. So may be you can write an if condition around the version number. For e.g :*/
if(jq_version == '1.7.1'){
//do something for ver 1.7.1
}
There are even more ways to Check the version of jQuery. But I'm not sure if that would help much with the CISCO library you are using.

Debugging a jQuery plugin

Background
I have a snippet of javascript that sites on customer pages. When this script is executed, it loads necessary assets and scripts and then executes the main code.
One of the script's I'm loading is Twitter's bootstrap JS to create a modal window. It's implemented as a jQuery plugin, and adds '$.modal()' to a jquery object.
Problem
This works on all but one customer site. On this particular site, as I invoke $('#idOfWindow').modal();, the page throws a javascript error Object has no method 'modal'
I've verified that the bootstrap code is invoked, and $.fn.modal is called to setup the plugin, My hunch is that something is clobbering the jQuery object, but I'm not sure how to debug this.
Comments
I'm currently using Chrome's Developer Tools to Debug, and I have verified that the plugin is loaded and executed before it's ever called. The client site is using jQuery 1.7.2. I'm familiar with chrome tools, but I'm not a power user.
If you are right, that your original jQuery object is being overwritten somewhere, you should be able to restore it with jQuery.noConflict().
Demo: jsfiddle.net/KthZu
Edit: Some debugging tips:
To help in debugging, you can check the jQuery version in the console with this code:
$().jquery
Another potential debugging trick would be to store a reference to jQuery when you create you plugin.
$.fn.myPlugin = function(){};
window.jQueryWithMyPlugin = $;
Then, when debugging, you can plainly see if window.$ has been overwritten:
$ === jQueryWithMyPlugin
Generally for any widget type of code that is to be embedded externally, you'll want to wrap your code:
(function($){
$('#idOfWindow').modal();
})(jQuery);
I have, in the past had issues where multiple versions of jQuery (or jQuery & prototype) were loaded on the same page. Be sure that the version of jQuery you're adding $.fn.modal to is identical to the version that's being called. $().jquery can be used to access the specific version of jQuery in use.

How to embed/import jQuery into another JavaScript library without causing potential clashes with jQuery/others deployed on the website?

I am working on a JavaScript library that makes internal use of jQuery. However, when the time comes for its deployment, I would prefer to simplify things (w.r.t. deployment) and not worry about whether the environment has (or doesn't have) jQuery already included in <head>.
I should mention that my library is implemented as a class in Coffescript and the resulting .js file has the default function wrapper to isolate scope.
I can think of a few solutions to this problem:
Ask the website admin whether they have jQuery already present. If
yes, just include the library itself. If not, then include jQuery +
the library, perhaps in a single file and concatenated. Deployment is harder, but problem is likely solved.
Create a file that concatenates jQuery and my lib code and always
have the website admins just include this file. To prevent clashes,
do a var myJ = jQuery.noConflict() right after the jQuery code.
Do as in (2) above, but call the var myJ = jQuery.noConflict() inside the encapsulated Coffeescript class.
In summary, the objectives for this challenge are:
Absolutely avoid any breakage on the deployed website, regardless of
what they might be using
Keep deployment as simple as possible
Avoid unnecessary transfer of jQuery, if possible
My concerns are:
What if the site already has jQuery, but needs an older/newer
version?
What if my noConflict() call breaks something on their
end?
What is the best way to tackle this situation? Am I missing something?
CoffeeScript has a -b option to compile without the top-level function wrapper. Using this method, you could use your own top-level function wrapper instead.
i.e.
(($) ->
# ...
# your code
# ...
)(jQuery.noConflict())
Then, as part of your build process, concatenate jQuery onto the front of the compiled output. The appropriate Linux/Mac shell command would be:
(cat jQuery.js; coffee -pb yourSource.coffee) > yourCompiledFileWithjQuery.js
The p option for CoffeeScript prints the compiled output to stdout, so that you can combine it with the cat jQuery.js and redirect that output into yourCompiledFileWithjQuery.js
This would ensure that, immediately before your code is executed, jQuery would be included. jQuery keeps a reference to the old owner of $, so if some library or another version of jQuery is already using it it will be returned to them with the jQuery.noConflict() call.
jQuery.noConflict() also returns a reference to that jQuery, so that is how it would be passed to the $ of your code.
I hope that's clear.
Extra:
As of CoffeeScript 1.3, released a few days ago, you could use this instead for your top-level function wrapper, which is somewhat clearer:
do ($ = jQuery.noConflict()) ->
# ...
# your code
# ...
If your end script as a closure you can simply pass it jQuery as an argument and check if that is undefined or not.
(function($) {
if (!$) {
// load jQuery
} else {
// check jQuery version if needed or just use jQuery
if ($.fn.jquery !== '1.7.2') {
// load 1.7.2
};
};
})(window.jQuery || false);
Must use window.jQuery as jQuery only will generate an error if jQuery is not declared

YUI Version Conflict Issue in Portlet

I'm loading yui.js 3.3.0 version file from portlet but liferay its using 3.2.0 yui.js file,
so whenever i'm loading that page js errors are coming like
G_ENV._loaded[VERSION] is undefined - this error is coming in yui.js which is liferay using that is 3.2.0 version.
so its replacing value like G_ENV._loaded[3.2.0] and that will throw an error becoz we loaded 3.3.0 version from portlet.
I replaced yui.js 3.2.0 version file in portlet but It was throwing some other js errors.
How will it work same in 3.2.0 or Is there any way to update existing version of yui?
This is the code of yui.js in this line its throwing error
if (!G_ENV._loaded[VERSION][name]) {
missing.push(name);
} else {
used[name] = true; // probably css
}
Any help would be appricated, anyone has faced this kind of problem.
Thanks
Upgrading, changing, or overwriting the native YUI installation in Liferay is extremely difficult, because of two reasons. One, Liferay 6.0's UI framework (Alloy) runs on YUI 3.2.0. Two, the native portal template instantiates alloy ui onto the global YUI object (YUI.AUI), which is awful, as it makes the YUI framework tightly coupled to the horrifically implemented AUI. Your browser calls this AUI reference, on the YUI object, as the page is loading and after the window.onload event. If you try to replace or modify the global YUI object, it screws up Alloy, which Liferay's UI runs on.
Since upgrading is out of the question, the next best alternative is to bring in just the new YUI modules you want to use. Also, you could try out Liferay 6.1 CE, which has 3.4.1 built in, but it looks like its still in beta testing.
Here's an example of bringing in and using the dom-core module from YUI 3.4.1 and using it in Liferay 6, which runs on YUI 3.2.0. I got the idea from YUI's docs on how to bring YUI 2 modules into 3 (http://yuilibrary.com/yui/docs/yui/yui-loader-ext.html). To quickly figure out dependencies each module has, you can use YUI's online configurator http://yuilibrary.com/yui/configurator/.
var config = {
ignore : ["skin-sam-overlay","skin-sam-widget","skin-sam-widget-stack","skin-sam-tabview"],
groups: {
yui341: {
base: '/js/yui-3.4.1/build/',
modules: {
yui341_yui_base: {
path: 'yui-base/yui-base.js'
},
yui341_oop: {
path: 'oop/oop.js',
requires: ['yui341_yui_base']
},
yui341_features: {
path: 'features/features.js',
requires: ['yui341_yui_base']
},
yui341_dom_core: {
path: 'dom-core/dom-core.js',
requires: ['yui341_yui_base','yui341_oop','yui341_features']
}
}
}
}
};
YUI(config).use('yui341_dom_core',function(Y){
//YUI 3.4.1 config modules are now accessable through 'use' call
console.log(Y.version); //say hello to the newer version (3.4.1)
Y.use('dom-core',function(Y){
//Finally have access to native 3.4.1 module
console.log(Y.DOM);
});
});
Actually, the problem isn't because of Alloy being shoved onto the YUI object. That would be fairly trivial to work around by just simply doing:
YUI.AUI = AUI after loading the new YUI.
The problem is because of everywhere Alloy (and even YUI modules already loaded on the page) references YUI, it expects it to be a specific version of YUI.
In many cases, this isn't that big of a deal, as the API between YUI releases isn't that far off. However, between 3.2 and 3.3 there were some changes in Widget, as well as other general changes that could cause problems.
The issue is that YUI currently doesn't have an acceptable way to load multiple versions of the library onto the page that won't cause some sort of conflict.
(Because the YUI global get's overwritten, as well as it's properties, such as YUI.Env).
However, I have been kicking around an idea for one possible way to handle this, using dynamically generated iframes to load a separate YUI instance in a different window and pass it along to the original page).
If I can get that working, it's something that we will be backporting to all versions of Alloy, so that Liferay users can leverage it as well.
Josh, if you have any thoughts on a better way to handle this, I'd definitely be all ears.
Thanks,

Categories