test(auth): address review findings on sign-in prompt (PR A)
All checks were successful
CI / flutter (pull_request) Successful in 1m38s
CI / web-build (pull_request) Successful in 1m38s
CI / supabase (pull_request) Successful in 13s
CI / supabase-tests (pull_request) Successful in 35s
CI / e2e (pull_request) Successful in 2m58s

Correct the focus comment (refs #61), test Space key and focus state,
prove scrollability at 200 percent text, drop needless minWidth, English
doc comment, allow OutlinedButton as secondary action in standalone cards,
English prompt text says plays instead of games.

Refs #49

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
cschlaefke 2026-11-04 01:25:22 +01:00
parent f078c8a094
commit 7eaed46dc2
7 changed files with 52 additions and 14 deletions

View file

@ -5,10 +5,10 @@ import 'package:shuffle/core/theme/app_spacing.dart';
import 'package:shuffle/features/auth/presentation/providers/sign_in_prompt_provider.dart';
import 'package:shuffle/features/auth/presentation/widgets/sign_in_prompt.dart';
/// Platzhalter-Startseite. Wird durch die Katalogsuche bzw. QUE-09 ersetzt.
/// Placeholder home screen. To be replaced by the catalog search / QUE-09.
///
/// Liest nur [showSignInPromptProvider]; die Entscheidung, ob der
/// Anmelde-Hinweis erscheint, liegt dort (Spec #49 § 5.1).
/// Reads only [showSignInPromptProvider]; the decision whether the sign-in
/// prompt appears lives there (Spec #49 § 5.1).
class HomeScreen extends ConsumerWidget {
const HomeScreen({super.key});
@ -26,7 +26,6 @@ class HomeScreen extends ConsumerWidget {
child: ConstrainedBox(
constraints: BoxConstraints(
minHeight: constraints.maxHeight - AppSpacing.lg * 2,
minWidth: constraints.maxWidth,
),
child: Center(
child: Column(

View file

@ -91,7 +91,7 @@
"authLoginForgotPassword": "Forgot password?",
"authLoginCreateAccount": "Create account",
"signInPromptTitle": "Your account",
"signInPromptBody": "Sign in to manage your collection and games.",
"signInPromptBody": "Sign in to manage your collection and plays.",
"signInPromptSignIn": "Sign in",
"signInPromptRegister": "Create account",
"authCheckEmailTitle": "Check your inbox",

View file

@ -413,7 +413,7 @@ abstract class AppLocalizations {
/// No description provided for @signInPromptBody.
///
/// In en, this message translates to:
/// **'Sign in to manage your collection and games.'**
/// **'Sign in to manage your collection and plays.'**
String get signInPromptBody;
/// No description provided for @signInPromptSignIn.

View file

@ -192,7 +192,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get signInPromptTitle => 'Your account';
@override
String get signInPromptBody => 'Sign in to manage your collection and games.';
String get signInPromptBody => 'Sign in to manage your collection and plays.';
@override
String get signInPromptSignIn => 'Sign in';

View file

@ -188,7 +188,15 @@ void main() {
locale: locale,
);
expect(tester.takeException(), isNull);
// The page scrolls: the buttons stay reachable and tappable.
// The page scrolls: dragging moves the content, the buttons stay
// reachable and tappable.
final before = tester.getTopLeft(registerButton).dy;
await tester.drag(
find.byType(SingleChildScrollView),
const Offset(0, -200),
);
await tester.pump();
expect(tester.getTopLeft(registerButton).dy, lessThan(before));
await tester.ensureVisible(registerButton);
await tester.pump();
expect(
@ -233,11 +241,31 @@ void main() {
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pump();
expect(focusIn(registerButton), isTrue);
// Visible focus: keyboard navigation switches to traditional highlight
// mode and the button holds the primary focus (the focus ring comes
// from the focused state of the theme).
expect(
FocusManager.instance.highlightMode,
FocusHighlightMode.traditional,
);
expect(FocusManager.instance.primaryFocus?.hasPrimaryFocus, isTrue);
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
expect(find.byKey(const Key('register_screen')), findsOneWidget);
},
);
testWidgets('QUE-06 Krit. 4: Leertaste löst „Konto erstellen“ aus', (
tester,
) async {
await open(tester);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.sendKeyEvent(LogicalKeyboardKey.tab);
await tester.pump();
await tester.sendKeyEvent(LogicalKeyboardKey.space);
await tester.pumpAndSettle();
expect(find.byKey(const Key('register_screen')), findsOneWidget);
});
});
}

View file

@ -15,7 +15,7 @@ Schlüssel sind `lowerCamelCase` mit Präfix `auth`.
- Titel: `headlineSmall`, darunter optional ein Einleitungssatz in `bodyMedium`. Titel als Semantics-Überschrift
(`Semantics(header: true)`).
- Banner (`AppBanner`) stehen **über** den Feldern, direkt unter dem Titel. Fehler am Feld stehen unter dem Feld.
- Genau ein Primärknopf (`AppPrimaryButton`, volle Breite). Sekundäraktionen sind `TextButton`s (48 dp).
- Genau ein Primärknopf (`AppPrimaryButton`, volle Breite). Sekundäraktionen sind `TextButton`s (48 dp). Für eigenständige Karten (z. B. `SignInPrompt`) ist ein `OutlinedButton` als Sekundäraktion zulässig.
- Eingaben bleiben bei jedem Fehler erhalten. Während `submitting` sind Knopf und Felder gesperrt, der Knopf
zeigt den Spinner und den gesprochenen Text `authSubmitting`.
- Nach einem Fehler setzt der Screen den Fokus auf das erste fehlerhafte Feld; bei Banner-Fehlern bleibt der

View file

@ -7,7 +7,7 @@ const gotrue = new GoTrueClient();
/** Texte (app_en.arb). */
const prompt = {
title: 'Your account',
body: 'Sign in to manage your collection and games.',
body: 'Sign in to manage your collection and plays.',
};
test.describe('#49 Kriterium 1 / QUE-09 Kriterium 13 (web): Anmelde-Hinweis auf der Startseite', () => {
@ -48,10 +48,10 @@ test.describe('#49 Kriterium 1 / QUE-09 Kriterium 13 (web): Anmelde-Hinweis auf
expect(pos.every((p) => p >= 0)).toBe(true);
expect([...pos].sort((x, y) => x - y)).toEqual(pos);
// Flutter-Web behandelt Tab im eigenen Fokussystem; `document.activeElement` bleibt auf dem
// Semantics-Platzhalter. Deshalb wird die Reihenfolge ueber das Verhalten geprueft: erster Tab
// = „Sign in“, zweiter Tab = „Create account“, Enter loest den fokussierten Knopf aus.
// Der Fokusring selbst wird im Canvas gezeichnet (Widget-Test, Theme).
// Reihenfolge ueber das Verhalten: erster Tab = „Sign in“, zweiter Tab = „Create account“,
// Enter loest den fokussierten Knopf aus. DOM-Fokus (toBeFocused) liegt nur auf „Create account“;
// „Sign in“ bekommt nie DOM-Fokus (kein tabindex, AppPrimaryButton verwirft focusable/focused
// des FilledButton), siehe Issue #61. Der Fokusring selbst wird im Canvas gezeichnet.
await page.keyboard.press('Tab');
await page.keyboard.press('Enter');
await expect(page).toHaveURL(/\/login$/);
@ -62,10 +62,21 @@ test.describe('#49 Kriterium 1 / QUE-09 Kriterium 13 (web): Anmelde-Hinweis auf
await expect(page.getByRole('button', { name: 'Create account', exact: true })).toBeVisible();
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await expect(page.getByRole('button', { name: 'Create account', exact: true })).toBeFocused();
await page.keyboard.press('Enter');
await expect(page).toHaveURL(/\/register$/);
});
test('QUE-06 Kriterium 4: Leertaste loest „Create account“ aus', async ({ page }) => {
await open(page, '/');
await expect(page.getByRole('button', { name: 'Create account', exact: true })).toBeVisible();
await page.keyboard.press('Tab');
await page.keyboard.press('Tab');
await expect(page.getByRole('button', { name: 'Create account', exact: true })).toBeFocused();
await page.keyboard.press('Space');
await expect(page).toHaveURL(/\/register$/);
});
test('#49 Kriterium 1: angemeldet erscheint der Hinweis nicht', async ({ page }) => {
const email = randomEmail();
const password = randomPassword();