diff --git a/integration_test/app_backend_test.dart b/integration_test/app_backend_test.dart new file mode 100644 index 0000000..a2fa765 --- /dev/null +++ b/integration_test/app_backend_test.dart @@ -0,0 +1,74 @@ +// Author: fruit.com.cn + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:fruitshorts/app.dart'; +import 'package:fruitshorts/core/config/app_config.dart'; +import 'package:fruitshorts/core/data/remote/api_client.dart'; +import 'package:fruitshorts/core/data/remote/catalog_api.dart'; +import 'package:integration_test/integration_test.dart'; + +/// 设备侧集成测试:**在手机进程里**真实请求开发机上的 app-api。 +/// +/// 跑法(红线第 10 条要求的"设备侧取证"): +/// ``` +/// IP=$(ipconfig getifaddr en0) +/// flutter test integration_test -d --dart-define=API_BASE_URL=http://$IP:8081 +/// ``` +/// 断言失败时会把实际使用的地址一起打出来,便于区分"地址不对"还是"设备连不上"。 +void main() { + IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + final baseUrl = AppConfig.apiBaseUrl; + + testWidgets('should_fetch_home_feed_and_drama_detail_when_device_reaches_backend', ( + tester, + ) async { + debugPrint('[device] API_BASE_URL=$baseUrl'); + final client = ApiClient(baseUrl: baseUrl); + addTearDown(client.close); + final api = CatalogApi(client); + + final feed = await api.homeFeed(); + expect( + feed.dramas, + isNotEmpty, + reason: '设备侧拿不到首页列表(API_BASE_URL=$baseUrl)', + ); + debugPrint('[device] 首页分区=${feed.sections.length} 剧目=${feed.dramas.length}'); + + final card = feed.dramas.first; + expect(card.id, isNotEmpty, reason: '设备侧首页卡片 id 为空'); + + final detail = await api.dramaDetail(card.id); + expect(detail.id, card.id, reason: '详情返回的 id 与卡片不一致'); + expect( + detail.episodes, + isNotEmpty, + reason: '设备侧拿到的详情没有剧集(dramaId=${card.id})', + ); + debugPrint('[device] 详情 id=${detail.id} 剧集=${detail.episodes.length}'); + }); + + testWidgets('should_render_home_drama_list_when_app_boots_on_device', ( + tester, + ) async { + // 用真 App(真网络、真目录控制器):首页底部标签栏出现即代表"剧目列表加载成功", + // 也就是用户在真机上遇到的那个「剧目列表加载失败」已经消失。 + await tester.pumpWidget(const LumaDramaApp()); + + var loaded = false; + for (var attempt = 0; attempt < 60 && !loaded; attempt++) { + await tester.pump(const Duration(milliseconds: 500)); + loaded = find.byKey(const Key('home-tabs-bar')).evaluate().isNotEmpty; + } + + expect( + loaded, + isTrue, + reason: '首页 30 秒内没渲染出剧目列表(API_BASE_URL=$baseUrl)', + ); + debugPrint('[device] 首页剧目列表已渲染:API_BASE_URL=$baseUrl'); + }); +} diff --git a/pubspec.lock b/pubspec.lock index 98d77df..7abd14d 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -134,6 +134,11 @@ packages: description: flutter source: sdk version: "0.0.0" + flutter_driver: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" flutter_launcher_icons: dependency: "direct dev" description: @@ -165,6 +170,11 @@ packages: description: flutter source: sdk version: "0.0.0" + fuchsia_remote_debug_protocol: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" html: dependency: transitive description: @@ -189,6 +199,11 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "4.12.0" + integration_test: + dependency: "direct dev" + description: flutter + source: sdk + version: "0.0.0" intl: dependency: transitive description: @@ -333,6 +348,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "6.5.2" + process: + dependency: transitive + description: + name: process + sha256: "4242ba3508d37e01808bdf71ad1d5bb93a8d671bf2e7450e6b1b353fb0808891" + url: "https://pub.flutter-io.cn" + source: hosted + version: "5.0.6" provider: dependency: "direct main" description: @@ -434,6 +457,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" + sync_http: + dependency: transitive + description: + name: sync_http + sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961" + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.3.1" term_glyph: dependency: transitive description: @@ -522,6 +553,14 @@ packages: url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" + webdriver: + dependency: transitive + description: + name: webdriver + sha256: "28b82ec894fed45dd71c23ba62d1af973ed97dd59a4f5790a4d38b0b13e5657e" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.2.0" xdg_directories: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 7f4fd45..4fe8149 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -46,6 +46,11 @@ dev_dependencies: flutter_test: sdk: flutter + # 设备侧集成测试:真机/模拟器进程里真实发请求,验证"设备能不能连到开发机后端" + # (红线第 10 条:设备验证必须设备侧取证,不能用开发机 curl 充数) + integration_test: + sdk: flutter + # The "flutter_lints" package below contains a set of recommended lints to # encourage good coding practices. The lint set provided by the package is # activated in the `analysis_options.yaml` file located at the root of your