Files
webmail-client/backend/tests/test_completion_phrases.py
lukasandClaude Opus 5.5 8a1b9979e9 Productivity features, UI translations, local email translation
- Snooze (with a Snoozed folder), keyboard shortcuts with a Settings
  section, mail templates (Compose > More > Templates), a Subscriptions
  folder with bulk unsubscribe/delete, and tracker blocking with a
  Privacy toggle (on by default)
- Own dialogs instead of browser popups; emoji picker in compose; compose
  windows survive a visit to Settings
- Sentence completion learned from the user's sent mail (no AI)
- Interface in English and Polish (one file per language, English as the
  fallback), picked from the browser on first run, then kept
- Email translation on the server via LibreTranslate: Translate button
  and "Translate to...", messages in several languages translated part
  by part, names/addresses/emoji/file names/phone numbers kept, an
  animated line while translating
- Antispam "Mark as spam" loads with the message, not when the menu opens

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-18 20:00:31 +02:00

90 lines
3.3 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Sentence completion: phrases from the user's own sent mail, plus starters."""
from __future__ import annotations
from collections import Counter
from app.completion import build_phrases, choose, complete, html_to_text, own_text
def personal(*texts: str) -> dict[str, Counter[str]]:
out: dict[str, Counter[str]] = {}
for (ctx, cont), n in build_phrases(texts).items():
out.setdefault(ctx, Counter())[cont] = n
return out
def test_own_text_drops_quotes_and_signature() -> None:
for quote in (
"On Mon, 5 Oct 2026 Anna wrote:",
"W dniu 5.10.2026 Anna pisze:",
"Am 05.10.2026 um 10:00 schrieb Anna:",
"-----Original Message-----",
"From: Anna <anna@x.example>",
"--",
):
text = f"Sure, I'll send it today.\n\n{quote}\nOld text here."
assert own_text(text).strip() == "Sure, I'll send it today.", quote
assert own_text("Yes.\n> quoted line\nAgreed.") == "Yes.\nAgreed."
def test_only_repeated_phrases_are_kept() -> None:
phrases = build_phrases(
["I'll send the drawings by Friday.", "I'll send the drawings by Friday."]
)
assert phrases[("the", "drawings by Friday.")] == 2
once = build_phrases(["A private sentence written once."])
assert once == {}
def test_personal_phrases_complete_in_any_language() -> None:
mine = personal(
"I'll send the drawings by Friday.",
"I'll send the drawings by Friday.",
"Prześlę rysunki do piątku.",
"Prześlę rysunki do piątku.",
)
assert complete(mine, "I'll send the dr") == "awings by Friday."
assert complete(mine, "Prześlę rys") == "unki do piątku."
assert complete(mine, "Prześlę rysunki ") == "do piątku."
def test_starter_phrases_and_spacing() -> None:
assert complete({}, "I look forward ") == "to hearing from you."
assert complete({}, "I look forward") == " to hearing from you." # space added
assert complete({}, "Z pow") == "ażaniem,"
assert complete({}, "Mit freundlichen ") == "Grüßen"
def test_nothing_after_punctuation_or_on_an_empty_line() -> None:
assert complete({}, "Thanks,") is None
assert complete({}, "Done. ") is None
assert complete({}, "") is None
assert complete({}, " ") is None
def test_suggestion_stops_where_habits_part() -> None:
# "see you" is followed by "soon" or "tomorrow" equally: suggest only "you".
assert choose(Counter({"you": 4, "you soon": 2, "you tomorrow": 2})) == "you"
assert choose(Counter({"you": 4, "you soon": 4, "you soon then": 3})) == "you soon then"
def test_polish_gmail_quotes_and_own_signature_are_left_out() -> None:
text = (
"Dziękuję, prześlę jutro.\n"
"Imaginary Pixels – visual effects studio\n"
"pon., 5 paź 2026 o 10:00 Anna Nowak <anna@x.example> napisał(a):\n"
"> stary tekst"
)
sig = html_to_text(
"<div>Imaginary Pixels – visual effects studio</div><div>+48 602 691 231</div>"
)
assert own_text(text, [sig]).strip() == "Dziękuję, prześlę jutro."
def test_numbers_addresses_and_links_are_never_learned() -> None:
texts = ["Call me at +48 602 691 231 or write to me@x.example today."] * 3
learned = " ".join(f"{c} {n}" for (c, n) in build_phrases(texts))
assert "602" not in learned and "@" not in learned
assert ("call me", "at") in build_phrases(texts)