Evaluate helpers is same context as tested file - javascript

I would like to test prototype functions that I make.
My set up consist of 3 files:
Base.js - Base file, that all my second files have in common
function prop(to, name, func) {
Object.defineProperty(to.prototype, name, {
value: func,
writable: true,
configurable: true
});
return func;
}
Array.js - file that modify the prototype of given Object.
prop(Array, 'hasPresent', function(what) {
return !!~this.indexOf(what)
});
/tests/Array.js - Test itself
describe('hasPresent()',function(){
it('number', function(done){
expect([0,1,2].hasPresent(0)).toBe(true)
done()
})
})
All this is done from nodeJS, that watches file for changes. My concern is, that it will return error from second file (prop is not defined ... at Array.js:1). This says me, that those files are not evaluated in same context. Is there any way to make this work? Or how to get __direcotory variable in test file, when started from node.
My setup in nodejs:
jasmine.loadConfig({
spec_files: ['Base.js','Array.js']
helpers: ['Base.js','Array.js']
})
jasmine.execute(['tests/Array.js']);
PS.: I tried putting eval in tests/Array.js, but the working directory is forgotten when loaded, so I would have to use absolute path, if there is any way to work around this, it would be great.

You just need to use the normal module mechanism for Node.js: https://nodejs.org/api/modules.html
Here is the basic example which you can adapt to your testing:
The contents of foo.js:
const circle = require('./circle.js');
console.log(`The area of a circle of radius 4 is ${circle.area(4)}`);
The contents of circle.js:
const PI = Math.PI;
exports.area = (r) => PI * r * r;
exports.circumference = (r) => 2 * PI * r;

Related

How to write a test case for a function inside another?

I have a js file (index.js) like below
function index() {
sum(a,b) {
return a+b;
};
test() {
---
---
}
};
In my test.js file (Using jest)
const index = require('./index');
console.log(index)
test('adds 1 + 2 to equal 3', () => {
expect(index.sum(1, 2)).toBe(3);
});
It throwing error
TypeError: index.sum is not a function
a good practice could always be - split the functions, do not nest functions, that way it is accessible and reusable.
if you have tried to create a Class or export a specific function then -
you can read here about classes in js - https://developer.mozilla.org/he/docs/Web/JavaScript/Reference/Classes
and you can read here about exporting with require -
https://www.openmymind.net/2012/2/3/Node-Require-and-Exports/
module.exports.sum = sum;
should solve you're problems probably (as long as sum is exposed to the outer scope)

Node Function Scope

