Sorry for the really bad 'title'...
I have a simple script running in Chrome (Version 46.0.2490.80 m) developer tools. Where is this undefined coming from?
This same script when run 'normally' works as expected:
It is because the developer tools parses the string you provide into the console and execute it as a function and return/print the return value whatever the statement you had put is returning.
Execute the below code, there is no return statement, hence it will print undefined.
(function(){
[1,2,3].forEach(function(val) {
console.log(val);
});
//no return statement //Hence undefined
})();
But look at the below case, the function is returning some value.
(function(){
[1,2,3].forEach(function(val) {
console.log(val);
});
return "YOU DONT SEE UNDEFINED NOW"; //prints the string
})();
whereas if it is executed by the browser script, the developer-tools prints whatever the function prints and the function returns value to its caller. (Here, developer-tools need not to print all the function return values run by browser script, then your logs becomes messed up. You will see random return values returned by each and every function.
Related
According to the documentation for chrome.tabs.executeScript (MDN), the callback function accepts an "array of any result" result set from the execution of the script(s). How exactly do you use this to get results? All of my attempts end up with undefined being passed to the callback.
I have tried returning a value at the end of my content script, which threw a Uncaught SyntaxError: Illegal return statement. I tried using the optional code object argument {code: "return "Hello";} with no success.
I feel like I am not understanding what is meant by "The result of the script in every injected frame", in the documentation.
chrome.tabs.executeScript() returns an Array with "the result of the script" from each tab/frame in which the script is run.
"The result of the script" is the value of the last evaluated statement, which can be the value returned by a function (i.e. an IIFE, using a return statement). Generally, this will be the same thing that the console would display as the results of the execution (not console.log(), but the results) if you executed the code/script from the Web Console (F12) (e.g. for the script var foo='my result';foo;, the results array will contain the string "my result" as an element). If your code is short, you can try executing it from the console.
Here is some example code taken from another answer of mine:
chrome.browserAction.onClicked.addListener(function(tab) {
console.log('Injecting content script(s)');
//On Firefox document.body.textContent is probably more appropriate
chrome.tabs.executeScript(tab.id,{
code: 'document.body.innerText;'
//If you had something somewhat more complex you can use an IIFE:
//code: '(function (){return document.body.innerText;})();'
//If your code was complex, you should store it in a
// separate .js file, which you inject with the file: property.
},receiveText);
});
//tabs.executeScript() returns the results of the executed script
// in an array of results, one entry per frame in which the script
// was injected.
function receiveText(resultsArray){
console.log(resultsArray[0]);
}
This will inject a content script to get the .innerText of the <body> when the browser action button is clicked. you will need the activeTab permission.
As an example of what these produce, you can open up the web page console (F12) and type in document.body.innerText; or (function (){return document.body.innerText;})(); to see what will be returned.
I am currently try to run some JavaScript within my robot framework code that creates a new function, and then uses the newly created function to return a value upon calling it. However, when I log the result to the console, I do. It get my desired output. Please help!
The code:
${test}= Execute Javascript return function test(){return 1}; test();
Log To Console ${test}
The console output:
{}
Move the return statement after the function definition, otherwise the return happens before test() is called.
*** Settings ***
Library Selenium2Library
*** Test Cases ***
Example
[Setup] open browser about:blank chrome
[Teardown] close all browsers
${test}= execute javascript function test() {return 1}; return test();
should be equal as strings ${test} 1
Sometimes we leave console.log debug statements in our javascript. Just so they don't accidentally make it into production (where some old browsers will die on these), we tried doing this in our first coffeescript file:
if !console?
console = {log: ->}
But this seems to kill all ability to use console.log in development, even in browsers that support it (webkit).
console? returns false, so the if block doesn't run, but it still seems to overwrite the functionality of console.log.
Any ideas? Thanks!
The problem is that this:
if !console?
console = {log: ->}
becomes this JavaScript:
var console;
if (typeof console === "undefined" || console === null) {
console = {
log: function() {}
};
}
The result is that you have a local console variable that shadows the window.console that you're looking for.
The solution is to say exactly what you mean:
if 'console' !of window
window.console = { log: -> }
Note that console is always localized to window here so you don't accidentally create any shadows.
There are various ways to say if 'console' !of window that will have the same effect, use whichever one is clearest for you.
My approach has been to define a DEBUG (or print) function to use instead of console.log. The default, production definition is
DEBUG = () ->
and prior to any test code (e.g. at the end like Python if __name__... block):
if not module.parent? and (!process.argv[2]? or process.argv[2]!='nodebug')
DEBUG = (arg...) ->
arg.unshift('==> ')
console.log arg...
or simply
DEBUG = console.log
To make debugging easier, I'm capturing all of the console logs in Chrome so that users who submit a feedback entry will also submit all of the logs to our server. When someone encounters a problem in production, I can first and foremost get them back to work so that I can then sit down and more thoroughly go through all of the logs to determine the root cause of whatever issue the user encountered in production.
The technique I use to capture the logs involves overriding console.log so that all text entered in the first argument gets stored in an array while simultaneously invoking the legacy function so that I can still see the logs in the console too.
The problem is when there's the occasional uncaught exception. These aren't included in the uploaded logs, so it's not always clear what caused the problem. So I tried overriding ReferenceError by writing a JavaScript function that takes a function as an argument, then returns a new function that does stuff with it, like storing data in a variable, and then invoking the legacy function as the last step:
function overrideException(legacyFn) {
/** arguments for original fn **/
return function() {
var args = [];
args[0] = arguments[0];
// pass in as arguments to original function and store result to
// prove we overrode the ReferenceError
output = ">> " + legacyFn.apply(this, args).stack;
return legacyFn.apply(this, arguments);
}
}
To test the overrideException function, I ran the following code on the console:
ReferenceError = overrideException(ReferenceError);
Afterwards, I tested the returned function, the new ReferenceError, by manually throwing a ReferenceError:
throw new ReferenceError("YES!! IT WORKS! HAHAHA!");
The resulting output on the console is:
ReferenceError: YES!! IT WORKS! HAHAHA!
And checking the global variable output from the overrideException function shows that it did indeed run:
output
">> ReferenceError: YES!! IT WORKS! HAHAHA!
at ReferenceError (<anonymous>)
at new <anonymous> (<anonymous>:18:35)
at <anonymous>:2:7
at Object.InjectedScript._evaluateOn (<anonymous>:562:39)
at Object.InjectedScript._evaluateAndWrap (<anonymous>:521:52)
at Object.InjectedScript.evaluate (<anonymous>:440:21)"
Now, here's where things start to fall apart. In our code, we're not going to know when an uncaught exception occurs, so I tested it by attempting to run a function that doesn't exist:
ttt();
Which results in:
ReferenceError: ttt is not defined
However, unlike the case where we explicitly throw an error, in this case, the function doesn't fire, and we're left with only the legacy functionality. The contents of the variable output is the same as in the first test.
So the question seems to be this: How do we override the ReferenceError functionality that the JavaScript engine uses to throw errors so that it's the same one we use when we throw a ReferenceError?
Keep in mind that my problem is limited only to Chrome at this time; I'm building a Chrome Packaged app.
I have done quite a bit of research for the same reason: I wanted to log errors and report them.
"Overriding" a native type (whether ReferenceError, String, or Array) is not possible.
Chrome binds these before any Javascript is run, so redefining window.ReferenceError has no effect.
You can extend ReferenceError with something like ReferenceError.prototype.extension = function() { return 0; }, or even override toString (for consistency, try it on the page, not the Dev Tools).
That doesn't help you much.
But not to worry....
(1) Use window.onerror to get file name, 1-indexed line number, and 0-indexed position of uncaught errors, as well as the error itself.
var errorData = [];
onerror = function(message, file, line, position, error) {
errorData.push({message:message, file:file, line:line, position:position, error:error});
};
See the fiddle for an example. Since the OP was Chrome-specific, this has only been tested to work in Chrome.
(2) Because of improvements to (1), this is no longer necessary, but I leave this second technique here for completeness, and since onerror is not guaranteed to work for all errors on all browsers. You will also sometimes see the following:
var errors = [];
function protectedFunction(f) {
return function() {
try {
f.apply(this, arguments);
} catch(e) {
errors.push(e);
throw e;
}
};
}
setTimeout = protectedFunction(setTimeout);
setInterval = protectedFunction(setInterval);
etc...
FYI, all this is very similar to what has been done in the Google Closure Compiler library, in goog.debug, created during Gmail development with the intent of doing exactly this. Of particular interest is goog.debug.ErrorHandler and goog.debug.ErrorReporter.
EDIT
I was a bit quick there, the problem arises in the function and not where I first said. Here is the function:
function returnAnObject(url) {
var apiurl = 'http://url.com';
var info = {};
$.getJSON(apiurl, function(data) {
$.extend(info, {
a : data.x,
b : data.y,
c : data.z
});
});
console.log(info); // Shows object as usual
console.log(info.a); // Shows undefined
return info;
}
Does that make it clearer?
END EDIT
Ok so, I have a little problem.
I have a function that returns a fairly simple object, which looks something like this:
{
a: 'x',
b: 'y',
c: 'z'
}
I save it to a variable like this:
var something = functionThatReturnsObject(someargument);
console.log(something); // In chrome dev tools, I see the object and its values
console.log(something.a); // This, however, logs undefined
console.log(something['a']); // This also logs undefined
Why is this? I think I'm going crazy here, I must have overlooked something...
The really weird part happens if instead of
var something = functionThatReturnsObject(someargument);
I write
window.something = functionThatReturnsObject(someargument);
console.log(something); // Still works, showing the object and properties
console.log(something.a); // Still doesn't work
console.log(someting['a']); // Still doesn't work
If I now access the object directly from the dev tools, inputting
something; // returns object, I can see everything in it etc.
something.a // Now, for some mysterious (to me) reason, this works, returning the value of a
So, does anyone understand what is going on here?
As I suspected. You're assigning info in the success handler for an asynchronous function call. The success handler does not execute until AFTER the ajax call completes, but your function returns right after the ajax call starts (and long before it finishes and succeeds). You may not believe this, but this is the fourth time I've answered this exact same type of issue today. It's a very common mistake. Because of the inline success handler, it appears that it all happens inside of the main function, but in reality it happens long after that function finishes.
You can't use the return result from the ajax call until after the success handler is called. If you want to pass that result on to subsequent code, you will have to call that subsequent code from the success handler, not continue it after the returnAnObject function call.
It works in the dev tools because the ajax call completes by the time you type anything into the dev tools or look there. But, info.a is not available at the end of the returnAnObject function. It's only available when the success handler for the ajax function is called.