diff --git a/app/config.py b/app/config.py index 1198d9f..94300c7 100644 --- a/app/config.py +++ b/app/config.py @@ -13,7 +13,7 @@ # (app/core/update_check.py) compares this against the latest GitHub release # tag to decide whether to prompt the user to update. Keep it in step with the # release tag and packaging/msix/AppxManifest.xml on every release. -APP_VERSION = "1.5.0" +APP_VERSION = "1.6.0" ICON_PATH = Path(__file__).parent / "resources" / "icons" / "app_icon.png" diff --git a/packaging/make_screenshots.py b/packaging/make_screenshots.py index d13a20d..b6fa584 100644 --- a/packaging/make_screenshots.py +++ b/packaging/make_screenshots.py @@ -56,6 +56,130 @@ sys.path.insert(0, str(REPO_ROOT)) +# --------------------------------------------------------------------------- +# Font coverage check — catch missing glyphs before rendering, not after. +# +# When the CI runner lacks fonts for a locale's primary script (Bengali, +# Telugu, …), Qt renders every native-script character as a tofu rectangle +# (□). The resulting screenshots are useless, and the run looks green. +# +# _LOCALE_WRITING_SYSTEMS maps each supported locale to its Qt WritingSystem. +# _check_font_coverage() queries QFontDatabase.families(writing_system) after +# the QApplication exists, and fails the job immediately if no fonts are +# available — so the error names the cause rather than producing bad images. +# --------------------------------------------------------------------------- + +_LOCALE_WRITING_SYSTEMS: dict[str, str] = { + "am": None, # Ethiopic — no matching Qt enum; skipped + "ar": "Arabic", + "bn": "Bengali", + "cs": "Latin", + "de": "Latin", + "el": "Greek", + "en": "Latin", + "es": "Latin", + "fa": "Arabic", + "fr": "Latin", + "gu": "Gujarati", + "ha": "Latin", + "he": "Hebrew", + "hi": "Devanagari", + "hr": "Latin", + "hu": "Latin", + "id": "Latin", + "ig": "Latin", + "it": "Latin", + "ja": "Japanese", + "jv": "Latin", + "kn": "Kannada", + "ko": "Korean", + "ml": "Malayalam", + "mr": "Devanagari", + "ms": "Latin", + "my": "Myanmar", + "ne": "Devanagari", + "nl": "Latin", + "or": "Oriya", + "pa": "Gurmukhi", + "pl": "Latin", + "pt": "Latin", + "ro": "Latin", + "ru": "Cyrillic", + "si": "Sinhala", + "so": "Latin", + "sv": "Latin", + "sw": "Latin", + "ta": "Tamil", + "te": "Telugu", + "th": "Thai", + "tr": "Latin", + "uk": "Cyrillic", + "ur": "Arabic", + "uz": "Latin", + "vi": "Vietnamese", + "yo": "Latin", + "zh": "SimplifiedChinese", + "zu": "Latin", +} + + +def _check_font_coverage(locale: str) -> list[str]: + """Verify the system has fonts for the locale's primary script. + + Returns a list of problem descriptions (empty = all clear). Must be + called after QApplication exists. + """ + from PySide6.QtGui import QFontDatabase + + ws_name = _LOCALE_WRITING_SYSTEMS.get(locale) + if ws_name is None: + return [] + + ws = getattr(QFontDatabase.WritingSystem, ws_name, None) + if ws is None: + return [] + + families = QFontDatabase.families(ws) + if not families: + return [ + f"No fonts found for {ws_name} script (locale '{locale}'). " + f"Screenshots will contain tofu characters (□). " + f"Install a font covering this script on the runner." + ] + return [] + + +def audit_for_tofu(root: Path) -> list[str]: + """Post-render check: flag screenshots with suspiciously low complexity. + + A belt-and-braces pass after the font check. Opens each PNG and checks + that the content area has enough visual variation to contain real text. + Tofu characters compress into very uniform images, so an abnormally small + file relative to its pixel count is a warning sign. + """ + from PIL import Image + + problems = [] + for png in sorted(root.rglob("*.png")): + img = Image.open(png) + pixel_count = img.width * img.height + file_bytes = png.stat().st_size + if pixel_count == 0: + problems.append(f"{png.name}: empty image") + continue + bits_per_pixel = (file_bytes * 8) / pixel_count + # Real screenshots with anti-aliased text and UI chrome compress to + # roughly 1–6 bits/pixel in PNG. Pure tofu with uniform backgrounds + # compresses below 0.1. A threshold of 0.15 catches obvious cases + # without false-positiving on sparse-but-real pages. + if bits_per_pixel < 0.15: + problems.append( + f"{png.name}: unusually low complexity " + f"({bits_per_pixel:.2f} bits/px) — may contain tofu text" + ) + return problems + + # Store-required canvas sizes, in logical pixels, with the scale factor each is # rendered at. Apple and Microsoft both police these dimensions, so they are # produced exactly rather than resized afterwards. @@ -1019,6 +1143,14 @@ def main() -> int: app = QApplication.instance() or QApplication([]) print(f"Qt platform plugin: {app.platformName()}") + + font_problems = _check_font_coverage(args.locale) + if font_problems: + for p in font_problems: + print(f"FONT ERROR: {p}") + sys.stdout.flush() + os._exit(3) + if args.unseeded: from app.core.db import init_db @@ -1126,6 +1258,13 @@ def main() -> int: shutil.rmtree(scratch, ignore_errors=True) print(f"{written} screenshot(s) written to {out_root}") + + tofu_problems = audit_for_tofu(out_root) + if tofu_problems: + print(f"\n{len(tofu_problems)} screenshot(s) may have encoding issues:") + for p in tofu_problems: + print(f" WARNING: {p}") + sys.stdout.flush() # Leave immediately rather than unwinding Qt. @@ -1137,7 +1276,8 @@ def main() -> int: # disk, but with a non-zero exit code that would fail the CI job for no # real reason. Every file is written and flushed by this point, so there is # nothing left to clean up. - os._exit(0 if written else 1) + exit_code = 0 if written and not tofu_problems else 1 + os._exit(exit_code) def audit_for_secrets(root: Path) -> list[str]: diff --git a/packaging/msix/AppxManifest.xml b/packaging/msix/AppxManifest.xml index a2319a8..9bd3b0f 100644 --- a/packaging/msix/AppxManifest.xml +++ b/packaging/msix/AppxManifest.xml @@ -9,7 +9,7 @@ diff --git a/site/ar/download.html b/site/ar/download.html index 1a0924b..9ef9b48 100644 --- a/site/ar/download.html +++ b/site/ar/download.html @@ -51,7 +51,7 @@

