feat(auth): Anmelde-Einstieg auf der Startseite (#49, PR A) #62
7 changed files with 52 additions and 14 deletions
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
Loading…
Reference in a new issue