diff --git a/apps/planner/app/routes/new.tsx b/apps/planner/app/routes/new.tsx index f37c72c..8a9f948 100644 --- a/apps/planner/app/routes/new.tsx +++ b/apps/planner/app/routes/new.tsx @@ -1,7 +1,7 @@ import { redirect, data } from "react-router"; import type { Route } from "./+types/new"; import { createSession, initializeSessionWithWaypoints } from "~/lib/sessions"; -import { parseGpx } from "@trails-cool/gpx"; +import { parseGpxAsync } from "@trails-cool/gpx"; import { checkRateLimit } from "~/lib/rate-limit"; export async function loader({ request }: Route.LoaderArgs) { @@ -35,7 +35,7 @@ export async function loader({ request }: Route.LoaderArgs) { if (gpxEncoded) { try { const gpx = decodeURIComponent(gpxEncoded); - const gpxData = parseGpx(gpx); + const gpxData = await parseGpxAsync(gpx); initializeSessionWithWaypoints(session.id, gpxData.waypoints); } catch { // Continue with empty session if GPX is invalid diff --git a/packages/gpx/src/generate.test.ts b/packages/gpx/src/generate.test.ts index 6b4fd34..0eefc9f 100644 --- a/packages/gpx/src/generate.test.ts +++ b/packages/gpx/src/generate.test.ts @@ -1,6 +1,6 @@ import { describe, it, expect } from "vitest"; import { generateGpx } from "./generate.ts"; -import { parseGpx } from "./parse.ts"; +import { parseGpxAsync } from "./parse.ts"; describe("generateGpx", () => { it("generates valid GPX with name", () => { @@ -36,13 +36,13 @@ describe("generateGpx", () => { expect(gpx).toContain("Route <A> & B"); }); - it("produces round-trippable GPX", () => { + it("produces round-trippable GPX", async () => { const original = generateGpx({ name: "Round Trip", waypoints: [{ lat: 52.52, lon: 13.405, name: "Start" }], tracks: [[{ lat: 52.52, lon: 13.405, ele: 34 }, { lat: 48.137, lon: 11.576, ele: 519 }]], }); - const parsed = parseGpx(original); + const parsed = await parseGpxAsync(original); expect(parsed.name).toBe("Round Trip"); expect(parsed.waypoints).toHaveLength(1); expect(parsed.waypoints[0]!.name).toBe("Start"); diff --git a/packages/gpx/src/index.ts b/packages/gpx/src/index.ts index edc7f3d..e84a560 100644 --- a/packages/gpx/src/index.ts +++ b/packages/gpx/src/index.ts @@ -1,3 +1,3 @@ -export { parseGpx, parseGpxAsync } from "./parse.ts"; +export { parseGpxAsync } from "./parse.ts"; export { generateGpx } from "./generate.ts"; export type { GpxData, TrackPoint, ElevationProfile } from "./types.ts"; diff --git a/packages/gpx/src/parse.test.ts b/packages/gpx/src/parse.test.ts index f38ac8b..6e05634 100644 --- a/packages/gpx/src/parse.test.ts +++ b/packages/gpx/src/parse.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { parseGpx } from "./parse.ts"; +import { parseGpxAsync } from "./parse.ts"; const sampleGpx = ` @@ -15,42 +15,50 @@ const sampleGpx = ` `; -describe("parseGpx", () => { - it("parses route name", () => { - const result = parseGpx(sampleGpx); +describe("parseGpxAsync", () => { + it("parses route name", async () => { + const result = await parseGpxAsync(sampleGpx); expect(result.name).toBe("Test Route"); }); - it("parses waypoints with lat, lon, and name", () => { - const result = parseGpx(sampleGpx); + it("parses waypoints with lat, lon, and name", async () => { + const result = await parseGpxAsync(sampleGpx); expect(result.waypoints).toHaveLength(2); expect(result.waypoints[0]).toEqual({ lat: 52.52, lon: 13.405, name: "Berlin" }); expect(result.waypoints[1]).toEqual({ lat: 48.137, lon: 11.576, name: "Munich" }); }); - it("parses track points with elevation", () => { - const result = parseGpx(sampleGpx); + it("parses track points with elevation", async () => { + const result = await parseGpxAsync(sampleGpx); expect(result.tracks).toHaveLength(1); expect(result.tracks[0]).toHaveLength(3); expect(result.tracks[0]![0]).toEqual({ lat: 52.52, lon: 13.405, ele: 34, time: undefined }); }); - it("computes elevation gain and loss", () => { - const result = parseGpx(sampleGpx); + it("computes elevation gain and loss", async () => { + const result = await parseGpxAsync(sampleGpx); expect(result.elevation.gain).toBeGreaterThan(0); expect(result.elevation.loss).toBe(0); // monotonically increasing elevation expect(result.elevation.gain).toBe(485); // 113-34 + 519-113 }); - it("builds elevation profile", () => { - const result = parseGpx(sampleGpx); + it("builds elevation profile", async () => { + const result = await parseGpxAsync(sampleGpx); expect(result.elevation.profile).toHaveLength(3); expect(result.elevation.profile[0]!.distance).toBe(0); expect(result.elevation.profile[0]!.elevation).toBe(34); expect(result.elevation.profile[2]!.distance).toBeGreaterThan(0); }); - it("throws on invalid XML", () => { - expect(() => parseGpx("not xml at all <<<<")).toThrow("Invalid GPX XML"); + it("computes total distance independently of elevation", async () => { + const result = await parseGpxAsync(sampleGpx); + expect(result.distance).toBeGreaterThan(0); + // Berlin to Munich is ~500km, our 3-point track should be in that range + expect(result.distance).toBeGreaterThan(400_000); + expect(result.distance).toBeLessThan(600_000); + }); + + it("throws on invalid XML", async () => { + await expect(parseGpxAsync("not xml at all <<<<")).rejects.toThrow(); }); }); diff --git a/packages/gpx/src/parse.ts b/packages/gpx/src/parse.ts index 70a3a0e..1ff1b79 100644 --- a/packages/gpx/src/parse.ts +++ b/packages/gpx/src/parse.ts @@ -15,7 +15,7 @@ async function getDOMParser(): Promise { return _LinkedDOMParser; } -export function parseGpx(xml: string): GpxData { +function parseGpx(xml: string): GpxData { // Synchronous path for browser if (typeof DOMParser !== "undefined") { return parseGpxWithParser(new DOMParser(), xml);