mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-12 07:19:49 -05:00
b0dde80ba9
Add a support modal with usage stats, tier and license links, share control, and offline-safe outbound CTAs. Surface support from the header, sidebar, user menu, dashboard card, and settings "Support & Community" section without hiding entry points when a supporter license is active. Introduce UsageStatsService and a persisted users.support_stats_reports_generated counter incremented on key report exports and custom report views. Add SupportPromptService for session-scoped soft toasts (after export, dashboard milestones, long session via POST /donate/request-soft-prompt). Wire consent-aware track_event names support.* and mirror funnel rows in DonationInteraction; fix has_recent_donation_click to treat link_clicked as a recent click. Document events and SUPPORT_* / migration notes in docs. Tests: tests/test_support_services.py for prompt and usage stats behavior.
64 lines
2.2 KiB
Python
64 lines
2.2 KiB
Python
"""Tests for support prompt and usage stats services."""
|
|
|
|
from app.services.support_prompt_service import SupportPromptService
|
|
from app.services.usage_stats_service import UsageStatsService
|
|
|
|
|
|
def test_usage_stats_service_shape(app, test_user):
|
|
with app.app_context():
|
|
s = UsageStatsService.get_for_user(test_user.id)
|
|
assert "total_hours" in s
|
|
assert "time_entries_count" in s
|
|
assert "days_since_signup" in s
|
|
assert "reports_generated_count" in s
|
|
|
|
|
|
def test_consume_layout_prompt_sets_consumed():
|
|
session = {"support_prompt_trigger": SupportPromptService.VARIANT_AFTER_REPORT}
|
|
payload = SupportPromptService.consume_layout_prompt(
|
|
session,
|
|
ui_show_donate=True,
|
|
is_supporter=False,
|
|
support_banner_suppressed=False,
|
|
)
|
|
assert payload is not None
|
|
assert payload.get("variant") == SupportPromptService.VARIANT_AFTER_REPORT
|
|
assert session.get(SupportPromptService.SESSION_SOFT_PROMPT_CONSUMED) is True
|
|
assert "support_prompt_trigger" not in session
|
|
|
|
|
|
def test_support_prompt_suppressed_for_supporter():
|
|
session = {"support_prompt_trigger": SupportPromptService.VARIANT_AFTER_REPORT}
|
|
payload = SupportPromptService.consume_layout_prompt(
|
|
session,
|
|
ui_show_donate=True,
|
|
is_supporter=True,
|
|
support_banner_suppressed=False,
|
|
)
|
|
assert payload is None
|
|
|
|
|
|
def test_support_prompt_respects_ui_show_donate():
|
|
session = {"support_prompt_trigger": SupportPromptService.VARIANT_AFTER_REPORT}
|
|
payload = SupportPromptService.consume_layout_prompt(
|
|
session,
|
|
ui_show_donate=False,
|
|
is_supporter=False,
|
|
support_banner_suppressed=False,
|
|
)
|
|
assert payload is None
|
|
|
|
|
|
def test_pick_dashboard_skips_when_after_report_pending():
|
|
session = {"support_prompt_trigger": SupportPromptService.VARIANT_AFTER_REPORT}
|
|
user_stats = {"days_since_signup": 100, "time_entries_count": 1, "total_hours": 1.0}
|
|
picked = SupportPromptService.pick_dashboard_prompt(
|
|
session,
|
|
user_stats,
|
|
ui_show_donate=True,
|
|
is_supporter=False,
|
|
support_banner_suppressed=False,
|
|
today_hours=8.0,
|
|
)
|
|
assert picked is None
|