I recently started the transition with a custom gulp script that was used to handle all sorts of things in webpack. I am working on the fact that the translation, integration and maintenance of the client application in the browser works fine.
Now that I used gulp to run my karma tests complete with the app.js file, the gulp script first linked the app.js file and then put it in the dist folder. This file will then be used by karma to run tests against it. My gulp test task will also keep track of any changes to the test file or to modify the package file and restart the tests based on this.
With webpack, I understand that dist / app.js is in memory and not written to disk (at least the way I configured it). The problem is that apparently my linked application (which gets great using webpack-dev-server --open ) for some reason does not load with karma, and I cannot figure out what the missing part of the puzzle is .
This is what my folder structure looks like (I left only the simplest things that may be relevant to the problem):
package.json webpack.config.js karma.conf.js src/ --app/ ----[other files/subfolders] ----app.ts ----index.ts --boot.ts --index.html tests/ --common/ ----services/ ------account.service.spec.js
This is my webpack.config.js
var path = require("path"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const CleanWebpackPlugin = require("clean-webpack-plugin"); const ForkTsCheckerWebpackPlugin = require("fork-ts-checker-webpack-plugin"); module.exports = { context: path.join(__dirname), entry: "./src/boot.ts", plugins: [ new webpack.HotModuleReplacementPlugin(), new ForkTsCheckerWebpackPlugin(), new CleanWebpackPlugin(["dist"]), new HtmlWebpackPlugin({ template: "./src/index.html" }) ], module: { rules: [ { test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader" }, { loader: "sass-loader" }] }, { test: /\.tsx?$/, use: [{ loader: "ts-loader", options: { transpileOnly: true, exclude: /node_modules/ } }] }, { test: /\.html$/, loaders: "html-loader", options: { attrs: [":data-src"], minimize: true } } ] }, resolve: { extensions: [".tsx", ".ts", ".js"], alias: { "common": path.resolve(__dirname, "src/app/common"), "common/*": path.resolve(__dirname, "src/app/common/*"), "modules": path.resolve(__dirname, "src/app/modules"), "modules/*": path.resolve(__dirname, "src/app/modules/*"), } }, output: { filename: "app.js", path: path.resolve(__dirname, "dist") }, devtool: "inline-source-map", devServer: { historyApiFallback: true, hot: false, contentBase: path.resolve(__dirname, "dist") } };
This is my karma.conf.js
const webpackConfig = require("./webpack.config"); module.exports = function (config) { config.set({ frameworks: ["jasmine"], files: [ "node_modules/angular/angular.js", "node_modules/angular-mocks/angular-mocks.js", "dist/app.js", // not sure about this "tests/common/*.spec.js", "tests/common/**/*.spec.js" ], preprocessors: { "dist/app.js": ["webpack", "sourcemap"], // not sure about this either "tests/common/*.spec.js": ["webpack", "sourcemap"], "tests/common/**/*.spec.js": ["webpack", "sourcemap"] }, webpack: webpackConfig, webpackMiddleware: { noInfo: true, stats: { chunks: false } }, reporters: ["progress", "coverage"], // , "teamcity"], coverageReporter: { dir: "coverage", reporters: [ { type: "html", subdir: "html" }, { type: "text-summary" } ] }, port: 9876, colors: true, logLevel: config.LOG_INFO, autoWatch: true, browsers: [ "PhantomJS" //"Chrome" ], singleRun: false, concurrency: Infinity, browserNoActivityTimeout: 100000 }); };
This is boot.ts, which is basically the entry point to the application:
import * as app from "./app/app"; import "./styles/app.scss"; // This never gets written to the console // so I know it never gets loaded by karma console.log("I NEVER OUTPUT TO CONSOLE");
This is the app.ts application (which then links to something below:
import * as ng from "angular"; import * as _ from "lodash"; import "@uirouter/angularjs"; import "angular-cookies"; import "angular-material" import "angular-local-storage"; import "angular-sanitize"; import "angular-messages"; import "angular-file-saver"; import "angular-loading-bar"; import "satellizer"; export * from "./index"; import * as Module from "common/module"; import * as AuthModule from "modules/auth/module"; import * as UserModule from "modules/user/module"; import { MyAppConfig } from "./app.config"; import { MyAppRun } from "./app.run"; export default ng.module("MyApp", [ "ngCookies", "ngSanitize", "ngMessages", "ngFileSaver", "LocalStorageModule", "ui.router", "ngMaterial", "satellizer", "angular-loading-bar", Module.name, AuthModule.name, UserModule.name ]) .config(MyAppConfig) .run(MyAppRun);
And finally, this is account.service.spec.js
describe("Account service", function () { // SETUP var _AccountService; beforeEach(angular.mock.module("MyApp.Common")); beforeEach(angular.mock.inject(function (_AccountService_) { // CODE NEVER GETS IN HERE EITHER console.log("I NEVER OUTPUT TO CONSOLE"); _AccountService = _AccountService_; })); function expectValidPassword(result) { expect(result).toEqual({ minCharacters: true, lowercase: true, uppercase: true, digits: true, isValid: true }); } // TESTS describe(".validatePassword()", function () { describe("on valid password", function () { it("returns valid true state", function () { expectValidPassword(_AccountService.validatePassword("asdfASDF123")); expectValidPassword(_AccountService.validatePassword("as#dfAS!DF123%")); expectValidPassword(_AccountService.validatePassword("aA1234%$2")); expectValidPassword(_AccountService.validatePassword(" YYyy22!@ ")); expectValidPassword(_AccountService.validatePassword("Ma#38Hr$")); expectValidPassword(_AccountService.validatePassword("aA1\"#$%(#/$\"#$/(=/#$=!\")(\")(")); }) }); }); });
And this is the result of starting karma start
> npm test > myapp@1.0.0 test E:\projects\Whatever > karma start clean-webpack-plugin: E:\projects\Whatever\dist has been removed. Starting type checking service... Using 1 worker with 2048MB memory limit 31 10 2017 21:47:23.372:WARN [watcher]: Pattern "E:/projects/Whatever/dist/app.js" does not match any file. 31 10 2017 21:47:23.376:WARN [watcher]: Pattern "E:/projects/Whatever/tests/common/*.spec.js" does not match any file. ts-loader: Using typescript@2.4.2 and E:\projects\Whatever\tsconfig.json No type errors found Version: typescript 2.4.2 Time: 2468ms 31 10 2017 21:47:31.991:WARN [karma]: No captured browser, open http://localhost:9876/ 31 10 2017 21:47:32.004:INFO [karma]: Karma v1.7.1 server started at http://0.0.0.0:9876/ 31 10 2017 21:47:32.004:INFO [launcher]: Launching browser PhantomJS with unlimited concurrency 31 10 2017 21:47:32.010:INFO [launcher]: Starting browser PhantomJS 31 10 2017 21:47:35.142:INFO [PhantomJS 2.1.1 (Windows 8 0.0.0)]: Connected on socket PT-pno0eF3hlcdNEAAAA with id 71358105 PhantomJS 2.1.1 (Windows 8 0.0.0) Account service .validatePassword() on valid password returns valid true state FAILED forEach@node _modules/angular/angular.js:410:24 loadModules@node _modules/angular/angular.js:4917:12 createInjector@node _modules/angular/angular.js:4839:30 WorkFn@node _modules/angular-mocks/angular-mocks.js:3172:60 loaded@http ://localhost:9876/context.js:162:17 node_modules/angular/angular.js:4958:53 TypeError: undefined is not an object (evaluating '_AccountService.validatePassword') in tests/common/services/account.service.spec.js (line 742) webpack:///tests/common/services/account.service.spec.js:27:0 <- tests/common/services/account.service.spec.js:742:44 loaded@http ://localhost:9876/context.js:162:17 PhantomJS 2.1.1 (Windows 8 0.0.0) Account service .validatePassword() on valid amount of characters returns minCharacters true FAILED forEach@node _modules/angular/angular.js:410:24 loadModules@node _modules/angular/angular.js:4917:12 createInjector@node _modules/angular/angular.js:4839:30 WorkFn@node _modules/angular-mocks/angular-mocks.js:3172:60 node_modules/angular/angular.js:4958:53 TypeError: undefined is not an object (evaluating '_AccountService.validatePassword') in tests/common/services/account.service.spec.js (line 753) webpack:///tests/common/services/account.service.spec.js:38:0 <- tests/common/services/account.service.spec.js:753:37 PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 2 of 2 (2 FAILED) ERROR (0.017 secs / 0.015 secs)
Pay attention to a few places where I left console.logs, which never start. As I know, the application does not load. It is also a fact that jasmine cannot introduce the service that I want to test.
I use:
karma v1.7.1 karma-webpack v2.0.5 webpack v3.3.0
Any ideas? What am I doing wrong? I get the impression that my webpack.config.js should bundle my AngularJS / TS application and then essentially feed it karma, but for some reason this does not work. Or do I have a fundamental misconception about how this should work?
Thanks.
I extracted some files in a simple application and put it on github so that the problem can be easily reproduced.
npm install
Edit:
I managed to run the tests by replacing:
"dist/app.js"
in setting up karma with
"src/boot.ts"
but it will not reduce or load / import the rest of the application. Then I tried to import only the class that I want to check for the specification, but then I could not make fun of any DI-injected services that the class I'm testing is using. Anyway, I pretty much gave up on this at this stage and stopped trying to figure out how to do this by moving to ang2 +.