Adding Karma, Jasmine specs to working anuglar app - node.js

We have an angular app under development. Business code is working and is almost stable. In the app, we have placed JSPs and ts (and generated *.js, *.js.map) files into different folders. Please find project folder structure below:
/angular
--node_modules
--package.json
--
--bills
--a.component.ts
--a.component.spec.ts
--a.component.js
--a.component.spec.js
--a.component.js.map
--a.component.spec.js.map
--pages
--com
--bills
--a.jsp
Karma start prints below error log:
c:\...\angular>.\node_modules\.bin\karma start
24 07 2018 15:33:27.883:ERROR [karma]: { Error: ENOENT: no such file or directory, open 'c:\..\angular\<jsp_path_mentioned_in_component_template_url>'
We cannot have JSPs and ts files in the same folder. We have lot of files and its not feasible to change the project structure now.
Could you help us know where to update karma config to pic JSP from a custom location.
Hope to find some helpful pointers/inputs. Thanks in advance.
Update: Added karma.config.js
karma.config.js:
module.exports = function(config) {
config.set({
basePath: '.',
frameworks: ['jasmine'],
plugins: [
require('karma-jasmine'),
require('karma-chrome-launcher'),
require('karma-jasmine-html-reporter'),
require('karma-coverage'),
require('karma-generic-preprocessor'),
require('karma-mocha-reporter'),
require('karma-sourcemap-loader')
],
client: {
clearContext: true // leave Jasmine Spec Runner output visible in browser
},
files: [
// System.js for module loading
'node_modules/systemjs/dist/system.src.js',
// Polyfills
'node_modules/core-js/client/shim.min.js',
'node_modules/systemjs/dist/system-polyfills.js',
// zone.js
'node_modules/zone.js/dist/zone.js',
'node_modules/zone.js/dist/long-stack-trace-zone.js',
'node_modules/zone.js/dist/proxy.js',
'node_modules/zone.js/dist/sync-test.js',
'node_modules/zone.js/dist/jasmine-patch.js',
'node_modules/zone.js/dist/async-test.js',
'node_modules/zone.js/dist/fake-async-test.js',
// RxJs
{ pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false },
{ pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false },
// Paths loaded via module imports: Angular itself
{ pattern: 'node_modules/#angular/**/*.js', included: false, watched: false },
{ pattern: 'node_modules/#angular/**/*.js.map', included: false, watched: false },
{ pattern: 'systemjs.config.js', included: false, watched: false },
'karma-test-shim.js',
{ pattern: 'payment-pending/payment-pending-select.component.js', included: false, watched: true },
{ pattern: '**/test.component.js', included: false, watched: true },
{ pattern: '**/test.component.spec.js', included: false, watched: true },
],
proxies: {
// required for modules fetched by SystemJS
'/base/resources/js/plugins/angular/': '/base/node_modules/'
},
exclude: [],
preprocessors: { 'test.component.js': ['coverage'], "**/*.component.js": ["generic"], "**/*.js" : ['sourcemap']},
coverageReporter: {
includeAllSources: true,
dir: 'mycoverage/',
reporters: [
{ type: "html", subdir: "html" },
{ type: 'text-summary' }
]
},
reporters: ['mocha','coverage'],
mochaReporter: {
colors: {
success: 'green',
info: 'grey',
warning: 'yellow',
error: 'red'
},
symbols: {
success: '-',
info: '#',
warning: '!',
error: 'x'
}
},
port: 9876,
colors: true,
logLevel: config.LOG_INFO,
autoWatch: true,
browsers: ['Chrome'],
singleRun: false,
genericPreprocessor: {
rules: [{
process: function (content, file, done, log) {
//Prepare content for parser
file.contents = new Buffer(content);
// Every file has a parser
var parse = require('gulp-inline-ng2-template/parser')(file, { base: "", useRelativePaths: true, templateExtension:'.jsp' });
// Call real parse function
parse(function (err, contents) {
// Callback with content with template and style inline
done(contents);
});
}
}]
},
})
}

Related

Extract local and global styles with mini-css-extract-plugin

Until now I have been using extract-text-webpack-plugin and webpack 3 to make two bundle files. One for local styles and one for gloabl styles.
So in global styles file we would extract css from like bootstrap, semantic ... and in local styles bundle we would put our own styles.
Both of those files would have contenthash so if for example I change something in my local styles and rebuild app, only the hash from local styles would change and not from globaly styles.
After updateing to webpack 4 a need to use mini-css-extract-plugin instead of extract-text-webpack-plugin.
This was my setup until now. I am trying different things but I dont know how to turn this setup for mini-css-extract-plugin?
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const ExtractLocal = new ExtractTextPlugin({
filename: 'stylesheet/stylesLocal.[contenthash].local.css',
disable: false,
allChunks: true,
})
const ExtractGlobal = new ExtractTextPlugin({
filename: 'stylesheet/stylesGlobal.[contenthash].css',
disable: false,
allChunks: true,
})
module.exports = {
module: {
rules: [
/* Local styles */
{
test: /\.local\.(css|scss)$/,
use: ExtractLocal.extract({
use: [
{
loader: 'css-loader',
options: {
sourceMap: true,
minimize: true,
modules: true,
importLoaders: 1,
localIdentName: '[local]___[name]__[hash:base64:5]',
},
},
...
],
}),
},
/* Global styles */
{
test: /^((?!\.local).)+\.(css)$/,
use: ExtractGlobal.extract({
use: [
{
loader: 'css-loader',
options: {
sourceMap: true,
minimize: true,
},
},
],
}),
},
],
},
plugins: [
ExtractLocal,
ExtractGlobal,
...
],
}
Your css loaders are correct.
In plugins, I see you want implement it using mini-css to extract multiple css files.
While it's definitely an options, I succeeded implementing it with webpack Optimization option, and only 1 mini-css in plugins.
Output config:
output: {
path: appConfig.paths.appBuild,
filename: 'scripts/[name].[chunkhash:8].js',
chunkFilename: 'scripts/[name].[chunkhash:8].chunk.js',
publicPath: publicPath,
},
Additional css rule for styles from node modules only (I made it the first css rule and after it the additional rules):
{
test: /\.(css|scss)$/,
include: /(node_modules)/,
use: [
MiniCssExtractPlugin.loader,
{ loader: 'css-loader', options: { sourceMap: true } },
{ loader: 'sass-loader', options: { sourceMap: true } },
],
},
Optimization config:
(This will extract vendor js as well.)
optimization: {
splitChunks: {
cacheGroups: {
commons: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all',
},
},
},
},
Plugin config:
new MiniCssExtractPlugin({
filename: 'styles/[name].[contenthash].css',
chunkFilename: 'styles/[name].[contenthash].css',
}),

