ReferenceError: TextEncoder is not defined when importing jsdom library - javascript

I have these two files:
test.js:
const jsdom = require("jsdom");
const hello = () => {
console.log("hello!");
};
module.exports = {
hello
};
and
server.js:
const hello = require('./stuff/test');
hello.hello();
directory structure:
myprojectfolder
backend
src
stuff
test.js
server.js
When I run server.js I get the ReferenceError: TextEncoder is not defined error:
/home/myusername/projects/myprojectfolder/node_modules/whatwg-url/lib/encoding.js:2
const utf8Encoder = new TextEncoder();
^
ReferenceError: TextEncoder is not defined
If I remove const jsdom = require("jsdom"); line from test.js, server.js runs fine and without any errors (outputs hello).
Why does it happen and how do I fix it (while still being able to import and use jsdom inside test.js?).

Well, it took me all day, but finally I managed to pull it together.
My problem was that tests didn't run because of that error.
So, in package.json under "scripts" I added the following:
"test": "jest --verbose --runInBand --detectOpenHandles --forceExit"
And in jest.config.js, I added the following:
globals: {
"ts-jest": {
tsConfigFile: "tsconfig.json"
},
TextEncoder: require("util").TextEncoder,
TextDecoder: require("util").TextDecoder
}

Related

NodeJs Testrunner freezes / stops when setting up Express server

I'm using Node v18 with the experimental testrunner. I use express as a dev dependency for http integration tests which works fine but there is one test freezing or stopping the testrunner ( it doesn't continue )
I'm using TS but can also reproduce it with JS, the test file HttpTests.js contains
import assert from 'assert/strict';
import express from 'express';
import test from 'node:test';
test('Http', async () => {
const server = express();
server.listen(3000);
assert.ok(false);
});
Running this with the npm script "test": "node --test $(find . -name '*Tests.js')" breaks the test runner.
Any ideas what is wrong or missing?
Why am I not using the default execution model?
Since I'm using TS I had to find a way to use ts-node with the testrunner. You can find more information here
https://github.com/nodejs/node/issues/43675
So currently my TS project is using this npm script, which works fine
Reproduction
I created a minimal reproduction repository with and without TypeScript
https://github.com/matthiashermsen/reproduce-broken-test-ts
https://github.com/matthiashermsen/reproduce-broken-test-js
For reproduction purposes run mkdir reproduction && cd reproduction && npm init -y && npm install express. After that create a test directory with a file HttpTests.js containing the content as shown above. Change the package.json to
{
"name": "reproduction",
"type": "module",
"scripts": {
"test": "node --test $(find . -name '*Tests.js')"
}
}
and run the script, the testrunner should not finish.
The testrunner is still experimental
Yes I know. But there are many tests in the project that work perfectly fine. Some sample code
await t.test('subtest - saves data.', async () => {
const expectedResult = {};
const api = express();
const port = await getRandomPort();
const server = api
.use(express.json())
.post('/save', (request, response) => {
response.json(expectedResult);
})
.listen(port);
const httpDataProvider = new HttpDataProvider({ url: `http://localhost:${port}` });
const actualResult = await httpDataProvider.saveSomething();
assert.deepEqual(actualResult, expectedResult);
server.close();
});
The issue is the async activity that you start (server.listen()) but don't stop before the test errors out (by an exception thrown by assert.ok(false)).
Your second test case will probably also stall if actualResult doesn't deep-equal expectedResult because of the same issue (server.close() won't be called).
A workaround would be to always make sure the server gets closed in the end:
test('Http', async () => {
const app = express();
const server = app.listen(3000);
try {
assert.ok(false);
} finally {
server.close();
}
});
Most test frameworks provide "before/after" functionality that can be used to set up or tear down auxiliary objects before and after a test.

command not found: nodejs-backend

