jsdom.env not working on node.js C9 - javascript

So I've been working with Node.js on C9 recently and ran into this problem in a javascript file:
jsdom.env("", function(err, window) {
TypeError: jsdom.env is not a function
Here is my code:
var jsdom = require('jsdom');
var $;
jsdom.env("", function(err, window) {
console.log("what");
if (err) {
console.error(err);
return;
}
$ = require("jquery")(window);
$.ajax(settings).done(function (response) {
console.log(response);
});
});
I updated all my dependencies as well as Node itself but still get this problem. Does anyone know what's up?

I was facing the same issue. Was looking for the solution all over the web. It turned out that jsdom has updated some of their features since v10. So, I wanted to use jQuery in the Node.js end of an express app.
For those who are just looking for answers about how to include jQuery in Node, I would like to mention that you'll need to install jsdom using npm install jsdom and jQuery using npm install jquery. Then:
For jQuery to work in Node, a window with a document is required. Since no such window exists natively in Node, one can be mocked by jsdom as below:
var jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { window } = new JSDOM();
const { document } = (new JSDOM('')).window;
global.document = document;
var $ = jQuery = require('jquery')(window);
.env() is deprecated since v10. Hope this helps you or anyone who has been facing these types of issues.

As mentioned above, .env() is deprecated.
Use the following:
const { window } = new JSDOM(``, { runScripts: "dangerously" });
const myLibrary = fs.readFileSync("../../whatever.js", { encoding: "utf-8" });
const scriptEl = window.document.createElement("script");
scriptEl.textContent = myLibrary;
window.document.body.appendChild(scriptEl);
In regard to the answer above, and from the jsdom docs:
Don't stuff jsdom globals onto the Node global
A common antipattern we see when people use jsdom is copying globals
from a jsdom window onto the Node.js global, and then trying to run
the code---intended for a browser---inside Node.js. This is very bad
and you should not do it. It runs code intended for the web browser in
some kind of hybrid franken-environment polluted with a ton of globals
that don't make sense there, and loses all benefits of isolating your
code into a jsdom window.
https://github.com/jsdom/jsdom/wiki/Don%27t-stuff-jsdom-globals-onto-the-Node-global

Step 1: npm install jquery
Step 2: npm install jsdom
<!-- language: lang-js -->
//add dummy function to test jquery in Node
function fn1( value ){ console.log( "fn1 says " + value );}
function fn2( value ) {console.log( "fn2 says " + value ); return false;}
var jsdom = require('jsdom');
const { JSDOM } = jsdom;
const { window } = new JSDOM();
const { document } = (new JSDOM('')).window;
//comment out the line below it create problem
//global.document = document;
var $ = jQuery = require('jquery')(window);
var callbacks = $.Callbacks();
callbacks.add(fn1);
callbacks.fire("foo!");
callbacks.add( fn2 );
callbacks.fire( "fool!" );

Related

Jest to test JS in a php file

I need to test some javascript I have in a php file (cannot be turned in any other extension file, for a few reasons, including it also contains php).
How can I achieve this using Jest?
For example, I have tried the following:
const parse = require('regenerator-runtime/runtime');
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
test("test JavaScript code in PHP file", async () => {
//create a mock of the DOM
const dom = new JSDOM(`<!DOCTYPE html><body>
<script>
// Add JS code from php file in here
let sum = function sum(a, b) {
return a + b
}
</script>
</body>`);
global.document = dom.window.document;
global.window = dom.window;
global.navigator = dom.window.navigator;
// run your tests here
expect(sum(1, 2)).toBe(3);
});
Unfortunately gives me an error:
ReferenceError: sum is not defined
How can I test the JS within the mock of the DOM since it is in it's own scope?
Is there a better way to test javascript in a php file?

mocha unit test - how to clear cached javascript after each test

I'm trying to test a javascript file (let's call it hello.js) using mocha and jsdom. I have several unit tests; each test basically sets up some window properties, calls hello.js, and checks window property values. Tests seem to be running somehow; however, it seems like mocha uses "cached" hello.js after the first test (logging only shows in the first test).
Can someone tell me how I can ensure that I reload hello.js in each test?
const expect = require('chai').expect;
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
var dom = (new JSDOM(`<!DOCTYPE html><html><head></head><body></body></html>`));
describe('hello', ()=>{
afterEach(()=>{
dom = (new JSDOM(`<!DOCTYPE html><html><head></head><body></body></html>`));
global.window = {};
document = {};
location = {};
});
it('should test A', ()=> {
global.window = dom.window;
global.window.document.cookie = "test=kiwi";
document = global.window.document;
const hello = require('../public/hello.js');
expect(document.getElementsByTagName('IFRAME').length).to.equal(1);
});
it('should test B', ()=> {
global.window = dom.window;
global.window.document.cookie = "test=apple";
document = global.window.document;
const hello = require('../public/hello.js');
expect(document.getElementsByTagName('IFRAME').length).to.equal(0);
});
});
There's a library that you can pull in to do this for you:
Example:
const resetCache = require('resnap')(); // Capture "clean" cache state
Reference: Resnap
you can use flush-cache
npm i flush-cache --save-dev
beforeEach(function () {
flush()
})

jsdom error: utf-8 is not recognized as encoding for HTML

I'm trying to do the simplest thing with jsdom and jest - loading up an HTML file to test its DOM:
const jsdom = require('jsdom');
const {JSDOM} = jsdom;
JSDOM.fromFile('../src/index.html'), {})
.then(dom => {
console.log(dom.serialize());
// need to run tests here...
})
.catch(error => {
console.log(error);
});
And I get the following error:
Error: Encoding not recognized: 'UTF-8' (searched as: 'utf8')
And I found the same issue against the library here, which was closed without offering any solution.
After having spent many hours, I'm buffled that I cannot find any solution to such a basic task :(
Any idea how to overcome this issue?
A better way is to read the file as a string and then not use fromFile() at all:
/* eslint-env jest, es6, node */
// loads the necessary node packages
const jsdom = require( 'jsdom' );
const { JSDOM } = jsdom;
const fs = require( 'fs' );
// __dirname is a Node.js global object
// https://nodejs.org/api/globals.html
const html = fs.readFileSync( __dirname + '/app/donation-types.html' ).toString();
// HTML to be imported as DOM
// const html = './app/donation-types.html';
var dom = new JSDOM( html );
// set the global window and document objects using JSDOM
// global is a node.js global object
if ( global !== undefined ) {
global.window = dom.window;
global.document = dom.window.document;
}
// requires my code to be tested
require( './app/file.js' );
test( 'sees if window is available before loading DOM...', () => {
expect( window !== undefined ).toBe( true );
} );
test( 'verifies document is available before loading DOM...', () => {
expect( document !== undefined && document !== null ).toBe( true );
} );
Adding to #Patrick Lewis' answer, you can add the following to start selecting elements:
const doc = dom.window.document
const box = doc.querySelector("#blue-box")
Though I can't for the life of me understand why this isn't possible:
const $ = doc.window.document.querySelector
const box = $("#blue-box")
On Jest, putting this on top of the file worked for me:
require('iconv-lite').encodingExists('foo'); // utf-8 support
I don't know why. :-|

