tor-browser

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

Weather.test.jsx (6983B)


      1 import React from "react";
      2 import { mount } from "enzyme";
      3 import { Provider } from "react-redux";
      4 import { INITIAL_STATE, reducers } from "common/Reducers.sys.mjs";
      5 import { combineReducers, createStore } from "redux";
      6 import { Weather } from "content-src/components/Weather/Weather";
      7 import { actionTypes as at } from "common/Actions.mjs";
      8 import { LinkMenu } from "content-src/components/LinkMenu/LinkMenu";
      9 
     10 const PREF_SYS_SHOW_WEATHER = "system.showWeather";
     11 const PREF_SYS_SHOW_WEATHER_OPT_IN = "system.showWeatherOptIn";
     12 const PREF_OPT_IN_DISPLAYED = "weather.optInDisplayed";
     13 const PREF_OPT_IN_ACCEPTED = "weather.optInAccepted";
     14 const PREF_STATIC_WEATHER_DATA = "weather.staticData.enabled";
     15 
     16 // keeps initialize = true and provides fake suggestion + location data
     17 // so the component skips <WeatherPlaceholder>.
     18 const weatherInit = {
     19  initialized: true,
     20  suggestions: [
     21    {
     22      forecast: { url: "https://example.com" },
     23      current_conditions: {
     24        temperature: { c: 22, f: 72 },
     25        icon_id: 3,
     26        summary: "Sunny",
     27      },
     28    },
     29  ],
     30  locationData: { city: "Testville" },
     31 };
     32 
     33 // base mockState for general Weather-rendering tests.
     34 // Opt-in is disabled here since it's only shown in specific locations
     35 const mockState = {
     36  ...INITIAL_STATE,
     37  Prefs: {
     38    ...INITIAL_STATE.Prefs,
     39    values: {
     40      ...INITIAL_STATE.Prefs.values,
     41      [PREF_SYS_SHOW_WEATHER]: true,
     42      [PREF_SYS_SHOW_WEATHER_OPT_IN]: false,
     43      "feeds.weatherfeed": true,
     44    },
     45  },
     46  Weather: { ...weatherInit },
     47 };
     48 
     49 // mock state for opt-in prompt tests.
     50 // Ensures the opt-in dialog appears by default.
     51 const optInMockState = {
     52  ...mockState,
     53  Prefs: {
     54    ...mockState.Prefs,
     55    values: {
     56      ...mockState.Prefs.values,
     57      showWeather: true,
     58      [PREF_SYS_SHOW_WEATHER_OPT_IN]: true,
     59      [PREF_OPT_IN_DISPLAYED]: true,
     60      [PREF_OPT_IN_ACCEPTED]: false,
     61      [PREF_STATIC_WEATHER_DATA]: true,
     62      "weather.locationSearchEnabled": true,
     63      "weather.display": "simple",
     64      "weather.temperatureUnits": "c",
     65    },
     66  },
     67 };
     68 
     69 function WrapWithProvider({ children, state = INITIAL_STATE }) {
     70  const store = createStore(combineReducers(reducers), state);
     71  return <Provider store={store}>{children}</Provider>;
     72 }
     73 
     74 describe("<Weather>", () => {
     75  let wrapper;
     76  let sandbox;
     77  let dispatch;
     78 
     79  beforeEach(() => {
     80    sandbox = sinon.createSandbox();
     81    dispatch = sandbox.stub();
     82  });
     83 
     84  afterEach(() => {
     85    sandbox.restore();
     86    wrapper?.unmount();
     87  });
     88 
     89  it("should render and show <Weather> if the `system.showWeather` pref is enabled", () => {
     90    wrapper = mount(
     91      <WrapWithProvider state={mockState}>
     92        <Weather dispatch={dispatch} />
     93      </WrapWithProvider>
     94    );
     95    assert.ok(wrapper.exists());
     96    assert.ok(wrapper.find(".weather").exists());
     97  });
     98 
     99  describe("Opt-in prompt actions", () => {
    100    it("should dispatch correct actions when user accepts weather opt-in", () => {
    101      const store = createStore(combineReducers(reducers), optInMockState);
    102      sinon.spy(store, "dispatch");
    103 
    104      wrapper = mount(
    105        <Provider store={store}>
    106          <Weather />
    107        </Provider>
    108      );
    109 
    110      const acceptBtn = wrapper.find("#accept-opt-in");
    111      acceptBtn.simulate("click", { preventDefault() {} });
    112 
    113      const dispatchedActions = store.dispatch
    114        .getCalls()
    115        .map(call => call.args[0]);
    116 
    117      assert.ok(
    118        dispatchedActions.some(
    119          action => action.type === at.WEATHER_USER_OPT_IN_LOCATION
    120        ),
    121        "Expected WEATHER_USER_OPT_IN_LOCATION to be dispatched"
    122      );
    123 
    124      assert.ok(
    125        dispatchedActions.some(
    126          action =>
    127            action.type === at.WEATHER_OPT_IN_PROMPT_SELECTION &&
    128            action.data === "accepted opt-in"
    129        ),
    130        "Expected WEATHER_OPT_IN_PROMPT_SELECTION with accepted opt-in"
    131      );
    132    });
    133 
    134    it("should dispatch correct actions when user rejects weather opt-in", () => {
    135      const store = createStore(combineReducers(reducers), optInMockState);
    136      sinon.spy(store, "dispatch");
    137 
    138      wrapper = mount(
    139        <Provider store={store}>
    140          <Weather />
    141        </Provider>
    142      );
    143 
    144      const acceptBtn = wrapper.find("#reject-opt-in");
    145      acceptBtn.simulate("click", { preventDefault() {} });
    146 
    147      const dispatchedActions = store.dispatch
    148        .getCalls()
    149        .map(call => call.args[0]);
    150 
    151      assert.ok(
    152        dispatchedActions.some(
    153          action =>
    154            action.type === at.WEATHER_OPT_IN_PROMPT_SELECTION &&
    155            action.data === "rejected opt-in"
    156        ),
    157        "Expected WEATHER_OPT_IN_PROMPT_SELECTION with rejected opt-in"
    158      );
    159    });
    160 
    161    it("should render a shorter context menu when system.showWeatherOptIn is enabled", () => {
    162      wrapper = mount(
    163        <WrapWithProvider state={optInMockState}>
    164          <Weather dispatch={dispatch} />
    165        </WrapWithProvider>
    166      );
    167 
    168      // find the inner _Weather component (the real class)
    169      const inner = wrapper.find("_Weather");
    170      assert.ok(inner.exists(), "Inner _Weather component should exist");
    171 
    172      // toggle context menu state on the real instance
    173      inner.instance().setState({ showContextMenu: true });
    174      wrapper.update();
    175 
    176      const menu = wrapper.find(LinkMenu);
    177      assert.ok(
    178        menu.exists(),
    179        "Expected LinkMenu to render when context menu opened"
    180      );
    181 
    182      const contextMenuOptions = menu.prop("options");
    183      assert.deepEqual(contextMenuOptions, [
    184        "ChangeWeatherLocation",
    185        "DetectLocation",
    186        "HideWeather",
    187        "OpenLearnMoreURL",
    188      ]);
    189    });
    190 
    191    it("should dispatch correct actions when 'Detect my location' option in context menu is clicked", () => {
    192      const store = createStore(combineReducers(reducers), optInMockState);
    193      sinon.spy(store, "dispatch");
    194 
    195      wrapper = mount(
    196        <Provider store={store}>
    197          <Weather />
    198        </Provider>
    199      );
    200 
    201      // find the inner _Weather component
    202      const inner = wrapper.find("_Weather");
    203      assert.ok(inner.exists(), "Inner _Weather component should exist");
    204 
    205      // toggle context menu state on the real instance
    206      inner.instance().setState({ showContextMenu: true });
    207      wrapper.update();
    208 
    209      const menu = wrapper.find(LinkMenu);
    210      assert.ok(
    211        menu.exists(),
    212        "Expected LinkMenu to render when context menu opened"
    213      );
    214 
    215      const detectLocationBtn = wrapper.find(
    216        '[data-l10n-id="newtab-weather-menu-detect-my-location"]'
    217      );
    218 
    219      assert.ok(detectLocationBtn.exists());
    220 
    221      detectLocationBtn.simulate("click", { preventDefault() {} });
    222 
    223      const dispatchedActions = store.dispatch
    224        .getCalls()
    225        .map(call => call.args[0]);
    226 
    227      assert.ok(
    228        dispatchedActions.some(
    229          action => action.type === at.WEATHER_USER_OPT_IN_LOCATION
    230        ),
    231        "Expected WEATHER_USER_OPT_IN_LOCATION to be dispatched"
    232      );
    233    });
    234  });
    235 });