From db531d2beffe90425d1f1e1cb0474740d2d9471c Mon Sep 17 00:00:00 2001 From: Maurice Date: Wed, 14 Oct 2026 12:05:49 +0200 Subject: [PATCH] test(tours): the wiring test reads the map stage, the phone timeline test hands the planner its tours --- client/src/pages/tripPlanner/b4a-wiring.test.ts | 14 +++++++++----- .../tests/unit/mobile/trip/MPlanTimeline.test.tsx | 4 ++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/client/src/pages/tripPlanner/b4a-wiring.test.ts b/client/src/pages/tripPlanner/b4a-wiring.test.ts index e17a03716..89a4ffca3 100644 --- a/client/src/pages/tripPlanner/b4a-wiring.test.ts +++ b/client/src/pages/tripPlanner/b4a-wiring.test.ts @@ -14,10 +14,13 @@ const pageControllerPath = 'client/src/pages/tripPlanner/useTripPlannerPage.ts' const pageText = readFileSync(resolve(process.cwd(), 'src/pages/TripPlannerPage.tsx'), 'utf8') const hookText = readFileSync(resolve(process.cwd(), 'src/pages/tripPlanner/useTripPlanner.ts'), 'utf8') const pageControllerText = readFileSync(resolve(process.cwd(), 'src/pages/tripPlanner/useTripPlannerPage.ts'), 'utf8') +// The Tours map and its edit gates live in the map stage the page renders. +const mapStageText = readFileSync(resolve(process.cwd(), 'src/pages/tripPlanner/TourPlannerMapStage.tsx'), 'utf8') const parse = (file: string, text: string) => ts.createSourceFile(file, text, ts.ScriptTarget.Latest, true) const page = parse(pagePath, pageText) const hook = parse(hookPath, hookText) const pageController = parse(pageControllerPath, pageControllerText) +const mapStage = parse('client/src/pages/tripPlanner/TourPlannerMapStage.tsx', mapStageText) function collect(source: ts.Node, predicate: (node: ts.Node) => boolean): ts.Node[] { const matches: ts.Node[] = [] @@ -180,10 +183,11 @@ test('B4A desktop page delegates Tours state and permissions to its page hook', } } - const mapClickGate = collect(page, node => ts.isJsxAttribute(node) - && node.name.getText(page) === 'onMapClick' + assert.ok(jsxTags(page, 'TourPlannerMapStage').length === 1) + const mapClickGate = collect(mapStage, node => ts.isJsxAttribute(node) + && node.name.getText(mapStage) === 'onMapClick' && node.initializer && ts.isJsxExpression(node.initializer) - && !!node.initializer.expression && node.initializer.expression.getText(page).includes('canPlaceEdit')) + && !!node.initializer.expression && node.initializer.expression.getText(mapStage).includes('canPlaceEdit')) assert.ok(mapClickGate.length > 0) }) @@ -191,9 +195,9 @@ test('B4A dedicated desktop controller retains GPX, draft, focus, and saved rail assert.ok(callNamed(pageController, 'useTourPlanner')) assert.ok(pageText.includes('const pageState = useTripPlannerPage()')) assert.ok(pageControllerText.includes("tourPlanner.mode.type === 'view-gpx'")) - for (const mode of ['new-draft', 'edit-saved']) assert.ok(pageText.includes(`tourPlanner.mode.type === '${mode}'`)) + for (const mode of ['new-draft', 'edit-saved']) assert.ok(mapStageText.includes(`tourPlanner.mode.type === '${mode}'`)) assert.match(pageControllerText, /readOnlyGpxAnalysis\?\.routeCoordinates/) - assert.match(pageText, /routeProfileFocus=\{tourPlanner\.routeProfileFocus\}/) + assert.match(mapStageText, /routeProfileFocus=\{tourPlanner\.routeProfileFocus\}/) assert.match(pageText, /tourPlanner\.viewGpxTour\(tour, place\?\.route_geometry \?\? null\)/) assert.match(pageControllerText, /onSaved:[\s\S]*?upsertTour\(result\.tour\)/) assert.doesNotMatch(pageText, /setSelectedPlaceId\(result\.tour/) diff --git a/client/tests/unit/mobile/trip/MPlanTimeline.test.tsx b/client/tests/unit/mobile/trip/MPlanTimeline.test.tsx index a7d7d10c2..3270ab256 100644 --- a/client/tests/unit/mobile/trip/MPlanTimeline.test.tsx +++ b/client/tests/unit/mobile/trip/MPlanTimeline.test.tsx @@ -365,7 +365,7 @@ describe('MPlanTimeline', () => { const row: PlanRow = { key: 'pl-91', kind: 'place', item, assignment: tour, linkedReservations: [], invalidTour: true } it('shows one localized invalid-endpoint warning and selects the Tour through the shared Place path', () => { - const { planner } = renderTimeline({ rows: [row], merged: [item], upNext: null }, { toursEnabled: true }) + const { planner } = renderTimeline({ rows: [row], merged: [item], upNext: null }, { toursEnabled: true, tours: [] }) expect(screen.getAllByText('tours.dayRoute.endpointUnknown')).toHaveLength(1) fireEvent.click(screen.getByText('Assigned Tour')) @@ -376,7 +376,7 @@ describe('MPlanTimeline', () => { it('does not show the warning for valid status or with Tours disabled', () => { const validRow: PlanRow = { ...row, invalidTour: false } - const first = renderTimeline({ rows: [validRow], merged: [item], upNext: null }, { toursEnabled: true }) + const first = renderTimeline({ rows: [validRow], merged: [item], upNext: null }, { toursEnabled: true, tours: [] }) expect(screen.queryByText('tours.dayRoute.endpointUnknown')).not.toBeInTheDocument() first.unmount()