So this is more of a curiosity, rather than a problem I'm facing. I've come across a website that calls window.location.reload() before the load event fires. How would you break out of this refresh loop, as a user? Can it even be done?
Anyways, I've tried the following:
Replacing window.location.reload (via greasemonkey)
This is not possible since window.location is read-only.
Listening for window.onbeforeunload and hitting cancel (via greasemonkey)
The reload call happens before the document loads. This means the beforeunload event is never fired due to lack of transient activation, so there's no prompting for you to cancel the reload.
Block JavaScript (or: block all network requests)
This does disable the reload. And also the entire site, so it's not ideal.
Block the request to the specific file with the reload call (via devtools)
Same as above, since the file in question is a Webpack bundle, containing the majority of the site's functionality.
Inject a userscript that replaces the function calling windows.location.reload() with an equivalent function that doesn't call windows.location.reload() (via greasemonkey)
I don't think it's possible for this site. Webpack loads every function in an IIFE, so it's not obvious how to modify the function in question before it gets passed as a callback and becomes untouchable. In any case, this approach doesn't generalize.
Place a breakpoint at the window.location.reload call, and modify some local function to throw an error before it can call reload (via devtools)
This didn't work (on Firefox, at least) -- try it out, (function() {throw new Error(), window.location.reload()})(). Due to the comma operator, it'll reload despite the thrown error, and every Webpack expression seems to be a string of comma-separated statements so there's no avoiding this. This approach also doesn't generalize.
Manually modify the construction of the global Webpack window.webpackChunk_N_E table (via greasemonkey)
I don't know about modern Webpack to know if this could be done, but I tried replacing the particular method in question in that table with a ()=>{}. It didn't work -- I believe the method was already passed as a callback before the tire change. We are dealing with a function that is far too powerful to care about its replaced reference in some mere global table.
Close the tab
I gave up.
Did I miss anything obvious, or is it impossible by design to break this particular kind of refresh loop in the browser (short of disabling JavaScript)?
Related
I have got a bug with some javascript code that it's hard to reproduce (so no jsfiddle, sorry). Exactly the same browser (FF 37.0.2), but two different machines, and I can only reproduce the bug on one of them.
I suspect the issue has something to do with localStorage and the fact that I check if an item is there outside $(document).ready().
Is that required? Do I need to wait for the DOM to be ready before reliably accessing localStorage? Is my hypothesis plausible?
localStorage is not something that needs to be "loaded" asynchronously. It is available the moment the page starts to load and can be used by Javascript anywhere in the page. If the browser has to fetch values form somewhere (e.g. the disk), that is done synchronously when you request the data or before.
There is no need to wait with $(document).ready() before accessing localStorage. The cause of your issue must be something else.
FYI, you can read the spec on WebStorage here: http://dev.w3.org/html5/webstorage/#dom-localstorage. There is no indication in the localStorage section of that document that JS code must "wait" before accessing.
If you use plain script tags on an HTML page, rendering is blocked until the script has been downloaded and parsed. To avoid that, for faster page display, you can add the 'async' attribute, which tells the browser to continue processing down the page without waiting for that script. However, that inherently means that other javascript that refers to anything in that script will probably crash, because the objects it requires don't exist yet.
As far as I know, there's no allScriptsLoaded event you can tie into, so I'm looking for ways to simulate one.
I'm aware of the following strategies to defer running other code until an async script is available:
For a single script, use their 'onload' event or attribute. However, there's no built-in way I know of to tell when ALL scripts have loaded if there's more than one.
Run all dependent code in onload event handlers attached to the window. However, those wait for all images too, not just all scripts, so the run later than would be ideal.
Use a loader library to load all scripts; those typically provide for a callback to run when everything has loaded. Downside (besides needing a library to do this, which has to load early), is that all code has to wrapped in a (typically anonymous) function that you pass into the loader library. That's as opposed to just creating a function that runs when my mythical allScriptsLoaded fires.
Am I missing something, or is that the state of the art?
The best you could hope for would be to know if there are any outstanding async calls (XMLHttpRequest, setTimeout, setInterval, SetImmediate, process.nextTick, Promise), and wait for there to not be one. However, that is an implementation detail that is lost to the underlying native code--javascript only has its own event loop, and async calls are passed off to the native code, if I understand it correctly. On top of that, you don't have access to the event loop. You can only insert, you can't read or control flow (unless you're in io.js and feeling frisky).
The way to simulate one would be to track your script calls yourself, and call after all script are complete. (i.e., track every time you insert a relevant script into the event loop.)
But yeah, the DOM doesn't provide a NoAsyncPending global or something, which is what you'd really require.
I've got some UI tests that are attempting to test that a click on an element makes something else appear. There's an existing check for all tests that looks to see if the DOM is Ready, however there's a small amount of time between that even firing and the app.controller() calls all completing where my test could jump in and wrongly determine that the click handler has not been added.
I can use angular.element('[ng-controller=myController]').scope() to determine if the scope is defined, however there is still a very small window where the test could run before the click handler is bound (a very, very small window).
<div ng-controller="myController">
<div ng-click="doWork()"></div>
</div>
Can anyone see a way to tell that the click handler has been added?
PS: There's an event that fires within a controller when the controller has loaded:$scope.$on('$viewContentLoaded', function(){ }); But that doesn't really help me unless I subscribe to it in the controller and flag a variable somewhere that I can check via Selenium.
PPS: A lot of these have classes that change when scope changes and they can be used to trigger the test, but many do not.
There is a specialized tool for testing AngularJS application - protractor. It is basically a wrapper around WebDriverJS - selenium javascript webdriver.
The main benefit of using protractor is that it knows when Angular is settled down and ready. It makes your tests flow in a natural way without having to use Explicit Waits:
You no longer need to add waits and sleeps to your test. Protractor
can automatically execute the next step in your test the moment the
webpage finishes pending tasks, so you don’t have to worry about
waiting for your test and webpage to sync.
It also provides several unique AngularJS-specific locators, like by.model, by.binding etc. And, in general, it provides a very convenient and well-designed API for end-to-end testing.
There are two issues to overcome here:
How do we know when Angular is done (with the sub issue of "what does done mean?"
How do we get that information to Selenium
Angular provides a method called "getTestability" that can be called with any element (assuming you've included it, it is optional). Usage:
angular.getTestability(angular.element('body')).whenStable(function(){/*Do things*/})
That seems to solve the first problem...
But, now what does Done mean in this case. Done means that anything that uses $browser.defer will have been executed. What does that mean? No idea, but in practice it at least verifies that there are no http requests in play when the callback is called.
Ok, now Selenium... You can ask it to execute JavaScript on the client and use the code above to set a variable. .whenStable(function(){window.someVar=true}) and then poll in the test until that variable is set.
Will this catch all cases of "Done"? Probably not, but it made my tests pass more consistently. As long as it works I'm not going to think any harder on the issue.
That said, I'm not marking this as the answer. It feels like a dirty solution.
A web application has certain timeliness constraints. How can I check the time from invocation of a JS function to having the information visible in the browser?
Clearly I can start a stopwatch, but on what event should I stop it?
Modern browsers offer the Navigation Timing API, which you can use to get this kind of information. Which information from it you use is up to you, probably domComplete or loadEventStart (or, of course, loadEventEnd if you want to know when everything is fully loaded, but you could do that with window.onload). This tutorial may be useful.
If you're talking about requesting something via ajax after page load (you've said "...from invocation of a JS function to having the informatin visible in the browser..."), adding that to the page, and seeing how long that took, you'd stop the timer after you were done appending the elements to the DOM, immediately before returning from your ajax onreadystatechange handler callback. Or if you want to be really sure the information has been rendered, after using setTimeout(function() { /*...end the timer...*/ }, 0); from that callback instead, which yields back to the browser for the minimum possible time, giving it a chance to render (if it doesn't render while JS is running).
I'm trying to debug a bunch of functions that redirect the user. I want to "kill" the script while it's running to to read the output of console.log(). I can't just use a simple return because there are many functions that work together.
Set a breakpoint at the spot in your code where you want to pause. If you add a debugger statement in your code browsers that support it will automatically stop at that point and open the debugger (actually I think some browsers (Chrome?) may ignore the statement unless you already have the console/dev tools open, but obviously you can just start with the dev tools open if necessary).
There are a few options, not all of them applicable to your case:
Throw an exception; that will stop the function and any caller function as well;
Use an alert(), as ismaelga suggested;
Set a breakpoint, as nnnnnn suggested; you can either use the debugger keyword or use your browser´s GUI if present;
Redefine console.log to an empty function; this way nothing will be logged from then on, so you can read what was logged so far without interference.
Some situations where the options will not be possible:
If your code is responding to events, or using things like setInterval, even if the "main loop" stops other code can still run and log stuff;
The alert window may make difficult to see the log, depending on how obtrusive it is for your browser;
There's no suitable debugger, or you can't install one;
Some functions have stored a local copy of console.log, so redefining it won't affect them.
Of all option, I'd sugest the 4th, since you can read your logs calmly without "breaking" the page. If you want to "filter" the logs, you can also have some functions call the "correct" log (a saved copy of console.log), while the rest calling the "fake" one (the one you redefined).
Declare a global variable that is outside of any function, and add code within each function or long-running loop that checks the value of that variable and only proceeds if the value is set a certain way. This way the variable serves as a semaphore and you can change its value from anywhere and terminate all processing in accordance with its value.
For example, a simple Boolean variable called stopProcessing that is initialized as false but can be set to true to let all functions know to return and all loops to break ASAP.