title: Testing Cross Browser Extension With Jest, How To Mock Chrome Storage API?
body: <p>After putting off testing for a while now due to Cypress not allowing visiting <code>chrome://</code> urls, I decided to finally understand how to unit/integration test my extension - TabMerger. This comes after the many times that I had to manually test the ever growing functionality and in some cases forgot to check a thing or two. Having automated testing will certainly speed up the process and help me be more at peace when adding new functionality.</p>
  <p>To do this, I chose Jest since my extension was made with React (CRA). I also used React Testing Library (<code>@testing-library/react</code>) to render all React components for testing.</p>
  <p>As I recently made TabMerger open source, the full testing script can be found <a href="https://github.com/lbragile/TabMerger/blob/master/__tests__/Tab.spec.js" rel="nofollow noreferrer">here</a></p>
  <p>Here is the test case that I want to focus on for this question:</p>
  <pre><code>import React from &quot;react&quot;;
  import { render, fireEvent } from &quot;@testing-library/react&quot;;
  
  import * as TabFunc from &quot;../src/Tab/Tab_functions&quot;;
  
  import Tab from &quot;../src/Tab/Tab&quot;;
  
  var init_groups = {
    &quot;group-0&quot;: {
      color: &quot;#d6ffe0&quot;,
      created: &quot;11/12/2020 @ 22:13:24&quot;,
      tabs: [
        {
          title:
            &quot;Stack Overflow - Where Developers Learn, Share, &amp; Build Careersaaaaaaaaaaaaaaaaaaaaaa&quot;,
          url: &quot;https://stackoverflow.com/&quot;,
        },
        {
          title: &quot;lichess.org • Free Online Chess&quot;,
          url: &quot;https://lichess.org/&quot;,
        },
        {
          title: &quot;Chess.com - Play Chess Online - Free Games&quot;,
          url: &quot;https://www.chess.com/&quot;,
        },
      ],
      title: &quot;Chess&quot;,
    },
    &quot;group-1&quot;: {
      color: &quot;#c7eeff&quot;,
      created: &quot;11/12/2020 @ 22:15:11&quot;,
      tabs: [
        {
          title: &quot;Twitch&quot;,
          url: &quot;https://www.twitch.tv/&quot;,
        },
        {
          title: &quot;reddit: the front page of the internet&quot;,
          url: &quot;https://www.reddit.com/&quot;,
        },
      ],
      title: &quot;Social&quot;,
    },
  };
  
  describe(&quot;removeTab&quot;, () =&gt; {
    it(&quot;correctly adjusts groups and counts when a tab is removed&quot;, () =&gt; {
      var tabs = init_groups[&quot;group-0&quot;].tabs;
      const { container } = render(&lt;Tab init_tabs={tabs} /&gt;);
      expect(container.getElementsByClassName(&quot;draggable&quot;).length).toEqual(3);
  
      var removeTabSpy = jest.spyOn(TabFunc, &quot;removeTab&quot;);
  
      fireEvent.click(container.querySelector(&quot;.close-tab&quot;));
      expect(removeTabSpy).toHaveBeenCalledTimes(1);
      expect(container.getElementsByClassName(&quot;draggable&quot;).length).toEqual(2); // fails (does not remove the tab for some reason)
    });
  });
  </code></pre>
  <p>I mocked the Chrome API according to my needs, but feel that something is missing. To mock the Chrome API I followed this post (along with many others, even for other test runners like Jasmine): <a href="https://stackoverflow.com/q/48894790/4298115">testing chrome.storage.local.set with jest</a>.</p>
  <p>Even though the Chrome storage API is mocked, I think the issue lies in this function which gets called upon initial render. That is, I think the <code>chrome.storage.local.get</code> is not actually being executed, but am not sure why.</p>
  <pre><code>// ./src/Tab/Tab_functions.js
  /**
   * Sets the initial tabs based on Chrome's local storage upon initial render.
   * If Chrome's local storage is empty, this is set to an empty array.
   * @param {function} setTabs For re-rendering the group's tabs
   * @param {string} id Used to get the correct group tabs
   */
  export function setInitTabs(setTabs, id) {
    chrome.storage.local.get(&quot;groups&quot;, (local) =&gt; {
      var groups = local.groups;
      setTabs((groups &amp;&amp; groups[id] &amp;&amp; groups[id].tabs) || []);
    });
  }
  </code></pre>
  <p>The reason I think the mocked Chrome storage API is not working properly is because when I manually set it in my tests, the number of tabs does not increase from 0. Which forced me to pass a prop (<code>props.init_tabs</code>) to my <code>Tab</code> component for testing purposes (<a href="https://github.com/lbragile/TabMerger/blob/f78a2694786d11e8270454521f92e679d182b577/src/Tab/Tab.js#L33-L35" rel="nofollow noreferrer">https://github.com/lbragile/TabMerger/blob/f78a2694786d11e8270454521f92e679d182b577/src/Tab/Tab.js#L33-L35</a>) - something I want to avoid if possible via setting local storage.</p>
  <p>Can someone point me in the right direction? I would like to avoid using libraries like <code>jest-chrome</code> since they abstract too much and make it harder for me to understand what is going on in my tests.</p>