328 lines
9.2 KiB
JavaScript
328 lines
9.2 KiB
JavaScript
import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
|
|
import { RecommendationProvider } from "lib/RecommendationProvider.sys.mjs";
|
|
import { combineReducers, createStore } from "redux";
|
|
import { reducers } from "common/Reducers.sys.mjs";
|
|
import { GlobalOverrider } from "test/unit/utils";
|
|
|
|
import { PersonalityProvider } from "lib/PersonalityProvider/PersonalityProvider.sys.mjs";
|
|
import { PersistentCache } from "lib/PersistentCache.sys.mjs";
|
|
|
|
const PREF_PERSONALIZATION_ENABLED = "discoverystream.personalization.enabled";
|
|
const PREF_PERSONALIZATION_MODEL_KEYS =
|
|
"discoverystream.personalization.modelKeys";
|
|
describe("RecommendationProvider", () => {
|
|
let feed;
|
|
let sandbox;
|
|
let clock;
|
|
let globals;
|
|
|
|
beforeEach(() => {
|
|
globals = new GlobalOverrider();
|
|
globals.set({
|
|
PersistentCache,
|
|
PersonalityProvider,
|
|
});
|
|
|
|
sandbox = sinon.createSandbox();
|
|
clock = sinon.useFakeTimers();
|
|
feed = new RecommendationProvider();
|
|
feed.store = createStore(combineReducers(reducers), {});
|
|
});
|
|
|
|
afterEach(() => {
|
|
sandbox.restore();
|
|
clock.restore();
|
|
globals.restore();
|
|
});
|
|
|
|
describe("#setProvider", () => {
|
|
it("should setup proper provider with modelKeys", async () => {
|
|
feed.setProvider();
|
|
|
|
assert.equal(feed.provider.modelKeys, undefined);
|
|
|
|
feed.provider = null;
|
|
feed._modelKeys = "1234";
|
|
|
|
feed.setProvider();
|
|
|
|
assert.equal(feed.provider.modelKeys, "1234");
|
|
feed._modelKeys = "12345";
|
|
|
|
// Calling it again should not rebuild the provider.
|
|
feed.setProvider();
|
|
assert.equal(feed.provider.modelKeys, "1234");
|
|
});
|
|
});
|
|
|
|
describe("#calculateItemRelevanceScore", () => {
|
|
it("should use personalized score with provider", async () => {
|
|
const item = {};
|
|
feed.provider = {
|
|
calculateItemRelevanceScore: async () => 0.5,
|
|
};
|
|
await feed.calculateItemRelevanceScore(item);
|
|
assert.equal(item.score, 0.5);
|
|
});
|
|
});
|
|
|
|
describe("#teardown", () => {
|
|
it("should call provider.teardown ", () => {
|
|
sandbox.stub(global.Services.obs, "removeObserver").returns();
|
|
feed.loaded = true;
|
|
feed.provider = {
|
|
teardown: sandbox.stub().resolves(),
|
|
};
|
|
feed.teardown();
|
|
assert.calledOnce(feed.provider.teardown);
|
|
assert.calledOnce(global.Services.obs.removeObserver);
|
|
assert.calledWith(global.Services.obs.removeObserver, feed, "idle-daily");
|
|
});
|
|
});
|
|
|
|
describe("#resetState", () => {
|
|
it("should null affinityProviderV2 and affinityProvider", () => {
|
|
feed._modelKeys = {};
|
|
feed.provider = {};
|
|
|
|
feed.resetState();
|
|
|
|
assert.equal(feed._modelKeys, null);
|
|
assert.equal(feed.provider, null);
|
|
});
|
|
});
|
|
|
|
describe("#onAction: DISCOVERY_STREAM_CONFIG_CHANGE", () => {
|
|
it("should call teardown, resetState, and setVersion", async () => {
|
|
sandbox.spy(feed, "teardown");
|
|
sandbox.spy(feed, "resetState");
|
|
feed.onAction({
|
|
type: at.DISCOVERY_STREAM_CONFIG_CHANGE,
|
|
});
|
|
assert.calledOnce(feed.teardown);
|
|
assert.calledOnce(feed.resetState);
|
|
});
|
|
});
|
|
|
|
describe("#onAction: PREF_CHANGED", () => {
|
|
beforeEach(() => {
|
|
sandbox.spy(feed.store, "dispatch");
|
|
});
|
|
it("should dispatch to DISCOVERY_STREAM_CONFIG_RESET PREF_PERSONALIZATION_MODEL_KEYS", async () => {
|
|
feed.onAction({
|
|
type: at.PREF_CHANGED,
|
|
data: {
|
|
name: PREF_PERSONALIZATION_MODEL_KEYS,
|
|
},
|
|
});
|
|
|
|
assert.calledWith(
|
|
feed.store.dispatch,
|
|
ac.BroadcastToContent({
|
|
type: at.DISCOVERY_STREAM_CONFIG_RESET,
|
|
})
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("#personalizationOverride", () => {
|
|
it("should dispatch setPref", async () => {
|
|
sandbox.spy(feed.store, "dispatch");
|
|
feed.store.getState = () => ({
|
|
Prefs: {
|
|
values: {
|
|
"discoverystream.personalization.enabled": true,
|
|
},
|
|
},
|
|
});
|
|
|
|
feed.personalizationOverride(true);
|
|
|
|
assert.calledWithMatch(feed.store.dispatch, {
|
|
data: {
|
|
name: "discoverystream.personalization.override",
|
|
value: true,
|
|
},
|
|
type: at.SET_PREF,
|
|
});
|
|
});
|
|
it("should dispatch CLEAR_PREF", async () => {
|
|
sandbox.spy(feed.store, "dispatch");
|
|
feed.store.getState = () => ({
|
|
Prefs: {
|
|
values: {
|
|
"discoverystream.personalization.enabled": true,
|
|
"discoverystream.personalization.override": true,
|
|
},
|
|
},
|
|
});
|
|
|
|
feed.personalizationOverride(false);
|
|
|
|
assert.calledWithMatch(feed.store.dispatch, {
|
|
data: {
|
|
name: "discoverystream.personalization.override",
|
|
},
|
|
type: at.CLEAR_PREF,
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("#onAction: DISCOVERY_STREAM_DEV_IDLE_DAILY", () => {
|
|
it("should trigger idle-daily observer", async () => {
|
|
sandbox.stub(global.Services.obs, "notifyObservers").returns();
|
|
await feed.onAction({
|
|
type: at.DISCOVERY_STREAM_DEV_IDLE_DAILY,
|
|
});
|
|
assert.calledWith(
|
|
global.Services.obs.notifyObservers,
|
|
null,
|
|
"idle-daily"
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("#onAction: INIT", () => {
|
|
it("should ", async () => {
|
|
sandbox.stub(feed, "enable").returns();
|
|
await feed.onAction({
|
|
type: at.INIT,
|
|
});
|
|
assert.calledOnce(feed.enable);
|
|
assert.calledWith(feed.enable, true);
|
|
});
|
|
});
|
|
|
|
describe("#onAction: DISCOVERY_STREAM_PERSONALIZATION_OVERRIDE", () => {
|
|
it("should ", async () => {
|
|
sandbox.stub(feed, "personalizationOverride").returns();
|
|
await feed.onAction({
|
|
type: at.DISCOVERY_STREAM_PERSONALIZATION_OVERRIDE,
|
|
data: { override: true },
|
|
});
|
|
assert.calledOnce(feed.personalizationOverride);
|
|
assert.calledWith(feed.personalizationOverride, true);
|
|
});
|
|
});
|
|
|
|
describe("#loadPersonalizationScoresCache", () => {
|
|
it("should create a personalization provider from cached scores", async () => {
|
|
sandbox.spy(feed.store, "dispatch");
|
|
sandbox.spy(feed.cache, "set");
|
|
feed.provider = {
|
|
init: async () => {},
|
|
getScores: () => "scores",
|
|
};
|
|
feed.store.getState = () => ({
|
|
Prefs: {
|
|
values: {
|
|
pocketConfig: {
|
|
recsPersonalized: true,
|
|
spocsPersonalized: true,
|
|
},
|
|
"discoverystream.personalization.enabled": true,
|
|
"feeds.section.topstories": true,
|
|
"feeds.system.topstories": true,
|
|
},
|
|
},
|
|
});
|
|
const fakeCache = {
|
|
personalization: {
|
|
scores: 123,
|
|
_timestamp: 456,
|
|
},
|
|
};
|
|
sandbox.stub(feed.cache, "get").returns(Promise.resolve(fakeCache));
|
|
|
|
await feed.loadPersonalizationScoresCache();
|
|
|
|
assert.equal(feed.personalizationLastUpdated, 456);
|
|
});
|
|
});
|
|
|
|
describe("#updatePersonalizationScores", () => {
|
|
beforeEach(() => {
|
|
sandbox.spy(feed.store, "dispatch");
|
|
sandbox.spy(feed.cache, "set");
|
|
sandbox.spy(feed, "setProvider");
|
|
feed.provider = {
|
|
init: async () => {},
|
|
getScores: () => "scores",
|
|
};
|
|
});
|
|
it("should update provider on updatePersonalizationScores", async () => {
|
|
feed.store.getState = () => ({
|
|
Prefs: {
|
|
values: {
|
|
pocketConfig: {
|
|
recsPersonalized: true,
|
|
spocsPersonalized: true,
|
|
},
|
|
"discoverystream.personalization.enabled": true,
|
|
"feeds.section.topstories": true,
|
|
"feeds.system.topstories": true,
|
|
},
|
|
},
|
|
});
|
|
|
|
await feed.updatePersonalizationScores();
|
|
|
|
assert.calledWith(
|
|
feed.store.dispatch,
|
|
ac.BroadcastToContent({
|
|
type: at.DISCOVERY_STREAM_PERSONALIZATION_LAST_UPDATED,
|
|
data: {
|
|
lastUpdated: 0,
|
|
},
|
|
})
|
|
);
|
|
assert.calledWith(feed.cache.set, "personalization", {
|
|
scores: "scores",
|
|
_timestamp: 0,
|
|
});
|
|
});
|
|
it("should not update provider on updatePersonalizationScores", async () => {
|
|
feed.store.getState = () => ({
|
|
Prefs: {
|
|
values: {
|
|
"discoverystream.spocs.personalized": true,
|
|
"discoverystream.recs.personalized": true,
|
|
"discoverystream.personalization.enabled": false,
|
|
},
|
|
},
|
|
});
|
|
await feed.updatePersonalizationScores();
|
|
|
|
assert.notCalled(feed.setProvider);
|
|
});
|
|
});
|
|
|
|
describe("#onAction: DISCOVERY_STREAM_PERSONALIZATION_TOGGLE", () => {
|
|
it("should fire SET_PREF with enabled", async () => {
|
|
sandbox.spy(feed.store, "dispatch");
|
|
feed.store.getState = () => ({
|
|
Prefs: {
|
|
values: {
|
|
[PREF_PERSONALIZATION_ENABLED]: false,
|
|
},
|
|
},
|
|
});
|
|
|
|
await feed.onAction({
|
|
type: at.DISCOVERY_STREAM_PERSONALIZATION_TOGGLE,
|
|
});
|
|
assert.calledWith(
|
|
feed.store.dispatch,
|
|
ac.SetPref(PREF_PERSONALIZATION_ENABLED, true)
|
|
);
|
|
});
|
|
});
|
|
|
|
describe("#observe", () => {
|
|
it("should call updatePersonalizationScores on idle daily", async () => {
|
|
sandbox.stub(feed, "updatePersonalizationScores").returns();
|
|
feed.observe(null, "idle-daily");
|
|
assert.calledOnce(feed.updatePersonalizationScores);
|
|
});
|
|
});
|
|
});
|