Node.js - Cannot read property 'toString' of null when running package on Windows

I am using the gulp and hercule package on node.js to transclude some plain text files. On Unix, everything seems to work fine. However, some coworkers are having issues running it on Windows. They are getting the following error message only when running on Windows:
[13:02:01] TypeError: Cannot read property 'toString' of null at Object.transcludeStringSync (D:\project\node_modules\hercule\lib\hercule.js:136:36)
I have tried the above with hercule#3.0.5 as well as hercule#2.0.5, and both packages give the above error. However, given that this occurs only on Windows and across many versions of the package, I suspect this issue has something to due with the Node.js installation or path.
The code that is using the hercule package:
var fs = require('fs');
var path = require('path');
var gulp = require('gulp');
var drakov = require('drakov');
var hercule = require('hercule');
gulp.task('mock', ['i18n','build_minify_no_tests'], function() {
var mockSpecificationTemplate= fs.readFileSync('test/mock/mock-template.apib','utf8');
var transcludedMockSpecification = hercule.transcludeStringSync(mockSpecificationTemplate, {
relativePath: path.resolve('../../../')
});
fs.writeFileSync('test/mock/mock.apib', transcludedMockSpecification, 'utf-8');
// Running mock server
var drakovArgv = {
sourceFiles: 'test/mock/mock.apib',
serverPort: 9000,
staticPaths: [
'../../'
],
discover: true,
watch: true
};
drakov.run(drakovArgv);
});
node and npm version information:
$ node -v
v6.3.0
$ npm -v
3.10.3
hercule.transcludeStringSync simply runs another hercule process and sends input to it:
const result = childProcess.spawnSync('../bin/hercule', syncArgs, syncOptions);
with the script ../bin/hercule:
#!/usr/bin/env node
"use strict";
require('../lib/main.js');
...obviously doesn't work on Windows
If that task must be synchronized, you may use the following function instead:
function transcludeStringSync(input, options) {
const {dirname, join} = require('path')
const hercule = join(dirname(require.resolve('hercule')), 'main')
const args = [hercule, '--reporter', 'json-err']
for (let name in options) {
args.push(`--${name}`, `--${options[name]}`)
}
const result = require('child_process').spawnSync('node', args, {input})
const err = result.stderr.toString()
if (err) throw new Error('Could not transclude input')
return result.stdout.toString()
}

