0

Is there a way to get this Jest test including document.fonts.ready to pass, e.g. by adding/removing any amount of setup/installing dependencies, without triggering or silencing any TypeScript errors/warnings?

describe('Input', () => {
  beforeAll(() => {
    // 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', () => {
    expect(renderInput()).toMatchSnapshot();
  });
});

Context: I had a production reason to use document.fonts.ready, for example as per this SO.

In flow (for legacy reasons, could rewrite in TypeScript if it helps) and React classes, this can be coded as something like:

export default class Input extends React.Component<any, any> {
  constructor(props: any) {
    super(props);

    (document: any).fonts.ready.then(() => this.setState({
      value: props.defaultValue || ''
    }));
  }
...

However when the test is brought into TypeScript contexts, for example by a component including (and not mocking) that Input component, it triggers TS2339: Property 'fonts' does not exist on type 'Document'. out of the box.

This is generally silenced by a // @ts-ignore TS2339, which is probably enough for most people. However, sometimes we're curious :D


Attempt 1: A yarn add -D @types/css-font-loading-module (thanks to all contributing here!) merely triggers a different later warning:

TS2739: Type '{ then: Mock<any, any>; }' is missing the following properties 
from type 'Promise<FontFaceSet>': catch, [Symbol.toStringTag], finally 

Unfortunately that seemed to lead down a rabbit hole of more errors...


Attempt 2: I was inspired by this post on mocking global.window with jest to try something that at least looked plausible (when combined with Attempt 1 above):

jest.spyOn(global.document.fonts, 'ready', 'get')

However that causes the test to fail to run Cannot spyOn on a primitive value; undefined given.

Exploring jsdom, e.g. here suggests document.fonts is not yet implemented. So I am stumped for now.

pzrq
  • 1,626
  • 1
  • 18
  • 24

1 Answers1

1

In one of your setupFiles add the following code:

Object.defineProperty(document, 'fonts', {
  value: { ready: Promise.resolve({}) },
})
thedude
  • 9,388
  • 1
  • 29
  • 30
  • Curiously this works fine on my laptop but fails eslint@^7 with error code 137 when using `registry.hub.docker.com/library/node:14` on AWS CodeBuild. Thanks for this though, in the absence of more info it's still a good answer. – pzrq Apr 04 '22 at 02:12
  • The error code 137 turned out to be a completely unrelated issue, literally that several parallel processes being run were consuming too much memory for the VM. – pzrq Jul 08 '22 at 04:21