├── .gitignore
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── libs
│ ├── auto-common-1.0-20150122.001631-7.jar
│ ├── dagger-2.0-20150122.022617-13.jar
│ ├── dagger-compiler-2.0-20150122.022637-13-jar-with-dependencies.jar
│ └── dagger-producers-2.0-20150122.022620-1.jar
├── proguard-rules.pro
└── src
│ ├── androidTest
│ ├── assets
│ │ └── events_response.json
│ └── java
│ │ └── org
│ │ └── andydyer
│ │ └── androidtestdemo
│ │ ├── ApplicationTest.java
│ │ ├── LoginActivityTest.java
│ │ ├── MainActivityTest.java
│ │ ├── api
│ │ └── ApiServiceTest.java
│ │ └── test
│ │ ├── CustomMatchers.java
│ │ └── CustomViewActions.java
│ ├── debug
│ └── java
│ │ └── org
│ │ └── andydyer
│ │ └── androidtestdemo
│ │ ├── Graph.java
│ │ ├── InjectedActivityTest.java
│ │ ├── InjectedInstrumentationTest.java
│ │ └── api
│ │ ├── DebugApiServiceModule.java
│ │ └── MockApiService.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── org
│ │ │ └── andydyer
│ │ │ └── androidtestdemo
│ │ │ ├── DemoApplication.java
│ │ │ ├── api
│ │ │ ├── Actor.java
│ │ │ ├── ApiService.java
│ │ │ ├── ApiServiceModule.java
│ │ │ ├── AuthenticationService.java
│ │ │ ├── Event.java
│ │ │ ├── Events.java
│ │ │ └── Repo.java
│ │ │ └── ui
│ │ │ ├── LoginActivity.java
│ │ │ ├── MainActivity.java
│ │ │ ├── WebViewActivity.java
│ │ │ ├── fragments
│ │ │ └── EventListFragment.java
│ │ │ └── widgets
│ │ │ └── SimpleDividerItemDecoration.java
│ └── res
│ │ ├── drawable-hdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-mdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-xhdpi
│ │ └── ic_launcher.png
│ │ ├── drawable-xxhdpi
│ │ └── ic_launcher.png
│ │ ├── drawable
│ │ └── line_divider.xml
│ │ ├── layout
│ │ ├── activity_login.xml
│ │ ├── activity_main.xml
│ │ ├── activity_webview.xml
│ │ ├── event_list_item.xml
│ │ └── fragment_event_list.xml
│ │ ├── menu
│ │ └── main.xml
│ │ ├── values-w820dp
│ │ └── dimens.xml
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── release
│ └── java
│ └── org
│ └── andydyer
│ └── androidtestdemo
│ └── Graph.java
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── presentation
├── Big Android BBQ 2014 - Automated Testing for Modern Android Applications.md
└── images
│ ├── alexanderplatz.jpg
│ ├── berlin_sunset.jpg
│ ├── c1.png
│ ├── jon_snow.jpg
│ ├── music_library.png
│ ├── test_source_code.png
│ ├── wrist_presenter.png
│ └── ygritte.jpg
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | .gradle
2 | /local.properties
3 | /.idea/*
4 | .DS_Store
5 | /build
6 |
7 | *.iml
8 |
9 | presentation/*.pdf
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Andrew Dyer
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 | android-test-demo
2 | =================
3 |
4 | A sample application to demonstrate dependency injection and unit testing in a modern Android application.
5 |
6 | ## Presentation
7 | To keep the repo size down, the large PDF file of the presentation is not included. Instead, it is available [on Google Drive](http://goo.gl/ZiaX7p).
8 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | mavenCentral()
4 | }
5 |
6 | dependencies {
7 | // This plugin helps Android Studio find Dagger's generated classes
8 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
9 | }
10 | }
11 |
12 | apply plugin: 'com.android.application'
13 | apply plugin: 'com.neenbedankt.android-apt'
14 |
15 | android {
16 | compileSdkVersion 21
17 | buildToolsVersion "21.1.2"
18 |
19 | packagingOptions {
20 | exclude 'META-INF/services/javax.annotation.processing.Processor'
21 | exclude 'META-INF/LICENSE.txt'
22 | exclude 'LICENSE.txt'
23 | exclude 'LICENSE'
24 | }
25 |
26 | defaultConfig {
27 | applicationId "org.andydyer.androidtestdemo"
28 | minSdkVersion 15
29 | targetSdkVersion 21
30 | versionCode 2
31 | versionName "1.1"
32 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
33 | }
34 |
35 | buildTypes {
36 | release {
37 | minifyEnabled false
38 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
39 | }
40 | }
41 | }
42 |
43 | repositories {
44 | // Currently Dagger 2 is published to Sonatype's maven repo
45 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
46 | }
47 |
48 | dependencies {
49 | compile 'com.squareup.retrofit:retrofit:1.9.0'
50 | compile 'com.squareup.picasso:picasso:2.4.0'
51 | compile 'com.google.code.gson:gson:2.3.1'
52 | provided 'org.projectlombok:lombok:1.14.8'
53 | apt 'org.projectlombok:lombok:1.14.8'
54 | compile 'com.jakewharton:butterknife:6.0.0'
55 | compile 'com.android.support:appcompat-v7:21.0.+'
56 | compile 'com.android.support:recyclerview-v7:21.0.+'
57 |
58 | // Dagger 2 dependencies
59 | compile files('libs/dagger-2.0-20150122.022617-13.jar')
60 | apt files('libs/dagger-compiler-2.0-20150122.022637-13-jar-with-dependencies.jar')
61 | apt files('libs/dagger-producers-2.0-20150122.022620-1.jar')
62 | provided 'org.glassfish:javax.annotation:10.0-b28'
63 | compile 'javax.inject:javax.inject:1'
64 |
65 | debugCompile('org.mockito:mockito-core:1.10.19') {
66 | exclude group: 'org.hamcrest'
67 | }
68 | debugCompile('com.google.dexmaker:dexmaker:1.2') {
69 | exclude group: 'org.hamcrest'
70 | }
71 | debugCompile('com.google.dexmaker:dexmaker-mockito:1.2') {
72 | exclude group: 'org.hamcrest'
73 | }
74 | debugCompile 'commons-io:commons-io:2.4'
75 |
76 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.0') {
77 | exclude group: 'javax.inject'
78 | }
79 | androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
80 | androidTestCompile('com.android.support.test.espresso:espresso-contrib:2.0') {
81 | exclude group: 'javax.inject'
82 | exclude group: 'com.android.support'
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/libs/auto-common-1.0-20150122.001631-7.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/libs/auto-common-1.0-20150122.001631-7.jar
--------------------------------------------------------------------------------
/app/libs/dagger-2.0-20150122.022617-13.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/libs/dagger-2.0-20150122.022617-13.jar
--------------------------------------------------------------------------------
/app/libs/dagger-compiler-2.0-20150122.022637-13-jar-with-dependencies.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/libs/dagger-compiler-2.0-20150122.022637-13-jar-with-dependencies.jar
--------------------------------------------------------------------------------
/app/libs/dagger-producers-2.0-20150122.022620-1.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/libs/dagger-producers-2.0-20150122.022620-1.jar
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in /Applications/Android Studio.app/sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
--------------------------------------------------------------------------------
/app/src/androidTest/assets/events_response.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": "2263128997",
4 | "type": "ForkEvent",
5 | "actor": {
6 | "id": 1996715,
7 | "login": "karthikraj-duraisamy",
8 | "gravatar_id": "4ec3af837e4f0a0e0c3893d897dcda1e",
9 | "url": "https://api.github.com/users/karthikraj-duraisamy",
10 | "avatar_url": "https://avatars.githubusercontent.com/u/1996715?"
11 | },
12 | "repo": {
13 | "id": 18347476,
14 | "name": "google/iosched",
15 | "url": "https://api.github.com/repos/google/iosched"
16 | },
17 | "payload": {
18 | "forkee": {
19 | "id": 23520190,
20 | "name": "iosched",
21 | "full_name": "karthikraj-duraisamy/iosched",
22 | "owner": {
23 | "login": "karthikraj-duraisamy",
24 | "id": 1996715,
25 | "avatar_url": "https://avatars.githubusercontent.com/u/1996715?v=2",
26 | "gravatar_id": "4ec3af837e4f0a0e0c3893d897dcda1e",
27 | "url": "https://api.github.com/users/karthikraj-duraisamy",
28 | "html_url": "https://github.com/karthikraj-duraisamy",
29 | "followers_url": "https://api.github.com/users/karthikraj-duraisamy/followers",
30 | "following_url": "https://api.github.com/users/karthikraj-duraisamy/following{/other_user}",
31 | "gists_url": "https://api.github.com/users/karthikraj-duraisamy/gists{/gist_id}",
32 | "starred_url": "https://api.github.com/users/karthikraj-duraisamy/starred{/owner}{/repo}",
33 | "subscriptions_url": "https://api.github.com/users/karthikraj-duraisamy/subscriptions",
34 | "organizations_url": "https://api.github.com/users/karthikraj-duraisamy/orgs",
35 | "repos_url": "https://api.github.com/users/karthikraj-duraisamy/repos",
36 | "events_url": "https://api.github.com/users/karthikraj-duraisamy/events{/privacy}",
37 | "received_events_url": "https://api.github.com/users/karthikraj-duraisamy/received_events",
38 | "type": "User",
39 | "site_admin": false
40 | },
41 | "private": false,
42 | "html_url": "https://github.com/karthikraj-duraisamy/iosched",
43 | "description": null,
44 | "fork": true,
45 | "url": "https://api.github.com/repos/karthikraj-duraisamy/iosched",
46 | "forks_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/forks",
47 | "keys_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/keys{/key_id}",
48 | "collaborators_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/collaborators{/collaborator}",
49 | "teams_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/teams",
50 | "hooks_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/hooks",
51 | "issue_events_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/issues/events{/number}",
52 | "events_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/events",
53 | "assignees_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/assignees{/user}",
54 | "branches_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/branches{/branch}",
55 | "tags_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/tags",
56 | "blobs_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/blobs{/sha}",
57 | "git_tags_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/tags{/sha}",
58 | "git_refs_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/refs{/sha}",
59 | "trees_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/trees{/sha}",
60 | "statuses_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/statuses/{sha}",
61 | "languages_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/languages",
62 | "stargazers_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/stargazers",
63 | "contributors_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/contributors",
64 | "subscribers_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/subscribers",
65 | "subscription_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/subscription",
66 | "commits_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/commits{/sha}",
67 | "git_commits_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/git/commits{/sha}",
68 | "comments_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/comments{/number}",
69 | "issue_comment_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/issues/comments/{number}",
70 | "contents_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/contents/{+path}",
71 | "compare_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/compare/{base}...{head}",
72 | "merges_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/merges",
73 | "archive_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/{archive_format}{/ref}",
74 | "downloads_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/downloads",
75 | "issues_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/issues{/number}",
76 | "pulls_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/pulls{/number}",
77 | "milestones_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/milestones{/number}",
78 | "notifications_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/notifications{?since,all,participating}",
79 | "labels_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/labels{/name}",
80 | "releases_url": "https://api.github.com/repos/karthikraj-duraisamy/iosched/releases{/id}",
81 | "created_at": "2014-08-31T18:31:22Z",
82 | "updated_at": "2014-08-31T18:31:21Z",
83 | "pushed_at": "2014-08-19T18:34:02Z",
84 | "git_url": "git://github.com/karthikraj-duraisamy/iosched.git",
85 | "ssh_url": "git@github.com:karthikraj-duraisamy/iosched.git",
86 | "clone_url": "https://github.com/karthikraj-duraisamy/iosched.git",
87 | "svn_url": "https://github.com/karthikraj-duraisamy/iosched",
88 | "homepage": null,
89 | "size": 19325,
90 | "stargazers_count": 0,
91 | "watchers_count": 0,
92 | "language": null,
93 | "has_issues": false,
94 | "has_downloads": true,
95 | "has_wiki": false,
96 | "forks_count": 0,
97 | "mirror_url": null,
98 | "open_issues_count": 0,
99 | "forks": 0,
100 | "open_issues": 0,
101 | "watchers": 0,
102 | "default_branch": "master",
103 | "public": true
104 | }
105 | },
106 | "public": true,
107 | "created_at": "2014-08-31T18:31:22Z",
108 | "org": {
109 | "id": 1342004,
110 | "login": "google",
111 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
112 | "url": "https://api.github.com/orgs/google",
113 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
114 | }
115 | },
116 | {
117 | "id": "2263128978",
118 | "type": "WatchEvent",
119 | "actor": {
120 | "id": 1996715,
121 | "login": "karthikraj-duraisamy",
122 | "gravatar_id": "4ec3af837e4f0a0e0c3893d897dcda1e",
123 | "url": "https://api.github.com/users/karthikraj-duraisamy",
124 | "avatar_url": "https://avatars.githubusercontent.com/u/1996715?"
125 | },
126 | "repo": {
127 | "id": 18347476,
128 | "name": "google/iosched",
129 | "url": "https://api.github.com/repos/google/iosched"
130 | },
131 | "payload": {
132 | "action": "started"
133 | },
134 | "public": true,
135 | "created_at": "2014-08-31T18:31:21Z",
136 | "org": {
137 | "id": 1342004,
138 | "login": "google",
139 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
140 | "url": "https://api.github.com/orgs/google",
141 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
142 | }
143 | },
144 | {
145 | "id": "2263123699",
146 | "type": "IssuesEvent",
147 | "actor": {
148 | "id": 2864371,
149 | "login": "IonicaBizau",
150 | "gravatar_id": "84352b749bca68ff058e27fca784d2d5",
151 | "url": "https://api.github.com/users/IonicaBizau",
152 | "avatar_url": "https://avatars.githubusercontent.com/u/2864371?"
153 | },
154 | "repo": {
155 | "id": 8608480,
156 | "name": "google/google-api-nodejs-client",
157 | "url": "https://api.github.com/repos/google/google-api-nodejs-client"
158 | },
159 | "payload": {
160 | "action": "opened",
161 | "issue": {
162 | "url": "https://api.github.com/repos/google/google-api-nodejs-client/issues/272",
163 | "labels_url": "https://api.github.com/repos/google/google-api-nodejs-client/issues/272/labels{/name}",
164 | "comments_url": "https://api.github.com/repos/google/google-api-nodejs-client/issues/272/comments",
165 | "events_url": "https://api.github.com/repos/google/google-api-nodejs-client/issues/272/events",
166 | "html_url": "https://github.com/google/google-api-nodejs-client/issues/272",
167 | "id": 41592521,
168 | "number": 272,
169 | "title": "What text editor/editor plugin do you use for JSDoc comments?",
170 | "user": {
171 | "login": "IonicaBizau",
172 | "id": 2864371,
173 | "avatar_url": "https://avatars.githubusercontent.com/u/2864371?v=2",
174 | "gravatar_id": "84352b749bca68ff058e27fca784d2d5",
175 | "url": "https://api.github.com/users/IonicaBizau",
176 | "html_url": "https://github.com/IonicaBizau",
177 | "followers_url": "https://api.github.com/users/IonicaBizau/followers",
178 | "following_url": "https://api.github.com/users/IonicaBizau/following{/other_user}",
179 | "gists_url": "https://api.github.com/users/IonicaBizau/gists{/gist_id}",
180 | "starred_url": "https://api.github.com/users/IonicaBizau/starred{/owner}{/repo}",
181 | "subscriptions_url": "https://api.github.com/users/IonicaBizau/subscriptions",
182 | "organizations_url": "https://api.github.com/users/IonicaBizau/orgs",
183 | "repos_url": "https://api.github.com/users/IonicaBizau/repos",
184 | "events_url": "https://api.github.com/users/IonicaBizau/events{/privacy}",
185 | "received_events_url": "https://api.github.com/users/IonicaBizau/received_events",
186 | "type": "User",
187 | "site_admin": false
188 | },
189 | "labels": [
190 |
191 | ],
192 | "state": "open",
193 | "locked": false,
194 | "assignee": null,
195 | "milestone": null,
196 | "comments": 0,
197 | "created_at": "2014-08-31T18:21:07Z",
198 | "updated_at": "2014-08-31T18:21:07Z",
199 | "closed_at": null,
200 | "body": "I'm interested what tools you use for adding such comments:\r\n\r\n```js\r\n/**\r\n * Foo\r\n * @param {Object} fooParam Description\r\n * @constructor\r\n */\r\n...\r\n```"
201 | }
202 | },
203 | "public": true,
204 | "created_at": "2014-08-31T18:21:07Z",
205 | "org": {
206 | "id": 1342004,
207 | "login": "google",
208 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
209 | "url": "https://api.github.com/orgs/google",
210 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
211 | }
212 | },
213 | {
214 | "id": "2263120263",
215 | "type": "WatchEvent",
216 | "actor": {
217 | "id": 849044,
218 | "login": "cstrouse",
219 | "gravatar_id": "c1c3365169107669a2756f3ad35bd076",
220 | "url": "https://api.github.com/users/cstrouse",
221 | "avatar_url": "https://avatars.githubusercontent.com/u/849044?"
222 | },
223 | "repo": {
224 | "id": 12169108,
225 | "name": "google/closure-library",
226 | "url": "https://api.github.com/repos/google/closure-library"
227 | },
228 | "payload": {
229 | "action": "started"
230 | },
231 | "public": true,
232 | "created_at": "2014-08-31T18:14:39Z",
233 | "org": {
234 | "id": 1342004,
235 | "login": "google",
236 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
237 | "url": "https://api.github.com/orgs/google",
238 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
239 | }
240 | },
241 | {
242 | "id": "2263119429",
243 | "type": "WatchEvent",
244 | "actor": {
245 | "id": 2356870,
246 | "login": "DaleWebb",
247 | "gravatar_id": "9f82fd4acb53eff8a7506c60aae175b9",
248 | "url": "https://api.github.com/users/DaleWebb",
249 | "avatar_url": "https://avatars.githubusercontent.com/u/2356870?"
250 | },
251 | "repo": {
252 | "id": 18511024,
253 | "name": "google/web-starter-kit",
254 | "url": "https://api.github.com/repos/google/web-starter-kit"
255 | },
256 | "payload": {
257 | "action": "started"
258 | },
259 | "public": true,
260 | "created_at": "2014-08-31T18:13:04Z",
261 | "org": {
262 | "id": 1342004,
263 | "login": "google",
264 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
265 | "url": "https://api.github.com/orgs/google",
266 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
267 | }
268 | },
269 | {
270 | "id": "2263114107",
271 | "type": "WatchEvent",
272 | "actor": {
273 | "id": 1296901,
274 | "login": "scribeGriff",
275 | "gravatar_id": "be83de65aa6e5e52c762adc41220f495",
276 | "url": "https://api.github.com/users/scribeGriff",
277 | "avatar_url": "https://avatars.githubusercontent.com/u/1296901?"
278 | },
279 | "repo": {
280 | "id": 18511024,
281 | "name": "google/web-starter-kit",
282 | "url": "https://api.github.com/repos/google/web-starter-kit"
283 | },
284 | "payload": {
285 | "action": "started"
286 | },
287 | "public": true,
288 | "created_at": "2014-08-31T18:02:38Z",
289 | "org": {
290 | "id": 1342004,
291 | "login": "google",
292 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
293 | "url": "https://api.github.com/orgs/google",
294 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
295 | }
296 | },
297 | {
298 | "id": "2263107088",
299 | "type": "WatchEvent",
300 | "actor": {
301 | "id": 21374,
302 | "login": "eggcaker",
303 | "gravatar_id": "b9c6b9667c5b69a559f8a1686ac35366",
304 | "url": "https://api.github.com/users/eggcaker",
305 | "avatar_url": "https://avatars.githubusercontent.com/u/21374?"
306 | },
307 | "repo": {
308 | "id": 18511024,
309 | "name": "google/web-starter-kit",
310 | "url": "https://api.github.com/repos/google/web-starter-kit"
311 | },
312 | "payload": {
313 | "action": "started"
314 | },
315 | "public": true,
316 | "created_at": "2014-08-31T17:49:08Z",
317 | "org": {
318 | "id": 1342004,
319 | "login": "google",
320 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
321 | "url": "https://api.github.com/orgs/google",
322 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
323 | }
324 | },
325 | {
326 | "id": "2263098694",
327 | "type": "WatchEvent",
328 | "actor": {
329 | "id": 222763,
330 | "login": "nyxtom",
331 | "gravatar_id": "e2cd690765781180062c095b0e05fca0",
332 | "url": "https://api.github.com/users/nyxtom",
333 | "avatar_url": "https://avatars.githubusercontent.com/u/222763?"
334 | },
335 | "repo": {
336 | "id": 18511024,
337 | "name": "google/web-starter-kit",
338 | "url": "https://api.github.com/repos/google/web-starter-kit"
339 | },
340 | "payload": {
341 | "action": "started"
342 | },
343 | "public": true,
344 | "created_at": "2014-08-31T17:32:05Z",
345 | "org": {
346 | "id": 1342004,
347 | "login": "google",
348 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
349 | "url": "https://api.github.com/orgs/google",
350 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
351 | }
352 | },
353 | {
354 | "id": "2263095666",
355 | "type": "WatchEvent",
356 | "actor": {
357 | "id": 473894,
358 | "login": "sneakersgames",
359 | "gravatar_id": "e9f7df8901b29f4b5178b52f92319e5a",
360 | "url": "https://api.github.com/users/sneakersgames",
361 | "avatar_url": "https://avatars.githubusercontent.com/u/473894?"
362 | },
363 | "repo": {
364 | "id": 18511024,
365 | "name": "google/web-starter-kit",
366 | "url": "https://api.github.com/repos/google/web-starter-kit"
367 | },
368 | "payload": {
369 | "action": "started"
370 | },
371 | "public": true,
372 | "created_at": "2014-08-31T17:25:59Z",
373 | "org": {
374 | "id": 1342004,
375 | "login": "google",
376 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
377 | "url": "https://api.github.com/orgs/google",
378 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
379 | }
380 | },
381 | {
382 | "id": "2263094394",
383 | "type": "WatchEvent",
384 | "actor": {
385 | "id": 2965242,
386 | "login": "donlion",
387 | "gravatar_id": "92c84dd35ed762d97e4ca2857f402a71",
388 | "url": "https://api.github.com/users/donlion",
389 | "avatar_url": "https://avatars.githubusercontent.com/u/2965242?"
390 | },
391 | "repo": {
392 | "id": 18511024,
393 | "name": "google/web-starter-kit",
394 | "url": "https://api.github.com/repos/google/web-starter-kit"
395 | },
396 | "payload": {
397 | "action": "started"
398 | },
399 | "public": true,
400 | "created_at": "2014-08-31T17:23:22Z",
401 | "org": {
402 | "id": 1342004,
403 | "login": "google",
404 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
405 | "url": "https://api.github.com/orgs/google",
406 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
407 | }
408 | },
409 | {
410 | "id": "2263081155",
411 | "type": "WatchEvent",
412 | "actor": {
413 | "id": 8014396,
414 | "login": "BrooksPatton",
415 | "gravatar_id": "df5dbd7543098d0249dee0b2a1fd3344",
416 | "url": "https://api.github.com/users/BrooksPatton",
417 | "avatar_url": "https://avatars.githubusercontent.com/u/8014396?"
418 | },
419 | "repo": {
420 | "id": 18511024,
421 | "name": "google/web-starter-kit",
422 | "url": "https://api.github.com/repos/google/web-starter-kit"
423 | },
424 | "payload": {
425 | "action": "started"
426 | },
427 | "public": true,
428 | "created_at": "2014-08-31T16:57:56Z",
429 | "org": {
430 | "id": 1342004,
431 | "login": "google",
432 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
433 | "url": "https://api.github.com/orgs/google",
434 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
435 | }
436 | },
437 | {
438 | "id": "2263080173",
439 | "type": "WatchEvent",
440 | "actor": {
441 | "id": 6818190,
442 | "login": "RobinDeMug",
443 | "gravatar_id": "03149949a9044d2ddb2ba6639b0cd557",
444 | "url": "https://api.github.com/users/RobinDeMug",
445 | "avatar_url": "https://avatars.githubusercontent.com/u/6818190?"
446 | },
447 | "repo": {
448 | "id": 12166778,
449 | "name": "google/google-api-php-client",
450 | "url": "https://api.github.com/repos/google/google-api-php-client"
451 | },
452 | "payload": {
453 | "action": "started"
454 | },
455 | "public": true,
456 | "created_at": "2014-08-31T16:56:02Z",
457 | "org": {
458 | "id": 1342004,
459 | "login": "google",
460 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
461 | "url": "https://api.github.com/orgs/google",
462 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
463 | }
464 | },
465 | {
466 | "id": "2263080006",
467 | "type": "WatchEvent",
468 | "actor": {
469 | "id": 2363636,
470 | "login": "mattgstevens",
471 | "gravatar_id": "0333a253d02cb7b56fd26c6b19f00bfa",
472 | "url": "https://api.github.com/users/mattgstevens",
473 | "avatar_url": "https://avatars.githubusercontent.com/u/2363636?"
474 | },
475 | "repo": {
476 | "id": 18511024,
477 | "name": "google/web-starter-kit",
478 | "url": "https://api.github.com/repos/google/web-starter-kit"
479 | },
480 | "payload": {
481 | "action": "started"
482 | },
483 | "public": true,
484 | "created_at": "2014-08-31T16:55:42Z",
485 | "org": {
486 | "id": 1342004,
487 | "login": "google",
488 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
489 | "url": "https://api.github.com/orgs/google",
490 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
491 | }
492 | },
493 | {
494 | "id": "2263075474",
495 | "type": "ForkEvent",
496 | "actor": {
497 | "id": 2546,
498 | "login": "skalnik",
499 | "gravatar_id": "9eabab0dc683ac153d5b5a44df59424d",
500 | "url": "https://api.github.com/users/skalnik",
501 | "avatar_url": "https://avatars.githubusercontent.com/u/2546?"
502 | },
503 | "repo": {
504 | "id": 23474151,
505 | "name": "google/recki-ct",
506 | "url": "https://api.github.com/repos/google/recki-ct"
507 | },
508 | "payload": {
509 | "forkee": {
510 | "id": 23518244,
511 | "name": "recki-ct",
512 | "full_name": "skalnik/recki-ct",
513 | "owner": {
514 | "login": "skalnik",
515 | "id": 2546,
516 | "avatar_url": "https://avatars.githubusercontent.com/u/2546?v=2",
517 | "gravatar_id": "9eabab0dc683ac153d5b5a44df59424d",
518 | "url": "https://api.github.com/users/skalnik",
519 | "html_url": "https://github.com/skalnik",
520 | "followers_url": "https://api.github.com/users/skalnik/followers",
521 | "following_url": "https://api.github.com/users/skalnik/following{/other_user}",
522 | "gists_url": "https://api.github.com/users/skalnik/gists{/gist_id}",
523 | "starred_url": "https://api.github.com/users/skalnik/starred{/owner}{/repo}",
524 | "subscriptions_url": "https://api.github.com/users/skalnik/subscriptions",
525 | "organizations_url": "https://api.github.com/users/skalnik/orgs",
526 | "repos_url": "https://api.github.com/users/skalnik/repos",
527 | "events_url": "https://api.github.com/users/skalnik/events{/privacy}",
528 | "received_events_url": "https://api.github.com/users/skalnik/received_events",
529 | "type": "User",
530 | "site_admin": true
531 | },
532 | "private": false,
533 | "html_url": "https://github.com/skalnik/recki-ct",
534 | "description": null,
535 | "fork": true,
536 | "url": "https://api.github.com/repos/skalnik/recki-ct",
537 | "forks_url": "https://api.github.com/repos/skalnik/recki-ct/forks",
538 | "keys_url": "https://api.github.com/repos/skalnik/recki-ct/keys{/key_id}",
539 | "collaborators_url": "https://api.github.com/repos/skalnik/recki-ct/collaborators{/collaborator}",
540 | "teams_url": "https://api.github.com/repos/skalnik/recki-ct/teams",
541 | "hooks_url": "https://api.github.com/repos/skalnik/recki-ct/hooks",
542 | "issue_events_url": "https://api.github.com/repos/skalnik/recki-ct/issues/events{/number}",
543 | "events_url": "https://api.github.com/repos/skalnik/recki-ct/events",
544 | "assignees_url": "https://api.github.com/repos/skalnik/recki-ct/assignees{/user}",
545 | "branches_url": "https://api.github.com/repos/skalnik/recki-ct/branches{/branch}",
546 | "tags_url": "https://api.github.com/repos/skalnik/recki-ct/tags",
547 | "blobs_url": "https://api.github.com/repos/skalnik/recki-ct/git/blobs{/sha}",
548 | "git_tags_url": "https://api.github.com/repos/skalnik/recki-ct/git/tags{/sha}",
549 | "git_refs_url": "https://api.github.com/repos/skalnik/recki-ct/git/refs{/sha}",
550 | "trees_url": "https://api.github.com/repos/skalnik/recki-ct/git/trees{/sha}",
551 | "statuses_url": "https://api.github.com/repos/skalnik/recki-ct/statuses/{sha}",
552 | "languages_url": "https://api.github.com/repos/skalnik/recki-ct/languages",
553 | "stargazers_url": "https://api.github.com/repos/skalnik/recki-ct/stargazers",
554 | "contributors_url": "https://api.github.com/repos/skalnik/recki-ct/contributors",
555 | "subscribers_url": "https://api.github.com/repos/skalnik/recki-ct/subscribers",
556 | "subscription_url": "https://api.github.com/repos/skalnik/recki-ct/subscription",
557 | "commits_url": "https://api.github.com/repos/skalnik/recki-ct/commits{/sha}",
558 | "git_commits_url": "https://api.github.com/repos/skalnik/recki-ct/git/commits{/sha}",
559 | "comments_url": "https://api.github.com/repos/skalnik/recki-ct/comments{/number}",
560 | "issue_comment_url": "https://api.github.com/repos/skalnik/recki-ct/issues/comments/{number}",
561 | "contents_url": "https://api.github.com/repos/skalnik/recki-ct/contents/{+path}",
562 | "compare_url": "https://api.github.com/repos/skalnik/recki-ct/compare/{base}...{head}",
563 | "merges_url": "https://api.github.com/repos/skalnik/recki-ct/merges",
564 | "archive_url": "https://api.github.com/repos/skalnik/recki-ct/{archive_format}{/ref}",
565 | "downloads_url": "https://api.github.com/repos/skalnik/recki-ct/downloads",
566 | "issues_url": "https://api.github.com/repos/skalnik/recki-ct/issues{/number}",
567 | "pulls_url": "https://api.github.com/repos/skalnik/recki-ct/pulls{/number}",
568 | "milestones_url": "https://api.github.com/repos/skalnik/recki-ct/milestones{/number}",
569 | "notifications_url": "https://api.github.com/repos/skalnik/recki-ct/notifications{?since,all,participating}",
570 | "labels_url": "https://api.github.com/repos/skalnik/recki-ct/labels{/name}",
571 | "releases_url": "https://api.github.com/repos/skalnik/recki-ct/releases{/id}",
572 | "created_at": "2014-08-31T16:47:14Z",
573 | "updated_at": "2014-08-31T16:16:43Z",
574 | "pushed_at": "2014-08-29T21:16:46Z",
575 | "git_url": "git://github.com/skalnik/recki-ct.git",
576 | "ssh_url": "git@github.com:skalnik/recki-ct.git",
577 | "clone_url": "https://github.com/skalnik/recki-ct.git",
578 | "svn_url": "https://github.com/skalnik/recki-ct",
579 | "homepage": null,
580 | "size": 0,
581 | "stargazers_count": 0,
582 | "watchers_count": 0,
583 | "language": null,
584 | "has_issues": false,
585 | "has_downloads": false,
586 | "has_wiki": false,
587 | "forks_count": 0,
588 | "mirror_url": null,
589 | "open_issues_count": 0,
590 | "forks": 0,
591 | "open_issues": 0,
592 | "watchers": 0,
593 | "default_branch": "master",
594 | "public": true
595 | }
596 | },
597 | "public": true,
598 | "created_at": "2014-08-31T16:47:14Z",
599 | "org": {
600 | "id": 1342004,
601 | "login": "google",
602 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
603 | "url": "https://api.github.com/orgs/google",
604 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
605 | }
606 | },
607 | {
608 | "id": "2263068042",
609 | "type": "WatchEvent",
610 | "actor": {
611 | "id": 919444,
612 | "login": "Valloric",
613 | "gravatar_id": "48f306c887fd9c7b32a2fc352194e0db",
614 | "url": "https://api.github.com/users/Valloric",
615 | "avatar_url": "https://avatars.githubusercontent.com/u/919444?"
616 | },
617 | "repo": {
618 | "id": 18511024,
619 | "name": "google/web-starter-kit",
620 | "url": "https://api.github.com/repos/google/web-starter-kit"
621 | },
622 | "payload": {
623 | "action": "started"
624 | },
625 | "public": true,
626 | "created_at": "2014-08-31T16:32:13Z",
627 | "org": {
628 | "id": 1342004,
629 | "login": "google",
630 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
631 | "url": "https://api.github.com/orgs/google",
632 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
633 | }
634 | },
635 | {
636 | "id": "2263063121",
637 | "type": "IssuesEvent",
638 | "actor": {
639 | "id": 1727302,
640 | "login": "pflannery",
641 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1",
642 | "url": "https://api.github.com/users/pflannery",
643 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?"
644 | },
645 | "repo": {
646 | "id": 9060347,
647 | "name": "google/traceur-compiler",
648 | "url": "https://api.github.com/repos/google/traceur-compiler"
649 | },
650 | "payload": {
651 | "action": "closed",
652 | "issue": {
653 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/1300",
654 | "labels_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/labels{/name}",
655 | "comments_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/comments",
656 | "events_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/events",
657 | "html_url": "https://github.com/google/traceur-compiler/issues/1300",
658 | "id": 41589319,
659 | "number": 1300,
660 | "title": "module myModule as './path/to/module'; outputs \"Semi-colon expected\" error",
661 | "user": {
662 | "login": "pflannery",
663 | "id": 1727302,
664 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2",
665 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1",
666 | "url": "https://api.github.com/users/pflannery",
667 | "html_url": "https://github.com/pflannery",
668 | "followers_url": "https://api.github.com/users/pflannery/followers",
669 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}",
670 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}",
671 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}",
672 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions",
673 | "organizations_url": "https://api.github.com/users/pflannery/orgs",
674 | "repos_url": "https://api.github.com/users/pflannery/repos",
675 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}",
676 | "received_events_url": "https://api.github.com/users/pflannery/received_events",
677 | "type": "User",
678 | "site_admin": false
679 | },
680 | "labels": [
681 |
682 | ],
683 | "state": "closed",
684 | "locked": false,
685 | "assignee": null,
686 | "milestone": null,
687 | "comments": 2,
688 | "created_at": "2014-08-31T15:49:04Z",
689 | "updated_at": "2014-08-31T16:22:51Z",
690 | "closed_at": "2014-08-31T16:22:51Z",
691 | "body": "I've been trying to bring in a module as a single object using \r\n```js\r\nmodule myModule as './path/to/module';\r\n```\r\nbut I get the following error ```Semi-colon expected```\r\n\r\nIs this because it's not implemented yet or am I doing something wrong?\r\n\r\nmy current work around is to replace the line with \r\n```js\r\nvar myModule = System.get(\"relative/path/to/module\");\r\n```"
692 | }
693 | },
694 | "public": true,
695 | "created_at": "2014-08-31T16:22:51Z",
696 | "org": {
697 | "id": 1342004,
698 | "login": "google",
699 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
700 | "url": "https://api.github.com/orgs/google",
701 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
702 | }
703 | },
704 | {
705 | "id": "2263063120",
706 | "type": "IssueCommentEvent",
707 | "actor": {
708 | "id": 1727302,
709 | "login": "pflannery",
710 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1",
711 | "url": "https://api.github.com/users/pflannery",
712 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?"
713 | },
714 | "repo": {
715 | "id": 9060347,
716 | "name": "google/traceur-compiler",
717 | "url": "https://api.github.com/repos/google/traceur-compiler"
718 | },
719 | "payload": {
720 | "action": "created",
721 | "issue": {
722 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/1300",
723 | "labels_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/labels{/name}",
724 | "comments_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/comments",
725 | "events_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/events",
726 | "html_url": "https://github.com/google/traceur-compiler/issues/1300",
727 | "id": 41589319,
728 | "number": 1300,
729 | "title": "module myModule as './path/to/module'; outputs \"Semi-colon expected\" error",
730 | "user": {
731 | "login": "pflannery",
732 | "id": 1727302,
733 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2",
734 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1",
735 | "url": "https://api.github.com/users/pflannery",
736 | "html_url": "https://github.com/pflannery",
737 | "followers_url": "https://api.github.com/users/pflannery/followers",
738 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}",
739 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}",
740 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}",
741 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions",
742 | "organizations_url": "https://api.github.com/users/pflannery/orgs",
743 | "repos_url": "https://api.github.com/users/pflannery/repos",
744 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}",
745 | "received_events_url": "https://api.github.com/users/pflannery/received_events",
746 | "type": "User",
747 | "site_admin": false
748 | },
749 | "labels": [
750 |
751 | ],
752 | "state": "closed",
753 | "locked": false,
754 | "assignee": null,
755 | "milestone": null,
756 | "comments": 2,
757 | "created_at": "2014-08-31T15:49:04Z",
758 | "updated_at": "2014-08-31T16:22:51Z",
759 | "closed_at": "2014-08-31T16:22:51Z",
760 | "body": "I've been trying to bring in a module as a single object using \r\n```js\r\nmodule myModule as './path/to/module';\r\n```\r\nbut I get the following error ```Semi-colon expected```\r\n\r\nIs this because it's not implemented yet or am I doing something wrong?\r\n\r\nmy current work around is to replace the line with \r\n```js\r\nvar myModule = System.get(\"relative/path/to/module\");\r\n```"
761 | },
762 | "comment": {
763 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/comments/53992985",
764 | "html_url": "https://github.com/google/traceur-compiler/issues/1300#issuecomment-53992985",
765 | "issue_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300",
766 | "id": 53992985,
767 | "user": {
768 | "login": "pflannery",
769 | "id": 1727302,
770 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2",
771 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1",
772 | "url": "https://api.github.com/users/pflannery",
773 | "html_url": "https://github.com/pflannery",
774 | "followers_url": "https://api.github.com/users/pflannery/followers",
775 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}",
776 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}",
777 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}",
778 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions",
779 | "organizations_url": "https://api.github.com/users/pflannery/orgs",
780 | "repos_url": "https://api.github.com/users/pflannery/repos",
781 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}",
782 | "received_events_url": "https://api.github.com/users/pflannery/received_events",
783 | "type": "User",
784 | "site_admin": false
785 | },
786 | "created_at": "2014-08-31T16:22:51Z",
787 | "updated_at": "2014-08-31T16:22:51Z",
788 | "body": "many places refer to the older syntax.\r\n\r\nbut great that works thanks."
789 | }
790 | },
791 | "public": true,
792 | "created_at": "2014-08-31T16:22:51Z",
793 | "org": {
794 | "id": 1342004,
795 | "login": "google",
796 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
797 | "url": "https://api.github.com/orgs/google",
798 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
799 | }
800 | },
801 | {
802 | "id": "2263059840",
803 | "type": "WatchEvent",
804 | "actor": {
805 | "id": 530406,
806 | "login": "liuggio",
807 | "gravatar_id": "662a52953053d94a145a00df9da9f0d2",
808 | "url": "https://api.github.com/users/liuggio",
809 | "avatar_url": "https://avatars.githubusercontent.com/u/530406?"
810 | },
811 | "repo": {
812 | "id": 23474151,
813 | "name": "google/recki-ct",
814 | "url": "https://api.github.com/repos/google/recki-ct"
815 | },
816 | "payload": {
817 | "action": "started"
818 | },
819 | "public": true,
820 | "created_at": "2014-08-31T16:16:44Z",
821 | "org": {
822 | "id": 1342004,
823 | "login": "google",
824 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
825 | "url": "https://api.github.com/orgs/google",
826 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
827 | }
828 | },
829 | {
830 | "id": "2263054492",
831 | "type": "WatchEvent",
832 | "actor": {
833 | "id": 7867821,
834 | "login": "mykhaelpierce",
835 | "gravatar_id": "d3ca7f5a26249dc2b0e80bf4bf5fe14b",
836 | "url": "https://api.github.com/users/mykhaelpierce",
837 | "avatar_url": "https://avatars.githubusercontent.com/u/7867821?"
838 | },
839 | "repo": {
840 | "id": 18511024,
841 | "name": "google/web-starter-kit",
842 | "url": "https://api.github.com/repos/google/web-starter-kit"
843 | },
844 | "payload": {
845 | "action": "started"
846 | },
847 | "public": true,
848 | "created_at": "2014-08-31T16:06:36Z",
849 | "org": {
850 | "id": 1342004,
851 | "login": "google",
852 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
853 | "url": "https://api.github.com/orgs/google",
854 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
855 | }
856 | },
857 | {
858 | "id": "2263054490",
859 | "type": "PullRequestEvent",
860 | "actor": {
861 | "id": 1078123,
862 | "login": "plnice",
863 | "gravatar_id": "5840be9b646085a32e785c3ea07125ef",
864 | "url": "https://api.github.com/users/plnice",
865 | "avatar_url": "https://avatars.githubusercontent.com/u/1078123?"
866 | },
867 | "repo": {
868 | "id": 18347476,
869 | "name": "google/iosched",
870 | "url": "https://api.github.com/repos/google/iosched"
871 | },
872 | "payload": {
873 | "action": "opened",
874 | "number": 47,
875 | "pull_request": {
876 | "url": "https://api.github.com/repos/google/iosched/pulls/47",
877 | "id": 20546887,
878 | "html_url": "https://github.com/google/iosched/pull/47",
879 | "diff_url": "https://github.com/google/iosched/pull/47.diff",
880 | "patch_url": "https://github.com/google/iosched/pull/47.patch",
881 | "issue_url": "https://api.github.com/repos/google/iosched/issues/47",
882 | "number": 47,
883 | "state": "open",
884 | "locked": false,
885 | "title": "Wearable: fixes for feedback notification.",
886 | "user": {
887 | "login": "plnice",
888 | "id": 1078123,
889 | "avatar_url": "https://avatars.githubusercontent.com/u/1078123?v=2",
890 | "gravatar_id": "5840be9b646085a32e785c3ea07125ef",
891 | "url": "https://api.github.com/users/plnice",
892 | "html_url": "https://github.com/plnice",
893 | "followers_url": "https://api.github.com/users/plnice/followers",
894 | "following_url": "https://api.github.com/users/plnice/following{/other_user}",
895 | "gists_url": "https://api.github.com/users/plnice/gists{/gist_id}",
896 | "starred_url": "https://api.github.com/users/plnice/starred{/owner}{/repo}",
897 | "subscriptions_url": "https://api.github.com/users/plnice/subscriptions",
898 | "organizations_url": "https://api.github.com/users/plnice/orgs",
899 | "repos_url": "https://api.github.com/users/plnice/repos",
900 | "events_url": "https://api.github.com/users/plnice/events{/privacy}",
901 | "received_events_url": "https://api.github.com/users/plnice/received_events",
902 | "type": "User",
903 | "site_admin": false
904 | },
905 | "body": "* Moved \"Rate this session\" action from notification Activity's onClick to the notification Action - I think this is more intuitive and in line with Wear platform; additionally shows how to add actions to the Wear notification.\r\n* Set custom size preset for the feedback notification (SIZE_LARGE). Before that, in case of session title in two lines, room number and speaker name were not visible.",
906 | "created_at": "2014-08-31T16:06:33Z",
907 | "updated_at": "2014-08-31T16:06:33Z",
908 | "closed_at": null,
909 | "merged_at": null,
910 | "merge_commit_sha": "4986639dff646add7014345dbb68b0861003fe23",
911 | "assignee": null,
912 | "milestone": null,
913 | "commits_url": "https://api.github.com/repos/google/iosched/pulls/47/commits",
914 | "review_comments_url": "https://api.github.com/repos/google/iosched/pulls/47/comments",
915 | "review_comment_url": "https://api.github.com/repos/google/iosched/pulls/comments/{number}",
916 | "comments_url": "https://api.github.com/repos/google/iosched/issues/47/comments",
917 | "statuses_url": "https://api.github.com/repos/google/iosched/statuses/ea244ed4d2664370a9975b4008eb7b3071a8bf29",
918 | "head": {
919 | "label": "plnice:gdg-wear-improv",
920 | "ref": "gdg-wear-improv",
921 | "sha": "ea244ed4d2664370a9975b4008eb7b3071a8bf29",
922 | "user": {
923 | "login": "plnice",
924 | "id": 1078123,
925 | "avatar_url": "https://avatars.githubusercontent.com/u/1078123?v=2",
926 | "gravatar_id": "5840be9b646085a32e785c3ea07125ef",
927 | "url": "https://api.github.com/users/plnice",
928 | "html_url": "https://github.com/plnice",
929 | "followers_url": "https://api.github.com/users/plnice/followers",
930 | "following_url": "https://api.github.com/users/plnice/following{/other_user}",
931 | "gists_url": "https://api.github.com/users/plnice/gists{/gist_id}",
932 | "starred_url": "https://api.github.com/users/plnice/starred{/owner}{/repo}",
933 | "subscriptions_url": "https://api.github.com/users/plnice/subscriptions",
934 | "organizations_url": "https://api.github.com/users/plnice/orgs",
935 | "repos_url": "https://api.github.com/users/plnice/repos",
936 | "events_url": "https://api.github.com/users/plnice/events{/privacy}",
937 | "received_events_url": "https://api.github.com/users/plnice/received_events",
938 | "type": "User",
939 | "site_admin": false
940 | },
941 | "repo": {
942 | "id": 22748955,
943 | "name": "iosched",
944 | "full_name": "plnice/iosched",
945 | "owner": {
946 | "login": "plnice",
947 | "id": 1078123,
948 | "avatar_url": "https://avatars.githubusercontent.com/u/1078123?v=2",
949 | "gravatar_id": "5840be9b646085a32e785c3ea07125ef",
950 | "url": "https://api.github.com/users/plnice",
951 | "html_url": "https://github.com/plnice",
952 | "followers_url": "https://api.github.com/users/plnice/followers",
953 | "following_url": "https://api.github.com/users/plnice/following{/other_user}",
954 | "gists_url": "https://api.github.com/users/plnice/gists{/gist_id}",
955 | "starred_url": "https://api.github.com/users/plnice/starred{/owner}{/repo}",
956 | "subscriptions_url": "https://api.github.com/users/plnice/subscriptions",
957 | "organizations_url": "https://api.github.com/users/plnice/orgs",
958 | "repos_url": "https://api.github.com/users/plnice/repos",
959 | "events_url": "https://api.github.com/users/plnice/events{/privacy}",
960 | "received_events_url": "https://api.github.com/users/plnice/received_events",
961 | "type": "User",
962 | "site_admin": false
963 | },
964 | "private": false,
965 | "html_url": "https://github.com/plnice/iosched",
966 | "description": null,
967 | "fork": true,
968 | "url": "https://api.github.com/repos/plnice/iosched",
969 | "forks_url": "https://api.github.com/repos/plnice/iosched/forks",
970 | "keys_url": "https://api.github.com/repos/plnice/iosched/keys{/key_id}",
971 | "collaborators_url": "https://api.github.com/repos/plnice/iosched/collaborators{/collaborator}",
972 | "teams_url": "https://api.github.com/repos/plnice/iosched/teams",
973 | "hooks_url": "https://api.github.com/repos/plnice/iosched/hooks",
974 | "issue_events_url": "https://api.github.com/repos/plnice/iosched/issues/events{/number}",
975 | "events_url": "https://api.github.com/repos/plnice/iosched/events",
976 | "assignees_url": "https://api.github.com/repos/plnice/iosched/assignees{/user}",
977 | "branches_url": "https://api.github.com/repos/plnice/iosched/branches{/branch}",
978 | "tags_url": "https://api.github.com/repos/plnice/iosched/tags",
979 | "blobs_url": "https://api.github.com/repos/plnice/iosched/git/blobs{/sha}",
980 | "git_tags_url": "https://api.github.com/repos/plnice/iosched/git/tags{/sha}",
981 | "git_refs_url": "https://api.github.com/repos/plnice/iosched/git/refs{/sha}",
982 | "trees_url": "https://api.github.com/repos/plnice/iosched/git/trees{/sha}",
983 | "statuses_url": "https://api.github.com/repos/plnice/iosched/statuses/{sha}",
984 | "languages_url": "https://api.github.com/repos/plnice/iosched/languages",
985 | "stargazers_url": "https://api.github.com/repos/plnice/iosched/stargazers",
986 | "contributors_url": "https://api.github.com/repos/plnice/iosched/contributors",
987 | "subscribers_url": "https://api.github.com/repos/plnice/iosched/subscribers",
988 | "subscription_url": "https://api.github.com/repos/plnice/iosched/subscription",
989 | "commits_url": "https://api.github.com/repos/plnice/iosched/commits{/sha}",
990 | "git_commits_url": "https://api.github.com/repos/plnice/iosched/git/commits{/sha}",
991 | "comments_url": "https://api.github.com/repos/plnice/iosched/comments{/number}",
992 | "issue_comment_url": "https://api.github.com/repos/plnice/iosched/issues/comments/{number}",
993 | "contents_url": "https://api.github.com/repos/plnice/iosched/contents/{+path}",
994 | "compare_url": "https://api.github.com/repos/plnice/iosched/compare/{base}...{head}",
995 | "merges_url": "https://api.github.com/repos/plnice/iosched/merges",
996 | "archive_url": "https://api.github.com/repos/plnice/iosched/{archive_format}{/ref}",
997 | "downloads_url": "https://api.github.com/repos/plnice/iosched/downloads",
998 | "issues_url": "https://api.github.com/repos/plnice/iosched/issues{/number}",
999 | "pulls_url": "https://api.github.com/repos/plnice/iosched/pulls{/number}",
1000 | "milestones_url": "https://api.github.com/repos/plnice/iosched/milestones{/number}",
1001 | "notifications_url": "https://api.github.com/repos/plnice/iosched/notifications{?since,all,participating}",
1002 | "labels_url": "https://api.github.com/repos/plnice/iosched/labels{/name}",
1003 | "releases_url": "https://api.github.com/repos/plnice/iosched/releases{/id}",
1004 | "created_at": "2014-08-08T07:01:19Z",
1005 | "updated_at": "2014-08-23T14:50:29Z",
1006 | "pushed_at": "2014-08-24T13:37:03Z",
1007 | "git_url": "git://github.com/plnice/iosched.git",
1008 | "ssh_url": "git@github.com:plnice/iosched.git",
1009 | "clone_url": "https://github.com/plnice/iosched.git",
1010 | "svn_url": "https://github.com/plnice/iosched",
1011 | "homepage": null,
1012 | "size": 18439,
1013 | "stargazers_count": 0,
1014 | "watchers_count": 0,
1015 | "language": "Java",
1016 | "has_issues": false,
1017 | "has_downloads": true,
1018 | "has_wiki": false,
1019 | "forks_count": 0,
1020 | "mirror_url": null,
1021 | "open_issues_count": 0,
1022 | "forks": 0,
1023 | "open_issues": 0,
1024 | "watchers": 0,
1025 | "default_branch": "master"
1026 | }
1027 | },
1028 | "base": {
1029 | "label": "google:master",
1030 | "ref": "master",
1031 | "sha": "73dd4d3fbd80a336abb594e29b152dfc4134b239",
1032 | "user": {
1033 | "login": "google",
1034 | "id": 1342004,
1035 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?v=2",
1036 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1037 | "url": "https://api.github.com/users/google",
1038 | "html_url": "https://github.com/google",
1039 | "followers_url": "https://api.github.com/users/google/followers",
1040 | "following_url": "https://api.github.com/users/google/following{/other_user}",
1041 | "gists_url": "https://api.github.com/users/google/gists{/gist_id}",
1042 | "starred_url": "https://api.github.com/users/google/starred{/owner}{/repo}",
1043 | "subscriptions_url": "https://api.github.com/users/google/subscriptions",
1044 | "organizations_url": "https://api.github.com/users/google/orgs",
1045 | "repos_url": "https://api.github.com/users/google/repos",
1046 | "events_url": "https://api.github.com/users/google/events{/privacy}",
1047 | "received_events_url": "https://api.github.com/users/google/received_events",
1048 | "type": "Organization",
1049 | "site_admin": false
1050 | },
1051 | "repo": {
1052 | "id": 18347476,
1053 | "name": "iosched",
1054 | "full_name": "google/iosched",
1055 | "owner": {
1056 | "login": "google",
1057 | "id": 1342004,
1058 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?v=2",
1059 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1060 | "url": "https://api.github.com/users/google",
1061 | "html_url": "https://github.com/google",
1062 | "followers_url": "https://api.github.com/users/google/followers",
1063 | "following_url": "https://api.github.com/users/google/following{/other_user}",
1064 | "gists_url": "https://api.github.com/users/google/gists{/gist_id}",
1065 | "starred_url": "https://api.github.com/users/google/starred{/owner}{/repo}",
1066 | "subscriptions_url": "https://api.github.com/users/google/subscriptions",
1067 | "organizations_url": "https://api.github.com/users/google/orgs",
1068 | "repos_url": "https://api.github.com/users/google/repos",
1069 | "events_url": "https://api.github.com/users/google/events{/privacy}",
1070 | "received_events_url": "https://api.github.com/users/google/received_events",
1071 | "type": "Organization",
1072 | "site_admin": false
1073 | },
1074 | "private": false,
1075 | "html_url": "https://github.com/google/iosched",
1076 | "description": null,
1077 | "fork": false,
1078 | "url": "https://api.github.com/repos/google/iosched",
1079 | "forks_url": "https://api.github.com/repos/google/iosched/forks",
1080 | "keys_url": "https://api.github.com/repos/google/iosched/keys{/key_id}",
1081 | "collaborators_url": "https://api.github.com/repos/google/iosched/collaborators{/collaborator}",
1082 | "teams_url": "https://api.github.com/repos/google/iosched/teams",
1083 | "hooks_url": "https://api.github.com/repos/google/iosched/hooks",
1084 | "issue_events_url": "https://api.github.com/repos/google/iosched/issues/events{/number}",
1085 | "events_url": "https://api.github.com/repos/google/iosched/events",
1086 | "assignees_url": "https://api.github.com/repos/google/iosched/assignees{/user}",
1087 | "branches_url": "https://api.github.com/repos/google/iosched/branches{/branch}",
1088 | "tags_url": "https://api.github.com/repos/google/iosched/tags",
1089 | "blobs_url": "https://api.github.com/repos/google/iosched/git/blobs{/sha}",
1090 | "git_tags_url": "https://api.github.com/repos/google/iosched/git/tags{/sha}",
1091 | "git_refs_url": "https://api.github.com/repos/google/iosched/git/refs{/sha}",
1092 | "trees_url": "https://api.github.com/repos/google/iosched/git/trees{/sha}",
1093 | "statuses_url": "https://api.github.com/repos/google/iosched/statuses/{sha}",
1094 | "languages_url": "https://api.github.com/repos/google/iosched/languages",
1095 | "stargazers_url": "https://api.github.com/repos/google/iosched/stargazers",
1096 | "contributors_url": "https://api.github.com/repos/google/iosched/contributors",
1097 | "subscribers_url": "https://api.github.com/repos/google/iosched/subscribers",
1098 | "subscription_url": "https://api.github.com/repos/google/iosched/subscription",
1099 | "commits_url": "https://api.github.com/repos/google/iosched/commits{/sha}",
1100 | "git_commits_url": "https://api.github.com/repos/google/iosched/git/commits{/sha}",
1101 | "comments_url": "https://api.github.com/repos/google/iosched/comments{/number}",
1102 | "issue_comment_url": "https://api.github.com/repos/google/iosched/issues/comments/{number}",
1103 | "contents_url": "https://api.github.com/repos/google/iosched/contents/{+path}",
1104 | "compare_url": "https://api.github.com/repos/google/iosched/compare/{base}...{head}",
1105 | "merges_url": "https://api.github.com/repos/google/iosched/merges",
1106 | "archive_url": "https://api.github.com/repos/google/iosched/{archive_format}{/ref}",
1107 | "downloads_url": "https://api.github.com/repos/google/iosched/downloads",
1108 | "issues_url": "https://api.github.com/repos/google/iosched/issues{/number}",
1109 | "pulls_url": "https://api.github.com/repos/google/iosched/pulls{/number}",
1110 | "milestones_url": "https://api.github.com/repos/google/iosched/milestones{/number}",
1111 | "notifications_url": "https://api.github.com/repos/google/iosched/notifications{?since,all,participating}",
1112 | "labels_url": "https://api.github.com/repos/google/iosched/labels{/name}",
1113 | "releases_url": "https://api.github.com/repos/google/iosched/releases{/id}",
1114 | "created_at": "2014-04-01T22:40:40Z",
1115 | "updated_at": "2014-08-31T13:58:24Z",
1116 | "pushed_at": "2014-08-19T18:34:02Z",
1117 | "git_url": "git://github.com/google/iosched.git",
1118 | "ssh_url": "git@github.com:google/iosched.git",
1119 | "clone_url": "https://github.com/google/iosched.git",
1120 | "svn_url": "https://github.com/google/iosched",
1121 | "homepage": null,
1122 | "size": 19325,
1123 | "stargazers_count": 4049,
1124 | "watchers_count": 4049,
1125 | "language": "Java",
1126 | "has_issues": true,
1127 | "has_downloads": true,
1128 | "has_wiki": false,
1129 | "forks_count": 1195,
1130 | "mirror_url": null,
1131 | "open_issues_count": 12,
1132 | "forks": 1195,
1133 | "open_issues": 12,
1134 | "watchers": 4049,
1135 | "default_branch": "master"
1136 | }
1137 | },
1138 | "_links": {
1139 | "self": {
1140 | "href": "https://api.github.com/repos/google/iosched/pulls/47"
1141 | },
1142 | "html": {
1143 | "href": "https://github.com/google/iosched/pull/47"
1144 | },
1145 | "issue": {
1146 | "href": "https://api.github.com/repos/google/iosched/issues/47"
1147 | },
1148 | "comments": {
1149 | "href": "https://api.github.com/repos/google/iosched/issues/47/comments"
1150 | },
1151 | "review_comments": {
1152 | "href": "https://api.github.com/repos/google/iosched/pulls/47/comments"
1153 | },
1154 | "review_comment": {
1155 | "href": "https://api.github.com/repos/google/iosched/pulls/comments/{number}"
1156 | },
1157 | "commits": {
1158 | "href": "https://api.github.com/repos/google/iosched/pulls/47/commits"
1159 | },
1160 | "statuses": {
1161 | "href": "https://api.github.com/repos/google/iosched/statuses/ea244ed4d2664370a9975b4008eb7b3071a8bf29"
1162 | }
1163 | },
1164 | "merged": false,
1165 | "mergeable": true,
1166 | "mergeable_state": "clean",
1167 | "merged_by": null,
1168 | "comments": 0,
1169 | "review_comments": 0,
1170 | "commits": 2,
1171 | "additions": 12,
1172 | "deletions": 19,
1173 | "changed_files": 3
1174 | }
1175 | },
1176 | "public": true,
1177 | "created_at": "2014-08-31T16:06:35Z",
1178 | "org": {
1179 | "id": 1342004,
1180 | "login": "google",
1181 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1182 | "url": "https://api.github.com/orgs/google",
1183 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1184 | }
1185 | },
1186 | {
1187 | "id": "2263053661",
1188 | "type": "ForkEvent",
1189 | "actor": {
1190 | "id": 4126534,
1191 | "login": "ALEXGUOQ",
1192 | "gravatar_id": "04ab7fb03094abb92db66880059286aa",
1193 | "url": "https://api.github.com/users/ALEXGUOQ",
1194 | "avatar_url": "https://avatars.githubusercontent.com/u/4126534?"
1195 | },
1196 | "repo": {
1197 | "id": 18347476,
1198 | "name": "google/iosched",
1199 | "url": "https://api.github.com/repos/google/iosched"
1200 | },
1201 | "payload": {
1202 | "forkee": {
1203 | "id": 23517329,
1204 | "name": "iosched",
1205 | "full_name": "ALEXGUOQ/iosched",
1206 | "owner": {
1207 | "login": "ALEXGUOQ",
1208 | "id": 4126534,
1209 | "avatar_url": "https://avatars.githubusercontent.com/u/4126534?v=2",
1210 | "gravatar_id": "04ab7fb03094abb92db66880059286aa",
1211 | "url": "https://api.github.com/users/ALEXGUOQ",
1212 | "html_url": "https://github.com/ALEXGUOQ",
1213 | "followers_url": "https://api.github.com/users/ALEXGUOQ/followers",
1214 | "following_url": "https://api.github.com/users/ALEXGUOQ/following{/other_user}",
1215 | "gists_url": "https://api.github.com/users/ALEXGUOQ/gists{/gist_id}",
1216 | "starred_url": "https://api.github.com/users/ALEXGUOQ/starred{/owner}{/repo}",
1217 | "subscriptions_url": "https://api.github.com/users/ALEXGUOQ/subscriptions",
1218 | "organizations_url": "https://api.github.com/users/ALEXGUOQ/orgs",
1219 | "repos_url": "https://api.github.com/users/ALEXGUOQ/repos",
1220 | "events_url": "https://api.github.com/users/ALEXGUOQ/events{/privacy}",
1221 | "received_events_url": "https://api.github.com/users/ALEXGUOQ/received_events",
1222 | "type": "User",
1223 | "site_admin": false
1224 | },
1225 | "private": false,
1226 | "html_url": "https://github.com/ALEXGUOQ/iosched",
1227 | "description": null,
1228 | "fork": true,
1229 | "url": "https://api.github.com/repos/ALEXGUOQ/iosched",
1230 | "forks_url": "https://api.github.com/repos/ALEXGUOQ/iosched/forks",
1231 | "keys_url": "https://api.github.com/repos/ALEXGUOQ/iosched/keys{/key_id}",
1232 | "collaborators_url": "https://api.github.com/repos/ALEXGUOQ/iosched/collaborators{/collaborator}",
1233 | "teams_url": "https://api.github.com/repos/ALEXGUOQ/iosched/teams",
1234 | "hooks_url": "https://api.github.com/repos/ALEXGUOQ/iosched/hooks",
1235 | "issue_events_url": "https://api.github.com/repos/ALEXGUOQ/iosched/issues/events{/number}",
1236 | "events_url": "https://api.github.com/repos/ALEXGUOQ/iosched/events",
1237 | "assignees_url": "https://api.github.com/repos/ALEXGUOQ/iosched/assignees{/user}",
1238 | "branches_url": "https://api.github.com/repos/ALEXGUOQ/iosched/branches{/branch}",
1239 | "tags_url": "https://api.github.com/repos/ALEXGUOQ/iosched/tags",
1240 | "blobs_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/blobs{/sha}",
1241 | "git_tags_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/tags{/sha}",
1242 | "git_refs_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/refs{/sha}",
1243 | "trees_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/trees{/sha}",
1244 | "statuses_url": "https://api.github.com/repos/ALEXGUOQ/iosched/statuses/{sha}",
1245 | "languages_url": "https://api.github.com/repos/ALEXGUOQ/iosched/languages",
1246 | "stargazers_url": "https://api.github.com/repos/ALEXGUOQ/iosched/stargazers",
1247 | "contributors_url": "https://api.github.com/repos/ALEXGUOQ/iosched/contributors",
1248 | "subscribers_url": "https://api.github.com/repos/ALEXGUOQ/iosched/subscribers",
1249 | "subscription_url": "https://api.github.com/repos/ALEXGUOQ/iosched/subscription",
1250 | "commits_url": "https://api.github.com/repos/ALEXGUOQ/iosched/commits{/sha}",
1251 | "git_commits_url": "https://api.github.com/repos/ALEXGUOQ/iosched/git/commits{/sha}",
1252 | "comments_url": "https://api.github.com/repos/ALEXGUOQ/iosched/comments{/number}",
1253 | "issue_comment_url": "https://api.github.com/repos/ALEXGUOQ/iosched/issues/comments/{number}",
1254 | "contents_url": "https://api.github.com/repos/ALEXGUOQ/iosched/contents/{+path}",
1255 | "compare_url": "https://api.github.com/repos/ALEXGUOQ/iosched/compare/{base}...{head}",
1256 | "merges_url": "https://api.github.com/repos/ALEXGUOQ/iosched/merges",
1257 | "archive_url": "https://api.github.com/repos/ALEXGUOQ/iosched/{archive_format}{/ref}",
1258 | "downloads_url": "https://api.github.com/repos/ALEXGUOQ/iosched/downloads",
1259 | "issues_url": "https://api.github.com/repos/ALEXGUOQ/iosched/issues{/number}",
1260 | "pulls_url": "https://api.github.com/repos/ALEXGUOQ/iosched/pulls{/number}",
1261 | "milestones_url": "https://api.github.com/repos/ALEXGUOQ/iosched/milestones{/number}",
1262 | "notifications_url": "https://api.github.com/repos/ALEXGUOQ/iosched/notifications{?since,all,participating}",
1263 | "labels_url": "https://api.github.com/repos/ALEXGUOQ/iosched/labels{/name}",
1264 | "releases_url": "https://api.github.com/repos/ALEXGUOQ/iosched/releases{/id}",
1265 | "created_at": "2014-08-31T16:05:13Z",
1266 | "updated_at": "2014-08-31T13:58:24Z",
1267 | "pushed_at": "2014-08-19T18:34:02Z",
1268 | "git_url": "git://github.com/ALEXGUOQ/iosched.git",
1269 | "ssh_url": "git@github.com:ALEXGUOQ/iosched.git",
1270 | "clone_url": "https://github.com/ALEXGUOQ/iosched.git",
1271 | "svn_url": "https://github.com/ALEXGUOQ/iosched",
1272 | "homepage": null,
1273 | "size": 19325,
1274 | "stargazers_count": 0,
1275 | "watchers_count": 0,
1276 | "language": null,
1277 | "has_issues": false,
1278 | "has_downloads": true,
1279 | "has_wiki": false,
1280 | "forks_count": 0,
1281 | "mirror_url": null,
1282 | "open_issues_count": 0,
1283 | "forks": 0,
1284 | "open_issues": 0,
1285 | "watchers": 0,
1286 | "default_branch": "master",
1287 | "public": true
1288 | }
1289 | },
1290 | "public": true,
1291 | "created_at": "2014-08-31T16:05:15Z",
1292 | "org": {
1293 | "id": 1342004,
1294 | "login": "google",
1295 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1296 | "url": "https://api.github.com/orgs/google",
1297 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1298 | }
1299 | },
1300 | {
1301 | "id": "2263052579",
1302 | "type": "IssueCommentEvent",
1303 | "actor": {
1304 | "id": 205570,
1305 | "login": "johnjbarton",
1306 | "gravatar_id": "88457ac5fd091ba1fad54a189348a4b1",
1307 | "url": "https://api.github.com/users/johnjbarton",
1308 | "avatar_url": "https://avatars.githubusercontent.com/u/205570?"
1309 | },
1310 | "repo": {
1311 | "id": 9060347,
1312 | "name": "google/traceur-compiler",
1313 | "url": "https://api.github.com/repos/google/traceur-compiler"
1314 | },
1315 | "payload": {
1316 | "action": "created",
1317 | "issue": {
1318 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/1300",
1319 | "labels_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/labels{/name}",
1320 | "comments_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/comments",
1321 | "events_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/events",
1322 | "html_url": "https://github.com/google/traceur-compiler/issues/1300",
1323 | "id": 41589319,
1324 | "number": 1300,
1325 | "title": "module myModule as './path/to/module'; outputs \"Semi-colon expected\" error",
1326 | "user": {
1327 | "login": "pflannery",
1328 | "id": 1727302,
1329 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2",
1330 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1",
1331 | "url": "https://api.github.com/users/pflannery",
1332 | "html_url": "https://github.com/pflannery",
1333 | "followers_url": "https://api.github.com/users/pflannery/followers",
1334 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}",
1335 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}",
1336 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}",
1337 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions",
1338 | "organizations_url": "https://api.github.com/users/pflannery/orgs",
1339 | "repos_url": "https://api.github.com/users/pflannery/repos",
1340 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}",
1341 | "received_events_url": "https://api.github.com/users/pflannery/received_events",
1342 | "type": "User",
1343 | "site_admin": false
1344 | },
1345 | "labels": [
1346 |
1347 | ],
1348 | "state": "open",
1349 | "locked": false,
1350 | "assignee": null,
1351 | "milestone": null,
1352 | "comments": 1,
1353 | "created_at": "2014-08-31T15:49:04Z",
1354 | "updated_at": "2014-08-31T16:03:15Z",
1355 | "closed_at": null,
1356 | "body": "I've been trying to bring in a module as a single object using \r\n```js\r\nmodule myModule as './path/to/module';\r\n```\r\nbut I get the following error ```Semi-colon expected```\r\n\r\nIs this because it's not implemented yet or am I doing something wrong?\r\n\r\nmy current work around is to replace the line with \r\n```js\r\nvar myModule = System.get(\"relative/path/to/module\");\r\n```"
1357 | },
1358 | "comment": {
1359 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/comments/53992343",
1360 | "html_url": "https://github.com/google/traceur-compiler/issues/1300#issuecomment-53992343",
1361 | "issue_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300",
1362 | "id": 53992343,
1363 | "user": {
1364 | "login": "johnjbarton",
1365 | "id": 205570,
1366 | "avatar_url": "https://avatars.githubusercontent.com/u/205570?v=2",
1367 | "gravatar_id": "88457ac5fd091ba1fad54a189348a4b1",
1368 | "url": "https://api.github.com/users/johnjbarton",
1369 | "html_url": "https://github.com/johnjbarton",
1370 | "followers_url": "https://api.github.com/users/johnjbarton/followers",
1371 | "following_url": "https://api.github.com/users/johnjbarton/following{/other_user}",
1372 | "gists_url": "https://api.github.com/users/johnjbarton/gists{/gist_id}",
1373 | "starred_url": "https://api.github.com/users/johnjbarton/starred{/owner}{/repo}",
1374 | "subscriptions_url": "https://api.github.com/users/johnjbarton/subscriptions",
1375 | "organizations_url": "https://api.github.com/users/johnjbarton/orgs",
1376 | "repos_url": "https://api.github.com/users/johnjbarton/repos",
1377 | "events_url": "https://api.github.com/users/johnjbarton/events{/privacy}",
1378 | "received_events_url": "https://api.github.com/users/johnjbarton/received_events",
1379 | "type": "User",
1380 | "site_admin": false
1381 | },
1382 | "created_at": "2014-08-31T16:03:15Z",
1383 | "updated_at": "2014-08-31T16:03:15Z",
1384 | "body": "You are doing something wrong. The new syntax is\r\n```js\r\nimport * as myModule from './path/to/module';\r\n```\r\nWhere did you read about the older syntax? "
1385 | }
1386 | },
1387 | "public": true,
1388 | "created_at": "2014-08-31T16:03:18Z",
1389 | "org": {
1390 | "id": 1342004,
1391 | "login": "google",
1392 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1393 | "url": "https://api.github.com/orgs/google",
1394 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1395 | }
1396 | },
1397 | {
1398 | "id": "2263052286",
1399 | "type": "WatchEvent",
1400 | "actor": {
1401 | "id": 6113842,
1402 | "login": "balysv",
1403 | "gravatar_id": "8067174fa3afcc5d140ed8e43a2b6278",
1404 | "url": "https://api.github.com/users/balysv",
1405 | "avatar_url": "https://avatars.githubusercontent.com/u/6113842?"
1406 | },
1407 | "repo": {
1408 | "id": 7968417,
1409 | "name": "google/dagger",
1410 | "url": "https://api.github.com/repos/google/dagger"
1411 | },
1412 | "payload": {
1413 | "action": "started"
1414 | },
1415 | "public": true,
1416 | "created_at": "2014-08-31T16:02:44Z",
1417 | "org": {
1418 | "id": 1342004,
1419 | "login": "google",
1420 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1421 | "url": "https://api.github.com/orgs/google",
1422 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1423 | }
1424 | },
1425 | {
1426 | "id": "2263049185",
1427 | "type": "WatchEvent",
1428 | "actor": {
1429 | "id": 1239359,
1430 | "login": "jmargeta",
1431 | "gravatar_id": "a7259256f02482925032f8534dbe1d1c",
1432 | "url": "https://api.github.com/users/jmargeta",
1433 | "avatar_url": "https://avatars.githubusercontent.com/u/1239359?"
1434 | },
1435 | "repo": {
1436 | "id": 18511024,
1437 | "name": "google/web-starter-kit",
1438 | "url": "https://api.github.com/repos/google/web-starter-kit"
1439 | },
1440 | "payload": {
1441 | "action": "started"
1442 | },
1443 | "public": true,
1444 | "created_at": "2014-08-31T15:57:29Z",
1445 | "org": {
1446 | "id": 1342004,
1447 | "login": "google",
1448 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1449 | "url": "https://api.github.com/orgs/google",
1450 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1451 | }
1452 | },
1453 | {
1454 | "id": "2263048295",
1455 | "type": "WatchEvent",
1456 | "actor": {
1457 | "id": 733158,
1458 | "login": "bobaaaaa",
1459 | "gravatar_id": "7bdc72c2f9aa1f3958d17aaa9429ae92",
1460 | "url": "https://api.github.com/users/bobaaaaa",
1461 | "avatar_url": "https://avatars.githubusercontent.com/u/733158?"
1462 | },
1463 | "repo": {
1464 | "id": 18511024,
1465 | "name": "google/web-starter-kit",
1466 | "url": "https://api.github.com/repos/google/web-starter-kit"
1467 | },
1468 | "payload": {
1469 | "action": "started"
1470 | },
1471 | "public": true,
1472 | "created_at": "2014-08-31T15:55:45Z",
1473 | "org": {
1474 | "id": 1342004,
1475 | "login": "google",
1476 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1477 | "url": "https://api.github.com/orgs/google",
1478 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1479 | }
1480 | },
1481 | {
1482 | "id": "2263044828",
1483 | "type": "IssuesEvent",
1484 | "actor": {
1485 | "id": 1727302,
1486 | "login": "pflannery",
1487 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1",
1488 | "url": "https://api.github.com/users/pflannery",
1489 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?"
1490 | },
1491 | "repo": {
1492 | "id": 9060347,
1493 | "name": "google/traceur-compiler",
1494 | "url": "https://api.github.com/repos/google/traceur-compiler"
1495 | },
1496 | "payload": {
1497 | "action": "opened",
1498 | "issue": {
1499 | "url": "https://api.github.com/repos/google/traceur-compiler/issues/1300",
1500 | "labels_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/labels{/name}",
1501 | "comments_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/comments",
1502 | "events_url": "https://api.github.com/repos/google/traceur-compiler/issues/1300/events",
1503 | "html_url": "https://github.com/google/traceur-compiler/issues/1300",
1504 | "id": 41589319,
1505 | "number": 1300,
1506 | "title": "module myModule as './path/to/module'; outputs \"Semi-colon expected\" error",
1507 | "user": {
1508 | "login": "pflannery",
1509 | "id": 1727302,
1510 | "avatar_url": "https://avatars.githubusercontent.com/u/1727302?v=2",
1511 | "gravatar_id": "7f2a377aaeb6cea73145b33975a030b1",
1512 | "url": "https://api.github.com/users/pflannery",
1513 | "html_url": "https://github.com/pflannery",
1514 | "followers_url": "https://api.github.com/users/pflannery/followers",
1515 | "following_url": "https://api.github.com/users/pflannery/following{/other_user}",
1516 | "gists_url": "https://api.github.com/users/pflannery/gists{/gist_id}",
1517 | "starred_url": "https://api.github.com/users/pflannery/starred{/owner}{/repo}",
1518 | "subscriptions_url": "https://api.github.com/users/pflannery/subscriptions",
1519 | "organizations_url": "https://api.github.com/users/pflannery/orgs",
1520 | "repos_url": "https://api.github.com/users/pflannery/repos",
1521 | "events_url": "https://api.github.com/users/pflannery/events{/privacy}",
1522 | "received_events_url": "https://api.github.com/users/pflannery/received_events",
1523 | "type": "User",
1524 | "site_admin": false
1525 | },
1526 | "labels": [
1527 |
1528 | ],
1529 | "state": "open",
1530 | "locked": false,
1531 | "assignee": null,
1532 | "milestone": null,
1533 | "comments": 0,
1534 | "created_at": "2014-08-31T15:49:04Z",
1535 | "updated_at": "2014-08-31T15:49:04Z",
1536 | "closed_at": null,
1537 | "body": "I've been trying to bring in a module as a single object using \r\n```js\r\nmodule myModule as './path/to/module';\r\n```\r\nbut I get the following error ```Semi-colon expected```\r\n\r\nIs this because it's not implemented yet or am I doing something wrong?\r\n\r\nmy current work around is to replace the line with \r\n```js\r\nvar myModule = System.get(\"relative/path/to/module\");\r\n```"
1538 | }
1539 | },
1540 | "public": true,
1541 | "created_at": "2014-08-31T15:49:04Z",
1542 | "org": {
1543 | "id": 1342004,
1544 | "login": "google",
1545 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1546 | "url": "https://api.github.com/orgs/google",
1547 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1548 | }
1549 | },
1550 | {
1551 | "id": "2263044542",
1552 | "type": "IssuesEvent",
1553 | "actor": {
1554 | "id": 72422,
1555 | "login": "pihvi",
1556 | "gravatar_id": "d4e5de1f46b26e98f175f0e2b9c6226b",
1557 | "url": "https://api.github.com/users/pihvi",
1558 | "avatar_url": "https://avatars.githubusercontent.com/u/72422?"
1559 | },
1560 | "repo": {
1561 | "id": 18511024,
1562 | "name": "google/web-starter-kit",
1563 | "url": "https://api.github.com/repos/google/web-starter-kit"
1564 | },
1565 | "payload": {
1566 | "action": "opened",
1567 | "issue": {
1568 | "url": "https://api.github.com/repos/google/web-starter-kit/issues/413",
1569 | "labels_url": "https://api.github.com/repos/google/web-starter-kit/issues/413/labels{/name}",
1570 | "comments_url": "https://api.github.com/repos/google/web-starter-kit/issues/413/comments",
1571 | "events_url": "https://api.github.com/repos/google/web-starter-kit/issues/413/events",
1572 | "html_url": "https://github.com/google/web-starter-kit/issues/413",
1573 | "id": 41589302,
1574 | "number": 413,
1575 | "title": "gulp fails in vinyl-fs",
1576 | "user": {
1577 | "login": "pihvi",
1578 | "id": 72422,
1579 | "avatar_url": "https://avatars.githubusercontent.com/u/72422?v=2",
1580 | "gravatar_id": "d4e5de1f46b26e98f175f0e2b9c6226b",
1581 | "url": "https://api.github.com/users/pihvi",
1582 | "html_url": "https://github.com/pihvi",
1583 | "followers_url": "https://api.github.com/users/pihvi/followers",
1584 | "following_url": "https://api.github.com/users/pihvi/following{/other_user}",
1585 | "gists_url": "https://api.github.com/users/pihvi/gists{/gist_id}",
1586 | "starred_url": "https://api.github.com/users/pihvi/starred{/owner}{/repo}",
1587 | "subscriptions_url": "https://api.github.com/users/pihvi/subscriptions",
1588 | "organizations_url": "https://api.github.com/users/pihvi/orgs",
1589 | "repos_url": "https://api.github.com/users/pihvi/repos",
1590 | "events_url": "https://api.github.com/users/pihvi/events{/privacy}",
1591 | "received_events_url": "https://api.github.com/users/pihvi/received_events",
1592 | "type": "User",
1593 | "site_admin": false
1594 | },
1595 | "labels": [
1596 |
1597 | ],
1598 | "state": "open",
1599 | "locked": false,
1600 | "assignee": null,
1601 | "milestone": null,
1602 | "comments": 0,
1603 | "created_at": "2014-08-31T15:48:29Z",
1604 | "updated_at": "2014-08-31T15:48:29Z",
1605 | "closed_at": null,
1606 | "body": "I made a clean chekout of https://github.com/google/web-starter-kit/commit/266ee0620dc5e2f81a8e7902a6f40c41665af6e0 and running gulp fails. Same thing happened for v0.5.0\r\n\r\n➜ web-starter-kit git:(master) ✗ gulp\r\n[18:41:03] Using gulpfile /private/tmp/web-starter-kit/gulpfile.js\r\n[18:41:03] Starting 'clean'...\r\n[18:41:03] Finished 'clean' after 7.76 ms\r\n[18:41:03] Starting 'default'...\r\n[18:41:03] Starting 'styles'...\r\n[18:41:04] gulp-ruby-sass: directory\r\n[18:41:05] gulp-ruby-sass: write components.css\r\n write components.css.map\r\n[18:41:06] gulp-ruby-sass: write main.css\r\n write main.css.map\r\n[18:41:08] gulp-size: 'styles' total 183.59 kB\r\n[18:41:08] Finished 'styles' after 4.66 s\r\n[18:41:08] Starting 'jshint'...\r\n[18:41:08] Starting 'html'...\r\n[18:41:08] Starting 'images'...\r\n[18:41:09] Starting 'fonts'...\r\n[18:41:09] Starting 'copy'...\r\n[18:41:09] Finished 'jshint' after 992 ms\r\nPossibly unhandled Error: No path specified! Can not get relative.\r\n at File.Object.defineProperty.get (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/vinyl/index.js:150:27)\r\n at DestroyableTransform.saveFile [as _transform] (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/lib/dest/index.js:40:48)\r\n at DestroyableTransform.Transform._read (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_transform.js:184:10)\r\n at DestroyableTransform.Transform._write (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_transform.js:172:12)\r\n at doWrite (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_writable.js:237:10)\r\n at writeOrBuffer (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_writable.js:227:5)\r\n at DestroyableTransform.Writable.write (/private/tmp/web-starter-kit/node_modules/gulp/node_modules/vinyl-fs/node_modules/through2/node_modules/readable-stream/lib/_stream_writable.js:194:11)\r\n at Stream.ondata (stream.js:51:26)\r\n at Stream.EventEmitter.emit (events.js:95:17)\r\n at queueData (/private/tmp/web-starter-kit/node_modules/gulp-cache/node_modules/map-stream/index.js:43:21)\r\n at next (/private/tmp/web-starter-kit/node_modules/gulp-cache/node_modules/map-stream/index.js:71:7)\r\n at /private/tmp/web-starter-kit/node_modules/gulp-cache/node_modules/map-stream/index.js:85:7\r\n at /private/tmp/web-starter-kit/node_modules/gulp-cache/index.js:93:13\r\n[18:41:09] gulp-size: 'html' total 44.14 kB\r\n[18:41:09] Finished 'html' after 935 ms\r\n[18:41:09] gulp-size: 'copy' total 41.35 kB\r\n[18:41:09] Finished 'copy' after 171 ms\r\n[18:41:09] gulp-size: 'fonts' total 1.09 MB\r\n[18:41:09] Finished 'fonts' after 177 ms\r\n➜ web-starter-kit git:(master) ✗ node --version\r\nv0.10.25\r\n➜ web-starter-kit git:(master) ✗ ruby --version\r\nruby 1.9.3p392 (2013-02-22 revision 39386) [x86_64-darwin12.3.0]\r\n➜ web-starter-kit git:(master) ✗ sass --version\r\nSass 3.4.2 (Selective Steve)\r\n➜ web-starter-kit git:(master) ✗ gulp --version\r\n[18:42:07] CLI version 3.8.6\r\n[18:42:07] Local version 3.8.7\r\n"
1607 | }
1608 | },
1609 | "public": true,
1610 | "created_at": "2014-08-31T15:48:29Z",
1611 | "org": {
1612 | "id": 1342004,
1613 | "login": "google",
1614 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1615 | "url": "https://api.github.com/orgs/google",
1616 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1617 | }
1618 | },
1619 | {
1620 | "id": "2263032302",
1621 | "type": "WatchEvent",
1622 | "actor": {
1623 | "id": 8606300,
1624 | "login": "whosluke",
1625 | "gravatar_id": "b24094d4e093ac2e32fb5981923d333b",
1626 | "url": "https://api.github.com/users/whosluke",
1627 | "avatar_url": "https://avatars.githubusercontent.com/u/8606300?"
1628 | },
1629 | "repo": {
1630 | "id": 18511024,
1631 | "name": "google/web-starter-kit",
1632 | "url": "https://api.github.com/repos/google/web-starter-kit"
1633 | },
1634 | "payload": {
1635 | "action": "started"
1636 | },
1637 | "public": true,
1638 | "created_at": "2014-08-31T15:26:04Z",
1639 | "org": {
1640 | "id": 1342004,
1641 | "login": "google",
1642 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1643 | "url": "https://api.github.com/orgs/google",
1644 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1645 | }
1646 | },
1647 | {
1648 | "id": "2263030192",
1649 | "type": "IssuesEvent",
1650 | "actor": {
1651 | "id": 110953,
1652 | "login": "addyosmani",
1653 | "gravatar_id": "96270e4c3e5e9806cf7245475c00b275",
1654 | "url": "https://api.github.com/users/addyosmani",
1655 | "avatar_url": "https://avatars.githubusercontent.com/u/110953?"
1656 | },
1657 | "repo": {
1658 | "id": 18511024,
1659 | "name": "google/web-starter-kit",
1660 | "url": "https://api.github.com/repos/google/web-starter-kit"
1661 | },
1662 | "payload": {
1663 | "action": "reopened",
1664 | "issue": {
1665 | "url": "https://api.github.com/repos/google/web-starter-kit/issues/97",
1666 | "labels_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/labels{/name}",
1667 | "comments_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/comments",
1668 | "events_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/events",
1669 | "html_url": "https://github.com/google/web-starter-kit/issues/97",
1670 | "id": 36131766,
1671 | "number": 97,
1672 | "title": "Live stage for demo",
1673 | "user": {
1674 | "login": "mangini",
1675 | "id": 852626,
1676 | "avatar_url": "https://avatars.githubusercontent.com/u/852626?v=2",
1677 | "gravatar_id": "61ffaf1af13c5626d3cd975275b2ddfd",
1678 | "url": "https://api.github.com/users/mangini",
1679 | "html_url": "https://github.com/mangini",
1680 | "followers_url": "https://api.github.com/users/mangini/followers",
1681 | "following_url": "https://api.github.com/users/mangini/following{/other_user}",
1682 | "gists_url": "https://api.github.com/users/mangini/gists{/gist_id}",
1683 | "starred_url": "https://api.github.com/users/mangini/starred{/owner}{/repo}",
1684 | "subscriptions_url": "https://api.github.com/users/mangini/subscriptions",
1685 | "organizations_url": "https://api.github.com/users/mangini/orgs",
1686 | "repos_url": "https://api.github.com/users/mangini/repos",
1687 | "events_url": "https://api.github.com/users/mangini/events{/privacy}",
1688 | "received_events_url": "https://api.github.com/users/mangini/received_events",
1689 | "type": "User",
1690 | "site_admin": false
1691 | },
1692 | "labels": [
1693 | {
1694 | "url": "https://api.github.com/repos/google/web-starter-kit/labels/documentation",
1695 | "name": "documentation",
1696 | "color": "006b75"
1697 | }
1698 | ],
1699 | "state": "open",
1700 | "locked": false,
1701 | "assignee": null,
1702 | "milestone": null,
1703 | "comments": 10,
1704 | "created_at": "2014-06-20T00:39:43Z",
1705 | "updated_at": "2014-08-31T15:22:07Z",
1706 | "closed_at": null,
1707 | "body": "\r\nCongrats, awesome project.\r\n\r\nTo increase adoption, I suggest you stage a live demo of [each layout](https://developers.google.com/web/fundamentals/tools/setup/setup_kit#pick-a-layout), quickly accessible from https://developers.google.com/web/starter-kit/, so developers can look at the it before downloading/installing/etc.\r\n"
1708 | }
1709 | },
1710 | "public": true,
1711 | "created_at": "2014-08-31T15:22:07Z",
1712 | "org": {
1713 | "id": 1342004,
1714 | "login": "google",
1715 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1716 | "url": "https://api.github.com/orgs/google",
1717 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1718 | }
1719 | },
1720 | {
1721 | "id": "2263030186",
1722 | "type": "IssueCommentEvent",
1723 | "actor": {
1724 | "id": 110953,
1725 | "login": "addyosmani",
1726 | "gravatar_id": "96270e4c3e5e9806cf7245475c00b275",
1727 | "url": "https://api.github.com/users/addyosmani",
1728 | "avatar_url": "https://avatars.githubusercontent.com/u/110953?"
1729 | },
1730 | "repo": {
1731 | "id": 18511024,
1732 | "name": "google/web-starter-kit",
1733 | "url": "https://api.github.com/repos/google/web-starter-kit"
1734 | },
1735 | "payload": {
1736 | "action": "created",
1737 | "issue": {
1738 | "url": "https://api.github.com/repos/google/web-starter-kit/issues/97",
1739 | "labels_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/labels{/name}",
1740 | "comments_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/comments",
1741 | "events_url": "https://api.github.com/repos/google/web-starter-kit/issues/97/events",
1742 | "html_url": "https://github.com/google/web-starter-kit/issues/97",
1743 | "id": 36131766,
1744 | "number": 97,
1745 | "title": "Live stage for demo",
1746 | "user": {
1747 | "login": "mangini",
1748 | "id": 852626,
1749 | "avatar_url": "https://avatars.githubusercontent.com/u/852626?v=2",
1750 | "gravatar_id": "61ffaf1af13c5626d3cd975275b2ddfd",
1751 | "url": "https://api.github.com/users/mangini",
1752 | "html_url": "https://github.com/mangini",
1753 | "followers_url": "https://api.github.com/users/mangini/followers",
1754 | "following_url": "https://api.github.com/users/mangini/following{/other_user}",
1755 | "gists_url": "https://api.github.com/users/mangini/gists{/gist_id}",
1756 | "starred_url": "https://api.github.com/users/mangini/starred{/owner}{/repo}",
1757 | "subscriptions_url": "https://api.github.com/users/mangini/subscriptions",
1758 | "organizations_url": "https://api.github.com/users/mangini/orgs",
1759 | "repos_url": "https://api.github.com/users/mangini/repos",
1760 | "events_url": "https://api.github.com/users/mangini/events{/privacy}",
1761 | "received_events_url": "https://api.github.com/users/mangini/received_events",
1762 | "type": "User",
1763 | "site_admin": false
1764 | },
1765 | "labels": [
1766 | {
1767 | "url": "https://api.github.com/repos/google/web-starter-kit/labels/documentation",
1768 | "name": "documentation",
1769 | "color": "006b75"
1770 | }
1771 | ],
1772 | "state": "open",
1773 | "locked": false,
1774 | "assignee": null,
1775 | "milestone": null,
1776 | "comments": 10,
1777 | "created_at": "2014-06-20T00:39:43Z",
1778 | "updated_at": "2014-08-31T15:22:07Z",
1779 | "closed_at": null,
1780 | "body": "\r\nCongrats, awesome project.\r\n\r\nTo increase adoption, I suggest you stage a live demo of [each layout](https://developers.google.com/web/fundamentals/tools/setup/setup_kit#pick-a-layout), quickly accessible from https://developers.google.com/web/starter-kit/, so developers can look at the it before downloading/installing/etc.\r\n"
1781 | },
1782 | "comment": {
1783 | "url": "https://api.github.com/repos/google/web-starter-kit/issues/comments/53991052",
1784 | "html_url": "https://github.com/google/web-starter-kit/issues/97#issuecomment-53991052",
1785 | "issue_url": "https://api.github.com/repos/google/web-starter-kit/issues/97",
1786 | "id": 53991052,
1787 | "user": {
1788 | "login": "addyosmani",
1789 | "id": 110953,
1790 | "avatar_url": "https://avatars.githubusercontent.com/u/110953?v=2",
1791 | "gravatar_id": "96270e4c3e5e9806cf7245475c00b275",
1792 | "url": "https://api.github.com/users/addyosmani",
1793 | "html_url": "https://github.com/addyosmani",
1794 | "followers_url": "https://api.github.com/users/addyosmani/followers",
1795 | "following_url": "https://api.github.com/users/addyosmani/following{/other_user}",
1796 | "gists_url": "https://api.github.com/users/addyosmani/gists{/gist_id}",
1797 | "starred_url": "https://api.github.com/users/addyosmani/starred{/owner}{/repo}",
1798 | "subscriptions_url": "https://api.github.com/users/addyosmani/subscriptions",
1799 | "organizations_url": "https://api.github.com/users/addyosmani/orgs",
1800 | "repos_url": "https://api.github.com/users/addyosmani/repos",
1801 | "events_url": "https://api.github.com/users/addyosmani/events{/privacy}",
1802 | "received_events_url": "https://api.github.com/users/addyosmani/received_events",
1803 | "type": "User",
1804 | "site_admin": false
1805 | },
1806 | "created_at": "2014-08-31T15:22:07Z",
1807 | "updated_at": "2014-08-31T15:22:07Z",
1808 | "body": "Good idea. Re-opening so we can get this done early next week."
1809 | }
1810 | },
1811 | "public": true,
1812 | "created_at": "2014-08-31T15:22:07Z",
1813 | "org": {
1814 | "id": 1342004,
1815 | "login": "google",
1816 | "gravatar_id": "24ba30616d2a20673f54c2aee36d159e",
1817 | "url": "https://api.github.com/orgs/google",
1818 | "avatar_url": "https://avatars.githubusercontent.com/u/1342004?"
1819 | }
1820 | }
1821 | ]
1822 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/andydyer/androidtestdemo/ApplicationTest.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo;
2 |
3 | import android.app.Application;
4 | import android.test.ApplicationTestCase;
5 |
6 | /**
7 | * Testing Fundamentals
8 | */
9 | public class ApplicationTest extends ApplicationTestCase {
10 | public ApplicationTest() {
11 | super(Application.class);
12 | }
13 | }
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/andydyer/androidtestdemo/LoginActivityTest.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo;
2 |
3 | import org.andydyer.androidtestdemo.ui.LoginActivity;
4 | import org.mockito.ArgumentCaptor;
5 |
6 | import retrofit.Callback;
7 | import retrofit.RetrofitError;
8 |
9 | import static android.support.test.espresso.Espresso.onView;
10 | import static android.support.test.espresso.action.ViewActions.click;
11 | import static android.support.test.espresso.action.ViewActions.closeSoftKeyboard;
12 | import static android.support.test.espresso.action.ViewActions.typeText;
13 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
14 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
15 | import static org.andydyer.androidtestdemo.test.CustomMatchers.withError;
16 | import static org.mockito.Matchers.anyString;
17 | import static org.mockito.Mockito.verify;
18 |
19 | /**
20 | * Created by andy on 9/6/14.
21 | */
22 | public class LoginActivityTest extends InjectedActivityTest {
23 |
24 | public LoginActivityTest() {
25 | super(LoginActivity.class);
26 | }
27 |
28 | @Override
29 | protected void setUp() throws Exception {
30 | super.setUp();
31 | getActivity();
32 | }
33 |
34 | public void testEmptyEmailShowsError() {
35 | onView(withId(R.id.email_sign_in_button)).perform(click());
36 | onView(withId(R.id.email)).check(matches(withError(
37 | getActivity().getString(R.string.error_field_required))));
38 | }
39 |
40 | public void testInvalidEmailShowsError() {
41 | onView(withId(R.id.email)).perform(typeText("abc"), closeSoftKeyboard());
42 | onView(withId(R.id.email_sign_in_button)).perform(click());
43 | onView(withId(R.id.email)).check(matches(withError(
44 | getActivity().getString(R.string.error_invalid_email))));
45 | }
46 |
47 | public void testShortPasswordShowsError() {
48 | onView(withId(R.id.email)).perform(typeText("email@host.com"), closeSoftKeyboard());
49 | onView(withId(R.id.password)).perform(typeText("a"), closeSoftKeyboard());
50 | onView(withId(R.id.email_sign_in_button)).perform(click());
51 | onView(withId(R.id.password)).check(matches(withError(
52 | getActivity().getString(R.string.error_invalid_password))));
53 | }
54 |
55 | public void testIncorrectPasswordShowsError() {
56 | onView(withId(R.id.email)).perform(typeText("email@host.com"), closeSoftKeyboard());
57 | onView(withId(R.id.password)).perform(typeText("abcde"), closeSoftKeyboard());
58 | onView(withId(R.id.email_sign_in_button)).perform(click());
59 |
60 | final ArgumentCaptor captor = ArgumentCaptor.forClass(Callback.class);
61 | verify(authenticationService).login(anyString(), anyString(), anyString(), captor.capture());
62 | getActivity().runOnUiThread(new Runnable() {
63 | @Override
64 | public void run() {
65 | captor.getValue().failure(RetrofitError.unexpectedError("", new Exception()));
66 | }
67 | });
68 |
69 | onView(withId(R.id.password)).check(matches(withError(
70 | getActivity().getString(R.string.error_incorrect_password))));
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/andydyer/androidtestdemo/MainActivityTest.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo;
2 |
3 | import android.support.test.espresso.contrib.RecyclerViewActions;
4 | import android.test.ActivityInstrumentationTestCase2;
5 |
6 | import org.andydyer.androidtestdemo.ui.MainActivity;
7 |
8 | import static android.support.test.espresso.Espresso.onView;
9 | import static android.support.test.espresso.action.ViewActions.click;
10 | import static android.support.test.espresso.assertion.ViewAssertions.matches;
11 | import static android.support.test.espresso.matcher.ViewMatchers.withId;
12 | import static org.andydyer.androidtestdemo.test.CustomMatchers.hasData;
13 | import static org.andydyer.androidtestdemo.test.CustomMatchers.showsUrl;
14 | import static org.andydyer.androidtestdemo.test.CustomViewActions.clickEventItemAvatar;
15 |
16 | /**
17 | * Created by andy on 8/18/14.
18 | */
19 | public class MainActivityTest extends ActivityInstrumentationTestCase2 {
20 |
21 | public MainActivityTest() {
22 | super(MainActivity.class);
23 | }
24 |
25 | @Override
26 | protected void setUp() throws Exception {
27 | super.setUp();
28 | DemoApplication.getInstance().setMockMode(true);
29 | getActivity();
30 | }
31 |
32 | @Override
33 | protected void tearDown() throws Exception {
34 | DemoApplication.getInstance().setMockMode(false);
35 | }
36 |
37 | public void testHasData() throws Exception {
38 | onView(withId(android.R.id.list)).check(matches(hasData()));
39 | }
40 |
41 | public void testRowClickLaunchesRepoUrl() throws Exception {
42 | onView(withId(android.R.id.list)).perform(
43 | RecyclerViewActions.actionOnItemAtPosition(0, click()));
44 | onView(withId(R.id.webview)).check(matches(showsUrl("github.com/google/iosched")));
45 | }
46 |
47 | public void testImageClickLaunchesProfileUrl() throws Exception {
48 | onView(withId(android.R.id.list)).perform(
49 | RecyclerViewActions.actionOnItemAtPosition(0, clickEventItemAvatar()));
50 | onView(withId(R.id.webview)).check(matches(showsUrl("github.com/karthikraj-duraisamy")));
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/andydyer/androidtestdemo/api/ApiServiceTest.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import org.andydyer.androidtestdemo.InjectedInstrumentationTest;
4 |
5 | import retrofit.Callback;
6 | import retrofit.RetrofitError;
7 | import retrofit.client.Response;
8 |
9 | /**
10 | * Created by andy on 8/30/14.
11 | */
12 | public class ApiServiceTest extends InjectedInstrumentationTest {
13 |
14 | public void testEventsRequest() {
15 | getApiService().getEvents("google", new Callback() {
16 | @Override
17 | public void success(Events events, Response response) {
18 | assertNotNull(events);
19 | assertFalse(events.isEmpty());
20 | }
21 |
22 | @Override
23 | public void failure(RetrofitError error) {
24 |
25 | }
26 | });
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/andydyer/androidtestdemo/test/CustomMatchers.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.test;
2 |
3 | import android.support.v7.widget.RecyclerView;
4 | import android.view.View;
5 | import android.webkit.WebView;
6 | import android.widget.EditText;
7 |
8 | import org.hamcrest.Description;
9 | import org.hamcrest.Matcher;
10 | import org.hamcrest.TypeSafeMatcher;
11 |
12 | /**
13 | * Created by andy on 9/6/14.
14 | */
15 | public class CustomMatchers {
16 |
17 | public static Matcher hasData() {
18 | return new TypeSafeMatcher() {
19 |
20 | @Override
21 | public void describeTo(Description description) {
22 | }
23 |
24 | @Override
25 | public boolean matchesSafely(View view) {
26 | if (!(view instanceof RecyclerView)) {
27 | return false;
28 | }
29 | return ((RecyclerView) view).getAdapter().getItemCount() > 0;
30 | }
31 | };
32 | }
33 |
34 | public static Matcher showsUrl(final String url) {
35 | return new TypeSafeMatcher() {
36 |
37 | @Override
38 | public void describeTo(Description description) {
39 | }
40 |
41 | @Override
42 | public boolean matchesSafely(View view) {
43 | if (!(view instanceof WebView)) {
44 | return false;
45 | }
46 | WebView webView = (WebView) view;
47 | return webView.getUrl().contains(url);
48 | }
49 | };
50 | }
51 |
52 | public static Matcher withError(final String expected) {
53 | return new TypeSafeMatcher() {
54 |
55 | @Override
56 | public boolean matchesSafely(View view) {
57 | if (!(view instanceof EditText)) {
58 | return false;
59 | }
60 | EditText editText = (EditText) view;
61 | return editText.getError().toString().equals(expected);
62 | }
63 |
64 | @Override
65 | public void describeTo(Description description) {
66 |
67 | }
68 | };
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/org/andydyer/androidtestdemo/test/CustomViewActions.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.test;
2 |
3 | import android.support.test.espresso.UiController;
4 | import android.support.test.espresso.ViewAction;
5 | import android.view.View;
6 |
7 | import org.andydyer.androidtestdemo.R;
8 | import org.hamcrest.Matcher;
9 |
10 | /**
11 | * Created by andy on 1/10/15.
12 | */
13 | public class CustomViewActions {
14 |
15 | public static ViewAction clickEventItemAvatar() {
16 | return new ViewAction() {
17 | @Override
18 | public Matcher getConstraints() {
19 | return null;
20 | }
21 |
22 | @Override
23 | public String getDescription() {
24 | return null;
25 | }
26 |
27 | @Override
28 | public void perform(UiController uiController, View view) {
29 | view.findViewById(R.id.event_list_item_avatar).callOnClick();
30 | }
31 | };
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/app/src/debug/java/org/andydyer/androidtestdemo/Graph.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo;
2 |
3 | import org.andydyer.androidtestdemo.api.DebugApiServiceModule;
4 | import org.andydyer.androidtestdemo.ui.LoginActivity;
5 | import org.andydyer.androidtestdemo.ui.fragments.EventListFragment;
6 |
7 | import javax.inject.Singleton;
8 |
9 | import dagger.Component;
10 |
11 | /**
12 | * Created by andy on 1/15/15.
13 | */
14 | @Singleton
15 | @Component(modules = {DebugApiServiceModule.class})
16 | public interface Graph {
17 |
18 | // In a real app, we'd minimize the number of inject overloads by using base classes for fragments, activities, etc.
19 | void inject(InjectedActivityTest activity);
20 | void inject(InjectedInstrumentationTest testCase);
21 | void inject(LoginActivity activity);
22 | void inject(EventListFragment fragment);
23 |
24 | public final static class Initializer {
25 | public static Graph init(boolean mockMode) {
26 | return Dagger_Graph.builder()
27 | .debugApiServiceModule(new DebugApiServiceModule(mockMode))
28 | .build();
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/debug/java/org/andydyer/androidtestdemo/InjectedActivityTest.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo;
2 |
3 | import android.test.ActivityInstrumentationTestCase2;
4 |
5 | import org.andydyer.androidtestdemo.api.AuthenticationService;
6 |
7 | import javax.inject.Inject;
8 |
9 | /**
10 | * Created by andy on 1/16/15.
11 | */
12 | public class InjectedActivityTest extends ActivityInstrumentationTestCase2 {
13 |
14 | @Inject AuthenticationService authenticationService;
15 |
16 | public InjectedActivityTest(Class activityClass) { super(activityClass); }
17 |
18 | @Override
19 | protected void setUp() throws Exception {
20 | super.setUp();
21 | DemoApplication.getInstance().setMockMode(true);
22 | DemoApplication.getInstance().getGraph().inject(this);
23 | }
24 |
25 | @Override
26 | protected void tearDown() throws Exception {
27 | DemoApplication.getInstance().setMockMode(false);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/app/src/debug/java/org/andydyer/androidtestdemo/InjectedInstrumentationTest.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo;
2 |
3 | import android.test.InstrumentationTestCase;
4 |
5 | import org.andydyer.androidtestdemo.api.ApiService;
6 |
7 | import javax.inject.Inject;
8 |
9 | import lombok.Getter;
10 |
11 | /**
12 | * Created by andy on 1/16/15.
13 | */
14 | public class InjectedInstrumentationTest extends InstrumentationTestCase {
15 |
16 | @Inject @Getter ApiService apiService;
17 |
18 | @Override
19 | protected void setUp() throws Exception {
20 | DemoApplication.getInstance().setMockMode(true);
21 | DemoApplication.getInstance().getGraph().inject(this);
22 | }
23 |
24 | @Override
25 | protected void tearDown() throws Exception {
26 | DemoApplication.getInstance().setMockMode(false);
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/debug/java/org/andydyer/androidtestdemo/api/DebugApiServiceModule.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import com.google.gson.FieldNamingPolicy;
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 |
7 | import javax.inject.Singleton;
8 |
9 | import dagger.Module;
10 | import dagger.Provides;
11 | import retrofit.RestAdapter;
12 | import retrofit.android.AndroidLog;
13 | import retrofit.converter.GsonConverter;
14 |
15 | import static org.mockito.Mockito.mock;
16 |
17 | /**
18 | * Created by andy on 1/15/15.
19 | */
20 | @Module
21 | public final class DebugApiServiceModule {
22 |
23 | private final boolean mockMode;
24 |
25 | public DebugApiServiceModule(boolean provideMocks) {
26 | mockMode = provideMocks;
27 | }
28 |
29 | @Provides @Singleton
30 | public ApiService provideApiService() {
31 | if(mockMode) {
32 | return new MockApiService();
33 | }
34 | else {
35 | Gson gson = new GsonBuilder()
36 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
37 | .create();
38 | return new RestAdapter.Builder()
39 | .setEndpoint(ApiService.API_URL)
40 | .setConverter(new GsonConverter(gson))
41 | .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("API"))
42 | .build()
43 | .create(ApiService.class);
44 | }
45 | }
46 |
47 | @Provides @Singleton
48 | public AuthenticationService provideAuthenticationService() {
49 | if(mockMode) {
50 | return mock(AuthenticationService.class); // not implemented
51 | }
52 | else {
53 | return null; // not implemented
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/debug/java/org/andydyer/androidtestdemo/api/MockApiService.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import com.google.gson.FieldNamingPolicy;
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 |
7 | import org.apache.commons.io.IOUtils;
8 |
9 | import java.io.BufferedReader;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.InputStreamReader;
13 | import java.util.Collections;
14 |
15 | import retrofit.Callback;
16 | import retrofit.client.Response;
17 | import retrofit.http.Path;
18 | import retrofit.mime.TypedByteArray;
19 |
20 | /**
21 | * Implement ApiService interface to return test data from JSON files and avoid making network requests.
22 | */
23 | public class MockApiService implements ApiService {
24 |
25 | private static final int HTTP_OK_STATUS = 200;
26 |
27 | private static final String EVENTS_RESPONSE_FILE = "events_response.json";
28 |
29 | @Override
30 | public void getEvents(@Path("organization") String organization, Callback callback) {
31 | try {
32 | String json = readFileFromAssets(EVENTS_RESPONSE_FILE);
33 | Response response = getMockResponse(HTTP_OK_STATUS, json);
34 | Events events = getMockResponseData(json, Events.class);
35 | callback.success(events, response);
36 | } catch (IOException e) {
37 | e.printStackTrace();
38 | }
39 | }
40 |
41 | private String readFileFromAssets(String fileName) throws IOException {
42 | InputStream stream = getClass().getClassLoader().getResourceAsStream("assets/" + fileName);
43 | BufferedReader reader = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
44 | return IOUtils.toString(reader);
45 | }
46 |
47 | private T getMockResponseData(String json, Class klass) {
48 | Gson gson = new GsonBuilder()
49 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
50 | .create();
51 | return gson.fromJson(json, klass);
52 | }
53 |
54 | private Response getMockResponse(int httpCode, String json) throws IOException {
55 | return new Response("url", httpCode, "", Collections.EMPTY_LIST,
56 | new TypedByteArray("application/json", json.getBytes()));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/DemoApplication.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo;
2 |
3 | import android.app.Application;
4 |
5 | import lombok.Getter;
6 |
7 | /**
8 | * Created by andy on 8/23/14.
9 | */
10 | public class DemoApplication extends Application {
11 |
12 | @Getter Graph graph;
13 |
14 | @Getter static DemoApplication instance;
15 |
16 | @Override
17 | public void onCreate() {
18 | super.onCreate();
19 | instance = this;
20 | graph = Graph.Initializer.init(false);
21 | }
22 |
23 | public void setMockMode(boolean useMock) {
24 | graph = Graph.Initializer.init(useMock);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/api/Actor.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import lombok.Getter;
4 |
5 | /**
6 | * Created by andy on 8/23/14.
7 | */
8 | public class Actor {
9 |
10 | @Getter long id;
11 | @Getter String login;
12 | @Getter String gravatarId;
13 | @Getter String url;
14 | @Getter String avatarUrl;
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/api/ApiService.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import retrofit.Callback;
4 | import retrofit.http.GET;
5 | import retrofit.http.Path;
6 |
7 | /**
8 | * Created by andy on 8/23/14.
9 | */
10 | public interface ApiService {
11 |
12 | public static final String API_URL = "https://api.github.com/";
13 |
14 | @GET("/orgs/{organization}/events")
15 | public void getEvents(@Path("organization") String organization, Callback callback);
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/api/ApiServiceModule.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import com.google.gson.FieldNamingPolicy;
4 | import com.google.gson.Gson;
5 | import com.google.gson.GsonBuilder;
6 |
7 | import org.andydyer.androidtestdemo.ui.fragments.EventListFragment;
8 | import org.andydyer.androidtestdemo.ui.LoginActivity;
9 |
10 | import javax.inject.Singleton;
11 |
12 | import dagger.Module;
13 | import dagger.Provides;
14 | import retrofit.RestAdapter;
15 | import retrofit.android.AndroidLog;
16 | import retrofit.converter.GsonConverter;
17 |
18 | /**
19 | * Created by andy on 8/23/14.
20 | */
21 | @Module
22 | public final class ApiServiceModule {
23 |
24 | @Provides @Singleton
25 | public ApiService provideApiService() {
26 | Gson gson = new GsonBuilder()
27 | .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
28 | .create();
29 | return new RestAdapter.Builder()
30 | .setEndpoint(ApiService.API_URL)
31 | .setConverter(new GsonConverter(gson))
32 | .setLogLevel(RestAdapter.LogLevel.FULL).setLog(new AndroidLog("API"))
33 | .build()
34 | .create(ApiService.class);
35 | }
36 |
37 | @Provides @Singleton
38 | public AuthenticationService provideAuthenticationService() {
39 | return null; // not implemented
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/api/AuthenticationService.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import retrofit.Callback;
4 | import retrofit.http.POST;
5 | import retrofit.http.Query;
6 |
7 | /**
8 | * Created by andy on 9/7/14.
9 | */
10 | public interface AuthenticationService {
11 |
12 | // Authentication is not implemented in this project. this endpoint is here to demonstrate
13 | // mocking a response for testing.
14 | @POST("/login/oauth/access_token")
15 | public void login(@Query("client_id") String clientId, @Query("client_secret") String clientSecret,
16 | @Query("code") String code, Callback callback);
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/api/Event.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import android.text.format.DateUtils;
4 |
5 | import java.text.SimpleDateFormat;
6 | import java.util.GregorianCalendar;
7 | import java.util.TimeZone;
8 |
9 | import lombok.Getter;
10 |
11 | /**
12 | * Created by andy on 8/23/14.
13 | */
14 | public class Event {
15 |
16 | @Getter long id;
17 | @Getter String type;
18 | @Getter Actor actor;
19 | @Getter Repo repo;
20 | @Getter String createdAt;
21 |
22 | public String getCreatedAtRelativeTime() {
23 | try {
24 | TimeZone utc = TimeZone.getTimeZone("UTC");
25 | SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
26 | format.setTimeZone(utc);
27 | GregorianCalendar calendar = new GregorianCalendar(utc);
28 | calendar.setTime(format.parse(createdAt));
29 | return DateUtils.getRelativeTimeSpanString(calendar.getTimeInMillis(),
30 | System.currentTimeMillis(), DateUtils.SECOND_IN_MILLIS).toString();
31 | } catch (Exception e) {
32 | return createdAt;
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/api/Events.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import java.util.ArrayList;
4 |
5 | /**
6 | * Created by andy on 8/31/14.
7 | */
8 | public class Events extends ArrayList {
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/api/Repo.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.api;
2 |
3 | import lombok.Getter;
4 |
5 | /**
6 | * Created by andy on 8/23/14.
7 | */
8 | public class Repo {
9 |
10 | @Getter long id;
11 | @Getter String name;
12 | @Getter String url;
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/ui/LoginActivity.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.ui;
2 |
3 | import android.app.LoaderManager.LoaderCallbacks;
4 | import android.content.CursorLoader;
5 | import android.content.Intent;
6 | import android.content.Loader;
7 | import android.database.Cursor;
8 | import android.net.Uri;
9 | import android.os.Bundle;
10 | import android.provider.ContactsContract;
11 | import android.support.v7.app.ActionBarActivity;
12 | import android.text.TextUtils;
13 | import android.view.KeyEvent;
14 | import android.view.View;
15 | import android.view.View.OnClickListener;
16 | import android.view.inputmethod.EditorInfo;
17 | import android.widget.ArrayAdapter;
18 | import android.widget.AutoCompleteTextView;
19 | import android.widget.Button;
20 | import android.widget.EditText;
21 | import android.widget.TextView;
22 |
23 | import org.andydyer.androidtestdemo.DemoApplication;
24 | import org.andydyer.androidtestdemo.R;
25 | import org.andydyer.androidtestdemo.api.AuthenticationService;
26 |
27 | import java.util.ArrayList;
28 | import java.util.List;
29 |
30 | import javax.inject.Inject;
31 |
32 | import retrofit.Callback;
33 | import retrofit.RetrofitError;
34 | import retrofit.client.Response;
35 |
36 |
37 | /**
38 | * A login screen that offers login via email/password.
39 |
40 | */
41 | public class LoginActivity extends ActionBarActivity implements LoaderCallbacks, Callback {
42 |
43 | @Inject AuthenticationService authenticationService;
44 |
45 | // UI references.
46 | private AutoCompleteTextView mEmailView;
47 | private EditText mPasswordView;
48 | private View mProgressView;
49 | private View mLoginFormView;
50 |
51 | @Override
52 | protected void onCreate(Bundle savedInstanceState) {
53 | super.onCreate(savedInstanceState);
54 | setContentView(R.layout.activity_login);
55 | DemoApplication.getInstance().getGraph().inject(this);
56 |
57 | // Set up the login form.
58 | mEmailView = (AutoCompleteTextView) findViewById(R.id.email);
59 | populateAutoComplete();
60 |
61 | mPasswordView = (EditText) findViewById(R.id.password);
62 | mPasswordView.setOnEditorActionListener(new TextView.OnEditorActionListener() {
63 | @Override
64 | public boolean onEditorAction(TextView textView, int id, KeyEvent keyEvent) {
65 | if (id == R.id.login || id == EditorInfo.IME_NULL) {
66 | attemptLogin();
67 | return true;
68 | }
69 | return false;
70 | }
71 | });
72 |
73 | Button mEmailSignInButton = (Button) findViewById(R.id.email_sign_in_button);
74 | mEmailSignInButton.setOnClickListener(new OnClickListener() {
75 | @Override
76 | public void onClick(View view) {
77 | attemptLogin();
78 | }
79 | });
80 |
81 | mLoginFormView = findViewById(R.id.login_form);
82 | mProgressView = findViewById(R.id.login_progress);
83 | }
84 |
85 | private void populateAutoComplete() {
86 | getLoaderManager().initLoader(0, null, this);
87 | }
88 |
89 |
90 | /**
91 | * Attempts to sign in or register the account specified by the login form.
92 | * If there are form errors (invalid email, missing fields, etc.), the
93 | * errors are presented and no actual login attempt is made.
94 | */
95 | public void attemptLogin() {
96 | // Reset errors.
97 | mEmailView.setError(null);
98 | mPasswordView.setError(null);
99 |
100 | // Store values at the time of the login attempt.
101 | String email = mEmailView.getText().toString();
102 | String password = mPasswordView.getText().toString();
103 |
104 | boolean cancel = false;
105 | View focusView = null;
106 |
107 |
108 | // Check for a valid password, if the user entered one.
109 | if (!TextUtils.isEmpty(password) && !isPasswordValid(password)) {
110 | mPasswordView.setError(getString(R.string.error_invalid_password));
111 | focusView = mPasswordView;
112 | cancel = true;
113 | }
114 |
115 | // Check for a valid email address.
116 | if (TextUtils.isEmpty(email)) {
117 | mEmailView.setError(getString(R.string.error_field_required));
118 | focusView = mEmailView;
119 | cancel = true;
120 | } else if (!isEmailValid(email)) {
121 | mEmailView.setError(getString(R.string.error_invalid_email));
122 | focusView = mEmailView;
123 | cancel = true;
124 | }
125 |
126 | if (cancel) {
127 | // There was an error; don't attempt login and focus the first
128 | // form field with an error.
129 | focusView.requestFocus();
130 | } else {
131 | // Kick off a background task to perform the user login attempt.
132 | authenticationService.login("test", "test", "test", this);
133 | }
134 | }
135 | private boolean isEmailValid(String email) {
136 | //TODO: Replace this with your own logic
137 | return email.contains("@");
138 | }
139 |
140 | private boolean isPasswordValid(String password) {
141 | //TODO: Replace this with your own logic
142 | return password.length() > 4;
143 | }
144 |
145 | @Override
146 | public Loader onCreateLoader(int i, Bundle bundle) {
147 | return new CursorLoader(this,
148 | // Retrieve data rows for the device user's 'profile' contact.
149 | Uri.withAppendedPath(ContactsContract.Profile.CONTENT_URI,
150 | ContactsContract.Contacts.Data.CONTENT_DIRECTORY), ProfileQuery.PROJECTION,
151 |
152 | // Select only email addresses.
153 | ContactsContract.Contacts.Data.MIMETYPE +
154 | " = ?", new String[]{ContactsContract.CommonDataKinds.Email
155 | .CONTENT_ITEM_TYPE},
156 |
157 | // Show primary email addresses first. Note that there won't be
158 | // a primary email address if the user hasn't specified one.
159 | ContactsContract.Contacts.Data.IS_PRIMARY + " DESC");
160 | }
161 |
162 | @Override
163 | public void onLoadFinished(Loader cursorLoader, Cursor cursor) {
164 | List emails = new ArrayList();
165 | cursor.moveToFirst();
166 | while (!cursor.isAfterLast()) {
167 | emails.add(cursor.getString(ProfileQuery.ADDRESS));
168 | cursor.moveToNext();
169 | }
170 |
171 | addEmailsToAutoComplete(emails);
172 | }
173 |
174 | @Override
175 | public void onLoaderReset(Loader cursorLoader) {
176 |
177 | }
178 |
179 | private interface ProfileQuery {
180 | String[] PROJECTION = {
181 | ContactsContract.CommonDataKinds.Email.ADDRESS,
182 | ContactsContract.CommonDataKinds.Email.IS_PRIMARY,
183 | };
184 |
185 | int ADDRESS = 0;
186 | int IS_PRIMARY = 1;
187 | }
188 |
189 |
190 | private void addEmailsToAutoComplete(List emailAddressCollection) {
191 | //Create adapter to tell the AutoCompleteTextView what to show in its dropdown list.
192 | ArrayAdapter adapter =
193 | new ArrayAdapter(LoginActivity.this,
194 | android.R.layout.simple_dropdown_item_1line, emailAddressCollection);
195 |
196 | mEmailView.setAdapter(adapter);
197 | }
198 |
199 | @Override
200 | public void success(Boolean success, Response response) {
201 | if (success) {
202 | startActivity(new Intent(this, MainActivity.class));
203 | finish();
204 | } else {
205 | mPasswordView.setError(getString(R.string.error_incorrect_password));
206 | mPasswordView.requestFocus();
207 | }
208 | }
209 |
210 | @Override
211 | public void failure(RetrofitError error) {
212 | mPasswordView.setError(getString(R.string.error_incorrect_password));
213 | mPasswordView.requestFocus();
214 | }
215 | }
216 |
217 |
218 |
219 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/ui/MainActivity.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.ui;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.ActionBarActivity;
5 | import android.view.Menu;
6 | import android.view.MenuItem;
7 |
8 | import org.andydyer.androidtestdemo.R;
9 | import org.andydyer.androidtestdemo.ui.fragments.EventListFragment;
10 |
11 | public class MainActivity extends ActionBarActivity {
12 |
13 | @Override
14 | protected void onCreate(Bundle savedInstanceState) {
15 | super.onCreate(savedInstanceState);
16 | setContentView(R.layout.activity_main);
17 |
18 | if (savedInstanceState == null) {
19 | getSupportFragmentManager().beginTransaction()
20 | .replace(R.id.container, new EventListFragment())
21 | .commit();
22 | }
23 | }
24 |
25 | @Override
26 | public boolean onCreateOptionsMenu(Menu menu) {
27 | // Inflate the menu; this adds items to the action bar if it is present.
28 | getMenuInflater().inflate(R.menu.main, menu);
29 | return true;
30 | }
31 |
32 | @Override
33 | public boolean onOptionsItemSelected(MenuItem item) {
34 | // Handle action bar item clicks here. The action bar will
35 | // automatically handle clicks on the Home/Up button, so long
36 | // as you specify a parent activity in AndroidManifest.xml.
37 | int id = item.getItemId();
38 | if (id == R.id.action_settings) {
39 | return true;
40 | }
41 | return super.onOptionsItemSelected(item);
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/ui/WebViewActivity.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.ui;
2 |
3 | import android.os.Bundle;
4 | import android.support.v7.app.ActionBarActivity;
5 | import android.view.MenuItem;
6 | import android.webkit.WebView;
7 | import android.webkit.WebViewClient;
8 |
9 | import org.andydyer.androidtestdemo.R;
10 |
11 | import butterknife.ButterKnife;
12 | import butterknife.InjectView;
13 |
14 | /**
15 | * Created by andy on 8/23/14.
16 | */
17 | public class WebViewActivity extends ActionBarActivity {
18 |
19 | public static final String EXTRA_URL = "url";
20 | public static final String EXTRA_TITLE = "title";
21 |
22 | @InjectView(R.id.webview) WebView webview;
23 |
24 | @Override
25 | protected void onCreate(Bundle savedInstanceState) {
26 | super.onCreate(savedInstanceState);
27 | setContentView(R.layout.activity_webview);
28 | ButterKnife.inject(this);
29 | getSupportActionBar().setTitle(getIntent().getStringExtra(EXTRA_TITLE));
30 | getSupportActionBar().setDisplayHomeAsUpEnabled(true);
31 | loadUrl();
32 | }
33 |
34 | private void loadUrl() {
35 | String url = getIntent().getStringExtra(EXTRA_URL);
36 | webview.setWebViewClient(new WebViewClient() {
37 | public boolean shouldOverrideUrlLoading(WebView view, String url) {
38 | return false;
39 | }
40 | });
41 | webview.loadUrl(url);
42 | }
43 |
44 | @Override
45 | public boolean onOptionsItemSelected(MenuItem item) {
46 | switch (item.getItemId()) {
47 | case android.R.id.home:
48 | finish();
49 | return true;
50 | default:
51 | return super.onOptionsItemSelected(item);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/ui/fragments/EventListFragment.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.ui.fragments;
2 |
3 | import android.content.Intent;
4 | import android.os.Bundle;
5 | import android.support.annotation.Nullable;
6 | import android.support.v4.app.Fragment;
7 | import android.support.v7.widget.DefaultItemAnimator;
8 | import android.support.v7.widget.LinearLayoutManager;
9 | import android.support.v7.widget.RecyclerView;
10 | import android.view.LayoutInflater;
11 | import android.view.View;
12 | import android.view.ViewGroup;
13 | import android.widget.ImageView;
14 | import android.widget.TextView;
15 | import android.widget.Toast;
16 |
17 | import com.squareup.picasso.Picasso;
18 |
19 | import org.andydyer.androidtestdemo.DemoApplication;
20 | import org.andydyer.androidtestdemo.R;
21 | import org.andydyer.androidtestdemo.ui.WebViewActivity;
22 | import org.andydyer.androidtestdemo.api.ApiService;
23 | import org.andydyer.androidtestdemo.api.Event;
24 | import org.andydyer.androidtestdemo.api.Events;
25 | import org.andydyer.androidtestdemo.ui.widgets.SimpleDividerItemDecoration;
26 |
27 | import java.util.ArrayList;
28 | import java.util.List;
29 |
30 | import javax.inject.Inject;
31 |
32 | import butterknife.ButterKnife;
33 | import butterknife.InjectView;
34 | import retrofit.Callback;
35 | import retrofit.RetrofitError;
36 | import retrofit.client.Response;
37 |
38 | /**
39 | * Created by andy on 8/23/14.
40 | */
41 | public class EventListFragment extends Fragment implements Callback {
42 |
43 | @Inject ApiService apiService;
44 |
45 | @InjectView(android.R.id.list) RecyclerView recyclerView;
46 |
47 | private EventsAdapter adapter;
48 |
49 | @Override
50 | public void onCreate(Bundle savedInstanceState) {
51 | super.onCreate(savedInstanceState);
52 | setRetainInstance(true);
53 | }
54 |
55 | @Override
56 | public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
57 | View view = inflater.inflate(R.layout.fragment_event_list, container, false);
58 | ButterKnife.inject(this, view);
59 |
60 | if(savedInstanceState == null) {
61 | adapter = new EventsAdapter();
62 | }
63 |
64 | recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
65 | recyclerView.setItemAnimator(new DefaultItemAnimator());
66 | recyclerView.addItemDecoration(new SimpleDividerItemDecoration(getActivity()));
67 | recyclerView.setAdapter(adapter);
68 |
69 | return view;
70 | }
71 |
72 | @Override
73 | public void onViewCreated(View view, Bundle savedInstanceState) {
74 | DemoApplication.getInstance().getGraph().inject(this);
75 |
76 | if(savedInstanceState == null) {
77 | //TODO: Show progress spinner while loading
78 | apiService.getEvents("google", this);
79 | }
80 | }
81 |
82 | @Override
83 | public void success(Events events, Response response) {
84 | adapter.addAll(events);
85 | }
86 |
87 | @Override
88 | public void failure(RetrofitError error) {
89 | Toast.makeText(getActivity(), R.string.unable_to_fech_events, Toast.LENGTH_SHORT).show();
90 | }
91 |
92 | private class EventsAdapter extends RecyclerView.Adapter {
93 |
94 | private List events = new ArrayList<>();
95 |
96 | @Override
97 | public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
98 | View view = LayoutInflater.from(parent.getContext())
99 | .inflate(R.layout.event_list_item, parent, false);
100 | return new ViewHolder(view);
101 | }
102 |
103 | @Override
104 | public void onBindViewHolder(ViewHolder holder, int position) {
105 | final Event event = events.get(position);
106 | String line1 = String.format("%s by %s", event.getType(), event.getActor().getLogin());
107 | holder.textLine1.setText(line1);
108 | holder.textLine2.setText(event.getCreatedAtRelativeTime());
109 | Picasso.with(getActivity()).load(event.getActor().getAvatarUrl()).into(holder.avatar);
110 | holder.avatar.setOnClickListener(new View.OnClickListener() {
111 | @Override
112 | public void onClick(View v) {
113 | launchWebViewActivity(event.getActor().getLogin());
114 | }
115 | });
116 | holder.itemView.setOnClickListener(new View.OnClickListener() {
117 | @Override
118 | public void onClick(View v) {
119 | launchWebViewActivity(event.getRepo().getName());
120 | }
121 | });
122 | }
123 |
124 | @Override
125 | public int getItemCount() {
126 | return events.size();
127 | }
128 |
129 | public void addAll(List items) {
130 | int startPosition = events.size();
131 | events.addAll(items);
132 | notifyItemRangeInserted(startPosition, items.size());
133 | }
134 | }
135 |
136 | public class ViewHolder extends RecyclerView.ViewHolder {
137 |
138 | @InjectView(R.id.event_list_item_avatar) ImageView avatar;
139 | @InjectView(R.id.event_list_item_text_line1) TextView textLine1;
140 | @InjectView(R.id.event_list_item_text_line2) TextView textLine2;
141 |
142 | public ViewHolder(View view) {
143 | super(view);
144 | ButterKnife.inject(this, view);
145 | }
146 | }
147 |
148 | private void launchWebViewActivity(String path) {
149 | String url = String.format("http://github.com/%s", path);
150 | Intent intent = new Intent(getActivity(), WebViewActivity.class);
151 | intent.putExtra(WebViewActivity.EXTRA_TITLE, path);
152 | intent.putExtra(WebViewActivity.EXTRA_URL, url);
153 | startActivity(intent);
154 | }
155 | }
156 |
--------------------------------------------------------------------------------
/app/src/main/java/org/andydyer/androidtestdemo/ui/widgets/SimpleDividerItemDecoration.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo.ui.widgets;
2 |
3 | import android.content.Context;
4 | import android.graphics.Canvas;
5 | import android.graphics.drawable.Drawable;
6 | import android.support.v7.widget.RecyclerView;
7 | import android.view.View;
8 |
9 | import org.andydyer.androidtestdemo.R;
10 |
11 | /**
12 | * https://gist.github.com/polbins/e37206fbc444207c0e92
13 | */
14 | public class SimpleDividerItemDecoration extends RecyclerView.ItemDecoration {
15 | private Drawable mDivider;
16 |
17 | public SimpleDividerItemDecoration(Context context) {
18 | mDivider = context.getResources().getDrawable(R.drawable.line_divider);
19 | }
20 |
21 | @Override
22 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
23 | int left = parent.getPaddingLeft();
24 | int right = parent.getWidth() - parent.getPaddingRight();
25 |
26 | int childCount = parent.getChildCount();
27 | for (int i = 0; i < childCount; i++) {
28 | View child = parent.getChildAt(i);
29 |
30 | RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
31 |
32 | int top = child.getBottom() + params.bottomMargin;
33 | int bottom = top + mDivider.getIntrinsicHeight();
34 |
35 | mDivider.setBounds(left, top, right, bottom);
36 | mDivider.draw(c);
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/line_divider.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_login.xml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
21 |
22 |
27 |
28 |
33 |
34 |
42 |
43 |
54 |
55 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_webview.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/event_list_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
13 |
14 |
20 |
21 |
28 |
29 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_event_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 64dp
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #4CAF50
4 | #388E3C
5 | #CDDC39
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 65dp
6 | 10dp
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Android Test Demo
4 | Settings
5 | Unable to fetch events
6 | Sign in
7 | Email
8 | Password (optional)
9 | Sign in or register
10 | Sign in
11 | This email address is invalid
12 | This password is too short
13 | This password is incorrect
14 | This field is required
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/release/java/org/andydyer/androidtestdemo/Graph.java:
--------------------------------------------------------------------------------
1 | package org.andydyer.androidtestdemo;
2 |
3 | import android.app.Activity;
4 | import android.support.v4.app.Fragment;
5 |
6 | import org.andydyer.androidtestdemo.api.ApiServiceModule;
7 |
8 | import javax.inject.Singleton;
9 |
10 | import dagger.Component;
11 |
12 | /**
13 | * Created by andy on 1/15/15.
14 | */
15 | @Singleton
16 | @Component(modules = {ApiServiceModule.class})
17 | public interface Graph {
18 |
19 | void inject(Activity activity);
20 | void inject(Fragment fragment);
21 |
22 | public final static class Initializer {
23 | public static Graph init(boolean mockMode) {
24 | return Dagger_Graph.builder()
25 | .build();
26 | }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | jcenter()
6 | }
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:1.0.0'
9 |
10 | // NOTE: Do not place your application dependencies here; they belong
11 | // in the individual module build.gradle files
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | jcenter()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Settings specified in this file will override any Gradle settings
5 | # configured through the IDE.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Jan 10 13:09:01 CST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.2.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # For Cygwin, ensure paths are in UNIX format before anything is touched.
46 | if $cygwin ; then
47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
48 | fi
49 |
50 | # Attempt to set APP_HOME
51 | # Resolve links: $0 may be a link
52 | PRG="$0"
53 | # Need this for relative symlinks.
54 | while [ -h "$PRG" ] ; do
55 | ls=`ls -ld "$PRG"`
56 | link=`expr "$ls" : '.*-> \(.*\)$'`
57 | if expr "$link" : '/.*' > /dev/null; then
58 | PRG="$link"
59 | else
60 | PRG=`dirname "$PRG"`"/$link"
61 | fi
62 | done
63 | SAVED="`pwd`"
64 | cd "`dirname \"$PRG\"`/" >&-
65 | APP_HOME="`pwd -P`"
66 | cd "$SAVED" >&-
67 |
68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
69 |
70 | # Determine the Java command to use to start the JVM.
71 | if [ -n "$JAVA_HOME" ] ; then
72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
73 | # IBM's JDK on AIX uses strange locations for the executables
74 | JAVACMD="$JAVA_HOME/jre/sh/java"
75 | else
76 | JAVACMD="$JAVA_HOME/bin/java"
77 | fi
78 | if [ ! -x "$JAVACMD" ] ; then
79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
80 |
81 | Please set the JAVA_HOME variable in your environment to match the
82 | location of your Java installation."
83 | fi
84 | else
85 | JAVACMD="java"
86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
87 |
88 | Please set the JAVA_HOME variable in your environment to match the
89 | location of your Java installation."
90 | fi
91 |
92 | # Increase the maximum file descriptors if we can.
93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
94 | MAX_FD_LIMIT=`ulimit -H -n`
95 | if [ $? -eq 0 ] ; then
96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
97 | MAX_FD="$MAX_FD_LIMIT"
98 | fi
99 | ulimit -n $MAX_FD
100 | if [ $? -ne 0 ] ; then
101 | warn "Could not set maximum file descriptor limit: $MAX_FD"
102 | fi
103 | else
104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
105 | fi
106 | fi
107 |
108 | # For Darwin, add options to specify how the application appears in the dock
109 | if $darwin; then
110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
111 | fi
112 |
113 | # For Cygwin, switch paths to Windows format before running java
114 | if $cygwin ; then
115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
158 | function splitJvmOpts() {
159 | JVM_OPTS=("$@")
160 | }
161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
163 |
164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
165 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/presentation/Big Android BBQ 2014 - Automated Testing for Modern Android Applications.md:
--------------------------------------------------------------------------------
1 | ## Automated Testing for _**Modern**_ Android Applications
2 |
3 | ---
4 |
5 | ## Andy Dyer
6 | ### _**+AndrewDyer || @dammitandy**_
7 |
8 | ---
9 |
10 | 
11 |
12 | ---
13 |
14 | ## Music Library
15 | 
16 |
17 | ---
18 |
19 | ## Wrist Presenter
20 | 
21 |
22 | ---
23 |
24 | ## Records,
25 | ## Coffee & Beer
26 |
27 | ---
28 |
29 | >"Ich lerne Deutsch."
30 |
31 | 
32 |
33 | ---
34 |
35 | ### Making Android apps testable
36 |
37 | ---
38 |
39 | ### Dynamic languages
40 | ### _**"Tests shouldn't drive implementation.**"_
41 | 
42 |
43 | ---
44 |
45 | ### Static languages
46 | ### _**"You know nothing."**_
47 | 
48 |
49 | ---
50 |
51 | ## Making Android apps testable
52 |
53 | - Dependency injection
54 | - Mocking & stubbing
55 | - Unit & integration tests
56 |
57 | ---
58 |
59 | ### Dependency injection
60 |
61 | ---
62 |
63 | ## Dependency injection
64 |
65 | - Decouples application components
66 | - Classes receive dependencies, don’t have to know where to find them or how to create them
67 | - Makes it easy to swap components for mocks in tests
68 |
69 | ---
70 |
71 | ### Basic dependency injection
72 |
73 | ```java
74 | public class Beer {
75 | Water water;
76 | Barley barley;
77 | Hops hops;
78 |
79 | public Beer(Water water, Barley barley, Hops hops) {
80 | this.water = water;
81 | this.barley = barley;
82 | this.hops = hops;
83 | }
84 | }
85 | ```
86 |
87 | ---
88 |
89 | ## Dagger
90 | ### _**A Java dependency injection library**_
91 |
92 | ---
93 |
94 | ## Dagger
95 |
96 | - Define dependencies at compile time to avoid reflection at runtime
97 | - Compiler validates modules and injections
98 | - Jake Wharton “Architecting Android Applications with Dagger" _*goo.gl/JIM7KI*_
99 |
100 | _*square.github.io/dagger*_
101 |
102 | ---
103 |
104 | ## Dagger Modules
105 |
106 | ```java
107 | @Module(injects = {MyActivity.class, MyFragment.class})
108 | public class MyModule {
109 | @Provides @Singleton
110 | public MyService provideMyService() {
111 | return new MyService();
112 | }
113 | }
114 | ```
115 |
116 | ---
117 |
118 | ## Dagger Object Graph
119 |
120 | ```java
121 | public class MyApplication extends Application {
122 | private dagger.ObjectGraph objectGraph;
123 |
124 | @Getter static MyApplication instance;
125 |
126 | @Override
127 | public void onCreate() {
128 | super.onCreate();
129 | instance = this;
130 | objectGraph = dagger.ObjectGraph.create(new MyModule());
131 | }
132 |
133 | public void inject(Object dependent) {
134 | objectGraph.inject(dependent);
135 | }
136 | }
137 | ```
138 |
139 | ---
140 |
141 | ## Dagger Dependency Injection
142 |
143 | ```java
144 | public class MyFragment extends Fragment {
145 | @Inject MyService service;
146 |
147 | @Override
148 | public void onViewCreated(View view, Bundle savedInstanceState) {
149 | MyApplication.getInstance().inject(this);
150 | service.getMyData();
151 | }
152 | }
153 | ```
154 |
155 | ---
156 |
157 | ### Mocking & Stubbing
158 |
159 | ---
160 |
161 | ## Mocking & Stubbing
162 |
163 | - Substitute runtime implementation for something that can be predictably tested in isolation
164 | - Verify behavior
165 |
166 | ---
167 |
168 | ## Mockito
169 | ### _**A Java mocking library**_
170 |
171 | ---
172 |
173 | ## Mockito
174 |
175 | - Mock/stub dependencies and function return values
176 | - Inject mocks to validate behavior in tests
177 | - Use included Hamcrest matchers for clear, readable tests
178 |
179 | _*code.google.com/p/mockito/*_
180 |
181 | ---
182 |
183 | ## Using Mockito
184 |
185 | ```java
186 | MyClass mocked = mock(MyClass.class);
187 |
188 | // code to inject mock & load activity/fragment
189 | // ...
190 |
191 | verify(mocked).getMyData(anyInt(), anyString());
192 | ```
193 | ---
194 |
195 | ### Unit & Integration Tests
196 |
197 | ---
198 |
199 | >"Tests or it didn't happen."
200 |
201 | 
202 |
203 | ---
204 |
205 | ## Espresso
206 | ### _**An Android UI test library**_
207 |
208 | ---
209 |
210 | ## Espresso
211 |
212 | - Handles activity creation & state sync
213 | - Simple, concise API
214 | - Really fast!
215 |
216 | _*code.google.com/p/android-test-kit*_
217 |
218 | ---
219 |
220 | ## Using Espresso
221 |
222 | ```java
223 | public class MyActivityTest extends ActivityInstrumentationTestCase2 {
224 |
225 | public MyActivityTest() { super(MyActivity.class); }
226 |
227 | @Override
228 | protected void setUp() throws Exception {
229 | super.setUp();
230 | getActivity(); // trigger activity launch
231 | }
232 |
233 | public void testInvalidEmailShowsError() {
234 | onView(withId(R.id.email)).perform(typeText("abc"), closeSoftKeyboard());
235 | onView(withId(R.id.email_sign_in_button)).perform(click());
236 | onView(withId(R.id.email)).check(matches(withError(
237 | getActivity().getString(R.string.error_invalid_email))));
238 | }
239 | }
240 | ```
241 |
242 | ---
243 |
244 | ## Sample application overview
245 |
246 | - Retrofit API with sample requests
247 | - Dagger module
248 | - Lombok & Android Studio plugin
249 | - Login activity
250 | - Activity with list fragment to make API request and display data
251 |
252 | ---
253 |
254 | ## Haben Sie Fragen?
255 |
256 | ---
257 |
258 | # Code & Slides
259 | _github.com/abdyer/android-test-demo_
260 |
261 | 
262 |
--------------------------------------------------------------------------------
/presentation/images/alexanderplatz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/presentation/images/alexanderplatz.jpg
--------------------------------------------------------------------------------
/presentation/images/berlin_sunset.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/presentation/images/berlin_sunset.jpg
--------------------------------------------------------------------------------
/presentation/images/c1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/presentation/images/c1.png
--------------------------------------------------------------------------------
/presentation/images/jon_snow.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/presentation/images/jon_snow.jpg
--------------------------------------------------------------------------------
/presentation/images/music_library.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/presentation/images/music_library.png
--------------------------------------------------------------------------------
/presentation/images/test_source_code.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/presentation/images/test_source_code.png
--------------------------------------------------------------------------------
/presentation/images/wrist_presenter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/presentation/images/wrist_presenter.png
--------------------------------------------------------------------------------
/presentation/images/ygritte.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdyer/android-test-demo/77d5fd47c44d7527dc6b095baa87ed61438c6aff/presentation/images/ygritte.jpg
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------