tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_uistate.js (12233B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { UIState } = ChromeUtils.importESModule(
      7  "resource://services-sync/UIState.sys.mjs"
      8 );
      9 
     10 const UIStateInternal = UIState._internal;
     11 
     12 add_task(async function test_isReady_unconfigured() {
     13  UIState.reset();
     14 
     15  let refreshState = sinon.spy(UIStateInternal, "refreshState");
     16 
     17  // On the first call, returns false
     18  // Does trigger a refresh of the state - even though services.sync.username
     19  // is undefined we still need to check the account state.
     20  ok(!UIState.isReady());
     21  // resfreshState is called when idle - so only check after idle.
     22  await new Promise(resolve => {
     23    Services.tm.idleDispatchToMainThread(resolve);
     24  });
     25  ok(refreshState.called);
     26  refreshState.resetHistory();
     27 
     28  // On subsequent calls, only return true
     29  ok(UIState.isReady());
     30  ok(!refreshState.called);
     31 
     32  refreshState.restore();
     33 });
     34 
     35 add_task(async function test_isReady_signedin() {
     36  UIState.reset();
     37  Services.prefs.setStringPref("services.sync.username", "foo");
     38 
     39  let refreshState = sinon.spy(UIStateInternal, "refreshState");
     40 
     41  // On the first call, returns false and triggers a refresh of the state
     42  ok(!UIState.isReady());
     43  await new Promise(resolve => {
     44    Services.tm.idleDispatchToMainThread(resolve);
     45  });
     46  ok(refreshState.calledOnce);
     47  refreshState.resetHistory();
     48 
     49  // On subsequent calls, only return true
     50  ok(UIState.isReady());
     51  ok(!refreshState.called);
     52 
     53  refreshState.restore();
     54 });
     55 
     56 add_task(async function test_refreshState_signedin() {
     57  UIState.reset();
     58  const fxAccountsOrig = UIStateInternal.fxAccounts;
     59 
     60  const now = new Date().toString();
     61  Services.prefs.setStringPref("services.sync.lastSync", now);
     62  Services.prefs.setStringPref("services.sync.username", "foo@bar.com");
     63  UIStateInternal.syncing = false;
     64 
     65  UIStateInternal.fxAccounts = {
     66    getSignedInUser: () =>
     67      Promise.resolve({
     68        verified: true,
     69        uid: "123",
     70        email: "foo@bar.com",
     71        displayName: "Foo Bar",
     72        avatar: "https://foo/bar",
     73      }),
     74    hasLocalSession: () => Promise.resolve(true),
     75    keys: {
     76      canGetKeyForScope: () => Promise.resolve(true),
     77      hasKeysForScope: () => Promise.resolve(true),
     78    },
     79  };
     80 
     81  let state = await UIState.refresh();
     82 
     83  equal(state.status, UIState.STATUS_SIGNED_IN);
     84  equal(state.uid, "123");
     85  equal(state.email, "foo@bar.com");
     86  equal(state.displayName, "Foo Bar");
     87  equal(state.avatarURL, "https://foo/bar");
     88  equal(state.lastSync, now);
     89  equal(state.syncing, false);
     90  equal(state.hasSyncKeys, true);
     91 
     92  UIStateInternal.fxAccounts = fxAccountsOrig;
     93  Services.prefs.clearUserPref("services.sync.username");
     94 });
     95 
     96 add_task(async function test_refreshState_syncButNoFxA() {
     97  UIState.reset();
     98  const fxAccountsOrig = UIStateInternal.fxAccounts;
     99 
    100  const now = new Date().toString();
    101  Services.prefs.setStringPref("services.sync.lastSync", now);
    102  Services.prefs.setStringPref("services.sync.username", "test@test.com");
    103  UIStateInternal.syncing = false;
    104 
    105  UIStateInternal.fxAccounts = {
    106    getSignedInUser: () => Promise.resolve(null),
    107  };
    108 
    109  let state = await UIState.refresh();
    110 
    111  equal(state.status, UIState.STATUS_LOGIN_FAILED);
    112  equal(state.uid, undefined);
    113  equal(state.email, "test@test.com");
    114  equal(state.displayName, undefined);
    115  equal(state.avatarURL, undefined);
    116  equal(state.lastSync, undefined); // only set when STATUS_SIGNED_IN.
    117  equal(state.syncing, false);
    118 
    119  UIStateInternal.fxAccounts = fxAccountsOrig;
    120  Services.prefs.clearUserPref("services.sync.lastSync");
    121  Services.prefs.clearUserPref("services.sync.username");
    122 });
    123 
    124 add_task(async function test_refreshState_signedin_profile_unavailable() {
    125  UIState.reset();
    126  const fxAccountsOrig = UIStateInternal.fxAccounts;
    127 
    128  const now = new Date().toString();
    129  Services.prefs.setStringPref("services.sync.lastSync", now);
    130  Services.prefs.setStringPref("services.sync.username", "test@test.com");
    131  UIStateInternal.syncing = false;
    132 
    133  UIStateInternal.fxAccounts = {
    134    getSignedInUser: () =>
    135      Promise.resolve({ verified: true, uid: "123", email: "foo@bar.com" }),
    136    hasLocalSession: () => Promise.resolve(true),
    137    keys: {
    138      canGetKeyForScope: () => Promise.resolve(true),
    139      hasKeysForScope: () => Promise.resolve(true),
    140    },
    141    _internal: {
    142      profile: {
    143        getProfile: () => {
    144          return Promise.reject(new Error("Profile unavailable"));
    145        },
    146      },
    147    },
    148  };
    149 
    150  let state = await UIState.refresh();
    151 
    152  equal(state.status, UIState.STATUS_SIGNED_IN);
    153  equal(state.uid, "123");
    154  equal(state.email, "foo@bar.com");
    155  equal(state.displayName, undefined);
    156  equal(state.avatarURL, undefined);
    157  equal(state.lastSync, now);
    158  equal(state.syncing, false);
    159 
    160  UIStateInternal.fxAccounts = fxAccountsOrig;
    161  Services.prefs.clearUserPref("services.sync.lastSync");
    162  Services.prefs.clearUserPref("services.sync.username");
    163 });
    164 
    165 add_task(async function test_refreshState_unverified() {
    166  UIState.reset();
    167  const fxAccountsOrig = UIStateInternal.fxAccounts;
    168 
    169  UIStateInternal.fxAccounts = {
    170    getSignedInUser: () =>
    171      Promise.resolve({ verified: false, uid: "123", email: "foo@bar.com" }),
    172    hasLocalSession: () => Promise.resolve(true),
    173    keys: {
    174      hasKeysForScope: () => Promise.resolve(true),
    175    },
    176  };
    177 
    178  let state = await UIState.refresh();
    179 
    180  equal(state.status, UIState.STATUS_NOT_VERIFIED);
    181  equal(state.uid, "123");
    182  equal(state.email, "foo@bar.com");
    183  equal(state.displayName, undefined);
    184  equal(state.avatarURL, undefined);
    185  equal(state.lastSync, undefined);
    186 
    187  UIStateInternal.fxAccounts = fxAccountsOrig;
    188 });
    189 
    190 add_task(async function test_refreshState_unverified_nosession() {
    191  UIState.reset();
    192  const fxAccountsOrig = UIStateInternal.fxAccounts;
    193 
    194  UIStateInternal.fxAccounts = {
    195    getSignedInUser: () =>
    196      Promise.resolve({ verified: false, uid: "123", email: "foo@bar.com" }),
    197    hasLocalSession: () => Promise.resolve(false),
    198    keys: {
    199      hasKeysForScope: () => Promise.resolve(true),
    200    },
    201  };
    202 
    203  let state = await UIState.refresh();
    204 
    205  // No session should "win" over the unverified state.
    206  equal(state.status, UIState.STATUS_LOGIN_FAILED);
    207  equal(state.uid, "123");
    208  equal(state.email, "foo@bar.com");
    209  equal(state.displayName, undefined);
    210  equal(state.avatarURL, undefined);
    211  equal(state.lastSync, undefined);
    212 
    213  UIStateInternal.fxAccounts = fxAccountsOrig;
    214 });
    215 
    216 add_task(async function test_refreshState_loginFailed() {
    217  UIState.reset();
    218  const fxAccountsOrig = UIStateInternal.fxAccounts;
    219 
    220  let loginFailed = sinon.stub(UIStateInternal, "_loginFailed");
    221  loginFailed.returns(true);
    222 
    223  UIStateInternal.fxAccounts = {
    224    getSignedInUser: () =>
    225      Promise.resolve({ verified: true, uid: "123", email: "foo@bar.com" }),
    226    keys: {
    227      canGetKeyForScope: () => Promise.resolve(true),
    228      hasKeysForScope: () => Promise.resolve(true),
    229    },
    230  };
    231 
    232  let state = await UIState.refresh();
    233 
    234  equal(state.status, UIState.STATUS_LOGIN_FAILED);
    235  equal(state.uid, "123");
    236  equal(state.email, "foo@bar.com");
    237  equal(state.displayName, undefined);
    238  equal(state.avatarURL, undefined);
    239  equal(state.lastSync, undefined);
    240 
    241  loginFailed.restore();
    242  UIStateInternal.fxAccounts = fxAccountsOrig;
    243 });
    244 
    245 add_task(async function test_observer_refreshState() {
    246  let refreshState = sinon.spy(UIStateInternal, "refreshState");
    247 
    248  let shouldRefresh = [
    249    "weave:service:login:got-hashed-id",
    250    "weave:service:login:error",
    251    "weave:service:ready",
    252    "fxaccounts:onverified",
    253    "fxaccounts:onlogin",
    254    "fxaccounts:onlogout",
    255    "fxaccounts:profilechange",
    256  ];
    257 
    258  for (let topic of shouldRefresh) {
    259    let uiUpdateObserved = observeUIUpdate();
    260    Services.obs.notifyObservers(null, topic);
    261    await uiUpdateObserved;
    262    ok(refreshState.calledOnce);
    263    refreshState.resetHistory();
    264  }
    265 
    266  refreshState.restore();
    267 });
    268 
    269 // Drive the UIState in a configured state.
    270 async function configureUIState(syncing, lastSync = new Date()) {
    271  UIState.reset();
    272  const fxAccountsOrig = UIStateInternal.fxAccounts;
    273 
    274  UIStateInternal._syncing = syncing;
    275  Services.prefs.setStringPref("services.sync.lastSync", lastSync.toString());
    276  Services.prefs.setStringPref("services.sync.username", "test@test.com");
    277 
    278  UIStateInternal.fxAccounts = {
    279    getSignedInUser: () =>
    280      Promise.resolve({ verified: true, uid: "123", email: "foo@bar.com" }),
    281    hasLocalSession: () => Promise.resolve(true),
    282    keys: {
    283      canGetKeyForScope: () => Promise.resolve(true),
    284      hasKeysForScope: () => Promise.resolve(true),
    285    },
    286  };
    287  await UIState.refresh();
    288  UIStateInternal.fxAccounts = fxAccountsOrig;
    289 }
    290 
    291 add_task(async function test_syncStarted() {
    292  await configureUIState(false);
    293 
    294  const oldState = Object.assign({}, UIState.get());
    295  ok(!oldState.syncing);
    296 
    297  let uiUpdateObserved = observeUIUpdate();
    298  Services.obs.notifyObservers(null, "weave:service:sync:start");
    299  await uiUpdateObserved;
    300 
    301  const newState = Object.assign({}, UIState.get());
    302  ok(newState.syncing);
    303 });
    304 
    305 add_task(async function test_syncFinished() {
    306  let yesterday = new Date();
    307  yesterday.setDate(yesterday.getDate() - 1);
    308  await configureUIState(true, yesterday);
    309 
    310  const oldState = Object.assign({}, UIState.get());
    311  ok(oldState.syncing);
    312 
    313  let uiUpdateObserved = observeUIUpdate();
    314  Services.prefs.setStringPref("services.sync.lastSync", new Date().toString());
    315  Services.obs.notifyObservers(null, "weave:service:sync:finish");
    316  await uiUpdateObserved;
    317 
    318  const newState = Object.assign({}, UIState.get());
    319  ok(!newState.syncing);
    320  Assert.greater(new Date(newState.lastSync), new Date(oldState.lastSync));
    321 });
    322 
    323 add_task(async function test_syncError() {
    324  let yesterday = new Date();
    325  yesterday.setDate(yesterday.getDate() - 1);
    326  await configureUIState(true, yesterday);
    327 
    328  const oldState = Object.assign({}, UIState.get());
    329  ok(oldState.syncing);
    330 
    331  let uiUpdateObserved = observeUIUpdate();
    332  Services.obs.notifyObservers(null, "weave:service:sync:error");
    333  await uiUpdateObserved;
    334 
    335  const newState = Object.assign({}, UIState.get());
    336  ok(!newState.syncing);
    337  deepEqual(newState.lastSync, oldState.lastSync);
    338 });
    339 
    340 add_task(async function test_refreshState_signedin_with_synckeys() {
    341  UIState.reset();
    342  const fxAccountsOrig = UIStateInternal.fxAccounts;
    343 
    344  Services.prefs.setStringPref("services.sync.username", "test@test.com");
    345 
    346  UIStateInternal.fxAccounts = {
    347    getSignedInUser: () =>
    348      Promise.resolve({
    349        verified: true,
    350        uid: "123",
    351        email: "foo@bar.com",
    352        displayName: "Foo Bar",
    353      }),
    354    hasLocalSession: () => Promise.resolve(true),
    355    keys: {
    356      canGetKeyForScope: () => Promise.resolve(true),
    357      hasKeysForScope: () => Promise.resolve(true),
    358    },
    359  };
    360 
    361  let state = await UIState.refresh();
    362 
    363  equal(state.status, UIState.STATUS_SIGNED_IN);
    364  equal(state.syncEnabled, true);
    365  equal(state.uid, "123");
    366  equal(state.email, "foo@bar.com");
    367 
    368  UIStateInternal.fxAccounts = fxAccountsOrig;
    369  Services.prefs.clearUserPref("services.sync.username");
    370 });
    371 
    372 // Testing third-party auth does not enable sync (should be impossible without keys)
    373 add_task(async function test_refreshState_third_party_auth_no_sync() {
    374  UIState.reset();
    375  const fxAccountsOrig = UIStateInternal.fxAccounts;
    376 
    377  // Mock signing in but NO keys (third-party auth)
    378  UIStateInternal.fxAccounts = {
    379    getSignedInUser: () =>
    380      Promise.resolve({
    381        verified: true,
    382        uid: "123",
    383        email: "foo@bar.com",
    384      }),
    385    hasLocalSession: () => Promise.resolve(true),
    386    keys: {
    387      hasKeysForScope: () => Promise.resolve(false),
    388    },
    389  };
    390 
    391  let state = await UIState.refresh();
    392 
    393  equal(state.status, UIState.STATUS_SIGNED_IN);
    394  equal(state.syncEnabled, false);
    395  equal(state.hasSyncKeys, false);
    396  equal(state.uid, "123");
    397  equal(state.email, "foo@bar.com");
    398 
    399  UIStateInternal.fxAccounts = fxAccountsOrig;
    400 });
    401 
    402 function observeUIUpdate() {
    403  return new Promise(resolve => {
    404    let obs = (aSubject, aTopic) => {
    405      Services.obs.removeObserver(obs, aTopic);
    406      const state = UIState.get();
    407      resolve(state);
    408    };
    409    Services.obs.addObserver(obs, UIState.ON_UPDATE);
    410  });
    411 }