I'm trying to create my own package that will initialize the boiler plate project by running node package execute npx. I once created one using node.js and inquirer and it worked when I run:
npx ts-config-files.json
This is the package that I've created and published on npm that generates tsconfigs.
The one that I've just created when i run the command:
npx nodejs-backend
I get this:
npx: installed 135 in 47.951s
command not found: nodejs-backend
Instead of getting some inquirer messages. The code of what i'm working on can be found here.
I've been thinking it's because I'm using typescript but when i changed to javascript it's still the same thing.
Sample code of the main file server.ts:
#!/usr/bin/env node
import path from "path";
import inquirer from "inquirer";
import { writeFile, readFile } from "fs/promises";
import fs from "fs";
import helperFunction from "./constants";
import { exec } from "child_process";
import { objJS, objTS } from "./utils";
import chalk from "chalk";
helperFunction.prompt();
const cwd = process.cwd();
const base_name = path.basename(cwd); // node
const main = async () => {
const baseDir: string = "src";
let fileName: string = "";
let packageObject: typeof objTS | typeof objJS;
const { language } = await inquirer.prompt([
{
choices: ["JavaScript", "TypeScript"],
type: "checkbox",
default: "TypeScript",
name: "language",
message: "which language do you want to use for your backend app?",
},
]);
....
You forgot to add:
"bin": {
"nodejs-backend": "server.ts"
},
Which should be in package.json.
Also after that make sure to npm link in your repo before trying to use npx otherwise it will try install it from the https://registry.npmjs.org registry instead of local.
That being said I'm fairly certain it won't execute as typescript even if you've done that (which isn't your issue right now but it will be). For that you can install ts-node and use #! /usr/bin/env ts-node.

How to transpile on fly before running script?

Does anyone knows how to tackle following problem?
What is the best approach to resolve it?
I wanted to present the simplest example, but generally I would like to run node script that have some dependencies files that are not written in es5. This script takes argument from cli and perform some action (eg. change content of index.html file), here for simplicity we only console log some greeting.
Currently we would get an error:
SyntaxError: Unexpected token export
Is there a way to somehow transpile on fly all file dependencies to es5 and run the script?
sample folder structure:
src/welcome.js
src/greeting.js
src/... // bunch of other files
package.json
greeting.js
const name = process.argv[2];
export const sayHello = `Hello ${name}!`;
export const sayHi = `Hi ${name}!`;
welcome.js
const { sayHello } = require('./greeting');
console.log(greeting, "Nice to meet you!");
in package.json
"scripts": {
"greet": "node src/welcome.js"
}
cli
npm run greet John

Cannot convert undefined or null to object at Function.keys

I'm using the matchFeature example from NodeJS but when i try to test the following error occurs in the
const cv = require('opencv4nodejs'); line from the example.
test/matchFeature.test.js
● Test suite failed to run
TypeError: Cannot convert undefined or null to object
at Function.keys ()
at Object. (node_modules/opencv4nodejs/lib/cv.js:63:8)
at Object.
(node_modules/opencv4nodejs/lib/opencv4nodejs.js:11:79)
I think is a problem when loading the module, but i cant fix it.
I'm using Jest for testing.
If i run the file with NodeJS, works without problems:
https://prnt.sc/sq41s9
And if i run test with jest, gives the error above:
https://prnt.sc/sq42mb
The images are from the file cv.js running the file and the test
EDIT: Screenshot from jest index.js (Module null)
https://prnt.sc/sq5rxl
I was able to solve it with a global setup script which loads the module.
// setup.js
module.exports = async () => {
const cv = require('opencv4nodejs');
console.log('cv.version', cv.version);
};
And in the jest Config in package.json:
"jest": {
...
"testEnvironment": "node",
"globalSetup": "./setup.js"
}

Disable Jasmine's fdescribe() and fit() based on environment

