From c481f90e7df21bdb464fbfa34249b2b7fae7c1f7 Mon Sep 17 00:00:00 2001 From: Bryan Housel Date: Wed, 29 Jan 2020 12:30:11 -0500 Subject: [PATCH] Stop bundling wmf-sitematrix, move it extern This also includes a bunch of tweaks to make the tests work in both PhantomJS and modern browsers like Chrome. Basically - introduce some more async into the test code so that the coreData.get promise is guaranteed to settle. Because in PhantomJS the promise is polyfilled, and Chrome it's native, they work slightly differently. --- data/index.js | 2 - modules/core/data.js | 3 +- modules/ui/fields/wikipedia.js | 447 ++++++++++++------------- package.json | 6 +- scripts/build_data.js | 2 + test/spec/core/data.js | 28 +- test/spec/ui/fields/wikipedia.js | 91 +++-- test/spec/validations/outdated_tags.js | 111 +++--- 8 files changed, 366 insertions(+), 324 deletions(-) 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); }); });