feat: affiliates disclosure — per-retailer tooltip copy + first-encounter banner constants
This commit is contained in:
parent
4c3f3a95a5
commit
73cec07bd2
2 changed files with 78 additions and 0 deletions
49
circuitforge_core/affiliates/disclosure.py
Normal file
49
circuitforge_core/affiliates/disclosure.py
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
"""Affiliate disclosure copy constants.
|
||||||
|
|
||||||
|
Follows the plain-language disclosure design from the affiliate links design
|
||||||
|
doc. All copy is centralized here so products don't drift out of sync and
|
||||||
|
legal/copy review has a single file to audit.
|
||||||
|
"""
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
# Per-retailer tooltip copy (shown on hover/tap of affiliate link indicator)
|
||||||
|
_TOOLTIP: dict[str, str] = {
|
||||||
|
"ebay": (
|
||||||
|
"Affiliate link — CircuitForge earns a small commission if you purchase "
|
||||||
|
"on eBay. No purchase data is shared with us. [Opt out in Settings]"
|
||||||
|
),
|
||||||
|
"amazon": (
|
||||||
|
"Affiliate link — CircuitForge earns a small commission if you purchase "
|
||||||
|
"on Amazon. No purchase data is shared with us. [Opt out in Settings]"
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
_GENERIC_TOOLTIP = (
|
||||||
|
"Affiliate link — CircuitForge may earn a small commission if you purchase. "
|
||||||
|
"No purchase data is shared with us. [Opt out in Settings]"
|
||||||
|
)
|
||||||
|
|
||||||
|
# First-encounter banner copy (shown once, then preference saved)
|
||||||
|
BANNER_COPY: dict[str, str] = {
|
||||||
|
"title": "A note on purchase links",
|
||||||
|
"body": (
|
||||||
|
"Some links in this product go to retailers using our affiliate code. "
|
||||||
|
"When you click one, the retailer knows you came from CircuitForge. "
|
||||||
|
"We don't see or store what you buy. The retailer may track your "
|
||||||
|
"purchase — that's between you and them.\n\n"
|
||||||
|
"If you'd rather use plain links with no tracking code, you can opt "
|
||||||
|
"out in Settings."
|
||||||
|
),
|
||||||
|
"dismiss_label": "Got it",
|
||||||
|
"opt_out_label": "Opt out now",
|
||||||
|
"learn_more_label": "Learn more",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_disclosure_text(retailer: str) -> str:
|
||||||
|
"""Return the tooltip disclosure string for *retailer*.
|
||||||
|
|
||||||
|
Falls back to a generic string for unregistered retailers so callers
|
||||||
|
never receive an empty string.
|
||||||
|
"""
|
||||||
|
return _TOOLTIP.get(retailer, _GENERIC_TOOLTIP)
|
||||||
29
tests/test_affiliates/test_disclosure.py
Normal file
29
tests/test_affiliates/test_disclosure.py
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
"""Tests for affiliate disclosure text."""
|
||||||
|
import pytest
|
||||||
|
from circuitforge_core.affiliates.disclosure import get_disclosure_text, BANNER_COPY
|
||||||
|
|
||||||
|
|
||||||
|
class TestGetDisclosureText:
|
||||||
|
def test_returns_string_for_known_retailer(self):
|
||||||
|
text = get_disclosure_text("ebay")
|
||||||
|
assert isinstance(text, str)
|
||||||
|
assert len(text) > 0
|
||||||
|
|
||||||
|
def test_ebay_copy_mentions_ebay(self):
|
||||||
|
text = get_disclosure_text("ebay")
|
||||||
|
assert "eBay" in text
|
||||||
|
|
||||||
|
def test_amazon_copy_mentions_amazon(self):
|
||||||
|
text = get_disclosure_text("amazon")
|
||||||
|
assert "Amazon" in text
|
||||||
|
|
||||||
|
def test_unknown_retailer_returns_generic(self):
|
||||||
|
text = get_disclosure_text("not_a_retailer")
|
||||||
|
assert isinstance(text, str)
|
||||||
|
assert len(text) > 0
|
||||||
|
|
||||||
|
def test_banner_copy_has_required_keys(self):
|
||||||
|
assert "title" in BANNER_COPY
|
||||||
|
assert "body" in BANNER_COPY
|
||||||
|
assert "opt_out_label" in BANNER_COPY
|
||||||
|
assert "dismiss_label" in BANNER_COPY
|
||||||
Loading…
Reference in a new issue