How to test a function with different inputs - javascript

function addTwo (a, b) {
return a + b;
}
//Leave the function call
addTwo(50, 100);
I'm learning React and I'm trying to create a codecademy type site as a 'learning project', but have run into a JS problem.
Say you have the function above, how do you test it for more than one case? So far I'm testing with:
eval(CODE PULLED IN HERE) === 150 ? alert('Correct!') : alert('Wrong!');
which is obviously going to alert Correct, which is ok for this case. But for other questions (and even this one) I'm going to want more than one test case and that's where I'm stuck.
So, how can I test for multiple test cases, or is there just a whole other way of doing what I'm trying to achieve?
Any help/tips greatly appreciated,
For those who know React here's some code to see a bit of what I currently have :
const CodeEditor = React.createClass({
getInitialState () {
var initialValue = [
"function addTwo () {",
" ",
"}",
"//Leave the function call",
"addTwo(50, 100);"
].join("\n");
return {
kataValue: initialValue
}
},
onChange (newValue) {
this.setState({kataValue: newValue});
},
evalCode () {
var val = this.state.kataValue
eval(val) === 150 ? alert('Correct!') : alert('Wrong!');
},
render () {
return (
<div className="code-editor-wrapper">
<AceEditor
name="editor"
mode="sh"
theme="chaos"
onChange={this.onChange}
value={this.state.kataValue}
editorProps={{$blockScrolling: true}}
/>
<button onClick={this.evalCode} className="spec-btn submit-code-btn">Evaluate</button>
</div>
)
}
})

Don't include the function call in the user's code. Only require the function to be named in a certain way. Instead of directly evaling the user's code, embed into a function that returns the user's function:
function getUserFunction(code, functionName) {
var userCode = new Function(code + '; return ' + functionName + ';');
return userCode();
}
After calling getUserFunction you have a reference to the function the user wrote and you can execute it as often as you want. How you structure your test cases and how much feedback you want to give to the user is up to you.
Here is small example:
var userFn = getUserFunction(this.state.kataValue, 'addTwo');
var testCases = [
[[50, 100], 150],
[[1, 2], 3],
];
var passes = testCases.every(
([input, output]) => userFn(...input) === output
);
if (passes) {
// all test cases pass
}

You can iterate over a bunch of inputs like so:
function addTwo(a, b) {
return a + b
}
for (var i = 0, j; i < 100; i++) {
for (j = 0; j < 100; j++) {
if (addTwo(i, j) !== i + j) console.error('Wrong output for inputs ' + i + ' and ' + j)
}
}

Related

Variables Not Being Retained

Alrighty.
My problem is with the Javascript Objects itemList and priceList - they are not retained properly into the .then() section.
Here's the part that I'm having trouble with:
var stmt = `SELECT * FROM SRV${msg.guild.id}`;
var itemList = {};
var priceList = {};
var num = -1;
sql.each(stmt, function(err, row) {
num++
if(row.items!="ITEMS") { itemList[num] = row.items; }
if(row.prices!="PRICES") { priceList[num] = row.prices; }
console.log("---------\n" + JSON.stringify(itemList, null, 4) + "\n" + JSON.stringify(priceList, null, 4));
})
.then((itemList, priceList) => {
console.log("----E----\n" + JSON.stringify(itemList, null, 4) + "\n" + JSON.stringify(priceList, null, 4) + "\n----E----");
let cmd = require("./itemsPROCESS.js");
cmd.run(transfer, msg, args, itemList, priceList);
});
And here's the important bit that it outputs:
I've tried using strings instead of Javascript Objects, but it doesn't seem to like that either, and it outputs the same.
The 5 and undefined part should resemble the bit above it, but it doesn't.
Might it have something to do with the null parts? If so, will that mean it won't work with true - false - undefined etc?
remove the arguments itemList and priceList and your code should just be fine.
The itemList you are referring inside the .then function is actually what is resolved by the SQL promise.
.then(() => {...});
You are using itemList and priceList of global scope so don't redefine them in local scope of then. Just remove those arguments from then
.then(() => {
console.log("----E----\n" + JSON.stringify(itemList, null, 4) + "\n" + JSON.stringify(priceList, null, 4) + "\n----E----");
let cmd = require("./itemsPROCESS.js");
cmd.run(transfer, msg, args, itemList, priceList);
});

