Jest ReferenceError on globally defined JS constants within Angular components - javascript

Today I've started using Jest to UnitTest some of our TypeScript files within our Angular project. After the initial Jest setup was done, creating a test for a pure TypeScript method in our util.ts was pretty straight-forward. Now I'm working on testing something in an Angular component, and I'm encountering an error because of our global constants within an imported other Component. So I'm looking for a way to either:
Give a default to these global variable in my Jest UnitTest class itself by setting them in a beforeAll.
Have a (separated) custom-jest-constants.setup.ts file to predefine all global variables for all tests
I'll save you the details of the steps I did this morning to setup and configure Jest within Angular, but I ended up with the following relevant changes because of it:
Within the angularelements/ root folder:
Added jest.config.ts:
export default {
clearMocks: true,
globalSetup: 'jest-preset-angular/global-setup',
preset: 'jest-preset-angular',
setupFilesAfterEnv: ['<rootDir>/setup-jest.ts'],
};
Added setup-jest.ts:
import 'jest-preset-angular/setup-jest';
Added tsconfig.spec.json:
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"module": "CommonJs",
"types": ["jest"]
},
"include": ["src/**/*.spec.ts", "src/**/*.d.ts"]
}
Modified package.json:
{
...,
"scripts": {
...,
"test": "jest"
},
"devDependencies": {
...,
"#types/jest": "^28.1.3",
"jest": "^28.1.1",
"jest-preset-angular": "^12.1.0"
}
}
Within a new test folder:
Added util.test.ts and question.component.test.ts, what they do isn't too relevant.
The error I'm getting when I run npm test:
PASS test/app/util.test.ts
FAIL test/app/questionnaire/runtime/question/question.component.test.ts
● Test suite failed to run
ReferenceError: maxFileUploadSizeInKb is not defined
42 | ...
> 43 | private static readonly UPLOAD_LIMIT_IN_BYTES = +maxFileUploadSizeInKb * 1000;
| ^
44 | private static readonly IMAGE_UPLOAD_LIMIT_IN_BYTES = +maxImageFileUploadSizeInKb * 1000;
45 | ...
at Object.<anonymous> (src/app/document-panel/document-uploading/document-uploading.component.ts:43:52)
at Object.<anonymous> (src/app/questionnaire/runtime/questionnaire-document-panel/questionnaire-document-panel.component.ts:10:1)
at Object.<anonymous> (src/app/questionnaire/runtime/question/question.component.ts:16:1)
at Object.<anonymous> (test/app/questionnaire/runtime/question/question.component.test.ts:1:1)
As for the relevant code within the document-uploading.component.ts:
declare const maxFileUploadSizeInKb: string;
declare const maxImageFileUploadSizeInKb: string;
#Component({ ... })
export class DocumentUploadingComponent extends ... {
private static readonly UPLOAD_LIMIT_IN_BYTES = +maxFileUploadSizeInKb * 1000;
private static readonly IMAGE_UPLOAD_LIMIT_IN_BYTES = +maxImageFileUploadSizeInKb * 1000;
Those declared constants are global constants that we define in our javascriptPre.jspf:
...
<script ...>
var maxFileUploadSizeInKb = '<%=Util.parseInt(SettingManager.get(Setting.maximumFileSizeInKb), Setting.DEFAULT_MAX_FILE_SIZE_KB)%>';
var maxImageFileUploadSizeInKb = '<%=Util.parseInt(SettingManager.get(Setting.maximumImageFileSizeInKb), Setting.DEFAULT_MAX_IMAGE_FILE_SIZE_KB)%>';
</script>
...
Which will basically inject the settings we've defined in our Java backend to this globally defined JS variable, which is accessible within our Angular/Typescript classes as can be seen in the document-uploading.component.ts.
If I temporarily modify the DocumentUploadingComponent to hard-coded constants like this:
private static readonly UPLOAD_LIMIT_IN_BYTES = 5_000_000;
private static readonly IMAGE_UPLOAD_LIMIT_IN_BYTES = 400_000;
Everything works as intended when I run npm test.
So, back to the original question: how could I overwrite/setup the maxFileUploadSizeInKb/maxImageFileUploadSizeInKb to some default value, either within a custom Jest setup file or in a beforeAll within my UnitTest file (or elsewhere perhaps)?

Right after I posted my answer above I finally found the solution, which was simpler than I thought.
In jest.config.ts I've added:
setupFiles: ['<rootDir>/custom-jest-setup.ts'],
And in that added new custom-jest-setup.ts file within my angularelements/ folder I've used:
// #ts-ignore
global.maxFileUploadSizeInKb = 5000;
// #ts-ignore
global.maxImageFileUploadSizeInKb = 400;
And that's it. My UnitTests run without any issues now!

Related

TypeError: mxgraph is not a function When Running Jest Unit Tests

I have a Vue JS Component That uses the mxgraph package:
https://www.npmjs.com/package/mxgraph
and imports it like this:
import * as mxgraph from 'mxgraph';
const {
mxClient,
mxStackLayout,
mxGraph,
mxRubberband,
mxUtils,
mxFastOrganicLayout,
mxSwimlaneLayout,
mxEvent,
mxGraphModel,
mxConstants,
mxHierarchicalLayout,
mxSwimlaneManager
} = mxgraph();
// noinspection TypeScriptCheckImport
import {
mxConstants as MxConstants
} from 'mxgraph/javascript/mxClient.js'
import axios from 'axios';
This is my jest.config.js
module.exports = {
preset: "#vue/cli-plugin-unit-jest/presets/no-babel",
moduleFileExtensions: ["js", "ts", "json", "vue"],
transform: {
".*\\.(vue)$": "vue-jest",
"^.+\\.tsx?$": "ts-jest"
},
globals: {
"ts-jest": {
tsConfig: "src/tsconfig.json"
}
}
};
When I run my tests I get:
TypeError: mxgraph is not a function
20 | import * as mxgraph from 'mxgraph';
21 |
> 22 | const {
| ^
23 | mxClient,
24 | mxStackLayout,
25 | mxGraph,
at src/components/task/job/job_pipeline_mxgraph.vue:22:1
at Object.<anonymous> (src/components/task/job/job_pipeline_mxgraph.vue:568:3)
at src/components/task/job/task_template_wizard_creation/step_attach_directories_task_template.vue:67:1
at Object.<anonymous> (src/components/task/job/task_template_wizard_creation/step_attach_directories_task_template.vue:367:3)
at Object.<anonymous> (tests/unit/task/job/task_template_wizard_creation/step_attach_directories_task_template.spec.js:3:1)
The import works fine when I run my app using my normal webpack configurations files. Is there anything I need to add to my jest.config to fix this error?
The mxGraph npm package has always been a mess.
Notice that maxGraph, the mxGraph successor, will handle it: https://github.com/maxGraph/maxGraph
As you are using Vue and your snippet indicates you are using TypeScript, I suggest you to use https://github.com/typed-mxgraph/typed-mxgraph. You will get TypeScript types and a fully working solution for the mxGraph npm integration. The README provides comprehensive usage examples and demos are also available.
When using mxGraph in jest test, depending on the mxGraph parts you are running, you will need to use the jsdom environment in the related tests. This is because mxGraph uses the window object, that is not available with the node environment.
If you still want to keep the js way for the mxGraph integration, you can have a look at https://github.com/jgraph/mxgraph/issues/175#issuecomment-482008896 or at other stack overflow questions (they are plenty).

Error 'Missing: SyncTestZoneSpec' during karma test with angular 4

First thing first : my project is totally sane on branch /develop with test passing and all.
I created a branch to clean imports and use aliases instead of ../../../../ each time I have to access classes. I added it into tsconfig.json :
"baseUrl": "src",
"paths": {
"#app/*": [
"app/*"
],
"#core/*": [
"app/core/*"
],
"#common/*": [
"app/common/*"
],
"#models/*": [
"app/models/*"
],
"#env/*": [
"environments/*"
],
"#assets/*": [
"assets/*"
]
}
I just finished but when executing the test with simple npm run test which do something like thiis is think karma start ./karma.conf.js --log-level error I get this error :
HeadlessChrome 67.0.3396 (Windows 10.0.0) ERROR
Uncaught Error: Missing: SyncTestZoneSpec
at http://localhost:9876/_karma_webpack_/vendor.bundle.js:270128
All I have changed is what I told above, what is this error telling me ?
EDIT : Correction with github links
Corrected this issue by updating the zone.js version to 0.8.26 and replaced the imports in test.ts by only one line :
import 'zone.js/dist/zone-testing';
But now I get this error for all tests :
HeadlessChrome 67.0.3396 (Windows 10.0.0) SomeService #getCurrentUser should return user object FAILED
TypeError: Cannot read property 'assertPresent' of undefined
at resetFakeAsyncZone node_modules/#angular/core/#angular/core/testing.es5.js:308:1)
at Object.<anonymous> node_modules/#angular/core/#angular/core/testing.es5.js:1015:1)
at ZoneQueueRunner.webpackJsonp../node_modules/zone.js/dist/zone-testing.js.jasmine.QueueRunner.ZoneQueueRunner.execute node_modules/zone.js/dist/zone-testing.js:437:1)
HeadlessChrome 67.0.3396 (Windows 10.0.0): Executed 120 of 120 (120 FAILED) ERROR (4.725 secs / 4.633 secs)
Relatedd issue on github but no solution for now.
The content of my test.ts :
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import { getTestBed } from '#angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '#angular/platform-browser-dynamic/testing';
import 'zone.js/dist/zone-testing';
// Unfortunately there's no typing for the `__karma__` variable. Just declare it as any.
declare const __karma__: any;
declare const require: any;
// Prevent Karma from running prematurely.
__karma__.loaded = function () {};
// First, initialize the Angular testing environment.
getTestBed().initTestEnvironment(
BrowserDynamicTestingModule,
platformBrowserDynamicTesting()
);
// Then we find all the tests.
const context = require.context('./', true, /\.spec\.ts$/);
// And load the modules.
context.keys().map(context);
// Finally, start Karma to run the tests.
__karma__.start();
Try moving the zone-testing import up to be one of the first imports like this:
// This file is required by karma.conf.js and loads recursively all the .spec and framework files
import 'zone.js/dist/zone-testing';
import { getTestBed } from '#angular/core/testing';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from
'#angular/platform-browser-dynamic/testing';
Taken from this bug report: All tests failed if order of imports is changed in test.ts

TypeError: Class extends value undefined is not a constructor or null

This problem has been causing me to lose my sanity for the last couple of days.
Here is my directory structure:
[src]
|- cmds/
| |- Gh.js
| \- Help.js
|- commands.js
|...
I am trying to import a class exported by commands.js into Help.js and Gh.js (and any other files I might add in the future). However, I keep getting an error:
class Gh extends _commands.Command {
^
TypeError: Class extends value undefined is not a constructor or null
All of the files are being transpiled using Babel, with env set to "node": "current" and using the wildcard package. I have tried to set it for "browser" to see if it was an issue of it being too "advanced", but I got a different error about super functions (or something), which I assume is the same issue.
Here is the class being exported from commands.js:
export class Command {
constructor (msg) {
this.id = msg.author.id
this.msg = msg
}
action () {}
get data () {
return readData().user[this.id]
}
updateUserData (key, val) {
updateUserData(this.id, key, val)
}
sendMsg (data) {
sendMsg(this.msg, data)
}
}
...and here is cmds/Gh.js, one of the files that I am trying to import Command into:
import {Command} from '../commands'
export class Gh extends Command {
constructor (msg) {
super(msg)
this.desc = 'Returns GitHub repository link and exits'
}
action () {
this.sendMsg('GitHub link: https://github.com/owm111/knife-wife')
}
}
I tried putting Command into both of the cmds/, and they worked perfectly. However, when moving it back into commands.js, it broke again. I tried changing the path it is importing from from ../commands to ./../commands, ../commands.js, ./../commands.js; none worked. I moving commands.js into cmds/, still broke. I tried to console.log(Command) in both of the cmds/, but they both returned undefined.
All of this makes it look like is a problem with importing, but I cannot figure out what for the life of me. Please help.
If anyone else sees this error, the first thing to look for is circular dependencies. Import file A into B, B into some other file C, and so on. If any of C through Z is imported into A, then JS will not be able to ensure that a file is defined at the time that another file needs it (and will not try to go back and fill in the blanks later).
This was likely the case here, since there was clearly other code not posted, and it only appeared when file dependencies were introduced. The problem exists regardless of file structure: the only structure guaranteed to avoid it is a single giant JS file. The solution is to ensure a strict tree structure of relationships between classes, and use factory methods or alternative communications like emitters to keep the couplings loose.
If you have more than a couple import / require statements, I recommend periodically running a checker like Madge to find and optionally visualize any loops before they become hard to undo.
npm install --save-dev madge
node_modules/madge/bin/cli.js --warning --circular --extensions js ./
As others have mentioned, this results from circular dependencies. I tried for hours to resolve it. Ultimately it was a tool called dpdm that worked wonders for me, finding 27 cycles and quickly leading to resolution. (I only had to solve a couple of those before the rest resolved as well.)
yarn global add dpdm or npm i -g dpdm
Then
dpdm file.js or dpdm file.ts
In my case this found a large number of cycles that Madge and manual inspection had failed to reveal. Great tool.
This is just a simple fix for node.js. Remove export from your class and at the bottom of your file put this in it.
module.exports.Command;
Now if you want to use the command class anywhere you just need to put this in each file where you would like to use it.
var { Command } = require('Command.js');
In my case, I made the dumb mistake of putting parentheses after extending React.Component like this:
class Classname extend React.Component() {
...
Removing the parentheses fixed the error.
In my case: I was importing a non-existing class:
Counter.js:
import React, { Compontent } from 'react';
class Counter extends Compontent {
App.js:
import React from 'react';
import Counter from './components/Counter';
function App() {
Another cause can be that you use the following syntax (after copying a class from a declaration file (xxxx.d.ts) -> xxxx.ts
export declare abstract class Something {
should be (of course):
export abstract class Something {
In my case, the order of the export dependency order was wrong. The dependency was in inheritance. I was using index.ts to export class, interfaces, abstract classes.
If B extends A, and on your index.ts you did the following, this will through the error mentioning A.
export { B } from 'somewhere';
export { A } from 'somewhere';
To fix that, you must maintain the order of dependencies like
export { A } from 'somewhere';
export { B } from 'somewhere';
This is an issue with your node version
What i did was to uninstall my node version manager, re installed and used it to install all my node versions , did nvm use to select which one to use , in my case it was 14.17.3 ,
ran npx react-native init command
That fixed it for me
I got this error that "Sequelize TypeError: Class extends value undefined is not a constructor or null, NodeJS" and solved it using that. If you have the same error, you can use the following solution that I've tried to explain it in code.
for example:
module.exports = (sequelize, DataTypes) => {
// Modeling a table: Inheritance
class Todo extends sequelize.Model {} // (!) Error
// if you want to use the above line, add the following line to "lib\sequelize.js file
// Sequelize.prototype.Model = Model; // add this line here
class Example extends sequelize.Sequelize.Model { }
Example.init({
title: DataTypes.STRING,
description: DataTypes.STRING,
status: DataTypes.BOOLEAN
}, {
sequelize,
modelName: 'todo',
timestamps: true
});
return Example;
};
For people getting this error when using JSweet java to javascript transpiler, I was able to fix it by enabling the 'bundle' option, mentioned here:
Bundle up all the generated code in a single file, which can be used
in the browser. The bundle files are called 'bundle.ts',
'bundle.d.ts', or 'bundle.js' depending on the kind of generated code.
NOTE: bundles are not compatible with any module kind other than
'none'.
This is part of my POM which contains the 'bundle true' addition:
<plugin>
<groupId>org.jsweet</groupId>
<artifactId>jsweet-maven-plugin</artifactId>
<version>${jsweet.transpiler.version}</version>
<configuration>
<verbose>true</verbose>
<tsOut>target/ts</tsOut>
<outDir>target/js</outDir>
<candiesJsOut>webapp</candiesJsOut>
<targetVersion>ES6</targetVersion>
<module>none</module>
<moduleResolution>classic</moduleResolution>
<bundle>true</bundle>
</configuration>
<executions>
<execution>
<id>generate-js</id>
<phase>generate-sources</phase>
<goals>
<goal>jsweet</goal>
</goals>
</execution>
</executions>
</plugin>
Then re-run 'mvn generate-sources', and make sure that you change the index.html file to load the new bundle.js file:
<html>
<head>
<link rel="icon" type="image/png" href="logo.png">
<link rel="shortcut icon" href="logo.png">
<meta charset="utf-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"></script>
</head>
<body>
<p>Test page</p>
<p id="target"></p>
<script type="text/javascript" src="../webapp/j4ts-0.7.0-SNAPSHOT/bundle.js"></script>
<script type="text/javascript" src="../target/js/bundle.js"></script>
</body>
</html>
I got this error on Angular "class extends value undefined is not a constructor or null" when running cypress and resolved it by changing the compiler options is tsconfig.json.
from:
"compilerOptions": {
"baseUrl": "./",
"downlevelIteration": true,
"resolveJsonModule": true,
"paths": {
"*": [
"./node_modules/*" <== throws error
],
to:
"compilerOptions": {
"baseUrl": "./",
"downlevelIteration": true,
"resolveJsonModule": true,
"paths": {
"*": [
"./node_modules/" <== correct
],
Another common cause of this error is if you installed Node.js into an already existing folding of an older installation of Node.js. Removing and reinstalling Node.js fixes that issue.
I got this error when I upgraded to Angular 13 from 12. ng update did not update webpack to v5 and their upgrade checklist doesn't mention it being a retirement. Once I upgraded webpack to latest, the error went away for me.
Anyone is facing this issue even though there is no circular dependency and using webpack then please add target:node in your webpack config file.

Using Third Party NPM packages in Angular-Cli

I have read countless tutorials on how to use third party packages but none actually say what needs to happen in the component.ts to make things work in regards to the import statement and how to get access to the function.
package: https://www.npmjs.com/package/#vote539/excel-as-json
Here are the steps I followed:
1) Npm Install excel-as-json --save
2) Add script to angluar-cli.json scripts array
3) Add typings in typings.d.ts
4) Add import statesments
1) Using Angular-Cli I installed the package
I have added this package to my scripts array inside angular-cli.json.
2) .Angular-Cli.json
...
"styles": [
"styles.scss"
],
"scripts": [ "../node_modules/excel-as-json/lib/excel-as-json.js" ],
"environmentSource": "environments/environment.ts",
"environments": {
...
3) I added in some typings
typings.d.ts
declare var module: {
id: string;
};
declare var require: any;
4) Inside my component.ts file I have added in the import statement
convertExcel = require('excel-as-json').processFile;
and then in my constructor I added in the code needed to run the conversion:
compoent.ts
export class DashboardComponent implements OnInit{
constructor() {
convertExcel('../assets/excel/assignedresources.xlsx', '../assets/excel/assignedresources.json');
}
ngOnInit() {
}
}
I tried many various of the import state, Always getting the following errors:
errors
Cannot find module 'excel-as-json'.)
(14,5): Cannot find name 'convertExcel'.)

module 'ng' has no exported member 'ui' when using ui-router type definition for typescript

I am trying to use this typescript definition file for ui-router:
https://github.com/borisyankov/DefinitelyTyped/blob/master/angular-ui/angular-ui-router.d.ts
Here is the code at the top of the definition file:
// Type definitions for Angular JS 1.1.5+ (ui.router module)
// Project: https://github.com/angular-ui/ui-router
// Definitions by: Michel Salib <https://github.com/michelsalib>
// Definitions: https://github.com/borisyankov/DefinitelyTyped
/// <reference path="../angularjs/angular.d.ts" />
declare module ng.ui {
interface IState {
...
Here is how I am using it:
module MyModule
{
export class MyStateConfig
{
constructor(
// -> error on the word ng.ui on next line
private $stateProvider: ng.ui.IStateProvider,
private $urlRouterProvider: ng.ui.IUrlRouterProvider
...)
{
this.$stateProvider.state(...
This was working in Visual Studio but now with WebStorm I get a message saying "
module 'ng' has no exported member 'ui'
Can someone give me advice on this. Is this something to do with a different module system with WebStorm?
Have you tried adding a reference comment in your module source file? Something like...
/// <reference path="path/to/angular-ui/angular-ui-router.d.ts" />
Visual Studio doesn't require this because its msbuild tasks automatically tell the compiler to reference any definition that are included in the project. I'm guessing WebStorm doesn't use msbuild project files.
Ensure the #types/angular-ui-bootstrap npm package is installed.
npm install #types/angular-ui-bootstrap
Check your tsConfig.json file, in compilerOptions, look for a types array. Try removing types or replacing with typeRoots. Something like this:
"compilerOptions": {
"target": "ES5",
"sourceMap": true,
....
....
"typeRoots": [
"node_modules/#types"
]
},

Categories

Resources