Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Merge work done in grunt-simple-mocha to this plugin #17

Closed
wants to merge 2 commits into from

8 participants

@lauripiispanen

I'm opening this pull request to propose merging work done in grunt-simple-mocha into this plugin. The patches I've created incorporate the functionality to this plugin in the following manner:

  1. If the list of files passed contains no .html files, the tests will be run in node.js
  2. If the list does contain .html files, PhantomJS will be used instead
  3. If the multi-task target is configured with type: unit, the tests will be run in node.js

This pull request was inspired by having to deal with the two plugins sharing task names (see some of the forks of the grunt-simple-mocha plugin). I'm also attempting to contact the author of grunt-simple-mocha to have him collaborate on this plugin instead on the node.js testing part.

@lauripiispanen lauripiispanen referenced this pull request in yaymukund/grunt-simple-mocha
Closed

Merge this plugin to grunt-mocha #3

@kmiyashiro
Owner

Can you provide an example of the problems you had? Is the purpose to have both server and client side tests in a single task? It seems like you could do that by creating an alias/registertask.

@lauripiispanen

Can you provide an example of the problems you had?

Both of these plugins register the task "mocha", and hence cannot be used simultaneously:

https://github.com/kmiyashiro/grunt-mocha/blob/master/tasks/mocha.js#L142
https://github.com/yaymukund/grunt-simple-mocha/blob/master/tasks/simple-mocha.js#L14

Conceptually, grunt-simple-mocha seems cleaner, since it's 'just mocha', versus grunt-mocha fires up a browser and a bunch of .html pages. Since grunt is used to develop not only browser-based solutions, it would be logical that choosing mocha as the test runner would not automatically mean switching to browser-based testing.

@yaymukund

Hello, I'm the author of simple-mocha. One of my main reasons for publishing simple-mocha is that PhantomJS shouldn't be a dependency for people who want to run server-side tests.

I feel like the right solution is for one of us to rename the task. I'd be happy to change it to simplemocha (as at least one fork has already done), but I'm still figuring out the best way to make the change. Maybe we just want to bump the version to 0.2 and add a big note in the README...

@kmiyashiro
Owner

@yaymukund Yeah, I think these tasks are for fundamentally different goals (client vs server tests), it just so happens mocha can be used for both without modification. I'd hate for either of us to change the task name since people will probably wonder why their task broke, but I'm more hesitant to change grunt-mocha's task name ever since yeoman added it as a dependency and there are now thousands of installations.

@lauripiispanen

Yes, I see this issue having more to do with grunt. One example grunt could do is have some sort of namespacing for npm tasks. Other option could maybe is to have grunt-mocha act as some sort of "base" mocha integration, which could be extended by further plugins - but this would break builds relying on the current functionality, as they would require one more plugin ("grunt-mocha-phantom?").

@kmiyashiro
Owner

The more I think about it, the more I like the idea of just making grunt-simple-mocha a dependency and then including the runner via the module. The only problem is, given the setup of grunt's task modules, we can't just include grunt-simple-mocha as-is. In order to make the task function exportable, it will have to expose a function that returns the task function so we can pass grunt in.

@zappan

Hi - Just to confirm that the forked version of simple-mocha with its grunt task renamed to simplemocha works fine, we're using both plugins and with that change there are no conflicts.

So, a readme of this project can be updated regarding the naming conflict, as Mukund has renamed its task, so things now can work together.

Cheers

@kmiyashiro
Owner

Cool, updated. I still like the idea of detecting whether there are html files in the files list and the trying to run simplemocha, maybe attempting a child process grunt call to it and echoing the stdout. Not sure what the best method is for calling another grunt task from within a grunt task.

@yaymukund

If I put the mocha_instance.run() code in a helper, I think you can reuse the helper. But I haven't had a chance to test this yet.

@kmiyashiro
Owner

@yaymukund helpers are going to be removed in grunt 0.4, instead you can just use normal node modules to import functions. Maybe you can wrap your main code in a normal node module and then it can easily be imported by any node module.

@sindresorhus

:+1:

@kmiyashiro Can we get this landed?

@kmiyashiro
Owner

