run: yarn build
- name: Testing
- run: yarn test
+ run: |
+ yarn test
+ API_ON_SAME_PORT=1 yarn test
```
yarn test
+ API_ON_SAME_PORT=1 yarn test
```
+ Setting API_ON_SAME_PORT simulates having both the API and UI on the same server
+ port. That's a rare setup but something https://nominatim.openstreetmap.org/ does
+ so worth testing.
+
* Run syntax linter (configuration in `.eslint.js`)
```
"@rollup/plugin-node-resolve": "^11.0.0",
"eslint-plugin-mocha": "^8.1.0",
"fs-extra": "^9.1.0",
+ "http": "^0.0.1-security",
+ "http-proxy": "^1.18.1",
"mocha": "^8.3.2",
"puppeteer": "^8.0.0",
"rollup": "^2.3.4",
{/if}
<tr class="all-columns"><td colspan="6"><h2>Keywords</h2></td></tr>
- {#if aPlace.keywords}
- <tr class="all-columns"><td colspan="6"><h3>Name Keywords</h3></td></tr>
- {#each aPlace.keywords.name as keyword}
- <tr>
- <td>{formatKeywordToken(keyword.token)}</td>
- {#if keyword.id}
- <td>word id: {keyword.id}</td>
- {/if}
- </tr>
- {/each}
+ {#if api_request_params.keywords}
- {#if aPlace.keywords.address}
- <tr class="all-columns"><td colspan="6"><h3>Address Keywords</h3></td></tr>
- {#each aPlace.keywords.address as keyword}
+ {#if aPlace.keywords && (aPlace.keywords.name || aPlace.keywords.address) }
+ <tr class="all-columns"><td colspan="6"><h3>Name Keywords</h3></td></tr>
+ {#each aPlace.keywords.name as keyword}
<tr>
<td>{formatKeywordToken(keyword.token)}</td>
{#if keyword.id}
{/if}
</tr>
{/each}
+
+ {#if aPlace.keywords.address}
+ <tr class="all-columns"><td colspan="6"><h3>Address Keywords</h3></td></tr>
+ {#each aPlace.keywords.address as keyword}
+ <tr>
+ <td>{formatKeywordToken(keyword.token)}</td>
+ {#if keyword.id}
+ <td>word id: {keyword.id}</td>
+ {/if}
+ </tr>
+ {/each}
+ {/if}
+ {:else}
+ <tr><td>Place has no keywords</td></tr>
{/if}
{:else}
<tr>
{/if}
<tr class="all-columns"><td colspan="6"><h2>Parent Of</h2></td></tr>
- {#if aPlace.hierarchy}
+ {#if api_request_params.hierarchy}
+ {#if aPlace.hierarchy && aPlace.hierarchy.length}
- {#each Object.keys(aPlace.hierarchy) as type}
- <tr class="all-columns"><td colspan="6"><h3>{type}</h3></td></tr>
- {#each aPlace.hierarchy[type] as line}
- <DetailsOneRow addressLine={line} bDistanceInMeters=true />
- {/each}
- {/each}
+ {#each Object.keys(aPlace.hierarchy) as type}
+ <tr class="all-columns"><td colspan="6"><h3>{type}</h3></td></tr>
+ {#each aPlace.hierarchy[type] as line}
+ <DetailsOneRow addressLine={line} bDistanceInMeters=true />
+ {/each}
+ {/each}
- {#if Object.keys(aPlace.hierarchy) > 500}
- <p>There are more child objects which are not shown.</p>
+ {#if Object.keys(aPlace.hierarchy) > 500}
+ <p>There are more child objects which are not shown.</p>
+ {/if}
+ {:else}
+ <tr><td>Place is not parent of other places</td></tr>
{/if}
{:else}
<tr>
const static_server = require('static-server');
+const http = require('http');
+const httpProxy = require('http-proxy');
const puppeteer = require('puppeteer');
const fse = require('fs-extra');
+const testing_port = 9999; // this is the port all tests expect nominatim-ui to listen to
+
+// The installation on https://nominatim.openstreetmap.org/ui/ is a bit more complex as
+// for backward compatiblity they run the API and the UI on the same port. Nominatim-UI
+// is installed in the /ui subdirectory plus their webserver has custom redirect rules.
+//
+// We can simulate that with a proxy.
+const use_proxy = !!process.env.API_ON_SAME_PORT;
+const static_port = use_proxy ? 9998 : 9999;
+
+
// Methods to run at the start and end of the mocha testsuite run
// https://mochajs.org/#global-setup-fixtures
-
exports.mochaGlobalSetup = async function () {
const workdir = 'dist_for_testing';
fse.mkdirpSync(workdir);
fse.copySync('dist', workdir);
+ let api_endpoint = use_proxy ? '/' : 'https:/nominatim.openstreetmap.org/';
+
fse.outputFile(workdir + '/theme/config.theme.js', `
-Nominatim_Config.Nominatim_API_Endpoint = 'https:/nominatim.openstreetmap.org/';
+Nominatim_Config.Nominatim_API_Endpoint = '${api_endpoint}';
`);
+
// 2. Start webserver pointing to build directory
// https://github.com/nbluis/static-server#readme
- this.server = new static_server({ port: 9999, rootPath: workdir });
- await this.server.start();
- console.log(`server running on port ${this.server.port}`);
+ this.static_http_server = new static_server({ port: static_port, rootPath: workdir });
+ await this.static_http_server.start();
+ console.log(`static server serving ${workdir} directory running on port ${static_port}`);
+
+ if (use_proxy) {
+ // https://github.com/http-party/node-http-proxy#readme
+ const proxy = await httpProxy.createProxy({ changeOrigin: true, followRedirects: true });
+ this.proxy = proxy;
+ console.log('proxy started');
+
+ this.proxy_server = await http.createServer((req, res) => {
+ // identify if the requests should be served by the (remote) API or static webserver
+ let api_url_match = req.url.match(/\/(\w+\.php)/);
+
+ let target = api_url_match
+ ? 'http://nominatim.openstreetmap.org/' + api_url_match[1]
+ : 'http://localhost:' + static_port;
+
+ // console.log(`http proxy ${req.url} => ${target + req.url}`)
+ return proxy.web(req, res, { target: target });
+ }).listen(testing_port);
+ console.log(`proxy server started on port ${testing_port}`);
+ }
+
// 3. Create browser instance
global.browser = await puppeteer.launch({
exports.mochaGlobalTeardown = async function () {
global.browser.close();
- await this.server.stop();
- console.log('server stopped');
+ await this.static_http_server.stop();
+ console.log('static server stopped');
+
+ if (use_proxy) {
+ await this.proxy.close();
+ console.log('proxy stopped');
+
+ this.proxy_server.close(() => console.log('proxy server stopped'));
+ }
};
});
});
- describe('With search', function () {
+ describe('With search - no place found', function () {
+ before(async function () {
+ page = await browser.newPage();
+ await page.goto('http://localhost:9999/details.html');
+ await page.type('input[type=edit]', 'n3');
+ await page.click('button[type=submit]');
+ await page.waitForSelector('#api-request');
+ });
+
+
+ it('should display error', async function () {
+ let page_content = await page.$eval('body', el => el.textContent);
+
+ assert.ok(page_content.includes('No place with that OSM ID found'));
+ });
+
+ after(async function () {
+ await page.close();
+ });
+ });
+
+ describe('With search - Eiffel Tower', function () {
before(async function () {
page = await browser.newPage();
await page.goto('http://localhost:9999/details.html');
assert.ok((await page.$eval('.container h1', el => el.textContent)).includes('Taj Mahal'));
});
});
+
+ describe('Place without name, keywords, hierarchy', function () {
+ // e.g. a numeric house number
+ before(async function () {
+ page = await browser.newPage();
+ await page.goto('http://localhost:9999/details.html?osmtype=N&osmid=946563004&keywords=1&hierarchy=1');
+ await page.waitForSelector('.container .row');
+ });
+
+ after(async function () {
+ await page.close();
+ });
+
+ it('should display No Name, no keywords, no hierarchy', async function () {
+ let page_content = await page.$eval('body', el => el.textContent);
+
+ assert.ok(page_content.includes('Name No Name'));
+ assert.ok(page_content.includes('Place has no keywords'));
+ assert.ok(page_content.includes('Place is not parent of other places'));
+ });
+ });
});
resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+eventemitter3@^4.0.0:
+ version "4.0.7"
+ resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
+ integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
+
extract-zip@^2.0.0:
version "2.0.1"
resolved "https://registry.yarnpkg.com/extract-zip/-/extract-zip-2.0.1.tgz#663dca56fe46df890d5f131ef4a06d22bb8ba13a"
resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.1.1.tgz#c4b489e80096d9df1dfc97c79871aea7c617c469"
integrity sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==
+follow-redirects@^1.0.0:
+ version "1.14.1"
+ resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.14.1.tgz#d9114ded0a1cfdd334e164e6662ad02bfd91ff43"
+ integrity sha512-HWqDgT7ZEkqRzBvc2s64vSZ/hfOceEol3ac/7tKwzuvEyWx3/4UegXh5oBOIotkGsObyk3xznnSRVADBgWSQVg==
+
fs-constants@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9"
integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==
+http-proxy@^1.18.1:
+ version "1.18.1"
+ resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
+ integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
+ dependencies:
+ eventemitter3 "^4.0.0"
+ follow-redirects "^1.0.0"
+ requires-port "^1.0.0"
+
+http@^0.0.1-security:
+ version "0.0.1-security"
+ resolved "https://registry.yarnpkg.com/http/-/http-0.0.1-security.tgz#3aac09129d12dc2747bbce4157afde20ad1f7995"
+ integrity sha512-RnDvP10Ty9FxqOtPZuxtebw1j4L/WiqNMDtuc1YMH1XQm5TgDRaR1G9u8upL6KD1bXHSp9eSXo/ED+8Q7FAr+g==
+
https-proxy-agent@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz#e2a90542abb68a762e0a0850f6c9edadfd8506b2"
resolved "https://registry.yarnpkg.com/require-relative/-/require-relative-0.8.7.tgz#7999539fc9e047a37928fa196f8e1563dabd36de"
integrity sha1-eZlTn8ngR6N5KPoZb44VY9q9Nt4=
+requires-port@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
+ integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=
+
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"