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?
Related
After some very helpful tips learning about javascript promises and how they behave, I am looking for some help with properly importing/exporting functions from one javascript file into another which is run via NodeJS.
Essentially I want basicNode.js to open an html doc, and then once that is open (and the promise implied via the open(html) statement is 100% complete/finalized) run the function from change.js to alter the html to show "Hello" in the place of "Welcome to JavaScript". This will provide proof of concept that can then be extrapolated for a project dealing with automating reports at my internship, so any help is greatly appreciated
basic.html
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<p>Date/Time: <span id="datetime"></span></p>
<p id="here">Welcome to JavaScript</p>
<form>
<input type="button" value="click" onclick="changeThis()"/>
</form>
</body>
<script>
var dt = new Date();
document.getElementById("datetime").innerHTML = dt.toLocaleString();
</script>
<script type="text/javascript" src="change.js"></script>
<!--
<script>waitForElementToDisplay("#here",function(){alert("Hi");},1000,9000);</script>
-->
</html>
change.js
function changeThis() {
document.getElementById("here").innerHTML = "Hello";
}
module.exports={changeThis};
basicNode.js
const open = require('open');
var { change } = require('./change')
const jsdom = require("jsdom");
const { JSDOM } = jsdom;
global.document = new JSDOM('./basic.html').window.document;
const main = async () => {
await open('./basic.html');
change.changeThis();
}
main();
EDIT:after some tinkering, this is how I accomplished my task: in change.js, I set up the following:
const fs = require('fs');
const html = fs.readFileSync('./basic.html');
const dom = new JSDOM(html);
function changeThis() {
console.log("called changeThis() function, should alter the html")
var x = dom.window.document.getElementById('here').textContent;
console.log(x);
dom.window.document.getElementById('here').innerHTML = 'Hello'
var x = dom.window.document.getElementById('here').textContent;
console.log(x);
}
module.exports.changeThis = changeThis
Then, to call/import this function in my basicNode.js file:
var change = require('./change');
change.changeThis();
In your change.js-file you are exporting your function as a named export. A named export is normally used if you are only exporting one function from a file (this is not a rule), and named exports are mainly used when you are exporting several functions from a file.
This is a named export
module.exports= { changeThis };
A named export is imported as
var { changeThis } = require('./change')
The opposite to a named export is a default export.
module.exports = changeThis
This is imported as
var changeThis = require("./change")
Edit:
If what you want is to read the file, moodify the DOM and then write it to a file again, then you can do it like this.
const jsdom = require('jsdom');
const { JSDOM } = jsdom;
// Read the file from the system
const fs = require('fs');
const html = fs.readFileSync('./basic.html');
// Create a jsdom and modify the file
const dom = new JSDOM(html);
dom.window.document.getElementById('here').innerHTML = 'Hello';
// write the file to disc
fs.writeFileSync(
'./basicUpdated.html',
dom.window.document.documentElement.outerHTML
);
i am writing unit test cases for a js file which contains Progress Bar library, how to mock this
myjs.js
console.log("File that contains some js code....")
function myFunc(para1,para2){
var par = para1 + para2;
swal("Info",para);
return 3;
}
var bar = new ProgressBar.Line(dynamic, {//some code here} );
i am planning to unit test this function named "myFunc" so , i have written a test script that contains below code:
test.js
const $ = require('jquery');
global.$ = global.jQuery = $;
var ProgressBar = require('progressbar.js');
var myJs = require('myjs.js');
test('test if myFunc returns "3"',()=> {
expect(myJs.myFunc).toBe(3);
})
The myJs.js is loaded as a part of django template including the progressBar.js, while running this test case i get a error like ProgressBar is not defined.
additionally there exists a sweetalert in myFunc, how to mock this as well , a part from ProgressBar ?
Any kind help is appreciable
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()
})
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. :-|
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!" );