تنزيل

-

الإصدار 1.3.1

+

الإصدار 1.6.0

لأنظمة Windows و macOS. التثبيت مجاني — ومفتاح الترخيص يفتح الاستخدام الإنتاجي. أما نسختا Microsoft Store و Mac App Store فتفتحانه داخل التطبيق نفسه.

@@ -65,7 +65,7 @@

الإصدار 1.3.1

Windows 10 و11

64 بت · 86 ميغابايت

فُك الضغط في أي مكان، ثم شغّل EasyPostDesktop.exe

- تنزيل لنظام Windows + تنزيل لنظام Windows
@@ -73,7 +73,7 @@

Windows 10 و11

macOS على معالجات Apple silicon

Apple silicon · 82 ميغابايت

موثّقة من Apple · تُفتح بلا أي تحذيرات

- تنزيل لنظام macOS + تنزيل لنظام macOS
diff --git a/site/bn/download.html b/site/bn/download.html index 37f5b3d..4378986 100644 --- a/site/bn/download.html +++ b/site/bn/download.html @@ -51,7 +51,7 @@

ডাউনলোড

-

সংস্করণ ১.৩.১

+

সংস্করণ ১.৬.০

Windows ও macOS। বিনামূল্যে ইনস্টল করুন — একটি লাইসেন্স কী প্রোডাকশন ব্যবহার আনলক করে। Microsoft Store এবং Mac App Store সংস্করণগুলি এর পরিবর্তে অ্যাপের মধ্যেই এটি আনলক করে।

