diff --git a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt index accb3b28c..54053a1bb 100644 --- a/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt +++ b/app/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/ui/NavigationTest.kt @@ -16,6 +16,7 @@ package com.google.samples.apps.nowinandroid.ui +import androidx.compose.ui.semantics.SemanticsActions.ScrollBy import androidx.compose.ui.test.assertCountEquals import androidx.compose.ui.test.assertIsOn import androidx.compose.ui.test.assertIsSelected @@ -290,9 +291,19 @@ class NavigationTest { // Note: Possible flakiness. If the content of the news resource is long then the topic // tag might not be visible meaning it cannot be clicked onNodeWithTag("forYou:feed") - .performScrollToNode( - hasTestTag("newsResourceCard:${newsResource.id}"), - ) + .performScrollToNode(hasTestTag("newsResourceCard:${newsResource.id}")) + .fetchSemanticsNode() + .apply { + val newsResourceCardNode = onNodeWithTag("newsResourceCard:${newsResource.id}") + .fetchSemanticsNode() + config[ScrollBy].action?.invoke( + 0f, + // to ensure the bottom of the card is visible, + // manually scroll the difference between the height of + // the scrolling node and the height of the card + (newsResourceCardNode.size.height - size.height).coerceAtLeast(0).toFloat(), + ) + } // Click the first topic tag onAllNodesWithTag("topicTag:${topic.id}", useUnmergedTree = true) diff --git a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt index 24baa4622..da90eae61 100644 --- a/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt +++ b/core/data-test/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/test/repository/FakeNewsRepository.kt @@ -17,13 +17,13 @@ package com.google.samples.apps.nowinandroid.core.data.test.repository import com.google.samples.apps.nowinandroid.core.data.Synchronizer +import com.google.samples.apps.nowinandroid.core.data.model.asExternalModel import com.google.samples.apps.nowinandroid.core.data.repository.NewsRepository import com.google.samples.apps.nowinandroid.core.data.repository.NewsResourceQuery import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.network.Dispatcher import com.google.samples.apps.nowinandroid.core.network.NiaDispatchers.IO import com.google.samples.apps.nowinandroid.core.network.demo.DemoNiaNetworkDataSource -import com.google.samples.apps.nowinandroid.core.network.model.asExternalModel import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow diff --git a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt index c3ad91dfe..01d0905f0 100644 --- a/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt +++ b/core/data/src/main/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NewsResource.kt @@ -19,8 +19,10 @@ package com.google.samples.apps.nowinandroid.core.data.model import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity +import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource -import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResourceExpanded +import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic +import com.google.samples.apps.nowinandroid.core.network.model.asExternalModel fun NetworkNewsResource.asEntity() = NewsResourceEntity( id = id, @@ -32,16 +34,6 @@ fun NetworkNewsResource.asEntity() = NewsResourceEntity( type = type, ) -fun NetworkNewsResourceExpanded.asEntity() = NewsResourceEntity( - id = id, - title = title, - content = content, - url = url, - headerImageUrl = headerImageUrl, - publishDate = publishDate, - type = type, -) - /** * A shell [TopicEntity] to fulfill the foreign key constraint when inserting * a [NewsResourceEntity] into the DB @@ -65,3 +57,17 @@ fun NetworkNewsResource.topicCrossReferences(): List topicId = topicId, ) } + +fun NetworkNewsResource.asExternalModel(topics: List) = + NewsResource( + id = id, + title = title, + content = content, + url = url, + headerImageUrl = headerImageUrl, + publishDate = publishDate, + type = type, + topics = topics + .filter { networkTopic -> this.topics.contains(networkTopic.id) } + .map(NetworkTopic::asExternalModel), + ) diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt deleted file mode 100644 index 7dd251a99..000000000 --- a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityKtTest.kt +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.data.model - -import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource -import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResourceExpanded -import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic -import kotlinx.datetime.Instant -import org.junit.Test -import kotlin.test.assertEquals - -class NetworkEntityKtTest { - - @Test - fun network_topic_can_be_mapped_to_topic_entity() { - val networkModel = NetworkTopic( - id = "0", - name = "Test", - shortDescription = "short description", - longDescription = "long description", - url = "URL", - imageUrl = "image URL", - ) - val entity = networkModel.asEntity() - - assertEquals("0", entity.id) - assertEquals("Test", entity.name) - assertEquals("short description", entity.shortDescription) - assertEquals("long description", entity.longDescription) - assertEquals("URL", entity.url) - assertEquals("image URL", entity.imageUrl) - } - - @Test - fun network_news_resource_can_be_mapped_to_news_resource_entity() { - val networkModel = - NetworkNewsResource( - id = "0", - title = "title", - content = "content", - url = "url", - headerImageUrl = "headerImageUrl", - publishDate = Instant.fromEpochMilliseconds(1), - type = "Article 📚", - ) - val entity = networkModel.asEntity() - - assertEquals("0", entity.id) - assertEquals("title", entity.title) - assertEquals("content", entity.content) - assertEquals("url", entity.url) - assertEquals("headerImageUrl", entity.headerImageUrl) - assertEquals(Instant.fromEpochMilliseconds(1), entity.publishDate) - assertEquals("Article 📚", entity.type) - - val expandedNetworkModel = - NetworkNewsResourceExpanded( - id = "0", - title = "title", - content = "content", - url = "url", - headerImageUrl = "headerImageUrl", - publishDate = Instant.fromEpochMilliseconds(1), - type = "Article 📚", - ) - - val entityFromExpanded = expandedNetworkModel.asEntity() - - assertEquals("0", entityFromExpanded.id) - assertEquals("title", entityFromExpanded.title) - assertEquals("content", entityFromExpanded.content) - assertEquals("url", entityFromExpanded.url) - assertEquals("headerImageUrl", entityFromExpanded.headerImageUrl) - assertEquals(Instant.fromEpochMilliseconds(1), entityFromExpanded.publishDate) - assertEquals("Article 📚", entityFromExpanded.type) - } -} diff --git a/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityTest.kt b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityTest.kt new file mode 100644 index 000000000..bde968810 --- /dev/null +++ b/core/data/src/test/kotlin/com/google/samples/apps/nowinandroid/core/data/model/NetworkEntityTest.kt @@ -0,0 +1,133 @@ +/* + * Copyright 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.data.model + +import com.google.samples.apps.nowinandroid.core.model.data.NewsResource +import com.google.samples.apps.nowinandroid.core.model.data.Topic +import com.google.samples.apps.nowinandroid.core.network.model.NetworkNewsResource +import com.google.samples.apps.nowinandroid.core.network.model.NetworkTopic +import kotlinx.datetime.Instant +import org.junit.Test +import kotlin.test.assertEquals + +class NetworkEntityTest { + + @Test + fun networkTopicMapsToDatabaseModel() { + val networkModel = NetworkTopic( + id = "0", + name = "Test", + shortDescription = "short description", + longDescription = "long description", + url = "URL", + imageUrl = "image URL", + ) + val entity = networkModel.asEntity() + + assertEquals("0", entity.id) + assertEquals("Test", entity.name) + assertEquals("short description", entity.shortDescription) + assertEquals("long description", entity.longDescription) + assertEquals("URL", entity.url) + assertEquals("image URL", entity.imageUrl) + } + + @Test + fun networkNewsResourceMapsToDatabaseModel() { + val networkModel = + NetworkNewsResource( + id = "0", + title = "title", + content = "content", + url = "url", + headerImageUrl = "headerImageUrl", + publishDate = Instant.fromEpochMilliseconds(1), + type = "Article 📚", + ) + val entity = networkModel.asEntity() + + assertEquals("0", entity.id) + assertEquals("title", entity.title) + assertEquals("content", entity.content) + assertEquals("url", entity.url) + assertEquals("headerImageUrl", entity.headerImageUrl) + assertEquals(Instant.fromEpochMilliseconds(1), entity.publishDate) + assertEquals("Article 📚", entity.type) + } + + @Test + fun networkNewsResourceMapsToExternalModel() { + val networkNewsResource = NetworkNewsResource( + id = "0", + title = "title", + content = "content", + url = "url", + headerImageUrl = "headerImageUrl", + publishDate = Instant.fromEpochMilliseconds(1), + type = "Article 📚", + topics = listOf("1", "2"), + ) + + val networkTopics = listOf( + NetworkTopic( + id = "1", + name = "Test 1", + shortDescription = "short description 1", + longDescription = "long description 1", + url = "url 1", + imageUrl = "imageUrl 1", + ), + NetworkTopic( + id = "2", + name = "Test 2", + shortDescription = "short description 2", + longDescription = "long description 2", + url = "url 2", + imageUrl = "imageUrl 2", + ), + ) + + val expected = NewsResource( + id = "0", + title = "title", + content = "content", + url = "url", + headerImageUrl = "headerImageUrl", + publishDate = Instant.fromEpochMilliseconds(1), + type = "Article 📚", + topics = listOf( + Topic( + id = "1", + name = "Test 1", + shortDescription = "short description 1", + longDescription = "long description 1", + url = "url 1", + imageUrl = "imageUrl 1", + ), + Topic( + id = "2", + name = "Test 2", + shortDescription = "short description 2", + longDescription = "long description 2", + url = "url 2", + imageUrl = "imageUrl 2", + ), + ), + ) + assertEquals(expected, networkNewsResource.asExternalModel(networkTopics)) + } +} diff --git a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt index 372d8c85b..7b66af796 100644 --- a/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt +++ b/core/network/src/main/kotlin/com/google/samples/apps/nowinandroid/core/network/model/NetworkNewsResource.kt @@ -34,32 +34,3 @@ data class NetworkNewsResource( val type: String, val topics: List = listOf(), ) - -fun NetworkNewsResource.asExternalModel(topics: List) = - NewsResource( - id = id, - title = title, - content = content, - url = url, - headerImageUrl = headerImageUrl, - publishDate = publishDate, - type = type, - topics = topics - .filter { networkTopic -> this.topics.contains(networkTopic.id) } - .map(NetworkTopic::asExternalModel), - ) - -/** - * Network representation of [NewsResource] when fetched from /newsresources/{id} - */ -@Serializable -data class NetworkNewsResourceExpanded( - val id: String, - val title: String, - val content: String, - val url: String, - val headerImageUrl: String, - val publishDate: Instant, - val type: String, - val topics: List = listOf(), -)