The developer asked for Todo, Train and Meditate to offer Learn's Appearance and Language (ADR 0014's amendment of 4 Nov 2026). Their model, store, cubit and card move into halkyon_app: Appearance and Look (a BCP 47 tag, null for the device's language), FileLookStore keeping it in the app's support folder as JSON written by rename (per device, not synced, read before the first frame), LookCubit for the apps to provide above InstanceGate, and LookSettingsCard / DeviceLookCard with the texts moved from Learn's arbs (look*, the same words). Each app passes the languages it speaks, named in their own language. Learn keeps its stored choice where it was, among its DeviceSettings, and feeds the shared card from them: no migration, so nobody's language or appearance moves, and nothing changes on its screen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
134 lines
4.3 KiB
Dart
134 lines
4.3 KiB
Dart
import 'package:flutter_bloc/flutter_bloc.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:halkyon_app/halkyon_app.dart';
|
|
import 'package:halkyon_app/testing.dart';
|
|
import 'package:halkyon_ui/halkyon_ui.dart';
|
|
import 'package:material_ui/material_ui.dart';
|
|
|
|
const _languages = <AppLanguage>[
|
|
(tag: 'en-GB', name: 'English (UK)'),
|
|
(tag: 'en-US', name: 'English (US)'),
|
|
(tag: 'de', name: 'Deutsch'),
|
|
];
|
|
|
|
void main() {
|
|
/// The card in an app following the look it chooses, as every Halkyon
|
|
/// app's does, on [store].
|
|
Future<void> show(WidgetTester tester, MemoryLookStore store) async {
|
|
final cubit = await LookCubit.open(store);
|
|
addTearDown(cubit.close);
|
|
await tester.pumpWidget(
|
|
BlocProvider.value(
|
|
value: cubit,
|
|
child: BlocBuilder<LookCubit, Look>(
|
|
builder: (context, look) => MaterialApp(
|
|
theme: appTheme(Brightness.light),
|
|
darkTheme: appTheme(Brightness.dark),
|
|
themeMode: look.themeMode,
|
|
locale: look.locale,
|
|
localeListResolutionCallback: resolveAppLocale,
|
|
localizationsDelegates: testLocalizationsDelegates,
|
|
supportedLocales: HalkyonAppLocalizations.supportedLocales,
|
|
home: const Scaffold(body: DeviceLookCard(languages: _languages)),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
}
|
|
|
|
Brightness brightness(WidgetTester tester) =>
|
|
Theme.of(tester.element(find.byType(DeviceLookCard))).brightness;
|
|
|
|
/// Opens the menu showing [current] and picks [choice].
|
|
Future<void> choose(
|
|
WidgetTester tester,
|
|
String current,
|
|
String choice,
|
|
) async {
|
|
await tester.tap(find.text(current));
|
|
await tester.pumpAndSettle();
|
|
await tester.tap(find.text(choice).last);
|
|
await tester.pumpAndSettle();
|
|
}
|
|
|
|
setUp(() {
|
|
TestWidgetsFlutterBinding.ensureInitialized()
|
|
.platformDispatcher
|
|
.platformBrightnessTestValue =
|
|
Brightness.light;
|
|
});
|
|
tearDown(
|
|
() => TestWidgetsFlutterBinding.instance.platformDispatcher
|
|
.clearPlatformBrightnessTestValue(),
|
|
);
|
|
|
|
testWidgets("the device's appearance and language first", (tester) async {
|
|
await show(tester, MemoryLookStore());
|
|
|
|
expect(find.text('Appearance'), findsOneWidget);
|
|
expect(find.text('Device theme'), findsOneWidget);
|
|
expect(find.text('Language'), findsOneWidget);
|
|
expect(find.text('Device language'), findsOneWidget);
|
|
});
|
|
|
|
testWidgets('Dark makes the app dark, and is kept', (tester) async {
|
|
final store = MemoryLookStore();
|
|
await show(tester, store);
|
|
expect(brightness(tester), Brightness.light);
|
|
|
|
await choose(tester, 'Device theme', 'Dark');
|
|
|
|
expect(brightness(tester), Brightness.dark);
|
|
expect(store.writes.last, const Look(appearance: Appearance.dark));
|
|
});
|
|
|
|
testWidgets('the languages offered, each in its own name, after the '
|
|
"device's", (tester) async {
|
|
await show(tester, MemoryLookStore());
|
|
|
|
await tester.tap(find.text('Device language'));
|
|
await tester.pumpAndSettle();
|
|
expect(
|
|
tester
|
|
.widgetList<DropdownMenuItem<String?>>(
|
|
find.byWidgetPredicate(
|
|
(widget) => widget is DropdownMenuItem<String?>,
|
|
),
|
|
)
|
|
.map((item) => item.value)
|
|
.toSet()
|
|
.toList(),
|
|
[null, 'en-GB', 'en-US', 'de'],
|
|
);
|
|
});
|
|
|
|
testWidgets('Deutsch switches the texts to German, the device language '
|
|
'back', (tester) async {
|
|
final store = MemoryLookStore();
|
|
await show(tester, store);
|
|
|
|
await choose(tester, 'Device language', 'Deutsch');
|
|
|
|
expect(find.text('Darstellung'), findsOneWidget);
|
|
expect(find.text('Gerätedesign'), findsOneWidget);
|
|
expect(store.writes.last, const Look(language: 'de'));
|
|
|
|
await choose(tester, 'Deutsch', 'Gerätesprache');
|
|
|
|
expect(find.text('Appearance'), findsOneWidget);
|
|
expect(store.writes.last, const Look());
|
|
});
|
|
|
|
testWidgets('starts at the look kept; a language this version no longer '
|
|
"speaks shows as the device's", (tester) async {
|
|
await show(
|
|
tester,
|
|
MemoryLookStore(const Look(appearance: Appearance.dark, language: 'fr')),
|
|
);
|
|
|
|
expect(brightness(tester), Brightness.dark);
|
|
expect(find.text('Dark'), findsOneWidget);
|
|
expect(find.text('Device language'), findsOneWidget);
|
|
});
|
|
}
|