2.0.1 • Published 3 years ago

run-dom-tests v2.0.1

Weekly downloads
1
License
ISC
Repository
github
Last release
3 years ago

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

ParamTypeDescription
conditionfunctionthe condition to wait
actionfunctionthe 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

ParamTypeDescription
typestringthe event type, such as click or DOMContentLoaded
nodeHTMLElementthe 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

ParamTypeDescription
responsestringthe 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

ParamTypeDescription
responsestringthe 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

2.0.1

3 years ago

2.0.0

3 years ago

1.2.3

5 years ago

1.2.2

5 years ago

1.2.1

5 years ago

1.2.0

5 years ago

1.1.1

5 years ago

1.1.0

5 years ago

1.0.3

5 years ago

1.0.2

5 years ago

1.0.1

6 years ago

1.0.0

6 years ago