Am working on Backbone application and i have to unit test it using sinon.js and Qunit.js.
The scenario is i have one carView which extends baseview and the baseview extends backbone's view.
I have one function say buildFormUrl in the car view which returns a string. The string value is changed on the basis of user action.
Is it possible to make stub of buildFromUrl stub using sinon.stub and calling the stub function and then checking the return values?
Backbone Code Snippet:
var CarSearchView = BaseView.extend({
intitalize : function(){
//some code
},
buildFormUrl: function(baseUrl){
// process a different form url based on new/used selection
var newUsedToggleValue = this.$('#cars-form-new-used-select').val(),
url = baseUrl;
if (newUsedToggleValue === 'used') {
url += 'for-sale/searchresults.action';
} else {
url += 'go/search/newBuyIndex.jsp';
}
return url;
}
});
Sinon code Snippet:
QUnit.test('_buildURLForm function', function(){
QUnit.expect(1);
var BuildFormURLStub = Sinon.stub(CarSearch.prototype, 'buildFormUrl');// building the sinon stub
var toggleButton = $(SampleHtml).find('cars-new-used-toggle');
$(toggleButton).first().click(); //clicking the button
var baseURL = "http:\\www.cars.com";
var output = Sinon.once(BuildFormURLStub.withArgs(baseURL)); // passing the argument and and calling the function using sinon once![enter image description here][1]
var Expected = baseURL + 'for-sale/searchresults.action';
QUnit.deepEqual(output,Expected,"For new Toggle button clicked");
});
Am getting the error " The function is undefined"
'undefined' is not a function (evaluating 'Sinon.once(BuildFormURLStub.withArgs(baseURL))')
TypeError: 'undefined' is not a function (evaluating 'Sinon.once(BuildFormURLStub.withArgs(baseURL))')
You need to pass in an object instead of the prototype:
var carSearchInstance = getInstanceSomehow();
var BuildFormURLStub = Sinon.stub(carSearchInstance , 'buildFormUrl');
Related
I have integrated some HTML/JS Code into my Vaadin WebApplication by creating an AbstractJavaScriptComponent. The Component almost works as intended.
How do I call the passInfo() method defined in the "connector.js" without having to manually click the Button defined in the innerHTML of the "chessControll.JsLabel" Component in "chessControll.js". My Goal is to pass Information, when the onChange Event of the init() function is called, which is located in the same file "chessControll.js", but not part of the Component.
I have already tried to create a Custom Event and then dispatch it whenever onChange() in the init() function is called, it worked as long as I didn't listen for the Event inside of my Component (chessControll.js, chessControll.JsLabel). It seems it can only be accessed in a static way.
How can I access the chessControll.JsLabel in "chessControll.js" from the init() function and then dispatch the button click or listen for events inside the component to achieve the same?
connector.js:
com_*myname*_*applicationName*_JsLabel = function() {
var mycomponent = new chessControll.JsLabel(this.getElement());
connector = this;
this.onStateChange = function() {
mycomponent = this.getState().boolState;
};
mycomponent.click = function() {
connector.passInfo(true);
};
};
chessControll.js:
var chessControll = chessControll || {};
chessControll.JsLabel = function (element) {
element.innerHTML =
"<input type='button' value='Click'/>";
// Getter and setter for the value property
this.getValue = function () {
return element.
getElementsByTagName("input")[0].value;
};
var button = element.getElementsByTagName("input")[0];
var self = this;
button.onclick = function () {
self.click();
};
};
var init = function() {
var onChange = function() {
/*Click Button defined in JsLabel Component */
};
};$(document).ready(init);
I figured out what the problem was.
The architecture of Java Web Applications doesn't allow a simple communication like i did in my example. The JavaScript made a call from the Client Side to the Server Side Vaadin Component.
I integrated the whole JavaScript, including the init function, as a Component. This way i can call the method from the init function because everything is known on the Server Side.
edited chessControll.js :
var chessControll = chessControll || {};
chessControll.JsLabel = function (element) {
element.innerHTML =
"<input type='button' value='Click'/>";
// Getter and setter for the value property
this.getValue = function () {
return element.
getElementsByTagName("input")[0].value;
};
var button = element.getElementsByTagName("input")[0];
var self = this;
//deleted bracket here
var init = function() {
var onChange = function() {
self.click();
};
};$(document).ready(init);
} //<-- that Simple
In my MainController I'd like to call a function from my ManualUploadController by pushing a button
The function call itself is working fine like this:
var oManualUploadController = new ManualUpload();
var backendData = oManualUploadController.onGenerateBackendData();
However, in the onGenerateBackendData() method, I retrieve models for example like this:
var oModel = this.getView().getModel('odataDetails');
What's funny is:
The ManualUploadController creates a Dialog with some logic in it and the MainController is the "mainpage" from where the dialog gets opened.
When initially accessing the mainpage and trying to push/execute that funciton, I get the error
"Uncaught TypeError: Cannot read property 'getModel' of undefined".
But when the Dialog has already been opened previously, then closed again and then the button on the mainpage gets pushed, the function call works fine without any errors.
Do I have to instantiate the controller somehow or how can I solve this problem?
If you own MainController and ManualUploadController, I'd suggest to put the function in MainController if you want to call it in both controller.
If you're using libraries, try to instantiate first.
But your problem is a bit complex.
If you want to get model, you can try to get it via Component instead.
If you want to solve the issue of failed to get view, you can try
var backendData = oManualUploadController.onGenerateBackendData().bind(this)
Trying the different suggestions of #Blangero I eventually came up with a solution that worked.
(the ".bind(this)" way unfortunately doesn't work. That would've been the sleekest solution)
So here is what I did:
I defined the manualDialogView outside of any function my controller:
sap.ui.define([
'workspace/controller/ManualUpload.controller',
],
function(ManualUpload) {
var oManualUploadController = new ManualUpload();
var manualDialogView = sap.ui.view({
viewName: "workspace.view.ManualDialog",
controller: oManualUploadController,
type: sap.ui.core.mvc.ViewType.XML
});
return BaseController.extend('workspace.controller.MainHeader', {
...
// rest of controller
...
}
Then I instantiated my Main view in onInit() and added the manualDialogView as a dependant:
onInit : function() {
var view = this.getView();
view.addDependent(manualDialogView);
},
Finally, opening my manualDialogView is in a separate function and the function call I'd like to get from the other controller is working fine in another function:
onOpenManualDialog: function(oEvent) {
var dialog = manualDialogView.byId("manualUploadDialog");
manualDialogView.callbackAPI = this.callbackAPI;
dialog.open();
},
onExportBackendTable: function() {
var backendData = oManualUploadController.onGenerateBackendData();
... not relevant code
},
If you have code that you want to use in multiple controllers I would suggest putting that logic in a new file/class. Pass the app component to the constructor, because it has access to all models defined in the manifest.json and the Component.js.
webapp/util/UploadHelper.js:
sap.ui.define([
"sap/ui/base/Object"
], function (BaseObject) {
"use strict";
return BaseObject.extend("workspace.util.UploadHelper", {
/**
* #param {sap.ui.core.UIComponent} oComponent reference to the app's component
*/
constructor: function (oComponent) {
this._oComponent = oComponent;
this._oResourceBundle = oComponent.getModel("i18n").getResourceBundle();
this._oModel = oComponent.getModel("odataDetails");
},
generateBackendData: function () {
// you can access this._oModel here and do stuff
return stuff;
}
});
});
Then in your controller you can do
webapp/controller/MainHeader.controller.js
sap.ui.define([
"workspace/controller/BaseController",
"workspace/util/UploadHelper"
],
function(BaseController, UploadHelper) {
"use strict";
return BaseController.extend('workspace.controller.MainHeader', {
onInit: function() {
// pass your component.js to the constructor
var oComponent = this.getOwnerComponent();
this._oUploadHelper = new UploadHelper(oComponent);
...
},
onExportBackendTable: function() {
var oBackendData = this._oUploadHelper.generateBackendData();
...
}
});
});
I have a function, is a function of polymer web component custom.
getListDedications: function(e){
var idDate = e.model.__data__.date.data.id;
var checkId = function(element){
return element.id == idDate;
};
var responseID = this.dedications.filter(checkId);
this.data = JSON.stringify(responseID[0].entries) || [];
console.log(JSON.stringify(responseID[0].entries) || []);
}
This function return a array or an array empty.
I want to test it, I'm using web-component-tester and I run the tests with gulp test:local.
I know that I need to mock e.model.__data__.date.data.id but I do not know how
Web component tester comes with sinon and sinon-chai bundled in it now.
You don't say what e.model._data__.date.date.id is. Obviously if its just data, you set it up and then call getListModifications with the a parameter. However, if its a function then use sinon stub (or spy) with
var sandbox;
beforeEach(function(){
sandbox = sinon.sandbox.create();
});
afterEach(function(){
sandbox.restore();
});
it('should ...',function(){
var e = sandbox.stub().returns('whatever data you want');
var answer = getListDedications(e);
expect(answer).to.be.an('Array');
});
I have a javascript code
Html5Template_300x250 = function(config) {
this.config = config;
var self = this;
adkit.onReady(this.init());
};
Html5Template_300x250.prototype = {
// Function That Creates Element Var
d: function(id) {
return document.getElementById(id);
},
// Initialize DCO HTML5 template
init: function() {
adkit.onReady(this.handleSVData);
},
handleSVData: function() {
var myData = adkit.getSVData("varName");
alert(myData);
this.startAd();
},
startAd: function(data) {
alert("test2");
}
}
In the above code i have used an external javascript adkit.js and using that method in my code. The initial method is started as
adkit.onReady(this.init());
It is calling a init function and which is then calling other methods including handleSVData which is getting a value from the json file which is in the root folder as
var myData = adkit.getSVData("varName");
The part of the code is working fine but after that line i am calling another method
this.startAd();
But this method is not working and i am getting error
TypeError: this.startAd is not a function
I am not good in javascript and giving me headaches can someone explain me why it is so complicated and what i am doing wrong here ??
When handleSVData is called by adkit it is called in the scope that is not an instance of Html5Template_300x250 - that is why this does not have startAd method.
As for adkit.onReady(this.init()); line.
adkit.onReady expects a function as a parameter. It stores this function variable and calls when it is time for onReady event. This is set correctly by adkit.onReady(this.handleSVData); line. this.init(), however, is a call to init function and your line adkit.onReady(this.init()); passes to adkit.onReady whatever init returns. But it does not return anything - you are passing undefined as parameter.
init: function() {
var template = this;
adkit.onReady(function(){
template.handleSVData();
});
},
And change line
adkit.onReady(this.init());
to
this.init();
I'm using require.js and have a library of functions I use in multiple places. I define the functions thusly:
define(function (require) {
"use strict";
var $ = require('jquery');
var UsefulFuncs = {};
UsefulFuncs.hideAlert = function() {
$('.alert').hide();
};
UsefulFuncs.loadURL = function (url){
navigator.app.loadUrl(url, { openExternal:true });
return false;
};
UsefulFuncs.linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
this.loadURL(url);
};
return UsefulFuncs;
});
Then, in my backbone view, I call a function from the library with:
UsefulFuncs = require('app/utils/useful_func'),
....
UsefulFuncs.linkClicked
This works fine for any standalone function in the library e.g. hideAlert(). However when one function in the library refers to another, such as linkClicked() calling loadURL(), I get an error:
Uncaught TypeError: Object [object Object] has no method 'loadURL'.
Any ideas how I can reference loadUrl()?
I would assume you set UsefulFuncs.linkClicked as a handler for an event.
If you were to use it like this:
UsefulFuncs.linkClicked()
, this inside the function would refer to UsefulFuncs, so this.loadURL() would be valid.
However, since you set it as a handler for an event, it can be called in other ways, and this is set to some other object (probably the element which triggered the event). To avoid the event handling mechanism changing your this, you can bind the function when you assign it:
element.onclick = UsefulFuncs.linkClicked.bind(UsefulFuncs);
Another way to go around it is to avoid using this in the handler, like so:
UsefulFuncs.linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
UsefulFuncs.loadURL(url);
};
This creates a closure over UsefulFuncs (the first method does as well, but it's more standardized).
Try something like this:
define(function (require) {
"use strict";
var $ = require('jquery');
var hideAlert = function() {
$('.alert').hide();
};
var loadURL = function (url){
navigator.app.loadUrl(url, { openExternal:true });
return false;
};
var linkClicked = function (e) {
e.preventDefault();
var url = $(e.currentTarget).attr("rel");
loadURL(url);
};
return {
hideAlert : hideAlert,
loadURL : loadURL,
linkClicked : linkClicked
};
});