JavaScript how to mock confirm method - javascript

I have following code in my JavaScript code.
if (window.confirm('Are you sure?')) {
AdminData.actOnResult('delete');
}
I am writing test for this piece of code. How do I mock window.confirm method? I tried following code but it did not work.
window.confirm = function(arg) {
return true;
};
I can move the window.confirm method to another function and then I can mock that method. However I was wondering if there is a better solution.

Your own code works fine for me in IE. Just the following in the global scope should override it:
var confirm = function () { return true; }
EDIT
I've seen a few questions on SO in the past about trying to override confirm, mostly because they don't like it (and who would?). If you're trying to bypass it for this sort of reason, I suggest you look at changing your code to implement a callback-based replacement for confirm. Take a look at jQuery UI's modal confirm for a good example of this.

I am using Jasmine for unit testing and have mocked alert and confirm with the following
alert = function (alertString) {debug.log('ALERT:', alertString);};
var confirmValue = true; //set this before you expect the confirm statement to be shown
confirm = function (confirmString) {
debug.log('CONFIRM:', confirmString, confirmValue);
return confirmValue;
};
Then I can say:
describe("test", function () {
it('should test true confirm workflow', function () {
confirmValue = true; // or false if you like
//expect outcomes that would come from any confirms being called with true
});
});
It's not perfect, and if you have multiple confirms that could pop between setting that confirmValue, you could be in trouble. Perhaps then it would be good to setup a cue of expected confirm return values... tricky...

I'd think about implementing a wrapper around static methods on the window (or other) object. Then provide your wrapper to whatever uses the static method. Obviously this is easier if you are using a "class"-based implementation. Then, in order to mock the method, simply provide a different wrapper that returns the value that you want.
var windowWrapper = {
confirm: function(msg) { return confirm(msg); },
...
};
var mockWrapper = {
confirm: function(msg) { return true; },
...
}
var wrapper = windowWrapper;
if (test) {
wrapper = mockWrapper;
}
...
if (wrapper.confirm('Are you sure?')) {
AdminData.actOnResult('delete');
}

Related

How can I check that a class constructor is called with proper attributes in Sinon?

