diff --git a/data/index.js b/data/index.js index 3ee567f12..d69487324 100644 --- a/data/index.js +++ b/data/index.js @@ -1,5 +1,3 @@ -export { wikipedia as dataWikipedia } from 'wmf-sitematrix'; - export { dataLocales } from './locales.json'; export { en as dataEn } from '../dist/locales/en.json'; diff --git a/modules/core/data.js b/modules/core/data.js index a47412232..42a8ae1db 100644 --- a/modules/core/data.js +++ b/modules/core/data.js @@ -18,7 +18,8 @@ export function coreData(context) { 'locales': 'data/locales.min.json', 'phone_formats': 'data/phone_formats.min.json', 'shortcuts': 'data/shortcuts.min.json', - 'territory_languages': 'data/territory_languages.min.json' + 'territory_languages': 'data/territory_languages.min.json', + 'wikipedia': 'data/wikipedia.min.json' }; diff --git a/modules/ui/fields/wikipedia.js b/modules/ui/fields/wikipedia.js index bfef413e8..3246e0de4 100644 --- a/modules/ui/fields/wikipedia.js +++ b/modules/ui/fields/wikipedia.js @@ -1,13 +1,8 @@ import { dispatch as d3_dispatch } from 'd3-dispatch'; - -import { - select as d3_select, - event as d3_event -} from 'd3-selection'; +import { select as d3_select, event as d3_event } from 'd3-selection'; import { t } from '../../util/locale'; import { actionChangeTags } from '../../actions/change_tags'; -import { dataWikipedia } from '../../../data/index'; import { services } from '../../services/index'; import { svgIcon } from '../../svg/icon'; import { uiCombobox } from '../combobox'; @@ -16,260 +11,262 @@ import { utilGetSetValue, utilNoAuto, utilRebind } from '../../util'; export function uiFieldWikipedia(field, context) { - var dispatch = d3_dispatch('change'); - var wikipedia = services.wikipedia; - var wikidata = services.wikidata; - var lang = d3_select(null); - var title = d3_select(null); - var _wikiURL = ''; - var _entity; + const dispatch = d3_dispatch('change'); + const wikipedia = services.wikipedia; + const wikidata = services.wikidata; + let _lang = d3_select(null); + let _title = d3_select(null); + let _wikiURL = ''; + let _entity; - var langCombo = uiCombobox(context, 'wikipedia-lang') - .fetcher(function(value, cb) { - var v = value.toLowerCase(); - - cb(dataWikipedia.filter(function(d) { - return d[0].toLowerCase().indexOf(v) >= 0 || - d[1].toLowerCase().indexOf(v) >= 0 || - d[2].toLowerCase().indexOf(v) >= 0; - }).map(function(d) { - return { value: d[1] }; - })); - }); - - var titleCombo = uiCombobox(context, 'wikipedia-title') - .fetcher(function(value, cb) { - if (!value && _entity) { - value = context.entity(_entity.id).tags.name || ''; - } - - var searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions; - searchfn(language()[2], value, function(query, data) { - cb(data.map(function(d) { - return { value: d }; - })); - }); - }); + // A concern here in switching to async data means that _dataWikipedia will not + // be available the first time through, so things like the fetchers and + // the language() function will not work immediately. + let _dataWikipedia = []; + context.data().get('wikipedia') + .then(d => _dataWikipedia = d) + .catch(() => { /* ignore */ }); - function wiki(selection) { - var wrap = selection.selectAll('.form-field-input-wrap') - .data([0]); + const langCombo = uiCombobox(context, 'wikipedia-lang') + .fetcher((value, callback) => { + const v = value.toLowerCase(); + callback(_dataWikipedia + .filter(d => { + return d[0].toLowerCase().indexOf(v) >= 0 || + d[1].toLowerCase().indexOf(v) >= 0 || + d[2].toLowerCase().indexOf(v) >= 0; + }) + .map(d => ({ value: d[1] })) + ); + }); - wrap = wrap.enter() - .append('div') - .attr('class', 'form-field-input-wrap form-field-input-' + field.type) - .merge(wrap); + const titleCombo = uiCombobox(context, 'wikipedia-title') + .fetcher((value, callback) => { + if (!value && _entity) { + value = context.entity(_entity.id).tags.name || ''; + } + const searchfn = value.length > 7 ? wikipedia.search : wikipedia.suggestions; + searchfn(language()[2], value, (query, data) => { + callback( data.map(d => ({ value: d })) ); + }); + }); - var langRow = wrap.selectAll('.wiki-lang-container') - .data([0]); + function wiki(selection) { + let wrap = selection.selectAll('.form-field-input-wrap') + .data([0]); - langRow = langRow.enter() - .append('div') - .attr('class', 'wiki-lang-container') - .merge(langRow); + wrap = wrap.enter() + .append('div') + .attr('class', `form-field-input-wrap form-field-input-${field.type}`) + .merge(wrap); - lang = langRow.selectAll('input.wiki-lang') - .data([0]); + let langContainer = wrap.selectAll('.wiki-lang-container') + .data([0]); - lang = lang.enter() - .append('input') - .attr('type', 'text') - .attr('class', 'wiki-lang') - .attr('placeholder', t('translate.localized_translation_language')) - .call(utilNoAuto) - .call(langCombo) - .merge(lang); - - utilGetSetValue(lang, language()[1]); - - lang - .on('blur', changeLang) - .on('change', changeLang); + langContainer = langContainer.enter() + .append('div') + .attr('class', 'wiki-lang-container') + .merge(langContainer); - var titleRow = wrap.selectAll('.wiki-title-container') - .data([0]); + _lang = langContainer.selectAll('input.wiki-lang') + .data([0]); - titleRow = titleRow.enter() - .append('div') - .attr('class', 'wiki-title-container') - .merge(titleRow); + _lang = _lang.enter() + .append('input') + .attr('type', 'text') + .attr('class', 'wiki-lang') + .attr('placeholder', t('translate.localized_translation_language')) + .call(utilNoAuto) + .call(langCombo) + .merge(_lang); - title = titleRow.selectAll('input.wiki-title') - .data([0]); + utilGetSetValue(_lang, language()[1]); - title = title.enter() - .append('input') - .attr('type', 'text') - .attr('class', 'wiki-title') - .attr('id', 'preset-input-' + field.safeid) - .attr('maxlength', context.maxCharsForTagValue() - 4) - .call(utilNoAuto) - .call(titleCombo) - .merge(title); - - title - .on('blur', blur) - .on('change', change); + _lang + .on('blur', changeLang) + .on('change', changeLang); - var link = titleRow.selectAll('.wiki-link') - .data([0]); + let titleContainer = wrap.selectAll('.wiki-title-container') + .data([0]); - link = link.enter() - .append('button') - .attr('class', 'form-field-button wiki-link') - .attr('tabindex', -1) - .attr('title', t('icons.view_on', { domain: 'wikipedia.org' })) - .call(svgIcon('#iD-icon-out-link')) - .merge(link); + titleContainer = titleContainer.enter() + .append('div') + .attr('class', 'wiki-title-container') + .merge(titleContainer); - link - .on('click', function() { - d3_event.preventDefault(); - if (_wikiURL) window.open(_wikiURL, '_blank'); - }); + _title = titleContainer.selectAll('input.wiki-title') + .data([0]); + + _title = _title.enter() + .append('input') + .attr('type', 'text') + .attr('class', 'wiki-title') + .attr('id', `preset-input-${field.safeid}`) + .attr('maxlength', context.maxCharsForTagValue() - 4) + .call(utilNoAuto) + .call(titleCombo) + .merge(_title); + + _title + .on('blur', blur) + .on('change', change); + + + let link = titleContainer.selectAll('.wiki-link') + .data([0]); + + link = link.enter() + .append('button') + .attr('class', 'form-field-button wiki-link') + .attr('tabindex', -1) + .attr('title', t('icons.view_on', { domain: 'wikipedia.org' })) + .call(svgIcon('#iD-icon-out-link')) + .merge(link); + + link + .on('click', () => { + d3_event.preventDefault(); + if (_wikiURL) window.open(_wikiURL, '_blank'); + }); + } + + + function language() { + const value = utilGetSetValue(_lang).toLowerCase(); + const locale = utilDetect().locale.toLowerCase(); + let localeLanguage; + return _dataWikipedia.find(d => { + if (d[2] === locale) localeLanguage = d; + return d[0].toLowerCase() === value || d[1].toLowerCase() === value || d[2] === value; + }) || localeLanguage || ['English', 'English', 'en']; + } + + + function changeLang() { + utilGetSetValue(_lang, language()[1]); + change(true); + } + + + function blur() { + change(true); + } + + + function change(skipWikidata) { + let value = utilGetSetValue(_title); + const m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/); + const l = m && _dataWikipedia.find(d => m[1] === d[2]); + let syncTags = {}; + + if (l) { + // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization + value = decodeURIComponent(m[2]).replace(/_/g, ' '); + if (m[3]) { + let anchor; + // try { + // leave this out for now - #6232 + // Best-effort `anchordecode:` implementation + // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1')); + // } catch (e) { + anchor = decodeURIComponent(m[3]); + // } + value += '#' + anchor.replace(/_/g, ' '); + } + value = value.slice(0, 1).toUpperCase() + value.slice(1); + utilGetSetValue(_lang, l[1]); + utilGetSetValue(_title, value); } - - function language() { - var value = utilGetSetValue(lang).toLowerCase(); - var locale = utilDetect().locale.toLowerCase(); - var localeLanguage; - return dataWikipedia.find(function(d) { - if (d[2] === locale) localeLanguage = d; - return d[0].toLowerCase() === value || - d[1].toLowerCase() === value || - d[2] === value; - }) || localeLanguage || ['English', 'English', 'en']; + if (value) { + syncTags.wikipedia = (language()[2] + ':' + value).substr(0, context.maxCharsForTagValue()); + } else { + syncTags.wikipedia = undefined; } - - function changeLang() { - utilGetSetValue(lang, language()[1]); - change(true); - } + dispatch.call('change', this, syncTags); - function blur() { - change(true); - } + if (skipWikidata || !value || !language()[2]) return; + + // attempt asynchronous update of wikidata tag.. + const initGraph = context.graph(); + const initEntityID = _entity.id; + + wikidata.itemsByTitle(language()[2], value, (err, data) => { + if (err || !data || !Object.keys(data).length) return; + + // If graph has changed, we can't apply this update. + if (context.graph() !== initGraph) return; + + const qids = Object.keys(data); + const value = qids && qids.find(id => id.match(/^Q\d+$/)); + let currTags = Object.assign({}, context.entity(initEntityID).tags); // shallow copy + + currTags.wikidata = value; + + // Coalesce the update of wikidata tag into the previous tag change + context.overwrite( + actionChangeTags(initEntityID, currTags), + context.history().undoAnnotation() + ); + + // do not dispatch.call('change') here, because entity_editor + // changeTags() is not intended to be called asynchronously + }); + } - function change(skipWikidata) { - var value = utilGetSetValue(title); - var m = value.match(/https?:\/\/([-a-z]+)\.wikipedia\.org\/(?:wiki|\1-[-a-z]+)\/([^#]+)(?:#(.+))?/); - var l = m && dataWikipedia.find(function(d) { return m[1] === d[2]; }); - var syncTags = {}; + wiki.tags = (tags) => { + const value = tags[field.key] || ''; + const m = value.match(/([^:]+):([^#]+)(?:#(.+))?/); + const l = m && _dataWikipedia.find(d => m[1] === d[2]); + let anchor = m && m[3]; - if (l) { - // Normalize title http://www.mediawiki.org/wiki/API:Query#Title_normalization - value = decodeURIComponent(m[2]).replace(/_/g, ' '); - if (m[3]) { - var anchor; - // try { - // leave this out for now - #6232 - // Best-effort `anchordecode:` implementation - // anchor = decodeURIComponent(m[3].replace(/\.([0-9A-F]{2})/g, '%$1')); - // } catch (e) { - anchor = decodeURIComponent(m[3]); - // } - value += '#' + anchor.replace(/_/g, ' '); - } - value = value.slice(0, 1).toUpperCase() + value.slice(1); - utilGetSetValue(lang, l[1]); - utilGetSetValue(title, value); + // value in correct format + if (l) { + utilGetSetValue(_lang, l[1]); + utilGetSetValue(_title, m[2] + (anchor ? ('#' + anchor) : '')); + if (anchor) { + try { + // Best-effort `anchorencode:` implementation + anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.'); + } catch (e) { + anchor = anchor.replace(/ /g, '_'); } + } + _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' + + m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''); - if (value) { - syncTags.wikipedia = (language()[2] + ':' + value).substr(0, context.maxCharsForTagValue()); - } else { - syncTags.wikipedia = undefined; - } - - dispatch.call('change', this, syncTags); - - - if (skipWikidata || !value || !language()[2]) return; - - // attempt asynchronous update of wikidata tag.. - var initGraph = context.graph(); - var initEntityID = _entity.id; - - wikidata.itemsByTitle(language()[2], value, function(err, data) { - if (err) return; - - // If graph has changed, we can't apply this update. - if (context.graph() !== initGraph) return; - - if (!data || !Object.keys(data).length) return; - - var qids = Object.keys(data); - var value = qids && qids.find(function(id) { return id.match(/^Q\d+$/); }); - var currTags = Object.assign({}, context.entity(initEntityID).tags); // shallow copy - - currTags.wikidata = value; - - // Coalesce the update of wikidata tag into the previous tag change - context.overwrite( - actionChangeTags(initEntityID, currTags), - context.history().undoAnnotation() - ); - - // do not dispatch.call('change') here, because entity_editor - // changeTags() is not intended to be called asynchronously - }); + // unrecognized value format + } else { + utilGetSetValue(_title, value); + if (value && value !== '') { + utilGetSetValue(_lang, ''); + _wikiURL = `https://en.wikipedia.org/wiki/Special:Search?search=${value}`; + } else { + _wikiURL = ''; + } } + }; - wiki.tags = function(tags) { - var value = tags[field.key] || ''; - var m = value.match(/([^:]+):([^#]+)(?:#(.+))?/); - var l = m && dataWikipedia.find(function(d) { return m[1] === d[2]; }); - var anchor = m && m[3]; - - // value in correct format - if (l) { - utilGetSetValue(lang, l[1]); - utilGetSetValue(title, m[2] + (anchor ? ('#' + anchor) : '')); - if (anchor) { - try { - // Best-effort `anchorencode:` implementation - anchor = encodeURIComponent(anchor.replace(/ /g, '_')).replace(/%/g, '.'); - } catch (e) { - anchor = anchor.replace(/ /g, '_'); - } - } - _wikiURL = 'https://' + m[1] + '.wikipedia.org/wiki/' + - m[2].replace(/ /g, '_') + (anchor ? ('#' + anchor) : ''); - - // unrecognized value format - } else { - utilGetSetValue(title, value); - if (value && value !== '') { - utilGetSetValue(lang, ''); - _wikiURL = 'https://en.wikipedia.org/wiki/Special:Search?search=' + value; - } else { - _wikiURL = ''; - } - } - }; + wiki.entity = function(val) { + if (!arguments.length) return _entity; + _entity = val; + return wiki; + }; - wiki.entity = function(val) { - if (!arguments.length) return _entity; - _entity = val; - return wiki; - }; + wiki.focus = () => { + _title.node().focus(); + }; - wiki.focus = function() { - title.node().focus(); - }; - - - return utilRebind(wiki, dispatch, 'on'); + return utilRebind(wiki, dispatch, 'on'); } diff --git a/package.json b/package.json index 5022c3424..3eb53a575 100644 --- a/package.json +++ b/package.json @@ -60,8 +60,7 @@ "pannellum": "2.4.1", "rbush": "3.0.1", "string.fromcodepoint": "0.2.1", - "which-polygon": "2.2.0", - "wmf-sitematrix": "0.1.4" + "which-polygon": "2.2.0" }, "devDependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.25", @@ -110,7 +109,8 @@ "smash": "0.0", "static-server": "^2.2.1", "svg-sprite": "1.5.0", - "uglify-js": "~3.7.1" + "uglify-js": "~3.7.1", + "wmf-sitematrix": "0.1.4" }, "greenkeeper": { "label": "chore-greenkeeper", diff --git a/scripts/build_data.js b/scripts/build_data.js index c32a3ffe9..c20015b36 100644 --- a/scripts/build_data.js +++ b/scripts/build_data.js @@ -97,6 +97,7 @@ function buildData() { let translations = generateTranslations(fields, presets, tstrings, searchableFieldIDs); let taginfo = generateTaginfo(presets, fields); let territoryLanguages = generateTerritoryLanguages(); + let wikipedia = require('wmf-sitematrix').wikipedia; // Additional consistency checks validateCategoryPresets(categories, presets); @@ -111,6 +112,7 @@ function buildData() { writeFileProm('data/presets.yaml', translationsToYAML(translations) ), writeFileProm('data/taginfo.json', prettyStringify(taginfo, { maxLength: 9999 }) ), writeFileProm('data/territory_languages.json', prettyStringify(territoryLanguages, { maxLength: 9999 }) ), + writeFileProm('dist/data/wikipedia.min.json', JSON.stringify(wikipedia)), writeEnJson(tstrings), writeFaIcons(faIcons), writeTnpIcons(tnpIcons), diff --git a/test/spec/core/data.js b/test/spec/core/data.js index da2ed8fd5..b9443c60d 100644 --- a/test/spec/core/data.js +++ b/test/spec/core/data.js @@ -2,7 +2,6 @@ describe('iD.coreData', function() { var _context; before(function() { - iD.data = iD.data || {}; iD.data.test = { hello: 'world' }; }); @@ -36,26 +35,27 @@ describe('iD.coreData', function() { .then(function(data) { expect(data).to.be.a('object'); expect(data.hello).to.eql('world'); - done(); }) - .catch(function(err) { - throw err; - done(); - }); + .finally(done); + + window.setTimeout(function() {}, 20); // async - to let the promise settle in phantomjs }); + it('returns a promise rejected if we can not get the data', function(done) { var data = iD.coreData(_context); var prom = data.get('wat'); prom .then(function(data) { - throw new Error('got data ' + data); - done(); + throw new Error('We were not supposed to get data but did: ' + data); }) .catch(function(err) { expect(/^Unknown data file/.test(err)).to.be.true; - done(); - }); + }) + .finally(done); + + window.setTimeout(function() {}, 20); // async - to let the promise settle in phantomjs }); + it('returns a promise to fetch data if we do not already have the data', function(done) { var files = { 'intro_graph': 'data/intro_graph.min.json' }; var data = iD.coreData(_context).fileMap(files); @@ -65,12 +65,10 @@ describe('iD.coreData', function() { .then(function(data) { expect(data).to.be.a('object'); expect(data.n1.tags.name).to.eql('Three Rivers City Hall'); - done(); }) - .catch(function(err) { - throw err; - done(); - }); + .finally(done); + + window.setTimeout(function() {}, 20); // async - to let the promise settle in phantomjs }); }); diff --git a/test/spec/ui/fields/wikipedia.js b/test/spec/ui/fields/wikipedia.js index b8c5719d3..01bb4ff35 100644 --- a/test/spec/ui/fields/wikipedia.js +++ b/test/spec/ui/fields/wikipedia.js @@ -35,11 +35,16 @@ describe('iD.uiFieldWikipedia', function() { } before(function() { + iD.data.wikipedia = [ + ['German','Deutsch','de'], + ['English','English','en'] + ]; iD.services.wikipedia = iD.serviceWikipedia; iD.services.wikidata = iD.serviceWikidata; }); after(function() { + delete iD.data.wikipedia; delete iD.services.wikipedia; delete iD.services.wikidata; }); @@ -61,59 +66,73 @@ describe('iD.uiFieldWikipedia', function() { server.restore(); }); - it('recognizes lang:title format', function() { + it('recognizes lang:title format', function(done) { var wikipedia = iD.uiFieldWikipedia(field, context); - selection.call(wikipedia); - wikipedia.tags({wikipedia: 'en:Title'}); + window.setTimeout(function() { // async, so data will be available + selection.call(wikipedia); + 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'); + expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('English'); + expect(iD.utilGetSetValue(selection.selectAll('.wiki-title'))).to.equal('Title'); + done(); + }, 20); }); - it('sets language, value', function() { + it('sets language, value', function(done) { var wikipedia = iD.uiFieldWikipedia(field, context).entity(entity); - wikipedia.on('change', changeTags); - selection.call(wikipedia); + window.setTimeout(function() { // async, so data will be available + wikipedia.on('change', changeTags); + selection.call(wikipedia); - var spy = sinon.spy(); - wikipedia.on('change.spy', spy); + var spy = sinon.spy(); + wikipedia.on('change.spy', spy); - iD.utilGetSetValue(selection.selectAll('.wiki-lang'), 'Deutsch'); - happen.once(selection.selectAll('.wiki-lang').node(), { type: 'change' }); - happen.once(selection.selectAll('.wiki-lang').node(), { type: 'blur' }); + iD.utilGetSetValue(selection.selectAll('.wiki-lang'), 'Deutsch'); + happen.once(selection.selectAll('.wiki-lang').node(), { type: 'change' }); + happen.once(selection.selectAll('.wiki-lang').node(), { type: 'blur' }); - 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' }); + 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' }); - 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(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 + 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(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() { + it('recognizes pasted URLs', function(done) { var wikipedia = iD.uiFieldWikipedia(field, context).entity(entity); - wikipedia.on('change', changeTags); - selection.call(wikipedia); + window.setTimeout(function() { // async, so data will be available + wikipedia.on('change', changeTags); + selection.call(wikipedia); - iD.utilGetSetValue(selection.selectAll('.wiki-title'), 'http://de.wikipedia.org/wiki/Title'); - happen.once(selection.selectAll('.wiki-title').node(), { type: 'change' }); + iD.utilGetSetValue(selection.selectAll('.wiki-title'), 'http://de.wikipedia.org/wiki/Title'); + happen.once(selection.selectAll('.wiki-title').node(), { type: 'change' }); - expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('Deutsch'); - expect(iD.utilGetSetValue(selection.selectAll('.wiki-title'))).to.equal('Title'); + expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('Deutsch'); + expect(iD.utilGetSetValue(selection.selectAll('.wiki-title'))).to.equal('Title'); + done(); + }, 20); }); - it('preserves existing language', function() { - selection.call(iD.uiFieldWikipedia(field, context)); - iD.utilGetSetValue(selection.selectAll('.wiki-lang'), 'Deutsch'); + it('preserves existing language', function(done) { + var wikipedia1 = iD.uiFieldWikipedia(field, context); + window.setTimeout(function() { // async, so data will be available + selection.call(wikipedia1); + iD.utilGetSetValue(selection.selectAll('.wiki-lang'), 'Deutsch'); - var wikipedia = iD.uiFieldWikipedia(field, context); - selection.call(wikipedia); - wikipedia.tags({}); - - expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('Deutsch'); + var wikipedia2 = iD.uiFieldWikipedia(field, context); + window.setTimeout(function() { // async, so data will be available + selection.call(wikipedia2); + wikipedia2.tags({}); + expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('Deutsch'); + done(); + }, 20); + }, 20); }); it.skip('does not set delayed wikidata tag if graph has changed', function(done) { diff --git a/test/spec/validations/outdated_tags.js b/test/spec/validations/outdated_tags.js index 433790d57..26aa3392d 100644 --- a/test/spec/validations/outdated_tags.js +++ b/test/spec/validations/outdated_tags.js @@ -9,7 +9,7 @@ describe('iD.validations.outdated_tags', function () { }); after(function() { - delete iD.data.deprecated; + iD.data.deprecated = []; }); beforeEach(function() { @@ -45,8 +45,7 @@ describe('iD.validations.outdated_tags', function () { ); } - function validate() { - var validator = iD.validationOutdatedTags(context); + function validate(validator) { var changes = context.history().changes(); var entities = changes.modified.concat(changes.created); var issues = []; @@ -56,63 +55,91 @@ describe('iD.validations.outdated_tags', function () { return issues; } - it('has no errors on init', function() { - var issues = validate(); - expect(issues).to.have.lengthOf(0); + it('has no errors on init', function(done) { + var validator = iD.validationOutdatedTags(context); + window.setTimeout(function() { // async, so data will be available + var issues = validate(validator); + expect(issues).to.have.lengthOf(0); + done(); + }, 20); }); - it('has no errors on good tags', function() { + it('has no errors on good tags', function(done) { createWay({'highway': 'unclassified'}); - var issues = validate(); - expect(issues).to.have.lengthOf(0); + var validator = iD.validationOutdatedTags(context); + window.setTimeout(function() { // async, so data will be available + var issues = validate(validator); + expect(issues).to.have.lengthOf(0); + done(); + }, 20); }); - it('flags deprecated tag with replacement', function() { + it('flags deprecated tag with replacement', function(done) { createWay({'highway': 'ford'}); - var issues = validate(); - expect(issues).to.have.lengthOf(1); - var issue = issues[0]; - expect(issue.type).to.eql('outdated_tags'); - expect(issue.subtype).to.eql('deprecated_tags'); - expect(issue.severity).to.eql('warning'); - expect(issue.entityIds).to.have.lengthOf(1); - expect(issue.entityIds[0]).to.eql('w-1'); + var validator = iD.validationOutdatedTags(context); + window.setTimeout(function() { // async, so data will be available + var issues = validate(validator); + expect(issues).to.have.lengthOf(1); + var issue = issues[0]; + expect(issue.type).to.eql('outdated_tags'); + expect(issue.subtype).to.eql('deprecated_tags'); + expect(issue.severity).to.eql('warning'); + expect(issue.entityIds).to.have.lengthOf(1); + expect(issue.entityIds[0]).to.eql('w-1'); + done(); + }, 20); }); - it('flags deprecated tag with no replacement', function() { + it('flags deprecated tag with no replacement', function(done) { createWay({'highway': 'no'}); - var issues = validate(); - expect(issues).to.have.lengthOf(1); - var issue = issues[0]; - expect(issue.type).to.eql('outdated_tags'); - expect(issue.subtype).to.eql('deprecated_tags'); - expect(issue.severity).to.eql('warning'); - expect(issue.entityIds).to.have.lengthOf(1); - expect(issue.entityIds[0]).to.eql('w-1'); + var validator = iD.validationOutdatedTags(context); + window.setTimeout(function() { // async, so data will be available + var issues = validate(validator); + expect(issues).to.have.lengthOf(1); + var issue = issues[0]; + expect(issue.type).to.eql('outdated_tags'); + expect(issue.subtype).to.eql('deprecated_tags'); + expect(issue.severity).to.eql('warning'); + expect(issue.entityIds).to.have.lengthOf(1); + expect(issue.entityIds[0]).to.eql('w-1'); + done(); + }, 20); }); - it('ignores way with no relations', function() { + it('ignores way with no relations', function(done) { createWay({}); - var issues = validate(); - expect(issues).to.have.lengthOf(0); + var validator = iD.validationOutdatedTags(context); + window.setTimeout(function() { // async, so data will be available + var issues = validate(validator); + expect(issues).to.have.lengthOf(0); + done(); + }, 20); }); - it('ignores multipolygon tagged on the relation', function() { + it('ignores multipolygon tagged on the relation', function(done) { createRelation({}, { type: 'multipolygon', building: 'yes' }); - var issues = validate(); - expect(issues).to.have.lengthOf(0); + var validator = iD.validationOutdatedTags(context); + window.setTimeout(function() { // async, so data will be available + var issues = validate(validator); + expect(issues).to.have.lengthOf(0); + done(); + }, 20); }); - it('flags multipolygon tagged on the outer way', function() { + it('flags multipolygon tagged on the outer way', function(done) { createRelation({ building: 'yes' }, { type: 'multipolygon' }); - var issues = validate(); - expect(issues).to.not.have.lengthOf(0); - var issue = issues[0]; - expect(issue.type).to.eql('outdated_tags'); - expect(issue.subtype).to.eql('old_multipolygon'); - expect(issue.entityIds).to.have.lengthOf(2); - expect(issue.entityIds[0]).to.eql('w-1'); - expect(issue.entityIds[1]).to.eql('r-1'); + var validator = iD.validationOutdatedTags(context); + window.setTimeout(function() { // async, so data will be available + var issues = validate(validator); + expect(issues).to.not.have.lengthOf(0); + var issue = issues[0]; + expect(issue.type).to.eql('outdated_tags'); + expect(issue.subtype).to.eql('old_multipolygon'); + expect(issue.entityIds).to.have.lengthOf(2); + expect(issue.entityIds[0]).to.eql('w-1'); + expect(issue.entityIds[1]).to.eql('r-1'); + done(); + }, 20); }); });