fdescribe() and fit() are great for reducing noise when you're working on a subset of tests. I sometimes forget to change them back to describe()/it() before merging my branch into master. (It's okay to have them in separate branch while working on code - i.e. a pre-commit check wouldn't work for me.)
My CI environment is Codeship. Is there a solution to this problem that would fail the tests in Codeship if it came across any focused methods?
Using something like no-focused-tests would be okay. Any idea how to enable this rule as an error in Codeship and disable it locally?
Edit 14.11.19:
To make things easier I created an installable package you can find at https://www.npmjs.com/package/tslint-jasmine
Original post:
If you're using TSLint and (like me) found that all the defocus and tslint-jasmine-noSkipOrFocus checkers are not working for you, I created a Gist for that: https://gist.github.com/djungowski/7d9126bb79970446b4ffeb5656c6bf1f
How to use:
Save Gist in a a folder called TSLint/Rules as noJasmineFocusRule.js
Add the Rules folder to your TSLint config: rulesDirectory: 'TSLint/Rules'
Enable option with "no-jasmine-focus": true
Using something like no-focused-tests would be okay. Any idea how to enable this rule as an error in Codeship and disable it locally?
You could use a combination of environment variables and redefining the fdescribe/fit global functions:
npm i --save cross-env
package.json:
"scripts": {
"test": "jasmine",
"test-safe": "cross-env FOCUSED_TESTS=off jasmine"
},
disableFocusedTestsIfNecessary.js (included after jasmine defines its globals):
if (process.env.FOCUSED_TESTS === "off") {
console.log("Focused tests must be off");
global.fdescribe = global.fit = function() {
throw new Error("fdescribe and fit are disabled in this environment");
};
}
else {
console.log("Focused tests enabled");
}
Tell codeship to run npm run test-safe instead of npm run test
For those interested, if you are using jasmine and eslint, you can use this plugin to ensure no focused tests: https://github.com/tlvince/eslint-plugin-jasmine.
First install eslint globally npm install -g eslint.
Then install the eslint-plugin-jasmine library npm install --save-dev eslint-plugin-jasmine.
Create a .eslintrc file which would look something like this:
{
"rules": {
"semi": 2
},
"plugins": ["jasmine"],
"env": {
"jasmine": true
},
"extends": "plugin:jasmine/recommended",
}
Then you are ready to run the linter eslint -c ./.eslintrc app.js
I'm late to the party.
I had a similar issue with my builds. We don't use ts / eslint so I just wrote a quick script to throw an error that would fail my dockerfile / build.
Here it is.
#!/bin/sh
files=$(find "./.." -type f -name '*.spec*')
errored=false
echo "Checking for focused tests"
for file in $files
do
if grep -E "fdescribe|fit" $file; [ $? -eq 0 ]; then
echo "-Focusing a test in the file $file"
errored=true
fi
done
if $errored; then
echo "Some tests were focused"
exit 1
else
echo "No tests were focused"
fi
This isn't the best solution. But it works for my needs.
To setup:
npm i lodash
npm i minimist
I call this from my gulp tasks:
node .\\build\\throwIfFocusedTest.js e2e/
node .\\build\\throwIfFocusedTest.js src/
throwIfFocusedTest.js:
const walkSync = require('./walkSync').default;
const _ = require('lodash');
const argv = require('minimist')(process.argv);
const fs = require('fs');
if (argv._.length !== 3) {
throw 'expecting 1 command line argument';
}
const directory = argv._[2];
const files = walkSync(directory);
const scriptFiles = _.filter(files, f => f.endsWith('.js') || f.endsWith('.ts'));
const invalidStrings = [
'fdescribe',
'fit',
];
_.each(scriptFiles, fileName => {
const contents = fs.readFileSync(fileName, 'utf8');
invalidStrings.forEach(is => {
if (contents.includes(is)) {
console.error(`throwIfFocusedTest: ${directory}: File contains ${is}: ${fileName}`);
process.exit(1);
}
});
});
console.log(`throwIfFocusedTest: ${directory}: No files contain: ${invalidStrings.join(', ')}`);
walkSync.js:
/**
* From: https://gist.github.com/kethinov/6658166
*/
exports.default = function walkSync(dir, filelist) {
var fs = fs || require('fs'),
files = fs.readdirSync(dir);
filelist = filelist || [];
files.forEach(function (file) {
var path = dir + file;
if (fs.statSync(dir + file).isDirectory()) {
filelist = walkSync(dir + file + '/', filelist);
}
else {
filelist.push(path);
}
});
return filelist;
};
If you're willing to fail on when tests are marked for focus or skip (fit + xit), there's a relatively new Karma feature that solves the problem with no plugins. Karma now supports a failOnSkippedTests config file / CLI option, which, per the docs, causes "failure on tests deliberately disabled, eg fit() or xit()".

Categories