Skip to content

Commit

Permalink
Add navigation components dependencies
Browse files Browse the repository at this point in the history
Start moving the main activity to a fragment

Continue moving stuff from the main activity to a fragment

Finish moving the SuperHeroesFragment

Navigate to super hero detail

Migrate edit super hero screen

Start migrating tests

Start migrating tests

Fix tests

Own code review
  • Loading branch information
Serchinastico committed Feb 6, 2019
1 parent 190583a commit d49192e
Show file tree
Hide file tree
Showing 37 changed files with 383 additions and 302 deletions.
12 changes: 12 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ apply plugin: "com.android.application"
apply plugin: "kotlin-android"
apply plugin: "kotlin-android-extensions"
apply plugin: "kotlin-kapt"
apply plugin: "androidx.navigation.safeargs.kotlin"
apply plugin: "shot"

android {
Expand Down Expand Up @@ -54,8 +55,12 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-runtime:2.0.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"
implementation "androidx.paging:paging-runtime-ktx:2.1.0"
implementation "android.arch.navigation:navigation-fragment-ktx:1.0.0-beta01"
implementation "android.arch.navigation:navigation-ui-ktx:1.0.0-beta01"
implementation "androidx.room:room-runtime:2.1.0-alpha04"
kapt "androidx.room:room-compiler:2.1.0-alpha04"
implementation "androidx.fragment:fragment:1.1.0-alpha03"
debugImplementation "androidx.fragment:fragment-testing:1.1.0-alpha03"

/* DI */
implementation "org.kodein.di:kodein-di-erased-jvm:6.0.1"
Expand Down Expand Up @@ -95,4 +100,11 @@ task ktlintFormat(type: JavaExec) {
main = "com.github.shyiko.ktlint.Main"
classpath = configurations.ktlint
args "-F", "src/**/*.kt"
}

configurations.all {
resolutionStrategy {
force "androidx.test:core:1.1.0-alpha01"
force "androidx.test:monitor:1.1.1-alpha01"
}
}
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ import org.kodein.di.erased.bind
import org.kodein.di.erased.instance
import org.mockito.Mock

class EditSuperHeroActivityTest :
AcceptanceTest<EditSuperHeroActivity>(EditSuperHeroActivity::class.java) {
class EditSuperHeroFragmentTest : FragmentTest<EditSuperHeroFragment>() {

companion object {
private const val ANY_ID = "#1"
}

@Mock
private lateinit var repository: SuperHeroRepository
override val fragmentBlock = { EditSuperHeroFragment() }

@Test
fun showsJustOneSuperHero() {
val superHero = givenThereIsASuperHero()

val activity = startActivity(superHero)
val activity = startFragment(superHero)

compareScreenshot(activity)
}
Expand All @@ -42,10 +42,9 @@ class EditSuperHeroActivityTest :
return superHero
}

private fun startActivity(superHero: SuperHero): EditSuperHeroActivity {
val args = Bundle()
args.putString("super_hero_id_key", superHero.id)
return startActivity(args)
private fun startFragment(superHero: SuperHero): EditSuperHeroFragment {
val args = Bundle().apply { putString("superHeroId", superHero.id) }
return startFragment(args)
}

override val testDependencies = Kodein.Module("Test dependencies", allowSilentOverride = true) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
package com.karumi.jetpack.superheroes.ui.view

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.fragment.app.Fragment
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.test.core.app.ApplicationProvider
import androidx.test.espresso.intent.rule.IntentsTestRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.LargeTest
import androidx.test.platform.app.InstrumentationRegistry
import com.karumi.jetpack.superheroes.R
import com.karumi.jetpack.superheroes.SuperHeroesApplication
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.mock
import org.junit.Before
import org.junit.Rule
import org.junit.runner.RunWith
import org.kodein.di.Kodein
import org.kodein.di.erased.bind
Expand All @@ -22,15 +22,15 @@ import java.util.concurrent.FutureTask

@LargeTest
@RunWith(AndroidJUnit4::class)
abstract class AcceptanceTest<T : Activity>(clazz: Class<T>) : ScreenshotTest {
abstract class FragmentTest<F : Fragment> : ScreenshotTest {

@Rule
@JvmField
val testRule: IntentsTestRule<T> = IntentsTestRule(clazz, true, false)
abstract val fragmentBlock: () -> F

private val executorServiceOnUiThread = mock<ExecutorService> {
on(it.execute(any())).thenAnswer { invocation ->
testRule.runOnUiThread { (invocation.getArgument(0) as Runnable).run() }
InstrumentationRegistry.getInstrumentation().runOnMainSync {
(invocation.getArgument(0) as Runnable).run()
}
FutureTask { null }
}
}
Expand All @@ -45,10 +45,10 @@ abstract class AcceptanceTest<T : Activity>(clazz: Class<T>) : ScreenshotTest {
})
}

fun startActivity(args: Bundle = Bundle()): T {
val intent = Intent()
intent.putExtras(args)
return testRule.launchActivity(intent)
protected fun startFragment(args: Bundle? = null): F {
val fragment = fragmentBlock()
launchFragmentInContainer(args, R.style.AppTheme) { fragment as Fragment }
return fragment
}

abstract val testDependencies: Kodein.Module
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
package com.karumi.jetpack.superheroes.ui.view

import android.app.Activity
import android.content.Context
import android.util.DisplayMetrics
import android.view.View
import android.view.WindowManager
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.RecyclerView
import androidx.test.platform.app.InstrumentationRegistry
import com.facebook.testing.screenshot.Screenshot
import com.facebook.testing.screenshot.ViewHelpers

interface ScreenshotTest {
fun compareScreenshot(activity: Activity) {
Screenshot.snapActivity(activity).record()
fun compareScreenshot(fragment: Fragment) {
Screenshot.snapActivity(fragment.requireActivity()).record()
}

fun compareScreenshot(holder: RecyclerView.ViewHolder, height: Int) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,11 @@ import org.kodein.di.erased.bind
import org.kodein.di.erased.instance
import org.mockito.Mock

class SuperHeroDetailActivityTest : AcceptanceTest<SuperHeroDetailActivity>(
SuperHeroDetailActivity::class.java
) {
class SuperHeroDetailFragmentTest : FragmentTest<SuperHeroDetailFragment>() {

@Mock
private lateinit var repository: SuperHeroRepository
override val fragmentBlock = { SuperHeroDetailFragment() }

@Test
fun showsAvengersBadgeIfSuperHeroIsPartOfTheAvengersTeam() {
Expand Down Expand Up @@ -45,10 +44,9 @@ class SuperHeroDetailActivityTest : AcceptanceTest<SuperHeroDetailActivity>(
return superHero
}

private fun startActivity(superHero: SuperHero): SuperHeroDetailActivity {
val args = Bundle()
args.putString("super_hero_id_key", superHero.id)
return startActivity(args)
private fun startActivity(superHero: SuperHero): SuperHeroDetailFragment {
val args = Bundle().apply { putString("superHeroId", superHero.id) }
return startFragment(args)
}

override val testDependencies = Kodein.Module("Test dependencies", allowSilentOverride = true) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.karumi.jetpack.superheroes.ui.view

import android.os.Looper
import androidx.fragment.app.Fragment
import androidx.paging.PagedList
import androidx.paging.PositionalDataSource
import com.karumi.jetpack.superheroes.data.repository.SuperHeroRepository
Expand All @@ -14,20 +15,21 @@ import org.kodein.di.erased.instance
import org.mockito.Mock
import java.util.concurrent.Executors.newSingleThreadExecutor

class MainActivityTest : AcceptanceTest<MainActivity>(MainActivity::class.java) {
class SuperHeroesFragmentTest : FragmentTest<SuperHeroesFragment>() {

companion object {
private const val ANY_NUMBER_OF_SUPER_HEROES = 100
}

@Mock
private lateinit var repository: SuperHeroRepository
override val fragmentBlock = { SuperHeroesFragment() }

@Test
fun showsEmptyCaseIfThereAreNoSuperHeroes() {
givenThereAreNoSuperHeroes()

val activity = startActivity()
val activity = startFragment()

compareScreenshot(activity)
}
Expand All @@ -36,7 +38,7 @@ class MainActivityTest : AcceptanceTest<MainActivity>(MainActivity::class.java)
fun showsJustOneSuperHero() {
givenThereAreSomeSuperHeroes(1)

val activity = startActivity()
val activity = startFragment()

compareScreenshot(activity)
}
Expand All @@ -45,7 +47,7 @@ class MainActivityTest : AcceptanceTest<MainActivity>(MainActivity::class.java)
fun showsSuperHeroesIfThereAreSomeSuperHeroes() {
givenThereAreSomeSuperHeroes(ANY_NUMBER_OF_SUPER_HEROES)

val activity = startActivity()
val activity = startFragment()

compareScreenshot(activity)
}
Expand All @@ -54,7 +56,7 @@ class MainActivityTest : AcceptanceTest<MainActivity>(MainActivity::class.java)
fun showsAvengersBadgeIfASuperHeroIsPartOfTheAvengersTeam() {
givenThereAreSomeAvengers(ANY_NUMBER_OF_SUPER_HEROES)

val activity = startActivity()
val activity = startFragment()

compareScreenshot(activity)
}
Expand All @@ -63,14 +65,14 @@ class MainActivityTest : AcceptanceTest<MainActivity>(MainActivity::class.java)
fun doesNotShowAvengersBadgeIfASuperHeroIsNotPartOfTheAvengersTeam() {
givenThereAreSomeSuperHeroes(ANY_NUMBER_OF_SUPER_HEROES)

val activity = startActivity()
val activity = startFragment()

compareScreenshot(activity)
}

private fun compareScreenshot(activity: MainActivity) {
override fun compareScreenshot(fragment: Fragment) {
Thread.sleep(100)
super.compareScreenshot(activity)
super.compareScreenshot(fragment)
}

private fun givenThereAreSomeAvengers(numberOfAvengers: Int): List<SuperHero> =
Expand Down
6 changes: 0 additions & 6 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,9 @@
<activity android:name=".ui.view.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity android:name=".ui.view.SuperHeroDetailActivity" />
<activity
android:name=".ui.view.EditSuperHeroActivity"
android:windowSoftInputMode="adjustPan" />
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.ViewModelProviders
import com.karumi.jetpack.superheroes.ui.view.BaseActivity
import com.karumi.jetpack.superheroes.ui.view.BaseFragment
import org.kodein.di.DKodein
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
Expand All @@ -26,7 +26,7 @@ class ViewModelFactory(

inline fun <reified VM : ViewModel, T> T.viewModel(): Lazy<VM>
where T : KodeinAware,
T : BaseActivity<*> {
T : BaseFragment<*> {
return lazy { ViewModelProviders.of(this, direct.instance()).get(VM::class.java) }
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.karumi.jetpack.superheroes.ui.view

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.databinding.ViewDataBinding
import androidx.fragment.app.Fragment
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.ViewModelProvider
import com.karumi.jetpack.superheroes.common.ViewModelFactory
import org.kodein.di.Kodein
import org.kodein.di.KodeinAware
import org.kodein.di.android.closestKodein
import org.kodein.di.erased.bind
import org.kodein.di.erased.instance
import org.kodein.di.erased.singleton

abstract class BaseFragment<T : ViewDataBinding> : Fragment(), KodeinAware {

private val appKodein by closestKodein { requireActivity() }
override val kodein: Kodein = Kodein.lazy {
extend(appKodein)
includeViewModelFactory()
import(activityModules)
}

abstract val layoutId: Int
abstract val activityModules: Kodein.Module
abstract val viewModel: AndroidViewModel
protected lateinit var binding: T

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(layoutInflater, layoutId, container, false)
binding.setLifecycleOwner(this)
configureBinding(binding)
return binding.root
}

private fun Kodein.MainBuilder.includeViewModelFactory() {
bind<ViewModelProvider.Factory>() with singleton {
ViewModelFactory(instance(), instance())
}
}

abstract fun configureBinding(binding: T)
}
Loading

0 comments on commit d49192e

Please sign in to comment.