tor-browser

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

RecommendationProvider.test.js (9439B)


      1 import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
      2 import { RecommendationProvider } from "lib/RecommendationProvider.sys.mjs";
      3 import { combineReducers, createStore } from "redux";
      4 import { reducers } from "common/Reducers.sys.mjs";
      5 import { GlobalOverrider } from "test/unit/utils";
      6 
      7 import { PersonalityProvider } from "lib/PersonalityProvider/PersonalityProvider.sys.mjs";
      8 import { PersistentCache } from "lib/PersistentCache.sys.mjs";
      9 
     10 const PREF_PERSONALIZATION_ENABLED = "discoverystream.personalization.enabled";
     11 const PREF_PERSONALIZATION_MODEL_KEYS =
     12  "discoverystream.personalization.modelKeys";
     13 describe("RecommendationProvider", () => {
     14  let feed;
     15  let sandbox;
     16  let clock;
     17  let globals;
     18 
     19  beforeEach(() => {
     20    globals = new GlobalOverrider();
     21    globals.set({
     22      PersistentCache,
     23      PersonalityProvider,
     24    });
     25 
     26    sandbox = sinon.createSandbox();
     27    clock = sinon.useFakeTimers();
     28    feed = new RecommendationProvider();
     29    feed.store = createStore(combineReducers(reducers), {});
     30  });
     31 
     32  afterEach(() => {
     33    sandbox.restore();
     34    clock.restore();
     35    globals.restore();
     36  });
     37 
     38  describe("#setProvider", () => {
     39    it("should setup proper provider with modelKeys", async () => {
     40      feed.setProvider();
     41 
     42      assert.equal(feed.provider.modelKeys, undefined);
     43 
     44      feed.provider = null;
     45      feed._modelKeys = "1234";
     46 
     47      feed.setProvider();
     48 
     49      assert.equal(feed.provider.modelKeys, "1234");
     50      feed._modelKeys = "12345";
     51 
     52      // Calling it again should not rebuild the provider.
     53      feed.setProvider();
     54      assert.equal(feed.provider.modelKeys, "1234");
     55    });
     56  });
     57 
     58  describe("#calculateItemRelevanceScore", () => {
     59    it("should use personalized score with provider", async () => {
     60      const item = {};
     61      feed.provider = {
     62        calculateItemRelevanceScore: async () => 0.5,
     63      };
     64      await feed.calculateItemRelevanceScore(item);
     65      assert.equal(item.score, 0.5);
     66    });
     67  });
     68 
     69  describe("#teardown", () => {
     70    it("should call provider.teardown ", () => {
     71      sandbox.stub(global.Services.obs, "removeObserver").returns();
     72      feed.loaded = true;
     73      feed.provider = {
     74        teardown: sandbox.stub().resolves(),
     75      };
     76      feed.teardown();
     77      assert.calledOnce(feed.provider.teardown);
     78      assert.calledOnce(global.Services.obs.removeObserver);
     79      assert.calledWith(global.Services.obs.removeObserver, feed, "idle-daily");
     80    });
     81  });
     82 
     83  describe("#resetState", () => {
     84    it("should null affinityProviderV2 and affinityProvider", () => {
     85      feed._modelKeys = {};
     86      feed.provider = {};
     87 
     88      feed.resetState();
     89 
     90      assert.equal(feed._modelKeys, null);
     91      assert.equal(feed.provider, null);
     92    });
     93  });
     94 
     95  describe("#onAction: DISCOVERY_STREAM_CONFIG_CHANGE", () => {
     96    it("should call teardown, resetState, and setVersion", async () => {
     97      sandbox.spy(feed, "teardown");
     98      sandbox.spy(feed, "resetState");
     99      feed.onAction({
    100        type: at.DISCOVERY_STREAM_CONFIG_CHANGE,
    101      });
    102      assert.calledOnce(feed.teardown);
    103      assert.calledOnce(feed.resetState);
    104    });
    105  });
    106 
    107  describe("#onAction: PREF_CHANGED", () => {
    108    beforeEach(() => {
    109      sandbox.spy(feed.store, "dispatch");
    110    });
    111    it("should dispatch to DISCOVERY_STREAM_CONFIG_RESET PREF_PERSONALIZATION_MODEL_KEYS", async () => {
    112      feed.onAction({
    113        type: at.PREF_CHANGED,
    114        data: {
    115          name: PREF_PERSONALIZATION_MODEL_KEYS,
    116        },
    117      });
    118 
    119      assert.calledWith(
    120        feed.store.dispatch,
    121        ac.BroadcastToContent({
    122          type: at.DISCOVERY_STREAM_CONFIG_RESET,
    123        })
    124      );
    125    });
    126  });
    127 
    128  describe("#personalizationOverride", () => {
    129    it("should dispatch setPref", async () => {
    130      sandbox.spy(feed.store, "dispatch");
    131      feed.store.getState = () => ({
    132        Prefs: {
    133          values: {
    134            "discoverystream.personalization.enabled": true,
    135          },
    136        },
    137      });
    138 
    139      feed.personalizationOverride(true);
    140 
    141      assert.calledWithMatch(feed.store.dispatch, {
    142        data: {
    143          name: "discoverystream.personalization.override",
    144          value: true,
    145        },
    146        type: at.SET_PREF,
    147      });
    148    });
    149    it("should dispatch CLEAR_PREF", async () => {
    150      sandbox.spy(feed.store, "dispatch");
    151      feed.store.getState = () => ({
    152        Prefs: {
    153          values: {
    154            "discoverystream.personalization.enabled": true,
    155            "discoverystream.personalization.override": true,
    156          },
    157        },
    158      });
    159 
    160      feed.personalizationOverride(false);
    161 
    162      assert.calledWithMatch(feed.store.dispatch, {
    163        data: {
    164          name: "discoverystream.personalization.override",
    165        },
    166        type: at.CLEAR_PREF,
    167      });
    168    });
    169  });
    170 
    171  describe("#onAction: DISCOVERY_STREAM_DEV_IDLE_DAILY", () => {
    172    it("should trigger idle-daily observer", async () => {
    173      sandbox.stub(global.Services.obs, "notifyObservers").returns();
    174      await feed.onAction({
    175        type: at.DISCOVERY_STREAM_DEV_IDLE_DAILY,
    176      });
    177      assert.calledWith(
    178        global.Services.obs.notifyObservers,
    179        null,
    180        "idle-daily"
    181      );
    182    });
    183  });
    184 
    185  describe("#onAction: INIT", () => {
    186    it("should ", async () => {
    187      sandbox.stub(feed, "enable").returns();
    188      await feed.onAction({
    189        type: at.INIT,
    190      });
    191      assert.calledOnce(feed.enable);
    192      assert.calledWith(feed.enable, true);
    193    });
    194  });
    195 
    196  describe("#onAction: DISCOVERY_STREAM_PERSONALIZATION_OVERRIDE", () => {
    197    it("should ", async () => {
    198      sandbox.stub(feed, "personalizationOverride").returns();
    199      await feed.onAction({
    200        type: at.DISCOVERY_STREAM_PERSONALIZATION_OVERRIDE,
    201        data: { override: true },
    202      });
    203      assert.calledOnce(feed.personalizationOverride);
    204      assert.calledWith(feed.personalizationOverride, true);
    205    });
    206  });
    207 
    208  describe("#loadPersonalizationScoresCache", () => {
    209    it("should create a personalization provider from cached scores", async () => {
    210      sandbox.spy(feed.store, "dispatch");
    211      sandbox.spy(feed.cache, "set");
    212      feed.provider = {
    213        init: async () => {},
    214        getScores: () => "scores",
    215      };
    216      feed.store.getState = () => ({
    217        Prefs: {
    218          values: {
    219            pocketConfig: {
    220              recsPersonalized: true,
    221              spocsPersonalized: true,
    222            },
    223            "discoverystream.personalization.enabled": true,
    224            "feeds.section.topstories": true,
    225            "feeds.system.topstories": true,
    226          },
    227        },
    228      });
    229      const fakeCache = {
    230        personalization: {
    231          scores: 123,
    232          _timestamp: 456,
    233        },
    234      };
    235      sandbox.stub(feed.cache, "get").returns(Promise.resolve(fakeCache));
    236 
    237      await feed.loadPersonalizationScoresCache();
    238 
    239      assert.equal(feed.personalizationLastUpdated, 456);
    240    });
    241  });
    242 
    243  describe("#updatePersonalizationScores", () => {
    244    beforeEach(() => {
    245      sandbox.spy(feed.store, "dispatch");
    246      sandbox.spy(feed.cache, "set");
    247      sandbox.spy(feed, "setProvider");
    248      feed.provider = {
    249        init: async () => {},
    250        getScores: () => "scores",
    251      };
    252    });
    253    it("should update provider on updatePersonalizationScores", async () => {
    254      feed.store.getState = () => ({
    255        Prefs: {
    256          values: {
    257            pocketConfig: {
    258              recsPersonalized: true,
    259              spocsPersonalized: true,
    260            },
    261            "discoverystream.personalization.enabled": true,
    262            "feeds.section.topstories": true,
    263            "feeds.system.topstories": true,
    264          },
    265        },
    266      });
    267 
    268      await feed.updatePersonalizationScores();
    269 
    270      assert.calledWith(
    271        feed.store.dispatch,
    272        ac.BroadcastToContent({
    273          type: at.DISCOVERY_STREAM_PERSONALIZATION_LAST_UPDATED,
    274          data: {
    275            lastUpdated: 0,
    276          },
    277        })
    278      );
    279      assert.calledWith(feed.cache.set, "personalization", {
    280        scores: "scores",
    281        _timestamp: 0,
    282      });
    283    });
    284    it("should not update provider on updatePersonalizationScores", async () => {
    285      feed.store.getState = () => ({
    286        Prefs: {
    287          values: {
    288            "discoverystream.spocs.personalized": true,
    289            "discoverystream.recs.personalized": true,
    290            "discoverystream.personalization.enabled": false,
    291          },
    292        },
    293      });
    294      await feed.updatePersonalizationScores();
    295 
    296      assert.notCalled(feed.setProvider);
    297    });
    298  });
    299 
    300  describe("#onAction: DISCOVERY_STREAM_PERSONALIZATION_TOGGLE", () => {
    301    it("should fire SET_PREF with enabled", async () => {
    302      sandbox.spy(feed.store, "dispatch");
    303      feed.store.getState = () => ({
    304        Prefs: {
    305          values: {
    306            [PREF_PERSONALIZATION_ENABLED]: false,
    307          },
    308        },
    309      });
    310 
    311      await feed.onAction({
    312        type: at.DISCOVERY_STREAM_PERSONALIZATION_TOGGLE,
    313      });
    314      assert.calledWith(
    315        feed.store.dispatch,
    316        ac.SetPref(PREF_PERSONALIZATION_ENABLED, true)
    317      );
    318    });
    319  });
    320 
    321  describe("#observe", () => {
    322    it("should call updatePersonalizationScores on idle daily", async () => {
    323      sandbox.stub(feed, "updatePersonalizationScores").returns();
    324      feed.observe(null, "idle-daily");
    325      assert.calledOnce(feed.updatePersonalizationScores);
    326    });
    327  });
    328 });