I'm trying to load a single page application that uses a heavy amount of async code execution involving RequireJS and jQuery deferreds. The application loads as expected inside the browser, but not in PhantomJS.
For instance, I spent some time trying to figure out how to make the following snippet work:
# index.html
<body>
<script>
require.config({
base: '.',
paths: {
main: 'main'
}
})
require(['main'], function() {
window.myglobal = {
something: 'foo'
}
});
</script>
</body>
# phantomjs
page.evaluateAsync(function() {
console.log(window.myglobal.something); // Should print out 'foo'.
}, 100);
I consider that using evaluateAsync with a fixed timeout that has to be determined by trial and error is not really satisfactory. Perhaps someone can suggest a better pattern.
The documentation for evaluateAsync does not say much so I'm going to take it at face value and assume that it just executes the code asynchronously, without any further constraint regarding what may or may not have loaded already. (The source code does not indicate any further constraints either.)
The problem I'm seeing is that you have two asynchronous functions that may execute in any order. When require(['main'], ...) is called, this tells RequireJS to start loading the module but you don't know when the module will actually load. Similarly, when you execute page.evaluateAsync you are telling PhantomJS to execute a piece of code asynchronously. So it will execute but you don't know when.
So the following can happen:
The module finishes loading: window.myglobal is set.
console.log is called, which outputs the correct value.
Or:
console.log is called, which fails.
The module finishes loading: window.myglobal is set.
Setting a timeout that delays the execution of console.log will make it more likely that the first case happens but it does not guarantee it.
What you could do is change your HTML like this:
<body>
<script>
require.config({
base: '.',
paths: {
main: 'main'
}
})
define('init', ['main'], function () {
window.myglobal = {
something: 'foo'
};
});
require(['init']);
</script>
</body>
Your PhantomJS script:
page.evaluateAsync(function() {
require(['init'], function () {
console.log(window.myglobal.something); // Should print out 'foo'.
});
});
What this does is define a module called init. (This is a rare case where explicitly naming your module with define is okay. Usually you just start the define call with the list of dependencies.) Then when evaluateAsync is called it asks for the init module, which guarantees that the assignment to window.myglobal will have happened before console.log runs.
It would also be possible to use promises to get the desired results but I've preferred to show a solution that uses only RequireJS.
PhantomJS is a headless browser that is used for all kinds of stuff. A big part of it is the testing/automation of websites. It means that you generally don't have the opportunity of changing the site code. Most of the time it is not necessary, such as in this case.
You simply need to wait until the page script/DOM is at a state that you want for further processing. This is usually done using waitFor from the examples of PhantomJS.
In your case, you can add the waitFor definition to the beginning of the script and wait for window.myglobal to be defined:
page.open(url, function(){
waitFor(function check(){
return page.evaluate(function(){
return !!window.myglobal;
});
}, function then(){
// do something useful
}, 10000); // upper bound on acceptable wait timeout
});
check is a function which is called periodically to check that a certain condition is met. So the logic is that as soon as the condition is met, you can do something useful including doing something on the page using page.evaluate from the then callback.
There are also ways not to wait for specific variables/DOM nodes, but waiting for general ending of network activity as in this answer.
Related
tl;dr: When I run my test case, steps executed seem to work, but the test bails out early on a failure to find an element that hasn't even loaded yet. It seems like the waits I have around locating certain elements are loaded/launched as soon as the test is launched, not when the lines should actually be executed in the test case. I think this is happening because the page is barely (correctly) loaded before the "search" for the element to verify the page has loaded bails out. How do I wrangle the event loop?
This is probably a promise question, which is fine, but I don't understand what's going on. How do I implement my below code to work as expected? I'm working on creating automated E2E test cases using Jasmine2 and Protractor 5.3.0 in an Angular2 web app.
describe('hardware sets', () => {
it('TC3780:My_Test', async function() {
const testLogger = new CustomLogger('TC3780');
const PROJECT_ID = '65';
// Test Setup
browser.waitForAngularEnabled(false); // due to nature of angular project, the app never leaves zones, leaving a macrotask constantly running, thus protractor's niceness with angular is not working on our web app
// Navigate via URL to planviewer page for PROJECT_ID
await planListingPage.navigateTo(PROJECT_ID); // go to listing page for particular project
await planListingPage.clickIntoFirstRowPlans(); // go to first plan on listing page
await planViewerPage.clickOnSetItem('100'); // click on item id 100 in the plan
});
});
planViewerPage.po.ts function:
clickOnSetItem(id: string) {
element(by.id(id)).click();
browser.wait(until.visibilityOf(element(by.css('app-side-bar .card .info-content'))), 30000); // verify element I want to verify is present and visible
return expect(element(by.css('app-side-bar .card .info-content')).getText).toEqual(id); //Verify values match, This line specifically is failing.
}
This is the test case so far. I need more verification, but it is mostly done. I switched to using async function and awaits instead of the typical (done) and '.then(()=>{' statement chaining because I prefer not having to do a bunch of nesting to get things to execute in the right order. I come from a java background, so this insanity of having to force things to run in the order you write them is a bit much for me sometimes. I've been pointed to information like Mozilla's on event loop, but this line just confuses me more:
whenever a function runs, it cannot be pre-empted and will run entirely before any other code
runs (and can modify data the function manipulates).
Thus, why does it seem like test case is pre-evaluated and the timer's set off before any of the pages have been clicked on/loaded? I've implemented the solution here: tell Protractor to wait for the page before executing expect pretty much verbatim and it still doesn't wait.
Bonus question: Is there a way to output the event-loop's expected event execution and timestamps? Maybe then I could understand what it's doing.
The behavior
The code in your function is running asynchronously
clickOnSetItem(id: string) {
element(by.id(id)).click().then(function(){
return browser.wait(until.visibilityOf(element(by.css('app-side-bar .card .info-content'))), 30000);
}).then(function(){
expect(element(by.css('app-side-bar .card .info-content')).getText).toEqual(id);
}).catch(function(err){
console.log('Error: ' + err);
})
}
I have a following function:
function prompt_mandatory_field_completion(){
$("#mandatory_fail").show(150, function() {
setTimeout(function() {
$("#mandatory_fail").fadeOut(500)
}, 2000);
});
window.scrollTo(0,0)
}
That I would like to test with jasmine but regardless to what I put in my spec file the test seems to pass.
The spec file contains the following code :
it(' NEED TO FIX THAT FADE OUT Should prompt user to fill in mandatory questions via prompt_mandatory_field_completion function', function() {
prompt_mandatory_field_completion();
setTimeout(2000, function(){
expect($('#mandatory_fail').css('display').toEqual('random thing'));
});
In my SpecRunner.html I am using the following function that I run in before each test in this description block:
function setupFixtures(){
setFixtures('<div id="mandatory_fail" style="display:none;"></div>');
prompt_mandatory_field_completion();
};
Any idea how to make this into a meaningful test? I guess I have been staring at it way too long and poking it from all the directions.
Best,
Adam
You're trying to write a functional test of asynchronous behavior. You might have a lot better experience trying to use protractor for this sort of test. It's tuned more toward asserting things that will eventually be true.
However, jasmine does have an asynchronous facility since about 2.0, known as done(), that will insist that all of the asynchronous code has run before the test passes or fails.
You have to pass the done function to get asynchronous tests :
it(' NEED TO FIX THAT FADE OUT Should prompt user to fill in mandatory questions via prompt_mandatory_field_completion function', function(done) {
prompt_mandatory_field_completion();
setTimeout(2000, function(){
expect($('#mandatory_fail').css('display').toEqual('random thing'));
done();
});
}, 3000);
You can also pass a timeout as a last parameter, depending on what you've set in your jasmine's settings.
Otherwise, Jasmine will consider this test to fail if its execution exceed its timeout.
Is it possible (and how) to abort RequireJS require request?
I want to be able to do something like this:
define([
'Backbone.View'
],
function (BackboneView) {
var View = BackboneView.extend({
initialize: function () {
// store require deferred
// (I know this is not possible in this way!)
// Is there a way to capture require deferred?
this.deferred = require(['someModule'], function () {
// do something
});
},
close: function () {
// abort require request if view is closed before request resolves
this.deferred.abort();
this.remove()
}
});
return View;
});
So, I want to be able to abort require request if Backbone View is closed before require request resolves.
I have checked RequireJS documentation and their GitHub page.
There is nothing describing how to handle this.
Any help is welcome :)
Tnx
RequireJS does not provide a means to cancel a require call.
I suppose in theory it would be possible to implement it but it would complicate RequireJS' internal by a lot for rather rare benefits. Suppose you require foo, consider:
How often will your code actually be in a position where cancelling the require call is desirable? Your view would have to be initialized and closed in quick succession.
If foo has already been fetched and initialized before the require call is issued the only job require has to do is return the already existing reference to foo. So there is essentially nothing to cancel. Note here that if the view is ever allowed to completely initialize even just once in your application, the scenario considered here applies.
If RequireJS was fast enough to complete the require call before cancellation, then cancellation has no effect. Once an application is optimized, it is quite likely that RequireJS will complete the call before cancellation is possible. In an optimized app, the code that runs require is in the same bundle as the code being required, so RequireJS does no have to fetch anything from the network to satisfy the require call.
So in many cases the cancellation would be without effect, but for the cases where RequireJS might have to cancel something, it would have to keep a close accounting of who is requiring what. Maybe your require call in your view is the only one that is requiring foo but foo may also be requiring bar and baz. If foo is the only one requiring them, then if you cancel the require call, then RequireJS can cancel fetching and initializing foo, bar and baz but there's always the possibility that after the require is issued but before it completes another part of your application needs baz. So now RequireJS has to remember that it is okay to cancel foo and bar but not baz because it was required somewhere else.
Your question is a little weird seeing you will deploy only one minimised file and there will be virtually no delay.
For science note that a require simply adds a script element to the DOM with certain proporties like data-requiremodule=[MODULE_NAME]
so you can basically do something like:
function abortModuleLoad(moduleName)
var scripts = document.getElementsByTagName('script');
for(var i = 0 ; i < scripts.length ; i++){
if(scripts[i].dataset.requiremodule === moduleName) document.head.removeChild(scripts[i])
}
}
close: function () {
// abort require request if view is closed before request resolves
abortModuleLoad("someModule");
this.remove()
}
I have this friend, who hates RequireJS, I recently started using it & I like it, but by his arguments, I started feeling like I might end up like him and give up using RequireJS.
require(['one'],function(){
require(['two'],function(){
require(['three'],function(){
});
});
});
Above code is fairly straight-forward, three depends on two & two depends on one. That makes the browser to load one first & then two & then three in sequence, that's how requirejs handles it, which makes the site real slow if there are lots of these kinds of dependencies.
Browser parallel loading feature it not used at all.
I want to know if there is any way in which require loads all these files asyncronously but makes sure to maintain the order in their execution, so that browser parallelism can be used.
RequireJS is a powerful tool that allows us to load script asynchronously (means we start the loading of each one, and don't wait until it is actually loaded), but still manage dependencies (if one file depends on another, we wanna wake sure the dependency will be loaded beforehand). The way you use RequireJS is not what it is made for. The callback function inside the require is called as soon as the dependency module is loaded ('one', 'two', 'three'). So you are just loading all the modules sequentially, not asynchronously (one is loaded -> callback function is called -> two is loaded -> callback function is called -> three is loaded -> callback function is called). That makes no sense. The way it is supposed to be:
in your HTML file:
<script data-main='main.js' src='require.js'></script>
in your main.js file (some file you wrote inside your script tag as data-main):
require(['one'], function(one){
one.function1();
});
in one.js:
define(['two', 'three'], function(two, three) {
return {
function1 : function() {
two.function2();
three.function3();
console.log('one');
}
}
});
in two.js:
define([], function() {
return {
function2 : function() {
console.log('two');
}
}
});
in three.js:
define([], function() {
return {
function3 : function() {
console.log('three');
}
}
});
In my example 'one' depends on 'two' and 'three' (it requires function2() from 'two' and function3() from 'three'), 'two' and 'three' have no dependencies. This example code assumes all the files are in one folder (including require.js). As a result, we see 'two', 'three', 'one' printed (in that order).
Although RequireJS uses AMD model for loading scripts, we can still manage module evaluation order by ourselves. If you don't want to use define(), you can use a special plugin called order!. It works for RequireJS 1.0 API. It allows to fetch files asynchronously, but make evaluation in a specific order: http://requirejs.org/docs/1.0/docs/api.html#order.
The accepted solution will work nicely for most use-cases, especially because it will usually make sense to use r.js to bundle everything, which makes parallel loading a moot point. However, this solution does not actually allow for loading all modules in parallel, instead either loading in a 3-step sequence that looks like: [main.js] -> [one.js] -> [two.js, three.js] (if you don't use r.js to package the files all to one module) or a single load of one packaged file (if you do use r.js to package all the files to one module).
If you do in fact want to make the files load in a single parallel step like: [one.js, two.js, three.js], you have a couple of options:
A. Use RequireJS 1.0 + order plugin
This one is covered in gthacoder's other answer.
B. Wrap the scripts so that Require can load them, then execute them in a separate stage
This introduces some complexity, but is very reliable. The key thing is that every module that you want to load in parallel should contain a named module inside it that does not match the name you use to load the file. This will prevent the module from executing until you explicitly tell it to:
one.js
define('one-inner', ['two-inner'], function () {
...
});
two.js
define('two-inner', ['three-inner'], function () {
...
});
three.js
define('three-inner', function () {
...
});
Your page or main.js file
// 1. Require loads one.js, two.js, and three.js in parallel,
// but does not execute the modules, because nothing has called
// them by the correct name yet.
require(['one', 'two', 'three']), function () {
// 2. Kickstart execution of the modules
require(['one-inner'], function () {
....
}
});
efor this problem i am using Node-Webkit (node.js) and Async, loading a Windows App.
The reason of this question is to definitively answer:
What really means asynchronous execution in Javascript and Node.Js.
My personal code problem is at the end of the Question. "The Case".
I am going to explain all about the problem i have directly with a schematic summary. (And I will be updating the info as you help me to understand it)
The Concept (theory)
Imagine a Primary Screen (JS, Html, css, ... Node.js frameworks) and a Background Procedure (JS execution every 10 min, JS internal checks, background Database Optimization, ...).
Whatever you do in Primary Screen wont affect background execution (except some important cases), and Background can change even the Screen if he needs to (screen timers, info about online web status, ...)
Then the behaviour is like:
Thread 1: Your actions inside the App framework. Thread 2: Background App routines
Any action as they finish gives his output to screen, despite of the rest of the actions in async parallel
The Interpretation (For me)
I think this is something that "Async" will handle without problems, as a parallel execution.
async.parallel([
function(){ ... },
function(){ ... }
], callback); //optional callback
So the Thread 1 and Thread 2 can work together correctly while they do not affect the same code or instruction.
The Content will be changing while any threads request something of/to it.
The Implementation (Reality)
Code is not fully asynchronous during the execution, there are sync parts with common actions, that when they need calls the async codes.
Sync: Startup with containers -> Async: load multiple content and do general stuff -> Sync: Do an action in the screen -> ...
The Case
So here it is my not working properly code:
win.on('loaded', function() {
$( "#ContentProgram" ).load( "view/launcherWorkSpace.html", function() {
$("#bgLauncher").hide();
win.show();
async.parallel([
function() //**Background Process: Access to DB and return HTML content**
{
var datacontent = new data.GetActiveData();
var exeSQL = new data.conn(datacontent);
if(exeSQL.Res)
{
var r = exeSQL.Content;
if(r.Found)
{
logSalon = new data.activeSData(r)
$('#RelativeInfo').empty();
$("#RelativeInfo").html("<h4 class='text-success'>Data found: <b>" + logData.getName + "</b></h4>");
}
}
},
function() //**Foreground Process: See an effect on screen during load.**
{
$("#bgLauncher").fadeIn(400);
$("#centralAccess").delay(500).animate({bottom:0},200);
}
]);
});
});
As you can see, im not using "Callback()" because i dont need to (and it does the same).
I want to do the Foreground Process even if Background Process is not finished, but the result of the code is done at same time when both request has finished...
If i disconect the DB manually, first function takes 3 seconds until gives an exception (that i wont handle). Until then, both proccess will not output (show on screen) anything. (Foreground Process should be launched whatever happends to Background Process).
Thanks and sorry for so much explanation for something that looks like trivial.
EDITED
This start to be annoying... I tried without Async, just a javascript with callback like this:
launchEffect(function () {
var datacontent = new data.GetActiveData();
var exeSQL = new data.conn(datacontent);
if(exeSQL.Res)
{
var r = exeSQL.Content;
if(r.Found)
{
logData = new data.activeData(r)
$('#RelativeInfo').empty();
$("#RelativeInfo").html("<h4 class='text-success'>Salón: <b>" + log.getName + "</b></h4>");
}
}
});
});
});
function launchEffect(callback)
{
$("#bgLauncher").fadeIn(400);
$("#centralAccess").delay(500).animate({bottom:0},200);
callback();
}
Even with this... Jquery doesnt work until the callback answer...
node-webkit let's you run code written like code for node.js, but is ultimately just a shim running in WebKit's Javascript runtime and only has one thread, which means that most 'asynchronous' code will still block the execution of any other code.
If you were running node.js itself, you'd see different behavior because it can do genuinely asynchronous threading behind the scenes. If you want more threads, you'll need to supply them in your host app.