"""Tests for the secondary marketing pages (A-2.8).""" 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 # === /tarifs === def test_tarifs_route_returns_200(): client = app.test_client() response = client.get('/tarifs') assert response.status_code == 200, "GET /tarifs must return 200" def test_tarifs_extends_marketing_base(): client = app.test_client() body = client.get('/tarifs').data.decode('utf-8') assert '' in body assert 'lang="fr-CA"' in body assert '/static/css/marketing.css' in body assert '/static/js/alpine.min.js' in body def test_tarifs_has_h1_with_anchor(): client = app.test_client() body = client.get('/tarifs').data.decode('utf-8') assert 'page-title' in body assert '' in body assert 'scope="col"' in body assert 'scope="row"' in body # v7.0 row keywords (matches the rows in tarifs.html) for kw in ['Hébergement', 'GPU', 'Capacité audio', 'Stockage', 'Utilisateurs', 'Diarisation pyannote', 'Loi 25', 'SLA', 'Délai']: assert kw in body, f"Missing matrix row keyword: {kw}" def test_tarifs_pricing_faq_v7(): """v7.0 tarifs FAQ has 7 questions (added DictIA Local + Cloud Pro onboarding + Pro+ explanations).""" client = app.test_client() body = client.get('/tarifs').data.decode('utf-8') assert 'tarifs-faq-title' in body for i in range(1, 8): assert f'tarifs-faq-panel-{i}' in body, f"Missing tarifs FAQ panel {i}" # Alpine accordion bindings assert body.count('x-data="{ open: false }"') >= 7 # Each accordion button has focus-visible (WCAG 2.4.7/2.4.11) assert 'focus-visible:outline-2' in body def test_tarifs_uses_oqlf_typography(): client = app.test_client() body = client.get('/tarifs').data.decode('utf-8') assert 'TPS 5 %' in body assert 'TVQ 9,975 %' in body assert 'Loi 25' in body # No double-escape assert '&nbsp;' not in body, "Pricing macro / safe filter regression" # === /fonctionnalites === def test_fonctionnalites_route_returns_200(): client = app.test_client() response = client.get('/fonctionnalites') assert response.status_code == 200 def test_fonctionnalites_extends_marketing_base(): client = app.test_client() body = client.get('/fonctionnalites').data.decode('utf-8') assert '' in body assert 'lang="fr-CA"' in body assert '/static/css/marketing.css' in body def test_fonctionnalites_h1_present(): client = app.test_client() body = client.get('/fonctionnalites').data.decode('utf-8') assert 'page-title' in body assert 'rester' in body or 'restant chez soi' in body def test_fonctionnalites_renders_6_bento_cards(): client = app.test_client() body = client.get('/fonctionnalites').data.decode('utf-8') assert 'features-title' in body # 6 watermark numbers for n in ['01', '02', '03', '04', '05', '06']: assert f'>{n}<' in body # 6 feature anchors for kw in ['WhisperX', 'Diarisation', 'Mistral 7B', 'RAG local', 'DOCX, PDF, SRT', 'Outlook, Teams']: assert kw in body def test_fonctionnalites_export_formats_section(): client = app.test_client() body = client.get('/fonctionnalites').data.decode('utf-8') assert 'exports-title' in body for ext in ['DOCX', 'PDF', 'SRT', 'VTT', 'TXT', 'JSON', 'MD']: # Each format has its own card with the .ext as a heading assert f'>{ext}<' in body, f"Missing export format card: {ext}" def test_fonctionnalites_integrations_grid(): client = app.test_client() body = client.get('/fonctionnalites').data.decode('utf-8') assert 'integrations-title' in body for name in ['Microsoft Word', 'Microsoft Outlook', 'Microsoft Teams', 'Notion', 'Obsidian', 'Zapier', 'Make', 'n8n']: assert name in body, f"Missing integration: {name}" # Trademark disclaimer assert 'marques de leurs propriétaires' in body or 'marques de leurs propriétaires' in body def test_fonctionnalites_tech_specs_6_items(): client = app.test_client() body = client.get('/fonctionnalites').data.decode('utf-8') assert 'specs-title' in body for spec_keyword in ['Modèle ASR', 'pyannote', 'Mistral 7B', 'Flask', 'WAV, MP3', 'québécois']: assert spec_keyword in body, f"Missing tech spec keyword: {spec_keyword}" def test_fonctionnalites_uses_oqlf_typography(): client = app.test_client() body = client.get('/fonctionnalites').data.decode('utf-8') # NBSP entities assert '95 %+' in body, "WhisperX precision NBSP entity" # No double-escape assert '&nbsp;' not in body # === Cross-page checks === def test_secondary_pages_in_main_nav(): """Header nav links to /tarifs and /fonctionnalites — verify both pages now respond.""" client = app.test_client() for url in ['/tarifs', '/fonctionnalites']: # Each page should self-link in its own nav (consistency) body = client.get(url).data.decode('utf-8') assert 'href="/tarifs"' in body and 'href="/fonctionnalites"' in body, \ f"Page {url} must include nav links to both new pages" def test_secondary_pages_have_canonical_meta(): """Both pages must have canonical URL + OG metadata via base.html.""" client = app.test_client() for url in ['/tarifs', '/fonctionnalites']: body = client.get(url).data.decode('utf-8') assert 'rel="canonical"' in body assert 'og:type' in body assert 'twitter:card' in body # === /conformite === def test_conformite_route_returns_200(): client = app.test_client() response = client.get('/conformite') assert response.status_code == 200 def test_conformite_extends_base_with_h1(): client = app.test_client() body = client.get('/conformite').data.decode('utf-8') assert '' in body assert 'lang="fr-CA"' in body assert 'page-title' in body assert body.count('' in body assert 'page-title' in body assert body.count('