Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
7cfa7c2
Created basic project configuration
92alanc Dec 4, 2018
581bbc5
Created MainActivity class and layout file
92alanc Dec 4, 2018
71af1bd
Implemented cover layout
92alanc Dec 5, 2018
fc08d68
Implemented RecyclerView news item
92alanc Dec 5, 2018
b8210b5
Reduced text size in cover headline
92alanc Dec 5, 2018
6d68fd4
Added Retrofit to dependencies
92alanc Dec 5, 2018
2cb322b
Added Gson converter
92alanc Dec 5, 2018
bc22f1a
Created basic models
92alanc Dec 5, 2018
b1b5dac
Created Product model
92alanc Dec 5, 2018
14ef6a7
Implemented product API
92alanc Dec 5, 2018
bc80003
Added lifecycle architecture components
92alanc Dec 5, 2018
e62dc7f
Implemented cache to API responses
92alanc Dec 5, 2018
0f3906d
Implemented dependency injection + repository
92alanc Dec 5, 2018
c14ed93
Implemented ArticleAdapter
92alanc Dec 5, 2018
5b9570b
Article and Section implementing Parcelable
92alanc Dec 6, 2018
765e904
Article, Image and Section implementing Parcelable
92alanc Dec 6, 2018
b75d374
MainActivity ready
92alanc Dec 6, 2018
9e55da7
Added click listener to cover article
92alanc Dec 6, 2018
e7974c6
Added instrumented test libs
92alanc Dec 6, 2018
a0de643
Implemented BaseActivityTest
92alanc Dec 6, 2018
613b3c7
Implemented MainActivityTest
92alanc Dec 6, 2018
c9147dd
Added mock API response
92alanc Dec 6, 2018
cd00324
Added MockWebServer
92alanc Dec 6, 2018
95984ed
Mocked API for tests
92alanc Dec 6, 2018
ee13c19
Set app theme to blue
92alanc Dec 6, 2018
c84dd85
Added divider to RecyclerView items
92alanc Dec 6, 2018
05f06ba
Changed app icon
92alanc Dec 6, 2018
1aff9f7
Changed MainActivity title
92alanc Dec 6, 2018
b0b6275
Implementing ArticleDetailsActivity layout
92alanc Dec 6, 2018
0ef6263
Refactored MainActivity
92alanc Dec 6, 2018
f541b02
ArticleDetailsActivity layout ready
92alanc Dec 6, 2018
44c57c9
Moved MainActivity into new package
92alanc Dec 6, 2018
1ba6aa5
Created ArticleDetailsActivity class
92alanc Dec 6, 2018
2e8ad5a
Created navigation to ArticleDetailsActivity
92alanc Dec 6, 2018
7e362d4
Linked article data to ArticleDetailsActivity
92alanc Dec 7, 2018
32a5d42
Implemented date string formatting
92alanc Dec 7, 2018
1606c16
Moved TextUtilsTest to correct package
92alanc Dec 7, 2018
d9f9dc6
Renamed HTML formatting function
92alanc Dec 7, 2018
ffbea70
Removed unused import
92alanc Dec 7, 2018
e3d034d
Updated Gradle version
92alanc Dec 7, 2018
ac5301a
Fixed null author bug
92alanc Dec 7, 2018
d69f8f6
Removed mapping to unused fields
92alanc Dec 7, 2018
3abeeaa
Updated assertion in MainActivityTest
92alanc Dec 7, 2018
2752e97
Implemented ImageHelper
92alanc Dec 7, 2018
16adcd5
Separated loadArticles function
92alanc Dec 7, 2018
f574723
Fixed cover photo's height
92alanc Dec 7, 2018
a65105d
Articles without author or text filtered out
92alanc Dec 7, 2018
24b5216
Trimmed headline photo's captions
92alanc Dec 7, 2018
67244e5
Author text no longer uppercase
92alanc Dec 7, 2018
7ce0822
Implementing ArticleDetailsActivityTest
92alanc Dec 7, 2018
ff313ef
ArticleDetailsActivityTest ready
92alanc Dec 8, 2018
0d48527
Added FIXME comment to MainActivityTest
92alanc Dec 8, 2018
f041819
Added JaCoCo
92alanc Dec 8, 2018
7027ffd
Added test case to ArticleDetailsActivity
92alanc Dec 8, 2018
b80a936
Fixed layouts
92alanc Dec 8, 2018
37d702d
Added headline photo validation
92alanc Dec 8, 2018
581e6b0
Moved activity tests to a more suitable package
92alanc Dec 8, 2018
5bebc9d
Add ready
92alanc Dec 8, 2018
c322a75
Fixed MainActivity layout
92alanc Dec 9, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.idea/
.gradle/
build/
*.iml
local.properties
*.jks
117 changes: 117 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'jacoco'

