ReactES6Class-test.jsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. import PropTypes from '../../lib/ReactPropTypes';
  2. import ReactTestUtils from "../../lib/ReactTestUtils";
  3. import React from '../../src/May';
  4. import { render, unmountComponentAtNode, findDOMNode } from '../../src/may-dom/MayDom';
  5. import {shallowCompare} from '../../src/PureComponent';
  6. var ReactDOM = {
  7. render: render,
  8. unmountComponentAtNode: unmountComponentAtNode,
  9. findDOMNode: findDOMNode
  10. }
  11. React.render = render;
  12. // import React from "../../dist/ReactANU";
  13. // var ReactDOM = React;
  14. // var ReactTestUtils = {
  15. // renderIntoDocument: function (element) {
  16. // var div = document.createElement("div");
  17. // return React.render(element, div);
  18. // }
  19. // };
  20. //https://github.com/facebook/react/blob/master/src/isomorphic/children/__tests__/ReactChildren-test.js
  21. describe("ReactES6Class", function() {
  22. // this.timeout(200000);
  23. var container;
  24. var freeze = function(expectation) {
  25. Object.freeze(expectation);
  26. return expectation;
  27. };
  28. var Inner;
  29. var attachedListener = null;
  30. var renderedName = null;
  31. beforeEach(() => {
  32. container = document.createElement("div");
  33. attachedListener = null;
  34. renderedName = null;
  35. Inner = class extends React.Component {
  36. getName() {
  37. return this.props.name;
  38. }
  39. render() {
  40. attachedListener = this.props.onClick;
  41. renderedName = this.props.name;
  42. return <div className={this.props.name} />;
  43. }
  44. };
  45. });
  46. function test(element, expectedTag, expectedClassName) {
  47. var instance = ReactDOM.render(element, container);
  48. expect(container.firstChild).not.toBeNull();
  49. expect(container.firstChild.tagName).toBe(expectedTag);
  50. expect(container.firstChild.className).toBe(expectedClassName);
  51. return instance;
  52. }
  53. it("preserves the name of the class for use in error messages", () => {
  54. class Foo extends React.Component {}
  55. expect(Foo.name).toBe("Foo");
  56. });
  57. it("renders a simple stateless component with prop", () => {
  58. class Foo extends React.Component {
  59. render() {
  60. return <Inner name={this.props.bar} />;
  61. }
  62. }
  63. test(<Foo bar="foo" />, "DIV", "foo");
  64. test(<Foo bar="bar" />, "DIV", "bar");
  65. });
  66. it("renders based on state using initial values in this.props", () => {
  67. class Foo extends React.Component {
  68. constructor(props) {
  69. super(props);
  70. this.state = {bar: this.props.initialValue};
  71. }
  72. render() {
  73. return <span className={this.state.bar} />;
  74. }
  75. }
  76. test(<Foo initialValue="foo" />, "SPAN", "foo");
  77. });
  78. it("renders based on state using props in the constructor", () => {
  79. class Foo extends React.Component {
  80. constructor(props) {
  81. super(props);
  82. this.state = {bar: props.initialValue};
  83. }
  84. changeState() {
  85. this.setState({bar: "bar"});
  86. }
  87. render() {
  88. if (this.state.bar === "foo") {
  89. return <div className="foo" />;
  90. }
  91. return <span className={this.state.bar} />;
  92. }
  93. }
  94. var instance = test(<Foo initialValue="foo" />, "DIV", "foo");
  95. instance.changeState();
  96. test(<Foo />, "SPAN", "bar");
  97. });
  98. it("renders based on context in the constructor", () => {
  99. class Foo extends React.Component {
  100. constructor(props, context) {
  101. super(props, context);
  102. this.state = {tag: context.tag, className: this.context.className};
  103. }
  104. render() {
  105. var Tag = this.state.tag;
  106. return <Tag className={this.state.className} />;
  107. }
  108. }
  109. Foo.contextTypes = {
  110. tag: PropTypes.string,
  111. className: PropTypes.string,
  112. };
  113. class Outer extends React.Component {
  114. getChildContext() {
  115. return {tag: "span", className: "foo"};
  116. }
  117. render() {
  118. return <Foo />;
  119. }
  120. }
  121. Outer.childContextTypes = {
  122. tag: PropTypes.string,
  123. className: PropTypes.string,
  124. };
  125. test(<Outer />, "SPAN", "foo");
  126. });
  127. it("renders only once when setting state in componentWillMount", () => {
  128. var renderCount = 0;
  129. class Foo extends React.Component {
  130. constructor(props) {
  131. super(props);
  132. this.state = {bar: props.initialValue};
  133. }
  134. componentWillMount() {
  135. this.setState({bar: "bar"});
  136. }
  137. render() {
  138. renderCount++;
  139. return <span className={this.state.bar} />;
  140. }
  141. }
  142. test(<Foo initialValue="foo" />, "SPAN", "bar");
  143. expect(renderCount).toBe(1);
  144. });
  145. it("should render with null in the initial state property", () => {
  146. class Foo extends React.Component {
  147. constructor() {
  148. super();
  149. this.state = null;
  150. }
  151. render() {
  152. return <span />;
  153. }
  154. }
  155. test(<Foo />, "SPAN", "");
  156. });
  157. it("setState through an event handler", () => {
  158. class Foo extends React.Component {
  159. constructor(props) {
  160. super(props);
  161. this.state = {bar: props.initialValue};
  162. }
  163. handleClick() {
  164. this.setState({bar: "bar"});
  165. }
  166. render() {
  167. return (
  168. <Inner name={this.state.bar} onClick={this.handleClick.bind(this)} />
  169. );
  170. }
  171. }
  172. test(<Foo initialValue="foo" />, "DIV", "foo");
  173. attachedListener();
  174. expect(renderedName).toBe("bar");
  175. });
  176. it("should not implicitly bind event handlers", () => {
  177. class Foo extends React.Component {
  178. constructor(props) {
  179. super(props);
  180. this.state = {bar: props.initialValue};
  181. }
  182. handleClick() {
  183. this.setState({bar: "bar"});
  184. }
  185. render() {
  186. return <Inner name={this.state.bar} onClick={this.handleClick} />;
  187. }
  188. }
  189. test(<Foo initialValue="foo" />, "DIV", "foo");
  190. expect(attachedListener).toThrow();
  191. });
  192. it("renders using forceUpdate even when there is no state", () => {
  193. class Foo extends React.Component {
  194. constructor(props) {
  195. super(props);
  196. this.mutativeValue = props.initialValue;
  197. }
  198. handleClick() {
  199. this.mutativeValue = "bar";
  200. this.forceUpdate();
  201. }
  202. render() {
  203. return (
  204. <Inner
  205. name={this.mutativeValue}
  206. onClick={this.handleClick.bind(this)}
  207. />
  208. );
  209. }
  210. }
  211. test(<Foo initialValue="foo" />, "DIV", "foo");
  212. attachedListener();
  213. expect(renderedName).toBe("bar");
  214. });
  215. it("will call all the normal life cycle methods", () => {
  216. var lifeCycles = [];
  217. class Foo extends React.Component {
  218. constructor() {
  219. super();
  220. this.state = {};
  221. }
  222. componentWillMount() {
  223. lifeCycles.push("will-mount");
  224. }
  225. componentDidMount() {
  226. lifeCycles.push("did-mount");
  227. }
  228. componentWillReceiveProps(nextProps) {
  229. lifeCycles.push("receive-props", nextProps);
  230. }
  231. shouldComponentUpdate(nextProps, nextState) {
  232. lifeCycles.push("should-update", nextProps, nextState);
  233. return true;
  234. }
  235. componentWillUpdate(nextProps, nextState) {
  236. lifeCycles.push("will-update", nextProps, nextState);
  237. }
  238. componentDidUpdate(prevProps, prevState) {
  239. lifeCycles.push("did-update", prevProps, prevState);
  240. }
  241. componentWillUnmount() {
  242. lifeCycles.push("will-unmount");
  243. }
  244. render() {
  245. return <span className={this.props.value} />;
  246. }
  247. }
  248. test(<Foo value="foo" />, "SPAN", "foo");
  249. expect(lifeCycles).toEqual(["will-mount", "did-mount"]);
  250. lifeCycles = []; // reset
  251. test(<Foo value="bar" />, "SPAN", "bar");
  252. // prettier-ignore
  253. expect(lifeCycles).toEqual([
  254. "receive-props", freeze({value: "bar"}),
  255. "should-update", freeze({value: "bar"}), {},
  256. "will-update", freeze({value: "bar"}), {},
  257. "did-update", freeze({value: "foo"}), {},
  258. ]);
  259. lifeCycles = []; // reset
  260. ReactDOM.unmountComponentAtNode(container);
  261. expect(lifeCycles).toEqual(["will-unmount"]);
  262. });
  263. it("warns when classic properties are defined on the instance, but does not invoke them.", () => {
  264. var getDefaultPropsWasCalled = false;
  265. var getInitialStateWasCalled = false;
  266. class Foo extends React.Component {
  267. constructor() {
  268. super();
  269. this.contextTypes = {};
  270. this.propTypes = {};
  271. }
  272. getInitialState() {
  273. getInitialStateWasCalled = true;
  274. return {};
  275. }
  276. getDefaultProps() {
  277. getDefaultPropsWasCalled = true;
  278. return {};
  279. }
  280. render() {
  281. return <span className="foo" />;
  282. }
  283. }
  284. test(<Foo />, "SPAN", "foo");
  285. expect(getInitialStateWasCalled).toBe(false);
  286. expect(getDefaultPropsWasCalled).toBe(false);
  287. });
  288. it('does not warn about getInitialState() on class components if state is also defined.', () => {
  289. spyOn(console, 'error');
  290. class Foo extends React.Component {
  291. state = this.getInitialState();
  292. getInitialState() {
  293. return {};
  294. }
  295. render() {
  296. return <span className="foo" />;
  297. }
  298. }
  299. test(<Foo />, 'SPAN', 'foo');
  300. expect(console.error.calls.count()).toBe(0);
  301. });
  302. it('should warn when misspelling shouldComponentUpdate', () => {
  303. class NamedComponent extends React.Component {
  304. componentShouldUpdate() {
  305. return false;
  306. }
  307. render() {
  308. return <span className="foo" />;
  309. }
  310. }
  311. test(<NamedComponent />, 'SPAN', 'foo');
  312. });
  313. it('should warn when misspelling componentWillReceiveProps', () => {
  314. class NamedComponent extends React.Component {
  315. componentWillRecieveProps() {
  316. return false;
  317. }
  318. render() {
  319. return <span className="foo" />;
  320. }
  321. }
  322. test(<NamedComponent />, 'SPAN', 'foo');
  323. });
  324. it('supports this.context passed via getChildContext', () => {
  325. class Bar extends React.Component {
  326. render() {
  327. return <div className={this.context.bar} />;
  328. }
  329. }
  330. Bar.contextTypes = {bar: PropTypes.string};
  331. class Foo extends React.Component {
  332. getChildContext() {
  333. return {bar: 'bar-through-context'};
  334. }
  335. render() {
  336. return <Bar />;
  337. }
  338. }
  339. Foo.childContextTypes = {bar: PropTypes.string};
  340. test(<Foo />, 'DIV', 'bar-through-context');
  341. });
  342. it('supports classic refs', () => {
  343. class Foo extends React.Component {
  344. render() {
  345. return <Inner name="foo" ref="inner" />;
  346. }
  347. }
  348. var instance = test(<Foo />, 'DIV', 'foo');
  349. expect(instance.refs.inner.getName()).toBe('foo');
  350. });
  351. it('supports drilling through to the DOM using findDOMNode', () => {
  352. var instance = test(<Inner name="foo" />, 'DIV', 'foo');
  353. var node = ReactDOM.findDOMNode(instance);
  354. expect(node).toBe(container.firstChild);
  355. });
  356. });