I jumped into the deep end recently and have been slowly learning to swim. I'm working on a CLI for building out a simple text game world. That code is becoming a convoluted mess and so I have tried to recreate the error I am getting in a simpler form below.
Try as I might I can't seem to understand the best way to structure all of my functions. In my project I have a parser function that breaks input up and searches for a 'verb' to invoke via a try/catch block. When a verb i.e. 'look' runs it accesses my database module and sends a query based on several parameters to return the description of a room or thing. Because this is all asynchronous virtually everything is wrapped in a promise but I am leaving that out of this example. The following is not the actual project, just a simple recreation of the way I have my objects set up.
APP:
// ***********************
const player = require('./scope_test_player');
player.look();
player.water();
Module1:
// ***********************
const apple_tree = require('./scope_test_apple_tree');
module.exports = {
look: function(){
console.log(
'The apple tree is '+apple_tree.height+'ft tall and has '
+apple_tree.apples+' apples growing on it'
);
},
water: function() {
apple_tree.grow();
}
};
Module2:
// ***********************
const player = require('./scope_test_player');
module.exports = {
height: 10,
nutrition: 0.3,
apples: [],
fertilize: function(number) {
this.nutrition+=number;
},
grow: function() {
this.height+=this.nutrition;
}
};
In the above code I get 'TypeError: apple_tree.grow is not a function' from water or undefined from look. This is the bane of my existence and I have been getting this seemingly at random in my main project which leads me to believe I dont understand scope. I know I can require the module within the function and it will work, but that is hideous and would add hundreds of lines of code by the end. How do I cleanly access the functions of objects from within other objects?
You problem is that have a cyclic dependencies in your project and that you overwrite the exports property of the module. Because of that and the way node cachges required modules, you will get the original module.exports object in scope_test_player file and not the one you have overwritten. To solve that you need to write it that way:
// ***********************
const apple_tree = require('./scope_test_apple_tree');
module.exports.look = function() {
console.log(
'The apple tree is ' + apple_tree.height + 'ft tall and has ' + apple_tree.apples + ' apples growing on it'
);
};
module.exports.water = function() {
apple_tree.grow();
};
And
// ***********************
const player = require('./scope_test_player');
module.exports.height = 10;
module.exports.nutrition = 10;
module.exports.apples = [];
module.exports.fertilize = function(number) {
this.nutrition = +number;
};
module.exports.growth = function() {
this.height = +this.nutrition;
}
But this is a really bad design in gerenal and you should find another way how to solve that. You should always avoid loops/circles in your dependency tree.
UPDATE
In node each file is wrappted into load function in this way:
function moduleLoaderFunction( module, exports /* some other paramteres that are not relavant here*/)
{
// the original code of your file
}
node.js internally does something like this for a require:
var loadedModules = {}
function require(moduleOrFile) {
var resolvedPath = getResolvedPath(moduleOrFile)
if( !loadedModules[resolvedPath] ) {
// if the file was not loaded already create and antry in the loaded modules object
loadedModules[resolvedPath] = {
exports : {}
}
// call the laoded function with the initial values
moduleLoaderFunction(loadedModules[resolvedPath], loadedModules[resolvedPath].exports)
}
return loadedModules[resolvedPath].exports
}
Because of the cyclic require, the require function will return the original cache[resolvedPath].exports, the one that was initially set before you assinged your own object to it.
Is Module1 = scope_test_player and Module2 = scope_test_apple_tree?
Maybe you have a cyclic reference here?
APP requires scope_test_player
// loop
scope_test_player requires scope_test_apple_tree
scope_test_apple_tree requires scope_test_player
// loop
As I can see scope_test_apple_tree doesn't use player.
Can you try to remove:
const player = require('./scope_test_player');
from Module2 ?
There are a few issues to address.
Remove the player require in Module 2(scope_test_apple_tree.js):
const player = require('./scope_test_player')
It doesn't do any damage keeping it there but it's just unnecessary.
Also, replace =+ with += in fertilize and grow which is what I think you are going for.
I was able to run the code natually with those fixes.
If you want to refactor, I'd probably flatten out the require files and do it in the main file controlling the player actions and explicitly name the functions with what is needed to run it (in this case...the tree).
Keeping mostly your coding conventions, my slight refactor would look something like:
index.js
const player = require('./scope_test_player');
const apple_tree = require('./scope_test_apple_tree');
player.lookAtTree(apple_tree);
player.waterTree(apple_tree);
scope_test_player.js
module.exports = {
lookAtTree: function(tree){
console.log(
'The apple tree is '+tree.height+'ft tall and has '
+tree.apples.length+' apples growing on it'
);
},
waterTree: function(tree) {
tree.grow();
console.log('The apple tree grew to', tree.height, 'in height')
}
};
scope_test_apple_tree.js
module.exports = {
height: 10,
nutrition: 0.3,
apples: [],
fertilize: function(number) {
this.nutrition += number;
},
grow: function() {
this.height += this.nutrition;
}
};
Yes, I had circular dependencies in my code because I was unaware of the danger they imposed. When I removed them from the main project sure enough it started working again. It now seems that I'm going to be forced into redesigning the project as having two modules randomly referencing each other is going to cause more problems.

RequireJS - Cannot Access External Module Function

