├── .gitignore
├── LICENSE
├── README.md
├── index.html
├── karma-test-shim.js
├── karma.conf.js
├── package.json
├── src
├── bootstrap.ts
├── components
│ ├── gh-card.spec.ts
│ ├── gh-card.ts
│ ├── gh-repo.spec.ts
│ └── gh-repo.ts
├── git-happ.ts
├── pipes
│ ├── thousands.spec.ts
│ └── thousands.ts
├── services
│ ├── gh-repository.ts
│ ├── gh-service.spec.ts
│ ├── gh-service.ts
│ └── gh-user.ts
└── tsconfig.json
├── system-config.js
└── typings.json
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | dist
3 | typings
4 | coverage
5 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Raúl Jiménez
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Unit Testing with Angular 2
2 |
3 | Unit Testing with Angular 2 - Fair Day Workshop for ngConf 2016.
4 |
5 | https://www.ng-conf.org/#/sessions/elecashFD
6 |
7 | ## Installation
8 |
9 | Install dependencies:
10 |
11 | ```
12 | npm install -g typings
13 | npm install
14 | ```
15 |
16 | Try it!
17 |
18 | ```
19 | npm run dev
20 | ```
21 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Angular2 Github Cards
6 |
7 |
8 |
9 |
10 |
11 |
16 |
17 |
18 |
19 |
20 | Loading...
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/karma-test-shim.js:
--------------------------------------------------------------------------------
1 | // Tun on full stack traces in errors to help debugging
2 | Error.stackTraceLimit = Infinity;
3 |
4 | jasmine.DEFAULT_TIMEOUT_INTERVAL = 1000;
5 |
6 | // // Cancel Karma's synchronous start,
7 | // // we will call `__karma__.start()` later, once all the specs are loaded.
8 | __karma__.loaded = function() {};
9 |
10 | function isJsFile(path) {
11 | return path.slice(-3) == '.js';
12 | }
13 |
14 | function isSpecFile(path) {
15 | return path.slice(-8) == '.spec.js';
16 | }
17 |
18 | function isBuiltFile(path) {
19 | var builtPath = '/base/dist/';
20 | return isJsFile(path) && (path.substr(0, builtPath.length) == builtPath);
21 | }
22 |
23 | var allSpecFiles = Object.keys(window.__karma__.files)
24 | .filter(isSpecFile)
25 | .filter(isBuiltFile);
26 |
27 | System.config({
28 | baseURL: '/base'
29 | });
30 |
31 | System.config({
32 | map: {
33 | 'rxjs': 'node_modules/rxjs',
34 | '@angular': 'node_modules/@angular',
35 | 'app': 'dist'
36 | },
37 | packages: {
38 | 'app': {
39 | main: 'bootstrap.js',
40 | defaultExtension: 'js'
41 | },
42 | '@angular/core': {
43 | main: 'index.js',
44 | defaultExtension: 'js'
45 | },
46 | '@angular/compiler': {
47 | main: 'index.js',
48 | defaultExtension: 'js'
49 | },
50 | '@angular/common': {
51 | main: 'index.js',
52 | defaultExtension: 'js'
53 | },
54 | '@angular/http': {
55 | main: 'index.js',
56 | defaultExtension: 'js'
57 | },
58 | '@angular/testing': {
59 | main: 'index.js',
60 | defaultExtension: 'js'
61 | },
62 | '@angular/platform-browser': {
63 | main: 'index.js',
64 | defaultExtension: 'js'
65 | },
66 | '@angular/platform-browser-dynamic': {
67 | main: 'index.js',
68 | defaultExtension: 'js'
69 | },
70 | 'rxjs': {
71 | defaultExtension: 'js'
72 | }
73 | }
74 | });
75 |
76 | Promise.all([
77 | System.import('@angular/core/testing'),
78 | System.import('@angular/platform-browser-dynamic/testing')
79 | ])
80 | .then(
81 | function (providers) {
82 | var testing = providers[0];
83 | var testingBrowser = providers[1];
84 |
85 | testing.setBaseTestProviders(
86 | testingBrowser.TEST_BROWSER_DYNAMIC_PLATFORM_PROVIDERS,
87 | testingBrowser.TEST_BROWSER_DYNAMIC_APPLICATION_PROVIDERS
88 | );
89 | }
90 | )
91 | .then(
92 | function() {
93 | // Finally, load all spec files.
94 | // This will run the tests directly.
95 | return Promise.all(
96 | allSpecFiles.map(
97 | function (moduleName) {
98 | return System.import(moduleName);
99 | }
100 | )
101 | );
102 | }
103 | )
104 | .then(
105 | __karma__.start, __karma__.error
106 | );
107 |
--------------------------------------------------------------------------------
/karma.conf.js:
--------------------------------------------------------------------------------
1 | module.exports = function(config) {
2 | config.set({
3 |
4 | basePath: '.',
5 |
6 | frameworks: ['jasmine'],
7 |
8 | files: [
9 | // Polyfills
10 | 'node_modules/es6-shim/es6-shim.js',
11 | 'node_modules/reflect-metadata/Reflect.js',
12 |
13 | // System.js
14 | 'node_modules/systemjs/dist/system-polyfills.js',
15 | 'node_modules/systemjs/dist/system.src.js',
16 |
17 | // Zone.js
18 | 'node_modules/zone.js/dist/zone.js',
19 | 'node_modules/zone.js/dist/jasmine-patch.js',
20 | 'node_modules/zone.js/dist/async-test.js',
21 | 'node_modules/zone.js/dist/fake-async-test.js',
22 |
23 | // RxJs
24 | {pattern: 'node_modules/rxjs/**/*.js', included: false, watched: false},
25 | {pattern: 'node_modules/rxjs/**/*.js.map', included: false, watched: false},
26 |
27 | // Karma shim
28 | {pattern: 'karma-test-shim.js', included: true, watched: true},
29 |
30 | // Angular
31 | {pattern: 'node_modules/@angular/**/*.js', included: false, watched: true},
32 | {pattern: 'node_modules/@angular/**/*.js.map', included: false, watched: true},
33 |
34 | // Our app
35 | {pattern: 'dist/**/*.js', included: false, watched: true},
36 |
37 | // Typescript files and map files
38 | {pattern: 'src/**/*.ts', included: false, watched: false},
39 | {pattern: 'dist/**/*.js.map', included: false, watched: false}
40 | ],
41 |
42 | // proxied base paths
43 | proxies: {
44 | // required for component assests fetched by Angular's compiler
45 | '/src/': '/base/src/'
46 | },
47 |
48 | port: 9876,
49 |
50 | logLevel: config.LOG_INFO,
51 |
52 | colors: true,
53 |
54 | autoWatch: true,
55 |
56 | browsers: ['Chrome'],
57 |
58 | // Karma plugins loaded
59 | plugins: [
60 | 'karma-jasmine',
61 | 'karma-coverage',
62 | 'karma-chrome-launcher'
63 | ],
64 |
65 | // Coverage reporter generates the coverage
66 | reporters: ['progress', 'dots', 'coverage'],
67 |
68 | // Source files that you wanna generate coverage for.
69 | // Do not include tests or libraries (these files will be instrumented by Istanbul)
70 | preprocessors: {
71 | 'dist/**/!(*spec).js': ['coverage']
72 | },
73 |
74 | coverageReporter: {
75 | reporters:[
76 | {type: 'json', subdir: '.', file: 'coverage-final.json'}
77 | ]
78 | },
79 |
80 | singleRun: true
81 | })
82 | };
83 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-conf-testing",
3 | "version": "1.0.0",
4 | "description": "Angular2 Testing Workshop for ngConf",
5 | "main": "index.js",
6 | "scripts": {
7 | "postinstall": "typings install",
8 | "start": "npm run build && http-server -c-1 -o -s -p 8875 .",
9 | "dev": "npm run test && http-server -c-1 -o -s -p 8875 .",
10 | "build": "rm -rf dist && tsc -p src",
11 | "pretest": "npm run build",
12 | "test": "karma start karma.conf.js",
13 | "posttest": "node_modules/.bin/remap-istanbul -i coverage/coverage-final.json -o coverage -t html",
14 | "coverage": "http-server -c-1 -o -s -p 9875 ./coverage"
15 | },
16 | "repository": {
17 | "type": "git",
18 | "url": "git+https://elecash@github.com/Elecash/ng-conf-testing.git"
19 | },
20 | "author": "Raul Jimenez (http://twofuckingedevelopers.com/)",
21 | "license": "MIT",
22 | "bugs": {
23 | "url": "https://github.com/Elecash/ng-conf-testing/issues"
24 | },
25 | "homepage": "https://github.com/Elecash/ng-conf-testing#readme",
26 | "dependencies": {
27 | "@angular/common": "2.0.0-rc.2",
28 | "@angular/compiler": "2.0.0-rc.2",
29 | "@angular/core": "2.0.0-rc.2",
30 | "@angular/http": "2.0.0-rc.2",
31 | "@angular/platform-browser": "2.0.0-rc.2",
32 | "@angular/platform-browser-dynamic": "2.0.0-rc.2",
33 | "es6-promise": "3.1.2",
34 | "es6-shim": "0.35.0",
35 | "reflect-metadata": "0.1.2",
36 | "rxjs": "5.0.0-beta.6",
37 | "systemjs": "0.19.26",
38 | "zone.js": "0.6.12"
39 | },
40 | "devDependencies": {
41 | "http-server": "0.9.0",
42 | "jasmine-core": "2.4.1",
43 | "karma": "0.13.22",
44 | "karma-chrome-launcher": "0.2.2",
45 | "karma-coverage": "0.5.3",
46 | "karma-jasmine": "0.3.6",
47 | "remap-istanbul": "0.6.3",
48 | "typescript": "1.8.10",
49 | "typings": "0.8.1"
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/bootstrap.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import {bootstrap} from "@angular/platform-browser-dynamic";
4 | import {HTTP_PROVIDERS} from "@angular/http";
5 |
6 | import {GitHapp} from "./git-happ";
7 |
8 | bootstrap(GitHapp, [
9 | HTTP_PROVIDERS
10 | ]);
11 |
--------------------------------------------------------------------------------
/src/components/gh-card.spec.ts:
--------------------------------------------------------------------------------
1 | import {it, describe, expect, async, inject, beforeEachProviders, beforeEach} from "@angular/core/testing";
2 | import {TestComponentBuilder} from "@angular/compiler/testing";
3 | import {Component, provide} from "@angular/core";
4 | import {HTTP_PROVIDERS, XHRBackend, ResponseOptions, Response} from "@angular/http";
5 | import {MockBackend, MockConnection} from "@angular/http/testing";
6 | import {GithubCard} from "./gh-card";
7 |
8 | @Component({
9 | template: ' ',
10 | directives: [GithubCard]
11 | })
12 | class TestGithubCard {}
13 |
14 | describe('Github Card Tests', () => {
15 | var builder;
16 |
17 | beforeEachProviders(() => {
18 | return [
19 | HTTP_PROVIDERS,
20 | provide(XHRBackend, {useClass: MockBackend})
21 | ]
22 | });
23 |
24 | beforeEach(
25 | inject([XHRBackend, TestComponentBuilder], (backend, tcb) => {
26 | builder = tcb;
27 |
28 | backend.connections.subscribe(
29 | (connection:MockConnection) => {
30 | var options = new ResponseOptions({
31 | body: {
32 | "login": "johnpapa",
33 | "id": 1202528,
34 | "avatar_url": "https://avatars.githubusercontent.com/u/1202528?v=3",
35 | "gravatar_id": "",
36 | "url": "https://api.github.com/users/johnpapa",
37 | "html_url": "https://github.com/johnpapa",
38 | "followers_url": "https://api.github.com/users/johnpapa/followers",
39 | "following_url": "https://api.github.com/users/johnpapa/following{/other_user}",
40 | "gists_url": "https://api.github.com/users/johnpapa/gists{/gist_id}",
41 | "starred_url": "https://api.github.com/users/johnpapa/starred{/owner}{/repo}",
42 | "subscriptions_url": "https://api.github.com/users/johnpapa/subscriptions",
43 | "organizations_url": "https://api.github.com/users/johnpapa/orgs",
44 | "repos_url": "https://api.github.com/users/johnpapa/repos",
45 | "events_url": "https://api.github.com/users/johnpapa/events{/privacy}",
46 | "received_events_url": "https://api.github.com/users/johnpapa/received_events",
47 | "type": "User",
48 | "site_admin": false,
49 | "name": "John Papa",
50 | "company": "JohnPapa.net, LLC",
51 | "blog": "http://johnpapa.net",
52 | "location": "Orlando, FL",
53 | "email": null,
54 | "hireable": null,
55 | "bio": null,
56 | "public_repos": 62,
57 | "public_gists": 28,
58 | "followers": 4612,
59 | "following": 0,
60 | "created_at": "2011-11-17T17:05:03Z",
61 | "updated_at": "2016-04-03T09:51:44Z"
62 | }
63 | });
64 |
65 | var response:Response = new Response(options);
66 |
67 | connection.mockRespond(response);
68 | }
69 | );
70 | })
71 | );
72 |
73 | it('Should create a GithubCard component',
74 | async(() => {
75 | builder.createAsync(TestGithubCard).then((fixture) => {
76 | fixture.detectChanges();
77 |
78 | let compiled = fixture.debugElement.nativeElement;
79 | let name = compiled.querySelector('.avatar strong');
80 | let username = compiled.querySelector('.avatar span');
81 | let list = compiled.querySelectorAll('.status li a strong');
82 | let repos = list[0];
83 | let gists = list[1];
84 | let followers = list[2];
85 |
86 | expect(name.innerHTML).toContain('John Papa');
87 | expect(username.innerHTML).toContain('@johnpapa');
88 | expect(repos.innerHTML).toContain('62');
89 | expect(gists.innerHTML).toContain('28');
90 | expect(followers.innerHTML).toContain('4.6K');
91 | });
92 | })
93 | );
94 | });
95 |
--------------------------------------------------------------------------------
/src/components/gh-card.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, OnChanges} from "@angular/core";
2 | import {GithubUser} from "../services/gh-user";
3 | import {Thousands} from "../pipes/thousands";
4 | import {GithubService} from "../services/gh-service";
5 | import "rxjs/add/operator/map";
6 |
7 | @Component({
8 | selector: 'gh-card',
9 | template: `
10 | {{ loadingMessage }}
11 |
12 |
20 |
21 |
38 |
39 |
42 |
43 | `,
44 | styles: [`
45 | :host {
46 | display: block;
47 | max-width: 400px;
48 | padding: 0;
49 | margin: 0;
50 | font-size: 14px;
51 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
52 | overflow: hidden;
53 | border: 1px solid #eee;
54 | border-radius: 5px;
55 | border-color: #eee #ddd #bbb;
56 | box-shadow: rgba(0, 0, 0, 0.14) 0 1px 3px;
57 | }
58 |
59 | .github-card {
60 | border-radius: 5px;
61 | padding: 8px 8px 0;
62 | background: #fff;
63 | color: #555;
64 | position: relative;
65 | }
66 |
67 | .github-card a {
68 | text-decoration: none;
69 | color: #4183c4;
70 | outline: 0;
71 | }
72 | .github-card a:hover {
73 | text-decoration: underline;
74 | }
75 |
76 | .github-card .header {
77 | position: relative;
78 | }
79 |
80 | .github-card .button {
81 | position: absolute;
82 | top: 0;
83 | right: 0;
84 | padding: 4px 8px 4px 7px;
85 | color: #555;
86 | text-shadow: 0 1px 0 #fff;
87 | border: 1px solid #d4d4d4;
88 | border-radius: 3px;
89 | font-size: 13px;
90 | font-weight: bold;
91 | line-height: 14px;
92 | background-color: #e6e6e6;
93 | background-image: -webkit-linear-gradient(#fafafa, #eaeaea);
94 | background-image: -moz-linear-gradient(#fafafa, #eaeaea);
95 | background-image: -ms-linear-gradient(#fafafa, #eaeaea);
96 | background-image: linear-gradient(#fafafa, #eaeaea);
97 | }
98 |
99 | .github-card .button:hover {
100 | color: #fff;
101 | text-decoration: none;
102 | background-color: #3072b3;
103 | background-image: -webkit-linear-gradient(#599bdc, #3072b3);
104 | background-image: -moz-linear-gradient(#599bdc, #3072b3);
105 | background-image: -ms-linear-gradient(#599bdc, #3072b3);
106 | background-image: linear-gradient(#599bdc, #3072b3);
107 | border-color: #518cc6 #518cc6 #2a65a0;
108 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
109 | }
110 |
111 | /* user card */
112 | .user-card .header {
113 | padding: 3px 0 4px 57px;
114 | min-height: 48px;
115 | }
116 |
117 | .user-card .header a {
118 | text-decoration: none;
119 | }
120 |
121 | .user-card .header a:hover strong {
122 | text-decoration: underline;
123 | }
124 |
125 | .user-card img {
126 | position: absolute;
127 | top: 0;
128 | left: 0;
129 | width: 48px;
130 | height: 48px;
131 | background: #fff;
132 | border-radius: 4px;
133 | }
134 |
135 | .user-card strong {
136 | display: block;
137 | color: #292f33;
138 | font-size: 16px;
139 | line-height: 1.6;
140 | }
141 |
142 | .user-card ul {
143 | text-transform: uppercase;
144 | font-size: 12px;
145 | color: #707070;
146 | list-style-type: none;
147 | margin: 0;
148 | padding: 0;
149 | border-top: 1px solid #eee;
150 | border-bottom: 1px solid #eee;
151 | zoom: 1;
152 | }
153 |
154 | .user-card ul:after {
155 | display: block;
156 | content: '';
157 | clear: both;
158 | }
159 |
160 | .user-card .status a {
161 | color: #707070;
162 | text-decoration: none;
163 | }
164 |
165 | .user-card .status a:hover {
166 | color: #4183c4;
167 | }
168 |
169 | .user-card .status li {
170 | float: left;
171 | padding: 4px 18px;
172 | border-left: 1px solid #eee;
173 | }
174 |
175 | .user-card .status li:first-child {
176 | border-left: 0;
177 | padding-left: 0;
178 | }
179 |
180 | .user-card .footer {
181 | font-size: 12px;
182 | font-weight: 700;
183 | padding: 11px 0 10px;
184 | color: #646464;
185 | }
186 |
187 | .user-card .footer a {
188 | color: #646464;
189 | }
190 | `],
191 | providers: [GithubService],
192 | pipes: [Thousands]
193 | })
194 | export class GithubCard implements OnChanges {
195 | @Input() ghUser:string;
196 |
197 | user:GithubUser;
198 | loadingMessage:string = '';
199 |
200 | constructor(public service:GithubService) {}
201 |
202 | ngOnChanges(changeRecord) {
203 | if (changeRecord.ghUser.currentValue) {
204 | this.searchUser(changeRecord.ghUser.currentValue);
205 | }
206 | }
207 |
208 | searchUser(username:string) {
209 | this.loadingMessage = 'loading user...';
210 |
211 | this.service.getUserByUsername(username)
212 | .subscribe(
213 | this.onLoadUser.bind(this),
214 | this.onLoadUserError.bind(this)
215 | );
216 | }
217 |
218 | onLoadUser(user) {
219 | this.loadingMessage = '';
220 | this.user = user;
221 | }
222 |
223 | onLoadUserError(user) {
224 | this.loadingMessage = "User doesn't exist";
225 | this.user = null;
226 | }
227 | }
228 |
--------------------------------------------------------------------------------
/src/components/gh-repo.spec.ts:
--------------------------------------------------------------------------------
1 | import {it, describe, expect, async, inject, beforeEachProviders, beforeEach} from "@angular/core/testing";
2 | import {TestComponentBuilder} from '@angular/compiler/testing';
3 | import {Component, provide} from "@angular/core";
4 | import {HTTP_PROVIDERS, XHRBackend, ResponseOptions, Response} from "@angular/http";
5 | import {MockBackend, MockConnection} from "@angular/http/testing";
6 | import {GithubRepo} from "./gh-repo";
7 |
8 | @Component({
9 | template: ' ',
10 | directives: [GithubRepo]
11 | })
12 | class TestGithubRepo {}
13 |
14 | describe('Github Repo Tests', () => {
15 | var builder;
16 |
17 | beforeEachProviders(() => {
18 | return [
19 | HTTP_PROVIDERS,
20 | provide(XHRBackend, {useClass: MockBackend})
21 | ]
22 | });
23 |
24 | beforeEach(
25 | inject([XHRBackend, TestComponentBuilder], (backend, tcb) => {
26 | builder = tcb;
27 |
28 | backend.connections.subscribe(
29 | (connection:MockConnection) => {
30 | var options = new ResponseOptions({
31 | body: {
32 | "id": 11823977,
33 | "name": "videogular",
34 | "full_name": "videogular/videogular",
35 | "owner": {
36 | "login": "videogular",
37 | "id": 15369882,
38 | "avatar_url": "https://avatars.githubusercontent.com/u/15369882?v=3",
39 | "gravatar_id": "",
40 | "url": "https://api.github.com/users/videogular",
41 | "html_url": "https://github.com/videogular",
42 | "followers_url": "https://api.github.com/users/videogular/followers",
43 | "following_url": "https://api.github.com/users/videogular/following{/other_user}",
44 | "gists_url": "https://api.github.com/users/videogular/gists{/gist_id}",
45 | "starred_url": "https://api.github.com/users/videogular/starred{/owner}{/repo}",
46 | "subscriptions_url": "https://api.github.com/users/videogular/subscriptions",
47 | "organizations_url": "https://api.github.com/users/videogular/orgs",
48 | "repos_url": "https://api.github.com/users/videogular/repos",
49 | "events_url": "https://api.github.com/users/videogular/events{/privacy}",
50 | "received_events_url": "https://api.github.com/users/videogular/received_events",
51 | "type": "Organization",
52 | "site_admin": false
53 | },
54 | "private": false,
55 | "html_url": "https://github.com/videogular/videogular",
56 | "description": "The HTML5 video player for AngularJS",
57 | "fork": false,
58 | "url": "https://api.github.com/repos/videogular/videogular",
59 | "forks_url": "https://api.github.com/repos/videogular/videogular/forks",
60 | "keys_url": "https://api.github.com/repos/videogular/videogular/keys{/key_id}",
61 | "collaborators_url": "https://api.github.com/repos/videogular/videogular/collaborators{/collaborator}",
62 | "teams_url": "https://api.github.com/repos/videogular/videogular/teams",
63 | "hooks_url": "https://api.github.com/repos/videogular/videogular/hooks",
64 | "issue_events_url": "https://api.github.com/repos/videogular/videogular/issues/events{/number}",
65 | "events_url": "https://api.github.com/repos/videogular/videogular/events",
66 | "assignees_url": "https://api.github.com/repos/videogular/videogular/assignees{/user}",
67 | "branches_url": "https://api.github.com/repos/videogular/videogular/branches{/branch}",
68 | "tags_url": "https://api.github.com/repos/videogular/videogular/tags",
69 | "blobs_url": "https://api.github.com/repos/videogular/videogular/git/blobs{/sha}",
70 | "git_tags_url": "https://api.github.com/repos/videogular/videogular/git/tags{/sha}",
71 | "git_refs_url": "https://api.github.com/repos/videogular/videogular/git/refs{/sha}",
72 | "trees_url": "https://api.github.com/repos/videogular/videogular/git/trees{/sha}",
73 | "statuses_url": "https://api.github.com/repos/videogular/videogular/statuses/{sha}",
74 | "languages_url": "https://api.github.com/repos/videogular/videogular/languages",
75 | "stargazers_url": "https://api.github.com/repos/videogular/videogular/stargazers",
76 | "contributors_url": "https://api.github.com/repos/videogular/videogular/contributors",
77 | "subscribers_url": "https://api.github.com/repos/videogular/videogular/subscribers",
78 | "subscription_url": "https://api.github.com/repos/videogular/videogular/subscription",
79 | "commits_url": "https://api.github.com/repos/videogular/videogular/commits{/sha}",
80 | "git_commits_url": "https://api.github.com/repos/videogular/videogular/git/commits{/sha}",
81 | "comments_url": "https://api.github.com/repos/videogular/videogular/comments{/number}",
82 | "issue_comment_url": "https://api.github.com/repos/videogular/videogular/issues/comments{/number}",
83 | "contents_url": "https://api.github.com/repos/videogular/videogular/contents/{+path}",
84 | "compare_url": "https://api.github.com/repos/videogular/videogular/compare/{base}...{head}",
85 | "merges_url": "https://api.github.com/repos/videogular/videogular/merges",
86 | "archive_url": "https://api.github.com/repos/videogular/videogular/{archive_format}{/ref}",
87 | "downloads_url": "https://api.github.com/repos/videogular/videogular/downloads",
88 | "issues_url": "https://api.github.com/repos/videogular/videogular/issues{/number}",
89 | "pulls_url": "https://api.github.com/repos/videogular/videogular/pulls{/number}",
90 | "milestones_url": "https://api.github.com/repos/videogular/videogular/milestones{/number}",
91 | "notifications_url": "https://api.github.com/repos/videogular/videogular/notifications{?since,all,participating}",
92 | "labels_url": "https://api.github.com/repos/videogular/videogular/labels{/name}",
93 | "releases_url": "https://api.github.com/repos/videogular/videogular/releases{/id}",
94 | "deployments_url": "https://api.github.com/repos/videogular/videogular/deployments",
95 | "created_at": "2013-08-01T18:05:20Z",
96 | "updated_at": "2016-04-29T01:33:33Z",
97 | "pushed_at": "2016-04-21T23:45:13Z",
98 | "git_url": "git://github.com/videogular/videogular.git",
99 | "ssh_url": "git@github.com:videogular/videogular.git",
100 | "clone_url": "https://github.com/videogular/videogular.git",
101 | "svn_url": "https://github.com/videogular/videogular",
102 | "homepage": "http://www.videogular.com/demo",
103 | "size": 146733,
104 | "stargazers_count": 1166,
105 | "watchers_count": 1166,
106 | "language": "JavaScript",
107 | "has_issues": true,
108 | "has_downloads": true,
109 | "has_wiki": true,
110 | "has_pages": false,
111 | "forks_count": 286,
112 | "mirror_url": null,
113 | "open_issues_count": 20,
114 | "forks": 286,
115 | "open_issues": 20,
116 | "watchers": 1166,
117 | "default_branch": "master",
118 | "organization": {
119 | "login": "videogular",
120 | "id": 15369882,
121 | "avatar_url": "https://avatars.githubusercontent.com/u/15369882?v=3",
122 | "gravatar_id": "",
123 | "url": "https://api.github.com/users/videogular",
124 | "html_url": "https://github.com/videogular",
125 | "followers_url": "https://api.github.com/users/videogular/followers",
126 | "following_url": "https://api.github.com/users/videogular/following{/other_user}",
127 | "gists_url": "https://api.github.com/users/videogular/gists{/gist_id}",
128 | "starred_url": "https://api.github.com/users/videogular/starred{/owner}{/repo}",
129 | "subscriptions_url": "https://api.github.com/users/videogular/subscriptions",
130 | "organizations_url": "https://api.github.com/users/videogular/orgs",
131 | "repos_url": "https://api.github.com/users/videogular/repos",
132 | "events_url": "https://api.github.com/users/videogular/events{/privacy}",
133 | "received_events_url": "https://api.github.com/users/videogular/received_events",
134 | "type": "Organization",
135 | "site_admin": false
136 | },
137 | "network_count": 286,
138 | "subscribers_count": 77
139 | }
140 | });
141 |
142 | var response:Response = new Response(options);
143 |
144 | connection.mockRespond(response);
145 | }
146 | );
147 | })
148 | );
149 |
150 | it('Should create a GithubRepo component',
151 | async(inject([TestComponentBuilder], (tcb) => {
152 | tcb.createAsync(TestGithubRepo).then((fixture) => {
153 | fixture.detectChanges();
154 |
155 | let compiled = fixture.debugElement.nativeElement;
156 | let name = compiled.querySelector('.header .name a');
157 | let language = compiled.querySelector('.header .name .language');
158 | let description = compiled.querySelector('.content p');
159 | let link = compiled.querySelector('.content p a');
160 | let stats = compiled.querySelectorAll('.footer .status strong');
161 | let forks = stats[0];
162 | let stars = stats[1];
163 |
164 | expect(name.innerHTML).toContain('videogular');
165 | expect(language.innerHTML).toContain('JavaScript');
166 | expect(description.innerHTML).toContain('The HTML5 video player for AngularJS');
167 | expect(link.innerHTML).toContain('http://www.videogular.com/demo');
168 | expect(forks.innerHTML).toContain('286');
169 | expect(stars.innerHTML).toContain('1166');
170 | });
171 | }))
172 | );
173 | });
174 |
--------------------------------------------------------------------------------
/src/components/gh-repo.ts:
--------------------------------------------------------------------------------
1 | import {Component, Input, OnChanges} from "@angular/core";
2 | import {GithubRepository} from "../services/gh-repository";
3 | import {Thousands} from "../pipes/thousands";
4 | import {GithubService} from "../services/gh-service";
5 | import "rxjs/add/operator/map";
6 |
7 | @Component({
8 | selector: 'gh-repo',
9 | template: `
10 | {{ loadingMessage }}
11 |
38 | `,
39 | styles: [`
40 | :host {
41 | display: block;
42 | max-width: 400px;
43 | padding: 0;
44 | margin: 0;
45 | font-size: 14px;
46 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
47 | overflow: hidden;
48 | border: 1px solid #eee;
49 | border-radius: 5px;
50 | border-color: #eee #ddd #bbb;
51 | box-shadow: rgba(0, 0, 0, 0.14) 0 1px 3px;
52 | }
53 |
54 | .github-card {
55 | border-radius: 5px;
56 | padding: 8px 8px 0;
57 | background: #fff;
58 | color: #555;
59 | position: relative;
60 | }
61 |
62 | .github-card a {
63 | text-decoration: none;
64 | color: #4183c4;
65 | outline: 0;
66 | }
67 | .github-card a:hover {
68 | text-decoration: underline;
69 | }
70 |
71 | .github-card .header {
72 | position: relative;
73 | }
74 |
75 | .github-card .button {
76 | position: absolute;
77 | top: 0;
78 | right: 0;
79 | padding: 4px 8px 4px 7px;
80 | color: #555;
81 | text-shadow: 0 1px 0 #fff;
82 | border: 1px solid #d4d4d4;
83 | border-radius: 3px;
84 | font-size: 13px;
85 | font-weight: bold;
86 | line-height: 14px;
87 | background-color: #e6e6e6;
88 | background-image: -webkit-linear-gradient(#fafafa, #eaeaea);
89 | background-image: -moz-linear-gradient(#fafafa, #eaeaea);
90 | background-image: -ms-linear-gradient(#fafafa, #eaeaea);
91 | background-image: linear-gradient(#fafafa, #eaeaea);
92 | }
93 |
94 | .github-card .button:hover {
95 | color: #fff;
96 | text-decoration: none;
97 | background-color: #3072b3;
98 | background-image: -webkit-linear-gradient(#599bdc, #3072b3);
99 | background-image: -moz-linear-gradient(#599bdc, #3072b3);
100 | background-image: -ms-linear-gradient(#599bdc, #3072b3);
101 | background-image: linear-gradient(#599bdc, #3072b3);
102 | border-color: #518cc6 #518cc6 #2a65a0;
103 | text-shadow: 0 -1px 0 rgba(0,0,0,.25);
104 | }
105 |
106 | /* repo card */
107 | .repo-card .header {
108 | padding: 3px 0 4px 57px;
109 | }
110 | .repo-card .avatar, .repo-card .avatar img {
111 | position: absolute;
112 | top: 0;
113 | left: 0;
114 | width: 48px;
115 | height: 48px;
116 | background: #fff;
117 | border-radius: 4px;
118 | }
119 | .repo-card .header a {
120 | color: #707070;
121 | }
122 | .repo-card .header a:hover {
123 | color: #FFF;
124 | }
125 | .repo-card .header strong {
126 | display: block;
127 | font-size: 18px;
128 | line-height: 1.4;
129 | }
130 | .repo-card .header strong a {
131 | color: #292f33;
132 | }
133 | .repo-card .header sup {
134 | font-size: 10px;
135 | margin-left: 3px;
136 | color: #797979;
137 | }
138 | .repo-card .content {
139 | padding: 6px 0 10px;
140 | }
141 | .repo-card .content p {
142 | margin: 0 5px 0 0;
143 | font: 18px/24px Georgia, "Times New Roman", Palatino, serif;
144 | overflow: hidden;
145 | clear: both;
146 | word-wrap: break-word;
147 | }
148 | .repo-card .footer {
149 | border-top: 1px solid #eee;
150 | padding: 8px 0 6px;
151 | }
152 | .repo-card .status {
153 | font-size: 10px;
154 | padding-right: 10px;
155 | text-transform: uppercase;
156 | }
157 | .repo-card .status strong {
158 | font-size: 12px;
159 | padding-right: 5px;
160 | }
161 | `],
162 | providers: [GithubService],
163 | pipes: [Thousands]
164 | })
165 | export class GithubRepo implements OnChanges {
166 | @Input() ghRepo:string;
167 |
168 | repo:GithubRepository;
169 | loadingMessage:string = '';
170 |
171 | constructor(public service:GithubService) {}
172 |
173 | ngOnChanges(changeRecord) {
174 | if (changeRecord.ghRepo.currentValue) {
175 | this.searchRepo(changeRecord.ghRepo.currentValue);
176 | }
177 | }
178 |
179 | searchRepo(repo:string) {
180 | this.loadingMessage = 'loading repository...';
181 |
182 | this.service.getRepository(repo)
183 | .subscribe(
184 | this.onLoadRepo.bind(this),
185 | this.onLoadRepoError.bind(this)
186 | );
187 | }
188 |
189 | onLoadRepo(repo) {
190 | this.loadingMessage = '';
191 | this.repo = repo;
192 | }
193 |
194 | onLoadRepoError(repo) {
195 | this.loadingMessage = "Repository doesn't exist";
196 | this.repo = null;
197 | }
198 | }
199 |
--------------------------------------------------------------------------------
/src/git-happ.ts:
--------------------------------------------------------------------------------
1 | import {Component} from "@angular/core";
2 | import {GithubCard} from "./components/gh-card";
3 | import {GithubRepo} from "./components/gh-repo";
4 |
5 | @Component({
6 | selector: 'git-happ',
7 | template: `
8 | Search by username:
9 |
10 |
11 |
12 | Search by repository:
13 |
14 |
15 | `,
16 | directives: [GithubCard, GithubRepo]
17 | })
18 | export class GitHapp {
19 | ghUser:string;
20 | ghRepo:string;
21 |
22 | constructor() {}
23 |
24 | searchUser(input) {
25 | this.ghUser = input.value;
26 | }
27 |
28 | searchRepo(input) {
29 | this.ghRepo = input.value;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/pipes/thousands.spec.ts:
--------------------------------------------------------------------------------
1 | import {it, describe, expect, beforeEach} from '@angular/core/testing';
2 | import {Thousands} from "./thousands";
3 |
4 | describe('Thousands pipe tests', () => {
5 | let pipe:Thousands;
6 |
7 | beforeEach(() => {
8 | pipe = new Thousands();
9 | });
10 |
11 | it('Should transform 300 to 300', () => {
12 | var result = pipe.transform('300', null);
13 |
14 | expect(result).toEqual('300');
15 | });
16 |
17 | it('Should transform 1300 to 1.3K', () => {
18 | var result = pipe.transform('1300', null);
19 |
20 | expect(result).toEqual('1.3K');
21 | });
22 |
23 | it('Should transform 13000 to 13K', () => {
24 | var result = pipe.transform('13000', null);
25 |
26 | expect(result).toEqual('13K');
27 | });
28 |
29 | it('Should transform 13500 to 13.5K', () => {
30 | var result = pipe.transform('13500', null);
31 |
32 | expect(result).toEqual('13.5K');
33 | });
34 |
35 | it('Should transform 13599 to 13.6K', () => {
36 | var result = pipe.transform('13599', null);
37 |
38 | expect(result).toEqual('13.6K');
39 | });
40 | });
41 |
--------------------------------------------------------------------------------
/src/pipes/thousands.ts:
--------------------------------------------------------------------------------
1 | import {PipeTransform, Pipe} from '@angular/core';
2 |
3 | @Pipe({name: 'thousands'})
4 | export class Thousands implements PipeTransform {
5 | constructor() {
6 | }
7 |
8 | transform(text:string, args:any[]):any {
9 | let num:number = parseInt(text);
10 | let result:string;
11 |
12 | if (isNaN(num) || num < 1000) {
13 | result = text;
14 | }
15 | else {
16 | let exp:number = (Math.floor(Math.log(num) / Math.log(1000)));
17 | let value:number = (num / Math.pow(1000, exp));
18 | let decimals:number = (value % 1 != 0) ? 1 : 0;
19 | result = value.toFixed(decimals) + 'K';
20 | }
21 |
22 | return result;
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/services/gh-repository.ts:
--------------------------------------------------------------------------------
1 | export class GithubRepository {
2 | id: number;
3 | name: string;
4 | full_name: string;
5 | owner: any;
6 | private: boolean;
7 | html_url: string;
8 | description: string;
9 | fork: boolean;
10 | url: string;
11 | forks_url: string;
12 | keys_url: string;
13 | collaborators_url: string;
14 | teams_url: string;
15 | hooks_url: string;
16 | issue_events_url: string;
17 | events_url: string;
18 | assignees_url: string;
19 | branches_url: string;
20 | tags_url: string;
21 | blobs_url: string;
22 | git_tags_url: string;
23 | git_refs_url: string;
24 | trees_url: string;
25 | statuses_url: string;
26 | languages_url: string;
27 | stargazers_url: string;
28 | contributors_url: string;
29 | subscribers_url: string;
30 | subscription_url: string;
31 | commits_url: string;
32 | git_commits_url: string;
33 | comments_url: string;
34 | issue_comment_url: string;
35 | contents_url: string;
36 | compare_url: string;
37 | merges_url: string;
38 | archive_url: string;
39 | downloads_url: string;
40 | issues_url: string;
41 | pulls_url: string;
42 | milestones_url: string;
43 | notifications_url: string;
44 | labels_url: string;
45 | releases_url: string;
46 | deployments_url: string;
47 | created_at: string;
48 | updated_at: string;
49 | pushed_at: string;
50 | git_url: string;
51 | ssh_url: string;
52 | clone_url: string;
53 | svn_url: string;
54 | homepage: string;
55 | size: number;
56 | stargazers_count: number;
57 | watchers_count: number;
58 | language: string;
59 | has_issues: boolean;
60 | has_downloads: boolean;
61 | has_wiki: boolean;
62 | has_pages: boolean;
63 | forks_count: number;
64 | mirror_url: string;
65 | open_issues_count: number;
66 | forks: number;
67 | open_issues: number;
68 | watchers: number;
69 | default_branch: string;
70 | organization: any;
71 | network_count: number;
72 | subscribers_count: number
73 | }
74 |
--------------------------------------------------------------------------------
/src/services/gh-service.spec.ts:
--------------------------------------------------------------------------------
1 | import {it, describe, expect, async, inject, beforeEachProviders} from "@angular/core/testing";
2 | import {provide} from "@angular/core";
3 | import {HTTP_PROVIDERS, XHRBackend, ResponseOptions, Response} from "@angular/http";
4 | import {MockBackend, MockConnection} from "@angular/http/testing";
5 | import {GithubService} from "./gh-service";
6 |
7 | describe('Github Service Tests', () => {
8 | beforeEachProviders(() => {
9 | return [
10 | HTTP_PROVIDERS,
11 | provide(XHRBackend, {useClass: MockBackend}),
12 | GithubService
13 | ]
14 | });
15 |
16 | it('Should get a user by username',
17 | async(inject([XHRBackend, GithubService], (backend, service) => {
18 | backend.connections.subscribe(
19 | (connection:MockConnection) => {
20 | var options = new ResponseOptions({
21 | body: {
22 | "login": "johnpapa",
23 | "id": 1202528,
24 | "avatar_url": "https://avatars.githubusercontent.com/u/1202528?v=3",
25 | "gravatar_id": "",
26 | "url": "https://api.github.com/users/johnpapa",
27 | "html_url": "https://github.com/johnpapa",
28 | "followers_url": "https://api.github.com/users/johnpapa/followers",
29 | "following_url": "https://api.github.com/users/johnpapa/following{/other_user}",
30 | "gists_url": "https://api.github.com/users/johnpapa/gists{/gist_id}",
31 | "starred_url": "https://api.github.com/users/johnpapa/starred{/owner}{/repo}",
32 | "subscriptions_url": "https://api.github.com/users/johnpapa/subscriptions",
33 | "organizations_url": "https://api.github.com/users/johnpapa/orgs",
34 | "repos_url": "https://api.github.com/users/johnpapa/repos",
35 | "events_url": "https://api.github.com/users/johnpapa/events{/privacy}",
36 | "received_events_url": "https://api.github.com/users/johnpapa/received_events",
37 | "type": "User",
38 | "site_admin": false,
39 | "name": "John Papa",
40 | "company": "JohnPapa.net, LLC",
41 | "blog": "http://johnpapa.net",
42 | "location": "Orlando, FL",
43 | "email": null,
44 | "hireable": null,
45 | "bio": null,
46 | "public_repos": 62,
47 | "public_gists": 28,
48 | "followers": 4612,
49 | "following": 0,
50 | "created_at": "2011-11-17T17:05:03Z",
51 | "updated_at": "2016-04-03T09:51:44Z"
52 | }
53 | });
54 |
55 | var response:Response = new Response(options);
56 |
57 | connection.mockRespond(response);
58 | });
59 |
60 | service.getUserByUsername('johnpapa').subscribe((user) => {
61 | expect(user.name).toBe('John Papa');
62 | expect(user.login).toBe('johnpapa');
63 | expect(user.public_repos).toBe(62);
64 | expect(user.public_gists).toBe(28);
65 | expect(user.followers).toBe(4612);
66 | });
67 | }))
68 | );
69 |
70 | it('Should get a repo by username/repository',
71 | async(inject([XHRBackend, GithubService], (backend, service) => {
72 | backend.connections.subscribe(
73 | (connection:MockConnection) => {
74 | var options = new ResponseOptions({
75 | body: {
76 | "id": 11823977,
77 | "name": "videogular",
78 | "full_name": "videogular/videogular",
79 | "owner": {
80 | "login": "videogular",
81 | "id": 15369882,
82 | "avatar_url": "https://avatars.githubusercontent.com/u/15369882?v=3",
83 | "gravatar_id": "",
84 | "url": "https://api.github.com/users/videogular",
85 | "html_url": "https://github.com/videogular",
86 | "followers_url": "https://api.github.com/users/videogular/followers",
87 | "following_url": "https://api.github.com/users/videogular/following{/other_user}",
88 | "gists_url": "https://api.github.com/users/videogular/gists{/gist_id}",
89 | "starred_url": "https://api.github.com/users/videogular/starred{/owner}{/repo}",
90 | "subscriptions_url": "https://api.github.com/users/videogular/subscriptions",
91 | "organizations_url": "https://api.github.com/users/videogular/orgs",
92 | "repos_url": "https://api.github.com/users/videogular/repos",
93 | "events_url": "https://api.github.com/users/videogular/events{/privacy}",
94 | "received_events_url": "https://api.github.com/users/videogular/received_events",
95 | "type": "Organization",
96 | "site_admin": false
97 | },
98 | "private": false,
99 | "html_url": "https://github.com/videogular/videogular",
100 | "description": "The HTML5 video player for AngularJS",
101 | "fork": false,
102 | "url": "https://api.github.com/repos/videogular/videogular",
103 | "forks_url": "https://api.github.com/repos/videogular/videogular/forks",
104 | "keys_url": "https://api.github.com/repos/videogular/videogular/keys{/key_id}",
105 | "collaborators_url": "https://api.github.com/repos/videogular/videogular/collaborators{/collaborator}",
106 | "teams_url": "https://api.github.com/repos/videogular/videogular/teams",
107 | "hooks_url": "https://api.github.com/repos/videogular/videogular/hooks",
108 | "issue_events_url": "https://api.github.com/repos/videogular/videogular/issues/events{/number}",
109 | "events_url": "https://api.github.com/repos/videogular/videogular/events",
110 | "assignees_url": "https://api.github.com/repos/videogular/videogular/assignees{/user}",
111 | "branches_url": "https://api.github.com/repos/videogular/videogular/branches{/branch}",
112 | "tags_url": "https://api.github.com/repos/videogular/videogular/tags",
113 | "blobs_url": "https://api.github.com/repos/videogular/videogular/git/blobs{/sha}",
114 | "git_tags_url": "https://api.github.com/repos/videogular/videogular/git/tags{/sha}",
115 | "git_refs_url": "https://api.github.com/repos/videogular/videogular/git/refs{/sha}",
116 | "trees_url": "https://api.github.com/repos/videogular/videogular/git/trees{/sha}",
117 | "statuses_url": "https://api.github.com/repos/videogular/videogular/statuses/{sha}",
118 | "languages_url": "https://api.github.com/repos/videogular/videogular/languages",
119 | "stargazers_url": "https://api.github.com/repos/videogular/videogular/stargazers",
120 | "contributors_url": "https://api.github.com/repos/videogular/videogular/contributors",
121 | "subscribers_url": "https://api.github.com/repos/videogular/videogular/subscribers",
122 | "subscription_url": "https://api.github.com/repos/videogular/videogular/subscription",
123 | "commits_url": "https://api.github.com/repos/videogular/videogular/commits{/sha}",
124 | "git_commits_url": "https://api.github.com/repos/videogular/videogular/git/commits{/sha}",
125 | "comments_url": "https://api.github.com/repos/videogular/videogular/comments{/number}",
126 | "issue_comment_url": "https://api.github.com/repos/videogular/videogular/issues/comments{/number}",
127 | "contents_url": "https://api.github.com/repos/videogular/videogular/contents/{+path}",
128 | "compare_url": "https://api.github.com/repos/videogular/videogular/compare/{base}...{head}",
129 | "merges_url": "https://api.github.com/repos/videogular/videogular/merges",
130 | "archive_url": "https://api.github.com/repos/videogular/videogular/{archive_format}{/ref}",
131 | "downloads_url": "https://api.github.com/repos/videogular/videogular/downloads",
132 | "issues_url": "https://api.github.com/repos/videogular/videogular/issues{/number}",
133 | "pulls_url": "https://api.github.com/repos/videogular/videogular/pulls{/number}",
134 | "milestones_url": "https://api.github.com/repos/videogular/videogular/milestones{/number}",
135 | "notifications_url": "https://api.github.com/repos/videogular/videogular/notifications{?since,all,participating}",
136 | "labels_url": "https://api.github.com/repos/videogular/videogular/labels{/name}",
137 | "releases_url": "https://api.github.com/repos/videogular/videogular/releases{/id}",
138 | "deployments_url": "https://api.github.com/repos/videogular/videogular/deployments",
139 | "created_at": "2013-08-01T18:05:20Z",
140 | "updated_at": "2016-04-29T01:33:33Z",
141 | "pushed_at": "2016-04-21T23:45:13Z",
142 | "git_url": "git://github.com/videogular/videogular.git",
143 | "ssh_url": "git@github.com:videogular/videogular.git",
144 | "clone_url": "https://github.com/videogular/videogular.git",
145 | "svn_url": "https://github.com/videogular/videogular",
146 | "homepage": "http://www.videogular.com/demo",
147 | "size": 146733,
148 | "stargazers_count": 1166,
149 | "watchers_count": 1166,
150 | "language": "JavaScript",
151 | "has_issues": true,
152 | "has_downloads": true,
153 | "has_wiki": true,
154 | "has_pages": false,
155 | "forks_count": 286,
156 | "mirror_url": null,
157 | "open_issues_count": 20,
158 | "forks": 286,
159 | "open_issues": 20,
160 | "watchers": 1166,
161 | "default_branch": "master",
162 | "organization": {
163 | "login": "videogular",
164 | "id": 15369882,
165 | "avatar_url": "https://avatars.githubusercontent.com/u/15369882?v=3",
166 | "gravatar_id": "",
167 | "url": "https://api.github.com/users/videogular",
168 | "html_url": "https://github.com/videogular",
169 | "followers_url": "https://api.github.com/users/videogular/followers",
170 | "following_url": "https://api.github.com/users/videogular/following{/other_user}",
171 | "gists_url": "https://api.github.com/users/videogular/gists{/gist_id}",
172 | "starred_url": "https://api.github.com/users/videogular/starred{/owner}{/repo}",
173 | "subscriptions_url": "https://api.github.com/users/videogular/subscriptions",
174 | "organizations_url": "https://api.github.com/users/videogular/orgs",
175 | "repos_url": "https://api.github.com/users/videogular/repos",
176 | "events_url": "https://api.github.com/users/videogular/events{/privacy}",
177 | "received_events_url": "https://api.github.com/users/videogular/received_events",
178 | "type": "Organization",
179 | "site_admin": false
180 | },
181 | "network_count": 286,
182 | "subscribers_count": 78
183 | }
184 | });
185 |
186 | var response:Response = new Response(options);
187 |
188 | connection.mockRespond(response);
189 | });
190 |
191 | service.getRepository('videogular/videogular').subscribe((repo) => {
192 | expect(repo.name).toBe('videogular');
193 | expect(repo.full_name).toBe('videogular/videogular');
194 | expect(repo.network_count).toBe(286);
195 | expect(repo.subscribers_count).toBe(78);
196 | expect(repo.watchers).toBe(1166);
197 | });
198 | }))
199 | );
200 | });
201 |
--------------------------------------------------------------------------------
/src/services/gh-service.ts:
--------------------------------------------------------------------------------
1 | import {Http} from '@angular/http';
2 | import {Observable} from 'rxjs/Observable';
3 | import {Inject} from '@angular/core';
4 | import {GithubUser} from './gh-user';
5 |
6 | export class GithubService {
7 | static GITHUB_API:string = 'https://api.github.com';
8 | static GITHUB_PARAMS:string = '?client_id=aaa27ad06049163bd846&client_secret=d478f87d9bdf95efa72015b77b26c5b6a17127a0';
9 | http:Http;
10 | user:GithubUser;
11 |
12 | constructor(@Inject(Http) http:Http) {
13 | this.http = http;
14 | }
15 |
16 | getUserByUsername(username:string):Observable {
17 | let url:string = GithubService.GITHUB_API + '/users/' + username + GithubService.GITHUB_PARAMS;
18 |
19 | return this.http.get(url).map(response => response.json());
20 | }
21 |
22 | getRepository(repo:string):Observable {
23 | let url:string = GithubService.GITHUB_API + '/repos/' + repo + GithubService.GITHUB_PARAMS;
24 |
25 | return this.http.get(url).map(response => response.json());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/services/gh-user.ts:
--------------------------------------------------------------------------------
1 | export class GithubUser {
2 | login:string;
3 | id:number;
4 | avatar_url:string;
5 | gravatar_id:string;
6 | url:string;
7 | html_url:string;
8 | followers_url:string;
9 | following_url:string;
10 | gists_url:string;
11 | starred_url:string;
12 | subscriptions_url:string;
13 | organizations_url:string;
14 | repos_url:string;
15 | events_url:string;
16 | received_events_url:string;
17 | type:string;
18 | site_admin:boolean;
19 | name:string;
20 | company:string;
21 | blog:string;
22 | location:string;
23 | email:string;
24 | hireable:boolean;
25 | bio:string;
26 | public_repos:number;
27 | public_gists:number;
28 | followers:number;
29 | following:number;
30 | created_at:string;
31 | updated_at:string;
32 | }
33 |
--------------------------------------------------------------------------------
/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES5",
4 | "module": "commonjs",
5 | "moduleResolution": "node",
6 | "emitDecoratorMetadata": true,
7 | "experimentalDecorators": true,
8 | "sourceMap": true,
9 | "removeComments": true,
10 | "declaration": true,
11 | "outDir": "../dist"
12 | },
13 | "exclude": [
14 | "node_modules"
15 | ]
16 | }
17 |
--------------------------------------------------------------------------------
/system-config.js:
--------------------------------------------------------------------------------
1 | System.config({
2 | map: {
3 | 'rxjs': 'node_modules/rxjs',
4 | '@angular': 'node_modules/@angular',
5 | 'app': 'dist'
6 | },
7 | packages: {
8 | 'app': {
9 | main: 'bootstrap.js',
10 | defaultExtension: 'js'
11 | },
12 | '@angular/core': {
13 | main: 'index.js',
14 | defaultExtension: 'js'
15 | },
16 | '@angular/compiler': {
17 | main: 'index.js',
18 | defaultExtension: 'js'
19 | },
20 | '@angular/common': {
21 | main: 'index.js',
22 | defaultExtension: 'js'
23 | },
24 | '@angular/http': {
25 | main: 'index.js',
26 | defaultExtension: 'js'
27 | },
28 | '@angular/platform-browser': {
29 | main: 'index.js',
30 | defaultExtension: 'js'
31 | },
32 | '@angular/platform-browser-dynamic': {
33 | main: 'index.js',
34 | defaultExtension: 'js'
35 | },
36 | 'rxjs': {
37 | defaultExtension: 'js'
38 | }
39 | }
40 | });
41 |
--------------------------------------------------------------------------------
/typings.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ng-conf-testing",
3 | "version": false,
4 | "dependencies": {},
5 | "ambientDependencies": {
6 | "es6-shim": "registry:dt/es6-shim#0.31.2+20160317120654"
7 | },
8 | "ambientDevDependencies": {
9 | "jasmine": "registry:dt/jasmine#2.2.0+20160412134438"
10 | }
11 | }
12 |
--------------------------------------------------------------------------------