在 Angular 2 中使用 SystemJS 和 Karma 加载节点模块
Load node module with SystemJS and Karma in Angular 2
我正在使用 Angular 2、SystemJS 和 Karma 构建一个网络应用程序进行测试。
我正在尝试在测试中加载节点模块 ngrx/store:
import {
it, describe, expect, beforeEach, inject
} from 'angular2/testing';
import { Store } from '@ngrx/store';
describe('Graphs store', () => {
let graphs;
beforeEach(inject([Store], (store: Store<any>) => {
graphs = store.select('graphs');
}));
it('works', () => {
// expect graphs to do something...
});
});
但是,我的测试失败并显示以下消息:
404: /@ngrx/store
Chrome 48.0.2564 (Mac OS X 10.11.3) ERROR
Error: XHR error (404 Not Found) loading http://localhost:9876/@ngrx/store
实际上我在开发中也遇到了同样的问题,结果是 SystemJS 不知道在哪里可以找到 @ngrx/store
。为了解决这个问题,我这样做了:
System.config({
packages: {
src: {
format: 'register',
defaultExtension: 'js'
}
},
map: { '@ngrx/store' : 'node_modules/@ngrx/store/dist/store.js' } // <-- this
});
我修改了我的 Karma shim 文件来做同样的事情。在 运行 第二次测试时,我现在得到一个不同的错误:
404: /node_modules/@ngrx/store/dist/store.js
Chrome 48.0.2564 (Mac OS X 10.11.3) ERROR
Error: XHR error (404 Not Found) loading http://localhost:9876/node_modules/@ngrx/store/dist/store.js
这意味着它必须考虑我给它的显式路径,但它仍然找不到模块。但是,它是模块的正确路径,并且在浏览器中加载时有效。
我完全不知道下一步该做什么。有人能给我指出正确的方向吗?
注意几点:
- 将节点模块添加到 Karma 的
files
数组不是一个选项,因为它的依赖关系需要用 SystemJS 解决
- 这只发生在 SystemJS 需要关于它们所在位置的自定义指令的节点模块上。只要 SystemJS 能够找到它,我就可以在我的测试中很好地加载其他模块而无需提供特定位置
这是我的 Karma 配置:
// Set up with the help of
// http://twofuckingdevelopers.com/2016/01/testing-angular-2-with-karma-and-jasmine/
module.exports = function(config) {
config.set({
basePath: '.',
frameworks: ['jasmine'],
files: [
// paths loaded by Karma
{pattern: 'node_modules/angular2/bundles/angular2-polyfills.js', included: true, watched: true},
{pattern: 'node_modules/systemjs/dist/system.src.js', included: true, watched: true},
{pattern: 'node_modules/rxjs/bundles/Rx.js', included: true, watched: true},
{pattern: 'node_modules/angular2/bundles/angular2.dev.js', included: true, watched: true},
{pattern: 'node_modules/angular2/bundles/testing.dev.js', included: true, watched: true},
{pattern: 'karma-test-shim.js', included: true, watched: true},
// paths loaded via module imports
{pattern: 'src/**/*.js', included: false, watched: true},
// paths to support debugging with source maps in dev tools
{pattern: 'src/**/*.ts', included: false, watched: false},
{pattern: 'src/**/*.js.map', included: false, watched: false}
],
// proxied base paths
proxies: {
// required for component assets fetched by Angular's compiler
'/src/': '/base/src/'
},
port: 9876,
logLevel: config.LOG_INFO,
colors: true,
autoWatch: true,
browsers: ['Chrome'],
// Karma plugins loaded
plugins: [
'karma-jasmine',
'karma-coverage',
'karma-chrome-launcher'
],
// // Coverage reporter generates the coverage
// reporters: ['progress', 'dots', 'coverage'],
//
// // Source files that you wanna generate coverage for.
// // Do not include tests or libraries (these files will be instrumented by Istanbul)
// preprocessors: {
// 'src/**/!(*spec).js': ['coverage']
// },
// coverageReporter: {
// reporters:[
// {type: 'json', subdir: '.', file: 'coverage-final.json'}
// ]
// },
singleRun: true
})
};
这是我的 Karma 垫片:
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
// // Cancel Karma's synchronous start,
// // we will call `__karma__.start()` later, once all the specs are loaded.
__karma__.loaded = function() {};
System.config({
packages: {
'base/src': {
defaultExtension: 'js',
format: 'register',
map: Object.keys(window.__karma__.files).filter(onlyAppFiles).reduce(createPathRecords, {})
}
},
// This makes it work in the browser, but not in my tests!
paths: { '@ngrx/store' : 'node_modules/@ngrx/store/dist/store.js' }
});
System.import('angular2/src/platform/browser/browser_adapter')
.then(function(browser_adapter) { browser_adapter.BrowserDomAdapter.makeCurrent(); })
.then(function() { return Promise.all(resolveTestFiles()); })
.then(function() { __karma__.start(); }, function(error) { __karma__.error(error.stack || error); });
function createPathRecords(pathsMapping, appPath) {
// creates local module name mapping to global path with karma's fingerprint in path, e.g.:
// './vg-player/vg-player':
// '/base/src/vg-player/vg-player.js?f4523daf879cfb7310ef6242682ccf10b2041b3e'
var pathParts = appPath.split('/');
var moduleName = './' + pathParts.slice(Math.max(pathParts.length - 2, 1)).join('/');
moduleName = moduleName.replace(/\.js$/, '');
pathsMapping[moduleName] = appPath + '?' + window.__karma__.files[appPath];
return pathsMapping;
}
function onlyAppFiles(filePath) {
return /\/base\/src\/(?!.*\.spec\.js$).*\.js$/.test(filePath);
}
function onlySpecFiles(path) {
return /\.spec\.js$/.test(path);
}
function resolveTestFiles() {
return Object.keys(window.__karma__.files) // All files served by Karma.
.filter(onlySpecFiles)
.map(function(moduleName) {
// loads all spec files via their global module names (e.g.
// 'base/src/vg-player/vg-player.spec')
return System.import(moduleName);
});
}
更新
有一个错误为 here. You can see the specific changes that cause the error here 的示例存储库。 运行 $ npm install
和 $ npm test
得到错误。
已发送带有修复程序的 PR。可能有更好的方法来做到这一点,但这行得通:
通过更新 package.json
的 build
脚本将 store.js
文件复制到 dist
文件夹
... && cp node_modules/@ngrx/store/dist/store.js dist/store.js
更新karma-test.shim.js
中的路径
paths: { '@ngrx/store' : '/base/dist/store.js' }
将 @ngrx/store
包含在您的其余捆绑包中,将解决 404 错误
// for testing in karma.conf.js
files: [
// paths loaded by Karma
{pattern: 'node_modules/@ngrx/store/dist/store.js', included: true, watched: true},
],
但是与编译为系统模块的其他包不同,@ngrx/store
被编译为 commonjs 模块
// 'node_modules/angular2/bundles/angular2.dev.js'
"format register";
System.register("angular2/src/facade/lang", [], true, function(require, exports, module) {
....
// 'node_modules/rxjs/bundles/Rx.js'
"format register";
System.register("rxjs/util/root", [], true, function(require, exports, module) {
....
// 'node_modules/@ngrx/store/dist/store.js'
....
var Observable_1 = require('rxjs/Observable');
....
导致错误:
Uncaught ReferenceError: require is not defined
// with {pattern: '~/store.js', indluded: true}
// context.html includes
<script type="text/javascript" src="/base/node_modules/@ngrx/store/dist/store.js?fb5e807149603c3c2f998c98faf6826c7e301d71"></script>
这就是为什么你不应该包括它:
{pattern: 'node_modules/@ngrx/store/dist/store.js', included: false, watched: true}
这基本上会将其列在 window.__karma__.files
对象中,但不会将其作为脚本添加到 karma 的 context.html
- 浏览器不会加载并且 运行 导致的代码错误。加载应由 SystemJS 处理...
如果您 运行 使用 singleRun: false
进行业力测试,您可以在 Chrome 的 Devtools > 网络中检查文件。您会在那里看到一个已加载文件的列表,这就是拼图的最后一块所在:
在您的 karma-test-shim.js
中将 System.config.map
更改为:
map: { '@ngrx/store' : '/base/node_modules/@ngrx/store/dist/store.js' }
Executed 4 of 4 SUCCESS (0.037 secs / 0.008 secs)
我正在使用 Angular 2、SystemJS 和 Karma 构建一个网络应用程序进行测试。
我正在尝试在测试中加载节点模块 ngrx/store:
import {
it, describe, expect, beforeEach, inject
} from 'angular2/testing';
import { Store } from '@ngrx/store';
describe('Graphs store', () => {
let graphs;
beforeEach(inject([Store], (store: Store<any>) => {
graphs = store.select('graphs');
}));
it('works', () => {
// expect graphs to do something...
});
});
但是,我的测试失败并显示以下消息:
404: /@ngrx/store
Chrome 48.0.2564 (Mac OS X 10.11.3) ERROR
Error: XHR error (404 Not Found) loading http://localhost:9876/@ngrx/store
实际上我在开发中也遇到了同样的问题,结果是 SystemJS 不知道在哪里可以找到 @ngrx/store
。为了解决这个问题,我这样做了:
System.config({
packages: {
src: {
format: 'register',
defaultExtension: 'js'
}
},
map: { '@ngrx/store' : 'node_modules/@ngrx/store/dist/store.js' } // <-- this
});
我修改了我的 Karma shim 文件来做同样的事情。在 运行 第二次测试时,我现在得到一个不同的错误:
404: /node_modules/@ngrx/store/dist/store.js
Chrome 48.0.2564 (Mac OS X 10.11.3) ERROR
Error: XHR error (404 Not Found) loading http://localhost:9876/node_modules/@ngrx/store/dist/store.js
这意味着它必须考虑我给它的显式路径,但它仍然找不到模块。但是,它是模块的正确路径,并且在浏览器中加载时有效。
我完全不知道下一步该做什么。有人能给我指出正确的方向吗?
注意几点:
- 将节点模块添加到 Karma 的
files
数组不是一个选项,因为它的依赖关系需要用 SystemJS 解决
- 这只发生在 SystemJS 需要关于它们所在位置的自定义指令的节点模块上。只要 SystemJS 能够找到它,我就可以在我的测试中很好地加载其他模块而无需提供特定位置
这是我的 Karma 配置:
// Set up with the help of
// http://twofuckingdevelopers.com/2016/01/testing-angular-2-with-karma-and-jasmine/
module.exports = function(config) {
config.set({
basePath: '.',
frameworks: ['jasmine'],
files: [
// paths loaded by Karma
{pattern: 'node_modules/angular2/bundles/angular2-polyfills.js', included: true, watched: true},
{pattern: 'node_modules/systemjs/dist/system.src.js', included: true, watched: true},
{pattern: 'node_modules/rxjs/bundles/Rx.js', included: true, watched: true},
{pattern: 'node_modules/angular2/bundles/angular2.dev.js', included: true, watched: true},
{pattern: 'node_modules/angular2/bundles/testing.dev.js', included: true, watched: true},
{pattern: 'karma-test-shim.js', included: true, watched: true},
// paths loaded via module imports
{pattern: 'src/**/*.js', included: false, watched: true},
// paths to support debugging with source maps in dev tools
{pattern: 'src/**/*.ts', included: false, watched: false},
{pattern: 'src/**/*.js.map', included: false, watched: false}
],
// proxied base paths
proxies: {
// required for component assets fetched by Angular's compiler
'/src/': '/base/src/'
},
port: 9876,
logLevel: config.LOG_INFO,
colors: true,
autoWatch: true,
browsers: ['Chrome'],
// Karma plugins loaded
plugins: [
'karma-jasmine',
'karma-coverage',
'karma-chrome-launcher'
],
// // Coverage reporter generates the coverage
// reporters: ['progress', 'dots', 'coverage'],
//
// // Source files that you wanna generate coverage for.
// // Do not include tests or libraries (these files will be instrumented by Istanbul)
// preprocessors: {
// 'src/**/!(*spec).js': ['coverage']
// },
// coverageReporter: {
// reporters:[
// {type: 'json', subdir: '.', file: 'coverage-final.json'}
// ]
// },
singleRun: true
})
};
这是我的 Karma 垫片:
// Tun on full stack traces in errors to help debugging
Error.stackTraceLimit = Infinity;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
// // Cancel Karma's synchronous start,
// // we will call `__karma__.start()` later, once all the specs are loaded.
__karma__.loaded = function() {};
System.config({
packages: {
'base/src': {
defaultExtension: 'js',
format: 'register',
map: Object.keys(window.__karma__.files).filter(onlyAppFiles).reduce(createPathRecords, {})
}
},
// This makes it work in the browser, but not in my tests!
paths: { '@ngrx/store' : 'node_modules/@ngrx/store/dist/store.js' }
});
System.import('angular2/src/platform/browser/browser_adapter')
.then(function(browser_adapter) { browser_adapter.BrowserDomAdapter.makeCurrent(); })
.then(function() { return Promise.all(resolveTestFiles()); })
.then(function() { __karma__.start(); }, function(error) { __karma__.error(error.stack || error); });
function createPathRecords(pathsMapping, appPath) {
// creates local module name mapping to global path with karma's fingerprint in path, e.g.:
// './vg-player/vg-player':
// '/base/src/vg-player/vg-player.js?f4523daf879cfb7310ef6242682ccf10b2041b3e'
var pathParts = appPath.split('/');
var moduleName = './' + pathParts.slice(Math.max(pathParts.length - 2, 1)).join('/');
moduleName = moduleName.replace(/\.js$/, '');
pathsMapping[moduleName] = appPath + '?' + window.__karma__.files[appPath];
return pathsMapping;
}
function onlyAppFiles(filePath) {
return /\/base\/src\/(?!.*\.spec\.js$).*\.js$/.test(filePath);
}
function onlySpecFiles(path) {
return /\.spec\.js$/.test(path);
}
function resolveTestFiles() {
return Object.keys(window.__karma__.files) // All files served by Karma.
.filter(onlySpecFiles)
.map(function(moduleName) {
// loads all spec files via their global module names (e.g.
// 'base/src/vg-player/vg-player.spec')
return System.import(moduleName);
});
}
更新
有一个错误为 here. You can see the specific changes that cause the error here 的示例存储库。 运行 $ npm install
和 $ npm test
得到错误。
已发送带有修复程序的 PR。可能有更好的方法来做到这一点,但这行得通:
通过更新 package.json
的 build
脚本将 store.js
文件复制到 dist
文件夹
... && cp node_modules/@ngrx/store/dist/store.js dist/store.js
更新karma-test.shim.js
paths: { '@ngrx/store' : '/base/dist/store.js' }
将 @ngrx/store
包含在您的其余捆绑包中,将解决 404 错误
// for testing in karma.conf.js
files: [
// paths loaded by Karma
{pattern: 'node_modules/@ngrx/store/dist/store.js', included: true, watched: true},
],
但是与编译为系统模块的其他包不同,@ngrx/store
被编译为 commonjs 模块
// 'node_modules/angular2/bundles/angular2.dev.js'
"format register";
System.register("angular2/src/facade/lang", [], true, function(require, exports, module) {
....
// 'node_modules/rxjs/bundles/Rx.js'
"format register";
System.register("rxjs/util/root", [], true, function(require, exports, module) {
....
// 'node_modules/@ngrx/store/dist/store.js'
....
var Observable_1 = require('rxjs/Observable');
....
导致错误:
Uncaught ReferenceError: require is not defined
// with {pattern: '~/store.js', indluded: true}
// context.html includes
<script type="text/javascript" src="/base/node_modules/@ngrx/store/dist/store.js?fb5e807149603c3c2f998c98faf6826c7e301d71"></script>
这就是为什么你不应该包括它:
{pattern: 'node_modules/@ngrx/store/dist/store.js', included: false, watched: true}
这基本上会将其列在 window.__karma__.files
对象中,但不会将其作为脚本添加到 karma 的 context.html
- 浏览器不会加载并且 运行 导致的代码错误。加载应由 SystemJS 处理...
如果您 运行 使用 singleRun: false
进行业力测试,您可以在 Chrome 的 Devtools > 网络中检查文件。您会在那里看到一个已加载文件的列表,这就是拼图的最后一块所在:
在您的 karma-test-shim.js
中将 System.config.map
更改为:
map: { '@ngrx/store' : '/base/node_modules/@ngrx/store/dist/store.js' }
Executed 4 of 4 SUCCESS (0.037 secs / 0.008 secs)