android {
compileSdkVersion 28

defaultConfig {
applicationId "com.alancamargo.desafioinfoglobo"
minSdkVersion 17
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
debug {
debuggable true
testCoverageEnabled true
}

release {
debuggable false
testCoverageEnabled false
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}

sourceSets {
androidTest.java.srcDirs += 'src/androidTest/kotlin'
main.java.srcDirs += 'src/main/kotlin'
test.java.srcDirs += 'src/test/kotlin'
main { assets.srcDirs = ['src/main/assets', 'src/androidTest/assets/'] }
}
}

jacoco {
toolVersion = '0.8.1'
}

tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
}

task deleteOldReports(type: Delete) {
delete "$project.buildDir/reports/"
}

// Run this task for test coverage reports
task jacocoTestReport(type: JacocoReport,
dependsOn: ['deleteOldReports', 'testDebugUnitTest', 'createDebugCoverageReport']) {
reports {
xml.enabled = true
html.enabled = true
}

def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*',
'**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
def debugTree = fileTree(dir: "$project.buildDir/tmp/kotlin-classes/debug", excludes: fileFilter)
def mainSrc = "$project.projectDir/src/main/kotlin"

sourceDirectories = files([mainSrc])
classDirectories = files([debugTree])
executionData = fileTree(dir: project.buildDir, includes: [
'jacoco/testDebugUnitTest.exec', 'outputs/code-coverage/connected/*coverage.ec'
])
}

dependencies {
// region Instrumented test libraries
// Android test runner
androidTestImplementation 'androidx.test:runner:1.1.1-alpha01'

// Espresso
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-intents:3.1.0'

// Kappuccino
androidTestImplementation 'br.com.concretesolutions:kappuccino:1.2.1'

// Mock web server
androidTestImplementation 'com.squareup.okhttp3:mockwebserver:3.11.0'
// endregion

// region Standard libraries
// Local .jar files
implementation fileTree(dir: 'libs', include: ['*.jar'])

// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"

// App compatibility
implementation 'androidx.appcompat:appcompat:1.1.0-alpha01'

// ConstraintLayout
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-alpha2'

// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'

// Lifecycle
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"

// Picasso
implementation 'com.squareup.picasso:picasso:2.71828'
// endregion

// region Unit test libraries
testImplementation 'junit:junit:4.12'
// endregion
}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
353 changes: 353 additions & 0 deletions app/src/androidTest/assets/fixtures/response_articles.json

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.alancamargo.desafioinfoglobo.activities

import android.content.Intent
import androidx.test.platform.app.InstrumentationRegistry
import com.alancamargo.desafioinfoglobo.base.BaseActivityTest
import com.alancamargo.desafioinfoglobo.model.Article
import com.alancamargo.desafioinfoglobo.model.Image
import com.alancamargo.desafioinfoglobo.model.Section
import com.alancamargo.desafioinfoglobo.robots.articleDetailsActivity
import org.junit.Test