I'm having an issue with RequireJS. Essentially, I'm not able to access a function defined inside another file from another one.
I need to do that because I want to export a given subset of functions like
define('submodule', [], function() {
let myFunction1 = function(){ return "Hello"; }
let myFunction2 = function(){ return " From"; }
let myFunction3 = function(){ return " Submodule!"; }
return {
myFunction1 : myFunction1,
myFunction2 : myFunction2,
myFunction3 : myFunction3,
};
});
And accessing them from another file
define('main', ['config', 'sub1', 'sub2', 'submodule'],
function(config, sub1, sub2, submodule) {
//Config
alert(config.conf);
//Submodule
let callSubmodule = function() {
alert(submodule.myFunction1() +
submodule.myFunction2() +
submodule.myFunction3());
}
//sub1
let callSub1 = function() {
alert(sub1.myFunction1());
}
//sub2
let callSub2 = function() {
alert(sub2.myFunction1());
}
});
The fact is that usually I'm able to do this with sub1 and
sub2, but, with submodule, I simply can't. I think it's somehow caused by the dependencies in require.config.js.
My require.config.js:
require(['common'], function () { //contains vendors
require(['config'], function () { //contains a js config file
require(['main'], function () { //main file
require(['sub1', 'sub2'], function () { //some subfiles
require(['submodule']);
});
});
});
});
For submodule.myFunction1() and othe two related functions I'm getting:
Uncaught (in promise) TypeError: Cannot read property 'myFunction1' of undefined
This is weird since I'm able to do that in other situations and I really can't understand why this is happening. For instance, I'm able to call sub1 and sub2 functions from main and other files but not submodule in particular.
Index.html
//Taken from Plunker
. . .
<script data-main="common" data-require="require.js#2.1.20" data-semver="2.1.20" src="http://requirejs.org/docs/release/2.1.20/minified/require.js"></script>
<script src="require.config.js"></script>
. . .
<button onclick = "callSubmodule()">Call Submodule</button>
<button onclick = "callSub1()">Call Sub1</button>
<button onclick = "callSub2()">Call Sub2</button>
common.js contains vendors, here's just an example
requirejs.config({
baseUrl : "",
paths : {
"jquery" : "http://code.jquery.com/jquery-latest.min.js"
}
});
sub1.js
define('sub1', ['submodule'], function(submodule) {
let myFunction1 = function(){ return "called sub1"; }
return {
myFunction1 : myFunction1
};
});
sub2.js
define('sub2', ['submodule'], function(submodule) {
let myFunction1 = function(){ return "called sub2"; }
return {
myFunction1 : myFunction1
};
});
I set up a Plunker with #SergGr help that tries to replicate application's structure but all the modules get undefined on click. On the real application this does not happen.
How can I solve this?
This is your code:
define('main', ['submodule'], function(submod) {
console.log(submodule.myFunction());
});
You have submod in the parameter list. But you then try to access submodule. Note that you return the function straight from your module (return myFunction), so your module has the value of the function myFunction and thus the module is what you should call. The code should be:
define('main', ['submodule'], function(submod) {
console.log(submod());
});
I Managed to solve this issue. Essentially, it was caused by a circular-dependency between the modules. So, a needed b and b needed a leading to one of them being undefined on the dependency resolution.
I found a solution to that on the answer provided by #jgillich at requirejs module is undefined.
So, I managed to solve using, in main
define('main', ['config', 'sub1', 'sub2', 'require'],
function(config, sub1, sub2, submodule, require) {
//Config
alert(config.conf);
//Submodule
let callSubmodule = function() {
alert(require('submodule').myFunction1() +
require('submodule').myFunction2() +
require('submodule').myFunction3());
}
});
As #jgillich said:
If you define a circular dependency ("a" needs "b" and "b" needs "a"), then in this case when "b"'s module function is called, it will get an undefined value for "a". "b" can fetch "a" later after modules have been defined by using the require() method (be sure to specify require as a dependency so the right context is used to look up "a"):
//Inside b.js:
define(["require", "a"],
function(require, a) {
//"a" in this case will be null if "a" also asked for "b",
//a circular dependency.
return function(title) {
return require("a").doSomething();
}
}
);
http://requirejs.org/docs/api.html#circular
The way you've named your modules I would expect they all came from a require config file. I would not expect that requirejs would know how to load those files without some sort of explicit compilation process. I also suspect that your server is returning something due to a 404 that JS is almost able to interpret without exploding.
Your setup seems and naming scheme seems quite strange. If you have the ability to start from scratch below are my recommendations.
Recommendations:
I'm noticing that you're using absolute paths. I highly recommend using relative paths for everything. There are many reasons for this.
Your data-main should be what you call "require.config.js". Your common.js is actually a require.config.js.
You load require.config.js (which is your main) separately using a script tag. You can do this but it's strange.
You can use the "commonjs" style syntax to require files without needing to use the array to define all your dependencies. I recommend that.
This is my recommendation for a set-up:
index.html
<script src="/js/config.js" />
<script src="http://requirejs.org/docs/release/2.1.20/minified/require.js" />
<script>
require('/js/main', function(main) {
main({});
});
</script>
/js/config.js
// setting requirejs to an object before its loaded will cause requirejs to use it as the config
window.requirejs = {
baseUrl : "/",
paths : {
"jquery" : "http://code.jquery.com/jquery-latest.min.js"
}
};
/js/main.js
define(function(require) {
const sum = require('./sum');
return (a, b) => sum(a, b);
});
/js/sum.js
define(function(require) {
return (a, b) => a + b;
});
Update (March 02, 2017)
Your plunker obviously will not work because you have direct calls from HTML to your module functions.
<button onclick = "callSubmodule()">Call Submodule</button>
<button onclick = "callSub1()">Call Sub1</button>
<button onclick = "callSub2()">Call Sub2</button>
RequireJS doesn't work that way. One of key purposes of RequireJS is to provide modules isolation and thus it just can't work that way: imagine if several different modules had functions callSubmodule.
To the best of my knowledge there is no way to bind calls from HTML back to the code in a RequireJS module, it should be other way around: module binds to HTML. And if you fix those issues, everything works fine for me as you can see at this fork of your plunker.
Old Answer
The bug is in your subModule.js
define('submodule', [], function() {
let myFunction = function(){ return "Hello"; }
//return myFunction; // old, wrong
return { myFunction: myFunction };
});
Even if you want to return just 1 function you should not return it as is, you should wrap it into an object and give it an explicit name.
P.S. if this is not your real issuse, please provide us real Minimal, Complete, and Verifiable example