How do I require() from the console using webpack?

How do I require() / import modules from the console? For example, say I've installed the ImmutableJS npm, I'd like to be able to use functions from the module while I'm working in the console.
Here's another more generic way of doing this.
Requiring a module by ID
The current version of WebPack exposes webpackJsonp(...), which can be used to require a module by ID:
function _requireById(id) {
return webpackJsonp([], null, [id]);
}
or in TypeScript
window['_requireById'] =
(id: number): any => window['webpackJsonp'];([], null, [id]);
The ID is visible at the top of the module in the bundled file or in the footer of the original source file served via source maps.
Requiring a module by name
Requiring a module by name is much trickier, as WebPack doesn't appear to keep any reference to the module path once it has processed all the sources. But the following code seems to do the trick in lot of the cases:
/**
* Returns a promise that resolves to the result of a case-sensitive search
* for a module or one of its exports. `makeGlobal` can be set to true
* or to the name of the window property it should be saved as.
* Example usage:
* _requireByName('jQuery', '$');
* _requireByName('Observable', true)ยด;
*/
window['_requireByName'] =
(name: string, makeGlobal?: (string|boolean)): Promise<any> =>
getAllModules()
.then((modules) => {
let returnMember;
let module = _.find<any, any>(modules, (module) => {
if (_.isObject(module.exports) && name in module.exports) {
returnMember = true;
return true;
} else if (_.isFunction(module.exports) &&
module.exports.name === name) {
return true;
}
});
if (module) {
module = returnMember ? module.exports[name] : module.exports;
if (makeGlobal) {
const moduleName = makeGlobal === true ? name : makeGlobal as string;
window[moduleName] = module;
console.log(`Module or module export saved as 'window.${moduleName}':`,
module);
} else {
console.log(`Module or module export 'name' found:`, module);
}
return module;
}
console.warn(`Module or module export '${name}'' could not be found`);
return null;
});
// Returns promise that resolves to all installed modules
function getAllModules() {
return new Promise((resolve) => {
const id = _.uniqueId('fakeModule_');
window['webpackJsonp'](
[],
{[id]: function(module, exports, __webpack_require__) {
resolve(__webpack_require__.c);
}},
[id]
);
});
}
This is quick first shot at this, so it's all up for improvement!
Including this in a module will allow require([modules], function) to be used from a browser
window['require'] = function(modules, callback) {
var modulesToRequire = modules.forEach(function(module) {
switch(module) {
case 'immutable': return require('immutable');
case 'jquery': return require('jquery');
}
})
callback.apply(this, modulesToRequire);
}
Example Usage:
require(['jquery', 'immutable'], function($, immutable) {
// immutable and $ are defined here
});
Note: Each switch-statement option should either be something this module already requires, or provided by ProvidePlugin
Sources:
Based on this answer, which can be used to add an entire folder.
Alternative method from Webpack Docs - which allows something like require.yourModule.function()
I found a way that works, for both WebPack 1 and 2. (as long as the source is non-minified)
Repo: https://github.com/Venryx/webpack-runtime-require
Install
npm install --save webpack-runtime-require
Usage
First, require the module at least once.
import "webpack-runtime-require";
It will then add a Require() function to the window object, for use in the console, or anywhere in your code.
Then just use it, like so:
let React = Require("react");
console.log("Retrieved React.Component: " + React.Component);
It's not very pretty (it uses regexes to search the module wrapper functions) or fast (takes ~50ms the first call, and ~0ms after), but both of these are perfectly fine if it's just for hack-testing in the console.
Technique
The below is a trimmed version of the source to show how it works. (see the repo for the full/latest)
var WebpackData;
webpackJsonp([],
{123456: function(module, exports, __webpack_require__) {
WebpackData = __webpack_require__;
}},
[123456]
);
var allModulesText;
var moduleIDs = {};
function GetIDForModule(name) {
if (allModulesText == null) {
let moduleWrapperFuncs = Object.keys(WebpackData.m).map(moduleID=>WebpackData.m[moduleID]);
allModulesText = moduleWrapperFuncs.map(a=>a.toString()).join("\n\n\n");
// these are examples of before and after webpack's transformation: (which the regex below finds the var-name of)
// require("react-redux-firebase") => var _reactReduxFirebase = __webpack_require__(100);
// require("./Source/MyComponent") => var _MyComponent = __webpack_require__(200);
let regex = /var ([a-zA-Z_]+) = __webpack_require__\(([0-9]+)\)/g;
let matches = [];
let match;
while (match = regex.exec(allModulesText))
matches.push(match);
for (let [_, varName, id] of matches) {
// these are examples of before and after the below regex's transformation:
// _reactReduxFirebase => react-redux-firebase
// _MyComponent => my-component
// _MyComponent_New => my-component-new
// _JSONHelper => json-helper
let moduleName = varName
.replace(/^_/g, "") // remove starting "_"
.replace(new RegExp( // convert chars where:
"([^_])" // is preceded by a non-underscore char
+ "[A-Z]" // is a capital-letter
+ "([^A-Z_])", // is followed by a non-capital-letter, non-underscore char
"g"),
str=>str[0] + "-" + str[1] + str[2] // to: "-" + char
)
.replace(/_/g, "-") // convert all "_" to "-"
.toLowerCase(); // convert all letters to lowercase
moduleIDs[moduleName] = parseInt(id);
}
}
return moduleIDs[name];
}
function Require(name) {
let id = GetIDForModule(name);
return WebpackData.c[id].exports;
}
Being able to use require modules in the console is handy for debugging and code analysis. #psimyn's answer is very specific so you aren't likely to maintain that function with all the modules you might need.
When I need one of my own modules for this purpose, I assign a window property to it so I can get at it e.g window.mymodule = whatever_im_exporting;. I use the same trick to expose a system module if I want to play with it e.g:
myservice.js:
let $ = require('jquery');
let myService = {};
// local functions service props etc...
module.exports = myService;
// todo: remove these window prop assignments when done playing in console
window.$ = $;
window.myService = myService;
It is still a bit of a pain, but digging into the bundles, I can't see any way to conveniently map over modules.
The answer from #Rene Hamburger is good but unfortunately doesn't work anymore (at least with my webpack version). So I updated it:
function getWebpackInternals() {
return new Promise((resolve) => {
const id = 'fakeId' + Math.random();
window['webpackJsonp'].push(["web", {
[id]: function(module, __webpack_exports__, __webpack_require__) {
resolve([module, __webpack_exports__, __webpack_require__])
}
},[[id]]]);
});
}
function getModuleByExportName(moduleName) {
return getWebpackInternals().then(([_, __webpack_exports__, __webpack_require__]) => {
const modules = __webpack_require__.c;
const moduleFound = Object.values(modules).find(module => {
if (module && module.exports && module.exports[moduleName]) return true;
});
if (!moduleFound) {
console.log('couldnt find module ' + moduleName);
return;
}
return moduleFound.exports[moduleName];
})
}
getModuleByExportName('ExportedClassOfModule');
expose-loader is, in my opinion, a more elegant solution:
require("expose-loader?libraryName!./file.js");
// Exposes the exports for file.js to the global context on property "libraryName".
// In web browsers, window.libraryName is then available.
Adding the below code to one of your modules will allow you to load modules by id.
window.require = __webpack_require__;
In the console use the following:
require(34)
You could do something similar as psimyn advised by
adding following code to some module in bundle:
require.ensure([], function () {
window.require = function (module) {
return require(module);
};
});
Use require from console:
require("./app").doSomething();
See more
After making an npm module for this (see my other answer), I did a search on npms.io and seem to have found an existing webpack-plugin available for this purpose.
Repo: https://www.npmjs.com/package/webpack-expose-require-plugin
Install
npm install --save webpack-expose-require-plugin
Usage
Add the plugin to your webpack config, then use at runtime like so:
let MyComponent = require.main("./path/to/MyComponent");
console.log("Retrieved MyComponent: " + MyComponent);
See package/repo readme page for more info.
EDIT
I tried the plugin out in my own project, but couldn't get it to work; I kept getting the error: Cannot read property 'resource' of undefined. I'll leave it here in case it works for other people, though. (I'm currently using the solution mentioned above instead)
After both making my own npm package for this (see here), as well as finding an existing one (see here), I also found a way to do it in one-line just using the built-in webpack functions.
It uses WebPack "contexts": https://webpack.github.io/docs/context.html
Just add the following line to a file directly in your "Source" folder:
window.Require = require.context("./", true, /\.js$/);
Now you can use it (eg. in the console) like so:
let MyComponent = Require("./Path/To/MyComponent");
console.log("Retrieved MyComponent: " + MyComponent);
However, one important drawback of this approach, as compared to the two solutions mentioned above, is that it seems to not work for files in the node_modules folder. When the path is adjusted to "../", webpack fails to compile -- at least in my project. (perhaps because the node_modules folder is just so massive)

Categories

Resources