tor-browser

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

PrefsFeed.test.js (17180B)


      1 import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
      2 import { GlobalOverrider } from "test/unit/utils";
      3 import { PrefsFeed } from "lib/PrefsFeed.sys.mjs";
      4 
      5 let overrider = new GlobalOverrider();
      6 
      7 describe("PrefsFeed", () => {
      8  let feed;
      9  let FAKE_PREFS;
     10  let sandbox;
     11  let ServicesStub;
     12  beforeEach(() => {
     13    sandbox = sinon.createSandbox();
     14    FAKE_PREFS = new Map([
     15      ["foo", 1],
     16      ["bar", 2],
     17      ["baz", { value: 1, skipBroadcast: true }],
     18      ["qux", { value: 1, skipBroadcast: true, alsoToPreloaded: true }],
     19    ]);
     20    feed = new PrefsFeed(FAKE_PREFS);
     21    ServicesStub = {
     22      prefs: {
     23        clearUserPref: sinon.spy(),
     24        getStringPref: sinon.spy(),
     25        getIntPref: sinon.spy(),
     26        getBoolPref: sinon.spy(),
     27      },
     28      obs: {
     29        removeObserver: sinon.spy(),
     30        addObserver: sinon.spy(),
     31      },
     32    };
     33    sinon.spy(feed, "_setPref");
     34    feed.store = {
     35      dispatch: sinon.spy(),
     36      getState() {
     37        return this.state;
     38      },
     39    };
     40    // Setup for tests that don't call `init`
     41    feed._prefs = {
     42      get: sinon.spy(item => FAKE_PREFS.get(item)),
     43      set: sinon.spy((name, value) => FAKE_PREFS.set(name, value)),
     44      observe: sinon.spy(),
     45      observeBranch: sinon.spy(),
     46      ignore: sinon.spy(),
     47      ignoreBranch: sinon.spy(),
     48      reset: sinon.stub(),
     49      _branchStr: "branch.str.",
     50    };
     51    overrider.set({
     52      PrivateBrowsingUtils: { enabled: true },
     53      Services: ServicesStub,
     54    });
     55  });
     56  afterEach(() => {
     57    overrider.restore();
     58    sandbox.restore();
     59  });
     60 
     61  it("should set a pref when a SET_PREF action is received", () => {
     62    feed.onAction(ac.SetPref("foo", 2));
     63    assert.calledWith(feed._prefs.set, "foo", 2);
     64  });
     65  it("should call clearUserPref with action CLEAR_PREF", () => {
     66    feed.onAction({ type: at.CLEAR_PREF, data: { name: "pref.test" } });
     67    assert.calledWith(ServicesStub.prefs.clearUserPref, "branch.str.pref.test");
     68  });
     69  it("should dispatch PREFS_INITIAL_VALUES on init with pref values and .isPrivateBrowsingEnabled", () => {
     70    feed.onAction({ type: at.INIT });
     71    assert.calledOnce(feed.store.dispatch);
     72    assert.equal(
     73      feed.store.dispatch.firstCall.args[0].type,
     74      at.PREFS_INITIAL_VALUES
     75    );
     76    const [{ data }] = feed.store.dispatch.firstCall.args;
     77    assert.equal(data.foo, 1);
     78    assert.equal(data.bar, 2);
     79    assert.isTrue(data.isPrivateBrowsingEnabled);
     80  });
     81  it("should dispatch PREFS_INITIAL_VALUES with a .featureConfig", () => {
     82    sandbox.stub(global.NimbusFeatures.newtab, "getAllVariables").returns({
     83      prefsButtonIcon: "icon-foo",
     84    });
     85    feed.onAction({ type: at.INIT });
     86    assert.equal(
     87      feed.store.dispatch.firstCall.args[0].type,
     88      at.PREFS_INITIAL_VALUES
     89    );
     90    const [{ data }] = feed.store.dispatch.firstCall.args;
     91    assert.deepEqual(data.featureConfig, { prefsButtonIcon: "icon-foo" });
     92  });
     93  it("should dispatch PREFS_INITIAL_VALUES with trainhopConfig", () => {
     94    const testObject = {
     95      meta: { isRollout: false },
     96      value: {
     97        type: "testExperiment",
     98        payload: { enabled: true },
     99      },
    100    };
    101    sandbox
    102      .stub(global.NimbusFeatures.newtabTrainhop, "getAllEnrollments")
    103      .returns([testObject]);
    104 
    105    feed.onAction({ type: at.INIT });
    106 
    107    assert.equal(
    108      feed.store.dispatch.firstCall.args[0].type,
    109      at.PREFS_INITIAL_VALUES
    110    );
    111    const [{ data }] = feed.store.dispatch.firstCall.args;
    112    assert.deepEqual(data.trainhopConfig, {
    113      testExperiment: { enabled: true },
    114    });
    115  });
    116  it("should dispatch PREFS_INITIAL_VALUES with an empty object if no experiment is returned", () => {
    117    sandbox.stub(global.NimbusFeatures.newtab, "getAllVariables").returns(null);
    118    feed.onAction({ type: at.INIT });
    119    assert.equal(
    120      feed.store.dispatch.firstCall.args[0].type,
    121      at.PREFS_INITIAL_VALUES
    122    );
    123    const [{ data }] = feed.store.dispatch.firstCall.args;
    124    assert.deepEqual(data.featureConfig, {});
    125  });
    126  it("should add one branch observer on init", () => {
    127    feed.onAction({ type: at.INIT });
    128    assert.calledOnce(feed._prefs.observeBranch);
    129    assert.calledWith(feed._prefs.observeBranch, feed);
    130  });
    131  it("should handle region on init", () => {
    132    feed.init();
    133    assert.equal(feed.geo, "US");
    134  });
    135  it("should add region observer on init", () => {
    136    sandbox.stub(global.Region, "home").get(() => "");
    137    feed.init();
    138    assert.equal(feed.geo, "");
    139    assert.calledWith(
    140      ServicesStub.obs.addObserver,
    141      feed,
    142      global.Region.REGION_TOPIC
    143    );
    144  });
    145  it("should remove the branch observer on uninit", () => {
    146    feed.onAction({ type: at.UNINIT });
    147    assert.calledOnce(feed._prefs.ignoreBranch);
    148    assert.calledWith(feed._prefs.ignoreBranch, feed);
    149  });
    150  it("should call removeObserver", () => {
    151    feed.geo = "";
    152    feed.uninit();
    153    assert.calledWith(
    154      ServicesStub.obs.removeObserver,
    155      feed,
    156      global.Region.REGION_TOPIC
    157    );
    158  });
    159  it("should send a PREF_CHANGED action when onPrefChanged is called", () => {
    160    feed.onPrefChanged("foo", 2);
    161    assert.calledWith(
    162      feed.store.dispatch,
    163      ac.BroadcastToContent({
    164        type: at.PREF_CHANGED,
    165        data: { name: "foo", value: 2 },
    166      })
    167    );
    168  });
    169  it("should send a PREF_CHANGED actions when onPocketExperimentUpdated is called", () => {
    170    sandbox
    171      .stub(global.NimbusFeatures.pocketNewtab, "getAllVariables")
    172      .returns({
    173        prefsButtonIcon: "icon-new",
    174      });
    175    feed.onPocketExperimentUpdated();
    176    assert.calledWith(
    177      feed.store.dispatch,
    178      ac.BroadcastToContent({
    179        type: at.PREF_CHANGED,
    180        data: {
    181          name: "pocketConfig",
    182          value: {
    183            prefsButtonIcon: "icon-new",
    184          },
    185        },
    186      })
    187    );
    188  });
    189  it("should not send a PREF_CHANGED actions when onPocketExperimentUpdated is called during startup", () => {
    190    sandbox
    191      .stub(global.NimbusFeatures.pocketNewtab, "getAllVariables")
    192      .returns({
    193        prefsButtonIcon: "icon-new",
    194      });
    195    feed.onPocketExperimentUpdated({}, "feature-experiment-loaded");
    196    assert.notCalled(feed.store.dispatch);
    197    feed.onPocketExperimentUpdated({}, "feature-rollout-loaded");
    198    assert.notCalled(feed.store.dispatch);
    199  });
    200  it("should send a PREF_CHANGED actions when onExperimentUpdated is called", () => {
    201    sandbox.stub(global.NimbusFeatures.newtab, "getAllVariables").returns({
    202      prefsButtonIcon: "icon-new",
    203    });
    204    feed.onExperimentUpdated();
    205    assert.calledWith(
    206      feed.store.dispatch,
    207      ac.BroadcastToContent({
    208        type: at.PREF_CHANGED,
    209        data: {
    210          name: "featureConfig",
    211          value: {
    212            prefsButtonIcon: "icon-new",
    213          },
    214        },
    215      })
    216    );
    217  });
    218  describe("newtabTrainhop", () => {
    219    it("should send a PREF_CHANGED actions when onTrainhopExperimentUpdated is called", () => {
    220      const testObject = {
    221        meta: {
    222          isRollout: false,
    223        },
    224        value: {
    225          type: "testExperiment",
    226          payload: {
    227            enabled: true,
    228          },
    229        },
    230      };
    231      sandbox
    232        .stub(global.NimbusFeatures.newtabTrainhop, "getAllEnrollments")
    233        .returns([testObject]);
    234      feed.onTrainhopExperimentUpdated();
    235      assert.calledWith(
    236        feed.store.dispatch,
    237        ac.BroadcastToContent({
    238          type: at.PREF_CHANGED,
    239          data: {
    240            name: "trainhopConfig",
    241            value: {
    242              testExperiment: testObject.value.payload,
    243            },
    244          },
    245        })
    246      );
    247    });
    248    it("should handle and dedupe multiple experiments and rollouts", () => {
    249      const testObject1 = {
    250        meta: {
    251          isRollout: false,
    252        },
    253        value: {
    254          type: "testExperiment1",
    255          payload: {
    256            enabled: true,
    257          },
    258        },
    259      };
    260      const testObject2 = {
    261        meta: {
    262          isRollout: false,
    263        },
    264        value: {
    265          type: "testExperiment1",
    266          payload: {
    267            enabled: false,
    268          },
    269        },
    270      };
    271      const testObject3 = {
    272        meta: {
    273          isRollout: true,
    274        },
    275        value: {
    276          type: "testExperiment2",
    277          payload: {
    278            enabled: true,
    279          },
    280        },
    281      };
    282      const testObject4 = {
    283        meta: {
    284          isRollout: false,
    285        },
    286        value: {
    287          type: "testExperiment2",
    288          payload: {
    289            enabled: false,
    290          },
    291        },
    292      };
    293      sandbox
    294        .stub(global.NimbusFeatures.newtabTrainhop, "getAllEnrollments")
    295        .returns([testObject1, testObject2, testObject3, testObject4]);
    296      feed.onTrainhopExperimentUpdated();
    297      assert.calledWith(
    298        feed.store.dispatch,
    299        ac.BroadcastToContent({
    300          type: at.PREF_CHANGED,
    301          data: {
    302            name: "trainhopConfig",
    303            value: {
    304              testExperiment1: testObject1.value.payload,
    305              testExperiment2: testObject4.value.payload,
    306            },
    307          },
    308        })
    309      );
    310    });
    311    it("should handle multi-payload format with single enrollment", () => {
    312      const testObject = {
    313        meta: {
    314          isRollout: false,
    315        },
    316        value: {
    317          type: "multi-payload",
    318          payload: [
    319            {
    320              type: "testExperiment",
    321              payload: {
    322                enabled: true,
    323                name: "test-name",
    324              },
    325            },
    326          ],
    327        },
    328      };
    329      sandbox
    330        .stub(global.NimbusFeatures.newtabTrainhop, "getAllEnrollments")
    331        .returns([testObject]);
    332      feed.onTrainhopExperimentUpdated();
    333      assert.calledWith(
    334        feed.store.dispatch,
    335        ac.BroadcastToContent({
    336          type: at.PREF_CHANGED,
    337          data: {
    338            name: "trainhopConfig",
    339            value: {
    340              testExperiment: {
    341                enabled: true,
    342                name: "test-name",
    343              },
    344            },
    345          },
    346        })
    347      );
    348    });
    349    it("should handle multi-payload format with multiple items in single enrollment", () => {
    350      const testObject = {
    351        meta: {
    352          isRollout: false,
    353        },
    354        value: {
    355          type: "multi-payload",
    356          payload: [
    357            {
    358              type: "testExperiment1",
    359              payload: {
    360                enabled: true,
    361              },
    362            },
    363            {
    364              type: "testExperiment2",
    365              payload: {
    366                enabled: false,
    367              },
    368            },
    369          ],
    370        },
    371      };
    372      sandbox
    373        .stub(global.NimbusFeatures.newtabTrainhop, "getAllEnrollments")
    374        .returns([testObject]);
    375      feed.onTrainhopExperimentUpdated();
    376      assert.calledWith(
    377        feed.store.dispatch,
    378        ac.BroadcastToContent({
    379          type: at.PREF_CHANGED,
    380          data: {
    381            name: "trainhopConfig",
    382            value: {
    383              testExperiment1: {
    384                enabled: true,
    385              },
    386              testExperiment2: {
    387                enabled: false,
    388              },
    389            },
    390          },
    391        })
    392      );
    393    });
    394    it("should dedupe multi-payload format with experiment taking precedence over rollout", () => {
    395      const rollout = {
    396        meta: {
    397          isRollout: true,
    398        },
    399        value: {
    400          type: "multi-payload",
    401          payload: [
    402            {
    403              type: "testExperiment",
    404              payload: {
    405                enabled: false,
    406                name: "rollout-name",
    407              },
    408            },
    409          ],
    410        },
    411      };
    412      const experiment = {
    413        meta: {
    414          isRollout: false,
    415        },
    416        value: {
    417          type: "multi-payload",
    418          payload: [
    419            {
    420              type: "testExperiment",
    421              payload: {
    422                enabled: true,
    423                name: "experiment-name",
    424              },
    425            },
    426          ],
    427        },
    428      };
    429      sandbox
    430        .stub(global.NimbusFeatures.newtabTrainhop, "getAllEnrollments")
    431        .returns([rollout, experiment]);
    432      feed.onTrainhopExperimentUpdated();
    433      assert.calledWith(
    434        feed.store.dispatch,
    435        ac.BroadcastToContent({
    436          type: at.PREF_CHANGED,
    437          data: {
    438            name: "trainhopConfig",
    439            value: {
    440              testExperiment: {
    441                enabled: true,
    442                name: "experiment-name",
    443              },
    444            },
    445          },
    446        })
    447      );
    448    });
    449  });
    450  it("should dispatch PREF_CHANGED when onWidgetsUpdated is called", () => {
    451    sandbox
    452      .stub(global.NimbusFeatures.newtabWidgets, "getAllVariables")
    453      .returns({
    454        enabled: true,
    455        listsEnabled: true,
    456        timerEnabled: false,
    457      });
    458 
    459    feed.onWidgetsUpdated();
    460 
    461    assert.calledWith(
    462      feed.store.dispatch,
    463      ac.BroadcastToContent({
    464        type: at.PREF_CHANGED,
    465        data: {
    466          name: "widgetsConfig",
    467          value: {
    468            enabled: true,
    469            listsEnabled: true,
    470            timerEnabled: false,
    471          },
    472        },
    473      })
    474    );
    475  });
    476  it("should remove all events on removeListeners", () => {
    477    feed.geo = "";
    478    sandbox.spy(global.NimbusFeatures.pocketNewtab, "offUpdate");
    479    sandbox.spy(global.NimbusFeatures.newtab, "offUpdate");
    480    sandbox.spy(global.NimbusFeatures.newtabTrainhop, "offUpdate");
    481    feed.removeListeners();
    482    assert.calledWith(
    483      global.NimbusFeatures.pocketNewtab.offUpdate,
    484      feed.onPocketExperimentUpdated
    485    );
    486    assert.calledWith(
    487      global.NimbusFeatures.newtab.offUpdate,
    488      feed.onExperimentUpdated
    489    );
    490    assert.calledWith(
    491      global.NimbusFeatures.newtabTrainhop.offUpdate,
    492      feed.onTrainhopExperimentUpdated
    493    );
    494    assert.calledWith(
    495      ServicesStub.obs.removeObserver,
    496      feed,
    497      global.Region.REGION_TOPIC
    498    );
    499  });
    500  it("should send OnlyToMain pref update if config for pref has skipBroadcast: true", async () => {
    501    feed.onPrefChanged("baz", { value: 2, skipBroadcast: true });
    502    assert.calledWith(
    503      feed.store.dispatch,
    504      ac.OnlyToMain({
    505        type: at.PREF_CHANGED,
    506        data: { name: "baz", value: { value: 2, skipBroadcast: true } },
    507      })
    508    );
    509  });
    510  it("should send AlsoToPreloaded pref update if config for pref has skipBroadcast: true and alsoToPreloaded: true", async () => {
    511    feed.onPrefChanged("qux", {
    512      value: 2,
    513      skipBroadcast: true,
    514      alsoToPreloaded: true,
    515    });
    516    assert.calledWith(
    517      feed.store.dispatch,
    518      ac.AlsoToPreloaded({
    519        type: at.PREF_CHANGED,
    520        data: {
    521          name: "qux",
    522          value: { value: 2, skipBroadcast: true, alsoToPreloaded: true },
    523        },
    524      })
    525    );
    526  });
    527  describe("#observe", () => {
    528    it("should call dispatch from observe", () => {
    529      feed.observe(undefined, global.Region.REGION_TOPIC);
    530      assert.calledOnce(feed.store.dispatch);
    531    });
    532  });
    533  describe("#_setStringPref", () => {
    534    it("should call _setPref and getStringPref from _setStringPref", () => {
    535      feed._setStringPref({}, "fake.pref", "default");
    536      assert.calledOnce(feed._setPref);
    537      assert.calledWith(
    538        feed._setPref,
    539        { "fake.pref": undefined },
    540        "fake.pref",
    541        "default"
    542      );
    543      assert.calledOnce(ServicesStub.prefs.getStringPref);
    544      assert.calledWith(
    545        ServicesStub.prefs.getStringPref,
    546        "browser.newtabpage.activity-stream.fake.pref",
    547        "default"
    548      );
    549    });
    550  });
    551  describe("#_setBoolPref", () => {
    552    it("should call _setPref and getBoolPref from _setBoolPref", () => {
    553      feed._setBoolPref({}, "fake.pref", false);
    554      assert.calledOnce(feed._setPref);
    555      assert.calledWith(
    556        feed._setPref,
    557        { "fake.pref": undefined },
    558        "fake.pref",
    559        false
    560      );
    561      assert.calledOnce(ServicesStub.prefs.getBoolPref);
    562      assert.calledWith(
    563        ServicesStub.prefs.getBoolPref,
    564        "browser.newtabpage.activity-stream.fake.pref",
    565        false
    566      );
    567    });
    568  });
    569  describe("#_setIntPref", () => {
    570    it("should call _setPref and getIntPref from _setIntPref", () => {
    571      feed._setIntPref({}, "fake.pref", 1);
    572      assert.calledOnce(feed._setPref);
    573      assert.calledWith(
    574        feed._setPref,
    575        { "fake.pref": undefined },
    576        "fake.pref",
    577        1
    578      );
    579      assert.calledOnce(ServicesStub.prefs.getIntPref);
    580      assert.calledWith(
    581        ServicesStub.prefs.getIntPref,
    582        "browser.newtabpage.activity-stream.fake.pref",
    583        1
    584      );
    585    });
    586  });
    587  describe("#_setPref", () => {
    588    it("should set pref value with _setPref", () => {
    589      const getPrefFunctionSpy = sinon.spy();
    590      const values = {};
    591      feed._setPref(values, "fake.pref", "default", getPrefFunctionSpy);
    592      assert.deepEqual(values, { "fake.pref": undefined });
    593      assert.calledOnce(getPrefFunctionSpy);
    594      assert.calledWith(
    595        getPrefFunctionSpy,
    596        "browser.newtabpage.activity-stream.fake.pref",
    597        "default"
    598      );
    599    });
    600  });
    601 });