From d8ab3fda8adf3d2f1f4f2b316e23cc13fe0a7459 Mon Sep 17 00:00:00 2001 From: Mitch Tabian Date: Fri, 22 Nov 2019 14:27:32 -0800 Subject: [PATCH 1/9] Simple Fragment testing in isolation --- .idea/misc.xml | 2 +- app/build.gradle | 10 ++ .../ui/movie/DirectorsFragmentTest.kt | 40 ++++++ .../ui/movie/MainActivityTest.kt | 63 -------- .../ui/movie/MovieDetailFragmentTest.kt | 61 ++++++++ ...TestSuite.kt => MovieFragmentTestSuite.kt} | 9 +- .../ui/movie/SecondaryActivityTest.kt | 67 --------- .../ui/movie/StarActorsFragmentTest.kt | 48 +++++++ .../factory/MovieFragmentFactory.kt | 6 +- .../ui/movie/MainActivity.kt | 17 +-- .../ui/movie/MovieDetailFragment.kt | 42 ++++-- .../main/res/layout/fragment_movie_detail.xml | 135 ++++++++---------- 12 files changed, 263 insertions(+), 237 deletions(-) create mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragmentTest.kt delete mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivityTest.kt create mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt rename app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/{ActivityTestSuite.kt => MovieFragmentTestSuite.kt} (55%) delete mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/SecondaryActivityTest.kt create mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragmentTest.kt diff --git a/.idea/misc.xml b/.idea/misc.xml index 29bb4c5..8a8f75b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -5,7 +5,7 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 7127e6c..34f3b25 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -21,6 +21,16 @@ android { } } + compileOptions { + sourceCompatibility = '1.8' + targetCompatibility = '1.8' + } + + tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).all { + kotlinOptions { + jvmTarget = "1.8" + } + } } dependencies { diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragmentTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragmentTest.kt new file mode 100644 index 0000000..035612b --- /dev/null +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragmentTest.kt @@ -0,0 +1,40 @@ +package com.codingwithmitch.espressouitestexamples.ui.movie + +import android.os.Bundle +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import com.codingwithmitch.espressouitestexamples.R +import com.codingwithmitch.espressouitestexamples.factory.MovieFragmentFactory +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4ClassRunner::class) +class DirectorsFragmentTest{ + + + @Test + fun test_isDirectorsListVisible() { + + // GIVEN + val directors = arrayListOf("R.J. Stewart", "James Vanderbilt") + val verifyDirectorsValue: StringBuilder = StringBuilder() + for(director in directors){ + verifyDirectorsValue.append(director + "\n") + } + val fragmentFactory = MovieFragmentFactory() + val bundle = Bundle() + bundle.putStringArrayList("args_directors", directors) + val scenario = launchFragmentInContainer( + fragmentArgs = bundle, + factory = fragmentFactory + ) + + // VERIFY + onView(withId(R.id.directors_text)) + .check(matches(withText(verifyDirectorsValue.toString()))) + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivityTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivityTest.kt deleted file mode 100644 index 739679d..0000000 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivityTest.kt +++ /dev/null @@ -1,63 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import androidx.test.core.app.ActivityScenario -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.* -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import com.codingwithmitch.espressouitestexamples.R -import org.junit.Test - -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4ClassRunner::class) -class MainActivityTest{ - - @Test - fun testActivity_inView() { - val activityScenario = ActivityScenario.launch(MainActivity::class.java) - - onView(withId(R.id.main)).check(matches(isDisplayed())) - } - - // Visibility - @Test - fun testVisibility_title_nextButton() { - val activityScenario = ActivityScenario.launch(MainActivity::class.java) - - onView(withId(R.id.activity_main_title)) - .check(matches(isDisplayed())) // method 1 - - onView(withId(R.id.activity_main_title)) - .check(matches(withEffectiveVisibility(Visibility.VISIBLE))) // method 2 - - onView(withId(R.id.button_next_activity)) - .check(matches(isDisplayed())) - } - - // Text - @Test - fun testTitleTextDisplayed() { - val activityScenario = ActivityScenario.launch(MainActivity::class.java) - - onView(withId(R.id.activity_main_title)) - .check(matches(withText(R.string.text_mainactivity))) - } - -} - - - - - - - - - - - - - - - - diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt new file mode 100644 index 0000000..308cb21 --- /dev/null +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt @@ -0,0 +1,61 @@ +package com.codingwithmitch.espressouitestexamples.ui.movie + +import android.os.Bundle +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import com.codingwithmitch.espressouitestexamples.R +import com.codingwithmitch.espressouitestexamples.data.Movie +import com.codingwithmitch.espressouitestexamples.factory.MovieFragmentFactory +import org.junit.Test + +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4ClassRunner::class) +class MovieDetailFragmentTest{ + + + @Test + fun test_isMovieDataVisible() { + + // SETUP + val movieId = 1 + val title = "The Rundown" + val description = "A tough aspiring chef is hired to bring home a mobster's son from the Amazon but " + + "becomes involved in the fight against an oppressive town operator and the search " + + "for a legendary treasure." + val movie = Movie( + movieId, + title, + "https://nyc3.digitaloceanspaces.com/open-api-spaces/open-api-static/blog/1/The_Rundown-the_rundown.png", + description , + arrayListOf("R.J. Stewart", "James Vanderbilt"), + arrayListOf("Dwayne Johnson", "Seann William Scott", "Rosario Dawson", "Christopher Walken") + ) + + val fragmentFactory = MovieFragmentFactory() + val bundle = Bundle() + bundle.putInt("movie_id", movieId) + val scenario = launchFragmentInContainer( + fragmentArgs = bundle, + factory = fragmentFactory + ) + + // VERIFY + onView(withId(R.id.movie_title)).check(matches(withText(title))) + + onView(withId(R.id.movie_description)).check(matches(withText(description))) + } +} + + + + + + + + + diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/ActivityTestSuite.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt similarity index 55% rename from app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/ActivityTestSuite.kt rename to app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt index 3eaef3c..ebdc6b5 100644 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/ActivityTestSuite.kt +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt @@ -5,7 +5,8 @@ import org.junit.runners.Suite @RunWith(Suite::class) @Suite.SuiteClasses( - MainActivityTest::class, - SecondaryActivityTest::class - ) -class ActivityTestSuite \ No newline at end of file + MovieDetailFragmentTest::class, + DirectorsFragmentTest::class, + StarActorsFragmentTest::class +) +class MovieFragmentTestSuite \ No newline at end of file diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/SecondaryActivityTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/SecondaryActivityTest.kt deleted file mode 100644 index 563626a..0000000 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/SecondaryActivityTest.kt +++ /dev/null @@ -1,67 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import androidx.lifecycle.Lifecycle -import androidx.test.espresso.Espresso.* -import androidx.test.espresso.assertion.ViewAssertions.* -import androidx.test.espresso.matcher.ViewMatchers.* -import androidx.test.ext.junit.rules.ActivityScenarioRule -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import com.codingwithmitch.espressouitestexamples.R -import org.junit.Rule -import org.junit.Test - -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4ClassRunner::class) -class SecondaryActivityTest{ - - /** - * ActivityScenarioRule: - * https://developer.android.com/reference/androidx/test/ext/junit/rules/ActivityScenarioRule.html - */ - @get:Rule - val activityRule = ActivityScenarioRule(SecondaryActivity::class.java) - - @Test - fun testActivity_inView() { - - onView(withId(R.id.secondary)) - .check(matches(isDisplayed())) - - // Notice this does not effect the next test - activityRule.scenario.moveToState(Lifecycle.State.DESTROYED) - } - - // Visibility - @Test - fun testVisibility_title_nextButton() { - onView(withId(R.id.activity_secondary_title)) - .check(matches(isDisplayed())) - - onView(withId(R.id.button_back)) - .check(matches(isDisplayed())) - } - - // Text - @Test - fun testTitleTextDisplayed() { - onView(withId(R.id.activity_secondary_title)) - .check(matches(withText(R.string.text_secondaryactivity))) - } - -} - - - - - - - - - - - - - - - diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragmentTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragmentTest.kt new file mode 100644 index 0000000..5042482 --- /dev/null +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragmentTest.kt @@ -0,0 +1,48 @@ +package com.codingwithmitch.espressouitestexamples.ui.movie + +import android.os.Bundle +import androidx.fragment.app.testing.launchFragmentInContainer +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import com.codingwithmitch.espressouitestexamples.R +import com.codingwithmitch.espressouitestexamples.factory.MovieFragmentFactory +import org.junit.Test + +import org.junit.runner.RunWith +import java.lang.StringBuilder + +@RunWith(AndroidJUnit4ClassRunner::class) +class StarActorsFragmentTest{ + + + @Test + fun test_isActorsListVisible() { + + // GIVEN + val actors = arrayListOf( + "Dwayne Johnson", + "Seann William Scott", + "Rosario Dawson", + "Christopher Walken" + ) + val verifyActorsValue: StringBuilder = StringBuilder() + for(director in actors){ + verifyActorsValue.append(director + "\n") + } + val fragmentFactory = MovieFragmentFactory() + val bundle = Bundle() + bundle.putStringArrayList("args_actors", actors) + val scenario = launchFragmentInContainer( + fragmentArgs = bundle, + factory = fragmentFactory + ) + + // VERIFY + onView(withId(R.id.star_actors_text)) + .check(matches(withText(verifyActorsValue.toString()))) + } +} + diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/factory/MovieFragmentFactory.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/factory/MovieFragmentFactory.kt index 5e0ac9c..6a9733d 100644 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/factory/MovieFragmentFactory.kt +++ b/app/src/main/java/com/codingwithmitch/espressouitestexamples/factory/MovieFragmentFactory.kt @@ -8,9 +8,7 @@ import com.codingwithmitch.espressouitestexamples.ui.movie.DirectorsFragment import com.codingwithmitch.espressouitestexamples.ui.movie.MovieDetailFragment import com.codingwithmitch.espressouitestexamples.ui.movie.StarActorsFragment -class MovieFragmentFactory( - private val requestManager: RequestManager -) : FragmentFactory(){ +class MovieFragmentFactory : FragmentFactory(){ override fun instantiate(classLoader: ClassLoader, className: String): Fragment { @@ -21,7 +19,7 @@ class MovieFragmentFactory( when(fragmentClass){ MovieDetailFragment::class.java -> { - fragment = MovieDetailFragment(requestManager) + fragment = MovieDetailFragment() } DirectorsFragment::class.java -> { diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt index 50f04c5..39b3f24 100644 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt +++ b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt @@ -2,20 +2,13 @@ package com.codingwithmitch.espressouitestexamples.ui.movie import android.os.Bundle import androidx.appcompat.app.AppCompatActivity -import com.bumptech.glide.Glide -import com.bumptech.glide.RequestManager -import com.bumptech.glide.request.RequestOptions import com.codingwithmitch.espressouitestexamples.R import com.codingwithmitch.espressouitestexamples.factory.MovieFragmentFactory class MainActivity : AppCompatActivity() { - // dependency (typically would be injected with dagger) - lateinit var requestManager: RequestManager - override fun onCreate(savedInstanceState: Bundle?) { - initGlideInstance() - supportFragmentManager.fragmentFactory = MovieFragmentFactory(requestManager) + supportFragmentManager.fragmentFactory = MovieFragmentFactory() super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) @@ -28,14 +21,6 @@ class MainActivity : AppCompatActivity() { .commit() } - private fun initGlideInstance(){ - val requestOptions = RequestOptions - .placeholderOf(R.drawable.default_image) - .error(R.drawable.default_image) - requestManager = Glide.with(application) - .setDefaultRequestOptions(requestOptions) - } - } diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt index cbb3a7d..3ab624b 100644 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt +++ b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt @@ -5,18 +5,41 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment +import com.bumptech.glide.Glide import com.bumptech.glide.RequestManager import com.codingwithmitch.espressouitestexamples.R import com.codingwithmitch.espressouitestexamples.data.Movie import com.codingwithmitch.espressouitestexamples.data.source.MoviesRemoteDataSource +import com.codingwithmitch.espressouitestexamples.ui.ErrorFragment import kotlinx.android.synthetic.main.fragment_movie_detail.* -class MovieDetailFragment -constructor( - val requestManager: RequestManager -): Fragment(){ +class MovieDetailFragment : Fragment(){ + + private lateinit var movie: Movie + + /** + * In production the MoviesRemoteDataSource would be either: + * 1) Be injected with a DI framework like dagger + * 2) Be passed as a constructor param to the Fragment (if using FragmentFactory) + * This is a simple use case so I'm just writing it here. + */ + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + arguments?.let { args -> + args.getInt("movie_id").let{ movieId -> + MoviesRemoteDataSource.getMovie(movieId)?.let{ movieFromRemote -> + movie = movieFromRemote + } + } + } + + if(!::movie.isInitialized){ + activity?.supportFragmentManager?.beginTransaction() + ?.replace(R.id.container, ErrorFragment::class.java, null) + ?.commit() + } + } - private var movie: Movie? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -32,7 +55,7 @@ constructor( movie_directiors.setOnClickListener { val bundle = Bundle() - bundle.putStringArrayList("args_directors", movie?.directors) + bundle.putStringArrayList("args_directors", movie.directors) activity?.supportFragmentManager?.beginTransaction() ?.replace(R.id.container, DirectorsFragment::class.java, bundle) ?.addToBackStack("DirectorsFragment") @@ -41,7 +64,7 @@ constructor( movie_star_actors.setOnClickListener { val bundle = Bundle() - bundle.putStringArrayList("args_actors", movie?.star_actors) + bundle.putStringArrayList("args_actors", movie.star_actors) activity?.supportFragmentManager?.beginTransaction() ?.replace(R.id.container, StarActorsFragment::class.java, bundle) ?.addToBackStack("StarActorsFragment") @@ -50,9 +73,8 @@ constructor( } private fun setMovieDetails(){ - movie = MoviesRemoteDataSource.getMovie(1) - movie?.let{nonNullMovie -> - requestManager + movie.let{ nonNullMovie -> + Glide.with(this) .load(nonNullMovie.image) .into(movie_image) movie_title.text = nonNullMovie.title diff --git a/app/src/main/res/layout/fragment_movie_detail.xml b/app/src/main/res/layout/fragment_movie_detail.xml index 1e8db35..2a19d2c 100644 --- a/app/src/main/res/layout/fragment_movie_detail.xml +++ b/app/src/main/res/layout/fragment_movie_detail.xml @@ -1,5 +1,5 @@ - - + + + + - - - + - - - - - - - + - + - - \ No newline at end of file + \ No newline at end of file From 6144ba3634361fdc375fbde1f9a01cc33751014d Mon Sep 17 00:00:00 2001 From: Mitch Tabian Date: Fri, 22 Nov 2019 14:30:31 -0800 Subject: [PATCH 2/9] add movieId in MainActivity --- .../espressouitestexamples/ui/movie/MainActivity.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt index 39b3f24..6aec184 100644 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt +++ b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt @@ -16,8 +16,11 @@ class MainActivity : AppCompatActivity() { } private fun init(){ + val movieId = 1 + val bundle = Bundle() + bundle.putInt("movie_id", movieId) supportFragmentManager.beginTransaction() - .replace(R.id.container, MovieDetailFragment::class.java, null) + .replace(R.id.container, MovieDetailFragment::class.java, bundle) .commit() } From 7fa3de5c599924b3aed7b2c61f06d3c7a56f2173 Mon Sep 17 00:00:00 2001 From: Mitch Tabian Date: Fri, 22 Nov 2019 15:02:32 -0800 Subject: [PATCH 3/9] Testing basic fragment navigation with Fragment Transactions --- .../ui/movie/MovieDetailFragmentTest.kt | 2 +- .../ui/movie/MovieFragmentTestSuite.kt | 3 +- .../ui/movie/NavigationTest.kt | 64 +++++++++++++++++++ .../ui/movie/MovieDetailFragment.kt | 2 +- .../main/res/layout/fragment_directors.xml | 3 +- .../main/res/layout/fragment_movie_detail.xml | 3 +- .../main/res/layout/fragment_star_actors.xml | 3 +- 7 files changed, 74 insertions(+), 6 deletions(-) create mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/NavigationTest.kt diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt index 308cb21..b343f11 100644 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt @@ -19,7 +19,7 @@ class MovieDetailFragmentTest{ @Test - fun test_isMovieDataVisible() { + fun test_navToDetailFragment() { // SETUP val movieId = 1 diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt index ebdc6b5..cad6611 100644 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt @@ -7,6 +7,7 @@ import org.junit.runners.Suite @Suite.SuiteClasses( MovieDetailFragmentTest::class, DirectorsFragmentTest::class, - StarActorsFragmentTest::class + StarActorsFragmentTest::class, + NavigationTest::class ) class MovieFragmentTestSuite \ No newline at end of file diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/NavigationTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/NavigationTest.kt new file mode 100644 index 0000000..b694230 --- /dev/null +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/NavigationTest.kt @@ -0,0 +1,64 @@ +package com.codingwithmitch.espressouitestexamples.ui.movie + +import androidx.test.core.app.ActivityScenario +import androidx.test.espresso.Espresso.* +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.* +import androidx.test.espresso.matcher.ViewMatchers.* +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import com.codingwithmitch.espressouitestexamples.R +import org.junit.Test +import org.junit.runner.RunWith + + +@RunWith(AndroidJUnit4ClassRunner::class) +class NavigationTest{ + + + @Test + fun testMovieFragmentsNavigation() { + + // SETUP + val activityScenario = ActivityScenario.launch(MainActivity::class.java) + + // NAV DirectorsFragment + onView(withId(R.id.movie_directors)).perform(click()) + + // VERIFY + onView(withId(R.id.fragment_directors_parent)) + .check(matches(isDisplayed())) + + // NAV MovieDetailFragment + pressBack() + + // VERIFY + onView(withId(R.id.fragment_movie_detail_parent)) + .check(matches(isDisplayed())) + + // NAV StarActorsFragment + onView(withId(R.id.movie_star_actors)).perform(click()) + + // VERIFY + onView(withId(R.id.fragment_star_actors_parent)) + .check(matches(isDisplayed())) + + // NAV MovieDetailFragment + pressBack() + + // VERIFY + onView(withId(R.id.fragment_movie_detail_parent)) + .check(matches(isDisplayed())) + } + +} + + + + + + + + + + + diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt index 3ab624b..a43de0f 100644 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt +++ b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt @@ -53,7 +53,7 @@ class MovieDetailFragment : Fragment(){ setMovieDetails() - movie_directiors.setOnClickListener { + movie_directors.setOnClickListener { val bundle = Bundle() bundle.putStringArrayList("args_directors", movie.directors) activity?.supportFragmentManager?.beginTransaction() diff --git a/app/src/main/res/layout/fragment_directors.xml b/app/src/main/res/layout/fragment_directors.xml index 78bd6b6..cdd01cb 100644 --- a/app/src/main/res/layout/fragment_directors.xml +++ b/app/src/main/res/layout/fragment_directors.xml @@ -5,7 +5,8 @@ android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" - android:padding="10dp"> + android:padding="10dp" + android:id="@+id/fragment_directors_parent"> + android:padding="10dp" + android:id="@+id/fragment_star_actors_parent"> Date: Fri, 22 Nov 2019 17:32:02 -0800 Subject: [PATCH 4/9] Test choose image from gallery intent --- app/build.gradle | 2 + .../MainActivityTest.kt | 73 +++++++++++++ .../ui/movie/DirectorsFragmentTest.kt | 40 ------- .../ui/movie/MovieDetailFragmentTest.kt | 61 ----------- .../ui/movie/MovieFragmentTestSuite.kt | 13 --- .../ui/movie/NavigationTest.kt | 64 ----------- .../ui/movie/StarActorsFragmentTest.kt | 48 --------- app/src/main/AndroidManifest.xml | 6 +- .../espressouitestexamples/MainActivity.kt | 51 +++++++++ .../espressouitestexamples/data/Movie.kt | 10 -- .../data/source/MoviesDataSource.kt | 8 -- .../data/source/MoviesRemoteDataSource.kt | 71 ------------ .../factory/MovieFragmentFactory.kt | 59 ---------- .../ui/ErrorFragment.kt | 41 ------- .../ui/movie/DirectorsFragment.kt | 62 ----------- .../ui/movie/MainActivity.kt | 34 ------ .../ui/movie/MovieDetailFragment.kt | 102 ------------------ .../ui/movie/StarActorsFragment.kt | 58 ---------- app/src/main/res/layout/activity_main.xml | 28 ++++- .../main/res/layout/fragment_directors.xml | 51 --------- app/src/main/res/layout/fragment_error.xml | 46 -------- .../main/res/layout/fragment_movie_detail.xml | 94 ---------------- .../main/res/layout/fragment_star_actors.xml | 51 --------- app/src/main/res/values/strings.xml | 1 + 24 files changed, 154 insertions(+), 920 deletions(-) create mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt delete mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragmentTest.kt delete mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt delete mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt delete mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/NavigationTest.kt delete mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragmentTest.kt create mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/MainActivity.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/data/Movie.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/data/source/MoviesDataSource.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/data/source/MoviesRemoteDataSource.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/factory/MovieFragmentFactory.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/ErrorFragment.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragment.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt delete mode 100644 app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragment.kt delete mode 100644 app/src/main/res/layout/fragment_directors.xml delete mode 100644 app/src/main/res/layout/fragment_error.xml delete mode 100644 app/src/main/res/layout/fragment_movie_detail.xml delete mode 100644 app/src/main/res/layout/fragment_star_actors.xml diff --git a/app/build.gradle b/app/build.gradle index 34f3b25..ca665a8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,6 +45,8 @@ dependencies { def androidx_test_espresso = "3.1.0" androidTestImplementation "androidx.test.espresso:espresso-core:$androidx_test_espresso" androidTestImplementation "androidx.test.espresso:espresso-contrib:$androidx_test_espresso" + androidTestImplementation "androidx.test.espresso:espresso-intents:$androidx_test_espresso" + // androidx.test def androidx_test = "1.1.0" diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt new file mode 100644 index 0000000..e6b7fcb --- /dev/null +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt @@ -0,0 +1,73 @@ +package com.codingwithmitch.espressouitestexamples + +import android.app.Activity.RESULT_OK +import android.app.Instrumentation.ActivityResult +import android.content.ContentResolver +import android.content.Intent +import android.content.res.Resources +import android.net.Uri +import android.provider.MediaStore +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.intent.Intents.intended +import androidx.test.espresso.intent.Intents.intending +import androidx.test.espresso.intent.matcher.IntentMatchers.* +import androidx.test.espresso.intent.rule.IntentsTestRule +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner +import androidx.test.platform.app.InstrumentationRegistry +import org.hamcrest.Matcher +import org.hamcrest.Matchers.allOf +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith + + +@RunWith(AndroidJUnit4ClassRunner::class) +class MainActivityTest{ + + @get:Rule + val intentsTestRule = IntentsTestRule(MainActivity::class.java) + + @Test + fun test_validateIntentSentToPickPackage() { + + // GIVEN + val expectedIntent: Matcher = allOf( + hasAction(Intent.ACTION_PICK), + hasData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI) + ) + val activityResult = createGalleryPickActivityResultStub() + intending(expectedIntent).respondWith(activityResult) + + // Execute and Verify + onView(withId(R.id.button_open_gallery)).perform(click()) + intended(expectedIntent) + } + + private fun createGalleryPickActivityResultStub(): ActivityResult { + val resources: Resources = InstrumentationRegistry.getInstrumentation().context.resources + val imageUri = Uri.parse( + ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + + resources.getResourcePackageName(R.drawable.ic_launcher_background) + '/' + + resources.getResourceTypeName(R.drawable.ic_launcher_background) + '/' + + resources.getResourceEntryName(R.drawable.ic_launcher_background) + ) + val resultIntent = Intent() + resultIntent.setData(imageUri) + return ActivityResult(RESULT_OK, resultIntent) + } +} + + + + + + + + + + + + + diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragmentTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragmentTest.kt deleted file mode 100644 index 035612b..0000000 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragmentTest.kt +++ /dev/null @@ -1,40 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import android.os.Bundle -import androidx.fragment.app.testing.launchFragmentInContainer -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import com.codingwithmitch.espressouitestexamples.R -import com.codingwithmitch.espressouitestexamples.factory.MovieFragmentFactory -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4ClassRunner::class) -class DirectorsFragmentTest{ - - - @Test - fun test_isDirectorsListVisible() { - - // GIVEN - val directors = arrayListOf("R.J. Stewart", "James Vanderbilt") - val verifyDirectorsValue: StringBuilder = StringBuilder() - for(director in directors){ - verifyDirectorsValue.append(director + "\n") - } - val fragmentFactory = MovieFragmentFactory() - val bundle = Bundle() - bundle.putStringArrayList("args_directors", directors) - val scenario = launchFragmentInContainer( - fragmentArgs = bundle, - factory = fragmentFactory - ) - - // VERIFY - onView(withId(R.id.directors_text)) - .check(matches(withText(verifyDirectorsValue.toString()))) - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt deleted file mode 100644 index b343f11..0000000 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragmentTest.kt +++ /dev/null @@ -1,61 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import android.os.Bundle -import androidx.fragment.app.testing.launchFragmentInContainer -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import com.codingwithmitch.espressouitestexamples.R -import com.codingwithmitch.espressouitestexamples.data.Movie -import com.codingwithmitch.espressouitestexamples.factory.MovieFragmentFactory -import org.junit.Test - -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4ClassRunner::class) -class MovieDetailFragmentTest{ - - - @Test - fun test_navToDetailFragment() { - - // SETUP - val movieId = 1 - val title = "The Rundown" - val description = "A tough aspiring chef is hired to bring home a mobster's son from the Amazon but " + - "becomes involved in the fight against an oppressive town operator and the search " + - "for a legendary treasure." - val movie = Movie( - movieId, - title, - "https://nyc3.digitaloceanspaces.com/open-api-spaces/open-api-static/blog/1/The_Rundown-the_rundown.png", - description , - arrayListOf("R.J. Stewart", "James Vanderbilt"), - arrayListOf("Dwayne Johnson", "Seann William Scott", "Rosario Dawson", "Christopher Walken") - ) - - val fragmentFactory = MovieFragmentFactory() - val bundle = Bundle() - bundle.putInt("movie_id", movieId) - val scenario = launchFragmentInContainer( - fragmentArgs = bundle, - factory = fragmentFactory - ) - - // VERIFY - onView(withId(R.id.movie_title)).check(matches(withText(title))) - - onView(withId(R.id.movie_description)).check(matches(withText(description))) - } -} - - - - - - - - - diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt deleted file mode 100644 index cad6611..0000000 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieFragmentTestSuite.kt +++ /dev/null @@ -1,13 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import org.junit.runner.RunWith -import org.junit.runners.Suite - -@RunWith(Suite::class) -@Suite.SuiteClasses( - MovieDetailFragmentTest::class, - DirectorsFragmentTest::class, - StarActorsFragmentTest::class, - NavigationTest::class -) -class MovieFragmentTestSuite \ No newline at end of file diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/NavigationTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/NavigationTest.kt deleted file mode 100644 index b694230..0000000 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/NavigationTest.kt +++ /dev/null @@ -1,64 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import androidx.test.core.app.ActivityScenario -import androidx.test.espresso.Espresso.* -import androidx.test.espresso.action.ViewActions.click -import androidx.test.espresso.assertion.ViewAssertions.* -import androidx.test.espresso.matcher.ViewMatchers.* -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import com.codingwithmitch.espressouitestexamples.R -import org.junit.Test -import org.junit.runner.RunWith - - -@RunWith(AndroidJUnit4ClassRunner::class) -class NavigationTest{ - - - @Test - fun testMovieFragmentsNavigation() { - - // SETUP - val activityScenario = ActivityScenario.launch(MainActivity::class.java) - - // NAV DirectorsFragment - onView(withId(R.id.movie_directors)).perform(click()) - - // VERIFY - onView(withId(R.id.fragment_directors_parent)) - .check(matches(isDisplayed())) - - // NAV MovieDetailFragment - pressBack() - - // VERIFY - onView(withId(R.id.fragment_movie_detail_parent)) - .check(matches(isDisplayed())) - - // NAV StarActorsFragment - onView(withId(R.id.movie_star_actors)).perform(click()) - - // VERIFY - onView(withId(R.id.fragment_star_actors_parent)) - .check(matches(isDisplayed())) - - // NAV MovieDetailFragment - pressBack() - - // VERIFY - onView(withId(R.id.fragment_movie_detail_parent)) - .check(matches(isDisplayed())) - } - -} - - - - - - - - - - - diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragmentTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragmentTest.kt deleted file mode 100644 index 5042482..0000000 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragmentTest.kt +++ /dev/null @@ -1,48 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import android.os.Bundle -import androidx.fragment.app.testing.launchFragmentInContainer -import androidx.test.espresso.Espresso.onView -import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.matcher.ViewMatchers.withId -import androidx.test.espresso.matcher.ViewMatchers.withText -import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import com.codingwithmitch.espressouitestexamples.R -import com.codingwithmitch.espressouitestexamples.factory.MovieFragmentFactory -import org.junit.Test - -import org.junit.runner.RunWith -import java.lang.StringBuilder - -@RunWith(AndroidJUnit4ClassRunner::class) -class StarActorsFragmentTest{ - - - @Test - fun test_isActorsListVisible() { - - // GIVEN - val actors = arrayListOf( - "Dwayne Johnson", - "Seann William Scott", - "Rosario Dawson", - "Christopher Walken" - ) - val verifyActorsValue: StringBuilder = StringBuilder() - for(director in actors){ - verifyActorsValue.append(director + "\n") - } - val fragmentFactory = MovieFragmentFactory() - val bundle = Bundle() - bundle.putStringArrayList("args_actors", actors) - val scenario = launchFragmentInContainer( - fragmentArgs = bundle, - factory = fragmentFactory - ) - - // VERIFY - onView(withId(R.id.star_actors_text)) - .check(matches(withText(verifyActorsValue.toString()))) - } -} - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 535cd01..e5cbc1a 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,7 +2,9 @@ - + + + - + diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/MainActivity.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/MainActivity.kt new file mode 100644 index 0000000..8d170ed --- /dev/null +++ b/app/src/main/java/com/codingwithmitch/espressouitestexamples/MainActivity.kt @@ -0,0 +1,51 @@ +package com.codingwithmitch.espressouitestexamples + +import android.app.Activity +import android.content.Intent +import android.os.Bundle +import android.provider.MediaStore +import android.util.Log +import androidx.appcompat.app.AppCompatActivity +import com.bumptech.glide.Glide +import kotlinx.android.synthetic.main.activity_main.* + +const val GALLERY_REQUEST_CODE = 1234 + +class MainActivity : AppCompatActivity(){ + + private val TAG: String = "AppDebug" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + + button_open_gallery.setOnClickListener { + pickFromGallery() + } + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if(resultCode == Activity.RESULT_OK){ + Log.d(TAG, "RESULT_OK") + when(requestCode){ + + GALLERY_REQUEST_CODE -> { + Log.d(TAG, "GALLERY_REQUEST_CODE detected.") + data?.data?.let { uri -> + Log.d(TAG, "URI: $uri") + Glide.with(this) + .load(uri) + .into(image) + } + } + } + } + } + + private fun pickFromGallery() { + val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) + startActivityForResult(intent, GALLERY_REQUEST_CODE) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/Movie.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/Movie.kt deleted file mode 100644 index 15729c6..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/Movie.kt +++ /dev/null @@ -1,10 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.data - -data class Movie ( - val id: Int, - val title: String, - val image: String, - val description: String, - val directors: ArrayList?, - val star_actors: ArrayList? -) \ No newline at end of file diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/source/MoviesDataSource.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/source/MoviesDataSource.kt deleted file mode 100644 index 15fa5a2..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/source/MoviesDataSource.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.data.source - -import com.codingwithmitch.espressouitestexamples.data.Movie - -interface MoviesDataSource { - - fun getMovie(movieId: Int): Movie? -} \ No newline at end of file diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/source/MoviesRemoteDataSource.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/source/MoviesRemoteDataSource.kt deleted file mode 100644 index 0b8b549..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/data/source/MoviesRemoteDataSource.kt +++ /dev/null @@ -1,71 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.data.source - -import com.codingwithmitch.espressouitestexamples.data.Movie - -object MoviesRemoteDataSource: MoviesDataSource { - - private var MOVIES_REMOTE_DATA = LinkedHashMap(2) - - init { - addMovie( - 0, - "Avengers: Infinity War", - "https://nyc3.digitaloceanspaces.com/open-api-spaces/open-api-static/blog/1/Infinity_War-infinity_war.png", - "The Avengers and their allies must be willing to sacrifice all in an attempt to " + - "defeat the powerful Thanos before his blitz of devastation and ruin puts an end to " + - "the universe.", - arrayListOf("Anthony Russo", "Joe Russo"), - arrayListOf("Robert Downey Jr.", "Chris Hemsworth", "Mark Ruffalo", "+ more...") - ) - - addMovie( - 1, - "The Rundown", - "https://nyc3.digitaloceanspaces.com/open-api-spaces/open-api-static/blog/1/The_Rundown-the_rundown.png", - "A tough aspiring chef is hired to bring home a mobster's son from the Amazon but " + - "becomes involved in the fight against an oppressive town operator and the search " + - "for a legendary treasure.", - arrayListOf("R.J. Stewart", "James Vanderbilt"), - arrayListOf("Dwayne Johnson", "Seann William Scott", "Rosario Dawson", "Christopher Walken") - ) - } - - override fun getMovie(movieId: Int): Movie? { - return MOVIES_REMOTE_DATA[movieId] - } - - private fun addMovie( - id: Int, - title: String, - image: String, - description: String, - directors: ArrayList?, - star_actors: ArrayList? - ){ - val movie = Movie( - id = id, - title = title, - image = image, - description = description, - directors = directors, - star_actors = star_actors - ) - MOVIES_REMOTE_DATA.put(id, movie) - } - - -} - - - - - - - - - - - - - - diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/factory/MovieFragmentFactory.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/factory/MovieFragmentFactory.kt deleted file mode 100644 index 6a9733d..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/factory/MovieFragmentFactory.kt +++ /dev/null @@ -1,59 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.factory - -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentFactory -import com.bumptech.glide.RequestManager -import com.codingwithmitch.espressouitestexamples.ui.ErrorFragment -import com.codingwithmitch.espressouitestexamples.ui.movie.DirectorsFragment -import com.codingwithmitch.espressouitestexamples.ui.movie.MovieDetailFragment -import com.codingwithmitch.espressouitestexamples.ui.movie.StarActorsFragment - -class MovieFragmentFactory : FragmentFactory(){ - - override fun instantiate(classLoader: ClassLoader, className: String): Fragment { - - val fragmentClass = loadFragmentClass(classLoader, className) - var fragment: Fragment? = null - var errorDescription: String? = null - - when(fragmentClass){ - - MovieDetailFragment::class.java -> { - fragment = MovieDetailFragment() - } - - DirectorsFragment::class.java -> { - fragment = DirectorsFragment() - } - - StarActorsFragment::class.java -> { - fragment = StarActorsFragment() - } - - else -> errorDescription = "Something went wrong." - } - - fragment?.let{ nonNullFragment -> - return nonNullFragment - } - fragment = ErrorFragment(errorDescription) - - return fragment - } - - - -} - - - - - - - - - - - - - diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/ErrorFragment.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/ErrorFragment.kt deleted file mode 100644 index f485f73..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/ErrorFragment.kt +++ /dev/null @@ -1,41 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.codingwithmitch.espressouitestexamples.R -import kotlinx.android.synthetic.main.fragment_error.* - -class ErrorFragment -constructor( - val errorDescription: String? = null -) : Fragment(){ - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_error, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - errorDescription?.let{description -> - fragment_error_description.text = description - } - } -} - - - - - - - - - - - diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragment.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragment.kt deleted file mode 100644 index 1d653eb..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/DirectorsFragment.kt +++ /dev/null @@ -1,62 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.codingwithmitch.espressouitestexamples.R -import com.codingwithmitch.espressouitestexamples.ui.ErrorFragment -import kotlinx.android.synthetic.main.fragment_directors.* - -class DirectorsFragment : Fragment(){ - - private val directors: ArrayList = ArrayList() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { args -> - directors.addAll(args.get("args_directors") as List) - }?:activity?.supportFragmentManager?.beginTransaction() - ?.replace(R.id.container, ErrorFragment::class.java, null) - ?.commit() - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_directors, container, false) - } - - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - setDirectors() - } - - private fun setDirectors(){ - for(director in directors){ - directors_text.append(director + "\n") - } - } -} - - - - - - - - - - - - - - - - - diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt deleted file mode 100644 index 6aec184..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MainActivity.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import android.os.Bundle -import androidx.appcompat.app.AppCompatActivity -import com.codingwithmitch.espressouitestexamples.R -import com.codingwithmitch.espressouitestexamples.factory.MovieFragmentFactory - -class MainActivity : AppCompatActivity() { - - override fun onCreate(savedInstanceState: Bundle?) { - supportFragmentManager.fragmentFactory = MovieFragmentFactory() - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - - init() - } - - private fun init(){ - val movieId = 1 - val bundle = Bundle() - bundle.putInt("movie_id", movieId) - supportFragmentManager.beginTransaction() - .replace(R.id.container, MovieDetailFragment::class.java, bundle) - .commit() - } - -} - - - - - - - diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt deleted file mode 100644 index a43de0f..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/MovieDetailFragment.kt +++ /dev/null @@ -1,102 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.bumptech.glide.Glide -import com.bumptech.glide.RequestManager -import com.codingwithmitch.espressouitestexamples.R -import com.codingwithmitch.espressouitestexamples.data.Movie -import com.codingwithmitch.espressouitestexamples.data.source.MoviesRemoteDataSource -import com.codingwithmitch.espressouitestexamples.ui.ErrorFragment -import kotlinx.android.synthetic.main.fragment_movie_detail.* - -class MovieDetailFragment : Fragment(){ - - private lateinit var movie: Movie - - /** - * In production the MoviesRemoteDataSource would be either: - * 1) Be injected with a DI framework like dagger - * 2) Be passed as a constructor param to the Fragment (if using FragmentFactory) - * This is a simple use case so I'm just writing it here. - */ - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { args -> - args.getInt("movie_id").let{ movieId -> - MoviesRemoteDataSource.getMovie(movieId)?.let{ movieFromRemote -> - movie = movieFromRemote - } - } - } - - if(!::movie.isInitialized){ - activity?.supportFragmentManager?.beginTransaction() - ?.replace(R.id.container, ErrorFragment::class.java, null) - ?.commit() - } - } - - - override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_movie_detail, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - setMovieDetails() - - movie_directors.setOnClickListener { - val bundle = Bundle() - bundle.putStringArrayList("args_directors", movie.directors) - activity?.supportFragmentManager?.beginTransaction() - ?.replace(R.id.container, DirectorsFragment::class.java, bundle) - ?.addToBackStack("DirectorsFragment") - ?.commit() - } - - movie_star_actors.setOnClickListener { - val bundle = Bundle() - bundle.putStringArrayList("args_actors", movie.star_actors) - activity?.supportFragmentManager?.beginTransaction() - ?.replace(R.id.container, StarActorsFragment::class.java, bundle) - ?.addToBackStack("StarActorsFragment") - ?.commit() - } - } - - private fun setMovieDetails(){ - movie.let{ nonNullMovie -> - Glide.with(this) - .load(nonNullMovie.image) - .into(movie_image) - movie_title.text = nonNullMovie.title - movie_description.text = nonNullMovie.description - } - } - -} - - - - - - - - - - - - - - - - - diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragment.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragment.kt deleted file mode 100644 index c20190c..0000000 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/ui/movie/StarActorsFragment.kt +++ /dev/null @@ -1,58 +0,0 @@ -package com.codingwithmitch.espressouitestexamples.ui.movie - -import android.os.Bundle -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import androidx.fragment.app.Fragment -import com.codingwithmitch.espressouitestexamples.R -import com.codingwithmitch.espressouitestexamples.ui.ErrorFragment -import kotlinx.android.synthetic.main.fragment_star_actors.* - -class StarActorsFragment : Fragment(){ - - private val starActors: ArrayList = ArrayList() - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - arguments?.let { args -> - starActors.addAll(args.get("args_actors") as List) - }?:activity?.supportFragmentManager?.beginTransaction() - ?.replace(R.id.container, ErrorFragment::class.java, null) - ?.commit() - } - - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { - return inflater.inflate(R.layout.fragment_star_actors, container, false) - } - - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { - super.onViewCreated(view, savedInstanceState) - - setActors() - } - - private fun setActors(){ - for(actor in starActors){ - star_actors_text.append(actor + "\n") - } - } -} - - - - - - - - - - - - - - diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index fa88eee..d963a58 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -6,16 +6,34 @@ android:id="@+id/main"> - + + + - \ No newline at end of file + + + diff --git a/app/src/main/res/layout/fragment_directors.xml b/app/src/main/res/layout/fragment_directors.xml deleted file mode 100644 index cdd01cb..0000000 --- a/app/src/main/res/layout/fragment_directors.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_error.xml b/app/src/main/res/layout/fragment_error.xml deleted file mode 100644 index ea28a4f..0000000 --- a/app/src/main/res/layout/fragment_error.xml +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_movie_detail.xml b/app/src/main/res/layout/fragment_movie_detail.xml deleted file mode 100644 index 0a9da2e..0000000 --- a/app/src/main/res/layout/fragment_movie_detail.xml +++ /dev/null @@ -1,94 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_star_actors.xml b/app/src/main/res/layout/fragment_star_actors.xml deleted file mode 100644 index 83ea42d..0000000 --- a/app/src/main/res/layout/fragment_star_actors.xml +++ /dev/null @@ -1,51 +0,0 @@ - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index df156fd..25e28b3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -15,4 +15,5 @@ Star Actors Directors: Star Actors: + Select an image from gallery From b73be337252e14dbfb78b45d0f05ad2e85d5aa23 Mon Sep 17 00:00:00 2001 From: Mitch Tabian Date: Fri, 22 Nov 2019 18:02:08 -0800 Subject: [PATCH 5/9] Test camera intents and custom ImageView matcher --- .../ImageViewHasDrawableMatcher.kt | 23 ++++++++++ .../MainActivityTest.kt | 46 +++++++++---------- app/src/main/AndroidManifest.xml | 5 +- .../espressouitestexamples/MainActivity.kt | 33 +++++++------ app/src/main/res/layout/activity_main.xml | 6 +-- app/src/main/res/values/strings.xml | 1 + 6 files changed, 71 insertions(+), 43 deletions(-) create mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ImageViewHasDrawableMatcher.kt diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ImageViewHasDrawableMatcher.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ImageViewHasDrawableMatcher.kt new file mode 100644 index 0000000..80c2ae3 --- /dev/null +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ImageViewHasDrawableMatcher.kt @@ -0,0 +1,23 @@ +package com.codingwithmitch.espressouitestexamples + +import android.view.View +import android.widget.ImageView +import androidx.test.espresso.matcher.BoundedMatcher +import org.hamcrest.Description + +object ImageViewHasDrawableMatcher { + + fun hasDrawable(): BoundedMatcher{ + return object: BoundedMatcher(ImageView::class.java){ + + override fun describeTo(description: Description?) { + description?.appendText("has drawable") + } + + override fun matchesSafely(item: ImageView?): Boolean { + return item?.getDrawable() != null; + } + + } + } +} \ No newline at end of file diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt index e6b7fcb..56e0bef 100644 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt @@ -1,23 +1,23 @@ package com.codingwithmitch.espressouitestexamples -import android.app.Activity.RESULT_OK +import android.app.Activity import android.app.Instrumentation.ActivityResult -import android.content.ContentResolver import android.content.Intent -import android.content.res.Resources -import android.net.Uri +import android.graphics.BitmapFactory +import android.os.Bundle import android.provider.MediaStore import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents.intended import androidx.test.espresso.intent.Intents.intending -import androidx.test.espresso.intent.matcher.IntentMatchers.* +import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction import androidx.test.espresso.intent.rule.IntentsTestRule import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import androidx.test.platform.app.InstrumentationRegistry +import com.codingwithmitch.espressouitestexamples.ImageViewHasDrawableMatcher.hasDrawable +import org.hamcrest.CoreMatchers.not import org.hamcrest.Matcher -import org.hamcrest.Matchers.allOf import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith @@ -30,32 +30,30 @@ class MainActivityTest{ val intentsTestRule = IntentsTestRule(MainActivity::class.java) @Test - fun test_validateIntentSentToPickPackage() { + fun test_cameraIntent() { // GIVEN - val expectedIntent: Matcher = allOf( - hasAction(Intent.ACTION_PICK), - hasData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI) - ) - val activityResult = createGalleryPickActivityResultStub() + val activityResult = createImageCaptureActivityResultStub() + val expectedIntent: Matcher = hasAction(MediaStore.ACTION_IMAGE_CAPTURE) intending(expectedIntent).respondWith(activityResult) // Execute and Verify - onView(withId(R.id.button_open_gallery)).perform(click()) + onView(withId(R.id.image)).check(matches(not(hasDrawable()))) + onView(withId(R.id.button_launch_camera)).perform(click()) intended(expectedIntent) + onView(withId(R.id.image)).check(matches(hasDrawable())) } - private fun createGalleryPickActivityResultStub(): ActivityResult { - val resources: Resources = InstrumentationRegistry.getInstrumentation().context.resources - val imageUri = Uri.parse( - ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + - resources.getResourcePackageName(R.drawable.ic_launcher_background) + '/' + - resources.getResourceTypeName(R.drawable.ic_launcher_background) + '/' + - resources.getResourceEntryName(R.drawable.ic_launcher_background) + private fun createImageCaptureActivityResultStub(): ActivityResult? { + val bundle = Bundle() + bundle.putParcelable( + KEY_IMAGE_DATA, BitmapFactory.decodeResource( + intentsTestRule.getActivity().getResources(), R.drawable.ic_launcher_background + ) ) - val resultIntent = Intent() - resultIntent.setData(imageUri) - return ActivityResult(RESULT_OK, resultIntent) + val resultData = Intent() + resultData.putExtras(bundle) + return ActivityResult(Activity.RESULT_OK, resultData) } } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e5cbc1a..eade9d0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,8 +2,9 @@ - - + { - Log.d(TAG, "GALLERY_REQUEST_CODE detected.") - data?.data?.let { uri -> - Log.d(TAG, "URI: $uri") - Glide.with(this) - .load(uri) - .into(image) + REQUEST_IMAGE_CAPTURE -> { + Log.d(TAG, "REQUEST_IMAGE_CAPTURE detected.") + data?.extras.let{ extras -> + if (extras == null || !extras.containsKey(KEY_IMAGE_DATA)) { + return + } + val imageBitmap = extras[KEY_IMAGE_DATA] as Bitmap? + image.setImageBitmap(imageBitmap) } } } } } - private fun pickFromGallery() { - val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI) - startActivityForResult(intent, GALLERY_REQUEST_CODE) + private fun dispatchCameraIntent() { + val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) + startActivityForResult(intent, REQUEST_IMAGE_CAPTURE); } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d963a58..7cea1b5 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -9,12 +9,12 @@ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 25e28b3..eae4f30 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -16,4 +16,5 @@ Directors: Star Actors: Select an image from gallery + Launch camera From ae13e578206000e1402fd4bd634400652960596e Mon Sep 17 00:00:00 2001 From: Mitch Tabian Date: Mon, 25 Nov 2019 10:41:32 -0800 Subject: [PATCH 6/9] simple dialog test --- app/build.gradle | 4 ++ .../ImageViewHasDrawableMatcher.kt | 23 -------- .../MainActivityTest.kt | 51 +++++----------- app/src/main/AndroidManifest.xml | 4 -- .../espressouitestexamples/MainActivity.kt | 58 ++++++++----------- app/src/main/res/layout/activity_main.xml | 24 ++------ app/src/main/res/values/strings.xml | 4 ++ app/src/main/res/values/styles.xml | 2 + 8 files changed, 52 insertions(+), 118 deletions(-) delete mode 100644 app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ImageViewHasDrawableMatcher.kt diff --git a/app/build.gradle b/app/build.gradle index ca665a8..29de7bb 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,10 @@ dependencies { implementation "com.github.bumptech.glide:glide:$glide_version" annotationProcessor "com.github.bumptech.glide:compiler:$glide_version" + // material dialogs + def matieral_dialogs_version = "3.1.0" + implementation "com.afollestad.material-dialogs:core:$matieral_dialogs_version" + } diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ImageViewHasDrawableMatcher.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ImageViewHasDrawableMatcher.kt deleted file mode 100644 index 80c2ae3..0000000 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/ImageViewHasDrawableMatcher.kt +++ /dev/null @@ -1,23 +0,0 @@ -package com.codingwithmitch.espressouitestexamples - -import android.view.View -import android.widget.ImageView -import androidx.test.espresso.matcher.BoundedMatcher -import org.hamcrest.Description - -object ImageViewHasDrawableMatcher { - - fun hasDrawable(): BoundedMatcher{ - return object: BoundedMatcher(ImageView::class.java){ - - override fun describeTo(description: Description?) { - description?.appendText("has drawable") - } - - override fun matchesSafely(item: ImageView?): Boolean { - return item?.getDrawable() != null; - } - - } - } -} \ No newline at end of file diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt index 56e0bef..473dba9 100644 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt @@ -1,59 +1,35 @@ package com.codingwithmitch.espressouitestexamples -import android.app.Activity -import android.app.Instrumentation.ActivityResult -import android.content.Intent -import android.graphics.BitmapFactory -import android.os.Bundle -import android.provider.MediaStore +import androidx.test.core.app.ActivityScenario import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches -import androidx.test.espresso.intent.Intents.intended -import androidx.test.espresso.intent.Intents.intending -import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction -import androidx.test.espresso.intent.rule.IntentsTestRule -import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.* import androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner -import com.codingwithmitch.espressouitestexamples.ImageViewHasDrawableMatcher.hasDrawable -import org.hamcrest.CoreMatchers.not -import org.hamcrest.Matcher -import org.junit.Rule import org.junit.Test -import org.junit.runner.RunWith +import org.junit.runner.RunWith @RunWith(AndroidJUnit4ClassRunner::class) class MainActivityTest{ - @get:Rule - val intentsTestRule = IntentsTestRule(MainActivity::class.java) @Test - fun test_cameraIntent() { + fun test_showDialog_confirmVisible() { // GIVEN - val activityResult = createImageCaptureActivityResultStub() - val expectedIntent: Matcher = hasAction(MediaStore.ACTION_IMAGE_CAPTURE) - intending(expectedIntent).respondWith(activityResult) + val activityScenario = ActivityScenario.launch(MainActivity::class.java) // Execute and Verify - onView(withId(R.id.image)).check(matches(not(hasDrawable()))) - onView(withId(R.id.button_launch_camera)).perform(click()) - intended(expectedIntent) - onView(withId(R.id.image)).check(matches(hasDrawable())) - } + onView(withId(R.id.button_launch_dialog)).perform(click()) - private fun createImageCaptureActivityResultStub(): ActivityResult? { - val bundle = Bundle() - bundle.putParcelable( - KEY_IMAGE_DATA, BitmapFactory.decodeResource( - intentsTestRule.getActivity().getResources(), R.drawable.ic_launcher_background - ) - ) - val resultData = Intent() - resultData.putExtras(bundle) - return ActivityResult(Activity.RESULT_OK, resultData) + onView(withText(R.string.text_title_of_dialog)).check(matches(isDisplayed())) + + onView(withText(R.string.text_ok)).perform(click()) + + // 'doesNotExist' must be used here b/c material dialog isn't on view hierarchy + onView(withText(R.string.text_title_of_dialog)).check(doesNotExist()) } } @@ -68,4 +44,3 @@ class MainActivityTest{ - diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index eade9d0..8f34717 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -2,10 +2,6 @@ - - { - Log.d(TAG, "REQUEST_IMAGE_CAPTURE detected.") - data?.extras.let{ extras -> - if (extras == null || !extras.containsKey(KEY_IMAGE_DATA)) { - return - } - val imageBitmap = extras[KEY_IMAGE_DATA] as Bitmap? - image.setImageBitmap(imageBitmap) - } - } + private fun showDialog(){ + MaterialDialog(this) + .show { + title(R.string.text_title_of_dialog) + message(R.string.text_some_information) + positiveButton(R.string.text_ok) } - } } - private fun dispatchCameraIntent() { - val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) - startActivityForResult(intent, REQUEST_IMAGE_CAPTURE); - } +} + + + + + + + + + + + + + + -} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 7cea1b5..5c5b0cb 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,31 +5,15 @@ android:layout_height="match_parent" android:id="@+id/main"> - - - - - diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index eae4f30..5a33238 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -17,4 +17,8 @@ Star Actors: Select an image from gallery Launch camera + launch dialog + Title of Dialog + This dialog is displaying some information. + Ok diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 0eb88fe..3ce0336 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -6,6 +6,8 @@ @color/colorPrimary @color/colorPrimaryDark @color/colorAccent + + @color/tealDark From b41e8a0e397e167dda7cb608d9c0dcf949434062 Mon Sep 17 00:00:00 2001 From: Mitch Tabian Date: Mon, 25 Nov 2019 10:55:09 -0800 Subject: [PATCH 7/9] showing a dialog, capturing input, validating input. --- app/build.gradle | 3 ++- .../MainActivityTest.kt | 20 +++++++++++++++---- .../espressouitestexamples/MainActivity.kt | 14 +++++++++++-- app/src/main/res/layout/activity_main.xml | 17 +++++++++++++++- app/src/main/res/values/strings.xml | 2 ++ 5 files changed, 48 insertions(+), 8 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 29de7bb..9bd0765 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -66,8 +66,9 @@ dependencies { annotationProcessor "com.github.bumptech.glide:compiler:$glide_version" // material dialogs - def matieral_dialogs_version = "3.1.0" + def matieral_dialogs_version = "3.1.1" implementation "com.afollestad.material-dialogs:core:$matieral_dialogs_version" + implementation "com.afollestad.material-dialogs:input:$matieral_dialogs_version" } diff --git a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt index 473dba9..01c4fe7 100644 --- a/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt +++ b/app/src/androidTest/java/com/codingwithmitch/espressouitestexamples/MainActivityTest.kt @@ -3,6 +3,7 @@ package com.codingwithmitch.espressouitestexamples import androidx.test.core.app.ActivityScenario import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.typeText import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.* @@ -16,20 +17,31 @@ class MainActivityTest{ @Test - fun test_showDialog_confirmVisible() { + fun test_showDialog_captureNameInput() { // GIVEN val activityScenario = ActivityScenario.launch(MainActivity::class.java) + val NAME = "Mitch" // Execute and Verify onView(withId(R.id.button_launch_dialog)).perform(click()) - onView(withText(R.string.text_title_of_dialog)).check(matches(isDisplayed())) + onView(withText(R.string.text_enter_name)).check(matches(isDisplayed())) onView(withText(R.string.text_ok)).perform(click()) - // 'doesNotExist' must be used here b/c material dialog isn't on view hierarchy - onView(withText(R.string.text_title_of_dialog)).check(doesNotExist()) + // make sure dialog is still visible (can't click ok without entering a name) + onView(withText(R.string.text_enter_name)).check(matches(isDisplayed())) + + // enter a name + onView(withId(R.id.md_input_message)).perform(typeText(NAME)) + + onView(withText(R.string.text_ok)).perform(click()) + + // make sure dialog is gone + onView(withText(R.string.text_enter_name)).check(doesNotExist()) + + onView(withId(R.id.text_name)).check(matches(withText(NAME))) } } diff --git a/app/src/main/java/com/codingwithmitch/espressouitestexamples/MainActivity.kt b/app/src/main/java/com/codingwithmitch/espressouitestexamples/MainActivity.kt index 586e95e..3699b9b 100644 --- a/app/src/main/java/com/codingwithmitch/espressouitestexamples/MainActivity.kt +++ b/app/src/main/java/com/codingwithmitch/espressouitestexamples/MainActivity.kt @@ -3,6 +3,7 @@ package com.codingwithmitch.espressouitestexamples import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.afollestad.materialdialogs.MaterialDialog +import com.afollestad.materialdialogs.input.input import kotlinx.android.synthetic.main.activity_main.* @@ -24,12 +25,21 @@ class MainActivity : AppCompatActivity(){ private fun showDialog(){ MaterialDialog(this) .show { - title(R.string.text_title_of_dialog) - message(R.string.text_some_information) + input ( + waitForPositiveButton = true, + allowEmpty = false + ){ dialog, name -> + setNameToTextView(name.toString()) + } + title(R.string.text_enter_name) positiveButton(R.string.text_ok) } } + private fun setNameToTextView(name: String){ + text_name.text = name + } + } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 5c5b0cb..b52e2be 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,6 +5,20 @@ android:layout_height="match_parent" android:id="@+id/main"> + + +