import 'zone.js/dist/zone';
import 'zone.js/dist/async-test';
import 'zone.js/dist/fake-async-test';
import 'zone.js/dist/sync-test';

import {setBaseTestProviders} from '@angular/core/testing';

import {
  TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
  TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS
} from '@angular/platform-browser-dynamic/testing';

setBaseTestProviders(
    TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
    TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS
);

import 'app/calculator.spec';
import 'app/hello.component.spec';
import 'app/user-store.spec';
import {
  it,
  expect,
  describe
} from '@angular/core/testing';

import {Calculator} from './calculator';

describe('Calculator', () => {

    it('should say 4 when asked 2 * 2', () => {
      
        let calculator = new Calculator();
      
        expect(calculator.calculate('2 * 2')).toEqual(4);
      
    });
  
});

export class Calculator {

    calculate(formula: string) {
      
        let [value1, value2] = formula.split('*');
        
        return parseFloat(value1) * parseFloat(value2);
        
    }
  
}
import {Injectable} from '@angular/core';
import {HTTP_PROVIDERS, Http} from '@angular/http';
import {Observable} from 'rxjs/Rx';

import {User} from './user';


@Injectable()
export class UserStore {

    private _userListObservable: Observable<User[]> = null;
    private _userListObserver = null;
    private _userList = null;

    static PROVIDERS = [
        HTTP_PROVIDERS,
        UserStore
    ];

    constructor(private _http: Http) {

        this._userListObservable = new Observable<User[]>(observer => {

            let httpSubscription = null;

            this._userListObserver = observer;

            if (this._userList === null) {

                httpSubscription = this._http.get('/api/v1/users/')
                    .map(response => response.json().objects)
                    .subscribe(userList => {
                        this._userList = userList;
                        observer.next(userList);
                    });

            }
            else {
                observer.next(this._userList);
            }


            return () => {

                if (httpSubscription !== null) {
                    httpSubscription.unsubscribe();
                }

            };

        });

    }

    addUser({user}: {user: User}): Promise<User> {

        return this._http.post('/api/v1/users/', JSON.stringify(user))
            .map(response => response.json())
            .do(user => {
                this._userList.push(user);
                this._userListObserver.next(this._userList);
            })
            .toPromise();

    }

    userList(): Observable<User[]> {
        return this._userListObservable;
    }

}
/**
 *
 * (c) 2013-2016 Wishtack
 *
 * $Id: $
 */

import {provide} from '@angular/core';
import {Http, Response, ResponseOptions, RequestOptions, RequestMethod} from '@angular/http';
import {beforeEach, beforeEachProviders, describe, expect, fakeAsync, inject, it, tick} from '@angular/core/testing';
import {MockBackend, MockConnection} from '@angular/http/testing';
import {UserStore} from './user-store';
import {User} from './user';