class ArticleDetailsActivityTest : BaseActivityTest<ArticleDetailsActivity>(ArticleDetailsActivity::class.java,
autoLaunch = false) {

private lateinit var article: Article

@Test
fun shouldLoadCorrectArticle() {
prepareArticleAndLaunch()

articleDetailsActivity {
articleIs(article)
}
}

@Test
fun shouldDisplayCorrectArticleInfo() {
prepareArticleAndLaunch()

articleDetailsActivity {
articleInfoIsDisplayed(article)
}
}

@Test
fun givenArticleHasNoImage_shouldNotDisplayImageView() {
prepareArticleAndLaunch(articleWithoutImage = true)

articleDetailsActivity {
imageViewIsNotVisible()
}
}

override fun intent(): Intent {
val context = InstrumentationRegistry.getInstrumentation().targetContext
return ArticleDetailsActivity.getIntent(context, article)
}

private fun prepareArticleAndLaunch(articleWithoutImage: Boolean = false) {
prepareArticle()
if (articleWithoutImage)
article.images = listOf()
launch()
}

private fun prepareArticle() {
article = Article(authors = listOf("O GLOBO"),
subHeadline = "MPF denuncia doleiro Juca Bala, acusado de movimentar US\$ 3 milhões para o ex-governador",
text = "\\n\\nRIO — O ex-governador do Rio Sérgio Cabral foi denunciado nesta quarta-feira pela sexta vez na Operação Lava-Jato. Nesta denúncia, a força-tarefa do Ministério Público Federal aponta ao ex-governador 25 crimes de evasão de divisas, 30 crimes de lavagem de dinheiro e 9 crimes de corrupção passiva decorrentes da Operação Eficiência, que prendeu o empresário Eike Batista. A denúncia foi apresentada à 7ª Vara Federal Criminal do Rio, cujo responsável é o juiz Marcelo Bretas. Cabral já é réu em cinco processos — quatro tramitam na 7ª Vara, com Bretas, e um na 13ª Vara Federal de Curitiba, com o juiz Sérgio Moro.\\n\\nENTENDA O QUE PESA CONTRA SÉRGIO CABRAL\\n\\nO MPF também denunciou o ex-secretário Wilson Carlos, os supostos operadores do esquema Carlos Miranda e Sérgio Castro de Oliveira, o “Serjão”, os doleiros Vinicius Claret, conhecido como “Juca Bala”, e Claudio de Souza, conhecido pelos apelidos “Tony” e “Peter”; além de Timothy Scorah Lynn. (Veja abaixo os crimes imputados a cada um)\\n\\nOutras duas pessoas que fizeram acordo de delação premiada também foram denunciadas. O MPF não divulgou seus nomes.\\n\\nNesta denúncia, os procuradores afirmam que Juca Bala movimentou US\$ 3.081.460 para Cabral. O doleiro usou o Banco BPA de Andorra, através de contrato de fachada firmado com empresa em nome de um dos colaboradores e Timothy Scorah Lynn. Juca Bala e Cláudio de Souza foram presos no Uruguai na última sexta-feira.\\n\\nIRMÃOS CHEBAR DELATARAM ESQUEMA\\n\\nO esquema envolvendo Cabral e Juca Bala foi revelado pelos doleiros Renato e Marcelo Chebar, que firmaram acordo de colaboração. Os irmãos contaram aos investigadores que Cabral e outros envolvidos no esquema ocultaram e lavaram valores que somam R\$ 318.554.478,91.\\n\\nDesse total, cerca de R\$ 39 milhões foram movimentados e guardados no Brasil, US\$ 100 milhões em contas no exterior, € 1 milhão em diamantes, US\$ 1 milhão também em diamantes e US\$ 247 mil em barras de ouro.\\n\\nSegundo os investigadores, o acordo de delação dos irmãos Chebar permitiu que o MPF recuperasse cerca de US\$ 85 milhões.\\n\\nNesta denúncia, os procuradores voltaram a afirmar que Cabral era o comandante de uma organização criminosa. O MPF afirmou que o esquema utilizou quatro formas de lavar dinheiro no exterior: realizaram depósitos em nome de terceiros, realizavam pagamentos em joias, compravam ouro e diamantes e fizeram transferências bancárias para parentes de Carlos Miranda. \\n\\nDETALHES DA DENÚNCIA\\n\\nWilson Carlos — 25 crimes de evasão de divisas e 18 de lavagem de dinheiro.\\n\\nCarlos Miranda — 25 crimes de evasão de divisas e 21 crimes de lavagem de dinheiro.\\n\\nSérgio Castro de Oliveira, o “Serjão” — 8 crimes de evasão de divisas.\\n\\nVinicius Claret, o “Juca Bala” — 25 crimes de evasão de divisas, 9 de corrupção passiva, 9 de lavagem de dinheiro e crime de pertencimento à organização criminosa.\\n\\nClaudio de Souza, o “Tony” ou “Peter” — 25 crimes de evasão de divisas, 9 de corrupção passiva, 9 de lavagem de dinheiro e crime de pertencimento à organização criminosa.\\n\\nTimothy Scorah Lynn — 9 crimes de corrupção ativa e 9 de lavagem de dinheiro.",
dateUpdated = "2017-03-08T13:25:03-0300",
section = Section("Brasil"),
headline = "Sérgio Cabral é denunciado pela sexta vez na Lava-Jato",
images = listOf(
Image(source = "Agência O Globo / 17-11-2016",
caption = "O ex-governador do Rio Sérgio Cabral ao ser preso pela Polícia Federal em novembro",
url = "https://ogimg.infoglobo.com.br/in/20620804-669-05e/FT1086A/cabral-preso.jpg"
)
)
)
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.alancamargo.desafioinfoglobo.activities

import com.alancamargo.desafioinfoglobo.base.BaseActivityTest
import com.alancamargo.desafioinfoglobo.robots.mainActivity
import okhttp3.mockwebserver.MockResponse
import org.junit.Before
import org.junit.Test

class MainActivityTest : BaseActivityTest<MainActivity>(MainActivity::class.java) {

@Before
override fun setup() {
super.setup()
mockApiResponse()
}

@Test
fun whenClickingOnArticleInList_shouldShowDetails() {
mainActivity {
articlePositionIs(0)
} clickArticleInList {
redirectToArticleDetailsActivity()
}
}

private fun mockApiResponse() {
val body = getJsonFromAsset("fixtures/response_articles.json")
mockArticleApi.enqueue(MockResponse().setResponseCode(200).setBody(body))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package com.alancamargo.desafioinfoglobo.base

import android.content.Intent
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.test.espresso.intent.Intents
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import br.com.concretesolutions.kappuccino.utils.doWait
import com.alancamargo.desafioinfoglobo.DependencyInjection
import com.alancamargo.desafioinfoglobo.images.MockImageHelper
import okhttp3.mockwebserver.MockWebServer
import org.junit.After
import org.junit.Before
import org.junit.Rule
import java.io.InputStream

open class BaseActivityTest<T: AppCompatActivity>(activityClass: Class<T>,
private val autoLaunch: Boolean = true) {

val mockArticleApi = MockWebServer()

@Rule
@JvmField
val rule = if (autoLaunch) {
IntentsTestRule(activityClass, true, false)
} else {
ActivityTestRule(activityClass, true, false)
}

@Before
open fun setup() {
DependencyInjection.init(mockArticleApi.url("/articles/").toString(), MockImageHelper)

if (autoLaunch)
launch()
else
Intents.init()
}

@After
open fun afterTest() {
if (!autoLaunch)
Intents.release()
}

open fun launch() {
rule.launchActivity(intent())
doWait(300)
}

open fun intent() = Intent()

fun getJsonFromAsset(fileName: String): String {
val json: String

try {
val context = InstrumentationRegistry.getInstrumentation().context
val inputStream: InputStream = context.assets.open(fileName)
json = inputStream.bufferedReader().use { it.readText() }
} catch (e: Exception) {
Log.e(LOG_TAG, e.message, e)
return ""
}

Log.d(LOG_TAG, json)
return json.replace("\n", "")
}

private companion object {
const val LOG_TAG = "androidTest"
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.alancamargo.desafioinfoglobo.images

import android.widget.ImageView
import com.alancamargo.desafioinfoglobo.R

object MockImageHelper : ImageHelper {

override fun loadImage(imageUrl: String, target: ImageView) {
target.setImageResource(R.drawable.cabral_preso)
}

}
Loading