I'd have to review it since this was for grunt 0.3 and the config has changed, but there were also questions about whether it makes sense to combine frontend and backend tests in the same task.

@syamanaka

It's not so much about frontend vs backend tests; rather it's plain Mocha vs Mocha+PhantomJS. We write most client-side tests using Zombie.js and the simplemocha task. When that's not sufficient, we use this task. Looking for html files is okay, but I prefer to have a PhantomJS option for each target.

@sindresorhus

I agree. Would be nice not to have to task for this. I have multiple modules that work both in the browser and in Node.js and currently I'm required to use two tasks to test it.

@Bartvds

Note: it could also be worth to look at grunt-mocha-test (compared to grunt-simple-mocha) for node.js testing as it is fully featured with many tests (less 'simple' :)

@kmiyashiro
Owner

I can't help but wonder what demons lie down that path.

@kmiyashiro kmiyashiro closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Sep 20, 2012
  1. @lauripiispanen
  2. @lauripiispanen

    updated readme

    lauripiispanen authored
This page is out of date. Refresh to see the latest.
View
26 README.md
@@ -1,10 +1,13 @@
-Automatically run *client-side* mocha specs via grunt/mocha/PhantomJS
+Automatically run mocha specs either *client-side* via grunt/mocha/PhantomJS, or with a straight-up node.js mocha runner.
# grunt-mocha
-(package/README format heavily borrowed from [grunt-jasmine-task](hhttps://github.com/creynders/grunt-jasmine-task) and builtin QUnit task)
+This plugin builds on the work from following projects:
-[Grunt](https://github.com/cowboy/grunt) plugin for running Mocha browser specs in a headless browser (PhantomJS)
+ * [Grunt](https://github.com/cowboy/grunt)
+ * [PhantomJS](http://phantomjs.org/)
+ * [grunt-simple-mocha](https://github.com/yaymukund/grunt-simple-mocha)
+ * (package/README format heavily borrowed from [grunt-jasmine-task](hhttps://github.com/creynders/grunt-jasmine-task) and builtin QUnit task)
## Getting Started
@@ -36,7 +39,22 @@ mocha: {
},
```
-Replace ```specs/index.html``` with the location of your mocha spec running html file.
+The plugin will automatically detect if the list of files contains html files, and will run the tests with phantomJS if any are found. Replace ```specs/index.html``` with the location of your mocha spec running html file.
+
+Otherwise the task will run the tests in straight-up node.js mocha runner. Example configuration:
+
+```javascript
+js: ['js/**/*.js', 'test/unit/**/*.js'],
+unit: {
+ src: [ 'js/**/*.js', 'test/unit/**/*.js' ],
+ // or you can specifically define a multi-task to be run as unit test
+ type: 'unit',
+ // you can pass any of the usual options from grunt-simple-mocha
+ options: {
+ reporter: 'nyan'
+ }
+}
+```
Now you can run the mocha task with `grunt mocha`, but it won't work. That's because you need...
View
12 example/grunt.js
@@ -47,6 +47,18 @@ module.exports = function(grunt) {
// you want. You can split them up into different groups here
// ex: admin: [ 'test/admin.html' ]
all: [ 'test/**/*.html' ],
+ // if the src set given does not contain any .html files, the tests
+ // will be run using mocha unit runner instead
+ js: ['js/**/*.js', 'test/unit/**/*.js'],
+ unit: {
+ src: [ 'js/**/*.js', 'test/unit/**/*.js' ],
+ // or you can specifically define a multi-task to be run as unit test
+ type: 'unit',
+ // you can pass any of the usual options from grunt-simple-mocha
+ options: {
+ reporter: 'nyan'
+ }
+ }
}
});
View
2  example/js/apple.js
@@ -1,4 +1,4 @@
-var Apple = function(opts) {
+Apple = function(opts) {
opts = opts || {};
this.name = opts.name || 'Fuji';
View
2  example/js/wombat.js
@@ -1,4 +1,4 @@
-var Wombat = function(opts) {
+Wombat = function(opts) {
opts = opts || {};
this.name = opts.name || 'Wally';
View
15 example/test/unit/apple.js
@@ -0,0 +1,15 @@
+var expect = require("../js/chai").expect;
+
+describe('Apple', function() {
+ beforeEach(function() {
+ this.apple = new Apple();
+ });
+
+ afterEach(function() {
+ delete this.apple;
+ });
+
+ it('should go crunch', function() {
+ expect(this.apple).property('sound', 'crunch');
+ });
+});
View
31 example/test/unit/wombat.js
@@ -0,0 +1,31 @@
+var expect = require("../js/chai").expect;
+
+describe('Wombat', function() {
+ beforeEach(function() {
+ this.wombat = new Wombat();
+ });
+
+ afterEach(function() {
+ delete this.wombat;
+ });
+
+ it('should create a wombat with defaults', function() {
+ expect(this.wombat).property('name', 'Wally');
+ });
+
+ it('should name itself if name passed in options', function() {
+ this.wombat = new Wombat({ name: 'Matt' });
+ expect(this.wombat).property('name', 'Matt');
+ });
+
+ describe('#eat', function() {
+ it('should throw if no food passed', function() {
+ expect(this.wombat.eat).to.throw('D:');
+ });
+
+ it('should return noms if food passed', function() {
+ expect(this.wombat.eat('apple')).to.eql('nom nom');
+ });
+ });
+
+});
View
3  package.json
@@ -29,7 +29,8 @@
"test": "grunt test"
},
"dependencies": {
- "temporary": "~0.0.2"
+ "temporary": "~0.0.2",
+ "mocha": "*"
},
"devDependencies": {
"grunt": "~0.3"
View
52 tasks/mocha.js
@@ -29,6 +29,9 @@ module.exports = function(grunt) {
var fs = require('fs');
var path = require('path');
+ // Mocha libs
+ var Mocha = require('mocha');
+
// External libs.
var Tempfile = require('temporary/lib/file');
var growl;
@@ -136,10 +139,30 @@ module.exports = function(grunt) {
};
// ==========================================================================
- // TASKS
+ // TEST RUNNERS
// ==========================================================================
+ var unitjsRunner = function() {
+ var filepaths = grunt.file.expandFiles(this.file.src);
+ grunt.file.clearRequireCache(filepaths);
- grunt.registerMultiTask('mocha', 'Run Mocha unit tests in a headless PhantomJS instance.', function() {
+ var paths = filepaths.map(path.resolve),
+ options = this.data.options || {},
+ mocha_instance = new Mocha(options);
+
+ paths.map(mocha_instance.addFile.bind(mocha_instance));
+
+ // we will now run mocha asynchronously and receive number of errors in a callback,
+ // which we'll use to report the result of the async task by calling done() with
+ // the appropriate value to indicate whether an error occurred
+ var done = this.async();
+ mocha_instance.run(function(errCount) {
+ // => done(false) if there were errors, done(true) if no errors
+ var withoutErrors = (0 === errCount);
+ done(withoutErrors);
+ });
+ };
+
+ var phantomjsRunner = function() {
// Get files as URLs.
var urls = file.expandFileURLs(this.file.src);
@@ -255,6 +278,31 @@ module.exports = function(grunt) {
// All done!
done();
});
+ };
+
+ // ==========================================================================
+ // TASKS
+ // ==========================================================================
+
+ grunt.registerMultiTask('mocha', 'Run Mocha unit tests in a headless PhantomJS instance.', function() {
+ var isHTMLfile = function(it) {
+ return it.toString().indexOf(".html") > -1;
+ };
+ var hasHTMLfile = function(src) {
+ return file.expandFileURLs(src).some(isHTMLfile)
+ }
+
+ if (this.data && this.data.type === 'unit') {
+ // if the user has explicitly specified a unit test
+ unitjsRunner.apply(this, arguments);
+ } else if (this.file && hasHTMLfile(this.file.src)) {
+ // there are .html files present in the src passed, run with phantom
+ phantomjsRunner.apply(this, arguments);
+ } else {
+ // doesn't have any .html files, run as unit test
+ unitjsRunner.apply(this, arguments);
+ }
+
});
// ==========================================================================
Something went wrong with that request. Please try again.