<commit_before>package io.udash.routing

import io.udash._
import io.udash.testing._
import org.scalactic.source.Position

class RoutingEngineTest extends UdashFrontendTest with TestRouting {

  "RoutingEngine" should {
    "render valid views on url change" in {
      val rootViewFactory = new TestViewFactory[RootState]
      val objectViewFactory = new TestViewFactory[ObjectState.type]
      val nextObjectViewFactory = new TestViewFactory[NextObjectState.type]
      val classViewFactory = new TestViewFactory[ClassState]
      val class2ViewFactory = new TestViewFactory[ClassState]
      val errorViewFactory = new TestViewFactory[ErrorState.type]
      val state2VP: Map[TestState, TestViewFactory[_ <: TestState]] = Map[TestState, TestViewFactory[_ <: TestState]](
        RootState(None) -> rootViewFactory,
        RootState(Some(1)) -> rootViewFactory,
        RootState(Some(2)) -> rootViewFactory,
        ObjectState -> objectViewFactory,
        NextObjectState -> nextObjectViewFactory,
        ClassState("abc", 1) -> classViewFactory,
        ClassState("abcd", 234) -> class2ViewFactory,
        ErrorState -> errorViewFactory
      )

      def testClosed(pattern: TestViewFactory[_] => Boolean)(implicit position: Position): Unit = {
        state2VP.mapValues(pattern).toVector.distinct.foreach { case (state, status) =>
          state -> status shouldBe state -> state2VP(state).view.closed
          state -> status shouldBe state -> state2VP(state).presenter.closed
        }
        state2VP.valuesIterator.foreach { vf =>
          vf.view.closed = false
          vf.presenter.closed = false
        }
      }

      initTestRoutingEngine(state2vp = state2VP)

      testClosed(_ => false)

      routingEngine.handleUrl(Url("/"))

      renderer.views.size should be(2)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.lastSubPathToLeave should be(Nil)
      renderer.lastPathToAdd.size should be(2)
      testClosed(_ => false)

      routingEngine.handleUrl(Url("/next"))

      renderer.views.size should be(3)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.views(2) should be(nextObjectViewFactory.view)
      renderer.lastSubPathToLeave.size should be(2)
      renderer.lastPathToAdd should be(nextObjectViewFactory.view :: Nil)
      testClosed(_ => false)

      routingEngine.handleUrl(Url("/"))

      renderer.views.size should be(2)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.lastSubPathToLeave.size should be(2)
      renderer.lastPathToAdd.size should be(0)
      testClosed {
        case `nextObjectViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/abc/1"))

      renderer.views.size should be(2)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(classViewFactory.view)
      renderer.lastSubPathToLeave.size should be(1)
      renderer.lastPathToAdd.size should be(1)
      testClosed {
        case `objectViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/abcd/234"))

      renderer.views.size should be(2)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(class2ViewFactory.view)
      renderer.lastSubPathToLeave.size should be(1)
      renderer.lastPathToAdd.size should be(1)
      testClosed {
        case `classViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/next"))

      renderer.views.size should be(3)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.views(2) should be(nextObjectViewFactory.view)
      renderer.lastSubPathToLeave.size should be(1)
      renderer.lastPathToAdd should be(objectViewFactory.view :: nextObjectViewFactory.view :: Nil)
      rootViewFactory.count shouldBe 1
      testClosed {
        case `class2ViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/next"), fullReload = true)

      renderer.views.size should be(3)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.views(2) should be(nextObjectViewFactory.view)
      renderer.lastSubPathToLeave.size should be(0)
      renderer.lastPathToAdd should be(rootViewFactory.view :: objectViewFactory.view :: nextObjectViewFactory.view :: Nil)
      rootViewFactory.count shouldBe 2
      testClosed {
        case `rootViewFactory` | `objectViewFactory` | `nextObjectViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/root/1"))

      rootViewFactory.count shouldBe 2
      testClosed {
        case `objectViewFactory` | `nextObjectViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/root/2"))

      rootViewFactory.count shouldBe 2
      testClosed(_ => false)
    }

    "fire state change callbacks" in {
      initTestRoutingEngine()

      var calls = 0
      var lastCallbackEvent: StateChangeEvent[TestState] = null
      val reg = routingEngine.onStateChange(ev => {
        lastCallbackEvent = ev<commit_msg>2.13 fix<commit_after>package io.udash.routing

import io.udash._
import io.udash.testing._
import org.scalactic.source.Position

class RoutingEngineTest extends UdashFrontendTest with TestRouting {

  "RoutingEngine" should {
    "render valid views on url change" in {
      val rootViewFactory = new TestViewFactory[RootState]
      val objectViewFactory = new TestViewFactory[ObjectState.type]
      val nextObjectViewFactory = new TestViewFactory[NextObjectState.type]
      val classViewFactory = new TestViewFactory[ClassState]
      val class2ViewFactory = new TestViewFactory[ClassState]
      val errorViewFactory = new TestViewFactory[ErrorState.type]
      val state2VP: Map[TestState, TestViewFactory[_ <: TestState]] = Map[TestState, TestViewFactory[_ <: TestState]](
        RootState(None) -> rootViewFactory,
        RootState(Some(1)) -> rootViewFactory,
        RootState(Some(2)) -> rootViewFactory,
        ObjectState -> objectViewFactory,
        NextObjectState -> nextObjectViewFactory,
        ClassState("abc", 1) -> classViewFactory,
        ClassState("abcd", 234) -> class2ViewFactory,
        ErrorState -> errorViewFactory
      )

      def testClosedAndReset(expectedClosed: TestViewFactory[_] => Boolean)(implicit position: Position): Unit = {
        import scala.collection.compat._
        state2VP.view.mapValues(expectedClosed).toVector.distinct.foreach { case (state, status) =>
          state -> status shouldBe state -> state2VP(state).view.closed
          state -> status shouldBe state -> state2VP(state).presenter.closed
        }
        state2VP.valuesIterator.foreach { vf =>
          vf.view.closed = false
          vf.presenter.closed = false
        }
      }

      initTestRoutingEngine(state2vp = state2VP)

      testClosedAndReset(_ => false)

      routingEngine.handleUrl(Url("/"))

      renderer.views.size should be(2)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.lastSubPathToLeave should be(Nil)
      renderer.lastPathToAdd.size should be(2)
      testClosedAndReset(_ => false)

      routingEngine.handleUrl(Url("/next"))

      renderer.views.size should be(3)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.views(2) should be(nextObjectViewFactory.view)
      renderer.lastSubPathToLeave.size should be(2)
      renderer.lastPathToAdd should be(nextObjectViewFactory.view :: Nil)
      testClosedAndReset(_ => false)

      routingEngine.handleUrl(Url("/"))

      renderer.views.size should be(2)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.lastSubPathToLeave.size should be(2)
      renderer.lastPathToAdd.size should be(0)
      testClosedAndReset {
        case `nextObjectViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/abc/1"))

      renderer.views.size should be(2)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(classViewFactory.view)
      renderer.lastSubPathToLeave.size should be(1)
      renderer.lastPathToAdd.size should be(1)
      testClosedAndReset {
        case `objectViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/abcd/234"))

      renderer.views.size should be(2)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(class2ViewFactory.view)
      renderer.lastSubPathToLeave.size should be(1)
      renderer.lastPathToAdd.size should be(1)
      testClosedAndReset {
        case `classViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/next"))

      renderer.views.size should be(3)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.views(2) should be(nextObjectViewFactory.view)
      renderer.lastSubPathToLeave.size should be(1)
      renderer.lastPathToAdd should be(objectViewFactory.view :: nextObjectViewFactory.view :: Nil)
      rootViewFactory.count shouldBe 1
      testClosedAndReset {
        case `class2ViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/next"), fullReload = true)

      renderer.views.size should be(3)
      renderer.views(0) should be(rootViewFactory.view)
      renderer.views(1) should be(objectViewFactory.view)
      renderer.views(2) should be(nextObjectViewFactory.view)
      renderer.lastSubPathToLeave.size should be(0)
      renderer.lastPathToAdd should be(rootViewFactory.view :: objectViewFactory.view :: nextObjectViewFactory.view :: Nil)
      rootViewFactory.count shouldBe 2
      testClosedAndReset {
        case `rootViewFactory` | `objectViewFactory` | `nextObjectViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/root/1"))

      rootViewFactory.count shouldBe 2
      testClosedAndReset {
        case `objectViewFactory` | `nextObjectViewFactory` => true
        case _ => false
      }

      routingEngine.handleUrl(Url("/root/2"))

      rootViewFactory.count shouldBe 2
      testClosedAndReset(_ => false)
    }

    "fire state change callbacks" in {
      initTestRoutingEngine()

      var calls = 0
      var lastCallbackEvent: StateChangeEvent[TestState] = null
      val reg = routingEngine.onStateChange(ev => {
        lastCallbackEvent = ev
{'fraction_non_alphanumeric': 0.09817581813181776, 'fraction_numerical': 0.011641763681364012, 'mean_word_length': 3.1216471477143934, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 67, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1791793', 'n_tokens_mistral': 3261, 'n_tokens_neox': 3163, 'n_words': 622}