Unable to return value from node js module

exports.getDefiniton = function (text) {
var definition = "";
wn.definitions(text, {
useCanonical: true
, includeRelated: true
, limit: 3
}, function (e, defs) {
definition = defs[0].word + ': 1.' + defs[0].text;
definition += '\n2.' + defs[1].text;
definition += '\n3.' + defs[2].text;
console.log(definition)
});
return definition;
};
Console.log inside function(e, defs) works.
but the return statement doesn't seem to return the value.
How to properly return 'definition' variable?
since wn.definition is an Asynchronous call you should use promise or async/await or callback features.
Using callback your code would be like something like this (for example lets say you store this in a def.js file):
exports.getDefiniton = function (text, callback) {
var definition = "";
wn.definitions(text, {
useCanonical: true
, includeRelated: true
, limit: 3
}, function (e, defs) {
definition = defs[0].word + ': 1.' + defs[0].text;
definition += '\n2.' + defs[1].text;
definition += '\n3.' + defs[2].text;
console.log(definition);
callback(definition);
});
};
and you can use def.js module like this:
var defModule = require("./def");
defModule.getDefiniton("Hello", function (defintion) {
console.log(defintion);
});
UPDATE:
#Xuva in that case check the code below:
var defModule = require("./def");
defModule.getDefiniton("Hello", function (definition) {
displayMessage(text, definition);
//rest of the code ...
});

Issues with protractor test when finding element array using repeatable

I've created a protractor test for the following html:
<div class="well well-sm" data-ng-repeat="feedback in f.feedbackList">
Rating: {{feedback.rating}}
<blockquote class="small">{{feedback.comment}}</blockquote>
</div>
In the page.js file I have:
"use strict";
module.exports = (function () {
function AdminFeedbackPage() {
this.comments = element.all(by.repeater('feedback in f.feedbackList').column('feedback.comment')).map(function (comments) {
return comments.getText();
});
this.ratings = element.all(by.repeater('feedback in f.feedbackList').column('feedback.rating')).map(function (ratings) {
return ratings.getText();
});
}
return AdminFeedbackPage; })();
and then in the test in my step.js file:
var feedbackFound = false;
var feedbackIndex;
adminFeedbackPage.comments.then(function (commments) {
for (var i = 0; i < commments.length; i++) {
console.log("Comments " + i + ": " + commments[i]);
if (commments[i] == "TestApplicationFeedback") {
feedbackIndex = i;
console.log("FEEDBACK INDEX - " + feedbackIndex)
feedbackFound = true;
break;
}
}
}).then(function () {
expect(feedbackFound).to.be.true;
}).then(function() {
adminFeedbackPage.ratings.then(function (ratings) {
console.log(ratings);
console.log("RATINGS length " + ratings.length + " and rating is " + ratings[feedbackIndex]);
expect(ratings[feedbackIndex]).to.equal(3);
})
});
And I get the following logs:
Comments 0: Decent App
Comments 1: Really like it
Comments 2: TestApplicationFeedback
FEEDBACK INDEX - 2
[]
RATINGS length 0 and rating is undefined
AssertionError: expected undefined to equal 3
This is really confusing my since the comments are being found without any issue, but the ratings are just an empty array and as far as I can tell I have done the same thing for both.
Does anybody have any suggestions/reasons why the ratings aren't being found? I suspect it's something to do with what is in the page.js file, but I have no idea what could be wrong?
Thanks!
Solved this in the comments above, posting as an answer:
It was just a guess/suggestion based on the HTML, one was a child element and the other was directly inside the repeater (this one was failing to be captured). So my suggestion was to try using .evaluate() source, which acts as if on scope of the given element. So replacing .column() seems to work:
element.all(by.repeater('feedback in f.feedbackList')).evaluate('feedback.rating').then(function(val) {
console.log(val) // should return an array of values (feedback.rating)
})