Calling a JavaScript function from C

Is it possible to call a JavaScript function (written over, say, node.js) from C?
(There are plenty of tutorials on calling C/C++ from node.js. But not the other way around.)
I've been working on the same problem recently, and found a tractable solution using QuickJS and esbuild. It's not the prettiest, but it works quite well!
To call JS from C, the general process is:
Get QuickJS and esbuild
esbuild your desired library/script into an ESM format using CommonJS. This will output one big script with all needed dependencies included.
output=/path/to/esbuild/output
npx esbuild --bundle /path/to/original/node-library --format=esm --outfile="$output"
Patch the output of esbuild to make it compatible with QuickJS:
sed -i 's/Function(\"return this\")()/globalThis/g' $output
sed -i 's#export default#//export default#g' $output
Load the script text into an object file using your linker:
ld -r -b binary my_obj_file.o $output
Depending on your compiler, this will automatically create 3 symbols in the object file:
- name_start
- name_end
- name_size
name in this context is automatically generated from the filename you provided as the last argument to ld. It replaces all non-alphanumeric characters with underscores, so my-cool-lib.mjs gives a name of my_cool_lib_mjs.
You can use ld_magic.h (here) for a cross platform way to access this data from your C code.
After the object file is generated, you should see the patched esbuild output if you run strings:
% strings foo_lib_js.o
var __getOwnPropNames = Object.getOwnPropertyNames;
var __commonJS = (cb, mod) => function __require() {
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
// src/foo.js
var require_foo = __commonJS({
"src/foo.js"(exports, module) {
function foo(bar, baz) {
return bar + baz;
}
module.exports = foo;
//export default require_foo();
_binary_my_foo_lib_mjs_end
_binary_my_foo_lib_mjs_start
_binary_my_foo_lib_mjs_size
.symtab
.strtab
.shstrtab
.data
Link the object file into your binary:
gcc my_obj_file.o <other object files> -o my_static_binary
You can also link the object file into a shared library, for use in other applications:
gcc -shared -o my_shared_library.so my_obj_file.o <other object files>
The source of this repo shows how to do this with a CMake project.
How to actually call the JS functions
Let's say you have a NodeJS library with a function you want to call from C:
// Let's say this lives in foo.js, and esbuild output goes in my-lib-foo.mjs
function foo(bar, baz) {
return bar + baz
}
module.exports = foo;
esbuild creates a series of require_thing() functions, which can be used to get the underlying thing(param1, param2...) function object which you can make calls with.
A simple loader in QuickJS looks like this:
JSValue commonjs_module_data_to_function(JSContext *ctx, const uint8_t *data, size_t data_length, const char *function_name)
{
JSValue result = JS_UNDEFINED;
char * module_function_name = NULL;
// Make sure you properly free all JSValues created from this procedure
if(data == NULL) {
goto done;
}
/**
* To pull the script objects, including require_thing() etc, into global scope,
* load the patched NodeJS script from the object file embedded in the binary
*/
result = JS_Eval(ctx, data, data_length, "<embed>", JS_EVAL_TYPE_GLOBAL);
if(JS_IsException(result)) {
printf("failed to parse module function '%s'\n", function_name);
goto cleanup_fail;
}
JSValue global = JS_GetGlobalObject(ctx);
/**
* Automatically create the require_thing() function name
*/
asprintf(&module_function_name, "require_%s", function_name);
JSValue module = JS_GetPropertyStr(ctx, global, module_function_name);
if(JS_IsException(module)) {
printf("failed to find %s module function\n", function_name);
goto cleanup_fail;
}
result = JS_Call(ctx, module, global, 0, NULL);
if(JS_IsException(result)) {
goto cleanup_fail;
}
/* don't lose the object we've built by passing over failure case */
goto done;
cleanup_fail:
/* nothing to do, cleanup context elsewhere */
result = JS_UNDEFINED;
done:
free(module_function_name);
return result;
}
If you wanted to, for example, get the foo(bar, baz) function mentioned above, you would write a function like this:
#include <stdio.h>
#include <inttypes.h>
// A simple helper for getting a JSContext
JSContext * easy_context(void)
{
JSRuntime *runtime = JS_NewRuntime();
if(runtime == NULL) {
puts("unable to create JS Runtime");
goto cleanup_content_fail;
}
JSContext *ctx = JS_NewContext(runtime);
if(ctx == NULL) {
puts("unable to create JS context");
goto cleanup_runtime_fail;
}
return ctx;
cleanup_runtime_fail:
free(runtime);
cleanup_content_fail:
return NULL;
}
int call_foo(int bar, int baz)
{
JSContext *ctx = easy_context();
JSValue global = JS_GetGlobalObject(ctx);
/**
* esbuild output was to my-foo-lib.mjs, so symbols will be named with my_foo_lib_mjs
*/
JSValue foo_fn = commonjs_module_data_to_function(
ctx
, _binary_my_foo_lib_mjs_start // gcc/Linux-specific naming
, _binary_my_foo_lib_mjs_size
, "foo"
);
/**
* To create more complex objects as arguments, use
* JS_ParseJSON(ctx, json_str, strlen(json_str), "<input>");
* You can also pass callback functions by loading them just like we loaded foo_fn
*/
JSValue args[] = {
JS_NewInt32(ctx, bar),
JS_NewInt32(ctx, baz)
};
JSValue js_result = JS_Call(ctx
, foo_fn
, global
, sizeof(args)/sizeof(*args)
, args
);
int32_t c_result = -1;
JS_ToInt32(ctx, &c_result, js_result);
return c_result;
}
Check out a minimal example project using CMake here: https://github.com/ijustlovemath/jescx/blob/master/README.md
You could use Emscripten.
Emscripten is an LLVM-to-JavaScript compiler.
It takes LLVM bitcode - which can be generated from C/C++, using llvm-gcc (DragonEgg) or clang, or any other language that can be converted into LLVM - and compiles that into JavaScript, which can be run on the web (or anywhere else JavaScript can run).
Also see this: How to execute Javascript function in C++

Simple TDD Jasmine / Karma test failing as undefined function

I'm completely new to jasmine / karma and having a few problems. Test run fine when i run a simple
describe('JavaScript addition operator', function () {
it('adds two numbers together', function () {
expect(1 + 2).toEqual(3);
});
});
test, it passes and is ok but I want to now start testing functions in my othe files, Naturally I started with the most difficult one and fell flat on my face. I then worked my way down the list / errors until I got to the most basic of functions, one that rounds a number to a decimal place by taking in the params. It gave me an undefined error, so I then thought I'd move the addition test that worked into that file just to see if I was being special and it doesn't work either so can someone please tell me what I'm doing wrong? :)
I've been hunting online for quite a while and haven't yet found an idiots guide. I'd like to be able to test my functions by passing in params that I'd expect. For ex:
describe("round results", function(){
var myFunc = roundresult(a,b);
var a = 99.923232;
var b = 1;
it("rounds the result to dec places", function(){
expect(myFunc(a,b).toEqual(99.9));
});
});
where this is the function:
function roundResult(value, places) {
var multiplier = Math.pow(10, places);
return (Math.round(value * multiplier) / multiplier);
}
the error:
ReferenceError: roundresult is not defined
at null.<anonymous> (http://localhost:9878/base/tests/objectTests.js:98:18)
at jasmine.Env.describe_ (http://localhost:9878/absolute/usr/local/lib/node_modules/karma-jasmine/lib/jasmine.js:884:21)
at jasmine.Env.describe (http://localhost:9878/absolute/usr/local/lib/node_modules/karma-jasmine/lib/jasmine.js:869:15)
at describe (http://localhost:9878/absolute/usr/local/lib/node_modules/karma-jasmine/lib/jasmine.js:629:27)
at http://localhost:9878/base/tests/objectTestTests.js:96:1
Any help is hugely appreciated, thanks.
In your describe block, roundresult should be roundResult.
SOLVED: the order in which you require your files determines whether a statement has been defined by the time you try to invoke it. Use plnkr.co to host a sample with multiple files.
expect(myFunc(a,b).toEqual(99.9)); // incorrect, should be as mentioned below
expect().toEqual();
And you have called the function and than initialize variables, which is also incorrect:
var myFunc = roundresult(a,b);
var a = 99.923232;
var b = 1;
Please have a look how I have done :
// I have created test.js
describe("round results", function(){ var a = 99.923232;
var b = 1;
var myfunc = roundResult(a,b);
it("rounds the result to dec places", function(){
expect(myfunc).toEqual(99.923232);
});
});
// main.js
function roundResult(value, places) {
var multiplier = value * places;
return multiplier;
}
I hope it will resolve your error.
Thanks.

Categories

Resources