feat: route board and card taps to detail screens
This commit is contained in:
@@ -11,6 +11,9 @@ import androidx.test.espresso.action.ViewActions.click
|
|||||||
import androidx.test.espresso.action.ViewActions.longClick
|
import androidx.test.espresso.action.ViewActions.longClick
|
||||||
import androidx.test.espresso.action.ViewActions.replaceText
|
import androidx.test.espresso.action.ViewActions.replaceText
|
||||||
import androidx.test.espresso.assertion.ViewAssertions.matches
|
import androidx.test.espresso.assertion.ViewAssertions.matches
|
||||||
|
import androidx.test.espresso.intent.Intents
|
||||||
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent
|
||||||
|
import androidx.test.espresso.intent.matcher.IntentMatchers.hasExtra
|
||||||
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
import androidx.test.espresso.matcher.RootMatchers.isDialog
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
import androidx.test.espresso.matcher.ViewMatchers.withContentDescription
|
||||||
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
|
||||||
@@ -53,12 +56,14 @@ class BoardDetailFlowTest {
|
|||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
originalLocale = Locale.getDefault()
|
originalLocale = Locale.getDefault()
|
||||||
|
Intents.init()
|
||||||
defaultDataSource = FakeBoardDetailDataSource(initialDetail = detailOneList())
|
defaultDataSource = FakeBoardDetailDataSource(initialDetail = detailOneList())
|
||||||
BoardDetailActivity.testDataSourceFactory = { defaultDataSource }
|
BoardDetailActivity.testDataSourceFactory = { defaultDataSource }
|
||||||
}
|
}
|
||||||
|
|
||||||
@After
|
@After
|
||||||
fun tearDown() {
|
fun tearDown() {
|
||||||
|
Intents.release()
|
||||||
BoardDetailActivity.testDataSourceFactory = null
|
BoardDetailActivity.testDataSourceFactory = null
|
||||||
originalLocale?.let { Locale.setDefault(it) }
|
originalLocale?.let { Locale.setDefault(it) }
|
||||||
}
|
}
|
||||||
@@ -245,6 +250,29 @@ class BoardDetailFlowTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cardTapNavigatesToCardPlaceholderWithExtras() {
|
||||||
|
launchBoardDetail()
|
||||||
|
|
||||||
|
onView(withText("Card 1")).perform(click())
|
||||||
|
|
||||||
|
Intents.intended(hasComponent(CardDetailPlaceholderActivity::class.java.name))
|
||||||
|
Intents.intended(hasExtra(CardDetailPlaceholderActivity.EXTRA_CARD_ID, "card-1"))
|
||||||
|
Intents.intended(hasExtra(CardDetailPlaceholderActivity.EXTRA_CARD_TITLE, "Card 1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun cardTapBlankTitle_usesCardFallbackInPlaceholderExtra() {
|
||||||
|
defaultDataSource.currentDetail = detailWithCardTitle(" ")
|
||||||
|
launchBoardDetail()
|
||||||
|
|
||||||
|
onView(withId(R.id.cardItemRoot)).perform(click())
|
||||||
|
|
||||||
|
Intents.intended(hasComponent(CardDetailPlaceholderActivity::class.java.name))
|
||||||
|
Intents.intended(hasExtra(CardDetailPlaceholderActivity.EXTRA_CARD_ID, "card-1"))
|
||||||
|
Intents.intended(hasExtra(CardDetailPlaceholderActivity.EXTRA_CARD_TITLE, "Card"))
|
||||||
|
}
|
||||||
|
|
||||||
private fun launchBoardDetail(): ActivityScenario<BoardDetailActivity> {
|
private fun launchBoardDetail(): ActivityScenario<BoardDetailActivity> {
|
||||||
val intent = Intent(
|
val intent = Intent(
|
||||||
androidx.test.core.app.ApplicationProvider.getApplicationContext(),
|
androidx.test.core.app.ApplicationProvider.getApplicationContext(),
|
||||||
@@ -353,5 +381,24 @@ class BoardDetailFlowTest {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun detailWithCardTitle(title: String): BoardDetail {
|
||||||
|
return detailOneList().copy(
|
||||||
|
lists = listOf(
|
||||||
|
BoardListDetail(
|
||||||
|
id = "list-1",
|
||||||
|
title = "To Do",
|
||||||
|
cards = listOf(
|
||||||
|
BoardCardSummary(
|
||||||
|
id = "card-1",
|
||||||
|
title = title,
|
||||||
|
tags = emptyList(),
|
||||||
|
dueAtEpochMillis = null,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ class BoardsFlowTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun boardTapNavigatesToDetailPlaceholderWithExtras() {
|
fun boardTapNavigatesToBoardDetailActivity() {
|
||||||
MainActivity.dependencies.apiClientFactory = {
|
MainActivity.dependencies.apiClientFactory = {
|
||||||
FakeBoardsApiClient(
|
FakeBoardsApiClient(
|
||||||
boards = mutableListOf(BoardSummary("1", "Alpha")),
|
boards = mutableListOf(BoardSummary("1", "Alpha")),
|
||||||
@@ -58,9 +58,9 @@ class BoardsFlowTest {
|
|||||||
|
|
||||||
onView(withText("Alpha")).perform(click())
|
onView(withText("Alpha")).perform(click())
|
||||||
|
|
||||||
Intents.intended(hasComponent(BoardDetailPlaceholderActivity::class.java.name))
|
Intents.intended(hasComponent(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity::class.java.name))
|
||||||
Intents.intended(hasExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_ID, "1"))
|
Intents.intended(hasExtra(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity.EXTRA_BOARD_ID, "1"))
|
||||||
Intents.intended(hasExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_TITLE, "Alpha"))
|
Intents.intended(hasExtra(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity.EXTRA_BOARD_TITLE, "Alpha"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -79,8 +79,8 @@ class BoardsFlowTest {
|
|||||||
onView(withId(R.id.useTemplateChip)).perform(click())
|
onView(withId(R.id.useTemplateChip)).perform(click())
|
||||||
onView(withId(android.R.id.button1)).inRoot(isDialog()).perform(click())
|
onView(withId(android.R.id.button1)).inRoot(isDialog()).perform(click())
|
||||||
|
|
||||||
Intents.intended(hasComponent(BoardDetailPlaceholderActivity::class.java.name))
|
Intents.intended(hasComponent(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity::class.java.name))
|
||||||
Intents.intended(hasExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_TITLE, "Roadmap"))
|
Intents.intended(hasExtra(space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity.EXTRA_BOARD_TITLE, "Roadmap"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
android:theme="@style/Theme.Kanbn4Droid">
|
android:theme="@style/Theme.Kanbn4Droid">
|
||||||
<activity
|
<activity
|
||||||
android:name=".BoardDetailPlaceholderActivity"
|
android:name=".CardDetailPlaceholderActivity"
|
||||||
android:exported="false" />
|
android:exported="false" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".boarddetail.BoardDetailActivity"
|
android:name=".boarddetail.BoardDetailActivity"
|
||||||
|
|||||||
@@ -1,24 +0,0 @@
|
|||||||
package space.hackenslacker.kanbn4droid.app
|
|
||||||
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
|
|
||||||
class BoardDetailPlaceholderActivity : AppCompatActivity() {
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
setContentView(R.layout.activity_board_detail_placeholder)
|
|
||||||
|
|
||||||
val boardTitle = intent.getStringExtra(EXTRA_BOARD_TITLE).orEmpty()
|
|
||||||
val boardId = intent.getStringExtra(EXTRA_BOARD_ID).orEmpty()
|
|
||||||
|
|
||||||
val titleView: TextView = findViewById(R.id.boardDetailPlaceholderTitle)
|
|
||||||
titleView.text = getString(R.string.board_detail_placeholder_title, boardTitle, boardId)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val EXTRA_BOARD_ID = "extra_board_id"
|
|
||||||
const val EXTRA_BOARD_TITLE = "extra_board_title"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -35,6 +35,7 @@ import space.hackenslacker.kanbn4droid.app.boards.BoardsRepository
|
|||||||
import space.hackenslacker.kanbn4droid.app.boards.BoardsUiEvent
|
import space.hackenslacker.kanbn4droid.app.boards.BoardsUiEvent
|
||||||
import space.hackenslacker.kanbn4droid.app.boards.BoardsUiState
|
import space.hackenslacker.kanbn4droid.app.boards.BoardsUiState
|
||||||
import space.hackenslacker.kanbn4droid.app.boards.BoardsViewModel
|
import space.hackenslacker.kanbn4droid.app.boards.BoardsViewModel
|
||||||
|
import space.hackenslacker.kanbn4droid.app.boarddetail.BoardDetailActivity
|
||||||
|
|
||||||
class BoardsActivity : AppCompatActivity() {
|
class BoardsActivity : AppCompatActivity() {
|
||||||
private lateinit var sessionStore: SessionStore
|
private lateinit var sessionStore: SessionStore
|
||||||
@@ -245,9 +246,9 @@ class BoardsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun navigateToBoard(board: BoardSummary) {
|
private fun navigateToBoard(board: BoardSummary) {
|
||||||
startActivity(
|
startActivity(
|
||||||
Intent(this, BoardDetailPlaceholderActivity::class.java)
|
Intent(this, BoardDetailActivity::class.java)
|
||||||
.putExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_ID, board.id)
|
.putExtra(BoardDetailActivity.EXTRA_BOARD_ID, board.id)
|
||||||
.putExtra(BoardDetailPlaceholderActivity.EXTRA_BOARD_TITLE, board.title),
|
.putExtra(BoardDetailActivity.EXTRA_BOARD_TITLE, board.title),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
package space.hackenslacker.kanbn4droid.app
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
|
||||||
|
class CardDetailPlaceholderActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_card_detail_placeholder)
|
||||||
|
|
||||||
|
val cardId = intent.getStringExtra(EXTRA_CARD_ID).orEmpty()
|
||||||
|
val cardTitle = intent.getStringExtra(EXTRA_CARD_TITLE).orEmpty()
|
||||||
|
|
||||||
|
val titleView: TextView = findViewById(R.id.cardDetailPlaceholderTitle)
|
||||||
|
titleView.text = "$cardTitle\n(id: $cardId)"
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EXTRA_CARD_ID = "extra_card_id"
|
||||||
|
const val EXTRA_CARD_TITLE = "extra_card_title"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package space.hackenslacker.kanbn4droid.app.boarddetail
|
package space.hackenslacker.kanbn4droid.app.boarddetail
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.Menu
|
import android.view.Menu
|
||||||
import android.view.MenuItem
|
import android.view.MenuItem
|
||||||
@@ -17,6 +18,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
|||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import space.hackenslacker.kanbn4droid.app.MainActivity
|
import space.hackenslacker.kanbn4droid.app.MainActivity
|
||||||
|
import space.hackenslacker.kanbn4droid.app.CardDetailPlaceholderActivity
|
||||||
import space.hackenslacker.kanbn4droid.app.R
|
import space.hackenslacker.kanbn4droid.app.R
|
||||||
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
import space.hackenslacker.kanbn4droid.app.auth.ApiKeyStore
|
||||||
import space.hackenslacker.kanbn4droid.app.auth.HttpKanbnApiClient
|
import space.hackenslacker.kanbn4droid.app.auth.HttpKanbnApiClient
|
||||||
@@ -157,7 +159,21 @@ class BoardDetailActivity : AppCompatActivity() {
|
|||||||
viewModel.events.collect { event ->
|
viewModel.events.collect { event ->
|
||||||
when (event) {
|
when (event) {
|
||||||
is BoardDetailUiEvent.NavigateToCardPlaceholder -> {
|
is BoardDetailUiEvent.NavigateToCardPlaceholder -> {
|
||||||
Snackbar.make(pager, getString(R.string.board_detail_card_detail_coming_soon), Snackbar.LENGTH_SHORT).show()
|
val cardTitle = viewModel.uiState.value.boardDetail
|
||||||
|
?.lists
|
||||||
|
.orEmpty()
|
||||||
|
.asSequence()
|
||||||
|
.flatMap { list -> list.cards.asSequence() }
|
||||||
|
.firstOrNull { card -> card.id == event.cardId }
|
||||||
|
?.title
|
||||||
|
.orEmpty()
|
||||||
|
.trim()
|
||||||
|
.ifBlank { "Card" }
|
||||||
|
startActivity(
|
||||||
|
Intent(this@BoardDetailActivity, CardDetailPlaceholderActivity::class.java)
|
||||||
|
.putExtra(CardDetailPlaceholderActivity.EXTRA_CARD_ID, event.cardId)
|
||||||
|
.putExtra(CardDetailPlaceholderActivity.EXTRA_CARD_TITLE, cardTitle),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
is BoardDetailUiEvent.ShowServerError -> {
|
is BoardDetailUiEvent.ShowServerError -> {
|
||||||
|
|||||||
30
app/src/main/res/layout/activity_card_detail_placeholder.xml
Normal file
30
app/src/main/res/layout/activity_card_detail_placeholder.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:padding="24dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cardDetailPlaceholderTitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Headline5"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/cardDetailPlaceholderSubtitle"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/board_detail_card_detail_coming_soon"
|
||||||
|
android:textAppearance="@style/TextAppearance.MaterialComponents.Body1"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/cardDetailPlaceholderTitle" />
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
Reference in New Issue
Block a user