5

我最近开始从用于处理各种事情的自定义 gulp 脚本迁移到 webpack。我已经让它在浏览器中转换、捆绑和提供客户端应用程序工作得很好。

现在,当我使用 gulp 对捆绑的 app.js 文件运行我的 karma 测试时,gulp 脚本将首先捆绑 app.js 文件,然后将其放入 dist 文件夹。然后这个文件将被 karma 用来针对它运行测试。我的 gulp 测试任务还将监视任何测试文件更改或捆绑文件更改,并基于此重新运行测试。

使用 webpack,我知道这个 dist/app.js 驻留在内存中,而不是被写入磁盘(至少我是这样设置的)。这样做的问题是,似乎我的捆绑应用程序(与webpack-dev-server --open.

这就是我的文件夹结构的样子(我只留下了可能与问题相关的最基本的东西):

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

这是我的 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")
  }
};

这是我的 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
  });
};

这是 boot.ts,它基本上是应用程序的入口点:

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"); 

这是 app.ts (然后引用它下面的任何内容:

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);

最后,这是 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\"#$%(#/$\"#$/(=/#$=!\")(\")("));
      })
    });
  });
});

这是运行的输出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)

请注意我离开 console.logs 的几个地方永远不会被触发。这就是我知道应用程序没有加载的方式。那以及茉莉花无法注入我要测试的服务的事实。

我在用着:

karma v1.7.1
karma-webpack v2.0.5
webpack v3.3.0

有任何想法吗?我究竟做错了什么?我的印象是我的 webpack.config.js 应该捆绑我的 AngularJS/TS 应用程序,然后基本上将其提供给业力,但无论出于何种原因,这似乎不起作用。还是我对这应该如何工作有一些基本的误解?

谢谢你。

我将一些文件提取到一个简单的应用程序中并将其放到github 上,以便可以轻松地重现该问题。

npm install # install deps
npm run serve:dev # run the app - works
npm run test # run karma tests - doesn't work

编辑:

我设法通过替换以下内容来运行测试:

"dist/app.js"

在业力设置中

"src/boot.ts"

但这不会向下钻取或加载/导入应用程序的其余部分。然后我尝试仅将我想要测试的类导入规范,但我无法模拟我正在测试的类正在使用的任何 DI 注入服务。无论如何,我在这个阶段几乎放弃了这个,不再试图弄清楚如何做到这一点,转向 ang2+。

4

1 回答 1

1

在将我的 AngularJS 项目构建从 Grunt 转移到 Webpack 时,我遇到了类似的问题,并尝试了 2 种不同的方法。

1. Webpack 和 Karma 作为两个独立的进程。我让一个 npm-scriptwebpack并行运行karma。看起来像

"dev-build": "webpack --config webpack/development.js",
"dev-test": "karma start test/karma.development.conf.js",
"test": "concurrently --kill-others --raw \"npm run dev-build\" \"npm run dev-test\""

而不是concurrently你可以做任何其他事情,npm-run-all甚至&. 在那个配置中,Karma 没有任何 Webpack 的东西,她只是看着 ./temp 文件夹来构建分发包并独立工作,每次测试或分发包被更改时都会重新运行自己。Webpack 在开发模式下启动(通过“dev-build”脚本),他观察 ./src 文件夹并将分发编译到 ./temp 文件夹中。当他更新 ./temp 时,Karma 开始重新运行测试。

尽管存在第一次业力失败的问题,但它仍然有效。Karma 在 Webpack 第一次编译完成之前就开始了测试。这并不重要。此外,玩restartOnFileChange设置可能会有所帮助......也许还有另一个很好的解决方法。我还没有完成这个故事,我切换到了选项 2,我相信它更符合 Webpack 的方式,而不仅仅是描述的方式。

2. Karma 是唯一的进程,它使用了 Webpack。我拒绝了 ./temp 文件夹,并决定对于开发模式,所有操作都应该在内存中。开发模式 Webpack 有以下设置(./webpack/development.js):

entry: { 'ui-scroll': path.resolve(__dirname, '../src/ui-scroll.js') },
output: { filename: '[name].js' }, // + path to ./dist in prod
devtool: 'inline-source-map', // 'source-map' in prod
compressing: false,
watch: true

开发模式业力(./test/karma.development.conf.js):

files: [
  // external libs
  // tests specs
  '../src/ui-scroll.js' // ../dist in prod
],
preprocessors: { // no preprocessors in prod
  '../src/ui-scroll.js': ['webpack', 'sourcemap']
},
webpack: require('../webpack/development.js'), // no webpack in prod
autoWatch: true, 
keepalive: true, 
singleRun: false

这还需要安装两个 npm 包:karma-webpackkarma-sourcemap-loader. 第一个选项在 Grunt/gulp 之后看起来更熟悉,但这个更简单、更短且更稳定。

于 2017-11-14T10:57:34.723 回答