mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
* warn if passive effects get queued outside of an act() call While the code itself isn't much (it adds the warning to mountEffect() and updateEffect() in ReactFiberHooks), it does change a lot of our tests. We follow a bad-ish pattern here, which is doing asserts inside act() scopes, but it makes sense for *us* because we're testing intermediate states, and we're manually flush/yield what we need in these tests. This commit has one last failing test. Working on it. * pass lint * pass failing test, fixes another - a test was failing in ReactDOMServerIntegrationHooks while testing an effect; the behaviour of yields was different from browser and server when wrapped with act(). further, because of how we initialized modules, act() around renders wasn't working corrrectly. solved by passing in ReactTestUtils in initModules, and checking on the finally yielded values in the specific test. - in ReactUpdates, while testing an infinite recursion detection, the test needed to be wrapped in an act(), which would have caused the recusrsion error to throw. solived by rethrowing the error from inside the act(). * pass ReactDOMServerSuspense * stray todo * a better message, consistent with the state update one.
254 lines
6.7 KiB
JavaScript
254 lines
6.7 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its 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';
|
|
|
|
const ReactDOMServerIntegrationUtils = require('./utils/ReactDOMServerIntegrationTestUtils');
|
|
|
|
let React;
|
|
let ReactDOM;
|
|
let ReactDOMServer;
|
|
let ReactTestUtils;
|
|
|
|
function initModules() {
|
|
// Reset warning cache.
|
|
jest.resetModuleRegistry();
|
|
React = require('react');
|
|
ReactDOM = require('react-dom');
|
|
ReactDOMServer = require('react-dom/server');
|
|
ReactTestUtils = require('react-dom/test-utils');
|
|
|
|
// Make them available to the helpers.
|
|
return {
|
|
ReactDOM,
|
|
ReactDOMServer,
|
|
ReactTestUtils,
|
|
};
|
|
}
|
|
|
|
const {
|
|
resetModules,
|
|
itRenders,
|
|
itThrowsWhenRendering,
|
|
} = ReactDOMServerIntegrationUtils(initModules);
|
|
|
|
describe('ReactDOMServerIntegrationSelect', () => {
|
|
let options;
|
|
beforeEach(() => {
|
|
resetModules();
|
|
|
|
options = [
|
|
<option key={1} value="foo" id="foo">
|
|
Foo
|
|
</option>,
|
|
<option key={2} value="bar" id="bar">
|
|
Bar
|
|
</option>,
|
|
<option key={3} value="baz" id="baz">
|
|
Baz
|
|
</option>,
|
|
];
|
|
});
|
|
|
|
// a helper function to test the selected value of a <select> element.
|
|
// takes in a <select> DOM element (element) and a value or array of
|
|
// values that should be selected (selected).
|
|
const expectSelectValue = (element, selected) => {
|
|
if (!Array.isArray(selected)) {
|
|
selected = [selected];
|
|
}
|
|
// the select DOM element shouldn't ever have a value or defaultValue
|
|
// attribute; that is not how select values are expressed in the DOM.
|
|
expect(element.getAttribute('value')).toBe(null);
|
|
expect(element.getAttribute('defaultValue')).toBe(null);
|
|
|
|
['foo', 'bar', 'baz'].forEach(value => {
|
|
const expectedValue = selected.indexOf(value) !== -1;
|
|
const option = element.querySelector(`#${value}`);
|
|
expect(option.selected).toBe(expectedValue);
|
|
});
|
|
};
|
|
|
|
itRenders('a select with a value and an onChange', async render => {
|
|
const e = await render(
|
|
<select value="bar" onChange={() => {}}>
|
|
{options}
|
|
</select>,
|
|
);
|
|
expectSelectValue(e, 'bar');
|
|
});
|
|
|
|
itRenders('a select with a value and readOnly', async render => {
|
|
const e = await render(
|
|
<select value="bar" readOnly={true}>
|
|
{options}
|
|
</select>,
|
|
);
|
|
expectSelectValue(e, 'bar');
|
|
});
|
|
|
|
itRenders('a select with a multiple values and an onChange', async render => {
|
|
const e = await render(
|
|
<select value={['bar', 'baz']} multiple={true} onChange={() => {}}>
|
|
{options}
|
|
</select>,
|
|
);
|
|
expectSelectValue(e, ['bar', 'baz']);
|
|
});
|
|
|
|
itRenders('a select with a multiple values and readOnly', async render => {
|
|
const e = await render(
|
|
<select value={['bar', 'baz']} multiple={true} readOnly={true}>
|
|
{options}
|
|
</select>,
|
|
);
|
|
expectSelectValue(e, ['bar', 'baz']);
|
|
});
|
|
|
|
itRenders('a select with a value and no onChange/readOnly', async render => {
|
|
// this configuration should raise a dev warning that value without
|
|
// onChange or readOnly is a mistake.
|
|
const e = await render(<select value="bar">{options}</select>, 1);
|
|
expectSelectValue(e, 'bar');
|
|
});
|
|
|
|
itRenders('a select with a defaultValue', async render => {
|
|
const e = await render(<select defaultValue="bar">{options}</select>);
|
|
expectSelectValue(e, 'bar');
|
|
});
|
|
|
|
itRenders('a select value overriding defaultValue', async render => {
|
|
const e = await render(
|
|
<select value="bar" defaultValue="baz" readOnly={true}>
|
|
{options}
|
|
</select>,
|
|
1,
|
|
);
|
|
expectSelectValue(e, 'bar');
|
|
});
|
|
|
|
itRenders(
|
|
'a select with options that use dangerouslySetInnerHTML',
|
|
async render => {
|
|
const e = await render(
|
|
<select defaultValue="baz" value="bar" readOnly={true}>
|
|
<option
|
|
id="foo"
|
|
value="foo"
|
|
dangerouslySetInnerHTML={{
|
|
__html: 'Foo',
|
|
}}>
|
|
{undefined}
|
|
</option>
|
|
<option
|
|
id="bar"
|
|
value="bar"
|
|
dangerouslySetInnerHTML={{
|
|
__html: 'Bar',
|
|
}}>
|
|
{null}
|
|
</option>
|
|
<option
|
|
id="baz"
|
|
value="baz"
|
|
dangerouslySetInnerHTML={{
|
|
__html: 'Baz',
|
|
}}
|
|
/>
|
|
</select>,
|
|
1,
|
|
);
|
|
expectSelectValue(e, 'bar');
|
|
},
|
|
);
|
|
|
|
itThrowsWhenRendering(
|
|
'a select with option that uses dangerouslySetInnerHTML and 0 as child',
|
|
async render => {
|
|
await render(
|
|
<select defaultValue="baz" value="foo" readOnly={true}>
|
|
<option
|
|
id="foo"
|
|
value="foo"
|
|
dangerouslySetInnerHTML={{
|
|
__html: 'Foo',
|
|
}}>
|
|
{0}
|
|
</option>
|
|
</select>,
|
|
1,
|
|
);
|
|
},
|
|
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
|
|
);
|
|
|
|
itThrowsWhenRendering(
|
|
'a select with option that uses dangerouslySetInnerHTML and empty string as child',
|
|
async render => {
|
|
await render(
|
|
<select defaultValue="baz" value="foo" readOnly={true}>
|
|
<option
|
|
id="foo"
|
|
value="foo"
|
|
dangerouslySetInnerHTML={{
|
|
__html: 'Foo',
|
|
}}>
|
|
{''}
|
|
</option>
|
|
</select>,
|
|
1,
|
|
);
|
|
},
|
|
'Can only set one of `children` or `props.dangerouslySetInnerHTML`.',
|
|
);
|
|
|
|
itRenders(
|
|
'a select value overriding defaultValue no matter the prop order',
|
|
async render => {
|
|
const e = await render(
|
|
<select defaultValue="baz" value="bar" readOnly={true}>
|
|
{options}
|
|
</select>,
|
|
1,
|
|
);
|
|
expectSelectValue(e, 'bar');
|
|
},
|
|
);
|
|
|
|
itRenders('a select option with flattened children', async render => {
|
|
const e = await render(
|
|
<select value="bar" readOnly={true}>
|
|
<option value="bar">A {'B'}</option>
|
|
</select>,
|
|
);
|
|
const option = e.options[0];
|
|
expect(option.childNodes.length).toBe(1);
|
|
expect(option.childNodes[0].nodeType).toBe(3);
|
|
expect(option.childNodes[0].nodeValue).toBe('A B');
|
|
});
|
|
|
|
itRenders(
|
|
'a select option with flattened children and a warning',
|
|
async render => {
|
|
const e = await render(
|
|
<select readOnly={true} value="bar">
|
|
<option value="bar">
|
|
{['Bar', false, 'Foo', <div key="1" />, 'Baz']}
|
|
</option>
|
|
</select>,
|
|
1,
|
|
);
|
|
expect(e.getAttribute('value')).toBe(null);
|
|
expect(e.getAttribute('defaultValue')).toBe(null);
|
|
expect(e.firstChild.innerHTML).toBe('BarFoo[object Object]Baz');
|
|
expect(e.firstChild.selected).toBe(true);
|
|
},
|
|
);
|
|
});
|