"""Verify the marketing landing template renders correctly.""" import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) os.environ.setdefault('SQLALCHEMY_DATABASE_URI', 'sqlite:///:memory:') os.environ.setdefault('SECRET_KEY', 'test-secret-key') from src.app import app # noqa: E402 def test_landing_renders_template_not_inline_html(): """GET / renders templates/marketing/landing.html (not inline HTML from Phase 1).""" client = app.test_client() response = client.get('/', follow_redirects=False) assert response.status_code == 200 body = response.data.decode('utf-8') # Phase 2 template hallmarks assert '' in body, "Missing DOCTYPE — base.html not rendering" assert 'lang="fr-CA"' in body, "Missing lang=fr-CA" assert '/static/css/marketing.css' in body, "Missing marketing.css link" assert '/static/fonts/Inter-Variable.woff2' in body, "Missing Inter font preload" assert '/static/js/alpine.min.js' in body, "Missing Alpine.js script" def test_landing_has_canonical_url(): """OG + canonical metadata present.""" client = app.test_client() response = client.get('/') body = response.data.decode('utf-8') assert 'rel="canonical"' in body assert 'og:type' in body assert 'og:locale' in body and 'fr_CA' in body assert 'twitter:card' in body def test_landing_has_glassmorphism_header(): """FlexiHub-style header present (navy + backdrop-blur).""" client = app.test_client() response = client.get('/') body = response.data.decode('utf-8') assert 'bg-brand-navy/[0.97]' in body or 'bg-brand-navy' in body assert 'backdrop-blur-xl' in body assert 'border-white/[0.045]' in body, "Missing FlexiHub-style 0.045 border opacity" def test_landing_has_main_nav(): """Main nav has 5 links: Fonctionnalités, Conformité, Tarifs, Blog, Contact.""" client = app.test_client() response = client.get('/') body = response.data.decode('utf-8') for link in ['/fonctionnalites', '/conformite', '/tarifs', '/blog', '/contact']: assert f'href="{link}"' in body, f"Missing nav link: {link}" def test_landing_has_login_and_signup_ctas(): """Login + Signup CTAs present in header.""" client = app.test_client() response = client.get('/') body = response.data.decode('utf-8') assert 'href="/login"' in body assert 'href="/signup"' in body assert 'Démarrer' in body or 'Démarrer' in body def test_landing_footer_has_legal_links(): """Footer placeholder includes legal links (full footer in A-2.7).""" client = app.test_client() response = client.get('/') body = response.data.decode('utf-8') assert '/legal/conditions' in body assert '/legal/confidentialite' in body assert 'info@dictia.ca' in body, "Missing canonical email info@dictia.ca" assert 'Inverness' in body, "Missing Inverness QC address" def test_landing_no_login_redirect_for_anonymous(): """Anonymous user GET / must see template (regression check from B-1.3).""" client = app.test_client() response = client.get('/', follow_redirects=False) assert response.status_code == 200, \ f"Expected 200, got {response.status_code} — possibly login_required regression" def test_hero_has_h1_with_grad_text_accent(): """Hero H1 contains grad-text span on the brand tagline.""" client = app.test_client() body = client.get('/').data.decode('utf-8') assert 'id="hero-title"' in body, "Missing hero-title id on H1" assert 'grad-text' in body, "Missing grad-text class somewhere" assert 'sans risquer votre permis' in body, "Missing key brand tagline" def test_hero_has_dual_cta(): """Hero has both primary (Réserver une démo) and ghost (Voir les tarifs) CTAs.""" client = app.test_client() body = client.get('/').data.decode('utf-8') assert 'href="/contact"' in body assert 'href="/tarifs"' in body assert 'Réserver une démo' in body or 'Réserver une démo' in body assert 'Voir les tarifs' in body def test_hero_has_cosmic_orbs_background(): """Hero has 3 radial gradient orbs (FlexiHub signature).""" client = app.test_client() body = client.get('/').data.decode('utf-8') # Look for the 3 orb opacities (16% blue, 7% cyan, 11% green) assert 'rgba(0,98,255,0.16)' in body, "Missing primary blue orb" assert 'rgba(0,189,216,0.07)' in body, "Missing cyan orb" assert 'rgba(0,200,150,0.11)' in body, "Missing green accent orb" def test_hero_has_social_proof_microcopy(): """Hero has defensible social proof: 9 ordres pros + waitlist + launch date.""" client = app.test_client() body = client.get('/').data.decode('utf-8') assert '9 ordres professionnels' in body, "Missing factual ordres pros count" assert 'Pré-inscription' in body or 'Pré-inscription' in body, "Missing waitlist mention" assert 'Lancement printemps 2026' in body, "Missing launch date" def test_hero_has_staggered_animations(): """Hero elements use tc-fade-in-up with staggered delays.""" client = app.test_client() body = client.get('/').data.decode('utf-8') assert 'animate-tc-fade-in-up' in body, "Missing fade-in animation" assert 'animation-delay: 0ms' in body assert 'animation-delay: 75ms' in body assert 'animation-delay: 150ms' in body assert 'animation-delay: 300ms' in body assert 'animation-delay: 400ms' in body assert 'animation-fill-mode: backwards' in body, \ "Missing animation-fill-mode (causes flash before delay fires)" def test_hero_eyebrow_has_brand_messaging(): """Hero eyebrow declares the 3 brand pillars.""" client = app.test_client() body = client.get('/').data.decode('utf-8') assert 'TRANSCRIPTION IA' in body assert 'CONFORME LOI 25' in body assert 'QU' in body # Either QUÉBEC or QUÉBEC def test_trust_bar_has_9_ordres_pros(): """Trust bar lists all 9 ordres professionnels by name.""" client = app.test_client() body = client.get('/').data.decode('utf-8') for ordre in ['Barreau', 'Notaires', 'CPA Québec', 'ChAD', 'OACIQ', 'CMQ', 'OIIQ', 'OPPQ', 'OPQ']: assert ordre in body, f"Missing ordre pro: {ordre}" def test_trust_bar_has_eyebrow_factual_phrasing(): """Trust bar eyebrow uses 'MAPPÉ AUX' (factual scope) not 'CERTIFIÉ PAR' (false endorsement).""" client = app.test_client() body = client.get('/').data.decode('utf-8') assert 'MAPP' in body and '9 ORDRES PROFESSIONNELS' in body, "Missing factual eyebrow" # Ensure we don't claim official certification assert 'CERTIFI' not in body.replace('CERTIFIE PAR', '').replace('CERTIFIÉ PAR', ''), \ "Avoid claiming certification — we map to ordres, we don't claim endorsement" def test_trust_bar_has_4_kpis_with_grad_text(): """Trust bar has 4 KPI metrics rendered with grad-text.""" client = app.test_client() body = client.get('/').data.decode('utf-8') assert '~5 min' in body assert '95 %+' in body or '95%+' in body assert '0 $' in body assert '100 %' in body or '100%' in body # Verify grad-text on KPI numbers assert 'grad-text mb-2' in body, "Missing grad-text on KPI numbers" def test_trust_bar_has_methodology_footnote(): """95%+ claim has a defensible methodology footnote (LPC art. 219 hygiene).""" client = app.test_client() body = client.get('/').data.decode('utf-8') # Look for the methodology disclosure assert 'test interne' in body, "Missing 'test interne' attribution on 95% claim" assert 'détails disponibles sur demande' in body or 'détails disponibles sur demande' in body, \ "Missing methodology disclosure footnote"