browser_fxa_config.js (10564B)
1 /* Any copyright is dedicated to the Public Domain. 2 * http://creativecommons.org/publicdomain/zero/1.0/ */ 3 4 "use strict"; 5 6 const { sinon } = ChromeUtils.importESModule( 7 "resource://testing-common/Sinon.sys.mjs" 8 ); 9 10 const { UIState } = ChromeUtils.importESModule( 11 "resource://services-sync/UIState.sys.mjs" 12 ); 13 14 var gTestTab; 15 var gContentAPI; 16 17 add_task(setup_UITourTest); 18 19 add_UITour_task(async function test_no_user() { 20 const sandbox = sinon.createSandbox(); 21 sandbox.stub(fxAccounts, "getSignedInUser").returns(null); 22 let result = await getConfigurationPromise("fxa"); 23 Assert.deepEqual(result, { setup: false }); 24 sandbox.restore(); 25 }); 26 27 add_UITour_task(async function test_no_sync_no_devices() { 28 const sandbox = sinon.createSandbox(); 29 sandbox 30 .stub(fxAccounts, "getSignedInUser") 31 .returns({ email: "foo@example.com" }); 32 sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => { 33 return [ 34 { 35 id: 1, 36 name: "This Device", 37 isCurrentDevice: true, 38 type: "desktop", 39 }, 40 ]; 41 }); 42 sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]); 43 sandbox.stub(fxAccounts, "hasLocalSession").resolves(true); 44 45 let result = await getConfigurationPromise("fxaConnections"); 46 Assert.deepEqual(result, { 47 setup: true, 48 numOtherDevices: 0, 49 numDevicesByType: {}, 50 accountServices: {}, 51 }); 52 sandbox.restore(); 53 }); 54 55 add_UITour_task(async function test_no_sync_many_devices() { 56 const sandbox = sinon.createSandbox(); 57 sandbox 58 .stub(fxAccounts, "getSignedInUser") 59 .returns({ email: "foo@example.com" }); 60 sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => { 61 return [ 62 { 63 id: 1, 64 name: "This Device", 65 isCurrentDevice: true, 66 type: "desktop", 67 }, 68 { 69 id: 2, 70 name: "Other Device", 71 type: "mobile", 72 }, 73 { 74 id: 3, 75 name: "My phone", 76 type: "phone", 77 }, 78 { 79 id: 4, 80 name: "Who knows?", 81 }, 82 { 83 id: 5, 84 name: "Another desktop", 85 type: "desktop", 86 }, 87 { 88 id: 6, 89 name: "Yet Another desktop", 90 type: "desktop", 91 }, 92 ]; 93 }); 94 sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]); 95 sandbox.stub(fxAccounts, "hasLocalSession").resolves(true); 96 97 let result = await getConfigurationPromise("fxaConnections"); 98 Assert.deepEqual(result, { 99 setup: true, 100 accountServices: {}, 101 numOtherDevices: 5, 102 numDevicesByType: { 103 desktop: 2, 104 mobile: 1, 105 phone: 1, 106 unknown: 1, 107 }, 108 }); 109 sandbox.restore(); 110 }); 111 112 add_UITour_task(async function test_fxa_connections_no_cached_devices() { 113 const sandbox = sinon.createSandbox(); 114 sandbox 115 .stub(fxAccounts, "getSignedInUser") 116 .returns({ email: "foo@example.com" }); 117 let devicesStub = sandbox.stub(fxAccounts.device, "recentDeviceList"); 118 devicesStub.get(() => { 119 // Sinon doesn't seem to support second `getters` returning a different 120 // value, so replace the getter here. 121 devicesStub.get(() => { 122 return [ 123 { 124 id: 1, 125 name: "This Device", 126 isCurrentDevice: true, 127 type: "desktop", 128 }, 129 { 130 id: 2, 131 name: "Other Device", 132 type: "mobile", 133 }, 134 ]; 135 }); 136 // and here we want to say "nothing is yet cached" 137 return null; 138 }); 139 140 sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]); 141 sandbox.stub(fxAccounts, "hasLocalSession").resolves(true); 142 let rdlStub = sandbox.stub(fxAccounts.device, "refreshDeviceList").resolves(); 143 144 let result = await getConfigurationPromise("fxaConnections"); 145 Assert.deepEqual(result, { 146 setup: true, 147 accountServices: {}, 148 numOtherDevices: 1, 149 numDevicesByType: { 150 mobile: 1, 151 }, 152 }); 153 Assert.ok(rdlStub.called); 154 sandbox.restore(); 155 }); 156 157 add_UITour_task(async function test_account_connections() { 158 const sandbox = sinon.createSandbox(); 159 sandbox 160 .stub(fxAccounts, "getSignedInUser") 161 .returns({ email: "foo@example.com" }); 162 sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => []); 163 sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([ 164 { 165 id: "802d56ef2a9af9fa", 166 lastAccessedDaysAgo: 2, 167 }, 168 { 169 id: "1f30e32975ae5112", 170 lastAccessedDaysAgo: 10, 171 }, 172 { 173 id: null, 174 name: "Some browser", 175 lastAccessedDaysAgo: 10, 176 }, 177 { 178 id: "null-last-accessed", 179 lastAccessedDaysAgo: null, 180 }, 181 ]); 182 Assert.deepEqual(await getConfigurationPromise("fxaConnections"), { 183 setup: true, 184 numOtherDevices: 0, 185 numDevicesByType: {}, 186 accountServices: { 187 "802d56ef2a9af9fa": { 188 id: "802d56ef2a9af9fa", 189 lastAccessedWeeksAgo: 0, 190 }, 191 "1f30e32975ae5112": { 192 id: "1f30e32975ae5112", 193 lastAccessedWeeksAgo: 1, 194 }, 195 "null-last-accessed": { 196 id: "null-last-accessed", 197 lastAccessedWeeksAgo: null, 198 }, 199 }, 200 }); 201 sandbox.restore(); 202 }); 203 204 add_UITour_task(async function test_sync() { 205 const sandbox = sinon.createSandbox(); 206 sandbox 207 .stub(fxAccounts, "getSignedInUser") 208 .returns({ email: "foo@example.com" }); 209 sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => []); 210 sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]); 211 sandbox.stub(fxAccounts, "hasLocalSession").resolves(true); 212 Services.prefs.setCharPref("services.sync.username", "tests@mozilla.org"); 213 Services.prefs.setIntPref("services.sync.clients.devices.desktop", 4); 214 Services.prefs.setIntPref("services.sync.clients.devices.mobile", 5); 215 Services.prefs.setIntPref("services.sync.numClients", 9); 216 217 Assert.deepEqual(await getConfigurationPromise("fxa"), { 218 setup: true, 219 accountStateOK: true, 220 browserServices: { 221 sync: { 222 setup: true, 223 mobileDevices: 5, 224 desktopDevices: 4, 225 totalDevices: 9, 226 }, 227 }, 228 }); 229 Services.prefs.clearUserPref("services.sync.username"); 230 Services.prefs.clearUserPref("services.sync.clients.devices.desktop"); 231 Services.prefs.clearUserPref("services.sync.clients.devices.mobile"); 232 Services.prefs.clearUserPref("services.sync.numClients"); 233 sandbox.restore(); 234 }); 235 236 add_UITour_task(async function test_fxa_fails() { 237 const sandbox = sinon.createSandbox(); 238 sandbox.stub(fxAccounts, "getSignedInUser").throws(); 239 let result = await getConfigurationPromise("fxa"); 240 Assert.deepEqual(result, {}); 241 sandbox.restore(); 242 }); 243 244 /** 245 * Tests that a UITour page can get notifications on FxA sign-in state 246 * changes. 247 */ 248 add_UITour_task(async function test_fxa_signedin_state_change() { 249 const sandbox = sinon.createSandbox(); 250 registerCleanupFunction(() => { 251 sandbox.restore(); 252 }); 253 254 let fxaConfig = await getConfigurationPromise("fxa"); 255 Assert.ok(!fxaConfig.setup, "FxA should not yet be set up."); 256 257 // A helper function that waits for the state change event to fire 258 // in content, and returns a Promise that resolves to the status 259 // parameter on the event detail. 260 let waitForSignedInStateChange = () => { 261 return SpecialPowers.spawn(gTestTab.linkedBrowser, [], async () => { 262 let event = await ContentTaskUtils.waitForEvent( 263 content.document, 264 "mozUITourNotification", 265 false, 266 e => { 267 return e.detail.event === "FxA:SignedInStateChange"; 268 }, 269 true 270 ); 271 return event.detail.params.status; 272 }); 273 }; 274 275 // We'll first test the STATUS_SIGNED_IN status. 276 277 let stateChangePromise = waitForSignedInStateChange(); 278 279 // Per bug 1743857, we wait for a JSWindowActor message round trip to 280 // ensure that the mozUITourNotification event listener has been setup 281 // in the SpecialPowers.spawn task. 282 await new Promise(resolve => { 283 gContentAPI.ping(resolve); 284 }); 285 286 let UIStateStub = sandbox.stub(UIState, "get").returns({ 287 status: UIState.STATUS_SIGNED_IN, 288 syncEnabled: true, 289 email: "email@example.com", 290 }); 291 292 Services.obs.notifyObservers(null, UIState.ON_UPDATE); 293 294 let status = await stateChangePromise; 295 Assert.equal( 296 status, 297 UIState.STATUS_SIGNED_IN, 298 "FxA:SignedInStateChange should have notified that we'd signed in." 299 ); 300 301 // We'll next test the STATUS_NOT_CONFIGURED status. 302 303 stateChangePromise = waitForSignedInStateChange(); 304 305 // Per bug 1743857, we wait for a JSWindowActor message round trip to 306 // ensure that the mozUITourNotification event listener has been setup 307 // in the SpecialPowers.spawn task. 308 await new Promise(resolve => { 309 gContentAPI.ping(resolve); 310 }); 311 312 UIStateStub.restore(); 313 UIStateStub = sandbox.stub(UIState, "get").returns({ 314 status: UIState.STATUS_NOT_CONFIGURED, 315 }); 316 317 Services.obs.notifyObservers(null, UIState.ON_UPDATE); 318 319 status = await stateChangePromise; 320 Assert.equal( 321 status, 322 UIState.STATUS_NOT_CONFIGURED, 323 "FxA:SignedInStateChange should have notified that we're not configured." 324 ); 325 326 // We'll next test the STATUS_LOGIN_FAILED status. 327 328 stateChangePromise = waitForSignedInStateChange(); 329 330 // Per bug 1743857, we wait for a JSWindowActor message round trip to 331 // ensure that the mozUITourNotification event listener has been setup 332 // in the SpecialPowers.spawn task. 333 await new Promise(resolve => { 334 gContentAPI.ping(resolve); 335 }); 336 337 UIStateStub.restore(); 338 UIStateStub = sandbox.stub(UIState, "get").returns({ 339 email: "foo@example.com", 340 status: UIState.STATUS_LOGIN_FAILED, 341 }); 342 343 Services.obs.notifyObservers(null, UIState.ON_UPDATE); 344 345 status = await stateChangePromise; 346 Assert.equal( 347 status, 348 UIState.STATUS_LOGIN_FAILED, 349 "FxA:SignedInStateChange should have notified that login has failed." 350 ); 351 352 // We'll next test the STATUS_NOT_VERIFIED status. 353 354 stateChangePromise = waitForSignedInStateChange(); 355 356 // Per bug 1743857, we wait for a JSWindowActor message round trip to 357 // ensure that the mozUITourNotification event listener has been setup 358 // in the SpecialPowers.spawn task. 359 await new Promise(resolve => { 360 gContentAPI.ping(resolve); 361 }); 362 363 UIStateStub.restore(); 364 UIStateStub = sandbox.stub(UIState, "get").returns({ 365 email: "foo@example.com", 366 status: UIState.STATUS_NOT_VERIFIED, 367 }); 368 369 Services.obs.notifyObservers(null, UIState.ON_UPDATE); 370 371 status = await stateChangePromise; 372 Assert.equal( 373 status, 374 UIState.STATUS_NOT_VERIFIED, 375 "FxA:SignedInStateChange should have notified that the login hasn't yet been verified." 376 ); 377 378 sandbox.restore(); 379 });