Simple grammar checker program - optimal data structure

I want to created a simple game for English that checks someone's sentences. They are able to construct their sentence using a fixed set of words from a word bank.
The word bank might be something scrambled like:
[want, eat, I, hi, to]
Then they'd create their sentence in the correct order:
hi I want to eat.
I asked this question on English SO as it originally pertained to grammatical questions- the question has evolved into more of a data structures question. You can read more about it at that link. My original thought to check sentence grammar using a set of generic English rules seemed like it could quickly grow too complex. It was recommended I just match using hard coded checks, shown below.
Before I further define these checks, I was wondering if a better data structure/method was known to check grammar for this purpose.
if (input === the_answer) {
msg = correct!
} else {
msg = 'Try again: ' + this.grammarRules(input, the_answer));
}
Language_System.prototype.grammarRules = function(input, answer) {
var grammar_hints = {
quest1 : {
task1 : [
'The subject, Bob, needs to be first',
'The phrase is Hello there'
]
}
};
var grammar_rules = {
quest1 : {
task1 : function (input, answer) {
var error = -1;
if (input[0] !== answer[0]) {
error = 0;
} else if (input.indexOf('hello') > input.indexOf('there')) {
error = 1;
}
return grammar_hints.quest1.task1[error];
}
}
};
return grammar_rules.Lee.quest1.task1(input, answer);
};
It would be much easier if you'd consider a more declarative approach:
- define a standard quest structure
- define a standard task structure with generic input formats
- define generic validators and re-use them
You started on the right path with the grammar_hints object, but I would actually put all the properties portraying to one task in the same object.
Suggestion:
var quests = [
{
name: 'Quest 1',
tasks: [
{
name: 'Task 1',
solution: 'hi I want to eat',
validators: [
validators.first('hi'),
validators.verbAfterNoun('want', 'I'),
]
}
],
},
];
You will be able to re-use a lot of the validators in multiple tasks so you want them to be as generic as possible, here is one example:
var validators = {
first: function (input, term) {
if (input[0] !== term) {
return 'The sentence needs to start with: ' + term;
}
},
verbAfterNoun: function (input, verb, noun) {
if (input.indexOf(verb) < input.indexOf(noun)) {
return 'The verb, ' + verb + ', needs to come after the noun ' + noun;
}
}
};
Now because you want to have a declarative format (I went with actually initializing the validators with their input and passing the result in the validators array), we would need a validator factory that takes a generic validator and returns a helper method that can be re-used with only the input. This will help us down the line so our testing framework won't need to know how many inputs to pass to each of the validator callbacks
// This is a factory method that applies the given callback (with the given arguments)
function makeValidator (fn) {
return function inputFN () {
var args = [].slice.call(arguments);
return function validate (input) {
return fn.apply(null, [input].concat(args));
}
}
}
// Apply the makeValidator() method on all the validators
for (var key in validators) {
validators[key] = makeValidator(validators[key]);
}
And finally we also want a standard way of checking our tasks against input:
// This method provides the generic validation framework for any task given any input
function validate (task, input) {
var wordList = input.split(' ');
if (input === task.solution) return {success: true, errors: []};
var errors = [];
task.validators.forEach(function (fn) {
var error = fn(wordList);
if (error) errors.push(error);
});
return {success: false, errors: errors};
}
And some examples:
var task = quests[0].tasks[0];
console.log(validate(task, 'hi I want to eat'));
console.log(validate(task, 'I want to eat hi'));
console.log(validate(task, 'hi want I to eat'));
console.log(validate(task, 'want I to eat hi'));
Putting it all together:
// This is a factory method that applies the given callback (with the given arguments)
function makeValidator (fn) {
return function inputFN () {
var args = [].slice.call(arguments);
return function validate (input) {
return fn.apply(null, [input].concat(args));
}
}
}
var validators = {
first: function (input, term) {
if (input[0] !== term) {
return 'The sentence needs to start with: ' + term;
}
},
verbAfterNoun: function (input, verb, noun) {
if (input.indexOf(verb) < input.indexOf(noun)) {
return 'The verb, ' + verb + ', needs to come after the noun ' + noun;
}
}
};
// Apply the makeValidator() method on all the validators
for (var key in validators) {
validators[key] = makeValidator(validators[key]);
}
var quests = [
{
name: 'Quest 1',
tasks: [
{
name: 'Task 1',
solution: 'hi I want to eat',
validators: [
validators.first('hi'),
validators.verbAfterNoun('want', 'I'),
]
}
],
},
];
// This method provides the generic validation framework for any task given any input
function validate (task, input) {
var wordList = input.split(' ');
if (input === task.solution) return {success: true, errors: []};
var errors = [];
task.validators.forEach(function (fn) {
var error = fn(wordList);
if (error) errors.push(error);
});
return {success: false, errors: errors};
}
function printTask (input) {
var task = quests[0].tasks[0];
var result = validate(task, input);
document.body.innerHTML += '<div><b>checking:</b> ' + input + '<pre>' + JSON.stringify(result, null, 4) + '</pre><hr />';
}
// Lets look at some examples
printTask('I want to eat hi');
printTask('hi want I to eat');
printTask('want I to eat hi');
printTask('hi I want to eat');

