ReactMultiChild-test.jsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  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("ReactMultiChild", function () {
  22. // this.timeout(200000);
  23. it("should update children when possible", () => {
  24. var container = document.createElement("div");
  25. var mockMount = jasmine.createSpy('mockMount');
  26. var mockUpdate = jasmine.createSpy('mockUpdate');
  27. var mockUnmount = jasmine.createSpy('mockUnmount');
  28. class MockComponent extends React.Component {
  29. componentDidMount = mockMount;
  30. componentDidUpdate = mockUpdate;
  31. componentWillUnmount = mockUnmount;
  32. render() {
  33. return <span />;
  34. }
  35. }
  36. expect(mockMount.calls.count()).toBe(0);
  37. expect(mockUpdate.calls.count()).toBe(0);
  38. expect(mockUnmount.calls.count()).toBe(0);
  39. ReactDOM.render(
  40. <div>
  41. <MockComponent />
  42. </div>,
  43. container
  44. );
  45. expect(mockMount.calls.count()).toBe(1);
  46. expect(mockUpdate.calls.count()).toBe(0);
  47. expect(mockUnmount.calls.count()).toBe(0);
  48. ReactDOM.render(
  49. <div>
  50. <MockComponent />
  51. </div>,
  52. container
  53. );
  54. expect(mockMount.calls.count()).toBe(1);
  55. expect(mockUpdate.calls.count()).toBe(1);
  56. expect(mockUnmount.calls.count()).toBe(0);
  57. });
  58. var LetterInner = class extends React.Component {
  59. render() {
  60. return <div>{this.props.char}</div>;
  61. }
  62. };
  63. var Letter = class extends React.Component {
  64. render() {
  65. return <LetterInner char={this.props.char} />;
  66. }
  67. shouldComponentUpdate() {
  68. return false;
  69. }
  70. };
  71. var Letters = class extends React.Component {
  72. render() {
  73. const letters = this.props.letters.split("");
  74. return <div>{letters.map(c => <Letter key={c} char={c} />)}</div>;
  75. }
  76. };
  77. it("should replace children with different constructors", () => {
  78. var container = document.createElement("div");
  79. var mockMount = jasmine.createSpy('mockMount');
  80. var mockUnmount = jasmine.createSpy('mockUnmount');
  81. class MockComponent extends React.Component {
  82. componentDidMount = mockMount;
  83. componentWillUnmount = mockUnmount;
  84. render() {
  85. return <span />;
  86. }
  87. }
  88. expect(mockMount.calls.count()).toBe(0);
  89. expect(mockUnmount.calls.count()).toBe(0);
  90. ReactDOM.render(
  91. <div>
  92. <MockComponent />
  93. </div>,
  94. container
  95. );
  96. expect(mockMount.calls.count()).toBe(1);
  97. expect(mockUnmount.calls.count()).toBe(0);
  98. ReactDOM.render(
  99. <div>
  100. <span />
  101. </div>,
  102. container
  103. );
  104. expect(mockMount.calls.count()).toBe(1);
  105. expect(mockUnmount.calls.count()).toBe(1);
  106. });
  107. it("should NOT replace children with different owners", () => {
  108. var container = document.createElement("div");
  109. var mockMount = jasmine.createSpy('mockMount');
  110. var mockUnmount = jasmine.createSpy('mockUnmount');
  111. class MockComponent extends React.Component {
  112. componentDidMount = mockMount;
  113. componentWillUnmount = mockUnmount;
  114. render() {
  115. return <span />;
  116. }
  117. }
  118. class WrapperComponent extends React.Component {
  119. render() {
  120. return this.props.children || <MockComponent />;
  121. }
  122. }
  123. expect(mockMount.calls.count()).toBe(0);
  124. expect(mockUnmount.calls.count()).toBe(0);
  125. ReactDOM.render(<WrapperComponent />, container);
  126. expect(mockMount.calls.count()).toBe(1);
  127. expect(mockUnmount.calls.count()).toBe(0);
  128. ReactDOM.render(
  129. <WrapperComponent>
  130. <MockComponent />
  131. </WrapperComponent>,
  132. container
  133. );
  134. expect(mockMount.calls.count()).toBe(1);
  135. expect(mockUnmount.calls.count()).toBe(0);
  136. });
  137. it("should replace children with different keys", () => {
  138. var container = document.createElement("div");
  139. var mockMount = jasmine.createSpy('mockMount');
  140. var mockUnmount = jasmine.createSpy('mockUnmount');
  141. class MockComponent extends React.Component {
  142. componentDidMount = mockMount;
  143. componentWillUnmount = mockUnmount;
  144. render() {
  145. return <span />;
  146. }
  147. }
  148. expect(mockMount.calls.count()).toBe(0);
  149. expect(mockUnmount.calls.count()).toBe(0);
  150. ReactDOM.render(
  151. <div>
  152. <MockComponent key="A" />
  153. </div>,
  154. container
  155. );
  156. expect(mockMount.calls.count()).toBe(1);
  157. expect(mockUnmount.calls.count()).toBe(0);
  158. ReactDOM.render(
  159. <div>
  160. <MockComponent key="B" />
  161. </div>,
  162. container
  163. );
  164. expect(mockMount.calls.count()).toBe(2);
  165. expect(mockUnmount.calls.count()).toBe(1);
  166. });
  167. it("should warn for duplicated array keys with component stack info", () => {
  168. class WrapperComponent extends React.Component {
  169. render() {
  170. return <div>{this.props.children}</div>;
  171. }
  172. }
  173. class Parent extends React.Component {
  174. render() {
  175. return (
  176. <div>
  177. <WrapperComponent>{this.props.children}</WrapperComponent>
  178. </div>
  179. );
  180. }
  181. }
  182. var instance = ReactTestUtils.renderIntoDocument(<Parent>{[<div className="child" />]}</Parent>);
  183. var array = ReactTestUtils.scryRenderedDOMComponentsWithClass(instance, "child");
  184. expect(array.length).toBe(1);
  185. var instance2 = ReactTestUtils.renderIntoDocument(<Parent>{[<div className="aaa" />, <div className="aaa" />]}</Parent>);
  186. var array2 = ReactTestUtils.scryRenderedDOMComponentsWithClass(instance2, "aaa");
  187. expect(array2.length).toBe(2);
  188. });
  189. it("should warn for duplicated iterable keys with component stack info", () => {
  190. class WrapperComponent extends React.Component {
  191. render() {
  192. return <div>{this.props.children}</div>;
  193. }
  194. }
  195. class Parent extends React.Component {
  196. render() {
  197. return (
  198. <div>
  199. <WrapperComponent>{this.props.children}</WrapperComponent>
  200. </div>
  201. );
  202. }
  203. }
  204. function createIterable(array) {
  205. return {
  206. "@@iterator": function() {
  207. var i = 0;
  208. return {
  209. next() {
  210. const next = {
  211. value: i < array.length ? array[i] : undefined,
  212. done: i === array.length
  213. };
  214. i++;
  215. return next;
  216. }
  217. };
  218. }
  219. };
  220. }
  221. var instance = ReactTestUtils.renderIntoDocument(<Parent>{createIterable([<div className="aaa" />])}</Parent>);
  222. var array = ReactTestUtils.scryRenderedDOMComponentsWithClass(instance, "aaa");
  223. expect(array.length).toBe(1);
  224. var instance = ReactTestUtils.renderIntoDocument(<Parent>{createIterable([<div className="aaa" />, <div className="aaa" />])}</Parent>);
  225. var array = ReactTestUtils.scryRenderedDOMComponentsWithClass(instance, "aaa");
  226. expect(array.length).toBe(2);
  227. });
  228. //暂不处理 maps children
  229. // it("should warn for using maps as children with owner info", () => {
  230. // if (typeof Map === "function") {
  231. // class Parent extends React.Component {
  232. // render() {
  233. // return <div>{new Map([["foo", 0], ["bar", 1]])}</div>;
  234. // }
  235. // }
  236. // var container = document.createElement("div");
  237. // ReactDOM.render(<Parent />, container);
  238. // expect(container.innerText || container.textContent).toBe("01");
  239. // }
  240. // });
  241. it("should reorder bailed-out children", () => {
  242. var container = document.createElement("div");
  243. // Two random strings -- some additions, some removals, some moves
  244. ReactDOM.render(<Letters letters="XKwHomsNjIkBcQWFbiZU" />, container);
  245. expect(container.textContent).toBe("XKwHomsNjIkBcQWFbiZU");
  246. ReactDOM.render(<Letters letters="EHCjpdTUuiybDvhRJwZt" />, container);
  247. expect(container.textContent).toBe("EHCjpdTUuiybDvhRJwZt");
  248. });
  249. it("添加增删改", () => {
  250. var container = document.createElement("div");
  251. // Two random strings -- some additions, some removals, some moves
  252. ReactDOM.render(<Letters letters="8ABC4D5EFGH" />, container);
  253. expect(container.textContent).toBe("8ABC4D5EFGH");
  254. ReactDOM.render(<Letters letters="GFE9DBACH" />, container);
  255. expect(container.textContent).toBe("GFE9DBACH");
  256. });
  257. //暂不处理 需要逐步diff
  258. /*it("prepares new children before unmounting old", () => {
  259. var list = [];
  260. class Spy extends React.Component {
  261. componentWillMount() {
  262. list.push(this.props.name + " componentWillMount");
  263. }
  264. render() {
  265. list.push(this.props.name + " render");
  266. return <div />;
  267. }
  268. componentDidMount() {
  269. list.push(this.props.name + " componentDidMount");
  270. }
  271. componentWillUnmount() {
  272. list.push(this.props.name + " componentWillUnmount");
  273. }
  274. }
  275. // These are reference-unequal so they will be swapped even if they have
  276. // matching keys
  277. var SpyA = props => <Spy {...props} />;
  278. var SpyB = props => <Spy {...props} />;
  279. var container = document.createElement("div");
  280. ReactDOM.render(
  281. <div>
  282. <SpyA key="one" name="oneA" />
  283. <SpyA key="two" name="twoA" />
  284. </div>,
  285. container
  286. );
  287. ReactDOM.render(
  288. <div>
  289. <SpyB key="one" name="oneB" />
  290. <SpyB key="two" name="twoB" />
  291. </div>,
  292. container
  293. );
  294. expect(list).toEqual([
  295. "oneA componentWillMount",
  296. "oneA render",
  297. "twoA componentWillMount",
  298. "twoA render",
  299. "oneA componentDidMount",
  300. "twoA componentDidMount",
  301. "oneA componentWillUnmount",
  302. "oneB componentWillMount",
  303. "oneB render",
  304. "twoA componentWillUnmount",
  305. "twoB componentWillMount",
  306. "twoB render",
  307. "oneB componentDidMount",
  308. "twoB componentDidMount"
  309. ]);
  310. }); */
  311. });