Yeoman helpers mockPrompt default values - unit-testing

I'm writing unit tests for my yeoman generator.
I've noticed that mockPrompt does not use default values for my prompt values.
For instance, I have a version parameter that defaults to 0.0.1, but unless I specify a value for version in my test, the result is an empty string.
Is this by design or am I doing something wrong.
This is my prompt function from index.js:
var prompts = [{
name: 'libName',
message: 'What do you want to call your lib?'
},{
name: 'libVersion',
message: 'What version would you like to set?',
default: "0.0.1"
},{
name: 'libDesc',
message: 'Describe your lib:'
},{
name: 'authorName',
message: 'What is your full name?'
},{
name: 'angularVersion',
message: 'Enter angular version:',
default: '1.2.7'
},{
name: 'ngResourceRequired',
message: 'Do you require ngResource module?',
type:'confirm',
default: false
}];
this.prompt(prompts, function (props) {
this.libName = props.libName;
this.libVersion = props.libVersion;
this.libDesc = props.libDesc;
this.authorName = props.authorName;
this.angularVersion = props.angularVersion;
this.ngResourceRequired = props.ngResourceRequired;
cb();
}.bind(this));
And this is my test code:
describe('my-jslib:app', function () {
var jslib;
var appPath = 'customAppPath';
var expected = [
appPath + '/.htaccess',
appPath + '/404.html',
appPath + '/favicon.ico',
appPath + '/robots.txt',
appPath + '/styles/main.scss',
appPath + '/views/main.html',
appPath + '/index.html',
'.bowerrc',
'.editorconfig',
'.gitignore',
'.jshintrc',
'Gruntfile.js',
'package.json',
'bower.json'
];
var mockPrompts = {
libName: "test",
//libVersion: "0.0.1",
angularVersion: "1.2.7",
ngResourceRequired: false
};
var genOptions = {
'appPath': appPath,
'skip-install': true,
'skip-welcome-message': true,
'skip-message': true
};
beforeEach(function (done) {
helpers.testDirectory(path.join(__dirname, 'tmp'), function (err) {
if (err) {
done(err);
}
jslib = helpers.createGenerator(
'my-jslib:app', [
'../../app', [
helpers.createDummyGenerator(), 'mocha:app'
]
],
false,
genOptions
);
helpers.mockPrompt(jslib, mockPrompts);
done();
});
});
it('creates expected files', function (done) {
var expected = [
'.bowerrc',
'.editorconfig',
'.gitignore',
'.gitattributes',
'.jshintrc',
'bower.json',
'Gruntfile.js',
'package.json',
'README.md',
'src/' + mockPrompts.libName + '.js',
'test/spec/' + mockPrompts.libName + '.js',
'test/.jshintrcs',
'test/karma.conf.js'
];
jslib.run({}, function () {
helpers.assertFile(expected);
helpers.assertFileContent('package.json', new RegExp('"name": "' + mockPrompts.libName + '"'));
helpers.assertFileContent('bower.json', new RegExp('"name": "' + mockPrompts.libName + '"'));
helpers.assertFileContent('bower.json', new RegExp('"angular": "' + mockPrompts.angularVersion + '"'));
helpers.assertNoFileContent('bower.json', new RegExp('"angular-resource": "' + mockPrompts.angularVersion + '"'));
done();
});
});
});
Thanks,
Lior

To mock the default prompt you have to omit the variable in the object of the mock.
var mockPrompts = {
libName: "test",
angularVersion: "1.2.7",
ngResourceRequired: false
};
This will trigger the default value for "libVersion"

Related

Postman - taking attributes and values from one JSON to create another JSON