How to pass a JavaScript function to another process over JSON?

I'm doing a chrome extension -- how would I pass a function from background to a content-script instance, obviously the function needs to be serialized?
Is it just okay to do eval(function_string)?
This is my first time practicing such voodoo, help invited.
Here is the code I used to achieve this :
The custom scripting language script containing functions to be sent :
// An example script
// Showing how dependent functions are defined later on
// With functions that have no dependencies grouped together in an associative array
// preceding, in the execution_context_loader's list, the functions that depend on them.
var collection_script.execution_context_loader = [
{
fa:function(a) { return a+1; },
fb:function(b) { return b*2; },
fc:function(c) { return c-3; }
},
{
fabc:function(a,b,c) {
return window.qwontical.execution_context.fa(a)^
window.qwontical.execution_context.fb(b)^
window.qwontical.execution_context.fc(c);
}
}
];
var script_point = {
name : 'alpha',
source : '/root/space',
data_args : [ '/j1', '/j2', '/j3' ],
function_applied : 'fabc'
};
Which when executed on the other side (say by calling some function execute("alpha");) will apply the function fabc (serialized and revivified) to the resources found at /root/space/j1, and also .../j2 and .../j3.
On the function sending side :
// Function to package a script's execution context for dispatch to a collection script
function package_script_for_dispatch( collection_script ) {
console.log(collection_script);
if ( !!collection_script.execution_context_loader ) {
for ( var i = 0; i < collection_script.execution_context_loader.length; i += 1) {
for ( var func_def in collection_script.execution_context_loader[i] ) {
collection_script.execution_context_loader[i][func_def] = collection_script.execution_context_loader[i][func_def].toString();
console.log("Packed : " + collection_script.execution_context_loader[i][func_def]);
}
}
} else {
collection_script.execution_context_loader = {};
}
}
On the function receiving side :
for( var i = 0; i < collectionscript.execution_context_loader.length; i+= 1) {
for (var func_name in collectionscript.execution_context_loader[i] ) {
var func_def_string = collectionscript.execution_context_loader[i][func_name];
console.log("About to revivify " + func_name + " : " + func_def_string);
eval("window.qwontical.execution_context['"+func_name+"'] = "+func_def_string);
}
}
The calling convention for such a function is then
window.qwontical.execution_context[func_name](args...);
This enables functions to be defined in a script by their name and then used later on, after that script has been serialized and "revivified"

Categories

Resources