Unit testing web-browser independent JavaScript - javascript

I have client-side JavaScript that does not interact with the DOM or the web-browser in any way. I would like to unit-test the functionality of this code (which is just a fancy database/buffer) in Travis-CI without starting up a web-browser. Command-line JavaScript made me think I need node.js. I looked through various unit-testing libraries and decided on Mocha for its simplicity, however testing a browser-based module/class seems to be excessively difficult with a node.js based library.
Specifically, I want to test this (simplified) browser JavaScript valid code:
// I need this NameSpace to organise my code and isolate from other code
var Nengo = {};
Nengo.DataStore = function(dims) {
this.times = [];
this.data = [];
for (var i=0; i < dims; i++) {
this.data.push([]);
}
}
Nengo.DataStore.prototype.push = function(row) {
this.times.push(row[0]);
for(var i = 0; i < this.data.length; i++){
this.data[i].push(row[i+1]);
}
}
When I try to test in Node.js, I can't import the idea of the Nengo namespace properly. This test can't even run:
// get the datastore code from the folder below
require("../simple_data")
var assert = require("assert")
describe("DataStore", function() {
var data_store = new Nengo.DataStore(2);
it("accepts data", function() {
data_store.push([0.0, 1.1, 1.2])
assert.deepEqual(data_store.data, [[1.1], [1.2]])
});
});
It fails with the following error:
/home/saubin/javascript_test/test/simple_test.js:5
var data_store = new Nengo.DataStore(2);
^
ReferenceError: Nengo is not defined
at Suite.<anonymous> (/home/saubin/javascript_test/test/simple_test.js:5:22)
at context.describe.context.context (/usr/local/lib/node_modules/mocha/lib/interfaces/bdd.js:49:10)
at Object.<anonymous> (/home/saubin/javascript_test/test/simple_test.js:4:1)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Module.require (module.js:364:17)
at require (module.js:380:17)
at /usr/local/lib/node_modules/mocha/lib/mocha.js:192:27
at Array.forEach (native)
at Mocha.loadFiles (/usr/local/lib/node_modules/mocha/lib/mocha.js:189:14)
at Mocha.run (/usr/local/lib/node_modules/mocha/lib/mocha.js:422:31)
at Object.<anonymous> (/usr/local/lib/node_modules/mocha/bin/_mocha:398:16)
at Module._compile (module.js:456:26)
at Object.Module._extensions..js (module.js:474:10)
at Module.load (module.js:356:32)
at Function.Module._load (module.js:312:12)
at Function.Module.runMain (module.js:497:10)
at startup (node.js:119:16)
at node.js:935:3
One way I have thought of solving this is abandoning node and outputting the test results to the DOM of a headless browser and getting the results, but this seems like a lot of excessive overhead. Can I change the structure of my code to be compatible with Node.js? Is there some other solution that I'm not seeing due to lack of knowledge in the area?

You can make your code "node.js-aware" so it puts global definitions into the actual global context while still remaining completely compatible with the browser environment:
if (typeof window !== "undefined") {
// in browser, define global to be an alias for window
// so global can be used to refer to the global namespace in
// both the browser and node.js
var global = window;
}
global.Nengo = {};
(function() {
var Nengo = global.Nengo;
Nengo.DataStore = function(dims) {
this.times = [];
this.data = [];
for (var i=0; i < dims; i++) {
this.data.push([]);
}
}
Nengo.DataStore.prototype.push = function(row) {
this.times.push(row[0]);
this.data.push(row.slice(1));
}
})();
Then, just remember that ANY global definitions, must be explicitly assigned to the global namespace. In node.js, this will assign them to the actual global namespace in node.js. In the browser, this will assign them to the window object which is the global namespace in the browser.

Although, #jfrien00's answer is technically correct, I came up with another answer later.
First, I needed to refactor my JavaScript code to declare my namespace outside of the DataStore file. Then I declare the Nengo global variable as jfriend00 describes global.Nengo = {};, but only in my Mocha test file.
This way, when I run in my web browser and my unit tests, my code is tested as expected.