This in relation to my older ticket here!
But now I need to get more advanced. I need to get only certain attributes and their values (from the 1st Response JSON) to create another JSON (and some values to be replaced in new JSON).
I.e. in the Response JSON below, I need to go through all "Items" (and I do not know in advance how many those can be), and get only Id + value, and Status+value for each of them. Then to build another new JSON, where the top attribute will be called Products instead of Variations, and contain only Id+value and Status+*NewValue*.
1st Response JSON:
"Variations":[
{
"ItemIds":[
"xxx"
],
"Items":[
{
"Id":"67-V1",
"GuId":"xxx",
"Type":"Unit",
"Status":"Active"
}
],
"Name":"VAR 1",
"Id":"67-V1"
},
{
"ItemIds":[
"yyy"
],
"Items":[
{
"Id":"67-V2",
"GuId":"yyy",
"Type":"Unit",
"Status":"Active"
}
],
"Name":"VAR 2",
"Id":"67-V2"
},
{
"ItemIds":[
"zzz"
],
"Items":[
{
"Id":"67-V3",
"GuId":"zzz",
"Type":"Unit",
"Status":"Active"
}
],
"Name":"VAR 3",
"Id":"67-V3"
}
],
"ItemIds":[
],
"Items":[
],
"Name":"MAINP",
"Id":"67",
"Color":null
}
New JSON I would like to create:
{
"Products":[
{
"Id":"67-V1",
"Status":"NonActive"
},
{
"Id":"67-V2",
"Status":"NonActive"
},
{
"Id":"67-V3",
"Status":"NonActive"
}
]
}
I have made following code:
var jsonData = pm.response.json();
var newData;
var newDataGroup = "";
function replaceValues() {
_.each(jsonData.Variations, (arrayItem) => {
_.each(arrayItem.Items, (item) => {
if(item.Status !== "NonActive") {
item.Status = "NonActive";
console.log("arrayItem " + item.Status);
}
newData = "{ \"Id\":\"" + item.Id + "\", " + " \"Status\":\"" + item.Status + "\"},";
newDataGroup = newDataGroup + newData;
});
});
newDataGroup = "{ \"Products\": [ " + newDataGroup + " ] }";
}
pm.test("Run Replace", replaceValues ());
console.log("newDataGroup Final: " + newDataGroup);
var newJson = JSON.parse(newDataGroup);
console.log("newJson: " + newJson);
Here is my outcome (based on the code above), but how to deal with the last , there?
{
"Products":[
{
"Id":"67-V1",
"Status":"NonActive"
},
{
"Id":"67-V2",
"Status":"NonActive"
},
{
"Id":"67-V3",
"Status":"NonActive"
},
]
}
Seems like here is the final code that will do the trick. While most likely, not the most elegant but does the job. If anyone can suggest the more elegant way, would be appreciated.
var jsonData = pm.response.json();
var newData;
var newDataGroup = "";
function replaceValues() {
_.each(jsonData.Variations, (arrayItem) => {
_.each(arrayItem.Items, (item) => {
if(item.Status !== "NonActive") {
item.Status = "NonActive";
}
newData = "{ \"Id\":\"" + item.Id + "\", " + " \"Status\":\"" + item.Status + "\"},";
newDataGroup = newDataGroup + newData;
});
});
newDataGroup = newDataGroup.slice(0, -1);
newDataGroup = "{ \"Products\": [ " + newDataGroup + " ] }";
}
pm.test("Run Replace", replaceValues ());
var newJson = JSON.parse(newDataGroup);

Typeahead not working for cyrillic

I'm using Typeahead version 0.10.5. It's working for english words but it's not working for each word written in cyrrilic. Some words written in cyrillic are shown, but others aren't. What is this due to?
I'm using it like this:
$('#edicode').typeahead({
source: function(query, process){
CallAPI("GET", "/companies/get/" + query + "/like", function (data) {
var sourcedata = new Array();
var jsonData = JSON.parse(data);
var count = 0;
$.each(jsonData, function(jsonId) {
sourcedata.push(jsonData[jsonId].CODE + ' / ' + jsonData[jsonId].NAME);
selectedItems[jsonData[jsonId].CODE] = JSON.stringify(jsonData[jsonId]);
count++;
});
if(count <= 0)
{
$('#company_name').val('');
$('#company_name').prop('readonly', false);
}
console.log(sourcedata);
return process(sourcedata);
});
},
updater: function (item) {
var info = item.split(" / ");
var company = jQuery.parseJSON(selectedItems[info[0]]);
$('#EDICode').val(company.CODE);
return company.CODE + '/ ' + company.NAME ;
},
name: 'Company',
displayKey: 'value',
minLength: 2,
maxItem: 15,
accent: true,
hint: true
}).blur(function(){
});
Took 1 hour to find:
open bootstrap-typeahead.js (not minified)
find:
matcher: function (item) {
return ~item.toLowerCase().indexOf(this.query.toLowerCase());
},
change to:
matcher: function (item) {
var x=item.toLowerCase().indexOf(this.query.toLowerCase());;
if(x==-1)
x=0;
return ~x
},

