def test_category_link(self): # Make a temporary Blog Post (and a Blog Category) blog = make_test_blog('Test Category Link') # Visit the blog post page set_request(path=blog.route) blog_page_response = get_response() blog_page_html = frappe.safe_decode(blog_page_response.get_data()) # On blog post page find link to the category page soup = BeautifulSoup(blog_page_html, "lxml") category_page_link = list( soup.find_all('a', href=re.compile(blog.blog_category)))[0] category_page_url = category_page_link["href"] # Visit the category page (by following the link found in above stage) set_request(path=category_page_url) category_page_response = get_response() category_page_html = frappe.safe_decode( category_page_response.get_data()) # Category page should contain the blog post title self.assertIn(blog.title, category_page_html) # Cleanup frappe.delete_doc("Blog Post", blog.name) frappe.delete_doc("Blog Category", blog.blog_category)
def test_caching(self): # to enable caching frappe.flags.force_website_cache = True print(frappe.session.user) clear_website_cache() # first response no-cache pages = frappe.get_all('Blog Post', fields=['name', 'route'], filters={ 'published': 1, 'title': "_Test Blog Post" }, limit=1) route = pages[0].route set_request(path=route) # response = get_response() response = get_response() # TODO: enable this assert # self.assertIn(('X-From-Cache', 'False'), list(response.headers)) set_request(path=route) response = get_response() self.assertIn(('X-From-Cache', 'True'), list(response.headers)) frappe.flags.force_website_cache = True
def test_caching(self): # to enable caching frappe.flags.force_website_cache = True clear_website_cache() # first response no-cache response = get_response('/_test/_test_folder/_test_page') self.assertIn(('X-From-Cache', 'False'), list(response.headers)) # first response returned from cache response = get_response('/_test/_test_folder/_test_page') self.assertIn(('X-From-Cache', 'True'), list(response.headers)) frappe.flags.force_website_cache = False
def test_homepage_section_custom_html(self): frappe.get_doc({ "doctype": "Homepage Section", "name": "Custom HTML Section", "section_based_on": "Custom HTML", "section_html": '<div class="custom-section">My custom html</div>', }).insert() set_request(method="GET", path="home") response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) soup = BeautifulSoup(html, "html.parser") sections = soup.find("main").find_all(class_="custom-section") self.assertEqual(len(sections), 1) homepage_section = sections[0] self.assertEqual(homepage_section.text, "My custom html") # cleanup frappe.db.rollback()
def application(request): response = None try: rollback = True init_request(request) frappe.recorder.record() frappe.monitor.start() frappe.rate_limiter.apply() frappe.api.validate_auth() if request.method == "OPTIONS": response = Response() elif frappe.form_dict.cmd: response = frappe.handler.handle() elif request.path.startswith("/api/"): response = frappe.api.handle() elif request.path.startswith('/backups'): response = frappe.utils.response.download_backup(request.path) elif request.path.startswith('/private/files/'): response = frappe.utils.response.download_private_file( request.path) elif request.method in ('GET', 'HEAD', 'POST'): response = get_response() else: raise NotFound except HTTPException as e: return e except frappe.SessionStopped as e: response = frappe.utils.response.handle_session_stopped() except Exception as e: response = handle_exception(e) else: rollback = after_request(rollback) finally: if request.method in ("POST", "PUT") and frappe.db and rollback: frappe.db.rollback() frappe.rate_limiter.update() frappe.monitor.stop(response) frappe.recorder.dump() log_request(request, response) process_response(response) frappe.destroy() return response
def test_meta_tag_generation(self): blogs = frappe.get_all('Blog Post', fields=['name', 'route'], filters={ 'published': 1, 'route': ('!=', '') }, limit=1) blog = blogs[0] # create meta tags for this route doc = frappe.new_doc('Website Route Meta') doc.append('meta_tags', {'key': 'type', 'value': 'blog_post'}) doc.append('meta_tags', {'key': 'og:title', 'value': 'My Blog'}) doc.name = blog.route doc.insert() # set request on this route set_request(path=blog.route) response = get_response() self.assertTrue(response.status_code, 200) html = response.get_data().decode() self.assertTrue('''<meta name="type" content="blog_post">''' in html) self.assertTrue( '''<meta property="og:title" content="My Blog">''' in html)
def test_homepage_load(self): set_request(method="GET", path="home") response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) self.assertTrue('<section class="hero-section' in html)
def test_login(self): set_request(method='GET', path='/login') response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) self.assertTrue('// login.js' in html) self.assertTrue('<!-- login.html -->' in html)
def test_custom_page_renderer(self): import frappe.hooks frappe.hooks.page_renderer = ['frappe.tests.test_website.CustomPageRenderer'] frappe.cache().delete_key('app_hooks') set_request(method='GET', path='/custom') response = get_response() self.assertEqual(response.status_code, 3984) set_request(method='GET', path='/new') content = get_response_content() self.assertIn("<div>Custom Page Response</div>", content) set_request(method='GET', path='/random') response = get_response() self.assertEqual(response.status_code, 404) delattr(frappe.hooks, 'page_renderer') frappe.cache().delete_key('app_hooks')
def test_app(self): frappe.set_user('Administrator') set_request(method='GET', path='/app') response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) self.assertTrue('window.app = true;' in html) frappe.local.session_obj = None
def test_homepage_section_card(self): try: frappe.get_doc({ "doctype": "Homepage Section", "name": "Card Section", "section_based_on": "Cards", "section_cards": [ { "title": "Card 1", "subtitle": "Subtitle 1", "content": "This is test card 1", "route": "/card-1", }, { "title": "Card 2", "subtitle": "Subtitle 2", "content": "This is test card 2", "image": "test.jpg", }, ], "no_of_columns": 3, }).insert(ignore_if_duplicate=True) except frappe.DuplicateEntryError: pass set_request(method="GET", path="home") response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) soup = BeautifulSoup(html, "html.parser") sections = soup.find("main").find_all("section") self.assertEqual(len(sections), 3) homepage_section = sections[2] self.assertEqual(homepage_section.h3.text, "Card Section") cards = homepage_section.find_all(class_="card") self.assertEqual(len(cards), 2) self.assertEqual(cards[0].h5.text, "Card 1") self.assertEqual(cards[0].a["href"], "/card-1") self.assertEqual(cards[1].p.text, "Subtitle 2") self.assertEqual( cards[1].find(class_="website-image-lazy")["data-src"], "test.jpg") # cleanup frappe.db.rollback()
def handle_session_stopped(): from frappe.website.serve import get_response frappe.respond_as_web_page( _("Updating"), _("Your system is being updated. Please refresh again after a few moments." ), http_status_code=503, indicator_color='orange', fullpage=True, primary_action=None) return get_response("message", http_status_code=503)
def test_generator_not_found(self): pages = frappe.get_all('Blog Post', fields=['name', 'route'], filters={'published': 0}, limit=1) route = f'test-route-{frappe.generate_hash(length=5)}' frappe.db.set_value('Blog Post', pages[0].name, 'route', route) set_request(path=route) response = get_response() self.assertTrue(response.status_code, 404)
def test_web_page_with_page_builder(self): self.create_web_page() set_request(method="GET", path="test-web-template") response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) soup = BeautifulSoup(html, "html.parser") sections = soup.find("main").find_all("section") self.assertEqual(len(sections), 2) self.assertEqual(sections[0].find("h2").text, "Test Title") self.assertEqual(sections[0].find("p").text, "test lorem ipsum") self.assertEqual(len(sections[1].find_all("a")), 3)
def test_custom_stylesheet(self): self.create_web_page() theme = self.create_website_theme() theme.set_as_default() frappe.conf.developer_mode = 1 set_request(method="GET", path="test-web-template") response = get_response() self.assertEqual(response.status_code, 200) html = frappe.safe_decode(response.get_data()) soup = BeautifulSoup(html, "html.parser") stylesheet = soup.select_one('link[rel="stylesheet"]') self.assertEqual(stylesheet.attrs["href"], theme.theme_url) frappe.get_doc("Website Theme", "Standard").set_as_default()
def test_generator_view(self): pages = frappe.get_all('Blog Post', fields=['name', 'route'], filters={ 'published': 1, 'route': ('!=', '') }, limit=1) set_request(path=pages[0].route) response = get_response() self.assertTrue(response.status_code, 200) html = response.get_data().decode() self.assertTrue( '<article class="blog-content" itemscope itemtype="http://schema.org/BlogPosting">' in html)
def test_redirect(self): import frappe.hooks frappe.set_user('Administrator') frappe.hooks.website_redirects = [ dict(source=r'/testfrom', target=r'://testto1'), dict(source=r'/testfromregex.*', target=r'://testto2'), dict(source=r'/testsub/(.*)', target=r'://testto3/\1'), dict(source=r'/courses/course\?course=(.*)', target=r'/courses/\1', match_with_query_string=True), ] website_settings = frappe.get_doc('Website Settings') website_settings.append('route_redirects', { 'source': '/testsource', 'target': '/testtarget' }) website_settings.save() set_request(method='GET', path='/testfrom') response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), r'://testto1') set_request(method='GET', path='/testfromregex/test') response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), r'://testto2') set_request(method='GET', path='/testsub/me') response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), r'://testto3/me') set_request(method='GET', path='/test404') response = get_response() self.assertEqual(response.status_code, 404) set_request(method='GET', path='/testsource') response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), '/testtarget') set_request(method='GET', path='/courses/course?course=data') response = get_response() self.assertEqual(response.status_code, 301) self.assertEqual(response.headers.get('Location'), '/courses/data') delattr(frappe.hooks, 'website_redirects') frappe.cache().delete_key('app_hooks')
def handle_exception(e): response = None http_status_code = getattr(e, "http_status_code", 500) return_as_message = False accept_header = frappe.get_request_header("Accept") or "" respond_as_json = ( frappe.get_request_header('Accept') and (frappe.local.is_ajax or 'application/json' in accept_header) or (frappe.local.request.path.startswith("/api/") and not accept_header.startswith("text"))) if frappe.conf.get('developer_mode'): # don't fail silently print(frappe.get_traceback()) if respond_as_json: # handle ajax responses first # if the request is ajax, send back the trace or error message response = frappe.utils.response.report_error(http_status_code) elif (http_status_code == 500 and (frappe.db and isinstance(e, frappe.db.InternalError)) and (frappe.db and (frappe.db.is_deadlocked(e) or frappe.db.is_timedout(e)))): http_status_code = 508 elif http_status_code == 401: frappe.respond_as_web_page( _("Session Expired"), _("Your session has expired, please login again to continue."), http_status_code=http_status_code, indicator_color='red') return_as_message = True elif http_status_code == 403: frappe.respond_as_web_page( _("Not Permitted"), _("You do not have enough permissions to complete the action"), http_status_code=http_status_code, indicator_color='red') return_as_message = True elif http_status_code == 404: frappe.respond_as_web_page( _("Not Found"), _("The resource you are looking for is not available"), http_status_code=http_status_code, indicator_color='red') return_as_message = True elif http_status_code == 429: response = frappe.rate_limiter.respond() else: traceback = "<pre>" + sanitize_html(frappe.get_traceback()) + "</pre>" # disable traceback in production if flag is set if frappe.local.flags.disable_traceback and not frappe.local.dev_server: traceback = "" frappe.respond_as_web_page("Server Error", traceback, http_status_code=http_status_code, indicator_color='red', width=640) return_as_message = True if e.__class__ == frappe.AuthenticationError: if hasattr(frappe.local, "login_manager"): frappe.local.login_manager.clear_cookies() if http_status_code >= 500: make_error_snapshot(e) if return_as_message: response = get_response("message", http_status_code=http_status_code) return response
def test_error_page(self): set_request(method='GET', path='/_test/problematic_page') response = get_response() self.assertEqual(response.status_code, 500)
def test_static_page(self): set_request(method='GET', path='/_test/static-file-test.png') response = get_response() self.assertEqual(response.status_code, 200)
def test_not_found(self): set_request(method='GET', path='/_test/missing') response = get_response() self.assertEqual(response.status_code, 404)
def get_html_for_route(route): from frappe.website.serve import get_response set_request(method='GET', path=route) response = get_response() html = frappe.safe_decode(response.get_data()) return html
def as_page(): """print web page""" from frappe.website.serve import get_response return get_response( frappe.response['route'], http_status_code=frappe.response.get("http_status_code"))