mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 21:48:20 +02:00
adding tests and updating/fixing external presets logic
This commit is contained in:
+22
-25
@@ -441,28 +441,6 @@ export function coreContext() {
|
||||
locale = locale.split('-')[0];
|
||||
}
|
||||
|
||||
/* Presets */
|
||||
presets = presetIndex();
|
||||
if (utilStringQs(window.location.hash).presets) {
|
||||
presets.fromExternal();
|
||||
} else {
|
||||
presets.init();
|
||||
}
|
||||
|
||||
context.presets = function() { return presets; };
|
||||
|
||||
if (utilStringQs(window.location.hash).validations) {
|
||||
var validationsUrl = utilStringQs(window.location.hash).validations;
|
||||
d3_json(validationsUrl, function (err, mapcss) {
|
||||
if (err) return;
|
||||
services.maprules.init(context.presets().areaKeys());
|
||||
_each(mapcss, function(mapcssSelector) {
|
||||
return services.maprules.addRule(mapcssSelector);
|
||||
});
|
||||
context.validationRules = true;
|
||||
});
|
||||
}
|
||||
|
||||
history = coreHistory(context);
|
||||
context.graph = history.graph;
|
||||
context.changes = history.changes;
|
||||
@@ -492,6 +470,18 @@ export function coreContext() {
|
||||
background = rendererBackground(context);
|
||||
features = rendererFeatures(context);
|
||||
presets = presetIndex();
|
||||
|
||||
if (utilStringQs(window.location.hash).validations) {
|
||||
var validationsUrl = utilStringQs(window.location.hash).validations;
|
||||
d3_json(validationsUrl, function (err, mapcss) {
|
||||
if (err) return;
|
||||
services.maprules.init(context.presets().areaKeys());
|
||||
_each(mapcss, function(mapcssSelector) {
|
||||
return services.maprules.addRule(mapcssSelector);
|
||||
});
|
||||
context.validationRules = true;
|
||||
});
|
||||
}
|
||||
|
||||
map = rendererMap(context);
|
||||
context.mouse = map.mouse;
|
||||
@@ -511,9 +501,16 @@ export function coreContext() {
|
||||
|
||||
background.init();
|
||||
features.init();
|
||||
presets.init();
|
||||
areaKeys = presets.areaKeys();
|
||||
|
||||
if (utilStringQs(window.location.hash).presets) {
|
||||
var external = utilStringQs(window.location.hash).presets;
|
||||
presets.fromExternal(external, function(externalPresets){
|
||||
presets = externalPresets; // default + external presets...
|
||||
areaKeys = presets.areaKeys();
|
||||
});
|
||||
} else {
|
||||
presets.init();
|
||||
areaKeys = presets.areaKeys();
|
||||
}
|
||||
|
||||
return utilRebind(context, dispatch, 'on');
|
||||
}
|
||||
|
||||
@@ -123,7 +123,7 @@ export function presetIndex() {
|
||||
return areaKeys;
|
||||
};
|
||||
|
||||
all.build = function(d, visibility) {
|
||||
all.build = function(d, visible) {
|
||||
if (d.fields) {
|
||||
_forEach(d.fields, function(d, id) {
|
||||
_fields[id] = presetField(id, d);
|
||||
@@ -139,7 +139,7 @@ export function presetIndex() {
|
||||
|
||||
if (d.presets) {
|
||||
_forEach(d.presets, function(d, id) {
|
||||
all.collection.push(presetPreset(id, d, _fields, visibility));
|
||||
all.collection.push(presetPreset(id, d, _fields, visible));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -171,6 +171,7 @@ export function presetIndex() {
|
||||
}
|
||||
}
|
||||
}
|
||||
return all;
|
||||
};
|
||||
|
||||
all.init = function() {
|
||||
@@ -180,22 +181,40 @@ export function presetIndex() {
|
||||
_universal = [];
|
||||
_index = { point: {}, vertex: {}, line: {}, area: {}, relation: {} };
|
||||
|
||||
all.build(data.presets);
|
||||
return all.build(data.presets);
|
||||
};
|
||||
|
||||
|
||||
all.reset = function() {
|
||||
all.collection = [];
|
||||
_defaults = { area: all, line: all, point: all, vertex: all, relation: all };
|
||||
_fields = {};
|
||||
_universal = [];
|
||||
_recent = presetCollection([]);
|
||||
|
||||
// Index of presets by (geometry, tag key).
|
||||
_index = {
|
||||
point: {},
|
||||
vertex: {},
|
||||
line: {},
|
||||
area: {},
|
||||
relation: {}
|
||||
};
|
||||
|
||||
return all;
|
||||
};
|
||||
|
||||
all.fromExternal = function() {
|
||||
var external = utilQsString(window.location.hash).presets;
|
||||
d3_json(external, function(err, presets) {
|
||||
all.fromExternal = function(external, done) {
|
||||
all.reset();
|
||||
d3_json(external, function(err, externalPresets) {
|
||||
if (err) {
|
||||
all.init();
|
||||
} else {
|
||||
all.build(data.presets, false); // make default presets hidden to begin
|
||||
all.build(presets, true); // make the external visible
|
||||
all.build(externalPresets, true); // make the external visible
|
||||
}
|
||||
done(all);
|
||||
});
|
||||
return all;
|
||||
};
|
||||
|
||||
all.field = function(id) {
|
||||
|
||||
+247
-32
@@ -1,5 +1,5 @@
|
||||
describe('iD.presetIndex', function() {
|
||||
var savedPresets;
|
||||
describe('iD.presetIndex', function () {
|
||||
var savedPresets, server;
|
||||
|
||||
before(function () {
|
||||
savedPresets = iD.data.presets;
|
||||
@@ -9,7 +9,7 @@ describe('iD.presetIndex', function() {
|
||||
iD.data.presets = savedPresets;
|
||||
});
|
||||
|
||||
describe('#match', function() {
|
||||
describe('#match', function () {
|
||||
var testPresets = {
|
||||
presets: {
|
||||
point: {
|
||||
@@ -35,7 +35,7 @@ describe('iD.presetIndex', function() {
|
||||
}
|
||||
};
|
||||
|
||||
it('returns a collection containing presets matching a geometry and tags', function() {
|
||||
it('returns a collection containing presets matching a geometry and tags', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets(),
|
||||
way = iD.Way({ tags: { highway: 'residential' } }),
|
||||
@@ -44,7 +44,7 @@ describe('iD.presetIndex', function() {
|
||||
expect(presets.match(way, graph).id).to.eql('residential');
|
||||
});
|
||||
|
||||
it('returns the appropriate fallback preset when no tags match', function() {
|
||||
it('returns the appropriate fallback preset when no tags match', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets(),
|
||||
point = iD.Node(),
|
||||
@@ -55,7 +55,7 @@ describe('iD.presetIndex', function() {
|
||||
expect(presets.match(line, graph).id).to.eql('line');
|
||||
});
|
||||
|
||||
it('matches vertices on a line as vertices', function() {
|
||||
it('matches vertices on a line as vertices', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets(),
|
||||
point = iD.Node({ tags: { leisure: 'park' } }),
|
||||
@@ -65,7 +65,7 @@ describe('iD.presetIndex', function() {
|
||||
expect(presets.match(point, graph).id).to.eql('vertex');
|
||||
});
|
||||
|
||||
it('matches vertices on an addr:interpolation line as points', function() {
|
||||
it('matches vertices on an addr:interpolation line as points', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets(),
|
||||
point = iD.Node({ tags: { leisure: 'park' } }),
|
||||
@@ -77,12 +77,12 @@ describe('iD.presetIndex', function() {
|
||||
});
|
||||
|
||||
|
||||
describe('#areaKeys', function() {
|
||||
describe('#areaKeys', function () {
|
||||
var testPresets = {
|
||||
presets: {
|
||||
'amenity/fuel/shell': {
|
||||
tags: { 'amenity': 'fuel' },
|
||||
geometry: ['point','area'],
|
||||
geometry: ['point', 'area'],
|
||||
suggestion: true
|
||||
},
|
||||
'highway/foo': {
|
||||
@@ -113,44 +113,44 @@ describe('iD.presetIndex', function() {
|
||||
|
||||
};
|
||||
|
||||
it('whitelists keys for presets with area geometry', function() {
|
||||
it('whitelists keys for presets with area geometry', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets();
|
||||
expect(presets.areaKeys()).to.include.keys('natural');
|
||||
});
|
||||
|
||||
it('blacklists key-values for presets with a line geometry', function() {
|
||||
it('blacklists key-values for presets with a line geometry', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets();
|
||||
expect(presets.areaKeys().natural).to.include.keys('tree_row');
|
||||
expect(presets.areaKeys().natural.tree_row).to.be.true;
|
||||
});
|
||||
|
||||
it('blacklists key-values for presets with both area and line geometry', function() {
|
||||
it('blacklists key-values for presets with both area and line geometry', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets();
|
||||
expect(presets.areaKeys().leisure).to.include.keys('track');
|
||||
});
|
||||
|
||||
it('does not blacklist key-values for presets with neither area nor line geometry', function() {
|
||||
it('does not blacklist key-values for presets with neither area nor line geometry', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets();
|
||||
expect(presets.areaKeys().natural).not.to.include.keys('peak');
|
||||
});
|
||||
|
||||
it('does not blacklist generic \'*\' key-values', function() {
|
||||
it('does not blacklist generic \'*\' key-values', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets();
|
||||
expect(presets.areaKeys().natural).not.to.include.keys('natural');
|
||||
});
|
||||
|
||||
it('ignores keys like \'highway\' that are assumed to be lines', function() {
|
||||
it('ignores keys like \'highway\' that are assumed to be lines', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets();
|
||||
expect(presets.areaKeys()).not.to.include.keys('highway');
|
||||
});
|
||||
|
||||
it('ignores suggestion presets', function() {
|
||||
it('ignores suggestion presets', function () {
|
||||
iD.data.presets = testPresets;
|
||||
var presets = iD.Context().presets();
|
||||
expect(presets.areaKeys()).not.to.include.keys('amenity');
|
||||
@@ -158,8 +158,8 @@ describe('iD.presetIndex', function() {
|
||||
});
|
||||
|
||||
describe('#build', function () {
|
||||
it('builds presets from provided', function() {
|
||||
var surfShop = iD.Node({ tags: { amenity: 'shop', 'shop:type': 'surf' }}),
|
||||
it('builds presets from provided', function () {
|
||||
var surfShop = iD.Node({ tags: { amenity: 'shop', 'shop:type': 'surf' } }),
|
||||
graph = iD.Graph([surfShop]),
|
||||
presets = iD.Context().presets(),
|
||||
morePresets = {
|
||||
@@ -173,11 +173,11 @@ describe('iD.presetIndex', function() {
|
||||
|
||||
expect(presets.match(surfShop, graph)).to.eql(undefined); // no surfshop preset yet...
|
||||
presets.build(morePresets, true);
|
||||
expect(presets.match(surfShop, graph).addTags).to.eql({amenity: 'shop', 'shop:type': 'surf' });
|
||||
expect(presets.match(surfShop, graph).addTags).to.eql({ amenity: 'shop', 'shop:type': 'surf' });
|
||||
});
|
||||
it('configures presets\' initial visibility', function() {
|
||||
var surfShop = iD.Node({ tags: { amenity: 'shop', 'shop:type': 'surf' }}),
|
||||
firstStreetJetty = iD.Node({ tags: { man_made: 'jetty' }}),
|
||||
it('configures presets\' initial visibility', function () {
|
||||
var surfShop = iD.Node({ tags: { amenity: 'shop', 'shop:type': 'surf' } }),
|
||||
firstStreetJetty = iD.Node({ tags: { man_made: 'jetty' } }),
|
||||
entities = [surfShop, firstStreetJetty],
|
||||
graph = iD.Graph(entities),
|
||||
presets = iD.Context().presets(),
|
||||
@@ -189,44 +189,259 @@ describe('iD.presetIndex', function() {
|
||||
},
|
||||
'man_made/jetty': {
|
||||
tags: { man_made: 'jetty' },
|
||||
geometry: [ 'point' ]
|
||||
geometry: ['point']
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
presets.build(morePresets, false);
|
||||
entities.forEach(function(entity) {
|
||||
entities.forEach(function (entity) {
|
||||
var preset = presets.match(entity, graph);
|
||||
expect(preset.visible()).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('expected matches', function() {
|
||||
|
||||
it('prefers building to multipolygon', function() {
|
||||
describe('expected matches', function () {
|
||||
|
||||
it('prefers building to multipolygon', function () {
|
||||
iD.data.presets = savedPresets;
|
||||
var presets = iD.Context().presets(),
|
||||
relation = iD.Relation({ tags: { type: 'multipolygon', building: 'yes' }}),
|
||||
relation = iD.Relation({ tags: { type: 'multipolygon', building: 'yes' } }),
|
||||
graph = iD.Graph([relation]);
|
||||
expect(presets.match(relation, graph).id).to.eql('building');
|
||||
});
|
||||
|
||||
it('prefers building to address', function() {
|
||||
it('prefers building to address', function () {
|
||||
iD.data.presets = savedPresets;
|
||||
var presets = iD.Context().presets(),
|
||||
way = iD.Way({ tags: { area: 'yes', building: 'yes', 'addr:housenumber': '1234' }}),
|
||||
way = iD.Way({ tags: { area: 'yes', building: 'yes', 'addr:housenumber': '1234' } }),
|
||||
graph = iD.Graph([way]);
|
||||
expect(presets.match(way, graph).id).to.eql('building');
|
||||
});
|
||||
|
||||
it('prefers pedestrian to area', function() {
|
||||
it('prefers pedestrian to area', function () {
|
||||
iD.data.presets = savedPresets;
|
||||
var presets = iD.Context().presets(),
|
||||
way = iD.Way({ tags: { area: 'yes', highway: 'pedestrian' }}),
|
||||
way = iD.Way({ tags: { area: 'yes', highway: 'pedestrian' } }),
|
||||
graph = iD.Graph([way]);
|
||||
expect(presets.match(way, graph).id).to.eql('highway/pedestrian_area');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#fromExternal', function () {
|
||||
var morePresets;
|
||||
before(function () {
|
||||
morePresets = {
|
||||
'categories': {
|
||||
'category-area': {
|
||||
'icon': 'maki-natural',
|
||||
'geometry': 'area',
|
||||
'name': 'MapRules area Features',
|
||||
'members': [
|
||||
'8bc64d6d-1dbb-44a8-a2f9-80d41d067d78',
|
||||
'a9b78746-ca8a-4380-b340-157414f1464d'
|
||||
]
|
||||
},
|
||||
'category-point': {
|
||||
'icon': 'maki-natural',
|
||||
'geometry': 'point',
|
||||
'name': 'MapRules point Features',
|
||||
'members': [
|
||||
'8bc64d6d-1dbb-44a8-a2f9-80d41d067d78',
|
||||
'8f83ed0b-6514-4772-a644-f04aad9d2308'
|
||||
]
|
||||
}
|
||||
},
|
||||
'presets': {
|
||||
'8bc64d6d-1dbb-44a8-a2f9-80d41d067d78': {
|
||||
'geometry': ['area', 'point'],
|
||||
'tags': { 'amenity': 'shop', 'shop:type': 'surf' },
|
||||
'icon': 'maki-natural',
|
||||
'name': 'Surf Shop',
|
||||
'fields': ['358f404a-c7d5-4267-94ed-41f789b16228'],
|
||||
'matchScore': 0.99
|
||||
},
|
||||
'a9b78746-ca8a-4380-b340-157414f1464d': {
|
||||
'geometry': ['area'],
|
||||
'tags': { 'amenity': 'marketplace' },
|
||||
'icon': 'maki-natural',
|
||||
'name': 'Market',
|
||||
'fields': [
|
||||
'name',
|
||||
'source',
|
||||
'2161a712-f67f-4759-92fa-f5d9488ba969',
|
||||
'368ecbdf-bc02-4de2-a82e-d51c250602da',
|
||||
'1887834c-0cdd-4d40-852b-d29b8df94567'
|
||||
],
|
||||
'matchScore': 0.99
|
||||
},
|
||||
'8f83ed0b-6514-4772-a644-f04aad9d2308': {
|
||||
'geometry': ['point'],
|
||||
'tags': {
|
||||
'amenity': 'drinking_water',
|
||||
'man_made': 'water_tap'
|
||||
},
|
||||
'icon': 'maki-natural',
|
||||
'name': 'Water Tap',
|
||||
'fields': ['name'],
|
||||
'matchScore': 0.99
|
||||
}
|
||||
},
|
||||
'fields': {
|
||||
'358f404a-c7d5-4267-94ed-41f789b16228': {
|
||||
'key': 'healthcare',
|
||||
'label': 'Healthcare',
|
||||
'overrideLabel': 'Healthcare',
|
||||
'placeholder': '...',
|
||||
'type': 'text'
|
||||
},
|
||||
'name': {
|
||||
'key': 'name',
|
||||
'type': 'localized',
|
||||
'label': 'Name',
|
||||
'universal': true,
|
||||
'placeholder': 'Common name (if any)'
|
||||
},
|
||||
'source': {
|
||||
'key': 'source',
|
||||
'type': 'semiCombo',
|
||||
'icon': 'source',
|
||||
'universal': true,
|
||||
'label': 'Sources',
|
||||
'snake_case': false,
|
||||
'caseSensitive': true,
|
||||
'options': [
|
||||
'survey',
|
||||
'local knowledge',
|
||||
'gps',
|
||||
'aerial imagery',
|
||||
'streetlevel imagery'
|
||||
]
|
||||
},
|
||||
'2161a712-f67f-4759-92fa-f5d9488ba969': {
|
||||
'key': 'building',
|
||||
'label': 'Building',
|
||||
'overrideLabel': 'Building',
|
||||
'placeholder': '...',
|
||||
'type': 'text'
|
||||
},
|
||||
'368ecbdf-bc02-4de2-a82e-d51c250602da': {
|
||||
'key': 'opening_hours',
|
||||
'label': 'Opening Hours',
|
||||
'overrideLabel': 'Opening Hours',
|
||||
'placeholder': '24/7, sunrise to sunset...',
|
||||
'strings': {
|
||||
'options': {
|
||||
'24/7': '24/7',
|
||||
'sunrise to sunset': 'sunrise to sunset'
|
||||
}
|
||||
},
|
||||
'type': 'combo'
|
||||
},
|
||||
'1887834c-0cdd-4d40-852b-d29b8df94567': {
|
||||
'key': 'height',
|
||||
'label': 'Height',
|
||||
'overrideLabel': 'Height',
|
||||
'placeholder': '...',
|
||||
'minValue': 1, 'type': 'number'
|
||||
},
|
||||
'relation': {
|
||||
'key': 'type',
|
||||
'type': 'combo',
|
||||
'label': 'Type'
|
||||
},
|
||||
'comment': {
|
||||
'key': 'comment',
|
||||
'type': 'textarea',
|
||||
'label': 'Changeset Comment',
|
||||
'placeholder': 'Brief description of your contributions (required)'
|
||||
},
|
||||
'hashtags': {
|
||||
'key': 'hashtags',
|
||||
'type': 'semiCombo',
|
||||
'label': 'Suggested Hashtags',
|
||||
'placeholder': '#example'
|
||||
}
|
||||
},
|
||||
'defaults': {
|
||||
'point': [
|
||||
'point',
|
||||
'8bc64d6d-1dbb-44a8-a2f9-80d41d067d78',
|
||||
'8f83ed0b-6514-4772-a644-f04aad9d2308'
|
||||
],
|
||||
'line': ['line'],
|
||||
'area': [
|
||||
'area',
|
||||
'8bc64d6d-1dbb-44a8-a2f9-80d41d067d78',
|
||||
'a9b78746-ca8a-4380-b340-157414f1464d'
|
||||
],
|
||||
'vertex': ['vertex'],
|
||||
'relation': ['relation']
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
beforeEach(function () {
|
||||
server = sinon.fakeServer.create();
|
||||
});
|
||||
afterEach(function () {
|
||||
server.restore();
|
||||
});
|
||||
it('builds presets w/external sources set to visible', function () {
|
||||
var surfShop = iD.Node({ tags: { amenity: 'shop', 'shop:type': 'surf' } }),
|
||||
graph = iD.Graph([surfShop]),
|
||||
maprules = 'https://fakemaprules.io',
|
||||
presetLocation = '/config/dfcfac13-ba7c-4223-8880-c856180e5c5b/presets/iD/',
|
||||
match = new RegExp(presetLocation),
|
||||
external = maprules + presetLocation;
|
||||
|
||||
// no exernal presets yet
|
||||
expect(iD.Context().presets().match(surfShop, graph).id).to.eql('amenity');
|
||||
// reset graph...
|
||||
graph = iD.Graph([surfShop]);
|
||||
|
||||
// add the validations query param...
|
||||
iD.Context().presets().fromExternal(external, function (externalPresets) {
|
||||
// includes newer presets...
|
||||
expect(externalPresets.match(surfShop, graph).id).to.eql('8bc64d6d-1dbb-44a8-a2f9-80d41d067d78');
|
||||
});
|
||||
|
||||
server.respondWith('GET', match,
|
||||
[200, { 'Content-Type': 'application/json' }, JSON.stringify(morePresets)]
|
||||
);
|
||||
server.respond();
|
||||
});
|
||||
it('makes only the external presets initially visible', function () {
|
||||
var maprules = 'https://fakemaprules.io',
|
||||
presetLocation = '/config/dfcfac13-ba7c-4223-8880-c856180e5c5b/presets/iD/',
|
||||
match = new RegExp(presetLocation),
|
||||
external = maprules + presetLocation;
|
||||
|
||||
iD.Context().presets().fromExternal(external, function(externalPresets) {
|
||||
var external = externalPresets.collection.reduce(function(presets, preset) {
|
||||
if (!preset.hasOwnProperty('members') && preset.visible()) {
|
||||
presets.push(preset.id);
|
||||
}
|
||||
return presets;
|
||||
}, []);
|
||||
|
||||
var morePresetKeys = Object.keys(morePresets.presets);
|
||||
|
||||
expect(morePresetKeys.length).to.eql(external.length);
|
||||
|
||||
morePresetKeys.forEach(function(presetId) {
|
||||
expect(external.indexOf(presetId)).to.be.at.least(0);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
server.respondWith('GET', match,
|
||||
[200, { 'Content-Type': 'application/json' }, JSON.stringify(morePresets)]
|
||||
);
|
||||
server.respond();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user