You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

366 line
12 KiB

  1. from unittest.mock import patch
  2. import frappe
  3. from frappe.tests.utils import FrappeTestCase
  4. from frappe.utils import set_request
  5. from frappe.website.page_renderers.static_page import StaticPage
  6. from frappe.website.serve import get_response, get_response_content
  7. from frappe.website.utils import build_response, clear_website_cache, get_home_page
  8. class TestWebsite(FrappeTestCase):
  9. def setUp(self):
  10. frappe.set_user("Guest")
  11. def tearDown(self):
  12. frappe.db.delete("Access Log")
  13. frappe.set_user("Administrator")
  14. def test_home_page(self):
  15. frappe.set_user("Administrator")
  16. # test home page via role
  17. user = frappe.get_doc(
  18. dict(doctype="User", email="test-user-for-home-page@example.com", first_name="test")
  19. ).insert(ignore_if_duplicate=True)
  20. user.reload()
  21. role = frappe.get_doc(
  22. dict(
  23. doctype="Role",
  24. role_name="home-page-test",
  25. desk_access=0,
  26. )
  27. ).insert(ignore_if_duplicate=True)
  28. user.add_roles(role.name)
  29. user.save()
  30. frappe.db.set_value("Role", "home-page-test", "home_page", "home-page-test")
  31. frappe.set_user("test-user-for-home-page@example.com")
  32. self.assertEqual(get_home_page(), "home-page-test")
  33. frappe.set_user("Administrator")
  34. frappe.db.set_value("Role", "home-page-test", "home_page", "")
  35. # home page via portal settings
  36. frappe.db.set_value("Portal Settings", None, "default_portal_home", "test-portal-home")
  37. frappe.set_user("test-user-for-home-page@example.com")
  38. frappe.cache().hdel("home_page", frappe.session.user)
  39. self.assertEqual(get_home_page(), "test-portal-home")
  40. frappe.db.set_value("Portal Settings", None, "default_portal_home", "")
  41. clear_website_cache()
  42. # home page via website settings
  43. frappe.db.set_value("Website Settings", None, "home_page", "contact")
  44. self.assertEqual(get_home_page(), "contact")
  45. frappe.db.set_value("Website Settings", None, "home_page", None)
  46. clear_website_cache()
  47. # fallback homepage
  48. self.assertEqual(get_home_page(), "me")
  49. # fallback homepage for guest
  50. frappe.set_user("Guest")
  51. self.assertEqual(get_home_page(), "login")
  52. frappe.set_user("Administrator")
  53. # test homepage via hooks
  54. clear_website_cache()
  55. set_home_page_hook(
  56. "get_website_user_home_page", "frappe.www._test._test_home_page.get_website_user_home_page"
  57. )
  58. self.assertEqual(get_home_page(), "_test/_test_folder")
  59. clear_website_cache()
  60. set_home_page_hook("website_user_home_page", "login")
  61. self.assertEqual(get_home_page(), "login")
  62. clear_website_cache()
  63. set_home_page_hook("home_page", "about")
  64. self.assertEqual(get_home_page(), "about")
  65. clear_website_cache()
  66. set_home_page_hook("role_home_page", {"home-page-test": "home-page-test"})
  67. self.assertEqual(get_home_page(), "home-page-test")
  68. def test_page_load(self):
  69. set_request(method="POST", path="login")
  70. response = get_response()
  71. self.assertEqual(response.status_code, 200)
  72. html = frappe.safe_decode(response.get_data())
  73. self.assertTrue("// login.js" in html)
  74. self.assertTrue("<!-- login.html -->" in html)
  75. def test_static_page(self):
  76. set_request(method="GET", path="/_test/static-file-test.png")
  77. response = get_response()
  78. self.assertEqual(response.status_code, 200)
  79. set_request(method="GET", path="/_test/assets/image.jpg")
  80. response = get_response()
  81. self.assertEqual(response.status_code, 200)
  82. set_request(method="GET", path="/_test/assets/image")
  83. response = get_response()
  84. self.assertEqual(response.status_code, 200)
  85. with patch.object(StaticPage, "render") as static_render:
  86. set_request(method="GET", path="/_test/assets/image")
  87. response = get_response()
  88. static_render.assert_called()
  89. def test_error_page(self):
  90. set_request(method="GET", path="/_test/problematic_page")
  91. response = get_response()
  92. self.assertEqual(response.status_code, 417)
  93. def test_login(self):
  94. set_request(method="GET", path="/login")
  95. response = get_response()
  96. self.assertEqual(response.status_code, 200)
  97. html = frappe.safe_decode(response.get_data())
  98. self.assertTrue("// login.js" in html)
  99. self.assertTrue("<!-- login.html -->" in html)
  100. def test_app(self):
  101. frappe.set_user("Administrator")
  102. set_request(method="GET", path="/app")
  103. response = get_response()
  104. self.assertEqual(response.status_code, 200)
  105. html = frappe.safe_decode(response.get_data())
  106. self.assertTrue("window.app = true;" in html)
  107. frappe.local.session_obj = None
  108. def test_not_found(self):
  109. set_request(method="GET", path="/_test/missing")
  110. response = get_response()
  111. self.assertEqual(response.status_code, 404)
  112. def test_redirect(self):
  113. import frappe.hooks
  114. frappe.set_user("Administrator")
  115. frappe.hooks.website_redirects = [
  116. dict(source=r"/testfrom", target=r"://testto1"),
  117. dict(source=r"/testfromregex.*", target=r"://testto2"),
  118. dict(source=r"/testsub/(.*)", target=r"://testto3/\1"),
  119. dict(
  120. source=r"/courses/course\?course=(.*)", target=r"/courses/\1", match_with_query_string=True
  121. ),
  122. ]
  123. website_settings = frappe.get_doc("Website Settings")
  124. website_settings.append("route_redirects", {"source": "/testsource", "target": "/testtarget"})
  125. website_settings.save()
  126. set_request(method="GET", path="/testfrom")
  127. response = get_response()
  128. self.assertEqual(response.status_code, 301)
  129. self.assertEqual(response.headers.get("Location"), r"://testto1")
  130. set_request(method="GET", path="/testfromregex/test")
  131. response = get_response()
  132. self.assertEqual(response.status_code, 301)
  133. self.assertEqual(response.headers.get("Location"), r"://testto2")
  134. set_request(method="GET", path="/testsub/me")
  135. response = get_response()
  136. self.assertEqual(response.status_code, 301)
  137. self.assertEqual(response.headers.get("Location"), r"://testto3/me")
  138. set_request(method="GET", path="/test404")
  139. response = get_response()
  140. self.assertEqual(response.status_code, 404)
  141. set_request(method="GET", path="/testsource")
  142. response = get_response()
  143. self.assertEqual(response.status_code, 301)
  144. self.assertEqual(response.headers.get("Location"), "/testtarget")
  145. set_request(method="GET", path="/courses/course?course=data")
  146. response = get_response()
  147. self.assertEqual(response.status_code, 301)
  148. self.assertEqual(response.headers.get("Location"), "/courses/data")
  149. delattr(frappe.hooks, "website_redirects")
  150. frappe.cache().delete_key("app_hooks")
  151. def test_custom_page_renderer(self):
  152. import frappe.hooks
  153. frappe.hooks.page_renderer = ["frappe.tests.test_website.CustomPageRenderer"]
  154. frappe.cache().delete_key("app_hooks")
  155. set_request(method="GET", path="/custom")
  156. response = get_response()
  157. self.assertEqual(response.status_code, 3984)
  158. set_request(method="GET", path="/new")
  159. content = get_response_content()
  160. self.assertIn("<div>Custom Page Response</div>", content)
  161. set_request(method="GET", path="/random")
  162. response = get_response()
  163. self.assertEqual(response.status_code, 404)
  164. delattr(frappe.hooks, "page_renderer")
  165. frappe.cache().delete_key("app_hooks")
  166. def test_printview_page(self):
  167. frappe.db.value_cache[("DocType", "Language", "name")] = (("Language",),)
  168. content = get_response_content("/Language/ru")
  169. self.assertIn('<div class="print-format">', content)
  170. self.assertIn("<div>Language</div>", content)
  171. def test_custom_base_template_path(self):
  172. content = get_response_content("/_test/_test_folder/_test_page")
  173. # assert the text in base template is rendered
  174. self.assertIn("<h1>This is for testing</h1>", content)
  175. # assert template block rendered
  176. self.assertIn("<p>Test content</p>", content)
  177. def test_json_sidebar_data(self):
  178. frappe.flags.look_for_sidebar = False
  179. content = get_response_content("/_test/_test_folder/_test_page")
  180. self.assertNotIn("Test Sidebar", content)
  181. clear_website_cache()
  182. frappe.flags.look_for_sidebar = True
  183. content = get_response_content("/_test/_test_folder/_test_page")
  184. self.assertIn("Test Sidebar", content)
  185. frappe.flags.look_for_sidebar = False
  186. def test_base_template(self):
  187. content = get_response_content("/_test/_test_custom_base.html")
  188. # assert the text in base template is rendered
  189. self.assertIn("<h1>This is for testing</h1>", content)
  190. # assert template block rendered
  191. self.assertIn("<p>Test content</p>", content)
  192. def test_index_and_next_comment(self):
  193. content = get_response_content("/_test/_test_folder")
  194. # test if {index} was rendered
  195. self.assertIn('<a href="/_test/_test_folder/_test_page"> Test Page</a>', content)
  196. self.assertIn('<a href="/_test/_test_folder/_test_toc">Test TOC</a>', content)
  197. content = get_response_content("/_test/_test_folder/_test_page")
  198. # test if {next} was rendered
  199. self.assertIn(
  200. 'Next: <a class="btn-next" href="/_test/_test_folder/_test_toc">Test TOC</a>', content
  201. )
  202. def test_colocated_assets(self):
  203. content = get_response_content("/_test/_test_folder/_test_page")
  204. self.assertIn("""<script>console.log("test data");\n</script>""", content)
  205. self.assertIn("background-color: var(--bg-color);", content)
  206. def test_raw_assets_are_loaded(self):
  207. content = get_response_content("/_test/assets/js_asset.min.js")
  208. # minified js files should not be passed through jinja renderer
  209. self.assertEqual("""//{% if title %} {{title}} {% endif %}\nconsole.log("in");\n""", content)
  210. content = get_response_content("/_test/assets/css_asset.css")
  211. self.assertEqual("""body{color:red}""", content)
  212. def test_breadcrumbs(self):
  213. content = get_response_content("/_test/_test_folder/_test_page")
  214. self.assertIn('<span itemprop="name">Test Folder</span>', content)
  215. self.assertIn('<span itemprop="name"> Test Page</span>', content)
  216. content = get_response_content("/_test/_test_folder/index")
  217. self.assertIn('<span itemprop="name"> Test</span>', content)
  218. self.assertIn('<span itemprop="name">Test Folder</span>', content)
  219. def test_get_context_without_context_object(self):
  220. content = get_response_content("/_test/_test_no_context")
  221. self.assertIn("Custom Content", content)
  222. def test_caching(self):
  223. # to enable caching
  224. frappe.flags.force_website_cache = True
  225. clear_website_cache()
  226. # first response no-cache
  227. response = get_response("/_test/_test_folder/_test_page")
  228. self.assertIn(("X-From-Cache", "False"), list(response.headers))
  229. # first response returned from cache
  230. response = get_response("/_test/_test_folder/_test_page")
  231. self.assertIn(("X-From-Cache", "True"), list(response.headers))
  232. frappe.flags.force_website_cache = False
  233. def test_safe_render(self):
  234. content = get_response_content("/_test/_test_safe_render_on")
  235. self.assertNotIn("Safe Render On", content)
  236. self.assertIn("frappe.exceptions.ValidationError: Illegal template", content)
  237. content = get_response_content("/_test/_test_safe_render_off")
  238. self.assertIn("Safe Render Off", content)
  239. self.assertIn("test.__test", content)
  240. self.assertNotIn("frappe.exceptions.ValidationError: Illegal template", content)
  241. def test_metatags(self):
  242. content = get_response_content("/_test/_test_metatags")
  243. self.assertIn('<meta name="title" content="Test Title Metatag">', content)
  244. self.assertIn('<meta name="description" content="Test Description for Metatag">', content)
  245. def test_resolve_class(self):
  246. from frappe.utils.jinja_globals import resolve_class
  247. context = frappe._dict(primary=True)
  248. self.assertEqual(resolve_class("test"), "test")
  249. self.assertEqual(resolve_class("test", "test-2"), "test test-2")
  250. self.assertEqual(resolve_class("test", {"test-2": False, "test-3": True}), "test test-3")
  251. self.assertEqual(
  252. resolve_class(["test1", "test2", context.primary and "primary"]), "test1 test2 primary"
  253. )
  254. content = '<a class="{{ resolve_class("btn btn-default", primary and "btn-primary") }}">Test</a>'
  255. self.assertEqual(
  256. frappe.render_template(content, context), '<a class="btn btn-default btn-primary">Test</a>'
  257. )
  258. def set_home_page_hook(key, value):
  259. from frappe import hooks
  260. # reset home_page hooks
  261. for hook in (
  262. "get_website_user_home_page",
  263. "website_user_home_page",
  264. "role_home_page",
  265. "home_page",
  266. ):
  267. if hasattr(hooks, hook):
  268. delattr(hooks, hook)
  269. setattr(hooks, key, value)
  270. frappe.cache().delete_key("app_hooks")
  271. class CustomPageRenderer:
  272. def __init__(self, path, status_code=None):
  273. self.path = path
  274. # custom status code
  275. self.status_code = 3984
  276. def can_render(self):
  277. if self.path in ("new", "custom"):
  278. return True
  279. def render(self):
  280. return build_response(self.path, """<div>Custom Page Response</div>""", self.status_code)