@@ -65,7 +65,7 @@

সংস্করণ ১.৩.১

Windows 10 ও 11

৬৪-বিট · ৮৬ MB

যেকোনো জায়গায় আনজিপ করুন, তারপর EasyPostDesktop.exe চালান

- Windows-এর জন্য ডাউনলোড করুন + Windows-এর জন্য ডাউনলোড করুন
@@ -73,7 +73,7 @@

Windows 10 ও 11

Apple silicon-এ macOS

Apple silicon · 82 MB

Apple দ্বারা নোটারাইজড · কোনো সতর্কবার্তা ছাড়াই খোলে

- macOS-এর জন্য ডাউনলোড করুন + macOS-এর জন্য ডাউনলোড করুন
diff --git a/site/de/download.html b/site/de/download.html index 9629d7c..3d33b0e 100644 --- a/site/de/download.html +++ b/site/de/download.html @@ -51,7 +51,7 @@

Download

-

Version 1.3.1

+

Version 1.6.0

Windows und macOS. Kostenlose Installation – ein Lizenzschlüssel schaltet die produktive Nutzung frei. Bei den Versionen aus dem Microsoft Store und dem Mac App Store erfolgt die Freischaltung stattdessen direkt in der App.

@@ -65,7 +65,7 @@

Version 1.3.1

Windows 10 & 11

64-Bit · 86 MB

Entpacken Sie die Datei an einem beliebigen Ort und führen Sie dann EasyPostDesktop.exe aus

- Für Windows herunterladen + Für Windows herunterladen
@@ -73,7 +73,7 @@

Windows 10 & 11

macOS auf Apple Silicon

Apple Silicon · 82 MB

Von Apple notarisiert · öffnet ohne Warnungen

- Für macOS herunterladen + Für macOS herunterladen
diff --git a/site/download.html b/site/download.html index 7c2dfac..c564fdd 100644 --- a/site/download.html +++ b/site/download.html @@ -55,7 +55,7 @@

Download

-

Version 1.3.1

+

Version 1.6.0

Windows and macOS. Free to install — a licence key unlocks production use. The Microsoft Store and Mac App Store versions unlock it in the app instead.

@@ -69,7 +69,7 @@

Version 1.3.1

Windows 10 & 11

64-bit · 86 MB

Unzip anywhere, then run EasyPostDesktop.exe

- Download for Windows + Download for Windows
@@ -77,7 +77,7 @@

Windows 10 & 11

macOS on Apple silicon

Apple silicon · 82 MB

Notarized by Apple · opens without warnings

- Download for macOS + Download for macOS
diff --git a/site/es/download.html b/site/es/download.html index 1679b68..0a9e815 100644 --- a/site/es/download.html +++ b/site/es/download.html @@ -51,7 +51,7 @@

Descargar

-

Versión 1.3.1

+

Versión 1.6.0

Windows y macOS. Instalación gratuita — una clave de licencia desbloquea el uso en producción. Las versiones de Microsoft Store y Mac App Store lo desbloquean directamente en la aplicación.

@@ -65,7 +65,7 @@

Versión 1.3.1

Windows 10 y 11

64 bits · 86 MB

Descomprima en cualquier carpeta y ejecute EasyPostDesktop.exe

- Descargar para Windows + Descargar para Windows
@@ -73,7 +73,7 @@

Windows 10 y 11

macOS en Apple silicon

Apple silicon · 82 MB

