test_var_operations.py 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975
  1. """Integration tests for var operations."""
  2. from typing import Generator
  3. import pytest
  4. from selenium.webdriver.common.by import By
  5. from reflex.testing import AppHarness
  6. def VarOperations():
  7. """App with var operations."""
  8. from typing import TypedDict
  9. import reflex as rx
  10. from reflex.vars.base import LiteralVar
  11. from reflex.vars.sequence import ArrayVar
  12. class Object(rx.Base):
  13. name: str = "hello"
  14. optional_none: str | None = None
  15. optional_str: str | None = "hello"
  16. class Person(TypedDict):
  17. name: str
  18. age: int
  19. class VarOperationState(rx.State):
  20. int_var1: rx.Field[int] = rx.field(10)
  21. int_var2: rx.Field[int] = rx.field(5)
  22. int_var3: rx.Field[int] = rx.field(7)
  23. float_var1: rx.Field[float] = rx.field(10.5)
  24. float_var2: rx.Field[float] = rx.field(5.5)
  25. list1: rx.Field[list] = rx.field([1, 2])
  26. list2: rx.Field[list] = rx.field([3, 4])
  27. list3: rx.Field[list] = rx.field(["first", "second", "third"])
  28. list4: rx.Field[list] = rx.field([Object(name="obj_1"), Object(name="obj_2")])
  29. optional_list: rx.Field[list | None] = rx.field(None)
  30. optional_dict: rx.Field[dict[str, str] | None] = rx.field(None)
  31. optional_list_value: rx.Field[list[str] | None] = rx.field(["red", "yellow"])
  32. optional_dict_value: rx.Field[dict[str, str] | None] = rx.field({"name": "red"})
  33. str_var1: rx.Field[str] = rx.field("first")
  34. str_var2: rx.Field[str] = rx.field("second")
  35. str_var3: rx.Field[str] = rx.field("ThIrD")
  36. str_var4: rx.Field[str] = rx.field("a long string")
  37. dict1: rx.Field[dict[int, int]] = rx.field({1: 2})
  38. dict2: rx.Field[dict[int, int]] = rx.field({3: 4})
  39. html_str: rx.Field[str] = rx.field("<div>hello</div>")
  40. people: rx.Field[list[Person]] = rx.field(
  41. [{"name": "Alice", "age": 30}, {"name": "Bob", "age": 25}]
  42. )
  43. obj: rx.Field[Object] = rx.field(Object())
  44. app = rx.App()
  45. @rx.memo
  46. def memo_comp(list1: list[int], int_var1: int, id: str):
  47. return rx.text(list1, int_var1, id=id)
  48. @rx.memo
  49. def memo_comp_nested(int_var2: int, id: str):
  50. return memo_comp(list1=[3, 4], int_var1=int_var2, id=id)
  51. @app.add_page
  52. def index():
  53. return rx.vstack(
  54. rx.el.input(
  55. id="token",
  56. value=VarOperationState.router.session.client_token,
  57. is_read_only=True,
  58. ),
  59. # INT INT
  60. rx.text(
  61. VarOperationState.int_var1 + VarOperationState.int_var2,
  62. id="int_add_int",
  63. ),
  64. rx.text(
  65. VarOperationState.int_var1 * VarOperationState.int_var2,
  66. id="int_mult_int",
  67. ),
  68. rx.text(
  69. VarOperationState.int_var1 - VarOperationState.int_var2,
  70. id="int_sub_int",
  71. ),
  72. rx.text(
  73. VarOperationState.int_var1**VarOperationState.int_var2,
  74. id="int_exp_int",
  75. ),
  76. rx.text(
  77. VarOperationState.int_var1 / VarOperationState.int_var2,
  78. id="int_div_int",
  79. ),
  80. rx.text(
  81. VarOperationState.int_var1 // VarOperationState.int_var3,
  82. id="int_floor_int",
  83. ),
  84. rx.text(
  85. VarOperationState.int_var1 % VarOperationState.int_var2,
  86. id="int_mod_int",
  87. ),
  88. rx.text(
  89. VarOperationState.int_var1 | VarOperationState.int_var2,
  90. id="int_or_int",
  91. ),
  92. rx.text(
  93. (VarOperationState.int_var1 > VarOperationState.int_var2).to_string(),
  94. id="int_gt_int",
  95. ),
  96. rx.text(
  97. (VarOperationState.int_var1 < VarOperationState.int_var2).to_string(),
  98. id="int_lt_int",
  99. ),
  100. rx.text(
  101. (VarOperationState.int_var1 >= VarOperationState.int_var2).to_string(),
  102. id="int_gte_int",
  103. ),
  104. rx.text(
  105. (VarOperationState.int_var1 <= VarOperationState.int_var2).to_string(),
  106. id="int_lte_int",
  107. ),
  108. rx.text(
  109. VarOperationState.int_var1 & VarOperationState.int_var2,
  110. id="int_and_int",
  111. ),
  112. rx.text(
  113. (VarOperationState.int_var1 | VarOperationState.int_var2).to_string(),
  114. id="int_or_int",
  115. ),
  116. rx.text(
  117. (VarOperationState.int_var1 == VarOperationState.int_var2).to_string(),
  118. id="int_eq_int",
  119. ),
  120. rx.text(
  121. (VarOperationState.int_var1 != VarOperationState.int_var2).to_string(),
  122. id="int_neq_int",
  123. ),
  124. # INT FLOAT OR FLOAT INT
  125. rx.text(
  126. VarOperationState.float_var1 + VarOperationState.int_var2,
  127. id="float_add_int",
  128. ),
  129. rx.text(
  130. VarOperationState.float_var1 * VarOperationState.int_var2,
  131. id="float_mult_int",
  132. ),
  133. rx.text(
  134. VarOperationState.float_var1 - VarOperationState.int_var2,
  135. id="float_sub_int",
  136. ),
  137. rx.text(
  138. VarOperationState.float_var1**VarOperationState.int_var2,
  139. id="float_exp_int",
  140. ),
  141. rx.text(
  142. VarOperationState.float_var1 / VarOperationState.int_var2,
  143. id="float_div_int",
  144. ),
  145. rx.text(
  146. VarOperationState.float_var1 // VarOperationState.int_var3,
  147. id="float_floor_int",
  148. ),
  149. rx.text(
  150. VarOperationState.float_var1 % VarOperationState.int_var2,
  151. id="float_mod_int",
  152. ),
  153. rx.text(
  154. (VarOperationState.float_var1 > VarOperationState.int_var2).to_string(),
  155. id="float_gt_int",
  156. ),
  157. rx.text(
  158. (VarOperationState.float_var1 < VarOperationState.int_var2).to_string(),
  159. id="float_lt_int",
  160. ),
  161. rx.text(
  162. (
  163. VarOperationState.float_var1 >= VarOperationState.int_var2
  164. ).to_string(),
  165. id="float_gte_int",
  166. ),
  167. rx.text(
  168. (
  169. VarOperationState.float_var1 <= VarOperationState.int_var2
  170. ).to_string(),
  171. id="float_lte_int",
  172. ),
  173. rx.text(
  174. (
  175. VarOperationState.float_var1 == VarOperationState.int_var2
  176. ).to_string(),
  177. id="float_eq_int",
  178. ),
  179. rx.text(
  180. (
  181. VarOperationState.float_var1 != VarOperationState.int_var2
  182. ).to_string(),
  183. id="float_neq_int",
  184. ),
  185. rx.text(
  186. (VarOperationState.float_var1 & VarOperationState.int_var2).to_string(),
  187. id="float_and_int",
  188. ),
  189. rx.text(
  190. (VarOperationState.float_var1 | VarOperationState.int_var2).to_string(),
  191. id="float_or_int",
  192. ),
  193. # INT, DICT
  194. rx.text(
  195. (VarOperationState.int_var1 | VarOperationState.dict1).to_string(),
  196. id="int_or_dict",
  197. ),
  198. rx.text(
  199. (VarOperationState.int_var1 & VarOperationState.dict1).to_string(),
  200. id="int_and_dict",
  201. ),
  202. rx.text(
  203. (VarOperationState.int_var1 == VarOperationState.dict1).to_string(),
  204. id="int_eq_dict",
  205. ),
  206. rx.text(
  207. (VarOperationState.int_var1 != VarOperationState.dict1).to_string(),
  208. id="int_neq_dict",
  209. ),
  210. # FLOAT FLOAT
  211. rx.text(
  212. VarOperationState.float_var1 + VarOperationState.float_var2,
  213. id="float_add_float",
  214. ),
  215. rx.text(
  216. VarOperationState.float_var1 * VarOperationState.float_var2,
  217. id="float_mult_float",
  218. ),
  219. rx.text(
  220. VarOperationState.float_var1 - VarOperationState.float_var2,
  221. id="float_sub_float",
  222. ),
  223. rx.text(
  224. VarOperationState.float_var1**VarOperationState.float_var2,
  225. id="float_exp_float",
  226. ),
  227. rx.text(
  228. VarOperationState.float_var1 / VarOperationState.float_var2,
  229. id="float_div_float",
  230. ),
  231. rx.text(
  232. VarOperationState.float_var1 // VarOperationState.float_var2,
  233. id="float_floor_float",
  234. ),
  235. rx.text(
  236. VarOperationState.float_var1 % VarOperationState.float_var2,
  237. id="float_mod_float",
  238. ),
  239. rx.text(
  240. (
  241. VarOperationState.float_var1 > VarOperationState.float_var2
  242. ).to_string(),
  243. id="float_gt_float",
  244. ),
  245. rx.text(
  246. (
  247. VarOperationState.float_var1 < VarOperationState.float_var2
  248. ).to_string(),
  249. id="float_lt_float",
  250. ),
  251. rx.text(
  252. (
  253. VarOperationState.float_var1 >= VarOperationState.float_var2
  254. ).to_string(),
  255. id="float_gte_float",
  256. ),
  257. rx.text(
  258. (
  259. VarOperationState.float_var1 <= VarOperationState.float_var2
  260. ).to_string(),
  261. id="float_lte_float",
  262. ),
  263. rx.text(
  264. (
  265. VarOperationState.float_var1 == VarOperationState.float_var2
  266. ).to_string(),
  267. id="float_eq_float",
  268. ),
  269. rx.text(
  270. (
  271. VarOperationState.float_var1 != VarOperationState.float_var2
  272. ).to_string(),
  273. id="float_neq_float",
  274. ),
  275. rx.text(
  276. (
  277. VarOperationState.float_var1 & VarOperationState.float_var2
  278. ).to_string(),
  279. id="float_and_float",
  280. ),
  281. rx.text(
  282. (
  283. VarOperationState.float_var1 | VarOperationState.float_var2
  284. ).to_string(),
  285. id="float_or_float",
  286. ),
  287. # FLOAT STR
  288. rx.text(
  289. VarOperationState.float_var1 | VarOperationState.str_var1,
  290. id="float_or_str",
  291. ),
  292. rx.text(
  293. VarOperationState.float_var1 & VarOperationState.str_var1,
  294. id="float_and_str",
  295. ),
  296. rx.text(
  297. (
  298. VarOperationState.float_var1 == VarOperationState.str_var1
  299. ).to_string(),
  300. id="float_eq_str",
  301. ),
  302. rx.text(
  303. (
  304. VarOperationState.float_var1 != VarOperationState.str_var1
  305. ).to_string(),
  306. id="float_neq_str",
  307. ),
  308. # FLOAT LIST
  309. rx.text(
  310. (VarOperationState.float_var1 | VarOperationState.list1).to_string(),
  311. id="float_or_list",
  312. ),
  313. rx.text(
  314. (VarOperationState.float_var1 & VarOperationState.list1).to_string(),
  315. id="float_and_list",
  316. ),
  317. rx.text(
  318. (VarOperationState.float_var1 == VarOperationState.list1).to_string(),
  319. id="float_eq_list",
  320. ),
  321. rx.text(
  322. (VarOperationState.float_var1 != VarOperationState.list1).to_string(),
  323. id="float_neq_list",
  324. ),
  325. # FLOAT DICT
  326. rx.text(
  327. (VarOperationState.float_var1 | VarOperationState.dict1).to_string(),
  328. id="float_or_dict",
  329. ),
  330. rx.text(
  331. (VarOperationState.float_var1 & VarOperationState.dict1).to_string(),
  332. id="float_and_dict",
  333. ),
  334. rx.text(
  335. (VarOperationState.float_var1 == VarOperationState.dict1).to_string(),
  336. id="float_eq_dict",
  337. ),
  338. rx.text(
  339. (VarOperationState.float_var1 != VarOperationState.dict1).to_string(),
  340. id="float_neq_dict",
  341. ),
  342. # STR STR
  343. rx.text(
  344. VarOperationState.str_var1 + VarOperationState.str_var2,
  345. id="str_add_str",
  346. ),
  347. rx.text(
  348. (VarOperationState.str_var1 > VarOperationState.str_var2).to_string(),
  349. id="str_gt_str",
  350. ),
  351. rx.text(
  352. (VarOperationState.str_var1 < VarOperationState.str_var2).to_string(),
  353. id="str_lt_str",
  354. ),
  355. rx.text(
  356. (VarOperationState.str_var1 >= VarOperationState.str_var2).to_string(),
  357. id="str_gte_str",
  358. ),
  359. rx.text(
  360. (VarOperationState.str_var1 <= VarOperationState.str_var2).to_string(),
  361. id="str_lte_str",
  362. ),
  363. rx.text(
  364. (
  365. VarOperationState.float_var1 == VarOperationState.float_var2
  366. ).to_string(),
  367. id="str_eq_str",
  368. ),
  369. rx.text(
  370. (
  371. VarOperationState.float_var1 != VarOperationState.float_var2
  372. ).to_string(),
  373. id="str_neq_str",
  374. ),
  375. rx.text(
  376. VarOperationState.str_var1.contains("fir").to_string(),
  377. id="str_contains",
  378. ),
  379. rx.text(
  380. VarOperationState.str_var1 | VarOperationState.str_var1,
  381. id="str_or_str",
  382. ),
  383. rx.text(
  384. VarOperationState.str_var1 & VarOperationState.str_var2,
  385. id="str_and_str",
  386. ),
  387. # STR, INT
  388. rx.text(
  389. VarOperationState.str_var1 * VarOperationState.int_var2,
  390. id="str_mult_int",
  391. ),
  392. rx.text(
  393. VarOperationState.str_var1 & VarOperationState.int_var2,
  394. id="str_and_int",
  395. ),
  396. rx.text(
  397. VarOperationState.str_var1 | VarOperationState.int_var2,
  398. id="str_or_int",
  399. ),
  400. rx.text(
  401. (VarOperationState.str_var1 == VarOperationState.int_var1).to_string(),
  402. id="str_eq_int",
  403. ),
  404. rx.text(
  405. (VarOperationState.str_var1 != VarOperationState.int_var1).to_string(),
  406. id="str_neq_int",
  407. ),
  408. # STR, LIST
  409. rx.text(
  410. VarOperationState.str_var1 | VarOperationState.list1,
  411. id="str_or_list",
  412. ),
  413. rx.text(
  414. (VarOperationState.str_var1 & VarOperationState.list1).to_string(),
  415. id="str_and_list",
  416. ),
  417. rx.text(
  418. (VarOperationState.str_var1 == VarOperationState.list1).to_string(),
  419. id="str_eq_list",
  420. ),
  421. rx.text(
  422. (VarOperationState.str_var1 != VarOperationState.list1).to_string(),
  423. id="str_neq_list",
  424. ),
  425. # STR, DICT
  426. rx.text(
  427. VarOperationState.str_var1 | VarOperationState.dict1,
  428. id="str_or_dict",
  429. ),
  430. rx.text(
  431. (VarOperationState.str_var1 & VarOperationState.dict1).to_string(),
  432. id="str_and_dict",
  433. ),
  434. rx.text(
  435. (VarOperationState.str_var1 == VarOperationState.dict1).to_string(),
  436. id="str_eq_dict",
  437. ),
  438. rx.text(
  439. (VarOperationState.str_var1 != VarOperationState.dict1).to_string(),
  440. id="str_neq_dict",
  441. ),
  442. # LIST, LIST
  443. rx.text(
  444. (VarOperationState.list1 + VarOperationState.list2).to_string(),
  445. id="list_add_list",
  446. ),
  447. rx.text(
  448. (VarOperationState.list1 & VarOperationState.list2).to_string(),
  449. id="list_and_list",
  450. ),
  451. rx.text(
  452. (VarOperationState.list1 | VarOperationState.list2).to_string(),
  453. id="list_or_list",
  454. ),
  455. rx.text(
  456. (VarOperationState.list1 > VarOperationState.list2).to_string(),
  457. id="list_gt_list",
  458. ),
  459. rx.text(
  460. (VarOperationState.list1 < VarOperationState.list2).to_string(),
  461. id="list_lt_list",
  462. ),
  463. rx.text(
  464. (VarOperationState.list1 >= VarOperationState.list2).to_string(),
  465. id="list_gte_list",
  466. ),
  467. rx.text(
  468. (VarOperationState.list1 <= VarOperationState.list2).to_string(),
  469. id="list_lte_list",
  470. ),
  471. rx.text(
  472. (VarOperationState.list1 == VarOperationState.list2).to_string(),
  473. id="list_eq_list",
  474. ),
  475. rx.text(
  476. (VarOperationState.list1 != VarOperationState.list2).to_string(),
  477. id="list_neq_list",
  478. ),
  479. rx.text(
  480. VarOperationState.list1.contains(1).to_string(),
  481. id="list_contains",
  482. ),
  483. rx.text(VarOperationState.list4.pluck("name").to_string(), id="list_pluck"),
  484. rx.text(VarOperationState.list1.reverse().to_string(), id="list_reverse"),
  485. # LIST, INT
  486. rx.text(
  487. (VarOperationState.list1 * VarOperationState.int_var2).to_string(),
  488. id="list_mult_int",
  489. ),
  490. rx.text(
  491. (VarOperationState.list1 | VarOperationState.int_var1).to_string(),
  492. id="list_or_int",
  493. ),
  494. rx.text(
  495. (VarOperationState.list1 & VarOperationState.int_var1).to_string(),
  496. id="list_and_int",
  497. ),
  498. rx.text(
  499. (VarOperationState.list1 == VarOperationState.int_var1).to_string(),
  500. id="list_eq_int",
  501. ),
  502. rx.text(
  503. (VarOperationState.list1 != VarOperationState.int_var1).to_string(),
  504. id="list_neq_int",
  505. ),
  506. # LIST, DICT
  507. rx.text(
  508. (VarOperationState.list1 | VarOperationState.dict1).to_string(),
  509. id="list_or_dict",
  510. ),
  511. rx.text(
  512. (VarOperationState.list1 & VarOperationState.dict1).to_string(),
  513. id="list_and_dict",
  514. ),
  515. rx.text(
  516. (VarOperationState.list1 == VarOperationState.dict1).to_string(),
  517. id="list_eq_dict",
  518. ),
  519. rx.text(
  520. (VarOperationState.list1 != VarOperationState.dict1).to_string(),
  521. id="list_neq_dict",
  522. ),
  523. # DICT, DICT
  524. rx.text(
  525. (VarOperationState.dict1 | VarOperationState.dict2).to_string(),
  526. id="dict_or_dict",
  527. ),
  528. rx.text(
  529. (VarOperationState.dict1 & VarOperationState.dict2).to_string(),
  530. id="dict_and_dict",
  531. ),
  532. rx.text(
  533. (VarOperationState.dict1 == VarOperationState.dict2).to_string(),
  534. id="dict_eq_dict",
  535. ),
  536. rx.text(
  537. (VarOperationState.dict1 != VarOperationState.dict2).to_string(),
  538. id="dict_neq_dict",
  539. ),
  540. rx.text(
  541. VarOperationState.dict1.contains(1).to_string(),
  542. id="dict_contains",
  543. ),
  544. rx.text(VarOperationState.str_var3.lower(), id="str_lower"),
  545. rx.text(VarOperationState.str_var3.upper(), id="str_upper"),
  546. rx.text(VarOperationState.str_var4.split(" ").to_string(), id="str_split"),
  547. rx.text(VarOperationState.list3.join(""), id="list_join"),
  548. rx.text(VarOperationState.list3.join(","), id="list_join_comma"),
  549. # Index from an op var
  550. rx.text(
  551. VarOperationState.list3[VarOperationState.int_var1 % 3],
  552. id="list_index_mod",
  553. ),
  554. rx.html(
  555. VarOperationState.html_str,
  556. id="html_str",
  557. ),
  558. rx.el.mark("second"),
  559. rx.text(ArrayVar.range(2, 5).join(","), id="list_join_range1"),
  560. rx.text(ArrayVar.range(2, 10, 2).join(","), id="list_join_range2"),
  561. rx.text(ArrayVar.range(5, 0, -1).join(","), id="list_join_range3"),
  562. rx.text(ArrayVar.range(0, 3).join(","), id="list_join_range4"),
  563. rx.box(
  564. rx.foreach(
  565. ArrayVar.range(0, 2),
  566. lambda x: rx.text(VarOperationState.list1[x], as_="p"),
  567. ),
  568. id="foreach_list_arg",
  569. ),
  570. rx.box(
  571. rx.foreach(
  572. ArrayVar.range(0, 2),
  573. lambda x, ix: rx.text(VarOperationState.list1[ix], as_="p"),
  574. ),
  575. id="foreach_list_ix",
  576. ),
  577. rx.box(
  578. rx.foreach(
  579. LiteralVar.create(list(range(0, 3))).to(ArrayVar, list[int]),
  580. lambda x: rx.foreach(
  581. ArrayVar.range(x),
  582. lambda y: rx.text(VarOperationState.list1[y], as_="p"),
  583. ),
  584. ),
  585. id="foreach_list_nested",
  586. ),
  587. memo_comp(
  588. list1=VarOperationState.list1,
  589. int_var1=VarOperationState.int_var1,
  590. id="memo_comp",
  591. ),
  592. memo_comp_nested(
  593. int_var2=VarOperationState.int_var2,
  594. id="memo_comp_nested",
  595. ),
  596. # foreach in a match
  597. rx.box(
  598. rx.match(
  599. VarOperationState.list3.length(),
  600. (0, rx.text("No choices")),
  601. (1, rx.text("One choice")),
  602. rx.foreach(VarOperationState.list3, lambda choice: rx.text(choice)),
  603. ),
  604. id="foreach_in_match",
  605. ),
  606. # Literal range var in a foreach
  607. rx.box(rx.foreach(range(42, 80, 27), rx.text.span), id="range_in_foreach1"),
  608. rx.box(rx.foreach(range(42, 80, 3), rx.text.span), id="range_in_foreach2"),
  609. rx.box(rx.foreach(range(42, 20, -6), rx.text.span), id="range_in_foreach3"),
  610. rx.box(rx.foreach(range(42, 43, 5), rx.text.span), id="range_in_foreach4"),
  611. # Literal dict in a foreach
  612. rx.box(rx.foreach({"a": 1, "b": 2}, rx.text.span), id="dict_in_foreach1"),
  613. # State Var dict in a foreach
  614. rx.box(
  615. rx.foreach(VarOperationState.dict1, rx.text.span),
  616. id="dict_in_foreach2",
  617. ),
  618. rx.box(
  619. rx.foreach(
  620. VarOperationState.dict1.merge(VarOperationState.dict2),
  621. rx.text.span,
  622. ),
  623. id="dict_in_foreach3",
  624. ),
  625. rx.box(
  626. rx.foreach("abcdef", lambda x: rx.text.span(x + " ")),
  627. id="str_in_foreach",
  628. ),
  629. rx.box(
  630. rx.foreach(VarOperationState.str_var1, lambda x: rx.text.span(x + " ")),
  631. id="str_var_in_foreach",
  632. ),
  633. rx.box(
  634. rx.foreach(
  635. VarOperationState.people,
  636. lambda person: rx.text.span(
  637. "Hello " + person["name"], person["age"] + 3
  638. ),
  639. ),
  640. id="typed_dict_in_foreach",
  641. ),
  642. rx.box(
  643. rx.foreach(VarOperationState.optional_list, rx.text.span),
  644. id="optional_list",
  645. ),
  646. rx.box(
  647. rx.foreach(VarOperationState.optional_dict, rx.text.span),
  648. id="optional_dict",
  649. ),
  650. rx.box(
  651. rx.foreach(VarOperationState.optional_list_value, rx.text.span),
  652. id="optional_list_value",
  653. ),
  654. rx.box(
  655. rx.foreach(VarOperationState.optional_dict_value, rx.text.span),
  656. id="optional_dict_value",
  657. ),
  658. rx.box(
  659. rx.text.span(f"{rx.Var.create(13212312312.1231231)}"),
  660. id="float_format",
  661. ),
  662. rx.box(
  663. rx.text.span(f"{rx.Var.create(13212312312.1231231):.0f}"),
  664. id="float_format_0f",
  665. ),
  666. rx.box(
  667. rx.text.span(f"{rx.Var.create(13212312312.1231231):.1f}"),
  668. id="float_format_1f",
  669. ),
  670. rx.box(
  671. rx.text.span(f"{rx.Var.create(13212312312.1231231):.2f}"),
  672. id="float_format_2f",
  673. ),
  674. rx.box(
  675. rx.text.span(f"{rx.Var.create(13212312312.1231231):,}"),
  676. id="float_format_comma",
  677. ),
  678. rx.box(
  679. rx.text.span(f"{rx.Var.create(13212312312.1231231):_}"),
  680. id="float_format_underscore",
  681. ),
  682. rx.box(
  683. rx.text.span(f"{rx.Var.create(13212312312.1231231):,.0f}"),
  684. id="float_format_comma_0f",
  685. ),
  686. rx.box(
  687. rx.text.span(f"{rx.Var.create(13212312312.1231231):,.1f}"),
  688. id="float_format_comma_1f",
  689. ),
  690. rx.box(
  691. rx.text.span(f"{rx.Var.create(13212312312.1231231):,.2f}"),
  692. id="float_format_comma_2f",
  693. ),
  694. rx.box(
  695. rx.text.span(f"{rx.Var.create(13212312312.1231231):_.0f}"),
  696. id="float_format_underscore_0f",
  697. ),
  698. rx.box(
  699. rx.text.span(f"{rx.Var.create(13212312312.1231231):_.1f}"),
  700. id="float_format_underscore_1f",
  701. ),
  702. rx.box(
  703. rx.text.span(f"{rx.Var.create(13212312312.1231231):_.2f}"),
  704. id="float_format_underscore_2f",
  705. ),
  706. # ObjectVar
  707. rx.box(
  708. rx.text(VarOperationState.obj.name),
  709. id="obj_name",
  710. ),
  711. rx.box(
  712. rx.text(VarOperationState.obj.optional_none),
  713. id="obj_optional_none",
  714. ),
  715. rx.box(
  716. rx.text(VarOperationState.obj.optional_str),
  717. id="obj_optional_str",
  718. ),
  719. rx.box(
  720. rx.text(VarOperationState.obj.get("optional_none")),
  721. id="obj_optional_none_get_none",
  722. ),
  723. rx.box(
  724. rx.text(VarOperationState.obj.get("optional_none", "foo")),
  725. id="obj_optional_none_get_foo",
  726. ),
  727. )
  728. @pytest.fixture(scope="module")
  729. def var_operations(tmp_path_factory) -> Generator[AppHarness, None, None]:
  730. """Start VarOperations app at tmp_path via AppHarness.
  731. Args:
  732. tmp_path_factory: pytest tmp_path_factory fixture
  733. Yields:
  734. running AppHarness instance
  735. """
  736. with AppHarness.create(
  737. root=tmp_path_factory.mktemp("var_operations"),
  738. app_source=VarOperations,
  739. ) as harness:
  740. assert harness.app_instance is not None, "app is not running"
  741. yield harness
  742. @pytest.fixture
  743. def driver(var_operations: AppHarness):
  744. """Get an instance of the browser open to the var operations app.
  745. Args:
  746. var_operations: harness for VarOperations app
  747. Yields:
  748. WebDriver instance.
  749. """
  750. driver = var_operations.frontend()
  751. try:
  752. token_input = driver.find_element(By.ID, "token")
  753. assert token_input
  754. # wait for the backend connection to send the token
  755. token = var_operations.poll_for_value(token_input)
  756. assert token is not None
  757. yield driver
  758. finally:
  759. driver.quit()
  760. def test_var_operations(driver, var_operations: AppHarness):
  761. """Test that the var operations produce the right results.
  762. Args:
  763. driver: selenium WebDriver open to the app
  764. var_operations: AppHarness for the var operations app
  765. """
  766. tests = [
  767. # int, int
  768. ("int_add_int", "15"),
  769. ("int_mult_int", "50"),
  770. ("int_sub_int", "5"),
  771. ("int_exp_int", "100000"),
  772. ("int_div_int", "2"),
  773. ("int_floor_int", "1"),
  774. ("int_mod_int", "0"),
  775. ("int_gt_int", "true"),
  776. ("int_lt_int", "false"),
  777. ("int_gte_int", "true"),
  778. ("int_lte_int", "false"),
  779. ("int_and_int", "5"),
  780. ("int_or_int", "10"),
  781. ("int_eq_int", "false"),
  782. ("int_neq_int", "true"),
  783. # int, float
  784. ("float_add_int", "15.5"),
  785. ("float_mult_int", "52.5"),
  786. ("float_sub_int", "5.5"),
  787. ("float_exp_int", "127628.15625"),
  788. ("float_div_int", "2.1"),
  789. ("float_floor_int", "1"),
  790. ("float_mod_int", "0.5"),
  791. ("float_gt_int", "true"),
  792. ("float_lt_int", "false"),
  793. ("float_gte_int", "true"),
  794. ("float_lte_int", "false"),
  795. ("float_eq_int", "false"),
  796. ("float_neq_int", "true"),
  797. ("float_and_int", "5"),
  798. ("float_or_int", "10.5"),
  799. # int, dict
  800. ("int_or_dict", "10"),
  801. ("int_and_dict", '{"1":2}'),
  802. ("int_eq_dict", "false"),
  803. ("int_neq_dict", "true"),
  804. # float, float
  805. ("float_add_float", "16"),
  806. ("float_mult_float", "57.75"),
  807. ("float_sub_float", "5"),
  808. ("float_exp_float", "413562.49323606625"),
  809. ("float_div_float", "1.9090909090909092"),
  810. ("float_floor_float", "1"),
  811. ("float_mod_float", "5"),
  812. ("float_gt_float", "true"),
  813. ("float_lt_float", "false"),
  814. ("float_gte_float", "true"),
  815. ("float_lte_float", "false"),
  816. ("float_eq_float", "false"),
  817. ("float_neq_float", "true"),
  818. ("float_and_float", "5.5"),
  819. ("float_or_float", "10.5"),
  820. # float, str
  821. ("float_or_str", "10.5"),
  822. ("float_and_str", "first"),
  823. ("float_eq_str", "false"),
  824. ("float_neq_str", "true"),
  825. # float, list
  826. ("float_or_list", "10.5"),
  827. ("float_and_list", "[1,2]"),
  828. ("float_eq_list", "false"),
  829. ("float_neq_list", "true"),
  830. # float, dict
  831. ("float_or_dict", "10.5"),
  832. ("float_and_dict", '{"1":2}'),
  833. ("float_eq_dict", "false"),
  834. ("float_neq_dict", "true"),
  835. # str, str
  836. ("str_add_str", "firstsecond"),
  837. ("str_gt_str", "false"),
  838. ("str_lt_str", "true"),
  839. ("str_gte_str", "false"),
  840. ("str_lte_str", "true"),
  841. ("str_eq_str", "false"),
  842. ("str_neq_str", "true"),
  843. ("str_and_str", "second"),
  844. ("str_or_str", "first"),
  845. ("str_contains", "true"),
  846. ("str_lower", "third"),
  847. ("str_upper", "THIRD"),
  848. ("str_split", '["a","long","string"]'),
  849. # str, int
  850. ("str_mult_int", "firstfirstfirstfirstfirst"),
  851. ("str_and_int", "5"),
  852. ("str_or_int", "first"),
  853. ("str_eq_int", "false"),
  854. ("str_neq_int", "true"),
  855. # str, list
  856. ("str_and_list", "[1,2]"),
  857. ("str_or_list", "first"),
  858. ("str_eq_list", "false"),
  859. ("str_neq_list", "true"),
  860. # str, dict
  861. ("str_or_dict", "first"),
  862. ("str_and_dict", '{"1":2}'),
  863. ("str_eq_dict", "false"),
  864. ("str_neq_dict", "true"),
  865. # list, list
  866. ("list_add_list", "[1,2,3,4]"),
  867. ("list_gt_list", "false"),
  868. ("list_lt_list", "true"),
  869. ("list_gte_list", "false"),
  870. ("list_lte_list", "true"),
  871. ("list_eq_list", "false"),
  872. ("list_neq_list", "true"),
  873. ("list_and_list", "[3,4]"),
  874. ("list_or_list", "[1,2]"),
  875. ("list_contains", "true"),
  876. ("list_pluck", '["obj_1","obj_2"]'),
  877. ("list_reverse", "[2,1]"),
  878. ("list_join", "firstsecondthird"),
  879. ("list_join_comma", "first,second,third"),
  880. ("list_join_range1", "2,3,4"),
  881. ("list_join_range2", "2,4,6,8"),
  882. ("list_join_range3", "5,4,3,2,1"),
  883. ("list_join_range4", "0,1,2"),
  884. # list, int
  885. ("list_mult_int", "[1,2,1,2,1,2,1,2,1,2]"),
  886. ("list_or_int", "[1,2]"),
  887. ("list_and_int", "10"),
  888. ("list_eq_int", "false"),
  889. ("list_neq_int", "true"),
  890. # list, dict
  891. ("list_and_dict", '{"1":2}'),
  892. ("list_or_dict", "[1,2]"),
  893. ("list_eq_dict", "false"),
  894. ("list_neq_dict", "true"),
  895. # dict, dict
  896. ("dict_or_dict", '{"1":2}'),
  897. ("dict_and_dict", '{"3":4}'),
  898. ("dict_eq_dict", "false"),
  899. ("dict_neq_dict", "true"),
  900. ("dict_contains", "true"),
  901. # index from an op var
  902. ("list_index_mod", "second"),
  903. # html component with var
  904. ("html_str", "hello"),
  905. # index into list with foreach
  906. ("foreach_list_arg", "1\n2"),
  907. ("foreach_list_ix", "1\n2"),
  908. ("foreach_list_nested", "1\n1\n2"),
  909. # rx.memo component with state
  910. ("memo_comp", "1210"),
  911. ("memo_comp_nested", "345"),
  912. # foreach in a match
  913. ("foreach_in_match", "first\nsecond\nthird"),
  914. # literal range in a foreach
  915. ("range_in_foreach1", "4269"),
  916. ("range_in_foreach2", "42454851545760636669727578"),
  917. ("range_in_foreach3", "42363024"),
  918. ("range_in_foreach4", "42"),
  919. ("dict_in_foreach1", "a1b2"),
  920. ("dict_in_foreach2", "12"),
  921. ("dict_in_foreach3", "1234"),
  922. ("str_in_foreach", "a b c d e f"),
  923. ("str_var_in_foreach", "f i r s t"),
  924. ("typed_dict_in_foreach", "Hello Alice33Hello Bob28"),
  925. # fstring operations
  926. ("float_format", "13212312312.123123"),
  927. ("float_format_0f", "13212312312"),
  928. ("float_format_1f", "13212312312.1"),
  929. ("float_format_2f", "13212312312.12"),
  930. ("float_format_comma", "13,212,312,312.123"),
  931. ("float_format_underscore", "13_212_312_312.123"),
  932. ("float_format_comma_0f", "13,212,312,312"),
  933. ("float_format_comma_1f", "13,212,312,312.1"),
  934. ("float_format_comma_2f", "13,212,312,312.12"),
  935. ("float_format_underscore_0f", "13_212_312_312"),
  936. ("float_format_underscore_1f", "13_212_312_312.1"),
  937. ("float_format_underscore_2f", "13_212_312_312.12"),
  938. ("obj_name", "hello"),
  939. ("obj_optional_none", ""),
  940. ("obj_optional_str", "hello"),
  941. ("obj_optional_none_get_none", ""),
  942. ("obj_optional_none_get_foo", "foo"),
  943. ]
  944. for tag, expected in tests:
  945. print(tag)
  946. assert driver.find_element(By.ID, tag).text == expected
  947. # Highlight component with var query (does not plumb ID)
  948. assert driver.find_element(By.TAG_NAME, "mark").text == "second"