State_test.res

  1// test/State_test.res
  2open VitestHelpers
  3
  4// Helper to push waypoints to Y.Array via raw JS
  5let pushWaypoint = (yarray: Y.Array.t, waypoint: JSON.t): unit => {
  6  let _ = %raw(`(ya, wp) => { ya.push([wp]); }`)(yarray, waypoint)
  7}
  8
  9// Helper to create a waypoint JSON object
 10let makeWaypointJSON = (~id, ~lon, ~lat, ~label, ~gid, ~country, ~from=?, ~cons=?, ~nonroutable=?, ()) => {
 11  let dict = Dict.fromArray([
 12    ("type", JSON.Encode.string("waypoint")),
 13    ("id", JSON.Encode.string(id)),
 14    ("point", JSON.Encode.array([JSON.Encode.float(lon), JSON.Encode.float(lat)])),
 15    ("label", JSON.Encode.string(label)),
 16    ("gid", JSON.Encode.string(gid)),
 17    ("country", JSON.Encode.string(country)),
 18  ])
 19
 20  // Add optional from field (now map[string]route format)
 21  switch from {
 22  | Some(fromDict) => dict->Dict.set("from", JSON.Encode.object(fromDict))
 23  | None => ()
 24  }
 25
 26  // Add optional cons field (now array<string>)
 27  switch cons {
 28  | Some(consArr) =>
 29    dict->Dict.set("cons", JSON.Encode.array(consArr->Array.map(JSON.Encode.string)))
 30  | None => ()
 31  }
 32
 33  // Add nonroutable field (default to false)
 34  dict->Dict.set("nonroutable", JSON.Encode.bool(nonroutable->Option.getOr(false)))
 35
 36  dict->JSON.Encode.object
 37}
 38
 39// Helper to set up observer for a document (simulates Editor.res behavior)
 40let setupObserver = (docID: string, ydoc: Y.Doc.t): Preact.Signal.t<array<State.waypoint>> => {
 41  let waypointsArray = ydoc->Y.Array.get("waypoints")
 42  let waypointsSignal = State.getOrCreateWaypointsSignal(docID)
 43
 44  waypointsArray->Y.Array.observe(_event => {
 45    let jsonArray = waypointsArray->Y.Array.toJSON
 46    let waypoints = Waypoint.fromYjsArray(jsonArray)
 47    waypointsSignal->Preact.Signal.set(waypoints)
 48  })
 49
 50  waypointsSignal
 51}
 52
 53describe("State.waypoints Map", () => {
 54  testAsync("signal starts empty for new docID", async t => {
 55    let signal = State.getOrCreateWaypointsSignal("doc1")
 56    t->expect(signal->Preact.Signal.get->Array.length)->toEqual(0)
 57  })
 58
 59  testAsync("getOrCreateWaypointsSignal returns same signal for same docID", async t => {
 60    let signal1 = State.getOrCreateWaypointsSignal("doc2")
 61    let signal2 = State.getOrCreateWaypointsSignal("doc2")
 62    t->expect(signal1)->toBe(signal2)
 63  })
 64
 65  testAsync("getOrCreateWaypointsSignal returns different signals for different docIDs", async t => {
 66    let signal1 = State.getOrCreateWaypointsSignal("doc3")
 67    let signal2 = State.getOrCreateWaypointsSignal("doc4")
 68    t->expect(signal1)->not_->toBe(signal2)
 69  })
 70
 71  testAsync("observer updates signal when waypoints are added", async t => {
 72    let doc = Y.Doc.make()
 73    let waypointsSignal = setupObserver("doc5", doc)
 74    let waypointsArray = doc->Y.Array.get("waypoints")
 75
 76    // Add a waypoint
 77    let wp1 = makeWaypointJSON(
 78      ~id="wp1",
 79      ~lon=85.324,
 80      ~lat=27.717,
 81      ~label="Kathmandu",
 82      ~gid="kathmandu_gid",
 83      ~country="NP",
 84      (),
 85    )
 86    pushWaypoint(waypointsArray, wp1)
 87
 88    // Verify signal was updated
 89    let waypoints = waypointsSignal->Preact.Signal.get
 90    t->expect(waypoints->Array.length)->toEqual(1)
 91
 92    switch waypoints->Array.get(0) {
 93    | Some(wp) => {
 94        t->expect(wp.id)->toEqual("wp1")
 95        t->expect(wp.point)->toEqual((85.324, 27.717))
 96        t->expect(wp.label)->toEqual("Kathmandu")
 97        t->expect(wp.gid)->toEqual("kathmandu_gid")
 98        t->expect(wp.country)->toEqual("NP")
 99      }
100    | None => t->expect("waypoint not found")->toEqual("")
101    }
102  })
103
104  testAsync("multiple documents have independent waypoints", async t => {
105    let docA = Y.Doc.make()
106    let docB = Y.Doc.make()
107
108    let signalA = setupObserver("docA", docA)
109    let signalB = setupObserver("docB", docB)
110
111    let arrayA = docA->Y.Array.get("waypoints")
112    let arrayB = docB->Y.Array.get("waypoints")
113
114    // Add waypoint to docA only
115    let wpA = makeWaypointJSON(
116      ~id="wpA",
117      ~lon=85.0,
118      ~lat=27.0,
119      ~label="Location A",
120      ~gid="gidA",
121      ~country="NP",
122      (),
123    )
124    pushWaypoint(arrayA, wpA)
125
126    // Verify docA has 1 waypoint, docB has 0
127    t->expect(signalA->Preact.Signal.get->Array.length)->toEqual(1)
128    t->expect(signalB->Preact.Signal.get->Array.length)->toEqual(0)
129
130    // Add waypoint to docB
131    let wpB = makeWaypointJSON(
132      ~id="wpB",
133      ~lon=83.0,
134      ~lat=28.0,
135      ~label="Location B",
136      ~gid="gidB",
137      ~country="NP",
138      (),
139    )
140    pushWaypoint(arrayB, wpB)
141
142    // Verify both docs have 1 waypoint each
143    t->expect(signalA->Preact.Signal.get->Array.length)->toEqual(1)
144    t->expect(signalB->Preact.Signal.get->Array.length)->toEqual(1)
145  })
146
147  testAsync("deserializes constraints correctly", async t => {
148    let doc = Y.Doc.make()
149    let waypointsSignal = setupObserver("doc6", doc)
150    let waypointsArray = doc->Y.Array.get("waypoints")
151
152    // Add waypoint with constraints (now array<string>)
153    let wp = makeWaypointJSON(
154      ~id="wp2",
155      ~lon=83.0,
156      ~lat=28.0,
157      ~label="Pokhara",
158      ~gid="pokhara_gid",
159      ~country="NP",
160      ~cons=["adv:high", "max-speed:80"],
161      (),
162    )
163    pushWaypoint(waypointsArray, wp)
164
165    let waypoints = waypointsSignal->Preact.Signal.get
166    t->expect(waypoints->Array.length)->toEqual(1)
167
168    switch waypoints->Array.get(0) {
169    | Some(wp) => {
170        // cons is now array<string>, not option<array<Constraint.t>>
171        t->expect(wp.cons->Array.length)->toEqual(2)
172        switch wp.cons->Array.get(0) {
173        | Some(c1) => {
174            t->expect(c1)->toEqual("adv:high")
175          }
176        | None => ()
177        }
178      }
179    | None => t->expect("waypoint not found")->toEqual("")
180    }
181  })
182})