Exported function is undefined - javascript

I'm facing an issue with my Mocha tests in Typescript, I fear it is related to Babel, but I am really not sure what's happening.
Essentially, I have a function that is being exported in a file
// src/my-ts-lib/tests/components/factoryMocks/componentConfigMocks.ts
...
export function getRandomConfig(type?: string): ComponentConfig {
const randomComponentType = type || randomType();
return {
type: randomComponentType,
config: configLibrary[randomComponentType]
}
}
And being imported in another, which is being called by a test:
// src/my-ts-lib/tests/components/RouteComponent/mocks/routeMocks.ts
...
import { getRandomConfig } from '../../factoryMocks/componentConfigMocks';
..
export const getSingleRouteConfigMock = (componentType?: string): RouteProps => {
const defaultComponentType = 'PageLayout';
return {
childComponent: {
type: componentType || defaultComponentType,
config: getRandomConfig(componentType || defaultComponentType)
},
pageName: randomString(),
path: randomString(),
};
};
...
When running the tests, I get the following error:
RouteCompnent/mocks/routeMocks.ts:10
config: getRandomConfig(componentType || defaultComponentType),
^
TypeError: componentConfigMocks_1.getRandomConfig is not a function
at Object.exports.getSingleRouteConfigMock (/Users/.../routeMocks.ts:10:44)
If I comment the call and console.log(getRandomConfig) I can see that it is undefined. I do not know why this is happening. What's even weirder is that, in subsequent tests that call getSingleRouteConfigMock, this same console.log correctly outputs the function, meaning that it has been exported then.
I've fiddled around with Babel, Mocha, and Typescript configs but have had no success.
Here's the Babel config:
.babelrc
{
"presets": ["#babel/preset-env", "#babel/preset-react"]
}
The Mocha config:
mocha.opts
--require ts-node/register
--watch-extensions ts tsx
--require source-map-support/register
--recursive
--require #babel/register
--require #babel/polyfill
src/**/*.spec.**
And the Typescript config:
tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/",
"sourceMap": true,
"noImplicitAny": false,
"module": "commonjs",
"target": "es6",
"jsx": "react"
},
"include": [
"./src/**/*"
],
"exclude": [
"./src/**/*.spec.ts",
"./src/my-ts-lib/components/**/*.spec.tsx",
"./src/my-ts-lib/test-helpers/*"
],
}
And the relevant sections of the package.json
...
"dependencies": {
...
"#babel/polyfill": "7.2.x",
...
},
"devDependencies": {
"#babel/core": "7.2.x",
"#babel/preset-env": "7.2.x",
"#babel/preset-react": "7.0.x",
"#babel/register": "7.0.x",
"babel-loader": "8.x",
"mocha": "3.2.x",
...
}

I found out I have a circular dependency. That's why this was not working.

Another possible cause for this symptom is that the function is actually missing in the module when you use a bundler like Parcel in production mode and it removes unused items (that particular issue discussed at Empty Javascript File When Building With Parcel JS 2 ). Check the compiled module file and make sure that the name exists.

Related

Jest (typescript): SyntaxError: Unexpected token 'export' (lowdb)

