diff --git a/domains/state/src/testFixtures/kotlin/test/ReducerTest.kt b/domains/state/src/testFixtures/kotlin/test/ReducerTest.kt index 950c7d1..ebdd7b7 100644 --- a/domains/state/src/testFixtures/kotlin/test/ReducerTest.kt +++ b/domains/state/src/testFixtures/kotlin/test/ReducerTest.kt @@ -45,7 +45,7 @@ class ReducerTestScope( private val actionCaptures = mutableListOf() private val reducerScope = object : ReducerScope { override val coroutineScope = CoroutineScope(UnconfinedTestDispatcher()) - override suspend fun dispatch(action: Action) { + override fun dispatch(action: Action) { actionCaptures.add(action) } @@ -123,4 +123,20 @@ class ReducerTestScope( assertNoEvents() assertNoDispatches() } +} + +fun ReducerTestScope.assertOnlyDispatches(vararg action: Action) { + this.assertOnlyDispatches(action.toList()) +} + +fun ReducerTestScope.assertDispatches(vararg action: Action) { + this.assertDispatches(action.toList()) +} + +fun ReducerTestScope.assertEvents(vararg event: E) { + this.assertEvents(event.toList()) +} + +fun ReducerTestScope.assertOnlyEvents(vararg event: E) { + this.assertOnlyEvents(event.toList()) } \ No newline at end of file diff --git a/features/messenger/src/test/kotlin/app/dapk/st/messenger/MessengerReducerTest.kt b/features/messenger/src/test/kotlin/app/dapk/st/messenger/MessengerReducerTest.kt index 9c3ddca..91c0540 100644 --- a/features/messenger/src/test/kotlin/app/dapk/st/messenger/MessengerReducerTest.kt +++ b/features/messenger/src/test/kotlin/app/dapk/st/messenger/MessengerReducerTest.kt @@ -272,7 +272,6 @@ class MessengerReducerTest { assertNoStateChange() } - @Test fun `given text composer with reply, when SendMessage, then clear composer and sends text message`() = runReducerTest { setState { it.copy(composerState = ComposerState.Text(A_MESSAGE_CONTENT, reply = A_REPLY.message), roomState = Lce.Content(A_MESSENGER_PAGE_STATE)) } diff --git a/features/settings/build.gradle b/features/settings/build.gradle index 7eb18c8..d88337d 100644 --- a/features/settings/build.gradle +++ b/features/settings/build.gradle @@ -15,6 +15,7 @@ dependencies { androidImportFixturesWorkaround(project, project(":matrix:common")) androidImportFixturesWorkaround(project, project(":core")) androidImportFixturesWorkaround(project, project(":domains:store")) + androidImportFixturesWorkaround(project, project(":domains:state")) androidImportFixturesWorkaround(project, project(":domains:android:viewmodel")) androidImportFixturesWorkaround(project, project(":domains:android:stub")) androidImportFixturesWorkaround(project, project(":chat-engine")) diff --git a/features/settings/src/main/kotlin/app/dapk/st/settings/state/SettingsReducer.kt b/features/settings/src/main/kotlin/app/dapk/st/settings/state/SettingsReducer.kt index 9ccc1b9..a17dd55 100644 --- a/features/settings/src/main/kotlin/app/dapk/st/settings/state/SettingsReducer.kt +++ b/features/settings/src/main/kotlin/app/dapk/st/settings/state/SettingsReducer.kt @@ -76,7 +76,6 @@ internal fun settingsReducer( dispatch(RootActions.FetchProviders) }, - async(RootActions.ImportKeysFromFile::class) { action -> withPageContext { pageDispatch(PageStateChange.UpdatePage(it.copy(importProgress = ImportResult.Update(0)))) @@ -95,7 +94,6 @@ internal fun settingsReducer( .launchIn(coroutineScope) }, onFailure = { - withPageContext { pageDispatch(PageStateChange.UpdatePage(it.copy(importProgress = ImportResult.Error(ImportResult.Error.Type.UnableToOpenFile)))) } @@ -115,6 +113,10 @@ internal fun settingsReducer( } }, + async(ScreenAction.OpenImportRoom::class) { + dispatch(PageAction.GoTo(SpiderPage(Page.Routes.importRoomKeys, "Import room keys", Page.Routes.encryption, Page.ImportRoomKey()))) + }, + multi(ScreenAction.OnClick::class) { action -> val item = action.item when (item.id) { @@ -133,10 +135,6 @@ internal fun settingsReducer( eventEmitter.invoke(Toast(message = "Cache deleted")) } - EventLog -> sideEffect { - eventEmitter.invoke(OpenEventLog) - } - Encryption -> async { dispatch(PageAction.GoTo(SpiderPage(Page.Routes.encryption, "Encryption", Page.Routes.root, Page.Security))) } @@ -164,16 +162,16 @@ internal fun settingsReducer( dispatch(ComponentLifecycle.Visible) } + EventLog -> sideEffect { + eventEmitter.invoke(OpenEventLog) + } + ToggleSendReadReceipts -> async { messageOptionsStore.setReadReceiptsDisabled(!messageOptionsStore.isReadReceiptsDisabled()) dispatch(ComponentLifecycle.Visible) } } }, - - async(ScreenAction.OpenImportRoom::class) { - dispatch(PageAction.GoTo(SpiderPage(Page.Routes.importRoomKeys, "Import room keys", Page.Routes.encryption, Page.ImportRoomKey()))) - }, ) } ) diff --git a/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsItemFactoryTest.kt b/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsItemFactoryTest.kt index 46a1620..c573624 100644 --- a/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsItemFactoryTest.kt +++ b/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsItemFactoryTest.kt @@ -78,5 +78,6 @@ class FakePushRegistrars { val instance = mockk() fun givenCurrentSelection() = coEvery { instance.currentSelection() }.delegateReturn() + fun givenOptions() = coEvery { instance.options() }.delegateReturn() } \ No newline at end of file diff --git a/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsReducerTest.kt b/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsReducerTest.kt new file mode 100644 index 0000000..4b6e2ec --- /dev/null +++ b/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsReducerTest.kt @@ -0,0 +1,281 @@ +package app.dapk.st.settings + +import app.dapk.st.core.Lce +import app.dapk.st.core.page.PageAction +import app.dapk.st.core.page.PageContainer +import app.dapk.st.core.page.PageStateChange +import app.dapk.st.design.components.SpiderPage +import app.dapk.st.engine.ImportResult +import app.dapk.st.push.Registrar +import app.dapk.st.settings.state.ComponentLifecycle +import app.dapk.st.settings.state.RootActions +import app.dapk.st.settings.state.ScreenAction +import app.dapk.st.settings.state.settingsReducer +import app.dapk.state.Combined2 +import fake.* +import fixture.aRoomId +import internalfake.FakeSettingsItemFactory +import internalfake.FakeUriFilenameResolver +import internalfixture.aImportRoomKeysPage +import internalfixture.aPushProvidersPage +import internalfixture.aSettingTextItem +import kotlinx.coroutines.flow.flowOf +import org.junit.Test +import test.* + +private const val APP_PRIVACY_POLICY_URL = "https://ouchadam.github.io/small-talk/privacy/" +private val A_LIST_OF_ROOT_ITEMS = listOf(aSettingTextItem()) +private val A_URI = FakeUri() +private const val A_FILENAME = "a-filename.jpg" +private val AN_INITIAL_IMPORT_ROOM_KEYS_PAGE = aImportRoomKeysPage() +private val AN_INITIAL_PUSH_PROVIDERS_PAGE = aPushProvidersPage() +private val A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION = aImportRoomKeysPage( + state = Page.ImportRoomKey(selectedFile = NamedUri(A_FILENAME, A_URI.instance)) +) +private val A_LIST_OF_ROOM_IDS = listOf(aRoomId()) +private val AN_IMPORT_SUCCESS = ImportResult.Success(A_LIST_OF_ROOM_IDS.toSet(), totalImportedKeysCount = 5) +private val AN_IMPORT_FILE_ERROR = ImportResult.Error(ImportResult.Error.Type.UnableToOpenFile) +private val AN_INPUT_STREAM = FakeInputStream() +private const val A_PASSPHRASE = "passphrase" +private val AN_ERROR = RuntimeException() +private val A_REGISTRAR = Registrar("a-registrar-id") +private val A_PUSH_OPTIONS = listOf(Registrar("a-registrar-id")) + +internal class SettingsReducerTest { + + private val fakeStoreCleaner = FakeStoreCleaner() + private val fakeContentResolver = FakeContentResolver() + private val fakeUriFilenameResolver = FakeUriFilenameResolver() + private val fakePushTokenRegistrars = FakePushRegistrars() + private val fakeSettingsItemFactory = FakeSettingsItemFactory() + private val fakeThemeStore = FakeThemeStore() + private val fakeLoggingStore = FakeLoggingStore() + private val fakeMessageOptionsStore = FakeMessageOptionsStore() + private val fakeChatEngine = FakeChatEngine() + private val fakeJobBag = FakeJobBag() + + private val runReducerTest = testReducer { fakeEventSource -> + settingsReducer( + fakeChatEngine, + fakeStoreCleaner, + fakeContentResolver.instance, + fakeUriFilenameResolver.instance, + fakeSettingsItemFactory.instance, + fakePushTokenRegistrars.instance, + fakeThemeStore.instance, + fakeLoggingStore.instance, + fakeMessageOptionsStore.instance, + fakeEventSource, + fakeJobBag.instance, + ) + } + + @Test + fun `initial state is root with loading`() = runReducerTest { + assertInitialState( + pageState(SpiderPage(Page.Routes.root, "Settings", null, Page.Root(Lce.Loading()))) + ) + } + + @Test + fun `given root content, when Visible, then goes to root page with content`() = runReducerTest { + fakeSettingsItemFactory.givenRoot().returns(A_LIST_OF_ROOT_ITEMS) + fakeJobBag.instance.expect { it.replace("page", any()) } + + reduce(ComponentLifecycle.Visible) + + assertOnlyDispatches( + PageAction.GoTo( + SpiderPage( + Page.Routes.root, + "Settings", + null, + Page.Root(Lce.Content(A_LIST_OF_ROOT_ITEMS)) + ) + ) + ) + } + + @Test + fun `when SelectPushProvider, then selects provider and refreshes`() = runReducerTest { + fakePushTokenRegistrars.instance.expect { it.makeSelection(A_REGISTRAR) } + + reduce(RootActions.SelectPushProvider(A_REGISTRAR)) + + assertOnlyDispatches(RootActions.FetchProviders) + } + + @Test + fun `when FetchProviders, then selects provider and refreshes`() = runReducerTest { + setState(pageState(aPushProvidersPage())) + fakePushTokenRegistrars.givenOptions().returns(A_PUSH_OPTIONS) + fakePushTokenRegistrars.givenCurrentSelection().returns(A_REGISTRAR) + + reduce(RootActions.FetchProviders) + + assertOnlyDispatches( + PageStateChange.UpdatePage( + aPushProvidersPage().state.copy(options = Lce.Loading()) + ), + PageStateChange.UpdatePage( + aPushProvidersPage().state.copy( + selection = A_REGISTRAR, + options = Lce.Content(A_PUSH_OPTIONS) + ) + ) + ) + } + + @Test + fun `when SelectKeysFile, then updates ImportRoomKey page with file`() = runReducerTest { + setState(pageState(AN_INITIAL_IMPORT_ROOM_KEYS_PAGE)) + fakeUriFilenameResolver.givenFilename(A_URI.instance).returns(A_FILENAME) + + reduce(RootActions.SelectKeysFile(A_URI.instance)) + + assertOnlyDispatches( + PageStateChange.UpdatePage( + AN_INITIAL_IMPORT_ROOM_KEYS_PAGE.state.copy( + selectedFile = NamedUri(A_FILENAME, A_URI.instance) + ) + ) + ) + } + + @Test + fun `when Click SignOut, then clears store and signs out`() = runReducerTest { + fakeStoreCleaner.expectUnit { it.cleanCache(removeCredentials = true) } + val aSignOutItem = aSettingTextItem(id = SettingItem.Id.SignOut) + + reduce(ScreenAction.OnClick(aSignOutItem)) + + assertEvents(SettingsEvent.SignedOut) + } + + @Test + fun `when Click Encryption, then goes to Encryption page`() = runReducerTest { + val anEncryptionItem = aSettingTextItem(id = SettingItem.Id.Encryption) + + reduce(ScreenAction.OnClick(anEncryptionItem)) + + assertOnlyDispatches( + PageAction.GoTo( + SpiderPage( + Page.Routes.encryption, + "Encryption", + Page.Routes.root, + Page.Security + ) + ) + ) + } + + @Test + fun `when Click PrivacyPolicy, then opens privacy policy url`() = runReducerTest { + val aPrivacyPolicyItem = aSettingTextItem(id = SettingItem.Id.PrivacyPolicy) + + reduce(ScreenAction.OnClick(aPrivacyPolicyItem)) + + assertOnlyEvents(SettingsEvent.OpenUrl(APP_PRIVACY_POLICY_URL)) + } + + @Test + fun `when Click PushProvider, then goes to PushProvider page`() = runReducerTest { + val aPushProviderItem = aSettingTextItem(id = SettingItem.Id.PushProvider) + + reduce(ScreenAction.OnClick(aPushProviderItem)) + + assertOnlyDispatches(PageAction.GoTo(aPushProvidersPage())) + } + + @Test + fun `when Click Ignored, then does nothing`() = runReducerTest { + val anIgnoredItem = aSettingTextItem(id = SettingItem.Id.Ignored) + + reduce(ScreenAction.OnClick(anIgnoredItem)) + + assertNoChanges() + } + + @Test + fun `when Click ToggleDynamicTheme, then toggles flag, recreates activity and reloads`() = runReducerTest { + val aToggleThemeItem = aSettingTextItem(id = SettingItem.Id.ToggleDynamicTheme) + fakeThemeStore.givenMaterialYouIsEnabled().returns(true) + fakeThemeStore.instance.expect { it.storeMaterialYouEnabled(false) } + + reduce(ScreenAction.OnClick(aToggleThemeItem)) + + assertEvents(SettingsEvent.RecreateActivity) + assertDispatches(ComponentLifecycle.Visible) + assertNoStateChange() + } + + @Test + fun `when Click ToggleEnableLogs, then toggles flag and reloads`() = runReducerTest { + val aToggleEnableLogsItem = aSettingTextItem(id = SettingItem.Id.ToggleEnableLogs) + fakeLoggingStore.givenLoggingIsEnabled().returns(true) + fakeLoggingStore.instance.expect { it.setEnabled(false) } + + reduce(ScreenAction.OnClick(aToggleEnableLogsItem)) + + assertOnlyDispatches(ComponentLifecycle.Visible) + } + + @Test + fun `when Click EventLog, then opens event log`() = runReducerTest { + val anEventLogItem = aSettingTextItem(id = SettingItem.Id.EventLog) + + reduce(ScreenAction.OnClick(anEventLogItem)) + + assertOnlyEvents(SettingsEvent.OpenEventLog) + } + + @Test + fun `when Click ToggleSendReadReceipts, then toggles flag and reloads`() = runReducerTest { + val aToggleReadReceiptsItem = aSettingTextItem(id = SettingItem.Id.ToggleSendReadReceipts) + fakeMessageOptionsStore.givenReadReceiptsDisabled().returns(true) + fakeMessageOptionsStore.instance.expect { it.setReadReceiptsDisabled(false) } + + reduce(ScreenAction.OnClick(aToggleReadReceiptsItem)) + + assertOnlyDispatches(ComponentLifecycle.Visible) + } + + @Test + fun `given success, when ImportKeysFromFile, then dispatches progress`() = runReducerTest { + setState(pageState(A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION)) + fakeContentResolver.givenFile(A_URI.instance).returns(AN_INPUT_STREAM.instance) + fakeChatEngine.givenImportKeys(AN_INPUT_STREAM.instance, A_PASSPHRASE).returns(flowOf(AN_IMPORT_SUCCESS)) + + reduce(RootActions.ImportKeysFromFile(A_URI.instance, A_PASSPHRASE)) + + assertOnlyDispatches( + PageStateChange.UpdatePage( + A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION.state.copy(importProgress = ImportResult.Update(0L)) + ), + PageStateChange.UpdatePage( + A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION.state.copy(importProgress = AN_IMPORT_SUCCESS) + ), + ) + } + + @Test + fun `given error, when ImportKeysFromFile, then dispatches error`() = runReducerTest { + setState(pageState(A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION)) + fakeContentResolver.givenFile(A_URI.instance).throws(AN_ERROR) + + reduce(RootActions.ImportKeysFromFile(A_URI.instance, A_PASSPHRASE)) + + assertOnlyDispatches( + PageStateChange.UpdatePage( + A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION.state.copy(importProgress = ImportResult.Update(0L)) + ), + PageStateChange.UpdatePage( + A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION.state.copy(importProgress = AN_IMPORT_FILE_ERROR) + ), + ) + } + +} + +private fun

pageState(page: SpiderPage) = Combined2(PageContainer(page), Unit) diff --git a/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsViewModelTest.kt b/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsViewModelTest.kt deleted file mode 100644 index 98c4f12..0000000 --- a/features/settings/src/test/kotlin/app/dapk/st/settings/SettingsViewModelTest.kt +++ /dev/null @@ -1,210 +0,0 @@ -package app.dapk.st.settings - -import ViewModelTest -import app.dapk.st.core.Lce -import app.dapk.st.design.components.SpiderPage -import app.dapk.st.engine.ImportResult -import fake.* -import fixture.aRoomId -import internalfake.FakeSettingsItemFactory -import internalfake.FakeUriFilenameResolver -import internalfixture.aImportRoomKeysPage -import internalfixture.aSettingTextItem -import kotlinx.coroutines.flow.flowOf -import org.junit.Test - -private const val APP_PRIVACY_POLICY_URL = "https://ouchadam.github.io/small-talk/privacy/" -private val A_LIST_OF_ROOT_ITEMS = listOf(aSettingTextItem()) -private val A_URI = FakeUri() -private const val A_FILENAME = "a-filename.jpg" -private val AN_INITIAL_IMPORT_ROOM_KEYS_PAGE = aImportRoomKeysPage() -private val A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION = aImportRoomKeysPage( - state = Page.ImportRoomKey(selectedFile = NamedUri(A_FILENAME, A_URI.instance)) -) -private val A_LIST_OF_ROOM_IDS = listOf(aRoomId()) -private val AN_IMPORT_SUCCESS = ImportResult.Success(A_LIST_OF_ROOM_IDS.toSet(), totalImportedKeysCount = 5) -private val AN_IMPORT_FILE_ERROR = ImportResult.Error(ImportResult.Error.Type.UnableToOpenFile) -private val AN_INPUT_STREAM = FakeInputStream() -private const val A_PASSPHRASE = "passphrase" -private val AN_ERROR = RuntimeException() - -internal class SettingsViewModelTest { - - private val runViewModelTest = ViewModelTest() - - private val fakeStoreCleaner = FakeStoreCleaner() - private val fakeContentResolver = FakeContentResolver() - private val fakeUriFilenameResolver = FakeUriFilenameResolver() - private val fakePushTokenRegistrars = FakePushRegistrars() - private val fakeSettingsItemFactory = FakeSettingsItemFactory() - private val fakeThemeStore = FakeThemeStore() - private val fakeLoggingStore = FakeLoggingStore() - private val fakeMessageOptionsStore = FakeMessageOptionsStore() - private val fakeChatEngine = FakeChatEngine() - - private val viewModel = SettingsViewModel( - fakeChatEngine, - fakeStoreCleaner, - fakeContentResolver.instance, - fakeUriFilenameResolver.instance, - fakeSettingsItemFactory.instance, - fakePushTokenRegistrars.instance, - fakeThemeStore.instance, - fakeLoggingStore.instance, - fakeMessageOptionsStore.instance, - runViewModelTest.testMutableStateFactory(), - ) - - @Test - fun `when creating view model then initial state is loading Root`() = runViewModelTest { - viewModel.test() - - assertInitialState( - SettingsScreenState(SpiderPage(Page.Routes.root, "Settings", null, Page.Root(Lce.Loading()))) - ) - } - - @Test - fun `when starting, then emits root page with content`() = runViewModelTest { - fakeSettingsItemFactory.givenRoot().returns(A_LIST_OF_ROOT_ITEMS) - - viewModel.test().start() - - assertStates( - SettingsScreenState( - SpiderPage( - Page.Routes.root, - "Settings", - null, - Page.Root(Lce.Content(A_LIST_OF_ROOT_ITEMS)) - ) - ) - ) - assertNoEvents() - } - - @Test - fun `when sign out clicked, then clears store`() = runViewModelTest { - fakeStoreCleaner.expectUnit { it.cleanCache(removeCredentials = true) } - val aSignOutItem = aSettingTextItem(id = SettingItem.Id.SignOut) - - viewModel.test().onClick(aSignOutItem) - - assertNoStates() - assertEvents(SettingsEvent.SignedOut) - verifyExpects() - } - - @Test - fun `when event log clicked, then opens event log`() = runViewModelTest { - val anEventLogItem = aSettingTextItem(id = SettingItem.Id.EventLog) - - viewModel.test().onClick(anEventLogItem) - - assertNoStates() - assertEvents(SettingsEvent.OpenEventLog) - } - - @Test - fun `when encryption clicked, then emits encryption page`() = runViewModelTest { - val anEncryptionItem = aSettingTextItem(id = SettingItem.Id.Encryption) - - viewModel.test().onClick(anEncryptionItem) - - assertNoEvents() - assertStates( - SettingsScreenState( - SpiderPage( - route = Page.Routes.encryption, - label = "Encryption", - parent = Page.Routes.root, - state = Page.Security - ) - ) - ) - } - - @Test - fun `when privacy policy clicked, then opens privacy policy url`() = runViewModelTest { - val aPrivacyPolicyItem = aSettingTextItem(id = SettingItem.Id.PrivacyPolicy) - - viewModel.test().onClick(aPrivacyPolicyItem) - - assertNoStates() - assertEvents(SettingsEvent.OpenUrl(APP_PRIVACY_POLICY_URL)) - } - - @Test - fun `when going to import room, then emits import room keys page`() = runViewModelTest { - viewModel.test().goToImportRoom() - - assertStates( - SettingsScreenState( - SpiderPage( - route = Page.Routes.importRoomKeys, - label = "Import room keys", - parent = Page.Routes.encryption, - state = Page.ImportRoomKey() - ) - ) - ) - assertNoEvents() - } - - @Test - fun `given on import room keys page, when selecting file, then emits selection`() = runViewModelTest { - fakeUriFilenameResolver.givenFilename(A_URI.instance).returns(A_FILENAME) - - viewModel.test(initialState = SettingsScreenState(AN_INITIAL_IMPORT_ROOM_KEYS_PAGE)).fileSelected(A_URI.instance) - - assertStates( - SettingsScreenState( - AN_INITIAL_IMPORT_ROOM_KEYS_PAGE.copy( - state = Page.ImportRoomKey( - selectedFile = NamedUri(A_FILENAME, A_URI.instance) - ) - ) - ) - ) - assertNoEvents() - } - - @Test - fun `given success when importing room keys, then emits progress`() = runViewModelTest { - fakeContentResolver.givenFile(A_URI.instance).returns(AN_INPUT_STREAM.instance) - fakeChatEngine.givenImportKeys(AN_INPUT_STREAM.instance, A_PASSPHRASE).returns(flowOf(AN_IMPORT_SUCCESS)) - - viewModel - .test(initialState = SettingsScreenState(A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION)) - .importFromFileKeys(A_URI.instance, A_PASSPHRASE) - - assertStates( - { copy(page = page.updateState { copy(importProgress = ImportResult.Update(0L)) }) }, - { copy(page = page.updateState { copy(importProgress = AN_IMPORT_SUCCESS) }) }, - ) - assertNoEvents() - verifyExpects() - } - - @Test - fun `given error when importing room keys, then emits error`() = runViewModelTest { - fakeContentResolver.givenFile(A_URI.instance).throws(AN_ERROR) - - viewModel - .test(initialState = SettingsScreenState(A_IMPORT_ROOM_KEYS_PAGE_WITH_SELECTION)) - .importFromFileKeys(A_URI.instance, A_PASSPHRASE) - - assertStates( - { copy(page = page.updateState { copy(importProgress = ImportResult.Update(0L)) }) }, - { copy(page = page.updateState { copy(importProgress = AN_IMPORT_FILE_ERROR) }) }, - ) - assertNoEvents() - } - -} - -@Suppress("UNCHECKED_CAST") -private inline fun SpiderPage.updateState(crossinline block: S.() -> S): SpiderPage { - require(this.state is S) - return (this as SpiderPage).copy(state = block(this.state)) as SpiderPage -} diff --git a/features/settings/src/test/kotlin/internalfixture/PageFixture.kt b/features/settings/src/test/kotlin/internalfixture/PageFixture.kt index 165bb20..d42834f 100644 --- a/features/settings/src/test/kotlin/internalfixture/PageFixture.kt +++ b/features/settings/src/test/kotlin/internalfixture/PageFixture.kt @@ -11,3 +11,12 @@ internal fun aImportRoomKeysPage( parent = Page.Routes.encryption, state = state ) + +internal fun aPushProvidersPage( + state: Page.PushProviders = Page.PushProviders() +) = SpiderPage( + route = Page.Routes.pushProviders, + label = "Push providers", + parent = Page.Routes.root, + state = state +)