import { setTimeout } from 'node:timers/promises'; describe('iD.uiFieldWikipedia', function() { var entity, context, selection, field; before(function() { iD.fileFetcher.cache().wmf_sitematrix = [ ['German','Deutsch','de'], ['English','English','en'] ]; iD.services.wikipedia = iD.serviceWikipedia; iD.services.wikidata = iD.serviceWikidata; }); after(function() { delete iD.fileFetcher.cache().wmf_sitematrix; delete iD.services.wikipedia; delete iD.services.wikidata; }); beforeEach(function() { entity = iD.osmNode({id: 'n12345'}); context = iD.coreContext().assetPath('../dist/').init(); context.history().merge([entity]); selection = d3.select(document.createElement('div')); field = iD.presetField('wikipedia', { key: 'wikipedia', keys: ['wikipedia', 'wikidata'], type: 'wikipedia' }); fetchMock.reset(); fetchMock.mock(new RegExp('\/w\/api\.php.*action=wbgetentities'), { body: '{"entities":{"Q216353":{"id":"Q216353"}}}', status: 200, headers: { 'Content-Type': 'application/json' } }); }); afterEach(function() { fetchMock.reset(); }); function changeTags(changed) { var e = context.entity(entity.id); var annotation = 'Changed tags.'; var tags = JSON.parse(JSON.stringify(e.tags)); // deep copy var didChange = false; for (var k in changed) { if (changed.hasOwnProperty(k)) { var v = changed[k]; if (tags[k] !== v && (v !== undefined || tags.hasOwnProperty(k))) { tags[k] = v; didChange = true; } } } if (didChange) { context.perform(iD.actionChangeTags(e.id, tags), annotation); } } it('recognizes lang:title format', async () => { var wikipedia = iD.uiFieldWikipedia(field, context); await setTimeout(20); 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'); }); it('sets language, value', async () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); await setTimeout(20); wikipedia.on('change', changeTags); selection.call(wikipedia); 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-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 }); it('recognizes pasted URLs', async () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); await setTimeout(20); 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' }); expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('Deutsch'); expect(iD.utilGetSetValue(selection.selectAll('.wiki-title'))).to.equal('Title'); }); describe('encodePath', function() { it('returns an encoded URI component that contains the title with spaces replaced by underscores', () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); expect(wikipedia.encodePath('? (film)', undefined)).to.equal('%3F_(film)'); }); it('returns an encoded URI component that includes an anchor fragment', () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); // this can be tested manually by entering '? (film)#Themes and style in the search box before focusing out' expect(wikipedia.encodePath('? (film)', 'Themes and style')).to.equal('%3F_(film)#Themes_and_style'); }); }); describe('encodeURIAnchorFragment', function() { it('returns an encoded URI anchor fragment', () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); // this can be similarly tested by entering 'Section#Arts, entertainment and media' in the search box before focusing out' expect(wikipedia.encodeURIAnchorFragment('Theme?')).to.equal('#Theme%3F'); }); it('replaces all whitespace characters with underscore', () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); expect(wikipedia.encodeURIAnchorFragment('Themes And Styles')).to.equal('#Themes_And_Styles'); }); it('encodes % characters, does not replace them with a dot', () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); expect(wikipedia.encodeURIAnchorFragment('Is%this_100% correct')).to.equal('#Is%25this_100%25_correct'); }); it('encodes characters that are URI encoded characters', () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); expect(wikipedia.encodeURIAnchorFragment('Section %20%25')).to.equal('#Section_%2520%2525'); }); }); // note - currently skipping the tests that use `options` to delay responses it('preserves existing language', async () => { var wikipedia1 = iD.uiFieldWikipedia(field, context); await setTimeout(20); selection.call(wikipedia1); iD.utilGetSetValue(selection.selectAll('.wiki-lang'), 'Deutsch'); var wikipedia2 = iD.uiFieldWikipedia(field, context); await setTimeout(20); selection.call(wikipedia2); wikipedia2.tags({}); expect(iD.utilGetSetValue(selection.selectAll('.wiki-lang'))).to.equal('Deutsch'); }); it.skip('does not set delayed wikidata tag if graph has changed', async () => { var wikipedia = iD.uiFieldWikipedia(field, context).entityIDs([entity.id]); wikipedia.on('change', changeTags); selection.call(wikipedia); var spy = sinon.spy(); wikipedia.on('change.spy', spy); // Create an XHR server that will respond after 60ms fetchMock.reset(); fetchMock.mock(new RegExp('\/w\/api\.php.*action=wbgetentities'), { body: '{"entities":{"Q216353":{"id":"Q216353"}}}', status: 200, headers: { 'Content-Type': 'application/json' } }, { delay: 60 }); // Set title to "Skip" iD.utilGetSetValue(selection.selectAll('.wiki-lang'), 'Deutsch'); iD.utilGetSetValue(selection.selectAll('.wiki-title'), 'Skip'); happen.once(selection.selectAll('.wiki-title').node(), { type: 'change' }); happen.once(selection.selectAll('.wiki-title').node(), { type: 'blur' }); // t0 expect(context.entity(entity.id).tags.wikidata).to.be.undefined; // Create a new XHR server that will respond after 60ms to // separate requests after this point from those before fetchMock.reset(); fetchMock.mock(new RegExp('\/w\/api\.php.*action=wbgetentities'), { body: '{"entities":{"Q216353":{"id":"Q216353"}}}', status: 200, headers: { 'Content-Type': 'application/json' } }, { delay: 60 }); // t30: graph change - Set title to "Title" window.setTimeout(function() { 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' }); }, 30); // t60: at t0 + 60ms (delay), wikidata SHOULD NOT be set because graph has changed. // t70: check that wikidata unchanged window.setTimeout(function() { expect(context.entity(entity.id).tags.wikidata).to.be.undefined; }, 70); // t90: at t30 + 60ms (delay), wikidata SHOULD be set because graph is unchanged. // t100: check that wikidata has changed await setTimeout(100); expect(context.entity(entity.id).tags.wikidata).to.equal('Q216353'); expect(spy.callCount).to.equal(4); expect(spy.getCall(0)).to.have.been.calledWith({ wikipedia: 'de:Skip' }); // 'Skip' on change expect(spy.getCall(1)).to.have.been.calledWith({ wikipedia: 'de:Skip' }); // 'Skip' on blur expect(spy.getCall(2)).to.have.been.calledWith({ wikipedia: 'de:Title' }); // 'Title' on change +10ms expect(spy.getCall(3)).to.have.been.calledWith({ wikipedia: 'de:Title' }); // 'Title' on blur +10ms }); });