Notarizado por Apple · se abre sin advertencias

- Descargar para macOS + Descargar para macOS
diff --git a/site/fr/download.html b/site/fr/download.html index dc7f020..36ea0bb 100644 --- a/site/fr/download.html +++ b/site/fr/download.html @@ -51,7 +51,7 @@

Télécharger

-

Version 1.3.1

+

Version 1.6.0

Windows et macOS. Installation gratuite — une clé de licence débloque l'usage en production. Les versions du Microsoft Store et du Mac App Store le débloquent directement dans l'application.

@@ -65,7 +65,7 @@

Version 1.3.1

Windows 10 et 11

64 bits · 86 Mo

Décompressez où vous le souhaitez, puis lancez EasyPostDesktop.exe

- Télécharger pour Windows + Télécharger pour Windows
@@ -73,7 +73,7 @@

Windows 10 et 11

macOS sur puce Apple

Puce Apple · 82 Mo

Notarisé par Apple · s'ouvre sans avertissement

- Télécharger pour macOS + Télécharger pour macOS
diff --git a/site/hi/download.html b/site/hi/download.html index 377e68f..b60c751 100644 --- a/site/hi/download.html +++ b/site/hi/download.html @@ -51,7 +51,7 @@

डाउनलोड

-

संस्करण 1.3.1

+

संस्करण 1.6.0

Windows और macOS। इंस्टॉल करना निःशुल्क है — एक लाइसेंस कुंजी उत्पादन उपयोग को अनलॉक करती है। Microsoft Store और Mac App Store संस्करण इसे इसके बजाय ऐप के भीतर अनलॉक करते हैं।

@@ -65,7 +65,7 @@

संस्करण 1.3.1

Windows 10 और 11

64-बिट · 86 MB

कहीं भी अनज़िप करें, फिर EasyPostDesktop.exe चलाएँ

- Windows के लिए डाउनलोड करें + Windows के लिए डाउनलोड करें
@@ -73,7 +73,7 @@

Windows 10 और 11

Apple silicon पर macOS

Apple silicon · 82 MB

Apple द्वारा सत्यापित (notarized) · बिना किसी चेतावनी के खुलता है

- macOS के लिए डाउनलोड करें + macOS के लिए डाउनलोड करें
diff --git a/site/id/download.html b/site/id/download.html index 68384dd..7eb62ee 100644 --- a/site/id/download.html +++ b/site/id/download.html @@ -51,7 +51,7 @@

Unduh

-

Versi 1.3.1

+

Versi 1.6.0

Windows dan macOS. Gratis dipasang — kunci lisensi membuka penggunaan produksi. Versi Microsoft Store dan Mac App Store membukanya langsung di dalam aplikasi.

@@ -65,7 +65,7 @@

Versi 1.3.1

Windows 10 & 11

64-bit · 86 MB

Ekstrak ke mana saja, lalu jalankan EasyPostDesktop.exe

- Unduh untuk Windows + Unduh untuk Windows
@@ -73,7 +73,7 @@

Windows 10 & 11

macOS pada Apple silicon

Apple silicon · 82 MB

Dinotarisasi oleh Apple · terbuka tanpa peringatan

- Unduh untuk macOS + Unduh untuk macOS
diff --git a/site/ja/download.html b/site/ja/download.html index 6f6d467..2fda00e 100644 --- a/site/ja/download.html +++ b/site/ja/download.html @@ -51,7 +51,7 @@

ダウンロード

-

バージョン 1.3.1

+

バージョン 1.6.0

Windows と macOS に対応しています。インストールは無料で、ライセンスキーにより本番利用が解放されます。Microsoft Store 版と Mac App Store 版はアプリ内で解放される仕組みです。

@@ -65,7 +65,7 @@

バージョン 1.3.1

Windows 10 & 11

64ビット・86 MB

任意の場所に解凍し、EasyPostDesktop.exe を実行してください

