ten1seven / what-input

A global utility for tracking the current input method (mouse/pointer, keyboard or touch).
https://ten1seven.github.io/what-input
MIT License
1.35k stars 89 forks source link

Error with Mocha and JSDOM when including what-input as a dependency #53

Closed marcysutton closed 7 years ago

marcysutton commented 7 years ago

I just added What-Input to our codebase for styling focus, and it worked beautifully–by using require, it extended feature support to 2 separate browser extensions quite easily. However, our Mocha tests are now blowing up:

Mocha exploded!
>> ReferenceError: document is not defined
>>     at /Users/marcysutton/Sites/Deque/axe-devtools-react/node_modules/what-input/dist/what-input.js:71:15
>>     at Object.<anonymous> (/Users/marcysutton/Sites/Deque/axe-devtools-react/node_modules/what-input/dist/what-input.js:333:3)
>>     at _webpack_require_ (/Users/marcysutton/Sites/Deque/axe-devtools-react/node_modules/what-input/dist/what-input.js:36:30)
>>     at module.exports.document (/Users/marcysutton/Sites/Deque/axe-devtools-react/node_modules/what-input/dist/what-input.js:56:18)
>>     at /Users/marcysutton/Sites/Deque/axe-devtools-react/node_modules/what-input/dist/what-input.js:59:10
>>     at webpackUniversalModuleDefinition (/Users/marcysutton/Sites/Deque/axe-devtools-react/node_modules/what-input/dist/what-input.js:9:20)
>>     at Object.<anonymous> (/Users/marcysutton/Sites/Deque/axe-devtools-react/node_modules/what-input/dist/what-input.js:16:3)

I've run into this problem a bunch with browser API scripts assembled with Node.js. In axe-core, we shim an IIFE with the window object, which keeps Node from complaining. Perhaps something similar could be done here?

Part 1: https://github.com/dequelabs/axe-core/blob/master/lib/intro.stub#L12 Part 2: https://github.com/dequelabs/axe-core/blob/master/lib/outro.stub#L2

Has anyone else run into this issue?

ten1seven commented 7 years ago

Hi @marcysutton! Possibly related to #51 ? I was about to look into that.

marcysutton commented 7 years ago

Hmm, hard to say with no error message. I was able to include the library in our React app via require just fine–my issue is that the module loader bubbles up a Node.js error when it tries including a browser-specific JavaScript file.

ten1seven commented 7 years ago

Gotcha. I'll take a look.

ten1seven commented 7 years ago

I tried out enclosing window as you suggested. Can you try out the version in this branch and see if it works?

marcysutton commented 7 years ago

Sadly the fix didn't prevent our test suite from blowing up...I think possibly because the module is hoisted with require and then instantiated right away before we shim objects with JSDOM. If we could require it without instantiating and then pass in a window object, that might fix the timing issue. The window object is defined in our build and the test suite, but in the latter for some reason What Input is being called before our JSDOM logic–despite being called in the correct order. Hence my theory about hoisting. I'll keep playing with it, but that's what I know now!

ten1seven commented 7 years ago

Sounds good! I'm happy to send more variations your way and, of course, am open to getting a PR 😉

marcysutton commented 7 years ago

Whew, I was able to finally get around it by reconfiguring our test suite. What Input is good as-is, so I'll close this issue! I was resistant to suggesting more changes since your library worked so well on the client-side, even when required/imported. I've included the solution for JSDOM and Mocha below if anyone else runs into it.

Add a test helper file to your build, and tell Mocha about it with the require option (after babel-register if you want to use ES6 import in the helper). Here's how I did it in our Grunt mocha task:

mochaTest: {
      test: {
        options: {
          reporter: 'spec',
          require: ['babel-register', 'src/jsdom-setup.js']
        },
        src: ['src/**/*.test.js']
      }
    }

Here's our test helper; you might not need ALL of the jsdomSetup if you start with jsdom-global/register. We have some extra code included for creating browser extensions.

import { jsdom } from 'jsdom'
import 'jsdom-global/register'
import { frameMessenger } from './utils'

export function jsdomSetup () {
  if (global.document && global.window) {
    global.document = jsdom('<!doctype html><html><body></body></html>')
    global.window = global.document.defaultView
    return
  }

  // Set up a basic DOM
  global.document = jsdom('<!doctype html><html><body></body></html>')

  // set the window object
  global.window = global.document.defaultView

  // Make all window properties available on the mocha global
  Object.keys(global.window)
  .filter(key => !(key in global))
  .forEach(key => {
    global[key] = global.window[key]
  })

  // Always connect frameMessenger
  frameMessenger.connect(global.window, 'your-plugin')
}

Hope that helps somebody!

ten1seven commented 7 years ago

This is great! Thanks for the thorough followup.