"use strict"; const {strict: assert} = require("assert"); const {mock_esm, mock_template, set_global, zrequire} = require("../zjsunit/namespace"); const {run_test} = require("../zjsunit/test"); const blueslip = require("../zjsunit/zblueslip"); const $ = require("../zjsunit/zjquery"); set_global("document", {}); const noop = () => {}; const example_img_link = "http://example.com/example.png"; mock_esm("../../static/js/ui_util", { place_caret_at_end: noop, }); set_global("getSelection", () => ({ anchorOffset: 0, })); const render_input_pill = mock_template("input_pill", true); const input_pill = zrequire("input_pill"); function pill_html(value, data_id, img_src) { const has_image = img_src !== undefined; const opts = { id: data_id, display_value: value, has_image, }; if (has_image) { opts.img_src = img_src; } return require("../../static/templates/input_pill.hbs")(opts); } function override_random_id({override}) { let id_seq = 0; override(input_pill, "random_id", () => { id_seq += 1; return "some_id" + id_seq; }); } run_test("random_id", () => { // just get coverage on a simple one-liner: input_pill.random_id(); }); run_test("basics", ({override}) => { override(render_input_pill, "f", (data, html) => { assert.equal(data.display_value, "JavaScript"); return html; }); override_random_id({override}); const config = {}; blueslip.expect("error", "Pill needs container."); input_pill.create(config); 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.expect("error", "Pill needs get_text_from_item"); config.create_item_from_text = noop; input_pill.create(config); config.get_text_from_item = noop; const widget = input_pill.create(config); // type for a pill can be any string but it needs to be // defined while creating any pill. const item = { display_value: "JavaScript", language: "js", type: "language", img_src: example_img_link, }; let inserted_before; const expected_html = pill_html("JavaScript", "some_id1", example_img_link); pill_input.before = (elem) => { inserted_before = true; assert.equal(elem.html(), expected_html); }; widget.appendValidatedData(item); assert.ok(inserted_before); assert.deepEqual(widget.items(), [item]); }); function set_up() { const items = { blue: { display_value: "BLUE", description: "color of the sky", type: "color", img_src: example_img_link, }, red: { display_value: "RED", type: "color", description: "color of stop signs", }, yellow: { display_value: "YELLOW", type: "color", description: "color of bananas", }, }; const pill_input = $.create("pill_input"); pill_input[0] = {}; pill_input.before = () => {}; const create_item_from_text = (text) => items[text]; const container = $.create("container"); container.set_find_results(".input", pill_input); const config = { container, create_item_from_text, get_text_from_item: (item) => item.display_value, }; return { config, pill_input, items, container, }; } run_test("copy from pill", ({override}) => { override(render_input_pill, "f", (data, html) => { assert.ok(["BLUE", "RED"].includes(data.display_value)); return html; }); override_random_id({override}); 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", ({override}) => { override(render_input_pill, "f", (data, html) => { assert.equal(typeof data.has_image, "boolean"); return html; }); 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]); let entered = false; widget.createPillonPaste(() => { entered = true; }); paste_handler(e); assert.ok(entered); }); run_test("arrows on pills", ({override}) => { override(render_input_pill, "f", (data, html) => { assert.equal(typeof data.has_image, "boolean"); return html; }); 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({ key: c, }); } let prev_focused = false; let next_focused = false; const pill_stub = { prev: () => ({ trigger: (type) => { if (type === "focus") { prev_focused = true; } }, }), next: () => ({ trigger: (type) => { if (type === "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("ArrowLeft"); assert.ok(prev_focused); test_key("ArrowRight"); assert.ok(next_focused); }); run_test("left arrow on input", ({override}) => { override(render_input_pill, "f", (data, html) => { assert.equal(typeof data.display_value, "string"); return html; }); 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", ".input"); let last_pill_focused = false; container.set_find_results(".pill", { last: () => ({ trigger: (type) => { if (type === "focus") { last_pill_focused = true; } }, }), }); key_handler({ key: "ArrowLeft", }); assert.ok(last_pill_focused); }); run_test("comma", ({override}) => { override(render_input_pill, "f", (data, html) => { assert.equal(typeof data.display_value, "string"); return html; }); 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 key_handler = container.get_on_handler("keydown", ".input"); pill_input.text(" yel"); key_handler({ key: ",", preventDefault: noop, }); assert.deepEqual(widget.items(), [items.blue, items.red]); pill_input.text(" yellow"); key_handler({ key: ",", preventDefault: noop, }); assert.deepEqual(widget.items(), [items.blue, items.red, items.yellow]); }); run_test("Enter key with text", ({override}) => { override(render_input_pill, "f", (data, html) => { assert.equal(typeof data.display_value, "string"); return html; }); 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 key_handler = container.get_on_handler("keydown", ".input"); key_handler({ key: "Enter", preventDefault: noop, stopPropagation: noop, target: { textContent: " yellow ", }, }); assert.deepEqual(widget.items(), [items.blue, items.red, items.yellow]); }); run_test("insert_remove", ({override}) => { override(render_input_pill, "f", (data, html) => { assert.equal(typeof data.display_value, "string"); assert.ok(html.startsWith, "