Related

JS Script using DOM running on pm2 server

I have read that serverside, you cant use DOM. I've created a web application using DOM and I wonder what needs to be done to reproduce this code to make it work on a server, so the people on the internet can work with it. Here is a little peak of a code:
// Listen for export button click
document.getElementById('exportForm').addEventListener('submit', function (e) {
setTimeout(exportData, 20);
e.preventDefault();
});
// Export data function
function exportData(e) {
console.log("Exporting...");
// device details and time range details
const devId = (document.getElementById('deviceInput')).value;
var dateFrom = (document.getElementById('dateFromInput')).value;
var dateTo = (document.getElementById('dateToInput')).value;
var timeFrom = (document.getElementById('timeFromInput')).value;
var timeTo = (document.getElementById('timeToInput')).value;
const limit = (document.getElementById('limitInput')).value;
const key = (document.getElementById('keysInput')).value;
When I try to run it on server using pm2 start app.js, it returns this error:
ReferenceError: document is not defined
at Object.<anonymous> (/home/cloud/sites/App/app.js:6:1)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
at Object.<anonymous> (/usr/lib/node_modules/pm2/lib/ProcessContainerFork.js:33:23)
at Module._compile (internal/modules/cjs/loader.js:999:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:1027:10)
at Module.load (internal/modules/cjs/loader.js:863:32)
at Function.Module._load (internal/modules/cjs/loader.js:708:14)
I've heard about JSDom, but I think there needs to be html included in a string and then I have no idea how to connect the css in there and so on. Is there a better way for me to make this work?
Document object is a browser feature hence you cant use it inside server, Instead you have other features like the FS (File System)
Anyhow using JSDom may be possible, you have to rewrite using inline styles https://www.codecademy.com/article/html-inline-styles which is not a good practice and you should avoid it

How to manage multiple .js files so as to run automated test cases using their tags in CucumberJS?

I am using CucumberJS with Selenium-Webdriver for automating my test cases. Currently I am having multiple feature files with their respective step-definition files. When I am trying to run the test cases then it is throwing an error:
Error: The previously configured ChromeDriver service is still
running. You must shut it down before you may adjust its
configuration.
at Object.setDefaultService (D:\code\egov-test-cases\node_modules\selenium-webdriver\chrome.js:305:11)
at new World (D:\code\egov-test-cases\features\support\world.js:21:12)
at Object. (D:\code\egov-test-cases\features\steps\create_approver_remittance_master.js:15:13)
at Module._compile (module.js:653:30)
at Object.Module._extensions..js (module.js:664:10)
at Module.load (module.js:566:32)
at tryModuleLoad (module.js:506:12)
at Function.Module._load (module.js:498:3)
at Module.require (module.js:597:17)
at require (internal/module.js:11:18)
at supportCodePaths.forEach.codePath (D:\code\egov-test-cases\node_modules\cucumber\lib\cli\index.js:142:42)
at Array.forEach ()
at Cli.getSupportCodeLibrary (D:\code\egov-test-cases\node_modules\cucumber\lib\cli\index.js:142:22)
at D:\code\egov-test-cases\node_modules\cucumber\lib\cli\index.js:169:41
at Generator.next ()
at asyncGeneratorStep (D:\code\egov-test-cases\node_modules\cucumber\lib\cli\index.js:44:103)
error Command failed with exit code 1. info Visit
https://yarnpkg.com/en/docs/cli/run for documentation about this
command.
Since I am automating the tests, I have put the below code for automating chrome in world.js file, and then tried importing the driver from world.js, but still it is giving the same error.
class World {
constructor() {
const { setDefaultTimeout } = require('cucumber');
const webdriver = require('selenium-webdriver');
const chrome = require('selenium-webdriver/chrome');
const path = require('chromedriver').path;
const screen = {
width: 640,
height: 480
};
setDefaultTimeout(100 * 5000);
var service = new chrome.ServiceBuilder(path).build();
chrome.setDefaultService(service);
this.driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build();
}
}
What you may need to do is kill your browser after each test run as the containers are reused(hence why a browser may already be running). To do this you will want to add a hooks file to your support folder and include something as follows
After({}, async function(scenario) {
this.driver.quit();
}
});
for more info take a look at the docs https://github.com/cucumber/cucumber-js/blob/master/docs/support_files/hooks.md
I found the solution to my problem. Actually the driver was getting initialized multiple times and that is why it was giving me the above error. I was creating the driver inside the constructor in the class World in world.js file. Everytime i was taking an instance of the class World, I was creating a new driver. I shifted the driver declaration outside the class as const driver = new webdriver.Builder().withCapabilities(webdriver.Capabilities.chrome()).build() and created a method as initialize() { return driver; } in world.js file. I am calling the initialize() method in my step definition files as let world = new World(); let driver = world.initialize(). Now I am good to go!

