Skip to content

Commit 768f40d

Browse files
authored
Fix coverage regression in Locator and Map tests (#548)
* Fix coverage regression in Locator and Map tests * Refactor Locator submit flow to try/catch/finally * Increase coverage with deterministic Locator and Map tests
1 parent feb74ad commit 768f40d

4 files changed

Lines changed: 414 additions & 39 deletions

File tree

src/Locator.svelte

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -57,36 +57,36 @@
5757
submitValue();
5858
};
5959
60-
const submitValue = (): void => {
60+
61+
const submitValue = async (): Promise<void> => {
6162
if (inputValue) {
6263
isFetching = true;
63-
fetch(
64-
`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(
65-
inputValue
66-
)}&format=json&countrycodes=ca&limit=1`
67-
)
68-
.then((response) => {
69-
if (!response.ok) throw new Error(`HTTP ${response.status}`);
70-
return response.json();
71-
})
72-
.then((data) => {
73-
if (Array.isArray(data) && data.length > 0) {
74-
const lat = parseFloat(data[0].lat);
75-
const lon = parseFloat(data[0].lon);
76-
if (!isFinite(lat) || !isFinite(lon)) {
77-
alert("No such location found.");
78-
return;
79-
}
80-
const placeName = data[0].display_name;
81-
onLocationFound(lat, lon, placeName);
82-
} else {
64+
try {
65+
const response = await fetch(
66+
`https://nominatim.openstreetmap.org/search?q=${encodeURIComponent(
67+
inputValue
68+
)}&format=json&countrycodes=ca&limit=1`
69+
);
70+
if (!response.ok) throw new Error(`HTTP ${response.status}`);
71+
72+
const data = await response.json();
73+
if (Array.isArray(data) && data.length > 0) {
74+
const lat = parseFloat(data[0].lat);
75+
const lon = parseFloat(data[0].lon);
76+
if (!isFinite(lat) || !isFinite(lon)) {
8377
alert("No such location found.");
78+
return;
8479
}
85-
})
86-
.catch((error) => console.log(error))
87-
.finally(() => {
88-
isFetching = false;
89-
});
80+
const placeName = data[0].display_name;
81+
onLocationFound(lat, lon, placeName);
82+
} else {
83+
alert("No such location found.");
84+
}
85+
} catch (error) {
86+
console.log(error);
87+
} finally {
88+
isFetching = false;
89+
}
9090
setTimeout(clearInput, 1000);
9191
} else {
9292
alert("You didn't type anything.");

tests/__snapshots__/map.test.ts.snap

Lines changed: 0 additions & 3 deletions
This file was deleted.

tests/locator.test.ts

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,4 +102,80 @@ describe('Locator.svelte', () => {
102102

103103
expect(input.value).toBe('');
104104
});
105+
106+
it('handles failed autocomplete fetch without rendering suggestions', async () => {
107+
const logMock = vi.spyOn(console, 'log').mockImplementation(() => undefined);
108+
109+
vi.stubGlobal('fetch', vi.fn((input: RequestInfo | URL) => {
110+
const url = typeof input === 'string' ? input : input.toString();
111+
112+
if (url.includes('limit=5')) {
113+
return Promise.resolve({
114+
ok: false,
115+
status: 500,
116+
json: () => Promise.resolve([]),
117+
});
118+
}
119+
120+
return Promise.resolve({
121+
ok: true,
122+
status: 200,
123+
json: () => Promise.resolve([]),
124+
});
125+
}));
126+
127+
render(App as unknown as new () => SvelteComponent, { target: document.body });
128+
const input = screen.getByPlaceholderText('Search Location') as HTMLInputElement;
129+
130+
await fireEvent.input(input, { target: { value: 'Toronto' } });
131+
await Promise.resolve();
132+
133+
expect(logMock).toHaveBeenCalled();
134+
expect(screen.queryByRole('listitem')).toBeNull();
135+
logMock.mockRestore();
136+
});
137+
138+
it('alerts when geocoding returns no results', async () => {
139+
const alertMock = vi.fn();
140+
vi.stubGlobal('alert', alertMock);
141+
142+
vi.stubGlobal('fetch', vi.fn(() => {
143+
return Promise.resolve({
144+
ok: true,
145+
status: 200,
146+
json: () => Promise.resolve([]),
147+
});
148+
}));
149+
150+
render(App as unknown as new () => SvelteComponent, { target: document.body });
151+
const input = screen.getByPlaceholderText('Search Location') as HTMLInputElement;
152+
const button = screen.getByDisplayValue('Submit');
153+
154+
await fireEvent.input(input, { target: { value: 'Nowhere' } });
155+
vi.useFakeTimers();
156+
await fireEvent.click(button);
157+
await vi.runAllTimersAsync();
158+
159+
expect(alertMock).toHaveBeenCalledWith('No such location found.');
160+
});
161+
162+
it('ignores unrelated key presses during navigation', async () => {
163+
render(App as unknown as new () => SvelteComponent, { target: document.body });
164+
165+
const input = screen.getByPlaceholderText('Search Location') as HTMLInputElement;
166+
await fireEvent.input(input, { target: { value: '' } });
167+
await fireEvent.keyDown(window, { key: 'Escape' });
168+
169+
expect(input.value).toBe('');
170+
});
171+
172+
it('returns early on ArrowDown with no suggestions', async () => {
173+
render(App as unknown as new () => SvelteComponent, { target: document.body });
174+
const input = screen.getByPlaceholderText('Search Location') as HTMLInputElement;
175+
176+
await fireEvent.input(input, { target: { value: '' } });
177+
await fireEvent.keyDown(window, { key: 'ArrowDown' });
178+
179+
expect(input.value).toBe('');
180+
});
105181
});

0 commit comments

Comments
 (0)