Search code examples
reactjsmocha.jsonsen-ui

react onsenui mocha test error "Error: Invalid state"


After I update react-onsenui from 0.7.3 to 1.0.0 . I use mocha to test my webapp. error occured like this:

Error: Invalid state
at /Users/*****/node_modules/onsenui/js/onsenui.js:581:11

onsenui.js' code is like this:

throw new Error('Invalid state');

Solution

  • How are you running your Mocha tests? I saw this error using Mocha with JSDOM. It's just one of the many errors that occur because OnsenUI expects a real browser environment, and JSDOM is not one.

    My approach has been to stub everything that OnsenUI needs in the browser.js file where I define my DOM.

    The code called at line 581 is looking for the key 'transitionDuration' in the results of window.getComputedStyle(document.documentElement, ''), and errors when it doesn't find it. I added this to my browser.js file:

    //** Polyfill for values missing from JSDOM
    window.getComputedStyle = function(el1, el2) {
      return [
       "transitionDuration"
      ]
    }
    

    This resolved this specific error, but there were a lot more.

    Read on for the gory details of getting Mocha tests to run with OnsenUI components

    The JSDOM window element's properties weren't available to as global variables for OnsenUI, so I saw a lot of errors like Element is not defined, Node is not defined and so on. I solved these by either matching them to the window property if it existed, or stubbing empty functions, like this:

    // browser.js
    global['Element'] = window.Element;
    global['HTMLElement'] = window.HTMLElement;
    global['WebComponents'] = function() {};
    global['Node'] = window.Node;
    global['Window'] = window;
    global['Viewport'] = function() { return { setup: function() {} } }
    

    This was still not enough to get it running. To resolve errors relating to web components and custom elements, I installed document-register-element, and imported it at the top of my tests. I also needed to import the MutationObserver from https://github.com/megawac/MutationObserver.js, like this:

    //shims.js
    import './shims/mutationobserver';
    global['MutationObserver'] = window.MutationObserver;
    

    In the end my test files look like this:

    import 'babel-polyfill'
    import React from 'react';
    import { mount, shallow } from 'enzyme';
    import {expect} from 'chai';
    import document from './helpers/browser';
    import './helpers/shims';
    import 'document-register-element';
    
    import Frame from '../react-app/components/frame';
    
    describe('<Frame />', function () {
      it('renders without problems', function () {
        var wrapper = shallow(<Frame />);
        expect(wrapper.find('iframe')).to.have.length(1);
      }); 
    });
    

    Here's the full text of browser.js:

     import { jsdom } from 'jsdom';
    
     //** Create a fake DOM to add the tests to
     const document = jsdom('<!doctype html><html><body></body></html>');
     const window = document.defaultView;
    
     //** Push the window object properties to the Mocha global object- no idea why it doesn't work for all of the properties
     Object.keys(window).forEach((key) => {
       if (!(key in global)) {
         global[key] = window[key];
       }
     });
    
     //** These ones need to be done manually
     global['Element'] = window.Element;
     global['HTMLElement'] = window.HTMLElement;
     global['WebComponents'] = function() {};
     global['Node'] = window.Node;
     global['Window'] = window;
     global['Viewport'] = function() { return { setup: function() {} } }
    
     //** Polyfill for values missing from JSDOM
     window.getComputedStyle = function(el1, el2) {
       return [
         "transitionDuration"
       ]
     }
    
     global.document = document;
     global.window = window;