AWS Lambda exports class works in node.js v6.4 but not in node.js v4.3, how to fix this? [duplicate]

This question already has answers here:
Node.js support for => (arrow function)
(4 answers)
Closed 6 years ago.
I have code works in node.js v6.4:
just two files, index.js:
// ------------ Index.js ------------
'use strict';
var Event = require('./models/event.js');
exports.handler = (event, context, callback) => {
console.log('done');
}
and event.js:
// ------------ Event.js ------------
class Event {
static get dynamoDBTableName() {
return
}
get hashValue() {
return
}
parseReference(reference) {
return
}
}
exports.Event = Event
when run index.handler on AWS Lambda which use version node.js 4.3, it throws a error:
Syntax error in module 'index': SyntaxError
at exports.runInThisContext (vm.js:53:16)
at Module._compile (module.js:373:25)
at Object.Module._extensions..js (module.js:416:10)
at Module.load (module.js:343:32)
at Function.Module._load (module.js:300:12)
at Module.require (module.js:353:17)
at require (internal/module.js:12:17)
at Object.<anonymous> (/var/task/index.js:16:13)
at Module._compile (module.js:409:26)
at Object.Module._extensions..js (module.js:416:10)
I think it's something wrong with exports.Event = Event,
Is there some trick to fix this.
I'm new to node.js.
Any help should be appreciated.
I think it's not SyntaxError with (event, context, callback) => { }
Because AWS Lambda sample code runs well with this Syntax:
I originally thought the arrow function was the culprit. However, AWS Node.js 4.3.2 DOES support the arrow function, as mentioned in this post about Node.js 4.3.2 Runtime on Lambda.
NEW (correct) ANSWER
Does the event.js file start with 'use strict';?
You must use strict mode for a class declaration in node.js 4.3.2
Mozilla Developer Network about strict mode
Hoping this will help...
ORIGINAL (incorrect) ANSWER
module.exports = Products
I believe the arrow function:
() => {}
is not yet implemented in the nodejs version you are using (4.3).
See this answer
Arrow functions are supported in Node.js since version 4.4.5
If updating your nodejs version is not an option for you, you could replace:
exports.handler = (event, context, callback) => {
console.log('done');
}
with
exports.handler = (event, context, callback) = function() {
console.log('done');
}

jQuery not defined using mocha for testing wordpress javascript

