Jest + TypeScript + @inject issue

Hey everyone. I’m stumped and can’t work this out.

I’ve created a repro of an issue I’m having: https://github.com/tomtomau/repro-localstorage-inject

  • jest testing of typescript components
  • @inject on the units
  • trying to inject something from lib.dom.d.ts

Refer to the components foo.ts and bar.ts as well as the corresponding jest tests.

Running yarn jest will run the tests and demonstrate:

  • foo.spec.ts will fail as Storage is not known
  • bar.spec.ts will be operate properly, including executing a call onto the fake Storage that was created

The only difference here is that foo has @inject .

Note that in tsconfig.json , dom is set in the lib entry - ergo the DOM library is being included. For some reason, with Jest + @inject, this has an issue.

I have no idea what’s going on here, thus casting out my net to see if anyone else can help explain/fix it.

1 Like

Without running your tests, I would guess it fails with error that reads like this:

Cannot inject with key null/undefined ...

If so, it’s the expected behavior as there is no localStorage as window, created from JSDOM. what you can do is to prepare it in your pretest:

import 'aurelia-polyfills';
import {Options} from 'aurelia-loader-nodejs';
import {globalize} from 'aurelia-pal-nodejs';
import * as path from 'path';
Options.relativeToDir = path.join(__dirname, 'unit');
globalize();
window.localStorage = window.Storage = class {};

That will give a fake storage constructor at window.localStorage. So later, in your real test, you can just do:

const myFakeStorageInstance = {};
container.registerInstance(window.localStorage, myFakeStorageInstance);

or you can construct the instance of Foo/Bar manually like existing tests.

Thanks for your reply Binh, but that’s actually not the exact issue (cannot inject etc.) but rather:

 FAIL  test/unit/foo.spec.ts
  ● Test suite failed to run

    ReferenceError: Storage is not defined



      at src/foo.ts:866:105
      at Object.<anonymous> (src/foo.ts:869:2)
      at Object.<anonymous> (test/unit/foo.spec.ts:1:1)

My intention with the code was to use Storage as the interface from lib.dom.d.ts bundled with Webpack, rather than the value of window.Storage. Without inject, this is fine, but once you add the @inject annotation, even if you don’t actually use DI to construct the object, it will fail.

To add further detail, if the jest environment is changed from node to jsdom it works, but proceeds to break some other templating stuff I’m not too sure about.

1 Like

I ran the tests, and i got the same error you described. Now I’m quite leaning on this is a TypeScript issue.

In tsconfig.json, emitDecoratorMetadata is true, making any class with decorators have their metadata in constructor params typings emitted. So:

@inject(window.localStorage)
export class Foo {
    constructor(private readonly storage: Storage) {
    }
}

will be something like

export class Foo {
    constructor(private readonly storage: Storage) {
    }
}
__metadata('design:parameter', Foo, [Storage]) // here is where we get the error
inject(window.localStorage)(Foo)

I tested my theory by adding the following:

(global as any).Storage = window.localStorage;

in jest-pretest.tsafter globalize()

and it works.

So I think the strategy to handle this would resolve around that.