Update the mobile sheet tests for the shared location helper

- reverse() now receives the UI language
- error assertions follow the translated journey.editor.* strings
  instead of the raw error.message and the generic common.error label
- a late error callback after a resolved one-shot fix is asserted to be
  a no-op, since a settled promise cannot reject anymore
- add tests for the new location button: reverse-geocoded fill and the
  translated inline denial message
This commit is contained in:
Renzo Beux
2026-08-03 22:35:59 -03:00
committed by Maurice
parent a66f70031c
commit 4b60cf0c0e
@@ -57,7 +57,7 @@ describe('MJourneyEntrySheet quick capture', () => {
}); });
const { props } = setup(); const { props } = setup();
await waitFor(() => expect(mapsApi.reverse).toHaveBeenCalledWith(1.3521, 103.8198)); await waitFor(() => expect(mapsApi.reverse).toHaveBeenCalledWith(1.3521, 103.8198, 'en'));
expect(weatherApi.getCurrent).toHaveBeenCalledWith(1.3521, 103.8198, 'en'); expect(weatherApi.getCurrent).toHaveBeenCalledWith(1.3521, 103.8198, 'en');
await waitFor(() => expect(screen.getByDisplayValue('Singapore')).toBeInTheDocument()); await waitFor(() => expect(screen.getByDisplayValue('Singapore')).toBeInTheDocument());
fireEvent.click(screen.getByRole('button', { name: 'Save' })); fireEvent.click(screen.getByRole('button', { name: 'Save' }));
@@ -115,12 +115,15 @@ describe('MJourneyEntrySheet quick capture', () => {
Object.defineProperty(navigator, 'geolocation', { Object.defineProperty(navigator, 'geolocation', {
configurable: true, configurable: true,
value: { value: {
getCurrentPosition: vi.fn((_success, failure) => failure({ message: 'Permission denied' })), getCurrentPosition: vi.fn((_success, failure) => failure({
code: 1, message: 'Permission denied',
PERMISSION_DENIED: 1, POSITION_UNAVAILABLE: 2, TIMEOUT: 3,
})),
}, },
}); });
const { props } = setup(); const { props } = setup();
expect(await screen.findByText('Permission denied')).toBeInTheDocument(); expect(await screen.findByText('Location access was denied. Allow it in your browser settings and try again.')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: 'Save' })); fireEvent.click(screen.getByRole('button', { name: 'Save' }));
await waitFor(() => await waitFor(() =>
@@ -839,11 +842,11 @@ describe('MJourneyEntrySheet quick capture edge cases', () => {
vi.restoreAllMocks(); vi.restoreAllMocks();
}); });
it('FE-MOB-JENTRY-031: falls back to a generic error when the device has no geolocation', () => { it('FE-MOB-JENTRY-031: shows a translated error when the device has no geolocation', async () => {
Object.defineProperty(navigator, 'geolocation', { configurable: true, value: undefined }); Object.defineProperty(navigator, 'geolocation', { configurable: true, value: undefined });
mountSheet(buildEntry({ entry_time: '09:07' }), { quickCapture: true }); mountSheet(buildEntry({ entry_time: '09:07' }), { quickCapture: true });
expect(screen.getByText('Error')).toBeInTheDocument(); expect(await screen.findByText('Could not determine your location.')).toBeInTheDocument();
}); });
it('FE-MOB-JENTRY-032: skips the location lookup when the entry already has coordinates', () => { it('FE-MOB-JENTRY-032: skips the location lookup when the entry already has coordinates', () => {
@@ -937,13 +940,50 @@ describe('MJourneyEntrySheet quick capture edge cases', () => {
expect(await screen.findByDisplayValue('Fresh')).toBeInTheDocument(); expect(await screen.findByDisplayValue('Fresh')).toBeInTheDocument();
expect(screen.queryByDisplayValue('Stale')).not.toBeInTheDocument(); expect(screen.queryByDisplayValue('Stale')).not.toBeInTheDocument();
// The abandoned watcher must not be able to paint an error either, and a // Late failures must not paint anything: the stale lookup was abandoned
// failure without a message falls back to the generic label. // by the effect restart, and the fresh lookup's one-shot promise already
// resolved with a position, so a trailing error callback is a no-op.
await act(async () => { await act(async () => {
failures[0]({ message: 'stale failure' } as GeolocationPositionError); failures[0]({ message: 'stale failure' } as GeolocationPositionError);
failures[1]({ message: '' } as GeolocationPositionError); failures[1]({ message: '' } as GeolocationPositionError);
}); });
expect(screen.queryByText('stale failure')).not.toBeInTheDocument(); expect(screen.queryByText('stale failure')).not.toBeInTheDocument();
expect(screen.getByText('Error')).toBeInTheDocument(); expect(screen.queryByText('Could not determine your location.')).not.toBeInTheDocument();
expect(screen.queryByText(/Location access was denied/)).not.toBeInTheDocument();
});
it('FE-MOB-JENTRY-039: the location field button fills the device position and reverse geocodes it', async () => {
Object.defineProperty(navigator, 'geolocation', {
configurable: true,
value: {
getCurrentPosition: vi.fn((s: PositionCallback) =>
s({ coords: { latitude: 41.9, longitude: 12.5 } } as GeolocationPosition)),
},
});
vi.spyOn(mapsApi, 'reverse').mockResolvedValue({ name: 'Colosseum', address: 'Rome, Italy' });
mountSheet(buildEntry());
fireEvent.click(screen.getByRole('button', { name: 'Use my current location' }));
expect(await screen.findByDisplayValue('Colosseum')).toBeInTheDocument();
expect(mapsApi.reverse).toHaveBeenCalledWith(41.9, 12.5, 'en');
});
it('FE-MOB-JENTRY-040: the location field button surfaces a translated denial inline', async () => {
Object.defineProperty(navigator, 'geolocation', {
configurable: true,
value: {
getCurrentPosition: vi.fn((_s: PositionCallback, e: PositionErrorCallback) =>
e({
code: 1, message: 'denied',
PERMISSION_DENIED: 1, POSITION_UNAVAILABLE: 2, TIMEOUT: 3,
} as GeolocationPositionError)),
},
});
mountSheet(buildEntry());
fireEvent.click(screen.getByRole('button', { name: 'Use my current location' }));
expect(await screen.findByText('Location access was denied. Allow it in your browser settings and try again.')).toBeInTheDocument();
}); });
}); });