I'm testing code that instantiates an object from an external library. In order to make this testable, I've decided to inject the dependency:
Boiled down to:
const decorator = function (obj, _extLib) {
var ExtLib = _extLib || require('extlib')
config = determineConfig(obj) //This is the part that needs testing.
var el = new ExtLib(obj.name, config)
return {
status: el.pay({ amt: "one million", to: "minime" })
bar: obj.bar
}
}
In my test, I need to determine that the external library is instantiated with the proper config. I'm not interested in whether this external library works (it does) nor wether calling it, gives results. For the sake of the example, let's assume that on instantiating, it calls a slow bank API and then locks up millions of dollars: we want it stubbed, mocked and spied upon.
In my test:
it('instantiates extLib with proper bank_acct', (done) => {
class FakeExtLib {
constructor(config) {
this.acct = config.bank_acct
}
this.payMillions = function() { return }
}
var spy = sandbox.spy(FakeExtLib)
decorator({}, spy) // or, maybe decorator({}, FakeExtLib)?
sinon.assert.calledWithNew(spy, { bank_acct: "1337" })
done()
})
Do note that testing wether e.g. el.pay() was called, works fine, using spies, in sinon. It is the instantiation with new, that seems untestable.
To investigate, let's make it simpler even, testing everything inline, avoiding the subject under test, the decorator function entirely:
it('instantiates inline ExtLib with proper bank_acct', (done) => {
class ExtLib {
constructor(config) {
this.acct = config.bank_acct
}
}
var spy = sandbox.spy(ExtLib)
el = new ExtLib({ bank_acct: "1337" })
expect(el.acct).to.equal("1337")
sinon.assert.calledWithNew(spy, { bank_acct: "1337" })
done()
})
The expect part passes. So apparently it is all called properly. But the sinon.assert fails. Still. Why?
How can I check that a class constructor is called with proper attributes in Sinon?" Is calledWithNew to be used this way? Should I spy on another function such as the ExtLib.prototype.constructor instead? If so, how?
You're really close.
In the case of your simplest example, you just need to create el using the spy instead of ExtLib:
it('instantiates inline ExtLib with proper bank_acct', (done) => {
class ExtLib {
constructor(config) {
this.acct = config.bank_acct
}
}
var spy = sandbox.spy(ExtLib)
var el = new spy({ bank_acct: "1337" }) // use the spy as the constructor
expect(el.acct).to.equal("1337") // SUCCESS
sinon.assert.calledWithNew(spy) // SUCCESS
sinon.assert.calledWithExactly(spy, { bank_acct: "1337" }) // SUCCESS
done()
})
(Note that I modified the test to use calledWithExactly to check the arguments since calledWithNew doesn't seem to check the arguments properly in v7.2.2)

Understanding Protractor and WebDriverJS control flow

Can someone help me understand how WebDriverJS/Protractor works in this case?
function MyPageObject(buttonElementFinder) {
this.getButtonByIndex = function(index) {
return {
myButton: buttonElementFinder.get(index)
}
}
}
1. describe('My button', function() {
2.
3. it('should contain the text foo', function() {
4. var myElementFinder = element.all(by.css('.foo'));
5. var pageObject = new MyPageObject(myElementFinder);
6. var button = pageObject.getButtonByIndex(0);
7. expect(button.text()).toBe('foo');
8. });
9.
10. });
Does the WebDriverJS control flow have an action added to it on line 6 because of the .get method of ElementFinders?
I presume the expect also adds another item to the control flow too on line 7?
Edit: I have update the code to use element.all.
var myElementFinder = element.all(by.css('.foo'));
myElementFinder is a ElementArrayFinder and is simply an object. Nothing async is happening here.
var pageObject = new MyPageObject(myElementFinder);
Obvious.
var button = pageObject.getButtonByIndex(0);
This will return an ElementFinder from buttonElementFinder.get. Nothing async is happening here.
expect(button.text()).toBe('foo');
button.text() returns a promise from Webdriver.schedule, which in turn is using the control flow which is retrieved using webdriver.promise.controlFlow(), which exposes an execute function.

Spying on jQuery $('...') selector in jasmine

When it comes to spying on jQuery functions (e.g. bind, click, etc) it is easy:
spyOn($.fn, "bind");
The problem is when you want to spy on $('...') and return defined array of elements.
Things tried after reading other related answers on SO:
spyOn($.fn, "init").andReturn(elements); // works, but breaks stuff that uses jQuery selectors in afterEach(), etc
spyOn($.fn, "merge").andReturn(elements); // merge function doesn't seem to exist in jQuery 1.9.1
spyOn($.fn, "val").andReturn(elements); // function never gets called
So how do I do this? Or if the only way is to spy on init function how do I "remove" spy from function when I'm done so afterEach() routing doesn't break.
jQuery version is 1.9.1.
WORKAROUND:
The only way I could make it work so far (ugly):
realDollar = $;
try {
$ = jasmine.createSpy("dollar").andReturn(elements);
// test code and asserts go here
} finally {
$ = realDollar;
}
Normally, a spy exists for the lifetime of the spec. However, there's nothing special about destroying a spy. You just restore the original function reference and that's that.
Here's a handy little helper function (with a test case) that will clean up your workaround and make it more usable. Call the unspy method in your afterEach to restore the original reference.
function spyOn(obj, methodName) {
var original = obj[methodName];
var spy = jasmine.getEnv().spyOn(obj, methodName);
spy.unspy = function () {
if (original) {
obj[methodName] = original;
original = null;
}
};
return spy;
}
describe("unspy", function () {
it("removes the spy", function () {
var mockDiv = document.createElement("div");
var mockResult = $(mockDiv);
spyOn(window, "$").and.returnValue(mockResult);
expect($(document.body).get(0)).toBe(mockDiv);
$.unspy();
expect(jasmine.isSpy($)).toEqual(false);
expect($(document.body).get(0)).toBe(document.body);
});
});
As an alternative to the above (and for anyone else reading this), you could change the way you're approaching the problem. Instead of spying on the $ function, try extracting the original call to $ to its own method and spying on that instead.
// Original
myObj.doStuff = function () {
$("#someElement").css("color", "red");
};
// Becomes...
myObj.doStuff = function () {
this.getElements().css("color", "red");
};
myObj.getElements = function () {
return $("#someElement");
};
// Test case
it("does stuff", function () {
spyOn(myObj, "getElements").and.returnValue($(/* mock elements */));
// ...
});
By spying on the window itself you have access to any window properties.
As Jquery is one of these you can easily mock it as below and return the value you require.
spyOn(window, '$').and.returnValue(mockElement);
Or add a callFake with the input if it needs to be dynamic.

How to form callback when using context.executeQueryAsync delegates in javascript

Sorry for yet another question about callbacks. In trying to solve this problem, I've run across about a million of them. However, I'm having trouble wrapping my head around this particular scenario.
I have the code below, which obviously doesn't work as delegates apparently don't return values (I'm learning as I go, here). So, I know I need a callback at this point, but I'm not sure how to change this code to do that. Can anyone help?
function MyFunction() {
var ThisLoggedInUser = checkCurrentUser();
//do some stuff with the current user
}
function checkCurrentUser() {
var context = SP.ClientContext.get_current();
var siteColl = context.get_site();
var web = siteColl.get_rootWeb();
this._currentUser = web.get_currentUser();
context.load(this._currentUser);
context.executeQueryAsync(Function.createDelegate(this, this.CheckUserSucceeded),
Function.createDelegate(this, this.CheckUserfailed));
}
function CheckUserSucceeded() {
var ThisUser = this._currentUser.get_title();
return ThisUser;
}
function CheckUserfailed() {
alert('failed');
}
Based on your comment, you have to rething the way you want your code because you cannot use ThisUser in MyFunction().
For example you could do that:
function CheckUser() { ... }
// then call the function to find the current user
CheckUser();
// then in CheckUserSucceeded you call MyFunction()
function CheckUserSucceeded() {
MyFunction(this._currentUser.getTitle())
}
// and now you can use ThisUser in MyFunction()
function MyFunction(ThisUser) {
// do something with ThisUser
}
Your CheckUserSucceed won't return anything because it's asynchronous....
So you have to do something like that:
var ThisUser;
function CheckUserSucceeded() {
ThisUser = this._currentUser.getTitle()
// here you can call an other action and do something with ThisUser
}
You may also want to check the $SP().whoami() function from http://aymkdn.github.io/SharepointPlus/ and see the documentation.

jQuery Plugins: If I want to call something like $('selector').plugin.group.method(), how can I achieve this?

I have written some relatively simple jQuery plug-ins, but I am contemplating writing something more advanced in order to keep commonly used methods on the site easily accessible and DRY
For example, I might have something like this for a structure:
plugin
- popup
- element
...
=== popup ===
- login
- product
...
=== element ===
- shoppingCart
- loginStatus
...
So, to bind a popup login popup event, I'd like to be able to do:
$('#login_button').plugin.popup.login();
What's the best way to do this? Is there a better way of achieving what I want to do?
Cheers,
The way farhan Ahmad did it was pretty much right... it just needs deeper levels to suit your needs your implementation would look like this:
jQuery.fn.plugin = function(){
//"community" (global to local methods) vars here.
var selectedObjects = this; //-- save scope so you can use it later
// return the objects so you can call them as necessary
return {
popup: { //plugin.popup
login: function(){ //plugin.popup.login
//selectedObjects contains the original scope
console.log(selectedObjects);
},
product: function(){} //plugin.popup.product
},
element: { //plugin.element
shoppingCart: function() {}, //plugin.element.shoppingCart
loginStatus: function() {} //plugin.element.loginStatus
}
}
}
So now if you call:
$("#someDiv").plugin.login(); the result will be as expected. I hope this helps.
jQuery.fn.messagePlugin = function(){
var selectedObjects = this;
return {
saySomething : function(message){
$(selectedObjects).each(function(){
$(this).html(message);
});
return selectedObjects; // Preserve the jQuery chainability
},
anotherAction : function(){
//...
return selectedObjects;
}
};
}
We use it like this:
$('p').messagePlugin().saySomething('I am a Paragraph').css('color', 'red');
The selected objects are stored in the messagePlugin closure, and that function returns an object that contains the functions associated with the plugin, the in each function you can perform the desired actions to the currently selected objects.

Categories