I'm having problems testing a flatiron cli app with Mocha.
The command-line command I'd like to test creates a directory and logs success with app.log.info.
This is the code to be tested (./lib/commands/create.js):
var flatiron = require('flatiron'),
app = flatiron.app,
fs = require('fs'),
path = require('path');
module.exports = function create(name, callback) {
"use strict";
fs.mkdir('./' + name);
app.log.info('Directory created!');
}
This is the test (./test/create.js):
var create = require('../lib/commands/create');
describe('Flatiron command', function () {
"use strict";
describe('#create()', function () {
it('should create a directory ', function () {
create('someDirectory');
// check if the directory was created,
// then remove the directory
});
});
});
mocha test/log -R spec gives me
Flatiron command
#log()
1) should log something
✖ 1 of 1 tests failed:
1) Flatiron command #create() should create a directory :
TypeError: Cannot call method 'info' of undefined
Why is app.lognot available to Mocha?
Is this because of how function logis exported?
Or has this something to do with how flatiron sets up the application? I tried requiring flatiron.app and starting it from the test like this
var create = require('../lib/commands/create'),
flatiron = require('flatiron'),
app = flatiron.app;
describe('Flatiron command', function () {
"use strict";
describe('#create()', function () {
it('should create a directory ', function () {
app.start();
create('someDirectory');
});
});
});
- but with no success, just a different error:
Flatiron command
#create()
1) should create a directory
✖ 1 of 1 tests failed:
1) Flatiron command #create() should create a directory :
TypeError: Object [object Object] has no method 'start'
Or is this a case where you would use spies/stubs/mocks with something like sinon.js to simulate the behavior of app.log somehow? Because I'm not really interested if the logging works, but if the directory is created.
Ok, I've got it.
Using app.start() was not quite it - but it works with app.init().
In flatiron, app.init() is normally called from within the main file by plugging flatiron.plugins.cliinto app.use() like this:
var flatiron = require('flatiron'),
path = require('path'),
app = flatiron.app;
app.config.file({ file: path.join(__dirname, '..', 'config', 'config.json') });
app.use(flatiron.plugins.cli, {
dir: path.join(__dirname, '..', 'lib', 'commands'),
usage: 'Empty Flatiron Application, please fill out commands'
});
app.start();
Calling app.init() sets up logging with winston, the flatiron logging plugin.
But you can call app.init()from within the test without calling app.start() after it.
So this works:
var create = require('../lib/commands/create'),
flatiron = require('flatiron'),
app = flatiron.app;
describe('Flatiron command', function () {
"use strict";
describe('#create()', function () {
it('should create a directory ', function () {
app.init();
create('someDirectory');
});
});
});
Mocha even takes care of the logging:
Flatiron command
#create()
◦ should create a directory : info: Directory created!
✓ should create a directory (48ms)
✔ 1 tests complete (50ms)
If you want to stop the logging, you can use app.log.loggers.default.remove(winston.transports.Console)after you've called app.init(). You have to require winston to do this.
Related
How do I read in a page from localhost into a headless Jasmine spec so test cases can work on the DOM elements?
My Gulp task is successfully running Jasmine specs for unit testing, and now I need to build integration tests to verify full web pages served from localhost. I'm using the gulp-jasmine-browser plugin to run PhantomJS.
Example:
gulpfile.js
var gulp = require('gulp');
var jasmineBrowser = require('gulp-jasmine-browser');
function specRunner() {
gulp.src(['node_modules/jquery/dist/jquery.js', 'src/js/*.js', 'spec/*.js'])
.pipe(jasmineBrowser.specRunner({ console: true }))
.pipe(jasmineBrowser.headless());
}
gulp.task('spec', specRunner);
spec/cart-spec.js
describe('Cart component', function() {
it('displays on the gateway page', function() {
var page = loadWebPage('http://localhost/'); //DOES NOT WORK
var cart = page.find('#cart');
expect(cart.length).toBe(1);
});
});
There is no loadWebPage() function. It's just to illustrate the functionality I believe is needed.
End-to-End testing frameworks like a Selenium, WebdriverIO, Nightwatch.js, Protractor and so on are more suitable in such case.
The gulp-jasmine-browser plugin still is about the Unit testing in the browser environment. It is not possible to navigate between pages.
I put together the following code that appears to work. Please feel free to check out my repo and confirm in your own environment.
package.json
{
"name": "40646680",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "gulp jasmine"
},
"devDependencies": {
"gulp": "^3.9.1",
"gulp-jasmine-browser": "^1.7.1",
"jasmine": "^2.5.2",
"phantomjs": "^2.1.7"
}
}
gulpfile.js
(() => {
"use strict";
var gulp = require("gulp"),
jasmineBrowser = require("gulp-jasmine-browser");
gulp.task("jasmine", () => {
return gulp.src("test/*.js")
.pipe(jasmineBrowser.specRunner({
console: true
}))
.pipe(jasmineBrowser.headless());
});
})();
test/sampleJasmine.js
describe("A suite", function() {
it("contains spec with an expectation", function() {
expect(true).toBe(true);
});
it("contains failing spec with an expectation", function() {
expect(true).toBe(false);
});
});
Execution
Bob Chatman#CHATBAG42 F:\Development\StackOverflow\40646680
> npm test
> 40646680#1.0.0 test F:\Development\StackOverflow\40646680
> gulp jasmine
[21:56:44] Using gulpfile F:\Development\StackOverflow\40646680\gulpfile.js
[21:56:44] Starting 'jasmine'...
[21:56:44] Jasmine server listening on port 8000
.F
Failures:
1) A suite contains failing spec with an expectation
1.1) Expected true to be false.
2 specs, 1 failure
Finished in 0 seconds
[21:56:49] 'jasmine' errored after 4.26 s
[21:56:49] Error in plugin 'gulp-jasmine-browser'
Message:
1 failure
npm ERR! Test failed. See above for more details.
Dependencies
node 7.2
npm 3.9.3
jasmine 2.5.2
phantomjs 2.1.7
gulp 3.9.1
jsdom to the rescue!
It turns out it's pretty easy to load a web page into a headless Jasmine spec... but you need to swap out PhantomJS for jsdom.
Strategy:
Use Jasmine's beforeAll() to call a function that will run JSDOM.fromURL() to request the web page.
Once the web page has been loaded into the DOM, expose window and jQuery for use in your test cases.
Finally, call done() to indicate the tests are now ready to run.
Make sure to close the window after the tests have run.
spec.js
const url = 'http://dnajs.org/';
const { JSDOM } = require('jsdom');
let window, $;
function loadWebPage(done) {
function handleWebPage(dom) {
function waitForScripts() {
window = dom.window;
$ = dom.window.jQuery;
done();
}
dom.window.onload = waitForScripts;
}
const options = { resources: 'usable', runScripts: 'dangerously' };
JSDOM.fromURL(url, options).then(handleWebPage);
}
function closeWebPage() { window.close(); }
describe('The web page', () => {
beforeAll(loadWebPage);
afterAll(closeWebPage);
it('has the correct URL', () => {
expect(window.location.href).toBe(url);
});
it('has exactly one header, main, and footer', () => {
const actual = {
header: $('body >header').length,
main: $('body >main').length,
footer: $('body >footer').length
};
const expected = { header: 1, main: 1, footer: 1 };
expect(actual).toEqual(expected);
});
});
Test output
Note: Above screenshot is from a similar Mocha spec since Mocha has a nice default reporter.
Project
It's on GitHub if you want try it out yourself:
https://github.com/dnajs/load-web-page-jsdom-jasmine
EDITED: Updated for jsdom 11
I have a virtual javascript file in a Jest unit test with the path '/widgets/1.0.js'. I have mocked the fs module to simulate its existence.
Now i would like to dynamically load it to invoke a method 'foo()'. I thought it would be a case of using a virtual mock:
index.test.js
jest.mock('/widgets/1.0.js', () => {foo: jest.fn(() => {console.log('foo!')})}, {virtual: true});
The code which calls the mock:
index.js
let module = require('/widgets/1.0.js');
module.foo();
When i run the test:
Cannot find module '/widgets/1.0.js' from 'index.js'
at Resolver.resolveModule (node_modules/jest-resolve/build/index.js:151:17)
at processWidgets (src/index.js:115:2418)
at Object.<anonymous> (src/__tests__/index.test.js:99:73)
I think it should be possible. Any ideas?
thanks!
It appears to be a problem with the module path. This works:
index.test.js
jest.mock('1.0', () => {
return {
foo: () => {return 42;}
}
}, {virtual: true});
index.js
const module = require('1.0');
let retval = module.foo();
console.log('retval: ', retval);
If i use '/widgets/1.0' it does not. Hope it helps..
I want to avoid duplicate code, so i am trying to load grunt task from Grunt file "a" and use them in gruntfile "b".
that means: i want to see all task of "a" in file "b" (but without code), just setup like a reference or template to another gruntfile.
here is grunt file "b":
module.exports = function (grunt) {
'use strict';
var karmaGrunt = './../../grunt',
abortHandler = function () {
var errors = grunt.fail.errorcount,
warnings = grunt.fail.warncount;
if (errors > 0 || warnings > 0) {
//run rocketlauncher python script and then stop the grunt runner.
grunt.task.run(["shell:rocketlauncher", "fatal"]);
}
},
fatal = function () {
// this function stops grunt and make the jenkins build red.
grunt.fail.fatal('failed');
};
require("grunt-load-gruntfile")(grunt);
// load grunt task from another file and add it.
grunt.loadGruntfile(karmaGrunt);
//grunt needs to continue on error or warnings, that's why we have to set the force property true
grunt.option('force', true);
grunt.initConfig({
shell: {
options: {
execOptions: {
cwd: '../scripts'
}
},
'rocketlauncher': {
command: './runRocketLauncher.sh'
}
}
});
grunt.loadNpmTasks('grunt-karma');
grunt.loadNpmTasks('grunt-shell');
grunt.registerTask('build-process', ['karma', 'abortHandler']);
grunt.registerTask('abortHandler', abortHandler);
grunt.registerTask('fatal', fatal);
}
here is file "a":
module.exports = function (grunt) {
"use strict";
var eConfig = '../e-specs/karma.config.js',
dConfig = '../d-specs/karma.config.js',
cConfig = '../c-specs/karma.config.js';
grunt.initConfig({
karma: {
options: {
reporters: ['progress', 'coverage', 'threshold']
},
c: {
configFile: cConfig
},
d: {
configFile: dConfig
},
e: {
configFile: eConfig
}
}
});
grunt.loadNpmTasks('grunt-karma');
};
my file b load the task "Karma" but if i run only the grunt file of a i have 3 nested task ("e","c","d") but if i load them from another file, the only task i can see is "karma"
the error is:
No "karma" targets found.
Warning: Task "karma" failed. Used --force, continuing.
Done, but with warnings.
If i run the same task in file "a" directly the task is working like a charm.
There is a grunt plugin to load another Gruntfile: grunt-load-gruntfile
With this you can merge two Grunt configurations, including the defined tasks.
Here is an example:
./Gruntfile.js:
module.exports = function (grunt) {
require("grunt-load-gruntfile")(grunt);
grunt.loadGruntfile("web"); //loads the Gruntfile from the folder web/
grunt.registerTask('showConfig', "shows the current config", function(){
console.log(JSON.stringify(grunt.config(), null, 2));
});
};
and the second Gruntfile in ./web/Gruntfile.js.
module.exports = function (grunt) {
grunt.config("WebConfig", "Configuration from the Gruntfile in web/Gruntfile.js");
grunt.registerTask('server', "runs the server",function(){
console.log("just shows this message");
});
};
running grunt showConfig executes the task from the first Gruntfile and displays the configuration, including the parameter defined in ./web/Gruntfile.js.
running grunt server executes the task from ./web/Gruntfile.js.
I'm new at unit testing but I've tried many methods of getting my script (require module) loaded for use with my mocha test script. No matter what I do I always get undefined when I try to read a property or function or anything. Can anyone help point out what may be causing this?
rmq.js (script to test)
define(['bx_slider/1/bx_slider'], {
...
ansrTotal: 0,
...
init: function(settings) {
var self = this;
// do some stuff
return self;
}
});
test-bootstrap.js
require.config({
paths: {
'chai': '/node_modules/chai/chai',
'bx_slider/1/bx_slider': '/test/lib/bx_slider'
},
baseUrl: '/',
nodeRequire: require
});
mocha.setup({
ui: 'bdd'
});
require(['test/test'], function() {
if (window.mochaPhantomJS) {
mochaPhantomJS.run();
} else {
mocha.run();
}
});
test.js
define(['chai'], function(chai) {
var expect = chai.expect;
var rmq = require(['../src/js/rmq']);
describe('rmq test suite', function() {
before(function() {
return rmq.init();
});
it('should blah', function() {
expect(rmq.ansrTotal).to.equal(0);
});
});
});
If it helps, my directory structure is
.
/node_modules
/src
/js
rmq.js
/test
/lib
bx_slider.js
require.js
test-bootstrap.js
test.js
The exact error (for what I have currently written and posted here) in my CLI is
Testing: test/test.js
rmq test suite
"before all" hook
'undefined' is not a function (evaluating 'rmq.init()')
As Mathletics mentioned in a comment, you could do:
define(['chai', '../src/js/rmq'], function(chai, rmq) {
If, for some reason, you cannot do that, there's an alternative. (Maybe you simplified your code so much in your question that the reason is no longer apparent.)
define(['chai'], function(chai) {
var expect = chai.expect;
describe('rmq test suite', function() {
var rmq;
before(function (done) {
require(['../src/js/rmq'], function (_rmq) {
// Save the module we got to the `rmq` variable.
rmq = _rmq;
// Init and call `done` when finished.
rmq.init().then(done);
});
});
it('should blah', function() {
expect(rmq.ansrTotal).to.equal(0);
});
});
});
I have assumed that rmq.init() returns a promise since you did return rmq.init() in your code, and doing this does not make sense unless rmq.init() returns a promise. If that's not the case then you'd have to call rmq.init() and then call done() after it.
The code you had cannot work because require(['../src/js/rmq']) gets the module asynchronously so it does not return the module. You have to use it like I've shown above.
I'm new to using Jasmine and Grunt (and StackOverflow). I'm trying to setup the most basic of projects. However, when I run my grunt task, I get an error that says:
Running "jasmine:testShared" (jasmine) task
Testing jasmine specs via PhantomJS
>> Error: notloaded: Module name "../" has not been loaded yet for context: _. Use require([])
>> http://requirejs.org/docs/errors.html#notloaded at
>> ..\..\C:\Tests\jasmine\_SpecRunner.html:21
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:12 v
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:26 h
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:31
>> ..\..\C:\Tests\jasmine\node_modules\glob\examples\g.js:1
>> Error: notloaded: Module name "../" has not been loaded yet for context: _. Use require([])
>> http://requirejs.org/docs/errors.html#notloaded at
>> ..\..\C:\Tests\jasmine\_SpecRunner.html:21
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:12 v
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:26 h
>> ..\..\C:\Tests\jasmine\.grunt\grunt-contrib-jasmine\require.js:31
>> ..\..\C:\Tests\jasmine\node_modules\glob\examples\usr-local.js:1
>> ReferenceError: Can't find variable: module at
>> ..\..\C:\Tests\jasmine\node_modules\glob\glob.js:37
>> Error caught from PhantomJS. More info can be found by opening the Spec Runner in a browser.
Warning: SyntaxError: Parse error Use --force to continue.
Aborted due to warnings.
I can't figure out what I'm doing wrong. The code for this project can be found here. My file structure looks like this:
/jasmine
/node_modules
/shared
/modules
myModule.js
/tests
/unit
myModule.tests.js
/tasks
/options
jasmine.js
protractor.js
test_task.js
_SpecRunner.html
gruntfile.js
package.json
My package.json file looks like this:
{
"name": "MyApp",
"version": "1.0.0",
"dependencies": {
"async": "~0.9.0",
"glob":"~4.0.3",
"grunt":"~0.4.0",
"grunt-cli":"~0.1.13",
"grunt-contrib-connect":"0.7.1",
"grunt-contrib-jasmine":"~0.6.5",
"grunt-protractor-runner":"1.0.1",
"grunt-start-webdriver":"0.0.2",
"grunt-template-jasmine-requirejs":"0.2.0",
"jasmine-core":"2.0.0",
"jasmine-node":"2.0.0-beta3",
"load-grunt-tasks":"0.2.x",
"lodash":"~2.4.1",
"phantomjs": "1.9.7-14",
"selenium-webdriver":"2.42.1",
"time-grunt":"~0.3.2"
}
}
My gruntfile.js file looks like this:
'use strict';
module.exports = function (grunt) {
var config = {
name: 'MyApp',
pkg: grunt.file.readJSON('package.json'),
baseDir: '.'
};
// load all grunt task details
require('load-grunt-tasks')(grunt);
// show elapsed time at the end
require('time-grunt')(grunt);
// load task definitions
grunt.loadTasks('tasks');
// Utility function to load plugin settings into config
function loadConfig(config,path) {
require('glob').sync('*', {cwd: path}).forEach(function(option) {
var key = option.replace(/\.js$/,'');
// If key already exists, extend it. It is your responsibility to avoid naming collisions
config[key] = config[key] || {};
grunt.util._.extend(config[key], require(path + option)(config,grunt));
});
}
// Merge that object with what with whatever we have here
loadConfig(config,'./tasks/options/');
grunt.loadNpmTasks('grunt-start-webdriver');
grunt.loadNpmTasks('grunt-protractor-runner');
// pass the config to grunt
grunt.initConfig(config);
};
myModule.js looks like this:
'use strict';
var _ = require('lodash');
_.mixin({
'myFunction' : function(s) {
return 'Hello ' + s;
}
});
module.exports = _;
myModule.tests.js looks like this:
'use strict';
describe('myModule', function() {
it('should work', function() {
expect(true).toBe(true);
});
});
test_task.js looks like this:
module.exports = function(grunt) {
grunt.loadNpmTasks('grunt-contrib-jasmine');
grunt.registerTask('test-shared', [
'jasmine:testShared'
]);
};
jasmine.js looks like this:
module.exports = function(config) {
return {
testShared: {
src: "shared/modules/*.js",
options: {
specs: "shared/tests/unit/**.tests.js",
vendor: "node_modules/**/*.js",
template: require('grunt-template-jasmine-requirejs')
}
}
};
};
protractor.js looks like this:
module.exports = function(config) {
return {
testShared: {
options: {
configFile: "shared/tests/unit/config.js"
}
}
};
};
I've spent two days on this. I have no idea what I'm doing wrong. I feel like I've followed the examples online. I feel like there is something stupid I'm overlooking. Can someone please show me what I'm doing wrong?
It's very hard to diagnose without a running example but that error is caused by RequireJS. It happens when you do require('something') without having loaded it. Loading is asynchronous so you either need to do require['lodash'], function(_) { do stuff }. Or the preferred define syntax:
define([
'lodash'
], function(_) {
'use strict';
_.mixin({
myFunction: function(s) {
return 'Hello ' + s;
}
});
return _;
});
With a test you would typically use the same structure and pull in the module you are testing in the define. You would set it up so the path to required files is the same as in the app.
To help more we need a running example.
My colleague made a good boilerplate using grunt/jasmine/requirejs maybe you can get some good practices from there: https://github.com/mderrick/backbone-boilerplate
Your directory structure is quite confusing have you considered simplifying it?
- Use a single Gruntfile.js - don't fragment unless your project is very big
index.html
Gruntfile.js
app/
test/
That is all you really need to get going. In app it could look something like:
app/app.js <-- entry point
app/modules/...