How to unit test var variables - javascript

I test Angular application. This fact should not be of high importance here though.
My function to be tested looks like this:
$scope.showItem = function (item) {
if (item.selected) {
activeItems.push(item);
} else {
var index = _.indexOf(activeItems, items);
activeItems.splice(index, 1);
}
};
Function works as desired. Array activeItems is used in another function which after some modifications assigns the result to the scope.
The behavior is that if the item is not selected it is added to the array. If the item is already selected, it will be removed from the array.
it('should remove an item from the array', function () {
var activeItems = [{id: 1, selected: false}, {id:2, selected: true}];
var item = {
id: 1,
selected: false
};
expect(activeItems.length).toEqual(2);
scope.showItem(item);
expect(activeItems.length).toEqual(1); // FAIL!
// expects do not work for var variables. would work, if activeItems is assigned to the scope
});
If I assign activeItems to the scope (scope.activeItems instead of var activeItems), it all works; however, I believe that if the variable is not to be shown in the view, it should not be assigned to the scope.
First expect will work because is defined inside it block:
expect(activeItems.length).toEqual(2);
Second will not:
expect(activeItems.length).toEqual(1);
but it would for scope variable:
expect(scope.activeItems.length).toEqual(1);
My question is how to test that var value?

I'm not positive, as it's hard to tell your intentions, and if you have any other globals, but I think your error is that your using activeItems outside of it's scope., but again its hard to tell because you may have other globals causing the problem. If it is that activeItems is outside of it's scope, then it will reach an error at expect,scope, andexpect again. Then it won't effect the array, and it will stay as it was edited during showItem
Hope this helps!

Related

Attribute empty after assigning

I'm trying to refactor an object into an array to fit an API that I have to use.
function buildObject(data) {
console.log(data) // correct ouput
var jsonLayers = Object.entries({...data}).map(([k, v]) => {
console.log(`value : ${v}`) // correct output
return {
label: k,
pointMap: v,
color: v.layerColor?v.layerColor:tabList[tabList.map(i => i.tabName).indexOf(k)].layerColor?tabList[tabList.map(i => i.tabName).indexOf(k)].layerColor:defaultPOILayerColor,
}}
)
console.log(jsonLayers) // incorrect output
return jsonLayers
}
I have 2 scenarios in which I call this function but with the same data (console.log(data) logs the exact same result).
In the first scenario I obtain the desired result and in the second the pointMap attribute is an empty Array.
In the case where it does not work the console.log(`value : ${v}`) logs the correct value but when I log console.log(jsonLayers) the attribute pointMap is empty.
Any ideas on why this happens ?
Edit: as mentionned below this code works with sample data so I suppose this must comes to how the function is called. Everything runs in a UWA widget with jQuery
So for the context here is an idea of how they are called in both cases :
var data = {
a: { tabName: "tab1", layerColor: 1 },
b: { tabName: "tab2", layerColor: 2 },
};
$('#button1').on('click', function() {
ExternalAPI.Publish('some-external-address', buildObject(data));
});
$('#button2').on('click', function() {
let jsonData = buildObject(data);
//some manipulations of jsonData
ExternalAPI.Publish('some-external-address', jsonData);
});
It works on button1 but not on button2, they are clicked are at a different moment but data contains the same object when both are clicked
Edit2 :
Found the issue,
In the manipulations of jsonData I accidentally use slice(1,1) instead of splice(1,1) which empties the array.
What drove me crazy is that this modification was perform after the log but the var was log with the modification.
At least I learnt that spread operator does not deepcopy
Found the issue,
In the manipulations of jsonData I accidentally use slice(1,1) instead of splice(1,1) which empties the array.
What drove me crazy is that this modification was perform after the log but the var was log with the modification

vue.js data not updating when incremented

I have an object containing an array, which gets incremented after some amount of logic has completed.
Vue.js doesn't seem to be capturing this increment and displaying it to the view.
HTML:
<div id="demo">
<p>{{points}}</p>
</div>
JS:
function Board()
{
this.team1 = {pointsMade:[0]};
}
var newBoard = new Board();
newBoard.team1.pointsMade[0]++
new Vue({
el: '#demo',
data: {
points: newBoard.team1.pointsMade
}
})
setTimeout(newBoard.team1.pointsMade[0]++,1000)
I have a JSFiddle that outlines the problem.
You can see that after setTimeout(newBoard.team1.pointsMade[0]++,1000) runs, the value should be '2', but is only displayed at '1'. What am I missing here?
When you define
data: {
points: newBoard.team1.pointsMade[0]
}
the points variable is just assigned current value of newBoard.team1.pointsMade[0], which is 1 at this moment. There is no magic here. JS primitives works by value, not by references.
So, after updating the newBoard.team1.pointsMade[0] variable, the point variable is not updating of course.
To make this work, use object instead of primitive value. Objects work by reference in JS.
From Properties and Methods example:
var data = { a: 1 }
var vm = new Vue({
data: data
})
vm.a === data.a // -> true
// setting the property also affects original data
vm.a = 2
data.a // -> 2
// ... and vice-versa
data.a = 3
vm.a // -> 3
edit
There is another caveat here:
Due to limitations in JavaScript, Vue cannot detect when you directly set an item with the index, e.g. vm.items[indexOfItem] = newValue. So, use Vue.set(newBoard.team1.pointsMade, 0, newBoard.team1.pointsMade[0] + 1);.
I updated your fiddle.

