angularjs - angular + mocha + typescript + webpack kiểm thử
Tôi có một dự án Angular lớn viết bằng Typescript và được Webpack đóng gói thành một bundle. Khi sử dụng Karma, tôi gặp vấn đề reload trang mà tôi không thể giải quyết. Vì vậy, tôi đã chuyển sang thử Mocha nhưng không thể làm cho nó hoạt động đúng cách. Tôi đã theo câu trả lời được chấp nhận cho câu hỏi này để đi đến mức này.
Vấn đề đầu tiên
Có lẽ ai đó có thể nói cho tôi biết tôi đang làm sai ở đâu hoặc chỉ cho tôi cách thiết lập đúng cách.
Tôi chạy webpack-dev-server để thử nghiệm như sau:
./node_modules/.bin/webpack-dev-server --config webpack.test.config.js
Điều này cho tôi một trang web nơi tôi có thể thấy kết quả chạy thử. Nếu tôi thay đổi bất kỳ mã Typescript nào của mình, gói sẽ được xây dựng lại và tải lại module nóng sẽ kích hoạt, nhưng sau đó tôi nhận được lỗi trên trang web:
TypeError: Cannot read property 'call' of undefined
at callFn (eval at <anonymous> (test/test.build.js:8459:9), <anonymous>:4471:20)
at Hook.Runnable.run (eval at <anonymous> (test/test.build.js:8459:9), <anonymous>:4464:7)
at next (eval at <anonymous> (test/test.build.js:8459:9), <anonymous>:4810:10)
at eval (eval at <anonymous> (test/test.build.js:8459:9), <anonymous>:4832:5)
at timeslice (eval at <anonymous> (test/test.build.js:8459:9), <anonymous>:75:27)
Nếu tôi làm mới trang thì các bài kiểm tra sẽ chạy lại.
Vấn đề thứ hai
Bài kiểm tra directive của tôi vốn hoạt động trước đây thì bây giờ bị phá vỡ vì đối tượng scope không còn giống như trước nữa. Scope mà tôi truyền vào hàm $compile không có các hàm của directive trên đó. Chúng bây giờ nằm trên scope.$$childTail. Tôi không biết liệu đây có phải là một tác dụng phụ của việc chuyển sang Mocha hay là do tôi làm sai điều gì đó.
webpack.config.js
var path = require('path');
var webpack = require('webpack');
var BundleTracker = require('webpack-bundle-tracker');
module.exports = {
devtool: 'inline-source-map', //just do inline source maps instead of the default
module: {
loaders: [
{ test: /\.css$/, loader: "style-loader!css-loader" },
{
test: /\.json$/,
loader: 'json',
},
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
loader: 'babel-loader!ts-loader',
//loader: 'babel-loader?presets[]=es2015&presets[]=react!ts-loader'
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
//loader: 'babel-loader?presets[]=es2015&presets[]=react!ts-loader'
},
{
test: /masonry|imagesloaded|fizzy\-ui\-utils|desandro\-|outlayer|get\-size|doc\-ready|eventie|eventemitter/,
loader: 'imports?define=>false&this=>window'
}
],
},
resolve: {
root: [ path.resolve('./ng/ng') ],
extensions: ['', '.ts', '.tsx', '.js', '.css'],
alias: {
'react-dom': 'react/lib/ReactDOM',
// 'react$' : path.resolve(__dirname, 'node_modules/react/dist/react-with-addons.js'),
'react/addons': path.resolve(__dirname, 'node_modules/react-addons/index.js'),
'xeditable.css': path.resolve(__dirname, 'node_modules/angular-xeditable/dist/css/xeditable.css'),
'textAngular.css': path.resolve(__dirname, 'node_modules/textangular/dist/textAngular.css'),
}
},
externals: {
'cheerio': 'window',
'react': 'React',
'react-addons': 'React',
'react-dom': 'ReactDOM',
'react-dom/server': true,
//'react-addons-test-utils': true,
'ChemDoodle': true,
//'react': true,
//'react/addons': true,
//'react/lib/ExecutionEnvironment': true,
//'react/lib/ReactContext': true
},
entry: {
itracker: './ng/ng/app.ts',
},
output: {
//path: '.',
//publicPath: '/website/js/',
path: path.resolve('.'),
publicPath: 'http://localhost:3000/assets/bundles/', // Tell django to use this URL to load packages and not use STATIC_URL + bundle_name
//filename: 'itracker.bundle.js'
filename: "[name]-[hash].js",
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin(), // don't reload if there is an error
new BundleTracker({filename: './webpack-stats.json'}),
//new webpack.optimize.OccurenceOrderPlugin(),
// Export a global jQuery
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
"window.jQuery": "jquery"
})
]
}
webpack.test.config.js
var path = require('path');
var webpack = require('webpack')
var BundleTracker = require('webpack-bundle-tracker')
var config = require('./webpack.config.js');
const index = path.resolve(__dirname, './test/test_index.js');
config.entry = { tests: 'mocha!'+ index };
config.module.loaders = [
{
test: /\.ts(x?)$/,
exclude: /node_modules/,
loader: 'babel-loader!ts-loader',
},
{
test: /\.js$/,
exclude: /node_modules/,
loader: 'babel-loader',
},
{
test: /masonry|imagesloaded|fizzy\-ui\-utils|desandro\-|outlayer|get\-size|doc\-ready|eventie|eventemitter/,
loader: 'imports?define=>false&this=>window'
},
{
test: /(\.jpg|\.jpeg|\.png|\.gif)$/,
loader: 'null-loader'
},
{
test: /(\.css|\.less|\.scss)$/,
loader: 'null-loader'
},
{
test: /\.html$/,
loader: 'ngtemplate?relativeTo=' + __dirname + '&prefix=/website/js!html'
}
];
// config.plugins = config.plugins.concat([
// // keeps hashes consistent between compilations
// new webpack.optimize.OccurenceOrderPlugin(),
// ]);
config.output = {
filename: 'test.build.js',
path: 'test/',
publicPath: 'http://0.0.0.0:3000/test',
};
config.devServer = {
host: '0.0.0.0',
port: 3000,
hot: true,
inline: true,
historyApiFallback: true,
watchOptions: {
aggregateTimeout: 300,
poll: 1000
}
};
config.target = 'web';
module.exports = config;
test_index.js
// Add all tests to context so they are put in bundle
const context = require.context('.', true, /.+[st]\.spec\.tsx?$/);
context.keys().forEach(context);
// Add all partials to context so they are put in bundle
var templates = require.context('../partials', true, /\.html$/);
templates.keys().forEach(function(key) {
templates(key);
});
module.exports = context;
test.html
<!DOCTYPE html>
<html>
<head>
<title>Mocha</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="./node_modules/mocha/mocha.css" />
<script src="/node_modules/react/dist/react-with-addons.js"></script>
<script src="/node_modules/react-dom/dist/react-dom.js"></script>
<script src="/test/test.build.js"></script>
</head>
<body>
</body>
</html>