"use strict"; let { SyncedTabs } = Cu.import("resource://services-sync/SyncedTabs.jsm", {}); let { SyncedTabsDeckComponent } = Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckComponent.js", {}); let { TabListComponent } = Cu.import("resource:///modules/syncedtabs/TabListComponent.js", {}); let { SyncedTabsListStore } = Cu.import("resource:///modules/syncedtabs/SyncedTabsListStore.js", {}); let { SyncedTabsDeckStore } = Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckStore.js", {}); let { TabListView } = Cu.import("resource:///modules/syncedtabs/TabListView.js", {}); let { DeckView } = Cu.import("resource:///modules/syncedtabs/SyncedTabsDeckView.js", {}); add_task(function* testInitUninit() { let deckStore = new SyncedTabsDeckStore(); let listComponent = {}; let ViewMock = sinon.stub(); let view = {render: sinon.spy(), destroy: sinon.spy(), container: {}}; ViewMock.returns(view); sinon.stub(SyncedTabs, "syncTabs", () => Promise.resolve()); sinon.spy(deckStore, "on"); sinon.stub(deckStore, "setPanels"); let component = new SyncedTabsDeckComponent({ window, deckStore, listComponent, SyncedTabs, DeckView: ViewMock, }); sinon.stub(component, "updatePanel"); component.init(); Assert.ok(SyncedTabs.syncTabs.called); SyncedTabs.syncTabs.restore(); Assert.ok(ViewMock.calledWithNew(), "view is instantiated"); Assert.equal(ViewMock.args[0][0], window); Assert.equal(ViewMock.args[0][1], listComponent); Assert.ok(ViewMock.args[0][2].onAndroidClick, "view is passed onAndroidClick prop"); Assert.ok(ViewMock.args[0][2].oniOSClick, "view is passed oniOSClick prop"); Assert.ok(ViewMock.args[0][2].onSyncPrefClick, "view is passed onSyncPrefClick prop"); Assert.equal(component.container, view.container, "component returns view's container"); Assert.ok(deckStore.on.calledOnce, "listener is added to store"); Assert.equal(deckStore.on.args[0][0], "change"); // Object.values only in nightly let values = Object.keys(component.PANELS).map(k => component.PANELS[k]); Assert.ok(deckStore.setPanels.calledWith(values), "panels are set on deck store"); Assert.ok(component.updatePanel.called); deckStore.emit("change", "mock state"); Assert.ok(view.render.calledWith("mock state"), "view.render is called on state change"); component.uninit(); Assert.ok(view.destroy.calledOnce, "view is destroyed on uninit"); }); function waitForObserver() { return new Promise((resolve, reject) => { Services.obs.addObserver((subject, topic) => { resolve(); }, SyncedTabs.TOPIC_TABS_CHANGED, false); }); } add_task(function* testObserver() { let deckStore = new SyncedTabsDeckStore(); let listStore = new SyncedTabsListStore(SyncedTabs); let listComponent = {}; let ViewMock = sinon.stub(); let view = {render: sinon.spy(), destroy: sinon.spy(), container: {}}; ViewMock.returns(view); sinon.stub(SyncedTabs, "syncTabs", () => Promise.resolve()); sinon.spy(deckStore, "on"); sinon.stub(deckStore, "setPanels"); sinon.stub(listStore, "getData"); let component = new SyncedTabsDeckComponent({ window, deckStore, listStore, listComponent, SyncedTabs, DeckView: ViewMock, }); sinon.spy(component, "observe"); sinon.stub(component, "updatePanel"); component.init(); SyncedTabs.syncTabs.restore(); Assert.ok(component.updatePanel.called, "triggers panel update during init"); Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED, ""); Assert.ok(component.observe.calledWith(null, SyncedTabs.TOPIC_TABS_CHANGED, ""), "component is notified"); Assert.ok(listStore.getData.called, "gets list data"); Assert.ok(component.updatePanel.calledTwice, "triggers panel update"); Services.obs.notifyObservers(null, FxAccountsCommon.ONLOGIN_NOTIFICATION, ""); Assert.ok(component.observe.calledWith(null, FxAccountsCommon.ONLOGIN_NOTIFICATION, ""), "component is notified of login"); Assert.equal(component.updatePanel.callCount, 3, "triggers panel update again"); }); add_task(function* testPanelStatus() { let deckStore = new SyncedTabsDeckStore(); let listStore = new SyncedTabsListStore(); let listComponent = {}; let fxAccounts = { accountStatus() {} }; let SyncedTabsMock = { getTabClients() {} }; sinon.stub(listStore, "getData"); let component = new SyncedTabsDeckComponent({ fxAccounts, deckStore, listComponent, SyncedTabs: SyncedTabsMock, }); let isAuthed = false; sinon.stub(fxAccounts, "accountStatus", () => Promise.resolve(isAuthed)); let result = yield component.getPanelStatus(); Assert.equal(result, component.PANELS.NOT_AUTHED_INFO); isAuthed = true; SyncedTabsMock.isConfiguredToSyncTabs = false; result = yield component.getPanelStatus(); Assert.equal(result, component.PANELS.TABS_DISABLED); SyncedTabsMock.isConfiguredToSyncTabs = true; SyncedTabsMock.hasSyncedThisSession = false; result = yield component.getPanelStatus(); Assert.equal(result, component.PANELS.TABS_FETCHING); SyncedTabsMock.hasSyncedThisSession = true; let clients = []; sinon.stub(SyncedTabsMock, "getTabClients", () => Promise.resolve(clients)); result = yield component.getPanelStatus(); Assert.equal(result, component.PANELS.SINGLE_DEVICE_INFO); clients = ["mock-client"]; result = yield component.getPanelStatus(); Assert.equal(result, component.PANELS.TABS_CONTAINER); fxAccounts.accountStatus.restore(); sinon.stub(fxAccounts, "accountStatus", () => Promise.reject("err")); result = yield component.getPanelStatus(); Assert.equal(result, component.PANELS.NOT_AUTHED_INFO); sinon.stub(component, "getPanelStatus", () => Promise.resolve("mock-panelId")); sinon.spy(deckStore, "selectPanel"); yield component.updatePanel(); Assert.ok(deckStore.selectPanel.calledWith("mock-panelId")); }); add_task(function* testActions() { let windowMock = { openUILink() {}, }; let chromeWindowMock = { gSyncUI: { openSetup() {} } }; sinon.spy(windowMock, "openUILink"); sinon.spy(chromeWindowMock.gSyncUI, "openSetup"); let getChromeWindowMock = sinon.stub(); getChromeWindowMock.returns(chromeWindowMock); let component = new SyncedTabsDeckComponent({ window: windowMock, getChromeWindowMock }); let href = Services.prefs.getCharPref("identity.mobilepromo.android") + "synced-tabs-sidebar"; component.openAndroidLink("mock-event"); Assert.ok(windowMock.openUILink.calledWith(href, "mock-event")); href = Services.prefs.getCharPref("identity.mobilepromo.ios") + "synced-tabs-sidebar"; component.openiOSLink("mock-event"); Assert.ok(windowMock.openUILink.calledWith(href, "mock-event")); component.openSyncPrefs(); Assert.ok(getChromeWindowMock.calledWith(windowMock)); Assert.ok(chromeWindowMock.gSyncUI.openSetup.called); });