karma-browserify coverage reports contain file include paths instead of source code

Using karma-browserify to do unit tests with Jasmine. The tests correctly run but the coverage reports show file include paths instead of source code. You can reproduce this by installing the following project and run 'gulp unit':
https://github.com/bshack/shackstack
Here is an example of the coverage report contents:
typeof require === "function" && require("/xxx/xxx/xxx/shackstack/app/media/script/service/utilities.js");
Here is my karma.config:
module.exports = function(karma) {
'use strict';
karma.set({
basePath: '',
frameworks: [
'jasmine',
'browserify'
],
files: [{
pattern: 'app/media/script/service/*.js',
included: true
},
{
pattern: 'app/media/test/spec/*Spec.js',
included: true
}],
reporters: [
'progress',
'coverage'
],
preprocessors: {
'app/media/script/service/*.js': [
'browserify',
'coverage'
],
'app/media/test/spec/*Spec.js': [
'browserify'
]
},
browsers: [
//'Chrome',
//'Firefox',
//'Safari',
'PhantomJS'
],
singleRun: false,
autoWatch: false,
// browserify configuration
browserify: {
debug: true,
transform: [
'brfs',
'browserify-shim'
]
},
coverageReporter: {
type: 'html',
dir: 'app/report/istanbul/',
subdir: '.'
},
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000
});
};
any thoughts?
Fixed, basically you don't use karma coverage as you would normally, instead you have to use istanbul as a browserify transform.
var istanbul = require('browserify-istanbul');
module.exports = function(karma) {
'use strict';
karma.set({
basePath: '',
frameworks: [
'jasmine',
'browserify'
],
files: [{
pattern: 'app/media/script/service/*.js'
},
{
pattern: 'app/media/test/spec/*Spec.js'
}],
reporters: [
'progress',
'coverage'
],
preprocessors: {
'app/media/script/service/*.js': [
'browserify'
],
'app/media/test/spec/*Spec.js': [
'browserify'
]
},
browsers: [
//'Chrome',
//'Firefox',
//'Safari',
'PhantomJS'
],
singleRun: false,
autoWatch: false,
browserify: {
debug: true,
transform: [
'brfs',
'browserify-shim',
istanbul({
ignore: ['**/node_modules/**']
})
]
},
coverageReporter: {
type: 'html',
dir: 'app/report/istanbul/',
subdir: '.'
},
// If browser does not capture in given timeout [ms], kill it
captureTimeout: 60000
});
};

