From f99b40cd814a6ac93af17c1fd3b7e607a78cd4a3 Mon Sep 17 00:00:00 2001 From: Maurice Date: Mon, 2 Nov 2026 21:54:42 +0000 Subject: [PATCH] Managed mode: keep the journey page suite within its size baseline - The owner sign-in and the photo provider the suite runs with move into tests/helpers/journeyDetail.ts, so the page test is back under its line baseline, which drops to 4028. - Drop help/registry.base.ts, an unused copy of the help registry whose key builders the i18n key check could not resolve. --- client/scripts/size-baseline.json | 2 +- client/src/help/registry.base.ts | 148 -------------------- client/src/pages/JourneyDetailPage.test.tsx | 11 +- client/tests/helpers/journeyDetail.ts | 17 +++ 4 files changed, 21 insertions(+), 157 deletions(-) delete mode 100644 client/src/help/registry.base.ts create mode 100644 client/tests/helpers/journeyDetail.ts diff --git a/client/scripts/size-baseline.json b/client/scripts/size-baseline.json index 02df83ed1..72a08a761 100644 --- a/client/scripts/size-baseline.json +++ b/client/scripts/size-baseline.json @@ -18,7 +18,7 @@ "src/components/Studio/countryShapes.ts": 2595, "src/index.css": 1636, "src/mobile/screens/admin/MAdminPluginsPanel.tsx": 1507, - "src/pages/JourneyDetailPage.test.tsx": 4030, + "src/pages/JourneyDetailPage.test.tsx": 4028, "src/pages/LoginPage.tsx": 1348, "src/pages/TripPlannerPage.tsx": 1128, "src/pages/atlas/useAtlas.ts": 1210, diff --git a/client/src/help/registry.base.ts b/client/src/help/registry.base.ts deleted file mode 100644 index 4b4c60899..000000000 --- a/client/src/help/registry.base.ts +++ /dev/null @@ -1,148 +0,0 @@ -import type { HelpContext, HelpGuide } from './types' -import { dashboardContext, dashboardGuides } from './contexts/dashboard' -import { vacayContext, vacayGuides } from './contexts/vacay' -import { atlasContext, atlasGuides } from './contexts/atlas' -import { collectionsContext, collectionsGuides } from './contexts/collections' -import { journeyContext, journeyGuides, journalContext, journalGuides, studioContext, studioGuides } from './contexts/journey' -import { settingsContext, settingsTabContexts, settingsGuides } from './contexts/settings' -import { adminContext, adminTabContexts, adminGuides } from './contexts/admin' -import { tripContext, tripGuides } from './contexts/trip' -import { tripPlacesContext, tripPlacesGuides } from './contexts/tripPlaces' -import { tripDaysContext, tripDaysGuides } from './contexts/tripDays' -import { tripRoadtripContext, tripRoadtripGuides } from './contexts/tripRoadtrip' -import { tripCollabContext, tripCollabGuides } from './contexts/tripCollab' -import { tripFilesContext, tripFilesGuides } from './contexts/tripFiles' -import { tripCostsContext, tripCostsGuides } from './contexts/tripCosts' -import { tripListsContext, tripListsGuides } from './contexts/tripLists' -import { tripBookingsContext, tripBookingsGuides } from './contexts/tripBookings' -import { tripTransportsContext, tripTransportsGuides } from './contexts/tripTransports' -import { tripMapContext, tripMapGuides } from './contexts/tripMap' -import { tripDayDetailContext, tripDayDetailGuides } from './contexts/tripDayDetail' -import { tripPlaceContext, tripPlaceGuides } from './contexts/tripPlace' -import { tripToursContext, tripToursGuides } from './contexts/tripTours' -import { tripTourDetailContext, tripTourDetailGuides } from './contexts/tripTourDetail' -import { tripTourPlannerContext, tripTourPlannerGuides } from './contexts/tripTourPlanner' -import { tripTourImportContext, tripTourImportGuides } from './contexts/tripTourImport' -import { tripTourMapContext, tripTourMapGuides } from './contexts/tripTourMap' -import { tripTourStagesContext, tripTourStagesGuides } from './contexts/tripTourStages' -import { tripTourHikeContext, tripTourHikeGuides } from './contexts/tripTourHike' -import { tripTourWalkContext, tripTourWalkGuides } from './contexts/tripTourWalk' -import { tripTourBikeContext, tripTourBikeGuides } from './contexts/tripTourBike' -import { tripTourSkiContext, tripTourSkiGuides } from './contexts/tripTourSki' -import { tripTourPaddleContext, tripTourPaddleGuides } from './contexts/tripTourPaddle' -import { tripTourHorseContext, tripTourHorseGuides } from './contexts/tripTourHorse' -import { notificationsContext, notificationsGuides } from './contexts/notifications' - -/** - * Everything the help center knows, composed from one file per screen under - * `contexts/`. A screen announces which context it is with `` - * (or `useHelpContext`), and the panel looks the rest up here. - */ - -// The order of the screen switcher: the trip right under My Trips, where it is opened from. -const CONTEXT_LIST: HelpContext[] = [ - dashboardContext, - // The trip and its own screens, in the order a reader meets them: the three - // columns of the plan, the two panels that open over it, then the tabs in the - // order the tab bar has them, and the drive last because an addon decides it. - tripContext, tripDaysContext, tripPlacesContext, tripMapContext, - tripPlaceContext, tripDayDetailContext, - tripTransportsContext, tripBookingsContext, tripListsContext, tripCostsContext, tripFilesContext, tripCollabContext, - tripRoadtripContext, - // Tours: the list and the card on the Plan tab, then the Tours tab with its - // jobs and one screen per kind of tour, in the order the type picker has them. - tripToursContext, tripTourDetailContext, - tripTourPlannerContext, tripTourImportContext, tripTourMapContext, tripTourStagesContext, - tripTourHikeContext, tripTourWalkContext, tripTourBikeContext, tripTourSkiContext, tripTourPaddleContext, tripTourHorseContext, - notificationsContext, - vacayContext, atlasContext, collectionsContext, journeyContext, journalContext, studioContext, - settingsContext, ...settingsTabContexts, adminContext, ...adminTabContexts, -] -const GUIDE_LIST: HelpGuide[] = [ - ...dashboardGuides, ...vacayGuides, ...atlasGuides, ...collectionsGuides, ...journeyGuides, ...journalGuides, ...studioGuides, - ...settingsGuides, ...adminGuides, - ...tripGuides, ...tripDaysGuides, ...tripPlacesGuides, ...tripMapGuides, - ...tripPlaceGuides, ...tripDayDetailGuides, - ...tripTransportsGuides, ...tripBookingsGuides, ...tripListsGuides, ...tripCostsGuides, ...tripFilesGuides, ...tripCollabGuides, - ...tripRoadtripGuides, - ...tripToursGuides, ...tripTourDetailGuides, - ...tripTourPlannerGuides, ...tripTourImportGuides, ...tripTourMapGuides, ...tripTourStagesGuides, - ...tripTourHikeGuides, ...tripTourWalkGuides, ...tripTourBikeGuides, ...tripTourSkiGuides, ...tripTourPaddleGuides, ...tripTourHorseGuides, - ...notificationsGuides, -] - -export const HELP_CONTEXTS: ReadonlyMap = new Map(CONTEXT_LIST.map(c => [c.id, c])) -export const HELP_GUIDES: ReadonlyMap = new Map(GUIDE_LIST.map(g => [g.id, g])) - -export function getHelpContext(id: string | null | undefined): HelpContext | null { - return id ? (HELP_CONTEXTS.get(id) ?? null) : null -} - -export function getHelpGuide(id: string | null | undefined): HelpGuide | null { - return id ? (HELP_GUIDES.get(id) ?? null) : null -} - -/** The guides of a context, in the order the context lists them. */ -export function guidesFor(context: HelpContext): HelpGuide[] { - return context.guides.map(id => HELP_GUIDES.get(id)).filter((g): g is HelpGuide => g !== undefined) -} - -export function allHelpGuides(): HelpGuide[] { - return GUIDE_LIST -} - -/** Every screen that has help, in registration order, for the screen switcher. */ -export function allHelpContexts(): HelpContext[] { - return CONTEXT_LIST -} - -/** The screens that stand on their own; their sub-screens hang under them in the switcher. */ -export function topLevelHelpContexts(): HelpContext[] { - return CONTEXT_LIST.filter(c => !c.parent) -} - -/** The sub-screens of a screen, in registration order. */ -export function childHelpContexts(parentId: string): HelpContext[] { - return CONTEXT_LIST.filter(c => c.parent === parentId) -} - -/** The screen and the screens above it, outermost first, for a breadcrumb. */ -export function helpContextTrail(context: HelpContext): HelpContext[] { - const trail: HelpContext[] = [context] - let parent = context.parent ? HELP_CONTEXTS.get(context.parent) : undefined - while (parent && !trail.includes(parent)) { - trail.unshift(parent) - parent = parent.parent ? HELP_CONTEXTS.get(parent.parent) : undefined - } - return trail -} - -// ── Catalogue keys ────────────────────────────────────────────────────────── - -export const ctxKey = (id: string, part: 'title' | 'summary'): string => `help.ctx.${id}.${part}` -export const ctxBulletKey = (id: string, n: number): string => `help.ctx.${id}.bullet.${n}` -export const guideKey = (id: string, part: 'title' | 'goal' | 'result' | 'link'): string => - `help.guide.${id}.${part}` -export const guideStepKey = (id: string, n: number): string => `help.guide.${id}.step.${n}` -export const guideTipKey = (id: string, n: number): string => `help.guide.${id}.tip.${n}` - -// ── Media paths ───────────────────────────────────────────────────────────── - -/** - * Generated artwork under `public/help-media/`. WebP and WebM on purpose: the - * service worker precaches `png`, and a couple of hundred help pictures do not - * belong in every visitor's first download. - */ -const MEDIA_BASE = '/help-media' - -export const helpMedia = { - hero: (contextId: string): string => `${MEDIA_BASE}/ctx/${contextId}.webp`, - step: (guideId: string, n: number): string => `${MEDIA_BASE}/${guideId}/step-${n}.webp`, - result: (guideId: string): string => `${MEDIA_BASE}/${guideId}/result.webp`, - clip: (guideId: string, n: number): string => `${MEDIA_BASE}/${guideId}/step-${n}.loop.webp`, -} - -/** Wiki route for a doc link, with the heading anchor when the link names one. */ -export function docsRoute(link: { slug: string; anchor?: string }): string { - return `/help/${link.slug}${link.anchor ? `#${link.anchor}` : ''}` -} diff --git a/client/src/pages/JourneyDetailPage.test.tsx b/client/src/pages/JourneyDetailPage.test.tsx index ac89ba936..7821f2a2c 100644 --- a/client/src/pages/JourneyDetailPage.test.tsx +++ b/client/src/pages/JourneyDetailPage.test.tsx @@ -4,11 +4,9 @@ import { render, screen, waitFor, cleanup, within } from '../../tests/helpers/re import userEvent from '@testing-library/user-event'; import { http, HttpResponse } from 'msw'; import { server } from '../../tests/helpers/msw/server'; -import { resetAllStores, seedStore } from '../../tests/helpers/store'; -import { useAddonStore } from '../store/addonStore'; +import { resetAllStores } from '../../tests/helpers/store'; +import { seedJourneyOwner } from '../../tests/helpers/journeyDetail'; import { buildUser } from '../../tests/helpers/factories'; -import { useAuthStore } from '../store/authStore'; -import { usePermissionsStore } from '../store/permissionsStore'; import { useJourneyStore } from '../store/journeyStore'; import JourneyDetailPage from './JourneyDetailPage'; @@ -239,10 +237,7 @@ beforeEach(() => { vi.clearAllMocks(); resetAllStores(); useJourneyStore.setState(useJourneyStore.getState(), true); - seedStore(useAuthStore, { isAuthenticated: true, user: ownerUser }); - seedStore(usePermissionsStore, { level: 'owner' } as any); - // A photo provider on for the install, so the entry editor offers External photos. - seedStore(useAddonStore, { addons: [{ id: 'immich', name: 'Immich', type: 'photo_provider', enabled: true } as never] }); + seedJourneyOwner(ownerUser); setupDefaultHandlers(); }); diff --git a/client/tests/helpers/journeyDetail.ts b/client/tests/helpers/journeyDetail.ts new file mode 100644 index 000000000..65cc548fe --- /dev/null +++ b/client/tests/helpers/journeyDetail.ts @@ -0,0 +1,17 @@ +import { useAddonStore } from '../../src/store/addonStore'; +import { useAuthStore } from '../../src/store/authStore'; +import { usePermissionsStore } from '../../src/store/permissionsStore'; +import type { User } from '../../src/types'; +import { seedStore } from './store'; + +/** + * The install the journey page suite runs on: the owner is signed in with full + * rights, and a photo provider is on, so the entry editor offers External photos. + */ +export function seedJourneyOwner(user: User): void { + seedStore(useAuthStore, { isAuthenticated: true, user }); + seedStore(usePermissionsStore, { level: 'owner' } as never); + seedStore(useAddonStore, { + addons: [{ id: 'immich', name: 'Immich', type: 'photo_provider', enabled: true } as never], + }); +}