From c08d79e4880af070765a25a196b57449c1fc5e00 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Thu, 22 Mar 2018 14:45:37 -0400 Subject: [PATCH] Extract tag cleaning code to utilCleanTags, add tests (closes #4925) --- modules/ui/entity_editor.js | 49 +---------------------- modules/util/clean_tags.js | 43 +++++++++++++++++++++ modules/util/index.js | 1 + test/index.html | 1 + test/spec/util/clean_tags.js | 75 ++++++++++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+), 47 deletions(-) create mode 100644 modules/util/clean_tags.js create mode 100644 test/spec/util/clean_tags.js diff --git a/modules/ui/entity_editor.js b/modules/ui/entity_editor.js index 55638d86b..53891db21 100644 --- a/modules/ui/entity_editor.js +++ b/modules/ui/entity_editor.js @@ -20,7 +20,7 @@ import { uiRawMembershipEditor } from './raw_membership_editor'; import { uiRawTagEditor } from './raw_tag_editor'; import { uiTagReference } from './tag_reference'; import { uiPresetEditor } from './preset_editor'; -import { utilRebind } from '../util'; +import { utilCleanTags, utilRebind } from '../util'; export function uiEntityEditor(context) { @@ -213,51 +213,6 @@ export function uiEntityEditor(context) { } - function clean(orig) { - - function cleanVal(k, v) { - function keepSpaces(k) { - return /_hours|_times|:conditional$/.test(k); - } - - function skip(k) { - return /^(description|note|fixme)$/.test(k); - } - - if (skip(k)) return v; - - var cleaned = v - .split(';') - .map(function(s) { return s.trim(); }) - .join(keepSpaces(k) ? '; ' : ';'); - - // The code below is not intended to validate websites and emails. - // It is only intended to prevent obvious copy-paste errors. (#2323) - // clean website- and email-like tags - if (k.indexOf('website') !== -1 || - k.indexOf('email') !== -1 || - cleaned.indexOf('http') === 0) { - cleaned = cleaned - .replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars - - } - - return cleaned; - } - - var out = {}; - for (var k in orig) { - if (!k) continue; - var v = orig[k]; - if (v !== undefined) { - out[k] = cleanVal(k, v); - } - } - - return out; - } - - // Tag changes that fire on input can all get coalesced into a single // history operation when the user leaves the field. #2342 function changeTags(changed, onInput) { @@ -274,7 +229,7 @@ export function uiEntityEditor(context) { } if (!onInput) { - tags = clean(tags); + tags = utilCleanTags(tags); } if (!_isEqual(entity.tags, tags)) { diff --git a/modules/util/clean_tags.js b/modules/util/clean_tags.js new file mode 100644 index 000000000..200da3a66 --- /dev/null +++ b/modules/util/clean_tags.js @@ -0,0 +1,43 @@ +export function utilCleanTags(tags) { + var out = {}; + for (var k in tags) { + if (!k) continue; + var v = tags[k]; + if (v !== undefined) { + out[k] = cleanValue(k, v); + } + } + + return out; + + + function cleanValue(k, v) { + function keepSpaces(k) { + return /_hours|_times|:conditional$/.test(k); + } + + function skip(k) { + return /^(description|note|fixme)$/.test(k); + } + + if (skip(k)) return v; + + var cleaned = v + .split(';') + .map(function(s) { return s.trim(); }) + .join(keepSpaces(k) ? '; ' : ';'); + + // The code below is not intended to validate websites and emails. + // It is only intended to prevent obvious copy-paste errors. (#2323) + // clean website- and email-like tags + if (k.indexOf('website') !== -1 || + k.indexOf('email') !== -1 || + cleaned.indexOf('http') === 0) { + cleaned = cleaned + .replace(/[\u200B-\u200F\uFEFF]/g, ''); // strip LRM and other zero width chars + + } + + return cleaned; + } +} diff --git a/modules/util/index.js b/modules/util/index.js index 577c42db5..f64856de8 100644 --- a/modules/util/index.js +++ b/modules/util/index.js @@ -1,5 +1,6 @@ export { utilAsyncMap } from './util'; export { utilCallWhenIdle } from './call_when_idle'; +export { utilCleanTags } from './clean_tags'; export { utilDisplayName } from './util'; export { utilDisplayNameForPath } from './util'; export { utilDisplayType } from './util'; diff --git a/test/index.html b/test/index.html index 9c0c50a29..42e7f145f 100644 --- a/test/index.html +++ b/test/index.html @@ -129,6 +129,7 @@ + diff --git a/test/spec/util/clean_tags.js b/test/spec/util/clean_tags.js new file mode 100644 index 000000000..44552c527 --- /dev/null +++ b/test/spec/util/clean_tags.js @@ -0,0 +1,75 @@ +describe('iD.utilCleanTags', function() { + it('handles empty tags object', function() { + var t = {}; + var result = iD.utilCleanTags(t); + expect(result).to.eql({}); + }); + + it('discards empty keys', function() { + var t = { '': 'bar' }; + var result = iD.utilCleanTags(t); + expect(result).to.eql({}); + }); + + it('discards undefined values', function() { + var t = { 'foo': undefined }; + var result = iD.utilCleanTags(t); + expect(result).to.eql({}); + }); + + it('trims whitespace', function() { + var t = { + 'leading': ' value', + 'trailing': 'value ', + 'both': ' value ' + }; + var result = iD.utilCleanTags(t); + expect(result).to.eql({ + 'leading': 'value', + 'trailing': 'value', + 'both': 'value' + }); + }); + + it('trims semicolon delimited whitespace', function() { + var t = { + 'leading': ' value1; value2', + 'trailing': 'value1 ;value2 ', + 'both': ' value1 ; value2 ' + }; + var result = iD.utilCleanTags(t); + expect(result).to.eql({ + 'leading': 'value1;value2', + 'trailing': 'value1;value2', + 'both': 'value1;value2' + }); + }); + + it('does not clean description, note, fixme', function() { + var t = { + 'description': ' value', + 'note': 'value ', + 'fixme': ' value ' + }; + var result = iD.utilCleanTags(t); + expect(result).to.eql(t); + }); + + it('uses semicolon-space delimiting for opening_hours, conditional: tags', function() { + var t = { + 'opening_hours': ' Mo-Su 08:00-18:00 ;Apr 10-15 off;Jun 08:00-14:00 ; Aug off; Dec 25 off ', + 'collection_times': ' Mo 10:00-12:00,12:30-15:00 ;Tu-Fr 08:00-12:00,12:30-15:00;Sa 08:00-12:00 ', + 'maxspeed:conditional': ' 120 @ (06:00-20:00) ;80 @ wet ', + 'restriction:conditional': ' no_u_turn @ (Mo-Fr 09:00-10:00,15:00-16:00;SH off) ' + }; + var result = iD.utilCleanTags(t); + expect(result).to.eql({ + 'opening_hours': 'Mo-Su 08:00-18:00; Apr 10-15 off; Jun 08:00-14:00; Aug off; Dec 25 off', + 'collection_times': 'Mo 10:00-12:00,12:30-15:00; Tu-Fr 08:00-12:00,12:30-15:00; Sa 08:00-12:00', + 'maxspeed:conditional': '120 @ (06:00-20:00); 80 @ wet', + 'restriction:conditional': 'no_u_turn @ (Mo-Fr 09:00-10:00,15:00-16:00; SH off)' + }); + }); + +}); +