I've set up mocha to run tests in the terminal. It works for basic test like expect(1).to.equal(1). The problem I run into is that my script is wrapped in the jQuery like so jQuery( function( $ ) { // my code here }); and I get an error when running the tests.
evalmachine.<anonymous>:15
jQuery( function ( $ ) {
^
ReferenceError: jQuery is not defined
at evalmachine.<anonymous>:15:1
at Object.exports.runInThisContext (vm.js:54:17)
at Suite.<anonymous> (/Users/grahamlutz/Documents/BBC/poolproof/test/test.js:21:8)
at context.describe.context.context (/usr/local/lib/node_modules/mocha/lib/interfaces/bdd.js:47:10)
at Object.<anonymous> (/Users/grahamlutz/Documents/BBC/poolproof/test/test.js:8:1)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Module.require (module.js:367:17)
at require (internal/module.js:16:19)
at /usr/local/lib/node_modules/mocha/lib/mocha.js:220:27
at Array.forEach (native)
at Mocha.loadFiles (/usr/local/lib/node_modules/mocha/lib/mocha.js:217:14)
at Mocha.run (/usr/local/lib/node_modules/mocha/lib/mocha.js:469:10)
at Object.<anonymous> (/usr/local/lib/node_modules/mocha/bin/_mocha:404:18)
at Module._compile (module.js:413:34)
at Object.Module._extensions..js (module.js:422:10)
at Module.load (module.js:357:32)
at Function.Module._load (module.js:314:12)
at Function.Module.runMain (module.js:447:10)
at startup (node.js:141:18)
at node.js:933:3
my test.js looks like this:
var assert = require('assert');
var chai = require('chai');
var expect = chai.expect;
var fs = require('fs');
var vm = require('vm');
var jsdom = require('mocha-jsdom');
describe('mocha tests', function () {
jsdom();
before(function () {
$ = require('jquery');
});
var path = __dirname + '/../wp-content/themes/bb-theme-child/myscript.js';
var code = fs.readFileSync(path);
vm.runInThisContext(code);
describe('getSession', function() {
it('should return the empty string because it fails', function () {
applyCoupon();
});
});
});
My question is either why is jQuery undefined or what incorrect assumptions am I making? Do I need to change the way I think about testing javascript within a wordpress set up?
The following two lines worked for me. For convenience I just put these at the top of my mocha test file. I used 'jsdom-global' package instead of 'mocha-jsdom' per the recommendation on the mocha-jsdom github readme. https://github.com/rstacruz/jsdom-global
this.jsdom = require('jsdom-global')()
global.$ = global.jQuery = require('jquery');
You have to export $ and jQuery to the global space yourself:
before(function () {
global.$ = global.jQuery = require('jquery');
});
If you read mocha-jsdom's documentation you'll see that it puts in the global space symbols like window and document. When you load jquery, it finds window and adds itself as window.$. In a browser, this also makes $ visible in the global space because window is the global space. In Node, however, the global space is global, and so you have to put $ in it yourself.

Why the added prototype method should be added after `util.inherits`?

I want to know why the added prototype method should be added after util.inherits?
var util = require('util');
var EventEmitter = require('events').EventEmitter;
function Server() {
console.log('init');
}
Server.prototype.say = function(){
console.log('say');
}
util.inherits(Server, EventEmitter);
var server = new Server();
server.on('abc', function(){
console.log('abc');
})
server.emit('abc');
server.say();
There is an error when i run this code:
C:\Users\elqstux\Desktop\wy.js:19
server.say();
^
TypeError: undefined is not a function
at Object.<anonymous> (C:\Users\elqstux\Desktop\wy.js:19:8)
at Module._compile (module.js:460:26)
at Object.Module._extensions..js (module.js:478:10)
at Module.load (module.js:355:32)
at Function.Module._load (module.js:310:12)
at Function.Module.runMain (module.js:501:10)
at startup (node.js:129:16)
at node.js:814:3
But if i modified code to this:
util.inherits(Server, EventEmitter);
Server.prototype.say = function(){
console.log('say');
}
the code runs ok.
From the node.js documentation https://nodejs.org/docs/latest/api/util.html#util_util_inherits_constructor_superconstructor:
util.inherits(constructor, superConstructor)
Inherit the prototype methods from one constructor into another. The prototype of constructor will be set to a new object created from superConstructor.
So basically after you use util.inherits on Server, its prototype is replaced by new, inheriting from EventEmitter, hence previously added methods are lost.

Categories

Resources