@@ -17,6 +17,7 @@ import dev.hotwire.core.turbo.config.PathConfiguration.Location
1717import dev.hotwire.core.turbo.nav.Presentation
1818import dev.hotwire.core.turbo.nav.PresentationContext
1919import dev.hotwire.core.turbo.nav.QueryStringPresentation
20+ import dev.hotwire.core.turbo.visit.VisitAction
2021import dev.hotwire.core.turbo.visit.VisitOptions
2122import org.assertj.core.api.Assertions.assertThat
2223import org.assertj.core.api.Assertions.assertThatThrownBy
@@ -37,6 +38,7 @@ class NavigatorRuleTest {
3738 private val homeUrl = " https://hotwired.dev/home"
3839 private val newHomeUrl = " https://hotwired.dev/new-home"
3940 private val featureUrl = " https://hotwired.dev/feature"
41+ private val featureTwoUrl = " https://hotwired.dev/feature-two"
4042 private val newUrl = " https://hotwired.dev/feature/new"
4143 private val editUrl = " https://hotwired.dev/feature/edit"
4244 private val recedeUrl = " https://hotwired.dev/custom/recede"
@@ -203,6 +205,56 @@ class NavigatorRuleTest {
203205 assertThat(rule.newNavOptions).isEqualTo(navOptions)
204206 }
205207
208+ @Test
209+ fun `navigating back to feature from modal context with replace action maintains replace` () {
210+ controller.navigate(webDestinationId, locationArgs(featureUrl))
211+ controller.navigate(webModalDestinationId, locationArgs(newUrl))
212+ val rule = getNavigatorRule(featureUrl, VisitOptions (action = VisitAction .REPLACE ))
213+
214+ // Current destination
215+ assertThat(rule.previousLocation).isEqualTo(featureUrl)
216+ assertThat(rule.currentLocation).isEqualTo(newUrl)
217+ assertThat(rule.currentPresentationContext).isEqualTo(PresentationContext .MODAL )
218+ assertThat(rule.isAtStartDestination).isFalse()
219+
220+ // New destination
221+ assertThat(rule.newLocation).isEqualTo(featureUrl)
222+ assertThat(rule.newPresentationContext).isEqualTo(PresentationContext .DEFAULT )
223+ assertThat(rule.newPresentation).isEqualTo(Presentation .POP )
224+ assertThat(rule.newQueryStringPresentation).isEqualTo(QueryStringPresentation .REPLACE )
225+ assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode .DISMISS_MODAL )
226+ assertThat(rule.newModalResult?.location).isEqualTo(featureUrl)
227+ assertThat(rule.newModalResult?.options?.action).isEqualTo(VisitAction .REPLACE )
228+ assertThat(rule.newDestinationUri).isEqualTo(webUri)
229+ assertThat(rule.newDestination).isNotNull()
230+ assertThat(rule.newNavOptions).isEqualTo(navOptions)
231+ }
232+
233+ @Test
234+ fun `navigating to new feature from modal context with replace action changes to advance` () {
235+ controller.navigate(webDestinationId, locationArgs(featureUrl))
236+ controller.navigate(webModalDestinationId, locationArgs(newUrl))
237+ val rule = getNavigatorRule(featureTwoUrl, VisitOptions (action = VisitAction .REPLACE ))
238+
239+ // Current destination
240+ assertThat(rule.previousLocation).isEqualTo(featureUrl)
241+ assertThat(rule.currentLocation).isEqualTo(newUrl)
242+ assertThat(rule.currentPresentationContext).isEqualTo(PresentationContext .MODAL )
243+ assertThat(rule.isAtStartDestination).isFalse()
244+
245+ // New destination
246+ assertThat(rule.newLocation).isEqualTo(featureTwoUrl)
247+ assertThat(rule.newPresentationContext).isEqualTo(PresentationContext .DEFAULT )
248+ assertThat(rule.newPresentation).isEqualTo(Presentation .POP )
249+ assertThat(rule.newQueryStringPresentation).isEqualTo(QueryStringPresentation .REPLACE )
250+ assertThat(rule.newNavigationMode).isEqualTo(NavigatorMode .DISMISS_MODAL )
251+ assertThat(rule.newModalResult?.location).isEqualTo(featureTwoUrl)
252+ assertThat(rule.newModalResult?.options?.action).isEqualTo(VisitAction .ADVANCE )
253+ assertThat(rule.newDestinationUri).isEqualTo(webUri)
254+ assertThat(rule.newDestination).isNotNull()
255+ assertThat(rule.newNavOptions).isEqualTo(navOptions)
256+ }
257+
206258 @Test
207259 fun `navigate from modal to same modal` () {
208260 controller.navigate(webModalDestinationId, locationArgs(newUrl))
0 commit comments