getPropValue-babelparser-test.js 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277
  1. /* eslint-env mocha */
  2. /* eslint no-template-curly-in-string: 0 */
  3. import assert from 'assert';
  4. import {
  5. extractProp,
  6. changePlugins,
  7. fallbackToBabylon,
  8. describeIfNotBabylon,
  9. setParserName,
  10. } from '../helper';
  11. import getPropValue from '../../src/getPropValue';
  12. describe('getPropValue', () => {
  13. beforeEach(() => {
  14. setParserName('babel');
  15. });
  16. it('should export a function', () => {
  17. const expected = 'function';
  18. const actual = typeof getPropValue;
  19. assert.equal(actual, expected);
  20. });
  21. it('should return undefined when not provided with a JSXAttribute', () => {
  22. const expected = undefined;
  23. const actual = getPropValue(1);
  24. assert.equal(actual, expected);
  25. });
  26. it('should not throw error when trying to get value from unknown node type', () => {
  27. const prop = {
  28. type: 'JSXAttribute',
  29. value: {
  30. type: 'JSXExpressionContainer',
  31. },
  32. };
  33. let counter = 0;
  34. // eslint-disable-next-line no-console
  35. const errorOrig = console.error;
  36. // eslint-disable-next-line no-console
  37. console.error = () => {
  38. counter += 1;
  39. };
  40. let value;
  41. assert.doesNotThrow(() => {
  42. value = getPropValue(prop);
  43. }, Error);
  44. assert.equal(null, value);
  45. assert.equal(counter, 1);
  46. // eslint-disable-next-line no-console
  47. console.error = errorOrig;
  48. });
  49. describe('Null', () => {
  50. it('should return true when no value is given', () => {
  51. const prop = extractProp('<div foo />');
  52. const expected = true;
  53. const actual = getPropValue(prop);
  54. assert.equal(actual, expected);
  55. });
  56. });
  57. describe('Literal', () => {
  58. it('should return correct string if value is a string', () => {
  59. const prop = extractProp('<div foo="bar" />');
  60. const expected = 'bar';
  61. const actual = getPropValue(prop);
  62. assert.equal(actual, expected);
  63. });
  64. it('should return correct string if value is a string expression', () => {
  65. const prop = extractProp('<div foo={"bar"} />');
  66. const expected = 'bar';
  67. const actual = getPropValue(prop);
  68. assert.equal(actual, expected);
  69. });
  70. it('should return correct integer if value is a integer expression', () => {
  71. const prop = extractProp('<div foo={1} />');
  72. const expected = 1;
  73. const actual = getPropValue(prop);
  74. assert.equal(actual, expected);
  75. });
  76. it('should convert "true" to boolean type', () => {
  77. const prop = extractProp('<div foo="true" />');
  78. const expected = true;
  79. const actual = getPropValue(prop);
  80. assert.equal(actual, expected);
  81. });
  82. it('should convert "false" to boolean type', () => {
  83. const prop = extractProp('<div foo="false" />');
  84. const expected = false;
  85. const actual = getPropValue(prop);
  86. assert.equal(actual, expected);
  87. });
  88. });
  89. describe('JSXElement', () => {
  90. it('should return correct representation of JSX element as a string', () => {
  91. const prop = extractProp('<div foo={<bar />} />');
  92. const expected = '<bar />';
  93. const actual = getPropValue(prop);
  94. assert.equal(actual, expected);
  95. });
  96. it('should return correct representation of JSX element with children as a string', () => {
  97. const prop = extractProp('<div foo={<bar><baz />foo</bar>} />');
  98. const expected = '<bar><baz />foo</bar>';
  99. const actual = getPropValue(prop);
  100. assert.equal(actual, expected);
  101. });
  102. });
  103. (fallbackToBabylon ? describe.skip : describe)('JSXFragment', () => {
  104. it('should return correct representation of JSX fragment as a string', () => {
  105. const prop = extractProp('<div foo={<></>} />');
  106. const expected = '<></>';
  107. const actual = getPropValue(prop);
  108. assert.equal(actual, expected);
  109. });
  110. it('should return correct representation of JSX fragment with children as a string', () => {
  111. const prop = extractProp('<div foo={<><div />abc</>} />');
  112. const expected = '<><div />abc</>';
  113. const actual = getPropValue(prop);
  114. assert.equal(actual, expected);
  115. });
  116. it('supports a prop value containing nested fragments', () => {
  117. const propCode = `
  118. <>
  119. <div>Hello</div>
  120. <>
  121. <div>There</div>
  122. </>
  123. </>
  124. `;
  125. const code = `
  126. <Hello
  127. role="checkbox"
  128. frag={${propCode}}
  129. />
  130. `;
  131. const prop = extractProp(code, 'frag');
  132. const actual = getPropValue(prop);
  133. assert.deepEqual(actual, propCode.trim());
  134. });
  135. });
  136. describe('Identifier', () => {
  137. it('should return string representation of variable identifier', () => {
  138. const prop = extractProp('<div foo={bar} />');
  139. const expected = 'bar';
  140. const actual = getPropValue(prop);
  141. assert.equal(actual, expected);
  142. });
  143. it('should return undefined when identifier is literally `undefined`', () => {
  144. const prop = extractProp('<div foo={undefined} />');
  145. const expected = undefined;
  146. const actual = getPropValue(prop);
  147. assert.equal(actual, expected);
  148. });
  149. it('should return String object when using a reserved JavaScript object', () => {
  150. const prop = extractProp('<div foo={String} />');
  151. const expected = String;
  152. const actual = getPropValue(prop);
  153. assert.equal(actual, expected);
  154. });
  155. it('should return Array object when using a reserved JavaScript object', () => {
  156. const prop = extractProp('<div foo={Array} />');
  157. const expected = Array;
  158. const actual = getPropValue(prop);
  159. assert.equal(actual, expected);
  160. });
  161. it('should return Date object when using a reserved JavaScript object', () => {
  162. const prop = extractProp('<div foo={Date} />');
  163. const expected = Date;
  164. const actual = getPropValue(prop);
  165. assert.equal(actual, expected);
  166. });
  167. it('should return Infinity object when using a reserved JavaScript object', () => {
  168. const prop = extractProp('<div foo={Infinity} />');
  169. const expected = Infinity;
  170. const actual = getPropValue(prop);
  171. assert.equal(actual, expected);
  172. });
  173. it('should return Math object when using a reserved JavaScript object', () => {
  174. const prop = extractProp('<div foo={Math} />');
  175. const expected = Math;
  176. const actual = getPropValue(prop);
  177. assert.equal(actual, expected);
  178. });
  179. it('should return Number object when using a reserved JavaScript object', () => {
  180. const prop = extractProp('<div foo={Number} />');
  181. const expected = Number;
  182. const actual = getPropValue(prop);
  183. assert.equal(actual, expected);
  184. });
  185. it('should return Object object when using a reserved JavaScript object', () => {
  186. const prop = extractProp('<div foo={Object} />');
  187. const expected = Object;
  188. const actual = getPropValue(prop);
  189. assert.equal(actual, expected);
  190. });
  191. });
  192. describe('Template literal', () => {
  193. it('should return template literal with vars wrapped in curly braces', () => {
  194. const prop = extractProp('<div foo={`bar ${baz}`} />');
  195. const expected = 'bar {baz}';
  196. const actual = getPropValue(prop);
  197. assert.equal(actual, expected);
  198. });
  199. it('should return string "undefined" for expressions that evaluate to undefined', () => {
  200. const prop = extractProp('<div foo={`bar ${undefined}`} />');
  201. const expected = 'bar undefined';
  202. const actual = getPropValue(prop);
  203. assert.equal(actual, expected);
  204. });
  205. it('should return template literal with expression type wrapped in curly braces', () => {
  206. const prop = extractProp('<div foo={`bar ${baz()}`} />');
  207. const expected = 'bar {CallExpression}';
  208. const actual = getPropValue(prop);
  209. assert.equal(actual, expected);
  210. });
  211. it('should ignore non-expressions in the template literal', () => {
  212. const prop = extractProp('<div foo={`bar ${<baz />}`} />');
  213. const expected = 'bar ';
  214. const actual = getPropValue(prop);
  215. assert.equal(actual, expected);
  216. });
  217. });
  218. describe('Tagged Template literal', () => {
  219. it('should return template literal with vars wrapped in curly braces', () => {
  220. const prop = extractProp('<div foo={noop`bar ${baz}`} />');
  221. const expected = 'bar {baz}';
  222. const actual = getPropValue(prop);
  223. assert.equal(actual, expected);
  224. });
  225. it('should return string "undefined" for expressions that evaluate to undefined', () => {
  226. const prop = extractProp('<div foo={noop`bar ${undefined}`} />');
  227. const expected = 'bar undefined';
  228. const actual = getPropValue(prop);
  229. assert.equal(actual, expected);
  230. });
  231. it('should return template literal with expression type wrapped in curly braces', () => {
  232. const prop = extractProp('<div foo={noop`bar ${baz()}`} />');
  233. const expected = 'bar {CallExpression}';
  234. const actual = getPropValue(prop);
  235. assert.equal(actual, expected);
  236. });
  237. it('should ignore non-expressions in the template literal', () => {
  238. const prop = extractProp('<div foo={noop`bar ${<baz />}`} />');
  239. const expected = 'bar ';
  240. const actual = getPropValue(prop);
  241. assert.equal(actual, expected);
  242. });
  243. });
  244. describe('Arrow function expression', () => {
  245. it('should return a function', () => {
  246. const prop = extractProp('<div foo={ () => { return "bar"; }} />');
  247. const expected = 'function';
  248. const actual = getPropValue(prop);
  249. assert.equal(expected, typeof actual);
  250. // For code coverage ¯\_(ツ)_/¯
  251. actual();
  252. });
  253. it('should handle ArrowFunctionExpression as conditional consequent', () => {
  254. const prop = extractProp('<div foo={ (true) ? () => null : () => ({})} />');
  255. const expected = 'function';
  256. const actual = getPropValue(prop);
  257. assert.equal(expected, typeof actual);
  258. // For code coverage ¯\_(ツ)_/¯
  259. actual();
  260. });
  261. });
  262. describe('Function expression', () => {
  263. it('should return a function', () => {
  264. const prop = extractProp('<div foo={ function() { return "bar"; } } />');
  265. const expected = 'function';
  266. const actual = getPropValue(prop);
  267. assert.equal(expected, typeof actual);
  268. // For code coverage ¯\_(ツ)_/¯
  269. actual();
  270. });
  271. });
  272. describe('Logical expression', () => {
  273. it('should correctly infer result of && logical expression based on derived values', () => {
  274. const prop = extractProp('<div foo={bar && baz} />');
  275. const expected = 'baz';
  276. const actual = getPropValue(prop);
  277. assert.equal(actual, expected);
  278. });
  279. it('should return undefined when evaluating `undefined && undefined` ', () => {
  280. const prop = extractProp('<div foo={undefined && undefined} />');
  281. const expected = undefined;
  282. const actual = getPropValue(prop);
  283. assert.equal(actual, expected);
  284. });
  285. it('should correctly infer result of || logical expression based on derived values', () => {
  286. const prop = extractProp('<div foo={bar || baz} />');
  287. const expected = 'bar';
  288. const actual = getPropValue(prop);
  289. assert.equal(actual, expected);
  290. });
  291. it('should correctly infer result of || logical expression based on derived values', () => {
  292. const prop = extractProp('<div foo={undefined || baz} />');
  293. const expected = 'baz';
  294. const actual = getPropValue(prop);
  295. assert.equal(actual, expected);
  296. });
  297. it('should return undefined when evaluating `undefined || undefined` ', () => {
  298. const prop = extractProp('<div foo={undefined || undefined} />');
  299. const expected = undefined;
  300. const actual = getPropValue(prop);
  301. assert.equal(actual, expected);
  302. });
  303. it('should correctly infer result of ?? logical expression based on derived values', () => {
  304. const runTest = () => {
  305. const prop = extractProp('<div foo={bar ?? baz} />');
  306. const expected = 'bar';
  307. const actual = getPropValue(prop);
  308. assert.equal(actual, expected);
  309. };
  310. if (fallbackToBabylon) {
  311. // eslint-disable-next-line no-undef
  312. expect(runTest).toThrow();
  313. } else {
  314. runTest();
  315. }
  316. });
  317. it('should correctly infer result of ?? logical expression based on derived values', () => {
  318. const runTest = () => {
  319. const prop = extractProp('<div foo={undefined ?? baz} />');
  320. const expected = 'baz';
  321. const actual = getPropValue(prop);
  322. assert.equal(actual, expected);
  323. };
  324. if (fallbackToBabylon) {
  325. // eslint-disable-next-line no-undef
  326. expect(runTest).toThrow();
  327. } else {
  328. runTest();
  329. }
  330. });
  331. it('should return undefined when evaluating `undefined ?? undefined` ', () => {
  332. const runTest = () => {
  333. const prop = extractProp('<div foo={undefined ?? undefined} />');
  334. const expected = undefined;
  335. const actual = getPropValue(prop);
  336. assert.equal(actual, expected);
  337. };
  338. if (fallbackToBabylon) {
  339. // eslint-disable-next-line no-undef
  340. expect(runTest).toThrow();
  341. } else {
  342. runTest();
  343. }
  344. });
  345. it('should return undefined when evaluating `null ?? undefined` ', () => {
  346. const runTest = () => {
  347. const prop = extractProp('<div foo={null ?? undefined} />');
  348. const expected = undefined;
  349. const actual = getPropValue(prop);
  350. assert.equal(actual, expected);
  351. };
  352. if (fallbackToBabylon) {
  353. // eslint-disable-next-line no-undef
  354. expect(runTest).toThrow();
  355. } else {
  356. runTest();
  357. }
  358. });
  359. it('should return undefined when evaluating `undefined ?? null` ', () => {
  360. const runTest = () => {
  361. const prop = extractProp('<div foo={undefined ?? null} />');
  362. const expected = null;
  363. const actual = getPropValue(prop);
  364. assert.equal(actual, expected);
  365. };
  366. if (fallbackToBabylon) {
  367. // eslint-disable-next-line no-undef
  368. expect(runTest).toThrow();
  369. } else {
  370. runTest();
  371. }
  372. });
  373. it('should return null when evaluating `null ?? null` ', () => {
  374. const runTest = () => {
  375. const prop = extractProp('<div foo={null ?? null} />');
  376. const expected = null;
  377. const actual = getPropValue(prop);
  378. assert.equal(actual, expected);
  379. };
  380. if (fallbackToBabylon) {
  381. // eslint-disable-next-line no-undef
  382. expect(runTest).toThrow();
  383. } else {
  384. runTest();
  385. }
  386. });
  387. });
  388. describe('Member expression', () => {
  389. it('should return string representation of form `object.property`', () => {
  390. const prop = extractProp('<div foo={bar.baz} />');
  391. const expected = 'bar.baz';
  392. const actual = getPropValue(prop);
  393. assert.equal(actual, expected);
  394. });
  395. it('should evaluate to a correct representation of member expression with a nullable member', () => {
  396. const runTest = () => {
  397. const prop = extractProp('<div foo={bar?.baz} />');
  398. const expected = 'bar?.baz';
  399. const actual = getPropValue(prop);
  400. assert.equal(actual, expected);
  401. };
  402. if (fallbackToBabylon) {
  403. // eslint-disable-next-line no-undef
  404. expect(runTest).toThrow();
  405. } else {
  406. runTest();
  407. }
  408. });
  409. it('should evaluate to a correct representation of optional call expression', () => {
  410. const runTest = () => {
  411. const prop = extractProp('<div foo={bar.baz?.(quux)} />');
  412. const expected = 'bar.baz?.(quux)';
  413. const actual = getPropValue(prop);
  414. assert.equal(actual, expected);
  415. };
  416. if (fallbackToBabylon) {
  417. // eslint-disable-next-line no-undef
  418. expect(runTest).toThrow();
  419. } else {
  420. runTest();
  421. }
  422. });
  423. });
  424. describe('Call expression', () => {
  425. it('should return string representation of callee', () => {
  426. const prop = extractProp('<div foo={bar()} />');
  427. const expected = 'bar()';
  428. const actual = getPropValue(prop);
  429. assert.equal(actual, expected);
  430. });
  431. it('should return string representation of callee', () => {
  432. const prop = extractProp('<div foo={bar.call()} />');
  433. const expected = 'bar.call()';
  434. const actual = getPropValue(prop);
  435. assert.equal(actual, expected);
  436. });
  437. });
  438. describe('Unary expression', () => {
  439. it('should correctly evaluate an expression that prefixes with -', () => {
  440. const prop = extractProp('<div foo={-bar} />');
  441. // -"bar" => NaN
  442. const expected = true;
  443. const actual = Number.isNaN(getPropValue(prop));
  444. assert.equal(actual, expected);
  445. });
  446. it('should correctly evaluate an expression that prefixes with -', () => {
  447. const prop = extractProp('<div foo={-42} />');
  448. const expected = -42;
  449. const actual = getPropValue(prop);
  450. assert.equal(actual, expected);
  451. });
  452. it('should correctly evaluate an expression that prefixes with +', () => {
  453. const prop = extractProp('<div foo={+bar} />');
  454. // +"bar" => NaN
  455. const expected = true;
  456. const actual = Number.isNaN(getPropValue(prop));
  457. assert.equal(actual, expected);
  458. });
  459. it('should correctly evaluate an expression that prefixes with +', () => {
  460. const prop = extractProp('<div foo={+42} />');
  461. const expected = 42;
  462. const actual = getPropValue(prop);
  463. assert.equal(actual, expected);
  464. });
  465. it('should correctly evaluate an expression that prefixes with !', () => {
  466. const prop = extractProp('<div foo={!bar} />');
  467. const expected = false; // !"bar" === false
  468. const actual = getPropValue(prop);
  469. assert.equal(actual, expected);
  470. });
  471. it('should correctly evaluate an expression that prefixes with ~', () => {
  472. const prop = extractProp('<div foo={~bar} />');
  473. const expected = -1; // ~"bar" === -1
  474. const actual = getPropValue(prop);
  475. assert.equal(actual, expected);
  476. });
  477. it('should return true when evaluating `delete foo`', () => {
  478. const prop = extractProp('<div foo={delete x} />');
  479. const expected = true;
  480. const actual = getPropValue(prop);
  481. assert.equal(actual, expected);
  482. });
  483. it('should return undefined when evaluating `void foo`', () => {
  484. const prop = extractProp('<div foo={void x} />');
  485. const expected = undefined;
  486. const actual = getPropValue(prop);
  487. assert.equal(actual, expected);
  488. });
  489. // TODO: We should fix this to check to see if we can evaluate it.
  490. it('should return undefined when evaluating `typeof foo`', () => {
  491. const prop = extractProp('<div foo={typeof x} />');
  492. const expected = undefined;
  493. const actual = getPropValue(prop);
  494. assert.equal(actual, expected);
  495. });
  496. });
  497. describe('Update expression', () => {
  498. it('should correctly evaluate an expression that prefixes with ++', () => {
  499. const prop = extractProp('<div foo={++bar} />');
  500. // ++"bar" => NaN
  501. const expected = true;
  502. const actual = Number.isNaN(getPropValue(prop));
  503. assert.equal(actual, expected);
  504. });
  505. it('should correctly evaluate an expression that prefixes with --', () => {
  506. const prop = extractProp('<div foo={--bar} />');
  507. const expected = true;
  508. const actual = Number.isNaN(getPropValue(prop));
  509. assert.equal(actual, expected);
  510. });
  511. it('should correctly evaluate an expression that suffixes with ++', () => {
  512. const prop = extractProp('<div foo={bar++} />');
  513. // "bar"++ => NaN
  514. const expected = true;
  515. const actual = Number.isNaN(getPropValue(prop));
  516. assert.equal(actual, expected);
  517. });
  518. it('should correctly evaluate an expression that suffixes with --', () => {
  519. const prop = extractProp('<div foo={bar--} />');
  520. const expected = true;
  521. const actual = Number.isNaN(getPropValue(prop));
  522. assert.equal(actual, expected);
  523. });
  524. });
  525. describe('This expression', () => {
  526. it('should return string value `this`', () => {
  527. const prop = extractProp('<div foo={this} />');
  528. const expected = 'this';
  529. const actual = getPropValue(prop);
  530. assert.equal(actual, expected);
  531. });
  532. });
  533. describe('Conditional expression', () => {
  534. it('should evaluate the conditional based on the derived values correctly', () => {
  535. const prop = extractProp('<div foo={bar ? baz : bam} />');
  536. const expected = 'baz';
  537. const actual = getPropValue(prop);
  538. assert.equal(actual, expected);
  539. });
  540. it('should evaluate the conditional based on the derived values correctly', () => {
  541. const prop = extractProp('<div foo={undefined ? baz : bam} />');
  542. const expected = 'bam';
  543. const actual = getPropValue(prop);
  544. assert.equal(actual, expected);
  545. });
  546. it('should evaluate the conditional based on the derived values correctly', () => {
  547. const prop = extractProp('<div foo={(1 > 2) ? baz : bam} />');
  548. const expected = 'bam';
  549. const actual = getPropValue(prop);
  550. assert.equal(actual, expected);
  551. });
  552. });
  553. describe('Binary expression', () => {
  554. it('should evaluate the `==` operator correctly', () => {
  555. const trueProp = extractProp('<div foo={1 == "1"} />');
  556. const falseProp = extractProp('<div foo={1 == bar} />');
  557. const trueVal = getPropValue(trueProp);
  558. const falseVal = getPropValue(falseProp);
  559. assert.equal(true, trueVal);
  560. assert.equal(false, falseVal);
  561. });
  562. it('should evaluate the `!=` operator correctly', () => {
  563. const trueProp = extractProp('<div foo={1 != "2"} />');
  564. const falseProp = extractProp('<div foo={1 != "1"} />');
  565. const trueVal = getPropValue(trueProp);
  566. const falseVal = getPropValue(falseProp);
  567. assert.equal(true, trueVal);
  568. assert.equal(false, falseVal);
  569. });
  570. it('should evaluate the `===` operator correctly', () => {
  571. const trueProp = extractProp('<div foo={1 === 1} />');
  572. const falseProp = extractProp('<div foo={1 === "1"} />');
  573. const trueVal = getPropValue(trueProp);
  574. const falseVal = getPropValue(falseProp);
  575. assert.equal(true, trueVal);
  576. assert.equal(false, falseVal);
  577. });
  578. it('should evaluate the `!==` operator correctly', () => {
  579. const trueProp = extractProp('<div foo={1 !== "1"} />');
  580. const falseProp = extractProp('<div foo={1 !== 1} />');
  581. const trueVal = getPropValue(trueProp);
  582. const falseVal = getPropValue(falseProp);
  583. assert.equal(true, trueVal);
  584. assert.equal(false, falseVal);
  585. });
  586. it('should evaluate the `<` operator correctly', () => {
  587. const trueProp = extractProp('<div foo={1 < 2} />');
  588. const falseProp = extractProp('<div foo={1 < 0} />');
  589. const trueVal = getPropValue(trueProp);
  590. const falseVal = getPropValue(falseProp);
  591. assert.equal(true, trueVal);
  592. assert.equal(false, falseVal);
  593. });
  594. it('should evaluate the `>` operator correctly', () => {
  595. const trueProp = extractProp('<div foo={1 > 0} />');
  596. const falseProp = extractProp('<div foo={1 > 2} />');
  597. const trueVal = getPropValue(trueProp);
  598. const falseVal = getPropValue(falseProp);
  599. assert.equal(true, trueVal);
  600. assert.equal(false, falseVal);
  601. });
  602. it('should evaluate the `<=` operator correctly', () => {
  603. const trueProp = extractProp('<div foo={1 <= 1} />');
  604. const falseProp = extractProp('<div foo={1 <= 0} />');
  605. const trueVal = getPropValue(trueProp);
  606. const falseVal = getPropValue(falseProp);
  607. assert.equal(true, trueVal);
  608. assert.equal(false, falseVal);
  609. });
  610. it('should evaluate the `>=` operator correctly', () => {
  611. const trueProp = extractProp('<div foo={1 >= 1} />');
  612. const falseProp = extractProp('<div foo={1 >= 2} />');
  613. const trueVal = getPropValue(trueProp);
  614. const falseVal = getPropValue(falseProp);
  615. assert.equal(true, trueVal);
  616. assert.equal(false, falseVal);
  617. });
  618. it('should evaluate the `<<` operator correctly', () => {
  619. const prop = extractProp('<div foo={1 << 2} />');
  620. const expected = 4;
  621. const actual = getPropValue(prop);
  622. assert.equal(actual, expected);
  623. });
  624. it('should evaluate the `>>` operator correctly', () => {
  625. const prop = extractProp('<div foo={1 >> 2} />');
  626. const expected = 0;
  627. const actual = getPropValue(prop);
  628. assert.equal(actual, expected);
  629. });
  630. it('should evaluate the `>>>` operator correctly', () => {
  631. const prop = extractProp('<div foo={2 >>> 1} />');
  632. const expected = 1;
  633. const actual = getPropValue(prop);
  634. assert.equal(actual, expected);
  635. });
  636. it('should evaluate the `+` operator correctly', () => {
  637. const prop = extractProp('<div foo={1 + 1} />');
  638. const expected = 2;
  639. const actual = getPropValue(prop);
  640. assert.equal(actual, expected);
  641. });
  642. it('should evaluate the `-` operator correctly', () => {
  643. const prop = extractProp('<div foo={1 - 1} />');
  644. const expected = 0;
  645. const actual = getPropValue(prop);
  646. assert.equal(actual, expected);
  647. });
  648. it('should evaluate the `*` operator correctly', () => {
  649. const prop = extractProp('<div foo={10 * 10} />');
  650. const expected = 100;
  651. const actual = getPropValue(prop);
  652. assert.equal(actual, expected);
  653. });
  654. it('should evaluate the `/` operator correctly', () => {
  655. const prop = extractProp('<div foo={10 / 2} />');
  656. const expected = 5;
  657. const actual = getPropValue(prop);
  658. assert.equal(actual, expected);
  659. });
  660. it('should evaluate the `%` operator correctly', () => {
  661. const prop = extractProp('<div foo={10 % 3} />');
  662. const expected = 1;
  663. const actual = getPropValue(prop);
  664. assert.equal(actual, expected);
  665. });
  666. it('should evaluate the `|` operator correctly', () => {
  667. const prop = extractProp('<div foo={10 | 1} />');
  668. const expected = 11;
  669. const actual = getPropValue(prop);
  670. assert.equal(actual, expected);
  671. });
  672. it('should evaluate the `^` operator correctly', () => {
  673. const prop = extractProp('<div foo={10 ^ 1} />');
  674. const expected = 11;
  675. const actual = getPropValue(prop);
  676. assert.equal(actual, expected);
  677. });
  678. it('should evaluate the `&` operator correctly', () => {
  679. const prop = extractProp('<div foo={10 & 1} />');
  680. const expected = 0;
  681. const actual = getPropValue(prop);
  682. assert.equal(actual, expected);
  683. });
  684. it('should evaluate the `in` operator correctly', () => {
  685. const prop = extractProp('<div foo={foo in bar} />');
  686. const expected = false;
  687. const actual = getPropValue(prop);
  688. assert.equal(actual, expected);
  689. });
  690. it('should evaluate the `instanceof` operator correctly', () => {
  691. const prop = extractProp('<div foo={{} instanceof Object} />');
  692. const expected = true;
  693. const actual = getPropValue(prop);
  694. assert.equal(actual, expected);
  695. });
  696. it('should evaluate the `instanceof` operator when right side is not a function', () => {
  697. const prop = extractProp('<div foo={"bar" instanceof Baz} />');
  698. const expected = false;
  699. const actual = getPropValue(prop);
  700. assert.equal(actual, expected);
  701. });
  702. });
  703. describe('Object expression', () => {
  704. it('should evaluate to a correct representation of the object in props', () => {
  705. const prop = extractProp('<div foo={ { bar: "baz" } } />');
  706. const expected = { bar: 'baz' };
  707. const actual = getPropValue(prop);
  708. assert.deepEqual(actual, expected);
  709. });
  710. it('should evaluate to a correct representation of the object, ignore spread properties', () => {
  711. const prop = extractProp('<div foo={{bar: "baz", ...{baz: "bar", foo: {...{bar: "meh"}}}}} />');
  712. const expected = { bar: 'baz', baz: 'bar', foo: { bar: 'meh' } };
  713. const actual = getPropValue(prop);
  714. assert.deepEqual(actual, expected);
  715. });
  716. it('should evaluate to a correct representation of the object, ignore spread properties', () => {
  717. const prop = extractProp('<div foo={{ pathname: manageRoute, state: {...data}}} />');
  718. const expected = { pathname: 'manageRoute', state: {} };
  719. const actual = getPropValue(prop);
  720. assert.deepEqual(actual, expected);
  721. });
  722. });
  723. describe('New expression', () => {
  724. it('should return a new empty object', () => {
  725. const prop = extractProp('<div foo={new Bar()} />');
  726. const expected = {};
  727. const actual = getPropValue(prop);
  728. assert.deepEqual(actual, expected);
  729. });
  730. });
  731. describe('Array expression', () => {
  732. it('should evaluate to correct representation of the the array in props', () => {
  733. const prop = extractProp('<div foo={["bar", 42, , null]} />');
  734. const expected = ['bar', 42, undefined, null];
  735. const actual = getPropValue(prop);
  736. assert.deepEqual(actual, expected);
  737. });
  738. it('should evaluate to a correct representation of an array with spread elements', () => {
  739. const prop = extractProp('<div foo={[...this.props.params, bar]} />');
  740. const expected = [undefined, 'bar'];
  741. const actual = getPropValue(prop);
  742. assert.deepEqual(actual, expected);
  743. });
  744. });
  745. it('should return an empty array provided an empty array in props', () => {
  746. const prop = extractProp('<div foo={[]} />');
  747. const expected = [];
  748. const actual = getPropValue(prop);
  749. assert.deepEqual(actual, expected);
  750. });
  751. describe('Bind expression', () => {
  752. it('should return string representation of bind function call when object is null', () => {
  753. const prop = extractProp('<div foo={::this.handleClick} />');
  754. const expected = 'this.handleClick.bind(this)';
  755. const actual = getPropValue(prop);
  756. assert.deepEqual(actual, expected);
  757. });
  758. it('should return string representation of bind function call when object is not null', () => {
  759. const prop = extractProp('<div foo={foo::bar} />');
  760. const expected = 'bar.bind(foo)';
  761. const actual = getPropValue(prop);
  762. assert.deepEqual(actual, expected);
  763. });
  764. it('should return string representation of bind function call when binding to object properties', () => {
  765. const prop = extractProp('<div foo={a.b::c} />');
  766. const otherProp = extractProp('<div foo={::a.b.c} />');
  767. const expected = 'a.b.c.bind(a.b)';
  768. const actual = getPropValue(prop);
  769. const otherExpected = 'a.b.c.bind(a.b)';
  770. const otherActual = getPropValue(otherProp);
  771. assert.deepEqual(actual, expected);
  772. assert.deepEqual(otherExpected, otherActual);
  773. });
  774. });
  775. describe('Type Cast Expression', () => {
  776. it('should throw a parsing error', () => {
  777. let counter = 0;
  778. // eslint-disable-next-line no-console
  779. const warnOrig = console.warn;
  780. // eslint-disable-next-line no-console
  781. console.warn = () => {
  782. counter += 1;
  783. };
  784. // eslint-disable-next-line no-undef
  785. expect(() => {
  786. extractProp('<div foo={(this.handleClick: (event: MouseEvent) => void))} />');
  787. }).toThrow();
  788. assert.equal(counter, 1);
  789. // eslint-disable-next-line no-console
  790. console.warn = warnOrig;
  791. });
  792. });
  793. describe('AssignmentExpression', () => {
  794. it('should recognize and extract assignment', () => {
  795. const prop = extractProp('<div foo={foo = bar} />');
  796. const expected = 'foo = bar';
  797. const actual = getPropValue(prop);
  798. assert.deepEqual(actual, expected);
  799. });
  800. it('should recognize and extract combination assignments', () => {
  801. const prop = extractProp('<div foo={foo += bar} />');
  802. const expected = 'foo += bar';
  803. const actual = getPropValue(prop);
  804. assert.deepEqual(actual, expected);
  805. });
  806. });
  807. describeIfNotBabylon('Typescript', () => {
  808. beforeEach(() => {
  809. changePlugins((pls) => [...pls, 'typescript']);
  810. });
  811. it('should return string representation of variable identifier wrapped in a Typescript non-null assertion', () => {
  812. const prop = extractProp('<div foo={bar!} />');
  813. const expected = 'bar!';
  814. const actual = getPropValue(prop);
  815. assert.equal(actual, expected);
  816. });
  817. it('should return string representation of variable identifier wrapped in a deep Typescript non-null assertion', () => {
  818. const prop = extractProp('<div foo={(bar!)!} />');
  819. const expected = '(bar!)!';
  820. const actual = getPropValue(prop);
  821. assert.equal(actual, expected);
  822. });
  823. it('should return string representation of variable identifier wrapped in a Typescript type coercion', () => {
  824. const prop = extractProp('<div foo={bar as any} />');
  825. const expected = 'bar';
  826. const actual = getPropValue(prop);
  827. assert.equal(actual, expected);
  828. });
  829. });
  830. describeIfNotBabylon('TSNonNullExpression', () => {
  831. beforeEach(() => {
  832. changePlugins((pls) => [...pls, 'typescript']);
  833. });
  834. it('should return string representation of a TSNonNullExpression of form `variable!`', () => {
  835. const prop = extractProp('<div foo={bar!} />');
  836. const expected = 'bar!';
  837. const actual = getPropValue(prop);
  838. assert.equal(actual, expected);
  839. });
  840. it('should return string representation of a TSNonNullExpression of form `object!.property`', () => {
  841. const prop = extractProp('<div foo={bar!.bar} />');
  842. const expected = 'bar!.bar';
  843. const actual = getPropValue(prop);
  844. assert.equal(actual, expected);
  845. });
  846. it('should return string representation of a TSNonNullExpression of form `function()!.property`', () => {
  847. const prop = extractProp('<div foo={bar()!.bar} />');
  848. const expected = 'bar()!.bar';
  849. const actual = getPropValue(prop);
  850. assert.equal(actual, expected);
  851. });
  852. it('should return string representation of a TSNonNullExpression of form `object!.property!`', () => {
  853. const prop = extractProp('<div foo={bar!.bar!} />');
  854. const actual = getPropValue(prop);
  855. const expected = 'bar!.bar!';
  856. assert.equal(actual, expected);
  857. });
  858. it('should return string representation of a TSNonNullExpression of form `object.property!`', () => {
  859. const prop = extractProp('<div foo={bar.bar!} />');
  860. const actual = getPropValue(prop);
  861. const expected = 'bar.bar!';
  862. assert.equal(actual, expected);
  863. });
  864. it('should return string representation of a TSNonNullExpression of form `object.property.property!`', () => {
  865. const prop = extractProp('<div foo={bar.bar.bar!} />');
  866. const actual = getPropValue(prop);
  867. const expected = 'bar.bar.bar!';
  868. assert.equal(actual, expected);
  869. });
  870. it('should return string representation of a TSNonNullExpression of form `object!.property.property!`', () => {
  871. const prop = extractProp('<div foo={bar!.bar.bar!} />');
  872. const actual = getPropValue(prop);
  873. const expected = 'bar!.bar.bar!';
  874. assert.equal(actual, expected);
  875. });
  876. it('should return string representation of an object wrapped in a deep Typescript non-null assertion', () => {
  877. const prop = extractProp('<div foo={(bar!.bar)!} />');
  878. const expected = '(bar!.bar)!';
  879. const actual = getPropValue(prop);
  880. assert.equal(actual, expected);
  881. });
  882. it('should return string representation of a cast wrapped in a deep Typescript non-null assertion', () => {
  883. const prop = extractProp('<div foo={(bar as Bar).baz!} />');
  884. const actual = getPropValue(prop);
  885. const expected = 'bar.baz!';
  886. assert.equal(actual, expected);
  887. });
  888. it('should return string representation of an object wrapped in a deep Typescript non-null assertion', () => {
  889. const prop = extractProp('<div foo={(bar.bar)!} />');
  890. const expected = '(bar.bar)!';
  891. const actual = getPropValue(prop);
  892. assert.equal(actual, expected);
  893. });
  894. it('should return string representation of an object wrapped in a deep Typescript non-null assertion', () => {
  895. const prop = extractProp('<div foo={(bar!.bar.bar!)!} />');
  896. const expected = '(bar!.bar.bar!)!';
  897. const actual = getPropValue(prop);
  898. assert.equal(actual, expected);
  899. });
  900. it('should return string representation of variable identifier wrapped in a deep Typescript non-null assertion', () => {
  901. const prop = extractProp('<div foo={(bar!)!} />');
  902. const expected = '(bar!)!';
  903. const actual = getPropValue(prop);
  904. assert.equal(actual, expected);
  905. });
  906. it('should work with a this.props value', () => {
  907. const prop = extractProp('<a foo={this.props.href!}>Download</a>');
  908. const expected = 'this.props.href!';
  909. const actual = getPropValue(prop);
  910. assert.equal(actual, expected);
  911. });
  912. it('should correctly evaluate a bracketed navigation expression that prefixes with !', () => {
  913. const prop = extractProp('<Link foo={data![0].url} />');
  914. const expected = 'data![0].url';
  915. const actual = getPropValue(prop);
  916. assert.equal(actual, expected);
  917. });
  918. it('works with an optional chain with an `as`', () => {
  919. const prop = extractProp('<img src={images?.footer as string} />', 'src');
  920. const expected = 'images?.footer';
  921. const actual = getPropValue(prop, 'src');
  922. assert.equal(actual, expected);
  923. });
  924. });
  925. describe('JSX empty expression', () => {
  926. it('should work with an empty expression', () => {
  927. const prop = extractProp('<div>\n{/* Hello there */}\n</div>', 'children');
  928. const expected = undefined;
  929. const actual = getPropValue(prop);
  930. assert.equal(actual, expected);
  931. });
  932. });
  933. });