fs.writefile only execute in last function in node js

My program has three functions shown below,
var userId = req.userId;
var appId = req.body.appId;
var main = 'temp/' + userId + '/templates/' + appId + '/css/main.css';
var color = req.body.color;
var font = req.body.font;
var fontSize = req.body.fontSize;
replaceThemecolor(color);
replaceFont(font);
replaceFontSize(fontSize);
function replaceThemecolor(color) {
fs.readFile(main, 'utf-8', function (err, data) {
var regex =/(\.made-easy-themeColor\s*{[^}]*color\s*:\s*)([^\n;}]+)([\s*;}])/;
var result = data.replace(regex, "$1" + color + "$3");
console.log(color);
fs.writeFile(main, result, 'utf-8', function (err) {
if (err) return console.log(err);
});
});
}
function replaceFont(font) {
fs.readFile(main, 'utf-8', function (err, data) {
var regex =/(\.made-easy-themeFont\s*{[^}]*font-family\s*:\s*)([^\n;}]+)([\s*;}])/;
var result = data.replace(regex, "$1" + font + "$3");
console.log(font);
fs.writeFile(main, result, 'utf-8', function (err) {
if (err) return console.log(err);
});
console.log(result);
})
}
function replaceFontSize(fontSize) {
fs.readFile(main, 'utf-8', function (err, data) {
var regex =/(\.made-easy-themeFontSize\s*{[^}]*font-size\s*:\s*)([^\n;}]+)([\s*;}])/;
var result1 = data.replace(regex, "$1" + fontSize + "em" + "$3");
console.log(fontSize);
fs.writeFile(main, result1, 'utf-8', function (err) {
if (err) return console.log(err);
});
});
}
In here only the last function executes all the time, when I execute them seperately they work well, but the problem arise when all the funtions execute at once. Is it a problem with fs.writeFile function? I want to execute three of this functions together, is there a way to do this? All the functions here work well when they execute seperately.
Your file functions are async. You cannot run them at the same time because they will conflict and one will overwrite the changes of the other. You must run one, then when it finishes, run the other.
Or, even better, only read the file once, the process the data with all your changes, then write it once.
If you were going to run them sequentially, then you would need to pass a callback to each of your functions that is called when it is done so then you know when to start the next function.
But, I think a better solution is to pass an array of replace instructions and just process all of them on one read and write of the file. I will work on a code example for that.
Here's a way to do all the updates in one read/write of the file and uses promises to know when the operation is done:
function updateFile(filename, replacements) {
return new Promise(function(resolve, reject) {
fs.readFile(filename, 'utf-8', function(err, data) {
if (err) {
reject(err);
} else {
// now cycle through and do all the replacements
for (var i = 0; i < replacements.length; i++) {
data = data.replace(replacements[i].regex, replacements[i].replacer);
}
fs.writeFile(filename, data, 'utf-8', function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
}
});
});
}
updateFile(main, [{regex: /(\.made-easy-themeColor\s*{[^}]*color\s*:\s*)([^\n;}]+)([\s*;}])/, replacer: "$1" + color + "$3"},
{regex: /(\.made-easy-themeFont\s*{[^}]*font-family\s*:\s*)([^\n;}]+)([\s*;}])/, replacer: "$1" + font + "$3"},
{regex: /(\.made-easy-themeFontSize\s*{[^}]*font-size\s*:\s*)([^\n;}]+)([\s*;}])/, replacer: "$1" + fontSize + "em$3"}]).then(function() {
// update done successfully
}, function(err) {
// error
});
With some more work, you could probably abstract out just the keywords from the regular expressions too so you only need to pass in the keywords, but I'll leave that to another time.
And here's a simplified version:
function updateFile(filename, replacements) {
return new Promise(function(resolve, reject) {
fs.readFile(filename, 'utf-8', function(err, data) {
var regex, replaceStr;
if (err) {
reject(err);
} else {
// now cycle through and do all the replacements
for (var i = 0; i < replacements.length; i++) {
regex = new Regex("(\\" + replacements[i].rule + "\\s*{[^}]*" + replacements[i].target + "\\s*:\\s*)([^\\n;}]+)([\\s*;}])");
replaceStr = "$1" + replacements[i].replacer + "$3";
data = data.replace(regex, replaceStr);
}
fs.writeFile(filename, data, 'utf-8', function(err) {
if (err) {
reject(err);
} else {
resolve();
}
});
}
});
});
}
updateFile(main, [
{rule: ".made-easy-themeColor", target: "color", replacer: color},
{rule: ".made-easy-themeFont", target: "font-family", replacer: font},
{rule: ".made-easy-themeFontSize", target: "font-size", replacer: fontSize + "em"}
], function() {
// update done successfully
}, function(err) {
// error
});
And, you don't have to use the promise at all if you don't want to know when it's all done or be able to return errors (which I wouldn't recommend, but the code is simpler).
function updateFile(filename, replacements) {
fs.readFile(filename, 'utf-8', function(err, data) {
var regex, replaceStr;
if (err) { return; }
// now cycle through and do all the replacements
for (var i = 0; i < replacements.length; i++) {
regex = new Regex("(\\" + replacements[i].rule + "\\s*{[^}]*" + replacements[i].target + "\\s*:\\s*)([^\\n;}]+)([\\s*;}])");
replaceStr = "$1" + replacements[i].replacer + "$3";
data = data.replace(regex, replaceStr);
}
fs.writeFile(filename, data, 'utf-8');
});
}
updateFile(main, [
{rule: ".made-easy-themeColor", target: "color", replacer: color},
{rule: ".made-easy-themeFont", target: "font-family", replacer: font},
{rule: ".made-easy-themeFontSize", target: "font-size", replacer: fontSize + "em"}
], function() {
// update done successfully
}, function(err) {
// error
});
Notice how easy it would be to add more replacements. You simply add one more line to the array you pass updateFile().
Node.js is inherently asynchronous. As such, you're doing three read operations in quick succession, and then trying to write to a file that's already file locked, or at the very least, when it was read, did not contain the write changes. I'd use something more like async's series or waterfall methods to solve this.
var async = require("async");
var userId = req.userId;
var appId = req.body.appId;
var main = 'temp/' + userId + '/templates/' + appId + '/css/main.css';
var color = req.body.color;
var font = req.body.font;
var fontSize = req.body.fontSize;
async.series({
replaceThemecolor: function(callback) {
fs.readFile(main, 'utf-8', function(err, data) {
var regex = /(\.made-easy-themeColor\s*{[^}]*color\s*:\s*)([^\n;}]+)([\s*;}])/;
var result = data.replace(regex, "$1" + color + "$3");
console.log(color);
fs.writeFile(main, result, 'utf-8', function(err) {
callback(err);
});
});
},
replaceFont: function(callback) {
fs.readFile(main, 'utf-8', function(err, data) {
var regex = /(\.made-easy-themeFont\s*{[^}]*font-family\s*:\s*)([^\n;}]+)([\s*;}])/;
var result = data.replace(regex, "$1" + font + "$3");
console.log(font);
fs.writeFile(main, result, 'utf-8', function(err) {
callback(err);
});
})
},
replaceFontSize: function(callback) {
fs.readFile(main, 'utf-8', function(err, data) {
var regex = /(\.made-easy-themeFontSize\s*{[^}]*font-size\s*:\s*)([^\n;}]+)([\s*;}])/;
var result1 = data.replace(regex, "$1" + fontSize + "em" + "$3");
console.log(fontSize);
fs.writeFile(main, result1, 'utf-8', function(err) {
callback(err);
});
});
}
}, function(err, results) {
// results is empty, but now the operation is done.
});

