18F/web-design-standards

View on GitHub
packages/usa-character-count/src/test/character-count.spec.js

Summary

Maintainability
B
4 hrs
Test Coverage
const fs = require("fs");
const path = require("path");
const assert = require("assert");
const CharacterCount = require("../index");

const { VALIDATION_MESSAGE, MESSAGE_INVALID_CLASS } = CharacterCount;

const TEMPLATE = fs.readFileSync(
  path.join(__dirname, "/character-count.template.html")
);

const EVENTS = {};

/**
 * send an input event
 * @param {HTMLElement} el the element to sent the event to
 */
EVENTS.input = (el) => {
  el.dispatchEvent(new KeyboardEvent("input", { bubbles: true }));
};

const characterCountSelector = () =>
  document.querySelector(".usa-character-count");
const tests = [
  { name: "document.body", selector: () => document.body },
  { name: "character count", selector: characterCountSelector },
];

tests.forEach(({ name, selector: containerSelector }) => {
  describe(`character count component initialized at ${name}`, () => {
    const { body } = document;

    let root;
    let input;
    let requirementsMessage;
    let statusMessageVisual;
    let statusMessageSR;

    beforeEach(() => {
      body.innerHTML = TEMPLATE;
      CharacterCount.on(containerSelector());

      root = characterCountSelector();
      input = root.querySelector(".usa-character-count__field");
      requirementsMessage = root.querySelector(".usa-character-count__message");
      statusMessageVisual = root.querySelector(".usa-character-count__status");
      statusMessageSR = root.querySelector(".usa-character-count__sr-status");
    });

    afterEach(() => {
      CharacterCount.off(containerSelector());
      body.textContent = "";
    });

    it("hides the requirements hint for screen readers", () => {
      assert.strictEqual(
        requirementsMessage.classList.contains("usa-sr-only"),
        true
      );
    });

    it("creates a visual status message on init", () => {
      const visibleStatus = document.querySelectorAll(
        ".usa-character-count__status"
      );

      assert.strictEqual(visibleStatus.length, 1);
    });

    it("creates a screen reader status message on init", () => {
      const srStatus = document.querySelectorAll(
        ".usa-character-count__sr-status"
      );

      assert.strictEqual(srStatus.length, 1);
    });

    it("adds initial status message for the character count component", () => {
      assert.strictEqual(
        statusMessageVisual.innerHTML,
        "20 characters allowed"
      );
      assert.strictEqual(statusMessageSR.innerHTML, "20 characters allowed");
    });

    it("informs the user how many more characters they are allowed", () => {
      input.value = "1";

      EVENTS.input(input);

      assert.strictEqual(statusMessageVisual.innerHTML, "19 characters left");
    });

    it("informs the user they are allowed a single character", () => {
      input.value = "1234567890123456789";

      EVENTS.input(input);

      assert.strictEqual(statusMessageVisual.innerHTML, "1 character left");
    });

    it("informs the user they are over the limit by a single character", () => {
      input.value = "123456789012345678901";

      EVENTS.input(input);

      assert.strictEqual(
        statusMessageVisual.innerHTML,
        "1 character over limit"
      );
    });

    it("informs the user how many characters they will need to remove", () => {
      input.value = "1234567890123456789012345";

      EVENTS.input(input);

      assert.strictEqual(
        statusMessageVisual.innerHTML,
        "5 characters over limit"
      );
    });

    it("should show the component and input as valid when the input is under the limit", () => {
      input.value = "1";

      EVENTS.input(input);

      assert.strictEqual(input.validationMessage, "");
      assert.strictEqual(
        statusMessageVisual.classList.contains(MESSAGE_INVALID_CLASS),
        false
      );
    });

    it("should show the component and input as invalid when the input is over the limit", () => {
      input.value = "123456789012345678901";

      EVENTS.input(input);

      assert.strictEqual(input.validationMessage, VALIDATION_MESSAGE);
      assert.strictEqual(
        statusMessageVisual.classList.contains(MESSAGE_INVALID_CLASS),
        true
      );
    });

    it("should not allow for innerHTML of child elements ", () => {
      Array.from(statusMessageVisual.childNodes).forEach((childNode) => {
        assert.strictEqual(childNode.nodeType, Node.TEXT_NODE);
      });
    });
  });
});