From e7f64a3abf4d39217bf0a30467927c736d5788ba Mon Sep 17 00:00:00 2001 From: Ritchie Martori Date: Tue, 11 Feb 2014 16:01:51 -0800 Subject: [PATCH] Add karma for running browser tests --- Gruntfile.js | 154 +++++++++++++++++++++++++++++++++++++++++ lib/browser-express.js | 7 ++ lib/connectors/mail.js | 7 +- lib/loopback.js | 38 +++++++--- package.json | 27 +++++++- test/geo-point.test.js | 2 +- test/model.test.js | 33 +++++---- test/support.js | 17 ++++- 8 files changed, 254 insertions(+), 31 deletions(-) create mode 100644 Gruntfile.js create mode 100644 lib/browser-express.js diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 00000000..885f909c --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,154 @@ +/*global module:false*/ +module.exports = function(grunt) { + + // Project configuration. + grunt.initConfig({ + // Metadata. + pkg: grunt.file.readJSON('package.json'), + banner: '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + + '<%= grunt.template.today("yyyy-mm-dd") %>\n' + + '<%= pkg.homepage ? "* " + pkg.homepage + "\\n" : "" %>' + + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */\n', + // Task configuration. + uglify: { + options: { + banner: '<%= banner %>' + }, + dist: { + files: { + 'dist/loopback.min.js': ['dist/loopback.js'] + } + } + }, + jshint: { + options: { + jshintrc: true + }, + gruntfile: { + src: 'Gruntfile.js' + }, + lib_test: { + src: ['lib/**/*.js', 'test/**/*.js'] + } + }, + watch: { + gruntfile: { + files: '<%= jshint.gruntfile.src %>', + tasks: ['jshint:gruntfile'] + }, + lib_test: { + files: '<%= jshint.lib_test.src %>', + tasks: ['jshint:lib_test'] + } + }, + browserify: { + dist: { + files: { + 'dist/loopback.js': ['index.js'], + }, + options: { + ignore: ['nodemailer', 'passport'], + standalone: 'loopback' + } + } + }, + karma: { + unit: { + options: { + // base path, that will be used to resolve files and exclude + basePath: '', + + // frameworks to use + frameworks: ['mocha', 'browserify'], + + // list of files / patterns to load in the browser + files: [ + 'test/support.js', + 'test/model.test.js', + 'test/geo-point.test.js' + ], + + // list of files to exclude + exclude: [ + + ], + + // test results reporter to use + // possible values: 'dots', 'progress', 'junit', 'growl', 'coverage' + reporters: ['dots'], + + // web server port + port: 9876, + + // cli runner port + runnerPort: 9100, + + // enable / disable colors in the output (reporters and logs) + colors: true, + + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + logLevel: 'warn', + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: true, + + // Start these browsers, currently available: + // - Chrome + // - ChromeCanary + // - Firefox + // - Opera + // - Safari (only Mac) + // - PhantomJS + // - IE (only Windows) + browsers: [ + 'Chrome', + 'Firefox', + 'Opera', + 'Safari', + 'PhantomJS' + ], + + // If browser does not capture in given timeout [ms], kill it + captureTimeout: 60000, + + // Continuous Integration mode + // if true, it capture browsers, run tests and exit + singleRun: false, + + // Browserify config (all optional) + browserify: { + // extensions: ['.coffee'], + ignore: [ + 'nodemailer', + 'passport', + 'passport-local', + 'superagent', + 'supertest' + ], + // transform: ['coffeeify'], + // debug: true, + // noParse: ['jquery'], + watch: true, + }, + + // Add browserify to preprocessors + preprocessors: {'test/*': ['browserify']} + } + } + } + + }); + + // These plugins provide necessary tasks. + grunt.loadNpmTasks('grunt-browserify'); + grunt.loadNpmTasks('grunt-contrib-uglify'); + grunt.loadNpmTasks('grunt-contrib-jshint'); + grunt.loadNpmTasks('grunt-contrib-watch'); + grunt.loadNpmTasks('grunt-karma'); + + // Default task. + grunt.registerTask('default', ['browserify']); + +}; diff --git a/lib/browser-express.js b/lib/browser-express.js new file mode 100644 index 00000000..386e8159 --- /dev/null +++ b/lib/browser-express.js @@ -0,0 +1,7 @@ +module.exports = browserExpress; + +function browserExpress() { + return {}; +} + +browserExpress.errorHandler = {}; diff --git a/lib/connectors/mail.js b/lib/connectors/mail.js index cb260c72..c0337f46 100644 --- a/lib/connectors/mail.js +++ b/lib/connectors/mail.js @@ -5,6 +5,7 @@ var mailer = require('nodemailer') , assert = require('assert') , debug = require('debug') + , loopback = require('../loopback') , STUB = 'STUB'; /** @@ -22,8 +23,10 @@ function MailConnector(settings) { var transports = settings.transports || []; this.transportsIndex = {}; this.transports = []; - - transports.forEach(this.setupTransport.bind(this)); + + if(loopback.isServer) { + transports.forEach(this.setupTransport.bind(this)); + } } MailConnector.initialize = function(dataSource, callback) { diff --git a/lib/loopback.js b/lib/loopback.js index 568441d7..f4e3dcb6 100644 --- a/lib/loopback.js +++ b/lib/loopback.js @@ -8,11 +8,11 @@ var express = require('express') , EventEmitter = require('events').EventEmitter , path = require('path') , proto = require('./application') - , utils = require('express/node_modules/connect').utils , DataSource = require('loopback-datasource-juggler').DataSource , ModelBuilder = require('loopback-datasource-juggler').ModelBuilder , assert = require('assert') - , i8n = require('inflection'); + , i8n = require('inflection') + , merge = require('util')._extend; /** * `loopback` is the main entry for LoopBack core module. It provides static @@ -28,6 +28,18 @@ var express = require('express') var loopback = exports = module.exports = createApplication; +/** + * Is this a browser environment? + */ + +loopback.isBrowser = typeof window !== 'undefined'; + +/** + * Is this a server environment? + */ + +loopback.isServer = !loopback.isBrowser; + /** * Framework version. */ @@ -55,7 +67,7 @@ loopback.compat = require('./compat'); function createApplication() { var app = express(); - utils.merge(app, proto); + merge(app, proto); // Create a new instance of models registry per each app instance app.models = function() { @@ -80,16 +92,20 @@ for (var key in express) { /*! * Expose additional loopback middleware * for example `loopback.configure` etc. + * + * ***only in node*** */ -fs - .readdirSync(path.join(__dirname, 'middleware')) - .filter(function (file) { - return file.match(/\.js$/); - }) - .forEach(function (m) { - loopback[m.replace(/\.js$/, '')] = require('./middleware/' + m); - }); +if (loopback.isServer) { + fs + .readdirSync(path.join(__dirname, 'middleware')) + .filter(function (file) { + return file.match(/\.js$/); + }) + .forEach(function (m) { + loopback[m.replace(/\.js$/, '')] = require('./middleware/' + m); + }); +} /*! * Error handler title diff --git a/package.json b/package.json index d504b4f6..fea9bc0f 100644 --- a/package.json +++ b/package.json @@ -37,11 +37,36 @@ "strong-task-emitter": "0.0.x", "supertest": "~0.8.1", "chai": "~1.8.1", - "loopback-testing": "~0.1.0" + "loopback-testing": "~0.1.0", + "browserify": "~3.14.1", + "grunt": "~0.4.2", + "grunt-browserify": "~1.3.0", + "grunt-contrib-uglify": "~0.3.2", + "grunt-contrib-jshint": "~0.8.0", + "grunt-contrib-watch": "~0.5.3", + "karma-script-launcher": "~0.1.0", + "karma-chrome-launcher": "~0.1.2", + "karma-firefox-launcher": "~0.1.3", + "karma-html2js-preprocessor": "~0.1.0", + "karma-jasmine": "~0.1.5", + "karma-coffee-preprocessor": "~0.1.2", + "requirejs": "~2.1.10", + "karma-requirejs": "~0.2.1", + "karma-phantomjs-launcher": "~0.1.2", + "karma": "~0.10.9", + "karma-browserify": "0.0.6", + "karma-mocha": "~0.1.1", + "grunt-karma": "~0.6.2" }, "repository": { "type": "git", "url": "https://github.com/strongloop/loopback" }, + "browser": { + "express": "./lib/browser-express.js", + "connect": false, + "passport": false, + "passport-local": false + }, "license": "MIT" } diff --git a/test/geo-point.test.js b/test/geo-point.test.js index 2e436be3..2f3da597 100644 --- a/test/geo-point.test.js +++ b/test/geo-point.test.js @@ -53,4 +53,4 @@ describe('GeoPoint', function() { assert.equal(m.geo.lat, 3.444); }); }); -}); \ No newline at end of file +}); diff --git a/test/model.test.js b/test/model.test.js index b2222888..d291db2b 100644 --- a/test/model.test.js +++ b/test/model.test.js @@ -1,4 +1,6 @@ +require('./support'); var ACL = require('../').ACL; +var loopback = require('../'); describe('Model', function() { @@ -194,21 +196,21 @@ describe('Model', function() { }); }); - describe('Model.deleteById([callback])', function () { - it("Delete a model instance from the attached data source", function (done) { - User.create({first: 'joe', last: 'bob'}, function (err, user) { - User.deleteById(user.id, function (err) { - User.findById(user.id, function (err, notFound) { - assert(!err); - assert.equal(notFound, null); - done(); - }); - }); - }); - }); - }); + describe('Model.deleteById([callback])', function () { + it("Delete a model instance from the attached data source", function (done) { + User.create({first: 'joe', last: 'bob'}, function (err, user) { + User.deleteById(user.id, function (err) { + User.findById(user.id, function (err, notFound) { + assert(!err); + assert.equal(notFound, null); + done(); + }); + }); + }); + }); + }); - describe('Model.destroyAll(callback)', function() { + describe('Model.destroyAll(callback)', function() { it("Delete all Model instances from data source", function(done) { (new TaskEmitter()) .task(User, 'create', {first: 'jill'}) @@ -260,7 +262,8 @@ describe('Model', function() { }); }); - describe('Remote Methods', function(){ + describe.onServer('Remote Methods', function(){ + beforeEach(function () { User.login = function (username, password, fn) { if(username === 'foo' && password === 'bar') { diff --git a/test/support.js b/test/support.js index 85aac797..6737119a 100644 --- a/test/support.js +++ b/test/support.js @@ -11,7 +11,6 @@ app = null; TaskEmitter = require('strong-task-emitter'); request = require('supertest'); - // Speed up the password hashing algorithm // for tests using the built-in User model loopback.User.settings.saltWorkFactor = 4; @@ -50,3 +49,19 @@ assert.isFunc = function (obj, name) { assert(obj, 'cannot assert function ' + name + ' on object that doesnt exist'); assert(typeof obj[name] === 'function', name + ' is not a function'); } + +describe.onServer = function describeOnServer(name, fn) { + if (loopback.isServer) { + describe(name, fn); + } else { + describe.skip(name, fn); + } +}; + +describe.inBrowser = function describeInBrowser(name, fn) { + if (loopback.isBrowser) { + describe(name, fn); + } else { + describe.skip(name, fn); + } +}; \ No newline at end of file