adding tests and updating/fixing external presets logic

This commit is contained in:
Max Grossman
2018-12-14 13:23:58 -05:00
parent c897331cd1
commit 1b51b8bc57
3 changed files with 296 additions and 65 deletions
+22 -25
View File
@@ -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');
}
+27 -8
View File
@@ -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
View File
@@ -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();
});
});
});