Friday, July 20, 2018

Angular.js+nw.js+webpack+karma+jasmine how to test

Leave a Comment

I have a nw.js native application with angular.js inside. My app bundled with webpack and contains native node.js modules. My entry point is index.js file that I organized like this:

var gui = require('nw.gui'); var angular = require('angular'); require('./app.css');  // other modules  var myApp = angular.module('myApp', [     'ngRaven',     'ngMaterial',     'ngMessages' ]).constant(     'fs', require('fs') )  require('./services')(myApp); require('./directives')(myApp); require('./factories')(myApp); require('./filters')(myApp); require('./controllers')(myApp); require('./app.js')(myApp); 

My webpack config looks like this:

const path = require('path');  const config = {     entry: [         './app/index.js'     ],     output: {         path: path.resolve(__dirname, 'app'),         filename: 'bundle.js'     },     devtool: "source-map",     target: 'node-webkit',     module:{         // css, html loaders     },     node: {         os: true,         fs: true,         child_process: true,         __dirname: true,         __filename: true     } };  module.exports = config; 

So every dependency include Node.js native modules like fs, path, child_process bundled in one big file bundle.js that i include in html and then package my nw.js app. So my app structure looks like:

my_project: --app ----controllers ------welcome --------welcome.js // Page controller --------welcome.html // Page HTML ------index.js // here I include each page controller ----app.js // My angular app initialization ----index.js // here I include all dependencies  

I'm trying to run tests with this structure. I tried karma+jasmine, karma+mocha, tried different configurations, my last one looks like:

module.exports = function (config) {     config.set({         basePath: '',         frameworks: ['jasmine'],         files: [             'app/bundle.js',             'app/**/*spec.js'         ],         exclude: [],         preprocessors: {             'app/bundle.js': ['webpack']         },         reporters: ['progress'],         port: 9876,         colors: true,         logLevel: config.LOG_INFO,         autoWatch: true,         browsers: ['ChromeHeadlessNoSandbox'],         customLaunchers: {             ChromeHeadlessNoSandbox: {                 base: 'ChromeHeadless',                 flags: ['--no-sandbox']             }         },         singleRun: true,          webpack: {             // you don't need to specify the entry option because             // karma watches the test entry points             // webpack watches dependencies              // ... remainder of webpack configuration (or import)         },          webpackMiddleware: {             // webpack-dev-middleware configuration             // i.e.             noInfo: true,             // and use stats to turn off verbose output             stats: {                 // options i.e.                 chunks: false             }         }     }); }; 

But my tests still not see the angular application.

describe('Welcome page', function() {     beforeEach(angular.mock.module('WelcomePageCtrl')); }); 

P.S I don't require exactly karma and jasminne, so any solution will be appreciated. I just want to cover my project with tests

1 Answers

Answers 1

I have gone through something similar myself. I don't think you need the bundle.js for your tests.

Here is how would do it:

I assume your controller/service/etc implementation is as follows:

    /* app/controller/welcome.js */     module.exports = function(app) {         return app.controller("MyCtrl", function($scope) {             $scope.name = "lebouf";         });     }; 

I like my test code to sit right beside the code I'm testing (Welcome.spec.js in the same directory as Welcome.js). That test code would look like so:

/* app/controller/welcome.spec.js */ beforeEach(function() {     var app = angular.module("myApp", []); //module definition     require("./welcome")(app); }); beforeEach(mockModule("myApp"));  describe("MyCtrl", () => {     var scope = {};     beforeEach(mockInject(function($controller) {         $controller("MyCtrl", {             $scope: scope         });     }));      it("has name in its scope", function() {         expect(scope.name).to.equal("not lebouf"); //the test will fail     }); }); 

Except, this is an angular controller we're testing and it's not that simple. We need the angular object itself. So lets set it up. I'll explain why it is done how it is done next:

/* test-setup.js */ const jsdom = require("jsdom").jsdom; //v5.6.1 const chai = require("chai");  global.document = jsdom("<html><head></head><body></body></html>", {}); global.window = document.defaultView; global.window.mocha = true; global.window.beforeEach = beforeEach; global.window.afterEach = afterEach;  require("angular/angular"); require("angular-mocks");  global.angular = window.angular; global.mockInject = angular.mock.inject; global.mockModule = angular.mock.module; global.expect = chai.expect;  console.log("ALL SET"); 

And we'll run the tests as:

node_modules/.bin/mocha ./init.js app/**/*.spec.js #or preferably as `npm test` by copying the abev statement into package.json 

extra info

Here is how init.js is setup as is:

  1. jsdom: f you require("angular/angular") you'll see that it needs a window instance. jsdom can create documents and windows and so on without a web browser!
  2. window.mocha: we need angular-mocks to populate our angular with the necessary utilities. But if you look at the code you'll notice that window.mocha || window.jasmine needs to be true. Thats whywindow.mocha = true`
  3. window.beforeEach, window.afterEach: the same reason as above; because angular-mocks.js demands it.
  4. I set some global variables that I plan to use commonly in my tests: angular, expect, mockInject, mockModule.

Also these may provide some additional information:

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment