@@ -182,6 +182,77 @@ def test_logout_endpoint(auth_client: TestClient):
182182# --- Error Case Tests ---
183183
184184
185+ def test_register_page_shows_password_requirements (unauth_client : TestClient ):
186+ """Issue #156: Register page must display password requirements visibly."""
187+ response = unauth_client .get (app .url_path_for ("read_register" ))
188+ assert response .status_code == 200
189+ html = response .text
190+ # Requirements should be visible as text (not just in hidden pattern/title attributes)
191+ assert "8" in html , "Page should mention minimum 8 characters"
192+ assert "uppercase" in html .lower (), "Page should mention uppercase requirement"
193+ assert "lowercase" in html .lower (), "Page should mention lowercase requirement"
194+ assert "number" in html .lower () or "digit" in html .lower (), "Page should mention digit requirement"
195+ assert "special" in html .lower (), "Page should mention special character requirement"
196+
197+
198+ def test_register_page_confirm_password_has_autocomplete (unauth_client : TestClient ):
199+ """Issue #156: Both password fields must have autocomplete='new-password' for Chrome autofill."""
200+ response = unauth_client .get (app .url_path_for ("read_register" ))
201+ assert response .status_code == 200
202+ html = response .text
203+ # The confirm_password field should have autocomplete="new-password"
204+ assert 'id="confirm_password"' in html
205+ # Find the confirm_password input and check it has autocomplete="new-password"
206+ import re
207+ confirm_input = re .search (r'<input[^>]*id="confirm_password"[^>]*>' , html )
208+ assert confirm_input is not None
209+ assert 'autocomplete="new-password"' in confirm_input .group (0 ), \
210+ "confirm_password field must have autocomplete='new-password' for Chrome autofill"
211+
212+
213+ def test_register_weak_password_error_restates_requirements (unauth_client : TestClient , session : Session ):
214+ """Issue #156: Error toast for weak password must restate the security policy requirements."""
215+ response = unauth_client .post (
216+ app .url_path_for ("register" ),
217+ data = {
218+ "name" : "Test User" ,
219+ "email" : "weak@example.com" ,
220+ "password" : "weak" ,
221+ "confirm_password" : "weak"
222+ },
223+ )
224+ assert response .status_code == 422
225+ text = response .text
226+ # The error message must include the actual requirements, not just a generic message
227+ assert "8" in text , "Error should mention minimum 8 characters"
228+ assert "uppercase" in text .lower () or "upper" in text .lower (), \
229+ "Error should mention uppercase requirement"
230+ assert "lowercase" in text .lower () or "lower" in text .lower (), \
231+ "Error should mention lowercase requirement"
232+
233+
234+ def test_register_weak_password_htmx_error_restates_requirements (unauth_client : TestClient , session : Session ):
235+ """Issue #156: HTMX error toast for weak password must restate the security policy requirements."""
236+ response = unauth_client .post (
237+ app .url_path_for ("register" ),
238+ data = {
239+ "name" : "Test User" ,
240+ "email" : "weak@example.com" ,
241+ "password" : "weak" ,
242+ "confirm_password" : "weak"
243+ },
244+ headers = {"HX-Request" : "true" },
245+ )
246+ assert response .status_code == 422
247+ text = response .text
248+ # The toast message must include the actual requirements
249+ assert "8" in text , "Toast should mention minimum 8 characters"
250+ assert "uppercase" in text .lower () or "upper" in text .lower (), \
251+ "Toast should mention uppercase requirement"
252+ assert "lowercase" in text .lower () or "lower" in text .lower (), \
253+ "Toast should mention lowercase requirement"
254+
255+
185256def test_register_with_existing_email (unauth_client : TestClient , test_account : Account ):
186257 response = unauth_client .post (
187258 app .url_path_for ("register" ),
0 commit comments