Skip to content

Commit 9839944

Browse files
nbradburyclaude
andauthored
Add missing tests for RS post settings (#22751)
* Add comprehensive tests for RS post settings Cover date/time selection, author selection and search, categories/tags events, featured image selection, dismiss dialog cleanup, save-while-saving guard, retry, and UiState computed properties (effective* and hasChanges). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Simplify RS post settings ViewModel tests Remove 5 redundant hasChanges tests (already covered by UiState tests) and fold 3 single-test regions into their related regions (Init, Dialog state, Save). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Strengthen test assertions for date, featured image, and sticky - Assert actual date components (year/month/day, hour/minute) instead of just isNotNull - Verify featuredImage stays Empty in the same-id no-op test - Add effectiveSticky false-original test to distinguish from default Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Prune redundant RS post settings tests Delete PostRsSettingsUiStateTest (trivial property tests) and remove 9 low-value ViewModel tests (symmetric duplicates, trivial side effects, weak assertions). Keeps 25 new focused tests. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 97dc2ad commit 9839944

1 file changed

Lines changed: 235 additions & 0 deletions

File tree

WordPress/src/test/java/org/wordpress/android/ui/postsrs/PostRsSettingsViewModelTest.kt

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.wordpress.android.util.NetworkUtilsWrapper
2323
import org.wordpress.android.viewmodel.ResourceProvider
2424
import uniffi.wp_api.PostFormat
2525
import uniffi.wp_api.PostStatus
26+
import java.util.Calendar
2627

2728
@ExperimentalCoroutinesApi
2829
@Suppress("LargeClass")
@@ -140,6 +141,18 @@ class PostRsSettingsViewModelTest :
140141
.isNotNull()
141142
}
142143

144+
@Test
145+
fun `retry while offline shows error`() {
146+
val viewModel = createViewModel()
147+
148+
viewModel.retry()
149+
150+
assertThat(viewModel.uiState.value.isLoading)
151+
.isFalse()
152+
assertThat(viewModel.uiState.value.error)
153+
.isNotNull()
154+
}
155+
143156
// endregion
144157

145158
// region Edit methods
@@ -361,6 +374,19 @@ class PostRsSettingsViewModelTest :
361374
.isEqualTo(DialogState.None)
362375
}
363376

377+
@Test
378+
fun `onDismissDialog clears author search state`() {
379+
val viewModel = createViewModel()
380+
381+
viewModel.onDismissDialog()
382+
383+
val state = viewModel.uiState.value
384+
assertThat(state.authorSearchQuery).isEmpty()
385+
assertThat(state.isSearchingAuthors).isFalse()
386+
assertThat(state.siteAuthors).isEmpty()
387+
assertThat(state.canLoadMoreAuthors).isFalse()
388+
}
389+
364390
// endregion
365391

366392
// region hasChanges
@@ -458,6 +484,21 @@ class PostRsSettingsViewModelTest :
458484
.isTrue()
459485
}
460486

487+
@Test
488+
fun `onSaveClicked while already saving is no-op`() {
489+
val viewModel = createViewModel()
490+
viewModel.onStatusSelected(PostStatus.Draft)
491+
viewModel.onSaveClicked()
492+
assertThat(viewModel.uiState.value.isSaving)
493+
.isTrue()
494+
495+
// Second save attempt should be no-op
496+
viewModel.onSaveClicked()
497+
498+
assertThat(viewModel.uiState.value.isSaving)
499+
.isTrue()
500+
}
501+
461502
// endregion
462503

463504
// region Back
@@ -581,6 +622,200 @@ class PostRsSettingsViewModelTest :
581622

582623
// endregion
583624

