Running unit test on JS runtime for es6 import statement - javascript

I am very new to NodeJS and JS as well. I have tried reading about resources like babel and rewire which help in resolving dependencies of ES6. But ended up being confused.
I am trying to write unit test for a JS runtime file which has import statements. The issue is that this file is a runtime file. This means that the import module only works when running in browser inside the dependent platform/application.
runtimeFile.js
import {something} from 'other-module'
function foo(){
}
export const toBeTested = {
foo
}
unitTest.js
const toBeTested = require('./runtimeFile').toBeTested
describe('some test', () => {
it('test a func', () => {
const result = tobeTested.foo();
});
});
When i try to run this unit test using mocha getting:
SyntaxError: Unexpected token {
This is for the import statement in runtimeFile.js.
So my question is around:
How do i mock the import statement
I am not sure what configuartion i need to do in order to get rid of this syntax error
I can provide more information as needed

Related

"Cannot use import statement outside a module" and "Uncaught SyntaxError: Unexpected identifier" when importing a class

I'm trying to import a class into my "main.js" file. Here is a minified version of my code.
// "extended.js" file
export default class Test {
constructor() {
this.prop = "Hello"
console.log(this.prop)
}
}
// "main.js" file
window.onload = function () {
import Test from "./extended"
new Test()
}
The main.js file is the frontend JavaScript file for my Express app so it is stored in the "public" folder. When you load the page the initial error that occurs is "Cannot use import statement outside a module". I've done research on my own and multiple people have stated that using
type="module" for the "main.js" script tag will resolve the issue. When I did that the error changed to "Uncaught SyntaxError: Unexpected identifier". Is there something I'm missing about how to use ES6 modules and classes?
You can only import at the top level - it can't be done inside a block. Change
window.onload = function () {
import Test from "./extended"
new Test()
}
to
import Test from "./extended"
window.onload = function () {
new Test()
};
Though, I'd also suggest not creating classes for side-effects - if you just want the class to do something, but not to return something useful, use a plain function instead.
export default () => {
const prop = "Hello";
console.log(prop);
};

running mocha in Browser using typescript

I cloud manage to run run Mocha tests in the browser using ES6 / Typescript like that:
// test.ts
import 'mocha/mocha.css';
import * as M from 'mocha/mocha-es2018';
import { expect } from 'chai';
M.setup('bdd');
// import more tests here
describe('test', () => {
it('test1', () => {
expect(true).to.true;
})
});
M.run();
The build works (using vite), the html page shows up - all nice and fine. BUT of course I would love to add more tests like that:
import './unit/sum.spec.ts';
// more imports
Doing so - will cause this error:
Uncaught ReferenceError: describe is not defined
Is there any way to make all those »mocha functions« globally available using es6?
I could finally make it like that:
import 'mocha/mocha.css';
import * as M from 'mocha/mocha-es2018';
(async () => {
M.setup('bdd');
await Promise.all([
import('./unit/add.spec'),
import('./unit/sub.spec'),
import('./component/calc.spec')
]);
M.run();
})();

How to create Jest Custom Environment with Typescript?

I am trying to create an extension off jest-node-environment as a CustomTestEnvironment but am getting the following error when trying to run jest
● Test suite failed to run
~/git/my-application/tests/environment/custom-test-environment.ts:1
import NodeEnvironment from 'jest-environment-node';
^^^^^^
SyntaxError: Cannot use import statement outside a module
at runTestInternal (../node_modules/jest-runner/build/runTest.js:226:5)
I believe this error means it doesn't recognize this as a typescript file, and hasn't transpiled it. ( I am using the latest version of jest 26.0.1)
Based on discussions in the jest github, the PR to make this work was slated for Jest 26 but was pulled from Jest 26 and is (hopefully) going to be in Jest 27.
https://github.com/facebook/jest/pull/8751
That being said, I've seen samples of people doing it this way online but for me I'm not having any luck following their lead.
import NodeEnvironment from "jest-environment-node";
import {LocalObject} from "./object/local-object.helper";
export class CustomTestEnvironment extends NodeEnvironment {
public async setup(): Promise<void> {
await super.setup();
this.global.localObject = LocalObject.init()
}
public async teardown(): Promise<void> {
LocalObject.teardown(this.global.localObject)
await super.teardown();
}
}
The LocalObject is just a thin wrapper around a test utility which has a complex startup and teardown and which I want to provide to tests to be able to publish test data and kick off the component test.
If I change the imports to be require -
const NodeEnvironment = require("jest-environment-node");
const {LocalObject} = require("./object/local-object.helper");
Then I get this error instead -
SyntaxError: Unexpected token 'export'
If I move the export into an module.exports then I get the following error
public async setup(): Promise<void> {
^^^^^
SyntaxError: Unexpected token 'async'
Seems to me like it's not treating this file as typescript.
Is there any workaround to be able to use this as a typescript file? The LocalObject is written in typescript, so I believe that I need this to be typescript to work right with that one, and it is important that LocalObject file remains typescript for test files to use it correctly.
Alternative question:
Can I do the same kind of setup/teardown logic in the setupFilesAfterEnv I only saw that they were run before, but not after tests. Thanks.
UPDATE: Jest 27 is now released and now supports this. If you are using an older version of Jest, update to Jest 27 to be able to use CustomEnvironment as typescript https://jestjs.io/blog/2021/05/25/jest-27#features-coming-with-breaking-changes
Modules used in the following configuration options are now transformed like the rest of your code, which may be breaking if you relied on them being loaded as-is:
testEnvironment
runner
testRunner
snapshotResolver
Alternatively, you can continue to use the workaround below for prior versions.
This isn't supported in Jest 26 and is slated to be in Jest 27 https://github.com/facebook/jest/pull/8751#issuecomment-699049851
For now the solution is to use setupFilesAfterEnv files in my jest.config https://jestjs.io/docs/en/configuration#setupfilesafterenv-array.
From there I could put all my my setup/teardown in beforeAll() and afterAll() blocks. This was effectively equivalent to using the Node Environment and the setupFilesAfterEnv files is compatible with typescript.
//jest.setup.ts
import {LocalObject} from "./object/local-object.helper";
let localObject: LocalObject;
beforeAll(async () => {
//Start my environment or seed data to DB or whatever
localObject = await LocalObject.init()
}
afterAll(async () => {
//teardown or clean things started in setup my environment
await localObject.teardown()
}

Jest virtual mock: how do I troubleshoot this failure?

So I have this import statement in a module that I'm trying to test using jest 25.1 running on node 11.1.0. The import statement is for a module that is only available when running on the jvm's nashorn runtime, so I'm using jest's virtual mock to control the behavior in the unit tests. Here's what the import statement looks like in the module under test:
import RequestBuilder from 'nashorn-js/request_builder'
...and after the other lines in the import block, this:
const request = RequestBuilder.create('some-string')
.sendTo('some-other-string')
.withAction('yet-another-string')
.getResultWith( consumer => consumer.result( consumer.message().body() ) )
export const functionA = () => {...} // uses 'request' variable
export const functionB = () => {...} // uses 'request' variable
In the corresponding .spec file, I have this virtual mock setup:
const mockGetResultWith = {
getResultWith: jest.fn()
}
const mockWithAction = {
withAction: jest.fn().mockImplementation(() => mockGetResultWith)
}
const mockSendTo = {
sendTo: jest.fn().mockImplementation(() => mockWithAction)
}
const mockBuilder = {
create: jest.fn().mockImplementation(() => mockSendTo)
}
jest.mock(
'nashorn-js/request_builder',
() => mockBuilder,
{ virtual: true }
)
require('nashorn-js/request_builder')
import { functionA, functionB } from './module-under-test'
I have been trying unsuccessfully to get past this failure from jest:
● Test suite failed to run
TypeError: Cannot read property 'create' of undefined
35 | }
36 |
> 37 | const verify = RequestBuilder.create('some-string')
| ^
38 | .sendTo('some-other-string')
39 | .withAction('yet-another-string')
40 | .getResultWith( consumer => consumer.result( consumer.message().body() ) )
I've tried all kinds of different mock structures, using require vs import, etc, but haven't found the magic bullet.
As near as I can tell, it does not appear that the RequestBuilder import statement is even invoking the virtual mock. Or at least, if I add console.log() statements to the virtual mock factory function, I never see those log messages in the output.
Anybody have any idea what I'm missing or what else to try? I have pretty much the same pattern in use in other parts of the code, where this setup works, but for some mystical reason with this module, I can't get the virtual mock working. Any help is greatly appreciated.
So, the problem here turned out to be jest's implementation of import vs require in the .spec file.
By changing this line:
import { functionA, functionB } from './module-under-test'
To this:
const module = require('./module-under-test')
const functionA = module.functionA
const functionB = module.functionB
The module under test now loads successfully, and the tests run as expected.
I have no explanation for this, and haven't been able to find jest documentation describing why I'd get any different behavior between require vs import. In fact, I have the mock configuration setup before any import statements as described here:
https://github.com/kentcdodds/how-jest-mocking-works
If anybody out there understands what's going on with this import behavior, or has a link describing what I'm missing, I'd sure appreciate the info.

.then() on node modules not working?

I'm working on a react native app right now and I wrote my own module to store some information separated from my components.
While I did that I noticed that in my module I created, I had a function like this:
testFetch = function(url){
return fetch(url);
}
exports.test = testFetch;
In my main module I did the following:
import ts from 'test-module'
ts.test("http://example.com").then((response) => {console.log(response)});
But .then() is not getting fired for any reason. The request went through and was successful.
Any help on this?
The problem is the way you mix CommonJS and ES6 modules. You seem to expect that ts in your example main module gives you the value of the whole export in your module dependency, but that's not how it works.
You can use export default to export your testFetch function, like so:
testFetch = function (url) {
return fetch(url);
}
export default testFetch;
Main module:
import testFetch from 'test-module';
testFetch("http://example.com").then((response) => {console.log(response)});

Categories