/* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ "use strict"; const { sinon } = ChromeUtils.importESModule( "resource://testing-common/Sinon.sys.mjs" ); const { UIState } = ChromeUtils.importESModule( "resource://services-sync/UIState.sys.mjs" ); var gTestTab; var gContentAPI; add_task(setup_UITourTest); add_UITour_task(async function test_no_user() { const sandbox = sinon.createSandbox(); sandbox.stub(fxAccounts, "getSignedInUser").returns(null); let result = await getConfigurationPromise("fxa"); Assert.deepEqual(result, { setup: false }); sandbox.restore(); }); add_UITour_task(async function test_no_sync_no_devices() { const sandbox = sinon.createSandbox(); sandbox .stub(fxAccounts, "getSignedInUser") .returns({ email: "foo@example.com" }); sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => { return [ { id: 1, name: "This Device", isCurrentDevice: true, type: "desktop", }, ]; }); sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]); sandbox.stub(fxAccounts, "hasLocalSession").resolves(true); let result = await getConfigurationPromise("fxaConnections"); Assert.deepEqual(result, { setup: true, numOtherDevices: 0, numDevicesByType: {}, accountServices: {}, }); sandbox.restore(); }); add_UITour_task(async function test_no_sync_many_devices() { const sandbox = sinon.createSandbox(); sandbox .stub(fxAccounts, "getSignedInUser") .returns({ email: "foo@example.com" }); sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => { return [ { id: 1, name: "This Device", isCurrentDevice: true, type: "desktop", }, { id: 2, name: "Other Device", type: "mobile", }, { id: 3, name: "My phone", type: "phone", }, { id: 4, name: "Who knows?", }, { id: 5, name: "Another desktop", type: "desktop", }, { id: 6, name: "Yet Another desktop", type: "desktop", }, ]; }); sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]); sandbox.stub(fxAccounts, "hasLocalSession").resolves(true); let result = await getConfigurationPromise("fxaConnections"); Assert.deepEqual(result, { setup: true, accountServices: {}, numOtherDevices: 5, numDevicesByType: { desktop: 2, mobile: 1, phone: 1, unknown: 1, }, }); sandbox.restore(); }); add_UITour_task(async function test_fxa_connections_no_cached_devices() { const sandbox = sinon.createSandbox(); sandbox .stub(fxAccounts, "getSignedInUser") .returns({ email: "foo@example.com" }); let devicesStub = sandbox.stub(fxAccounts.device, "recentDeviceList"); devicesStub.get(() => { // Sinon doesn't seem to support second `getters` returning a different // value, so replace the getter here. devicesStub.get(() => { return [ { id: 1, name: "This Device", isCurrentDevice: true, type: "desktop", }, { id: 2, name: "Other Device", type: "mobile", }, ]; }); // and here we want to say "nothing is yet cached" return null; }); sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]); sandbox.stub(fxAccounts, "hasLocalSession").resolves(true); let rdlStub = sandbox.stub(fxAccounts.device, "refreshDeviceList").resolves(); let result = await getConfigurationPromise("fxaConnections"); Assert.deepEqual(result, { setup: true, accountServices: {}, numOtherDevices: 1, numDevicesByType: { mobile: 1, }, }); Assert.ok(rdlStub.called); sandbox.restore(); }); add_UITour_task(async function test_account_connections() { const sandbox = sinon.createSandbox(); sandbox .stub(fxAccounts, "getSignedInUser") .returns({ email: "foo@example.com" }); sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => []); sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([ { id: "802d56ef2a9af9fa", lastAccessedDaysAgo: 2, }, { id: "1f30e32975ae5112", lastAccessedDaysAgo: 10, }, { id: null, name: "Some browser", lastAccessedDaysAgo: 10, }, { id: "null-last-accessed", lastAccessedDaysAgo: null, }, ]); Assert.deepEqual(await getConfigurationPromise("fxaConnections"), { setup: true, numOtherDevices: 0, numDevicesByType: {}, accountServices: { "802d56ef2a9af9fa": { id: "802d56ef2a9af9fa", lastAccessedWeeksAgo: 0, }, "1f30e32975ae5112": { id: "1f30e32975ae5112", lastAccessedWeeksAgo: 1, }, "null-last-accessed": { id: "null-last-accessed", lastAccessedWeeksAgo: null, }, }, }); sandbox.restore(); }); add_UITour_task(async function test_sync() { const sandbox = sinon.createSandbox(); sandbox .stub(fxAccounts, "getSignedInUser") .returns({ email: "foo@example.com" }); sandbox.stub(fxAccounts.device, "recentDeviceList").get(() => []); sandbox.stub(fxAccounts, "listAttachedOAuthClients").resolves([]); sandbox.stub(fxAccounts, "hasLocalSession").resolves(true); Services.prefs.setCharPref("services.sync.username", "tests@mozilla.org"); Services.prefs.setIntPref("services.sync.clients.devices.desktop", 4); Services.prefs.setIntPref("services.sync.clients.devices.mobile", 5); Services.prefs.setIntPref("services.sync.numClients", 9); Assert.deepEqual(await getConfigurationPromise("fxa"), { setup: true, accountStateOK: true, browserServices: { sync: { setup: true, mobileDevices: 5, desktopDevices: 4, totalDevices: 9, }, }, }); Services.prefs.clearUserPref("services.sync.username"); Services.prefs.clearUserPref("services.sync.clients.devices.desktop"); Services.prefs.clearUserPref("services.sync.clients.devices.mobile"); Services.prefs.clearUserPref("services.sync.numClients"); sandbox.restore(); }); add_UITour_task(async function test_fxa_fails() { const sandbox = sinon.createSandbox(); sandbox.stub(fxAccounts, "getSignedInUser").throws(); let result = await getConfigurationPromise("fxa"); Assert.deepEqual(result, {}); sandbox.restore(); }); /** * Tests that a UITour page can get notifications on FxA sign-in state * changes. */ add_UITour_task(async function test_fxa_signedin_state_change() { const sandbox = sinon.createSandbox(); registerCleanupFunction(() => { sandbox.restore(); }); let fxaConfig = await getConfigurationPromise("fxa"); Assert.ok(!fxaConfig.setup, "FxA should not yet be set up."); // A helper function that waits for the state change event to fire // in content, and returns a Promise that resolves to the status // parameter on the event detail. let waitForSignedInStateChange = () => { return SpecialPowers.spawn(gTestTab.linkedBrowser, [], async () => { let event = await ContentTaskUtils.waitForEvent( content.document, "mozUITourNotification", false, e => { return e.detail.event === "FxA:SignedInStateChange"; }, true ); return event.detail.params.status; }); }; // We'll first test the STATUS_SIGNED_IN status. let stateChangePromise = waitForSignedInStateChange(); // Per bug 1743857, we wait for a JSWindowActor message round trip to // ensure that the mozUITourNotification event listener has been setup // in the SpecialPowers.spawn task. await new Promise(resolve => { gContentAPI.ping(resolve); }); let UIStateStub = sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_SIGNED_IN, syncEnabled: true, email: "email@example.com", }); Services.obs.notifyObservers(null, UIState.ON_UPDATE); let status = await stateChangePromise; Assert.equal( status, UIState.STATUS_SIGNED_IN, "FxA:SignedInStateChange should have notified that we'd signed in." ); // We'll next test the STATUS_NOT_CONFIGURED status. stateChangePromise = waitForSignedInStateChange(); // Per bug 1743857, we wait for a JSWindowActor message round trip to // ensure that the mozUITourNotification event listener has been setup // in the SpecialPowers.spawn task. await new Promise(resolve => { gContentAPI.ping(resolve); }); UIStateStub.restore(); UIStateStub = sandbox.stub(UIState, "get").returns({ status: UIState.STATUS_NOT_CONFIGURED, }); Services.obs.notifyObservers(null, UIState.ON_UPDATE); status = await stateChangePromise; Assert.equal( status, UIState.STATUS_NOT_CONFIGURED, "FxA:SignedInStateChange should have notified that we're not configured." ); // We'll next test the STATUS_LOGIN_FAILED status. stateChangePromise = waitForSignedInStateChange(); // Per bug 1743857, we wait for a JSWindowActor message round trip to // ensure that the mozUITourNotification event listener has been setup // in the SpecialPowers.spawn task. await new Promise(resolve => { gContentAPI.ping(resolve); }); UIStateStub.restore(); UIStateStub = sandbox.stub(UIState, "get").returns({ email: "foo@example.com", status: UIState.STATUS_LOGIN_FAILED, }); Services.obs.notifyObservers(null, UIState.ON_UPDATE); status = await stateChangePromise; Assert.equal( status, UIState.STATUS_LOGIN_FAILED, "FxA:SignedInStateChange should have notified that login has failed." ); // We'll next test the STATUS_NOT_VERIFIED status. stateChangePromise = waitForSignedInStateChange(); // Per bug 1743857, we wait for a JSWindowActor message round trip to // ensure that the mozUITourNotification event listener has been setup // in the SpecialPowers.spawn task. await new Promise(resolve => { gContentAPI.ping(resolve); }); UIStateStub.restore(); UIStateStub = sandbox.stub(UIState, "get").returns({ email: "foo@example.com", status: UIState.STATUS_NOT_VERIFIED, }); Services.obs.notifyObservers(null, UIState.ON_UPDATE); status = await stateChangePromise; Assert.equal( status, UIState.STATUS_NOT_VERIFIED, "FxA:SignedInStateChange should have notified that the login hasn't yet been verified." ); sandbox.restore(); });