首页 > 解决方案 > 无法正确地将 Angular 测试与 HTML 模板捆绑在一起

问题描述

在我们当前的项目中,我们构建了一个自定义的混合 NGJS/NGX 应用程序,作为完全迁移到 Angular 的中间步骤。

混合将每个 Angular 版本都包含在一个单独的目录中。它们在两个方向上互通——我们在 AngularJS 中包含降级的 Angular 文件,在 Angular 中包含升级的 AngularJS 文件。

构建完全按预期工作(所有文件都正确输出),但是,我们无法使测试工作(Karma + Webpack)。

它们在一个非常特定的点失败 - 当您包含需要任何类型 HTML 的 AngularJS 文件时,例如import Tpl from '../templates/my-template.tpl.html.

错误如下:

TypeError: Cannot read property 'module' of undefined
at eval (webpack-internal:///./src/ngjs/Account/templates/account.password.edit.tpl.html:4:16)
at Module../src/ngjs/Account/templates/account.password.edit.tpl.html (http://localhost:9879/absolute/path-to-repo/karma.bundle.js?b7d10f4cb57d9b36c6b149128e3c9810b2901be0:7594:1)
at __webpack_require__ (http://localhost:9879/absolute/path-to-repo/karma.bundle.js?b7d10f4cb57d9b36c6b149128e3c9810b2901be0:20:30)
at eval (webpack-internal:///./src/ngjs/Core/services/user.service.ts:11:64)
at Object../src/ngjs/Core/services/user.service.ts (http://localhost:9879/absolute/path-to-repo/karma.bundle.js?b7d10f4cb57d9b36c6b149128e3c9810b2901be0:7610:1)
at __webpack_require__ (http://localhost:9879/absolute/path-to-repo/karma.bundle.js?b7d10f4cb57d9b36c6b149128e3c9810b2901be0:20:30)
at eval (webpack-internal:///./src/ngx/core/components/display-groups-dropdown/display-groups-dropdown.component.ts:14:22)
at Object../src/ngx/core/components/display-groups-dropdown/display-groups-dropdown.component.ts (http://localhost:9879/absolute/path-to-repo/karma.bundle.js?b7d10f4cb57d9b36c6b149128e3c9810b2901be0:8092:1)
at __webpack_require__ (http://localhost:9879/absolute/path-to-repo/karma.bundle.js?b7d10f4cb57d9b36c6b149128e3c9810b2901be0:20:30)
at eval (webpack-internal:///./src/ngx/core/components/display-groups-dropdown/display-groups-dropdown.component.spec.ts:4:43)

ngx作为应用程序的 Angular 部分并且ngjs是 AngularJS)。

我们的业力配置:

export default (config) => {
config.set({
    basePath: 'src',
    frameworks: ['jasmine'],
    plugins: [
        karmaJasminePlugin,
        karmaChromeLauncherPlugin,
        karmaWebpackPlugin,
        tsLoaderPlugin,
        karmaMochaReporterPlugin,
        karmaCoverageIstanbulReporterPlugin,
    ],
    preprocessors: {
        '../karma.bundle.ts': ['webpack'],
    },
    client: { clearContext: false }, // leave Jasmine Spec Runner output visible in browser
    files: [
        { pattern: '../karma.bundle.ts', watched: false },
    ],
    mime: {
        'text/x-typescript': ['ts', 'tsx'],
    },
    coverageIstanbulReporter: {
        dir: require('path').join(__dirname, 'coverage'),
        reports: ['html', 'lcovonly', 'text-summary'],
        combineBrowserReports: true,
        fixWebpackSourcePaths: true
    },
    reporters: config.codeCoverage ? ['mocha', 'coverage-istanbul'] : ['mocha'],
    mochaReporter: { ignoreSkipped: true },
    port: 9876,
    colors: true,
    logLevel: config.LOG_INFO,
    autoWatch: false,
    singleRun: true,
    browsers: ['ChromeHeadless'],
    customLaunchers: {
        ChromeDebug: {
            base: 'Chrome',
            flags: ['--remote-debugging-port=9333'],
            debug: true
        },
    },
    webpack: config.codeCoverage ? webpackMerge(webpackConf,
        {
            module: {
                rules: [
                    {
                        test: /\.ts$/,
                        exclude: /\.spec\.ts$/,
                        enforce: 'post',
                        use: {
                            loader: 'istanbul-instrumenter-loader',
                            options: {
                                esModules: true
                            },
                        }
                    }
                ]
            }
        }
    ) : webpackConf,
    webpackMiddleware: {
        noInfo: true,
        stats: 'errors-only'
    },
    concurrency: Infinity,
    browserDisconnectTolerance: 3,
    browserDisconnectTimeout: 210000,
    browserNoActivityTimeout: 210000,
});
};

我们用于测试的 tsconfig:

{
"extends": "../tsconfig.json",
"compilerOptions": {
    "outDir": "../out-tsc/spec",
    "types": [
        "jasmine",
        "node"
    ]
},
"include": [
    "../karma.bundle.ts",
    "**/*.spec.ts",
    "**/*.d.ts"
]
}

karma.bundle.ts(相当标准):

// First, initialize the Angular testing environment.
beforeAll(() => {
    testing.TestBed.resetTestEnvironment();
    testing.TestBed.initTestEnvironment(browser.BrowserDynamicTestingModule,
        browser.platformBrowserDynamicTesting());
});

/**
 * Get all the files, for each file, call the context function
 * that will require the file and load it up here. Context will
 * loop and require those spec files here
 */
function requireAll(requireContext) {
    return requireContext.keys().map((key) => {
        requireContext(key);
    });
}

/**
 * Requires and returns all modules that match
 */
const context = (require as any).context('./src', true, /\.spec\.ts$/);
console.log('Keys: ', context.keys());
requireAll(context);

到目前为止,我有以下怀疑:

我很乐意提供任何帮助,因为我们完全陷入困境,我们的测试暂时无法使用。

标签: angulartypescriptwebpackkarma-jasmine

解决方案


After further investigation we found a partial solution that allows tests to pass and ignores module errors. We modified our code for requiring context in Karma:

function requireAll(requireContext) {
    return requireContext.keys().map((key) => {
        try {
            requireContext(key);
        } catch (err) {
            console.log('Cannot require:', err);
        }
    }
}

Also worth mentioning: import order is important in bundle file (that was another problem we had with karma.bundle.ts). We switched from:

import * as testing from '@angular/core/testing';
import * as browser from '@angular/platform-browser-dynamic/testing';
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';

to:

import 'core-js/es7/reflect';
import 'zone.js/dist/zone';
import 'zone.js/dist/zone-testing';

import * as testing from '@angular/core/testing';
import * as browser from '@angular/platform-browser-dynamic/testing';

This doesn't solve our issue directly, but allows for tests to pass as long as imported templates that cause errors aren't used in the tests (all the import errors will be suppressed by catch() clause).

Another part of the solution was to modify Webpack config for tests - we added additional rule that processes HTML in the simplest possible way. It occured that Karma probably changed the paths that were used in Webpack pipelines and Webpack was not able to properly process HTMLs. This:

rules: [
            {
                test: /\.tpl\.html$/,
                use: [
                    'raw-loader',
                    'html-loader'
                ]
            },
            { // ts
                test: /\.ts$/,
                exclude: [/node_modules/],
                use: [
                    {
                        loader: 'ts-loader',
                        options: {
                            configFile: path.join(srcDir, 'tsconfig.spec.json'),
                            onlyCompileBundledFiles: true,
                            experimentalWatchApi: true,
                            transpileOnly: true
                        },
                    },
                    'angular2-template-loader'
                ],
            },
        ]

solved the issue.


推荐阅读