I'm trying to stub/mock/override a function call during testing which writes a log to a DB.
function logit(msg) {
writeMessageToDb(msg);
}
function tryingToTestThisFunction(){
var error = processSomething();
if (error) {
logit(error);
}
}
I'd like logit() to simply print to the console during testing...and doing a "isTesting()" if/else block inside the logit() function is not an option.
Is this possible without including some additional mocking framework. I'm currently using JsTestDriver for unit testing and have not had a chance to evaluate any mocking frameworks. An ideal solution at the moment would be to handle this without another framework.
I use Jasmine and Sinon.js (using Coffeescript), here's how I stub out the confirm() method to, for example, just return true.
beforeEach ->
#confirmStub = sinon.stub(window, 'confirm')
#confirmStub.returns(true)
afterEach ->
#confirmStub.restore()
In javascript the latest definition is the prevalent.
so just redefine the logit method after the first definition.
function logit(msg) {
console.log(msg);
}
example : http://www.jsfiddle.net/gaby/UeeQZ/
I have been working on exactly the same problem. The developers gave me an HTML5 app to test, so of course I can't change their code for testing. I decided to use qunit and sinon, along with sinon-qunit.
For a newb to JavaScript unit testing like me, I was going nuts with the sinon documentation and various examples on the web, as most of it seems for an implied environment that isn't mentioned. The code below is a complete page, so I hope nothing is left for confusion.
The function that I have to call is caller() and I can't do anything about stubme() because it's in the developer's code. However, I can add sinonstub() in my test code. But how to get it to work with sinon? The sinon documentation really confused me for a while, but below is the simple solution. The stub4stubme object can be used to control the stub action, and also get the information about what's happening with the stub calls.
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
<link rel="stylesheet" href="qunit-1.12.0.css" type="text/css" media="screen" />
</head>
<body>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<script src="sinon-1.7.3.js"></script>
<script src="qunit-1.12.0.js"></script>
<script src="sinon-qunit-0.8.0.js"></script>
<script>
// Dev code in another file
function stubme() {
return "stubme";
}
function caller() {
return "caller " + stubme();
}
// End of dev code
var sinonstub = function () {
return "u haz bin stubbed";
};
test("Stubbing global environments", function () {
equal(caller(), "caller stubme");
var stub4stubme = this.stub(window, "stubme", sinonstub);
equal(caller(), "caller u haz bin stubbed");
ok(stubme.called);
});
</script>
</body>
</html>
Javascript is not only runtime linked, but is the last word wins linked as well. This means you can redeclare the method with the behavior you want in your test (since your test has the last word):
function yourTest(){
oldImpl = logit; // An even better approach is to do this in a setup.
logit = function(msg){ Console.log.apply(console, s.call(arguments));};
// do you assertions: assert.... yada.
logit = oldImpl; // Do this to keep your test isolated from the others you'll be executing in this test run. An even better approach is to do this in a teardown.
}
can you just override the method on the window object? In Chrome console this works
function test() {console.log('test')};
window.test();
just override the logit function, this can be called anytime later than logit is defined.
(function(){
//keep handle to original logit method.
var ol = logit;
//shorter lookup path for slice
var s = Array.prototype.slice;
//logit override
logit = function() {
//if in testing
if (typeof IsTesting == "function" && !!IsTesting()) {
//log the arguments
console.log.apply(console, s.call(arguments));
} else {
//otherwise, call the original function.
ol.apply(this, s.call(arguments))
}
}
}());
Related
I'm trying to make unit and e2e test on a project, i decided to use jest and puppeteer (with also jest-puppeteer) to achive this.
My problem is that I initialize a var, named tools, in an script of index.html and i want to get it to do some test after, but he return me an error that is "tools" is not defined.
I already tryed to see on the web if a solution exist but without success.
Can I have somme help ? :')
Code extracts:
// index.html
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8'>
<script src="./js/Variables.js"></script>
<script src="./js/Tools.js"></script>
</head>
<body>
<script>
tools = new Tools();
</script>
</body>
</html>
// Variables.js
let tools;
// Tools.js
class Tools {
constructor(){
// do some stuff
}
test(){
return "test string";
}
}
// app.test.js
beforeAll(async () => {
await page.goto('http://myPage/');
});
test("can i get \"tools\"", () => {
console.log(tools); // tools is not defined
expect(tools.test()).toBe("test string");
});
EDIT 22/07/2022 15:38
I finally managed to get something BUT now i can't use functions on it, the error says that tools.test() is not a function, it seems to retrieve only his "pure" value and not the Tools instance.
test("can i get \"tools\"", async () => {
let tools = await page.evaluate('tools');
console.log(tools); // gets {} (doesn't seems to retrieve instance)
expect(tools.test()).toBe("test string"); // TypeError: tools.test() is not a function
});
I can use class method by using
let toolsTest = await page.evaluate(() => tools.test());
BUT it's not really what i want... I really want to get an instance to test some sh!t on it.
However let tools = await page.evaluate(() => tools); still doesn't give me the instance, there is really no way to achive this ?
So i really need to know how to get variables in script tag to use them with jest. Maybe another test library can do the job such as mocha ?
I implement library with Google App Script and I have some difficulties to call a function from library using google.script.run.
Here is the code of my Library :
Code.gs
function ShowSideBar() {
var html = HtmlService.createTemplateFromFile('Index_librairie').evaluate()
.setTitle('Console de gestion')
.setWidth(300);
SpreadsheetApp.getUi() // Or DocumentApp or FormApp.
.showSidebar(html);
}
function execution_appeler_par_html(){
Logger.log("execution_appeler_par_html");
}
Index_librairie.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
google.script.run.withSuccessHandler(work_off).execution_appeler_par_html();
function work_off(e){
alert(e);
}
</script>
</head>
<body>
test de ouf
</body>
</html>
Here is my Spreadsheet that use the Library :
Code.gs
function onopen() {
lbrairietestedouard.ShowSideBar();
}
Google.script.run does not reconize execution_appeler_par_html() function.
I should use libraryname.execution_appeler_par_html() but this syntaxe doesn't work in configuration of google.script.run
It seems that google.script.run can't look inside Objects or self-executing anonymous functions. In my case, putting any code inside an object or IIFE resulted in "is not a function" type of error being thrown in the console.
You can work around this by declaring a single function that will call nested methods inside libraries and objects.
.GS file
function callLibraryFunction(func, args){
var arr = func.split(".");
var libName = arr[0];
var libFunc = arr[1];
args = args || [];
return this[libName][libFunc].apply(this, args);
}
In JavaScript, every object behaves like an associative array of key-value pairs, including the global object that 'this' would be pointing to in this scenario. Although both 'libName' and 'libFunc' are of String type, we can still reference them inside the global object by using the above syntax. apply() simply calls the function on 'this', making the result available in global scope.
Here's how you call the library function from the client:
google.script.run.callLibraryFunction("Library.libraryFunction", [5, 3]);
I don't claim this solution as my own - this is something I saw at Bruce McPherson's website a while back. You could come up with other ad-hoc solutions that may be more appropriate for your case, but I think this one is the most universal.
Noticed the same problem trying to invoque library from google.script.run within HTML code.
Here is the workaround I use :
LIBRARY SIDE : aLibrary
function aFunction(){
//code...;
}
ADDON SIDE (requires library "aLibrary")
function aFunction(){
aLibrary.aFunction();
}
HTML
<input type="button" value="run aFunction" onclick="google.script.run.aFunction()" />
I like the workaround because I keep a clear view and organisation of my functions names, and do not need to alter HTML code if I bring my functions directly inside the addOn project, once development is satsifying.
Only thing I did not try yet : handling arguments and return values....
I hope this contribution is not to foolish, please forgive me I am very amateur...
After quite long time working on this, I found out that any function that you call from the library using google.script.run must exist in both the main script and the library script. The function must at least be declared in the main script while the implementation as well as the parameters are not important. The implementation will be in your library. If the main script does not include the name of the function, the error <your function> is not a function will appear.
Now that there is no meaning of having a library if every single function needs to exist in the main function, the solution provided by #Anton Dementiev would be used as a workaround. Suppose you have a function named testFunc in your library, you can call it using the following method:
google.script.run.callLibraryFunction("testFunc", [5, 3]);
where the library has this function:
function callLibraryFunction(func, args) {
args = args || [];
return this[func].apply(this, args);
}
and the main script has this declaration:
function callLibraryFunction() {
}
Google must fix this stupid behaviour
I've been trying to implement basic Javascript with Webpack but have found a lot of trouble trying to get basic methods to work. I know it is probably a problem with my js loading before the DOM but all my fixes to this issue have been futile.
For instance, I'm trying to just run let taskinput = document.getElementById('new-task'); and when I search the taskinput variable in the console I get a return of Uncaught ReferenceError: taskinput is not defined(…).
I've tried quite a few different solutions to make this code operate after the DOM has loaded but nothing seems to work.
I tried the basic attempt of just putting my javascript file at the bottom of the DOM.
I've tried a basic js method like so:
document.onreadystatechange = function() {
if (document.readyState === "complete") {
initApplication();
}
function initApplication() {(... placed code in here ...)}
I've tried using jqueries
$( document ).ready(function() { });
I've tried injecting my javascript file into my HTML with the html-webpack-plugin
Is there something I'm missing with Webpack?
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Sample Site</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
<button id="new-task">click</button>
<script src="/assets/app.bundle.js"></script>
</body>
</html>
app.js
'use strict';
document.onreadystatechange = function() {
if (document.readyState === "complete") {
initApplication();
}
}
function initApplication() {
onButtonClick();
}
let onButtonClick = function() {
let taskInput = document.getElementById('new-task');
taskInput.onclick = alert('hey');
}
For instance, I'm trying to just run let taskinput = document.getElementById('new-task'); and when I search the taskinput variable in the console I get a return of Uncaught ReferenceError: taskinput is not defined(…).
First of all, module code in webpack bundles is run in its own scope, effectively in a closure, and not in the global scope (which you can access with window). Secondly, even if you go to your console and declare a variable with let, for example let a = 1, even though your console operates in the global (window) scope, if you try to do window.a you will get undefined because let variables do not get attached to the window scope. See this
Is there something I'm missing with Webpack?
Probably the fact that the code in your bundles, generated by webpack, does not run in the global scope, as explained above.
If you want to expose a variable to the global scope in order to be able to use it, for debugging purposes, in the console, declare it as window.variableName. Or you can add a breakpoint in your code, by adding debugger, after the variable you want to check out, without exposing it to the global scope.
Using the browser console you can only access variables which are declared globally, and if you define a JavaScript variable inside a function, it doesn't become a global variable. Also, variables declared using let never become global variables.
If you want to declare a variable globally, you can make it a property of window, for example:
window.taskinput = document.getElementById('new-task');
This might be useful for debugging, but avoid doing it in production code, as using global variables is considered a bad practice.
I'd like to be able to programmatically add a class of console to the <body> tag if Firebug's console.log() was called anywhere on a page. Then I could put some obnoxious message on the screen to remind me not to deploy code with those statements still in it.
The very similar to Eric Meyer's Diagnostic CSS.
Is it possible?
Hehehe. It's an easy mistake to make, isn't it.
Option 1: Always write if(window.console) console.log(...); instead of just console.log(..);
Option 2:
function mydebug(thingtodebug) {
if(window.console) console.log(thingtodebug);
}
..then always use mydebug() instead of console.log();. You could include an else clause that throws up an alert box if console isn't defined.
Option 3:
if(!window.console) {
var console={
log : function() {alert("Don't call console.log");}
}
}
...this will do pretty much exactly what you're asking.
The trouble is that all these options involve including extra code in your live system just to help you avoid embarrassment. (and of course, it'd be even more embarrassing if you miss that alert box!)
If you want to avoid this, a better solution might be to have a separate script that scans your code for any occurrences of console.log. You could run this script as part of your deployment process.
Hope that helps.
This works for me (using jQuery):
$(document).ready(function() {
var body = $('body'),
console = window.console;
console.debug = function() {
if (!body.hasClass('console')) {
body.addClass('console');
console.debug = oldDebug;
}
}
console.debug('foo');
});
It will only add the class the first time our custom function is called. Then it sets console.debug to the original function.
That's the cool thing with javascript, you can override nearly everything :)
It's quite simple. Just override the default console object with your own:
<!DOCTYPE html>
<html>
<head>
<title>Console Test</title>
<script>
var oldConsole = (typeof window.console === "object") ? window.console : null;
var console = {
log: function() {
oldConsole.log(arguments);
document.body.className = "console";
alert('applied class');
}
};
</script>
</head>
<body>
<input type="button" value="click me" onclick="console.log('this is a test');">
</body>
</html>
Live example.
This example is a simplified version of my code. I'm still trying to grasp the new way of writing javascript (as opposed to the way 10 years ago) so thanks for your patience. I need globalVal's value to be accessible and I'm having trouble. The value is obtained from a function that is called as an argument from another method. The example is probably easier to see. Just need to be able to have access to globalvar from everywhere in the DOM. Is this possible? Thanks
<html>
<head>
<script type="text/javascript">
var globalvar;
function initialize() {
var someVariable = 5;
doSomething(someVariable, getTheVar);
}
function doSomething(someVariable, expectGlobalVar) {
//alert(someVariable);
alert(expectGlobalVar);
}
function getTheVar() {
globalVar = "test";
return globalVar;
}
</script>
<title></title>
</head>
<body onload="initialize()">
This is a test
</body>
</html>
You're mostly fine, you can directly access globalVar from any script running anywhere in the page if you declare it the way you have.
Specifically: Using var x; at page-level scope (that is, outside of any function) declares a property on the window object (it has a special feature in that it can't be deleted, but that's not important here).
var foo = 2;
window.foo = 2; // Basically the same other than the delete thing we're not worrying about here
And so:
var foo = 2;
alert(foo); // alerts "2"
alert(window.foo); // also alerts "2"
window.bar = 4;
alert(window.bar); // alerts "4"
alert(bar); // also alerts "4"
Naturally this is only true at the top level, outside of any functions. Inside functions, you're declaring something local to the function. (In essence; it's actually a lot more interesting than that.)
But since you've asked about scope, it's worth nothing that all of the other things you've defined (initialize, getTheVar, doSomething) are also globals. In general, you want to avoid putting anything in the global namespace that you can avoid putting there.
For that reason, I advocate always using a "scoping function":
(function() {
// your code here
})();
...and explicitly exporting exactly and only the things you really need to be global (by assigning them to properties on window).
In your case, you've said you need globalVar and you've also used initialize (although there are other ways to do what you're doing in initialize), so you could do this:
(function() {
var globalvar;
// Exports
window.globalVar = globalVar;
window.initialize = initialize;
// Implementation
function initialize() {
var someVariable = 5;
doSomething(someVariable, getTheVar);
}
function doSomething(someVariable, expectGlobalVar) {
//alert(someVariable);
alert(expectGlobalVar);
}
function getTheVar() {
globalVar = "test";
return globalVar;
}
})();
But you can take it further. Since you're not calling initialize until the load event of the body element, you could avoid publishing initialize. Just put your script tag at the end of the document, just before the closing </body> tag (as the YUI folks recommend), and do your initialization there:
<html>
<head>
<title>...</title>
</head>
<body>This is a test
<script type='text/javascript'>
(function() {
var globalvar;
// Initialization
initialize();
// Exports
window.globalVar = globalVar;
// Implementation
function initialize() {
var someVariable = 5;
doSomething(someVariable, getTheVar);
}
function doSomething(someVariable, expectGlobalVar) {
//alert(someVariable);
alert(expectGlobalVar);
}
function getTheVar() {
globalVar = "test";
return globalVar;
}
})();
</script>
</body>
</html>
The DOM is fully loaded and ready to go at that point.
But we can go even further if we want: We can have nothing in the global namespace if we like. If you hook up all of your handlers within your initialize function rather than using onload, onclick, and similar attributes, there's no need for globalVar to be global except to your code. (You hook up handlers after the fact by using attachEvent [on IE], addEventListener [on standards-based browsers], or better yet using a library like jQuery, Closure, Prototype, YUI, or any of several others.)
You should call function getTheVar instead of passing it:
function initialize() {
var someVariable = 5;
doSomething(someVariable, getTheVar());
}
You're doing it right.
Any variable which is declared in global scope, just like you have in the example, will be available from every scope in the window.
(BTW, declaring a global var is [almost] equivalent to window.myVar = someValue;)
The problem in your example is that you are not actually calling getTheVar on the fourth line, but rather just passing the function itself. You probably want this:
doSomething(someVariable, getTheVar());