tor-browser

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

init-store.test.js (5438B)


      1 import { actionCreators as ac, actionTypes as at } from "common/Actions.mjs";
      2 import { addNumberReducer, GlobalOverrider } from "test/unit/utils";
      3 import {
      4  INCOMING_MESSAGE_NAME,
      5  initStore,
      6  MERGE_STORE_ACTION,
      7  OUTGOING_MESSAGE_NAME,
      8  rehydrationMiddleware,
      9 } from "content-src/lib/init-store";
     10 
     11 describe("initStore", () => {
     12  let globals;
     13  let store;
     14  beforeEach(() => {
     15    globals = new GlobalOverrider();
     16    globals.set("RPMSendAsyncMessage", globals.sandbox.spy());
     17    globals.set("RPMAddMessageListener", globals.sandbox.spy());
     18    store = initStore({ number: addNumberReducer });
     19  });
     20  afterEach(() => globals.restore());
     21  it("should create a store with the provided reducers", () => {
     22    assert.ok(store);
     23    assert.property(store.getState(), "number");
     24  });
     25  it("should add a listener that dispatches actions", () => {
     26    assert.calledWith(global.RPMAddMessageListener, INCOMING_MESSAGE_NAME);
     27    const [, listener] = global.RPMAddMessageListener.firstCall.args;
     28    globals.sandbox.spy(store, "dispatch");
     29    const message = { name: INCOMING_MESSAGE_NAME, data: { type: "FOO" } };
     30 
     31    listener(message);
     32 
     33    assert.calledWith(store.dispatch, message.data);
     34  });
     35  it("should not throw if RPMAddMessageListener is not defined", () => {
     36    // Note: this is being set/restored by GlobalOverrider
     37    delete global.RPMAddMessageListener;
     38 
     39    assert.doesNotThrow(() => initStore({ number: addNumberReducer }));
     40  });
     41  it("should log errors from failed messages", () => {
     42    const [, callback] = global.RPMAddMessageListener.firstCall.args;
     43    globals.sandbox.stub(global.console, "error");
     44    globals.sandbox.stub(store, "dispatch").throws(Error("failed"));
     45 
     46    const message = {
     47      name: INCOMING_MESSAGE_NAME,
     48      data: { type: MERGE_STORE_ACTION },
     49    };
     50    callback(message);
     51 
     52    assert.calledOnce(global.console.error);
     53  });
     54  it("should replace the state if a MERGE_STORE_ACTION is dispatched", () => {
     55    store.dispatch({ type: MERGE_STORE_ACTION, data: { number: 42 } });
     56    assert.deepEqual(store.getState(), { number: 42 });
     57  });
     58  it("should call .send and update the local store if an AlsoToMain action is dispatched", () => {
     59    const subscriber = sinon.spy();
     60    const action = ac.AlsoToMain({ type: "FOO" });
     61 
     62    store.subscribe(subscriber);
     63    store.dispatch(action);
     64 
     65    assert.calledWith(
     66      global.RPMSendAsyncMessage,
     67      OUTGOING_MESSAGE_NAME,
     68      action
     69    );
     70    assert.calledOnce(subscriber);
     71  });
     72  it("should call .send but not update the local store if an OnlyToMain action is dispatched", () => {
     73    const subscriber = sinon.spy();
     74    const action = ac.OnlyToMain({ type: "FOO" });
     75 
     76    store.subscribe(subscriber);
     77    store.dispatch(action);
     78 
     79    assert.calledWith(
     80      global.RPMSendAsyncMessage,
     81      OUTGOING_MESSAGE_NAME,
     82      action
     83    );
     84    assert.notCalled(subscriber);
     85  });
     86  it("should not send out other types of actions", () => {
     87    store.dispatch({ type: "FOO" });
     88    assert.notCalled(global.RPMSendAsyncMessage);
     89  });
     90  describe("rehydrationMiddleware", () => {
     91    it("should allow NEW_TAB_STATE_REQUEST to go through", () => {
     92      const action = ac.AlsoToMain({ type: at.NEW_TAB_STATE_REQUEST });
     93      const next = sinon.spy();
     94      rehydrationMiddleware(store)(next)(action);
     95      assert.calledWith(next, action);
     96    });
     97    it("should dispatch an additional NEW_TAB_STATE_REQUEST if INIT was received after a request", () => {
     98      const requestAction = ac.AlsoToMain({ type: at.NEW_TAB_STATE_REQUEST });
     99      const next = sinon.spy();
    100      const dispatch = rehydrationMiddleware(store)(next);
    101 
    102      dispatch(requestAction);
    103      next.resetHistory();
    104      dispatch({ type: at.INIT });
    105 
    106      assert.calledWith(next, requestAction);
    107    });
    108    it("should allow MERGE_STORE_ACTION to go through", () => {
    109      const action = { type: MERGE_STORE_ACTION };
    110      const next = sinon.spy();
    111      rehydrationMiddleware(store)(next)(action);
    112      assert.calledWith(next, action);
    113    });
    114    it("should not allow actions from main to go through before MERGE_STORE_ACTION was received", () => {
    115      const next = sinon.spy();
    116      const dispatch = rehydrationMiddleware(store)(next);
    117 
    118      dispatch(ac.BroadcastToContent({ type: "FOO" }));
    119      dispatch(ac.AlsoToOneContent({ type: "FOO" }, 123));
    120 
    121      assert.notCalled(next);
    122    });
    123    it("should allow all local actions to go through", () => {
    124      const action = { type: "FOO" };
    125      const next = sinon.spy();
    126      rehydrationMiddleware(store)(next)(action);
    127      assert.calledWith(next, action);
    128    });
    129    it("should allow actions from main to go through after MERGE_STORE_ACTION has been received", () => {
    130      const next = sinon.spy();
    131      const dispatch = rehydrationMiddleware(store)(next);
    132 
    133      dispatch({ type: MERGE_STORE_ACTION });
    134      next.resetHistory();
    135 
    136      const action = ac.AlsoToOneContent({ type: "FOO" }, 123);
    137      dispatch(action);
    138      assert.calledWith(next, action);
    139    });
    140    it("should not let startup actions go through for the preloaded about:home document", () => {
    141      globals.set("__FROM_STARTUP_CACHE__", true);
    142      const next = sinon.spy();
    143      const dispatch = rehydrationMiddleware(store)(next);
    144      const action = ac.BroadcastToContent(
    145        { type: "FOO", meta: { isStartup: true } },
    146        123
    147      );
    148      dispatch(action);
    149      assert.notCalled(next);
    150    });
    151  });
    152 });