Jsdom: HTML5 Form Validation

Created on 14 Dec 2012  ·  9Comments  ·  Source: jsdom/jsdom

JSDOM does not currently handle HTML5 forms validation, and in particular, the checkValidity API.

This is needed to accurately test modern applications that utilize HTML5 forms.

I wonder if it would be possible to add this support, using http://www.thecssninja.com/javascript/h5f as a starting point. H5F is browser shim to add HTML5 forms support to browsers that don't support them. As an aside, I'd be happy to add this behavior myself if someone can point me to the best way to do it.

feature html living standard needs tests

Most helpful comment

:+1: It would be a great feature.

All 9 comments

I agree this would be nice and is important. However, it's going to be quite the undertaking.

The biggest thing this needs is a solid test suite. If you look in the Contributing.md guidelines, there's some pointers to where to find them, or failing that, what mailing lists to ask. If you can dig them up, I think we have a shot at implementing this, especially as you point out by using H5F as a starting point.

The only other important part of the actual implementation is that we don't have a proper "level4" or "html5" folder for implementing things beyond DOM Level 3. But that's something we've needed for a while, so I'll try to get on it soon.

:+1: It would be a great feature.

Right now I'm adding https://github.com/hyperform/hyperform as a polyfill after the jsdom setup. It works great.

Yay! I'm the author of Hyperform. And as luck would have it, I use JSDom already to set up a test environment.

The part that might be interesting for the JSDom maintainers: I played around with using Hyperform inside JSDom, too, to cover the appropriate web platform tests:

$ git diff test/web-platform-tests/create-jsdom.js
diff --git a/test/web-platform-tests/create-jsdom.js b/test/web-platform-tests/create-jsdom.js
index 7009df7..9edcb16 100644
--- a/test/web-platform-tests/create-jsdom.js
+++ b/test/web-platform-tests/create-jsdom.js
@@ -1,5 +1,6 @@
 "use strict";
 const jsdom = require("../..");
+const hyperform = require("hyperform");
 const nodeResolverPromise = require("../util").nodeResolverPromise;

 const globalPool = { maxSockets: 6 };
@@ -35,6 +36,9 @@ module.exports = (urlPrefix, testPath) => {

   return created
   .then(window => {
+    global.window = window;
+    global.document = window.document;
+    hyperform(window);
     return new Promise((resolve, reject) => {
       const errors = [];

and uncommenting in test/web-platform-tests/index.js the "html/semantics/forms/constraints/*.html" tests.

Unfortunately I only got a couple test cases to turn green. But if you are interested in adding (in whatever way) Hyperform's features to JSDom, I'd put some more work in

a) getting those tests to pass, and
b) providing a version of or configuration to Hyperform, that excludes the high level API

FWIW, I'm quite interested in this; I'm working on a Vue component that renders a form based on a JSON schema, and relies on the Validity API to yield a validation status upon submission. My unit tests are ran with Jest (which uses JSDOM under the hood), and as of now I'm unable to test this part.

@Boldewyn if you have any suggestion regarding my case, I'd gladly take it. Maybe I could provide some help in implementing this as well, if given some guidance to start upon :)

Edit: I used @EricHenry's solution below, and it worked. I just had to set checkValidity for HTMLInputElement, HTMLSelectElement, and HTMLTextAreaElement as well (and actually didn't need to fiddle with HTMLFormElement). Cheers!

For anyone having this issue when testing React components using Jest and Enzyme I was able to polyfill JSDOM by using the hyperform package mentioned above by @fernandopasik and @Boldewyn

In your test file you can do the following after installing hyperform.

import * as hyperform from 'hyperform';

var global = global;

const defineValidity = {
  get() {
    return hyperform.ValidityState(this);
  },
  configurable: true
};

global.HTMLFormElement.prototype.checkValidity = function() {
  return hyperform.checkValidity(this);
};

Object.defineProperty(global.HTMLFormElement.prototype, 'validity', defineValidity);
Object.defineProperty(global.HTMLInputElement.prototype, 'validity', defineValidity);
Object.defineProperty(global.HTMLSelectElement.prototype, 'validity', defineValidity);
Object.defineProperty(global.HTMLTextAreaElement.prototype, 'validity', defineValidity);

you can follow the same method for any other HTMLElements you may need to modify or any other attributes you need to polyfill.

PR #2142 implements the full constraint api without any polyfills

The Jest docs suggests mocking methods which are not implemented in JSDOM. I think this is a cleaner solution than polluting your environment because of tests.

In my specific case, I needed to validate a form and then inspect the classList of the form for the presence of a certain class, the presence of which would inform me about the form's validity.

My solution was to mock the form's properties e.g mock the native implementation of DOMTokenList for classList property inside

beforeAll(() => {
    window.DOMTokenList = jest.fn().mockImplementation(() => {
        return {
            list: [],
            remove: jest.fn().mockImplementation(function (item) {
                const idx = this.list.indexOf(item);

                if (idx > -1) {
                    this.list.splice(idx, 1)
                }
            }),
            add: jest.fn().mockImplementation(function (item) {
                this.list.push(item);
            }),
            contains: jest.fn().mockImplementation(function (item) {
                return this.list.indexOf(item) > -1;
            })
        };
    });
})

Then I used that to pass properties to the event handler. I'm able to access the form in my react component from the submit button with event.currentTarget.form

let mockClassList = new DOMTokenList();

submitBtn.simulate('click', {
        currentTarget: {
            form: {
                checkValidity: () => false,
                classList: mockClassList
            }
        },
        preventDefault: jest.fn(),
        stopPropagation: jest.fn()
    })

This allows me to set the validity of the form to false and true, and inspect the mockClassList for the presence of the was-validated class each time

    submitBtn.simulate('click', {
        currentTarget: {
            form: {
                checkValidity: () => false,
                classList: mockClassList
            }
        },
        preventDefault: jest.fn(),
        stopPropagation: jest.fn()
    });

    expect(mockClassList.contains('was-validated')).toBeTruthy();

    submitBtn.simulate('click', {
        currentTarget: {
            form: {
                checkValidity: () => true,
                classList: mockClassList
            }
        },
        preventDefault: jest.fn(),
        stopPropagation: jest.fn()
    });

    expect(mockClassList.contains('was-validated')).toBeFalsy();

FYI: I'm using Enzyme's shallow rendering for this, mount doesn't seem to work with this solution. However, I'm inclined to think that the other suggestion for mocking methods which are not implemented in JSDOM, when the method is executed directly in the tested file statement might just work with mount rendering

I have problem with input.validity which isn't available for me in j[email protected].

// EditableInput.js:
this.inputElement = React.createRef();
...
const input = this.inputElement.current;
console.error('input.validity: ', input.validity);
// EditableInput.test.js run outcome:
    console.error src/components/Form/Input/EditableInput.js:47
      input.validity:  undefined
// yarn.lock:
jsdom@^11.5.1:
  version "11.12.0"
  resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-11.12.0.tgz#1a80d40ddd378a1de59656e9e6dc5a3ba8657bc8"
  integrity sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==

I see that version this should've been fixed is 11.8.0 as it was released the day PR with validity inteface were merged. Am I missing something?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

vsemozhetbyt picture vsemozhetbyt  ·  4Comments

Progyan1997 picture Progyan1997  ·  3Comments

philipwalton picture philipwalton  ·  4Comments

khalyomede picture khalyomede  ·  3Comments

amfio picture amfio  ·  3Comments