mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
ReactDOMComponent maintains a copy of the previous style object to support in-place mutations of props.style. This cached object was not cleared when the style property was removed in a props update. Fixes #3409
657 lines
22 KiB
JavaScript
657 lines
22 KiB
JavaScript
/**
|
|
* Copyright 2013-2015, Facebook, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This source code is licensed under the BSD-style license found in the
|
|
* LICENSE file in the root directory of this source tree. An additional grant
|
|
* of patent rights can be found in the PATENTS file in the same directory.
|
|
*
|
|
* @emails react-core
|
|
*/
|
|
|
|
/*jslint evil: true */
|
|
|
|
'use strict';
|
|
|
|
var assign = require('Object.assign');
|
|
var mocks = require('mocks');
|
|
|
|
describe('ReactDOMComponent', function() {
|
|
|
|
describe('updateDOM', function() {
|
|
var React;
|
|
var ReactTestUtils;
|
|
|
|
beforeEach(function() {
|
|
React = require('React');
|
|
ReactTestUtils = require('ReactTestUtils');
|
|
});
|
|
|
|
it("should handle className", function() {
|
|
var container = document.createElement('div');
|
|
React.render(<div style={{}} />, container);
|
|
|
|
React.render(<div className={'foo'} />, container);
|
|
expect(container.firstChild.className).toEqual('foo');
|
|
React.render(<div className={'bar'} />, container);
|
|
expect(container.firstChild.className).toEqual('bar');
|
|
React.render(<div className={null} />, container);
|
|
expect(container.firstChild.className).toEqual('');
|
|
});
|
|
|
|
it("should gracefully handle various style value types", function() {
|
|
var container = document.createElement('div');
|
|
React.render(<div style={{}} />, container);
|
|
var stubStyle = container.firstChild.style;
|
|
|
|
// set initial style
|
|
var setup = {display: 'block', left: '1', top: 2, fontFamily: 'Arial'};
|
|
React.render(<div style={setup} />, container);
|
|
expect(stubStyle.display).toEqual('block');
|
|
expect(stubStyle.left).toEqual('1px');
|
|
expect(stubStyle.fontFamily).toEqual('Arial');
|
|
|
|
// reset the style to their default state
|
|
var reset = {display: '', left: null, top: false, fontFamily: true};
|
|
React.render(<div style={reset} />, container);
|
|
expect(stubStyle.display).toEqual('');
|
|
expect(stubStyle.left).toEqual('');
|
|
expect(stubStyle.top).toEqual('');
|
|
expect(stubStyle.fontFamily).toEqual('');
|
|
});
|
|
|
|
it("should update styles when mutating style object", function() {
|
|
var styles = {display: 'none', fontFamily: 'Arial', lineHeight: 1.2};
|
|
var container = document.createElement('div');
|
|
React.render(<div style={styles} />, container);
|
|
|
|
var stubStyle = container.firstChild.style;
|
|
stubStyle.display = styles.display;
|
|
stubStyle.fontFamily = styles.fontFamily;
|
|
|
|
styles.display = 'block';
|
|
|
|
React.render(<div style={styles} />, container);
|
|
expect(stubStyle.display).toEqual('block');
|
|
expect(stubStyle.fontFamily).toEqual('Arial');
|
|
expect(stubStyle.lineHeight).toEqual('1.2');
|
|
|
|
styles.fontFamily = 'Helvetica';
|
|
|
|
React.render(<div style={styles} />, container);
|
|
expect(stubStyle.display).toEqual('block');
|
|
expect(stubStyle.fontFamily).toEqual('Helvetica');
|
|
expect(stubStyle.lineHeight).toEqual('1.2');
|
|
|
|
styles.lineHeight = 0.5;
|
|
|
|
React.render(<div style={styles} />, container);
|
|
expect(stubStyle.display).toEqual('block');
|
|
expect(stubStyle.fontFamily).toEqual('Helvetica');
|
|
expect(stubStyle.lineHeight).toEqual('0.5');
|
|
|
|
React.render(<div style={undefined} />, container);
|
|
expect(stubStyle.display).toBe('');
|
|
expect(stubStyle.fontFamily).toBe('');
|
|
expect(stubStyle.lineHeight).toBe('');
|
|
});
|
|
|
|
it("should update styles if initially null", function() {
|
|
var styles = null;
|
|
var container = document.createElement('div');
|
|
React.render(<div style={styles} />, container);
|
|
|
|
var stubStyle = container.firstChild.style;
|
|
|
|
styles = {display: 'block'};
|
|
|
|
React.render(<div style={styles} />, container);
|
|
expect(stubStyle.display).toEqual('block');
|
|
});
|
|
|
|
it("should remove attributes", function() {
|
|
var container = document.createElement('div');
|
|
React.render(<img height='17' />, container);
|
|
|
|
expect(container.firstChild.hasAttribute('height')).toBe(true);
|
|
React.render(<img />, container);
|
|
expect(container.firstChild.hasAttribute('height')).toBe(false);
|
|
});
|
|
|
|
it("should remove properties", function() {
|
|
var container = document.createElement('div');
|
|
React.render(<div className='monkey' />, container);
|
|
|
|
expect(container.firstChild.className).toEqual('monkey');
|
|
React.render(<div />, container);
|
|
expect(container.firstChild.className).toEqual('');
|
|
});
|
|
|
|
it("should clear a single style prop when changing 'style'", function() {
|
|
var styles = {display: 'none', color: 'red'};
|
|
var container = document.createElement('div');
|
|
React.render(<div style={styles} />, container);
|
|
|
|
var stubStyle = container.firstChild.style;
|
|
|
|
styles = {color: 'green'};
|
|
React.render(<div style={styles} />, container);
|
|
expect(stubStyle.display).toEqual('');
|
|
expect(stubStyle.color).toEqual('green');
|
|
});
|
|
|
|
it("should clear all the styles when removing 'style'", function() {
|
|
var styles = {display: 'none', color: 'red'};
|
|
var container = document.createElement('div');
|
|
React.render(<div style={styles} />, container);
|
|
|
|
var stubStyle = container.firstChild.style;
|
|
|
|
React.render(<div />, container);
|
|
expect(stubStyle.display).toEqual('');
|
|
expect(stubStyle.color).toEqual('');
|
|
});
|
|
|
|
it("should update styles when 'style' changes from null to object", function() {
|
|
var container = document.createElement('div');
|
|
var styles = {color: 'red'};
|
|
React.render(<div style={styles} />, container);
|
|
React.render(<div />, container);
|
|
React.render(<div style={styles} />, container);
|
|
|
|
var stubStyle = container.firstChild.style;
|
|
expect(stubStyle.color).toEqual('red');
|
|
});
|
|
|
|
it("should empty element when removing innerHTML", function() {
|
|
var container = document.createElement('div');
|
|
React.render(<div dangerouslySetInnerHTML={{__html: ':)'}} />, container);
|
|
|
|
expect(container.firstChild.innerHTML).toEqual(':)');
|
|
React.render(<div />, container);
|
|
expect(container.firstChild.innerHTML).toEqual('');
|
|
});
|
|
|
|
it("should transition from string content to innerHTML", function() {
|
|
var container = document.createElement('div');
|
|
React.render(<div>hello</div>, container);
|
|
|
|
expect(container.firstChild.innerHTML).toEqual('hello');
|
|
React.render(
|
|
<div dangerouslySetInnerHTML={{__html: 'goodbye'}} />,
|
|
container
|
|
);
|
|
expect(container.firstChild.innerHTML).toEqual('goodbye');
|
|
});
|
|
|
|
it("should transition from innerHTML to string content", function() {
|
|
var container = document.createElement('div');
|
|
React.render(
|
|
<div dangerouslySetInnerHTML={{__html: 'bonjour'}} />,
|
|
container
|
|
);
|
|
|
|
expect(container.firstChild.innerHTML).toEqual('bonjour');
|
|
React.render(<div>adieu</div>, container);
|
|
expect(container.firstChild.innerHTML).toEqual('adieu');
|
|
});
|
|
|
|
it("should not incur unnecessary DOM mutations", function() {
|
|
var container = document.createElement('div');
|
|
React.render(<div value="" />, container);
|
|
|
|
var node = container.firstChild;
|
|
var nodeValue = ''; // node.value always returns undefined
|
|
var nodeValueSetter = mocks.getMockFunction();
|
|
Object.defineProperty(node, 'value', {
|
|
get: function() {
|
|
return nodeValue;
|
|
},
|
|
set: nodeValueSetter.mockImplementation(function(newValue) {
|
|
nodeValue = newValue;
|
|
})
|
|
});
|
|
|
|
React.render(<div value="" />, container);
|
|
expect(nodeValueSetter.mock.calls.length).toBe(0);
|
|
|
|
React.render(<div />, container);
|
|
expect(nodeValueSetter.mock.calls.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('createOpenTagMarkup', function() {
|
|
var genMarkup;
|
|
|
|
function quoteRegexp(str) {
|
|
return (str + '').replace(/([.?*+\^$\[\]\\(){}|-])/g, "\\$1");
|
|
}
|
|
|
|
beforeEach(function() {
|
|
require('mock-modules').dumpCache();
|
|
|
|
var ReactDefaultInjection = require('ReactDefaultInjection');
|
|
ReactDefaultInjection.inject();
|
|
|
|
var ReactDOMComponent = require('ReactDOMComponent');
|
|
var ReactReconcileTransaction = require('ReactReconcileTransaction');
|
|
|
|
var NodeStub = function(initialProps) {
|
|
this._currentElement = {props: initialProps};
|
|
this._rootNodeID = 'test';
|
|
};
|
|
assign(NodeStub.prototype, ReactDOMComponent.Mixin);
|
|
|
|
genMarkup = function(props) {
|
|
var transaction = new ReactReconcileTransaction();
|
|
return (new NodeStub(props))._createOpenTagMarkupAndPutListeners(
|
|
transaction
|
|
);
|
|
};
|
|
|
|
this.addMatchers({
|
|
toHaveAttribute: function(attr, value) {
|
|
var expected = '(?:^|\\s)' + attr + '=[\\\'"]';
|
|
if (typeof value != 'undefined') {
|
|
expected += quoteRegexp(value) + '[\\\'"]';
|
|
}
|
|
return this.actual.match(new RegExp(expected));
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should generate the correct markup with className", function() {
|
|
expect(genMarkup({className: 'a'})).toHaveAttribute('class', 'a');
|
|
expect(genMarkup({className: 'a b'})).toHaveAttribute('class', 'a b');
|
|
expect(genMarkup({className: ''})).toHaveAttribute('class', '');
|
|
});
|
|
|
|
it("should escape style names and values", function() {
|
|
expect(genMarkup({
|
|
style: {'b&ckground': '<3'}
|
|
})).toHaveAttribute('style', 'b&ckground:<3;');
|
|
});
|
|
});
|
|
|
|
describe('createContentMarkup', function() {
|
|
var genMarkup;
|
|
|
|
function quoteRegexp(str) {
|
|
return (str + '').replace(/([.?*+\^$\[\]\\(){}|-])/g, "\\$1");
|
|
}
|
|
|
|
beforeEach(function() {
|
|
require('mock-modules').dumpCache();
|
|
|
|
var ReactDOMComponent = require('ReactDOMComponent');
|
|
var ReactReconcileTransaction = require('ReactReconcileTransaction');
|
|
|
|
var NodeStub = function(initialProps) {
|
|
this._currentElement = {props: initialProps};
|
|
this._rootNodeID = 'test';
|
|
};
|
|
assign(NodeStub.prototype, ReactDOMComponent.Mixin);
|
|
|
|
genMarkup = function(props) {
|
|
var transaction = new ReactReconcileTransaction();
|
|
return (new NodeStub(props))._createContentMarkup(transaction, {});
|
|
};
|
|
|
|
this.addMatchers({
|
|
toHaveInnerhtml: function(html) {
|
|
var expected = '^' + quoteRegexp(html) + '$';
|
|
return this.actual.match(new RegExp(expected));
|
|
}
|
|
});
|
|
});
|
|
|
|
it("should handle dangerouslySetInnerHTML", function() {
|
|
var innerHTML = {__html: 'testContent'};
|
|
expect(
|
|
genMarkup({dangerouslySetInnerHTML: innerHTML})
|
|
).toHaveInnerhtml('testContent');
|
|
});
|
|
});
|
|
|
|
describe('mountComponent', function() {
|
|
var React;
|
|
var mountComponent;
|
|
|
|
beforeEach(function() {
|
|
require('mock-modules').dumpCache();
|
|
|
|
React = require('React');
|
|
var ReactMultiChild = require('ReactMultiChild');
|
|
var ReactDOMComponent = require('ReactDOMComponent');
|
|
var ReactReconcileTransaction = require('ReactReconcileTransaction');
|
|
|
|
var StubNativeComponent = function(element) {
|
|
this._currentElement = element;
|
|
};
|
|
assign(StubNativeComponent.prototype, ReactDOMComponent.Mixin);
|
|
assign(StubNativeComponent.prototype, ReactMultiChild.Mixin);
|
|
|
|
mountComponent = function(props) {
|
|
var transaction = new ReactReconcileTransaction();
|
|
var stubComponent = new StubNativeComponent({
|
|
type: StubNativeComponent,
|
|
props: props,
|
|
_owner: null,
|
|
_context: null
|
|
});
|
|
return stubComponent.mountComponent('test', transaction, {});
|
|
};
|
|
});
|
|
|
|
it("should warn against children for void elements", function() {
|
|
spyOn(console, 'warn');
|
|
|
|
var container = document.createElement('div');
|
|
|
|
React.render(<input>children</input>, container);
|
|
|
|
expect(console.warn.argsForCall.length).toBe(1);
|
|
expect(console.warn.argsForCall[0][0]).toContain('void element');
|
|
});
|
|
|
|
it("should warn against dangerouslySetInnerHTML for void elements", function() {
|
|
spyOn(console, 'warn');
|
|
|
|
var container = document.createElement('div');
|
|
|
|
React.render(
|
|
<input dangerouslySetInnerHTML={{__html: 'content'}} />,
|
|
container
|
|
);
|
|
|
|
expect(console.warn.argsForCall.length).toBe(1);
|
|
expect(console.warn.argsForCall[0][0]).toContain('void element');
|
|
});
|
|
|
|
it("should treat menuitem as a void element but still create the closing tag", function() {
|
|
spyOn(console, 'warn');
|
|
|
|
var container = document.createElement('div');
|
|
|
|
React.render(<menuitem />, container);
|
|
|
|
expect(container.innerHTML).toContain('</menuitem>');
|
|
|
|
React.render(<menuitem>children</menuitem>, container);
|
|
|
|
expect(console.warn.argsForCall.length).toBe(1);
|
|
expect(console.warn.argsForCall[0][0]).toContain('void element');
|
|
});
|
|
|
|
it("should validate against multiple children props", function() {
|
|
expect(function() {
|
|
mountComponent({children: '', dangerouslySetInnerHTML: ''});
|
|
}).toThrow(
|
|
'Invariant Violation: Can only set one of `children` or ' +
|
|
'`props.dangerouslySetInnerHTML`.'
|
|
);
|
|
});
|
|
|
|
it('should validate against use of innerHTML', function() {
|
|
|
|
spyOn(console, 'warn');
|
|
mountComponent({innerHTML: '<span>Hi Jim!</span>'});
|
|
expect(console.warn.argsForCall.length).toBe(1);
|
|
expect(console.warn.argsForCall[0][0]).toContain(
|
|
'Directly setting property `innerHTML` is not permitted. '
|
|
);
|
|
});
|
|
|
|
it('should validate use of dangerouslySetInnerHTML', function() {
|
|
expect(function() {
|
|
mountComponent({dangerouslySetInnerHTML: '<span>Hi Jim!</span>'});
|
|
}).toThrow(
|
|
'Invariant Violation: ' +
|
|
'`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
|
|
'Please visit http://fb.me/react-invariant-dangerously-set-inner-html for more information.'
|
|
);
|
|
});
|
|
|
|
it('should validate use of dangerouslySetInnerHTML', function() {
|
|
expect(function() {
|
|
mountComponent({dangerouslySetInnerHTML: {foo: 'bar'} });
|
|
}).toThrow(
|
|
'Invariant Violation: ' +
|
|
'`props.dangerouslySetInnerHTML` must be in the form `{__html: ...}`. ' +
|
|
'Please visit http://fb.me/react-invariant-dangerously-set-inner-html for more information.'
|
|
);
|
|
});
|
|
|
|
it("should warn about contentEditable and children", function() {
|
|
spyOn(console, 'warn');
|
|
mountComponent({contentEditable: true, children: ''});
|
|
expect(console.warn.argsForCall.length).toBe(1);
|
|
expect(console.warn.argsForCall[0][0]).toContain('contentEditable');
|
|
});
|
|
|
|
it("should validate against invalid styles", function() {
|
|
expect(function() {
|
|
mountComponent({style: 'display: none'});
|
|
}).toThrow(
|
|
'Invariant Violation: The `style` prop expects a mapping from style ' +
|
|
'properties to values, not a string. For example, ' +
|
|
'style={{marginRight: spacing + \'em\'}} when using JSX.'
|
|
);
|
|
});
|
|
|
|
it("should execute custom event plugin listening behavior", function() {
|
|
var React = require('React');
|
|
var SimpleEventPlugin = require('SimpleEventPlugin');
|
|
|
|
SimpleEventPlugin.didPutListener = mocks.getMockFunction();
|
|
SimpleEventPlugin.willDeleteListener = mocks.getMockFunction();
|
|
|
|
var container = document.createElement('div');
|
|
React.render(
|
|
<div onClick={() => true} />,
|
|
container
|
|
);
|
|
|
|
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(1);
|
|
|
|
React.unmountComponentAtNode(container);
|
|
|
|
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
|
|
});
|
|
|
|
it("should handle null and missing properly with event hooks", function() {
|
|
var React = require('React');
|
|
var SimpleEventPlugin = require('SimpleEventPlugin');
|
|
|
|
SimpleEventPlugin.didPutListener = mocks.getMockFunction();
|
|
SimpleEventPlugin.willDeleteListener = mocks.getMockFunction();
|
|
var container = document.createElement('div');
|
|
|
|
React.render(<div onClick={null} />, container);
|
|
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(0);
|
|
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(0);
|
|
|
|
React.render(<div onClick={() => 'apple'} />, container);
|
|
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(1);
|
|
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(0);
|
|
|
|
React.render(<div onClick={() => 'banana'} />, container);
|
|
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(2);
|
|
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(0);
|
|
|
|
React.render(<div onClick={null} />, container);
|
|
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(2);
|
|
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
|
|
|
|
React.render(<div />, container);
|
|
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(2);
|
|
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
|
|
|
|
React.unmountComponentAtNode(container);
|
|
expect(SimpleEventPlugin.didPutListener.mock.calls.length).toBe(2);
|
|
expect(SimpleEventPlugin.willDeleteListener.mock.calls.length).toBe(1);
|
|
});
|
|
});
|
|
|
|
describe('updateComponent', function() {
|
|
var React;
|
|
var container;
|
|
|
|
beforeEach(function() {
|
|
React = require('React');
|
|
container = document.createElement('div');
|
|
});
|
|
|
|
it("should warn against children for void elements", function() {
|
|
spyOn(console, 'warn');
|
|
|
|
React.render(<input />, container);
|
|
React.render(<input>children</input>, container);
|
|
|
|
expect(console.warn.argsForCall.length).toBe(1);
|
|
expect(console.warn.argsForCall[0][0]).toContain('void element');
|
|
});
|
|
|
|
it("should warn against dangerouslySetInnerHTML for void elements", function() {
|
|
spyOn(console, 'warn');
|
|
|
|
React.render(<input />, container);
|
|
React.render(
|
|
<input dangerouslySetInnerHTML={{__html: 'content'}} />,
|
|
container
|
|
);
|
|
|
|
expect(console.warn.argsForCall.length).toBe(1);
|
|
expect(console.warn.argsForCall[0][0]).toContain('void element');
|
|
});
|
|
|
|
it("should validate against multiple children props", function() {
|
|
React.render(<div></div>, container);
|
|
|
|
expect(function() {
|
|
React.render(
|
|
<div children="" dangerouslySetInnerHTML={{__html: ''}}></div>,
|
|
container
|
|
);
|
|
}).toThrow(
|
|
'Invariant Violation: Can only set one of `children` or ' +
|
|
'`props.dangerouslySetInnerHTML`.'
|
|
);
|
|
});
|
|
|
|
it("should warn about contentEditable and children", function() {
|
|
spyOn(console, 'warn');
|
|
React.render(
|
|
<div contentEditable><div /></div>,
|
|
container
|
|
);
|
|
expect(console.warn.argsForCall.length).toBe(1);
|
|
expect(console.warn.argsForCall[0][0]).toContain('contentEditable');
|
|
});
|
|
|
|
it("should validate against invalid styles", function() {
|
|
React.render(<div></div>, container);
|
|
|
|
expect(function() {
|
|
React.render(<div style={1}></div>, container);
|
|
}).toThrow(
|
|
'Invariant Violation: The `style` prop expects a mapping from style ' +
|
|
'properties to values, not a string. For example, ' +
|
|
'style={{marginRight: spacing + \'em\'}} when using JSX.'
|
|
);
|
|
});
|
|
|
|
it("should properly escape text content and attributes values", function() {
|
|
expect(
|
|
React.renderToStaticMarkup(
|
|
React.DOM.div({
|
|
title: '\'"<>&',
|
|
style: {
|
|
textAlign: '\'"<>&'
|
|
}
|
|
}, '\'"<>&')
|
|
)
|
|
).toBe(
|
|
'<div title="'"<>&" style="text-align:'"<>&;">' +
|
|
''"<>&' +
|
|
'</div>'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('unmountComponent', function() {
|
|
it("should clean up listeners", function() {
|
|
var React = require('React');
|
|
var ReactBrowserEventEmitter = require('ReactBrowserEventEmitter');
|
|
var ReactMount = require('ReactMount');
|
|
|
|
var container = document.createElement('div');
|
|
document.documentElement.appendChild(container);
|
|
|
|
var callback = function() {};
|
|
var instance = <div onClick={callback} />;
|
|
instance = React.render(instance, container);
|
|
|
|
var rootNode = React.findDOMNode(instance);
|
|
var rootNodeID = ReactMount.getID(rootNode);
|
|
expect(
|
|
ReactBrowserEventEmitter.getListener(rootNodeID, 'onClick')
|
|
).toBe(callback);
|
|
expect(rootNode).toBe(instance.getDOMNode());
|
|
|
|
React.unmountComponentAtNode(container);
|
|
|
|
expect(
|
|
ReactBrowserEventEmitter.getListener(rootNodeID, 'onClick')
|
|
).toBe(undefined);
|
|
});
|
|
});
|
|
|
|
describe('onScroll warning', function() {
|
|
it('should warn about the `onScroll` issue when unsupported (IE8)', () => {
|
|
// Mock this here so we can mimic IE8 support. We require isEventSupported
|
|
// before React so it's pre-mocked before React qould require it.
|
|
require('mock-modules')
|
|
.dumpCache()
|
|
.mock('isEventSupported');
|
|
var isEventSupported = require('isEventSupported');
|
|
isEventSupported.mockReturnValueOnce(false);
|
|
|
|
var React = require('React');
|
|
var ReactTestUtils = require('ReactTestUtils');
|
|
|
|
spyOn(console, 'warn');
|
|
ReactTestUtils.renderIntoDocument(<div onScroll={function() {}} />);
|
|
expect(console.warn.calls.length).toBe(1);
|
|
expect(console.warn.mostRecentCall.args[0]).toBe(
|
|
'Warning: This browser doesn\'t support the `onScroll` event'
|
|
);
|
|
});
|
|
});
|
|
|
|
describe('tag sanitization', function() {
|
|
it('should throw when an invalid tag name is used', () => {
|
|
var React = require('React');
|
|
var ReactTestUtils = require('ReactTestUtils');
|
|
var hackzor = React.createElement('script tag');
|
|
expect(
|
|
() => ReactTestUtils.renderIntoDocument(hackzor)
|
|
).toThrow(
|
|
'Invariant Violation: Invalid tag: script tag'
|
|
);
|
|
});
|
|
|
|
it('should throw when an attack vector is used', () => {
|
|
var React = require('React');
|
|
var ReactTestUtils = require('ReactTestUtils');
|
|
var hackzor = React.createElement('div><img /><div');
|
|
expect(
|
|
() => ReactTestUtils.renderIntoDocument(hackzor)
|
|
).toThrow(
|
|
'Invariant Violation: Invalid tag: div><img /><div'
|
|
);
|
|
});
|
|
|
|
});
|
|
});
|