625+
// region Date and Time selection
626+
627+
@Test
628+
fun `onDateSelected updates editedDate`() {
629+
val viewModel = createViewModel()
630+
631+
viewModel.onDateSelected(2025, 5, 15)
632+
633+
val date = viewModel.uiState.value.editedDate
634+
assertThat(date).isNotNull()
635+
val cal = Calendar.getInstance().apply {
636+
time = date!!
637+
}
638+
assertThat(cal[Calendar.YEAR]).isEqualTo(2025)
639+
assertThat(cal[Calendar.MONTH]).isEqualTo(5)
640+
assertThat(cal[Calendar.DAY_OF_MONTH])
641+
.isEqualTo(15)
642+
}
643+
644+
@Test
645+
fun `onDateSelected transitions to TimeDialog`() {
646+
val viewModel = createViewModel()
647+
648+
viewModel.onDateSelected(2025, 5, 15)
649+
650+
assertThat(viewModel.uiState.value.dialogState)
651+
.isEqualTo(DialogState.TimeDialog)
652+
}
653+
654+
@Test
655+
fun `onTimeSelected updates editedDate`() {
656+
val viewModel = createViewModel()
657+
658+
viewModel.onTimeSelected(14, 30)
659+
660+
val date = viewModel.uiState.value.editedDate
661+
assertThat(date).isNotNull()
662+
val cal = Calendar.getInstance().apply {
663+
time = date!!
664+
}
665+
assertThat(cal[Calendar.HOUR_OF_DAY])
666+
.isEqualTo(14)
667+
assertThat(cal[Calendar.MINUTE]).isEqualTo(30)
668+
}
669+
670+
// endregion
671+
672+
// region Author selection
673+
674+
@Test
675+
fun `onAuthorSelected updates editedAuthor`() {
676+
val viewModel = createViewModel()
677+
678+
viewModel.onAuthorSelected(99L)
679+
680+
assertThat(
681+
viewModel.uiState.value.editedAuthor
682+
).isEqualTo(99L)
683+
}
684+
685+
@Test
686+
fun `onAuthorSelected with same as original clears edit`() {
687+
val viewModel = createViewModel()
688+
// authorId defaults to 0
689+
690+
viewModel.onAuthorSelected(0L)
691+
692+
assertThat(
693+
viewModel.uiState.value.editedAuthor
694+
).isNull()
695+
}
696+
697+
// endregion
698+
699+
// region Categories and Tags
700+
701+
@Test
702+
fun `onCategoriesClicked emits LaunchCategorySelection`() =
703+
test {
704+
val viewModel = createViewModel()
705+
706+
viewModel.events.test {
707+
viewModel.onCategoriesClicked()
708+
709+
val event = awaitItem()
710+
assertThat(event).isInstanceOf(
711+
PostRsSettingsEvent
712+
.LaunchCategorySelection::class.java
713+
)
714+
cancelAndIgnoreRemainingEvents()
715+
}
716+
}
717+
718+
@Test
719+
fun `onCategoriesSelected with new ids updates edit`() {
720+
val viewModel = createViewModel()
721+
722+
viewModel.onCategoriesSelected(
723+
longArrayOf(1L, 2L)
724+
)
725+
726+
assertThat(
727+
viewModel.uiState.value.editedCategoryIds
728+
).containsExactly(1L, 2L)
729+
}
730+
731+
@Test
732+
fun `onTagsSelected with new ids updates edit`() {
733+
val viewModel = createViewModel()
734+
735+
viewModel.onTagsSelected(longArrayOf(3L, 4L))
736+
737+
assertThat(
738+
viewModel.uiState.value.editedTagIds
739+
).containsExactly(3L, 4L)
740+
}
741+
742+
@Test
743+
fun `onCategoriesSelected with same ids clears edit`() {
744+
val viewModel = createViewModel()
745+
// original categoryIds is empty list
746+
747+
viewModel.onCategoriesSelected(longArrayOf())
748+
749+
assertThat(
750+
viewModel.uiState.value.editedCategoryIds
751+
).isNull()
752+
}
753+
754+
// endregion
755+
756+
// region Featured image
757+
758+
@Test
759+
fun `onFeaturedImageSelected updates editedId`() {
760+
val viewModel = createViewModel()
761+
762+
viewModel.onFeaturedImageSelected(99L)
763+
764+
assertThat(
765+
viewModel.uiState.value.editedFeaturedImageId
766+
).isEqualTo(99L)
767+
}
768+
769+
@Test
770+
fun `onFeaturedImageSelected with same id is no-op`() {
771+
val viewModel = createViewModel()
772+
// effectiveFeaturedImageId defaults to 0
773+
774+
viewModel.onFeaturedImageSelected(0L)
775+
776+
assertThat(
777+
viewModel.uiState.value.editedFeaturedImageId
778+
).isNull()
779+
assertThat(
780+
viewModel.uiState.value.featuredImage
781+
).isEqualTo(FieldState.Empty)
782+
}
783+
784+
@Test
785+
fun `onChooseFromWpMedia emits LaunchWpMediaPicker`() =
786+
test {
787+
val viewModel = createViewModel()
788+
789+
viewModel.events.test {
790+
viewModel.onChooseFromWpMedia()
791+
792+
val event = awaitItem()
793+
assertThat(event).isEqualTo(
794+
PostRsSettingsEvent.LaunchWpMediaPicker
795+
)
796+
cancelAndIgnoreRemainingEvents()
797+
}
798+
}
799+
800+
@Test
801+
fun `onChooseFromDevice emits LaunchDeviceMediaPicker`() =
802+
test {
803+
val viewModel = createViewModel()
804+
805+
viewModel.events.test {
806+
viewModel.onChooseFromDevice()
807+
808+
val event = awaitItem()
809+
assertThat(event).isEqualTo(
810+
PostRsSettingsEvent
811+
.LaunchDeviceMediaPicker
812+
)
813+
cancelAndIgnoreRemainingEvents()
814+
}
815+
}
816+
817+
// endregion
818+
584819
companion object {
585820
private const val TEST_POST_ID = 42L
586821
}

0 commit comments

Comments
 (0)