summaryrefslogtreecommitdiffstats
path: root/browser/components/newtab/test/unit/lib/PersonalityProvider/PersonalityProvider.test.js
diff options
context:
space:
mode:
Diffstat (limited to 'browser/components/newtab/test/unit/lib/PersonalityProvider/PersonalityProvider.test.js')
-rw-r--r--browser/components/newtab/test/unit/lib/PersonalityProvider/PersonalityProvider.test.js356
1 files changed, 356 insertions, 0 deletions
diff --git a/browser/components/newtab/test/unit/lib/PersonalityProvider/PersonalityProvider.test.js b/browser/components/newtab/test/unit/lib/PersonalityProvider/PersonalityProvider.test.js
new file mode 100644
index 0000000000..833a9d5a7c
--- /dev/null
+++ b/browser/components/newtab/test/unit/lib/PersonalityProvider/PersonalityProvider.test.js
@@ -0,0 +1,356 @@
+import { GlobalOverrider } from "test/unit/utils";
+import { PersonalityProvider } from "lib/PersonalityProvider/PersonalityProvider.jsm";
+
+describe("Personality Provider", () => {
+ let instance;
+ let RemoteSettingsStub;
+ let RemoteSettingsOnStub;
+ let RemoteSettingsOffStub;
+ let RemoteSettingsGetStub;
+ let sandbox;
+ let globals;
+ let baseURLStub;
+
+ beforeEach(() => {
+ sandbox = sinon.createSandbox();
+ globals = new GlobalOverrider();
+
+ RemoteSettingsOnStub = sandbox.stub().returns();
+ RemoteSettingsOffStub = sandbox.stub().returns();
+ RemoteSettingsGetStub = sandbox.stub().returns([]);
+
+ RemoteSettingsStub = name => ({
+ get: RemoteSettingsGetStub,
+ on: RemoteSettingsOnStub,
+ off: RemoteSettingsOffStub,
+ });
+
+ sinon.spy(global, "BasePromiseWorker");
+ sinon.spy(global.BasePromiseWorker.prototype, "post");
+
+ baseURLStub = "https://baseattachmentsurl";
+ global.fetch = async server => ({
+ ok: true,
+ json: async () => {
+ if (server === "bogus://foo/") {
+ return { capabilities: { attachments: { base_url: baseURLStub } } };
+ }
+ return {};
+ },
+ });
+ globals.set("RemoteSettings", RemoteSettingsStub);
+
+ instance = new PersonalityProvider();
+ instance.interestConfig = {
+ history_item_builder: "history_item_builder",
+ history_required_fields: ["a", "b", "c"],
+ interest_finalizer: "interest_finalizer",
+ item_to_rank_builder: "item_to_rank_builder",
+ item_ranker: "item_ranker",
+ interest_combiner: "interest_combiner",
+ };
+ });
+ afterEach(() => {
+ sinon.restore();
+ sandbox.restore();
+ globals.restore();
+ });
+ describe("#personalityProviderWorker", () => {
+ it("should create a new promise worker on first call", async () => {
+ const { personalityProviderWorker } = instance;
+ assert.calledOnce(global.BasePromiseWorker);
+ assert.isDefined(personalityProviderWorker);
+ });
+ it("should cache _personalityProviderWorker on first call", async () => {
+ instance._personalityProviderWorker = null;
+ const { personalityProviderWorker } = instance;
+ assert.isDefined(instance._personalityProviderWorker);
+ assert.isDefined(personalityProviderWorker);
+ });
+ it("should use old promise worker on second call", async () => {
+ let { personalityProviderWorker } = instance;
+ personalityProviderWorker = instance.personalityProviderWorker;
+ assert.calledOnce(global.BasePromiseWorker);
+ assert.isDefined(personalityProviderWorker);
+ });
+ });
+ describe("#_getBaseAttachmentsURL", () => {
+ it("should return a fresh value", async () => {
+ await instance._getBaseAttachmentsURL();
+ assert.equal(instance._baseAttachmentsURL, baseURLStub);
+ });
+ it("should return a cached value", async () => {
+ const cachedURL = "cached";
+ instance._baseAttachmentsURL = cachedURL;
+ await instance._getBaseAttachmentsURL();
+ assert.equal(instance._baseAttachmentsURL, cachedURL);
+ });
+ });
+ describe("#setup", () => {
+ it("should setup two sync attachments", () => {
+ sinon.spy(instance, "setupSyncAttachment");
+ instance.setup();
+ assert.calledTwice(instance.setupSyncAttachment);
+ });
+ });
+ describe("#teardown", () => {
+ it("should teardown two sync attachments", () => {
+ sinon.spy(instance, "teardownSyncAttachment");
+ instance.teardown();
+ assert.calledTwice(instance.teardownSyncAttachment);
+ });
+ it("should terminate worker", () => {
+ const terminateStub = sandbox.stub().returns();
+ instance._personalityProviderWorker = {
+ terminate: terminateStub,
+ };
+ instance.teardown();
+ assert.calledOnce(terminateStub);
+ });
+ });
+ describe("#setupSyncAttachment", () => {
+ it("should call remote settings on twice for setupSyncAttachment", () => {
+ assert.calledTwice(RemoteSettingsOnStub);
+ });
+ });
+ describe("#teardownSyncAttachment", () => {
+ it("should call remote settings off for teardownSyncAttachment", () => {
+ instance.teardownSyncAttachment();
+ assert.calledOnce(RemoteSettingsOffStub);
+ });
+ });
+ describe("#onSync", () => {
+ it("should call worker onSync", () => {
+ instance.onSync();
+ assert.calledWith(global.BasePromiseWorker.prototype.post, "onSync");
+ });
+ });
+ describe("#getAttachment", () => {
+ it("should call worker onSync", () => {
+ instance.getAttachment();
+ assert.calledWith(
+ global.BasePromiseWorker.prototype.post,
+ "getAttachment"
+ );
+ });
+ });
+ describe("#getRecipe", () => {
+ it("should call worker getRecipe and remote settings get", async () => {
+ RemoteSettingsGetStub = sandbox.stub().returns([
+ {
+ key: 1,
+ },
+ ]);
+ sinon.spy(instance, "getAttachment");
+ RemoteSettingsStub = name => ({
+ get: RemoteSettingsGetStub,
+ on: RemoteSettingsOnStub,
+ off: RemoteSettingsOffStub,
+ });
+ globals.set("RemoteSettings", RemoteSettingsStub);
+
+ const result = await instance.getRecipe();
+ assert.calledOnce(RemoteSettingsGetStub);
+ assert.calledOnce(instance.getAttachment);
+ assert.equal(result.recordKey, 1);
+ });
+ });
+ describe("#fetchHistory", () => {
+ it("should return a history object for fetchHistory", async () => {
+ const history = await instance.fetchHistory(["requiredColumn"], 1, 1);
+ assert.equal(
+ history.sql,
+ `SELECT url, title, visit_count, frecency, last_visit_date, description\n FROM moz_places\n WHERE last_visit_date >= 1000000\n AND last_visit_date < 1000000 AND IFNULL(requiredColumn, '') <> '' LIMIT 30000`
+ );
+ assert.equal(history.options.columns.length, 1);
+ assert.equal(Object.keys(history.options.params).length, 0);
+ });
+ });
+ describe("#getHistory", () => {
+ it("should return an empty array", async () => {
+ instance.interestConfig = {
+ history_required_fields: [],
+ };
+ const result = await instance.getHistory();
+ assert.equal(result.length, 0);
+ });
+ it("should call fetchHistory", async () => {
+ sinon.spy(instance, "fetchHistory");
+ await instance.getHistory();
+ });
+ });
+ describe("#setBaseAttachmentsURL", () => {
+ it("should call worker setBaseAttachmentsURL", async () => {
+ await instance.setBaseAttachmentsURL();
+ assert.calledWith(
+ global.BasePromiseWorker.prototype.post,
+ "setBaseAttachmentsURL"
+ );
+ });
+ });
+ describe("#setInterestConfig", () => {
+ it("should call worker setInterestConfig", async () => {
+ await instance.setInterestConfig();
+ assert.calledWith(
+ global.BasePromiseWorker.prototype.post,
+ "setInterestConfig"
+ );
+ });
+ });
+ describe("#setInterestVector", () => {
+ it("should call worker setInterestVector", async () => {
+ await instance.setInterestVector();
+ assert.calledWith(
+ global.BasePromiseWorker.prototype.post,
+ "setInterestVector"
+ );
+ });
+ });
+ describe("#fetchModels", () => {
+ it("should call worker fetchModels and remote settings get", async () => {
+ await instance.fetchModels();
+ assert.calledOnce(RemoteSettingsGetStub);
+ assert.calledWith(global.BasePromiseWorker.prototype.post, "fetchModels");
+ });
+ });
+ describe("#generateTaggers", () => {
+ it("should call worker generateTaggers", async () => {
+ await instance.generateTaggers();
+ assert.calledWith(
+ global.BasePromiseWorker.prototype.post,
+ "generateTaggers"
+ );
+ });
+ });
+ describe("#generateRecipeExecutor", () => {
+ it("should call worker generateRecipeExecutor", async () => {
+ await instance.generateRecipeExecutor();
+ assert.calledWith(
+ global.BasePromiseWorker.prototype.post,
+ "generateRecipeExecutor"
+ );
+ });
+ });
+ describe("#createInterestVector", () => {
+ it("should call worker createInterestVector", async () => {
+ await instance.createInterestVector();
+ assert.calledWith(
+ global.BasePromiseWorker.prototype.post,
+ "createInterestVector"
+ );
+ });
+ });
+ describe("#init", () => {
+ it("should return early if setInterestConfig fails", async () => {
+ sandbox.stub(instance, "setBaseAttachmentsURL").returns();
+ sandbox.stub(instance, "setInterestConfig").returns();
+ instance.interestConfig = null;
+ const callback = globals.sandbox.stub();
+ await instance.init(callback);
+ assert.notCalled(callback);
+ });
+ it("should return early if fetchModels fails", async () => {
+ sandbox.stub(instance, "setBaseAttachmentsURL").returns();
+ sandbox.stub(instance, "setInterestConfig").returns();
+ sandbox.stub(instance, "fetchModels").resolves({
+ ok: false,
+ });
+ const callback = globals.sandbox.stub();
+ await instance.init(callback);
+ assert.notCalled(callback);
+ });
+ it("should return early if createInterestVector fails", async () => {
+ sandbox.stub(instance, "setBaseAttachmentsURL").returns();
+ sandbox.stub(instance, "setInterestConfig").returns();
+ sandbox.stub(instance, "fetchModels").resolves({
+ ok: true,
+ });
+ sandbox.stub(instance, "generateRecipeExecutor").resolves({
+ ok: true,
+ });
+ sandbox.stub(instance, "createInterestVector").resolves({
+ ok: false,
+ });
+ const callback = globals.sandbox.stub();
+ await instance.init(callback);
+ assert.notCalled(callback);
+ });
+ it("should call callback on successful init", async () => {
+ sandbox.stub(instance, "setBaseAttachmentsURL").returns();
+ sandbox.stub(instance, "setInterestConfig").returns();
+ sandbox.stub(instance, "fetchModels").resolves({
+ ok: true,
+ });
+ sandbox.stub(instance, "generateRecipeExecutor").resolves({
+ ok: true,
+ });
+ sandbox.stub(instance, "createInterestVector").resolves({
+ ok: true,
+ });
+ sandbox.stub(instance, "setInterestVector").resolves();
+ const callback = globals.sandbox.stub();
+ await instance.init(callback);
+ assert.calledOnce(callback);
+ assert.isTrue(instance.initialized);
+ });
+ it("should do generic init stuff when calling init with no cache", async () => {
+ sandbox.stub(instance, "setBaseAttachmentsURL").returns();
+ sandbox.stub(instance, "setInterestConfig").returns();
+ sandbox.stub(instance, "fetchModels").resolves({
+ ok: true,
+ });
+ sandbox.stub(instance, "generateRecipeExecutor").resolves({
+ ok: true,
+ });
+ sandbox.stub(instance, "createInterestVector").resolves({
+ ok: true,
+ interestVector: "interestVector",
+ });
+ sandbox.stub(instance, "setInterestVector").resolves();
+ await instance.init();
+ assert.calledOnce(instance.setBaseAttachmentsURL);
+ assert.calledOnce(instance.setInterestConfig);
+ assert.calledOnce(instance.fetchModels);
+ assert.calledOnce(instance.generateRecipeExecutor);
+ assert.calledOnce(instance.createInterestVector);
+ assert.calledOnce(instance.setInterestVector);
+ });
+ });
+ describe("#calculateItemRelevanceScore", () => {
+ it("should return score for uninitialized provider", async () => {
+ instance.initialized = false;
+ assert.equal(
+ await instance.calculateItemRelevanceScore({ item_score: 2 }),
+ 2
+ );
+ });
+ it("should return score for initialized provider", async () => {
+ instance.initialized = true;
+
+ instance._personalityProviderWorker = {
+ post: (postName, [item]) => ({
+ rankingVector: { score: item.item_score },
+ }),
+ };
+
+ assert.equal(
+ await instance.calculateItemRelevanceScore({ item_score: 2 }),
+ 2
+ );
+ });
+ it("should post calculateItemRelevanceScore to PersonalityProviderWorker", async () => {
+ instance.initialized = true;
+ await instance.calculateItemRelevanceScore({ item_score: 2 });
+ assert.calledWith(
+ global.BasePromiseWorker.prototype.post,
+ "calculateItemRelevanceScore"
+ );
+ });
+ });
+ describe("#getScores", () => {
+ it("should return correct data for getScores", () => {
+ const scores = instance.getScores();
+ assert.isDefined(scores.interestConfig);
+ });
+ });
+});