fix(client): the audit round over the 4.3.1 release

Ride minutes and late marks follow an edited timetable instead of the routing
that fetched them; a landing day's join band reads as the ride it is rather
than a nine hour drive of no length; the refuel offer is there on the road
out of a terminal or a pick-up desk; the phone's stage map draws the ride;
the share page numbers its stops the way the planner does; the PDF lists a
day the way the day plan lists it, from both shells; a poster-less clip draws
its play badge everywhere, not only in the editor; the hero title has room
for its descenders and the list card keeps its badge clear; the release
notice keeps its close control in reach when it stacks; a link chip's actions
are thumb-sized and its delete asks first; the Google retry line and the
Google-only switch appear only where Google answers.
This commit is contained in:
Maurice
2026-09-22 12:10:21 +02:00
parent cb85ba915c
commit 85b5756e89
78 changed files with 2476 additions and 486 deletions
@@ -233,6 +233,81 @@ describe('PlaceFormModal', () => {
expect(screen.queryByRole('button', { name: /Search Google instead/ })).toBeNull();
});
it('FE-PLANNER-PLACEFORM-018d: a key alone is not enough: with Amap or OpenStreetMap picked the list offers nothing', async () => {
// The server only honours the request while Google holds the keyed slot;
// under another provider the link would re-run the same search and stay.
const user = userEvent.setup();
seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true, hasMapsKey: true, placesProvider: 'openstreetmap' });
server.use(
http.post('/api/maps/search', () =>
HttpResponse.json({ places: [{ name: 'Weigh station', address: 'Ritzville', lat: '47.1', lng: '-118.4' }], source: 'trek-places+openstreetmap' }),
),
);
render(<PlaceFormModal {...defaultProps} />);
const searchInput = screen.getByPlaceholderText('Search places...');
await user.type(searchInput, 'Tokyo Station');
await user.click(within(searchInput.closest('.flex') as HTMLElement).getByRole('button'));
expect(await screen.findByText('Weigh station')).toBeInTheDocument();
expect(screen.queryByRole('button', { name: /Search Google instead/ })).toBeNull();
});
it('FE-PLANNER-PLACEFORM-018e: the link sends the query the list came from, not what the field holds by then', async () => {
const user = userEvent.setup();
seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true, hasMapsKey: true });
const bodies: Record<string, unknown>[] = [];
server.use(
http.post('/api/maps/autocomplete', () => HttpResponse.json({ suggestions: [], source: 'trek-places' })),
http.post('/api/maps/search', async ({ request }) => {
const body = (await request.json()) as Record<string, unknown>;
bodies.push(body);
return body.provider === 'google'
? HttpResponse.json({ places: [{ name: 'Tokyo Station', address: 'Chiyoda', lat: '35.68', lng: '139.77' }], source: 'google' })
: HttpResponse.json({ places: [{ name: 'Weigh station', address: 'Ritzville', lat: '47.1', lng: '-118.4' }], source: 'trek-places+openstreetmap' });
}),
);
render(<PlaceFormModal {...defaultProps} />);
const searchInput = screen.getByPlaceholderText('Search places...');
await user.type(searchInput, 'Tokyo Station');
await user.click(within(searchInput.closest('.flex') as HTMLElement).getByRole('button'));
expect(await screen.findByText('Weigh station')).toBeInTheDocument();
// The list stays while the field is retyped; the line still means this list.
await user.clear(searchInput);
await user.type(searchInput, 'Kyoto');
await user.click(screen.getByRole('button', { name: /Search Google instead/ }));
expect(await screen.findByText('Tokyo Station')).toBeInTheDocument();
expect(bodies).toHaveLength(2);
expect(bodies[1]).toMatchObject({ query: 'Tokyo Station', provider: 'google' });
});
it('FE-PLANNER-PLACEFORM-018f: the link still works after the field was cleared', async () => {
const user = userEvent.setup();
seedStore(useAuthStore, { user: buildUser(), isAuthenticated: true, hasMapsKey: true });
const bodies: Record<string, unknown>[] = [];
server.use(
http.post('/api/maps/search', async ({ request }) => {
const body = (await request.json()) as Record<string, unknown>;
bodies.push(body);
return body.provider === 'google'
? HttpResponse.json({ places: [{ name: 'Tokyo Station', address: 'Chiyoda', lat: '35.68', lng: '139.77' }], source: 'google' })
: HttpResponse.json({ places: [{ name: 'Weigh station', address: 'Ritzville', lat: '47.1', lng: '-118.4' }], source: 'trek-places+openstreetmap' });
}),
);
render(<PlaceFormModal {...defaultProps} />);
const searchInput = screen.getByPlaceholderText('Search places...');
await user.type(searchInput, 'Tokyo Station');
await user.click(within(searchInput.closest('.flex') as HTMLElement).getByRole('button'));
expect(await screen.findByText('Weigh station')).toBeInTheDocument();
// An empty field used to make the click a silent no-op.
await user.clear(searchInput);
await user.click(screen.getByRole('button', { name: /Search Google instead/ }));
expect(await screen.findByText('Tokyo Station')).toBeInTheDocument();
expect(bodies[1]).toMatchObject({ query: 'Tokyo Station', provider: 'google' });
});
it('FE-PLANNER-PLACEFORM-019: pressing Enter in search input triggers search', async () => {
const user = userEvent.setup();
server.use(