From 4a8f4239979be1fe159b3d54c5a0bc980cc383f8 Mon Sep 17 00:00:00 2001 From: Wouter van der Plas Date: Thu, 9 Sep 2021 17:34:27 +0200 Subject: [PATCH] taginfo --- my.conf.js | 3 - package.json | 2 +- test/spec/services/osm.js | 230 ++++++++++------- test/spec/services/taginfo.js | 465 +++++++++++++++++----------------- test/spec/spec_helpers.js | 13 +- test/spec/util/util.js | 2 +- 6 files changed, 381 insertions(+), 334 deletions(-) diff --git a/my.conf.js b/my.conf.js index 390582538..243d1b2a4 100644 --- a/my.conf.js +++ b/my.conf.js @@ -17,14 +17,11 @@ module.exports = function (config) { // list of files / patterns to load in the browser files: [ - 'node_modules/mocha/mocha.css', - 'node_modules/mocha/mocha.js', 'node_modules/chai/chai.js', 'node_modules/sinon/pkg/sinon.js', 'node_modules/sinon-chai/lib/sinon-chai.js', 'node_modules/happen/happen.js', 'node_modules/fetch-mock/es5/client-bundle.js', - { pattern: 'dist/iD.js.map', included: false }, { pattern: 'dist/iD.js', included: true }, { pattern: 'dist/iD.css', included: true }, { pattern: 'dist/**/*', included: false }, diff --git a/package.json b/package.json index 7fcb7975f..595e7c265 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,7 @@ "quickstart": "npm-run-all -s build:dev start:server", "start:server": "node scripts/server.js", "test": "npm-run-all -s lint build:css build:data build:legacy test:spec", - "test:spec": "phantomjs --web-security=no node_modules/mocha-phantomjs-core/mocha-phantomjs-core.js test/index.html spec", + "test:spec": "karma start my.conf.js", "translations": "node scripts/update_locales.js" }, "dependencies": { diff --git a/test/spec/services/osm.js b/test/spec/services/osm.js index 1e76aadbd..14a9d8966 100644 --- a/test/spec/services/osm.js +++ b/test/spec/services/osm.js @@ -34,7 +34,8 @@ describe('iD.serviceOsm', function () { }); afterEach(function () { - fetchMock.resetBehavior(); + fetchMock.reset(); + serverXHR.restore(); }); @@ -151,8 +152,8 @@ describe('iD.serviceOsm', function () { '}'; it('returns an object', function (done) { - fetchMock.mock('http://www.openstreetmap.org' + path, , { - body: nodeResponse, + fetchMock.mock('http://www.openstreetmap.org' + path, { + body: response, status: 200, headers: { 'Content-Type': 'application/json' } }); @@ -165,63 +166,80 @@ describe('iD.serviceOsm', function () { }); it('retries an authenticated call unauthenticated if 400 Bad Request', function (done) { - fetchMock.mock('http://www.openstreetmap.org' + path, ) + fetchMock.mock('http://www.openstreetmap.org' + path, { + body: response, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + serverXHR.respondWith('GET', 'http://www.openstreetmap.org' + path, + [400, { 'Content-Type': 'text/plain' }, 'Bad Request']); + login(); connection.loadFromAPI(path, function (err, xml) { expect(err).to.be.not.ok; expect(typeof xml).to.eql('object'); expect(connection.authenticated()).to.be.not.ok; + expect(fetchMock.called()).to.be.true; + done(); }); - serverXHR.respondWith('GET', 'http://www.openstreetmap.org' + path, - [400, { 'Content-Type': 'text/plain' }, 'Bad Request']); - serverFetch.respondWith('GET', 'http://www.openstreetmap.org' + path, - [200, { 'Content-Type': 'application/json' }, response]); - serverXHR.respond(); - serverFetch.respond(); }); it('retries an authenticated call unauthenticated if 401 Unauthorized', function (done) { + fetchMock.mock('http://www.openstreetmap.org' + path, { + body: response, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + serverXHR.respondWith('GET', 'http://www.openstreetmap.org' + path, + [401, { 'Content-Type': 'text/plain' }, 'Unauthorized']); + login(); connection.loadFromAPI(path, function (err, xml) { expect(err).to.be.not.ok; expect(typeof xml).to.eql('object'); expect(connection.authenticated()).to.be.not.ok; + expect(fetchMock.called()).to.be.true; + done(); }); - serverXHR.respondWith('GET', 'http://www.openstreetmap.org' + path, - [401, { 'Content-Type': 'text/plain' }, 'Unauthorized']); - serverFetch.respondWith('GET', 'http://www.openstreetmap.org' + path, - [200, { 'Content-Type': 'application/json' }, response]); - serverXHR.respond(); - serverFetch.respond(); }); it('retries an authenticated call unauthenticated if 403 Forbidden', function (done) { + fetchMock.mock('http://www.openstreetmap.org' + path, { + body: response, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + serverXHR.respondWith('GET', 'http://www.openstreetmap.org' + path, + [403, { 'Content-Type': 'text/plain' }, 'Forbidden']); + login(); connection.loadFromAPI(path, function (err, xml) { expect(err).to.be.not.ok; expect(typeof xml).to.eql('object'); expect(connection.authenticated()).to.be.not.ok; + expect(fetchMock.called()).to.be.true; + done(); }); - serverXHR.respondWith('GET', 'http://www.openstreetmap.org' + path, - [403, { 'Content-Type': 'text/plain' }, 'Forbidden']); - serverFetch.respondWith('GET', 'http://www.openstreetmap.org' + path, - [200, { 'Content-Type': 'application/json' }, response]); - serverXHR.respond(); - serverFetch.respond(); }); it('dispatches change event if 509 Bandwidth Limit Exceeded', function (done) { + fetchMock.mock('http://www.openstreetmap.org' + path, { + body: 'Bandwidth Limit Exceeded', + status: 509, + headers: { 'Content-Type': 'text/plain' } + }); + logout(); connection.on('change', spy); connection.loadFromAPI(path, function (err) { @@ -229,13 +247,15 @@ describe('iD.serviceOsm', function () { expect(spy).to.have.been.calledOnce; done(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org' + path, - [509, { 'Content-Type': 'text/plain' }, 'Bandwidth Limit Exceeded']); - serverFetch.respond(); }); it('dispatches change event if 429 Too Many Requests', function (done) { + fetchMock.mock('http://www.openstreetmap.org' + path, { + body: '429 Too Many Requests', + status: 429, + headers: { 'Content-Type': 'text/plain' } + }); + logout(); connection.on('change', spy); connection.loadFromAPI(path, function (err) { @@ -243,10 +263,6 @@ describe('iD.serviceOsm', function () { expect(spy).to.have.been.calledOnce; done(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org' + path, - [429, { 'Content-Type': 'text/plain' }, '429 Too Many Requests']); - serverFetch.respond(); }); }); @@ -270,13 +286,15 @@ describe('iD.serviceOsm', function () { }); it('calls callback when data tiles are loaded', function (done) { + fetchMock.mock(/map.json\?bbox/, { + body: tileResponse, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var spy = sinon.spy(); connection.loadTiles(context.projection, spy); - serverFetch.respondWith('GET', /map.json\?bbox/, - [200, { 'Content-Type': 'application/json' }, tileResponse]); - serverFetch.respond(); - window.setTimeout(function () { expect(spy).to.have.been.calledOnce; done(); @@ -284,15 +302,27 @@ describe('iD.serviceOsm', function () { }); it('#isDataLoaded', function (done) { - expect(connection.isDataLoaded([-74.0444216, 40.6694299])).to.be.not.ok; + fetchMock.mock(/map.json\?bbox/, { + body: tileResponse, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + + // resetting the cache + const caches = connection.caches('get'); + caches.tile.toLoad = {}; + caches.tile.loaded = {}; + caches.tile.inflight = {}; + caches.tile.seen = {}; + caches.tile.rtree.clear(); + + expect(connection.isDataLoaded([-74.0444216, 40.6694299])).to.be.false; connection.loadTiles(context.projection); - serverFetch.respondWith('GET', /map.json\?bbox/, - [200, { 'Content-Type': 'application/json' }, tileResponse]); - serverFetch.respond(); window.setTimeout(function () { - expect(connection.isDataLoaded([-74.0444216, 40.6694299])).to.be.ok; + expect(fetchMock.called()).to.be.true; + expect(connection.isDataLoaded([-74.0444216, 40.6694299])).to.be.true; done(); }, 500); }); @@ -332,34 +362,38 @@ describe('iD.serviceOsm', function () { }); it('loads a way', function (done) { + fetchMock.mock('http://www.openstreetmap.org/api/0.6/way/1/full.json', { + body: wayResponse, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var id = 'w1'; connection.loadEntity(id, function (err, result) { var entity = result.data.find(function (e) { return e.id === id; }); expect(entity).to.be.an.instanceOf(iD.osmWay); done(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org/api/0.6/way/1/full.json', - [200, { 'Content-Type': 'application/json' }, wayResponse]); - serverFetch.respond(); }); it('does not ignore repeat requests', function (done) { + fetchMock.mock('http://www.openstreetmap.org/api/0.6/node/1.json', { + body: wayResponse, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var id = 'n1'; connection.loadEntity(id, function (err1, result1) { var entity1 = result1.data.find(function (e1) { return e1.id === id; }); expect(entity1).to.be.an.instanceOf(iD.osmNode); + connection.loadEntity(id, function (err2, result2) { var entity2 = result2.data.find(function (e2) { return e2.id === id; }); expect(entity2).to.be.an.instanceOf(iD.osmNode); done(); }); - serverFetch.respond(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org/api/0.6/node/1.json', - [200, { 'Content-Type': 'application/json' }, nodeResponse]); - serverFetch.respond(); }); }); @@ -382,32 +416,42 @@ describe('iD.serviceOsm', function () { '}'; it('loads a node', function (done) { + fetchMock.mock('http://www.openstreetmap.org/api/0.6/node/1/1.json', { + body: nodeResponse, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var id = 'n1'; connection.loadEntityVersion(id, 1, function (err, result) { var entity = result.data.find(function (e) { return e.id === id; }); expect(entity).to.be.an.instanceOf(iD.osmNode); done(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org/api/0.6/node/1/1.json', - [200, { 'Content-Type': 'application/json' }, nodeResponse]); - serverFetch.respond(); }); it('loads a way', function (done) { + fetchMock.mock('http://www.openstreetmap.org/api/0.6/way/1/1.json', { + body: wayResponse, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var id = 'w1'; connection.loadEntityVersion(id, 1, function (err, result) { var entity = result.data.find(function (e) { return e.id === id; }); expect(entity).to.be.an.instanceOf(iD.osmWay); done(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org/api/0.6/way/1/1.json', - [200, { 'Content-Type': 'application/json' }, wayResponse]); - serverFetch.respond(); }); it('does not ignore repeat requests', function (done) { + fetchMock.mock('http://www.openstreetmap.org/api/0.6/node/1/1.json', { + body: nodeResponse, + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var id = 'n1'; connection.loadEntityVersion(id, 1, function (err1, result1) { var entity1 = result1.data.find(function (e1) { return e1.id === id; }); @@ -417,12 +461,7 @@ describe('iD.serviceOsm', function () { expect(entity2).to.be.an.instanceOf(iD.osmNode); done(); }); - serverFetch.respond(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org/api/0.6/node/1/1.json', - [200, { 'Content-Type': 'application/json' }, nodeResponse]); - serverFetch.respond(); }); }); @@ -611,13 +650,15 @@ describe('iD.serviceOsm', function () { }); it('fires loadedNotes when notes are loaded', function (done) { + fetchMock.mock(/notes\?/, { + body: notesXML, + status: 200, + headers: { 'Content-Type': 'text/xml' } + }); + connection.on('loadedNotes', spy); connection.loadNotes(context.projection, {}); - serverFetch.respondWith('GET', /notes\?/, - [200, { 'Content-Type': 'text/xml' }, notesXML]); - serverFetch.respond(); - window.setTimeout(function () { expect(spy).to.have.been.calledOnce; done(); @@ -704,47 +745,58 @@ describe('iD.serviceOsm', function () { describe('API capabilities', function () { - var capabilitiesXML = '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - ''; + var capabilitiesXML = ` + + + + + + + + + + + + + + + + + + `; describe('#status', function () { it('gets API status', function (done) { + fetchMock.mock('http://www.openstreetmap.org/api/capabilities', { + body: capabilitiesXML, + status: 200, + headers: { 'Content-Type': 'text/xml' } + }, { + overwriteRoutes: true + }); + connection.status(function (err, val) { expect(val).to.eql('online'); done(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org/api/capabilities', - [200, { 'Content-Type': 'text/xml' }, capabilitiesXML]); - serverFetch.respond(); }); }); describe('#imageryBlocklists', function () { it('updates imagery blocklists', function (done) { + fetchMock.mock('http://www.openstreetmap.org/api/capabilities', { + body: capabilitiesXML, + status: 200, + headers: { 'Content-Type': 'text/xml' } + }, { + overwriteRoutes: true + }); + connection.status(function () { var blocklists = connection.imageryBlocklists(); expect(blocklists).to.deep.equal([new RegExp('\.foo\.com'), new RegExp('\.bar\.org')]); done(); }); - - serverFetch.respondWith('GET', 'http://www.openstreetmap.org/api/capabilities', - [200, { 'Content-Type': 'text/xml' }, capabilitiesXML]); - serverFetch.respond(); }); }); diff --git a/test/spec/services/taginfo.js b/test/spec/services/taginfo.js index 0207fa0bd..3fe2e3094 100644 --- a/test/spec/services/taginfo.js +++ b/test/spec/services/taginfo.js @@ -1,404 +1,401 @@ -describe('iD.serviceTaginfo', function() { - var server, taginfo; +describe('iD.serviceTaginfo', function () { + var taginfo; - before(function() { + before(function () { iD.services.taginfo = iD.serviceTaginfo; }); - after(function() { + after(function () { delete iD.services.taginfo; }); - beforeEach(function() { - server = window.fakeFetch().create(); + beforeEach(function () { taginfo = iD.services.taginfo; + fetchMock.mock(new RegExp('\/keys\/all.*sortname=values_all'), { + body: '{"data":[{"count_all":56136034,"key":"name","count_all_fraction":0.0132}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + // prepopulate popular keys list with "name" taginfo.init(); - server.respondWith('GET', - new RegExp('\/keys\/all.*sortname=values_all'), - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"count_all":56136034,"key":"name","count_all_fraction":0.0132}]}'] - ); - server.respond(); - server.restore(); - server = window.fakeFetch().create(); + fetchMock.reset(); }); - afterEach(function() { - server.restore(); + afterEach(function () { + fetchMock.reset(); }); - function query(url) { + function parseQueryString(url) { return iD.utilStringQs(url.substring(url.indexOf('?'))); } + describe('#keys', function () { + it('calls the given callback with the results of the keys query', function (done) { + fetchMock.mock(/\/keys\/all/, { + body: '{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); - describe('#keys', function() { - it('calls the given callback with the results of the keys query', function(done) { var callback = sinon.spy(); - taginfo.keys({query: 'amen'}, callback); + taginfo.keys({ query: 'amen' }, callback); - server.respondWith('GET', /\/keys\/all/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0}]}'] - ); - server.respond(); - - window.setTimeout(function() { - expect(query(server.requests()[0].url)).to.eql( - {query: 'amen', page: '1', rp: '10', sortname: 'count_all', sortorder: 'desc', lang: 'en'} + window.setTimeout(function () { + expect(parseQueryString(fetchMock.calls()[0][0])).to.eql( + { query: 'amen', page: '1', rp: '10', sortname: 'count_all', sortorder: 'desc', lang: 'en' } ); expect(callback).to.have.been.calledWith( - null, [{'title':'amenity', 'value':'amenity'}] + null, [{ 'title': 'amenity', 'value': 'amenity' }] ); done(); }, 50); }); - it('includes popular keys', function(done) { + it('includes popular keys', function (done) { + fetchMock.mock(/\/keys\/all/, { + body: '{"data":[{"count_all":5190337,"count_nodes":500000,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},' + + '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes":100}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.keys({query: 'amen'}, callback); + taginfo.keys({ query: 'amen' }, callback); - server.respondWith('GET', /\/keys\/all/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0,"count_nodes_fraction":1.0},' - + '{"count_all":1,"key":"amenityother","count_all_fraction":0.0,"count_nodes_fraction":0.0}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'title':'amenity', 'value':'amenity'}] + null, [{ 'title': 'amenity', 'value': 'amenity' }] ); done(); }, 50); }); - it('includes popular keys with an entity type filter', function(done) { + it('includes popular keys with an entity type filter', function (done) { + fetchMock.mock(/\/keys\/all/, { + body: '{"data":[{"count_all":5190337,"count_nodes":500000,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},' + + '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes":100}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.keys({query: 'amen', filter: 'nodes'}, callback); + taginfo.keys({ query: 'amen', filter: 'nodes' }, callback); - server.respondWith('GET', /\/keys\/all/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"count_all":5190337,"count_nodes":500000,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},' - + '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes":100}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'title':'amenity', 'value':'amenity'}] + null, [{ 'title': 'amenity', 'value': 'amenity' }] ); done(); }, 50); }); - it('includes unpopular keys with a wiki page', function(done) { + it('includes unpopular keys with a wiki page', function (done) { + fetchMock.mock(/\/keys\/all/, { + body: '{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},' + + '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes_fraction":0.0, "in_wiki": true}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.keys({query: 'amen'}, callback); + taginfo.keys({ query: 'amen' }, callback); - server.respondWith('GET', /\/keys\/all/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"count_all":5190337,"key":"amenity","count_all_fraction":1.0, "count_nodes_fraction":1.0},' - + '{"count_all":1,"key":"amenityother","count_all_fraction":0.0, "count_nodes_fraction":0.0, "in_wiki": true}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith(null, [ - {'title':'amenity', 'value':'amenity'}, - {'title':'amenityother', 'value':'amenityother'} + { 'title': 'amenity', 'value': 'amenity' }, + { 'title': 'amenityother', 'value': 'amenityother' } ]); done(); }, 50); }); - it('sorts keys with \':\' below keys without \':\'', function(done) { + it('sorts keys with \':\' below keys without \':\'', function (done) { + fetchMock.mock(/\/keys\/all/, { + body: '{"data":[{"key":"ref:bag","count_all":9790586,"count_all_fraction":0.0028},' + + '{"key":"ref","count_all":7933528,"count_all_fraction":0.0023}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.keys({query: 'ref'}, callback); + taginfo.keys({ query: 'ref' }, callback); - server.respondWith('GET', /\/keys\/all/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"key":"ref:bag","count_all":9790586,"count_all_fraction":0.0028},' + - '{"key":"ref","count_all":7933528,"count_all_fraction":0.0023}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'title':'ref', 'value':'ref'},{'title':'ref:bag', 'value':'ref:bag'}] + null, [{ 'title': 'ref', 'value': 'ref' }, { 'title': 'ref:bag', 'value': 'ref:bag' }] ); done(); }, 50); }); }); - describe('#multikeys', function() { - it('calls the given callback with the results of the multikeys query', function(done) { + describe('#multikeys', function () { + it('calls the given callback with the results of the multikeys query', function (done) { + fetchMock.mock(/\/keys\/all/, { + body: '{"data":[{"count_all":69593,"key":"recycling:glass","count_all_fraction":0.0}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.multikeys({query: 'recycling:'}, callback); + taginfo.multikeys({ query: 'recycling:' }, callback); - server.respondWith('GET', /\/keys\/all/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"count_all":69593,"key":"recycling:glass","count_all_fraction":0.0}]}'] - ); - server.respond(); - - window.setTimeout(function() { - expect(query(server.requests()[0].url)).to.eql( - {query: 'recycling:', page: '1', rp: '25', sortname: 'count_all', sortorder: 'desc', lang: 'en'} + window.setTimeout(function () { + expect(parseQueryString(fetchMock.calls()[0][0])).to.eql( + { query: 'recycling:', page: '1', rp: '25', sortname: 'count_all', sortorder: 'desc', lang: 'en' } ); expect(callback).to.have.been.calledWith( - null, [{'title':'recycling:glass', 'value':'recycling:glass'}] + null, [{ 'title': 'recycling:glass', 'value': 'recycling:glass' }] ); done(); }, 50); }); - it('excludes multikeys with extra colons', function(done) { + it('excludes multikeys with extra colons', function (done) { + fetchMock.mock(/\/keys\/all/, { + body: '{"data":[{"count_all":4426,"key":"service:bicycle:retail","count_all_fraction":0.0},' + + '{"count_all":22,"key":"service:bicycle:retail:ebikes","count_all_fraction":0.0}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.multikeys({query: 'service:bicycle:'}, callback); + taginfo.multikeys({ query: 'service:bicycle:' }, callback); - server.respondWith('GET', /\/keys\/all/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"count_all":4426,"key":"service:bicycle:retail","count_all_fraction":0.0},' + - '{"count_all":22,"key":"service:bicycle:retail:ebikes","count_all_fraction":0.0}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'title':'service:bicycle:retail', 'value':'service:bicycle:retail'}] + null, [{ 'title': 'service:bicycle:retail', 'value': 'service:bicycle:retail' }] ); done(); }, 50); }); - it('excludes multikeys with wrong prefix', function(done) { + it('excludes multikeys with wrong prefix', function (done) { + fetchMock.mock(/\/keys\/all/, { + body: '{"data":[{"count_all":4426,"key":"service:bicycle:retail","count_all_fraction":0.0},' + + '{"count_all":22,"key":"disused:service:bicycle","count_all_fraction":0.0}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.multikeys({query: 'service:bicycle:'}, callback); + taginfo.multikeys({ query: 'service:bicycle:' }, callback); - server.respondWith('GET', /\/keys\/all/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"count_all":4426,"key":"service:bicycle:retail","count_all_fraction":0.0},' + - '{"count_all":22,"key":"disused:service:bicycle","count_all_fraction":0.0}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'title':'service:bicycle:retail', 'value':'service:bicycle:retail'}] + null, [{ 'title': 'service:bicycle:retail', 'value': 'service:bicycle:retail' }] ); done(); }, 50); }); }); - describe('#values', function() { - it('calls the given callback with the results of the values query', function(done) { + describe('#values', function () { + it('calls the given callback with the results of the values query', function (done) { + fetchMock.mock(/\/key\/values/, { + body: '{"data":[{"value":"parking","description":"A place for parking cars", "fraction":0.1}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.values({key: 'amenity', query: 'par'}, callback); + taginfo.values({ key: 'amenity', query: 'par' }, callback); - server.respondWith('GET', /\/key\/values/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"value":"parking","description":"A place for parking cars", "fraction":0.1}]}'] - ); - server.respond(); - - window.setTimeout(function() { - expect(query(server.requests()[0].url)).to.eql( - {key: 'amenity', query: 'par', page: '1', rp: '25', sortname: 'count_all', sortorder: 'desc', lang: 'en'} + window.setTimeout(function () { + expect(parseQueryString(fetchMock.calls()[0][0])).to.eql( + { key: 'amenity', query: 'par', page: '1', rp: '25', sortname: 'count_all', sortorder: 'desc', lang: 'en' } ); expect(callback).to.have.been.calledWith( - null, [{'value':'parking','title':'A place for parking cars'}] + null, [{ 'value': 'parking', 'title': 'A place for parking cars' }] ); done(); }, 50); }); - it('includes popular values', function(done) { + it('includes popular values', function (done) { + fetchMock.mock(/\/key\/values/, { + body: '{"data":[{"value":"parking","description":"A place for parking cars", "fraction":1.0},' + + '{"value":"party","description":"A place for partying", "fraction":0.0}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.values({key: 'amenity', query: 'par'}, callback); + taginfo.values({ key: 'amenity', query: 'par' }, callback); - server.respondWith('GET', /\/key\/values/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"value":"parking","description":"A place for parking cars", "fraction":1.0},' + - '{"value":"party","description":"A place for partying", "fraction":0.0}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'value':'parking','title':'A place for parking cars'}] + null, [{ 'value': 'parking', 'title': 'A place for parking cars' }] ); done(); }, 50); }); - it('does not get values for extremely unpopular keys', function(done) { + it('does not get values for extremely unpopular keys', function (done) { + fetchMock.mock(/\/key\/values/, { + body: '{"data":[{"value":"Rue Pasteur","description":"", "fraction":0.0001},' + + '{"value":"Via Trieste","description":"", "fraction":0.0001}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.values({key: 'name', query: 'ste'}, callback); + taginfo.values({ key: 'name', query: 'ste' }, callback); - server.respondWith('GET', /\/key\/values/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"value":"Rue Pasteur","description":"", "fraction":0.0001},' + - '{"value":"Via Trieste","description":"", "fraction":0.0001}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith(null, []); done(); }, 50); }); - it('excludes values with capital letters and some punctuation', function(done) { + it('excludes values with capital letters and some punctuation', function (done) { + fetchMock.mock(/\/key\/values/, { + body: '{"data":[{"value":"parking","description":"A place for parking cars", "fraction":0.2},' + + '{"value":"PArking","description":"A common misspelling", "fraction":0.2},' + + '{"value":"parking;partying","description":"A place for parking cars *and* partying", "fraction":0.2},' + + '{"value":"parking, partying","description":"A place for parking cars *and* partying", "fraction":0.2},' + + '{"value":"*","description":"", "fraction":0.2}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.values({key: 'amenity', query: 'par'}, callback); + taginfo.values({ key: 'amenity', query: 'par' }, callback); - server.respondWith('GET', /\/key\/values/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"value":"parking","description":"A place for parking cars", "fraction":0.2},' - + '{"value":"PArking","description":"A common misspelling", "fraction":0.2},' - + '{"value":"parking;partying","description":"A place for parking cars *and* partying", "fraction":0.2},' - + '{"value":"parking, partying","description":"A place for parking cars *and* partying", "fraction":0.2},' - + '{"value":"*","description":"", "fraction":0.2}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'value':'parking','title':'A place for parking cars'}] + null, [{ 'value': 'parking', 'title': 'A place for parking cars' }] ); done(); }, 50); }); - it('includes network values with capital letters and some punctuation', function(done) { + it('includes network values with capital letters and some punctuation', function (done) { + fetchMock.mock(/\/key\/values/, { + body: '{"data":[{"value":"US:TX:FM","description":"Farm to Market Roads in the U.S. state of Texas.", "fraction":0.34},' + + '{"value":"US:KY","description":"Primary and secondary state highways in the U.S. state of Kentucky.", "fraction":0.31},' + + '{"value":"US:US","description":"U.S. routes in the United States.", "fraction":0.19},' + + '{"value":"US:I","description":"Interstate highways in the United States.", "fraction":0.11},' + + '{"value":"US:MD","description":"State highways in the U.S. state of Maryland.", "fraction":0.06}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.values({key: 'network', query: 'us'}, callback); + taginfo.values({ key: 'network', query: 'us' }, callback); - server.respondWith('GET', /\/key\/values/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"value":"US:TX:FM","description":"Farm to Market Roads in the U.S. state of Texas.", "fraction":0.34},' - + '{"value":"US:KY","description":"Primary and secondary state highways in the U.S. state of Kentucky.", "fraction":0.31},' - + '{"value":"US:US","description":"U.S. routes in the United States.", "fraction":0.19},' - + '{"value":"US:I","description":"Interstate highways in the United States.", "fraction":0.11},' - + '{"value":"US:MD","description":"State highways in the U.S. state of Maryland.", "fraction":0.06}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith(null, [ - {'value':'US:TX:FM','title':'Farm to Market Roads in the U.S. state of Texas.'}, - {'value':'US:KY','title':'Primary and secondary state highways in the U.S. state of Kentucky.'}, - {'value':'US:US','title':'U.S. routes in the United States.'}, - {'value':'US:I','title':'Interstate highways in the United States.'}, - {'value':'US:MD','title':'State highways in the U.S. state of Maryland.'} + { 'value': 'US:TX:FM', 'title': 'Farm to Market Roads in the U.S. state of Texas.' }, + { 'value': 'US:KY', 'title': 'Primary and secondary state highways in the U.S. state of Kentucky.' }, + { 'value': 'US:US', 'title': 'U.S. routes in the United States.' }, + { 'value': 'US:I', 'title': 'Interstate highways in the United States.' }, + { 'value': 'US:MD', 'title': 'State highways in the U.S. state of Maryland.' } ]); done(); }, 50); }); - it('includes biological genus values with capital letters', function(done) { + it('includes biological genus values with capital letters', function (done) { + fetchMock.mock(/\/key\/values/, { + body: '{"data":[{"value":"Quercus","description":"Oak", "fraction":0.5}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.values({key: 'genus', query: 'qu'}, callback); + taginfo.values({ key: 'genus', query: 'qu' }, callback); - server.respondWith('GET', /\/key\/values/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"value":"Quercus","description":"Oak", "fraction":0.5}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'value':'Quercus','title':'Oak'}] + null, [{ 'value': 'Quercus', 'title': 'Oak' }] ); done(); }, 50); }); - it('includes biological taxon values with capital letters', function(done) { + it('includes biological taxon values with capital letters', function (done) { + fetchMock.mock(/\/key\/values/, { + body: '{"data":[{"value":"Quercus robur","description":"Oak", "fraction":0.5}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.values({key: 'taxon', query: 'qu'}, callback); + taginfo.values({ key: 'taxon', query: 'qu' }, callback); - server.respondWith('GET', /\/key\/values/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"value":"Quercus robur","description":"Oak", "fraction":0.5}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'value':'Quercus robur','title':'Oak'}] + null, [{ 'value': 'Quercus robur', 'title': 'Oak' }] ); done(); }, 50); }); - it('includes biological species values with capital letters', function(done) { + it('includes biological species values with capital letters', function (done) { + fetchMock.mock(/\/key\/values/, { + body: '{"data":[{"value":"Quercus robur","description":"Oak", "fraction":0.5}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.values({key: 'species', query: 'qu'}, callback); + taginfo.values({ key: 'species', query: 'qu' }, callback); - server.respondWith('GET', /\/key\/values/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"value":"Quercus robur","description":"Oak", "fraction":0.5}]}'] - ); - server.respond(); - - window.setTimeout(function() { + window.setTimeout(function () { expect(callback).to.have.been.calledWith( - null, [{'value':'Quercus robur','title':'Oak'}] + null, [{ 'value': 'Quercus robur', 'title': 'Oak' }] ); done(); }, 50); }); }); - describe('#roles', function() { - it('calls the given callback with the results of the roles query', function(done) { + describe('#roles', function () { + it('calls the given callback with the results of the roles query', function (done) { + fetchMock.mock(/\/relation\/roles/, { + body: '{"data":[{"role":"stop","count_relation_members_fraction":0.1757},' + + '{"role":"south","count_relation_members_fraction":0.0035}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.roles({rtype: 'route', query: 's', geometry: 'relation'}, callback); + taginfo.roles({ rtype: 'route', query: 's', geometry: 'relation' }, callback); - server.respondWith('GET', /\/relation\/roles/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"role":"stop","count_relation_members_fraction":0.1757},' + - '{"role":"south","count_relation_members_fraction":0.0035}]}'] - ); - server.respond(); - - window.setTimeout(function() { - expect(query(server.requests()[0].url)).to.eql( - {rtype: 'route', query: 's', page: '1', rp: '25', sortname: 'count_relation_members', sortorder: 'desc', lang: 'en'} + window.setTimeout(function () { + expect(parseQueryString(fetchMock.calls()[0][0])).to.eql( + { rtype: 'route', query: 's', page: '1', rp: '25', sortname: 'count_relation_members', sortorder: 'desc', lang: 'en' } ); expect(callback).to.have.been.calledWith(null, [ - {'value': 'stop', 'title': 'stop'}, - {'value': 'south', 'title': 'south'} + { 'value': 'stop', 'title': 'stop' }, + { 'value': 'south', 'title': 'south' } ]); done(); }, 50); }); }); - describe('#docs', function() { - it('calls the given callback with the results of the docs query', function(done) { + describe('#docs', function () { + it('calls the given callback with the results of the docs query', function (done) { + fetchMock.mock(/\/tag\/wiki_page/, { + body: '{"data":[{"on_way":false,"lang":"en","on_area":true,"image":"File:Car park2.jpg"}]}', + status: 200, + headers: { 'Content-Type': 'application/json' } + }); + var callback = sinon.spy(); - taginfo.docs({key: 'amenity', value: 'parking'}, callback); + taginfo.docs({ key: 'amenity', value: 'parking' }, callback); - server.respondWith('GET', /\/tag\/wiki_page/, - [200, { 'Content-Type': 'application/json' }, - '{"data":[{"on_way":false,"lang":"en","on_area":true,"image":"File:Car park2.jpg"}]}'] - ); - server.respond(); - - window.setTimeout(function() { - expect(query(server.requests()[0].url)).to.eql( - {key: 'amenity', value: 'parking'} + window.setTimeout(function () { + expect(parseQueryString(fetchMock.calls()[0][0])).to.eql( + { key: 'amenity', value: 'parking' } ); expect(callback).to.have.been.calledWith( - null, [{'on_way':false,'lang':'en','on_area':true,'image':'File:Car park2.jpg'}] + null, [{ 'on_way': false, 'lang': 'en', 'on_area': true, 'image': 'File:Car park2.jpg' }] ); done(); }, 50); diff --git a/test/spec/spec_helpers.js b/test/spec/spec_helpers.js index 17fe57ccc..baccff507 100644 --- a/test/spec/spec_helpers.js +++ b/test/spec/spec_helpers.js @@ -113,9 +113,7 @@ window.d3 = iD.d3; // Remove this if we can avoid exporting all of d3.js delete window.PointerEvent; // force the brower to use mouse events // some sticky fallbacks - -fetchMock.mock('https://www.openstreetmap.org/api/capabilities', ` - +const capabilities = ` @@ -134,7 +132,10 @@ fetchMock.mock('https://www.openstreetmap.org/api/capabilities', ` - -`, {sticky: true}); +`; -fetchMock.config.fallbackToNetwork = true; \ No newline at end of file +fetchMock.sticky('https://www.openstreetmap.org/api/capabilities', capabilities, {sticky: true}); +fetchMock.sticky('http://www.openstreetmap.org/api/capabilities', capabilities, {sticky: true}); + +fetchMock.config.fallbackToNetwork = true; +fetchMock.config.overwriteRoutes = false; \ No newline at end of file diff --git a/test/spec/util/util.js b/test/spec/util/util.js index 95f8e1c18..3490e2ad0 100644 --- a/test/spec/util/util.js +++ b/test/spec/util/util.js @@ -78,7 +78,7 @@ describe('iD.util', function() { expect(iD.utilTagText({tags:{foo:'bar',two:'three'}})).to.eql('foo=bar, two=three'); }); - it('utilStringQs', function() { + describe('utilStringQs', function() { it('splits a parameter string into k=v pairs', function() { expect(iD.utilStringQs('foo=bar')).to.eql({foo: 'bar'}); expect(iD.utilStringQs('foo=bar&one=2')).to.eql({foo: 'bar', one: '2' });