Can modernizr load scripts asynchronously but execute them in order? - modernizr

I'm experimenting with Modernizer.load.
I have this:
Modernizr.load([
{
load : ['/js/jquery-1.6.1.js', '/js/jquery.tools.min.js', '/js/myscript.js']
}
]);
If I understand correctly, I can use code like this to load scripts asynchronously. However, can I then execute them in order? What if myscript.js requires the jquery object to be loaded first?
In the example in the modernizr documentation, load([]) can take a 'complete' property, the parameter of which can be a function that can load another script when everything else is done. However, if I use a function here to load my post-dependancy script, then it loads in serial. The docs specifically say that this can harm perfomance.
However, if I load everything asynchronously, I don't have any idea about the order in which they run. And of course, I need my dependancies to run first.

If you use Modernizr.load, all the files you include via the embedded list/hash will be loaded asynchronously, but they’ll each be executed in the order that you put them in.
So, your example will load the files asynchronously but execute them in this order:
1: /js/jquery-1.6.1.js
2: /js/jquery.tools.min.js
3: /js/myscript.js`
You can simplify your example, by the way:
Modernizr.load(['/js/jquery-1.6.1.js', '/js/jquery.tools.min.js', '/js/myscript.js']);
For more details, see the Modernizr.load() tutorial in the Documentation, or check out Yepnopejs.com (which is what Modernizr.load() basically is, at this time).

Related

Cypress: How to capture text from a selector on one page to use as text on another page

New cypress user here, I am aware that cypress does not handle variables like how testcafe and others do due to the asyn nature of it. Using the example given and what I could find I have this as an example:
cy.get('selector').invoke('text').as('text_needed')
cy.get('#text_needed')
const txtneeded = this.text_needed
cy.log(txtneeded)
This looks at a given selector, takes what it finds and uses it as text and set it as a variable usable at any time in the test and outputs it to the log. The plan is to use that text in a search filter in another page to find the item it references.
The problem is that it fails with Cannot read properties of undefined (reading 'text_needed')
Is this because the content of the selector is not assigned to text properly, the outer html is <a data-v-78d50a00="" data-v-3d3629a7="" href="#">PO90944</a> The PO90944 is what I want to capture.
Your help would be appreciated!
You cannot save an alias and access it via this.* in the same execution context (callback) because it's a synchronous operation and your alias is not yet resolved at this time.
This is a correct way to go:
cy.get('selector').invoke('text').as('text_needed')
cy.get('#text_needed').then(txtneeded => {
cy.log(txtneeded)
})
First, make sure to define it as traditional function, not as an arrow function as this context doesn't work as you'd expect there, more info here.
Next, typically in a single test you should use .then() callback to perform additional actions on elements, and use aliases when you need to share context between hooks or different tests, so please consider the following:
// using aliases together with this within the single test won't work
cy.get(<selector>).invoke('text').as('text_needed')
cy.get('#text_needed').should('contain', 'PO90944') // works fine
cy.log(this.text_needed) // undefined
// this will work as expected
cy.get(<selector>).invoke('text').then(($el) => {
cy.wrap($el).should('contain', 'PO90944'); // works fine
cy.log($el) // works fine
});
Setting alias in beforeEach hook for example, would let you access this.text_needed in your tests without problems.
Everything nicely explained here.
Edit based on comments:
it('Some test', function() {
cy.visit('www.example.com');
cy.get('h1').invoke('text').as('someVar');
});
it('Some other test', function() {
cy.visit('www.example.com');
cy.log('I expect "Example Domain" here: ' + this.someVar);
});
And here's the output from cypress runner:

How to reuse code inside another spec.js?

I have two test scripts that must be called by a third one. I don't would like to replicate the code for all test cases that uses the same two scripts and do many other things after.
I tried to use the require command but it seems to be ignored and the code after it is executed, skipping the intended script AbrirNavegador.spec.js
before(function() {require('./AbrirNavegador.spec.js')});
There's no information about error or something else. It's just skipped.
I never got that to work. But I do use another work around. What I do:
// commands.js
Cypress.Commands.add('reuseMethod1', function({
// first set of steps that need to be reused
})
Cypress.Commands.add('reuseMethod2', function({
// second set of steps that need to be reused
})
// testscript_1.js
cy.reuseMethod1()
// testscript_2.js
cy.reuseMethod1()
cy.reuseMethod2()
You can call the methods at any place, so also in a before/beforeEach/after/afterEach. So the only code duplication you have is the part of calling the method.

How to run multiple specs parallel with single conf.js?

I've splitted my project from one huge test to a few smaller to speed up tests and avoid some errors. Is there any way to run all of them parallel with single conf file? I must pass through login.js before every testcase
specs: ['login.js', 'test1.js'],
I suggest changing your login.js spec into a file which exports a login function. Then create a beforeAll in your onPrepare in your conf. This will be executed before every describe block, which in your case is every test.
onPrepare: function {
beforeAll(function(){
loginToApp();
});
};
I know you have already split up the files but I would seriously consider using the page object model to structure your tests if you have the time.

Modernizr.load() confusion with Yepnope

I'm more than a little confused about Modernizr and it's relation to Yepnope.js. As I understand it, Modernizr comes with Yepnope.js (assuming you select the Modernizr.load() option). According to the Yepnope documentation, there are optional prefix plugins which can used. For example, you can test for versions of IE (assuming you also load the yepnope.ie-prefix.js script). However, when I attempt to run the following, I get 'undefined' alert:
Modernizr.load({
load: 'ie!my-ie-specific.js',
complete : function (url, result, key){
alert(url, result, key);
}
});
What am I doing wrong? Does Modernizr include Yepnope completely or only bits and pieces?
I got stuck on this too. Struggled for an hour.
The complete callback doesn't support tests. Change it to callback which fires when the external script is loaded.
complete runs when all scripts are loaded, OR if nothing is loaded.
I wish complete took the result variable though. I could use that.

Prevent re-loading of javascript if functions already exist. Otherwise ensure synchronous loading

Using JQuery.load(), I change the content of my website's mainWindow to allow the user to switch between tabs. For each tab, there is one or multiple scipts that contain functions that are executed once the tab content is loaded.
Obviously when switching to the tab for the first time, the script has to be fetched from the server and interpreted, but this shouldn't happen if the user switches back to the tab later on. So, to put it short:
Load() html
make sure javascript functions exist, otherwise load script and interpret it.
call a a function on the javascript after the DOM is rebuilt.
Step one and two have to be complete before step 3 is performed.
At the moment, I am using nested callbacks to realize this:
function openFirstTab(){
$("#mainWindow").load("firstTab.php", function(){
if(typeof(onloadfFirstTab) != "function"){
jQuery.getScript("assets/js/FirstTab.js", function(){
onloadFirstTab();
});
}
else{
onloadFirstTab();
}
} );
}
but I feel that there should be a better way.
You can't write the code entirely synchronously since you can't load script synchronously after page load ( unless you do a synchronous XHR request and eval the results - not recommended ).
You've got a couple of choices. There are pre-existing dependency management libs like RequireJS which may fit the bill here, or if you just need to load a single file you can do something like this to clean up your code a bit rather than using if/else:
function loadDependencies() {
// For the sake of example, the script adds "superplugin" to the jQuery prototype
return $.getScript( "http://mysite.com/jquery.superplugin.js" );
}
function action() {
// If superplugin hasn't been loaded yet, then load it
$.when( jQuery.fn.superplugin || loadDependencies() ).done(function() {
// Your dependencies are loaded now
});
}
This makes use of jQuery Deferreds/Promises to make the code much nicer.
If you don't want to load the JS more than once and you are going to dynamically load it, then the only way to know whether it's already loaded is to test for some condition that indicates it has already been loaded. The choices I'm aware of are:
The simplest I know of is what you are already doing (check for the existence of a function that is defined in the javascript).
You could also use a property on each tab (using jQuery's .data() that you set to true after you load the script.
You could write the dynamically loaded code so that it knows how to avoid re-initializing itself if it has already been loaded. In that case, you just load it each time, but the successive times don't do anything. Hint, you can't have any statically defined globals and you have to test if it's already been loaded before it runs its own initialization code.
(Haven't tested it yet, so I am not sure if it works, especially since I didn't yet really understand scope in javascript:)
function require(scripts, callback){
var loadCount = 0;
function done(){
loadCount -=1;
if (loadCount==0){
callback();
}
}
for ( var script in scripts){
if (!script.exitsts()){
loadCount +=1;
jQuery.getScript(script.url, done);
}
}
}
This function takes an array of scripts that are required and makes sure all of them are interpreted before it calls the callback().
The "script" class:
function script(url, testFunc){
this.url =url;
this.testFunction = testFunc;
this.exists = function(){
if(typeof(testFunction)=="function"){
return true;
}
else{
return false;
}
}
}
Where the test-function is a function that is defined (only) in the concerned script.
PS:
To enable caching in JQuery and thus prevent the browser from doing a GET request every time getScript() is called, you can use one of the methods that are presented here.
Even though unnecessary GET - requests are avoided, the script is still getting interpreted every time getScript() is called. This might sometimes be the desired behavior. But in many cases, there is no need to re-interpret library functions. In these cases it makes sense to avoid calling getScript() if the required library functions are already available. (As it is done in this example with script.exists().

Resources