fixed more tests

This commit is contained in:
Wouter van der Plas
2021-10-18 23:10:53 +02:00
parent a87142aaf3
commit 2a28f32f7a
10 changed files with 435 additions and 418 deletions

View File

@@ -75,7 +75,7 @@ module.exports = function (config) {
// available browser launchers: https://www.npmjs.com/search?q=keywords:karma-launcher
browsers: [
'Chrome',
// 'PhantomJS'
'PhantomJS'
],

View File

@@ -29,12 +29,12 @@ function abortRequest(controller) {
function maxPageAtZoom(z) {
if (z < 15) return 2;
if (z < 15) return 2;
if (z === 15) return 5;
if (z === 16) return 10;
if (z === 17) return 20;
if (z === 18) return 40;
if (z > 18) return 80;
if (z > 18) return 80;
}
@@ -44,15 +44,15 @@ function loadTiles(which, url, projection) {
// abort inflight requests that are no longer needed
var cache = _oscCache[which];
Object.keys(cache.inflight).forEach(function(k) {
var wanted = tiles.find(function(tile) { return k.indexOf(tile.id + ',') === 0; });
Object.keys(cache.inflight).forEach(function (k) {
var wanted = tiles.find(function (tile) { return k.indexOf(tile.id + ',') === 0; });
if (!wanted) {
abortRequest(cache.inflight[k]);
delete cache.inflight[k];
}
});
tiles.forEach(function(tile) {
tiles.forEach(function (tile) {
loadNextTilePage(which, currZoom, url, tile);
});
}
@@ -87,14 +87,14 @@ function loadNextTilePage(which, currZoom, url, tile) {
};
d3_json(url, options)
.then(function(data) {
.then(function (data) {
cache.loaded[id] = true;
delete cache.inflight[id];
if (!data || !data.currentPageItems || !data.currentPageItems.length) {
throw new Error('No Data');
}
var features = data.currentPageItems.map(function(item) {
var features = data.currentPageItems.map(function (item) {
var loc = [+item.lng, +item.lat];
var d;
@@ -138,7 +138,7 @@ function loadNextTilePage(which, currZoom, url, tile) {
dispatch.call('loadedImages');
}
})
.catch(function() {
.catch(function () {
cache.loaded[id] = true;
delete cache.inflight[id];
});
@@ -152,7 +152,7 @@ function partitionViewport(projection) {
var tiler = utilTiler().zoomExtent([z2, z2]);
return tiler.getTiles(projection)
.map(function(tile) { return tile.extent; });
.map(function (tile) { return tile.extent; });
}
@@ -161,10 +161,10 @@ function searchLimited(limit, projection, rtree) {
limit = limit || 5;
return partitionViewport(projection)
.reduce(function(result, extent) {
.reduce(function (result, extent) {
var found = rtree.search(extent.bbox())
.slice(0, limit)
.map(function(d) { return d.data; });
.map(function (d) { return d.data; });
return (found.length ? result.concat(found) : result);
}, []);
@@ -173,7 +173,7 @@ function searchLimited(limit, projection, rtree) {
export default {
init: function() {
init: function () {
if (!_oscCache) {
this.reset();
}
@@ -181,7 +181,7 @@ export default {
this.event = utilRebind(this, dispatch, 'on');
},
reset: function() {
reset: function () {
if (_oscCache) {
Object.values(_oscCache.images.inflight).forEach(abortRequest);
}
@@ -195,13 +195,13 @@ export default {
},
images: function(projection) {
images: function (projection) {
var limit = 5;
return searchLimited(limit, projection, _oscCache.images.rtree);
},
sequences: function(projection) {
sequences: function (projection) {
var viewport = projection.clipExtent();
var min = [viewport[0][0], viewport[1][1]];
var max = [viewport[1][0], viewport[0][1]];
@@ -210,12 +210,12 @@ export default {
// all sequences for images in viewport
_oscCache.images.rtree.search(bbox)
.forEach(function(d) { sequenceKeys[d.data.sequence_id] = true; });
.forEach(function (d) { sequenceKeys[d.data.sequence_id] = true; });
// make linestrings from those sequences
var lineStrings = [];
Object.keys(sequenceKeys)
.forEach(function(sequenceKey) {
.forEach(function (sequenceKey) {
var seq = _oscCache.sequences[sequenceKey];
var images = seq && seq.images;
@@ -224,8 +224,8 @@ export default {
type: 'LineString',
coordinates: images.map(function (d) { return d.loc; }).filter(Boolean),
properties: {
captured_at: images[0] ? images[0].captured_at: null,
captured_by: images[0] ? images[0].captured_by: null,
captured_at: images[0] ? images[0].captured_at : null,
captured_by: images[0] ? images[0].captured_by : null,
key: sequenceKey
}
});
@@ -235,18 +235,18 @@ export default {
},
cachedImage: function(imageKey) {
cachedImage: function (imageKey) {
return _oscCache.images.forImageKey[imageKey];
},
loadImages: function(projection) {
loadImages: function (projection) {
var url = apibase + '/1.0/list/nearby-photos/';
loadTiles('images', url, projection);
},
ensureViewerLoaded: function(context) {
ensureViewerLoaded: function (context) {
if (_loadViewerPromise) return _loadViewerPromise;
@@ -299,7 +299,7 @@ export default {
// Register viewer resize handler
context.ui().photoviewer.on('resize.openstreetcam', function(dimensions) {
context.ui().photoviewer.on('resize.openstreetcam', function (dimensions) {
imgZoom = d3_zoom()
.extent([[0, 0], dimensions])
.translateExtent([[0, 0], dimensions])
@@ -316,7 +316,7 @@ export default {
function rotate(deg) {
return function() {
return function () {
if (!_oscSelectedImage) return;
var sequenceKey = _oscSelectedImage.sequence_id;
var sequence = _oscCache.sequences[sequenceKey];
@@ -344,7 +344,7 @@ export default {
}
function step(stepBy) {
return function() {
return function () {
if (!_oscSelectedImage) return;
var sequenceKey = _oscSelectedImage.sequence_id;
var sequence = _oscCache.sequences[sequenceKey];
@@ -368,7 +368,7 @@ export default {
},
showViewer: function(context) {
showViewer: function (context) {
var viewer = context.container().select('.photoviewer')
.classed('hide', false);
@@ -388,7 +388,7 @@ export default {
},
hideViewer: function(context) {
hideViewer: function (context) {
_oscSelectedImage = null;
this.updateUrlImage(null);
@@ -408,7 +408,7 @@ export default {
},
selectImage: function(context, imageKey) {
selectImage: function (context, imageKey) {
var d = this.cachedImage(imageKey);
@@ -494,12 +494,12 @@ export default {
},
getSelectedImage: function() {
getSelectedImage: function () {
return _oscSelectedImage;
},
getSequenceKeyForImage: function(d) {
getSequenceKeyForImage: function (d) {
return d && d.sequence_id;
},
@@ -507,7 +507,7 @@ export default {
// Updates the currently highlighted sequence and selected bubble.
// Reset is only necessary when interacting with the viewport because
// this implicitly changes the currently selected bubble/sequence
setStyles: function(context, hovered, reset) {
setStyles: function (context, hovered, reset) {
if (reset) { // reset all layers
context.container().selectAll('.viewfield-group')
.classed('highlighted', false)
@@ -535,13 +535,13 @@ export default {
var highlightedImageKeys = utilArrayUnion(hoveredImageKeys, selectedImageKeys);
context.container().selectAll('.layer-openstreetcam .viewfield-group')
.classed('highlighted', function(d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
.classed('hovered', function(d) { return d.key === hoveredImageKey; })
.classed('currentView', function(d) { return d.key === selectedImageKey; });
.classed('highlighted', function (d) { return highlightedImageKeys.indexOf(d.key) !== -1; })
.classed('hovered', function (d) { return d.key === hoveredImageKey; })
.classed('currentView', function (d) { return d.key === selectedImageKey; });
context.container().selectAll('.layer-openstreetcam .sequence')
.classed('highlighted', function(d) { return d.properties.key === hoveredSequenceKey; })
.classed('currentView', function(d) { return d.properties.key === selectedSequenceKey; });
.classed('highlighted', function (d) { return d.properties.key === hoveredSequenceKey; })
.classed('currentView', function (d) { return d.properties.key === selectedSequenceKey; });
// update viewfields if needed
context.container().selectAll('.layer-openstreetcam .viewfield-group .viewfield')
@@ -560,7 +560,7 @@ export default {
},
updateUrlImage: function(imageKey) {
updateUrlImage: function (imageKey) {
if (!window.mocha) {
var hash = utilStringQs(window.location.hash);
if (imageKey) {
@@ -573,7 +573,7 @@ export default {
},
cache: function() {
cache: function () {
return _oscCache;
}

View File

@@ -3,8 +3,10 @@ import { actionChangeTags } from '../actions/change_tags';
import { actionMergeNodes } from '../actions/merge_nodes';
import { actionSplit } from '../actions/split';
import { modeSelect } from '../modes/select';
import { geoAngle, geoExtent, geoLatToMeters, geoLonToMeters, geoLineIntersection,
geoSphericalClosestNode, geoSphericalDistance, geoVecAngle, geoVecLength, geoMetersToLat, geoMetersToLon } from '../geo';
import {
geoAngle, geoExtent, geoLatToMeters, geoLonToMeters, geoLineIntersection,
geoSphericalClosestNode, geoSphericalDistance, geoVecAngle, geoVecLength, geoMetersToLat, geoMetersToLon
} from '../geo';
import { osmNode } from '../osm/node';
import { osmFlowingWaterwayTagValues, osmPathHighwayTagValues, osmRailwayTrackTagValues, osmRoutableHighwayTagValues } from '../osm/tags';
import { t } from '../core/localizer';
@@ -312,7 +314,7 @@ export function validationCrossingWays(context) {
if (entity.type === 'way') {
return [entity];
} else if (entity.type === 'relation') {
return entity.members.reduce(function(array, member) {
return entity.members.reduce(function (array, member) {
if (member.type === 'way' &&
// only look at geometry ways
(!member.role || member.role === 'outer' || member.role === 'inner')) {
@@ -351,7 +353,7 @@ export function validationCrossingWays(context) {
function createIssue(crossing, graph) {
// use the entities with the tags that define the feature type
crossing.wayInfos.sort(function(way1Info, way2Info) {
crossing.wayInfos.sort(function (way1Info, way2Info) {
var type1 = way1Info.featureType;
var type2 = way2Info.featureType;
if (type1 === type2) {
@@ -363,7 +365,7 @@ export function validationCrossingWays(context) {
}
return type1 < type2;
});
var entities = crossing.wayInfos.map(function(wayInfo) {
var entities = crossing.wayInfos.map(function (wayInfo) {
return getFeatureWithFeatureTypeTagsForWay(wayInfo.way, graph);
});
var edges = [crossing.wayInfos[0].edge, crossing.wayInfos[1].edge];
@@ -376,9 +378,9 @@ export function validationCrossingWays(context) {
var isCrossingIndoors = taggedAsIndoor(entities[0].tags) && taggedAsIndoor(entities[1].tags);
var isCrossingTunnels = allowsTunnel(featureType1) && hasTag(entities[0].tags, 'tunnel') &&
allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
allowsTunnel(featureType2) && hasTag(entities[1].tags, 'tunnel');
var isCrossingBridges = allowsBridge(featureType1) && hasTag(entities[0].tags, 'bridge') &&
allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
allowsBridge(featureType2) && hasTag(entities[1].tags, 'bridge');
var subtype = [featureType1, featureType2].sort().join('-');
@@ -396,13 +398,13 @@ export function validationCrossingWays(context) {
}
// Differentiate based on the loc rounded to 4 digits, since two ways can cross multiple times.
var uniqueID = '' + crossing.crossPoint[0].toFixed(4) + ',' + crossing.crossPoint[1].toFixed(4);
var uniqueID = String(crossing.crossPoint[0].toFixed(4)) + ',' + String(crossing.crossPoint[1].toFixed(4));
return new validationIssue({
type: type,
subtype: subtype,
severity: 'warning',
message: function(context) {
message: function (context) {
var graph = context.graph();
var entity1 = graph.hasEntity(this.entityIds[0]),
entity2 = graph.hasEntity(this.entityIds[1]);
@@ -412,7 +414,7 @@ export function validationCrossingWays(context) {
}) : '';
},
reference: showReference,
entityIds: entities.map(function(entity) {
entityIds: entities.map(function (entity) {
return entity.id;
}),
data: {
@@ -422,7 +424,7 @@ export function validationCrossingWays(context) {
},
hash: uniqueID,
loc: crossing.crossPoint,
dynamicFixes: function(context) {
dynamicFixes: function (context) {
var mode = context.mode();
if (!mode || mode.id !== 'select' || mode.selectedIDs().length !== 1) return [];
@@ -444,12 +446,12 @@ export function validationCrossingWays(context) {
} else if (isCrossingTunnels ||
isCrossingBridges ||
featureType1 === 'building' ||
featureType2 === 'building') {
featureType2 === 'building') {
fixes.push(makeChangeLayerFix('higher'));
fixes.push(makeChangeLayerFix('lower'));
// can only add bridge/tunnel if both features are lines
// can only add bridge/tunnel if both features are lines
} else if (context.graph().geometry(this.entityIds[0]) === 'line' &&
context.graph().geometry(this.entityIds[1]) === 'line') {
@@ -485,11 +487,11 @@ export function validationCrossingWays(context) {
}
}
function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel){
function makeAddBridgeOrTunnelFix(fixTitleID, iconName, bridgeOrTunnel) {
return new validationIssueFix({
icon: iconName,
title: t.html('issues.fix.' + fixTitleID + '.title'),
onClick: function(context) {
onClick: function (context) {
var mode = context.mode();
if (!mode || mode.id !== 'select') return;
@@ -581,10 +583,10 @@ export function validationCrossingWays(context) {
]);
}
var endpointLocGetter1 = function(lengthMeters) {
var endpointLocGetter1 = function (lengthMeters) {
return locSphericalDistanceFromCrossingLoc(projectedAngle, lengthMeters);
};
var endpointLocGetter2 = function(lengthMeters) {
var endpointLocGetter2 = function (lengthMeters) {
return locSphericalDistanceFromCrossingLoc(projectedAngle + Math.PI, lengthMeters);
};
@@ -612,8 +614,8 @@ export function validationCrossingWays(context) {
} else {
var edgeCount = 0;
endNode.parentIntersectionWays(graph).forEach(function(way) {
way.nodes.forEach(function(nodeID) {
endNode.parentIntersectionWays(graph).forEach(function (way) {
way.nodes.forEach(function (nodeID) {
if (nodeID === endNode.id) {
if ((endNode.id === way.first() && endNode.id !== way.last()) ||
(endNode.id === way.last() && endNode.id !== way.first())) {
@@ -657,15 +659,15 @@ export function validationCrossingWays(context) {
var structEndNode1 = determineEndpoint(edge, edgeNodes[1], endpointLocGetter1);
var structEndNode2 = determineEndpoint([edgeNodes[0].id, structEndNode1.id], edgeNodes[0], endpointLocGetter2);
var structureWay = resultWayIDs.map(function(id) {
var structureWay = resultWayIDs.map(function (id) {
return graph.entity(id);
}).find(function(way) {
}).find(function (way) {
return way.nodes.indexOf(structEndNode1.id) !== -1 &&
way.nodes.indexOf(structEndNode2.id) !== -1;
});
var tags = Object.assign({}, structureWay.tags); // copy tags
if (bridgeOrTunnel === 'bridge'){
if (bridgeOrTunnel === 'bridge') {
tags.bridge = 'yes';
tags.layer = '1';
} else {
@@ -698,7 +700,7 @@ export function validationCrossingWays(context) {
return new validationIssueFix({
icon: 'iD-icon-crossing',
title: t.html('issues.fix.' + fixTitleID + '.title'),
onClick: function(context) {
onClick: function (context) {
var loc = this.issue.loc;
var connectionTags = this.issue.data.connectionTags;
var edges = this.issue.data.edges;
@@ -712,16 +714,16 @@ export function validationCrossingWays(context) {
var nodesToMerge = [node.id];
var mergeThresholdInMeters = 0.75;
edges.forEach(function(edge) {
edges.forEach(function (edge) {
var edgeNodes = [graph.entity(edge[0]), graph.entity(edge[1])];
var nearby = geoSphericalClosestNode(edgeNodes, loc);
// if there is already a suitable node nearby, use that
// use the node if node has no interesting tags or if it is a crossing node #8326
if ((!nearby.node.hasInterestingTags() || nearby.node.isCrossing()) && nearby.distance < mergeThresholdInMeters) {
nodesToMerge.push(nearby.node.id);
// else add the new node to the way
// else add the new node to the way
} else {
graph = actionAddMidpoint({loc: loc, edge: edge}, node)(graph);
graph = actionAddMidpoint({ loc: loc, edge: edge }, node)(graph);
}
});
@@ -742,7 +744,7 @@ export function validationCrossingWays(context) {
return new validationIssueFix({
icon: 'iD-icon-' + (higherOrLower === 'higher' ? 'up' : 'down'),
title: t.html('issues.fix.tag_this_as_' + higherOrLower + '.title'),
onClick: function(context) {
onClick: function (context) {
var mode = context.mode();
if (!mode || mode.id !== 'select') return;
@@ -751,7 +753,7 @@ export function validationCrossingWays(context) {
if (selectedIDs.length !== 1) return;
var selectedID = selectedIDs[0];
if (!this.issue.entityIds.some(function(entityId) {
if (!this.issue.entityIds.some(function (entityId) {
return entityId === selectedID;
})) return;

View File

@@ -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": "karma start my.conf.js",
"test:spec": "karma start karma.conf.js",
"translations": "node scripts/update_locales.js"
},
"dependencies": {
@@ -133,4 +133,4 @@
"browserslist": [
"> 0.2%, last 6 major versions, Firefox ESR, IE 11, maintained node versions"
]
}
}

View File

@@ -1,4 +1,4 @@
describe.only('iD.behaviorSelect', function () {
describe('iD.behaviorSelect', function () {
var a, b, context, behavior, container;
function simulateClick(el, o) {
@@ -10,7 +10,7 @@ describe.only('iD.behaviorSelect', function () {
happen.mouseup(el, Object.assign({}, click, o));
}
beforeEach(function() {
beforeEach(function () {
container = d3.select('body').append('div');
context = iD.coreContext().assetPath('../dist/').init().container(container);
@@ -43,72 +43,72 @@ describe.only('iD.behaviorSelect', function () {
container.remove();
});
it('refuses to enter select mode with no ids', function() {
it('refuses to enter select mode with no ids', function () {
context.enter(iD.modeSelect(context, []));
expect(context.mode().id, 'empty array').to.eql('browse');
context.enter(iD.modeSelect(context, undefined));
expect(context.mode().id, 'undefined').to.eql('browse');
});
it('refuses to enter select mode with nonexistent ids', function() {
it('refuses to enter select mode with nonexistent ids', function () {
context.enter(iD.modeSelect(context, ['w-1']));
expect(context.mode().id).to.eql('browse');
});
it('click on entity selects the entity', function(done) {
it('click on entity selects the entity', function (done) {
var el = context.surface().selectAll('.' + a.id).node();
simulateClick(el, {});
window.setTimeout(function() {
window.setTimeout(function () {
expect(context.selectedIDs()).to.eql([a.id]);
done();
}, 50);
});
it('click on empty space clears the selection', function(done) {
it('click on empty space clears the selection', function (done) {
context.enter(iD.modeSelect(context, [a.id]));
var el = context.surface().node();
simulateClick(el, {});
window.setTimeout(function() {
window.setTimeout(function () {
expect(context.mode().id).to.eql('browse');
done();
}, 50);
});
it('shift-click on unselected entity adds it to the selection', function(done) {
it('shift-click on unselected entity adds it to the selection', function (done) {
context.enter(iD.modeSelect(context, [a.id]));
var el = context.surface().selectAll('.' + b.id).node();
simulateClick(el, { shiftKey: true });
window.setTimeout(function() {
window.setTimeout(function () {
expect(context.selectedIDs()).to.eql([a.id, b.id]);
done();
}, 50);
});
it('shift-click on selected entity removes it from the selection', function(done) {
it('shift-click on selected entity removes it from the selection', function (done) {
context.enter(iD.modeSelect(context, [a.id, b.id]));
var el = context.surface().selectAll('.' + b.id).node();
simulateClick(el, { shiftKey: true });
window.setTimeout(function() {
window.setTimeout(function () {
expect(context.selectedIDs()).to.eql([a.id]);
done();
}, 50);
});
it('shift-click on last selected entity clears the selection', function(done) {
it('shift-click on last selected entity clears the selection', function (done) {
context.enter(iD.modeSelect(context, [a.id]));
var el = context.surface().selectAll('.' + a.id).node();
simulateClick(el, { shiftKey: true });
window.setTimeout(function() {
window.setTimeout(function () {
expect(context.mode().id).to.eql('browse');
done();
}, 50);
});
it('shift-click on empty space leaves the selection unchanged', function(done) {
it('shift-click on empty space leaves the selection unchanged', function (done) {
context.enter(iD.modeSelect(context, [a.id]));
var el = context.surface().node();
simulateClick(el, { shiftKey: true });
window.setTimeout(function() {
window.setTimeout(function () {
expect(context.selectedIDs()).to.eql([a.id]);
done();
}, 50);

View File

@@ -1,127 +1,127 @@
describe('iD.coreLocations', function() {
describe('iD.coreLocations', function () {
var locationManager, loco;
var colorado = {
type: 'Feature',
id: 'colorado.geojson',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [
[
[-107.9197, 41.0039],
[-102.0539, 41.0039],
[-102.043, 36.9948],
[-109.0425, 37.0003],
[-109.048, 40.9984],
[-107.9197, 41.0039]
]
]
}
type: 'Feature',
id: 'colorado.geojson',
properties: {},
geometry: {
type: 'Polygon',
coordinates: [
[
[-107.9197, 41.0039],
[-102.0539, 41.0039],
[-102.043, 36.9948],
[-109.0425, 37.0003],
[-109.048, 40.9984],
[-107.9197, 41.0039]
]
]
}
};
var fc = { type: 'FeatureCollection', features: [colorado] };
beforeEach(function() {
beforeEach(function () {
// make a new one each time, so we aren't accidentally testing the "global" locationManager
locationManager = iD.coreLocations();
loco = locationManager.loco();
});
describe('#mergeCustomGeoJSON', function() {
it('merges geojson into lococation-conflation cache', function() {
describe('#mergeCustomGeoJSON', function () {
it('merges geojson into lococation-conflation cache', function () {
locationManager.mergeCustomGeoJSON(fc);
expect(loco._cache['colorado.geojson']).to.be.eql(colorado);
});
});
describe('#mergeLocationSets', function() {
it('returns a promise rejected if not passed an array', function(done) {
describe('#mergeLocationSets', function () {
it('returns a promise rejected if not passed an array', function (done) {
var prom = locationManager.mergeLocationSets({});
prom
.then(function() {
.then(function () {
done(new Error('This was supposed to fail, but somehow succeeded.'));
})
.catch(function(err) {
.catch(function (err) {
expect(/^nothing to do/.test(err)).to.be.true;
done();
});
window.setTimeout(function() {}, 20); // async - to let the promise settle in phantomjs
window.setTimeout(function () { }, 20); // async - to let the promise settle in phantomjs
});
it('resolves locationSets, assigning locationSetID', function(done) {
it('resolves locationSets, assigning locationSetID', function (done) {
var data = [
{ id: 'world', locationSet: { include: ['001'] } },
{ id: 'usa', locationSet: { include: ['usa'] } }
{ id: 'usa', locationSet: { include: ['usa'] } }
];
var prom = locationManager.mergeLocationSets(data);
prom
.then(function(data) {
.then(function (data) {
expect(data).to.be.a('array');
expect(data[0].locationSetID).to.eql('+[Q2]');
expect(data[1].locationSetID).to.eql('+[Q30]');
done();
})
.catch(function(err) {
.catch(function (err) {
done(err);
});
window.setTimeout(function() {}, 20); // async - to let the promise settle in phantomjs
window.setTimeout(function () { }, 20); // async - to let the promise settle in phantomjs
});
it('resolves locationSets, falls back to world locationSetID on errror', function(done) {
it('resolves locationSets, falls back to world locationSetID on errror', function (done) {
var data = [
{ id: 'bogus1', locationSet: { foo: 'bar' } },
{ id: 'bogus2', locationSet: { include: ['fake.geojson'] } }
];
var prom = locationManager.mergeLocationSets(data);
prom
.then(function(data) {
.then(function (data) {
expect(data).to.be.a('array');
expect(data[0].locationSetID).to.eql('+[Q2]');
expect(data[1].locationSetID).to.eql('+[Q2]');
done();
})
.catch(function(err) {
.catch(function (err) {
done(err);
});
window.setTimeout(function() {}, 20); // async - to let the promise settle in phantomjs
window.setTimeout(function () { }, 20); // async - to let the promise settle in phantomjs
});
});
describe('#locationSetID', function() {
it('calculates a locationSetID for a locationSet', function() {
describe('#locationSetID', function () {
it('calculates a locationSetID for a locationSet', function () {
expect(locationManager.locationSetID({ include: ['usa'] })).to.be.eql('+[Q30]');
});
it('falls back to the world locationSetID in case of errors', function() {
it('falls back to the world locationSetID in case of errors', function () {
expect(locationManager.locationSetID({ foo: 'bar' })).to.be.eql('+[Q2]');
expect(locationManager.locationSetID({ include: ['fake.geojson'] })).to.be.eql('+[Q2]');
});
});
describe('#feature', function() {
it('has the world locationSet pre-resolved', function() {
describe('#feature', function () {
it('has the world locationSet pre-resolved', function () {
var result = locationManager.feature('+[Q2]');
expect(result).to.include({ type: 'Feature', id: '+[Q2]' });
});
it('falls back to the world locationSetID in case of errors', function() {
it('falls back to the world locationSetID in case of errors', function () {
var result = locationManager.feature('fake');
expect(result).to.include({ type: 'Feature', id: '+[Q2]' });
});
});
describe('#locationsAt', function() {
it('has the world locationSet pre-resolved', function() {
describe('#locationsAt', function () {
it('has the world locationSet pre-resolved', function () {
var result1 = locationManager.locationsAt([-108.557, 39.065]); // Grand Junction
expect(result1).to.be.an('object').that.has.all.keys('+[Q2]');
var result2 = locationManager.locationsAt([-74.481, 40.797]); // Morristown
@@ -130,7 +130,7 @@ describe('iD.coreLocations', function() {
expect(result3).to.be.an('object').that.has.all.keys('+[Q2]');
});
it('returns valid locations at a given lon,lat', function(done) {
it('returns valid locations at a given lon,lat', function (done) {
// setup, load colorado.geojson and resolve some locationSets
locationManager.mergeCustomGeoJSON(fc);
locationManager.mergeLocationSets([
@@ -138,7 +138,7 @@ describe('iD.coreLocations', function() {
{ id: 'OSM-USA', locationSet: { include: ['us'] } },
{ id: 'OSM-Colorado', locationSet: { include: ['colorado.geojson'] } }
])
.then(function() {
.then(function () {
var result1 = locationManager.locationsAt([-108.557, 39.065]); // Grand Junction
expect(result1).to.be.an('object').that.has.all.keys('+[Q2]', '+[Q30]', '+[colorado.geojson]');
var result2 = locationManager.locationsAt([-74.481, 40.797]); // Morristown
@@ -147,11 +147,11 @@ describe('iD.coreLocations', function() {
expect(result3).to.be.an('object').that.has.all.keys('+[Q2]');
done();
})
.catch(function(err) {
.catch(function (err) {
done(err);
});
window.setTimeout(function() {}, 20); // async - to let the promise settle in phantomjs
window.setTimeout(function () { }, 20); // async - to let the promise settle in phantomjs
});
});

View File

@@ -1,7 +1,7 @@
describe('maprules', function() {
describe('maprules', function () {
var _ruleChecks, _savedAreaKeys, validationRules;
before(function() {
before(function () {
_savedAreaKeys = iD.osmAreaKeys;
iD.osmSetAreaKeys({ building: {}, amenity: {} });
@@ -10,16 +10,16 @@ describe('maprules', function() {
_ruleChecks = iD.serviceMapRules.ruleChecks();
});
after(function() {
after(function () {
iD.osmSetAreaKeys(_savedAreaKeys);
delete iD.services.maprules;
});
describe('#filterRuleChecks', function() {
it('returns shortlist of mapcss checks relevant to provided selector', function() {
describe('#filterRuleChecks', function () {
it('returns shortlist of mapcss checks relevant to provided selector', function () {
var selector = {
geometry: 'closedway',
equals: {amenity: 'marketplace'},
equals: { amenity: 'marketplace' },
absence: 'name',
error: '\'Marketplace\' preset must be coupled with name'
};
@@ -34,8 +34,8 @@ describe('maprules', function() {
});
});
describe('#buildTagMap', function() {
it('builds a map of tag keys/values found in mapcss selector', function() {
describe('#buildTagMap', function () {
it('builds a map of tag keys/values found in mapcss selector', function () {
[
{
t: {
@@ -90,14 +90,14 @@ describe('maprules', function() {
lanes: [4, 1]
}
}
].forEach(function(test) {
].forEach(function (test) {
expect(iD.serviceMapRules.buildTagMap(test.t)).to.eql(test.r);
});
});
});
describe('#inferGeometry', function() {
it('infers geometry using selector keys', function() {
describe('#inferGeometry', function () {
it('infers geometry using selector keys', function () {
var amenityDerivedArea = {
geometry: 'closedway',
@@ -148,32 +148,32 @@ describe('maprules', function() {
});
});
describe('#addRule', function() {
describe('#addRule', function () {
it('builds a rule from provided selector and adds it to _validationRules', function () {
var selector = {
geometry:'node',
equals: {amenity:'marketplace'},
absence:'name',
warning:'\'Marketplace\' preset must be coupled with name'
geometry: 'node',
equals: { amenity: 'marketplace' },
absence: 'name',
warning: '\'Marketplace\' preset must be coupled with name'
};
expect(iD.serviceMapRules.validationRules()).to.be.empty;
iD.serviceMapRules.addRule(selector);
expect(iD.serviceMapRules.validationRules().length).to.eql(1);
});
});
describe('#clearRules', function() {
it('clears _validationRules array', function() {
describe('#clearRules', function () {
it('clears _validationRules array', function () {
expect(iD.serviceMapRules.validationRules().length).to.eql(1);
iD.serviceMapRules.clearRules();
expect(iD.serviceMapRules.validationRules()).to.be.empty;
});
});
describe('#validationRules', function() {
it('returns _validationRules array', function() {
describe('#validationRules', function () {
it('returns _validationRules array', function () {
var selector = {
geometry: 'closedway',
equals: {amenity: 'marketplace'},
equals: { amenity: 'marketplace' },
absence: 'name',
error: '\'Marketplace\' preset must be coupled with name'
};
@@ -185,71 +185,71 @@ describe('maprules', function() {
});
describe('_ruleChecks', function () {
describe('#equals', function() {
it('is true when two tag maps intersect', function() {
describe('#equals', function () {
it('is true when two tag maps intersect', function () {
var a = { amenity: 'school' };
var b = { amenity: 'school' };
expect(_ruleChecks.equals(a)(b)).to.be.true;
});
it('is false when two tag maps intersect', function() {
it('is false when two tag maps intersect', function () {
var a = { man_made: 'water_tap' };
var b = { amenity: 'school' };
expect(_ruleChecks.equals(a)(b)).to.be.false;
});
});
describe('#notEquals', function() {
it('is true when two tag maps do not intersect', function() {
describe('#notEquals', function () {
it('is true when two tag maps do not intersect', function () {
var a = { man_made: 'water_tap' };
var b = { amenity: 'school' };
expect(_ruleChecks.notEquals(a)(b)).to.be.true;
});
it('is not true when two tag maps intersect', function() {
it('is not true when two tag maps intersect', function () {
var a = { amenity: 'school' };
var b = { amenity: 'school', opening_hours: '9-5' };
expect(_ruleChecks.notEquals(a)(b)).to.be.false;
});
});
describe('absence', function() {
it('is true when tag map keys does not include key in question', function() {
describe('absence', function () {
it('is true when tag map keys does not include key in question', function () {
var key = 'amenity';
var map = { building: 'yes' };
expect(_ruleChecks.absence(key)(map)).to.be.true;
});
it('is false when tag map keys does include key in question', function() {
it('is false when tag map keys does include key in question', function () {
var key = 'amenity';
var map = { amenity: 'school' };
expect(_ruleChecks.absence(key)(map)).to.be.false;
});
});
describe('presence', function() {
it('is true when tag map keys includes key in question', function() {
describe('presence', function () {
it('is true when tag map keys includes key in question', function () {
var key = 'amenity';
var map = { amenity: 'school'};
var map = { amenity: 'school' };
expect(_ruleChecks.presence(key)(map)).to.be.true;
});
it('is false when tag map keys do not include key in question', function() {
it('is false when tag map keys do not include key in question', function () {
var key = 'amenity';
var map = { building: 'yes'};
var map = { building: 'yes' };
expect(_ruleChecks.presence(key)(map)).to.be.false;
});
});
describe('greaterThan', function() {
it('is true when a tag value is greater than the selector value', function() {
describe('greaterThan', function () {
it('is true when a tag value is greater than the selector value', function () {
var selectorTags = { lanes: 5 };
var tags = { lanes : 6 };
var tags = { lanes: 6 };
expect(_ruleChecks.greaterThan(selectorTags)(tags)).to.be.true;
});
it('is false when a tag value is less than or equal to the selector value', function() {
it('is false when a tag value is less than or equal to the selector value', function () {
var selectorTags = { lanes: 5 };
[4, 5].forEach(function(val) {
[4, 5].forEach(function (val) {
expect(_ruleChecks.greaterThan(selectorTags)({ lanes: val })).to.be.false;
});
});
});
describe('greaterThanEqual', function() {
it('is true when a tag value is greater than or equal to the selector value', function() {
describe('greaterThanEqual', function () {
it('is true when a tag value is greater than or equal to the selector value', function () {
var selectorTags = { lanes: 5 };
[5, 6].forEach(function(val) {
[5, 6].forEach(function (val) {
expect(_ruleChecks.greaterThanEqual(selectorTags)({ lanes: val })).to.be.true;
});
});
@@ -259,64 +259,64 @@ describe('maprules', function() {
expect(_ruleChecks.greaterThanEqual(selectorTags)(tags)).to.be.false;
});
});
describe('lessThan', function() {
it('is true when a tag value is less than the selector value', function() {
describe('lessThan', function () {
it('is true when a tag value is less than the selector value', function () {
var selectorTags = { lanes: 5 };
var tags = { lanes: 4 };
expect(_ruleChecks.lessThan(selectorTags)(tags)).to.be.true;
});
it('is false when a tag value is greater than or equal to the selector value', function() {
it('is false when a tag value is greater than or equal to the selector value', function () {
var selectorTags = { lanes: 5 };
[6, 7].forEach(function(val) {
expect(_ruleChecks.lessThan(selectorTags)({ lanes: val })).to.be.false;
});
[6, 7].forEach(function (val) {
expect(_ruleChecks.lessThan(selectorTags)({ lanes: val })).to.be.false;
});
});
});
describe('lessThanEqual', function() {
it('is true when a tag value is less than or equal to the selector value', function() {
describe('lessThanEqual', function () {
it('is true when a tag value is less than or equal to the selector value', function () {
var selectorTags = { lanes: 5 };
[4, 5].forEach(function(val) {
[4, 5].forEach(function (val) {
expect(_ruleChecks.lessThanEqual(selectorTags)({ lanes: val })).to.be.true;
});
});
it('is false when a tag value is greater than the selector value', function() {
var selectorTags = { lanes: 5 };
var tags = { lanes: 6 };
expect(_ruleChecks.lessThanEqual(selectorTags)(tags)).to.be.false;
it('is false when a tag value is greater than the selector value', function () {
var selectorTags = { lanes: 5 };
var tags = { lanes: 6 };
expect(_ruleChecks.lessThanEqual(selectorTags)(tags)).to.be.false;
});
});
describe('positiveRegex', function() {
var positiveRegex = { amenity: ['^hospital$','^clinic$']};
it('is true when tag value matches positiveRegex', function() {
describe('positiveRegex', function () {
var positiveRegex = { amenity: ['^hospital$', '^clinic$'] };
it('is true when tag value matches positiveRegex', function () {
var tags = { amenity: 'hospital' };
expect(_ruleChecks.positiveRegex(positiveRegex)(tags)).to.be.true;
});
it('is false when tag value does not match negative regex', function() {
it('is false when tag value does not match negative regex', function () {
var tags = { amenity: 'school' };
expect(_ruleChecks.positiveRegex(positiveRegex)(tags)).to.be.false;
});
});
describe('negativeRegex', function() {
var negativeRegex = { bicycle: [ 'use_path', 'designated' ] };
it('is true when tag value does not match negativeRegex', function() {
describe('negativeRegex', function () {
var negativeRegex = { bicycle: ['use_path', 'designated'] };
it('is true when tag value does not match negativeRegex', function () {
var tags = { bicycle: 'yes' };
expect(_ruleChecks.negativeRegex(negativeRegex)(tags)).to.be.true;
});
it('is false when tag value matches negativeRegex', function() {
it('is false when tag value matches negativeRegex', function () {
var tags = { bicycle: 'designated' };
expect(_ruleChecks.negativeRegex(negativeRegex)(tags)).to.be.false;
});
});
});
describe('rule', function() {
describe('rule', function () {
var selectors;
before(function() {
before(function () {
selectors = [
{
geometry:'node',
equals: {amenity:'marketplace'},
absence:'name',
error:'\'Marketplace\' preset must be coupled with name'
geometry: 'node',
equals: { amenity: 'marketplace' },
absence: 'name',
error: '\'Marketplace\' preset must be coupled with name'
},
{
geometry: 'closedway',
@@ -324,10 +324,10 @@ describe('maprules', function() {
error: '\'Clinic\' preset must be coupled with building=yes'
},
{
geometry:'node',
equals: {man_made: 'tower', 'tower:type': 'communication'},
geometry: 'node',
equals: { man_made: 'tower', 'tower:type': 'communication' },
presence: 'height',
error:'\'Communication Tower\' preset must not be coupled with height'
error: '\'Communication Tower\' preset must not be coupled with height'
},
{
geometry: 'node',
@@ -368,18 +368,18 @@ describe('maprules', function() {
];
iD.serviceMapRules.clearRules();
selectors.forEach(function(selector) { iD.serviceMapRules.addRule(selector); });
selectors.forEach(function (selector) { iD.serviceMapRules.addRule(selector); });
validationRules = iD.serviceMapRules.validationRules();
});
describe('#matches', function() {
describe('#matches', function () {
var selectors, entities;
before(function() {
before(function () {
selectors = [
{
geometry:'node',
equals: {amenity:'marketplace'},
absence:'name',
error:'\'Marketplace\' preset must be coupled with name'
geometry: 'node',
equals: { amenity: 'marketplace' },
absence: 'name',
error: '\'Marketplace\' preset must be coupled with name'
},
{
geometry: 'closedway',
@@ -387,10 +387,10 @@ describe('maprules', function() {
error: '\'Clinic\' preset must be coupled with building=yes'
},
{
geometry:'node',
equals: {man_made: 'tower', 'tower:type': 'communication'},
geometry: 'node',
equals: { man_made: 'tower', 'tower:type': 'communication' },
presence: 'height',
error:'\'Communication Tower\' preset must not be coupled with height'
error: '\'Communication Tower\' preset must not be coupled with height'
},
{
geometry: 'node',
@@ -430,34 +430,34 @@ describe('maprules', function() {
}
];
entities = [
iD.osmEntity({ type: 'node', tags: { amenity: 'marketplace' }}),
iD.osmWay({ tags: { building: 'house', amenity: 'clinic' }, nodes: [ 'a', 'b', 'c', 'a' ]}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', 'tower:type': 'communication', height: 5 }}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 6 }}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 9 }}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 5 }}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 10 }}),
iD.osmWay({ tags: { amenity: 'clinic', emergency: 'definitely' }, nodes: [ 'd', 'e', 'f', 'd' ]}),
iD.osmWay({ tags: { highway: 'residential', structure: 'bridge' }}),
iD.osmEntity({ type: 'node', tags: { amenity: 'marketplace' } }),
iD.osmWay({ tags: { building: 'house', amenity: 'clinic' }, nodes: ['a', 'b', 'c', 'a'] }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', 'tower:type': 'communication', height: 5 } }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 6 } }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 9 } }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 5 } }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 10 } }),
iD.osmWay({ tags: { amenity: 'clinic', emergency: 'definitely' }, nodes: ['d', 'e', 'f', 'd'] }),
iD.osmWay({ tags: { highway: 'residential', structure: 'bridge' } }),
];
iD.serviceMapRules.clearRules();
selectors.forEach(function(selector) { iD.serviceMapRules.addRule(selector); });
selectors.forEach(function (selector) { iD.serviceMapRules.addRule(selector); });
validationRules = iD.serviceMapRules.validationRules();
});
it('is true when each rule check is \'true\'', function() {
validationRules.forEach(function(rule, i) {
it('is true when each rule check is \'true\'', function () {
validationRules.forEach(function (rule, i) {
expect(rule.matches(entities[i])).to.be.true;
});
});
it('is true when at least one rule check is \'false\'', function() {
it('is true when at least one rule check is \'false\'', function () {
var selector = {
geometry: 'way',
equals: { highway: 'residential' },
positiveRegex: { structure: ['embarkment', 'bridge'] },
error: '\'suburban road\' structure tag cannot be \'bridge\' or \'tunnel\''
};
var entity = iD.osmWay({ tags: { highway: 'residential', structure: 'tunnel' }});
var entity = iD.osmWay({ tags: { highway: 'residential', structure: 'tunnel' } });
iD.serviceMapRules.clearRules();
iD.serviceMapRules.addRule(selector);
var rule = iD.serviceMapRules.validationRules()[0];
@@ -465,16 +465,16 @@ describe('maprules', function() {
expect(rule.matches(entity)).to.be.false;
});
});
describe('#findIssues', function() {
describe('#findIssues', function () {
var selectors, entities, _graph;
before(function() {
before(function () {
selectors = [
{
geometry:'node',
equals: {amenity:'marketplace'},
absence:'name',
error:'\'Marketplace\' preset must be coupled with name'
geometry: 'node',
equals: { amenity: 'marketplace' },
absence: 'name',
error: '\'Marketplace\' preset must be coupled with name'
},
{
geometry: 'closedway',
@@ -482,10 +482,10 @@ describe('maprules', function() {
error: '\'Clinic\' preset must be coupled with building=yes'
},
{
geometry:'node',
equals: {man_made: 'tower', 'tower:type': 'communication'},
geometry: 'node',
equals: { man_made: 'tower', 'tower:type': 'communication' },
presence: 'height',
error:'\'Communication Tower\' preset must not be coupled with height'
error: '\'Communication Tower\' preset must not be coupled with height'
},
{
geometry: 'node',
@@ -525,15 +525,15 @@ describe('maprules', function() {
}
];
entities = [
iD.osmEntity({ type: 'node', tags: { amenity: 'marketplace' }}),
iD.osmWay({ tags: { building: 'house', amenity: 'clinic' }, nodes: [ 'a', 'b', 'c', 'a' ]}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', 'tower:type': 'communication', height: 5 }}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 6 }}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 9 }}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 5 }}),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 10 }}),
iD.osmWay({ tags: { amenity: 'clinic', emergency: 'definitely' }, nodes: [ 'd', 'e', 'f', 'd' ]}),
iD.osmWay({ tags: { highway: 'residential', structure: 'bridge' }}),
iD.osmEntity({ type: 'node', tags: { amenity: 'marketplace' } }),
iD.osmWay({ tags: { building: 'house', amenity: 'clinic' }, nodes: ['a', 'b', 'c', 'a'] }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', 'tower:type': 'communication', height: 5 } }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 6 } }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 9 } }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 5 } }),
iD.osmEntity({ type: 'node', tags: { man_made: 'tower', height: 10 } }),
iD.osmWay({ tags: { amenity: 'clinic', emergency: 'definitely' }, nodes: ['d', 'e', 'f', 'd'] }),
iD.osmWay({ tags: { highway: 'residential', structure: 'bridge' } }),
];
var wayNodes = [
@@ -546,11 +546,11 @@ describe('maprules', function() {
];
_graph = iD.coreGraph(entities.concat(wayNodes));
iD.serviceMapRules.clearRules();
selectors.forEach(function(selector) { iD.serviceMapRules.addRule(selector); });
selectors.forEach(function (selector) { iD.serviceMapRules.addRule(selector); });
validationRules = iD.serviceMapRules.validationRules();
});
it('finds issues', function() {
validationRules.forEach(function(rule, i) {
it('finds issues', function () {
validationRules.forEach(function (rule, i) {
var issues = [];
var entity = entities[i];
var selector = selectors[i];

View File

@@ -1,34 +1,35 @@
describe('iD.serviceOpenstreetcam', function() {
describe('iD.serviceOpenstreetcam', function () {
var dimensions = [64, 64];
var context, server, openstreetcam;
var context, openstreetcam;
before(function() {
before(function () {
iD.services.openstreetcam = iD.serviceOpenstreetcam;
fetchMock.reset();
});
after(function() {
after(function () {
delete iD.services.openstreetcam;
});
beforeEach(function() {
beforeEach(function () {
context = iD.coreContext().assetPath('../dist/').init();
context.projection
.scale(iD.geoZoomToScale(14))
.translate([-116508, 0]) // 10,0
.clipExtent([[0,0], dimensions]);
.clipExtent([[0, 0], dimensions]);
server = window.fakeFetch().create();
openstreetcam = iD.services.openstreetcam;
openstreetcam.reset();
fetchMock.reset();
});
afterEach(function() {
server.restore();
afterEach(function () {
fetchMock.reset();
});
describe('#init', function() {
it('Initializes cache one time', function() {
describe('#init', function () {
it('Initializes cache one time', function () {
var cache = openstreetcam.cache();
expect(cache).to.have.property('images');
expect(cache).to.have.property('sequences');
@@ -39,10 +40,10 @@ describe('iD.serviceOpenstreetcam', function() {
});
});
describe('#reset', function() {
it('resets cache and image', function() {
describe('#reset', function () {
it('resets cache and image', function () {
openstreetcam.cache().foo = 'bar';
openstreetcam.selectImage(context, {key: 'baz'});
openstreetcam.selectImage(context, { key: 'baz' });
openstreetcam.reset();
expect(openstreetcam.cache()).to.not.have.property('foo');
@@ -50,18 +51,11 @@ describe('iD.serviceOpenstreetcam', function() {
});
});
describe('#loadImages', function() {
it('fires loadedImages when images are loaded', function(done) {
openstreetcam.on('loadedImages', function() {
expect(server.requests().length).to.eql(1); // 1 nearby-photos
done();
});
openstreetcam.loadImages(context.projection);
describe('#loadImages', function () {
it('fires loadedImages when images are loaded', function (done) {
var data = {
status: { apiCode: '600', httpCode: 200, httpMessage: 'Success' },
currentPageItems:[{
currentPageItems: [{
id: '1',
sequence_id: '100',
sequence_index: '1',
@@ -101,21 +95,24 @@ describe('iD.serviceOpenstreetcam', function() {
totalFilteredItems: ['3']
};
server.respondWith('POST', /nearby-photos/,
[200, { 'Content-Type': 'application/json' }, JSON.stringify(data) ]);
server.respond();
fetchMock.mock(new RegExp('/nearby-photos/'), {
body: JSON.stringify(data),
status: 200,
headers: { 'Content-Type': 'application/json' }
});
openstreetcam.on('loadedImages', function () {
expect(fetchMock.calls().length).to.eql(1); // 1 nearby-photos
done();
});
openstreetcam.loadImages(context.projection);
});
it('does not load images around null island', function(done) {
var spy = sinon.spy();
context.projection.translate([0,0]);
openstreetcam.on('loadedImages', spy);
openstreetcam.loadImages(context.projection);
it('does not load images around null island', function (done) {
var data = {
status: { apiCode: '600', httpCode: 200, httpMessage: 'Success' },
currentPageItems:[{
currentPageItems: [{
id: '1',
sequence_id: '100',
sequence_index: '1',
@@ -155,25 +152,26 @@ describe('iD.serviceOpenstreetcam', function() {
totalFilteredItems: ['3']
};
server.respondWith('POST', /nearby-photos/,
[200, { 'Content-Type': 'application/json' }, JSON.stringify(data) ]);
server.respond();
var spy = sinon.spy();
fetchMock.mock(new RegExp('/nearby-photos/'), {
body: JSON.stringify(data),
status: 200,
headers: { 'Content-Type': 'application/json' }
});
window.setTimeout(function() {
context.projection.translate([0, 0]);
openstreetcam.on('loadedImages', spy);
openstreetcam.loadImages(context.projection);
window.setTimeout(function () {
expect(spy).to.have.been.not.called;
expect(server.requests().length).to.eql(0); // no tile requests of any kind
expect(fetchMock.calls().length).to.eql(0); // no tile requests of any kind
done();
}, 200);
});
it('loads multiple pages of image results', function(done) {
openstreetcam.on('loadedImages', function() {
expect(server.requests().length).to.eql(2); // 2 nearby-photos
done();
});
openstreetcam.loadImages(context.projection);
it('loads multiple pages of image results', function (done) {
var features = [];
for (var i = 0; i < 1000; i++) {
var key = String(i);
@@ -191,44 +189,54 @@ describe('iD.serviceOpenstreetcam', function() {
username: 'test'
});
}
var response = {
status: { apiCode: '600', httpCode: 200, httpMessage: 'Success' },
currentPageItems: features,
totalFilteredItems: ['1000']
};
server.respondWith('POST', /nearby-photos/,
[200, { 'Content-Type': 'application/json' }, JSON.stringify(response) ]);
server.respond();
fetchMock.mock(new RegExp('/nearby-photos/'), {
body: JSON.stringify(response),
status: 200,
headers: { 'Content-Type': 'application/json' }
});
openstreetcam.on('loadedImages', function () {
expect(fetchMock.calls().length).to.eql(2); // 2 nearby-photos
done();
});
openstreetcam.loadImages(context.projection);
});
});
describe('#images', function() {
it('returns images in the visible map area', function() {
describe('#images', function () {
it('returns images in the visible map area', function () {
var features = [
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 0 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 1 } },
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], ca: 90, sequence_id: '100', sequence_index: 2 } }
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 0 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 1 } },
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10, 1], ca: 90, sequence_id: '100', sequence_index: 2 } }
];
openstreetcam.cache().images.rtree.load(features);
var res = openstreetcam.images(context.projection);
expect(res).to.deep.eql([
{ key: '0', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 0 },
{ key: '1', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 1 }
{ key: '0', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 0 },
{ key: '1', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 1 }
]);
});
it('limits results no more than 5 stacked images in one spot', function() {
it('limits results no more than 5 stacked images in one spot', function () {
var features = [
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 0 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 1 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 2 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 3 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 4 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '5', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 5 } }
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 0 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 1 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '2', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 2 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '3', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 3 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '4', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 4 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '5', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 5 } }
];
openstreetcam.cache().images.rtree.load(features);
@@ -238,21 +246,21 @@ describe('iD.serviceOpenstreetcam', function() {
});
describe('#sequences', function() {
it('returns sequence linestrings in the visible map area', function() {
describe('#sequences', function () {
it('returns sequence linestrings in the visible map area', function () {
var features = [
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 0 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10,0], ca: 90, sequence_id: '100', sequence_index: 1 } },
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10,1], ca: 90, sequence_id: '100', sequence_index: 2 } }
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '0', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 0 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: '1', loc: [10, 0], ca: 90, sequence_id: '100', sequence_index: 1 } },
{ minX: 10, minY: 1, maxX: 10, maxY: 1, data: { key: '2', loc: [10, 1], ca: 90, sequence_id: '100', sequence_index: 2 } }
];
openstreetcam.cache().images.rtree.load(features);
openstreetcam.cache().sequences['100'] = { rotation: 0, images: [ features[0].data, features[1].data, features[2].data ] };
openstreetcam.cache().sequences['100'] = { rotation: 0, images: [features[0].data, features[1].data, features[2].data] };
var res = openstreetcam.sequences(context.projection);
expect(res).to.deep.eql([{
type: 'LineString',
coordinates: [[10,0], [10,0], [10,1]],
coordinates: [[10, 0], [10, 0], [10, 1]],
properties: {
captured_at: undefined,
captured_by: undefined,
@@ -262,10 +270,10 @@ describe('iD.serviceOpenstreetcam', function() {
});
});
describe('#selectedImage', function() {
it('sets and gets selected image', function() {
describe('#selectedImage', function () {
it('sets and gets selected image', function () {
var d = { key: 'foo' };
openstreetcam.cache().images = { forImageKey: { foo: d }};
openstreetcam.cache().images = { forImageKey: { foo: d } };
openstreetcam.selectImage(context, 'foo');
expect(openstreetcam.getSelectedImage()).to.eql(d);
});

View File

@@ -1,33 +1,33 @@
describe('iD.serviceStreetside', function() {
describe('iD.serviceStreetside', function () {
var dimensions = [64, 64];
var context, streetside;
before(function() {
before(function () {
iD.services.streetside = iD.serviceStreetside;
});
after(function() {
after(function () {
delete iD.services.streetside;
});
beforeEach(function() {
beforeEach(function () {
context = iD.coreContext().assetPath('../dist/').init();
context.projection
.scale(iD.geoZoomToScale(14))
.translate([-116508, 0]) // 10,0
.clipExtent([[0,0], dimensions]);
.clipExtent([[0, 0], dimensions]);
streetside = iD.services.streetside;
streetside.reset();
});
afterEach(function() {
afterEach(function () {
window.JSONP_FIX = undefined;
});
describe('#init', function() {
it('Initializes cache one time', function() {
describe('#init', function () {
it('Initializes cache one time', function () {
var cache = streetside.cache();
expect(cache).to.have.property('bubbles');
expect(cache).to.have.property('sequences');
@@ -38,82 +38,82 @@ describe('iD.serviceStreetside', function() {
});
});
describe('#reset', function() {
it('resets cache', function() {
describe('#reset', function () {
it('resets cache', function () {
streetside.cache().foo = 'bar';
streetside.reset();
expect(streetside.cache()).to.not.have.property('foo');
});
});
describe('#loadBubbles', function() {
it('fires loadedImages when bubbles are loaded', function(done) {
describe('#loadBubbles', function () {
it('fires loadedImages when bubbles are loaded', function (done) {
// adjust projection so that only one tile is fetched
// (JSONP hack will return the same data for every fetch)
context.projection
.scale(iD.geoZoomToScale(18))
.translate([-1863988.9381333336, 762.8270222954452]) // 10.002,0.002
.clipExtent([[0,0], dimensions]);
.clipExtent([[0, 0], dimensions]);
var spy = sinon.spy();
streetside.on('loadedImages', spy);
window.JSONP_DELAY = 0;
window.JSONP_FIX = [{
elapsed: 0.001
}, {
id: 1, la: 0, lo: 10.001, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:00 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: undefined, ne: 2
}, {
id: 2, la: 0, lo: 10.002, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:01 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: 1, ne: 3
}, {
id: 3, la: 0, lo: 10.003, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:02 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: 2, ne: undefined
}
elapsed: 0.001
}, {
id: 1, la: 0, lo: 10.001, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:00 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: undefined, ne: 2
}, {
id: 2, la: 0, lo: 10.002, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:01 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: 1, ne: 3
}, {
id: 3, la: 0, lo: 10.003, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:02 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: 2, ne: undefined
}
];
streetside.loadBubbles(context.projection, 0); // 0 = don't fetch margin tiles
window.setTimeout(function() {
window.setTimeout(function () {
expect(spy).to.have.been.calledOnce;
done();
}, 200);
});
it('does not load bubbles around null island', function(done) {
it('does not load bubbles around null island', function (done) {
context.projection
.scale(iD.geoZoomToScale(18))
.translate([0, 0])
.clipExtent([[0,0], dimensions]);
.clipExtent([[0, 0], dimensions]);
var spy = sinon.spy();
streetside.on('loadedImages', spy);
window.JSONP_DELAY = 0;
window.JSONP_FIX = [{
elapsed: 0.001
}, {
id: 1, la: 0, lo: 0, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:00 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: undefined, ne: 2
}, {
id: 2, la: 0, lo: 0, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:01 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: 1, ne: 3
}, {
id: 3, la: 0, lo: 0, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:02 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: 2, ne: undefined
}
elapsed: 0.001
}, {
id: 1, la: 0, lo: 0, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:00 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: undefined, ne: 2
}, {
id: 2, la: 0, lo: 0, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:01 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: 1, ne: 3
}, {
id: 3, la: 0, lo: 0, al: 0, ro: 0, pi: 0, he: 0, bl: '',
cd: '1/1/2018 12:00:02 PM', ml: 3, nbn: [], pbn: [], rn: [],
pr: 2, ne: undefined
}
];
streetside.loadBubbles(context.projection, 0); // 0 = don't fetch margin tiles
window.setTimeout(function() {
window.setTimeout(function () {
expect(spy).to.have.been.not.called;
done();
}, 200);
@@ -121,8 +121,8 @@ describe('iD.serviceStreetside', function() {
});
describe('#bubbles', function() {
it('returns bubbles in the visible map area', function() {
describe('#bubbles', function () {
it('returns bubbles in the visible map area', function () {
var features = [
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 1, loc: [10, 0], ca: 90, pr: undefined, ne: 2, pano: true, sequenceKey: 1 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 2, loc: [10, 0], ca: 90, pr: 1, ne: 3, pano: true, sequenceKey: 1 } },
@@ -138,7 +138,7 @@ describe('iD.serviceStreetside', function() {
]);
});
it('limits results no more than 5 stacked bubbles in one spot', function() {
it('limits results no more than 5 stacked bubbles in one spot', function () {
var features = [
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 1, loc: [10, 0], ca: 90, pr: undefined, ne: 2, pano: true, sequence_id: 1 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 2, loc: [10, 0], ca: 90, pr: 1, ne: 3, pano: true, sequence_id: 1 } },
@@ -155,8 +155,8 @@ describe('iD.serviceStreetside', function() {
});
describe('#sequences', function() {
it('returns sequence linestrings in the visible map area', function() {
describe('#sequences', function () {
it('returns sequence linestrings in the visible map area', function () {
var features = [
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 1, loc: [10, 0], ca: 90, pr: undefined, ne: 2, pano: true, sequenceKey: 1 } },
{ minX: 10, minY: 0, maxX: 10, maxY: 0, data: { key: 2, loc: [10, 0], ca: 90, pr: 1, ne: 3, pano: true, sequenceKey: 1 } },
@@ -167,11 +167,11 @@ describe('iD.serviceStreetside', function() {
var seq = {
key: 1,
bubbles: features.map(function(f) { return f.data; }),
bubbles: features.map(function (f) { return f.data; }),
geojson: {
type: 'LineString',
properties: { key: 1 },
coordinates: features.map(function(f) { return f.data.loc; }),
coordinates: features.map(function (f) { return f.data.loc; }),
}
};

View File

@@ -1,23 +1,23 @@
describe('iD.uiFieldWikipedia', function() {
var entity, context, selection, field, server;
describe('iD.uiFieldWikipedia', function () {
var entity, context, selection, field;
before(function() {
before(function () {
iD.fileFetcher.cache().wmf_sitematrix = [
['German','Deutsch','de'],
['English','English','en']
['German', 'Deutsch', 'de'],
['English', 'English', 'en']
];
iD.services.wikipedia = iD.serviceWikipedia;
iD.services.wikidata = iD.serviceWikidata;
});
after(function() {
after(function () {
delete iD.fileFetcher.cache().wmf_sitematrix;
delete iD.services.wikipedia;
delete iD.services.wikidata;
});
beforeEach(function() {
entity = iD.osmNode({id: 'n12345'});
beforeEach(function () {
entity = iD.osmNode({ id: 'n12345' });
context = iD.coreContext().assetPath('../dist/').init();
context.history().merge([entity]);
selection = d3.select(document.createElement('div'));
@@ -26,11 +26,16 @@ describe('iD.uiFieldWikipedia', function() {
keys: ['wikipedia', 'wikidata'],
type: 'wikipedia'
});
server = createServer({ respondImmediately: true });
fetchMock.reset();
fetchMock.mock(new RegExp('\/w\/api\.php.*action=wbgetentities'), {
body: '{"entities":{"Q216353":{"id":"Q216353"}}}',
status: 200,
headers: { 'Content-Type': 'application/json' }
});
});
afterEach(function() {
server.restore();
afterEach(function () {
fetchMock.reset();
});
@@ -55,24 +60,11 @@ describe('iD.uiFieldWikipedia', function() {
}
}
function createServer(options) { // eslint-disable-line no-unused-vars
// note - currently skipping the tests that use `options` to delay responses
// var server = sinon.fakeServer.create(options);
var server = window.fakeFetch().create();
server.respondWith('GET',
new RegExp('\/w\/api\.php.*action=wbgetentities'),
[200, { 'Content-Type': 'application/json' },
'{"entities":{"Q216353":{"id":"Q216353"}}}']
);
return server;
}
it('recognizes lang:title format', function(done) {
it('recognizes lang:title format', function (done) {
var wikipedia = iD.uiFieldWikipedia(field, context);
window.setTimeout(function() { // async, so data will be available
window.setTimeout(function () { // async, so data will be available
selection.call(wikipedia);
wikipedia.tags({wikipedia: 'en:Title'});
wikipedia.tags({ wikipedia: 'en:Title' });
expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('English');
expect(iD.utilGetSetValue(selection.selectAll('.wiki-title'))).to.equal('Title');
@@ -80,9 +72,9 @@ describe('iD.uiFieldWikipedia', function() {
}, 20);
});
it('sets language, value', function(done) {
it('sets language, value', function (done) {
var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]);
window.setTimeout(function() { // async, so data will be available
window.setTimeout(function () { // async, so data will be available
wikipedia.on('change', changeTags);
selection.call(wikipedia);
@@ -98,17 +90,17 @@ describe('iD.uiFieldWikipedia', function() {
happen.once(selection.selectAll('.wiki-title').node(), { type: 'blur' });
expect(spy.callCount).to.equal(4);
expect(spy.getCall(0)).to.have.been.calledWith({ wikipedia: undefined}); // lang on change
expect(spy.getCall(1)).to.have.been.calledWith({ wikipedia: undefined}); // lang on blur
expect(spy.getCall(0)).to.have.been.calledWith({ wikipedia: undefined }); // lang on change
expect(spy.getCall(1)).to.have.been.calledWith({ wikipedia: undefined }); // lang on blur
expect(spy.getCall(2)).to.have.been.calledWith({ wikipedia: 'de:Title' }); // title on change
expect(spy.getCall(3)).to.have.been.calledWith({ wikipedia: 'de:Title' }); // title on blur
done();
}, 20);
});
it('recognizes pasted URLs', function(done) {
it('recognizes pasted URLs', function (done) {
var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]);
window.setTimeout(function() { // async, so data will be available
window.setTimeout(function () { // async, so data will be available
wikipedia.on('change', changeTags);
selection.call(wikipedia);
@@ -121,14 +113,15 @@ describe('iD.uiFieldWikipedia', function() {
}, 20);
});
it('preserves existing language', function(done) {
// note - currently skipping the tests that use `options` to delay responses
it('preserves existing language', function (done) {
var wikipedia1 = iD.uiFieldWikipedia(field, context);
window.setTimeout(function() { // async, so data will be available
window.setTimeout(function () { // async, so data will be available
selection.call(wikipedia1);
iD.utilGetSetValue(selection.selectAll('.wiki-lang'), 'Deutsch');
var wikipedia2 = iD.uiFieldWikipedia(field, context);
window.setTimeout(function() { // async, so data will be available
window.setTimeout(function () { // async, so data will be available
selection.call(wikipedia2);
wikipedia2.tags({});
expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('Deutsch');
@@ -137,7 +130,7 @@ describe('iD.uiFieldWikipedia', function() {
}, 20);
});
it.skip('does not set delayed wikidata tag if graph has changed', function(done) {
it.skip('does not set delayed wikidata tag if graph has changed', function (done) {
var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]);
wikipedia.on('change', changeTags);
selection.call(wikipedia);
@@ -146,7 +139,14 @@ describe('iD.uiFieldWikipedia', function() {
wikipedia.on('change.spy', spy);
// Create an XHR server that will respond after 60ms
createServer({ autoRespond: true, autoRespondAfter: 60 });
fetchMock.reset();
fetchMock.mock(new RegExp('\/w\/api\.php.*action=wbgetentities'), {
body: '{"entities":{"Q216353":{"id":"Q216353"}}}',
status: 200,
headers: { 'Content-Type': 'application/json' }
}, {
delay: 60
});
// Set title to "Skip"
iD.utilGetSetValue(selection.selectAll('.wiki-lang'), 'Deutsch');
@@ -159,10 +159,17 @@ describe('iD.uiFieldWikipedia', function() {
// Create a new XHR server that will respond after 60ms to
// separate requests after this point from those before
createServer({ autoRespond: true, autoRespondAfter: 60 });
fetchMock.reset();
fetchMock.mock(new RegExp('\/w\/api\.php.*action=wbgetentities'), {
body: '{"entities":{"Q216353":{"id":"Q216353"}}}',
status: 200,
headers: { 'Content-Type': 'application/json' }
}, {
delay: 60
});
// t30: graph change - Set title to "Title"
window.setTimeout(function() {
window.setTimeout(function () {
iD.utilGetSetValue(selection.selectAll('.wiki-title'), 'Title');
happen.once(selection.selectAll('.wiki-title').node(), { type: 'change' });
happen.once(selection.selectAll('.wiki-title').node(), { type: 'blur' });
@@ -171,14 +178,14 @@ describe('iD.uiFieldWikipedia', function() {
// t60: at t0 + 60ms (delay), wikidata SHOULD NOT be set because graph has changed.
// t70: check that wikidata unchanged
window.setTimeout(function() {
window.setTimeout(function () {
expect(context.entity(entity.id).tags.wikidata).to.be.undefined;
}, 70);
// t90: at t30 + 60ms (delay), wikidata SHOULD be set because graph is unchanged.
// t100: check that wikidata has changed
window.setTimeout(function() {
window.setTimeout(function () {
expect(context.entity(entity.id).tags.wikidata).to.equal('Q216353');
expect(spy.callCount).to.equal(4);