"use strict"; let { SyncedTabs } = ChromeUtils.importESModule( "resource://services-sync/SyncedTabs.sys.mjs" ); let { SyncedTabsListStore } = ChromeUtils.importESModule( "resource:///modules/syncedtabs/SyncedTabsListStore.sys.mjs" ); const FIXTURE = [ { id: "2xU5h-4bkWqA", type: "client", lastModified: 1492201200, name: "laptop", isMobile: false, tabs: [ { type: "tab", title: "Firefox for iOS — Mobile Web browser for your iPhone, iPad and iPod touch — Mozilla", url: "https://www.mozilla.org/en-US/firefox/ios/?utm_source=firefox-browser&utm_medium=firefox-browser&utm_campaign=synced-tabs-sidebar", icon: "moz-anno:favicon:https://www.mozilla.org/media/img/firefox/favicon.dc6635050bf5.ico", client: "2xU5h-4bkWqA", lastUsed: 1451519425, }, { type: "tab", title: "Firefox Nightly First Run Page", url: "https://www.mozilla.org/en-US/firefox/nightly/firstrun/?oldversion=45.0a1", icon: "moz-anno:favicon:https://www.mozilla.org/media/img/firefox/favicon-nightly.560395bbb2e1.png", client: "2xU5h-4bkWqA", lastUsed: 1451519420, }, ], }, { id: "OL3EJCsdb2JD", type: "client", lastModified: 1492201200, name: "desktop", isMobile: false, tabs: [], }, ]; add_task(async function testGetDataEmpty() { let store = new SyncedTabsListStore(SyncedTabs); let spy = sinon.spy(); sinon.stub(SyncedTabs, "getTabClients").callsFake(() => { return Promise.resolve([]); }); store.on("change", spy); await store.getData(); Assert.ok(SyncedTabs.getTabClients.calledWith("")); Assert.ok( spy.calledWith({ clients: [], canUpdateAll: false, canUpdateInput: false, filter: "", inputFocused: false, }) ); await store.getData("filter"); Assert.ok(SyncedTabs.getTabClients.calledWith("filter")); Assert.ok( spy.calledWith({ clients: [], canUpdateAll: false, canUpdateInput: true, filter: "filter", inputFocused: false, }) ); SyncedTabs.getTabClients.restore(); }); add_task(async function testRowSelectionWithoutFilter() { let store = new SyncedTabsListStore(SyncedTabs); let spy = sinon.spy(); sinon.stub(SyncedTabs, "getTabClients").callsFake(() => { return Promise.resolve(FIXTURE); }); await store.getData(); SyncedTabs.getTabClients.restore(); store.on("change", spy); store.selectRow(0, -1); Assert.ok(spy.args[0][0].canUpdateAll, "can update the whole view"); Assert.ok(spy.args[0][0].clients[0].selected, "first client is selected"); store.moveSelectionUp(); Assert.ok( spy.calledOnce, "can't move up past first client, no change triggered" ); store.selectRow(0, 0); Assert.ok( spy.args[1][0].clients[0].tabs[0].selected, "first tab of first client is selected" ); store.selectRow(0, 0); Assert.ok(spy.calledTwice, "selecting same row doesn't trigger change"); store.selectRow(0, 1); Assert.ok( spy.args[2][0].clients[0].tabs[1].selected, "second tab of first client is selected" ); store.selectRow(1); Assert.ok(spy.args[3][0].clients[1].selected, "second client is selected"); store.moveSelectionDown(); Assert.equal( spy.callCount, 4, "can't move selection down past last client, no change triggered" ); store.moveSelectionUp(); Assert.equal(spy.callCount, 5, "changed"); Assert.ok( spy.args[4][0].clients[0].tabs[FIXTURE[0].tabs.length - 1].selected, "move selection up from client selects last tab of previous client" ); store.moveSelectionUp(); Assert.ok( spy.args[5][0].clients[0].tabs[FIXTURE[0].tabs.length - 2].selected, "move selection up from tab selects previous tab of client" ); }); add_task(async function testToggleBranches() { let store = new SyncedTabsListStore(SyncedTabs); let spy = sinon.spy(); sinon.stub(SyncedTabs, "getTabClients").callsFake(() => { return Promise.resolve(FIXTURE); }); await store.getData(); SyncedTabs.getTabClients.restore(); store.selectRow(0); store.on("change", spy); let clientId = FIXTURE[0].id; store.closeBranch(clientId); Assert.ok(spy.args[0][0].clients[0].closed, "first client is closed"); store.openBranch(clientId); Assert.ok(!spy.args[1][0].clients[0].closed, "first client is open"); store.toggleBranch(clientId); Assert.ok(spy.args[2][0].clients[0].closed, "first client is toggled closed"); store.moveSelectionDown(); Assert.ok( spy.args[3][0].clients[1].selected, "selection skips tabs if client is closed" ); store.moveSelectionUp(); Assert.ok( spy.args[4][0].clients[0].selected, "selection skips tabs if client is closed" ); }); add_task(async function testRowSelectionWithFilter() { let store = new SyncedTabsListStore(SyncedTabs); let spy = sinon.spy(); sinon.stub(SyncedTabs, "getTabClients").callsFake(() => { return Promise.resolve(FIXTURE); }); await store.getData("filter"); SyncedTabs.getTabClients.restore(); store.on("change", spy); store.selectRow(0); Assert.ok( spy.args[0][0].clients[0].tabs[0].selected, "first tab is selected" ); store.moveSelectionUp(); Assert.ok( spy.calledOnce, "can't move up past first tab, no change triggered" ); store.moveSelectionDown(); Assert.ok( spy.args[1][0].clients[0].tabs[1].selected, "selection skips tabs if client is closed" ); store.moveSelectionDown(); Assert.equal( spy.callCount, 2, "can't move selection down past last tab, no change triggered" ); store.selectRow(1); Assert.equal(spy.callCount, 2, "doesn't trigger change if same row selected"); }); add_task(async function testFilterAndClearFilter() { let store = new SyncedTabsListStore(SyncedTabs); let spy = sinon.spy(); sinon.stub(SyncedTabs, "getTabClients").callsFake(() => { return Promise.resolve(FIXTURE); }); store.on("change", spy); await store.getData("filter"); Assert.ok(SyncedTabs.getTabClients.calledWith("filter")); Assert.ok(!spy.args[0][0].canUpdateAll, "can't update all"); Assert.ok(spy.args[0][0].canUpdateInput, "can update just input"); store.selectRow(0); Assert.equal(spy.args[1][0].filter, "filter"); Assert.ok(spy.args[1][0].clients[0].tabs[0].selected, "tab is selected"); await store.clearFilter(); Assert.ok(SyncedTabs.getTabClients.calledWith("")); Assert.ok(!spy.args[2][0].canUpdateAll, "can't update all"); Assert.ok(!spy.args[2][0].canUpdateInput, "can't just update input"); Assert.equal(spy.args[2][0].filter, ""); Assert.ok( !spy.args[2][0].clients[0].tabs[0].selected, "tab is no longer selected" ); SyncedTabs.getTabClients.restore(); }); add_task(async function testFocusBlurInput() { let store = new SyncedTabsListStore(SyncedTabs); let spy = sinon.spy(); sinon.stub(SyncedTabs, "getTabClients").callsFake(() => { return Promise.resolve(FIXTURE); }); store.on("change", spy); await store.getData(); SyncedTabs.getTabClients.restore(); Assert.ok(!spy.args[0][0].canUpdateAll, "must rerender all"); store.selectRow(0); Assert.ok(!spy.args[1][0].inputFocused, "input is not focused"); Assert.ok(spy.args[1][0].clients[0].selected, "client is selected"); Assert.ok(spy.args[1][0].clients[0].focused, "client is focused"); store.focusInput(); Assert.ok(spy.args[2][0].inputFocused, "input is focused"); Assert.ok(spy.args[2][0].clients[0].selected, "client is still selected"); Assert.ok(!spy.args[2][0].clients[0].focused, "client is no longer focused"); store.blurInput(); Assert.ok(!spy.args[3][0].inputFocused, "input is not focused"); Assert.ok(spy.args[3][0].clients[0].selected, "client is selected"); Assert.ok(spy.args[3][0].clients[0].focused, "client is focused"); });