diff --git a/lib/core/l10n/app_localizations.dart b/lib/core/l10n/app_localizations.dart index 361990d..d5a0cbe 100644 --- a/lib/core/l10n/app_localizations.dart +++ b/lib/core/l10n/app_localizations.dart @@ -511,6 +511,12 @@ abstract class AppLocalizations { /// **'Depth {depth}'** String formatDepth(int depth); + /// Settings font size dropdown option with display label and numeric point size + /// + /// In en, this message translates to: + /// **'{label} ({size})'** + String formatFontSizeOption(String label, int size); + /// Fallback liked-post card subtitle showing when a post was liked /// /// In en, this message translates to: @@ -667,6 +673,12 @@ abstract class AppLocalizations { /// **'Account'** String get labelAccount; + /// Settings item for account-level preferences + /// + /// In en, this message translates to: + /// **'Account settings'** + String get labelAccountSettings; + /// Settings section for account maintenance actions /// /// In en, this message translates to: @@ -781,6 +793,12 @@ abstract class AppLocalizations { /// **'Bluesky'** String get labelBluesky; + /// Blacksky provider option label + /// + /// In en, this message translates to: + /// **'Blacksky'** + String get labelBlacksky; + /// Short all-caps label for media alt text controls /// /// In en, this message translates to: @@ -805,6 +823,12 @@ abstract class AppLocalizations { /// **'Clean Follows'** String get labelCleanFollows; + /// Settings label for code font selection + /// + /// In en, this message translates to: + /// **'Code Font'** + String get labelCodeFont; + /// Close button tooltip /// /// In en, this message translates to: @@ -835,6 +859,12 @@ abstract class AppLocalizations { /// **'Content Moderation'** String get labelContentModeration; + /// Settings label for content font selection + /// + /// In en, this message translates to: + /// **'Content Font'** + String get labelContentFont; + /// Semantic label for continuing sign in /// /// In en, this message translates to: @@ -925,6 +955,30 @@ abstract class AppLocalizations { /// **'Force Next XRPC 401'** String get labelForceNextXrpc401; + /// Settings label for content font size selection + /// + /// In en, this message translates to: + /// **'Font Size'** + String get labelFontSize; + + /// Small content font size option label + /// + /// In en, this message translates to: + /// **'Small'** + String get labelFontSizeSmall; + + /// Normal content font size option label + /// + /// In en, this message translates to: + /// **'Normal'** + String get labelFontSizeNormal; + + /// Large content font size option label + /// + /// In en, this message translates to: + /// **'Large'** + String get labelFontSizeLarge; + /// Developer setting to simulate offline mode /// /// In en, this message translates to: @@ -937,6 +991,12 @@ abstract class AppLocalizations { /// **'Guest'** String get labelGuest; + /// Settings label for heading font selection + /// + /// In en, this message translates to: + /// **'Heading Font'** + String get labelHeadingFont; + /// Provider diagnostics health label /// /// In en, this message translates to: @@ -1501,12 +1561,30 @@ abstract class AppLocalizations { /// **'Video Upload Limits'** String get labelVideoUploadLimits; + /// Settings subtitle for the about page + /// + /// In en, this message translates to: + /// **'Stormlight Labs'** + String get messageAboutSubtitle; + + /// Settings subtitle for account-level preferences + /// + /// In en, this message translates to: + /// **'Feed display preferences and account defaults'** + String get messageAccountSettingsSubtitle; + /// Debug app password help text /// /// In en, this message translates to: /// **'Can be generated via Bluesky\'\'s App Passwords section at bsky.app.'** String get messageAppPasswordGeneratedViaBluesky; + /// Settings subtitle for AT Explorer + /// + /// In en, this message translates to: + /// **'View PDS Records'** + String get messageAtExplorerSubtitle; + /// Snackbar message after arming debug unauthorized response /// /// In en, this message translates to: @@ -1687,6 +1765,12 @@ abstract class AppLocalizations { /// **'Manage semantic search from Bookmarks & Likes -> Search'** String get messageManageSemanticSearchSubtitle; + /// Settings subtitle for opening app logs + /// + /// In en, this message translates to: + /// **'View app log files'** + String get messageLogsSubtitle; + /// Trending banner shown when supplemental metadata cannot be loaded /// /// In en, this message translates to: @@ -1711,6 +1795,12 @@ abstract class AppLocalizations { /// **'Moderation/ranking can differ by provider. Verify health and recent fallback state.'** String get messageProviderDiagnosticsSubtitle; + /// Settings subtitle for the privacy policy + /// + /// In en, this message translates to: + /// **'How Lazurite handles data'** + String get messagePrivacyPolicySubtitle; + /// Message shown when liked posts dependencies are unavailable /// /// In en, this message translates to: @@ -2233,6 +2323,12 @@ abstract class AppLocalizations { /// **'Turn off non-essential motion effects'** String get messageTurnOffNonEssentialMotion; + /// Settings subtitle for the terms of service + /// + /// In en, this message translates to: + /// **'Usage rules and responsibilities'** + String get messageTermsOfServiceSubtitle; + /// Settings subtitle for video upload limits /// /// In en, this message translates to: diff --git a/lib/core/l10n/app_localizations_en.dart b/lib/core/l10n/app_localizations_en.dart index dcbf0a3..2aab1e9 100644 --- a/lib/core/l10n/app_localizations_en.dart +++ b/lib/core/l10n/app_localizations_en.dart @@ -256,6 +256,11 @@ class AppLocalizationsEn extends AppLocalizations { return 'Depth $depth'; } + @override + String formatFontSizeOption(String label, int size) { + return '$label ($size)'; + } + @override String formatLikedOn(String date) { return 'Liked on $date'; @@ -376,6 +381,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get labelAccount => 'Account'; + @override + String get labelAccountSettings => 'Account settings'; + @override String get labelAccountMaintenance => 'Account Maintenance'; @@ -433,6 +441,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get labelBluesky => 'Bluesky'; + @override + String get labelBlacksky => 'Blacksky'; + @override String get labelAlt => 'ALT'; @@ -445,6 +456,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get labelCleanFollows => 'Clean Follows'; + @override + String get labelCodeFont => 'Code Font'; + @override String get labelClose => 'Close'; @@ -460,6 +474,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get labelContentModeration => 'Content Moderation'; + @override + String get labelContentFont => 'Content Font'; + @override String get labelContinueSignIn => 'Continue sign in'; @@ -505,12 +522,27 @@ class AppLocalizationsEn extends AppLocalizations { @override String get labelForceNextXrpc401 => 'Force Next XRPC 401'; + @override + String get labelFontSize => 'Font Size'; + + @override + String get labelFontSizeSmall => 'Small'; + + @override + String get labelFontSizeNormal => 'Normal'; + + @override + String get labelFontSizeLarge => 'Large'; + @override String get labelGoOffline => 'Go Offline'; @override String get labelGuest => 'Guest'; + @override + String get labelHeadingFont => 'Heading Font'; + @override String get labelHealth => 'Health'; @@ -793,10 +825,19 @@ class AppLocalizationsEn extends AppLocalizations { @override String get labelVideoUploadLimits => 'Video Upload Limits'; + @override + String get messageAboutSubtitle => 'Stormlight Labs'; + + @override + String get messageAccountSettingsSubtitle => 'Feed display preferences and account defaults'; + @override String get messageAppPasswordGeneratedViaBluesky => 'Can be generated via Bluesky\'s App Passwords section at bsky.app.'; + @override + String get messageAtExplorerSubtitle => 'View PDS Records'; + @override String get messageAppViewDebug401Armed => 'Armed: next XRPC request will return debug 401 Unauthorized'; @@ -895,6 +936,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get messageManageSemanticSearchSubtitle => 'Manage semantic search from Bookmarks & Likes -> Search'; + @override + String get messageLogsSubtitle => 'View app log files'; + @override String get messageMetadataTemporarilyUnavailable => 'Metadata temporarily unavailable'; @@ -908,6 +952,9 @@ class AppLocalizationsEn extends AppLocalizations { String get messageProviderDiagnosticsSubtitle => 'Moderation/ranking can differ by provider. Verify health and recent fallback state.'; + @override + String get messagePrivacyPolicySubtitle => 'How Lazurite handles data'; + @override String get messageLikedPostsUnavailable => 'Liked posts are unavailable right now.'; @@ -1175,6 +1222,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get messageTurnOffNonEssentialMotion => 'Turn off non-essential motion effects'; + @override + String get messageTermsOfServiceSubtitle => 'Usage rules and responsibilities'; + @override String get messageVideoUploadLimitsSubtitle => 'Check your daily video quota'; diff --git a/lib/core/l10n/intl_en.arb b/lib/core/l10n/intl_en.arb index 034cce3..405e556 100644 --- a/lib/core/l10n/intl_en.arb +++ b/lib/core/l10n/intl_en.arb @@ -341,6 +341,20 @@ } } }, + "formatFontSizeOption": "{label} ({size})", + "@formatFontSizeOption": { + "description": "Settings font size dropdown option with display label and numeric point size", + "placeholders": { + "label": { + "type": "String", + "example": "Normal" + }, + "size": { + "type": "int", + "example": "16" + } + } + }, "formatLikedOn": "Liked on {date}", "@formatLikedOn": { "description": "Fallback liked-post card subtitle showing when a post was liked", @@ -563,6 +577,10 @@ "@labelAccount": { "description": "Account settings section label" }, + "labelAccountSettings": "Account settings", + "@labelAccountSettings": { + "description": "Settings item for account-level preferences" + }, "labelAccountMaintenance": "Account Maintenance", "@labelAccountMaintenance": { "description": "Settings section for account maintenance actions" @@ -639,6 +657,10 @@ "@labelBluesky": { "description": "Bluesky source tab label" }, + "labelBlacksky": "Blacksky", + "@labelBlacksky": { + "description": "Blacksky provider option label" + }, "labelAlt": "ALT", "@labelAlt": { "description": "Short all-caps label for media alt text controls" @@ -655,6 +677,10 @@ "@labelCleanFollows": { "description": "Settings item for follow audit feature" }, + "labelCodeFont": "Code Font", + "@labelCodeFont": { + "description": "Settings label for code font selection" + }, "labelClose": "Close", "@labelClose": { "description": "Close button tooltip" @@ -675,6 +701,10 @@ "@labelContentModeration": { "description": "Content moderation settings label" }, + "labelContentFont": "Content Font", + "@labelContentFont": { + "description": "Settings label for content font selection" + }, "labelContinueSignIn": "Continue sign in", "@labelContinueSignIn": { "description": "Semantic label for continuing sign in" @@ -735,6 +765,22 @@ "@labelForceNextXrpc401": { "description": "Developer setting to force the next XRPC request to return Unauthorized" }, + "labelFontSize": "Font Size", + "@labelFontSize": { + "description": "Settings label for content font size selection" + }, + "labelFontSizeSmall": "Small", + "@labelFontSizeSmall": { + "description": "Small content font size option label" + }, + "labelFontSizeNormal": "Normal", + "@labelFontSizeNormal": { + "description": "Normal content font size option label" + }, + "labelFontSizeLarge": "Large", + "@labelFontSizeLarge": { + "description": "Large content font size option label" + }, "labelGoOffline": "Go Offline", "@labelGoOffline": { "description": "Developer setting to simulate offline mode" @@ -743,6 +789,10 @@ "@labelGuest": { "description": "Fallback account display name when no user is signed in" }, + "labelHeadingFont": "Heading Font", + "@labelHeadingFont": { + "description": "Settings label for heading font selection" + }, "labelHealth": "Health", "@labelHealth": { "description": "Provider diagnostics health label" @@ -1119,10 +1169,22 @@ "@labelVideoUploadLimits": { "description": "Video upload limits settings label" }, + "messageAboutSubtitle": "Stormlight Labs", + "@messageAboutSubtitle": { + "description": "Settings subtitle for the about page" + }, + "messageAccountSettingsSubtitle": "Feed display preferences and account defaults", + "@messageAccountSettingsSubtitle": { + "description": "Settings subtitle for account-level preferences" + }, "messageAppPasswordGeneratedViaBluesky": "Can be generated via Bluesky''s App Passwords section at bsky.app.", "@messageAppPasswordGeneratedViaBluesky": { "description": "Debug app password help text" }, + "messageAtExplorerSubtitle": "View PDS Records", + "@messageAtExplorerSubtitle": { + "description": "Settings subtitle for AT Explorer" + }, "messageAppViewDebug401Armed": "Armed: next XRPC request will return debug 401 Unauthorized", "@messageAppViewDebug401Armed": { "description": "Snackbar message after arming debug unauthorized response" @@ -1253,6 +1315,10 @@ "@messageManageSemanticSearchSubtitle": { "description": "Settings subtitle for semantic search management" }, + "messageLogsSubtitle": "View app log files", + "@messageLogsSubtitle": { + "description": "Settings subtitle for opening app logs" + }, "messageMetadataTemporarilyUnavailable": "Metadata temporarily unavailable", "@messageMetadataTemporarilyUnavailable": { "description": "Trending banner shown when supplemental metadata cannot be loaded" @@ -1269,6 +1335,10 @@ "@messageProviderDiagnosticsSubtitle": { "description": "Settings subtitle for provider diagnostics" }, + "messagePrivacyPolicySubtitle": "How Lazurite handles data", + "@messagePrivacyPolicySubtitle": { + "description": "Settings subtitle for the privacy policy" + }, "messageLikedPostsUnavailable": "Liked posts are unavailable right now.", "@messageLikedPostsUnavailable": { "description": "Message shown when liked posts dependencies are unavailable" @@ -1617,6 +1687,10 @@ "@messageTurnOffNonEssentialMotion": { "description": "Settings subtitle for animations" }, + "messageTermsOfServiceSubtitle": "Usage rules and responsibilities", + "@messageTermsOfServiceSubtitle": { + "description": "Settings subtitle for the terms of service" + }, "messageVideoUploadLimitsSubtitle": "Check your daily video quota", "@messageVideoUploadLimitsSubtitle": { "description": "Settings subtitle for video upload limits" diff --git a/lib/features/settings/presentation/settings_screen.dart b/lib/features/settings/presentation/settings_screen.dart index 31574b4..e0702b4 100644 --- a/lib/features/settings/presentation/settings_screen.dart +++ b/lib/features/settings/presentation/settings_screen.dart @@ -114,8 +114,8 @@ class SettingsScreen extends StatelessWidget { const SizedBox(height: 12), SettingsTile( icon: Icons.manage_accounts_outlined, - title: 'Account settings', - subtitle: 'Feed display preferences and account defaults', + title: l10n.labelAccountSettings, + subtitle: l10n.messageAccountSettingsSubtitle, onTap: () => context.push('/settings/account'), ), SettingsTile( @@ -161,25 +161,25 @@ class SettingsScreen extends StatelessWidget { SettingsTile( icon: Icons.explore_outlined, title: l10n.labelAtExplorer, - subtitle: 'View PDS Records', + subtitle: l10n.messageAtExplorerSubtitle, onTap: () => context.push('/settings/devtools'), ), SettingsTile( icon: Icons.info_outline, title: l10n.labelAbout, - subtitle: 'Stormlight Labs', + subtitle: l10n.messageAboutSubtitle, onTap: () => context.push('/settings/about'), ), SettingsTile( icon: Icons.gavel_outlined, title: l10n.labelTermsOfService, - subtitle: 'Usage rules and responsibilities', + subtitle: l10n.messageTermsOfServiceSubtitle, onTap: () => context.push('/terms'), ), SettingsTile( icon: Icons.privacy_tip_outlined, title: l10n.labelPrivacyPolicy, - subtitle: 'How Lazurite handles data', + subtitle: l10n.messagePrivacyPolicySubtitle, onTap: () => context.push('/privacy'), ), if (showAccountSettings) ...[ @@ -258,7 +258,7 @@ class SettingsScreen extends StatelessWidget { ), const Divider(height: 1), SettingsDropdownTile( - title: 'Heading Font', + title: context.l10n.labelHeadingFont, value: state.headingFontFamily, options: AppHeadingFontFamily.values, labelBuilder: (fontFamily) => fontFamily.label, @@ -271,7 +271,7 @@ class SettingsScreen extends StatelessWidget { ), const Divider(height: 1), SettingsDropdownTile( - title: 'Content Font', + title: context.l10n.labelContentFont, value: state.contentFontFamily, options: AppContentFontFamily.values, labelBuilder: (fontFamily) => fontFamily.label, @@ -284,10 +284,10 @@ class SettingsScreen extends StatelessWidget { ), const Divider(height: 1), SettingsDropdownTile( - title: 'Font Size', + title: context.l10n.labelFontSize, value: state.contentFontSize, options: AppFontSize.values, - labelBuilder: (fontSize) => fontSize.label, + labelBuilder: (fontSize) => _fontSizeLabel(context, fontSize), optionBuilder: _fontSizeOption, onChanged: (value) { if (value != null) { @@ -297,7 +297,7 @@ class SettingsScreen extends StatelessWidget { ), const Divider(height: 1), SettingsDropdownTile( - title: 'Code Font', + title: context.l10n.labelCodeFont, value: state.codeFontFamily, options: AppCodeFontFamily.values, labelBuilder: (fontFamily) => fontFamily.label, @@ -328,7 +328,7 @@ class SettingsScreen extends StatelessWidget { ); Widget _fontSizeOption(BuildContext context, AppFontSize fontSize) => Text( - '${fontSize.label} (${fontSize.value.toInt()})', + context.l10n.formatFontSizeOption(_fontSizeLabel(context, fontSize), fontSize.value.toInt()), textAlign: TextAlign.right, style: AppTypography.content( context.fontTheme.contentFontFamily, @@ -337,6 +337,12 @@ class SettingsScreen extends StatelessWidget { ), ); + String _fontSizeLabel(BuildContext context, AppFontSize fontSize) => switch (fontSize) { + AppFontSize.small => context.l10n.labelFontSizeSmall, + AppFontSize.normal => context.l10n.labelFontSizeNormal, + AppFontSize.large => context.l10n.labelFontSizeLarge, + }; + Widget _codeFontOption(BuildContext context, AppCodeFontFamily fontFamily) => Text( fontFamily.label, textAlign: TextAlign.right, @@ -345,26 +351,23 @@ class SettingsScreen extends StatelessWidget { Widget _buildLayoutSettings(BuildContext context) { final settingsCubit = context.read(); - final theme = Theme.of(context); - final l10n = context.l10n; - return BlocBuilder( builder: (context, state) { return Column( mainAxisSize: MainAxisSize.min, children: [ - Divider(height: 1, color: theme.dividerColor), + Divider(height: 1, color: context.theme.dividerColor), Material( - color: theme.cardColor, + color: context.theme.cardColor, child: Column( children: [ SettingsDropdownTile( - title: l10n.labelFeedLayout, + title: context.l10n.labelFeedLayout, value: state.feedLayout, options: FeedLayout.values, labelBuilder: (layout) => switch (layout) { - FeedLayout.comfortable => l10n.messageFeedLayoutComfortable, - FeedLayout.compact => l10n.messageFeedLayoutCompact, + FeedLayout.comfortable => context.l10n.messageFeedLayoutComfortable, + FeedLayout.compact => context.l10n.messageFeedLayoutCompact, }, onChanged: (value) { if (value != null) { @@ -374,18 +377,18 @@ class SettingsScreen extends StatelessWidget { ), const Divider(height: 1), SettingsDropdownTile( - title: l10n.labelThreadAutoCollapse, - subtitle: l10n.messageThreadAutoCollapseSubtitle, + title: context.l10n.labelThreadAutoCollapse, + subtitle: context.l10n.messageThreadAutoCollapseSubtitle, value: state.threadAutoCollapseDepth, options: const [null, 1, 2, 3, 4, 5, 6], - labelBuilder: (depth) => depth == null ? l10n.commonOff : l10n.formatDepth(depth), + labelBuilder: (depth) => depth == null ? context.l10n.commonOff : context.l10n.formatDepth(depth), onChanged: settingsCubit.setThreadAutoCollapseDepth, ), const Divider(height: 1), SettingsTile( icon: Icons.motion_photos_off_outlined, - title: l10n.labelAnimations, - subtitle: l10n.messageTurnOffNonEssentialMotion, + title: context.l10n.labelAnimations, + subtitle: context.l10n.messageTurnOffNonEssentialMotion, trailing: Switch.adaptive( value: state.animationsEnabled, onChanged: settingsCubit.setAnimationsEnabled, @@ -394,7 +397,7 @@ class SettingsScreen extends StatelessWidget { ], ), ), - Divider(height: 1, color: theme.dividerColor), + Divider(height: 1, color: context.theme.dividerColor), ], ); }, @@ -430,7 +433,7 @@ class SettingsScreen extends StatelessWidget { alignment: Alignment.centerLeft, child: SegmentedButton( segments: [ - const ButtonSegment(value: 'bluesky', label: Text('Bluesky')), + ButtonSegment(value: 'bluesky', label: Text(context.l10n.labelBluesky)), ButtonSegment(value: 'community', label: Text(context.l10n.labelCommunity)), ], selected: {settingsState.typeaheadProvider}, @@ -538,8 +541,7 @@ class SettingsScreen extends StatelessWidget { SettingsTile( icon: Icons.description_outlined, title: context.l10n.labelLogs, - // TODO: l10n - subtitle: 'View app log files', + subtitle: context.l10n.messageLogsSubtitle, onTap: () => context.push('/settings/logs'), ), const Divider(height: 1), @@ -556,9 +558,15 @@ class SettingsScreen extends StatelessWidget { alignment: Alignment.centerLeft, child: SegmentedButton( key: const Key('appview-provider-segmented'), - segments: const [ - ButtonSegment(value: AppViewProviders.blueskyKey, label: Text('Bluesky')), - ButtonSegment(value: AppViewProviders.blackskyKey, label: Text('Blacksky')), + segments: [ + ButtonSegment( + value: AppViewProviders.blueskyKey, + label: Text(context.l10n.labelBluesky), + ), + ButtonSegment( + value: AppViewProviders.blackskyKey, + label: Text(context.l10n.labelBlacksky), + ), ], selected: {state.appViewProvider}, onSelectionChanged: (selection) async { @@ -611,7 +619,7 @@ class SettingsScreen extends StatelessWidget { ), ConnectionDetailRow( label: context.l10n.labelActiveProvider, - value: AppViewProviders.providerDisplayName(state.appViewProvider), + value: _appViewProviderLabel(context, state.appViewProvider), ), const Divider(height: 1), ConnectionDetailRow( @@ -660,10 +668,15 @@ class SettingsScreen extends StatelessWidget { } String _appViewSubtitle(BuildContext context, String providerKey) { - final provider = AppViewProviders.providerDisplayName(providerKey); + final provider = _appViewProviderLabel(context, providerKey); return context.l10n.formatAppViewProviderSelected(provider); } + String _appViewProviderLabel(BuildContext context, String providerKey) => switch (providerKey) { + AppViewProviders.blackskyKey => context.l10n.labelBlacksky, + _ => context.l10n.labelBluesky, + }; + Future _confirmAndApplyProviderChange(BuildContext context, String selectedProvider) async { final shouldApply = await showDialog( context: context, @@ -815,7 +828,7 @@ class _ModerationSettingsPreviewState extends State<_ModerationSettingsPreview> } } catch (error) { if (mounted) { - showAppSnackBar(context, 'Failed to update adult content: $error', isError: true); + showAppSnackBar(context, context.l10n.errorFailedToUpdateAdultContent(error), isError: true); } } finally { if (mounted) { diff --git a/test/features/settings/presentation/settings_screen_test.dart b/test/features/settings/presentation/settings_screen_test.dart index 5b80309..507bfc1 100644 --- a/test/features/settings/presentation/settings_screen_test.dart +++ b/test/features/settings/presentation/settings_screen_test.dart @@ -63,6 +63,7 @@ void main() { registerFallbackValue(AppHeadingFontFamily.lora); registerFallbackValue(AppContentFontFamily.googleSans); registerFallbackValue(AppCodeFontFamily.googleSansCode); + registerFallbackValue(AppFontSize.normal); }); setUp(() { @@ -110,6 +111,7 @@ void main() { when(() => settingsCubit.setHeadingFontFamily(any())).thenAnswer((_) async {}); when(() => settingsCubit.setContentFontFamily(any())).thenAnswer((_) async {}); when(() => settingsCubit.setCodeFontFamily(any())).thenAnswer((_) async {}); + when(() => settingsCubit.setContentFontSize(any())).thenAnswer((_) async {}); when(() => settingsCubit.refreshAppViewHealth()).thenAnswer((_) async {}); when(() => settingsCubit.setCrashReportingEnabled(any())).thenAnswer((_) async {}); when(() => settingsCubit.setCrashReportingConsentPrompted(any())).thenAnswer((_) async {}); @@ -241,6 +243,7 @@ void main() { final headingFinder = find.byType(DropdownButton); final contentFinder = find.byType(DropdownButton); final codeFinder = find.byType(DropdownButton); + final fontSizeFinder = find.byType(DropdownButton); final headingDropdown = tester.widget>(headingFinder); expect(headingDropdown.alignment, AlignmentDirectional.centerEnd); _expectRightAlignedSelectedFontLabel( @@ -257,6 +260,14 @@ void main() { ); contentDropdown.onChanged?.call(AppContentFontFamily.openSans); + final fontSizeDropdown = tester.widget>(fontSizeFinder); + expect(fontSizeDropdown.alignment, AlignmentDirectional.centerEnd); + final fontSizeItems = fontSizeDropdown.selectedItemBuilder!(tester.element(fontSizeFinder)); + _expectRightAlignedSelectedFontLabel(fontSizeItems[0], 'Small (14)'); + _expectRightAlignedSelectedFontLabel(fontSizeItems[1], 'Normal (16)'); + _expectRightAlignedSelectedFontLabel(fontSizeItems[2], 'Large (18)'); + fontSizeDropdown.onChanged?.call(AppFontSize.large); + final codeDropdown = tester.widget>(codeFinder); expect(codeDropdown.alignment, AlignmentDirectional.centerEnd); _expectRightAlignedSelectedFontLabel( @@ -267,6 +278,7 @@ void main() { verify(() => settingsCubit.setHeadingFontFamily(AppHeadingFontFamily.merriweather)).called(1); verify(() => settingsCubit.setContentFontFamily(AppContentFontFamily.openSans)).called(1); + verify(() => settingsCubit.setContentFontSize(AppFontSize.large)).called(1); verify(() => settingsCubit.setCodeFontFamily(AppCodeFontFamily.sourceCodePro)).called(1); });