test_compiler.py 6.9 KB

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