From 3e1ddaf6ebec99c134aa354b73ceb10b9636566f Mon Sep 17 00:00:00 2001 From: Willem Date: Fri, 22 Apr 2016 10:34:11 +0200 Subject: [PATCH] wip: made karma test running --- README.md | 2 + es5-ff-spa-loader.js | 2 +- example/package.json | 23 ------ gulpfile.js | 20 ++++-- package.json | 42 ++++++++--- test/jshint.spec.js | 3 - test/jshint.spec.js.bak | 3 + test/karma.conf.js | 50 ++++++++++++++ test/spec-runner.js | 26 +++++++ test/spec/test-boot.js | 33 +++++++++ test/spec/test-exports.js | 37 ++++++++++ test/spec/test-factory.js | 21 ++++++ test/test-exports.js | 32 --------- test/test-factory.js | 23 ------ test/test-server.js | 142 ++++++++++++++++++++++++++++++++++++++ 15 files changed, 360 insertions(+), 99 deletions(-) delete mode 100644 example/package.json delete mode 100644 test/jshint.spec.js create mode 100644 test/jshint.spec.js.bak create mode 100644 test/karma.conf.js create mode 100644 test/spec-runner.js create mode 100644 test/spec/test-boot.js create mode 100644 test/spec/test-exports.js create mode 100644 test/spec/test-factory.js delete mode 100644 test/test-exports.js delete mode 100644 test/test-factory.js create mode 100644 test/test-server.js diff --git a/README.md b/README.md index 6fee42a..0fb647f 100644 --- a/README.md +++ b/README.md @@ -226,6 +226,8 @@ Add unit tests for any new or changed functionality. Lint and test your code. * Added auto resetting server.url when is file:// for "Page save as" feature. * Fixed askUrl to accept host and context path with auto clean/depathing. * Fixed duplicate starts on askUrl submit multiple clicks. +* Switch to karma + jasmine unit tests. +* Fixed define.amd module constructor. ### 0.2.1 * Fixed clearCache method to added json header. diff --git a/es5-ff-spa-loader.js b/es5-ff-spa-loader.js index 3b400a9..dfcc189 100644 --- a/es5-ff-spa-loader.js +++ b/es5-ff-spa-loader.js @@ -32,7 +32,7 @@ */ (function (root, factory) { if ( typeof define === 'function' && define.amd ) { - define('FFSpaLoader', factory(root)); + define(factory(root)); } else if ( typeof exports === 'object' ) { module.exports = factory(root); } else { diff --git a/example/package.json b/example/package.json deleted file mode 100644 index 9d8d3a8..0000000 --- a/example/package.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "es5-ff-spa-loader-example", - "description": "es5-ff-spa-loader-example", - "version": "1.0.0", - "private": true, - "scripts": { - "start": "node app_server/example.js" - }, - "dependencies": { - "angular": "^1.4.8", - "angular-route": "^1.4.8", - "bootstrap": "3.3.x", - "cors": "^2.7.1", - "ejs": "2.3.x", - "express": "4.11.x", - "fetch": "0.3.x", - "jquery": "^2.1.4", - "jshashes": "^1.0.5", - "morgan": "^1.6.1", - "minify": "1.4.x", - "uglify-js": "^2.6.1" - } -} diff --git a/gulpfile.js b/gulpfile.js index ef25a0a..d0e3f11 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -4,10 +4,10 @@ var uglify = require('gulp-uglify'); var rename = require('gulp-rename'); var clean = require('gulp-clean'); var srcmaps = require('gulp-sourcemaps'); -var mocha = require('gulp-mocha'); var jsdoc = require('gulp-jsdoc3'); var cssnano = require('gulp-cssnano'); - +var karma = require('karma'); +var testServer = require('./test/test-server.js'); var srcPath = './'; var srcFile = 'es5-ff-spa-loader.js'; var srcCss = 'es5-ff-spa-loader.css'; @@ -45,9 +45,17 @@ gulp.task('buildCssMin',['buildCss'], function () { .pipe(gulp.dest(distPathCSS)); }); -gulp.task('testMocha',['clean'], function() { - process.env.JUNIT_REPORT_PATH = 'test/data/report.xml'; - return gulp.src(testSrc, {read: false}).pipe(mocha({reporter: 'mocha-jenkins-reporter'})); +gulp.task('testKarma',['clean'], function (done) { + testServer.start(function () { + new karma.Server({ + configFile: __dirname + '/test/karma.conf.js', + singleRun: true + }, function(exitCode) { + console.log('Karma exit code:', exitCode); + //done(); + testServer.stop(done); + }).start(); + }); }); gulp.task('buildJSDoc', function (cb) { @@ -64,7 +72,7 @@ gulp.task('buildJSDoc', function (cb) { gulp.src(srcFile, {read: false}).pipe(jsdoc(jsdocConfig, cb)); }); -gulp.task('test', ['clean','testMocha']); +gulp.task('test', ['clean','testKarma']); gulp.task('build', ['test','buildCssMin','buildScriptMin'/*,'buildJSDoc'*/]); diff --git a/package.json b/package.json index e4691d6..4dcce17 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,8 @@ "prepublish": "gulp build", "clean": "gulp clean", "build": "gulp build", - "test": "gulp test" + "test": "gulp test", + "start": "node example/app_server/example.js" }, "author": "Willem (http://forwardfire.net/)", "keywords": [ @@ -35,16 +36,35 @@ "url": "https://bitbucket.org/im_ik/es5-ff-spa-loader/issues" }, "devDependencies": { - "gulp": "^3.9.0", - "gulp-clean": "^0.3.1", - "gulp-cssnano": "^2.1.1", - "gulp-jsdoc3": "^0.1.0", - "gulp-mocha": "^2.2.0", - "gulp-rename": "^1.2.2", - "gulp-sourcemaps": "^1.6.0", - "gulp-uglify": "^1.5.1", - "mocha-jenkins-reporter": "^0.1.9", - "mocha-jshint": "^2.2.6" + "gulp": "3.9.0", + "gulp-clean": "0.3.1", + "gulp-cssnano": "2.1.1", + "gulp-jsdoc3": "0.1.0", + "gulp-rename": "1.2.2", + "gulp-sourcemaps": "1.6.0", + "gulp-uglify": "1.5.1", + "jasmine-core": "2.4.1", + "requirejs": "2.2.0", + "socket.io": "1.3.7", + "karma": "0.13.9", + "karma-coverage": "0.5.5", + "karma-jasmine": "0.3.8", + "karma-phantomjs-launcher": "1.0.0", + "karma-requirejs": "0.2.6", + "karma-mocha-reporter": "2.0.1", + "karma-junit-reporter": "0.4.2", + "angular": "^1.4.8", + "angular-route": "^1.4.8", + "bootstrap": "3.3.x", + "cors": "^2.7.1", + "ejs": "2.3.x", + "express": "4.11.x", + "fetch": "0.3.x", + "jquery": "^2.1.4", + "jshashes": "^1.0.5", + "morgan": "^1.6.1", + "minify": "1.4.x", + "uglify-js": "^2.6.1" }, "dependencies": {}, "files": [ diff --git a/test/jshint.spec.js b/test/jshint.spec.js deleted file mode 100644 index a6665ff..0000000 --- a/test/jshint.spec.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -require('mocha-jshint')(['es5-ff-spa-loader.js','example']); diff --git a/test/jshint.spec.js.bak b/test/jshint.spec.js.bak new file mode 100644 index 0000000..1cb92f9 --- /dev/null +++ b/test/jshint.spec.js.bak @@ -0,0 +1,3 @@ +'use strict'; + +//require('mocha-jshint')(['es5-ff-spa-loader.js','example']); diff --git a/test/karma.conf.js b/test/karma.conf.js new file mode 100644 index 0000000..a2a47e8 --- /dev/null +++ b/test/karma.conf.js @@ -0,0 +1,50 @@ +module.exports = function(config) { + config.set({ + basePath: '..', + frameworks: ['jasmine', 'requirejs'], + files: [{ + pattern: 'es5-ff-spa-loader.js', + included: false + }, { + pattern: 'test/spec/**/*.js', + included: false + }, + 'test/spec-runner.js'], + + preprocessors: { + 'es5-ff-spa-loader.js': ['coverage'] + }, + reporters: ['coverage', 'mocha', 'junit'], + + // the default configuration + junitReporter: { + outputDir: 'test/data/coverage', // results will be saved as $outputDir/$browserName.xml + outputFile: undefined, // if included, results will be saved as $outputDir/$browserName/$outputFile + suite: '', // suite will become the package name attribute in xml testsuite element + useBrowserName: false, // add browser name to report and classes names + nameFormatter: undefined, // function (browser, result) to customize the name attribute in xml testcase element + classNameFormatter: undefined, // function (browser, result) to customize the classname attribute in xml testcase element, + properties: {} // key value pair of properties to add to the section of the report + }, + + plugins: [ + 'karma-requirejs', + 'karma-coverage', + 'karma-phantomjs-launcher', + 'karma-jasmine', + 'karma-mocha-reporter', + 'karma-junit-reporter' + ], + + coverageReporter: { + // specify a common output directory + dir: 'test/data/coverage', + reporters: [ + { type: 'lcov', subdir: 'report-lcov' }, + { type: 'lcovonly', subdir: '.', file: 'report-lcovonly.txt' } + ] + }, + + browsers: ['PhantomJS'] + }); +}; \ No newline at end of file diff --git a/test/spec-runner.js b/test/spec-runner.js new file mode 100644 index 0000000..77a9923 --- /dev/null +++ b/test/spec-runner.js @@ -0,0 +1,26 @@ +var testModules = []; +var TEST_REGEXP = /^\/base\/test\/spec\/\S*.js$/i; + +var pathToModule = function(path) { + return path.replace(/^\/base\//, '').replace(/\.js$/, ''); +}; + +testModules.push('es5-ff-spa-loader'); + +Object.keys(window.__karma__.files).forEach(function(file) { + //console.log('file: '+file); + if (TEST_REGEXP.test(file)) { + var fileNew = pathToModule(file); + testModules.push(fileNew); + //console.log('file: '+file+' fileNew: '+fileNew); + } +}); + +require.config({ + baseUrl : '/base/', + enforceDefine : true, + xhtml : false, + waitSeconds : 30, + deps : testModules, + callback : window.__karma__.start +}); \ No newline at end of file diff --git a/test/spec/test-boot.js b/test/spec/test-boot.js new file mode 100644 index 0000000..6845223 --- /dev/null +++ b/test/spec/test-boot.js @@ -0,0 +1,33 @@ +'use strict'; + +define(['es5-ff-spa-loader'], function(FFSpaLoader) { + describe('Start loader', function() { + it('FFSpaLoader start with error', function(done) { + FFSpaLoader.options.server.url = 'http://localhost:9999'; + FFSpaLoader.options.error.handler = function(err) { + done(); + }; + FFSpaLoader.start(); + }); + it('FFSpaLoader starts', function(done) { + FFSpaLoader.options.server.url = 'http://localhost:9090/test'; + FFSpaLoader.options.server.assets = '/static/spa-client-resources'; + FFSpaLoader.options.error.handler = function(err) { + fail(); // TIODI + }; + FFSpaLoader.start(function() { + done(); + }); + }); + it('FFSpaLoader starts again', function(done) { + FFSpaLoader.options.server.url = 'http://localhost:9090/test'; + FFSpaLoader.options.server.assets = '/static/spa-client-resources'; + FFSpaLoader.options.error.handler = function(err) { + fail(); // TIODI + }; + FFSpaLoader.start(function() { + done(); + }); + }); + }); +}); diff --git a/test/spec/test-exports.js b/test/spec/test-exports.js new file mode 100644 index 0000000..12cda77 --- /dev/null +++ b/test/spec/test-exports.js @@ -0,0 +1,37 @@ +'use strict'; + +define(['es5-ff-spa-loader'], function(FFSpaLoader) { + describe('Check module exports', function() { + describe('Check undefined', function() { + it('FFSpaLoader.options should be defined', function() { + expect(FFSpaLoader.options === undefined).toEqual(false); + }); + it('FFSpaLoader.factory should be defined', function() { + expect(FFSpaLoader.factory === undefined).toEqual(false); + }); + it('FFSpaLoader.start should be defined', function() { + expect(FFSpaLoader.start === undefined).toEqual(false); + }); + it('FFSpaLoader.clearServerUrl should be defined', function() { + expect(FFSpaLoader.clearServerUrl === undefined).toEqual(false); + }); + it('FFSpaLoader.clearCache should be defined', function() { + expect(FFSpaLoader.clearCache === undefined).toEqual(false); + }); + }); + describe('Check function', function() { + it('FFSpaLoader.start should be function', function() { + expect(typeof FFSpaLoader.start === 'function').toEqual(true); + }); + it('FFSpaLoader.clearServerUrl should be function', function() { + expect(typeof FFSpaLoader.clearServerUrl === 'function').toEqual(true); + }); + it('FFSpaLoader.clearCache should be function', function() { + expect(typeof FFSpaLoader.clearCache === 'function').toEqual(true); + }); + }); + }); +}); + + + diff --git a/test/spec/test-factory.js b/test/spec/test-factory.js new file mode 100644 index 0000000..a3b4845 --- /dev/null +++ b/test/spec/test-factory.js @@ -0,0 +1,21 @@ +'use strict'; + +define(['es5-ff-spa-loader'], function(FFSpaLoader) { + describe('Check factory detect', function() { + it('FFSpaLoader.factory.detect.localStorage', function() { + expect(FFSpaLoader.factory.detect.localStorage()).toEqual(true); + }); + it('FFSpaLoader.factory.detect.openDatabase', function() { + expect(FFSpaLoader.factory.detect.openDatabase()).toEqual(true); + }); + it('FFSpaLoader.factory.detect.sqlitePlugin', function() { + expect(FFSpaLoader.factory.detect.sqlitePlugin()).toEqual(false); + }); + it('FFSpaLoader.factory.detect.cordova', function() { + expect(FFSpaLoader.factory.detect.cordova()).toEqual(false); + }); + it('FFSpaLoader.factory.detect.cordovaDevice', function() { + expect(FFSpaLoader.factory.detect.cordovaDevice()).toEqual(false); + }); + }); +}); diff --git a/test/test-exports.js b/test/test-exports.js deleted file mode 100644 index ed891f4..0000000 --- a/test/test-exports.js +++ /dev/null @@ -1,32 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var FFSpaLoader = require('../es5-ff-spa-loader'); - -describe('Check module exports', function() { - it('FFSpaLoader.options should be defined', function() { - assert.equal(false, FFSpaLoader.options === undefined); - }); - it('FFSpaLoader.factory should be defined', function() { - assert.equal(false, FFSpaLoader.factory === undefined); - }); - it('FFSpaLoader.start should be defined', function() { - assert.equal(false, FFSpaLoader.start === undefined); - }); - it('FFSpaLoader.start should be function', function() { - assert.equal(true, typeof FFSpaLoader.start === 'function'); - }); - it('FFSpaLoader.clearServerUrl should be defined', function() { - assert.equal(false, FFSpaLoader.clearServerUrl === undefined); - }); - it('FFSpaLoader.clearServerUrl should be function', function() { - assert.equal(true, typeof FFSpaLoader.clearServerUrl === 'function'); - }); - it('FFSpaLoader.clearCache should be defined', function() { - assert.equal(false, FFSpaLoader.clearCache === undefined); - }); - it('FFSpaLoader.clearCache should be function', function() { - assert.equal(true, typeof FFSpaLoader.clearCache === 'function'); - }); -}); - diff --git a/test/test-factory.js b/test/test-factory.js deleted file mode 100644 index 606056e..0000000 --- a/test/test-factory.js +++ /dev/null @@ -1,23 +0,0 @@ -'use strict'; - -var assert = require('assert'); -var FFSpaLoader = require('../es5-ff-spa-loader'); - -describe('Check factory detect', function() { - it('FFSpaLoader.factory.detect.localStorage', function() { - assert.equal(false, FFSpaLoader.factory.detect.localStorage()); - }); - it('FFSpaLoader.factory.detect.openDatabase', function() { - assert.equal(false, FFSpaLoader.factory.detect.openDatabase()); - }); - it('FFSpaLoader.factory.detect.sqlitePlugin', function() { - assert.equal(false, FFSpaLoader.factory.detect.sqlitePlugin()); - }); - it('FFSpaLoader.factory.detect.cordova', function() { - assert.equal(false, FFSpaLoader.factory.detect.cordova()); - }); - it('FFSpaLoader.factory.detect.cordovaDevice', function() { - assert.equal(false, FFSpaLoader.factory.detect.cordovaDevice()); - }); -}); - diff --git a/test/test-server.js b/test/test-server.js new file mode 100644 index 0000000..b1efc4a --- /dev/null +++ b/test/test-server.js @@ -0,0 +1,142 @@ +'use strict'; + +var httpPort = 9090; +var express = require('express'); +var path = require('path'); +var fs = require('fs'); +var fetch = require('fetch'); +var cors = require('cors'); +var morgan = require('morgan'); +var UglifyJS = require("uglify-js"); +var Hashes = require('jshashes'); +var minify = require('minify'); + +var clientResourcesWeb = []; +var clientResources = { + js: [], + css: [], + dss: [] +}; + +var addClientResource = function(clientResource, resourceType) { + clientResources[resourceType].push(clientResource); +}; + +var fetchHashResource = function(fetchEntry,cb) { + var serverUrl = 'http://localhost:'+httpPort; + var hashDigest = new Hashes.SHA1; + fetch.fetchUrl(serverUrl + fetchEntry.url,function(err, meta, data) { + if (err !== null) { return cb(err); } + var assetHash = hashDigest.hex(''+data); + clientResourcesWeb.push({ + url: fetchEntry.url, + type: fetchEntry.type, + hash: assetHash + }); + cb(null); + }); +}; + +var fetchHashResources = function(fetchList, cb) { + var resourceStack = fetchList; + var resourceLoader = function() { + resourceStack = resourceStack.slice(1); + if (resourceStack.length === 0) { + cb(null); + } else { + fetchHashResource(resourceStack[0],resourceLoader); + } + }; + fetchHashResource(resourceStack[0],resourceLoader); +}; + +var createClientResourceFetchList = function() { + var fetchList = []; + for (var clientResourceIdxJs in clientResources.js) { + var urlJs = clientResources.js[clientResourceIdxJs]; + fetchList.push({url:urlJs,type:'js'}); + } + for (var clientResourceIdxCss in clientResources.css) { + var urlCss = clientResources.css[clientResourceIdxCss]; + fetchList.push({url:urlCss,type:'css'}); + } + for (var clientResourceIdxCssData in clientResources.dss) { + var urlCssData = clientResources.cssData[clientResourceIdxCssData]; + fetchList.push({url:urlCssData,type:'dss'}); + } + return fetchList; +}; + +function renderTemplatePath(viewPath) { + return function (req, res) { + res.locals.query = req.query; + var qi = req.url.indexOf('?'); + if (qi === -1) { + qi = req.url.length; + } + res.render(viewPath + req.url.substring(req.route.path.length-1, qi)); + }; +} + +function renderIndex() { + return function (req, res) { + var inlineScript = UglifyJS.minify(__dirname+'/../../es5-ff-spa-loader.js'); + minify(__dirname+'/../../es5-ff-spa-loader.css', {}, function(err, data) { + res.render('index', { + inlineScript: inlineScript.code, + inlineStyle: data + }); + }); + }; +} + +// Add resources ORDERED per type +addClientResource('/static/module/jquery/jquery.js','js'); +//addClientResource('/static/module/angular/angular.js','js'); +//addClientResource('/static/module/angular-route/angular-route.js','js'); +addClientResource('/static/module/bootstrap/css/bootstrap.css','css'); +addClientResource('/static/module/bootstrap/js/bootstrap.js','js'); +//addClientResource('/static/css/boot.css','css'); +//addClientResource('/static/css/style.css','css'); +//addClientResource('/static/js/example-app.js','js'); // deps: jquery,angular +//addClientResource('/static/js/controller/page-bar.js','js'); // deps: example-app.js +//addClientResource('/static/js/controller/page-foo.js','js'); +//addClientResource('/static/js/controller/page-index.js','js'); + +var appPath = '/test'; +var server = express(); +server.use(morgan('dev')); +server.use(cors({credentials: true, origin: '*', exposedHeaders: ['X-My-Api']})); +server.set('view engine', 'ejs'); +server.set('views', path.join(__dirname,'www_views')); +server.use(function(req, res, next) { res.header('X-My-Api', 'noknok');next(); }); +server.use(appPath+'/static', express.static(path.join(__dirname,'www_static'))); +server.use(appPath+'/static/module/bootstrap', express.static(path.join(__dirname,'../node_modules/bootstrap/dist'))); +server.use(appPath+'/static/module/jquery', express.static(path.join(__dirname,'../node_modules/jquery/dist'))); +server.use(appPath+'/static/module/angular', express.static(path.join(__dirname,'../node_modules/angular'))); +server.use(appPath+'/static/module/angular-route', express.static(path.join(__dirname,'../node_modules/angular-route'))); +server.get(appPath+'/static/spa-client-resources', function (req,res) {res.json({data: {resources: clientResourcesWeb}});}); +server.get(appPath+'/static/spa-loader.css', function (req,res) {res.sendFile('es5-ff-spa-loader.css', { root: path.join(__dirname, '/../../') });}); +server.get(appPath+'/', function (req, res) {res.redirect(appPath+'/example-ui');}); +server.get(appPath+'/example-ui/thtml/*', renderTemplatePath('thtml/')); +server.get(appPath+'/example-ui', renderIndex()); +server.get('/', function (req, res) {res.redirect(appPath);}); + +var serverPort = null; + +module.exports={ + start: function(cb) { + serverPort = server.listen(httpPort); + console.info('Server started on port '+httpPort); + + var res = createClientResourceFetchList(); + fetchHashResources(res, function(err) { + cb(err); + console.log('Total assets build: '+clientResourcesWeb.length); + }); + }, + stop: function(cb) { + serverPort.close(cb); + } +}; +