component.spec.jsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646
  1. // import PureComponent from "src/PureComponent";
  2. import ReactTestUtils from "../../lib/ReactTestUtils";
  3. import React from '../../src/May';
  4. import { render, unmountComponentAtNode } from '../../src/may-dom/MayDom';
  5. var ReactDOM = {
  6. render: render,
  7. unmountComponentAtNode: unmountComponentAtNode
  8. }
  9. React.render = render;
  10. // import React from "../../dist/ReactANU";
  11. // var ReactDOM=React;
  12. // var React = require('react');//hyphenate
  13. // var ReactDOM = require('react-dom');
  14. describe("组件相关", function() {
  15. // this.timeout(200000);
  16. // before(async () => {
  17. // await beforeHook();
  18. // });
  19. // after(async () => {
  20. // await afterHook(false);
  21. // });
  22. var body = document.body,
  23. div;
  24. beforeEach(function() {
  25. div = document.createElement("div");
  26. body.appendChild(div);
  27. });
  28. afterEach(function() {
  29. body.removeChild(div);
  30. });
  31. it("stateless", function() {
  32. function HelloComponent(
  33. props
  34. //context
  35. ) {
  36. return <div onClick={() => (props.name = 11)}>Hello {props.name}</div>;
  37. }
  38. var vnode = <HelloComponent name="Sebastian" />
  39. React.render(vnode, div);
  40. expect(vnode.mayInfo.instance.mayInst.hostNode.innerHTML).toBe("Hello Sebastian");
  41. });
  42. it("shouldComponentUpdate什么也不返回", function() {
  43. var a = 1;
  44. class App extends React.Component {
  45. constructor(props) {
  46. super(props);
  47. this.state = {
  48. aaa: 1
  49. };
  50. }
  51. shouldComponentUpdate() {
  52. //这里相当于返回false
  53. }
  54. click() {
  55. this.setState(
  56. function(a) {
  57. a.aaa++;
  58. },
  59. function() {
  60. a++;
  61. }
  62. );
  63. this.setState(
  64. function(a) {
  65. a.aaa++;
  66. },
  67. function() {
  68. a++;
  69. }
  70. );
  71. }
  72. render() {
  73. return <div onClick={this.click.bind(this)}>{this.state.aaa}</div>;
  74. }
  75. }
  76. var vnode = <App />
  77. ReactDOM.render(vnode, div);
  78. expect(vnode.mayInfo.instance.mayInst.hostNode.innerHTML).toBe("1");
  79. // ReactTestUtils.Simulate.click(vnode._hostNode)
  80. // expect(vnode.mayInfo.instance.mayInst.hostNode.innerHTML).toBe("1");
  81. // expect(a).toBe(3);
  82. });
  83. it("shouldComponentUpdate返回false", function() {
  84. var a = 1;
  85. class App extends React.Component {
  86. constructor(props) {
  87. super(props);
  88. this.state = {
  89. aaa: 1
  90. };
  91. }
  92. shouldComponentUpdate() {
  93. return false;
  94. }
  95. click() {
  96. this.setState(
  97. function(a) {
  98. a.aaa++;
  99. },
  100. function() {
  101. a++;
  102. }
  103. );
  104. this.setState(
  105. function(a) {
  106. a.aaa++;
  107. },
  108. function() {
  109. a++;
  110. }
  111. );
  112. }
  113. render() {
  114. return <div onClick={this.click.bind(this)}>{this.state.aaa}</div>;
  115. }
  116. }
  117. var vnode = <App />
  118. ReactDOM.render(vnode, div);
  119. expect(vnode.mayInfo.instance.mayInst.hostNode.innerHTML).toBe("1");
  120. // ReactTestUtils.Simulate.click(vnode.mayInfo.hostNode)
  121. // expect(vnode.mayInfo.instance.mayInst.hostNode.innerHTML).toBe("1");
  122. // expect(a).toBe(3);
  123. });
  124. it("PureComponent", function() {
  125. var a = 1;
  126. class App extends React.PureComponent {
  127. constructor(props) {
  128. super(props);
  129. this.state = {
  130. aaa: {
  131. a: 7
  132. }
  133. };
  134. }
  135. click() {
  136. this.setState(function(state) {
  137. state.aaa.a = 8;
  138. });
  139. }
  140. render() {
  141. return <div onClick={this.click.bind(this)}>{this.state.aaa.a}</div>;
  142. }
  143. }
  144. var vnode = <App />
  145. ReactDOM.render(vnode, div);
  146. expect(vnode.mayInfo.instance.mayInst.hostNode.innerHTML).toBe("7");
  147. // ReactTestUtils.Simulate.click(vnode._hostNode)
  148. // expect(vnode._hostNode.innerHTML).toBe("7");
  149. });
  150. it("PureComponent2", function() {
  151. class App extends React.PureComponent {
  152. constructor(props) {
  153. super(props);
  154. this.state = {
  155. aaa: {
  156. a: 7
  157. }
  158. };
  159. }
  160. click() {
  161. var aaa = this.state.aaa;
  162. aaa.a = 9;
  163. this.setState({
  164. aaa: aaa,
  165. ccc: 222
  166. });
  167. }
  168. render() {
  169. return <div onClick={this.click.bind(this)}>{this.state.aaa.a}</div>;
  170. }
  171. }
  172. var vnode = <App />
  173. ReactDOM.render(vnode, div);
  174. expect(vnode.mayInfo.instance.mayInst.hostNode.innerHTML).toBe("7");
  175. // ReactTestUtils.Simulate.click(vnode._hostNode)
  176. // expect(vnode._hostNode.innerHTML).toBe("9");
  177. });
  178. it("子组件是无状态组件",() => {// async
  179. function Select(props) {
  180. return <strong>{props.value}</strong>;
  181. }
  182. class App extends React.Component {
  183. constructor(props) {
  184. super(props);
  185. this.state = {
  186. value: "南京"
  187. };
  188. }
  189. onChange(e) {
  190. this.setState({
  191. value: e.target.value
  192. });
  193. }
  194. render() {
  195. return (
  196. <div>
  197. <Select value={this.state.value} />
  198. <input ref="a" value={this.state.value} onInput={this.onChange.bind(this)} />
  199. </div>
  200. );
  201. }
  202. }
  203. var s = React.render(<App />, div);
  204. expect(s.refs.a.value).toBe("南京");//await
  205. // browser
  206. // .setValue(s.refs.a, "南京22")
  207. // .pause(200)
  208. // .$apply();
  209. expect(s.refs.a.value).toBe("南京");
  210. expect(div.getElementsByTagName("strong")[0].innerHTML).toBe("南京");
  211. });
  212. it("多选下拉框",() => {// async
  213. class App extends React.Component {
  214. constructor(props) {
  215. super(props);
  216. this.state = {
  217. value: ["aaa", "ccc"]
  218. };
  219. }
  220. onChange(e) {
  221. var values = [];
  222. var elems = e.target.getElementsByTagName("option");
  223. for (var i = 0, el; (el = elems[i++]); ) {
  224. if (el.selected) {
  225. if (el.getAttribute("value") != null) {
  226. values.push(el.getAttribute("value"));
  227. } else {
  228. values.push(el.text);
  229. }
  230. }
  231. }
  232. this.setState({
  233. values: values
  234. });
  235. }
  236. render() {
  237. return (
  238. <select value={this.state.value} multiple="true" onChange={this.onChange.bind(this)}>
  239. <optgroup>
  240. <option ref="a">aaa</option>
  241. <option ref="b">bbb</option>
  242. </optgroup>
  243. <optgroup>
  244. <option ref="c">ccc</option>
  245. <option ref="d">ddd</option>
  246. </optgroup>
  247. </select>
  248. );
  249. }
  250. }
  251. // var s = React.render(<App />, div);
  252. // // await browser.pause(100).$apply();
  253. // expect(s.refs.a.selected).toBe(true);
  254. // expect(s.refs.b.selected).toBe(false);
  255. // expect(s.refs.c.selected).toBe(true);
  256. // expect(s.refs.d.selected).toBe(false);
  257. // s.setState({
  258. // value: ["bbb", "ddd"]
  259. // });
  260. // // await browser.pause(100).$apply();
  261. // expect(s.refs.a.selected).toBe(false);
  262. // expect(s.refs.b.selected).toBe(true);
  263. // expect(s.refs.c.selected).toBe(false);
  264. // expect(s.refs.d.selected).toBe(true);
  265. });
  266. /*it("多选下拉框defaultValue", function() {
  267. class App extends React.Component {
  268. constructor(props) {
  269. super(props);
  270. this.state = {
  271. value: "ccc"
  272. };
  273. }
  274. render() {
  275. return (
  276. <select defaultValue={this.state.value}>
  277. <option ref="a">aaa</option>
  278. <option ref="b">bbb</option>
  279. <option ref="c">ccc</option>
  280. <option ref="d">ddd</option>
  281. </select>
  282. );
  283. }
  284. }
  285. var s = React.render(<App />, div);
  286. expect(s.refs.c.selected).toBe(true);
  287. });
  288. it("多选下拉框没有defaultValue与ReactDOM.render的回调this指向问题", function() {
  289. class App extends React.Component {
  290. constructor(props) {
  291. super(props);
  292. this.state = {};
  293. }
  294. render() {
  295. return (
  296. <select>
  297. <option ref="a">aaa</option>
  298. <option ref="b">bbb</option>
  299. <option ref="c">ccc</option>
  300. <option ref="d">ddd</option>
  301. </select>
  302. );
  303. }
  304. }
  305. var s = React.render(<App />, div, function() {
  306. expect(this.constructor.name).toBe("App");
  307. });
  308. expect(s.refs.a.selected).toBe(true);
  309. });
  310. it("一个组件由元素节点变注释节点再回元素节点,不触发componentWillUnmount", function() {
  311. class App extends React.Component {
  312. constructor(props) {
  313. super(props);
  314. this.state = {
  315. path: "111"
  316. };
  317. }
  318. change(path) {
  319. this.setState({
  320. path: path || "333"
  321. });
  322. }
  323. render() {
  324. return (
  325. <div>
  326. <span>xx</span>
  327. <Route path={this.state.path} />
  328. </div>
  329. );
  330. }
  331. }
  332. var updateCount = 0;
  333. var receiveCount = 0;
  334. var destroyCount = 0;
  335. class Route extends React.Component {
  336. constructor(props) {
  337. super(props);
  338. this.state = {
  339. path: props.path
  340. };
  341. }
  342. componentWillReceiveProps(props) {
  343. receiveCount++;
  344. console.log("receiveCount", receiveCount);
  345. this.setState(function(nextState, props) {
  346. nextState.path = props.path;
  347. return nextState;
  348. });
  349. }
  350. componentWillUpdate() {
  351. updateCount++;
  352. console.log("updateCount", updateCount);
  353. }
  354. componentWillUnmount() {
  355. destroyCount++;
  356. console.log("destroyCount", destroyCount);
  357. }
  358. render() {
  359. return this.state.path == "111" ? <p>{this.state.path}</p> : null;
  360. }
  361. }
  362. var s = React.render(<App />, div);
  363. expect(updateCount).toBe(0);
  364. expect(receiveCount).toBe(0);
  365. s.change("111");
  366. expect(updateCount).toBe(1);
  367. expect(receiveCount).toBe(1);
  368. s.change("111x");
  369. expect(updateCount).toBe(2);
  370. expect(receiveCount).toBe(2);
  371. expect(div.firstChild.childNodes[1].nodeType).toBe(8);
  372. expect(destroyCount).toBe(0);
  373. s.change("111");
  374. expect(updateCount).toBe(3);
  375. expect(receiveCount).toBe(3);
  376. expect(div.firstChild.childNodes[1].nodeType).toBe(1);
  377. expect(destroyCount).toBe(0);
  378. });
  379. it("select的准确匹配", function() {
  380. var dom = ReactDOM.render(
  381. <select value={222}>
  382. <option value={111}>aaa</option>
  383. <option value={"222"}>xxx</option>
  384. <option value={222}>bbb</option>
  385. <option value={333}>ccc</option>
  386. </select>,
  387. div
  388. );
  389. expect(dom.options[2].selected).toBe(true);
  390. });
  391. it("确保ref执行在componentDidMount之前", function() {
  392. var str = "";
  393. class Test extends React.Component {
  394. componentDidMount() {
  395. expect(typeof this.refs.wrapper).toBe("object");
  396. str += "111";
  397. }
  398. render() {
  399. return (
  400. <div ref="wrapper" id="aaa">
  401. xxx<B />
  402. </div>
  403. );
  404. }
  405. }
  406. class B extends React.Component {
  407. componentDidMount() {
  408. expect(typeof this.refs.wrapper2).toBe("object");
  409. str += "222";
  410. }
  411. render() {
  412. return <p ref="wrapper2">son</p>;
  413. }
  414. }
  415. var s = React.render(<Test />, div);
  416. expect(str).toBe("222111");
  417. expect(React.findDOMNode(s.refs.wrapper)).toBe(div.querySelector("#aaa"));
  418. });
  419. it("确保componentDidUpdate的第一个参数是prevProps", function() {
  420. class HasChild extends React.Component {
  421. constructor(props) {
  422. super(props);
  423. this.state = {};
  424. this.onClick = this.onClick.bind(this);
  425. this.a = 0;
  426. }
  427. onClick() {
  428. this.a = 1;
  429. this.forceUpdate();
  430. }
  431. render() {
  432. return (
  433. <div onClick={this.onClick} ref="componentDidUpdate">
  434. {this.a == 0 ? <A title="xxx" ref="a" /> : <A ddd="ddd" ref="a" />}
  435. </div>
  436. );
  437. }
  438. }
  439. var title = 0;
  440. class A extends React.Component {
  441. constructor(props) {
  442. super(props);
  443. this.state = {};
  444. }
  445. componentDidUpdate(a) {
  446. title = 1;
  447. }
  448. render() {
  449. return <span>{this.props.title}</span>;
  450. }
  451. }
  452. A.defaultProps = {
  453. title: "默认值"
  454. };
  455. var s = React.render(<HasChild />, div);
  456. expect(s.refs.a.props.title).toBe("xxx");
  457. eactTestUtils.Simulate.click(s.refs.componentDidUpdate)
  458. expect(s.refs.a.props.title).toBe("默认值");
  459. expect(s.refs.a.props.ddd).toBe("ddd");
  460. expect(title).toBe(1);
  461. });
  462. it("defaultProps的处理", function() {
  463. class HasChild extends React.Component {
  464. constructor(props) {
  465. super(props);
  466. this.state = {};
  467. this.onClick = this.onClick.bind(this);
  468. this.a = 0;
  469. }
  470. onClick() {
  471. this.a = 1;
  472. this.forceUpdate();
  473. }
  474. render() {
  475. return (
  476. <div onClick={this.onClick} ref="componentDidUpdate2">
  477. {this.a == 0 ? <A title="xxx" ref="a" /> : <A ddd="ddd" ref="a" />}
  478. </div>
  479. );
  480. }
  481. }
  482. function A(props) {
  483. return <span>{props.title}</span>;
  484. }
  485. A.defaultProps = {
  486. title: "默认值"
  487. };
  488. var s = React.render(<HasChild />, div);
  489. var span = div.getElementsByTagName("span")[0];
  490. expect(span.innerHTML).toBe("xxx");
  491. eactTestUtils.Simulate.click(s.refs.componentDidUpdate2)
  492. expect(span.innerHTML).toBe("默认值");
  493. });
  494. it("新旧两种无状态组件的ref传参", function(){
  495. var list = []
  496. function Old (props){
  497. return <span>{props.aaa}</span>
  498. }
  499. ReactDOM.render(<Old ref={(a)=>{ list.push(!!a)}} aaa={111} />, div);
  500. ReactDOM.render(<Old ref={(a)=>{ list.push(!!a)}} aaa={222} />, div);
  501. expect(list).toEqual([false, false, false])
  502. var list2 = []
  503. function New (props){
  504. return {
  505. componentWillMount(){
  506. list2.push("mount")
  507. },
  508. componentWillUpdate(){
  509. list2.push("update")
  510. },
  511. render(){
  512. return <span>{props.aaa}</span>
  513. }
  514. }
  515. }
  516. ReactDOM.render(<New ref={(a)=>{ list2.push(!!a)}} aaa={111} />, div);
  517. ReactDOM.render(<New ref={(a)=>{ list2.push(!!a)}} aaa={222} />, div);
  518. expect(list2).toEqual(["mount", true, false, "update",true])
  519. })
  520. it("componentWillUnmount钩子中调用ReactDOM.findDOMNode 应该还能找到元素", () => {
  521. var assertions = 0;
  522. class Inner extends React.Component {
  523. render() {
  524. return <span />;
  525. }
  526. componentDidMount() {
  527. expect(!!ReactDOM.findDOMNode(this)).toBe(true);
  528. assertions++;
  529. }
  530. componentWillUnmount() {
  531. expect(!!ReactDOM.findDOMNode(this)).toBe(true);
  532. assertions++;
  533. }
  534. }
  535. class Wrapper extends React.Component {
  536. render() {
  537. return this.props.showInner ? <Inner /> : null;
  538. }
  539. }
  540. var el = document.createElement("div");
  541. var component;
  542. component = React.render(<Wrapper showInner={true} />, el);
  543. expect(!!ReactDOM.findDOMNode(component)).toBe(true);
  544. component = React.render(<Wrapper showInner={false} />, el);
  545. expect(ReactDOM.findDOMNode(component).tagName).toBe(undefined);
  546. component = React.render(<Wrapper showInner={true} />, el);
  547. expect(!!ReactDOM.findDOMNode(component)).toBe(true);
  548. expect(assertions).toBe(3);
  549. });
  550. it("虚拟DOM的_owner必须在render中加上", function() {
  551. class B extends React.Component {
  552. render() {
  553. return <div className="xxx">{this.props.children}</div>;
  554. }
  555. }
  556. var b, c;
  557. class App extends React.Component {
  558. constructor(props) {
  559. super(props);
  560. this.state = {
  561. value: "南京"
  562. };
  563. }
  564. _renderPopup(a) {
  565. return (
  566. <B>
  567. <p {...a} />
  568. </B>
  569. );
  570. }
  571. onChange(e) {
  572. this.setState({
  573. value: e.target.value
  574. });
  575. }
  576. render() {
  577. return (
  578. <div>
  579. {this._renderPopup({ ref: "xxxx", children: [(b = <span>333</span>)] })}
  580. {this._renderPopup({ ref: "yyyy", children: [(c = <strong>444</strong>)] })}
  581. </div>
  582. );
  583. }
  584. }
  585. var s = React.render(<App />, div);
  586. expect(b._owner.constructor).toBe(App);
  587. expect(c._owner.constructor).toBe(App);
  588. });*/
  589. });