Grunt-contrib-watch doesn't see new files

I made Gruntfile.js like this:
'use strict';
module.exports = function (grunt) {
grunt.loadNpmTasks('grunt-favicons');
grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.loadNpmTasks('grunt-bake');
grunt.loadNpmTasks('grunt-contrib-htmlmin');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-autoprefixer');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-cwebp');
grunt.loadNpmTasks('grunt-responsive-images');
grunt.loadNpmTasks('grunt-retinafy');
grunt.loadNpmTasks('grunt-spritesmith');
grunt.loadNpmTasks('grunt-notify');
grunt.loadNpmTasks('grunt-contrib-watch');
require('load-grunt-tasks')(grunt);
grunt.initConfig({
pkg : grunt.file.readJSON('package.json'),
watch: {
html: {
files: ['./assets/**/*.html'],
tasks: ['html'],
options: {
spawn: false,
livereload: true,
},
},
less: {
files: ['./assets/less/**/*.less'],
tasks: ['less_files'],
options: {
spawn: false,
livereload: true,
},
},
js: {
files: ['./assets/js/**/*.js'],
tasks: ['js'],
options: {
spawn: false,
livereload: true,
},
},
img: {
files: ['./assets/images/**/*.png'],
tasks: ['img'],
options: {
spawn: false,
livereload: true,
},
},
favicon: {
files: ['./assets/favicon/favicon.png'],
tasks: ['favicon'],
options: {
spawn: false,
livereload: true,
},
},
icons: {
files: ['./assets/sprites/**/*.png'],
tasks: ['icons'],
options: {
spawn: false,
livereload: true,
},
},
},
shell: {
can_i_use_update: {
command: 'npm update caniuse-db'
}
},
favicons: {
options: {},
icons: {
src: 'assets/favicon/favicon.png',
dest: 'dist/favicons/'
}
},
imagemin: {
images: {
options: {
optimizationLevel: 4,
progressive: true,
interlaced: true
},
files: [{
expand: true,
cwd: './dist/images',
src: ['**/*.{png,jpg,jpeg,gif}'],
dest: './dist/images/'
}]
},
favicons: {
options: {
optimizationLevel: 4,
progressive: true,
interlaced: true
},
files: [{
expand: true,
cwd: './dist/favicons',
src: ['**/*.{png,jpg,jpeg,gif}'],
dest: './dist/favicons/'
}]
},
sprite: {
options: {
optimizationLevel: 4,
progressive: true,
interlaced: true
},
files: {
'./dist/images/sprite.png': './dist/images/sprite.png',
}
},
},
bake: {
build: {
options: {},
files: [{
expand: true,
cwd: './assets/',
src: ['*.html'],
dest: './dist/',
ext: '.html'
}]
}
},
htmlmin: {
dist: {
options: {
removeComments: true,
collapseWhitespace: true,
conservativeCollapse: true,
preserveLineBreaks: true
},
files: [{
expand: true,
cwd: './dist/',
src: ['**/*.html'],
dest: './dist/'
}]
}
},
less: {
build: {
options: {
compress: true,
ieCompat: true,
sourceMap: true,
sourceMapFilename: './dist/css/style.map',
sourceMapURL: 'css/style.map'
},
files: {
"./dist/css/style.css": "./assets/less/style.less"
}
}
},
autoprefixer: {
options: {
browsers: ['last 5 versions', 'ie 8', 'ie 9', '> 5%']
},
build: {
"./dist/css/style.css":"./dist/css/style.css"
},
},
concat: {
options: {},
dist: {
src: ['./assets/js/*.js'],
dest: './dist/js/script.js',
},
},
uglify: {
build: {
options: { compress: true },
files: { "./dist/js/script.js":"./dist/js/script.js" }
}
},
cwebp: {
build: {
options: {
q: 80,
alpha_q: 80
},
files: [{
expand: true,
cwd: './dist/images/',
src: ['**/*.{png,jpg,jpeg,gif}'],
dest: './dist/images/webp'
}]
}
},
responsive_images: {
build: {
options: {
engine:"im",
sizes: [{
width: 640,
},{
width: 1024,
},{
width: 1920
}]
},
files: [{
expand: true,
src: ['**.{jpg,gif,png}'],
cwd: './assets/images/',
custom_dest: './dist/images/responsive/{%= width %}/'
}]
}
},
retinafy: {
build: {
options: {
sizes: {
'75%': { suffix: '#1.5x' },
'100%': { suffix: '#2x' }
}
},
files: [{
expand: true,
cwd: './dist/images/',
src: ['**/*.{jpg,jpeg,gif,png}'],
dest: './dist/images/'
}],
}
},
sprite:{
all: {
src: './assets/sprites/*.png',
dest: './dist/images/sprite.png',
destCss: './assets/less/sprite.less'
}
},
notify_hooks: {
options: {
enabled: true,
max_jshint_notifications: 5, // maximum number of notifications from jshint output
title: "Project Name", // defaults to the name in package.json, or will use project directory's name
success: true, // whether successful grunt executions should be notified automatically
duration: 2 // the duration of notification in seconds, for `notify-send only
}
}
});
grunt.task.run('notify_hooks');
grunt.registerTask('html', ['bake', 'htmlmin']);
grunt.registerTask('less_files', ['less', 'autoprefixer']);
grunt.registerTask('js', ['concat', 'uglify']);
grunt.registerTask('img', ['responsive_images', 'retinafy', 'imagemin:images', 'cwebp']);
grunt.registerTask('favicon', ['favicons', 'imagemin:favicons']);
grunt.registerTask('icons', ['sprite', 'imagemin:sprite']);
grunt.registerTask('default', ['html','less_files','js','img','favicon','icons','watch']);
};
Everything works, even grunt-contrib watch, but only for old and modified files. It doesn't see any new files in watched folders. It doesn't work on different versions of grunt-contrib-watch. Any ideas?
node -v
v0.12.7
npm -v
2.11.3
OSX 10.10.4 Yosemite
I think you should strip the ./ at the beginning. It never needs to be prepended. It could be related to this issue when ./ is prepended, it breaks some of the events.
Try also setting the options to be:
options: {
event: ['changed', 'added', 'deleted']
}
The default setting is set to all but it has been reported that sometimes it doesn't work.
If all failed, there is an alternative solution. Drop grunt-contrib-watch and try grunt-simple-watch. Here you can find why grunt-contrib-watch is causing troubles.
Side note: I realized that you are loading all the tasks manually then using load-grunt-task like this:
grunt.loadNpmTasks('grunt-favicons');
grunt.loadNpmTasks('grunt-contrib-imagemin');
grunt.loadNpmTasks('grunt-bake');
grunt.loadNpmTasks('grunt-contrib-htmlmin');
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-autoprefixer');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-cwebp');
grunt.loadNpmTasks('grunt-responsive-images');
grunt.loadNpmTasks('grunt-retinafy');
grunt.loadNpmTasks('grunt-spritesmith');
grunt.loadNpmTasks('grunt-notify');
grunt.loadNpmTasks('grunt-contrib-watch');
require('load-grunt-tasks')(grunt);
For your information; you don't need to load all the tasks because load-grunt-tasks do it for you automatically. load-grunt-tasks scan the packages.json file for the tasks that you have and automatically load them. You can safely remove all the grunt.loadNpmTasks(xxx) and that task will be loaded (as long as it is mentioned in packages.json)

