test_compiler.py 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. import os
  2. from typing import List
  3. import pytest
  4. from reflex.compiler import compiler, utils
  5. from reflex.utils.imports import ImportList, ImportVar
  6. @pytest.mark.parametrize(
  7. "fields,test_default,test_rest",
  8. [
  9. (
  10. [ImportVar(tag="axios", is_default=True)],
  11. "axios",
  12. [],
  13. ),
  14. (
  15. [ImportVar(tag="foo"), ImportVar(tag="bar")],
  16. "",
  17. ["bar", "foo"],
  18. ),
  19. (
  20. [
  21. ImportVar(tag="axios", is_default=True),
  22. ImportVar(tag="foo"),
  23. ImportVar(tag="bar"),
  24. ],
  25. "axios",
  26. ["bar", "foo"],
  27. ),
  28. ],
  29. )
  30. def test_compile_import_statement(
  31. fields: List[ImportVar], test_default: str, test_rest: str
  32. ):
  33. """Test the compile_import_statement function.
  34. Args:
  35. fields: The fields to import.
  36. test_default: The expected output of default library.
  37. test_rest: The expected output rest libraries.
  38. """
  39. default, rest = utils.compile_import_statement(fields)
  40. assert default == test_default
  41. assert sorted(rest) == test_rest
  42. @pytest.mark.parametrize(
  43. "import_list,test_dicts",
  44. [
  45. (ImportList(), []),
  46. (
  47. ImportList([ImportVar(library="axios", tag="axios", is_default=True)]),
  48. [{"lib": "axios", "default": "axios", "rest": []}],
  49. ),
  50. (
  51. ImportList(
  52. [
  53. ImportVar(library="axios", tag="foo"),
  54. ImportVar(library="axios", tag="bar"),
  55. ]
  56. ),
  57. [{"lib": "axios", "default": "", "rest": ["bar", "foo"]}],
  58. ),
  59. (
  60. ImportList(
  61. [
  62. ImportVar(library="axios", tag="axios", is_default=True),
  63. ImportVar(library="axios", tag="foo"),
  64. ImportVar(library="axios", tag="bar"),
  65. ImportVar(library="react", tag="react", is_default=True),
  66. ]
  67. ),
  68. [
  69. {"lib": "axios", "default": "axios", "rest": ["bar", "foo"]},
  70. {"lib": "react", "default": "react", "rest": []},
  71. ],
  72. ),
  73. (
  74. ImportList(
  75. [
  76. ImportVar(library="", tag="lib1.js"),
  77. ImportVar(library="", tag="lib2.js"),
  78. ]
  79. ),
  80. [
  81. {"lib": "lib1.js", "default": "", "rest": []},
  82. {"lib": "lib2.js", "default": "", "rest": []},
  83. ],
  84. ),
  85. (
  86. ImportList(
  87. [
  88. ImportVar(library="", tag="lib1.js"),
  89. ImportVar(library="", tag="lib2.js"),
  90. ImportVar(library="axios", tag="axios", is_default=True),
  91. ]
  92. ),
  93. [
  94. {"lib": "lib1.js", "default": "", "rest": []},
  95. {"lib": "lib2.js", "default": "", "rest": []},
  96. {"lib": "axios", "default": "axios", "rest": []},
  97. ],
  98. ),
  99. ],
  100. )
  101. def test_compile_imports(import_list: ImportList, test_dicts: List[dict]):
  102. """Test the compile_imports function.
  103. Args:
  104. import_list: The list of ImportVar.
  105. test_dicts: The expected output.
  106. """
  107. imports = utils.compile_imports(import_list)
  108. for import_dict, test_dict in zip(imports, test_dicts):
  109. assert import_dict["lib"] == test_dict["lib"]
  110. assert import_dict["default"] == test_dict["default"]
  111. assert sorted(import_dict["rest"]) == test_dict["rest"] # type: ignore
  112. def test_compile_stylesheets(tmp_path, mocker):
  113. """Test that stylesheets compile correctly.
  114. Args:
  115. tmp_path: The test directory.
  116. mocker: Pytest mocker object.
  117. """
  118. project = tmp_path / "test_project"
  119. project.mkdir()
  120. assets_dir = project / "assets"
  121. assets_dir.mkdir()
  122. (assets_dir / "styles.css").touch()
  123. mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project)
  124. stylesheets = [
  125. "https://fonts.googleapis.com/css?family=Sofia&effect=neon|outline|emboss|shadow-multiple",
  126. "https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css",
  127. "/styles.css",
  128. "https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css",
  129. ]
  130. assert compiler.compile_root_stylesheet(stylesheets) == (
  131. os.path.join(".web", "styles", "styles.css"),
  132. f"@import url('./tailwind.css'); \n"
  133. f"@import url('https://fonts.googleapis.com/css?family=Sofia&effect=neon|outline|emboss|shadow-multiple'); \n"
  134. f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css'); \n"
  135. f"@import url('@/styles.css'); \n"
  136. f"@import url('https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css'); \n",
  137. )
  138. def test_compile_stylesheets_exclude_tailwind(tmp_path, mocker):
  139. """Test that Tailwind is excluded if tailwind config is explicitly set to None.
  140. Args:
  141. tmp_path: The test directory.
  142. mocker: Pytest mocker object.
  143. """
  144. project = tmp_path / "test_project"
  145. project.mkdir()
  146. assets_dir = project / "assets"
  147. assets_dir.mkdir()
  148. mock = mocker.Mock()
  149. mocker.patch.object(mock, "tailwind", None)
  150. mocker.patch("reflex.compiler.compiler.get_config", return_value=mock)
  151. (assets_dir / "styles.css").touch()
  152. mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project)
  153. stylesheets = [
  154. "/styles.css",
  155. ]
  156. assert compiler.compile_root_stylesheet(stylesheets) == (
  157. os.path.join(".web", "styles", "styles.css"),
  158. "@import url('@/styles.css'); \n",
  159. )
  160. def test_compile_nonexistent_stylesheet(tmp_path, mocker):
  161. """Test that an error is thrown for non-existent stylesheets.
  162. Args:
  163. tmp_path: The test directory.
  164. mocker: Pytest mocker object.
  165. """
  166. project = tmp_path / "test_project"
  167. project.mkdir()
  168. assets_dir = project / "assets"
  169. assets_dir.mkdir()
  170. mocker.patch("reflex.compiler.compiler.Path.cwd", return_value=project)
  171. stylesheets = ["/styles.css"]
  172. with pytest.raises(FileNotFoundError):
  173. compiler.compile_root_stylesheet(stylesheets)
  174. def test_create_document_root():
  175. """Test that the document root is created correctly."""
  176. # Test with no components.
  177. root = utils.create_document_root()
  178. root.render()
  179. assert isinstance(root, utils.Html)
  180. assert isinstance(root.children[0], utils.DocumentHead)
  181. # Default language.
  182. assert root.lang == "en" # type: ignore
  183. # No children in head.
  184. assert len(root.children[0].children) == 0
  185. # Test with components.
  186. comps = [
  187. utils.NextScript.create(src="foo.js"),
  188. utils.NextScript.create(src="bar.js"),
  189. ]
  190. root = utils.create_document_root(
  191. head_components=comps, # type: ignore
  192. html_lang="rx",
  193. html_custom_attrs={"project": "reflex"},
  194. )
  195. # Two children in head.
  196. assert isinstance(root, utils.Html)
  197. assert len(root.children[0].children) == 2
  198. assert root.lang == "rx" # type: ignore
  199. assert isinstance(root.custom_attrs, dict)
  200. assert root.custom_attrs == {"project": "reflex"}