- Windows 用をダウンロード + Windows 用をダウンロード
@@ -73,7 +73,7 @@

Windows 10 & 11

Apple silicon 搭載の macOS

Apple silicon · 82 MB

Apple による公証済み · 警告なしで開きます

- macOS 用をダウンロード + macOS 用をダウンロード
diff --git a/site/mr/download.html b/site/mr/download.html index 85c21e4..878ad53 100644 --- a/site/mr/download.html +++ b/site/mr/download.html @@ -51,7 +51,7 @@

डाउनलोड

-

आवृत्ती 1.3.1

+

आवृत्ती 1.6.0

Windows आणि macOS. विनामूल्य इंस्टॉल करा — लायसन्स की उत्पादन वापर अनलॉक करते. Microsoft Store आणि Mac App Store आवृत्त्या त्याऐवजी अ‍ॅपमध्येच अनलॉक करतात.

@@ -65,7 +65,7 @@

आवृत्ती 1.3.1

Windows 10 आणि 11

64-बिट · 86 MB

कुठेही अनझिप करा, नंतर EasyPostDesktop.exe चालवा

- Windows साठी डाउनलोड करा + Windows साठी डाउनलोड करा
@@ -73,7 +73,7 @@

Windows 10 आणि 11

Apple silicon वर macOS

Apple silicon · 82 MB

Apple कडून नोटराइझ्ड · कोणत्याही चेतावणीशिवाय उघडते

- macOS साठी डाउनलोड करा + macOS साठी डाउनलोड करा
diff --git a/site/pt/download.html b/site/pt/download.html index cbfac7c..4cb6037 100644 --- a/site/pt/download.html +++ b/site/pt/download.html @@ -51,7 +51,7 @@

Transferir

-

Versão 1.3.1

+

Versão 1.6.0

Windows e macOS. Instalação gratuita — uma chave de licença desbloqueia o uso em produção. As versões da Microsoft Store e da Mac App Store desbloqueiam-no diretamente na aplicação.

@@ -65,7 +65,7 @@

Versão 1.3.1

Windows 10 e 11

64 bits · 86 MB

Descompacte em qualquer local e execute o EasyPostDesktop.exe

- Transferir para Windows + Transferir para Windows
@@ -73,7 +73,7 @@

Windows 10 e 11

macOS em Apple silicon

Apple silicon · 82 MB

Notarizado pela Apple · abre sem avisos

- Transferir para macOS + Transferir para macOS
diff --git a/site/ru/download.html b/site/ru/download.html index 6289880..9ad3508 100644 --- a/site/ru/download.html +++ b/site/ru/download.html @@ -51,7 +51,7 @@

Скачать

-

Версия 1.3.1

+

Версия 1.6.0

Windows и macOS. Бесплатная установка — ключ лицензии открывает работу в продуктивном режиме. Версии из Microsoft Store и Mac App Store открывают её прямо в приложении.

@@ -65,7 +65,7 @@

Версия 1.3.1

Windows 10 и 11

64-бит · 86 МБ

Распакуйте архив в любую папку, затем запустите EasyPostDesktop.exe

- Скачать для Windows + Скачать для Windows
@@ -73,7 +73,7 @@

Windows 10 и 11

macOS на Apple silicon

Apple silicon · 82 МБ

Нотариально заверено Apple · открывается без предупреждений

- Скачать для macOS + Скачать для macOS
diff --git a/site/sv/download.html b/site/sv/download.html index a92b927..d081566 100644 --- a/site/sv/download.html +++ b/site/sv/download.html @@ -51,7 +51,7 @@

Ladda ner

-

Version 1.3.1

+

Version 1.6.0

Windows och macOS. Gratis att installera — en licensnyckel låser upp produktionsanvändning. Versionerna från Microsoft Store och Mac App Store låser i stället upp den i appen.

@@ -65,7 +65,7 @@

Version 1.3.1

Windows 10 och 11

