How to unit test subfunctions in QUnit? - javascript

I have the following code in my SAPUI5 app.
This function is trigger when a button is clicked.
onClear(oEvent){
var source = oEvent.getSource().getText(); //get the text on the button
....
}
In my Unit test I have
QUnit.module('onClear functio test);
Qunit.test('onClear press test', function(assert){
var getSource = sinon.spy();
var getText = sinon.spy();
var oEvent = {getSsource: getSource};
var source = "Clear"; //this is what should be return for this test
//call the function
mainController.onClear(oEvent, source);
}
The error I get is "Unable to get property 'getText' of undefined or null reference"
From SAP API, oEvent.getSource() simply returns a reference to the object triggering the event, in this case a button. During the testing .getSource() doesn't return anything I believe.
So how can make this work? I know I can break down my code into 2 different lines and spy/stub those 2 functions separately but I don't want to do that as the code I have is valid JavaScript code.

Related

TypeError: Undefined is not a constuctor (evaluating 'selectList.append(options)')

I am getting this error when I run the test Cases (Jasmine):
TypeError: Undefined is not a constuctor (evaluating 'selectList.append(options)')
I am not sure why a few of my test cases are failing because of this. The tests in my browser run absolutely fine but failing locally (Maven test). (Using Jasmine, Phantom JS).
Here I have an editUser button, which when clicked hides and (update and cancel) buttons pop up which are hidden until then.
function userEditUser(id) {
var grandParent = $('#updateUser' + id).parent().parent().parent();
var updateUser = $('#updateUser' + id);
updateUser.siblings().removeClass('hideElement');
updateUser.addClass('hideElement');
var column_authority = document.getElementById('name1');
var name_list = ["A", "B", "C"];
var selectList = document.createElement('select');
selectList.setAttribute('name', 'userName');
for(var i = 0; i <name_list.length; i++){
var options = document.createElement('option');
options.text=name_list[i];
selectList.append(options);
}
column_authority.appendChild(selectList);
One of the Test Cases that is failing:
it('update button should not have a css class hideElement', function () {
$('button[name="updateUser"]').click();
expect($('button[name="updateBtn"]')).not.toHaveClass('hideElement');
});
But it is failing at this point of the function:
selectList.append(options)
I guess Phantom JS does not recognize append function. Things are working fine for me now,
all I did is changed
selectList.append(options); to
selectList.appendChild(options);
This change answered all my failing test cases.
Thank you guys very much for your interest in providing a solution for my issue.

Test individual line in javascript unit testing

I have a javascript function like this
function formatInput(input) {
//want to test only this immediate statement
var type = input.ipType.toString().toLowerCase().trim();
var afterVormat = someFunction(type);
return afterFormat;
}
I am able to test this function(value of afterFormat) correctly , but is it possible/how to test a specific line in function since I am not returning type.
For example I want to test if var type is as it is expected
Is it possible/how to test a specific line in function?
The immediate answer: no.
The solution
One of the outcomes of adhering to TDD is that it forces you to build code in isolated, testable blocks. This is a direct consequence of the fact that you cannot perform test(s) of the individual lines of a function. In your case the solution is to restructure your code to:
var type = function(){
return input.ipType.toString().toLowercase().trim();
};
function formatInput(input) {
var type2 = type();
var afterVormat = someFunction(type);
return afterFormat;
}
Now you have made type an isolated block that you can test.
If you combine this with use of Sinon.JS you can use a spy to test that an invocation of function formatInput() will also result in the invocation of type() and thereby you know for sure that var type2 has been assigned the intended value.
I’m not aware of any specific and more advanced unit testing method/system for javascript, but you can have a simple assertion function to test individual lines of code for debugging purpose like this:
function assert(condition, message) {
if (!condition) {
message = message || "Assertion failed";
if (typeof Error !== "undefined") {
throw new Error(message);
}
throw message; // Fallback
}
}
(Code taken from TJ Crowder's answer to another question.)
Then you can just use it to check for instance the var type like this:
assert(type == "something expected here and shall throw an error otherwise");
You can use console.log() function for that. As below.
function formatInput(input) {
var type = input.ipType.toString().toLowerCase().trim();
console.log(type);
var afterVormat = someFunction(type);
return afterFormat;
}
Also you can use debugger; also, to debug the code line by line.
function formatInput(input) {
var type = input.ipType.toString().toLowerCase().trim();
debugger;
var afterVormat = someFunction(type);
return afterFormat;
}
and just press F10 key to debug the code and you can check the values in console.

"this" value that can not be converted to type Ui

I'm using Google Apps Script and I'm trying to re-use code that calls render so that I don't have to re-type everything. I'm running into an issue that seems to relate to "this" getting transformed.
Code:
function render(displayMedium, template_name) {
var js = HtmlService.createTemplateFromFile('javascript.html');
var css = HtmlService.createTemplateFromFile('stylesheet.html');
var t = HtmlService.createTemplateFromFile(template_name);
js.config = config;
css.config = config;
t.config = config;
t.appProperties = appProperties;
js.appProperties = appProperties;
t.jsBlock = js.evaluate().getContent();
t.cssBlock = css.evaluate().getContent();
displayMedium(
HtmlService
.createHtmlOutput(t.evaluate())
.setSandboxMode(HtmlService.SandboxMode.NATIVE)
};
function renderSidebar(){
var displayMedium = DocumentApp.getUi().showSidebar;
var template_name = "app.html";
render(displayMedium, template_name);
};
And when I call renderSidebar() the error I get is as follows:
[ERROR: InternalError: Method "showSidebar" was invoked with [object Object] as "this" value that can not be converted to type Ui.
Any ideas how to fix this?
It should work if you bind the function to the UI object:
var displayMedium = DocumentApp.getUi().showSidebar.bind(DocumentApp.getUi());
When you grab that function reference, then unless you do something like the above the runtime will (or might; it clearly does here) invoke the function with some other this value (depends on the API).

ExternalInterface.call not working

I am attempting to ExternalInterface.call() from inside my SWF.
Note the call comes from inside a SWF I have embedded into another SWF (the _root SWF I have no control over).
Here is my JavaScript:
function player_DoFSCommand(command, args)
{
args = String(args);
command = String(command);
var arrArgs = args.split(g_strDelim);
switch (command)
{
case "CC_ClosePlayer":
console.log("yo");
break;
default:
// alert(command);
break;
}
}
Here is my AS2 code:
import flash.external.ExternalInterface;
var quiz = _root;
quiz.g_mcFrame.mcFinish.swapDepths(quiz.getNextHighestDepth());
quiz.g_mcFrame.mcFinish._visible = false;
quiz.oSlide.m_oInteraction.m_oVariableMgr.m_arrBoolResumeData = false;
var arrVars:Object = quiz.oSlide.m_oActionHandler.m_oFrame.m_oVariableMgr.m_arrVariables;
var args:Array = [
arrVars[2].m_nNumber, // Points awarded
arrVars[3].m_nNumber, // Max points
arrVars[5].m_nNumber, // Pass percentage
arrVars[6].m_nNumber, // Pass points
arrVars[7].m_nNumber, // Score percent
arrVars[10].m_strString // Result
];
ExternalInterface.call('player_DoFSCommand("CC_ClosePlayer", args)');
ExternalInterface.call('console.log("hello")');
The problem is ExternalInterface.call('player_DoFSCommand("CC_ClosePlayer", args)') doesn't work, or atleast "yo" doesn't appear in the console as expected.
The script is definitely loaded by the time the ExternalInterface is called. The second call works and "hello" appears in the console, however the first function doesn't fire.
I can verify the function works by typing directly into the console player_DoFSCommand("CC_ClosePlayer", args) and it logs successfully.
Can you verify that allowScriptAccess is set to true in the embed code?
Try this:
var isAvailable:Boolean = ExternalInterface.available;
trace(isAvailable);
You also want to use ExternalInterface like this, with your arguments passed as the second variable:
ExternalInterface.call("player_DoFSCommand",args);
or
ExternalInterface.call("console.log","testing...");

Linked select boxes coming back 'sel.selectedIndex undefined' on second select area... why?

I've got chained/linked select boxes. Everything works fine if the first select box is chosen, then the second, etc. However. What I want to do is when someone changes the first select area, the second is autopopulated, and the third is also autopopulated based on the first option in the second box.
Here is the code I'm using so far:
function loadTypes(sel)
{
var whichCurr = sel.options[sel.selectedIndex].value;
if(whichCurr.length>0){
var theIndex2 = newAjax2.length;
newAjax2[theIndex2] = new sack();
newAjax2[theIndex2].requestFile = 'getTypes.php?theType='+whichCurr; // Specifying which file to get
newAjax2[theIndex2].onCompletion = function(){ createTypes(theIndex2) }; // Specify function that will be executed after file has been found
newAjax2[theIndex2].runAJAX(); // Execute AJAX function
}
}
function createTypes(theIndex2)
{
var obj3 = document.getElementById('types');
document.getElementById('types').options.length = 0; // Empty city select box
eval(newAjax2[theIndex2].response); // Executing the response from Ajax as Javascript code
getModelList('types');
}
You'll notice at the end of createTypes, the function calls getModelList() which is the function that is called when the SECOND select box is called. It (getModelList()) works fine when manually changing the second box, but when I try to call it from createTypes, it just won't work. Here's the code for getModelList:
function getModelList(sel)
{
var manuCode = sel.options[sel.selectedIndex].value;
var mytext = manuCode.length;
if(manuCode.length>0){
var index = ajax.length;
ajax[index] = new sack();
ajax[index].requestFile = 'getModels.php?manuCode='+manuCode; // Specifying which file to get
ajax[index].onCompletion = function(){ createModelList(index) }; // Specify function that will be executed after file has been found
ajax[index].runAJAX(); // Execute AJAX function
}
}
function createModelList(index)
{
var obj = document.getElementById('sub_types');
$numOpts=(document.getElementById('sub_types').length);
if($numOpts>1){
document.getElementById('sub_types').options.length = 0; // Empty select box
eval(ajax[index].response); // Executing the response from Ajax as Javascript code
$num_of_entries=(document.getElementById('sub_types').length);
if($num_of_entries>1){
}
else
{
hidediv('p_sub_types');
}
}
else
{
document.getElementById('sub_types').options.length = 0; // Empty select box
eval(ajax[index].response); // Executing the response from Ajax as Javascript code
$num_of_entries=(document.getElementById('sub_types').length);
if($num_of_entries>1){
showdiv('p_sub_types');
}
else
{
}
}
}
Again, everything works fine when MANUALLY changing the select box. But when I try to automatically call getModelList() from createTypes() I get an error in Firebug saying: sel.selectedIndex is undefined.
So I'm guessing that it is trying to call that before the select box has been populated... however, I tried adding a pause (of up to 2 seconds!) before calling the function and the same thing happens.
I believe your issue lies in your call to getModelList() at the end of createTypes().
When you're calling getModelList() here you are passing it the string "types" rather than the "types" element (which you've already acquired as "obj3") which it is expecting.
The simplest solution should be to alter your createTypes function to look more like:
function createTypes(theIndex2)
{
var obj3 = document.getElementById('types');
obj3.options.length = 0; // Empty city select box
eval(newAjax2[theIndex2].response); // Executing the response from Ajax as Javascript code
getModelList(obj3);
}
By passing getModelList the element it will have direct access to its properties and should allow your script to function properly.

Categories