Conversation
…nouncement_ui - flutter_core: ApIcon (filled/outlined), ApiResult.isSuccess extension, DioException i18n extensions (isJsonResponse, falconMessage) - flutter_ui: Toast constants and ToastWidget positioning, ApTheme constants and updateShouldNotify logic - flutter_platform: ApNotificationUtil (getDay, parseTime, getNextWeekdayDateTime), ApPreferenceUtil (all type get/set, encryption, key operations) - announcement_ui: ImgurHelper and ImgbbHelper upload tests with ApiResult pattern (success, error, failure cases) Total new tests: 129 (across 9 test files) Closes #150
|
Failed to generate code suggestions for PR |
There was a problem hiding this comment.
Code Review
This pull request introduces a comprehensive suite of unit and widget tests across several packages, including ap_common_announcement_ui, ap_common_flutter_core, ap_common_flutter_platform, and ap_common_flutter_ui. The new tests cover API helpers for image uploads, localization extensions for error handling, UI components like icons and themes, and utility classes for notifications and preferences. The review feedback suggests improving test assertions to verify specific error details, adding missing test cases for toast positioning, using a clock package to prevent flaky time-based tests, and adopting a mocking framework to simplify manual mock implementations.
| }); | ||
| }); | ||
|
|
||
| group('uploadImage', () { |
There was a problem hiding this comment.
這組測試中的錯誤情境(returns ApiError on non-200 status 和 returns ApiError on 400 bad request)只驗證了回傳型別為 ApiError,但沒有檢查錯誤內容。這會讓一些潛在的 bug 溜走。
例如,ImgurHelper 的實作在收到 400 錯誤時,回傳的 ApiError 中 statusCode 卻是 201,這顯然是個錯誤。
建議讓測試更具體,去驗證 ApiError 內的 statusCode 和 message,以確保錯誤處理的正確性。
範例如下:
test('returns ApiError on 400 bad request', () async {
// ...
final result = await helper.uploadImage(...);
expect(result, isA<ApiError<String>>());
final error = result as ApiError<String>;
expect(error.response.statusCode, 400); // 這會捕捉到目前的 bug
});| }); | ||
| }); | ||
|
|
||
| group('ToastWidget', () { |
| test('should return a future date', () { | ||
| const TimeOfDay time = TimeOfDay(hour: 0, minute: 0); | ||
| final DateTime result = | ||
| util.getNextWeekdayDateTime(Day.monday, time); | ||
| // Result should be within 7 days from now | ||
| final DateTime now = DateTime.now(); | ||
| expect( | ||
| result.difference(now).inDays, | ||
| lessThanOrEqualTo(7), | ||
| ); | ||
| }); |
There was a problem hiding this comment.
should return a future date 這個測試案例依賴於 DateTime.now(),這會導致測試結果不確定,可能變得不穩定。建議使用 clock 套件來固定測試時間,這樣就能對回傳的 DateTime 進行精確的斷言。
例如:
import 'package:clock/clock.dart';
test('should return next Monday if today is Wednesday', () {
// A Wednesday
withClock(Clock.fixed(DateTime(2023, 10, 25, 10, 0)), () {
const time = TimeOfDay(hour: 14, minute: 30);
final result = util.getNextWeekdayDateTime(Day.monday, time);
// Next Monday
expect(result, DateTime(2023, 10, 30, 14, 30));
});
});| class _MockPreferenceUtil extends PreferenceUtil { | ||
| final Map<String, dynamic> _store = <String, dynamic>{}; | ||
|
|
||
| @override | ||
| bool get isSupport => true; | ||
|
|
||
| @override | ||
| Future<void> setString(String key, String data) async => | ||
| _store[key] = data; | ||
|
|
||
| @override | ||
| String getString(String key, String defaultValue) => | ||
| (_store[key] as String?) ?? defaultValue; | ||
|
|
||
| @override | ||
| Future<void> setStringSecurity(String key, String data) async => | ||
| _store[key] = data; | ||
|
|
||
| @override | ||
| String getStringSecurity(String key, String defaultValue) => | ||
| (_store[key] as String?) ?? defaultValue; | ||
|
|
||
| @override | ||
| Future<void> setInt(String key, int data) async => | ||
| _store[key] = data; | ||
|
|
||
| @override | ||
| int getInt(String key, int defaultValue) => | ||
| (_store[key] as int?) ?? defaultValue; | ||
|
|
||
| @override | ||
| Future<void> setDouble(String key, double data) async => | ||
| _store[key] = data; | ||
|
|
||
| @override | ||
| double getDouble(String key, double defaultValue) => | ||
| (_store[key] as double?) ?? defaultValue; | ||
|
|
||
| @override | ||
| Future<void> setBool(String key, bool data) async => | ||
| _store[key] = data; | ||
|
|
||
| @override | ||
| bool getBool(String key, bool defaultValue) => | ||
| (_store[key] as bool?) ?? defaultValue; | ||
|
|
||
| @override | ||
| Future<void> setStringList(String key, List<String> data) async => | ||
| _store[key] = data; | ||
|
|
||
| @override | ||
| List<String> getStringList(String key, List<String> defaultValue) => | ||
| (_store[key] as List<String>?) ?? defaultValue; | ||
|
|
||
| @override | ||
| Set<String> getKeys() => _store.keys.toSet(); | ||
|
|
||
| @override | ||
| Future<bool>? remove(String key) { | ||
| _store.remove(key); | ||
| return Future<bool>.value(true); | ||
| } | ||
| } |
|
Visit the preview URL for this PR (updated for commit 544327a): https://ap-common--example-preview-z8c3pggl.web.app (expires Tue, 07 Apr 2026 14:04:39 GMT) 🔥 via Firebase Hosting GitHub Action 🌎 Sign: 61ecbaa5d4e2786c745a5e21d2a66c533b7f072b |
…on utils Replace DateTime.now() with clock.now() in getNextWeekdayDateTime to enable deterministic testing. Update tests to use withClock for precise date assertions instead of relying on current time.
摘要
為 4 個原本無測試的套件新增共 9 個測試檔案、129 個測試案例:
ApIcon:filled/outlined 兩種模式下 40+ icon 屬性的回傳值、index 計算ApiResult.isSuccess:擴充方法的三種狀態判斷DioExceptioni18n 擴充:isJsonResponse、falconMessage邏輯Toast常數驗證、ToastWidget定位邏輯(top/bottom)ApTheme常數、ThemeColor資料類別、updateShouldNotify變更偵測ApNotificationUtil:getDay星期轉換、parseTime時間解析與提前分鐘數、getNextWeekdayDateTime日期計算ApPreferenceUtil:String/Int/Double/Bool/StringList 的 get/set、AES 加解密、key 操作ImgurHelper、ImgbbHelper圖片上傳:成功/錯誤/網路失敗的 ApiResult 回傳測試計畫
melos run analyze— 9 個套件全數通過Closes #150