Running Jasmine Unit Testing with Angular 2 from CDN running SystemJS - javascript

I am looking to run Angular 2 from CDN links with SystemJS and also want to setup unit testing with Jasmine.
I have managed to get it loading the application and setup Jasmine in my code which is shown below:
/**
* based on systemjs.config.js in angular.io
* System configuration for Angular 2 samples
* Adjust as necessary for your application needs.
*/
(function(global) {
var addRouting = false;
//map tells the System loader where to look for things
var map = {
'app': 'bundles/core/src', // 'dist',
'rxjs': 'https://npmcdn.com/rxjs#' + rxJsVersion,
'angular2-in-memory-web-api': 'https://npmcdn.com/angular2-in-memory-web-api', // get latest
'ts': 'https://npmcdn.com/plugin-typescript#' + typescriptPluginVersion + '/lib/plugin.js',
'typescript': 'https://npmcdn.com/typescript#' + typescriptVersion + '/lib/typescript.js'
};
//packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main: 'main.ts', defaultExtension: 'ts' },
'rxjs': { defaultExtension: 'js' },
'angular2-in-memory-web-api': { defaultExtension: 'js' },
};
var packageNames = [
'#angular/common',
'#angular/compiler',
'#angular/core',
'#angular/http',
'#angular/platform-browser',
'#angular/platform-browser-dynamic',
'#angular/testing',
'#angular/upgrade',
'#angular/forms',
]
// add map entries for angular packages in the form '#angular/common': 'https://npmcdn.com/#angular/common#0.0.0-3'
packageNames.forEach(function(pkgName) {
map[pkgName] = 'https://npmcdn.com/' + pkgName + '#' + angularVersion;
});
// add package entries for angular packages in the form '#angular/common': { main: 'index.js', defaultExtension: 'js' }
packageNames.forEach(function(pkgName) {
packages[pkgName] = { main: 'index.js', defaultExtension: 'js' };
});
if ( addRouting ){
// Router API
map['#angular/router'] = 'https://npmcdn.com/#angular/router#' + angularRouterVersion;
packages['#angular/router'] = { main: 'index.js', defaultExtension: 'js' };
}
var config = {
transpiler: 'ts',
typescriptOptions: {
emitDecoratorMetadata: true,
module: "commonjs",
experimentalDecorators: true
},
meta: {
'typescript': {
"exports": "ts"
}
},
map: map,
packages: packages
}
// filterSystemConfig - index.html's chance to modify config before we register it.
if (global.filterSystemConfig) { global.filterSystemConfig(config); }
System.config(config);
})(this);
This loads the Angular 2 library sucessfully and I am able to see Angular render in the page using the following markup:
<script src="{{ asset('bundles/core/js/config.js') }}"></script>
<script>
function loadTestingScripts(){
return Promise.all([
System.import('bundles/core/src/app/app.spec.ts')
]);
}
Promise.all([
System.import('app')
])
.then( loadTestingScripts() )
.then( window.onload )
.catch( console.error.bind(console) );
</script>
However when I try and run the following test code:
import { ComponentFixture, TestBed } from '#angular/core/testing/index';
import { By } from '#angular/platform-browser/index';
import { DebugElement } from '#angular/core/index';
import { AppComponent } from './app.component';
let comp: AppComponent;
let fixture: ComponentFixture<AppComponent>;
let el: DebugElement;
describe('1st tests', () => {
it('true is true', () => expect(true).toBe(true));
});
describe('AppComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ AppComponent ], // declare the test component
});
fixture = TestBed.createComponent(AppComponent);
});
});
I receive an error 'TypeError: callback.apply is not a function' (zone.js#0.6.21:192).
However I have seen this achieved here with a beta version of the library:
http://embed.plnkr.co/vUjNLCVG8SVRYK6ZnKG0/
Is there any reason why the latest version cannot run unit testing with Angular 2 via a CDN url?
I am running Angular 2 final (2.0.0).
Below shows what I am trying to achieve:
https://embed.plnkr.co/7CCtow/

There seems to be two parts to this question, so I'll call them out and answer them individually.
Can we run jasmine unit testing with angular2 from CDN running systemjs?
Yes. I've created a plunker that seems to do what you wanted to do.
At the time of this answer, the systemjs.config.js in this plunk has been pretty hacked to help illustrate the changes, but I've copied the important parts below in case I clean it up in the future. The main changes that I needed to make to go from an older RC to the "latest" angular2 release version (2.4 at the time of writing) was to resolve the testing dependencies from the CDN for:
#angular/core/testing
#angular/platform-browser-dynamic/testing
#angular/platform-browser/testing
#angular/compiler/testing
And here's the appropriate fragment
var map = {
...
"#angular/core/testing": "https://npmcdn.com/#angular/core#2.4.0/bundles/core-testing.umd.js",
"#angular/platform-browser-dynamic/testing": "https://npmcdn.com/#angular/platform-browser-dynamic#2.4.0/bundles/platform-browser-dynamic-testing.umd.js",
"#angular/platform-browser/testing": "https://npmcdn.com/#angular/platform-browser#2.4.0/bundles/platform-browser-testing.umd.js",
"#angular/compiler/testing": "https://npmcdn.com/#angular/compiler#2.4.0/bundles/compiler-testing.umd.js",
...
};
Note: I am mapping the /testing dependencies to the umd format packages in the /bundles location. It looks like you're trying to map to the index.js inside #angular/core/testing/index which from what I can tell isn't the dependency that you're looking for, we want umd in this case.
Is there any reason why the latest version cannot run unit testing with Angular 2 via a CDN url?
Regarding the second part of your question, It seems you may be referencing the latest zone.js dependency as opposed to angular2.
No, there isn't a reason I can see. With the right configuration it works perfectly well with the version I tested:
0.6.21
0.6.23
0.6.25 which was the latest 0.6.x (at the time of writing)
I did find that there were a lot of changes and a few issues were raised in the zone.js project around this time, though many seem to be resolved by the versions we're talking about. the right configuration and order were important in my test running page where I have the fragment.
<script src="https://npmcdn.com/zone.js#0.6.25?main=browser"></script>
<script src="https://npmcdn.com/zone.js#0.6.25/dist/long-stack-trace-zone.js?main=browser"></script>
<script src="https://npmcdn.com/zone.js#0.6.25/dist/async-test.js?main=browser"></script>
<script src="https://npmcdn.com/zone.js#0.6.25/dist/fake-async-test.js?main=browser"></script>
<script src="https://npmcdn.com/zone.js#0.6.25/dist/sync-test.js?main=browser"></script>
<script src="https://npmcdn.com/zone.js#0.6.25/dist/proxy.js?main=browser"></script>
<script src="https://npmcdn.com/zone.js#0.6.25/dist/jasmine-patch.js?main=browser"></script>

Related

How do I include libphonenumber-js in my angular2 app using systemJS?

After installing https://github.com/halt-hammerzeit/libphonenumber-js using npm and updating my systemjs.config.ts to include
map:{
...
'libphonenumber-js': 'node_modules/libphonenumber-js'
},
packages: {
...
'libphonenumber-js': {
main: './custom.es6.js',
defaultExtension: 'js'
}
},
and attempting to use
import { parse, format, asYouType } from 'libphonenumber-js';
in my #Directive, I'm stuck with
Cannot find module 'libphonenumber-js'
How on earth am I supposed to wire this library into my app?
EDIT:
Directory layout:
websiteName
websiteName/index.html
websiteName/node_modules
websiteName/node_modules/libphonenumber-js
websiteName/app
websiteName/app/systemjs.config.ts
Index.html contains:
<script src="/app/systemjs.config.js"></script>
<script>
System.import('app').catch(function (err) { console.error(err); });
</script>
systemjs.config.ts contains:
declare var System: any;
/**
* System configuration for Angular samples
* Adjust as necessary for your application needs.
*/
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': 'node_modules/'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// other references
'libphonenumber-js': 'npm:libphonenumber-js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
// other references
'libphonenumber-js': {
main: './bundle/libphonenumber-js.min.js',
defaultExtension: 'js'
}
}
});
})(this);
Your package configuration is incorrect. It needs to point at the source file for libphonenumber-js. Need to update the references to match your project structure.
Try this:
(function (global) {
System.config({
paths: {
// paths serve as alias
'npm:': './node_modules'
},
// map tells the System loader where to look for things
map: {
// our app is within the app folder
app: 'app',
// other references
'libphonenumber-js': 'npm:libphonenumber-js'
},
// packages tells the System loader how to load when no filename and/or no extension
packages: {
app: {
main: './main.js',
defaultExtension: 'js'
},
// other references
'libphonenumber-js': {
main: 'libphonenumber-js.min',
defaultExtension: 'js'
}
}
});
})(this);