64-bitars · 86 MB

Packa upp var som helst, och kör sedan EasyPostDesktop.exe

- Ladda ner för Windows + Ladda ner för Windows
@@ -73,7 +73,7 @@

Windows 10 och 11

macOS för Apple silicon

Apple silicon · 82 MB

Notariserad av Apple · öppnas utan varningar

- Ladda ned för macOS + Ladda ned för macOS
diff --git a/site/te/download.html b/site/te/download.html index f4fce25..44c6533 100644 --- a/site/te/download.html +++ b/site/te/download.html @@ -51,7 +51,7 @@

డౌన్‌లోడ్

-

వెర్షన్ 1.3.1

+

వెర్షన్ 1.6.0

Windows మరియు macOS. ఇన్‌స్టాల్ చేయడం ఉచితం — ఒక లైసెన్స్ కీ ప్రొడక్షన్ ఉపయోగాన్ని అన్‌లాక్ చేస్తుంది. Microsoft Store మరియు Mac App Store వెర్షన్‌లు దీన్ని యాప్‌లోనే అన్‌లాక్ చేస్తాయి.

@@ -65,7 +65,7 @@

వెర్షన్ 1.3.1

Windows 10 & 11

64-బిట్ · 86 MB

ఎక్కడైనా అన్‌జిప్ చేసి, తర్వాత EasyPostDesktop.exe రన్ చేయండి

- Windows కోసం డౌన్‌లోడ్ చేయండి + Windows కోసం డౌన్‌లోడ్ చేయండి
@@ -73,7 +73,7 @@

Windows 10 & 11

Apple సిలికాన్‌పై macOS

Apple సిలికాన్ · 82 MB

Apple ద్వారా నోటరైజ్ చేయబడింది · హెచ్చరికలు లేకుండా తెరుచుకుంటుంది

- macOS కోసం డౌన్‌లోడ్ చేయండి + macOS కోసం డౌన్‌లోడ్ చేయండి
diff --git a/site/ur/download.html b/site/ur/download.html index 14a1b07..d4a4eb0 100644 --- a/site/ur/download.html +++ b/site/ur/download.html @@ -51,7 +51,7 @@

ڈاؤن لوڈ

-

ورژن 1.3.1

+

ورژن 1.6.0

Windows اور macOS۔ انسٹال کرنا مفت ہے — ایک لائسنس کلید پروڈکشن استعمال کو غیر مقفل کرتی ہے۔ Microsoft Store اور Mac App Store کے ورژنز اسے ایپ کے اندر ہی غیر مقفل کر دیتے ہیں۔

@@ -65,7 +65,7 @@

ورژن 1.3.1

Windows 10 اور 11

64-بٹ · 86 MB

کہیں بھی ان زپ کریں، پھر EasyPostDesktop.exe چلائیں

- Windows کے لیے ڈاؤن لوڈ کریں + Windows کے لیے ڈاؤن لوڈ کریں
@@ -73,7 +73,7 @@

Windows 10 اور 11

Apple silicon پر macOS

Apple silicon · 82 MB

Apple کی جانب سے نوٹرائزڈ · بغیر کسی وارننگ کے کھلتا ہے

- macOS کے لیے ڈاؤن لوڈ کریں + macOS کے لیے ڈاؤن لوڈ کریں
diff --git a/site/zh/download.html b/site/zh/download.html index 040cdec..c2b3609 100644 --- a/site/zh/download.html +++ b/site/zh/download.html @@ -51,7 +51,7 @@

下载

-

版本 1.3.1

+

版本 1.6.0

Windows 和 macOS。免费安装——许可证密钥可解锁正式使用。Microsoft Store 和 Mac App Store 版本则在应用内解锁。

@@ -65,7 +65,7 @@

版本 1.3.1

Windows 10 和 11

64 位 · 86 MB

解压到任意位置,然后运行 EasyPostDesktop.exe