Source maps in uglified requirejs project

I have a requirejs project, I'm compiling with grunt-requirejs ("grunt-contrib-requirejs": "~0.4.1") into 1 big file: main.js. This task has source map generation enabled:
requirejs: {
compile: {
options: {
baseUrl: 'source/js',
name: 'main',
optimize: 'none',
generateSourceMaps: true,
out: 'build/js/main.js',
wrap: true,
shim: requireJsConfig.shim,
paths: requireJsConfig.paths
}
}
}
After that I minify this main.js with grunt-uglify ("grunt-contrib-uglify": "~0.2.7") using this configuration:
app: {
options: {
beautify : {
quote_keys: true
},
compress: false,
report: 'min',
sourceMap: 'build/js/main.js.map',
sourceMapIn: 'build/js/main.js.map', // input from requirejs
sourceMapIncludeSources: true
},
files: {
'build/js/main.js': ['build/js/main.js']
}
}
I would like to have a source map that will tell me an error in the source files (the ones requirejs consumes), but instead source map refuses to work at all. Please help me to get there as I'm feeling helpless already.
grunt-require comes with it's own uglify package built in:
eg.
requirejs: {
compile: {
options: {
generateSourceMaps: true,
logLevel: 4,
baseUrl: "common/scripts/",
include: "./main",
out: "common/dist/main.js",
preserveLicenseComments: false,
optimize: "uglify2",
mainConfigFile: "common/scripts/main.js"
}
}
}

