mirror of
https://github.com/FoggedLens/iD.git
synced 2026-05-15 05:30:35 +02:00
@@ -43,6 +43,7 @@ _Breaking developer changes, which may affect downstream projects or sites that
|
||||
#### :camera: Street-Level
|
||||
* Add prev/next button to viewer for local georeferenced photos ([#10852], thanks [@0xatulpatil])
|
||||
#### :white_check_mark: Validation
|
||||
* The Suspicious Names validator warning now also compares the Name field to the preset’s aliases (in addition to the preset’s name) in the user’s language
|
||||
#### :bug: Bugfixes
|
||||
* Fix some direction cones not appearing on railway tracks ([#10843], thanks [@k-yle])
|
||||
* Better handling of rate limited API calls and other API errors ([#10299])
|
||||
|
||||
@@ -45,17 +45,18 @@ export function validationSuspiciousName(context) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @param {string} name @param {string} presetName */
|
||||
function nameMatchesPresetName(name, presetName) {
|
||||
if (!presetName) return false;
|
||||
/** @param {string} name */
|
||||
function nameMatchesPresetName(name, preset) {
|
||||
if (!preset) return false;
|
||||
|
||||
return name.toLowerCase() === presetName.toLowerCase();
|
||||
name = name.toLowerCase();
|
||||
return name === preset.name().toLowerCase() || preset.aliases().some(alias => name === alias.toLowerCase());
|
||||
}
|
||||
|
||||
/** @param {string} name @param {string} presetName */
|
||||
function isGenericName(name, tags, presetName) {
|
||||
/** @param {string} name */
|
||||
function isGenericName(name, tags, preset) {
|
||||
name = name.toLowerCase();
|
||||
return nameMatchesRawTag(name, tags) || nameMatchesPresetName(name, presetName) || isGenericMatchInNsi(tags);
|
||||
return nameMatchesRawTag(name, tags) || nameMatchesPresetName(name, preset) || isGenericMatchInNsi(tags);
|
||||
}
|
||||
|
||||
function makeGenericNameIssue(entityId, nameKey, genericName, langCode) {
|
||||
@@ -113,7 +114,7 @@ export function validationSuspiciousName(context) {
|
||||
|
||||
let issues = [];
|
||||
|
||||
const presetName = presetManager.match(entity, context.graph()).name();
|
||||
const preset = presetManager.match(entity, context.graph());
|
||||
|
||||
for (let key in tags) {
|
||||
const m = key.match(/^name(?:(?::)([a-zA-Z_-]+))?$/);
|
||||
@@ -122,7 +123,7 @@ export function validationSuspiciousName(context) {
|
||||
const langCode = m.length >= 2 ? m[1] : null;
|
||||
const value = tags[key];
|
||||
|
||||
if (isGenericName(value, tags, presetName)) {
|
||||
if (isGenericName(value, tags, preset)) {
|
||||
issues.provisional = _waitingForNsi; // retry later if we are waiting on NSI to finish loading
|
||||
issues.push(makeGenericNameIssue(entity.id, key, value, langCode));
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ describe('iD.validations.suspicious_name', function () {
|
||||
genericWords: ['^stores?$']
|
||||
};
|
||||
iD.fileFetcher.cache().preset_presets = {
|
||||
'Velero': { tags: { craft: 'sailmaker' }, geometry: ['line'] },
|
||||
'Velero': { tags: { craft: 'sailmaker' }, aliases: ['Velaio'], geometry: ['line'] },
|
||||
'Constructor de barco': { tags: { craft: 'boatbuilder' }, geometry: ['line'] },
|
||||
};
|
||||
});
|
||||
@@ -71,57 +71,57 @@ describe('iD.validations.suspicious_name', function () {
|
||||
return issues;
|
||||
}
|
||||
|
||||
it('has no errors on init', async () => {
|
||||
it('has no errors on init', () => {
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
it('ignores way with no tags', async () => {
|
||||
it('ignores way with no tags', () => {
|
||||
createWay({});
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
it('ignores feature with no name', async () => {
|
||||
it('ignores feature with no name', () => {
|
||||
createWay({ shop: 'supermarket' });
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
it('ignores feature with a specific name', async () => {
|
||||
it('ignores feature with a specific name', () => {
|
||||
createWay({ shop: 'supermarket', name: 'Lou\'s' });
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
it('ignores feature with a specific name that includes a generic name', async () => {
|
||||
it('ignores feature with a specific name that includes a generic name', () => {
|
||||
createWay({ shop: 'supermarket', name: 'Lou\'s Store' });
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
it('ignores feature matching excludeNamed pattern in name-suggestion-index', async () => {
|
||||
it('ignores feature matching excludeNamed pattern in name-suggestion-index', () => {
|
||||
createWay({ shop: 'supermarket', name: 'famiglia cooperativa' });
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(0);
|
||||
});
|
||||
|
||||
it('flags feature matching a excludeGeneric pattern in name-suggestion-index', async () => {
|
||||
it('flags feature matching a excludeGeneric pattern in name-suggestion-index', () => {
|
||||
createWay({ shop: 'supermarket', name: 'super mercado' });
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(1);
|
||||
var issue = issues[0];
|
||||
@@ -131,10 +131,10 @@ describe('iD.validations.suspicious_name', function () {
|
||||
expect(issue.entityIds[0]).to.eql('w-1');
|
||||
});
|
||||
|
||||
it('flags feature matching a global exclude pattern in name-suggestion-index', async () => {
|
||||
it('flags feature matching a global exclude pattern in name-suggestion-index', () => {
|
||||
createWay({ shop: 'supermarket', name: 'store' });
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(1);
|
||||
var issue = issues[0];
|
||||
@@ -144,10 +144,10 @@ describe('iD.validations.suspicious_name', function () {
|
||||
expect(issue.entityIds[0]).to.eql('w-1');
|
||||
});
|
||||
|
||||
it('flags feature with a name that is just a defining tag key', async () => {
|
||||
it('flags feature with a name that is just a defining tag key', () => {
|
||||
createWay({ amenity: 'drinking_water', name: 'Amenity' });
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(1);
|
||||
var issue = issues[0];
|
||||
@@ -157,10 +157,10 @@ describe('iD.validations.suspicious_name', function () {
|
||||
expect(issue.entityIds[0]).to.eql('w-1');
|
||||
});
|
||||
|
||||
it('flags feature with a name that is just a defining tag value', async () => {
|
||||
it('flags feature with a name that is just a defining tag value', () => {
|
||||
createWay({ shop: 'red_bicycle_emporium', name: 'Red Bicycle Emporium' });
|
||||
var validator = iD.validationSuspiciousName(context);
|
||||
await setTimeout(20);
|
||||
|
||||
var issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(1);
|
||||
var issue = issues[0];
|
||||
@@ -181,6 +181,17 @@ describe('iD.validations.suspicious_name', function () {
|
||||
expect(issues[0].hash).to.eql('name:ca=Velero');
|
||||
});
|
||||
|
||||
it('flags feature with a name that matches a preset alias', async () => {
|
||||
await iD.presetManager.ensureLoaded(true);
|
||||
createWay({ craft: 'sailmaker', 'name:it': 'Velaio' });
|
||||
const validator = iD.validationSuspiciousName(context);
|
||||
|
||||
const issues = validate(validator);
|
||||
expect(issues).to.have.lengthOf(1);
|
||||
expect(issues[0].type).to.eql('suspicious_name');
|
||||
expect(issues[0].hash).to.eql('name:it=Velaio');
|
||||
});
|
||||
|
||||
it('flags feature with a name that matches the preset name and tag name', async () => {
|
||||
await iD.presetManager.ensureLoaded(true);
|
||||
createWay({ craft: 'boatbuilder', 'name:mi': 'boatbuilder', name: 'cOnStRuCtOr de barco' });
|
||||
|
||||
Reference in New Issue
Block a user