<!DOCTYPE html>
<html>

<head>
  <title>Angular 2 Playground</title>

  <link rel="stylesheet" href="style.css" />

  <script src="https://unpkg.com/core-js@2.4.1/client/shim.min.js"></script>
  <script src="https://unpkg.com/zone.js@0.7.2/dist/zone.js"></script>
  <script src="https://unpkg.com/reflect-metadata@0.1.3/Reflect.js"></script>
  <script src="https://unpkg.com/systemjs@0.19.31/dist/system.js"></script>

  <script src="config.js"></script>

  <script>
    System.import('app')
      .catch(console.error.bind(console));
  </script>
</head>

<body>
  <vm-app>
    loading...
  </vm-app>
</body>

</html>
const angularVersion = '4.3.3';

System.config({
  //use typescript for compilation
  transpiler: 'typescript',
  //typescript compiler options
  typescriptOptions: {
    emitDecoratorMetadata: true
  },
  paths: {
    'npm:': 'https://unpkg.com/'
  },
  //map tells the System loader where to look for things
  map: {

    'app': './src',

    '@angular/core': `npm:@angular/core@${angularVersion}/bundles/core.umd.js`,
    '@angular/common': `npm:@angular/common@${angularVersion}/bundles/common.umd.js`,
    '@angular/compiler': `npm:@angular/compiler@${angularVersion}/bundles/compiler.umd.js`,
    '@angular/platform-browser': `npm:@angular/platform-browser@${angularVersion}/bundles/platform-browser.umd.js`,
    '@angular/platform-browser-dynamic': `npm:@angular/platform-browser-dynamic@${angularVersion}/bundles/platform-browser-dynamic.umd.js`,
    '@angular/http': `npm:@angular/http@${angularVersion}/bundles/http.umd.js`,
    '@angular/router': `npm:@angular/router@${angularVersion}/bundles/router.umd.js`,
    '@angular/forms': `npm:@angular/forms@${angularVersion}/bundles/forms.umd.js`,
    '@angular/animations': `npm:@angular/animations@${angularVersion}/bundles/animations.umd.js`,
    '@angular/platform-browser/animations': `npm:@angular/platform-browser@${angularVersion}/bundles/platform-browser-animations.umd.js`,
    '@angular/animations/browser': `npm:@angular/animations@${angularVersion}/bundles/animations-browser.umd.js`,

    '@angular/core/testing': `npm:@angular/core@${angularVersion}/bundles/core-testing.umd.js`,
    '@angular/common/testing': `npm:@angular/common@${angularVersion}/bundles/common-testing.umd.js`,
    '@angular/compiler/testing': `npm:@angular/compiler@${angularVersion}/bundles/compiler-testing.umd.js`,
    '@angular/platform-browser/testing': `npm:@angular/platform-browser@${angularVersion}/bundles/platform-browser-testing.umd.js`,
    '@angular/platform-browser-dynamic/testing': `npm:@angular@${angularVersion}/platform-browser-dynamic/bundles/platform-browser-dynamic-testing.umd.js`,
    '@angular/http/testing': `npm:@angular/http@${angularVersion}/bundles/http-testing.umd.js`,
    '@angular/router/testing': `npm:@angular/router@${angularVersion}/bundles/router-testing.umd.js`,

    'rxjs': 'npm:rxjs@5.0.2',
    'typescript': 'npm:typescript@2.2.1/lib/typescript.js'
  },
  //packages defines our app package
  packages: {
    app: {
      main: './index.ts',
      defaultExtension: 'ts'
    },
    rxjs: {
      defaultExtension: 'js'
    }
  }
});
# Angular 2 Playground #
import {Component} from '@angular/core';
import {mockModule} from './typescriptHelper.ts';

@Component({
  selector: 'vm-app',
  template: `
    <div>Hello!</div>
  `
})
export class AppComponent {
  constructor() {
    
    let obj: any,
            prop: any,
            mock: any,
            revert: Function;
            
            
    prop = jasmine.createSpy('orig prop');
    obj = {
        prop
    };
    mock = {
        prop: jasmine.createSpy('mock prop')
    };

    revert = mockModule(obj, mock);

    obj.prop();
  }
}
import {NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {FormsModule} from '@angular/forms';
import {Http, HttpModule} from '@angular/http';
import {APP_BASE_HREF, HashLocationStrategy, LocationStrategy} from '@angular/common';
import {AppComponent} from './app.component';

@NgModule({
    imports: [
        BrowserModule,
        FormsModule,
        HttpModule
    ],
    declarations: [
        AppComponent
    ],
    providers: [
        {provide: APP_BASE_HREF, useValue: '/'},
        {provide: LocationStrategy, useClass: HashLocationStrategy},
        //{provide: Http, useFactory: httpFactory, useClass: MockHttp},
        //MockHttp,
        //appRoutingProviders
    ],
    bootstrap: [AppComponent]
})
export class AppModule {
}
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
import {AppModule} from './app.module';

platformBrowserDynamic().bootstrapModule(AppModule);
interface IMockModule {
    [key: string]: any;
}

function mockModule(module: IMockModule,
                    mockModule: IMockModule): Function {

    return Object.keys(mockModule)
        .map(name => mockPropertyInModule(module, name, module[name]))
        .reduce(
            (prev: Function, revert: Function) => () => {
                prev();
                revert();
            },
            () => {
            });
}

function mockClassProperties(classDefinition: Function,
                             mockProperties: { [name: string]: any }) {
    const oldProto = classDefinition.prototype;
    const newProto = {...oldProto};
    classDefinition.prototype = newProto;

    Object.keys(mockProperties)
        .forEach(key =>
            Object.defineProperty(newProto, key, {
                configurable: true,
                get: () => {
                    return mockProperties[key];
                },
                set: () => {
                    /* comment needed to prevent lint error */
                }
            }));

    return function revert() {
        classDefinition.prototype = oldProto;
    };
}

function mockPropertyInModule(module: IMockModule,
                              property: string,
                              value: any): Function {

    return mockProperty(module, property, {
        writable: true,
        value: value
    });
}

function mockPropertyGetter(module: IMockModule,
                            property: string,
                            getter: () => any): Function {

    return mockProperty(module, property, {
        get: getter
    });
}

function mockProperty(module: IMockModule,
                      property: string,
                      descriptor: PropertyDescriptor): Function {

    let target: any = module,
        bakProp: PropertyDescriptor,
        proto;
//    console.log('target', target); // tslint:disable-line:no-console

    while (target) {
        bakProp = Object.getOwnPropertyDescriptor(target, property);
//        console.log('bakProp', target, bakProp); // tslint:disable-line:no-console

        if (bakProp || target.constructor.prototype === proto) {
            break;
        }
        proto = target.constructor.prototype;
        target = proto;
    }

    if (!bakProp) {
        target = module;
    }
//    console.log('bef', Object.prototype.toString.call(target), target, bakProp); // tslint:disable-line:no-console

    Object.defineProperty(target, property, descriptor);

    return () => {
        // console.log('rev', Object.prototype.toString.call(target), target, bakProp, target[property]); // tslint:disable-line:no-console
        if (bakProp) {
            Object.defineProperty(target, property, bakProp);
        } else {
            delete target[property];
        }
    };
}

export {
    mockModule,
    mockClassProperties,
    mockPropertyInModule,
    mockPropertyGetter,
    mockProperty
};