UNPKG

@launchmenu/core

Version:

An environment for visual keyboard controlled applets

993 lines 113 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const model_react_1 = require("model-react"); const getCategoryAction_1 = require("../../../actions/types/category/getCategoryAction"); const onCursorAction_1 = require("../../../actions/types/onCursor/onCursorAction"); const onMenuChangAction_1 = require("../../../actions/types/onMenuChange/onMenuChangAction"); const onSelectAction_1 = require("../../../actions/types/onSelect/onSelectAction"); const context_helper_1 = require("../../../_tests/context.helper"); const wait_helper_1 = require("../../../_tests/wait.helper"); const PrioritizedMenu_1 = require("../PrioritizedMenu"); const Priority_1 = require("../priority/Priority"); const MenuItem_helper_1 = require("./MenuItem.helper"); const PrioritizedMenuItem_helper_1 = require("./PrioritizedMenuItem.helper"); const createMenu = (items) => { const menu = new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { batchInterval: 10, }); if (items) { items.forEach(item => menu.addItem(item)); menu.flushBatch(); } return menu; }; describe("PrioritizedMenu", () => { describe("new PrioritizedMenu", () => { it("Properly creates a new prioritized menu", () => { new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext); }); it("Can be initialized with a config", () => { new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { batchInterval: 200, }); }); }); describe("PrioritizedMenu.addItem(s) / PrioritizedMenu.getItems", () => { it("Can add an item", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); menu.addItem(item); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item.item]); }); it("Can add multiple items at once", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); menu.addItems([item, item2]); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item.item, item2.item]); }); describe("Handles priorities properly", () => { it("Orders the items by priority", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4 }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item2.item, item3.item, item.item]); }); it("Considers nested 'nested' priorities", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: [Priority_1.Priority.MEDIUM, Priority_1.Priority.HIGH], }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: [Priority_1.Priority.MEDIUM, Priority_1.Priority.MEDIUM], }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: [Priority_1.Priority.MEDIUM, Priority_1.Priority.MEDIUM], }); const item4 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: [Priority_1.Priority.MEDIUM, Priority_1.Priority.LOW], }); const item5 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: Priority_1.Priority.HIGH }); const item6 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: [Priority_1.Priority.HIGH, Priority_1.Priority.HIGH], }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); menu.addItem(item4); menu.addItem(item5); menu.addItem(item6); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([ item6.item, item5.item, item.item, item2.item, item3.item, item4.item, ]); }); it("Orders items with equivalent priority in addition order", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item2.item, item3.item, item.item]); }); it("Doesn't add items with priority 0", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 0 }); menu.addItem(item); menu.addItem(item2); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item.item]); }); }); it("Batches additions", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4 }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); const listener = jest.fn(); new model_react_1.Observer(h => menu.getItems(h)).listen(listener, true); await wait_helper_1.wait(20); expect(listener.mock.calls.length).toBe(2); // First observer call there weren't any items, second call contains all items expect(listener.mock.calls[0][0]).toEqual([]); expect(listener.mock.calls[1][0]).toEqual([ item2.item, item3.item, item.item, ]); }); describe("Uses categories", () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem({ name: "c1" }), }; const someCategory2 = { name: "John", description: "some category for John", item: MenuItem_helper_1.createTestDummyMenuItem({ name: "c2" }), }; const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 5, name: "0" }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, name: "1" }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3, category: someCategory, name: "2", }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 5, category: someCategory, name: "3", }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 6, category: someCategory2, name: "4", }), ]; it("Inserts the correct category in the menu", async () => { const menu = createMenu(); menu.addItem(items[2]); menu.addItem(items[3]); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([ someCategory.item, items[3].item, items[2].item, ]); }); it("Orders the categories properly based on top priority", async () => { const menu = createMenu(); items.forEach(item => menu.addItem(item)); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([ items[0].item, items[1].item, someCategory2.item, items[4].item, someCategory.item, items[3].item, items[2].item, ]); }); it("Moves items to their new category when its category changes", async () => { const menu = createMenu(); const category = new model_react_1.Field(someCategory); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ generateID: true, actionBindings: h => { return [getCategoryAction_1.getCategoryAction.createBinding(category.get(h))]; }, name: "item", }); items.forEach(item => menu.addItem(item)); menu.addItem(item); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([ items[0].item, items[1].item, someCategory2.item, items[4].item, someCategory.item, items[3].item, items[2].item, item.item, ]); category.set(someCategory2); await wait_helper_1.wait(); expect(menu.getItems()).toEqual([ items[0].item, items[1].item, someCategory2.item, items[4].item, item.item, someCategory.item, items[3].item, items[2].item, ]); category.set(someCategory); await wait_helper_1.wait(); expect(menu.getItems()).toEqual([ items[0].item, items[1].item, someCategory2.item, items[4].item, someCategory.item, items[3].item, items[2].item, item.item, ]); }); }); it("Calls onMenuChange actions", async () => { const onMenuChange = jest.fn(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, actionBindings: [onMenuChangAction_1.onMenuChangeAction.createBinding(onMenuChange)], }); const menu = createMenu(); menu.addItem(item); await wait_helper_1.wait(20); expect(onMenuChange.mock.calls.length).toBe(1); expect(onMenuChange.mock.calls[0]).toEqual([menu, true]); }); }); describe("Menu.getCategories", () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const someCategory2 = { name: "John", description: "some category for John", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 5 }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3, category: someCategory }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 5, category: someCategory }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 6, category: someCategory2 }), ]; it("Correctly retrieves the categories", async () => { const menu = createMenu(); items.forEach(item => menu.addItem(item)); await wait_helper_1.wait(20); expect(menu.getCategories()).toEqual([ { category: undefined, items: [items[0].item, items[1].item] }, { category: someCategory2, items: [items[4].item] }, { category: someCategory, items: [items[3].item, items[2].item] }, ]); }); it("Can be subscribed to", async () => { const menu = createMenu(); items.forEach(item => menu.addItem(item)); await wait_helper_1.wait(20); const cb = jest.fn(); new model_react_1.Observer(h => menu.getCategories(h)).listen(cb); const newItem = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4, category: someCategory2, }); menu.addItem(newItem); await wait_helper_1.wait(20); expect(cb.mock.calls.length).toBe(1); expect(cb.mock.calls[0][0]).toEqual([ { category: undefined, items: [items[0].item, items[1].item] }, { category: someCategory2, items: [items[4].item, newItem.item] }, { category: someCategory, items: [items[3].item, items[2].item] }, ]); }); }); describe("PrioritizedMenu.removeItem(s)", () => { it("Removes the specified items, based on id", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4, generateID: true }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3, generateID: true }); const item4 = { ...PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 6 }), ID: item3.ID, }; menu.addItem(item); menu.addItem(item2); menu.addItem(item3); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item2.item, item3.item, item.item]); menu.removeItem(item2); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item3.item, item.item]); menu.removeItem(item4); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item.item]); }); it("Removes the specified items, based on item equivalence (slower)", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4 }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item2.item, item3.item, item.item]); menu.removeItem(item2); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item3.item, item.item]); }); it("Removes multiple items", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4 }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item2.item, item3.item, item.item]); menu.removeItems([item, item2]); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item3.item]); }); it("Batches item removals", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, generateID: true }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4, generateID: true }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3, generateID: true }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); await wait_helper_1.wait(20); const listener = jest.fn(); new model_react_1.Observer(h => menu.getItems(h)).listen(listener, true); menu.removeItem(item2); menu.removeItem(item3); await wait_helper_1.wait(20); // First observer call there weren't any items, second call contains all items expect(listener.mock.calls[0][0]).toEqual([ item2.item, item3.item, item.item, ]); expect(listener.mock.calls[1][0]).toEqual([item.item]); }); it("Removes the specified items, even if added in the same batch", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4, generateID: true }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); menu.removeItem(item2); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([item3.item, item.item]); }); it("Considers categories", async () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const someCategory2 = { name: "John", description: "some category for John", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 5, generateID: true }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, generateID: true }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3, category: someCategory, generateID: true, }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 5, category: someCategory, generateID: true, }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 6, category: someCategory2, generateID: true, }), ]; const menu = createMenu(); items.forEach(item => menu.addItem(item)); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([ items[0].item, items[1].item, someCategory2.item, items[4].item, someCategory.item, items[3].item, items[2].item, ]); menu.removeItem(items[2]); menu.removeItem(items[3]); menu.removeItem(items[1]); await wait_helper_1.wait(20); expect(menu.getItems()).toEqual([ items[0].item, someCategory2.item, items[4].item, ]); }); it("Calls onMenuChange actions", async () => { const onMenuChange = jest.fn(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, generateID: true, actionBindings: [onMenuChangAction_1.onMenuChangeAction.createBinding(onMenuChange)], }); const menu = createMenu(); menu.addItem(item); await wait_helper_1.wait(20); expect(onMenuChange.mock.calls.length).toBe(1); expect(onMenuChange.mock.calls[0]).toEqual([menu, true]); menu.removeItem(item); await wait_helper_1.wait(20); expect(onMenuChange.mock.calls.length).toBe(2); expect(onMenuChange.mock.calls[1]).toEqual([menu, false]); }); }); describe("PrioritizedMenu.flushBatch", () => { it("Synchronously flushes item addition changes", () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, generateID: true }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4, generateID: true }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3, generateID: true }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); menu.flushBatch(); expect(menu.getItems()).toEqual([item2.item, item3.item, item.item]); }); it("Synchronously flushes item removal changes", async () => { const menu = createMenu(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, generateID: true }); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 4, generateID: true }); const item3 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3, generateID: true }); menu.addItem(item); menu.addItem(item2); menu.addItem(item3); await wait_helper_1.wait(20); menu.removeItem(item2); menu.removeItem(item3); menu.flushBatch(); expect(menu.getItems()).toEqual([item.item]); }); }); describe("PrioritizedMenu.setSelected / PrioritizedMenu.getSelected", () => { const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 5 }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 2 }), ]; it("Can select items", () => { const menu = createMenu(items); menu.setSelected(items[0].item, true); expect(menu.getSelected()).toEqual([items[0].item]); menu.setSelected(items[3].item, true); expect(menu.getSelected()).toEqual([items[0].item, items[3].item]); }); it("Can deselect items", () => { const menu = createMenu(items); menu.setSelected(items[0].item, true); menu.setSelected(items[3].item, true); expect(menu.getSelected()).toEqual([items[0].item, items[3].item]); menu.setSelected(items[3].item, false); expect(menu.getSelected()).toEqual([items[0].item]); menu.setSelected(items[0].item, false); expect(menu.getSelected()).toEqual([]); }); it("Calls onSelect actions", () => { let selectCount = 0; let deselectCount = 0; const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ actionBindings: [ onSelectAction_1.onSelectAction.createBinding((selected, m) => { if (selected) selectCount++; else deselectCount++; expect(m).toEqual(menu); }), ], }); const menu = createMenu([...items, item]); expect(selectCount).toBe(0); expect(deselectCount).toBe(0); menu.setSelected(item.item, true); expect(selectCount).toBe(1); expect(deselectCount).toBe(0); menu.setSelected(item.item, false); expect(selectCount).toBe(1); expect(deselectCount).toBe(1); }); it("Can't select items that aren't in the menu", () => { const menu = createMenu(items); const item = MenuItem_helper_1.createTestDummyMenuItem(); menu.setSelected(item, true); expect(menu.getSelected()).toEqual([]); }); it("Automatically deselects items when removed", () => { const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ generateID: true }); const menu = createMenu([...items, item]); menu.setSelected(item.item, true); expect(menu.getSelected()).toEqual([item.item]); menu.removeItem(item); menu.flushBatch(); expect(menu.getSelected()).toEqual([]); }); }); describe("PrioritizedMenu.setCursor / PrioritizedMenu.getCursor", () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ noSelect: true, generateID: true }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ generateID: true }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory, generateID: true }), ]; it("Has the correct initial cursor", () => { expect(createMenu().getCursor()).toEqual(null); // It won't select unselectable items expect(createMenu(items).getCursor()).toEqual(items[1].item); }); it("Properly updates the cursor", () => { const menu = createMenu(items); menu.setCursor(items[2].item); expect(menu.getCursor()).toEqual(items[2].item); }); it("Calls onCursor actions", () => { let selectCount = 0; let deselectCount = 0; const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ actionBindings: [ onCursorAction_1.onCursorAction.createBinding((selected, m) => { if (selected) selectCount++; else deselectCount++; expect(m).toEqual(menu); }), ], }); const menu = createMenu(items); menu.addItem(item); menu.flushBatch(); expect(selectCount).toBe(0); expect(deselectCount).toBe(0); menu.setCursor(item.item); expect(selectCount).toBe(1); expect(deselectCount).toBe(0); menu.setCursor(null); expect(selectCount).toBe(1); expect(deselectCount).toBe(1); }); it("Can't set item as cursor if not in the menu", () => { const menu = createMenu(items); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); menu.setCursor(item.item); expect(menu.getCursor()).not.toEqual(item.item); }); it("Automatically selects another cursor when cursor is removed", () => { const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ generateID: true }); const menu = createMenu([...items, item]); menu.setCursor(item.item); expect(menu.getCursor()).toEqual(item.item); menu.removeItem(item); menu.flushBatch(); expect(menu.getCursor()).not.toEqual(item.item); expect(menu.getCursor()).not.toEqual(null); items.forEach(item => menu.removeItem(item)); menu.flushBatch(); expect(menu.getItems().length).toBe(0); expect(menu.getCursor()).toEqual(null); }); }); describe("PrioritizedMenu.getAllSelected", () => { const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), ]; it("Combines the selection and cursor if present", () => { const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); const menu = createMenu([...items, item, item2]); menu.setCursor(item.item); expect(menu.getAllSelected()).toEqual([item.item]); menu.setSelected(item2.item); expect(menu.getAllSelected()).toEqual([item2.item, item.item]); }); it("Returns the selection if no cursor is present", () => { const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); const menu = createMenu([...items, item, item2]); menu.setCursor(null); menu.setSelected(item2.item); expect(menu.getAllSelected()).toEqual([item2.item]); }); it("Only includes items once", () => { const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); const menu = createMenu([...items, item, item2]); menu.setCursor(item.item); expect(menu.getAllSelected()).toEqual([item.item]); menu.setSelected(item.item); expect(menu.getAllSelected()).toEqual([item.item]); }); }); describe("PrioritizedMenu.destroy", () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), ]; let menu; beforeEach(() => { menu = createMenu(items); }); it("Deselects all items", () => { let deselectCount = 0; const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ actionBindings: [ onSelectAction_1.onSelectAction.createBinding((selected, m) => { if (!selected) deselectCount++; expect(m).toEqual(menu); }), ], }); menu.addItem(item); menu.flushBatch(); menu.setSelected(item.item, true); expect(menu.getSelected()).toEqual([item.item]); expect(deselectCount).toBe(0); menu.destroy(); expect(menu.getSelected()).toEqual([]); expect(deselectCount).toBe(1); }); it("Deselects the cursor", () => { let deselectCount = 0; const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ actionBindings: [ onCursorAction_1.onCursorAction.createBinding((selected, m) => { if (!selected) deselectCount++; expect(m).toEqual(menu); }), ], }); menu.addItem(item); menu.flushBatch(); menu.setCursor(item.item); expect(menu.getCursor()).toEqual(item.item); expect(deselectCount).toBe(0); menu.destroy(); expect(menu.getCursor()).toEqual(null); expect(deselectCount).toBe(1); }); it("Doesn't remove all items", () => { // Logically the items are removed, but for rendering purposes they are still obtainable // One shouldn't logically rely on these items if the menu indicates it has been destroyed however const ci = [items[0].item, items[1].item, someCategory.item, items[2].item]; expect(menu.getItems()).toEqual(ci); menu.destroy(); expect(menu.getItems()).toEqual(ci); }); it("Blocks changing the cursor", () => { let selectCount = 0; const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ actionBindings: [ onCursorAction_1.onCursorAction.createBinding((selected, m) => { if (selected) selectCount++; expect(m).toEqual(menu); }), ], }); menu.addItem(item); menu.flushBatch(); expect(menu.getCursor()).not.toEqual(null); menu.destroy(); menu.setCursor(items[0].item); expect(menu.getCursor()).toEqual(null); expect(selectCount).toBe(0); }); it("Blocks selecting of items", () => { let selectCount = 0; const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ actionBindings: [ onSelectAction_1.onSelectAction.createBinding((selected, m) => { if (selected) selectCount++; expect(m).toEqual(menu); }), ], }); menu.addItem(item); menu.flushBatch(); menu.destroy(); menu.setSelected(items[0].item, true); expect(menu.getSelected()).toEqual([]); expect(selectCount).toBe(0); }); }); describe("Getters can be subscribed to", () => { let menu; const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), ]; beforeEach(() => { menu = createMenu(items); }); describe("Menu.getItems", () => { it("Correctly subscribes to changes", () => { const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); const item2 = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}); const callback = jest.fn(() => { }); expect(menu.getItems({ call: callback, registerRemover: () => { } })).toEqual(items.map(({ item }) => item)); expect(callback.mock.calls.length).toBe(0); menu.addItem(item); menu.addItem(item2); menu.flushBatch(); expect(callback.mock.calls.length).toBe(1); }); it("Correctly indicates the loading state of the menu", async () => { const loading = new model_react_1.Field(false); const menu = new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { batchInterval: 10, isLoading: loading, }); const callback = jest.fn(() => { }); menu.getItems({ markIsLoading: callback }); expect(callback.mock.calls.length).toBe(0); loading.set(true); menu.getItems({ markIsLoading: callback }); expect(callback.mock.calls.length).toBe(1); menu.getItems({ markIsLoading: callback }); expect(callback.mock.calls.length).toBe(2); loading.set(false); menu.getItems({ markIsLoading: callback }); expect(callback.mock.calls.length).toBe(2); }); it("Correctly calls the hook when the loading status changes", async () => { const loading = new model_react_1.Field(false); const menu = new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { batchInterval: 10, isLoading: loading, }); const callback = jest.fn(() => { }); new model_react_1.Observer(h => menu.getItems(h)).listen(callback, true); expect(callback.mock.calls.length).toBe(1); expect(callback.mock.calls[0][1]).toEqual({ isLoading: false, exceptions: [], }); loading.set(true); await wait_helper_1.wait(); expect(callback.mock.calls.length).toBe(2); expect(callback.mock.calls[1][1]).toEqual({ isLoading: true, exceptions: [], }); loading.set(false); await wait_helper_1.wait(); expect(callback.mock.calls.length).toBe(3); expect(callback.mock.calls[2][1]).toEqual({ isLoading: false, exceptions: [], }); }); }); describe("Menu.getSelected", () => { it("Correctly subscribes to changes", () => { const callback = jest.fn(() => { }); expect(menu.getSelected({ call: callback, registerRemover: () => { } })).toEqual([]); expect(callback.mock.calls.length).toBe(0); menu.setSelected(items[2].item, true); expect(callback.mock.calls.length).toBe(1); }); }); describe("Menu.getCursor", () => { it("Correctly subscribes to changes", () => { const callback = jest.fn(() => { }); expect(menu.getCursor({ call: callback, registerRemover: () => { } })).toEqual(items[0].item); expect(callback.mock.calls.length).toBe(0); menu.setCursor(items[2].item); expect(callback.mock.calls.length).toBe(1); }); }); describe("Menu.getAllSelected", () => { it("Correctly subscribes to changes", () => { const callback = jest.fn(() => { }); expect(menu.getAllSelected({ call: callback, registerRemover: () => { } })).toEqual([items[0].item]); expect(callback.mock.calls.length).toBe(0); menu.setCursor(items[2].item); expect(callback.mock.calls.length).toBe(1); menu.setSelected(items[1].item, true); expect(callback.mock.calls.length).toBe(2); }); }); }); describe("categoryConfig", () => { describe("categoryConfig.maxItemCount", () => { const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 2 }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1 }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 3 }), ]; let menu; beforeEach(() => { menu = new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { maxItemCount: 2 }); }); it("Allows the number of items for each category to be limited", () => { items.forEach(item => menu.addItem(item)); menu.flushBatch(); expect(menu.getItems()).toEqual([items[2].item, items[0].item]); }); it("Doesn't considers categories separately", () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const menu = new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { maxItemCount: 2 }); const items2 = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory, priority: 4 }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), ]; items.forEach(item => menu.addItem(item)); items2.forEach(item => menu.addItem(item)); menu.flushBatch(); expect(menu.getItems()).toEqual([ items[2].item, someCategory.item, items2[0].item, ]); }); describe("OnMenuChange", () => { it("Doesn't call onMenuChange to inform about addition if the item wasn't added", () => { items.forEach(item => menu.addItem(item)); const onMenuChange = jest.fn(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, generateID: true, actionBindings: [onMenuChangAction_1.onMenuChangeAction.createBinding(onMenuChange)], }); menu.addItem(item); menu.flushBatch(); expect(onMenuChange.mock.calls.length).toBe(0); }); it("Does call onMenuChange to inform about removal if an item got pushed off the list", () => { const onMenuChange = jest.fn(); const item = PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ priority: 1, generateID: true, actionBindings: [onMenuChangAction_1.onMenuChangeAction.createBinding(onMenuChange)], }); menu.addItem(item); menu.flushBatch(); // Add higher priority items items.forEach(item => menu.addItem(item)); menu.addItem(item); menu.flushBatch(); expect(onMenuChange.mock.calls.length).toBe(2); expect(onMenuChange.mock.calls[0]).toEqual([menu, true]); expect(onMenuChange.mock.calls[1]).toEqual([menu, false]); }); }); }); describe("categoryConfig.getCategory", () => { it("Allows categories to be ignored", () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const menu = new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { getCategory: () => undefined, }); const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), ]; const items2 = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), ]; items.forEach(item => menu.addItem(item)); items2.forEach(item => menu.addItem(item)); menu.flushBatch(); expect(menu.getItems()).toEqual([ ...items.map(({ item }) => item), ...items2.map(({ item }) => item), ]); }); it("Allows categories to be altered", () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const menu = new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { getCategory: () => someCategory, }); const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), ]; const items2 = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), ]; items.forEach(item => menu.addItem(item)); items2.forEach(item => menu.addItem(item)); menu.flushBatch(); expect(menu.getItems()).toEqual([ someCategory.item, ...items.map(({ item }) => item), ...items2.map(({ item }) => item), ]); }); }); describe("categoryConfig.sortCategories", () => { it("Allows category orders to be changed", () => { const someCategory = { name: "Bob", description: "some category for Bob", item: MenuItem_helper_1.createTestDummyMenuItem(), }; const menu = new PrioritizedMenu_1.PrioritizedMenu(context_helper_1.dummyContext, { sortCategories: categories => categories.map(({ category }) => category).reverse(), }); const items = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({}), ]; const items2 = [ PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), PrioritizedMenuItem_helper_1.createDummyPrioritizedMenuItem({ category: someCategory }), PrioritizedMenuItem_hel