It seems problematic to allow arbitary non-lowercase attribute on HTML nodes. Because the server has no ability to keep them case sensitive (HTML isn't). So they will end up different on the client.

We should probably warn if the attribute is not a known one *and* is not lowercase.
describe('Custom attributes', function() {
    it('allows assignment of custom attributes with string values', function() {
      var el = ReactTestUtils.renderIntoDocument(<div whatever="30" />);
      expect(el.getAttribute('whatever')).toBe('30');
    });
    it('removes custom attributes', function() {
      const container = document.createElement('div');
      ReactDOM.render(<div whatever="30" />, container);
      expect(container.firstChild.getAttribute('whatever')).toBe('30');
      ReactDOM.render(<div whatever={null} />, container);
      expect(container.firstChild.hasAttribute('whatever')).toBe(false);
    });
    it('does not assign a boolean custom attributes as a string', function() {
      spyOn(console, 'error');
      var el = ReactTestUtils.renderIntoDocument(<div whatever={true} />);
      expect(el.hasAttribute('whatever')).toBe(false);
      expectDev(console.error.calls.argsFor(0)[0]).toContain(
        'Warning: Received `true` for non-boolean attribute `whatever`',
      );
    });
    it('does not assign an implicit boolean custom attributes', function() {
      spyOn(console, 'error');
      
      var el = ReactTestUtils.renderIntoDocument(<div whatever />);
      expect(el.hasAttribute('whatever')).toBe(false);
      expectDev(console.error.calls.argsFor(0)[0]).toContain(
        'Warning: Received `true` for non-boolean attribute `whatever`',
      );
    });
    it('assigns a numeric custom attributes as a string', function() {