2026-07-13 22:14:27 +02:00
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
|
import 'package:plezy/theme/mono_theme.dart';
|
|
|
|
|
import 'package:plezy/widgets/focusable_list_tile.dart';
|
|
|
|
|
import 'package:plezy/widgets/setting_tile.dart';
|
|
|
|
|
import 'package:plezy/widgets/settings_section.dart';
|
|
|
|
|
|
2026-07-26 21:04:53 +02:00
|
|
|
/// Settings rows must render exactly like every other row in the app on every
|
|
|
|
|
/// platform. Each row type is measured against an untouched [FocusableListTile]
|
|
|
|
|
/// — the app-wide reference row — rather than against a hard-coded size, so the
|
|
|
|
|
/// assertions still hold if the app's row density is ever retuned.
|
2026-07-13 22:14:27 +02:00
|
|
|
void main() {
|
2026-07-26 21:04:53 +02:00
|
|
|
for (final platform in [TargetPlatform.android, TargetPlatform.macOS]) {
|
|
|
|
|
testWidgets('settings rows match the standard app row on $platform', (tester) async {
|
|
|
|
|
await tester.pumpWidget(_harness(platform));
|
2026-07-13 22:14:27 +02:00
|
|
|
|
2026-07-26 21:04:53 +02:00
|
|
|
final referenceTitle = _titleFontSize(tester, 'Clear Cache');
|
|
|
|
|
final referenceSubtitle = _subtitleFontSize(tester, 'Clear cached data');
|
|
|
|
|
final referenceHeight = tester.getSize(find.byKey(const ValueKey('reference'))).height;
|
2026-07-13 22:14:27 +02:00
|
|
|
|
2026-07-26 21:04:53 +02:00
|
|
|
// The dense ListTile title/subtitle sizes; spelled out so a silent
|
|
|
|
|
// theme-wide inflation can't make every side of the comparison agree.
|
|
|
|
|
expect(referenceTitle, 13);
|
|
|
|
|
expect(referenceSubtitle, 12);
|
2026-07-13 22:14:27 +02:00
|
|
|
|
2026-07-26 21:04:53 +02:00
|
|
|
for (final title in ['View Logs', 'Enable Thing', 'View Mode', 'Account']) {
|
|
|
|
|
expect(_titleFontSize(tester, title), referenceTitle, reason: '$title title size');
|
|
|
|
|
}
|
|
|
|
|
for (final subtitle in ['View application logs', 'Toggles the thing', 'signed in']) {
|
|
|
|
|
expect(_subtitleFontSize(tester, subtitle), referenceSubtitle, reason: '$subtitle subtitle size');
|
|
|
|
|
}
|
|
|
|
|
for (final key in ['navigation', 'switch', 'plain']) {
|
|
|
|
|
expect(tester.getSize(find.byKey(ValueKey(key))).height, referenceHeight, reason: '$key row height');
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-07-13 22:14:27 +02:00
|
|
|
|
2026-07-26 21:04:53 +02:00
|
|
|
double? _titleFontSize(WidgetTester tester, String text) {
|
|
|
|
|
final finder = find.text(text);
|
|
|
|
|
final inherited = DefaultTextStyle.of(tester.element(finder)).style;
|
|
|
|
|
return inherited.merge(tester.widget<Text>(finder).style).fontSize;
|
2026-07-13 22:14:27 +02:00
|
|
|
}
|
|
|
|
|
|
2026-07-26 21:04:53 +02:00
|
|
|
double? _subtitleFontSize(WidgetTester tester, String text) =>
|
|
|
|
|
DefaultTextStyle.of(tester.element(find.text(text))).style.fontSize;
|
|
|
|
|
|
|
|
|
|
Widget _harness(TargetPlatform platform) {
|
2026-07-13 22:14:27 +02:00
|
|
|
return MaterialApp(
|
|
|
|
|
theme: monoTheme(dark: false).copyWith(platform: platform),
|
|
|
|
|
home: Scaffold(
|
2026-07-26 21:04:53 +02:00
|
|
|
body: SettingsGroup(
|
2026-07-13 22:14:27 +02:00
|
|
|
children: [
|
|
|
|
|
SettingNavigationTile(
|
2026-07-26 21:04:53 +02:00
|
|
|
key: const ValueKey('navigation'),
|
2026-07-13 22:14:27 +02:00
|
|
|
icon: Icons.article,
|
|
|
|
|
title: 'View Logs',
|
|
|
|
|
subtitle: 'View application logs',
|
|
|
|
|
onTap: () {},
|
|
|
|
|
),
|
|
|
|
|
FocusableListTile(
|
2026-07-26 21:04:53 +02:00
|
|
|
key: const ValueKey('reference'),
|
2026-07-13 22:14:27 +02:00
|
|
|
leading: const Icon(Icons.cleaning_services),
|
|
|
|
|
title: const Text('Clear Cache'),
|
|
|
|
|
subtitle: const Text('Clear cached data'),
|
|
|
|
|
trailing: const Icon(Icons.chevron_right),
|
|
|
|
|
onTap: () {},
|
|
|
|
|
),
|
2026-07-26 21:04:53 +02:00
|
|
|
FocusableSwitchListTile(
|
|
|
|
|
key: const ValueKey('switch'),
|
|
|
|
|
secondary: const Icon(Icons.toggle_on),
|
|
|
|
|
title: const Text('Enable Thing'),
|
|
|
|
|
subtitle: const Text('Toggles the thing'),
|
|
|
|
|
value: true,
|
|
|
|
|
onChanged: (_) {},
|
|
|
|
|
),
|
2026-07-13 22:14:27 +02:00
|
|
|
SegmentedSetting<String>(
|
|
|
|
|
icon: Icons.view_list,
|
|
|
|
|
title: 'View Mode',
|
|
|
|
|
segments: const [
|
|
|
|
|
ButtonSegment(value: 'grid', label: Text('Grid')),
|
|
|
|
|
ButtonSegment(value: 'list', label: Text('List')),
|
|
|
|
|
],
|
|
|
|
|
selected: 'grid',
|
|
|
|
|
onChanged: (_) {},
|
|
|
|
|
),
|
2026-07-26 21:04:53 +02:00
|
|
|
// Non-interactive info rows are plain ListTiles; the group hands them
|
|
|
|
|
// the same geometry as their interactive siblings.
|
|
|
|
|
const ListTile(key: ValueKey('plain'), title: Text('Account'), subtitle: Text('signed in')),
|
2026-07-13 22:14:27 +02:00
|
|
|
],
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
}
|