Angular2 + ag-Grid cannot load ag-grid-ng2/main.js (404 Not Found)

I'm trying to integrate ag-Grid with Angular2 but I'm stuck at following error:
zone.js:101 GET http://localhost:4200/node_modules/ag-grid-ng2/main.js
404 (Not Found)
I'm importing the third party component using:
import {AgGridNg2} from 'ag-grid-ng2/main';
...
directives: [AgGridNg2]
This is my system-config.ts file:
"use strict";
// SystemJS configuration file, see links for more information
// https://github.com/systemjs/systemjs
// https://github.com/systemjs/systemjs/blob/master/docs/config-api.md
/***********************************************************************************************
* User Configuration.
**********************************************************************************************/
/** Map relative paths to URLs. */
const map: any = {
// ag libraries
'ag-grid-ng2': 'node_modules/ag-grid-ng2',
'ag-grid': 'node_modules/ag-grid',
'ag-grid-enterprise' : 'node_modules/ag-grid-enterprise'
};
/** User packages configuration. */
const packages: any = {
'ag-grid-ng2': {
defaultExtension: "js"
},
'ag-grid': {
defaultExtension: "js"
},
'ag-grid-enterprise': {
defaultExtension: "js"
}
};
////////////////////////////////////////////////////////////////////////////////////////////////
/***********************************************************************************************
* Everything underneath this line is managed by the CLI.
**********************************************************************************************/
const barrels: string[] = [
// Angular specific barrels.
'#angular/core',
'#angular/common',
'#angular/compiler',
'#angular/forms',
'#angular/http',
'#angular/router',
'#angular/platform-browser',
'#angular/platform-browser-dynamic',
// Thirdparty barrels.
'rxjs',
// App specific barrels.
'app',
'app/shared',
'app/full-width-renderer',
/** #cli-barrel */
];
const cliSystemConfigPackages: any = {};
barrels.forEach((barrelName: string) => {
cliSystemConfigPackages[barrelName] = { main: 'index' };
});
/** Type declaration for ambient System. */
declare var System: any;
// Apply the CLI SystemJS configuration.
System.config({
map: {
'#angular': 'vendor/#angular',
'rxjs': 'vendor/rxjs',
'main': 'main.js'
},
packages: cliSystemConfigPackages
});
// Apply the user's configuration.
System.config({ map, packages });
I can see the dependency under the /node_modules/ag-grid-ng2 folder:
Any help is strongly appreciated.
I haven't used the enterprise grid, but for using ag-grid with Angular 2 you will need to include this in the map section of your System.config:
'ag-grid': 'node_modules/ag-grid',
'ag-grid-ng2': 'node_modules/ag-grid-ng2'
Also make sure you have these modules in your node_modules package, else make sure to include these descriptions in your package.json and run 'npm install' first.

