import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:jj_mkvmaker/ui/jj_title_bar.dart'; import 'package:jj_mkvmaker/ui/tail_text.dart'; /// W 공용 위젯 약속 (JjTitleBar): 제목은 한 줄 (넘치면 "…", 경로는 끝이 보이게) · 좁으면 뒤 단추들을 둘째 줄로 (숨기지 않음) · /// 300 · 412 폭 · 글자 배율 2 에서 넘치지 않음. 위젯 하나에 대해 한 번만 둔다 (설계 7-2) void main() { const long = '휴지통 · 1234개 · 56.7 GB - 아주 긴 제목이 이어져 한 줄에 다 들어가지 않는 경우'; Future show(WidgetTester t, PreferredSizeWidget Function(BuildContext) bar, {double width = 412, double scale = 1}) async { t.view.physicalSize = Size(width, 800); t.view.devicePixelRatio = 1; addTearDown(t.view.reset); await t.pumpWidget(MaterialApp( home: MediaQuery( data: MediaQueryData(size: Size(width, 800), textScaler: TextScaler.linear(scale)), child: Builder(builder: (ctx) => Scaffold(appBar: bar(ctx), body: const SizedBox.expand())), ), )); } List wide() => [ OutlinedButton.icon(onPressed: () {}, icon: const Icon(Icons.create_new_folder_outlined), label: const Text('새 폴더')), FilledButton(onPressed: () {}, child: const Text('이 폴더 고르기')), IconButton(tooltip: '보이기', icon: const Icon(Icons.visibility_off_outlined), onPressed: () {}), ]; for (final width in [300.0, 412.0]) { for (final scale in [1.0, 2.0]) { testWidgets('폭 $width · 배율 $scale: 긴 제목 한 줄 · 넘치지 않음', (t) async { await show(t, (ctx) => jjTitleBar(ctx, title: long, actions: [IconButton(icon: const Icon(Icons.refresh), onPressed: () {})]), width: width, scale: scale); expect(t.takeException(), isNull); final text = t.widget(find.text(long)); expect([text.maxLines, text.overflow], [1, TextOverflow.ellipsis]); expect(t.getRect(find.byIcon(Icons.refresh)).right, lessThanOrEqualTo(width)); }); testWidgets('폭 $width · 배율 $scale: 넓은 단추는 좁으면 둘째 줄 - 모두 보이고 넘치지 않음', (t) async { await show(t, (ctx) => jjTitleBar(ctx, title: long, lowerActions: wide(), actions: [IconButton(icon: const Icon(Icons.save), onPressed: () {})], twoLinesBelow: 480), width: width, scale: scale); expect(t.takeException(), isNull); final title = t.getRect(find.text(long)); for (final f in [find.text('새 폴더'), find.text('이 폴더 고르기'), find.byIcon(Icons.visibility_off_outlined)]) { expect(f, findsOneWidget); final r = t.getRect(f); expect(r.top, greaterThan(title.bottom), reason: '제목 아래 줄'); expect([r.left >= 0, r.right <= width], [true, true], reason: '화면 안에 다 보임'); } expect(t.getRect(find.byIcon(Icons.save)).bottom, lessThanOrEqualTo(title.bottom + 20), reason: '[actions] 는 위 줄에 남음'); }); } } testWidgets('넓은 화면에서는 한 줄 (둘째 줄 없음)', (t) async { await show(t, (ctx) => jjTitleBar(ctx, title: '제목', lowerActions: wide(), twoLinesBelow: 480), width: 960); expect(t.takeException(), isNull); expect((t.getRect(find.text('새 폴더')).center.dy - t.getRect(find.text('제목')).center.dy).abs(), lessThan(4)); }); testWidgets('경로 제목은 끝 (지금 폴더) 이 보임', (t) async { const path = 'NAS / 동영상 / 2026 / 여름 휴가 / 바다에서 찍은 것'; await show(t, (ctx) => jjTitleBar(ctx, title: path, path: true), width: 300); expect(t.takeException(), isNull); expect(find.byType(TailText), findsOneWidget); expect(find.textContaining('바다에서 찍은 것'), findsOneWidget); }); }