As the title suggests, I am having trouble with jest not allowing an export from the lowdb package within my tests. It seems to only throw this error for this single package -- the rest of my code is also using ES6 exports and my package.json file has the key type: module.
What have I tried
Adding type module -- Jest: SyntaxError: Unexpected token 'export'
Adding transformIgnorePatterns: ["<rootDir>/node_modules/(?!lowdb)"] -- Jest setup "SyntaxError: Unexpected token export"
Add a transform for js which uses babel-jest -- Jest encountered an unexpected token - SyntaxError: Unexpected token 'export'
Update my tsconfig to output commonJS modules
Update my tsconfig to allowJs:true and update my jest transforms to parse JS with ts-jest
I'm not sure where I am going wrong and am sorry in advance if I am being dim.
This is my TS Config
{
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"esModuleInterop": true,
"allowJs": true,
"target": "ES6",
"moduleResolution": "node",
"resolveJsonModule": true,
"module": "ES6",
"baseUrl": "src",
"declaration": true,
"allowSyntheticDefaultImports": true,
"paths": {
"#services/*": ["services/*"],
"#constants/*": ["constants/*"],
"#typeDefs/*": ["typeDefs/*"],
"#config/*": ["config/*"],
"#utils/*": ["utils/*"],
"#assets/*": ["assets/*"]
}
},
"include": ["src/**/*.ts"],
"exclude": ["rollup.config.ts", "jest.config.ts"]
}
And this is my jest.config.ts file:
import type { Config } from "#jest/types";
import { pathsToModuleNameMapper } from "ts-jest";
import { compilerOptions } from "./tsconfig.json";
// Sync object
const config: Config.InitialOptions = {
verbose: true,
roots: ["<rootDir>"],
preset: "ts-jest",
testEnvironment: "node",
transform: {
"^.+\\.ts$": "ts-jest",
"^.+\\.js$": "babel-jest",
},
testRegex: ["^.+\\.test\\.ts$"],
moduleDirectories: ["src", "node_modules"],
moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths),
};
export default config;
Finally, I am invoking jest as follows:
"test:ts": "jest --config=jest.config.ts",
Thank you in advance.
EDIT: Some additional context
I am pretty sure it should not affect this issue but I figure it could help to provide more context. I am running jest with two different configs -- one for JS and one for TS and the repo to which it relates has some build scripts written in JS that will only ever be run in nodeJS.
The JS config is as follows (has no issues):
// Sync object
/** #type {import('#jest/types').Config.InitialOptions} */
const config = {
verbose: true,
testEnvironment: "jest-environment-node",
transformIgnorePatterns: ["/node_modules/"],
transform: {},
testRegex: ["^.+\\.test\\.js$"],
};
export default config;
And it is invoked as follows:
"test:js": "yarn node --experimental-vm-modules $(yarn bin jest) --config=jest.config.js",
I too ran into similar issue. Here is how I fixed by tweaking jest.config.js:
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
transformIgnorePatterns: ["/node_modules/(?!lowdb|steno)"],
transform: {
"^.+\\.(js)?$": require.resolve("babel-jest"),
},
};
We want the babel-jest to parse the js files, and the lowDB and one of its dependency -steno.
Next, we need to ensure the babel.config.js to contain the following (we need to configure babel with #babel/plugin-transform-modules-commonjs plugin to correctly parse import/exports and should use importInterop:'node'.
module.exports = {
env: {
test: {
presets: [
[
"#babel/preset-env",
{
targets: {
node: "current",
},
modules: "commonjs",
},
],
],
plugins: [
[
"#babel/plugin-transform-modules-commonjs",
{
importInterop: "node",
},
],
],
},
},
};
Ensure you have installed all necessary dev dependencies:
npm i -D babel-jest #babel/core
npm i -D #babel/preset-env
npm i -D #babel/plugin-transform-modules-commonjs

[jest]: TypeError: Object prototype may only be an Object or null: undefined

While writing jest tests, I needed to use memfs as a mock for the Nodejs native file system module, so I used jest's manual mocks, but I'm getting this error:
> rimraf tests/{coverage,public} && jest
PASS tests/x.test.ts (19.926 s)
FAIL tests/mix.test.ts
● Test suite failed to run
TypeError: Object prototype may only be an Object or null: undefined
at Function.setPrototypeOf (<anonymous>)
at node_modules/graceful-fs/polyfills.js:139:39
at patch (node_modules/graceful-fs/polyfills.js:141:5)
at patch (node_modules/graceful-fs/graceful-fs.js:104:3)
at Object.<anonymous> (node_modules/graceful-fs/graceful-fs.js:96:18)
at Object.<anonymous> (node_modules/fs-extra/lib/fs/index.js:5:12)
PASS tests/options.test.ts (35.412 s)
Test Suites: 1 failed, 2 passed, 3 total
Tests: 6 passed, 6 total
Snapshots: 0 total
Time: 36.834 s
Ran all test suites.
Here are the files for a minimal reproduction of the error:
// src/index.ts
// this is just a minimal reproduction
import "laravel-mix";
import fs from "fs";
export default function getContent(path: string) {
return fs.readFileSync(path, "utf-8");
}
// tests/index.test.ts
import path from "path";
import fs from "fs";
import memfs from "memfs";
import getContent from "../src";
// Use memfs instead of native fs module.
jest.mock("fs");
jest.mock("fs/promises");
beforeAll(() => {
memfs.fs.mkdirSync(path.resolve(), {recursive: true});
});
// this is for demonstration only.
test("should mock fs", () => {
expect(fs).toBe(memfs.fs);
});
test("returns content from memfs", () => {
memfs.fs.writeFileSync("test.txt", "test text");
const result = getContent("test.txt");
expect(result).toBe("test text");
});
// more tests
// jest.config.js
module.exports = {
collectCoverageFrom: ["src/*.ts"],
coverageDirectory: "tests/coverage",
preset: "ts-jest",
testEnvironment: "node",
testMatch: ["<rootDir>/tests/**/*.test.ts"],
};
// tsconfig.json
{
"compilerOptions": {
"downlevelIteration": true,
"declaration": true,
"declarationDir": "dist",
"strict": true,
"esModuleInterop": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"sourceMap": true,
"noImplicitReturns": true,
"noImplicitOverride": true,
"importHelpers": true
}
}
// package.json
{
"name": "jest-mock-error-reproduction",
"version": "1.0.0",
"description": "",
"main": "src/index.ts",
"scripts": {
"test": "jest"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"#types/jest": "^27.0.1",
"#types/node": "^16.7.8",
"#types/serve-static": "^1.13.10",
"jest": "^27.1.0",
"laravel-mix": "^6.0.29",
"memfs": "^3.2.4",
"ts-jest": "^27.0.5",
"typescript": "~4.2.0"
}
}
And the manual mock files:
// __mocks__/fs.ts
import {fs} from "memfs";
export default fs;
// __mocks__/fs/promises.ts
import {fs} from "memfs";
export default fs.promises;
Note that when I remove :
jest.mock("fs");
jest.mock("fs/promises");
from tests/index.test.ts, tests fail as expected.
I tried debugging the source code that produces the error, and I couldn't find the problem.
I also tried to use import * as memfs from "memfs" syntax in the __mock__ files, because from other answers it seems like that solves the problem, but the error persists.
Any help would be appreciated.
I was able to narrow the source of the problem down to a dependency.
It seems like memfs is not compatible with graceful-fs which is a dependency of fs-extra which in turn is a dependency of a library I'm using in my own code (laravel-mix). So here's a very minimal reproduction of the error now:
// tests/index.test.ts
import fs from "fs";
import memfs from "memfs";
import "graceful-fs"; // comment this and the test should pass.
jest.mock("fs");
jest.mock("fs/promises");
test("should mock fs", () => {
expect(fs).toBe(memfs.fs);
});
To solve my issue, I changed the virtual file system library in use. I switched from memfs to mock-fs.
First, I installed mock-fs and #types/mock-fs:
npm i -D mock-fs #types/mock-fs
And then I used it in tests/index.test.ts:
// tests/index.test.ts
import path from "path";
import fs from "fs";
import mock_fs from "mock-fs";
import getContent from "../src";
beforeEach(() => {
mock_fs();
});
afterEach(() => {
mock_fs.restore();
});
test("returns content from mocked fs", () => {
fs.writeFileSync("test.txt", "test text");
const result = getContent("test.txt");
expect(result).toBe("test text");
});
// more tests

typescript throws configure not a function error with dotenv and jest

I am trying to use dotenv and jest together, and run into an error immediately.
A single test file, tests/authenticationt.test.ts with only
require('dotenv').configure();
is sufficient to cause this error (it throws this regardless of the presence of tests):
PS C:\Users\rj\code\BGA.ts> npm run test
Debugger attached.
> # test C:\Users\rj\code\BGA.ts
> jest
Debugger attached.
FAIL tests/authentication.test.ts
● Test suite failed to run
TypeError: require(...).configure is not a function
> 1 | require('dotenv').configure();
| ^
2 |
at Object.<anonymous> (tests/authentication.test.ts:1:31)
at TestScheduler.scheduleTests (node_modules/#jest/core/build/TestScheduler.js:333:13)
jest.config.js
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
modulePaths: [
"."
],
};
package.json
{
"scripts": {
"test": "jest"
},
"devDependencies": {
"#types/jest": "^26.0.23",
"dotenv": "^10.0.0",
"jest": "^27.0.4",
"ts-jest": "^27.0.3",
"typescript": "^4.3.2"
},
"dependencies": {
"#types/puppeteer": "^5.4.3"
}
}
tsconfig.json
{
"compilerOptions": {
"target": "es2017",
"module": "commonjs",
"strictNullChecks": true,
"outDir": "build",
"lib": [
"es2017",
"DOM"
],
},
"include": ["src/**/*"]
}
Additional details
I have not been able to find a stackoverflow question that matches this error; however, this is a basic setup question. This is on a Windows 10 machine, and I'm triggering the tests via powershell. dotenv works fine with my other typescript files (i.e. this is only a problem with jest).
try require('dotenv').config()

`import username from './username.txt'` with typescript+webpack gives `undefined`

I'm making a demo of importing .txt files as strings with typescript + webpack, almost done, but have this problem:
hello.ts
import username from './username.txt'
console.log(`Hello, ${username.trim()}!`)
Reports:
TypeError: Cannot read property 'trim' of undefined
My other files:
txt.d.ts
declare module '*.txt' {
const value: string
export default value;
}
webpack.config.js
module.exports = {
mode: 'development',
entry: './hello.ts',
devtool: 'inline-source-map',
output: {
path: __dirname,
filename: 'bundle.js'
},
resolve: {
extensions: ['.ts', '.js']
},
module: {
rules: [{
test: /\.ts?$/,
loader: 'ts-loader'
}, {
test: /\.txt$/,
loader: 'raw-loader'
}]
}
}
tsconfig.json
{
"compilerOptions": {
"strict": true,
"target": "es6",
"module": "commonjs",
"noImplicitAny": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"types": [
"node"
]
}
}
package.json
{
"scripts": {
"demo": "webpack && node bundle.js"
},
"devDependencies": {
"#types/jquery": "^3.3.9",
"#types/node": "^10.10.3",
"raw-loader": "^0.5.1",
"ts-loader": "^5.1.0",
"ts-node": "7.0.0",
"typescript": "^3.0.3",
"webpack": "^4.18.0",
"webpack-cli": "^3.1.0"
}
}
If I change the importing code in hello.ts as:
import * as username from './username.txt'
console.log(`Hello, ${username.trim()}!`)
It will have another type error:
console.log(`Hello, ${username.trim()}!`)
^^^^^^
TS2339: Property 'trim' does not exist on type 'typeof import("*.txt")'
Although I can find a way to make it work:
const username = require('./username.txt')
But I still want to know how to fix it with the import style.
The demo project for this: https://github.com/freewind-demos/typescript-import-txt-file-as-string-issue-demo, you can clone and run it
It looks like the raw-loader is generating a module that has the text string as a CommonJS-style export assignment, not as a default export. You should either update the type declaration and code to work with that:
declare module '*.txt' {
const value: string
export = value;
}
import username = require('./username.txt')
or enable the esModuleInterop compiler option in tsconfig.json to make default exports interoperable with export assignments. You can read a little more about the issue here.

Vue + Typescript + Webpack: Module build failing - Can't find vue.esm.js

I am trying to build a basic project using typescript, webpack and vue.js. However, when i run webpack from the command line, I am currently getting the following error:
ERROR in ./node_modules/vue/dist/vue.esm.js
Module build failed: Error: Could not find file: 'c:\Users\user\Games\javascriptTimeGame\node_modules\vue\dist\vue.esm.js'.
at getValidSourceFile (c:\Users\user\Games\javascriptTimeGame\node_modules\typescript\lib\typescript.js:89078:23)
at Object.getEmitOutput (c:\Users\user\Games\javascriptTimeGame\node_modules\typescript\lib\typescript.js:89448:30)
at getEmit (c:\Users\user\Games\javascriptTimeGame\node_modules\ts-loader\dist\index.js:122:43)
at successLoader (c:\Users\user\Games\javascriptTimeGame\node_modules\ts-loader\dist\index.js:42:11)
at Object.loader (c:\Users\user\Games\javascriptTimeGame\node_modules\ts-loader\dist\index.js:29:12)
This is strange, because the file it says it can't find is definitely there.
My main script: script.ts, looks like:
import Vue from 'vue'
function main(){
let vueApp = new Vue({
el: "#vue-test",
data : {
message: "Hello World"
}
});
}
main();
My tsconfig.json looks like:
{
"compileOnSave": true,
"compilerOptions": {
"target": "es6",
"module": "es6",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
"strict": true,
"removeComments": true,
"preserveConstEnums": true,
"sourceMap": true,
"outDir": "../jsDist",
"allowJs": true
}
}
My package.json dev dependencies look like:
"devDependencies": {
"#types/vue": "^2.0.0",
"http-server": "^0.10.0",
"node-sass": "^4.5.3",
"npm-run-all": "^4.0.2",
"ts-loader": "^2.3.3",
"typescript": "^2.4.2",
"vue": "^2.4.2",
"watch": "^1.0.2",
"webpack": "^3.5.5"
}
}
And my webpack.config.js looks like:
module.exports = {
devtool: 'inline-source-map',
entry: "./ts/script.ts",
output: {
filename: "bundle.js",
path: `${__dirname}/jsDist`
},
module: {
rules : [
{
loader: 'ts-loader'
}
]
},
resolve: {
extensions: ['.ts', '.js'],
alias: {
'vue$': 'vue/dist/vue.esm.js'
},
}
}
A few things I've noticed that might isolate the problem.
First, If I simply run tsc to directly build my source files with the typescript compiler, then it works perfectly without error. This suggests there is something wrong with the webpack part specifically.
Second, If I alter script.ts to just be:
import Vue from 'vue'
function main(){
console.log("Harmless");
}
main();
Then webpack builds it without fuss. This suggests that it is not the top-line import that webpack seems to have a problem with, but specifically the usage of Vue.
Finally if I alter my webpack.config.js such that ts-loader has the additional option transpileOnly: true then it also seems to build without fuss (but of course I don't want to do this, as then I lose my reason for using typescript in the first place!)
Any ideas what might be causing this error?
Try this:
import Vue from 'vue';
let vueApp = {};
if (document.querySelector('#authorities-container')) {
vueApp = new Vue({
el: "#vue-test",
data : {
message: "Hello World"
}
});
};
export {vueApp}

Categories

Resources