title: TypeScript - Is there a way in Jest to mock/spyOn the document.fonts API?
body: <p><strong>Is there a way to get this <a href="https://jestjs.io/" rel="nofollow noreferrer">Jest</a> test including <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/fonts" rel="nofollow noreferrer"><code>document.fonts.ready</code></a> to pass, e.g. by adding/removing any amount of setup/installing dependencies, without triggering or silencing any <a href="https://www.typescriptlang.org/" rel="nofollow noreferrer">TypeScript</a> errors/warnings?</strong></p>
  <pre><code>describe('Input', () =&gt; {
    beforeAll(() =&gt; {
      // Core part of the workaround...it seems the global prefix can also optionally be added
      global.document.fonts = {
        ready: {
          then: jest.fn()
        }
      };
    });
  
    test('should render an Input', () =&gt; {
      expect(renderInput()).toMatchSnapshot();
    });
  });
  </code></pre>
  <hr />
  <p><strong>Context</strong>: I had a production reason to use <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/fonts" rel="nofollow noreferrer"><code>document.fonts.ready</code></a>, for example as per <a href="https://stackoverflow.com/questions/5680013/how-to-be-notified-once-a-web-font-has-loaded">this SO</a>.</p>
  <p>In <a href="https://flow.org/" rel="nofollow noreferrer">flow</a> (for legacy reasons, could rewrite in TypeScript if it helps) and <a href="https://reactjs.org/" rel="nofollow noreferrer">React</a> classes, this can be coded as something like:</p>
  <pre><code>export default class Input extends React.Component&lt;any, any&gt; {
    constructor(props: any) {
      super(props);
  
      (document: any).fonts.ready.then(() =&gt; this.setState({
        value: props.defaultValue || ''
      }));
    }
  ...
  </code></pre>
  <p>However when the test is brought into TypeScript contexts, for example by a component including (and not mocking) that <code>Input</code> component, it triggers <code>TS2339: Property 'fonts' does not exist on type 'Document'.</code> out of the box.</p>
  <p>This is generally silenced by a <code>// @ts-ignore TS2339</code>, which is probably enough for most people. However, sometimes we're curious :D</p>
  <hr />
  <p><strong>Attempt 1</strong>: A <code>yarn add -D @types/css-font-loading-module</code> (thanks to <a href="https://github.com/Microsoft/TypeScript/issues/30984#issuecomment-831643164" rel="nofollow noreferrer">all contributing here</a>!) merely triggers a different later warning:</p>
  <pre><code>TS2739: Type '{ then: Mock&lt;any, any&gt;; }' is missing the following properties 
  from type 'Promise&lt;FontFaceSet&gt;': catch, [Symbol.toStringTag], finally 
  </code></pre>
  <p>Unfortunately that seemed to lead down a rabbit hole of more errors...</p>
  <hr />
  <p><strong>Attempt 2</strong>: I was inspired by <a href="https://www.grzegorowski.com/how-to-mock-global-window-with-jest" rel="nofollow noreferrer">this post on mocking <code>global.window</code> with jest</a> to try something that at least looked plausible (when combined with <em>Attempt 1</em> above):</p>
  <pre><code>jest.spyOn(global.document.fonts, 'ready', 'get')
  </code></pre>
  <p>However that causes the test to fail to run <code>Cannot spyOn on a primitive value; undefined given</code>.</p>
  <p>Exploring jsdom, e.g. <a href="https://github.com/jsdom/jsdom/blob/04f6c13f4a4d387c7fc979b8f62c6f68d8a0c639/test/web-platform-tests/to-run.yaml#L86" rel="nofollow noreferrer">here</a> suggests <code>document.fonts</code> is not yet implemented. So I am stumped for now.</p>