1import pytest 2from werkzeug.http import parse_set_header 3 4import flask.views 5 6 7def common_test(app): 8 c = app.test_client() 9 10 assert c.get("/").data == b"GET" 11 assert c.post("/").data == b"POST" 12 assert c.put("/").status_code == 405 13 meths = parse_set_header(c.open("/", method="OPTIONS").headers["Allow"]) 14 assert sorted(meths) == ["GET", "HEAD", "OPTIONS", "POST"] 15 16 17def test_basic_view(app): 18 class Index(flask.views.View): 19 methods = ["GET", "POST"] 20 21 def dispatch_request(self): 22 return flask.request.method 23 24 app.add_url_rule("/", view_func=Index.as_view("index")) 25 common_test(app) 26 27 28def test_method_based_view(app): 29 class Index(flask.views.MethodView): 30 def get(self): 31 return "GET" 32 33 def post(self): 34 return "POST" 35 36 app.add_url_rule("/", view_func=Index.as_view("index")) 37 38 common_test(app) 39 40 41def test_view_patching(app): 42 class Index(flask.views.MethodView): 43 def get(self): 44 1 // 0 45 46 def post(self): 47 1 // 0 48 49 class Other(Index): 50 def get(self): 51 return "GET" 52 53 def post(self): 54 return "POST" 55 56 view = Index.as_view("index") 57 view.view_class = Other 58 app.add_url_rule("/", view_func=view) 59 common_test(app) 60 61 62def test_view_inheritance(app, client): 63 class Index(flask.views.MethodView): 64 def get(self): 65 return "GET" 66 67 def post(self): 68 return "POST" 69 70 class BetterIndex(Index): 71 def delete(self): 72 return "DELETE" 73 74 app.add_url_rule("/", view_func=BetterIndex.as_view("index")) 75 76 meths = parse_set_header(client.open("/", method="OPTIONS").headers["Allow"]) 77 assert sorted(meths) == ["DELETE", "GET", "HEAD", "OPTIONS", "POST"] 78 79 80def test_view_decorators(app, client): 81 def add_x_parachute(f): 82 def new_function(*args, **kwargs): 83 resp = flask.make_response(f(*args, **kwargs)) 84 resp.headers["X-Parachute"] = "awesome" 85 return resp 86 87 return new_function 88 89 class Index(flask.views.View): 90 decorators = [add_x_parachute] 91 92 def dispatch_request(self): 93 return "Awesome" 94 95 app.add_url_rule("/", view_func=Index.as_view("index")) 96 rv = client.get("/") 97 assert rv.headers["X-Parachute"] == "awesome" 98 assert rv.data == b"Awesome" 99 100 101def test_view_provide_automatic_options_attr(): 102 app = flask.Flask(__name__) 103 104 class Index1(flask.views.View): 105 provide_automatic_options = False 106 107 def dispatch_request(self): 108 return "Hello World!" 109 110 app.add_url_rule("/", view_func=Index1.as_view("index")) 111 c = app.test_client() 112 rv = c.open("/", method="OPTIONS") 113 assert rv.status_code == 405 114 115 app = flask.Flask(__name__) 116 117 class Index2(flask.views.View): 118 methods = ["OPTIONS"] 119 provide_automatic_options = True 120 121 def dispatch_request(self): 122 return "Hello World!" 123 124 app.add_url_rule("/", view_func=Index2.as_view("index")) 125 c = app.test_client() 126 rv = c.open("/", method="OPTIONS") 127 assert sorted(rv.allow) == ["OPTIONS"] 128 129 app = flask.Flask(__name__) 130 131 class Index3(flask.views.View): 132 def dispatch_request(self): 133 return "Hello World!" 134 135 app.add_url_rule("/", view_func=Index3.as_view("index")) 136 c = app.test_client() 137 rv = c.open("/", method="OPTIONS") 138 assert "OPTIONS" in rv.allow 139 140 141def test_implicit_head(app, client): 142 class Index(flask.views.MethodView): 143 def get(self): 144 return flask.Response("Blub", headers={"X-Method": flask.request.method}) 145 146 app.add_url_rule("/", view_func=Index.as_view("index")) 147 rv = client.get("/") 148 assert rv.data == b"Blub" 149 assert rv.headers["X-Method"] == "GET" 150 rv = client.head("/") 151 assert rv.data == b"" 152 assert rv.headers["X-Method"] == "HEAD" 153 154 155def test_explicit_head(app, client): 156 class Index(flask.views.MethodView): 157 def get(self): 158 return "GET" 159 160 def head(self): 161 return flask.Response("", headers={"X-Method": "HEAD"}) 162 163 app.add_url_rule("/", view_func=Index.as_view("index")) 164 rv = client.get("/") 165 assert rv.data == b"GET" 166 rv = client.head("/") 167 assert rv.data == b"" 168 assert rv.headers["X-Method"] == "HEAD" 169 170 171def test_endpoint_override(app): 172 app.debug = True 173 174 class Index(flask.views.View): 175 methods = ["GET", "POST"] 176 177 def dispatch_request(self): 178 return flask.request.method 179 180 app.add_url_rule("/", view_func=Index.as_view("index")) 181 182 with pytest.raises(AssertionError): 183 app.add_url_rule("/", view_func=Index.as_view("index")) 184 185 # But these tests should still pass. We just log a warning. 186 common_test(app) 187 188 189def test_methods_var_inheritance(app, client): 190 class BaseView(flask.views.MethodView): 191 methods = ["GET", "PROPFIND"] 192 193 class ChildView(BaseView): 194 def get(self): 195 return "GET" 196 197 def propfind(self): 198 return "PROPFIND" 199 200 app.add_url_rule("/", view_func=ChildView.as_view("index")) 201 202 assert client.get("/").data == b"GET" 203 assert client.open("/", method="PROPFIND").data == b"PROPFIND" 204 assert ChildView.methods == {"PROPFIND", "GET"} 205 206 207def test_multiple_inheritance(app, client): 208 class GetView(flask.views.MethodView): 209 def get(self): 210 return "GET" 211 212 class DeleteView(flask.views.MethodView): 213 def delete(self): 214 return "DELETE" 215 216 class GetDeleteView(GetView, DeleteView): 217 pass 218 219 app.add_url_rule("/", view_func=GetDeleteView.as_view("index")) 220 221 assert client.get("/").data == b"GET" 222 assert client.delete("/").data == b"DELETE" 223 assert sorted(GetDeleteView.methods) == ["DELETE", "GET"] 224 225 226def test_remove_method_from_parent(app, client): 227 class GetView(flask.views.MethodView): 228 def get(self): 229 return "GET" 230 231 class OtherView(flask.views.MethodView): 232 def post(self): 233 return "POST" 234 235 class View(GetView, OtherView): 236 methods = ["GET"] 237 238 app.add_url_rule("/", view_func=View.as_view("index")) 239 240 assert client.get("/").data == b"GET" 241 assert client.post("/").status_code == 405 242 assert sorted(View.methods) == ["GET"] 243