diff --git a/.gitignore b/.gitignore index 8ecdb7f..dd0d9fa 100644 --- a/.gitignore +++ b/.gitignore @@ -122,3 +122,4 @@ docs/.* .vscode/tasks.json .vscode/launch.json devtools_options.yaml +test/features/*_test.dart diff --git a/build.yaml b/build.yaml index 4c29746..0ecf930 100644 --- a/build.yaml +++ b/build.yaml @@ -6,6 +6,6 @@ targets: - lib/** - $package$ builders: - pdf_signature|prune_unused_steps: + pdf_signature: generate_for: - test/features/** diff --git a/test/features/app_preferences.feature b/test/features/app_preferences.feature new file mode 100644 index 0000000..103c83a --- /dev/null +++ b/test/features/app_preferences.feature @@ -0,0 +1,61 @@ +Feature: App preferences + + As a user + I want to configure app preferences such as theme and language + So that the app matches my personal and regional needs, and remembers them next time + + Scenario Outline: Choose a theme and apply it immediately + Given the settings screen is open + When the user selects the "" theme + Then the app UI updates to use the "" theme + And the preference {theme} is saved as {""} + + Examples: + | theme | + | light | + | dark | + | system | + + Scenario Outline: Choose a language and apply it immediately + Given the settings screen is open + When the user selects a supported language "" + Then all visible texts are displayed in "" + And the preference {language} is saved as {""} + + Examples: + | language | + | en | + | zh-TW | + | es | + + Scenario Outline: Remember preferences across app restarts + Given the user previously set theme {""} and language {""} + When the app launches + Then the app UI theme is {""} + And the app language is {""} + + Examples: + | theme | language | + | dark | en | + | light | zh-TW | + | system | es | + + Scenario: Follow system appearance when theme is set to system + Given the user selects the "system" theme + And the OS appearance switches to dark mode + When the app is resumed or returns to foreground + Then the app UI updates to use the "dark" theme + + Scenario: Reset preferences to defaults + Given the user has theme {"dark"} and language {"es"} saved + When the user taps "Reset to defaults" + Then the theme is set to {"system"} + And the language is set to the device locale + And both preferences are saved + + Scenario: Ignore invalid stored values and fall back safely + Given stored preferences contain theme {"sepia"} and language {"xx"} + When the app launches + Then the theme falls back to {"system"} + And the language falls back to the device locale + And invalid values are replaced with valid defaults in storage diff --git a/test/features/step/all_visible_texts_are_displayed_in.dart b/test/features/step/all_visible_texts_are_displayed_in.dart new file mode 100644 index 0000000..92f9a47 --- /dev/null +++ b/test/features/step/all_visible_texts_are_displayed_in.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: all visible texts are displayed in "" +Future allVisibleTextsAreDisplayedIn( + WidgetTester tester, dynamic language) async { + throw UnimplementedError(); +} diff --git a/test/features/step/both_preferences_are_saved.dart b/test/features/step/both_preferences_are_saved.dart new file mode 100644 index 0000000..cf6c1d5 --- /dev/null +++ b/test/features/step/both_preferences_are_saved.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: both preferences are saved +Future bothPreferencesAreSaved(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/invalid_values_are_replaced_with_valid_defaults_in_storage.dart b/test/features/step/invalid_values_are_replaced_with_valid_defaults_in_storage.dart new file mode 100644 index 0000000..5e8def0 --- /dev/null +++ b/test/features/step/invalid_values_are_replaced_with_valid_defaults_in_storage.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: invalid values are replaced with valid defaults in storage +Future invalidValuesAreReplacedWithValidDefaultsInStorage( + WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/stored_preferences_contain_theme_and_language.dart b/test/features/step/stored_preferences_contain_theme_and_language.dart new file mode 100644 index 0000000..64f1000 --- /dev/null +++ b/test/features/step/stored_preferences_contain_theme_and_language.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: stored preferences contain theme {"sepia"} and language {"xx"} +Future storedPreferencesContainThemeAndLanguage( + WidgetTester tester, String param1, String param2) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_app_is_resumed_or_returns_to_foreground.dart b/test/features/step/the_app_is_resumed_or_returns_to_foreground.dart new file mode 100644 index 0000000..2e12143 --- /dev/null +++ b/test/features/step/the_app_is_resumed_or_returns_to_foreground.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the app is resumed or returns to foreground +Future theAppIsResumedOrReturnsToForeground(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_app_language_is.dart b/test/features/step/the_app_language_is.dart new file mode 100644 index 0000000..7f59382 --- /dev/null +++ b/test/features/step/the_app_language_is.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the app language is {""} +Future theAppLanguageIs( + WidgetTester tester, String param1, dynamic language) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_app_launches.dart b/test/features/step/the_app_launches.dart new file mode 100644 index 0000000..99e5147 --- /dev/null +++ b/test/features/step/the_app_launches.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the app launches +Future theAppLaunches(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_app_ui_theme_is.dart b/test/features/step/the_app_ui_theme_is.dart new file mode 100644 index 0000000..ea2a26e --- /dev/null +++ b/test/features/step/the_app_ui_theme_is.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the app UI theme is {""} +Future theAppUiThemeIs( + WidgetTester tester, String param1, dynamic theme) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_app_ui_updates_to_use_the_dark_theme.dart b/test/features/step/the_app_ui_updates_to_use_the_dark_theme.dart new file mode 100644 index 0000000..0fac2b8 --- /dev/null +++ b/test/features/step/the_app_ui_updates_to_use_the_dark_theme.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the app UI updates to use the "dark" theme +Future theAppUiUpdatesToUseTheDarkTheme(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_app_ui_updates_to_use_the_theme.dart b/test/features/step/the_app_ui_updates_to_use_the_theme.dart new file mode 100644 index 0000000..7396f98 --- /dev/null +++ b/test/features/step/the_app_ui_updates_to_use_the_theme.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the app UI updates to use the "" theme +Future theAppUiUpdatesToUseTheTheme( + WidgetTester tester, dynamic theme) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_language_falls_back_to_the_device_locale.dart b/test/features/step/the_language_falls_back_to_the_device_locale.dart new file mode 100644 index 0000000..40abade --- /dev/null +++ b/test/features/step/the_language_falls_back_to_the_device_locale.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the language falls back to the device locale +Future theLanguageFallsBackToTheDeviceLocale(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_language_is_set_to_the_device_locale.dart b/test/features/step/the_language_is_set_to_the_device_locale.dart new file mode 100644 index 0000000..6f4977a --- /dev/null +++ b/test/features/step/the_language_is_set_to_the_device_locale.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the language is set to the device locale +Future theLanguageIsSetToTheDeviceLocale(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_os_appearance_switches_to_dark_mode.dart b/test/features/step/the_os_appearance_switches_to_dark_mode.dart new file mode 100644 index 0000000..c367885 --- /dev/null +++ b/test/features/step/the_os_appearance_switches_to_dark_mode.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the OS appearance switches to dark mode +Future theOsAppearanceSwitchesToDarkMode(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_preference_is_saved_as.dart b/test/features/step/the_preference_is_saved_as.dart new file mode 100644 index 0000000..fa0133c --- /dev/null +++ b/test/features/step/the_preference_is_saved_as.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the preference {language} is saved as {""} +Future thePreferenceIsSavedAs(WidgetTester tester, dynamic param1, + String param2, dynamic language) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_settings_screen_is_open.dart b/test/features/step/the_settings_screen_is_open.dart new file mode 100644 index 0000000..6f6d0bd --- /dev/null +++ b/test/features/step/the_settings_screen_is_open.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the settings screen is open +Future theSettingsScreenIsOpen(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_theme_falls_back_to.dart b/test/features/step/the_theme_falls_back_to.dart new file mode 100644 index 0000000..eeb9b46 --- /dev/null +++ b/test/features/step/the_theme_falls_back_to.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the theme falls back to {"system"} +Future theThemeFallsBackTo(WidgetTester tester, String param1) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_theme_is_set_to.dart b/test/features/step/the_theme_is_set_to.dart new file mode 100644 index 0000000..3608b12 --- /dev/null +++ b/test/features/step/the_theme_is_set_to.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the theme is set to {"system"} +Future theThemeIsSetTo(WidgetTester tester, String param1) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_user_has_theme_and_language_saved.dart b/test/features/step/the_user_has_theme_and_language_saved.dart new file mode 100644 index 0000000..185e035 --- /dev/null +++ b/test/features/step/the_user_has_theme_and_language_saved.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the user has theme {"dark"} and language {"es"} saved +Future theUserHasThemeAndLanguageSaved( + WidgetTester tester, String param1, String param2) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_user_previously_set_theme_and_language.dart b/test/features/step/the_user_previously_set_theme_and_language.dart new file mode 100644 index 0000000..f28337c --- /dev/null +++ b/test/features/step/the_user_previously_set_theme_and_language.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the user previously set theme {""} and language {""} +Future theUserPreviouslySetThemeAndLanguage(WidgetTester tester, + String param1, String param2, dynamic theme, dynamic language) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_user_selects_a_supported_language.dart b/test/features/step/the_user_selects_a_supported_language.dart new file mode 100644 index 0000000..0778de0 --- /dev/null +++ b/test/features/step/the_user_selects_a_supported_language.dart @@ -0,0 +1,7 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the user selects a supported language "" +Future theUserSelectsASupportedLanguage( + WidgetTester tester, dynamic language) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_user_selects_the_system_theme.dart b/test/features/step/the_user_selects_the_system_theme.dart new file mode 100644 index 0000000..433afdb --- /dev/null +++ b/test/features/step/the_user_selects_the_system_theme.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the user selects the "system" theme +Future theUserSelectsTheSystemTheme(WidgetTester tester) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_user_selects_the_theme.dart b/test/features/step/the_user_selects_the_theme.dart new file mode 100644 index 0000000..342f7d9 --- /dev/null +++ b/test/features/step/the_user_selects_the_theme.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the user selects the "" theme +Future theUserSelectsTheTheme(WidgetTester tester, dynamic theme) async { + throw UnimplementedError(); +} diff --git a/test/features/step/the_user_taps_reset_to_defaults.dart b/test/features/step/the_user_taps_reset_to_defaults.dart new file mode 100644 index 0000000..29bf9a8 --- /dev/null +++ b/test/features/step/the_user_taps_reset_to_defaults.dart @@ -0,0 +1,6 @@ +import 'package:flutter_test/flutter_test.dart'; + +/// Usage: the user taps "Reset to defaults" +Future theUserTapsResetToDefaults(WidgetTester tester) async { + throw UnimplementedError(); +}