SystemJS import fails silently on bundled Angular2 app

I am having trouble with SystemJS.
I have a typical Angular2 application written in Typescript.
I am trying to bundle all my application .js into a single file to optimize load time.
The bundle is created by gulp using systemjs-builder like this :
gulpfile.js
paths.compiledTs = "./wwwroot/app";
gulp.task('bundle:app', function (done) {
var builder = new Builder("/", './systemjs.config.js');
var builderConfig = {
normalize: true,
minify: true,
mangle: true,
runtime: false
};
builder.bundle(paths.compiledTs + '/main.js', paths.compiledTs + '/middleware-app.js', builderConfig)
.then(function () {
done();
})
.catch(function (err) {
console.log(err);
done();
});
});
The bundle is loaded though script tag. It works correcly as a call to System.defined in the browser console shows my modules :
System.defined
System.defined(picture)
Here is my systemjs.config.js. If I understand things correctly, an import to "app" should resolve to wwwroot/app/main.ts, then to the bundle file through the "bundles" parameter.
systemjs.config.js
/**
* System configuration
*/
(function (global) {
// map tells the System loader where to look for things
var map = {
'app': 'wwwroot/app',
'#angular': 'wwwroot/lib/#angular'
};
// packages tells the System loader how to load when no filename and/or no extension
var packages = {
'app': { main:'main.js', defaultExtension: 'js' },
'rxjs': { defaultExtension: 'js' },
'lodash': { defaultExtension: 'js' },
'moment': { defaultExtension: 'js' },
'pikaday': { defaultExtension: 'js' },
'ng2-translate': { defaultExtension: 'js' },
};
var ngPackageNames = [
'common',
'compiler',
'core',
'forms',
'http',
'platform-browser',
'platform-browser-dynamic',
'router'
];
// Individual files (~300 requests):
function packNgIndex(pkgName) {
packages['#angular/' + pkgName] = { main: 'index.js', defaultExtension: 'js' };
}
// Bundled (~40 requests):
function packNgUmd(pkgName) {
packages['#angular/' + pkgName] = { main: 'bundles/' + pkgName + '.umd.min.js', defaultExtension: 'js' };
}
// Most environments should use UMD; some (Karma) need the individual index files
var setNgPackageConfig = System.packageWithIndex ? packNgIndex : packNgUmd;
// Add package entries for angular packages
ngPackageNames.forEach(setNgPackageConfig);
var config = {
defaultJSExtensions: true,
baseURL: global.baseUrl,
paths: {
"lodash": "wwwroot/lib/lodash.min.js",
"moment": "wwwroot/lib/moment.min.js",
"pikaday": "wwwroot/lib/pikaday.js"
},
map: map,
packages: packages,
meta: {
pikaday: {
deps: ['moment']
},
'angular2/*': {
build: false
},
'rxjs/*': {
build: false
},
'ng2-translate/*': {
build: false
}
},
bundles: {
"wwwroot/lib/bundles/rxjs.min.js": [
"rxjs/*",
"rxjs/operator/*",
"rxjs/observable/*",
"rxjs/add/operator/*",
"rxjs/add/observable/*",
"rxjs/util/*"
],
"wwwroot/lib/bundles/ng2-translate.min.js": [
"ng2-translate/*"
],
"wwwroot/app/middleware-app.js": [
"app/*",
"app/common/busy-indicator/*",
"app/common/config/*",
"app/common/date-picker/*",
"app/common/dialog/*",
"app/common/guards/*",
"app/common/interface/*",
"app/common/login/*",
"app/common/models/*",
"app/common/pagination/*",
"app/common/services/*",
"app/common/storage/*",
"app/i18n/*",
"app/layout/*",
"app/pages/authentication/*",
"app/pages/logs/*",
"app/pages/monitoring/*",
"app/pages/not-found/*",
"app/pages/referentials/*",
"app/pages/reports/*"
]
}
};
System.config(config);
})(this);
However once I attempt to import my entry point from index.html, nothing happens (meaning I don't enter my then callback or my catch block). Here is an extract of my index :
<script src="#Href("~/wwwroot/app/middleware-app.js")"></script>
<script type="text/javascript">
System.import('app')
.then(app => {
console.log("never entered");
var endpoint = '#endpointUrl';
var version = '#Html.Version()';
var production = #Html.IsProductionEnabled();
app.run(endpoint, version, production);
})
.catch(function (err) {
console.log("never entered as well");
console.error(err);
});
</script>
I have tried various things like using bundleStatic instead, importing app/main.js, changing the bundling options.
Thank you for any help you can provide.
I assume that you are quite advanced and you have good angular2 understanding.
I had similar issue with #angular/upgrade component. After serious headache, I have found out that some of the error messages are lost inside angular2 (even in currently stable package).
The possible source of error can be inside #angular/compiler. The RuntimeCompiler.compileComponents() is losing messages from CompileMetadataResolver.getNgModuleMetadata(). Simple, yet not ideal solution is to wrap call to getNgModuleMetadata in try-catch block.
Solution when you are using bundled compiler and angular 2.0.0:
compiler.umd.js:line 16799
Change this:
RuntimeCompiler.prototype._compileComponents = function (mainModule, isSync) {
var _this = this;
var templates = new Set();
var loadingPromises = [];
var ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
ngModule.transitiveModule.modules.forEach(function (localModuleMeta) {
...
to:
RuntimeCompiler.prototype._compileComponents = function (mainModule, isSync) {
var _this = this;
var templates = new Set();
var loadingPromises = [];
try {
var ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
} catch(e) {
console.log(e);
throw e;
}
ngModule.transitiveModule.modules.forEach(function (localModuleMeta) {
...
Obviously, this is not ideal solution, but I was able to find out that one of my modules had circular dependency (which was causing undefined instead of valid service inside NgModule.import).

Angular 2 SFX production build

I have an Angular 2 Beta 8 (cannot update now) app that I need to bundle and minify for production deployment. With the following configs I can generate a SFX bundle but a minified version of the bundle does not work. It keeps on executing something and overflows the tab. I have component in it that has a console.log in in its constructor. That keeps running like more than 1000 times before the tab crashes. But the unminified version of the bundle runs as expected which is totally weird to me.
system.config.js
System.config({
defaultJSExtensions: true,
map: {
app: 'wwwroot/app/core',
angular2: 'node_modules/angular2',
rxjs: 'node_modules/rxjs',
dragula: 'wwwroot/lib/dragula/dragula.min',
'ng2-dragula/ng2-dragula': 'wwwroot/lib/dragula/ng2-dragula',
'ng2-cookies/ng2-cookies': 'wwwroot/lib/ng2-cookies/ng2-cookies'
},
packages: {
app: {
defaultExtension: 'js',
main: 'main.js'
},
angular2: {
defaultExtension: 'js'
},
rxjs: {
defaultExtension: 'js'
}
}
});
gulpfile.js
function getBuilder(configPath) {
var builder = new SystemBuilder();
return builder.loadConfig(configPath)
.then(function () {
return builder;
});
}
gulp.task('bundle', function () {
return getBuilder('./system.config.js')
.then(function (builder) {
return builder.buildStatic('app', './bundledapp.js', { minify: true });
});
});
Any help is really appreciated.
Due to some bug in Angular 2 Beta versions a prod build was not possible

Systemjs-Builder - Cannot configure properly - Bundling Typescript into a package

I want to build a quick nodejs script to package a Typescript app as SystemJS modules, a lot like what Angular2 bundles look like.
I tried different configurations but I can't seem to put my finger on it, and haven't found clear enough documentation as of yet.
Note that for this "test", I am not using Gulp or Jspm at all, just systemjs-builder for the time being (and don't plan on using jspm at all either)
Here's what my "project" looks like:
---- Project's Root
-------- index.ts // export * from './modules/index' and eventually more
-------- modules
------------ index.ts // export * from './menu/index'
------------ menu
---------------- menu.component.ts // export class
---------------- menu.service.ts // export class
I want to package this under a single file, where I will have multiple SystemRegister modules that can be consumed in an app thereafter
I tried the following without success:
var Builder = require('systemjs-builder');
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('./modules');
builder.bundle('./modules/index.ts', {
/* SystemJS Configuration Here */
baseURL: './modules',
transpiler: 'typescript',
typescriptOptions: {
"module": "system",
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
defaultExtension: 'ts',
packages: {
'modules': {
defaultExtension: 'ts'
}
}
}, 'infrastructure.js')
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.error(err);
})
First of all, the defaultExtension options doesn't seem to work at all
So when I do import {something} from 'filePath'; (without extension), it tries to load filePath, instead of filePath.ts;
Second, if I try adding the .ts extension in my imports (which I don't want to do), it complains that the code is invalid (unexpected token #, unexpected token menuItem and so forth)
Anyone have a good example or some explanations on how this is supposed to work?
Thank you
here you have an example: angular typescript skeleton
build task looks like this:
const path = require('path');
const Builder = require('jspm').Builder;
const builder = new Builder();
const packageJson = require(path.join(config.projectDir, 'package.json'));
return beginBuild()
.then(buildSFX)
.catch((err) => console.log('Build Failed', err));
function beginBuild() {
builder.reset();
return builder.loadConfig(path.join(config.projectDir, packageJson.jspm.configFile))
}
function buildSFX() {
const appName = packageJson.name;
const distFileName = `${appName}.min.js`;
const outFile = path.join(config.distDir, distFileName);
const moduleName = 'app';
const buildConfig = {
format: 'global',
minify: true,
sourceMaps: true
};
return builder.buildStatic(moduleName, outFile, buildConfig);
}
and jspm conf looks like this:
System.config({
defaultJSExtensions: true,
transpiler: "typescript",
typescriptOptions: {
"tsconfig": "src/tsconfig.json"
},
paths: {
"github:*": "vendor/jspm_packages/github/*",
"npm:*": "vendor/jspm_packages/npm/*",
"app": "src/index"
}
/// ...
}
Why do you want to bundle typescript? Bundling is a method used for optimizing the delivery of source code to the browser. The browser doesn't know typescript, it only knows javascript (unless you do on the fly transpiling).

Categories