describe('UserStore', () => {

    let spyConnection = null;

    beforeEachProviders(() => [
        MockBackend,
        UserStore.PROVIDERS,
        provide(Http, {
            deps: [MockBackend, RequestOptions],
            useFactory: (mockBackend, requestOptions) => {
                return new Http(mockBackend, requestOptions);
            }
        })
    ]);

    beforeEach(inject([MockBackend], (mockBackend:MockBackend) => {

        spyConnection = jasmine.createSpy('connection');

        mockBackend.connections.subscribe((connection:MockConnection) => {
            connection.mockRespond(new Response(new ResponseOptions(spyConnection({
                body: connection.request.text(),
                method: connection.request.method,
                url: connection.request.url
            }))));
        });

    }));

    it('should get user list from server', inject(
        [UserStore],
        (userStore:UserStore) => {

            let userList = null;

            spyConnection.and.returnValue({
                body: {
                    meta: {totalCount: 3},
                    objects: [
                        {
                            id: '1',
                            firstName: 'Foo',
                            lastName: 'BAR'
                        },
                        {
                            id: '2',
                            firstName: 'Mads',
                            lastName: 'MIKKELSEN'
                        }
                    ]
                }
            });

            userStore.userList()
                .subscribe(_userList_ => userList = _userList_)
                .unsubscribe();

            expect(userList).toEqual([
                {
                    id: '1',
                    firstName: 'Foo',
                    lastName: 'BAR'
                },
                {
                    id: '2',
                    firstName: 'Mads',
                    lastName: 'MIKKELSEN'
                }
            ]);

            expect(spyConnection.calls.count()).toEqual(1);

            expect(spyConnection).toHaveBeenCalledWith({
                body: '',
                method: RequestMethod.Get,
                url: '/api/v1/users/'
            });

            spyConnection.calls.reset();

            /* Second call should reuse cached result. */
            userStore.userList()
                .subscribe(result => userList = result)
                .unsubscribe();

            expect(userList).toEqual([
                {
                    id: '1',
                    firstName: 'Foo',
                    lastName: 'BAR'
                },
                {
                    id: '2',
                    firstName: 'Mads',
                    lastName: 'MIKKELSEN'
                }
            ]);

            expect(spyConnection.calls.count()).toEqual(0);

        })
    );

    it('should send user to server and invalidate observable cache', fakeAsync(inject(
        [UserStore],
        (userStore) => {

            let serverUser = null;
            let subscription = null;
            let userList = null;

            spyConnection.and.returnValue({
                body: {
                    meta: {totalCount: 3},
                    objects: [
                        {
                            id: '1',
                            firstName: 'Foo',
                            lastName: 'BAR'
                        },
                        {
                            id: '2',
                            firstName: 'Mads',
                            lastName: 'MIKKELSEN'
                        }
                    ]
                }
            });

            userStore.userList()
                .subscribe(data => userList = data)
                .unsubscribe();

            spyConnection.calls.reset();

            /* Add user */

            spyConnection.and.returnValue({
                body: {
                    id: '3',
                    firstName: 'Jack',
                    lastName: 'WILD'
                }
            });

            /* Subscribe should be called after user is added as user list has been updated. */
            subscription = userStore.userList()
                .subscribe(data => userList = data);

            userList = null;
            userStore.addUser({user: new User({firstName: 'Jack', lastName: 'WILD'})})
              .then(data => serverUser = data);
            
            tick();
            expect(serverUser.id).toEqual('3');

            expect(spyConnection).toHaveBeenCalledWith({
                body: JSON.stringify({firstName: 'Jack', lastName: 'WILD'}),
                method: RequestMethod.Post,
                url: '/api/v1/users/'
            });

            expect(userList.length).toEqual(3);
            expect(userList[2].id).toEqual('3');
            expect(userList[2].firstName).toEqual('Jack');
            expect(userList[2].lastName).toEqual('WILD');

            subscription.unsubscribe();

        })
    ));

});



export class User {

    id: string;
    firstName: string;
    lastName: string;

    constructor({id, firstName, lastName}: {id?: string, firstName: string, lastName: string}) {
        this.id = id;
        this.firstName = firstName;
        this.lastName = lastName;
    }
}
<!DOCTYPE html>
<html>

  <head>
    <title>Angular 2 QuickStart</title>

    <!-- 1. Load libraries -->
    <!-- Polyfill(s) for older browsers -->
    <script src="https://npmcdn.com/core-js/client/shim.min.js"></script>
    <script src="https://npmcdn.com/zone.js@0.6.12?main=browser"></script>
    <script src="https://npmcdn.com/reflect-metadata@0.1.3"></script>
    <script src="https://npmcdn.com/systemjs@0.19.27/dist/system.src.js"></script>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.2/jasmine.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.2/jasmine-html.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.3.2/boot.js"></script>
    
    <!-- 2. Configure SystemJS -->
    <script src="systemjs.config.js"></script>
    <script>
      System.import('app/bootstrap').catch(function(err){ console.error(err); });
    </script>
    <script>
      System.config({
        transpiler: 'typescript', 
        typescriptOptions: { emitDecoratorMetadata: true }, 
        packages: {'app': {defaultExtension: 'ts'}} 
      });
      System.import('app/bootstrap')
            .then(window.onload, console.error.bind(console));
    </script>

  </head>

  <!-- 3. Display the application -->
  <body>
  </body>

</html>