- 下载 Windows 版 + 下载 Windows 版
@@ -73,7 +73,7 @@

Windows 10 和 11

适用于 Apple silicon 的 macOS

Apple silicon · 82 MB

已通过 Apple 公证 · 打开时不会出现警告

- 下载 macOS 版 + 下载 macOS 版
diff --git a/store_assets/RELEASE-NOTES-1.5.0.md b/store_assets/RELEASE-NOTES-1.6.0.md similarity index 87% rename from store_assets/RELEASE-NOTES-1.5.0.md rename to store_assets/RELEASE-NOTES-1.6.0.md index ed2243d..1703ccd 100644 --- a/store_assets/RELEASE-NOTES-1.5.0.md +++ b/store_assets/RELEASE-NOTES-1.6.0.md @@ -1,10 +1,11 @@ -# Easy-Post Desktop — release notes 1.5.0 +# Easy-Post Desktop — release notes 1.6.0 Covers everything since 1.3.2, which was the last published version on both -stores. The version jump to 1.5.0 is deliberate: 1.4.0 was submitted to the Mac -App Store but not to Partner Center, and the two stores now share a version. +stores. The version jump to 1.6.0 is deliberate: 1.4.0 was submitted to the Mac +App Store, 1.5.0 went live on the Microsoft Store, and 1.6.0 brings all three +channels (Partner Center, Mac App Store, direct download) back in sync. -## What is in 1.5.0 for a customer +## What is in 1.6.0 for a customer **Royal Mail Click & Drop — ship with Royal Mail directly (#92).** A second carrier path alongside EasyPost. Enter a Click & Drop API key in Settings, and diff --git a/tests/test_screenshot_guard.py b/tests/test_screenshot_guard.py index 2c5d39d..c6e7a4e 100644 --- a/tests/test_screenshot_guard.py +++ b/tests/test_screenshot_guard.py @@ -77,3 +77,45 @@ def test_audit_flags_a_forbidden_capture(shots, tmp_path): def test_audit_allows_a_settings_capture(shots, tmp_path): (tmp_path / "en_9_settings.png").write_bytes(b"") assert shots.audit_for_secrets(tmp_path) == [] + + +# ── Font coverage and tofu detection ────────────────────────────────────── + + +def test_locale_writing_system_mapping_covers_all_locales(shots): + """Every locale JSON must have an entry in the writing system mapping.""" + locales_dir = REPO_ROOT / "app" / "resources" / "locales" + for f in sorted(locales_dir.glob("*.json")): + locale = f.stem + assert locale in shots._LOCALE_WRITING_SYSTEMS, ( + f"Locale '{locale}' has no entry in _LOCALE_WRITING_SYSTEMS" + ) + + +def test_audit_for_tofu_flags_tiny_images(shots, tmp_path): + """A PNG with near-zero complexity should be flagged.""" + from PIL import Image + + img = Image.new("RGB", (400, 300), color=(255, 255, 255)) + path = tmp_path / "blank.png" + img.save(str(path), "PNG") + problems = shots.audit_for_tofu(tmp_path) + assert len(problems) == 1 + assert "blank.png" in problems[0] + + +def test_audit_for_tofu_passes_complex_images(shots, tmp_path): + """A PNG with real visual content should pass.""" + from PIL import Image, ImageDraw + + img = Image.new("RGB", (400, 300), color=(245, 245, 245)) + draw = ImageDraw.Draw(img) + for y in range(0, 300, 20): + for x in range(0, 400, 15): + draw.rectangle([x, y, x + 10, y + 12], fill=(50, 50, 50)) + draw.text((20, 20), "Create Shipment", fill=(0, 0, 0)) + draw.text((20, 50), "Royal Mail Click & Drop", fill=(0, 0, 0)) + path = tmp_path / "good.png" + img.save(str(path), "PNG") + problems = shots.audit_for_tofu(tmp_path) + assert problems == []