test_SyncedTabsDeckComponent.js (7506B)
1 "use strict"; 2 3 let { SyncedTabs } = ChromeUtils.importESModule( 4 "resource://services-sync/SyncedTabs.sys.mjs" 5 ); 6 let { SyncedTabsDeckComponent } = ChromeUtils.importESModule( 7 "resource:///modules/syncedtabs/SyncedTabsDeckComponent.sys.mjs" 8 ); 9 let { SyncedTabsListStore } = ChromeUtils.importESModule( 10 "resource:///modules/syncedtabs/SyncedTabsListStore.sys.mjs" 11 ); 12 let { SyncedTabsDeckStore } = ChromeUtils.importESModule( 13 "resource:///modules/syncedtabs/SyncedTabsDeckStore.sys.mjs" 14 ); 15 const { UIState } = ChromeUtils.importESModule( 16 "resource://services-sync/UIState.sys.mjs" 17 ); 18 19 add_task(async function testInitUninit() { 20 let deckStore = new SyncedTabsDeckStore(); 21 let listComponent = {}; 22 let mockWindow = {}; 23 24 let ViewMock = sinon.stub(); 25 let view = { render: sinon.spy(), destroy: sinon.spy(), container: {} }; 26 ViewMock.returns(view); 27 28 sinon.stub(SyncedTabs, "syncTabs").callsFake(() => Promise.resolve()); 29 30 sinon.spy(deckStore, "on"); 31 sinon.stub(deckStore, "setPanels"); 32 33 let component = new SyncedTabsDeckComponent({ 34 window: mockWindow, 35 deckStore, 36 listComponent, 37 SyncedTabs, 38 DeckView: ViewMock, 39 }); 40 41 sinon.stub(component, "updatePanel"); 42 43 component.init(); 44 45 Assert.ok(SyncedTabs.syncTabs.called); 46 SyncedTabs.syncTabs.restore(); 47 48 Assert.ok(ViewMock.calledWithNew(), "view is instantiated"); 49 Assert.equal(ViewMock.args[0][0], mockWindow); 50 Assert.equal(ViewMock.args[0][1], listComponent); 51 Assert.ok( 52 ViewMock.args[0][2].onConnectDeviceClick, 53 "view is passed onConnectDeviceClick prop" 54 ); 55 Assert.ok( 56 ViewMock.args[0][2].onSyncPrefClick, 57 "view is passed onSyncPrefClick prop" 58 ); 59 60 Assert.equal( 61 component.container, 62 view.container, 63 "component returns view's container" 64 ); 65 66 Assert.ok(deckStore.on.calledOnce, "listener is added to store"); 67 Assert.equal(deckStore.on.args[0][0], "change"); 68 // Object.values only in nightly 69 let values = Object.keys(component.PANELS).map(k => component.PANELS[k]); 70 Assert.ok( 71 deckStore.setPanels.calledWith(values), 72 "panels are set on deck store" 73 ); 74 75 Assert.ok(component.updatePanel.called); 76 77 deckStore.emit("change", "mock state"); 78 Assert.ok( 79 view.render.calledWith("mock state"), 80 "view.render is called on state change" 81 ); 82 83 component.uninit(); 84 85 Assert.ok(view.destroy.calledOnce, "view is destroyed on uninit"); 86 }); 87 88 add_task(async function testObserver() { 89 let deckStore = new SyncedTabsDeckStore(); 90 let listStore = new SyncedTabsListStore(SyncedTabs); 91 let listComponent = {}; 92 let mockWindow = {}; 93 94 let ViewMock = sinon.stub(); 95 let view = { render: sinon.spy(), destroy: sinon.spy(), container: {} }; 96 ViewMock.returns(view); 97 98 sinon.stub(SyncedTabs, "syncTabs").callsFake(() => Promise.resolve()); 99 100 sinon.spy(deckStore, "on"); 101 sinon.stub(deckStore, "setPanels"); 102 103 sinon.stub(listStore, "getData"); 104 105 let component = new SyncedTabsDeckComponent({ 106 window: mockWindow, 107 deckStore, 108 listStore, 109 listComponent, 110 SyncedTabs, 111 DeckView: ViewMock, 112 }); 113 114 sinon.spy(component, "observe"); 115 sinon.stub(component, "updatePanel"); 116 sinon.stub(component, "updateDir"); 117 118 component.init(); 119 SyncedTabs.syncTabs.restore(); 120 Assert.ok(component.updatePanel.called, "triggers panel update during init"); 121 Assert.ok( 122 component.updateDir.called, 123 "triggers UI direction update during init" 124 ); 125 126 Services.obs.notifyObservers(null, SyncedTabs.TOPIC_TABS_CHANGED); 127 128 Assert.ok( 129 component.observe.calledWith(null, SyncedTabs.TOPIC_TABS_CHANGED), 130 "component is notified" 131 ); 132 133 Assert.ok(listStore.getData.called, "gets list data"); 134 Assert.ok(component.updatePanel.calledTwice, "triggers panel update"); 135 136 Services.obs.notifyObservers(null, UIState.ON_UPDATE); 137 138 Assert.ok( 139 component.observe.calledWith(null, UIState.ON_UPDATE), 140 "component is notified of FxA/Sync UI Update" 141 ); 142 Assert.equal( 143 component.updatePanel.callCount, 144 3, 145 "triggers panel update again" 146 ); 147 148 Services.locale.availableLocales = ["ab-CD"]; 149 Services.locale.requestedLocales = ["ab-CD"]; 150 151 Assert.ok( 152 component.updateDir.calledTwice, 153 "locale change triggers UI direction update" 154 ); 155 156 Services.prefs.setStringPref("intl.l10n.pseudo", "bidi"); 157 158 Assert.equal( 159 component.updateDir.callCount, 160 3, 161 "pref change triggers UI direction update" 162 ); 163 }); 164 165 add_task(async function testPanelStatus() { 166 let deckStore = new SyncedTabsDeckStore(); 167 let listStore = new SyncedTabsListStore(); 168 let listComponent = {}; 169 let SyncedTabsMock = { 170 getTabClients() {}, 171 }; 172 173 sinon.stub(listStore, "getData"); 174 175 let component = new SyncedTabsDeckComponent({ 176 deckStore, 177 listComponent, 178 SyncedTabs: SyncedTabsMock, 179 }); 180 181 sinon.stub(UIState, "get").returns({ status: UIState.STATUS_NOT_CONFIGURED }); 182 let result = await component.getPanelStatus(); 183 Assert.equal(result, component.PANELS.NOT_AUTHED_INFO); 184 UIState.get.restore(); 185 186 sinon.stub(UIState, "get").returns({ status: UIState.STATUS_NOT_VERIFIED }); 187 result = await component.getPanelStatus(); 188 Assert.equal(result, component.PANELS.UNVERIFIED); 189 UIState.get.restore(); 190 191 sinon.stub(UIState, "get").returns({ status: UIState.STATUS_LOGIN_FAILED }); 192 result = await component.getPanelStatus(); 193 Assert.equal(result, component.PANELS.LOGIN_FAILED); 194 UIState.get.restore(); 195 196 sinon 197 .stub(UIState, "get") 198 .returns({ status: UIState.STATUS_SIGNED_IN, syncEnabled: false }); 199 SyncedTabsMock.isConfiguredToSyncTabs = true; 200 result = await component.getPanelStatus(); 201 Assert.equal(result, component.PANELS.SYNC_DISABLED); 202 UIState.get.restore(); 203 204 sinon 205 .stub(UIState, "get") 206 .returns({ status: UIState.STATUS_SIGNED_IN, syncEnabled: true }); 207 SyncedTabsMock.isConfiguredToSyncTabs = false; 208 result = await component.getPanelStatus(); 209 Assert.equal(result, component.PANELS.TABS_DISABLED); 210 211 SyncedTabsMock.isConfiguredToSyncTabs = true; 212 213 SyncedTabsMock.hasSyncedThisSession = false; 214 result = await component.getPanelStatus(); 215 Assert.equal(result, component.PANELS.TABS_FETCHING); 216 217 SyncedTabsMock.hasSyncedThisSession = true; 218 219 let clients = []; 220 sinon 221 .stub(SyncedTabsMock, "getTabClients") 222 .callsFake(() => Promise.resolve(clients)); 223 result = await component.getPanelStatus(); 224 Assert.equal(result, component.PANELS.SINGLE_DEVICE_INFO); 225 226 clients = ["mock-client"]; 227 result = await component.getPanelStatus(); 228 Assert.equal(result, component.PANELS.TABS_CONTAINER); 229 230 sinon 231 .stub(component, "getPanelStatus") 232 .callsFake(() => Promise.resolve("mock-panelId")); 233 sinon.spy(deckStore, "selectPanel"); 234 await component.updatePanel(); 235 Assert.ok(deckStore.selectPanel.calledWith("mock-panelId")); 236 }); 237 238 add_task(async function testActions() { 239 let windowMock = {}; 240 let chromeWindowMock = { 241 gSync: { 242 openPrefs() {}, 243 openConnectAnotherDevice() {}, 244 }, 245 }; 246 sinon.spy(chromeWindowMock.gSync, "openPrefs"); 247 sinon.spy(chromeWindowMock.gSync, "openConnectAnotherDevice"); 248 249 let getChromeWindowMock = sinon.stub(); 250 getChromeWindowMock.returns(chromeWindowMock); 251 252 let component = new SyncedTabsDeckComponent({ 253 window: windowMock, 254 getChromeWindowMock, 255 }); 256 257 component.openConnectDevice(); 258 Assert.ok(chromeWindowMock.gSync.openConnectAnotherDevice.called); 259 260 component.openSyncPrefs(); 261 Assert.ok(getChromeWindowMock.calledWith(windowMock)); 262 Assert.ok(chromeWindowMock.gSync.openPrefs.called); 263 });