diff --git a/app/lint.xml b/app/lint.xml index 7b2c2b0..55028d1 100644 --- a/app/lint.xml +++ b/app/lint.xml @@ -20,6 +20,8 @@ + + diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 4c00cb7..76c8835 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -29,3 +29,5 @@ -dontwarn android.test.** -dontwarn kotlin.jvm.internal.Reflection -dontwarn com.google.errorprone.annotations.* +-dontwarn com.squareup.okhttp.** + diff --git a/app/src/androidTest/kotlin/app/AndroidTestApplication.kt b/app/src/androidTest/kotlin/app/AndroidTestApplication.kt index 5b23c9b..9cf6d01 100644 --- a/app/src/androidTest/kotlin/app/AndroidTestApplication.kt +++ b/app/src/androidTest/kotlin/app/AndroidTestApplication.kt @@ -16,10 +16,11 @@ internal open class AndroidTestApplication : MainApplication() { * @see app.gaming.TopGamingAllTimePostsActivity */ override fun buildTopGamingAllTimePostsFeatureComponent( - contentView: RecyclerView, errorView: View, progressView: View, activity: Activity) = + contentView: RecyclerView, errorView: View, progressView: View, guideView: View, + activity: Activity) = DaggerTopGamingAllTimePostsFeatureInstrumentationComponent.builder() .topGamingAllTimePostsFeatureInstrumentationModule( TopGamingAllTimePostsFeatureInstrumentationModule( - contentView, errorView, progressView, activity)) + contentView, errorView, progressView, guideView, activity)) .build() } diff --git a/app/src/androidTest/kotlin/app/gaming/TopGamingActivityInstrumentation.kt b/app/src/androidTest/kotlin/app/gaming/TopGamingActivityInstrumentation.kt index 0ead4b6..d38cdcd 100644 --- a/app/src/androidTest/kotlin/app/gaming/TopGamingActivityInstrumentation.kt +++ b/app/src/androidTest/kotlin/app/gaming/TopGamingActivityInstrumentation.kt @@ -95,7 +95,7 @@ internal class TopGamingActivityInstrumentation { @Test fun onLoadItemsAreShown() { SUBJECT = ReplaySubject.create() - SUBJECT.onNext(Post("0", "Bananas title", "r/bananas", 879, "bananaLink")) + SUBJECT.onNext(Post("0", "Bananas title", "r/bananas", 879, "bananaLink", "tb")) SUBJECT.onCompleted() launchActivity() onView(withId(R.id.progress)).check { view, _ -> @@ -123,7 +123,7 @@ internal class TopGamingActivityInstrumentation { @Test fun onItemClickIntentIsFired() { SUBJECT = ReplaySubject.create() - SUBJECT.onNext(Post("0", "Bananas title", "r/bananas", 879, "http://www.banan.as")) + SUBJECT.onNext(Post("0", "Bananas title", "r/bananas", 879, "http://www.banan.as", "tb")) SUBJECT.onCompleted() launchActivity() Intents.init() diff --git a/app/src/androidTest/kotlin/app/gaming/TopGamingPostsFeatureInstrumentationModule.kt b/app/src/androidTest/kotlin/app/gaming/TopGamingPostsFeatureInstrumentationModule.kt index 621e2fa..2b7a46c 100644 --- a/app/src/androidTest/kotlin/app/gaming/TopGamingPostsFeatureInstrumentationModule.kt +++ b/app/src/androidTest/kotlin/app/gaming/TopGamingPostsFeatureInstrumentationModule.kt @@ -27,6 +27,7 @@ internal class TopGamingAllTimePostsFeatureInstrumentationModule( private val contentView: RecyclerView, private val errorView: View, private val progressView: View, + private val guideView: View, private val activity: Activity) { @Provides @Singleton @@ -84,7 +85,7 @@ internal class TopGamingAllTimePostsFeatureInstrumentationModule( @Provides @Singleton fun topGamingAllTimePostsView() = TopGamingAllTimePostsView( - contentView, errorView, progressView) + contentView, errorView, progressView, guideView) @Provides @Singleton diff --git a/app/src/main/kotlin/app/MainApplication.kt b/app/src/main/kotlin/app/MainApplication.kt index a6fc9dd..0fc5b7c 100644 --- a/app/src/main/kotlin/app/MainApplication.kt +++ b/app/src/main/kotlin/app/MainApplication.kt @@ -24,10 +24,11 @@ internal open class MainApplication : Application() { * @see app.gaming.TopGamingAllTimePostsActivity */ internal open fun buildTopGamingAllTimePostsFeatureComponent( - contentView: RecyclerView, errorView: View, progressView: View, activity: Activity) = + contentView: RecyclerView, errorView: View, progressView: View, guideView: View, + activity: Activity) = DaggerTopGamingAllTimePostsFeatureComponent.builder() .topGamingAllTimePostsFeatureModule( TopGamingAllTimePostsFeatureModule( - contentView, errorView, progressView, activity)) + contentView, errorView, progressView, guideView, activity)) .build() } diff --git a/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsActivity.kt b/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsActivity.kt index 4afbac2..7c75cf7 100644 --- a/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsActivity.kt +++ b/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsActivity.kt @@ -107,7 +107,7 @@ class TopGamingAllTimePostsActivity : AppCompatActivity() { private fun inject() { (application as MainApplication).buildTopGamingAllTimePostsFeatureComponent( // https://kotlinlang.org/docs/tutorials/android-plugin.html#using-kotlin-android-extensions - content, error, progress, this).inject(this) + content, error, progress, scroll_guide, this).inject(this) } /** diff --git a/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsFeatureModule.kt b/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsFeatureModule.kt index 071e453..a8a3cab 100644 --- a/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsFeatureModule.kt +++ b/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsFeatureModule.kt @@ -39,6 +39,7 @@ internal class TopGamingAllTimePostsFeatureModule( private val contentView: RecyclerView, private val errorView: View, private val progressView: View, + private val guideView: View, private val activity: Activity) { @Provides @Singleton @@ -93,7 +94,7 @@ internal class TopGamingAllTimePostsFeatureModule( @Provides @Singleton fun topGamingAllTimePostsView() = TopGamingAllTimePostsView( - contentView, errorView, progressView) + contentView, errorView, progressView, guideView) @Provides @Singleton diff --git a/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsFeatureView.kt b/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsFeatureView.kt index cf58457..2158e7e 100644 --- a/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsFeatureView.kt +++ b/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsFeatureView.kt @@ -10,12 +10,13 @@ import android.view.View import android.view.ViewGroup import android.widget.Filter import android.widget.Filterable -import android.widget.FrameLayout +import android.widget.ImageView import android.widget.TextView +import com.squareup.picasso.Callback +import com.squareup.picasso.Picasso import domain.entity.Post import org.jorge.ms.app.R import util.android.HtmlCompat -import util.android.ext.getDimension /** * Configuration for the recycler view holding the post list. @@ -82,7 +83,7 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder(LayoutInflater.from(parent.context).inflate( - R.layout.item_post, parent, false), recyclerView, { callback.onItemClicked(it) }) + R.layout.item_post, parent, false), { callback.onItemClicked(it) }) override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.render(shownItems[position]) @@ -95,7 +96,7 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav return } // This is used to take the latest valid value in the given payload list - val combinator: (Bundle, String) -> Unit = { bundle, key -> + val folder: (Bundle, String) -> Unit = { bundle, key -> @Suppress("UNCHECKED_CAST") bundle.putString(key, (payloads as List).fold(Bundle(), { old, new -> val oldTitle = old.getString(key) @@ -104,8 +105,8 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav }).getString(key)) } val combinedBundle = Bundle().also { bundle -> - arrayOf(KEY_TITLE, KEY_SUBREDDIT, KEY_SCORE).forEach { - combinator(bundle, it) + arrayOf(KEY_TITLE, KEY_SUBREDDIT, KEY_SCORE, KEY_THUMBNAIL).forEach { + folder(bundle, it) } } // Now combinedBundle contains the latest version of each of the fields that can be updated @@ -183,9 +184,10 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav } override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int) = - shownItems[oldItemPosition].let { (_, oldTitle, oldSubreddit, oldScore) -> + shownItems[oldItemPosition].let { + (_, oldTitle, oldSubreddit, oldScore, oldThumbnail) -> filteredItems[newItemPosition].let { - (_, newTitle, newSubreddit, newScore) -> + (_, newTitle, newSubreddit, newScore, newThumbnail) -> Bundle().apply { putString(KEY_TITLE, newTitle.takeIf { !it.contentEquals(oldTitle) @@ -196,6 +198,9 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav putString(KEY_SCORE, "${newScore.takeIf { it != oldScore }}") + putString(KEY_THUMBNAIL, newThumbnail.takeIf { + it != oldThumbnail + }) } } } @@ -209,7 +214,6 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav override fun publishResults(constraint: CharSequence?, results: FilterResults?) { @Suppress("UNCHECKED_CAST") shownItems = results?.values as List? ?: items - (recyclerView.layoutParams as FrameLayout.LayoutParams).bottomMargin = 0 diff.dispatchUpdatesTo(this@Adapter) } @@ -222,23 +226,25 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav /** * Very simple viewholder that sets text and click event handling. * @param itemView The view to dump the held data. + * @param onItemClicked What to run when a click happens. */ internal class ViewHolder internal constructor( itemView: View, - private val recyclerView: RecyclerView, private val onItemClicked: (Post) -> Unit): RecyclerView.ViewHolder(itemView) { private val titleView: TextView = itemView.findViewById(R.id.text_title) as TextView private val scoreView: TextView = itemView.findViewById(R.id.text_score) as TextView private val subredditView: TextView = itemView.findViewById(R.id.text_subreddit) as TextView + private val thumbnailView: ImageView = itemView.findViewById(R.id.thumbnail) as ImageView /** * Draw an item. * @title The item to draw. */ internal fun render(item: Post) { - titleView.text = HtmlCompat.fromHtml(item.title) - subredditView.text = item.subreddit - scoreView.text = item.score.toString() + setTitle(item.title) + setSubreddit(item.subreddit) + setScore(item.score) + setThumbnail(item.thumbnailLink) itemView.setOnClickListener { onItemClicked(item) } } @@ -248,10 +254,12 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav * @param item The item these updates correspond to. */ internal fun renderPartial(bundle: Bundle, item: Post) { - bundle.getString(KEY_TITLE).takeIf { it != null }.let { titleView.text = - HtmlCompat.fromHtml(it!!) } - bundle.getString(KEY_SUBREDDIT).takeIf { it != null }.let { subredditView.text = it } - bundle.getString(KEY_SCORE).takeIf { it != null }.let { scoreView.text = it } + bundle.getString(KEY_TITLE).takeIf { it != null }.let { setTitle(it!!) } + bundle.getString(KEY_SUBREDDIT).takeIf { it != null }.let { setSubreddit(it!!) } + bundle.getString(KEY_SCORE).takeIf { it != null }.let { + setScore(Integer.valueOf(it!!)) + } + setThumbnail(bundle.getString(KEY_THUMBNAIL)) itemView.setOnClickListener { onItemClicked(item) } } @@ -259,8 +267,56 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav * Adds a margin under the recycler view for the progress and error views to show. */ internal fun addBottomMargin() { - (recyclerView.layoutParams as FrameLayout.LayoutParams).bottomMargin = - itemView.context.getDimension(R.dimen.footer_padding).toInt() + } + + /** + * Updates the layout according to the changes required by a new title. + * @param title The new title. + */ + private fun setTitle(title: String) { + val formattedTitle = HtmlCompat.fromHtml(title) + titleView.text = formattedTitle + thumbnailView.contentDescription = formattedTitle.toString() + } + + /** + * Updates the layout according to the changes required by a new subreddit. + * @param name The new subreddit name. + */ + private fun setSubreddit(name: String) { + subredditView.text = name + } + + /** + * Updates the layout according to the changes required by a new score. + * @param score The new score. + */ + private fun setScore(score: Int) { + scoreView.text = score.toString() + } + + /** + * Updates the layout according to the changes required by a new thumbnail link. + * @param thumbnailLink The new thumbnail link, or null if none is applicable. + */ + private fun setThumbnail(thumbnailLink: String?) { + if (thumbnailLink != null) { + Picasso.with(thumbnailView.context) + .load(thumbnailLink) + .into(thumbnailView, object : Callback { + override fun onError() { + thumbnailView.visibility = View.GONE + thumbnailView.setImageDrawable(null) + } + + override fun onSuccess() { + thumbnailView.visibility = View.VISIBLE + } + }) + } else { + thumbnailView.visibility = View.GONE + thumbnailView.setImageDrawable(null) + } } } @@ -268,6 +324,7 @@ internal class Adapter(private val callback: TopGamingAllTimePostsActivity.Behav private val KEY_TITLE = "KEY_TITLE" private val KEY_SUBREDDIT = "KEY_SUBREDDIT" private val KEY_SCORE = "KEY_SCORE" + private val KEY_THUMBNAIL = "KEY_THUMBNAIL" } } diff --git a/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsView.kt b/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsView.kt index f984de2..a3be93e 100644 --- a/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsView.kt +++ b/app/src/main/kotlin/app/gaming/TopGamingAllTimePostsView.kt @@ -2,8 +2,11 @@ package app.gaming import android.support.v7.widget.RecyclerView import android.view.View +import android.widget.FrameLayout import app.LoadableContentView import domain.entity.Post +import org.jorge.ms.app.R +import util.android.ext.getDimension /** * Wraps UI behavior for top all time gaming posts scenario. @@ -11,9 +14,12 @@ import domain.entity.Post internal class TopGamingAllTimePostsView( internal val contentView: RecyclerView, internal val errorView: View, - private val progressView: View) : LoadableContentView { + private val progressView: View, + private val guideView: View) : LoadableContentView { override fun showLoadingLayout() { + pushInfoArea() progressView.visibility = View.VISIBLE + guideView.visibility = View.INVISIBLE } override fun hideLoadingLayout() { @@ -22,13 +28,21 @@ internal class TopGamingAllTimePostsView( override fun updateContent(actionResult: List) { (contentView.adapter as Adapter).addItems(actionResult) + guideView.visibility = View.VISIBLE } override fun showErrorLayout() { + pushInfoArea() errorView.visibility = View.VISIBLE + guideView.visibility = View.INVISIBLE } override fun hideErrorLayout() { errorView.visibility = View.GONE } + + private fun pushInfoArea() { + (contentView.layoutParams as FrameLayout.LayoutParams).bottomMargin = + contentView.context.getDimension(R.dimen.footer_padding).toInt() + } } diff --git a/app/src/main/res/layout-v21/item_post.xml b/app/src/main/res/layout-v21/item_post.xml index 9c2c8a5..81ee944 100644 --- a/app/src/main/res/layout-v21/item_post.xml +++ b/app/src/main/res/layout-v21/item_post.xml @@ -3,6 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" + xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:padding="@dimen/appbar_horizontal_padding" android:theme="@style/AppTheme.Card"> @@ -10,13 +11,20 @@ android:id="@+id/text_title" android:layout_width="wrap_content" android:layout_height="wrap_content" + android:layout_marginBottom="@dimen/section_separator" android:textIsSelectable="false" android:clickable="false" style="@style/AppTheme.TextTitle" /> + diff --git a/app/src/main/res/layout/include_top_posts_view.xml b/app/src/main/res/layout/include_top_posts_view.xml index 2c2c36f..e495345 100644 --- a/app/src/main/res/layout/include_top_posts_view.xml +++ b/app/src/main/res/layout/include_top_posts_view.xml @@ -36,4 +36,14 @@ android:textStyle="bold" android:text="@string/top_posts_error_action" /> + diff --git a/app/src/main/res/layout/item_post.xml b/app/src/main/res/layout/item_post.xml index 50bf495..50fcd1f 100644 --- a/app/src/main/res/layout/item_post.xml +++ b/app/src/main/res/layout/item_post.xml @@ -1,6 +1,7 @@ + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 55180e9..dfe7a4b 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -3,5 +3,6 @@ reddit Error loading Retry + Scroll down to see more! Search r/gaming]]> diff --git a/app/src/test/kotlin/app/gaming/TopGamingAllTimePostsViewSpek.kt b/app/src/test/kotlin/app/gaming/TopGamingAllTimePostsViewSpek.kt deleted file mode 100644 index 8c7f2fd..0000000 --- a/app/src/test/kotlin/app/gaming/TopGamingAllTimePostsViewSpek.kt +++ /dev/null @@ -1,70 +0,0 @@ -package app.gaming - -import android.support.v7.widget.RecyclerView -import android.view.View -import com.nhaarman.mockito_kotlin.doAnswer -import com.nhaarman.mockito_kotlin.doReturn -import com.nhaarman.mockito_kotlin.eq -import com.nhaarman.mockito_kotlin.mock -import com.nhaarman.mockito_kotlin.reset -import com.nhaarman.mockito_kotlin.verify -import com.nhaarman.mockito_kotlin.verifyNoMoreInteractions -import com.nhaarman.mockito_kotlin.whenever -import domain.entity.Post -import org.jetbrains.spek.api.SubjectSpek -import org.jetbrains.spek.api.dsl.it -import org.junit.platform.runner.JUnitPlatform -import org.junit.runner.RunWith - -/** - * Tests for the all-time top in r/gaming view - * @see TopGamingAllTimePostsView - */ -@RunWith(JUnitPlatform::class) -internal class TopGamingAllTimePostsViewSpek : SubjectSpek({ - subject { TopGamingAllTimePostsView(MOCK_RECYCLER_VIEW, MOCK_ERROR_VIEW, MOCK_PROGRESS_VIEW) } - - afterEachTest { reset(MOCK_RECYCLER_VIEW, MOCK_ERROR_VIEW, MOCK_PROGRESS_VIEW) } - - it ("should make the progress view visible") { - subject.showLoadingLayout() - verify(MOCK_PROGRESS_VIEW).visibility = eq(View.VISIBLE) - verifyNoMoreInteractions(MOCK_RECYCLER_VIEW, MOCK_ERROR_VIEW, MOCK_PROGRESS_VIEW) - } - - it ("should make the progress view go away") { - subject.hideLoadingLayout() - verify(MOCK_PROGRESS_VIEW).visibility = eq(View.GONE) - verifyNoMoreInteractions(MOCK_RECYCLER_VIEW, MOCK_ERROR_VIEW, MOCK_PROGRESS_VIEW) - } - - it ("should update the content") { - val mockList = mock>() - val mockAdapter = mock { - onGeneric { addItems(eq(mockList)) } doAnswer { } - } - whenever (MOCK_RECYCLER_VIEW.adapter) doReturn mockAdapter - subject.updateContent(mockList) - verify(MOCK_RECYCLER_VIEW).adapter - verify(mockAdapter).addItems(eq(mockList)) - verifyNoMoreInteractions(MOCK_RECYCLER_VIEW, MOCK_ERROR_VIEW, MOCK_PROGRESS_VIEW) - } - - it ("should make the error view visible") { - subject.showErrorLayout() - verify(MOCK_ERROR_VIEW).visibility = eq(View.VISIBLE) - verifyNoMoreInteractions(MOCK_RECYCLER_VIEW, MOCK_ERROR_VIEW, MOCK_PROGRESS_VIEW) - } - - it ("should make the error view go away") { - subject.hideErrorLayout() - verify(MOCK_ERROR_VIEW).visibility = eq(View.GONE) - verifyNoMoreInteractions(MOCK_RECYCLER_VIEW, MOCK_ERROR_VIEW, MOCK_PROGRESS_VIEW) - } -}) { - companion object { - private val MOCK_RECYCLER_VIEW = mock() - private val MOCK_ERROR_VIEW = mock() - private val MOCK_PROGRESS_VIEW = mock() - } -} diff --git a/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker b/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker deleted file mode 100644 index 1f0955d..0000000 --- a/app/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker +++ /dev/null @@ -1 +0,0 @@ -mock-maker-inline diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index de32761..b84c4d8 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -1,12 +1,13 @@ final def daggerVersion = "2.10" -final def firebaseVersion = "10.2.4" final def espressoVersion = "2.2.2" +final def firebaseVersion = "10.2.4" final def jsr250Version = "1.0" final def junitPlatformRunnerVersion = "1.0.0-M3" final def jUnitVersion = "4.12" final def kotlinVersion = rootProject.ext.kotlinVersion final def mockitoKotlinVersion = "1.4.0" final def mockitoAndroidVersion = "2.7.22" +final def picassoVersion = "2.5.2" final def retrofitVersion = "2.1.0" final def rxAndroidVersion = "1.2.1" final def rxJavaVersion = "1.2.6" @@ -65,7 +66,8 @@ ext { "com.android.support:appcompat-v7:$supportVersion", "com.android.support:design:$supportVersion", "com.google.firebase:firebase-crash:$firebaseVersion", - "com.google.dagger:dagger-android:$daggerVersion" + "com.google.dagger:dagger-android:$daggerVersion", + "com.squareup.picasso:picasso:$picassoVersion" ] providedUtilAndroidDependencies = [ "com.android.support:support-annotations:$supportVersion" diff --git a/data/src/main/kotlin/data/common/DataPost.kt b/data/src/main/kotlin/data/common/DataPost.kt index 7bc76e2..d1b2245 100644 --- a/data/src/main/kotlin/data/common/DataPost.kt +++ b/data/src/main/kotlin/data/common/DataPost.kt @@ -11,7 +11,8 @@ internal data class DataPost( @Json(name = "title") val title: String, @Json(name = "subreddit_name_prefixed") val subreddit: String, @Json(name = "score") val score: Int, - @Json(name = "permalink") val permalink: String) { + @Json(name = "permalink") val permalink: String, + @Json(name= "thumbnail") val thumbnailLink: String) { override fun hashCode() = id.hashCode() override fun equals(other: Any?) = other is DataPost && id == other.id diff --git a/data/src/main/kotlin/data/top/TopRequestEntityMapper.kt b/data/src/main/kotlin/data/top/TopRequestEntityMapper.kt index 9a06f36..efe5e2b 100644 --- a/data/src/main/kotlin/data/top/TopRequestEntityMapper.kt +++ b/data/src/main/kotlin/data/top/TopRequestEntityMapper.kt @@ -17,5 +17,6 @@ internal class TopRequestEntityMapper { dataPost.title, dataPost.subreddit, dataPost.score, - "${BuildConfig.API_URL}${dataPost.permalink.drop(1)}") + "${BuildConfig.API_URL}${dataPost.permalink.drop(1)}", + dataPost.thumbnailLink) } diff --git a/data/src/test/kotlin/data/top/TopRequestEntityMapperSpek.kt b/data/src/test/kotlin/data/top/TopRequestEntityMapperSpek.kt index cc07fe7..d7400ad 100644 --- a/data/src/test/kotlin/data/top/TopRequestEntityMapperSpek.kt +++ b/data/src/test/kotlin/data/top/TopRequestEntityMapperSpek.kt @@ -18,22 +18,22 @@ internal class TopRequestEntityMapperSpek : SubjectSpek( subject { TopRequestEntityMapper() } // <- Specify the test subject (the singleton instance, in this case) it ("should transform a happy case") { - val source = DataPost("r54553", "random title", "random subreddit", 23, "some permalink") + val source = DataPost("r54553", "random title", "random subreddit", 23, "some permalink", "https://media.giphy.com/media/3o6ZtdtckQKDQWAet2/giphy.gif") assertEquivalent(source, subject.transform(source)) } it ("should transform an all empty/0s case") { - val source = DataPost("", "", "", 0, "") + val source = DataPost("", "", "", 0, "", "") assertEquivalent(source, subject.transform(source)) } it ("should transform a mixed case") { - val source = DataPost("aa", "", "another subreddit", 0, "another permalink") + val source = DataPost("aa", "", "another subreddit", 0, "another permalink", "self") assertEquivalent(source, subject.transform(source)) } it ("should transform when score is negative") { - val source = DataPost("87", "a title", "yet another subreddit", -7, "one more permalink") + val source = DataPost("87", "a title", "yet another subreddit", -7, "one more permalink", "feafea") assertEquivalent(source, subject.transform(source)) } }) { @@ -44,6 +44,7 @@ internal class TopRequestEntityMapperSpek : SubjectSpek( assertEquals(dataPost.subreddit, post.subreddit) assertEquals(dataPost.score, post.score) assertEquals("${BuildConfig.API_URL}${dataPost.permalink.drop(1)}", post.detailLink) + assertEquals(dataPost.thumbnailLink, post.thumbnailLink) } } } diff --git a/domain/src/main/kotlin/domain/entity/Post.kt b/domain/src/main/kotlin/domain/entity/Post.kt index 86370eb..e354fca 100644 --- a/domain/src/main/kotlin/domain/entity/Post.kt +++ b/domain/src/main/kotlin/domain/entity/Post.kt @@ -9,7 +9,8 @@ data class Post( val title: String, val subreddit: String, val score: Int, - val detailLink: String) { + val detailLink: String, + val thumbnailLink: String) { override fun hashCode() = id.hashCode() override fun equals(other: Any?) = other is Post && id == other.id diff --git a/domain/src/test/kotlin/domain/interactor/TopGamingAllTimePostsFetchUseCaseSpek.kt b/domain/src/test/kotlin/domain/interactor/TopGamingAllTimePostsFetchUseCaseSpek.kt index 7bf641d..cc24074 100644 --- a/domain/src/test/kotlin/domain/interactor/TopGamingAllTimePostsFetchUseCaseSpek.kt +++ b/domain/src/test/kotlin/domain/interactor/TopGamingAllTimePostsFetchUseCaseSpek.kt @@ -38,9 +38,9 @@ internal class TopGamingAllTimePostsFetchUseCaseSpek : SubjectSpek() // Cannot mock Post as it is a data class - val values = arrayOf(Post("", "title", "sr", -8, "permalink"), - Post("rafe", "titfle", "eeesr", 9, ""), - Post("123", "titlea", "sr", 0, "pfaefaermalink")) + val values = arrayOf(Post("", "title", "sr", -8, "permalink", "a"), + Post("rafe", "titfle", "eeesr", 9, "", "a"), + Post("123", "titlea", "sr", 0, "pfaefaermalink", "a")) whenever(MOCK_FACADE.fetchTop(any(), any(), any())) doReturn Observable.from(values) subject.execute(testSubscriber) testSubscriber.awaitTerminalEvent() diff --git a/domain/src/test/kotlin/domain/interactor/TopGamingAllTimePostsGetUseCaseSpek.kt b/domain/src/test/kotlin/domain/interactor/TopGamingAllTimePostsGetUseCaseSpek.kt index 7545fd8..4d9688f 100644 --- a/domain/src/test/kotlin/domain/interactor/TopGamingAllTimePostsGetUseCaseSpek.kt +++ b/domain/src/test/kotlin/domain/interactor/TopGamingAllTimePostsGetUseCaseSpek.kt @@ -38,9 +38,9 @@ internal class TopGamingAllTimePostsGetUseCaseSpek : SubjectSpek() // Cannot mock Post as it is a data class - val values = arrayOf(Post("", "title", "sr", -8, "permalink"), - Post("fafe", "titfle", "eeesr", 9, ""), - Post("id-1 23132", "titlea", "sr", 0, "pfaefaermalink")) + val values = arrayOf(Post("", "title", "sr", -8, "permalink", "a"), + Post("fafe", "titfle", "eeesr", 9, "", "a"), + Post("id-1 23132", "titlea", "sr", 0, "pfaefaermalink", "a")) whenever(MOCK_FACADE.getTop(any(), any(), any())) doReturn Observable.from(values) subject.execute(testSubscriber) testSubscriber.awaitTerminalEvent()