set_global('$', global.make_zjquery()); zrequire('input_pill'); set_global('Handlebars', global.make_handlebars()); zrequire('templates'); set_global('document', {}); const noop = function () {}; const example_img_link = 'http://example.com/example.png'; set_global('ui_util', { place_caret_at_end: noop, }); set_global('getSelection', () => { return { anchorOffset: 0, }; }); let id_seq = 0; run_test('set_up_ids', () => { // just get coverage on a simple one-liner: input_pill.random_id(); input_pill.random_id = function () { id_seq += 1; return 'some_id' + id_seq; }; }); function pill_html(value, data_id, img_src) { const has_image = img_src !== undefined; const opts = { id: data_id, display_value: value, has_image: has_image, }; if (has_image) { opts.img_src = img_src; } return require('../../static/templates/input_pill.hbs')(opts); } run_test('basics', () => { const config = {}; blueslip.expect('error', 'Pill needs container.'); input_pill.create(config); blueslip.reset(); const pill_input = $.create('pill_input'); const container = $.create('container'); container.set_find_results('.input', pill_input); blueslip.expect('error', 'Pill needs create_item_from_text'); config.container = container; input_pill.create(config); blueslip.reset(); blueslip.expect('error', 'Pill needs get_text_from_item'); config.create_item_from_text = noop; input_pill.create(config); blueslip.reset(); config.get_text_from_item = noop; const widget = input_pill.create(config); const item = { display_value: 'JavaScript', language: 'js', img_src: example_img_link, }; let inserted_before; const expected_html = pill_html('JavaScript', 'some_id1', example_img_link); pill_input.before = function (elem) { inserted_before = true; assert.equal(elem.html(), expected_html); }; widget.appendValidatedData(item); assert(inserted_before); assert.deepEqual(widget.items(), [item]); }); function set_up() { set_global('$', global.make_zjquery()); const items = { blue: { display_value: 'BLUE', description: 'color of the sky', img_src: example_img_link, }, red: { display_value: 'RED', description: 'color of stop signs', }, yellow: { display_value: 'YELLOW', description: 'color of bananas', }, }; const pill_input = $.create('pill_input'); pill_input.before = () => {}; const create_item_from_text = function (text) { return items[text]; }; const container = $.create('container'); container.set_find_results('.input', pill_input); const config = { container: container, create_item_from_text: create_item_from_text, get_text_from_item: (item) => item.display_value, }; id_seq = 0; return { config: config, pill_input: pill_input, items: items, container: container, }; } run_test('copy from pill', () => { const info = set_up(); const config = info.config; const container = info.container; const widget = input_pill.create(config); widget.appendValue('blue,red'); const copy_handler = container.get_on_handler('copy', '.pill'); let copied_text; const pill_stub = { data: (field) => { assert.equal(field, 'id'); return 'some_id2'; }, }; const e = { originalEvent: { clipboardData: { setData: (format, text) => { assert.equal(format, 'text/plain'); copied_text = text; }, }, }, preventDefault: noop, }; container.set_find_results(':focus', pill_stub); copy_handler(e); assert.equal(copied_text, 'RED'); }); run_test('paste to input', () => { const info = set_up(); const config = info.config; const container = info.container; const items = info.items; const widget = input_pill.create(config); const paste_handler = container.get_on_handler('paste', '.input'); const paste_text = 'blue,yellow'; const e = { originalEvent: { clipboardData: { getData: (format) => { assert.equal(format, 'text/plain'); return paste_text; }, }, }, preventDefault: noop, }; document.execCommand = (cmd, _, text) => { assert.equal(cmd, 'insertText'); container.find('.input').text(text); }; paste_handler(e); assert.deepEqual(widget.items(), [ items.blue, items.yellow, ]); }); run_test('arrows on pills', () => { const info = set_up(); const config = info.config; const container = info.container; const widget = input_pill.create(config); widget.appendValue('blue,red'); const key_handler = container.get_on_handler('keydown', '.pill'); function test_key(c) { key_handler({ charCode: c, }); } const LEFT_ARROW = 37; const RIGHT_ARROW = 39; let prev_focused = false; let next_focused = false; const pill_stub = { prev: () => { return { focus: () => { prev_focused = true; }, }; }, next: () => { return { focus: () => { next_focused = true; }, }; }, }; container.set_find_results('.pill:focus', pill_stub); // We use the same stub to test both arrows, since we don't // actually cause any real state changes here. We stub out // the only interaction, which is to move the focus. test_key(LEFT_ARROW); assert(prev_focused); test_key(RIGHT_ARROW); assert(next_focused); }); run_test('left arrow on input', () => { const info = set_up(); const config = info.config; const container = info.container; const widget = input_pill.create(config); widget.appendValue('blue,red'); const LEFT_ARROW = 37; const key_handler = container.get_on_handler('keydown', '.input'); let last_pill_focused = false; container.set_find_results('.pill', { last: () => { return { focus: () => { last_pill_focused = true; }, }; }, }); key_handler({ keyCode: LEFT_ARROW, }); assert(last_pill_focused); }); run_test('comma', () => { const info = set_up(); const config = info.config; const items = info.items; const pill_input = info.pill_input; const container = info.container; const widget = input_pill.create(config); widget.appendValue('blue,red'); assert.deepEqual(widget.items(), [ items.blue, items.red, ]); const COMMA = 188; const key_handler = container.get_on_handler('keydown', '.input'); pill_input.text = () => ' yel'; key_handler({ keyCode: COMMA, preventDefault: noop, }); assert.deepEqual(widget.items(), [ items.blue, items.red, ]); pill_input.text = () => ' yellow'; key_handler({ keyCode: COMMA, }); assert.deepEqual(widget.items(), [ items.blue, items.red, items.yellow, ]); }); run_test('enter key with text', () => { const info = set_up(); const config = info.config; const items = info.items; const container = info.container; const widget = input_pill.create(config); widget.appendValue('blue,red'); assert.deepEqual(widget.items(), [ items.blue, items.red, ]); const ENTER = 13; const key_handler = container.get_on_handler('keydown', '.input'); key_handler({ keyCode: ENTER, preventDefault: noop, stopPropagation: noop, target: { innerText: ' yellow ', }, }); assert.deepEqual(widget.items(), [ items.blue, items.red, items.yellow, ]); }); run_test('insert_remove', () => { const info = set_up(); const config = info.config; const pill_input = info.pill_input; const items = info.items; const container = info.container; const inserted_html = []; pill_input.before = function (elem) { inserted_html.push(elem.html()); }; const widget = input_pill.create(config); let created; let removed; widget.onPillCreate(function () { created = true; }); widget.onPillRemove(function () { removed = true; }); widget.appendValue('blue,chartreuse,red,yellow,mauve'); assert(created); assert(!removed); assert.deepEqual(inserted_html, [ pill_html('BLUE', 'some_id1', example_img_link), pill_html('RED', 'some_id2'), pill_html('YELLOW', 'some_id3'), ]); assert.deepEqual(widget.items(), [ items.blue, items.red, items.yellow, ]); assert.equal(pill_input.text(), 'chartreuse, mauve'); assert.equal(widget.is_pending(), true); widget.clear_text(); assert.equal(pill_input.text(), ''); assert.equal(widget.is_pending(), false); const BACKSPACE = 8; let key_handler = container.get_on_handler('keydown', '.input'); key_handler({ keyCode: BACKSPACE, target: { innerText: '', }, preventDefault: noop, }); assert(removed); assert.deepEqual(widget.items(), [ items.blue, items.red, ]); let next_pill_focused = false; const next_pill_stub = { focus: () => { next_pill_focused = true; }, }; const focus_pill_stub = { next: () => next_pill_stub, data: (field) => { assert.equal(field, 'id'); return 'some_id1'; }, }; container.set_find_results('.pill:focus', focus_pill_stub); key_handler = container.get_on_handler('keydown', '.pill'); key_handler({ keyCode: BACKSPACE, preventDefault: noop, }); assert(next_pill_focused); }); run_test('exit button on pill', () => { const info = set_up(); const config = info.config; const items = info.items; const container = info.container; const widget = input_pill.create(config); widget.appendValue('blue,red'); let next_pill_focused = false; const next_pill_stub = { focus: () => { next_pill_focused = true; }, }; const curr_pill_stub = { next: () => next_pill_stub, data: (field) => { assert.equal(field, 'id'); return 'some_id1'; }, }; const exit_button_stub = { to_$: () => { return { closest: (sel) => { assert.equal(sel, '.pill'); return curr_pill_stub; }, }; }, }; const e = { stopPropagation: noop, }; const exit_click_handler = container.get_on_handler('click', '.exit'); exit_click_handler.call(exit_button_stub, e); assert(next_pill_focused); assert.deepEqual(widget.items(), [ items.red, ]); }); run_test('misc things', () => { const info = set_up(); const config = info.config; const container = info.container; const pill_input = info.pill_input; const widget = input_pill.create(config); // animation const animation_end_handler = container.get_on_handler('animationend', '.input'); let shake_class_removed = false; const input_stub = { to_$: () => { return { removeClass: (cls) => { assert.equal(cls, 'shake'); shake_class_removed = true; }, }; }, }; animation_end_handler.call(input_stub); assert(shake_class_removed); // bad data blueslip.expect('error', 'no display_value returned'); widget.appendValidatedData('this-has-no-item-attribute'); // click on container const container_click_handler = container.get_on_handler('click'); const stub = $.create('the-pill-container'); stub.set_find_results('.input', pill_input); stub.is = (sel) => { assert.equal(sel, '.pill-container'); return true; }; const this_ = { to_$: () => stub, }; container_click_handler.call(this_, {target: this_}); });