fix(auth): B-2.2 review fixes — Tailwind path + WCAG + race + flash + tests

C-1: Add templates/register.html (and templates/auth/**) to tailwind.config.js
content array so utility classes used by the signup template don't get purged
on next build. Rebuilt static/css/marketing.css; verified text-brand-navy/90
and min-h-[calc(100vh-62px)] are now compiled.

I-1: Replace flash() calls for missing required consents with WTForms
field-level errors (form.consent_cgu.errors.append / form.consent_confidentialite
.errors.append). Errors render inline next to each consent checkbox via
{% if form.consent_cgu.errors %}<p role="alert">…</p>{% endif %}. Prevents
session-backed flash messages from leaking across unrelated navigations.

I-2: Wrap user creation + flush in IntegrityError retry loop (max 5 attempts);
import IntegrityError from sqlalchemy.exc. Absorbs the inherent race between
_generate_unique_username's lookup and the subsequent flush under concurrent
signups. Added docstring note to _generate_unique_username explaining the
wrapper.

I-3: Move db.create_all() inside the try/finally in
test_signup_route_csrf_enforced so WTF_CSRF_ENABLED is restored even if
table creation fails.

I-4: Pin test_signup_rejects_duplicate_email assertion to status_code == 200
(WTForms validate_email raises ValidationError → form fails validation →
fall-through to default 200 render_template).

I-5: Add id="password-help" to the password help paragraph and
aria-describedby="password-help" to the password input so screen readers
announce the password requirements when the field is focused.

I-6: Bump flash banner text colors from -700/-800 to -900 variants
(text-amber-900, text-blue-900, text-red-900, text-green-900) for safer
WCAG 2.2 AA contrast against the -50 backgrounds. Same bump applied to the
new consent and password inline error renders.
This commit is contained in:
Allison
2026-04-27 22:43:00 -04:00
parent d2fc1f03ed
commit 3b324ad0b9
5 changed files with 75 additions and 27 deletions

View File

@@ -209,7 +209,12 @@ def test_signup_rejects_duplicate_email():
data['consent_confidentialite'] = 'y'
resp = client.post('/signup', data=data)
# Form re-rendered (200) with validation error from validate_email.
assert resp.status_code in (200, 400)
# Pinned to 200 exactly: validate_email raises ValidationError →
# form fails validate_on_submit() → fall-through to the final
# `return render_template(...)` which uses the default 200.
assert resp.status_code == 200, (
f"Form should re-render with error inline (200), got {resp.status_code}"
)
body = resp.data.decode('utf-8').lower()
assert 'courriel' in body and ('déjà' in body or 'deja' in body)
@@ -285,8 +290,10 @@ def test_signup_route_csrf_enforced():
with app.app_context():
prev = app.config.get('WTF_CSRF_ENABLED', True)
app.config['WTF_CSRF_ENABLED'] = True
db.create_all()
try:
# create_all() is INSIDE the try so a failure here still restores
# WTF_CSRF_ENABLED for the rest of the test session.
db.create_all()
client = app.test_client()
data = dict(VALID_FORM)
data['email'] = 'csrf@example.qc.ca'