Alternative to 'options' in backbone.js 1.1.2

From my view menuitemdetails.js i'am calling options in app.js - itemDetails
My menuitemdetails.js
var MenuItemDetails = Backbone.View.extend({
render: function () {
var markup = '<div>' +
'<h1>' + this.options.name + '</h1>' +
'<p><span class="label">' + this.options.category + '</span></p>' +
'<img src="photos/' + this.options.imagepath + '" class="img-polaroid" />' +
'</div>';
this.$el.html(markup);
return this;
}
});
My app.js
var AppRouter = Backbone.Router.extend({
routes: {
"": "list",
"menu-items/new": "itemForm",
"menu-items/:item": "itemDetails"
},
list: function () {
$('#app').html('List screen');
},
itemDetails: function (item) {
var view = new MenuItemDetails(
{
name: item,
category: 'Entree',
imagepath: 'garden-salad.jpg'
}
);
$('#app').html(view.render().el);
},
itemForm: function () {
$('#app').html('New item form');
}
});
var app = new AppRouter();
$(function () {
Backbone.history.start();
});
Looking forward for an alternative of 'options' because from my view it is not working in backbone.js 1.1.2

Angular scope in jasmine controller tests

Trying to understand how do jasmine tests work.
I've got a module and a controller:
var app = angular.module('planApp', []);
app.controller('PlanCtrl', function($scope, plansStorage){
var plans = $scope.plans = plansStorage.get();
$scope.formHidden = true;
$scope.togglePlanForm = function() {
this.formHidden = !this.formHidden;
};
$scope.newPlan = {title: '', description: ''} ;
$scope.$watch('plans', function() {
plansStorage.put(plans);
}, true);
$scope.addPlan = function() {
var newPlan = {
title: $scope.newPlan.title.trim(),
description: $scope.newPlan.description
};
if (!newPlan.title.length || !newPlan.description.length) {
return;
}
plans.push({
title: newPlan.title,
description: newPlan.description
});
$scope.newPlan = {title: '', description: ''};
$scope.formHidden = true;
};
});
plansStorage.get() is a method of a service that gets a json string from localstorage and returns an object.
When I run this test:
var storedPlans = [
{
title: 'Good plan',
description: 'Do something right'
},
{
title: 'Bad plan',
description: 'Do something wrong'
}
];
describe('plan controller', function () {
var ctrl,
scope,
service;
beforeEach(angular.mock.module('planApp'));
beforeEach(angular.mock.inject(function($rootScope, $controller, plansStorage) {
scope = $rootScope.$new();
service = plansStorage;
spyOn(plansStorage, 'get').andReturn(storedPlans);
ctrl = $controller('PlanCtrl', {
$scope: scope,
plansStorage: service
});
spyOn(scope, 'addPlan')
}));
it('should get 2 stored plans', function(){
expect(scope.plans).toBeUndefined;
expect(service.get).toHaveBeenCalled();
expect(scope.plans).toEqual([
{
title: 'Good plan',
description: 'Do something right'
},
{
title: 'Bad plan',
description: 'Do something wrong'
}
]);
});
it('should add a plan', function() {
scope.newPlan = {title: 'new', description: 'plan'};
expect(scope.newPlan).toEqual({title: 'new', description: 'plan'});
scope.addPlan();
expect(scope.addPlan).toHaveBeenCalled();
expect(scope.plans.length).toEqual(3);
});
});
first test passes ok, but second one fails. The length of the scope.plans expected to be 3, but it is 2. scope.plans didn't change after scope.addPlan() call.
If I understand that right, the $scope inside addPlan method is not the same as scope that I trying to test in second test.
The question is why? And how do I test the addPlan method?
the solution is just to add andCallThrough() method after spy:
spyOn(scope, 'addPlan').andCallThrough()