conftest.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  1. """Test fixtures."""
  2. import platform
  3. from typing import Dict, Generator, List
  4. import pytest
  5. import reflex as rx
  6. from reflex import constants
  7. from reflex.event import EventSpec
  8. @pytest.fixture(scope="function")
  9. def windows_platform() -> Generator:
  10. """Check if system is windows.
  11. Yields:
  12. whether system is windows.
  13. """
  14. yield platform.system() == "Windows"
  15. @pytest.fixture
  16. def list_mutation_state():
  17. """Create a state with list mutation features.
  18. Returns:
  19. A state with list mutation features.
  20. """
  21. class TestState(rx.State):
  22. """The test state."""
  23. # plain list
  24. plain_friends = ["Tommy"]
  25. def make_friend(self):
  26. self.plain_friends.append("another-fd")
  27. def change_first_friend(self):
  28. self.plain_friends[0] = "Jenny"
  29. def unfriend_all_friends(self):
  30. self.plain_friends.clear()
  31. def unfriend_first_friend(self):
  32. del self.plain_friends[0]
  33. def remove_last_friend(self):
  34. self.plain_friends.pop()
  35. def make_friends_with_colleagues(self):
  36. colleagues = ["Peter", "Jimmy"]
  37. self.plain_friends.extend(colleagues)
  38. def remove_tommy(self):
  39. self.plain_friends.remove("Tommy")
  40. # list in dict
  41. friends_in_dict = {"Tommy": ["Jenny"]}
  42. def remove_jenny_from_tommy(self):
  43. self.friends_in_dict["Tommy"].remove("Jenny")
  44. def add_jimmy_to_tommy_friends(self):
  45. self.friends_in_dict["Tommy"].append("Jimmy")
  46. def tommy_has_no_fds(self):
  47. self.friends_in_dict["Tommy"].clear()
  48. # nested list
  49. friends_in_nested_list = [["Tommy"], ["Jenny"]]
  50. def remove_first_group(self):
  51. self.friends_in_nested_list.pop(0)
  52. def remove_first_person_from_first_group(self):
  53. self.friends_in_nested_list[0].pop(0)
  54. def add_jimmy_to_second_group(self):
  55. self.friends_in_nested_list[1].append("Jimmy")
  56. return TestState()
  57. @pytest.fixture
  58. def dict_mutation_state():
  59. """Create a state with dict mutation features.
  60. Returns:
  61. A state with dict mutation features.
  62. """
  63. class TestState(rx.State):
  64. """The test state."""
  65. # plain dict
  66. details = {"name": "Tommy"}
  67. def add_age(self):
  68. self.details.update({"age": 20}) # type: ignore
  69. def change_name(self):
  70. self.details["name"] = "Jenny"
  71. def remove_last_detail(self):
  72. self.details.popitem()
  73. def clear_details(self):
  74. self.details.clear()
  75. def remove_name(self):
  76. del self.details["name"]
  77. def pop_out_age(self):
  78. self.details.pop("age")
  79. # dict in list
  80. address = [{"home": "home address"}, {"work": "work address"}]
  81. def remove_home_address(self):
  82. self.address[0].pop("home")
  83. def add_street_to_home_address(self):
  84. self.address[0]["street"] = "street address"
  85. # nested dict
  86. friend_in_nested_dict = {"name": "Nikhil", "friend": {"name": "Alek"}}
  87. def change_friend_name(self):
  88. self.friend_in_nested_dict["friend"]["name"] = "Tommy"
  89. def remove_friend(self):
  90. self.friend_in_nested_dict.pop("friend")
  91. def add_friend_age(self):
  92. self.friend_in_nested_dict["friend"]["age"] = 30
  93. return TestState()
  94. class UploadState(rx.State):
  95. """The base state for uploading a file."""
  96. async def handle_upload1(self, files: List[rx.UploadFile]):
  97. """Handle the upload of a file.
  98. Args:
  99. files: The uploaded files.
  100. """
  101. pass
  102. class BaseState(rx.State):
  103. """The test base state."""
  104. pass
  105. class SubUploadState(BaseState):
  106. """The test substate."""
  107. img: str
  108. async def handle_upload(self, files: List[rx.UploadFile]):
  109. """Handle the upload of a file.
  110. Args:
  111. files: The uploaded files.
  112. """
  113. pass
  114. @pytest.fixture
  115. def upload_sub_state_event_spec():
  116. """Create an event Spec for a substate.
  117. Returns:
  118. Event Spec.
  119. """
  120. return EventSpec(handler=SubUploadState.handle_upload, upload=True) # type: ignore
  121. @pytest.fixture
  122. def upload_event_spec():
  123. """Create an event Spec for a multi-upload base state.
  124. Returns:
  125. Event Spec.
  126. """
  127. return EventSpec(handler=UploadState.handle_upload1, upload=True) # type: ignore
  128. @pytest.fixture
  129. def upload_state(tmp_path):
  130. """Create upload state.
  131. Args:
  132. tmp_path: pytest tmp_path
  133. Returns:
  134. The state
  135. """
  136. class FileUploadState(rx.State):
  137. """The base state for uploading a file."""
  138. img_list: List[str]
  139. async def handle_upload2(self, files):
  140. """Handle the upload of a file.
  141. Args:
  142. files: The uploaded files.
  143. """
  144. for file in files:
  145. upload_data = await file.read()
  146. outfile = f"{tmp_path}/{file.filename}"
  147. # Save the file.
  148. with open(outfile, "wb") as file_object:
  149. file_object.write(upload_data)
  150. # Update the img var.
  151. self.img_list.append(file.filename)
  152. async def multi_handle_upload(self, files: List[rx.UploadFile]):
  153. """Handle the upload of a file.
  154. Args:
  155. files: The uploaded files.
  156. """
  157. for file in files:
  158. upload_data = await file.read()
  159. outfile = f"{tmp_path}/{file.filename}"
  160. # Save the file.
  161. with open(outfile, "wb") as file_object:
  162. file_object.write(upload_data)
  163. # Update the img var.
  164. assert file.filename is not None
  165. self.img_list.append(file.filename)
  166. return FileUploadState
  167. @pytest.fixture
  168. def upload_sub_state(tmp_path):
  169. """Create upload substate.
  170. Args:
  171. tmp_path: pytest tmp_path
  172. Returns:
  173. The state
  174. """
  175. class FileState(rx.State):
  176. """The base state."""
  177. pass
  178. class FileUploadState(FileState):
  179. """The substate for uploading a file."""
  180. img_list: List[str]
  181. async def handle_upload2(self, files):
  182. """Handle the upload of a file.
  183. Args:
  184. files: The uploaded files.
  185. """
  186. for file in files:
  187. upload_data = await file.read()
  188. outfile = f"{tmp_path}/{file.filename}"
  189. # Save the file.
  190. with open(outfile, "wb") as file_object:
  191. file_object.write(upload_data)
  192. # Update the img var.
  193. self.img_list.append(file.filename)
  194. async def multi_handle_upload(self, files: List[rx.UploadFile]):
  195. """Handle the upload of a file.
  196. Args:
  197. files: The uploaded files.
  198. """
  199. for file in files:
  200. upload_data = await file.read()
  201. outfile = f"{tmp_path}/{file.filename}"
  202. # Save the file.
  203. with open(outfile, "wb") as file_object:
  204. file_object.write(upload_data)
  205. # Update the img var.
  206. assert file.filename is not None
  207. self.img_list.append(file.filename)
  208. return FileUploadState
  209. @pytest.fixture
  210. def upload_grand_sub_state(tmp_path):
  211. """Create upload grand-state.
  212. Args:
  213. tmp_path: pytest tmp_path
  214. Returns:
  215. The state
  216. """
  217. class BaseFileState(rx.State):
  218. """The base state."""
  219. pass
  220. class FileSubState(BaseFileState):
  221. """The substate."""
  222. pass
  223. class FileUploadState(FileSubState):
  224. """The grand-substate for uploading a file."""
  225. img_list: List[str]
  226. async def handle_upload2(self, files):
  227. """Handle the upload of a file.
  228. Args:
  229. files: The uploaded files.
  230. """
  231. for file in files:
  232. upload_data = await file.read()
  233. outfile = f"{tmp_path}/{file.filename}"
  234. # Save the file.
  235. with open(outfile, "wb") as file_object:
  236. file_object.write(upload_data)
  237. # Update the img var.
  238. assert file.filename is not None
  239. self.img_list.append(file.filename)
  240. async def multi_handle_upload(self, files: List[rx.UploadFile]):
  241. """Handle the upload of a file.
  242. Args:
  243. files: The uploaded files.
  244. """
  245. for file in files:
  246. upload_data = await file.read()
  247. outfile = f"{tmp_path}/{file.filename}"
  248. # Save the file.
  249. with open(outfile, "wb") as file_object:
  250. file_object.write(upload_data)
  251. # Update the img var.
  252. assert file.filename is not None
  253. self.img_list.append(file.filename)
  254. return FileUploadState
  255. @pytest.fixture
  256. def base_config_values() -> Dict:
  257. """Get base config values.
  258. Returns:
  259. Dictionary of base config values
  260. """
  261. return {"app_name": "app", "db_url": constants.DB_URL, "env": rx.Env.DEV}
  262. @pytest.fixture
  263. def base_db_config_values() -> Dict:
  264. """Get base DBConfig values.
  265. Returns:
  266. Dictionary of base db config values
  267. """
  268. return {"database": "db"}
  269. @pytest.fixture
  270. def sqlite_db_config_values(base_db_config_values) -> Dict:
  271. """Get sqlite DBConfig values.
  272. Args:
  273. base_db_config_values: Base DBConfig fixture.
  274. Returns:
  275. Dictionary of sqlite DBConfig values
  276. """
  277. base_db_config_values["engine"] = "sqlite"
  278. return base_db_config_values
  279. class GenState(rx.State):
  280. """A state with event handlers that generate multiple updates."""
  281. value: int
  282. def go(self, c: int):
  283. """Increment the value c times and update each time.
  284. Args:
  285. c: The number of times to increment.
  286. Yields:
  287. After each increment.
  288. """
  289. for _ in range(c):
  290. self.value += 1
  291. yield
  292. @pytest.fixture
  293. def gen_state() -> GenState:
  294. """A state.
  295. Returns:
  296. A test state.
  297. """
  298. return GenState # type: ignore
  299. @pytest.fixture
  300. def router_data_headers() -> Dict[str, str]:
  301. """Router data headers.
  302. Returns:
  303. client headers
  304. """
  305. return {
  306. "host": "localhost:8000",
  307. "connection": "Upgrade",
  308. "pragma": "no-cache",
  309. "cache-control": "no-cache",
  310. "user-agent": "Mock Agent",
  311. "upgrade": "websocket",
  312. "origin": "http://localhost:3000",
  313. "sec-websocket-version": "13",
  314. "accept-encoding": "gzip, deflate, br",
  315. "accept-language": "en-US,en;q=0.9",
  316. "cookie": "csrftoken=mocktoken; "
  317. "name=reflex;"
  318. " list_cookies=%5B%22some%22%2C%20%22random%22%2C%20%22cookies%22%5D;"
  319. " dict_cookies=%7B%22name%22%3A%20%22reflex%22%7D; val=true",
  320. "sec-websocket-key": "mock-websocket-key",
  321. "sec-websocket-extensions": "permessage-deflate; client_max_window_bits",
  322. }
  323. @pytest.fixture
  324. def router_data(router_data_headers) -> Dict[str, str]:
  325. """Router data.
  326. Args:
  327. router_data_headers: Headers fixture.
  328. Returns:
  329. Dict of router data.
  330. """
  331. return { # type: ignore
  332. "pathname": "/",
  333. "query": {},
  334. "token": "b181904c-3953-4a79-dc18-ae9518c22f05",
  335. "sid": "9fpxSzPb9aFMb4wFAAAH",
  336. "headers": router_data_headers,
  337. "ip": "127.0.0.1",
  338. }