angular - 无法正确地将 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);
到目前为止,我有以下怀疑:
- 加载模块时,Karma 看不到类型,因此 HTML 不是可识别的类型
- 尽管配置正确,但 Webpack 并没有按应有的方式处理 HTML,但仅在 Karma 上下文中
我很乐意提供任何帮助,因为我们完全陷入困境,我们的测试暂时无法使用。
解决方案
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.
推荐阅读
- android - 如何解决多个 D8 警告:
没有找到,它是默认或静态接口方法脱糖所必需的 ? - apache-kafka - Nifi 处理器(发布和消费 kafka)没有与 Ambari 平台上的 kafka 通信
- bash - Bash - For循环不适用于声明中的变量
- c# - MediatR NotificationHandler 触发并忘记
- python - 在每个文件合并后添加换行符
- c# - Net Core Identity 2.1 身份验证无法正常工作访问受保护的端点
- python - 使用 unittest 进行 Python Selenium 测试 - 驱动程序错误
- scala - 如何从 Spark DataFrame 中的 AWS S3 读取多个文件?
- azure-active-directory - Azure 所有者角色和共同管理员之间的区别
- python - 如何有效地比较日期时间对象