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.ts
after globalize()
and it works.
So I think the strategy to handle this would resolve around that.