diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt b/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt deleted file mode 100644 index f957879fe..000000000 --- a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.arkivanov.decompose.router.stack.webhistory - -internal actual class DefaultWebHistoryControllerWindow actual constructor() : DefaultWebHistoryController.Window { - - actual override val history: DefaultWebHistoryController.History = HistoryImpl() - - actual override fun setOnPopStateListener(listener: (state: String?) -> Unit) { - kotlinx.browser.window.onpopstate = { listener(it.state?.unsafeCast()) } - } - - private class HistoryImpl : DefaultWebHistoryController.History { - override val state: String? - get() = kotlinx.browser.window.history.state?.unsafeCast() - - override fun go(delta: Int) { - kotlinx.browser.window.history.go(delta = delta) - } - - override fun pushState(data: String, url: String?) { - kotlinx.browser.window.history.pushState(data = data, title = "", url = url) - } - - override fun replaceState(data: String, url: String?) { - kotlinx.browser.window.history.replaceState(data = data, title = "", url = url) - } - } -} diff --git a/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/webhistory/DefaultBrowserHistory.kt b/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/webhistory/DefaultBrowserHistory.kt new file mode 100644 index 000000000..173a100c2 --- /dev/null +++ b/decompose/src/jsMain/kotlin/com/arkivanov/decompose/router/webhistory/DefaultBrowserHistory.kt @@ -0,0 +1,24 @@ +package com.arkivanov.decompose.router.webhistory + +import kotlinx.browser.window + +internal actual object DefaultBrowserHistory : BrowserHistory { + + override val state: String? get() = window.history.state?.unsafeCast() + + override fun go(delta: Int) { + window.history.go(delta = delta) + } + + override fun pushState(data: String?, url: String?) { + window.history.pushState(data = data, title = "", url = url) + } + + override fun replaceState(data: String?, url: String?) { + window.history.replaceState(data = data, title = "", url = url) + } + + override fun setOnPopStateListener(listener: (state: String?) -> Unit) { + window.onpopstate = { listener(it.state?.unsafeCast()) } + } +} diff --git a/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt b/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt deleted file mode 100644 index 61af6e969..000000000 --- a/decompose/src/wasmJsMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.arkivanov.decompose.router.stack.webhistory - -internal actual class DefaultWebHistoryControllerWindow actual constructor() : DefaultWebHistoryController.Window { - - actual override val history: DefaultWebHistoryController.History = HistoryImpl() - - actual override fun setOnPopStateListener(listener: (state: String?) -> Unit) { - kotlinx.browser.window.onpopstate = { listener(it.state?.toString()) } - } - - private class HistoryImpl : DefaultWebHistoryController.History { - override val state: String? - get() = kotlinx.browser.window.history.state?.toString() - - override fun go(delta: Int) { - kotlinx.browser.window.history.go(delta = delta) - } - - override fun pushState(data: String, url: String?) { - kotlinx.browser.window.history.pushState(data = data.toJsString(), title = "", url = url) - } - - override fun replaceState(data: String, url: String?) { - kotlinx.browser.window.history.replaceState(data = data.toJsString(), title = "", url = url) - } - } -} diff --git a/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt index 26a67bf56..f6f0a9b44 100644 --- a/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt +++ b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryController.kt @@ -8,6 +8,8 @@ import com.arkivanov.decompose.router.stack.findFirstDifferentIndex import com.arkivanov.decompose.router.stack.navigate import com.arkivanov.decompose.router.stack.startsWith import com.arkivanov.decompose.router.stack.subscribe +import com.arkivanov.decompose.router.webhistory.BrowserHistory +import com.arkivanov.decompose.router.webhistory.DefaultBrowserHistory import com.arkivanov.decompose.value.Value import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable @@ -15,23 +17,23 @@ import kotlinx.serialization.builtins.ListSerializer @ExperimentalDecomposeApi class DefaultWebHistoryController internal constructor( - private val window: Window, + private val browserHistory: BrowserHistory, ) : WebHistoryController { @Suppress("unused") // Public API - constructor() : this(DefaultWebHistoryControllerWindow()) + constructor() : this(DefaultBrowserHistory) override val historyPaths: List - get() = window.history.getItems().map(PageItem::path) + get() = browserHistory.getItems().map(PageItem::path) - private fun History.getItems(): List = + private fun BrowserHistory.getItems(): List = state?.let(::deserializeItems) ?: emptyList() - private fun History.pushState(items: List) { + private fun BrowserHistory.pushState(items: List) { pushState(data = serializeItems(items), url = items.last().path) } - private fun History.replaceState(items: List) { + private fun BrowserHistory.replaceState(items: List) { replaceState(data = serializeItems(items), url = items.last().path) } @@ -51,7 +53,7 @@ class DefaultWebHistoryController internal constructor( ) { val impl = Impl(navigator, stack, serializer, getPath, getConfiguration, onWebNavigation) impl.init() - window.setOnPopStateListener(impl::onPopState) + browserHistory.setOnPopStateListener(impl::onPopState) } private inner class Impl( @@ -67,15 +69,15 @@ class DefaultWebHistoryController internal constructor( fun init() { // Initialise the history if it's empty - if (window.history.getItems().isEmpty()) { + if (browserHistory.getItems().isEmpty()) { val configurations = stack.value.configurations() - window.history.replaceState(configurations[0]) + browserHistory.replaceState(configurations[0]) for (i in 1..configurations.lastIndex) { - window.history.pushState(configurations[i]) + browserHistory.pushState(configurations[i]) } } - stack.subscribe(::onStackChanged) + stack.subscribe(observer = ::onStackChanged) } private fun onStackChanged(newStack: ChildStack, oldStack: ChildStack) { @@ -95,59 +97,59 @@ class DefaultWebHistoryController internal constructor( // One or more configurations were popped from the stack oldConfigurationStack.startsWith(newConfigurationStack) -> { // Pop removed pages from the history - window.history.go(delta = newConfigurationStack.size - oldConfigurationStack.size) + browserHistory.go(delta = newConfigurationStack.size - oldConfigurationStack.size) } // One or more configurations were pushed to the history newConfigurationStack.startsWith(oldConfigurationStack) -> { // Push new pages to the history for (i in oldConfigurationStack.size..newConfigurationStack.lastIndex) { - window.history.pushState(newConfigurationStack[i]) + browserHistory.pushState(newConfigurationStack[i]) } } // The active configuration was changed, and new configurations could be pushed firstDifferentIndex == oldConfigurationStack.lastIndex -> { // Replace the current page with a new one - window.history.replaceState(newConfigurationStack[firstDifferentIndex]) + browserHistory.replaceState(newConfigurationStack[firstDifferentIndex]) // Push the rest of the pages to the history for (i in (firstDifferentIndex + 1)..newConfigurationStack.lastIndex) { - window.history.pushState(newConfigurationStack[i]) + browserHistory.pushState(newConfigurationStack[i]) } } // Some configurations were popped, and one or more configurations were pushed firstDifferentIndex > 0 -> { - window.setOnPopStateListener { - window.setOnPopStateListener(::onPopState) + browserHistory.setOnPopStateListener { + browserHistory.setOnPopStateListener(::onPopState) // Push new pages to the history for (i in firstDifferentIndex..newConfigurationStack.lastIndex) { - window.history.pushState(newConfigurationStack[i]) + browserHistory.pushState(newConfigurationStack[i]) } } // Pop removed pages from the history - window.history.go(delta = firstDifferentIndex - oldConfigurationStack.size) + browserHistory.go(delta = firstDifferentIndex - oldConfigurationStack.size) } // All configurations were popped, and one or more configurations were pushed else -> { - window.setOnPopStateListener { - window.setOnPopStateListener(::onPopState) + browserHistory.setOnPopStateListener { + browserHistory.setOnPopStateListener(::onPopState) // Replace the current page with a new one - window.history.replaceState(newConfigurationStack[firstDifferentIndex]) + browserHistory.replaceState(newConfigurationStack[firstDifferentIndex]) // Push the rest of the pages to the history // Corner case: if there is nothing to push, old pages will remain in the history for (i in (firstDifferentIndex + 1)..newConfigurationStack.lastIndex) { - window.history.pushState(newConfigurationStack[i]) + browserHistory.pushState(newConfigurationStack[i]) } } // Pop removed pages from the history, except the first one - window.history.go(delta = -oldConfigurationStack.lastIndex) + browserHistory.go(delta = -oldConfigurationStack.lastIndex) } } } @@ -158,8 +160,8 @@ class DefaultWebHistoryController internal constructor( val oldConfigurations = stack.value.configurations() if (!onWebNavigation(newConfigurations, oldConfigurations)) { - window.setOnPopStateListener { window.setOnPopStateListener(::onPopState) } - window.history.go(stack.value.items.size - newConfigurations.size) + browserHistory.setOnPopStateListener { browserHistory.setOnPopStateListener(::onPopState) } + browserHistory.go(stack.value.items.size - newConfigurations.size) return } @@ -179,20 +181,20 @@ class DefaultWebHistoryController internal constructor( } } - window.history.replaceState(stack.value.configurations()) + browserHistory.replaceState(stack.value.configurations()) isStateObserverEnabled = true } - private fun History.pushState(configuration: C) { - pushState(items = window.history.getItems() + PageItem(configuration = configuration)) + private fun BrowserHistory.pushState(configuration: C) { + pushState(items = browserHistory.getItems() + PageItem(configuration = configuration)) } - private fun History.replaceState(configuration: C) { - replaceState(items = window.history.getItems().dropLast(1) + PageItem(configuration = configuration)) + private fun BrowserHistory.replaceState(configuration: C) { + replaceState(items = browserHistory.getItems().dropLast(1) + PageItem(configuration = configuration)) } - private fun History.replaceState(configurations: List) { + private fun BrowserHistory.replaceState(configurations: List) { replaceState(items = configurations.map(::PageItem)) } @@ -219,18 +221,4 @@ class DefaultWebHistoryController internal constructor( ListSerializer(serializer()) } } - - internal interface Window { - val history: History - - fun setOnPopStateListener(listener: (state: String?) -> Unit) - } - - internal interface History { - val state: String? - - fun go(delta: Int) - fun pushState(data: String, url: String?) - fun replaceState(data: String, url: String?) - } } diff --git a/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt deleted file mode 100644 index 67b5c0f82..000000000 --- a/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerWindow.kt +++ /dev/null @@ -1,8 +0,0 @@ -package com.arkivanov.decompose.router.stack.webhistory - -internal expect class DefaultWebHistoryControllerWindow() : DefaultWebHistoryController.Window { - - override val history: DefaultWebHistoryController.History - - override fun setOnPopStateListener(listener: (state: String?) -> Unit) -} diff --git a/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/webhistory/BrowserHistory.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/webhistory/BrowserHistory.kt new file mode 100644 index 000000000..86ff1285e --- /dev/null +++ b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/webhistory/BrowserHistory.kt @@ -0,0 +1,11 @@ +package com.arkivanov.decompose.router.webhistory + +internal interface BrowserHistory { + + val state: String? + + fun go(delta: Int) + fun pushState(data: String?, url: String?) + fun replaceState(data: String?, url: String?) + fun setOnPopStateListener(listener: (state: String?) -> Unit) +} diff --git a/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/webhistory/DefaultBrowserHistory.kt b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/webhistory/DefaultBrowserHistory.kt new file mode 100644 index 000000000..05915e472 --- /dev/null +++ b/decompose/src/webMain/kotlin/com/arkivanov/decompose/router/webhistory/DefaultBrowserHistory.kt @@ -0,0 +1,3 @@ +package com.arkivanov.decompose.router.webhistory + +internal expect object DefaultBrowserHistory : BrowserHistory diff --git a/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt index 2fdd695a1..55b0c2f95 100644 --- a/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt +++ b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/DefaultWebHistoryControllerTest.kt @@ -5,7 +5,9 @@ import com.arkivanov.decompose.router.stack.TestStackRouter import com.arkivanov.decompose.router.stack.navigate import com.arkivanov.decompose.router.stack.pop import com.arkivanov.decompose.router.stack.push +import com.arkivanov.decompose.router.stack.pushNew import com.arkivanov.decompose.router.stack.replaceCurrent +import com.arkivanov.decompose.router.webhistory.TestBrowserHistory import kotlinx.serialization.Serializable import kotlin.test.Test import kotlin.test.assertEquals @@ -14,9 +16,8 @@ import kotlin.test.assertTrue @Suppress("TestFunctionName") class DefaultWebHistoryControllerTest { - private val window = TestWindow() - private val history = window.history - private val controller = DefaultWebHistoryController(window) + private val history = TestBrowserHistory() + private val controller = DefaultWebHistoryController(history) @Test fun WHEN_created_THEN_historyPaths_empty() { @@ -58,12 +59,13 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() assertStack(listOf("/0", "/1")) } + @Suppress("OPT_IN_USAGE") @Test fun WHEN_router_push_same_config_THEN_url_pushed_to_history() { if (isNodeJs()) { @@ -74,7 +76,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.push(Config(0)) - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/0")) } @@ -89,7 +91,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.pop() - window.runPendingOperations() + history.runPendingOperations() history.assertStack(listOf("/0", "/1"), 0) } @@ -104,7 +106,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.pop() - window.runPendingOperations() + history.runPendingOperations() history.assertStack(listOf("/0", "/0"), 0) } @@ -117,15 +119,16 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() router.pop() - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/1"), 0) } + @Suppress("OPT_IN_USAGE") @Test fun GIVEN_router_push_same_config_WHEN_router_pop_THEN_history_changed_to_previous_page() { if (isNodeJs()) { @@ -135,10 +138,10 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) router.push(Config(0)) - window.runPendingOperations() + history.runPendingOperations() router.pop() - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/0"), 0) } @@ -153,7 +156,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { it.dropLast(2) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/1", "/2"), 0) } @@ -167,10 +170,10 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) router.navigate { it + listOf(Config(1), Config(2)) } - window.runPendingOperations() + history.runPendingOperations() router.navigate { it.dropLast(2) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/1", "/2"), 0) } @@ -183,13 +186,13 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() router.pop() - window.runPendingOperations() + history.runPendingOperations() - router.push(Config(1)) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() assertStack(listOf("/0", "/1")) } @@ -202,13 +205,13 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() router.pop() - window.runPendingOperations() + history.runPendingOperations() - router.push(Config(2)) - window.runPendingOperations() + router.pushNew(Config(2)) + history.runPendingOperations() assertStack(listOf("/0", "/2")) } @@ -223,7 +226,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { listOf(Config(0), Config(3)) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/3")) } @@ -238,7 +241,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { listOf(Config(0), Config(3), Config(4)) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/3", "/4")) } @@ -253,7 +256,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { listOf(Config(0), Config(2)) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/2")) } @@ -268,7 +271,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { listOf(Config(0), Config(2), Config(3)) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/0", "/2", "/3")) } @@ -283,7 +286,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { listOf(Config(2)) } - window.runPendingOperations() + history.runPendingOperations() // Corner case: old pages remain in the history assertStack(listOf("/2", "/1"), 0) @@ -299,7 +302,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { listOf(Config(2), Config(3)) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/2", "/3")) } @@ -314,7 +317,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { listOf(Config(1)) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/1")) } @@ -329,7 +332,7 @@ class DefaultWebHistoryControllerTest { attach(router) router.navigate { listOf(Config(1), Config(2)) } - window.runPendingOperations() + history.runPendingOperations() assertStack(listOf("/1", "/2")) } @@ -344,11 +347,11 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() - window.history.go(delta = -1) - window.runPendingOperations() + history.go(delta = -1) + history.runPendingOperations() assertEquals(listOf(Config(0)), router.configurations) } @@ -361,13 +364,13 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() - window.history.go(delta = -1) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() + history.go(delta = -1) + history.runPendingOperations() - window.history.go(delta = 1) - window.runPendingOperations() + history.go(delta = 1) + history.runPendingOperations() assertStack(listOf("/0", "/1")) } @@ -380,13 +383,13 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() - window.history.go(delta = -1) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() + history.go(delta = -1) + history.runPendingOperations() - window.history.go(delta = 1) - window.runPendingOperations() + history.go(delta = 1) + history.runPendingOperations() assertEquals(listOf(Config(0), Config(1)), router.configurations) } @@ -399,15 +402,15 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() - window.history.go(delta = -1) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() + history.go(delta = -1) + history.runPendingOperations() router.replaceCurrent(Config(2)) - window.runPendingOperations() + history.runPendingOperations() - window.history.go(delta = 1) - window.runPendingOperations() + history.go(delta = 1) + history.runPendingOperations() assertStack(listOf("/2", "/1")) } @@ -420,15 +423,15 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0))) attach(router) - router.push(Config(1)) - window.runPendingOperations() - window.history.go(delta = -1) - window.runPendingOperations() + router.pushNew(Config(1)) + history.runPendingOperations() + history.go(delta = -1) + history.runPendingOperations() router.replaceCurrent(Config(2)) - window.runPendingOperations() + history.runPendingOperations() - window.history.go(delta = 1) - window.runPendingOperations() + history.go(delta = 1) + history.runPendingOperations() assertEquals(listOf(Config(2), Config(1)), router.configurations) } @@ -442,10 +445,10 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0), Config(1))) attach(router) router.navigate { listOf(Config(2)) } - window.runPendingOperations() + history.runPendingOperations() - window.history.go(delta = 1) - window.runPendingOperations() + history.go(delta = 1) + history.runPendingOperations() assertEquals(listOf(Config(2), Config(1)), router.configurations) } @@ -459,10 +462,10 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0), Config(1))) attach(router) router.navigate { listOf(Config(2)) } - window.runPendingOperations() + history.runPendingOperations() - window.history.go(delta = 1) - window.runPendingOperations() + history.go(delta = 1) + history.runPendingOperations() assertStack(listOf("/2", "/1")) } @@ -476,8 +479,8 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0), Config(1))) attach(router) { _, _ -> false } - window.history.go(-1) - window.runPendingOperations() + history.go(-1) + history.runPendingOperations() assertStack(listOf("/0", "/1")) } @@ -490,8 +493,8 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0), Config(1))) attach(router) { _, _ -> true } - window.history.go(-1) - window.runPendingOperations() + history.go(-1) + history.runPendingOperations() assertStack(listOf("/0", "/1"), 0) } @@ -505,10 +508,10 @@ class DefaultWebHistoryControllerTest { val router = TestStackRouter(listOf(Config(0), Config(1))) attach(router) { newStack, _ -> newStack.last().value != 1 } - window.history.go(-1) - window.runPendingOperations() - window.history.go(1) - window.runPendingOperations() + history.go(-1) + history.runPendingOperations() + history.go(1) + history.runPendingOperations() assertStack(listOf("/0", "/1"), 0) } @@ -528,8 +531,8 @@ class DefaultWebHistoryControllerTest { true } - window.history.go(-1) - window.runPendingOperations() + history.go(-1) + history.runPendingOperations() assertEquals(listOf(Config(0)), newStackVar) assertEquals(listOf(Config(0), Config(1)), oldStackVar) @@ -550,12 +553,12 @@ class DefaultWebHistoryControllerTest { true } - window.history.go(-1) - window.runPendingOperations() + history.go(-1) + history.runPendingOperations() newStackVar = emptyList() oldStackVar = emptyList() - window.history.go(1) - window.runPendingOperations() + history.go(1) + history.runPendingOperations() assertEquals(listOf(Config(0), Config(1)), newStackVar) assertEquals(listOf(Config(0)), oldStackVar) @@ -574,7 +577,7 @@ class DefaultWebHistoryControllerTest { onWebNavigation = callback, ) - window.runPendingOperations() + history.runPendingOperations() } private fun assertStack(urls: List, index: Int = urls.lastIndex) { diff --git a/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt deleted file mode 100644 index e45aa2c77..000000000 --- a/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestHistory.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.arkivanov.decompose.router.stack.webhistory - -import kotlin.test.assertEquals - -class TestHistory( - private val scheduleOperation: (() -> Unit) -> Unit, - private val onPopState: (state: String?) -> Unit, -) : DefaultWebHistoryController.History { - - private val stack: MutableList = mutableListOf(Entry()) - private var index = 0 - - override val state: String? get() = stack[index].data - - override fun go(delta: Int) { - scheduleOperation { - index += delta - onPopState(stack[index].data) - } - } - - override fun pushState(data: String, url: String?) { - while (stack.lastIndex > index) { - stack.removeLast() - } - - stack += Entry(data = data, url = url) - index++ - } - - override fun replaceState(data: String, url: String?) { - stack[index] = Entry(data = data, url = url) - } - - fun assertStack(urls: List, index: Int = urls.lastIndex) { - assertEquals(urls, stack.map(Entry::url)) - assertEquals(index, this.index) - } - - class Entry( - val data: String? = null, - val url: String? = null, - ) -} diff --git a/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt deleted file mode 100644 index 1ddf1b151..000000000 --- a/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/stack/webhistory/TestWindow.kt +++ /dev/null @@ -1,25 +0,0 @@ -package com.arkivanov.decompose.router.stack.webhistory - -class TestWindow : DefaultWebHistoryController.Window { - - private val pendingOperations = ArrayList<() -> Unit>() - private var onPopStateListener: ((state: String?) -> Unit)? = null - - override val history: TestHistory = - TestHistory( - scheduleOperation = pendingOperations::add, - onPopState = { onPopStateListener?.invoke(it) }, - ) - - override fun setOnPopStateListener(listener: (state: String?) -> Unit) { - onPopStateListener = listener - } - - fun runPendingOperations() { - while (true) { - val operations = pendingOperations.toList().takeUnless(List<*>::isEmpty) ?: break - pendingOperations.clear() - operations.forEach { it() } - } - } -} diff --git a/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/webhistory/TestBrowserHistory.kt b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/webhistory/TestBrowserHistory.kt new file mode 100644 index 000000000..d48e47922 --- /dev/null +++ b/decompose/src/webTest/kotlin/com/arkivanov/decompose/router/webhistory/TestBrowserHistory.kt @@ -0,0 +1,64 @@ +package com.arkivanov.decompose.router.webhistory + +import kotlin.test.assertEquals + +class TestBrowserHistory : BrowserHistory { + + private val pendingOperations = ArrayList<() -> Unit>() + private var onPopStateListener: ((state: String?) -> Unit)? = null + private val stack: MutableList = mutableListOf(Entry()) + private var index = 0 + + override val state: String? get() = stack[index].data + + override fun go(delta: Int) { + scheduleOperation { + index += delta + onPopStateListener?.invoke(stack[index].data) + } + } + + fun navigate(delta: Int) { + go(delta = delta) + runPendingOperations() + } + + override fun pushState(data: String?, url: String?) { + while (stack.lastIndex > index) { + stack.removeLast() + } + + stack += Entry(data = data, url = url) + index++ + } + + override fun replaceState(data: String?, url: String?) { + stack[index] = Entry(data = data, url = url) + } + + override fun setOnPopStateListener(listener: (state: String?) -> Unit) { + onPopStateListener = listener + } + + private fun scheduleOperation(block: () -> Unit) { + pendingOperations += block + } + + fun runPendingOperations() { + while (true) { + val operations = pendingOperations.toList().takeUnless(List<*>::isEmpty) ?: break + pendingOperations.clear() + operations.forEach { it() } + } + } + + fun assertStack(urls: List, index: Int = urls.lastIndex) { + assertEquals(urls, stack.map(Entry::url)) + assertEquals(index, this.index) + } + + private class Entry( + val data: String? = null, + val url: String? = null, + ) +}