test_compiler.py 6.9 KB

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