tor-browser

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

MomentsPageHub.test.js (10756B)


      1 import { GlobalOverrider } from "tests/unit/utils";
      2 import { PanelTestProvider } from "modules/PanelTestProvider.sys.mjs";
      3 import { _MomentsPageHub } from "modules/MomentsPageHub.sys.mjs";
      4 const HOMEPAGE_OVERRIDE_PREF = "browser.startup.homepage_override.once";
      5 
      6 describe("MomentsPageHub", () => {
      7  let globals;
      8  let sandbox;
      9  let instance;
     10  let handleMessageRequestStub;
     11  let addImpressionStub;
     12  let blockMessageByIdStub;
     13  let sendTelemetryStub;
     14  let getStringPrefStub;
     15  let setStringPrefStub;
     16  let setIntervalStub;
     17  let clearIntervalStub;
     18 
     19  beforeEach(async () => {
     20    globals = new GlobalOverrider();
     21    sandbox = sinon.createSandbox();
     22    instance = new _MomentsPageHub();
     23    const messages = (await PanelTestProvider.getMessages()).filter(
     24      ({ template }) => template === "update_action"
     25    );
     26    handleMessageRequestStub = sandbox.stub().resolves(messages);
     27    addImpressionStub = sandbox.stub();
     28    blockMessageByIdStub = sandbox.stub();
     29    getStringPrefStub = sandbox.stub();
     30    setStringPrefStub = sandbox.stub();
     31    setIntervalStub = sandbox.stub();
     32    clearIntervalStub = sandbox.stub();
     33    sendTelemetryStub = sandbox.stub();
     34    globals.set({
     35      setInterval: setIntervalStub,
     36      clearInterval: clearIntervalStub,
     37      Services: {
     38        prefs: {
     39          getStringPref: getStringPrefStub,
     40          setStringPref: setStringPrefStub,
     41        },
     42      },
     43      Glean: {
     44        messagingExperiments: {
     45          reachMomentsPage: {
     46            record: () => {},
     47          },
     48        },
     49        messagingSystem: {
     50          messageRequestTime: {
     51            start() {},
     52            stopAndAccumulate() {},
     53          },
     54        },
     55      },
     56    });
     57  });
     58 
     59  afterEach(() => {
     60    sandbox.restore();
     61    globals.restore();
     62  });
     63 
     64  it("should create an instance", async () => {
     65    setIntervalStub.returns(42);
     66    assert.ok(instance);
     67    await instance.init(Promise.resolve(), {
     68      handleMessageRequest: handleMessageRequestStub,
     69      addImpression: addImpressionStub,
     70      blockMessageById: blockMessageByIdStub,
     71    });
     72    assert.equal(instance.state._intervalId, 42);
     73  });
     74 
     75  it("should init only once", async () => {
     76    assert.notCalled(handleMessageRequestStub);
     77 
     78    await instance.init(Promise.resolve(), {
     79      handleMessageRequest: handleMessageRequestStub,
     80      addImpression: addImpressionStub,
     81      blockMessageById: blockMessageByIdStub,
     82    });
     83    await instance.init(Promise.resolve(), {
     84      handleMessageRequest: handleMessageRequestStub,
     85      addImpression: addImpressionStub,
     86      blockMessageById: blockMessageByIdStub,
     87    });
     88 
     89    assert.calledOnce(handleMessageRequestStub);
     90 
     91    instance.uninit();
     92 
     93    await instance.init(Promise.resolve(), {
     94      handleMessageRequest: handleMessageRequestStub,
     95      addImpression: addImpressionStub,
     96      blockMessageById: blockMessageByIdStub,
     97    });
     98 
     99    assert.calledTwice(handleMessageRequestStub);
    100  });
    101 
    102  it("should uninit the instance", () => {
    103    instance.uninit();
    104    assert.calledOnce(clearIntervalStub);
    105  });
    106 
    107  it("should setInterval for `checkHomepageOverridePref`", async () => {
    108    await instance.init(sandbox.stub().resolves(), {});
    109    sandbox.stub(instance, "checkHomepageOverridePref");
    110 
    111    assert.calledOnce(setIntervalStub);
    112    assert.calledWithExactly(setIntervalStub, sinon.match.func, 5 * 60 * 1000);
    113 
    114    assert.notCalled(instance.checkHomepageOverridePref);
    115    const [cb] = setIntervalStub.firstCall.args;
    116 
    117    cb();
    118 
    119    assert.calledOnce(instance.checkHomepageOverridePref);
    120  });
    121 
    122  describe("#messageRequest", () => {
    123    beforeEach(async () => {
    124      await instance.init(Promise.resolve(), {
    125        handleMessageRequest: handleMessageRequestStub,
    126        addImpression: addImpressionStub,
    127        blockMessageById: blockMessageByIdStub,
    128        sendTelemetry: sendTelemetryStub,
    129      });
    130    });
    131    afterEach(() => {
    132      instance.uninit();
    133    });
    134    it("should fetch a message with the provided trigger and template", async () => {
    135      await instance.messageRequest({
    136        triggerId: "trigger",
    137        template: "template",
    138      });
    139 
    140      assert.calledTwice(handleMessageRequestStub);
    141      assert.calledWithExactly(handleMessageRequestStub, {
    142        triggerId: "trigger",
    143        template: "template",
    144        returnAll: true,
    145      });
    146    });
    147    it("shouldn't do anything if no message is provided", async () => {
    148      // Reset the call from `instance.init`
    149      setStringPrefStub.reset();
    150      handleMessageRequestStub.resolves([]);
    151      await instance.messageRequest({ triggerId: "trigger" });
    152 
    153      assert.notCalled(setStringPrefStub);
    154    });
    155    it("should record a message request time", async () => {
    156      const fakeTimerId = 42;
    157      const start = sandbox
    158        .stub(global.Glean.messagingSystem.messageRequestTime, "start")
    159        .returns(fakeTimerId);
    160      const stopAndAccumulate = sandbox.stub(
    161        global.Glean.messagingSystem.messageRequestTime,
    162        "stopAndAccumulate"
    163      );
    164 
    165      await instance.messageRequest({ triggerId: "trigger" });
    166 
    167      assert.calledOnce(start);
    168      assert.calledWithExactly(start);
    169      assert.calledOnce(stopAndAccumulate);
    170      assert.calledWithExactly(stopAndAccumulate, fakeTimerId);
    171    });
    172    it("should record Reach event for the Moments page experiment", async () => {
    173      const momentsMessages = (await PanelTestProvider.getMessages()).filter(
    174        ({ template }) => template === "update_action"
    175      );
    176      const messages = [
    177        {
    178          forReachEvent: { sent: false },
    179          experimentSlug: "foo",
    180          branchSlug: "bar",
    181        },
    182        ...momentsMessages,
    183      ];
    184      handleMessageRequestStub.resolves(messages);
    185      sandbox.spy(global.Glean.messagingExperiments.reachMomentsPage, "record");
    186      sandbox.spy(instance, "executeAction");
    187 
    188      await instance.messageRequest({ triggerId: "trigger" });
    189 
    190      assert.calledOnce(
    191        global.Glean.messagingExperiments.reachMomentsPage.record
    192      );
    193      assert.calledOnce(instance.executeAction);
    194    });
    195    it("should not record the Reach event if it's already sent", async () => {
    196      const messages = [
    197        {
    198          forReachEvent: { sent: true },
    199          experimentSlug: "foo",
    200          branchSlug: "bar",
    201        },
    202      ];
    203      handleMessageRequestStub.resolves(messages);
    204      sandbox.spy(global.Glean.messagingExperiments.reachMomentsPage, "record");
    205 
    206      await instance.messageRequest({ triggerId: "trigger" });
    207 
    208      assert.notCalled(
    209        global.Glean.messagingExperiments.reachMomentsPage.record
    210      );
    211    });
    212    it("should not trigger the action if it's only for the Reach event", async () => {
    213      const messages = [
    214        {
    215          forReachEvent: { sent: false },
    216          experimentSlug: "foo",
    217          branchSlug: "bar",
    218        },
    219      ];
    220      handleMessageRequestStub.resolves(messages);
    221      sandbox.spy(global.Glean.messagingExperiments.reachMomentsPage, "record");
    222      sandbox.spy(instance, "executeAction");
    223 
    224      await instance.messageRequest({ triggerId: "trigger" });
    225 
    226      assert.calledOnce(
    227        global.Glean.messagingExperiments.reachMomentsPage.record
    228      );
    229      assert.notCalled(instance.executeAction);
    230    });
    231  });
    232  describe("executeAction", () => {
    233    beforeEach(async () => {
    234      blockMessageByIdStub = sandbox.stub();
    235      await instance.init(sandbox.stub().resolves(), {
    236        addImpression: addImpressionStub,
    237        blockMessageById: blockMessageByIdStub,
    238        sendTelemetry: sendTelemetryStub,
    239      });
    240    });
    241    it("should set HOMEPAGE_OVERRIDE_PREF on `moments-wnp` action", async () => {
    242      const [msg] = await handleMessageRequestStub();
    243      sandbox.useFakeTimers();
    244      instance.executeAction(msg);
    245 
    246      assert.calledOnce(setStringPrefStub);
    247      assert.calledWithExactly(
    248        setStringPrefStub,
    249        HOMEPAGE_OVERRIDE_PREF,
    250        JSON.stringify({
    251          message_id: msg.id,
    252          url: msg.content.action.data.url,
    253          expire: instance.getExpirationDate(
    254            msg.content.action.data.expireDelta
    255          ),
    256        })
    257      );
    258    });
    259    it("should block after taking the action", async () => {
    260      const [msg] = await handleMessageRequestStub();
    261      instance.executeAction(msg);
    262 
    263      assert.calledOnce(blockMessageByIdStub);
    264      assert.calledWithExactly(blockMessageByIdStub, msg.id);
    265    });
    266    it("should compute expire based on expireDelta", async () => {
    267      sandbox.spy(instance, "getExpirationDate");
    268 
    269      const [msg] = await handleMessageRequestStub();
    270      instance.executeAction(msg);
    271 
    272      assert.calledOnce(instance.getExpirationDate);
    273      assert.calledWithExactly(
    274        instance.getExpirationDate,
    275        msg.content.action.data.expireDelta
    276      );
    277    });
    278    it("should compute expire based on expireDelta", async () => {
    279      sandbox.spy(instance, "getExpirationDate");
    280 
    281      const [msg] = await handleMessageRequestStub();
    282      const msgWithExpire = {
    283        ...msg,
    284        content: {
    285          ...msg.content,
    286          action: {
    287            ...msg.content.action,
    288            data: { ...msg.content.action.data, expire: 41 },
    289          },
    290        },
    291      };
    292      instance.executeAction(msgWithExpire);
    293 
    294      assert.notCalled(instance.getExpirationDate);
    295      assert.calledOnce(setStringPrefStub);
    296      assert.calledWithExactly(
    297        setStringPrefStub,
    298        HOMEPAGE_OVERRIDE_PREF,
    299        JSON.stringify({
    300          message_id: msg.id,
    301          url: msg.content.action.data.url,
    302          expire: 41,
    303        })
    304      );
    305    });
    306    it("should send user telemetry", async () => {
    307      const [msg] = await handleMessageRequestStub();
    308      const sendUserEventTelemetrySpy = sandbox.spy(
    309        instance,
    310        "sendUserEventTelemetry"
    311      );
    312      instance.executeAction(msg);
    313 
    314      assert.calledOnce(sendTelemetryStub);
    315      assert.calledWithExactly(sendUserEventTelemetrySpy, msg);
    316      assert.calledWithExactly(sendTelemetryStub, {
    317        type: "MOMENTS_PAGE_TELEMETRY",
    318        data: {
    319          action: "moments_user_event",
    320          bucket_id: "WNP_THANK_YOU",
    321          event: "MOMENTS_PAGE_SET",
    322          message_id: "WNP_THANK_YOU",
    323        },
    324      });
    325    });
    326  });
    327  describe("#checkHomepageOverridePref", () => {
    328    let messageRequestStub;
    329    beforeEach(() => {
    330      messageRequestStub = sandbox.stub(instance, "messageRequest");
    331    });
    332    it("should catch parse errors", () => {
    333      getStringPrefStub.returns({});
    334 
    335      instance.checkHomepageOverridePref();
    336 
    337      assert.calledOnce(messageRequestStub);
    338      assert.calledWithExactly(messageRequestStub, {
    339        template: "update_action",
    340        triggerId: "momentsUpdate",
    341      });
    342    });
    343  });
    344 });