diff --git a/.github/workflows/handbook-build-check.yaml b/.github/workflows/handbook-build-check.yaml index f0db79181..89c0547d7 100644 --- a/.github/workflows/handbook-build-check.yaml +++ b/.github/workflows/handbook-build-check.yaml @@ -96,8 +96,8 @@ jobs: set -euo pipefail bash scripts/assemble-handbook-screenshots.sh /tmp/handbook-shots count=$(ls -1 /tmp/handbook-shots/*.png | wc -l | tr -d ' ') - if [ "$count" != "299" ]; then - echo "expected 299 screenshots, got $count" >&2 + if [ "$count" != "301" ]; then + echo "expected 301 screenshots, got $count" >&2 exit 1 fi @@ -177,10 +177,10 @@ jobs: exit 1 fi - # Spot-check handbook screenshots assembled from Goldens (299 PNGs). + # Spot-check handbook screenshots assembled from Goldens (301 PNGs). # Mix of original 01-61 slots, later Golden batches, and referral # baselines so a regression in any range surfaces here. - for name in 01-welcome 11-dashboard 26-terms 35-dashboard-with-balance 46-buy-kyc-required 52-sell-unknown-error 53-buy-payment-details 61-kyc-registration-tax-tin-error 62-welcome-page-android 219-settings-security-page-default 268-phone-number-field-default 269-dashboard-insider-unlocked 270-handbook-persona-dca 275-handbook-persona-exit-1j 276-referral-overview 282-referral-payout-row 283-referral-terms-readonly 287-kyc-registration-referral-recognized 288-transaction-history-referral-payout 289-dashboard-referral-entry-and-payout 290-kyc-registration-referral-promo; do + for name in 01-welcome 11-dashboard 26-terms 35-dashboard-with-balance 46-buy-kyc-required 52-sell-unknown-error 53-buy-payment-details 61-kyc-registration-tax-tin-error 62-welcome-page-android 219-settings-security-page-default 268-phone-number-field-default 269-dashboard-insider-unlocked 270-handbook-persona-dca 275-handbook-persona-exit-1j 276-referral-overview 282-referral-payout-row 283-referral-terms-readonly 287-kyc-registration-referral-recognized 288-transaction-history-referral-payout 289-dashboard-referral-entry-and-payout 290-kyc-registration-referral-promo 291-referral-bind-error-invalid 292-referral-bind-error-already-registered; do # Auth is checked before the file exists: a missing PNG also # returns 401, so HTTP status cannot prove assembly. Look on disk. if ! docker exec handbook test -f "/usr/share/nginx/html/screenshots/${name}.png"; then diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6fd25f0c7..58765c8ca 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -248,7 +248,7 @@ The app supports three wallet modes (`software`, `bitbox`, `debug`) with differe ```bash rg "^//\s*@no-integration-test:" lib/ ``` -- Visual-regression Goldens under `test/goldens/screens/` and `test/goldens/widgets/` are also the source of the 299 screenshots served at `handbook.realunit.app`. When you add a handbook page, you MUST add a matching Golden test AND a row in the mapping table at `scripts/assemble-handbook-screenshots.sh` — the handbook will not pick up a Maestro-captured PNG anymore. The `Handbook Build Check` workflow on every PR runs the assembly script and fails loudly if a mapped Golden is missing. +- Visual-regression Goldens under `test/goldens/screens/` and `test/goldens/widgets/` are also the source of the 301 screenshots served at `handbook.realunit.app`. When you add a handbook page, you MUST add a matching Golden test AND a row in the mapping table at `scripts/assemble-handbook-screenshots.sh` — the handbook will not pick up a Maestro-captured PNG anymore. The `Handbook Build Check` workflow on every PR runs the assembly script and fails loudly if a mapped Golden is missing. - Why: single source of truth — a UI regression that breaks a Golden also breaks the handbook image before either ships; eliminates the previous "two pipelines, two truths" problem. - See: [`docs/visual-regression-tests.md`](docs/visual-regression-tests.md) section "Handbook screenshots are sourced from Goldens". diff --git a/assets/languages/strings_de.arb b/assets/languages/strings_de.arb index 0ece64c72..36da5d176 100644 --- a/assets/languages/strings_de.arb +++ b/assets/languages/strings_de.arb @@ -268,45 +268,48 @@ "receiver": "Empfänger", "recoveryWords": "Wiederherstellungs-Wörter", "recoveryWordsInvalid": "Wiederherstellungs-Wörter ungültig", - "referralAlreadyBound": "Du hast bereits einen Einladungs- oder Promo-Code eingelöst.", - "referralAlreadyRegistered": "Diese Einladung gilt nur für neue Kundinnen und Kunden.", + "referralAlreadyBound": "Sie haben bereits einen Einladungs- oder Promo-Code eingelöst.", + "referralAlreadyBoundTitle": "Bereits ein Code eingelöst", + "referralAlreadyRegistered": "Einladungs- und Promo-Codes müssen vor dem ersten REALU-Kauf eingelöst werden.", + "referralAlreadyRegisteredTitle": "Nur für neue Kundinnen und Kunden", "referralChfAtSharePrice": "≈ ${chf} zum ${label}", "referralCodeChecking": "Code wird geprüft…", - "referralCodeDescription": "Optional. Über den Einladungslink wird der Code automatisch übernommen; hier kannst du ihn auch manuell eingeben.", - "referralCodeHeading": "Hast du einen Empfehlungscode?", + "referralCodeDescription": "Optional. Über den Einladungslink wird der Code automatisch übernommen; hier können Sie ihn auch manuell eingeben.", + "referralCodeHeading": "Haben Sie einen Empfehlungscode?", "referralCodeHint": "Einladungs- oder Promo-Code", "referralCodeInvalid": "Dieser Code ist ungültig oder abgelaufen.", "referralCodeInvalidTitle": "Link ungültig oder abgelaufen", "referralCodeOptional": "Einladungs- oder Promo-Code (optional)", "referralCodeSpent": "Dieser Einladungs- oder Promo-Code wurde bereits verwendet.", "referralCodeSpentTitle": "Code bereits eingelöst", - "referralCodeUnavailable": "Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.", + "referralCodeUnavailable": "Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.", "referralCopied": "Kopiert", "referralCopyInviteLink": "Einladungslink kopieren", "referralCreateInvite": "Einladungslink erstellen", - "referralCreateInviteDescription": "Gib den Vornamen der Person ein, die du einladen möchtest.", + "referralCreateInviteDescription": "Geben Sie den Vornamen der Person ein, die Sie einladen möchten.", "referralCreating": "Einladung wird erstellt…", "referralGuestName": "Name der eingeladenen Person", "referralGuestNameRequired": "Bitte den Vornamen der eingeladenen Person eingeben.", - "referralInviteRecognized": "Einladung von ${inviter} erkannt. ${inviter} erhält seine Prämie automatisch, sobald du verifiziert bist und deinen ersten Kauf abgeschlossen hast.", + "referralInviteRecognized": "Einladung von ${inviter} erkannt. ${inviter} erhält seine Prämie automatisch, sobald Sie verifiziert sind und Ihren ersten Kauf abgeschlossen haben.", "referralInvitesLoading": "Offene Einladungen werden geladen…", "referralInviteUrlLabel": "Persönlicher Einladungslink", "referralLoading": "Empfehlungen werden geladen…", "referralNotEligible": "Das Empfehlungsprogramm steht verifizierten Aktionären mit dem erforderlichen Bestand zur Verfügung.", "referralOpenInvitesExpire": "Offene Einladungen verfallen nach 3 Monaten.", "referralOverviewHint": "20 REALU pro Weiterempfehlung, sobald der erste Kauf erfolgreich abgewickelt ist.", - "referralOverviewTitle": "Deine Empfehlungen", + "referralOverviewTitle": "Ihre Empfehlungen", "referralPayout": "Empfehlungsprämie", "referralPayoutChf": "CHF ${chf} zum Zeitpunkt der Gutschrift", "referralPromoTitle": "Aktion", "referralQuarterCap": "In diesem Quartal sind keine weiteren Prämien möglich.", "referrals": "Empfehlungen", - "referralSelfReferral": "Du kannst deine eigene Einladung nicht einlösen.", + "referralSelfReferral": "Sie können Ihre eigene Einladung nicht einlösen.", + "referralSelfReferralTitle": "Eigene Einladung", "referralShareInviteLink": "Einladungslink versenden", "referralSharePrice": "Aktienkurs", "referralShareText": "Hey ${guestName}, ${hostName} lädt dich ein zu RealUnit: ${url}", "referralShareTextNoName": "${hostName} lädt dich ein zu RealUnit: ${url}", - "referralsSubtitle": "Erhalte 20 REALU pro Weiterempfehlung", + "referralsSubtitle": "Erhalten Sie 20 REALU pro Weiterempfehlung", "referralStatusCredited": "Gutgeschrieben", "referralStatusOpen": "Offen", "referralTermsAccepting": "Teilnahmebedingungen werden akzeptiert…", @@ -315,8 +318,8 @@ "referralTermsRequired": "Bitte zuerst die Teilnahmebedingungen akzeptieren.", "referralTermsTitle": "Teilnahmebedingungen Referral-Programm", "referralTotalReceived": "Insgesamt erhalten", - "referralYourInvite": "Deine Einladung", - "referralYourInviteFor": "Deine Einladung für ${guestName}", + "referralYourInvite": "Ihre Einladung", + "referralYourInviteFor": "Ihre Einladung für ${guestName}", "refresh": "Aktualisieren", "registerAccountType": "Kontotyp", "registerCitizenship": "Staatsangehörigkeit", diff --git a/assets/languages/strings_en.arb b/assets/languages/strings_en.arb index 9e75b8cda..4d5578e31 100644 --- a/assets/languages/strings_en.arb +++ b/assets/languages/strings_en.arb @@ -269,7 +269,9 @@ "recoveryWords": "recovery words", "recoveryWordsInvalid": "Recovery words invalid", "referralAlreadyBound": "You have already applied an invite or promo code.", - "referralAlreadyRegistered": "This invitation is only for new customers.", + "referralAlreadyBoundTitle": "Code already applied", + "referralAlreadyRegistered": "Invite and promo codes must be applied before the first REALU purchase.", + "referralAlreadyRegisteredTitle": "Only for new customers", "referralChfAtSharePrice": "≈ ${chf} at ${label}", "referralCodeChecking": "Checking code…", "referralCodeDescription": "Optional. The invite link fills this automatically; you can also enter the code here.", @@ -302,6 +304,7 @@ "referralQuarterCap": "No further prizes can be credited this quarter.", "referrals": "Referrals", "referralSelfReferral": "You cannot apply your own invitation.", + "referralSelfReferralTitle": "Your own invitation", "referralShareInviteLink": "Share invite link", "referralSharePrice": "Share price", "referralShareText": "Hey ${guestName}, ${hostName} is inviting you to RealUnit: ${url}", diff --git a/docs/handbook/README.md b/docs/handbook/README.md index fd77e22d8..2bc7b928b 100644 --- a/docs/handbook/README.md +++ b/docs/handbook/README.md @@ -21,7 +21,7 @@ deployten Image (`handbook.realunit.app`). ## Screenshots regenerieren -Es gibt keinen separaten Regeneration-Schritt: Die 299 Handbook-Screenshots +Es gibt keinen separaten Regeneration-Schritt: Die 301 Handbook-Screenshots sind direkt die Golden-Baselines unter `test/goldens/` (gemappt in `scripts/assemble-handbook-screenshots.sh`). Eine UI-Änderung an einer der gemappten Pages produziert beim `flutter test test/goldens` einen Diff — diff --git a/docs/handbook/de/index.html b/docs/handbook/de/index.html index eab666c43..5b3d12c18 100644 --- a/docs/handbook/de/index.html +++ b/docs/handbook/de/index.html @@ -1186,14 +1186,14 @@

RealUnit Wallet — Handbook

📱 App: RealUnit Wallet (Flutter) 📂 Code: RealUnitCH/app - 🧪 Tests: 350 Visual-Regression Goldens (299 davon im Handbook) · 26 Tier-3 Smoke-Flows + 🧪 Tests: 355 Visual-Regression Goldens (301 davon im Handbook) · 26 Tier-3 Smoke-Flows

Single Source of Truth

- Die 299 App-Screenshots dieser Seite sind Goldens aus + Die 301 App-Screenshots dieser Seite sind Goldens aus test/goldens/{screens,widgets}/<feature>/goldens/macos/<file>.png. Welcher Golden welcher Handbook-Slot ist, definiert die Mapping-Tabelle in scripts/assemble-handbook-screenshots.sh. Der Docker-Build @@ -1233,7 +1233,7 @@

Single Source of Truth

Pipeline: Page-Edit → flutter test --update-goldens test/goldens/ auf dem self-hosted Runner (Hardware-Determinismus) → neue Baseline committen → handbook-deploy.yaml baut das Docker-Image aus dem gepushten Branch - und assembliert die 299 Slots aus test/goldens/ → Push auf + und assembliert die 301 Slots aus test/goldens/ → Push auf staging deployt das Handbook-Image nach Produktion, via Cloudflare Tunnel.

@@ -11025,7 +11025,7 @@

REmpfehlungen & Promo-Code

Verifizierte Aktionäre mit mindestens 70 REALU im Wallet (geprüft bei der Einladung und bei der Qualifikation) sehen auf dem Dashboard - die Karte Empfehlungen («Erhalte 20 REALU pro Weiterempfehlung») + die Karte Empfehlungen («Erhalten Sie 20 REALU pro Weiterempfehlung») und denselben Einstieg in den Einstellungen. Sichtbarkeit, Mindestbestand und Auszahlung entscheidet die DFX API; die App rendert eligible 1:1. Die Prämie von 20 REALU wird dem Empfehler @@ -11100,10 +11100,14 @@

REmpfehlungen & Promo-Code

Deeplink-Stash stehen. Ein abgelehnter Bind (4xx) zeigt einmal einen Dialog: Titel Code bereits eingelöst mit Body Dieser Einladungs- oder Promo-Code wurde bereits verwendet. (Einlösungslimit erreicht); - Titel Link ungültig oder abgelaufen für die übrigen Fälle - Du hast bereits einen Einladungs- oder Promo-Code eingelöst. - (ein Code pro Konto) oder Diese Einladung gilt nur für neue Kundinnen - und Kunden. (Eingabe nur vor dem ersten REALU-Kauf). + Titel Bereits ein Code eingelöst mit Body + Sie haben bereits einen Einladungs- oder Promo-Code eingelöst. + (ein Code pro Konto); Titel Nur für neue Kundinnen und Kunden + mit Body Einladungs- und Promo-Codes müssen vor dem ersten + REALU-Kauf eingelöst werden. (Eingabe nur vor dem ersten Kauf); + Titel Eigene Einladung bei Selbst-Einlösung; + Titel Link ungültig oder abgelaufen nur für unbekannte oder + abgelaufene Codes. Promo-Codes legt RealUnit unter app.dfx.swiss/realunit an: je Code ein Gültigkeitsfenster, ein Mindestkauf (Default 200 REALU) und ein Einlösungslimit als Anzahl Einlösungen (Pflichtfeld, ganze Zahl ab 1, @@ -11187,7 +11191,8 @@

REmpfehlungen & Promo-Code

TB nach Annahme (`283`), Namenseingabe (`284`), Einstellungen-Karte (`285`), Rechtliche Dokumente letzter Eintrag (`286`), Einladung erkannt (`287`), Historie mit Prämie (`288`), Dashboard-Einstieg - (`289`), Promo-Aktionstext (`290`). + (`289`), Promo-Aktionstext (`290`), Bind-Overlay ungültig (`291`), + Bind-Overlay nur für neue Kundinnen (`292`).

@@ -11238,7 +11243,7 @@

REmpfehlungen & Promo-Code

/>
- Karte Empfehlungen mit «Erhalte 20 REALU pro Weiterempfehlung», + Karte Empfehlungen mit «Erhalten Sie 20 REALU pro Weiterempfehlung», nur wenn die API eligible setzt.
@@ -11359,7 +11364,7 @@

REmpfehlungen & Promo-Code

Einstieg «Empfehlungen» in den Einstellungen, nur wenn die API - eligible setzt. Kartentext «Erhalte 20 REALU pro + eligible setzt. Kartentext «Erhalten Sie 20 REALU pro Weiterempfehlung».
@@ -11412,8 +11417,6 @@

REmpfehlungen & Promo-Code

Historie: Prämie mit ganzer Anzahl (20 REALU), Datum und Frankenwert, fixiert im Zeitpunkt der Gutschrift (TB Ziff. 6). - Im Golden liegt die Prämie ausserhalb des angezeigten Datumsfilters - (Testdaten).
@@ -11429,7 +11432,7 @@

REmpfehlungen & Promo-Code

/>
- Einstieg «Empfehlungen» («Erhalte 20 REALU pro + Einstieg «Empfehlungen» («Erhalten Sie 20 REALU pro Weiterempfehlung») und Prämie in den letzten Transaktionen.
@@ -11450,6 +11453,46 @@

REmpfehlungen & Promo-Code

Aktionstext in einem eigenen Fenster (Schließen oder Tippen daneben). +
+
+ + + referral_bind_error_dialog_golden_test.dart +
+
+ Dashboard-Overlay: Link ungültig oder abgelaufen +
+
+ Nach einem Bind-4xx für unbekannten oder abgelaufenen Code: + Overlay auf dem Dashboard, Titel Link ungültig oder abgelaufen, + Body Dieser Code ist ungültig oder abgelaufen., + Schließen mit Tastaturfokus, nicht per Barrier schliessbar. + API-Codes EXPIRED / NOT_FOUND. +
+
+
+
+ + + referral_bind_error_dialog_golden_test.dart +
+
+ Dashboard-Overlay: Nur für neue Kundinnen und Kunden +
+
+ Nach einem Bind-4xx, weil das Konto schon einen REALU-Kauf hat: + derselbe Overlay, Titel Nur für neue Kundinnen und Kunden, + Body Einladungs- und Promo-Codes müssen vor dem ersten REALU-Kauf + eingelöst werden. — nicht «Link ungültig». + API-Code ALREADY_REGISTERED. +
+

diff --git a/docs/referral-programme.md b/docs/referral-programme.md index b554e305b..0c3447533 100644 --- a/docs/referral-programme.md +++ b/docs/referral-programme.md @@ -293,7 +293,7 @@ row. tile is one name (REALU, CHF at the current share price, Aktienkurs). Open invites show the personalised share text (API copyText 1:1, otherwise the localised template) and can be copied and shared again, - including when the guest name is blank («Deine Einladung», share text + including when the guest name is blank («Ihre Einladung», share text without «Hey ,»); credited names stay hidden. Copy and share behave as after create. With no open-invite rows and no list error, Create is keyboard-focused. @@ -429,8 +429,12 @@ row. Close is keyboard-focused. The boot and deeplink bind dialogs stay modal. A 4xx bind (stacking, self-referral, spent) shows the matching copy once (spent: «Code bereits eingelöst»; - self-referral / already-bound / already-registered have their own - sentence; unknown/expired stay «Link ungültig oder abgelaufen»). Close + already-registered: «Nur für neue Kundinnen und Kunden»; + already-bound: «Bereits ein Code eingelöst»; + self-referral: «Eigene Einladung»; + unknown/expired stay «Link ungültig oder abgelaufen»). + Handbook slots `291` (expired/unknown) and `292` (already-registered) + are the dashboard overlay goldens. Close is focused, not barrier-dismissible. If the navigator is not attached yet (dashboard boot bind), the promo, invalid, or unavailable dialog is shown on the next frame instead of diff --git a/docs/screens.md b/docs/screens.md index 01640199f..2f5adea2d 100644 --- a/docs/screens.md +++ b/docs/screens.md @@ -48,7 +48,7 @@ Column meaning: | PIN & lock | `VerifyPinPage` | `pinGate` | `/pinGate` | `17`, `76`, `77`, `78`, `79`, `80`, `81`, `82`, `83`, `84`, `85`, `88` | | PIN & lock | `SetupPinPage` | `setupPin` | `/setupPin` | `08`, `09`, `10`, `73`, `74`, `75` | | PIN & lock | `VerifyPinPage` | `verifyPin` | `/verifyPin` | `86`, `87` | -| Dashboard & trading | `DashboardPage` | `dashboard` | `/dashboard` | `35`, `89`, `90`, `91`, `92`, `93`, `94`, `289` | +| Dashboard & trading | `DashboardPage` | `dashboard` | `/dashboard` | `35`, `89`, `90`, `91`, `92`, `93`, `94`, `289`, `291`, `292` | | Dashboard & trading | `TransactionHistoryPage` | `transactionHistory` | `/dashboard/transactionHistory` | `36`, `95`, `96`, `97`, `98`, `99`, `100`, `288` | | Dashboard & trading | `BuyPage` | `buy` | `/buy` | `44`, `45`, `46`, `47`, `48`, `103`, `104`, `105`, `106`, `107`, `108`, `109`, `110`, `111`, `112`, `113` | | Dashboard & trading | `BuyPaymentDetailsPage` | `buyPaymentDetails` | `/buyPaymentDetails` | `53`, `114`, `115`, `116`, `117` | @@ -125,8 +125,10 @@ Column meaning: 81 screens — 47 routed (`GoRoute`) + 34 non-routed. The table also carries two shared-widget baselines (`PhoneNumberField`, `ReferralPayoutTransactionRow`), which are not screens. -Referral surfaces have Golden baselines (`276`–`290`); the handbook -documents the programme in `#spec-referral`. +Referral surfaces have Golden baselines (`276`–`292`); the handbook +documents the programme in `#spec-referral`. Overlay slots `291` and `292` +are listed on `DashboardPage` (the host screen), not as a second shared-widget +row. ## Notes @@ -145,7 +147,7 @@ documents the programme in `#spec-referral`. `243`–`245`. `SetupPinPage` also backs the `settingsChangePin` route (`/settings/security/changePin`) via a second constructor; that reuse has no separate Golden and is not given its own row. -- **Handbook numbering.** Each of the 299 handbook slots is a Visual-Regression +- **Handbook numbering.** Each of the 301 handbook slots is a Visual-Regression Golden under `test/goldens/`, mapped to its handbook position by `scripts/assemble-handbook-screenshots.sh`. A parallel Tier-3 Maestro flow (`.maestro/handbook/NN-*.yaml`) covers navigation/tap-routing smoke for the diff --git a/docs/visual-regression-tests.md b/docs/visual-regression-tests.md index 6db160578..a43d1d01f 100644 --- a/docs/visual-regression-tests.md +++ b/docs/visual-regression-tests.md @@ -1,7 +1,7 @@ # Visual Regression Tests Pixel-exact baseline tests for every page in the app. 81 `lib/screens/**/*_page.dart` -files mapped to 350 Golden PNGs under `test/goldens/` (`screens/` and `widgets/`) (page renderings +files mapped to 355 Golden PNGs under `test/goldens/` (`screens/` and `widgets/`) (page renderings plus state variants: Buy/Sell error banners, KYC loading/failure, Dashboard with-balance, RestoreWallet valid/invalid, Legal-Disclaimer steps, etc.), validated on each PR by the `Visual Regression` job (required status check @@ -191,7 +191,7 @@ public repos are free even for macOS minutes. ## Handbook screenshots are sourced from Goldens -The 299 PNGs the handbook serves at `handbook.realunit.app/screenshots/` +The 301 PNGs the handbook serves at `handbook.realunit.app/screenshots/` are assembled from the Golden baselines at docker-build time. One Golden → one handbook page, via the explicit mapping in `scripts/assemble-handbook-screenshots.sh`. The handbook does **not** @@ -231,7 +231,7 @@ that directory into `/usr/share/nginx/html/screenshots/`. Golden file. 4. Open the PR. The `Handbook Build Check` workflow runs `docker build` and a container smoke (`/healthz` + auth gate + - spot-checks selected mapped screenshots via `docker exec test -f`; the full set is gated by the assemble step (`expected 299`)). A missing Golden surfaces here + spot-checks selected mapped screenshots via `docker exec test -f`; the full set is gated by the assemble step (`expected 301`)). A missing Golden surfaces here as a missing-source error from the assembly script before docker even spins up. diff --git a/lib/screens/referral/referral_bind_error_dialog.dart b/lib/screens/referral/referral_bind_error_dialog.dart new file mode 100644 index 000000000..52cf30651 --- /dev/null +++ b/lib/screens/referral/referral_bind_error_dialog.dart @@ -0,0 +1,32 @@ +import 'package:flutter/material.dart'; +import 'package:realunit_wallet/generated/i18n.dart'; +import 'package:realunit_wallet/screens/referral/referral_error_message.dart'; +import 'package:realunit_wallet/styles/colors.dart'; + +/// Bind-error overlay after `POST /v1/realunit/referral/bind` returns 4xx. +/// +/// Title and body come from [localizedReferralErrorTitle] / +/// [localizedReferralError] so goldens and the live dashboard share one widget. +class ReferralBindErrorDialog extends StatelessWidget { + final String token; + + const ReferralBindErrorDialog({super.key, required this.token}); + + @override + Widget build(BuildContext context) { + final style = Theme.of(context).textTheme.bodyMedium?.copyWith( + color: RealUnitColors.status.red600, + ); + return AlertDialog( + title: Text(localizedReferralErrorTitle(context, token), style: style), + content: Text(localizedReferralError(context, token), style: style), + actions: [ + TextButton( + autofocus: true, + onPressed: () => Navigator.of(context).maybePop(), + child: Text(S.of(context).close), + ), + ], + ); + } +} diff --git a/lib/screens/referral/referral_error_message.dart b/lib/screens/referral/referral_error_message.dart index 5e5b37267..96ee8ee58 100644 --- a/lib/screens/referral/referral_error_message.dart +++ b/lib/screens/referral/referral_error_message.dart @@ -100,5 +100,14 @@ String localizedReferralErrorTitle(BuildContext context, String message) { if (message == referralSpentMessage) { return S.of(context).referralCodeSpentTitle; } + if (message == referralAlreadyRegisteredMessage) { + return S.of(context).referralAlreadyRegisteredTitle; + } + if (message == referralAlreadyBoundMessage) { + return S.of(context).referralAlreadyBoundTitle; + } + if (message == referralSelfReferralMessage) { + return S.of(context).referralSelfReferralTitle; + } return S.of(context).referralCodeInvalidTitle; } diff --git a/lib/setup/routing/referral_bind.dart b/lib/setup/routing/referral_bind.dart index cbe13bdd8..812b5150d 100644 --- a/lib/setup/routing/referral_bind.dart +++ b/lib/setup/routing/referral_bind.dart @@ -9,11 +9,11 @@ import 'package:realunit_wallet/packages/service/dfx/models/referral/dto/referra import 'package:realunit_wallet/packages/service/dfx/real_unit_referral_service.dart'; import 'package:realunit_wallet/packages/service/dfx/referral_lookup_status.dart'; import 'package:realunit_wallet/screens/pin/bloc/auth/pin_auth_cubit.dart'; +import 'package:realunit_wallet/screens/referral/referral_bind_error_dialog.dart'; import 'package:realunit_wallet/screens/referral/referral_error_message.dart'; import 'package:realunit_wallet/setup/di.dart'; import 'package:realunit_wallet/setup/routing/effective_location.dart'; import 'package:realunit_wallet/setup/routing/referral_pending_code.dart'; -import 'package:realunit_wallet/styles/colors.dart'; import 'package:realunit_wallet/widgets/buttons/app_filled_button.dart'; bool _bindInFlight = false; @@ -323,27 +323,7 @@ Future _showInvalidBindDialog(GoRouter router, Object error) { await showDialog( context: ctx, barrierDismissible: false, - builder: (dialogContext) => AlertDialog( - title: Text( - localizedReferralErrorTitle(dialogContext, token), - style: Theme.of(dialogContext).textTheme.bodyMedium?.copyWith( - color: RealUnitColors.status.red600, - ), - ), - content: Text( - localizedReferralError(dialogContext, token), - style: Theme.of(dialogContext).textTheme.bodyMedium?.copyWith( - color: RealUnitColors.status.red600, - ), - ), - actions: [ - TextButton( - autofocus: true, - onPressed: () => Navigator.of(dialogContext).pop(), - child: Text(S.of(dialogContext).close), - ), - ], - ), + builder: (dialogContext) => ReferralBindErrorDialog(token: token), ); }); } diff --git a/scripts/assemble-handbook-screenshots.sh b/scripts/assemble-handbook-screenshots.sh index f1a3dbae3..73aee07fd 100755 --- a/scripts/assemble-handbook-screenshots.sh +++ b/scripts/assemble-handbook-screenshots.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # -# Assemble the 299 handbook screenshots from the visual-regression Golden +# Assemble the 301 handbook screenshots from the visual-regression Golden # baselines. The flat `NN-name.png` output layout matches what # docs/handbook/de/index.html links to (`` # — the relative path resolves to `docs/handbook/screenshots/NN-name.png`). @@ -322,7 +322,7 @@ MAPPING=( "273-handbook-persona-mix=screens/dashboard/goldens/macos/handbook_persona_mix.png" "274-handbook-persona-scale=screens/dashboard/goldens/macos/handbook_persona_scale.png" "275-handbook-persona-exit-1j=screens/dashboard/goldens/macos/handbook_persona_exit_1j.png" - # 276–290: referral/promo goldens. No matching .maestro/handbook flow — + # 276–292: referral/promo goldens. No matching .maestro/handbook flow — # eligibility (KYC 30 + 70 REALU) is not reachable in the 01–26 chain. "276-referral-overview=screens/referral/goldens/macos/referral_overview_page_default.png" "277-kyc-registration-referral=screens/kyc/goldens/macos/kyc_registration_referral_step_default.png" @@ -339,6 +339,8 @@ MAPPING=( "288-transaction-history-referral-payout=screens/transaction_history/goldens/macos/transaction_history_page_referral_payout.png" "289-dashboard-referral-entry-and-payout=screens/dashboard/goldens/macos/dashboard_referral_entry_and_payout.png" "290-kyc-registration-referral-promo=screens/kyc/goldens/macos/kyc_registration_referral_step_promo.png" + "291-referral-bind-error-invalid=screens/referral/goldens/macos/referral_bind_error_invalid.png" + "292-referral-bind-error-already-registered=screens/referral/goldens/macos/referral_bind_error_already_registered.png" ) missing=() diff --git a/scripts/run-handbook-flows.sh b/scripts/run-handbook-flows.sh index e5f7b02de..0e1a62f94 100755 --- a/scripts/run-handbook-flows.sh +++ b/scripts/run-handbook-flows.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # # Tier-3 navigation smoke for the 26 .maestro/handbook/*.yaml flows -# (01-welcome … 26-terms). Handbook pages 276–290 (referral/promo) are +# (01-welcome … 26-terms). Handbook pages 276–292 (referral/promo) are # golden-mapped in assemble-handbook-screenshots.sh and are not Maestro # flows: the sequential onboarding chain cannot reach an eligible # Empfehler (KYC Level 30 + 70 REALU). diff --git a/test/goldens/screens/dashboard/goldens/macos/dashboard_referral_entry_and_payout.png b/test/goldens/screens/dashboard/goldens/macos/dashboard_referral_entry_and_payout.png index f21ec2c87..48ff23577 100644 Binary files a/test/goldens/screens/dashboard/goldens/macos/dashboard_referral_entry_and_payout.png and b/test/goldens/screens/dashboard/goldens/macos/dashboard_referral_entry_and_payout.png differ diff --git a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_default.png b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_default.png index c862c948d..d407603c5 100644 Binary files a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_default.png and b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_default.png differ diff --git a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_invalid.png b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_invalid.png index ae9f2313a..0e7fc95aa 100644 Binary files a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_invalid.png and b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_invalid.png differ diff --git a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_loading.png b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_loading.png index bfcf6eb0e..bf44f3c08 100644 Binary files a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_loading.png and b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_loading.png differ diff --git a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_promo.png b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_promo.png index 2f433ad8d..6c2425b10 100644 Binary files a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_promo.png and b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_promo.png differ diff --git a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_recognized.png b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_recognized.png index aee87b520..6577461aa 100644 Binary files a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_recognized.png and b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_recognized.png differ diff --git a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_spent.png b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_spent.png index f3095e562..b69db0380 100644 Binary files a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_spent.png and b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_spent.png differ diff --git a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_unavailable.png b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_unavailable.png index 661fd83de..564db0b65 100644 Binary files a/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_unavailable.png and b/test/goldens/screens/kyc/goldens/macos/kyc_registration_referral_step_unavailable.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_bind_error_already_bound.png b/test/goldens/screens/referral/goldens/macos/referral_bind_error_already_bound.png new file mode 100644 index 000000000..3b1bbefed Binary files /dev/null and b/test/goldens/screens/referral/goldens/macos/referral_bind_error_already_bound.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_bind_error_already_registered.png b/test/goldens/screens/referral/goldens/macos/referral_bind_error_already_registered.png new file mode 100644 index 000000000..ebd1cda90 Binary files /dev/null and b/test/goldens/screens/referral/goldens/macos/referral_bind_error_already_registered.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_bind_error_invalid.png b/test/goldens/screens/referral/goldens/macos/referral_bind_error_invalid.png new file mode 100644 index 000000000..b48faf354 Binary files /dev/null and b/test/goldens/screens/referral/goldens/macos/referral_bind_error_invalid.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_bind_error_self_referral.png b/test/goldens/screens/referral/goldens/macos/referral_bind_error_self_referral.png new file mode 100644 index 000000000..4ea83d502 Binary files /dev/null and b/test/goldens/screens/referral/goldens/macos/referral_bind_error_self_referral.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_bind_error_spent.png b/test/goldens/screens/referral/goldens/macos/referral_bind_error_spent.png new file mode 100644 index 000000000..4e82e569d Binary files /dev/null and b/test/goldens/screens/referral/goldens/macos/referral_bind_error_spent.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_create_page_creating.png b/test/goldens/screens/referral/goldens/macos/referral_create_page_creating.png index beec17dd3..602fdfedb 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_create_page_creating.png and b/test/goldens/screens/referral/goldens/macos/referral_create_page_creating.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_create_page_error.png b/test/goldens/screens/referral/goldens/macos/referral_create_page_error.png index cb3c4b368..28441eb65 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_create_page_error.png and b/test/goldens/screens/referral/goldens/macos/referral_create_page_error.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_create_page_invite_created.png b/test/goldens/screens/referral/goldens/macos/referral_create_page_invite_created.png index 1aae8e992..6de5a8d32 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_create_page_invite_created.png and b/test/goldens/screens/referral/goldens/macos/referral_create_page_invite_created.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_create_page_name_entry.png b/test/goldens/screens/referral/goldens/macos/referral_create_page_name_entry.png index d9fe23231..e02a2eb90 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_create_page_name_entry.png and b/test/goldens/screens/referral/goldens/macos/referral_create_page_name_entry.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_create_page_quota.png b/test/goldens/screens/referral/goldens/macos/referral_create_page_quota.png index d2196d159..915299824 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_create_page_quota.png and b/test/goldens/screens/referral/goldens/macos/referral_create_page_quota.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_entry_card_eligible.png b/test/goldens/screens/referral/goldens/macos/referral_entry_card_eligible.png index 9f256aeb2..462feecde 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_entry_card_eligible.png and b/test/goldens/screens/referral/goldens/macos/referral_entry_card_eligible.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_gate_failure.png b/test/goldens/screens/referral/goldens/macos/referral_gate_failure.png index 1d7fe088e..bb9205adc 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_gate_failure.png and b/test/goldens/screens/referral/goldens/macos/referral_gate_failure.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_overview_page_default.png b/test/goldens/screens/referral/goldens/macos/referral_overview_page_default.png index 559700e02..a2cd3e1ae 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_overview_page_default.png and b/test/goldens/screens/referral/goldens/macos/referral_overview_page_default.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_overview_page_empty.png b/test/goldens/screens/referral/goldens/macos/referral_overview_page_empty.png index eb1e70c35..765b01293 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_overview_page_empty.png and b/test/goldens/screens/referral/goldens/macos/referral_overview_page_empty.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_overview_page_failure.png b/test/goldens/screens/referral/goldens/macos/referral_overview_page_failure.png index 91d98b8fb..deedc3d2c 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_overview_page_failure.png and b/test/goldens/screens/referral/goldens/macos/referral_overview_page_failure.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_overview_page_invites_error.png b/test/goldens/screens/referral/goldens/macos/referral_overview_page_invites_error.png index 7096ef2bb..8b720a339 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_overview_page_invites_error.png and b/test/goldens/screens/referral/goldens/macos/referral_overview_page_invites_error.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_overview_page_loading.png b/test/goldens/screens/referral/goldens/macos/referral_overview_page_loading.png index 6a0a6d34f..210501469 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_overview_page_loading.png and b/test/goldens/screens/referral/goldens/macos/referral_overview_page_loading.png differ diff --git a/test/goldens/screens/referral/goldens/macos/referral_terms_page_error.png b/test/goldens/screens/referral/goldens/macos/referral_terms_page_error.png index 446bad251..b3c0afae7 100644 Binary files a/test/goldens/screens/referral/goldens/macos/referral_terms_page_error.png and b/test/goldens/screens/referral/goldens/macos/referral_terms_page_error.png differ diff --git a/test/goldens/screens/referral/referral_bind_error_dialog_golden_test.dart b/test/goldens/screens/referral/referral_bind_error_dialog_golden_test.dart new file mode 100644 index 000000000..fe4953f9b --- /dev/null +++ b/test/goldens/screens/referral/referral_bind_error_dialog_golden_test.dart @@ -0,0 +1,191 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:get_it/get_it.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:realunit_wallet/models/balance.dart'; +import 'package:realunit_wallet/models/portfolio_value_point.dart'; +import 'package:realunit_wallet/models/price_point.dart'; +import 'package:realunit_wallet/models/transaction.dart'; +import 'package:realunit_wallet/packages/config/api_config.dart'; +import 'package:realunit_wallet/packages/repository/transaction_repository.dart'; +import 'package:realunit_wallet/packages/service/app_store.dart'; +import 'package:realunit_wallet/packages/service/dfx/models/transactions/dto/transactions_dto.dart'; +import 'package:realunit_wallet/packages/service/dfx/real_unit_pdf_service.dart'; +import 'package:realunit_wallet/packages/service/dfx/real_unit_referral_service.dart'; +import 'package:realunit_wallet/packages/utils/default_assets.dart'; +import 'package:realunit_wallet/screens/dashboard/bloc/balance_cubit.dart'; +import 'package:realunit_wallet/screens/dashboard/bloc/dashboard_bloc.dart'; +import 'package:realunit_wallet/screens/dashboard/bloc/pending_transactions_cubit.dart'; +import 'package:realunit_wallet/screens/dashboard/dashboard_page.dart'; +import 'package:realunit_wallet/screens/referral/referral_bind_error_dialog.dart'; +import 'package:realunit_wallet/screens/referral/referral_error_message.dart'; +import 'package:realunit_wallet/screens/settings/bloc/settings_bloc.dart'; +import 'package:realunit_wallet/styles/currency.dart'; + +import '../../../helper/helper.dart'; + +class _MockTransactionRepository extends Mock implements TransactionRepository {} + +class _MockRealUnitPdfService extends Mock implements RealUnitPdfService {} + +class _MockApiConfig extends Mock implements ApiConfig {} + +class _MockDashboardBloc extends MockBloc + implements DashboardBloc {} + +class _MockBalanceCubit extends MockCubit implements BalanceCubit {} + +class _MockPendingTransactionsCubit extends MockCubit> + implements PendingTransactionsCubit {} + +void main() { + const walletAddress = '0xcabd3f4b10a7089986e708d19140bfc98e5880c0'; + const counterparty = '0x1234567890abcdef1234567890abcdef12345678'; + + late _MockDashboardBloc dashboardBloc; + late _MockBalanceCubit balanceCubit; + late _MockPendingTransactionsCubit pendingTxCubit; + late MockSettingsBloc settingsBloc; + final transactionRepository = _MockTransactionRepository(); + + final priceChart = [ + PricePoint(asset: realUnitAsset, price: BigInt.from(148), time: DateTime.utc(2025, 11)), + PricePoint(asset: realUnitAsset, price: BigInt.from(150), time: DateTime.utc(2025, 12)), + PricePoint(asset: realUnitAsset, price: BigInt.from(149), time: DateTime.utc(2026, 1)), + PricePoint(asset: realUnitAsset, price: BigInt.from(152), time: DateTime.utc(2026, 2)), + PricePoint(asset: realUnitAsset, price: BigInt.from(151), time: DateTime.utc(2026, 3)), + PricePoint(asset: realUnitAsset, price: BigInt.from(155), time: DateTime.utc(2026, 4)), + PricePoint(asset: realUnitAsset, price: BigInt.from(153), time: DateTime.utc(2026, 5)), + ]; + + final portfolioHistory = [ + PortfolioValuePoint(value: BigInt.from(14800), balance: BigInt.from(100), time: DateTime.utc(2025, 11)), + PortfolioValuePoint(value: BigInt.from(15000), balance: BigInt.from(100), time: DateTime.utc(2025, 12)), + PortfolioValuePoint(value: BigInt.from(14900), balance: BigInt.from(100), time: DateTime.utc(2026, 1)), + PortfolioValuePoint(value: BigInt.from(15200), balance: BigInt.from(100), time: DateTime.utc(2026, 2)), + PortfolioValuePoint(value: BigInt.from(15100), balance: BigInt.from(100), time: DateTime.utc(2026, 3)), + PortfolioValuePoint(value: BigInt.from(15500), balance: BigInt.from(100), time: DateTime.utc(2026, 4)), + PortfolioValuePoint(value: BigInt.from(15300), balance: BigInt.from(100), time: DateTime.utc(2026, 5)), + ]; + + final recentBuy = Transaction( + height: 200, + txId: '0xrecent1', + chainId: 1, + senderAddress: counterparty, + receiverAddress: walletAddress, + amount: BigInt.from(99), + asset: realUnitAsset, + type: TransactionTypes.tokenTransfer, + note: null, + data: null, + timestamp: DateTime.utc(2026, 8, 14, 10, 26), + ); + + setUpAll(() { + final getIt = GetIt.instance; + final apiConfig = _MockApiConfig(); + final appStore = MockAppStore(); + when(() => apiConfig.asset).thenReturn(realUnitAsset); + when(() => appStore.apiConfig).thenReturn(apiConfig); + when(() => appStore.primaryAddress).thenReturn(walletAddress); + getIt.registerSingleton(appStore); + getIt.registerSingleton(_MockRealUnitPdfService()); + getIt.registerSingleton(transactionRepository); + getIt.registerSingleton( + MockRealUnitReferralService(), + ); + }); + + tearDownAll(() async => GetIt.instance.reset()); + + setUp(() { + dashboardBloc = _MockDashboardBloc(); + balanceCubit = _MockBalanceCubit(); + pendingTxCubit = _MockPendingTransactionsCubit(); + settingsBloc = MockSettingsBloc(); + + when(() => dashboardBloc.state).thenReturn( + DashboardState( + price: BigInt.from(138), + priceChart: priceChart, + portfolioHistory: portfolioHistory, + currency: Currency.chf, + ), + ); + when(() => balanceCubit.state).thenReturn( + Balance( + chainId: realUnitAsset.chainId, + contractAddress: realUnitAsset.address, + walletAddress: walletAddress, + balance: BigInt.from(99), + asset: realUnitAsset, + ), + ); + when(() => balanceCubit.asset).thenReturn(realUnitAsset); + when(() => pendingTxCubit.state).thenReturn(const []); + when(() => settingsBloc.state).thenReturn(const SettingsState()); + when(() => transactionRepository.watchTransactionsOfAssets(any(), any(), any())) + .thenAnswer((_) => Stream.value([recentBuy])); + }); + + Widget dashboard() => MultiBlocProvider( + providers: [ + BlocProvider.value(value: settingsBloc), + BlocProvider.value(value: dashboardBloc), + BlocProvider.value(value: balanceCubit), + BlocProvider.value(value: pendingTxCubit), + ], + child: const DashboardView(), + ); + + Widget overlay(String token) => wrapForGolden( + Stack( + fit: StackFit.expand, + children: [ + dashboard(), + const ModalBarrier(dismissible: false, color: Colors.black54), + Center(child: ReferralBindErrorDialog(token: token)), + ], + ), + ); + + group('$ReferralBindErrorDialog', () { + goldenTest( + 'invalid or expired on the live dashboard', + fileName: 'referral_bind_error_invalid', + constraints: phoneConstraints, + builder: () => overlay(referralInvalidMessage), + ); + + goldenTest( + 'already registered (first RealUnit purchase done)', + fileName: 'referral_bind_error_already_registered', + constraints: phoneConstraints, + builder: () => overlay(referralAlreadyRegisteredMessage), + ); + + goldenTest( + 'already bound', + fileName: 'referral_bind_error_already_bound', + constraints: phoneConstraints, + builder: () => overlay(referralAlreadyBoundMessage), + ); + + goldenTest( + 'self-referral', + fileName: 'referral_bind_error_self_referral', + constraints: phoneConstraints, + builder: () => overlay(referralSelfReferralMessage), + ); + + goldenTest( + 'spent', + fileName: 'referral_bind_error_spent', + constraints: phoneConstraints, + builder: () => overlay(referralSpentMessage), + ); + }); +} diff --git a/test/goldens/screens/settings/goldens/macos/settings_page_referral_eligible.png b/test/goldens/screens/settings/goldens/macos/settings_page_referral_eligible.png index ae9c3a331..7d8a1b3fe 100644 Binary files a/test/goldens/screens/settings/goldens/macos/settings_page_referral_eligible.png and b/test/goldens/screens/settings/goldens/macos/settings_page_referral_eligible.png differ diff --git a/test/goldens/screens/transaction_history/goldens/macos/transaction_history_page_referral_payout.png b/test/goldens/screens/transaction_history/goldens/macos/transaction_history_page_referral_payout.png index 4a958014f..b9bcb60fb 100644 Binary files a/test/goldens/screens/transaction_history/goldens/macos/transaction_history_page_referral_payout.png and b/test/goldens/screens/transaction_history/goldens/macos/transaction_history_page_referral_payout.png differ diff --git a/test/goldens/screens/transaction_history/transaction_history_states_golden_test.dart b/test/goldens/screens/transaction_history/transaction_history_states_golden_test.dart index 71c1aa331..cf58bb97a 100644 --- a/test/goldens/screens/transaction_history/transaction_history_states_golden_test.dart +++ b/test/goldens/screens/transaction_history/transaction_history_states_golden_test.dart @@ -174,7 +174,8 @@ void main() { type: TransactionTypes.referralPayout, note: '', data: '246.50', - timestamp: DateTime.utc(2026, 8, 24, 10), + // Prize date must fall inside the pinned clock's default filter window. + timestamp: DateTime.utc(2026, 5, 20, 10), ); return withClock(pinnedClock, () { when(() => filterCubit.state).thenReturn( diff --git a/test/screens/kyc/steps/registration/widgets/referral_code_field_test.dart b/test/screens/kyc/steps/registration/widgets/referral_code_field_test.dart index a9c1bb645..d76f59c81 100644 --- a/test/screens/kyc/steps/registration/widgets/referral_code_field_test.dart +++ b/test/screens/kyc/steps/registration/widgets/referral_code_field_test.dart @@ -139,7 +139,7 @@ void main() { await tester.pump(); await tester.pump(const Duration(milliseconds: 500)); - expect(find.text('Hast du einen Empfehlungscode?'), findsOneWidget); + expect(find.text('Haben Sie einen Empfehlungscode?'), findsOneWidget); final field = tester.widget(find.byType(TextField)); expect(field.enableIMEPersonalizedLearning, isFalse); expect(find.textContaining('Einladung von Björn erkannt'), findsOneWidget); @@ -418,7 +418,7 @@ void main() { ); expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -426,7 +426,7 @@ void main() { _isLiveRegion( tester, find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), ), isTrue, @@ -532,7 +532,7 @@ void main() { expect(calls, 2); expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -1324,7 +1324,7 @@ void main() { findsNothing, ); expect( - find.text('Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.'), + find.text('Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.'), findsOneWidget, ); expect(ctrl.text, 'AB12'); @@ -1344,7 +1344,7 @@ void main() { await tester.pump(const Duration(seconds: 15)); expect(find.text('Code wird geprüft…'), findsNothing); expect( - find.text('Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.'), + find.text('Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.'), findsOneWidget, ); expect(find.text('Wiederholen'), findsOneWidget); diff --git a/test/screens/referral/flows/referral_create_invite_flow_test.dart b/test/screens/referral/flows/referral_create_invite_flow_test.dart index 7214a7e5a..974ca6aad 100644 --- a/test/screens/referral/flows/referral_create_invite_flow_test.dart +++ b/test/screens/referral/flows/referral_create_invite_flow_test.dart @@ -110,7 +110,7 @@ void main() { await pumpCreate(tester); await tester.pump(); - expect(find.text('Deine Einladung für Alice'), findsOneWidget); + expect(find.text('Ihre Einladung für Alice'), findsOneWidget); expect(find.text('Persönlicher Einladungslink'), findsOneWidget); }); } diff --git a/test/screens/referral/flows/referral_entry_card_flow_test.dart b/test/screens/referral/flows/referral_entry_card_flow_test.dart index 52dbb0f20..0cd48117c 100644 --- a/test/screens/referral/flows/referral_entry_card_flow_test.dart +++ b/test/screens/referral/flows/referral_entry_card_flow_test.dart @@ -80,7 +80,7 @@ void main() { await tester.pumpAndSettle(); expect(find.text('Empfehlungen'), findsOneWidget); - expect(find.text('Erhalte 20 REALU pro Weiterempfehlung'), findsOneWidget); + expect(find.text('Erhalten Sie 20 REALU pro Weiterempfehlung'), findsOneWidget); }); testWidgets('taps the Einstiegskarte through to the referral route', (tester) async { diff --git a/test/screens/referral/flows/referral_overview_flow_test.dart b/test/screens/referral/flows/referral_overview_flow_test.dart index b59c19a77..86ae7f62a 100644 --- a/test/screens/referral/flows/referral_overview_flow_test.dart +++ b/test/screens/referral/flows/referral_overview_flow_test.dart @@ -130,7 +130,7 @@ void main() { expect(find.text('INSGESAMT ERHALTEN'), findsOneWidget); expect(find.text('40 REALU'), findsOneWidget); expect(find.textContaining('Aktienkurs'), findsOneWidget); - expect(find.text('Deine Einladung für AliceShouldNotAppear'), findsOneWidget); + expect(find.text('Ihre Einladung für AliceShouldNotAppear'), findsOneWidget); expect(find.text('BobShouldNotAppear'), findsNothing); }); diff --git a/test/screens/referral/referral_create_page_test.dart b/test/screens/referral/referral_create_page_test.dart index 97f05214f..22ac759cb 100644 --- a/test/screens/referral/referral_create_page_test.dart +++ b/test/screens/referral/referral_create_page_test.dart @@ -74,7 +74,7 @@ void main() { await pumpCreateView(tester); await tester.pump(); - expect(find.text('Deine Einladung für Alice'), findsOneWidget); + expect(find.text('Ihre Einladung für Alice'), findsOneWidget); expect(find.text('Persönlicher Einladungslink'), findsOneWidget); expect( find.text( @@ -114,8 +114,8 @@ void main() { await pumpCreateView(tester); await tester.pump(); - expect(find.text('Deine Einladung'), findsOneWidget); - expect(find.textContaining('Deine Einladung für'), findsNothing); + expect(find.text('Ihre Einladung'), findsOneWidget); + expect(find.textContaining('Ihre Einladung für'), findsNothing); expect(find.text('Hey ,'), findsNothing); expect( find.text('RealUnit lädt dich ein zu RealUnit: https://realunit.app/invite/AB12CD'), @@ -283,7 +283,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -291,7 +291,7 @@ void main() { tester .widget( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), ) .style @@ -301,7 +301,7 @@ void main() { expect( find.ancestor( of: find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), matching: find.byWidgetPredicate( (widget) => widget is Semantics && widget.properties.liveRegion == true, diff --git a/test/screens/referral/referral_entry_card_test.dart b/test/screens/referral/referral_entry_card_test.dart index c9a5741f7..966bd0325 100644 --- a/test/screens/referral/referral_entry_card_test.dart +++ b/test/screens/referral/referral_entry_card_test.dart @@ -80,7 +80,7 @@ void main() { await tester.pump(); expect(find.text('Empfehlungen'), findsNothing); - expect(find.text('Erhalte 20 REALU pro Weiterempfehlung'), findsNothing); + expect(find.text('Erhalten Sie 20 REALU pro Weiterempfehlung'), findsNothing); }); testWidgets('resume reloads the gate and can open the card', (tester) async { @@ -115,7 +115,7 @@ void main() { await tester.pumpAndSettle(); expect(find.text('Empfehlungen'), findsOneWidget); - expect(find.text('Erhalte 20 REALU pro Weiterempfehlung'), findsOneWidget); + expect(find.text('Erhalten Sie 20 REALU pro Weiterempfehlung'), findsOneWidget); }); testWidgets('popping back to this route reloads the gate', (tester) async { @@ -216,7 +216,7 @@ void main() { await tester.pump(const Duration(milliseconds: 25)); await tester.pump(); expect(find.text('Empfehlungen'), findsOneWidget); - expect(find.text('Erhalte 20 REALU pro Weiterempfehlung'), findsOneWidget); + expect(find.text('Erhalten Sie 20 REALU pro Weiterempfehlung'), findsOneWidget); }); testWidgets('does not poll when the API gate is closed', (tester) async { @@ -336,7 +336,7 @@ void main() { await tester.pumpAndSettle(); expect(find.text('Empfehlungen'), findsOneWidget); - expect(find.text('Erhalte 20 REALU pro Weiterempfehlung'), findsOneWidget); + expect(find.text('Erhalten Sie 20 REALU pro Weiterempfehlung'), findsOneWidget); expect( find.descendant( of: find.byType(ReferralEntryCard), diff --git a/test/screens/referral/referral_error_message_test.dart b/test/screens/referral/referral_error_message_test.dart index fdee5765d..02d544ac2 100644 --- a/test/screens/referral/referral_error_message_test.dart +++ b/test/screens/referral/referral_error_message_test.dart @@ -203,6 +203,7 @@ void main() { late String unknown; late String quota; late String spentTitle; + late String alreadyRegisteredTitle; await tester.pumpWidget( MaterialApp( locale: const Locale('de'), @@ -221,6 +222,10 @@ void main() { context, referralSpentMessage, ); + alreadyRegisteredTitle = localizedReferralErrorTitle( + context, + referralAlreadyRegisteredMessage, + ); return const SizedBox.shrink(); }, ), @@ -229,7 +234,7 @@ void main() { expect( unknown, - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ); expect(unknown, isNot('nope')); expect( @@ -237,6 +242,8 @@ void main() { 'In diesem Quartal sind keine weiteren Prämien möglich.', ); expect(spentTitle, 'Code bereits eingelöst'); + expect(alreadyRegisteredTitle, 'Nur für neue Kundinnen und Kunden'); + expect(alreadyRegisteredTitle, isNot('Link ungültig oder abgelaufen')); }); testWidgets('maps remaining tokens to localized DE copy', (tester) async { @@ -301,7 +308,7 @@ void main() { expect( unavailable, - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ); expect( notEligible, @@ -321,15 +328,15 @@ void main() { ); expect( selfReferral, - 'Du kannst deine eigene Einladung nicht einlösen.', + 'Sie können Ihre eigene Einladung nicht einlösen.', ); expect( alreadyBound, - 'Du hast bereits einen Einladungs- oder Promo-Code eingelöst.', + 'Sie haben bereits einen Einladungs- oder Promo-Code eingelöst.', ); expect( alreadyRegistered, - 'Diese Einladung gilt nur für neue Kundinnen und Kunden.', + 'Einladungs- und Promo-Codes müssen vor dem ersten REALU-Kauf eingelöst werden.', ); expect(invalid, 'Dieser Code ist ungültig oder abgelaufen.'); expect(invalidTitle, 'Link ungültig oder abgelaufen'); diff --git a/test/screens/referral/referral_overview_page_test.dart b/test/screens/referral/referral_overview_page_test.dart index 53efa6d9d..88f02f4ac 100644 --- a/test/screens/referral/referral_overview_page_test.dart +++ b/test/screens/referral/referral_overview_page_test.dart @@ -137,11 +137,11 @@ void main() { ), findsOneWidget, ); - expect(find.text('Deine Empfehlungen'), findsOneWidget); + expect(find.text('Ihre Empfehlungen'), findsOneWidget); expect(find.text('INSGESAMT ERHALTEN'), findsOneWidget); expect(find.textContaining('Aktienkurs'), findsOneWidget); expect(find.text('Offene Einladungen verfallen nach 3 Monaten.'), findsOneWidget); - expect(find.text('Deine Einladung für AliceShouldNotAppear'), findsOneWidget); + expect(find.text('Ihre Einladung für AliceShouldNotAppear'), findsOneWidget); expect(find.text('Persönlicher Einladungslink'), findsOneWidget); expect( find.text( @@ -410,8 +410,8 @@ void main() { ); await tester.pump(); - expect(find.text('Deine Einladung'), findsOneWidget); - expect(find.textContaining('Deine Einladung für'), findsNothing); + expect(find.text('Ihre Einladung'), findsOneWidget); + expect(find.textContaining('Ihre Einladung für'), findsNothing); expect(find.text('Einladungslink kopieren'), findsOneWidget); expect(find.text('Hey ,'), findsNothing); expect( @@ -481,7 +481,7 @@ void main() { ); await tester.pump(); - expect(find.text('Deine Einladung'), findsNWidgets(2)); + expect(find.text('Ihre Einladung'), findsNWidgets(2)); expect(find.text('Einladungslink kopieren'), findsNWidgets(2)); }, ); @@ -768,7 +768,7 @@ void main() { ), ); await tester.pump(); - await tester.tap(find.text('Deine Empfehlungen')); + await tester.tap(find.text('Ihre Empfehlungen')); await tester.pumpAndSettle(); expect(find.text('tb'), findsOneWidget); }); @@ -1027,14 +1027,14 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); expect( find.ancestor( of: find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), matching: find.byWidgetPredicate( (widget) => widget is Semantics && widget.properties.liveRegion == true, @@ -1120,11 +1120,11 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); - expect(find.textContaining('Deine Einladung für Alice'), findsOneWidget); + expect(find.textContaining('Ihre Einladung für Alice'), findsOneWidget); expect(find.text('Wiederholen'), findsOneWidget); expect( tester @@ -1194,7 +1194,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -1275,7 +1275,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -1291,7 +1291,7 @@ void main() { expect( find.ancestor( of: find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), matching: find.byWidgetPredicate( (widget) => widget is Semantics && widget.properties.liveRegion == true, @@ -1337,7 +1337,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -1345,7 +1345,7 @@ void main() { tester .widget( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), ) .style @@ -1355,7 +1355,7 @@ void main() { expect( find.ancestor( of: find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), matching: find.byWidgetPredicate( (widget) => widget is Semantics && widget.properties.liveRegion == true, @@ -1413,7 +1413,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); diff --git a/test/screens/referral/referral_page_test.dart b/test/screens/referral/referral_page_test.dart index 33cd87a98..ecc9896a5 100644 --- a/test/screens/referral/referral_page_test.dart +++ b/test/screens/referral/referral_page_test.dart @@ -188,7 +188,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -196,7 +196,7 @@ void main() { tester .widget( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), ) .style @@ -206,7 +206,7 @@ void main() { expect( find.ancestor( of: find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), matching: find.byWidgetPredicate( (widget) => @@ -237,7 +237,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -259,7 +259,7 @@ void main() { expect(find.text('nope'), findsNothing); expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -283,7 +283,7 @@ void main() { ), ); - expect(find.text('Deine Empfehlungen'), findsOneWidget); + expect(find.text('Ihre Empfehlungen'), findsOneWidget); expect(find.text('Einladungslink erstellen'), findsOneWidget); }); @@ -396,7 +396,7 @@ void main() { await tester.pump(); await tester.pump(); - expect(find.text('Deine Empfehlungen'), findsOneWidget); + expect(find.text('Ihre Empfehlungen'), findsOneWidget); }); testWidgets('accepting terms from the accepting state opens create-invite', ( diff --git a/test/screens/referral/referral_terms_page_test.dart b/test/screens/referral/referral_terms_page_test.dart index e62d6c865..82366eb3a 100644 --- a/test/screens/referral/referral_terms_page_test.dart +++ b/test/screens/referral/referral_terms_page_test.dart @@ -194,13 +194,13 @@ void main() { await tester.pump(); expect( - find.text('Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.'), + find.text('Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.'), findsOneWidget, ); expect( find.ancestor( of: find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), matching: find.byWidgetPredicate( (widget) => widget is Semantics && widget.properties.liveRegion == true, @@ -310,7 +310,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); diff --git a/test/screens/settings/settings_referral_entry_test.dart b/test/screens/settings/settings_referral_entry_test.dart index d252e29bc..99d4ebc3b 100644 --- a/test/screens/settings/settings_referral_entry_test.dart +++ b/test/screens/settings/settings_referral_entry_test.dart @@ -64,7 +64,7 @@ void main() { await tester.pumpAndSettle(); expect(find.text('Empfehlungen'), findsOneWidget); - expect(find.text('Erhalte 20 REALU pro Weiterempfehlung'), findsOneWidget); + expect(find.text('Erhalten Sie 20 REALU pro Weiterempfehlung'), findsOneWidget); expect( find.ancestor( of: find.byIcon(Icons.card_giftcard_outlined), @@ -106,6 +106,6 @@ void main() { await tester.pump(); expect(find.text('Empfehlungen'), findsNothing); - expect(find.text('Erhalte 20 REALU pro Weiterempfehlung'), findsNothing); + expect(find.text('Erhalten Sie 20 REALU pro Weiterempfehlung'), findsNothing); }); } diff --git a/test/setup/routing/referral_bind_test.dart b/test/setup/routing/referral_bind_test.dart index 2f3695aea..629f95681 100644 --- a/test/setup/routing/referral_bind_test.dart +++ b/test/setup/routing/referral_bind_test.dart @@ -205,20 +205,20 @@ void main() { await tester.pump(); await tester.pump(); - expect(find.text('Link ungültig oder abgelaufen'), findsOneWidget); + expect(find.text('Bereits ein Code eingelöst'), findsOneWidget); expect( - find.text('Du hast bereits einen Einladungs- oder Promo-Code eingelöst.'), + find.text('Sie haben bereits einen Einladungs- oder Promo-Code eingelöst.'), findsOneWidget, ); expect( - tester.widget(find.text('Link ungültig oder abgelaufen')).style?.color, + tester.widget(find.text('Bereits ein Code eingelöst')).style?.color, RealUnitColors.status.red600, ); expect( tester .widget( find.text( - 'Du hast bereits einen Einladungs- oder Promo-Code eingelöst.', + 'Sie haben bereits einen Einladungs- oder Promo-Code eingelöst.', ), ) .style @@ -228,7 +228,7 @@ void main() { await tester.tapAt(const Offset(2, 2)); await tester.pump(); expect( - find.text('Du hast bereits einen Einladungs- oder Promo-Code eingelöst.'), + find.text('Sie haben bereits einen Einladungs- oder Promo-Code eingelöst.'), findsOneWidget, ); expect( @@ -547,7 +547,7 @@ void main() { expect( find.text( - 'Einladung von Björn erkannt. Björn erhält seine Prämie automatisch, sobald du verifiziert bist und deinen ersten Kauf abgeschlossen hast.', + 'Einladung von Björn erkannt. Björn erhält seine Prämie automatisch, sobald Sie verifiziert sind und Ihren ersten Kauf abgeschlossen haben.', ), findsOneWidget, ); @@ -716,7 +716,7 @@ void main() { ); await bindPendingReferralCode(router); - expect(find.text('Link ungültig oder abgelaufen'), findsNothing); + expect(find.text('Bereits ein Code eingelöst'), findsNothing); await tester.pumpWidget( MaterialApp.router( @@ -734,7 +734,7 @@ void main() { ); await tester.pump(); - expect(find.text('Link ungültig oder abgelaufen'), findsOneWidget); + expect(find.text('Bereits ein Code eingelöst'), findsOneWidget); expect( tester.widget(find.widgetWithText(TextButton, 'Schließen')).autofocus, isTrue, @@ -775,7 +775,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -791,7 +791,7 @@ void main() { await tester.pump(); expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -837,7 +837,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -883,7 +883,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -933,7 +933,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -944,7 +944,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsNothing, ); @@ -997,7 +997,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -1018,7 +1018,7 @@ void main() { await pending; expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsNothing, ); @@ -1064,7 +1064,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -1129,7 +1129,7 @@ void main() { expect(await peekPendingReferralCode(), 'NEWER1'); expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsOneWidget, ); @@ -1184,7 +1184,7 @@ void main() { await tester.pump(); await tester.pump(); - expect(find.text('Link ungültig oder abgelaufen'), findsOneWidget); + expect(find.text('Bereits ein Code eingelöst'), findsOneWidget); expect(await peekPendingReferralCode(), isNull); await tester.tap(find.text('Schließen')); await tester.pumpAndSettle(); @@ -1209,7 +1209,7 @@ void main() { expect( find.text( - 'Wir konnten den Code gerade nicht prüfen. Bitte versuche es später erneut.', + 'Wir konnten den Code gerade nicht prüfen. Bitte versuchen Sie es später erneut.', ), findsNothing, ); @@ -1312,7 +1312,7 @@ void main() { await tester.pump(); await tester.pump(); - expect(find.text('Link ungültig oder abgelaufen'), findsOneWidget); + expect(find.text('Bereits ein Code eingelöst'), findsOneWidget); expect(calls, 2); await tester.tap(find.text('Schließen')); await tester.pumpAndSettle();