test_var_operations.py 35 KB

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