Why is length of array not displaying in browser?

Code:
initialize: function() {
this.todos = [
{id: 100, text: 'Rich'},
{id: 200, text: 'Dave'}
];
},
activeTodos: function() {
this.todos = this.todos.length(function() {
return this.todos;
});
this.emitChange();
}
<p>Todo's Remaining: {this.activeTodos} </p>
activeItems: function(){
this.context.executeAction(activeToDosAction);
},
Explanation:
I am trying to print out the size of the array to the browser window (this can be seen in the <p> tags within the code). So far nothing is displaying and I cant figure out why. activeTodos should be calling the length of todos.
i can post more code if people require it. i am using reactjs hence the { } brackets within HTML
There are a couple of weird things there. If you only need to display the length of this.todos you can do something like this:
<p>Todo's Remaining: {this.todos.length} </p>
I think the problem here is that you pass a function to length. That is not required.
Your code should work if you change it to:
activeTodos: function() {
return this.todos.length;
}
I'm not sure what your function emitChange should be doing, so I omitted it for now in the code example. If you need it to be called, put it before the return as that is the last thing that will run in your function.
First, to access the length of an array you just have to do
myArray = [1, 2, 3];
myArray.length; //3
Moreover you have 2 fonctions : initialize and activeTodos where you use this.todos = ...
Without further explanations I assume these variables created with the keyword 'this' inside 2 different functions are out of score.
Verify that your this.todos refers to the same thing.
You do not need active.Todos() as .length is a built in function by itself.
Just do
Todo's Remaining: {this.todos.length}
Cheers.

PolymerJS: How to Update all Items in an Array with set() and a forEach loop properly

I have an array that looks like this:
items: [
{title: 'First Title', completed: false},
{title: 'Second Title', completed: false},
{title: 'Third Title', completed: false}
];
I'd like to set each item to true. For this I have a button that fires an event on-tap that executes the following code snippets.
The Polymer team sets the Boolean value with a for loop:
for (var i = 0; i < this.items.length; ++i) {
this.set(['items', i, 'completed'], true);
}
I personally prefer to use a forEach loop, because I'd like to compare Polymer to different frameworks and it happens to happen that I am using forEach loops in similar cases.
My working solution:
var that = this;
this.items.forEach(function(item) {
var i = that.items.indexOf(item);
that.set('items.' + i + '.completed', true);
// or
// that.set(['items', i, 'completed'], true);
});
Specifically the part where I use dots to connect with i seems hacky to me.
Same code with Vue:
this.items.forEach(function(item) {
return item.completed = true;
});
The Polymer API states:
set(path, value, root) path (string|Array<(string|number)>)
Path to the value to read. The path may be specified as a string (e.g. foo.bar.baz) or an array of path parts (e.g. ['foo.bar', 'baz']). Note that bracketed expressions are not supported; string-based path parts must be separated by dots. Note that when dereferencing array indicies, the index may be used as a dotted part directly (e.g. users.12.name or ['users', 12, 'name']).
value *
Value to set at the specified path.
root Object=
Root object from which the path is evaluated.
Question:
Because the part where I use an index seems just a bit hacky for a lack of a better term, I wonder, if there is a more convenient way to use a forEach loop in Polymer to update all items in the Array.
Thanks.
forEach's callback function can have a second parameter that refers to the current index. This also goes for Array's map, every, some, and filter methods.
ES5 Version
this.items.forEach(function(item, index) {
this.set('items.'+index+'.completed', true);
}.bind(this));
ES6 Version
this.items.forEach((item, index) => this.set(`items.${index}.completed`, true));
Source: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Parameters

angularjs function failing because of extra $$hashKey

I have the below HTML:
<li ng-click="toggleBeep(beep)" ng-class-odd="'gradient-two'"
ng-class-even="'gradient-three'" ng-repeat="beep in beeps">
<span>{{beep.name}}</span>
<label class="bold" ng-show="isSelected(beep)">selected</label>
</li>
JavaScript (AngularJS):
$scope.beeps = $sounds.getAll();
// get stored beep from localStorage
var notification_beep =
angular.fromJson(localStorage.getItem('notification_beep'));
console.log($scope.beeps[0]);
console.log(notification_beep);
// handle change sound on click event
$scope.toggleBeep = function (beep) {
$cbSounds.play(beep.file);
$scope.selected = beep;
localStorage.setItem('notification_beep', angular.toJson(beep));
};
$scope.isSelected = function (beep) {
return $scope.selected === beep;
};
Now, when I click on any li I get the selected label is shown because of the $scope.isSelected function. However, when I try to add this line $scope.selected = notification_beep which is the beep object stored in the localStorage the label is not shown and I get the below return values.
The only difference I could spot is that $$hashkey is present on $scope.beeps[0] while it's not on notification_beep. Could this be the cause? Thanks.
The following comparison:
$scope.selected === beep
Will only return true if the two variables reference the same object.
The following line will create a new object:
var notification_beep = angular.fromJson(localStorage.getItem('notification_beep'));
So it will not reference the same object as $scope.selected.
To clarify, this will return false: { name: 'Beep 1' } === { name: 'Beep 1' }
The simplest solution is to instead compare against a unique primtive of the objects.
For example:
return $scope.selected.name === beep.name;
The $$hashkey property is inserted into the object by ng-repeat and is used to track which object corresponds to which DOM template. The reason it doesn't exist in notification_beep is because angular.toJson removes the property from the object.

Categories