/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails react-core
*/
'use strict';
// Fix JSDOM. setAttribute is supposed to throw on things that can't be implicitly toStringed.
const setAttribute = Element.prototype.setAttribute;
Element.prototype.setAttribute = function (name, value) {
// eslint-disable-next-line react-internal/safe-string-coercion
return setAttribute.call(this, name, '' + value);
};
describe('ReactDOMSelect', () => {
let React;
let ReactDOM;
let ReactDOMServer;
let ReactTestUtils;
const noop = function () {};
beforeEach(() => {
jest.resetModules();
React = require('react');
ReactDOM = require('react-dom');
ReactDOMServer = require('react-dom/server');
ReactTestUtils = require('react-dom/test-utils');
});
it('should allow setting `defaultValue`', () => {
const stub = (
<select defaultValue="giraffe">
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.value).toBe('giraffe');
// Changing `defaultValue` should do nothing.
ReactDOM.render(
<select defaultValue="gorilla">{options}</select>,
container,
);
expect(node.value).toEqual('giraffe');
});
it('should not throw with `defaultValue` and without children', () => {
const stub = <select defaultValue="dummy" />;
expect(() => {
ReactTestUtils.renderIntoDocument(stub);
}).not.toThrow();
});
it('should not control when using `defaultValue`', () => {
const el = (
<select defaultValue="giraffe">
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const container = document.createElement('div');
const node = ReactDOM.render(el, container);
expect(node.value).toBe('giraffe');
node.value = 'monkey';
ReactDOM.render(el, container);
// Uncontrolled selects shouldn't change the value after first mounting
expect(node.value).toEqual('monkey');
});
it('should allow setting `defaultValue` with multiple', () => {
const stub = (
<select multiple={true} defaultValue={['giraffe', 'gorilla']}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(true); // gorilla
// Changing `defaultValue` should do nothing.
ReactDOM.render(
<select multiple={true} defaultValue={['monkey']}>
{options}
</select>,
container,
);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(true); // gorilla
});
it('should allow setting `value`', () => {
const stub = (
<select value="giraffe" onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.value).toBe('giraffe');
// Changing the `value` prop should change the selected option.
ReactDOM.render(
<select value="gorilla" onChange={noop}>
{options}
</select>,
container,
);
expect(node.value).toEqual('gorilla');
});
it('should default to the first non-disabled option', () => {
const stub = (
<select defaultValue="">
<option disabled={true}>Disabled</option>
<option disabled={true}>Still Disabled</option>
<option>0</option>
<option disabled={true}>Also Disabled</option>
</select>
);
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false);
expect(node.options[2].selected).toBe(true);
});
it('should allow setting `value` to __proto__', () => {
const stub = (
<select value="__proto__" onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="__proto__">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.value).toBe('__proto__');
// Changing the `value` prop should change the selected option.
ReactDOM.render(
<select value="gorilla" onChange={noop}>
{options}
</select>,
container,
);
expect(node.value).toEqual('gorilla');
});
it('should not throw with `value` and without children', () => {
const stub = <select value="dummy" onChange={noop} />;
expect(() => {
ReactTestUtils.renderIntoDocument(stub);
}).not.toThrow();
});
it('should allow setting `value` with multiple', () => {
const stub = (
<select multiple={true} value={['giraffe', 'gorilla']} onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(true); // gorilla
// Changing the `value` prop should change the selected options.
ReactDOM.render(
<select multiple={true} value={['monkey']} onChange={noop}>
{options}
</select>,
container,
);
expect(node.options[0].selected).toBe(true); // monkey
expect(node.options[1].selected).toBe(false); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
});
it('should allow setting `value` to __proto__ with multiple', () => {
const stub = (
<select multiple={true} value={['__proto__', 'gorilla']} onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="__proto__">A __proto__!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // __proto__
expect(node.options[2].selected).toBe(true); // gorilla
// Changing the `value` prop should change the selected options.
ReactDOM.render(
<select multiple={true} value={['monkey']} onChange={noop}>
{options}
</select>,
container,
);
expect(node.options[0].selected).toBe(true); // monkey
expect(node.options[1].selected).toBe(false); // __proto__
expect(node.options[2].selected).toBe(false); // gorilla
});
it('should not select other options automatically', () => {
const stub = (
<select multiple={true} value={['12']} onChange={noop}>
<option value="1">one</option>
<option value="2">two</option>
<option value="12">twelve</option>
</select>
);
const node = ReactTestUtils.renderIntoDocument(stub);
expect(node.options[0].selected).toBe(false); // one
expect(node.options[1].selected).toBe(false); // two
expect(node.options[2].selected).toBe(true); // twelve
});
it('should reset child options selected when they are changed and `value` is set', () => {
const stub = <select multiple={true} value={['a', 'b']} onChange={noop} />;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
ReactDOM.render(
<select multiple={true} value={['a', 'b']} onChange={noop}>
<option value="a">a</option>
<option value="b">b</option>
<option value="c">c</option>
</select>,
container,
);
expect(node.options[0].selected).toBe(true); // a
expect(node.options[1].selected).toBe(true); // b
expect(node.options[2].selected).toBe(false); // c
});
it('should allow setting `value` with `objectToString`', () => {
const objectToString = {
animal: 'giraffe',
toString: function () {
return this.animal;
},
};
const el = (
<select multiple={true} value={[objectToString]} onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const container = document.createElement('div');
const node = ReactDOM.render(el, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
// Changing the `value` prop should change the selected options.
objectToString.animal = 'monkey';
const el2 = (
<select multiple={true} value={[objectToString]}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
ReactDOM.render(el2, container);
expect(node.options[0].selected).toBe(true); // monkey
expect(node.options[1].selected).toBe(false); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
});
it('should allow switching to multiple', () => {
const stub = (
<select defaultValue="giraffe">
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
// When making it multiple, giraffe and gorilla should be selected
ReactDOM.render(
<select multiple={true} defaultValue={['giraffe', 'gorilla']}>
{options}
</select>,
container,
);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(true); // gorilla
});
it('should allow switching from multiple', () => {
const stub = (
<select multiple={true} defaultValue={['giraffe', 'gorilla']}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(true); // gorilla
// When removing multiple, defaultValue is applied again, being omitted
// means that "monkey" will be selected
ReactDOM.render(
<select defaultValue="gorilla">{options}</select>,
container,
);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(false); // giraffe
expect(node.options[2].selected).toBe(true); // gorilla
});
it('does not select an item when size is initially set to greater than 1', () => {
const stub = (
<select size="2">
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const container = document.createElement('div');
const select = ReactDOM.render(stub, container);
expect(select.options[0].selected).toBe(false);
expect(select.options[1].selected).toBe(false);
expect(select.options[2].selected).toBe(false);
expect(select.value).toBe('');
expect(select.selectedIndex).toBe(-1);
});
it('should remember value when switching to uncontrolled', () => {
const stub = (
<select value={'giraffe'} onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
ReactDOM.render(<select>{options}</select>, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
});
it('should remember updated value when switching to uncontrolled', () => {
const stub = (
<select value={'giraffe'} onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const options = stub.props.children;
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
ReactDOM.render(
<select value="gorilla" onChange={noop}>
{options}
</select>,
container,
);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(false); // giraffe
expect(node.options[2].selected).toBe(true); // gorilla
ReactDOM.render(<select>{options}</select>, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(false); // giraffe
expect(node.options[2].selected).toBe(true); // gorilla
});
it('should support server-side rendering', () => {
const stub = (
<select value="giraffe" onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const container = document.createElement('div');
container.innerHTML = ReactDOMServer.renderToString(stub);
const options = container.firstChild.options;
expect(options[0].value).toBe('monkey');
expect(options[0].selected).toBe(false);
expect(options[1].value).toBe('giraffe');
expect(options[1].selected).toBe(true);
expect(options[2].value).toBe('gorilla');
expect(options[2].selected).toBe(false);
});
it('should support server-side rendering with defaultValue', () => {
const stub = (
<select defaultValue="giraffe">
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const container = document.createElement('div');
container.innerHTML = ReactDOMServer.renderToString(stub);
const options = container.firstChild.options;
expect(options[0].value).toBe('monkey');
expect(options[0].selected).toBe(false);
expect(options[1].value).toBe('giraffe');
expect(options[1].selected).toBe(true);
expect(options[2].value).toBe('gorilla');
expect(options[2].selected).toBe(false);
});
it('should support server-side rendering with dangerouslySetInnerHTML', () => {
const stub = (
<select defaultValue="giraffe">
<option
value="monkey"
dangerouslySetInnerHTML={{
__html: 'A monkey!',
}}>
{undefined}
</option>
<option
value="giraffe"
dangerouslySetInnerHTML={{
__html: 'A giraffe!',
}}>
{null}
</option>
<option
value="gorilla"
dangerouslySetInnerHTML={{
__html: 'A gorilla!',
}}
/>
</select>
);
const container = document.createElement('div');
container.innerHTML = ReactDOMServer.renderToString(stub);
const options = container.firstChild.options;
expect(options[0].value).toBe('monkey');
expect(options[0].selected).toBe(false);
expect(options[1].value).toBe('giraffe');
expect(options[1].selected).toBe(true);
expect(options[2].value).toBe('gorilla');
expect(options[2].selected).toBe(false);
});
it('should support server-side rendering with multiple', () => {
const stub = (
<select multiple={true} value={['giraffe', 'gorilla']} onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const container = document.createElement('div');
container.innerHTML = ReactDOMServer.renderToString(stub);
const options = container.firstChild.options;
expect(options[0].value).toBe('monkey');
expect(options[0].selected).toBe(false);
expect(options[1].value).toBe('giraffe');
expect(options[1].selected).toBe(true);
expect(options[2].value).toBe('gorilla');
expect(options[2].selected).toBe(true);
});
it('should not control defaultValue if re-adding options', () => {
const container = document.createElement('div');
const node = ReactDOM.render(
<select multiple={true} defaultValue={['giraffe']}>
<option key="monkey" value="monkey">
A monkey!
</option>
<option key="giraffe" value="giraffe">
A giraffe!
</option>
<option key="gorilla" value="gorilla">
A gorilla!
</option>
</select>,
container,
);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
ReactDOM.render(
<select multiple={true} defaultValue={['giraffe']}>
<option key="monkey" value="monkey">
A monkey!
</option>
<option key="gorilla" value="gorilla">
A gorilla!
</option>
</select>,
container,
);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(false); // gorilla
ReactDOM.render(
<select multiple={true} defaultValue={['giraffe']}>
<option key="monkey" value="monkey">
A monkey!
</option>
<option key="giraffe" value="giraffe">
A giraffe!
</option>
<option key="gorilla" value="gorilla">
A gorilla!
</option>
</select>,
container,
);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(false); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
});
it('should support options with dynamic children', () => {
const container = document.createElement('div');
let node;
function App({value}) {
return (
<select value={value} ref={n => (node = n)} onChange={noop}>
<option key="monkey" value="monkey">
A monkey {value === 'monkey' ? 'is chosen' : null}!
</option>
<option key="giraffe" value="giraffe">
A giraffe {value === 'giraffe' && 'is chosen'}!
</option>
<option key="gorilla" value="gorilla">
A gorilla {value === 'gorilla' && 'is chosen'}!
</option>
</select>
);
}
ReactDOM.render(<App value="monkey" />, container);
expect(node.options[0].selected).toBe(true); // monkey
expect(node.options[1].selected).toBe(false); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
ReactDOM.render(<App value="giraffe" />, container);
expect(node.options[0].selected).toBe(false); // monkey
expect(node.options[1].selected).toBe(true); // giraffe
expect(node.options[2].selected).toBe(false); // gorilla
});
it('should warn if value is null', () => {
expect(() =>
ReactTestUtils.renderIntoDocument(
<select value={null}>
<option value="test" />
</select>,
),
).toErrorDev(
'`value` prop on `select` should not be null. ' +
'Consider using an empty string to clear the component or `undefined` ' +
'for uncontrolled components.',
);
ReactTestUtils.renderIntoDocument(
<select value={null}>
<option value="test" />
</select>,
);
});
it('should warn if selected is set on <option>', () => {
function App() {
return (
<select>
<option selected={true} />
<option selected={true} />
</select>
);
}
expect(() => ReactTestUtils.renderIntoDocument(<App />)).toErrorDev(
'Use the `defaultValue` or `value` props on <select> instead of ' +
'setting `selected` on <option>.',
);
ReactTestUtils.renderIntoDocument(<App />);
});
it('should warn if value is null and multiple is true', () => {
expect(() =>
ReactTestUtils.renderIntoDocument(
<select value={null} multiple={true}>
<option value="test" />
</select>,
),
).toErrorDev(
'`value` prop on `select` should not be null. ' +
'Consider using an empty array when `multiple` is ' +
'set to `true` to clear the component or `undefined` ' +
'for uncontrolled components.',
);
ReactTestUtils.renderIntoDocument(
<select value={null} multiple={true}>
<option value="test" />
</select>,
);
});
it('should refresh state on change', () => {
const stub = (
<select value="giraffe" onChange={noop}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const container = document.createElement('div');
document.body.appendChild(container);
try {
const node = ReactDOM.render(stub, container);
node.dispatchEvent(
new Event('change', {bubbles: true, cancelable: false}),
);
expect(node.value).toBe('giraffe');
} finally {
document.body.removeChild(container);
}
});
it('should warn if value and defaultValue props are specified', () => {
expect(() =>
ReactTestUtils.renderIntoDocument(
<select value="giraffe" defaultValue="giraffe" readOnly={true}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>,
),
).toErrorDev(
'Select elements must be either controlled or uncontrolled ' +
'(specify either the value prop, or the defaultValue prop, but not ' +
'both). Decide between using a controlled or uncontrolled select ' +
'element and remove one of these props. More info: ' +
'https://reactjs.org/link/controlled-components',
);
ReactTestUtils.renderIntoDocument(
<select value="giraffe" defaultValue="giraffe" readOnly={true}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>,
);
});
it('should not warn about missing onChange in uncontrolled textareas', () => {
const container = document.createElement('div');
ReactDOM.render(<select />, container);
ReactDOM.unmountComponentAtNode(container);
ReactDOM.render(<select value={undefined} />, container);
});
it('should be able to safely remove select onChange', () => {
function changeView() {
ReactDOM.unmountComponentAtNode(container);
}
const container = document.createElement('div');
const stub = (
<select value="giraffe" onChange={changeView}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>
);
const node = ReactDOM.render(stub, container);
expect(() => ReactTestUtils.Simulate.change(node)).not.toThrow();
});
it('should select grandchild options nested inside an optgroup', () => {
const stub = (
<select value="b" onChange={noop}>
<optgroup label="group">
<option value="a">a</option>
<option value="b">b</option>
<option value="c">c</option>
</optgroup>
</select>
);
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false); // a
expect(node.options[1].selected).toBe(true); // b
expect(node.options[2].selected).toBe(false); // c
});
it('should allow controlling `value` in a nested render', () => {
let selectNode;
class Parent extends React.Component {
state = {
value: 'giraffe',
};
componentDidMount() {
this._renderNested();
}
componentDidUpdate() {
this._renderNested();
}
_handleChange(event) {
this.setState({value: event.target.value});
}
_renderNested() {
ReactDOM.render(
<select
onChange={this._handleChange.bind(this)}
ref={n => (selectNode = n)}
value={this.state.value}>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
<option value="gorilla">A gorilla!</option>
</select>,
this._nestingContainer,
);
}
render() {
return <div ref={n => (this._nestingContainer = n)} />;
}
}
const container = document.createElement('div');
document.body.appendChild(container);
ReactDOM.render(<Parent />, container);
expect(selectNode.value).toBe('giraffe');
selectNode.value = 'gorilla';
let nativeEvent = document.createEvent('Event');
nativeEvent.initEvent('input', true, true);
selectNode.dispatchEvent(nativeEvent);
expect(selectNode.value).toEqual('gorilla');
nativeEvent = document.createEvent('Event');
nativeEvent.initEvent('change', true, true);
selectNode.dispatchEvent(nativeEvent);
expect(selectNode.value).toEqual('gorilla');
document.body.removeChild(container);
});
it('should not select first option by default when multiple is set and no defaultValue is set', () => {
const stub = (
<select multiple={true} onChange={noop}>
<option value="a">a</option>
<option value="b">b</option>
<option value="c">c</option>
</select>
);
const container = document.createElement('div');
const node = ReactDOM.render(stub, container);
expect(node.options[0].selected).toBe(false); // a
expect(node.options[1].selected).toBe(false); // b
expect(node.options[2].selected).toBe(false); // c
});
describe('When given a Symbol value', () => {
it('treats initial Symbol value as missing', () => {
let node;
expect(() => {
node = ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={Symbol('foobar')}>
<option value={Symbol('foobar')}>A Symbol!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
}).toErrorDev('Invalid value for prop `value`');
expect(node.value).toBe('A Symbol!');
});
it('treats updated Symbol value as missing', () => {
let node;
expect(() => {
node = ReactTestUtils.renderIntoDocument(
<select onChange={noop} value="monkey">
<option value={Symbol('foobar')}>A Symbol!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
}).toErrorDev('Invalid value for prop `value`');
expect(node.value).toBe('monkey');
node = ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={Symbol('foobar')}>
<option value={Symbol('foobar')}>A Symbol!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
expect(node.value).toBe('A Symbol!');
});
it('treats initial Symbol defaultValue as an empty string', () => {
let node;
expect(() => {
node = ReactTestUtils.renderIntoDocument(
<select defaultValue={Symbol('foobar')}>
<option value={Symbol('foobar')}>A Symbol!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
}).toErrorDev('Invalid value for prop `value`');
expect(node.value).toBe('A Symbol!');
});
it('treats updated Symbol defaultValue as an empty string', () => {
let node;
expect(() => {
node = ReactTestUtils.renderIntoDocument(
<select defaultValue="monkey">
<option value={Symbol('foobar')}>A Symbol!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
}).toErrorDev('Invalid value for prop `value`');
expect(node.value).toBe('monkey');
node = ReactTestUtils.renderIntoDocument(
<select defaultValue={Symbol('foobar')}>
<option value={Symbol('foobar')}>A Symbol!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
expect(node.value).toBe('A Symbol!');
});
});
describe('When given a function value', () => {
it('treats initial function value as missing', () => {
let node;
expect(() => {
node = ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={() => {}}>
<option value={() => {}}>A function!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
}).toErrorDev('Invalid value for prop `value`');
expect(node.value).toBe('A function!');
});
it('treats initial function defaultValue as an empty string', () => {
let node;
expect(() => {
node = ReactTestUtils.renderIntoDocument(
<select defaultValue={() => {}}>
<option value={() => {}}>A function!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
}).toErrorDev('Invalid value for prop `value`');
expect(node.value).toBe('A function!');
});
it('treats updated function value as an empty string', () => {
let node;
expect(() => {
node = ReactTestUtils.renderIntoDocument(
<select onChange={noop} value="monkey">
<option value={() => {}}>A function!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
}).toErrorDev('Invalid value for prop `value`');
expect(node.value).toBe('monkey');
node = ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={() => {}}>
<option value={() => {}}>A function!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
expect(node.value).toBe('A function!');
});
it('treats updated function defaultValue as an empty string', () => {
let node;
expect(() => {
node = ReactTestUtils.renderIntoDocument(
<select defaultValue="monkey">
<option value={() => {}}>A function!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
}).toErrorDev('Invalid value for prop `value`');
expect(node.value).toBe('monkey');
node = ReactTestUtils.renderIntoDocument(
<select defaultValue={() => {}}>
<option value={() => {}}>A function!</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
expect(node.value).toBe('A function!');
});
});
describe('When given a Temporal.PlainDate-like value', () => {
class TemporalLike {
valueOf() {
// Throwing here is the behavior of ECMAScript "Temporal" date/time API.
// See https://tc39.es/proposal-temporal/docs/plaindate.html#valueOf
throw new TypeError('prod message');
}
toString() {
return '2020-01-01';
}
}
it('throws when given a Temporal.PlainDate-like value (select)', () => {
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={new TemporalLike()}>
<option value="2020-01-01">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.',
);
});
it('throws when given a Temporal.PlainDate-like value (option)', () => {
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value="2020-01-01">
<option value={new TemporalLike()}>
like a Temporal.PlainDate
</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.',
);
});
it('throws when given a Temporal.PlainDate-like value (both)', () => {
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={new TemporalLike()}>
<option value={new TemporalLike()}>
like a Temporal.PlainDate
</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.',
);
});
it('throws with updated Temporal.PlainDate-like value (select)', () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value="monkey">
<option value="2020-01-01">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={new TemporalLike()}>
<option value="2020-01-01">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.',
);
});
it('throws with updated Temporal.PlainDate-like value (option)', () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value="2020-01-01">
<option value="donkey">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value="2020-01-01">
<option value={new TemporalLike()}>
like a Temporal.PlainDate
</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.',
);
});
it('throws with updated Temporal.PlainDate-like value (both)', () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value="donkey">
<option value="donkey">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={new TemporalLike()}>
<option value={new TemporalLike()}>
like a Temporal.PlainDate
</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.',
);
});
it('throws when given a Temporal.PlainDate-like defaultValue (select)', () => {
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} defaultValue={new TemporalLike()}>
<option value="2020-01-01">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.',
);
});
it('throws when given a Temporal.PlainDate-like defaultValue (option)', () => {
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} defaultValue="2020-01-01">
<option value={new TemporalLike()}>
like a Temporal.PlainDate
</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.',
);
});
it('throws when given a Temporal.PlainDate-like value (both)', () => {
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} defaultValue={new TemporalLike()}>
<option value={new TemporalLike()}>
like a Temporal.PlainDate
</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.',
);
});
it('throws with updated Temporal.PlainDate-like defaultValue (select)', () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} defaultValue="monkey">
<option value="2020-01-01">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} defaultValue={new TemporalLike()}>
<option value="2020-01-01">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'Form field values (value, checked, defaultValue, or defaultChecked props)' +
' must be strings, not TemporalLike. ' +
'This value must be coerced to a string before using it here.',
);
});
it('throws with updated Temporal.PlainDate-like defaultValue (both)', () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} defaultValue="monkey">
<option value="donkey">like a Temporal.PlainDate</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
const test = () => {
ReactTestUtils.renderIntoDocument(
<select onChange={noop} value={new TemporalLike()}>
<option value={new TemporalLike()}>
like a Temporal.PlainDate
</option>
<option value="monkey">A monkey!</option>
<option value="giraffe">A giraffe!</option>
</select>,
);
};
expect(() =>
expect(test).toThrowError(new TypeError('prod message')),
).toErrorDev(
'The provided `value` attribute is an unsupported type TemporalLike.' +
' This value must be coerced to a string before using it here.',
);
});
});
});