feat(j0.3): socle transverse (Result/Failure, theme, router, DI)
- core/error : Result<S> maison (Ok/Err) + Failure scellee avec egalite de valeur - core/theme : AppTheme Material 3 (palette coucher, cibles tactiles enfant) - core/router : routes nommees child/parentGate/parent (Navigator 1, placeholders) - core/di : conventions providers - CLAUDE.md §7 : Result maison & Navigator 1 actes (YAGNI) - ROADMAP : 0.3 cochee, Jalon 0 termine - corrections code review : egalite Failure, assertions tests, Map.unmodifiable Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:storytime/core/error/failure.dart';
|
||||
import 'package:storytime/core/error/result.dart';
|
||||
|
||||
void main() {
|
||||
group('Result', () {
|
||||
const failure = UnexpectedFailure('boom');
|
||||
|
||||
group('Ok', () {
|
||||
test('map transforme la valeur', () {
|
||||
const result = Ok<int>(1);
|
||||
expect(result.map((v) => v + 1), equals(const Ok<int>(2)));
|
||||
});
|
||||
|
||||
test('map préserve le type Ok et produit la bonne valeur', () {
|
||||
const result = Ok<String>('hello');
|
||||
final mapped = result.map((v) => v.length);
|
||||
expect(mapped, isA<Ok<int>>());
|
||||
expect((mapped as Ok<int>).value, equals(5));
|
||||
});
|
||||
|
||||
test('fold appelle la branche ok', () {
|
||||
const result = Ok<int>(42);
|
||||
final out = result.fold(onOk: (v) => 'ok:$v', onErr: (_) => 'err');
|
||||
expect(out, equals('ok:42'));
|
||||
});
|
||||
|
||||
test('when appelle la branche ok et pas la branche err', () {
|
||||
const result = Ok<int>(7);
|
||||
var okCalled = false;
|
||||
var errCalled = false;
|
||||
result.when(ok: (_) => okCalled = true, err: (_) => errCalled = true);
|
||||
expect(okCalled, isTrue);
|
||||
expect(errCalled, isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('Err', () {
|
||||
test('map ne transforme pas — reste Err avec le même Failure', () {
|
||||
const result = Err<int>(failure);
|
||||
final mapped = result.map((v) => v + 1);
|
||||
expect(mapped, isA<Err<int>>());
|
||||
expect((mapped as Err<int>).failure, same(failure));
|
||||
});
|
||||
|
||||
test('fold appelle la branche err', () {
|
||||
const result = Err<int>(failure);
|
||||
final out = result.fold(
|
||||
onOk: (_) => 'ok',
|
||||
onErr: (f) => 'err:${f.message}',
|
||||
);
|
||||
expect(out, equals('err:boom'));
|
||||
});
|
||||
|
||||
test('when appelle la branche err et pas la branche ok', () {
|
||||
const result = Err<int>(failure);
|
||||
var okCalled = false;
|
||||
var errCalled = false;
|
||||
result.when(ok: (_) => okCalled = true, err: (_) => errCalled = true);
|
||||
expect(errCalled, isTrue);
|
||||
expect(okCalled, isFalse);
|
||||
});
|
||||
});
|
||||
|
||||
group('Failure sous-types', () {
|
||||
test('NetworkFailure expose le message', () {
|
||||
const f = NetworkFailure('timeout');
|
||||
expect(f.message, equals('timeout'));
|
||||
});
|
||||
|
||||
test('NotFoundFailure expose le message', () {
|
||||
const f = NotFoundFailure('episode 42');
|
||||
expect(f.message, equals('episode 42'));
|
||||
});
|
||||
|
||||
test('UnexpectedFailure expose le message', () {
|
||||
const f = UnexpectedFailure('oops');
|
||||
expect(f.message, equals('oops'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:storytime/core/theme/app_theme.dart';
|
||||
|
||||
void main() {
|
||||
group('AppTheme', () {
|
||||
testWidgets('useMaterial3 est activé', (tester) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(theme: AppTheme.light(), home: const SizedBox.shrink()),
|
||||
);
|
||||
|
||||
final theme = Theme.of(tester.element(find.byType(SizedBox)));
|
||||
expect(theme.useMaterial3, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('la couleur primaire correspond à la palette coucher', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(theme: AppTheme.light(), home: const SizedBox.shrink()),
|
||||
);
|
||||
|
||||
final theme = Theme.of(tester.element(find.byType(SizedBox)));
|
||||
// Palette « coucher » : violet/indigo doux.
|
||||
// On vérifie que la teinte de seed est bien dans le spectre attendu.
|
||||
final primary = theme.colorScheme.primary;
|
||||
// HSL : le bleu/violet doit dominer sur le rouge et le vert.
|
||||
final hsl = HSLColor.fromColor(primary);
|
||||
expect(
|
||||
hsl.hue,
|
||||
inInclusiveRange(200.0, 320.0),
|
||||
reason: 'La couleur primaire doit être dans le spectre bleu-violet',
|
||||
);
|
||||
});
|
||||
|
||||
test('AppTheme.light() retourne un ThemeData non nul', () {
|
||||
expect(AppTheme.light(), isA<ThemeData>());
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user