I am new to react and not able to find how can i get the code coverage done for my below mentioned function using jest.
For the sake of clarity I have created a function just similar to what I am using :
setName: function setName () {
var storage = localStorage.getItem(user);
var session = sessionStorage.getItem(temp);
if ((session === null || session === undefined) && (storage === null && storage === undefined)) {
var name = "window.name";
if (name === 'John') {
changeName(name);
} else if (name === 'Jane') {
changeName(name);
} else if (name === 'Mark') {
changeName(name);
} else {
changeName('John');
}
} else if (session !== null) {
changeName(session);
} else if (storage !== null) {
changeName(storage);
}
}
Any help is appreciated
You need to be able to specify the name 'John', 'Jane', etc. To do this with your sample you will need to save the appropriate data into localStorage and sessionStorage before running the test.
In your test file:
localStorage.setItem('John');
sessionStorage.setItem('A Session');
// do the test now and test what you expect to get back when the user is John.
localStorage.setItem('Jane');
// do the test now and test what you expect to get back when the user is Jane.
The tests could be done with a loop and arrays if the results being tested for are basic and you can simply do a foreach or similar.
testData = [
{ Name: 'John', Result: true},
{ Name: 'Jane', Result: false}
];
testData.forEach(function(testItem) {
expect(setName(testItem.Name)).toBe(testItem.Result);
}
Another option is to move the access to local and session storage out of your function setName() and then use dependency injection or other functions in your code to get these values. Then you can set up a mock (or dummy function) for returning the data for the storage which returns hard set literal data to test each part of the if() structure.
Related
I've been trying to put a custom experiment made with jsPsych, a library for running behavioral experiments, with custom plugins into a React container but i've run into some issues.
The first thing i tried to do was use makebrainwave's jspsych-react package, but when i try to run the example, after having to change the routes of the files in the import section and replacing the use of 'fs', i'm still running into this error:
Trial level node is missing the "type" parameter. The parameters for the node are: {} experiment.js:924
TypeError: jsPsych.plugins[trial.type] is undefined[Learn More] experiment.js:1051
Could anyone help me with it? It looks like it's trying to initialize something and failing, but otherwise I'm kinda lost in the error.
The first error occurs in the constructor function:
// constructor
var _construct = function() {
// store a link to the parent of this node
parent_node = parent;
// create the ID for this node
if (typeof parent == 'undefined') {
relative_id = 0;
} else {
relative_id = relativeID;
}
// check if there is a timeline parameter
// if there is, then this node has its own timeline
if ((typeof parameters.timeline !== 'undefined') || (typeof jsPsych.plugins[trial_type] == 'function')) {
// create timeline properties
timeline_parameters = {
timeline: [],
loop_function: parameters.loop_function,
conditional_function: parameters.conditional_function,
sample: parameters.sample,
randomize_order: typeof parameters.randomize_order == 'undefined' ? false : parameters.randomize_order,
repetitions: typeof parameters.repetitions == 'undefined' ? 1 : parameters.repetitions,
timeline_variables: typeof parameters.timeline_variables == 'undefined' ? [{}] : parameters.timeline_variables
};
self.setTimelineVariablesOrder();
// extract all of the node level data and parameters
var node_data = Object.assign({}, parameters);
delete node_data.timeline;
delete node_data.conditional_function;
delete node_data.loop_function;
delete node_data.randomize_order;
delete node_data.repetitions;
delete node_data.timeline_variables;
delete node_data.sample;
node_trial_data = node_data; // store for later...
// create a TimelineNode for each element in the timeline
for (var i = 0; i < parameters.timeline.length; i++) {
timeline_parameters.timeline.push(new TimelineNode(Object.assign({}, node_data, parameters.timeline[i]), self, i));
}
}
// if there is no timeline parameter, then this node is a trial node
else {
// check to see if a valid trial type is defined
var trial_type = parameters.type;
if (typeof trial_type == 'undefined') {
console.error('Trial level node is missing the "type" parameter. The parameters for the node are: ' + JSON.stringify(parameters));
} else if ((typeof jsPsych.plugins[trial_type] == 'undefined') && (trial_type.toString().replace(/\s/g,'') != "function(){returntimeline.timelineVariable(varname);}")) {
console.error('No plugin loaded for trials of type "' + trial_type + '"');
}
// create a deep copy of the parameters for the trial
trial_parameters = Object.assign({}, parameters);
}
}();
And the second one in the first line of the folowing function
function setDefaultValues(trial){
var trial_parameters = Object.keys(jsPsych.plugins[trial.type].info.parameters);
for(var i=0; i<trial_parameters.length; i++){
if(typeof trial[trial_parameters[i]] == 'undefined' || trial[trial_parameters[i]] === null){
if(typeof jsPsych.plugins[trial.type].info.parameters[trial_parameters[i]].default == 'undefined'){
console.error('You must specify a value for the '+trial_parameters[i]+' parameter in the '+trial.type+' plugin.');
} else {
trial[trial_parameters[i]] = jsPsych.plugins[trial.type].info.parameters[trial_parameters[i]].default;
}
}
}
}
I installed jspsych-react with yarn into the project and the test container is the following:
import React, { Component } from 'react'
import { Experiment } from "jspsych-react";
import { visualOddball } from "./examples/timelines/visualOddball";
import { callbackHTMLDisplay } from "./examples/plugins/callbackHTMLDisplay";
import { callbackImageDisplay } from "./examples/plugins/callbackImageDisplay";
export default class ExperimentComponent extends Component {
render() {
return (
<div>
<Experiment
settings={{ timeline: visualOddball }}
plugins={{
"callback-html-display": callbackHTMLDisplay,
"callback-image-display": callbackImageDisplay
}}
/>
</div>
);
}
}
Has anyone integrated something similar without using the jspsych-react package? which approach did you take?
Thanks in advance!
I have two tabs in my admin interface. I am storing the response in my session storage. When I do the updation of any records in the tab or if I insert new record also, the same thing should be reflected in the storage also. But currently, the changes are not getting reflected in the storage. I tried my best to sort out, but I could not able to succeed. Any help/advice greatly appreciated.
Angularjs:
$scope.Pool = [];
if (!localStorageService.get('Pool')) {
Role.getPool().success(function(data) {
if (data.responseCode === 0) {
_.forEach(data.response.demoPool, function(value, key) {
dataObj = {};
dataObj.id = value.poolId;
dataObj.value = value.poolName;
$scope.Pool.push(dataObj);
});
localStorageService.set('Pool', $scope.Pool);
} else {
$scope.alerts.alert = true;
$scope.alerts.type = 'danger';
$scope.alerts.msg = data.errorMsg;
}
})
First time it will do because !localStorageService.get('Pool') becomes true. But next time it will return false because storage has value already, it will not get inside the if condition. so to resolve this remove the session storage 'Pool' to allow to execute your Role.getPool().success(function(data) {
if (!sessionStorage.length) {
// Ask other tabs for session storage
localStorage.setItem('getSessionStorage', Date.now());
};
window.addEventListener('storage', function (event) {
switch (event.key) {
case 'getSessionStorage':
// Some tab asked for the sessionStorage -> send it
localStorage.setItem('sessionStorage', JSON.stringify(sessionStorage));
localStorage.removeItem('sessionStorage');
break;
case 'sessionStorage':
// sessionStorage is empty -> fill it
var data = JSON.parse(event.newValue);
for (key in data) {
sessionStorage.setItem(key, data[key]);
}
break;
}
});
I have a scope array called $scope.groups
$scope.groups = [{
id: 1,
name: "Group 1"
},
{
id: 2,
name: "Group 2"
}]
When I updated one of the groups I need to check if that updated group exists in the groups array however when i filter the array it checks the group i need to update as well so it outputs "Group exists".
function ifGroupExists(GroupName,GroupId) {
var match;
match = $scope.groups.filter(function (item) { return angular.lowercase(item.name) === angular.lowercase(GroupName); });
if (match.length > 0) {
console.log("group exists");
return true;
}
else {
console.log("group does not exists");
return false;
}
}
This code works if im adding a totally new group to the array however how do i edit this so that it doesnt check the group currently being updated and have it so it only checks the other groups to see if there is a match.
Can someone help? Im sure there is a simple way to do this. Just cant seem to figure it out..
It can be simple like, just pass the currently updating group to function as well, and ignore it in the filter.
Code should be like :
function ifGroupExists(groupName_filter, groupName_current) {
var match;
match = $scope.groups.filter(function (item) {
return (angular.lowercase(item.name) === angular.lowercase(groupName_filter) &&
angular.lowercase(item.name) !== angular.lowercase(groupName_current));
});
return (match.length > 0);
}
and I hope you need console.log only during development :), so return can be simplified.
Further: If you have groupName_current as $scope property, then use it directly, no need to pass.
In my users profile collection I have array with image objects in it.
A user can have a max of 3 images in their profile collection. If the user has 3, throw an error that the maximum has been reached. The user has the option to remove an image themselves in the frontend.
I thought the solution would be to check the length of the array with $size. if it's less then 3, insert the image, else throw error.
I'm using the tomi:upload-jquery package.
client:
Template.uploadImage.helpers({
uploadUserData: function() {
return Meteor.user();
},
finishUpload: function() {
return {
finished: function(index, fileInfo, context) {
Meteor.call('insert.profileImage', fileInfo, function(error, userId) {
if (error) {
// todo: display modal with error
return console.log(error.reason);
} else {
// console.log('success ' +userId);
// console.log('success ' + fileInfo);
}
});
}
};
}
});
The method (server) I use:
'insert.profileImage': function(postImage) {
check(postImage, Object);
// check array profile.images max 3
Meteor.users.update(this.userId, {
$push: {
'profile.images': postImage
}
});
},
You may do it with a function using the $where operator:
'insert.profileImage': function(postImage) {
var updateResults;
check(postImage, Object);
updateResults = Meteor.users.update(
{
_id : this.userId,
$where : 'this.profile.images.length < 3' //'this' is the tested doc
},
{
$push: {
'profile.images': postImage
}
});
if(updateResults === 0) {
throw new Meteor.Error('too-many-profile-images',
'A user can only have up to 3 images on his/her profile');
}
},
The Mongo docs warns about potential performance issues (if you run a JavaScript function on all documents of the store, you're in for bad surprises) but since we also search by _id I guess it should be fine.
This way, the update just doesn't run if the user has too many images. You can also check the number of affected document (the return value of the update) to know if something happened. If nothing (returns 0) happened, there's not many possibilities: The user has too many images.
Use the $exists operator to check the existence of all documents that have at least a fourth profile image array element (index position 3) with the dot notation. For example you could use it to check whether the size of the profile.image array is greater than 3 with the find() method as follows:
var hasSizeGreaterThanThree = Meteor.users.find(
{
'_id': this.userId,
'profile.image.3': { '$exists': true }
}).count() > 0;
So you could use that in your code as:
'insert.profileImage': function(postImage) {
check(postImage, Object);
// check array profile.images max 3
var hasSizeGreaterThanThree = Meteor.users.find(
{
'_id': this.userId,
'profile.image.3': { '$exists': true }
}).count() > 0;
if (!hasSizeGreaterThanThree){
Meteor.users.update(this.userId, {
$push: {
'profile.images': postImage
}
});
}
},
I'm using Windows Azure Mobile Service to build the backend for my app. For server script's read operation, now I want to retrieve the query parameter like $filter, $select in the script, etc. Any idea?
After hacking around with the 'query' object in the 'read' function's parameter (by using console.log ), I finally found the solution:
function isObject(variable) {
return variable !== null &&
variable !== undefined &&
typeof variable === 'object';
}
// Find all the member-value pairs from the expression object
function findMemberValuePairsFromExpression (expr, ret) {
if (!isObject(expr)) {
return null;
}
ret = ret || {};
for (var name in expr) {
if (expr.hasOwnProperty(name)) {
var prop = expr[name];
if (name === 'parent') { // Ignore parent property since it's added by us
continue;
}
else if (name === 'left') { // member expression are in the left subtree
if (isObject(prop)) {
prop.parent = expr; // Remember the parent
findMemberValuePairsFromExpression(prop, ret);
}
}
else if (name === 'member') {
// Found a member expression, find the value expression
// by the knowledge of the structure of the expression
var value = expr.parent.right.value;
ret[prop] = value;
}
}
}
if (expr.parent) {
// Remove the added parent property
delete expr.parent;
}
return ret;
}
// Get the filters component from query object and
// find the member-value pairs in it
function findMemberValuePairsFromQuery (query) {
var filters = query.getComponents().filters;
return findMemberValuePairsFromExpression(filters);
}
function read (query, user, request) {
request.execute();
}
Remember that this approach heavily relies on the inner structure of the query object so it may break in the future.
query.getComponents() also returns other parts of the query, like 'select', 'skip', 'top', etc. Basically anything of the oData protocol