0

Many of suggested that I just utilize the .onload and fire a callback once the script is ready. That doesn't work for me. I'm writing a function and it expects an element immediately, not at a later time. Here's what I've got:

webdriver.By.sizzle = function(selector) {
    driver.executeScript("if(typeof Sizzle==='undefined'){var s=document.createElement('script');s.type='text/javascript';s.src='https://raw.github.com/jquery/sizzle/master/src/sizzle.js';document.head.appendChild(s);}");
    return new webdriver.By.js("return Sizzle('"+selector.replace(/"/g,'\\"')+"')[0]");
};

I want to inject Sizzle (if it's not already included) and then return an HtmlElement. It can busy-wait if it needs to, but it has to return an HtmlElement.

All the answers here basically just said "don't do it", but I can't think of another way.

This is a selector for selenium web driver for JavaScript.


driver.executeScript will take arguments. I wonder if I can pass in a webdriver.promise and have it invoked when the script is loaded...? I don't know how to 'fufill' a promise.

Community
  • 1
  • 1
mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Pass a callback/return a promise. – Fabrício Matté Sep 04 '13 at 22:28
  • 1
    A simple [Google search](https://www.google.com/search?q=load+script+dynamically+javascript&rlz=1C1CHFX_enUS515US515&oq=load+script+dynamical&aqs=chrome.1.69i57j0l3.4831j0&sourceid=chrome&ie=UTF-8) reveals how to load a script dynamically: http://unixpapa.com/js/dyna.html. It is unclear what you mean by "return an HTML element". – jfriend00 Sep 04 '13 at 22:28
  • 2
    @jfriend00 dynamically generated scripts are loaded asynchronously, OP wants to do that synchronously so he can return from the function after inserting the script. – Fabrício Matté Sep 04 '13 at 22:31
  • 2
    @FabrícioMatté - There is no "good" way to load an external script dynamically and synchronous. During the loading of the page, it can be done with `document.write()` and it could perhaps be done with a blocking AJAX call (hack, cough, cough), but both are generally bad ways to develop. The code SHOULD be modified to deal with the async nature of loading remote resources using either callbacks or promies or something of that nature. This is how good javascript design works. – jfriend00 Sep 04 '13 at 22:34
  • @jfriend00: Not sure you read anything I wrote... the example code I posted does exactly that. The problem is I'm working in two different contexts; I can execute code client-side, but it has to return something or I can't do anything with it. – mpen Sep 04 '13 at 22:34
  • @jfriend00 exactly, that's why my first comment suggests restructuring the code a bit to use callbacks/promises. `=]` – Fabrício Matté Sep 04 '13 at 22:34
  • @FabrícioMatté - why did you make this into an argument? I never commented one way or the other about your first comment. I merely suggested that the OP do some Google searches and see what the options were. – jfriend00 Sep 04 '13 at 22:35
  • @jfriend00 I didn't intend to make this an argument, I merely pointed out what OP mentioned. – Fabrício Matté Sep 04 '13 at 22:36
  • @Mark - your question is obviously not clear to me. I still don't understand what you're trying to accomplish. Synchronously loading remote resources is NOT something that there is a good solution for in javascript. Period. – jfriend00 Sep 04 '13 at 22:36
  • @jfriend00: I'm using server-side JavaScript to inject a JavaScript library into a client-side script. The "inject" method (`driver.executeScript`) returns immediately. There is no way to have the client-side injected script to call back into my server-side script (AFAIK) which is why these onload/callback solutions don't work for me. – mpen Sep 05 '13 at 01:14
  • @Mark - now your question is even less clear. What does it mean to "use server-side javascript to inject javascript into a client-side script"? If you're creating the page's HTML on the server, you can just embed the ` – jfriend00 Sep 05 '13 at 01:19

3 Answers3

2

The best choice here would be to redesign/restructure your code to handle asynchronous loading of Sizzle and return your result via a callback function. This is how javascript is designed. It does NOT have a "good" way of loading remote resources synchronously.

The only way I know of to load a remote resource like a JS file synchronously is to use a blocking AJAX call to load the resource (will be subject to same origin restrictions so you will have to host it on your server) and then once you've loaded the resource into memory, you can execute it (to cause it be loaded). Then, you can call the functions defined in it and return their result.

The reason this is bad is that the blocking ajax call will lock up the browser for an indeterminate amount of time while the remote resource is loaded. That type of design is bad for the user experience.


The only other choice I can think of is to make sure, during the page loading, that all required resources are already specified so that they are already preloaded when your script then requires them. Loading a remote resource on demand is what causes the async problem.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • "The reason this is bad is that the blocking ajax call will lock up the browser for an indeterminate amount of time while the remote resource is loaded. That type of design is bad for the user experience." -- There is no user. This is an automated test. It can wait as long as it needs to. Can't specify the resources ahead of time; I'm injecting this into a page I'm loading with selenium. – mpen Sep 04 '13 at 23:00
  • @Mark, then feel free to lock up the browser with synchronous ajax. I've given you a path to pursue. Here's a similar question/answer: http://stackoverflow.com/questions/2879509/dynamically-loading-javascript-synchronously – jfriend00 Sep 04 '13 at 23:01
0

why aren't you just including sizzle as an on-page script requirement. Just stick

<script src="sizzle.js"></script>

somewhere in the head and it'll get loaded before your other script runs. To do this dynamically, just append the script to the head. http://jsfiddle.net/nQePg has a demonstrator that shows you script execution halts while the injection is being parsed and evaluated.

Mike 'Pomax' Kamermans
  • 49,297
  • 16
  • 112
  • 153
  • I don't have access because I'm loading a webpage via Selenium and then injecting my own code. – mpen Sep 04 '13 at 22:49
  • Read the code I posted... I am. Doesn't help any. It's not ready by the line after. – mpen Sep 04 '13 at 22:58
  • At this point, it's worth considering that selenium may not be the right tool for the job if you need to load code that immediately returns something, but the way to do that doesn't work. If you can't make the code wait, then the problem you're trying to solve may not be the problem you actually need to solve. – Mike 'Pomax' Kamermans Sep 04 '13 at 23:05
0

It's a bit slow, but I think I found a solution:

webdriver.By.sizzle = function(selector) {
    driver.executeScript("return typeof Sizzle==='undefined'").then(function(noSizzle) {
        if(noSizzle) driver.executeScript(fs.readFileSync('sizzle.min.js', {encoding: 'utf8'}));
    });
    return new webdriver.By.js("return Sizzle('"+selector.replace(/"/g,'\\"')+"')[0]");
};

I've just downloaded the script and I'm injecting the whole thing inline. This way I don't need to wait for anything.

mpen
  • 272,448
  • 266
  • 850
  • 1,236