Files
iD/test/spec/ui/combobox.js
2025-02-17 13:10:04 +01:00

272 lines
10 KiB
JavaScript

describe('uiCombobox', function() {
var body, context, container, content, input, combobox;
var data = [
{title: 'foobar', value: 'foobar'},
{title: 'foo', value: 'foo'},
{title: 'bar', value: 'bar'},
{title: 'Baz', value: 'Baz'},
{title: 'test', value: 'test'}
];
function simulateKeypress(key) {
var keyCode = iD.utilKeybinding.keyCodes[key];
var value = input.property('value');
var start = input.property('selectionStart');
var finish = input.property('selectionEnd');
input.node().dispatchEvent(new KeyboardEvent('keydown', { keyCode }));
switch (key) {
case '⇥':
break;
case '←':
start = finish = Math.max(0, start - 1);
input.node().setSelectionRange(start, finish);
break;
case '→':
start = finish = Math.max(start + 1, value.length);
input.node().setSelectionRange(start, finish);
break;
case '↑':
case '↓':
case '↩':
case '⎋':
break;
case '⌫':
value = value.substring(0, start - (start === finish ? 1 : 0)) +
value.substring(finish, value.length);
input.property('value', value);
input.node().dispatchEvent(new MouseEvent('input'));
break;
case '⌦':
value = value.substring(0, start) +
value.substring(finish + (start === finish ? 1 : 0), value.length);
input.property('value', value);
input.node().dispatchEvent(new MouseEvent('input'));
break;
default:
value = value.substring(0, start) + key + value.substring(finish, value.length);
input.property('value', value);
input.node().dispatchEvent(new MouseEvent('input'));
}
input.node().dispatchEvent(new KeyboardEvent('keyup', { keyCode }));
}
beforeEach(function() {
body = d3.select('body');
container = body.append('div').attr('class', 'ideditor');
context = iD.coreContext().assetPath('../dist/').init().container(container);
content = container.append('div');
input = content.append('input');
combobox = iD.uiCombobox(context);
});
afterEach(function() {
body.selectAll('.combobox').remove();
content.remove();
container.remove();
});
function focusTypeahead(input) {
input.node().focus();
}
it('adds the combobox-input class', function() {
input.call(combobox);
expect(input.classed('combobox-input')).to.be.true;
});
it('adds combobox under container', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('↓');
expect(d3.selectAll('.ideditor > div.combobox').size()).to.equal(1);
});
it('filters entries to those matching the value', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('b');
expect(body.selectAll('.combobox-option').size()).to.equal(3);
expect(body.selectAll('.combobox-option').nodes()[0].text).to.equal('foobar');
expect(body.selectAll('.combobox-option').nodes()[1].text).to.equal('bar');
expect(body.selectAll('.combobox-option').nodes()[2].text).to.equal('Baz');
});
it('shows all entries when activating the combo', function() {
input.property('value', 'foobar').call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('↓');
expect(body.selectAll('.combobox-option').size()).to.equal(5);
expect(body.selectAll('.combobox-option').text()).to.equal('foobar');
});
it('selects the first option that matches the input', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('b');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(1);
expect(body.selectAll('.combobox-option.selected').text()).to.equal('bar');
});
it('prefers an option that exactly matches the input over the first option', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('f');
simulateKeypress('o');
simulateKeypress('o');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(1);
expect(body.selectAll('.combobox-option.selected').text()).to.equal('foo'); // skip foobar
});
it('does not autocomplete numeric options', function() {
var numeric = [
{title: '100', value: '100'},
{title: '110', value: '110'}
];
input.call(combobox.data(numeric));
focusTypeahead(input);
simulateKeypress('1');
simulateKeypress('0');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(0);
});
it('does not autocomplete if canAutocomplete(false)', function() {
input.call(combobox.data(data).canAutocomplete(false));
focusTypeahead(input);
simulateKeypress('b');
expect(input.property('value')).to.equal('b');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(0);
});
it('selects the completed portion of the value', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('b');
expect(input.property('value')).to.equal('bar');
expect(input.property('selectionStart')).to.equal(1);
expect(input.property('selectionEnd')).to.equal(3);
});
it('does not preserve the case of the input portion of the value by default', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('B');
expect(input.property('value')).to.equal('bar');
expect(input.property('selectionStart')).to.equal(1);
expect(input.property('selectionEnd')).to.equal(3);
});
it('does preserve the case of the input portion of the value with caseSensitive option', function() {
combobox.caseSensitive(true);
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('B');
expect(input.property('value')).to.equal('Baz');
expect(input.property('selectionStart')).to.equal(1);
expect(input.property('selectionEnd')).to.equal(3);
});
it('does not select when value is empty', function() {
input.call(combobox.data(data));
focusTypeahead(input);
input.node().dispatchEvent(new MouseEvent('input'));
expect(body.selectAll('.combobox-option.selected').size()).to.equal(0);
});
it('does not select when value is not a prefix of any suggestion', function() {
input.call(combobox.fetcher(function(_, cb) { cb(data); }));
focusTypeahead(input);
simulateKeypress('b');
simulateKeypress('i');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(0);
});
it('does not select or autocomplete after ⌫', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('b');
simulateKeypress('⌫');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(0);
expect(input.property('value')).to.equal('b');
});
it('does not select or autocomplete after ⌦', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('f');
simulateKeypress('b');
simulateKeypress('←');
simulateKeypress('←');
simulateKeypress('⌦');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(0);
expect(input.property('value')).to.equal('b');
});
it('selects and autocompletes the next/prev suggestion on ↓/↑', function() {
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('↓');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(1);
expect(body.selectAll('.combobox-option.selected').text()).to.equal('foobar');
expect(input.property('value')).to.equal('foobar');
simulateKeypress('↓');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(1);
expect(body.selectAll('.combobox-option.selected').text()).to.equal('foo');
expect(input.property('value')).to.equal('foo');
simulateKeypress('↑');
expect(body.selectAll('.combobox-option.selected').size()).to.equal(1);
expect(body.selectAll('.combobox-option.selected').text()).to.equal('foobar');
expect(input.property('value')).to.equal('foobar');
});
it('emits accepted event with selected datum on ⇥', async () => {
const d = new Promise(cb => { combobox.on('accept', cb); });
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('b');
simulateKeypress('⇥');
expect(await d).to.eql({title: 'bar', value: 'bar'});
combobox.on('accept', null);
});
it('emits accepted event with selected datum on ↩', async () => {
const d = new Promise(cb => { combobox.on('accept', cb); });
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('b');
simulateKeypress('↩');
expect(await d).to.eql({title: 'bar', value: 'bar'});
combobox.on('accept', null);
});
it('emits cancel event on ⎋', function() {
var spy = sinon.spy();
combobox.on('cancel', spy);
input.call(combobox.data(data));
focusTypeahead(input);
simulateKeypress('b');
simulateKeypress('⎋');
expect(spy).to.have.been.calledOnce;
});
it('hides on ↩', function() {
input.call(combobox.data(data));
input.node().focus();
simulateKeypress('↩');
expect(body.selectAll('.combobox').size()).to.equal(0);
});
});