(function(global) {

  var ngVer     = '@2.0.0-rc.1';
  var routerVer = '@3.0.0-alpha.3';

  //map tells the System loader where to look for things
  var  map = {
    'app':                        'app',

    '@angular':                   'https://npmcdn.com/@angular',
    '@angular/router':            'https://npmcdn.com/@angular/router' + routerVer,
    'rxjs':                       'https://npmcdn.com/rxjs@5.0.0-beta.6',
    'ts':                         'https://npmcdn.com/plugin-typescript@4.0.10/lib/plugin.js',
    'typescript':                 'https://npmcdn.com/typescript@1.9.0-dev.20160409/lib/typescript.js',
    'zone.js':                    'https://npmcdn.com/zone.js@0.6.12'
 };

  var packages = {
    'app':                        { main: 'main.ts',  defaultExtension: 'ts' },
    'rxjs':                       { defaultExtension: 'js' },
    'zone.js':                    { defaultExtension: 'js' }
  };

  var ngPackageNames = [
    'common',
    'compiler',
    'core',
    'http',
    'platform-browser',
    'platform-browser-dynamic',
    'router-deprecated',
    'upgrade',
  ];

  // Add map entries for each angular package
  // only because we're pinning the version with `ngVer`.
  ngPackageNames.forEach(function(pkgName) {
    map['@angular/'+pkgName] = 'https://npmcdn.com/@angular/' + pkgName + ngVer;
  });

  // Add package entries for angular packages
  ngPackageNames.forEach(function(pkgName) {

    // Bundled (~40 requests):
    // packages['@angular/'+pkgName] = { main: pkgName + '.umd.js', defaultExtension: 'js' };

    // Individual files (~300 requests):
    packages['@angular/'+pkgName] = { main: 'index.js', defaultExtension: 'js' };
  });

  // No umd for router yet
  packages['@angular/router'] = { main: 'index.js', defaultExtension: 'js' };

  var config = {
    // DEMO ONLY! REAL CODE SHOULD NOT TRANSPILE IN THE BROWSER
    transpiler: 'ts',
    typescriptOptions: {
      tsconfig: true
    },
    meta: {
      'typescript': {
        "exports": "ts"
      }
    },
    map: map,
    packages: packages
  }

  System.config(config);

})(this);
{
  "compilerOptions": {
    "target": "es5",
    "module": "commonjs",
    "moduleResolution": "node",
    "sourceMap": true,
    "emitDecoratorMetadata": true,
    "experimentalDecorators": true,
    "removeComments": false,
    "noImplicitAny": true,
    "suppressImplicitAnyIndexErrors": true
  }
}
import {Component} from '@angular/core';
import {TestComponentBuilder} from '@angular/compiler/testing';
import {beforeEachProviders, describe, fakeAsync, inject, it} from '@angular/core/testing';

import {HelloComponent} from './hello.component';


describe('Hello component', () => {

    beforeEachProviders(() => [
        TestComponentBuilder
    ]);

    it('should say hi', fakeAsync(inject([TestComponentBuilder], (testComponentBuilder) => {

        let fixture = testComponentBuilder.createFakeAsync(HelloComponent);
        let element = fixture.nativeElement;

        fixture.componentInstance.name = 'John';

        fixture.detectChanges();

        expect(element.innerText).toEqual('Hello John');

    })));

    it('should say hi with wrapping test component', fakeAsync(inject([TestComponentBuilder], (testComponentBuilder) => {

        @Component({
            directives: [HelloComponent],
            selector: 'wt-test',
            template: `<wt-hello [name]="'John'"></wt-hello>`
        })
        class TestComponent {}

        let fixture = testComponentBuilder.createFakeAsync(TestComponent);
        let element = fixture.nativeElement;

        fixture.detectChanges();

        expect(element.innerText).toEqual('Hello John');

    })));

});

import {Component, Input} from '@angular/core';

@Component({
    selector: 'wt-hello',
    template: `
<div>
    <span>Hello </span><span [innerText]="name"></span>
</div>
`
})
export class HelloComponent {

    static PROVIDERS = [HelloComponent];

    @Input() name: string;

}