Configuring grunt-ts and make it work with LiveReload

I am trying to use TypeScript inside a Yeoman / Grunt project. To compile TypeScript I use a grunt plugin called grunt-ts, the compilation of the .ts files works just fine, but the live reload doesn't works:
When I run grunt server I correctly get this:
Running "ts:dev" (ts) task
Compiling.
Success: 3.37s for 2 typescript files
Watching all Typescript files under : /home/mimo/webroot/tsyong/app/scripts
But then the liveReload task is not loaded.
This is how I configured my Gruntfile.js about grunt-ts.
grunt.initConfig({
...
ts: {
options: { // use to override the default options, http://gruntjs.com/configuring-tasks#options
target: 'es3', // es3 (default) / or es5
module: 'commonjs', // amd , commonjs (default)
sourcemap: true, // true (default) | false
declaration: false, // true | false (default)
nolib: false, // true | false (default)
comments: false // true | false (default)
},
dev: { // a particular target
src: ['<%= yeoman.app %>/scripts/{,*/}*.ts'], // The source typescript files, http://gruntjs.com/configuring-tasks#files
reference: '<%= yeoman.app %>/scripts/reference.ts', // If specified, generate this file that you can use for your reference management
out: '<%= yeoman.app %>/scripts/out.js', // If specified, generate an out.js file which is the merged js file
watch: '<%= yeoman.app %>/scripts/', // If specified, configures this target to watch the specified director for ts changes and reruns itself.
options: { // override the main options, http://gruntjs.com/configuring-tasks#options
sourcemap: true,
declaration: true
},
},
build: { // another target
src: ['<%= yeoman.app %>/scripts/*.ts'],
options: { // overide the main options for this target
sourcemap: false,
}
},
},
...
...
grunt.task.run([
...
'ts',
...
]);
...
grunt.registerTask('build', [
...
'ts',
...
]);
You can have a look at the full Gruntfile.js: https://github.com/mimo84/tsyong/blob/master/Gruntfile.js
Short answer: remove the watch config line https://github.com/mimo84/tsyong/blob/master/Gruntfile.js#L46 and add something like https://github.com/mimo84/tsyong/blob/master/Gruntfile.js#L60
But for ts. i.e.
ts: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.ts'],
tasks: ['ts:dev']
},
Reason: That's because when you ask grunt-ts to watch a folder, grunt-ts marks itself as an async task. This means that then no other tasks can execute afterwards. Its the same with grunt-contrib-watch I think which is why you must have it as the last task:
grunt.task.run([
'clean:server',
'concurrent:server',
'ts',
'connect:livereload',
'open',
'watch' // last task
]);
In short you can only have one task do your watching :) In your case it would have to be grunt-contrib-watch.
I use a very fast and simple way, using browserify & typescriptifier (<2s reload):
module.exports = function (grunt) {
grunt.initConfig({
clean: {
dev: ['dest/**/*.*']
},
browserify: {
dev: {
src: ['src/root.ts'],
dest: 'dest/App.js',
options: {
external: ['angular'],
transform: ['typescriptifier'],
debug: true,
bundleOptions: { debug: true },
browserifyOptions: { debug: true }
}
}
},
express: {
dev: {
options: {
bases: ['src'],
port: 5000,
hostname: '0.0.0.0',
livereload: false
}
}
},
watch: {
ts: {
files: ['src/**/*.ts', '!src/**/*.d.ts'],
tasks: ['dest'],
options: {
livereload: true,
debug: false,
debounceDelay: 100
}
},
html: {
files: ['src/**/*.css', 'src/**/*.html'],
options: {
livereload: true,
debug: false,
debounceDelay: 100,
spawn: false
}
}
}
});
grunt.loadNpmTasks('grunt-express');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.registerTask('dev', ['rebuild', 'express:dev', 'watch' ]);
grunt.registerTask('build', ['browserify:dev']);
grunt.registerTask('rebuild', ['clean:dev', 'build']);
};
See
https://www.npmjs.org/package/typescriptifier
Not exactly the answer but goes to the underlying point: fast workflow.

Resources