I've got a typescript module that uses the browser's native Blob. I'd like to test it in node so I need that same module to see a global fake Blob implementation. A very simple fake blob implementation will do for me
class Blob {
parts?: any;
options?: any;
constructor(parts: any, options?: any) {
this.parts = parts;
this.options = options;
}
getType():string {
return options.type; // I know, hacky by just a demo
}
}
But how do I inject it into the global namespace of node so that my code that normally runs in the browser will see it when I run the tests in node?
In other words imagine I have a class that's expected to return a native browser Blob
export class BlobMaker {
static makeTextBlob(text):Blob {
return new Blob([text], {type: 'text/text'});
}
}
And now I want to test it in node (not in the browser) as in
import { BlobMaker } from '../src/blob-maker';
import * as expect from 'expect';
describe('BlobMaker', () => {
it('makes a text blob', () => {
const blob = BlobMaker.makeTextBlob('foo');
expect(blob.getType()).equalTo('text/text');
});
});
This fails to compile because Blob doesn't exist in node.
Apparently I can claim it exists by adding?
export interface Global extends NodeJS.Global {
Blob: Blob;
}
But I still need to inject my fake Blob class above so that the code I'm testing will use it. Is there a way to do that or am I supposed to solve this some other way?
It doesn't seem like I can abstract Blob into some kind of interface since I need the signature of makeTextBlob to be an actual native browser Blob and not some custom type.
I guess I get that I could pass a Blob factory deep into my library from the tests. Passing a Blob factory that deep in seems like overkill. I actually tried this by typescript complained. I think because it thinks the type being returned by BlobMaker.makeBlob is different
Here's that code
let makeBlob = function(...args):Blob {
return new Blob(...args);
};
export function setMakeBlob(fn):void {
makeBlob = fn;
};
export class BlobMaker {
static makeTextBlob(text):Blob {
return makeBlob([text], {type: 'text/text'});
}
}
Then in the tests
import { BlobMaker, setMakeBlob } from '../src/blob-maker';
import { Blob } from "./fake-blob';
import * as expect from 'expect';
describe('BlobMaker', () => {
it('makes a text blob', () => {
setMakeBlob(function(parts, options) {
return new Blob();
});
const blob = BlobMaker.makeTextBlob('foo');
expect(blob.getType()).equalTo('text/text'); // ERROR!
});
});
I get an error there is no getType method on Blob. I'm guessing TS thinks the Blob returned by BlobMaker.makeTextBlob is the native one. I tried casting it
const blob = BlobMaker.makeTextBlob('foo') as Blob;
But it didn't like that either.
Effectively this seems like it would all be solved if I could just inject my Blob class into the global namespace in node. So, how do I do that?
/// <referenceing the.d.tsfile from yourBlobMakermodule would be sufficient, have you tried that?Blob?Blobis aclass, not aninterface(which should mean it's in a normal.tsfile. YourBlobMakermodule shouldimport Blob from './blob'or wherever it's exported from. Even though it's understood to be a global variable, it's much easier to deal with it as part of a module when using TypeScript.Blob(updated the question). I just want to test it with a fake blob in tests running in node.Blobmodule. I found this answer which may or may not be useful to you, but what you can do is rewrite the file thatBlobis in to attach toglobalonly if nothing is already defined onglobal.Blob.