]> git.openstreetmap.org Git - nominatim-ui.git/blob - test/search.spec.js
Rebundle latest version
[nominatim-ui.git] / test / search.spec.js
1 import { test, expect } from './shared.js';
2
3 test.describe('Search Page', () => {
4   test.skip(() => !!process.env.REVERSE_ONLY, 'Skipped in reverse-only mode');
5
6   test.describe('No search', () => {
7     let page;
8
9     test.beforeAll(async ({ browser }) => {
10       page = await browser.newPage();
11       await page.goto('/search.html');
12     });
13
14     test.afterAll(async () => {
15       await page.close();
16     });
17
18     test('should have a HTML page title', async () => {
19       expect(await page.title()).toBe('Nominatim Demo');
20     });
21
22     test('should have a welcome message', async () => {
23       await expect(page.locator('#welcome h2')).toHaveText(
24         'Welcome to Nominatim'
25       );
26     });
27
28     test('should have a last_updated_: ... ago data', async () => {
29       await page.waitForFunction(
30         () => {
31           const el = document.querySelector('abbr[id="data-date"]');
32           return el && el.textContent.includes('ago');
33         },
34         { timeout: 10000 }
35       );
36     });
37
38     test('should show map bounds buttons', async () => {
39       await page.locator('#map').waitFor();
40       await expect(page.locator('#map-position-inner')).toHaveCount(0);
41
42       await page.locator('#show-map-position').click();
43
44       let map_pos_details = await page.locator(
45         '#map-position-inner'
46       ).textContent();
47       map_pos_details = map_pos_details.split('  ');
48
49       const map_center_coords = map_pos_details[0]
50         .split('map center: ')[1].split(' view')[0].split(',');
51       const map_zoom = map_pos_details[1].split('map zoom: ')[1];
52       const map_viewbox = map_pos_details[2]
53         .split('viewbox: ')[1].split(',');
54       const last_click = map_pos_details[3].split('last click: ')[1];
55
56       expect(map_center_coords.length).toBe(2);
57       expect(map_zoom).toBeTruthy();
58       expect(map_viewbox.length).toBe(4);
59       expect(last_click).toBe('-');
60
61       await page.locator('#map-position-close a').click();
62       await expect(page.locator('#map-position-inner')).toHaveCount(0);
63       await expect(page.locator('#show-map-position')).toBeVisible();
64     });
65   });
66
67   test.describe('Search for Paris', () => {
68     let page;
69
70     test.beforeAll(async ({ browser }) => {
71       page = await browser.newPage();
72       await page.goto('/search.html');
73       await page.locator('input[name=q]').fill('Paris');
74       await page.locator('button[type=submit]').first().click();
75       await page.locator('#searchresults').waitFor();
76     });
77
78     test.afterAll(async () => {
79       await page.close();
80     });
81
82     test('should have a HTML page title', async () => {
83       expect(await page.title()).toBe('Result for Paris | Nominatim Demo');
84     });
85
86     test('should have added search params', async () => {
87       const current_url = new URL(page.url());
88       expect(current_url.searchParams.get('q')).toBe('Paris');
89     });
90
91     test('should have at least one result', async () => {
92       const results_count = await page.locator(
93         '#searchresults .result'
94       ).count();
95       expect(results_count).toBeGreaterThan(1);
96     });
97
98     test('should display the API request and debug URL', async () => {
99       const link_titles = await page.locator(
100         '#api-request a'
101       ).evaluateAll(links => links.map(l => l.innerHTML));
102       expect(link_titles).toEqual(['API request', 'debug output']);
103     });
104
105     test('should not have polygon params in API request and debug URL',
106       async () => {
107         const links_href = await page.locator(
108           '#api-request a'
109         ).evaluateAll(links => links.map(l => l.href));
110         const api_request_url = new URL(links_href[0]);
111         const debug_url = new URL(links_href[1]);
112
113         expect(api_request_url.searchParams.has('polygon_geojson')).toBe(
114           false
115         );
116         expect(debug_url.searchParams.has('polygon_geojson')).toBe(false);
117       }
118     );
119
120     test('should display a map', async () => {
121       await expect(page.locator('#map')).toHaveCount(1);
122     });
123
124     test('should default to dedupe=1', async () => {
125       expect(
126         await page.locator('#option_dedupe').isChecked()
127       ).toBe(true);
128
129       const links_href = await page.locator(
130         '#api-request a'
131       ).evaluateAll(links => links.map(l => l.href));
132       const api_request_url = new URL(links_href[0]);
133       const debug_url = new URL(links_href[1]);
134
135       expect(api_request_url.searchParams.has('dedupe')).toBe(false);
136       expect(debug_url.searchParams.has('dedupe')).toBe(false);
137     });
138
139     test('should have polygon and marker in map and minimap', async () => {
140       await expect(
141         page.locator('#map .leaflet-overlay-pane path')
142       ).toHaveCount(4);
143     });
144
145     test('should redirect to details page on clicking details button',
146       async () => {
147         await page.locator('#searchresults .result a').first().click();
148         await page.locator('table#address').waitFor();
149
150         const current_url = new URL(page.url());
151         expect(current_url.pathname).toBe('/details.html');
152
153         await expect(page.locator('.container h1')).toContainText('Paris');
154       }
155     );
156   });
157
158   test.describe('Structured search for Paris', () => {
159     let page;
160
161     test.beforeAll(async ({ browser }) => {
162       page = await browser.newPage();
163       await page.goto('/search.html');
164       await page.locator(".nav-link[href='#structured']").click();
165       await page.locator('input[name=city]').fill('Paris');
166       await page.locator('input[name=country]').fill('USA');
167       await page.locator('#structured button[type=submit]').click();
168       await page.locator('#searchresults').waitFor();
169     });
170
171     test.afterAll(async () => {
172       await page.close();
173     });
174
175     test('should have a HTML page title', async () => {
176       expect(await page.title()).toBe(
177         'Result for Paris, USA | Nominatim Demo'
178       );
179     });
180
181     test('should have added search params', async () => {
182       const current_url = new URL(page.url());
183       expect(current_url.searchParams.get('q')).toBeNull();
184       expect(current_url.searchParams.get('city')).toBe('Paris');
185       expect(current_url.searchParams.get('country')).toBe('USA');
186     });
187
188     test('should have at least one result', async () => {
189       const results_count = await page.locator(
190         '#searchresults .result'
191       ).count();
192       expect(results_count).toBeGreaterThan(1);
193     });
194   });
195
196   test.describe('Search for more results', () => {
197     let page;
198     let initial_count;
199
200     test.beforeAll(async ({ browser }) => {
201       page = await browser.newPage();
202       await page.goto('/search.html');
203       await page.locator('input[name=q]').fill('Paris');
204       await page.locator('button[type=submit]').first().click();
205       await page.locator('#searchresults').waitFor();
206     });
207
208     test.afterAll(async () => {
209       await page.close();
210     });
211
212     test('should have show more results button', async () => {
213       await expect(
214         page.locator('button', { hasText: 'Search for more results' })
215       ).toBeVisible();
216     });
217
218     test('should append results when clicking more results', async () => {
219       initial_count = await page.locator(
220         '#searchresults .result'
221       ).count();
222       expect(initial_count).toBeGreaterThan(0);
223
224       await page.locator(
225         'button', { hasText: 'Search for more results' }
226       ).click();
227
228       await page.waitForFunction(
229         (prev) => document.querySelectorAll('#searchresults .result').length > prev,
230         initial_count,
231         { timeout: 10000 }
232       );
233
234       const new_count = await page.locator(
235         '#searchresults .result'
236       ).count();
237       expect(new_count).toBeGreaterThan(initial_count);
238     });
239
240     test('should add result-previous class to older results', async () => {
241       expect(initial_count).toBeGreaterThan(0);
242       await expect(page.locator('#searchresults .result-previous')).toHaveCount(initial_count);
243     });
244
245     test('should highlight first new result', async () => {
246       const highlighted = page.locator('#searchresults .result.highlight');
247       await expect(highlighted).toHaveCount(1);
248       const position = await highlighted.getAttribute('data-position');
249       expect(Number(position)).toBe(initial_count);
250     });
251
252     test('should display API URL for more results', async () => {
253       const api_div = page.locator('.more-api-request[data-request-num="2"]');
254       await expect(api_div).toBeVisible();
255       const api_link = api_div.locator('a').first();
256       await expect(api_link).toHaveText('API request #2');
257       const href = await api_link.getAttribute('href');
258       expect(href).toContain('exclude_place_ids');
259     });
260
261     test('should update browser URL with exclude_place_ids', async () => {
262       const current_url = new URL(page.url());
263       expect(current_url.searchParams.has('exclude_place_ids')).toBe(true);
264     });
265
266     test('should go back to original search URL on browser back', async () => {
267       await page.goBack();
268       const url = new URL(page.url());
269       expect(url.searchParams.has('q')).toBe(true);
270       expect(url.searchParams.has('exclude_place_ids')).toBe(false);
271     });
272   });
273
274   test.describe('Search for OSM URL', () => {
275     let page;
276
277     test.beforeAll(async ({ browser }) => {
278       page = await browser.newPage();
279       await page.goto('/search.html');
280       await page.locator('input[name=q]').fill(
281         'https://www.openstreetmap.org/relation/3459013#map=11/41.2388/-8.3867'
282       );
283       await page.locator('button[type=submit]').first().click();
284       await page.locator('table#address').waitFor();
285     });
286
287     test.afterAll(async () => {
288       await page.close();
289     });
290
291     test('should redirect to detail page search', async () => {
292       expect(await page.title()).toBe(
293         'Details for R3459013 | Nominatim Demo'
294       );
295       await expect(page.locator('.container h1')).toContainText('Porto');
296     });
297   });
298 });