mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-10-20 05:56:53 +00:00
test: follow the qol sweep in the e2e schemas and the assertions it changed
The hand-written e2e schemas gain the new columns and place_regions, and the unit assertions take the new arguments and fields. The Public API page names GET /api/auth/app-config for the running version.
This commit is contained in:
@@ -1917,8 +1917,9 @@ describe('DayDetailPanel time format', () => {
|
||||
);
|
||||
render(<DayDetailPanel {...defaultProps} />);
|
||||
|
||||
// The arrival day shows only the check-in; the check-out belongs to the last day (#2393).
|
||||
expect(await screen.findByText('15:00')).toBeInTheDocument();
|
||||
expect(screen.getByText('11:00')).toBeInTheDocument();
|
||||
expect(screen.queryByText('11:00')).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -99,7 +99,8 @@ describe('LocationSelect', () => {
|
||||
await screen.findByText('Gare du Nord');
|
||||
|
||||
expect(body.query).toBe('Gare du Nord');
|
||||
expect(lang).toBe('en-US');
|
||||
// The place name language, which follows the app's language when none is picked (#1799).
|
||||
expect(lang).toBe('en');
|
||||
});
|
||||
|
||||
it('FE-PLANNER-LOCSEL-008: a hit whose address equals its name shows no duplicate subtitle', async () => {
|
||||
|
||||
@@ -369,7 +369,7 @@ describe('VacayCalendar', () => {
|
||||
// Now click a month card cell
|
||||
await user.click(screen.getByText('click-0'))
|
||||
|
||||
expect(toggleCompanyHoliday).toHaveBeenCalledWith('2025-01-01')
|
||||
expect(toggleCompanyHoliday.mock.calls[0][0]).toBe('2025-01-01')
|
||||
expect(toggleEntry).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import MobileEntryView from '../components/Journey/MobileEntryView';
|
||||
import MobileMapTimeline from '../components/Journey/MobileMapTimeline';
|
||||
import PhotoLightbox from '../components/Journey/PhotoLightbox';
|
||||
import EmptyState from '../components/shared/EmptyState';
|
||||
import PublicLanguagePicker from '../components/shared/PublicLanguagePicker';
|
||||
import { PAGE_WIDTH, SharedFooter, SharedHero, SharedLoading, SharedTabBar, SharedTopBar } from './sharedTrip/SharedChrome';
|
||||
import { useTranslation } from '../i18n';
|
||||
import { formatLocationName } from '../utils/formatters';
|
||||
import { posterlessVideo } from './journeyDetail/JourneyDetailPage.helpers';
|
||||
@@ -129,13 +129,7 @@ export default function JourneyPublicPage() {
|
||||
desktopTwoColumn,
|
||||
} = useJourneyPublic();
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex min-h-screen items-center justify-center bg-zinc-50 dark:bg-zinc-950">
|
||||
<div className="h-6 w-6 animate-spin rounded-full border-2 border-zinc-300 border-t-zinc-900" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (loading) return <SharedLoading />;
|
||||
|
||||
if (error || !data) {
|
||||
return (
|
||||
@@ -578,202 +572,29 @@ export default function JourneyPublicPage() {
|
||||
</div>
|
||||
);
|
||||
|
||||
// Shared view tab bar
|
||||
const renderTabs = (views: typeof availableViews) =>
|
||||
views.length > 1 && (
|
||||
<div className="mb-6 flex w-fit overflow-hidden rounded-lg border border-zinc-200 bg-white dark:border-zinc-700 dark:bg-zinc-800">
|
||||
{views.map((v) => (
|
||||
<button type="button"
|
||||
key={v.id}
|
||||
onClick={() => setView(v.id)}
|
||||
className={`flex items-center gap-1.5 px-3 py-[7px] text-[12px] font-medium ${view === v.id
|
||||
? 'bg-zinc-900 text-white dark:bg-white dark:text-zinc-900'
|
||||
: 'text-zinc-500 hover:text-zinc-700 dark:hover:text-zinc-300'
|
||||
}`}
|
||||
>
|
||||
<v.icon size={13} />
|
||||
{v.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
// The same frame as a shared trip (#2541): the planner's top bar, the journey as
|
||||
// a postcard, and the views as tabs that stay in reach. The mobile map view keeps
|
||||
// its own floating switch above the full screen map.
|
||||
const firstDate = sortedDates[0];
|
||||
const lastDate = sortedDates[sortedDates.length - 1];
|
||||
const heroStats = [
|
||||
{ key: 'entries', value: stats.entries, label: t('journey.stats.entries') },
|
||||
{ key: 'photos', value: stats.photos, label: t('journey.stats.photos') },
|
||||
{ key: 'places', value: stats.places, label: t('journey.stats.places') },
|
||||
].filter(st => st.value > 0);
|
||||
const floatingSwitch = isMobile && view === 'timeline' && perms.share_timeline && perms.share_map;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-zinc-50 dark:bg-zinc-950">
|
||||
{/* Hero */}
|
||||
<div
|
||||
className="relative text-center text-white"
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #000 0%, #0f172a 50%, #1e293b 100%)',
|
||||
padding: '32px 20px 28px',
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{journey.cover_image && (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
inset: 0,
|
||||
backgroundImage: `url(/uploads/${journey.cover_image})`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundPosition: 'center',
|
||||
opacity: 0.15,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: -60,
|
||||
right: -60,
|
||||
width: 200,
|
||||
height: 200,
|
||||
borderRadius: '50%',
|
||||
background: 'rgba(255,255,255,0.03)',
|
||||
}}
|
||||
<div className="flex min-h-screen flex-col bg-surface-secondary" style={{ fontFamily: 'var(--font-system)' }}>
|
||||
<SharedTopBar title={journey.title} locale={locale} langOpen={showLangPicker} onLangOpenChange={setShowLangPicker} />
|
||||
<div className={`${PAGE_WIDTH} pt-5`}>
|
||||
<SharedHero
|
||||
trip={{ title: journey.title, description: journey.subtitle, cover_image: journey.cover_image, start_date: firstDate, end_date: lastDate }}
|
||||
stats={heroStats}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
bottom: -40,
|
||||
left: -40,
|
||||
width: 150,
|
||||
height: 150,
|
||||
borderRadius: '50%',
|
||||
background: 'rgba(255,255,255,0.02)',
|
||||
}}
|
||||
/>
|
||||
|
||||
<PublicLanguagePicker locale={locale} open={showLangPicker} onOpenChange={setShowLangPicker} />
|
||||
|
||||
{/* Logo */}
|
||||
<div
|
||||
style={{
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
width: 44,
|
||||
height: 44,
|
||||
borderRadius: 12,
|
||||
background: 'rgba(255,255,255,0.08)',
|
||||
backdropFilter: 'blur(8px)',
|
||||
marginBottom: 12,
|
||||
border: '1px solid rgba(255,255,255,0.1)',
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
<img src="/icons/icon-white.svg" alt="TREK" width={26} height={26} />
|
||||
</div>
|
||||
|
||||
<div
|
||||
style={{
|
||||
fontSize: 'calc(10px * var(--fs-scale-caption, 1))',
|
||||
fontWeight: 600,
|
||||
letterSpacing: 3,
|
||||
textTransform: 'uppercase',
|
||||
opacity: 0.35,
|
||||
marginBottom: 12,
|
||||
position: 'relative',
|
||||
}}
|
||||
>
|
||||
{t('journey.public.tagline')}
|
||||
</div>
|
||||
|
||||
<h1
|
||||
className="relative"
|
||||
style={{
|
||||
margin: '0 0 4px',
|
||||
fontSize: 'calc(26px * var(--fs-scale-title, 1))',
|
||||
fontWeight: 700,
|
||||
letterSpacing: -0.5,
|
||||
}}
|
||||
>
|
||||
{journey.title}
|
||||
</h1>
|
||||
|
||||
{journey.subtitle && (
|
||||
<div
|
||||
className="relative"
|
||||
style={{
|
||||
fontSize: 'calc(13px * var(--fs-scale-body, 1))',
|
||||
opacity: 0.5,
|
||||
maxWidth: 400,
|
||||
margin: '0 auto',
|
||||
lineHeight: 1.5,
|
||||
}}
|
||||
>
|
||||
{journey.subtitle}
|
||||
</div>
|
||||
{!floatingSwitch && (
|
||||
<SharedTabBar tabs={availableViews.map(v => ({ id: v.id, label: v.label, icon: v.icon }))} active={view} onChange={id => setView(id as typeof view)} />
|
||||
)}
|
||||
|
||||
{/* Stats pill */}
|
||||
<div
|
||||
className="relative"
|
||||
style={{
|
||||
marginTop: 12,
|
||||
display: 'inline-flex',
|
||||
alignItems: 'center',
|
||||
gap: 12,
|
||||
padding: '8px 18px',
|
||||
borderRadius: 20,
|
||||
background: 'rgba(255,255,255,0.08)',
|
||||
backdropFilter: 'blur(4px)',
|
||||
border: '1px solid rgba(255,255,255,0.08)',
|
||||
}}
|
||||
>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'calc(12px * var(--fs-scale-body, 1))',
|
||||
fontWeight: 500,
|
||||
opacity: 0.8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 5,
|
||||
}}
|
||||
>
|
||||
<BookOpen size={12} /> {stats.entries} {t('journey.stats.entries')}
|
||||
</span>
|
||||
<span style={{ fontSize: 'calc(11px * var(--fs-scale-caption, 1))', opacity: 0.4 }}>·</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'calc(12px * var(--fs-scale-body, 1))',
|
||||
fontWeight: 500,
|
||||
opacity: 0.8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 5,
|
||||
}}
|
||||
>
|
||||
<Camera size={12} /> {stats.photos} {t('journey.stats.photos')}
|
||||
</span>
|
||||
<span style={{ fontSize: 'calc(11px * var(--fs-scale-caption, 1))', opacity: 0.4 }}>·</span>
|
||||
<span
|
||||
style={{
|
||||
fontSize: 'calc(12px * var(--fs-scale-body, 1))',
|
||||
fontWeight: 500,
|
||||
opacity: 0.8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: 5,
|
||||
}}
|
||||
>
|
||||
<MapPin size={12} /> {stats.places} {t('journey.stats.places')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className="relative"
|
||||
style={{
|
||||
marginTop: 12,
|
||||
fontSize: 'calc(9px * var(--fs-scale-caption, 1))',
|
||||
fontWeight: 500,
|
||||
letterSpacing: 1.5,
|
||||
textTransform: 'uppercase',
|
||||
opacity: 0.25,
|
||||
}}
|
||||
>
|
||||
{t('journey.public.readOnly')}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
@@ -782,7 +603,6 @@ export default function JourneyPublicPage() {
|
||||
<div className="mx-auto flex max-w-[1440px]" style={{ alignItems: 'flex-start' }}>
|
||||
{/* Left: feed */}
|
||||
<div className="min-w-0 flex-1 px-8 py-6 xl:max-w-[50%]">
|
||||
{renderTabs(availableViews)}
|
||||
{view === 'timeline' && perms.share_timeline && renderTimeline()}
|
||||
{view === 'gallery' && perms.share_gallery && renderGallery()}
|
||||
</div>
|
||||
@@ -843,8 +663,6 @@ export default function JourneyPublicPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
{renderTabs(availableViews)}
|
||||
|
||||
{/* Mobile combined map+timeline (public, read-only) */}
|
||||
{isMobile && view === 'timeline' && perms.share_timeline && perms.share_map && (
|
||||
<MobileMapTimeline
|
||||
@@ -903,6 +721,8 @@ export default function JourneyPublicPage() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<SharedFooter />
|
||||
|
||||
{/* Lightbox */}
|
||||
{lightbox && (
|
||||
<PhotoLightbox photos={lightbox.photos} startIndex={lightbox.index} onClose={() => setLightbox(null)} />
|
||||
|
||||
@@ -153,7 +153,7 @@ describe('vacayStore optimistic updates', () => {
|
||||
await useVacayStore.getState().toggleCompanyHoliday('2025-12-25');
|
||||
|
||||
expect(seen[0]).toEqual([]);
|
||||
expect(seen[1]).toEqual([{ date: '2025-12-25' }]);
|
||||
expect(seen[1]).toEqual([{ date: '2025-12-25', fraction: 1 }]);
|
||||
});
|
||||
|
||||
it('FE-STORE-VCY-008: a rejected company-holiday toggle rolls the day back', async () => {
|
||||
|
||||
@@ -667,6 +667,7 @@ describe('useMPlanTimeline', () => {
|
||||
expect(result.current.routeModeOptions).toEqual([
|
||||
{ key: 'driving', label: 'mobileTrip.profileDriving' },
|
||||
{ key: 'walking', label: 'mobileTrip.profileWalking' },
|
||||
{ key: 'cycling', label: 'mobileTrip.profileCycling' },
|
||||
{ key: 'plugin:ev/eco', label: 'Eco' },
|
||||
{ key: 'plugin:ev/fast', label: 'Fast' },
|
||||
])
|
||||
|
||||
@@ -332,7 +332,7 @@ describe('useMVacay', () => {
|
||||
mocks.vacay = buildVacay({ plan: buildPlan({ company_holidays_enabled: true }) });
|
||||
act(() => { rerender(); });
|
||||
await act(async () => { await result.current.handleDayTap('2026-12-24'); });
|
||||
expect(toggleCompanyHoliday).toHaveBeenCalledWith('2026-12-24');
|
||||
expect(toggleCompanyHoliday.mock.calls[0][0]).toBe('2026-12-24');
|
||||
});
|
||||
|
||||
it('FE-MOB-MVAC-019: the entitlement stepper caps at 365 and never drops below used', async () => {
|
||||
|
||||
@@ -34,7 +34,7 @@ const { db } = vi.hoisted(() => {
|
||||
tmp.exec(`CREATE TABLE day_assignments (id INTEGER PRIMARY KEY AUTOINCREMENT, day_id INTEGER NOT NULL,
|
||||
place_id INTEGER NOT NULL, order_index INTEGER NOT NULL DEFAULT 0, notes TEXT,
|
||||
assignment_time TEXT, assignment_end_time TEXT, leg_transport_mode TEXT,
|
||||
accommodation_id INTEGER,
|
||||
accommodation_id INTEGER, route_excluded INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TEXT DEFAULT (datetime('now')));`);
|
||||
// The auto-sort reads a booked night's hour off its booking, so the table has to be
|
||||
// here even though nothing in this file books one.
|
||||
|
||||
@@ -37,6 +37,7 @@ const { db } = vi.hoisted(() => {
|
||||
is_private INTEGER NOT NULL DEFAULT 0,
|
||||
owner_id INTEGER,
|
||||
updated_at DATETIME,
|
||||
packed_quantity INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
);`);
|
||||
tmp.exec(`CREATE TABLE packing_bags (
|
||||
|
||||
@@ -41,8 +41,11 @@ const { db } = vi.hoisted(() => {
|
||||
tmp.exec('CREATE TABLE days (id INTEGER PRIMARY KEY AUTOINCREMENT, trip_id INTEGER NOT NULL, day_number INTEGER, date TEXT, title TEXT);');
|
||||
// The GPX export reads the trip title for <metadata> and the filename.
|
||||
tmp.exec('CREATE TABLE trips (id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT);');
|
||||
// The places list reads each place's country and region (#2537).
|
||||
tmp.exec(`CREATE TABLE place_regions (place_id INTEGER PRIMARY KEY, country_code TEXT NOT NULL,
|
||||
region_code TEXT NOT NULL, region_name TEXT NOT NULL);`);
|
||||
tmp.exec(`CREATE TABLE day_assignments (id INTEGER PRIMARY KEY AUTOINCREMENT, day_id INTEGER NOT NULL,
|
||||
place_id INTEGER NOT NULL, order_index INTEGER DEFAULT 0);`);
|
||||
place_id INTEGER NOT NULL, order_index INTEGER DEFAULT 0, route_excluded INTEGER NOT NULL DEFAULT 0);`);
|
||||
// reclaimPlaceImage ref-counts an uploaded thumbnail across both tables.
|
||||
// Deleting a place cancels the nights booked at it (#2354), so the delete path
|
||||
// reads this table even in a file that never books one.
|
||||
|
||||
@@ -34,7 +34,7 @@ const { db } = vi.hoisted(() => {
|
||||
tmp.exec(`CREATE TABLE day_assignments (id INTEGER PRIMARY KEY AUTOINCREMENT, day_id INTEGER NOT NULL,
|
||||
place_id INTEGER NOT NULL, order_index INTEGER DEFAULT 0, notes TEXT, reservation_status TEXT,
|
||||
reservation_notes TEXT, reservation_datetime TEXT, assignment_time TEXT, assignment_end_time TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`);
|
||||
route_excluded INTEGER NOT NULL DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`);
|
||||
tmp.exec(`CREATE TABLE day_notes (id INTEGER PRIMARY KEY AUTOINCREMENT, day_id INTEGER NOT NULL,
|
||||
trip_id INTEGER NOT NULL, text TEXT, time TEXT, icon TEXT, sort_order INTEGER DEFAULT 0,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`);
|
||||
@@ -51,7 +51,7 @@ const { db } = vi.hoisted(() => {
|
||||
tmp.exec(`CREATE TABLE packing_items (id INTEGER PRIMARY KEY AUTOINCREMENT, trip_id INTEGER NOT NULL,
|
||||
name TEXT NOT NULL, checked INTEGER DEFAULT 0, category TEXT, sort_order INTEGER DEFAULT 0,
|
||||
weight_grams INTEGER, bag_id INTEGER, quantity INTEGER NOT NULL DEFAULT 1,
|
||||
is_private INTEGER NOT NULL DEFAULT 0, owner_id INTEGER, updated_at DATETIME,
|
||||
is_private INTEGER NOT NULL DEFAULT 0, owner_id INTEGER, updated_at DATETIME, packed_quantity INTEGER,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`);
|
||||
tmp.exec(`CREATE TABLE packing_item_recipients (item_id INTEGER NOT NULL, user_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (item_id, user_id));`);
|
||||
@@ -75,6 +75,9 @@ const { db } = vi.hoisted(() => {
|
||||
needs_review INTEGER DEFAULT 0, day_plan_position REAL, external_source TEXT, sync_enabled INTEGER,
|
||||
ingest_state TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`);
|
||||
tmp.exec('CREATE TABLE days (id INTEGER PRIMARY KEY AUTOINCREMENT, trip_id INTEGER NOT NULL, day_number INTEGER, date TEXT);');
|
||||
// The places list reads each place's country and region (#2537).
|
||||
tmp.exec(`CREATE TABLE place_regions (place_id INTEGER PRIMARY KEY, country_code TEXT NOT NULL,
|
||||
region_code TEXT NOT NULL, region_name TEXT NOT NULL);`);
|
||||
tmp.exec(`CREATE TABLE places (id INTEGER PRIMARY KEY AUTOINCREMENT, trip_id INTEGER NOT NULL, name TEXT,
|
||||
image_url TEXT, address TEXT, lat REAL, lng REAL, category_id INTEGER, description TEXT,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP);`);
|
||||
|
||||
@@ -51,7 +51,7 @@ const { db } = vi.hoisted(() => {
|
||||
UNIQUE(user_id, plan_id, date));`);
|
||||
tmp.exec(`CREATE TABLE vacay_company_holidays (id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
||||
date TEXT NOT NULL, note TEXT DEFAULT '', UNIQUE(plan_id, date));`);
|
||||
date TEXT NOT NULL, note TEXT DEFAULT '', fraction REAL NOT NULL DEFAULT 1, UNIQUE(plan_id, date));`);
|
||||
tmp.exec(`CREATE TABLE vacay_holiday_calendars (id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
plan_id INTEGER NOT NULL REFERENCES vacay_plans(id) ON DELETE CASCADE,
|
||||
type TEXT NOT NULL DEFAULT 'public_holiday', region TEXT NOT NULL, label TEXT,
|
||||
|
||||
@@ -298,7 +298,7 @@ describe('Tool: clear_day_assignments (#2470)', () => {
|
||||
expect(data.success).toBe(true);
|
||||
expect([...data.removedIds].sort()).toEqual([a1.id, a2.id].sort());
|
||||
expect(testDb.prepare('SELECT COUNT(*) AS n FROM day_assignments WHERE day_id = ?').get(day.id)).toEqual({ n: 0 });
|
||||
expect(broadcastMock).toHaveBeenCalledWith(trip.id, 'assignment:deleted', { assignmentId: a1.id, dayId: day.id });
|
||||
expect(broadcastMock).toHaveBeenCalledWith(trip.id, 'assignment:deleted', expect.objectContaining({ assignmentId: a1.id, dayId: day.id }));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1223,9 +1223,9 @@ describe('applySettlementUpdate', () => {
|
||||
|
||||
const res = budget.applySettlementUpdate(7, 1, { from_user_id: 2, to_user_id: 1, amount: 10.126 });
|
||||
// from, to, rounded amount, currency-flag(0)/value(null), rate-flag(null)/value(1),
|
||||
// settled_at-flag(0)/value(null), id.
|
||||
// No currency/exchange_rate/settled_at passed → all three CASE guards keep the existing columns.
|
||||
expect(run).toHaveBeenCalledWith(2, 1, 10.13, 0, null, null, 1, 0, null, 7);
|
||||
// settled_at-flag(0)/value(null), note-flag(0)/value(null) (#2340), id.
|
||||
// No currency/exchange_rate/settled_at/note passed → every CASE guard keeps the existing column.
|
||||
expect(run).toHaveBeenCalledWith(2, 1, 10.13, 0, null, null, 1, 0, null, 0, null, 7);
|
||||
expect(res).toMatchObject({ id: 7, from_user_id: 2, to_user_id: 1, amount: 10.13 });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -932,14 +932,15 @@ describe('MapsService: Amap first inside China when picked outright (#1636)', ()
|
||||
mockProviderGet.mockReturnValue({ value: 'amap' });
|
||||
keys({ amap: 'akey' });
|
||||
osmAnswers([]);
|
||||
const fetchSpy = vi.fn().mockResolvedValue(ok({ count: '0', pois: [] }));
|
||||
vi.stubGlobal('fetch', fetchSpy);
|
||||
vi.stubGlobal('fetch', vi.fn().mockResolvedValue(ok({ count: '0', pois: [] })));
|
||||
const searchText = vi.spyOn(AmapPlacesProvider.prototype, 'searchText');
|
||||
spies.push(searchText);
|
||||
|
||||
const result = await svc.searchPlaces(1, 'nothing here', 'zh', BEIJING);
|
||||
|
||||
expect(result).toEqual({ places: [], source: 'amap' });
|
||||
expect(trekPlacesSearch).toHaveBeenCalled();
|
||||
expect(fetchSpy).toHaveBeenCalledTimes(1);
|
||||
expect(searchText).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('AMAP-097: a search centred outside China keeps the usual order, so the Eiffel Tower does not land in Macau', async () => {
|
||||
|
||||
@@ -1514,7 +1514,7 @@ describe('getSharedCalendars', () => {
|
||||
|
||||
const calendars = svc.getSharedCalendars(viewer.id, '2025');
|
||||
|
||||
expect(calendars[0].companyHolidays).toEqual([{ date: '2025-12-24' }]);
|
||||
expect(calendars[0].companyHolidays).toEqual([{ date: '2025-12-24', fraction: 1 }]);
|
||||
});
|
||||
|
||||
it('VACAY-SVC-065: an owner without any plan yields empty arrays (no lazy creation)', () => {
|
||||
|
||||
@@ -236,7 +236,7 @@ describe('addon-gated domains validate their input', () => {
|
||||
const toggleCompanyHoliday = vi.fn(() => ({}));
|
||||
const host = build({ vacay: { toggleCompanyHoliday } });
|
||||
await host.dispatch(req('vacay.toggleCompanyHoliday', { date: '2027-01-01' }), 42);
|
||||
expect(toggleCompanyHoliday).toHaveBeenCalledWith(1, '2027-01-01', undefined, undefined);
|
||||
expect(toggleCompanyHoliday).toHaveBeenCalledWith(1, '2027-01-01', undefined, undefined, undefined);
|
||||
});
|
||||
|
||||
it('ADDONERR-010e a collab message without a replyTo passes null', async () => {
|
||||
|
||||
@@ -285,7 +285,7 @@ describe('every schema-validated method rejects a payload its schema refuses', (
|
||||
createTestPluginRegistry([new VacayRpc(vacay, guards), new TagsRpc(tags)]),
|
||||
);
|
||||
await host.dispatch(req('vacay.toggleCompanyHoliday', { date: '2027-01-01', note: 42 }), 42);
|
||||
expect(toggleCompanyHoliday).toHaveBeenCalledWith(1, '2027-01-01', undefined, undefined);
|
||||
expect(toggleCompanyHoliday).toHaveBeenCalledWith(1, '2027-01-01', undefined, undefined, undefined);
|
||||
await host.dispatch(req('tags.update', { tagId: 1, input: { name: 'kept' } }), 42);
|
||||
expect(update).toHaveBeenCalledWith(1, 'kept', undefined);
|
||||
});
|
||||
|
||||
@@ -242,6 +242,8 @@ A Homepage widget then needs no scripting:
|
||||
| `404` | no such trip, or not one of yours |
|
||||
| `429` | rate limit exceeded |
|
||||
|
||||
**Which TREK is running.** To check the running version, for example against the latest GitHub release, call `GET /api/auth/app-config`. It needs no key and no login, and the `version` field holds the running version (such as `4.3.3`). It is not part of `/api/v1`, so it follows the app's own release rather than the promise below.
|
||||
|
||||
**Versioning.** `/api/v1` may gain fields; it will not lose them or change their types. A breaking change ships as `/api/v2` and both run side by side for a transition period. Write your client to ignore fields it does not know.
|
||||
|
||||
## What is not here yet
|
||||
|
||||
Reference in New Issue
Block a user