title: Stencil, Leaflet, unit testing component, gives TypeError: Cannot read property 'deviceXDPI' of undefined
body: <p>So we are developing a Stenciljs component which wraps leaflet map and adds some additional functionality. </p>
  
  <p>Now obviously we don't want or need to test Leaflet, but instead, just the parts in our wrapper components.</p>
  
  <p>So, using the test examples, we create our tests, </p>
  
  <pre><code>import { LeMap } from "./le-map";
  
  describe("Map component tests", () =&gt; {
      it("Should build the map component", async () =&gt; {
          const map = new LeMap();
          expect(map).not.toBeNull();
      });     
  });
  </code></pre>
  
  <p>try and load the components and test the public functions, but we get </p>
  
  <pre><code>TypeError: Cannot read property 'deviceXDPI' of undefined
  
  &gt; 1 | import {Component, Element, Listen, Method, Prop, Watch} from 
  '@stencil/core';
  &gt; 2 | import L from 'leaflet';
      | ^
    3 |
    4 | @Component({
    5 |       shadow: false,
  </code></pre>
  
  <p>We believe this message is because the test is trying to render leaflet, and because it's not a true browser, it can't detect a view so throwing this error, so we've tried to mock leaflet in the tests, but still get the same problem.</p>
  
  <p>We're tried to mock the leaflet module by using jest mocking</p>
  
  <pre><code>jest.genMockFromModule('leaflet');
  </code></pre>
  
  <p>but this made no diffrence</p>
  
  <p>Only idea I've had is to separate the logic from the components, but that feels wrong, as we'd just be doing this for purpose of testing.</p>
  
  <p>Versions in use are: leaflet: 1.3.4, @stencil: 0.15.2, jest: 23.4.2</p>
  
  <p>Any other suggestions?</p>
  
  <p>Further investigation with, thanks to @skyboyer 's suggestions, leads me to this line of the leaflet core browser.js file</p>
  
  <p>leads me to this line of the leaflet core browser.js file</p>
  
  <pre><code>export var retina = (window.devicePixelRatio || (window.screen.deviceXDPI/window.screen.logicalXDPI)) &gt; 1;
  </code></pre>
  
  <p>But I'm unable to mock the screen property of window as I get the following error </p>
  
  <pre><code>[ts] Cannot assign to 'screen' because it is a constant or a read-only property, 
  </code></pre>
  
  <p>so I try the following.</p>
  
  <pre><code>const screen =  {
      deviceXDPI:0,
      logicalXDPI:0
  }
  
  Object.defineProperty(window, 'screen', screen);
  Object.defineProperty(window, 'devicePixelRatio', 0);
  </code></pre>
  
  <p>Same error, completely ignores this, so I try over riding the export.</p>
  
  <pre><code>jest.spyOn(L.Browser,'retina').mockImplementation(() =&gt; false);
  </code></pre>
  
  <p>No joy either, so tried </p>
  
  <pre><code>L.Browser.retina = jest.fn(() =&gt; false); 
  </code></pre>
  
  <p>but get it tells me it's a constant and can't be changed (yet the implication stats var so ¯_(ツ)_/¯ )</p>
  
  <p>Anything else I can try?</p>
  
  <p>Further update, 
  I've managed to mock the window, but this sadly doesn't solve it.</p>
  
  <pre><code>const screenMock = {            
      deviceXDPI: 0,
      logicalXDPI: 0          
  }
  
  const windowMock = {
      value: {
          'devicePixelRatio': 0,
          'screen': screenMock
      }
  }
  Object.defineProperty(global, 'window', windowMock);
  </code></pre>
  
  <p>If I console log this, I get the right properties but as soon as I test the instantiation of the component it fails with</p>
  
  <pre><code>  TypeError: Cannot read property 'deviceXDPI' of undefined
  </code></pre>
  
  <p>Reading around it seems Leaflet doesn't check for a DOM and just tries to render anyway, I can't see anyway around this, I saw a leaflet-headless package, but I don't know how I could swap them out just for testing.</p>
  
  <p>Think I will need to look at another strategy for testing, probably protractor.</p>