run-dom-tests
v2.0.1
Published
Run mocha tests on node.js using a virtual DOM.
Downloads
6
Readme
run-dom-tests
Run mocha + chai + chai-dom tests on node.js using a virtual DOM.
Install
npm install -g run-dom-tests
run-dom-tests test.json
JSON file format: { html, tests }
Test
npm test
Usage sample
Document testing
You can make assertions about the document
, both based on its plain, static structure - _originalDocument_
- or its dynamic structure document
. For example, given the following HTML...
<html>
<head>
<script>
function insertSection() {
const secondDiv = document.getElementById("second");
const section = document.createElement("section");
document.body.insertBefore(section, secondDiv);
}
document.getElementById("insert-section").addEventListener("click", insertSection);
insertSection();
</script>
</head>
<body>
<p id="first">
This is a paragraph
</p>
<button id="insert-section">Insert a new section</button>
<p id="second">
This is another paragraph
</p>
</body>
</html>
...you can write the following tests:
describe("DOM API", function() {
// you can validate the static structure of the DOM...
context("static dom", () => {
// ...both using plain chai https://www.chaijs.com/ assertions...
it("works with plain assertions", () => {
_originalDocument_.getElementsByTagName("p").length.should.eql(2);
document.getElementsByTagName("p").length.should.eql(2);
})
// ...or chai-dom https://www.chaijs.com/plugins/chai-dom/ assertions:
it("works with mocha-chai assertions", () => {
_originalDocument_.body.should.contain("p");
_originalDocument_.body.should.not.contain("section");
document.getElementsByTagName("p").should.have.length(2);
document.getElementById("first").should.exist;
document.getElementById("first").should.have.trimmed.text("This is a paragraph");
})
})
// you can also validate the
// dynamic structure of the DOM just after the initial script tags execution
describe("script tags", () => {
it("inserts dom elements", () => {
_originalDocument_.getElementsByTagName("section").should.have.length(0);
document.getElementsByTagName("section").should.have.length(1);
});
})
// finally you can interact with the dynamic dom
// through events and make assertions about them
describe("event handlers", () => {
beforeEach(() => {
// if your test mutates the DOM
// you will need to reset document to a fresh state
// before each test
_resetDocument_();
});
it("inserts dom elements after a single event", () => {
const button = document.getElementById("insert-section");
_dispatch_("click", button);
document.getElementsByTagName("section").should.have.length(2);
});
it("inserts dom elements after multiple events", () => {
const button = document.getElementById("insert-section");
_dispatch_("click", button);
_dispatch_("click", button);
_dispatch_("click", button);
document.getElementsByTagName("section").should.have.length(4);
});
});
});
User interactions testing
User interactions like alert
and prompt
can be tested by stubbing user responses using _stubXxxResponse_
and checking prompted messages with _shiftXxxMessage_
.
For example given the following HTML document...
<html>
<head>
<title>events</title>
</head>
<body>
<button id="prompt-and-alert">Prompt and Alert</button>
<button id="confirm-and-alert">Confirm and Alert</button>
<script>
document.querySelector("#prompt-and-alert").addEventListener("click", () => {
let name = prompt("Please tell us who you are");
alert("Hello " + name);
});
document.querySelector("#confirm-and-alert").addEventListener("click", () => {
let result = confirm("Are you really sure?");
if (result) {
alert("Yay");
} else {
alert("Boo");
}
});
</script>
</body>
</html>
...you can write the following user interaction tests:
describe("User interactions", () => {
beforeEach(() => {
// call before each test in
// order to clear user interactions registered and tested
// through _stubXxxResponse_ and _shiftXxxMessage_
_resetUserInteractions_();
// if you also need to reset the document state
// you can call `_resetDocument_()`
})
it("allows stubbing sequential confirm interaction", function() {
// stub responses before calling the code
_stubConfirmResponse_(false);
_stubConfirmResponse_(true);
// fire the actual code
// by simulating events
_dispatch_("click", document.querySelector("#confirm-and-alert"));
_dispatch_("click", document.querySelector("#confirm-and-alert"));
// run assertions
_alertMessagesCount_().should.eql(2);
_confirmMessagesCount_().should.eql(2);
"Are you really sure?".should.eql(_shiftConfirmMessage_());
"Boo".should.eql(_shiftAlertMessage_());
"Are you really sure?".should.eql(_shiftConfirmMessage_());
"Yay".should.eql(_shiftAlertMessage_());
_alertMessagesCount_().should.eql(0);
_confirmMessagesCount_().should.eql(0);
});
it("allows stubbing sequential prompt interaction", function() {
// stub responses before calling the code
_stubPromptResponse_("Node");
_stubPromptResponse_("Mumuki");
_stubPromptResponse_("JS");
// fire the actual code
// by simulating events
_dispatch_("click", document.querySelector("#prompt-and-alert"));
_dispatch_("click", document.querySelector("#prompt-and-alert"));
_dispatch_("click", document.querySelector("#prompt-and-alert"));
// run assertions
_alertMessagesCount_().should.eql(3);
_promptMessagesCount_().should.eql(3);
"Hello Node".should.eql(_shiftAlertMessage_());
"Hello Mumuki".should.eql(_shiftAlertMessage_());
"Hello JS".should.eql(_shiftAlertMessage_());
_alertMessagesCount_().should.eql(0);
_promptMessagesCount_().should.eql(3);
});
});
HTTP interactions testing
HTTP Interaction tests are are built on top of nock, using the _nock_
object.
Given the following HTML document...
<html>
<head>
<title>ajax</title>
<script>
document.addEventListener("DOMContentLoaded", () => {
document.querySelector("#get-data").addEventListener("click", () => {
fetch("https://some-domain.com/some-data.json")
.then((response) => {
return response.json();
})
.then((data) => {
document.querySelector("#data").innerHTML = data.content;
});
});
});
</script>
</head>
<body>
<div>
<button id="get-data">GET DATA NOW!</button>
</div>
<h1>Remote data:</h1>
<pre id="data">Nothing yet...</pre>
</body>
</html>
...you can write the following HTTP interaction tests:
describe("HTTP Interactions", function() {
beforeEach(() => {
// resets all user interactions,
// dom state and http interactions
_resetAll_();
// if only http interactions need to be reseted,
// call _resetHttpInteractions_() instead
});
it("shows the downloaded content when the button is clicked", function(done) {
document.querySelector("#data").innerHTML.should.eql("Nothing yet...");
const mockedGet = _nock_("https://some-domain.com/")
.get("/some-data.json")
.reply(200, { content: "Some remote data" });
_dispatch_('click', document.querySelector("#get-data"));
_waitFor_(() => mockedGet.isDone(), () => {
document.querySelector("#data").innerHTML.should.eql("Some remote data");
done();
});
});
});
Reference
Members
Constants
Functions
_nock_
nock object for mocking http interactions
Kind: global variable See: https://github.com/nock/nock
_waitFor_
Waits for a condition to occur, and then executes an action.
This function will check for the condition with a period of WAIT_FOR_TIMEOUT
.
Kind: global variable See: WAIT_FOR_TIMEOUT
| Param | Type | Description | | --- | --- | --- | | condition | function | the condition to wait | | action | function | the action to execute |
~~oldDocument~~
Deprecated
Kind: global variable
_originalDocument_ : HTMLDocument
The original HTML document, before any JavaScript actions are executed
Kind: global variable
WAIT_FOR_TIMEOUT : number
Polling period of _waitFor_
Kind: global constant
~~_wait_for_()~~
Deprecated
Kind: global function
_dispatch_(type, node)
Simulates the dispatch of an event of the given type to the given node
Kind: global function
| Param | Type | Description |
| --- | --- | --- |
| type | string | the event type, such as click
or DOMContentLoaded
|
| node | HTMLElement | the simulated event target. document
by default |
_stubConfirmResponse_(response)
Enqueues an stubbed confirm window response message.
This function must be called in order, before the first alert confirm
is performed
Kind: global function
| Param | Type | Description | | --- | --- | --- | | response | string | the stubbed response of the confirm window |
_stubPromptResponse_(response)
Enqueues an stubbed a prompt window response message
This function must be called in order, before the first alert prompt
is performed
Kind: global function
| Param | Type | Description | | --- | --- | --- | | response | string | the stubbed response of the prompt window |
_shiftAlertMessage_() ⇒ string
Dequeues the first pending alert message to check.
Subsequent calls to this function will produce different results. When there are no more alert messages to dequeue, undefined is returned
Kind: global function Returns: string - the first pending alert message
_shiftConfirmMessage_() ⇒ boolean
Dequeues the first pending confirm message to check.
Subsequent calls to this function will produce different results. When there are no more confirm messages to dequeue, undefined is returned
Kind: global function Returns: boolean - the first pending confirm message
_shiftPromptMessage_() ⇒ string
Dequeues the first pending prompt message to check.
Subsequent calls to this function will produce different results. When there are no more prompt messages to dequeue, undefined is returned
Kind: global function Returns: string - the first pending prompt message
_alertMessagesCount_() ⇒ number
Answers the number of the pending alert message to check
Kind: global function Returns: number - the pending messages count to check
_confirmMessagesCount_() ⇒ number
Answers the number of the pending confirm message to check
Kind: global function Returns: number - the pending messages count to check
_promptMessagesCount_() ⇒ number
Answers the number of the pending prompt message to check
Kind: global function Returns: number - the pending messages count to check
_resetUserInteractions_()
Reset all stubs and messages
Kind: global function
_resetHttpInteractions_()
Reset nock state
Kind: global function
_resetDocument_()
Resets the document
to its original state,
discarding every document polyfill
and then runs its scripts again.
window
is not cleared.
Kind: global function
_resetAll_()
Resets the document, interactions and nock state
Kind: global function