134 lines
6.7 KiB
Dart
134 lines
6.7 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:jj_mkvmaker/core/file_ops.dart';
|
|
import 'package:jj_mkvmaker/core/text_codec.dart';
|
|
import 'package:jj_mkvmaker/ui/explorer_grid.dart';
|
|
import 'package:jj_mkvmaker/ui/text_viewer.dart';
|
|
import 'package:path/path.dart' as p;
|
|
|
|
/// X-plore 비교 g · h: 격자 보기 · 글 뷰어
|
|
void main() {
|
|
late Directory tmp;
|
|
setUp(() => tmp = Directory.systemTemp.createTempSync('jj_xptools_'));
|
|
tearDown(() => tmp.deleteSync(recursive: true));
|
|
|
|
testWidgets('h 글 뷰어: CP949 를 알아보고 나눠 읽기 [더 읽기]', (t) async {
|
|
final text = List.generate(40, (i) => '줄 $i 한글').join('\n');
|
|
final f = File(p.join(tmp.path, 'k.txt'))..writeAsBytesSync(encodeText(text, 'CP949').bytes);
|
|
await t.pumpWidget(MaterialApp(home: TextViewerPage(path: f.path, chunk: 120)));
|
|
for (var i = 0; i < 20 && find.textContaining('줄 0').evaluate().isEmpty; i++) {
|
|
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 30)));
|
|
await t.pump();
|
|
}
|
|
expect(find.text('줄 0 한글'), findsOneWidget);
|
|
expect(find.text('더 읽기'), findsOneWidget, reason: '큰 파일은 나눠 읽음');
|
|
for (var k = 0; k < 10 && find.text('더 읽기').evaluate().isNotEmpty; k++) {
|
|
await t.tap(find.text('더 읽기'));
|
|
for (var i = 0; i < 5; i++) {
|
|
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 30)));
|
|
await t.pump();
|
|
}
|
|
}
|
|
expect(find.text('더 읽기'), findsNothing, reason: '다 읽음');
|
|
expect(find.text('CP949 (EUC-KR)'), findsOneWidget);
|
|
});
|
|
|
|
test('h 글 뷰어: 아주 긴 줄만 나눠 그린다 (줄 번호는 첫 조각에만, 이모지를 반으로 자르지 않음)', () {
|
|
expect(viewerRows('a\nbb', 10), [(1, 'a'), (2, 'bb')]);
|
|
final long = 'x' * 1000;
|
|
final rows = viewerRows('first\n$long\nlast', 10);
|
|
expect(rows.first, (1, 'first'));
|
|
expect(rows.last, (3, 'last'));
|
|
final mid = rows.sublist(1, rows.length - 1);
|
|
expect(mid.map((r) => r.$2).join(), long, reason: '글은 그대로 (빠지거나 겹치지 않음)');
|
|
expect(mid.first.$1, 2);
|
|
expect(mid.skip(1).every((r) => r.$1 == 0), isTrue);
|
|
expect(mid.every((r) => r.$2.length <= 40), isTrue, reason: '화면 네 줄쯤씩');
|
|
expect(viewerRows('y' * 80, 10).length, 1, reason: '8배까지는 그대로 (화면에서 줄바꿈)');
|
|
// 나누는 자리에 두 단위 글자 (😀) 가 걸리면 앞으로 당긴다
|
|
final emoji = '${'z' * 39}😀${'z' * 100}';
|
|
final cut = viewerRows(emoji, 10);
|
|
expect(cut.map((r) => r.$2).join(), emoji);
|
|
expect(cut.every((r) => !r.$2.endsWith('\uD83D')), isTrue);
|
|
});
|
|
|
|
testWidgets('h 글 뷰어: 줄바꿈 없는 큰 파일 (200KB 한 줄) 도 나눠 그려 멈추지 않는다', (t) async {
|
|
final f = File(p.join(tmp.path, 'one.txt'))..writeAsStringSync('${'0123456789' * 20000}END');
|
|
t.view.physicalSize = const Size(412, 900);
|
|
t.view.devicePixelRatio = 1;
|
|
addTearDown(t.view.reset);
|
|
await t.pumpWidget(MaterialApp(home: TextViewerPage(path: f.path)));
|
|
for (var i = 0; i < 40 && find.textContaining('0123456789').evaluate().isEmpty; i++) {
|
|
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 30)));
|
|
await t.pump();
|
|
}
|
|
final texts = t.widgetList<Text>(find.textContaining('0123456789')).toList();
|
|
expect(texts.length, greaterThan(1), reason: '한 줄을 여러 조각으로');
|
|
expect(texts.every((x) => (x.data ?? '').length < 2000), isTrue, reason: '조각마다 짧게');
|
|
expect(find.text('1'), findsOneWidget, reason: '줄 번호는 첫 조각에만');
|
|
});
|
|
|
|
testWidgets('h 글 뷰어 (28 지적 · 6366a2f 회귀): 긴 줄을 나눠 그려도 Ctrl+A → Ctrl+C 는 줄 전체 · 막대 컨트롤러', (t) async {
|
|
final line = List.generate(1109, (i) => 'abcdefghi${i % 10}').join();
|
|
final f = File(p.join(tmp.path, 'long.txt'))..writeAsStringSync('$line\nend');
|
|
t.view.physicalSize = const Size(412, 900);
|
|
t.view.devicePixelRatio = 1;
|
|
addTearDown(t.view.reset);
|
|
String? copied;
|
|
t.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, (call) async {
|
|
if (call.method == 'Clipboard.setData') copied = (call.arguments as Map)['text'] as String?;
|
|
return null;
|
|
});
|
|
addTearDown(() => t.binding.defaultBinaryMessenger.setMockMethodCallHandler(SystemChannels.platform, null));
|
|
await t.pumpWidget(MaterialApp(home: TextViewerPage(path: f.path)));
|
|
for (var i = 0; i < 40 && find.textContaining('abcdefghi0').evaluate().isEmpty; i++) {
|
|
await t.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 30)));
|
|
await t.pump();
|
|
}
|
|
expect(t.widgetList<Text>(find.textContaining('abcdefghi')).length, greaterThan(1), reason: '화면에는 나눠 그림');
|
|
await t.tapAt(const Offset(150, 140));
|
|
await t.pump();
|
|
await t.sendKeyDownEvent(LogicalKeyboardKey.controlLeft);
|
|
await t.sendKeyEvent(LogicalKeyboardKey.keyA);
|
|
await t.sendKeyEvent(LogicalKeyboardKey.keyC);
|
|
await t.sendKeyUpEvent(LogicalKeyboardKey.controlLeft);
|
|
await t.pump();
|
|
expect(copied, isNotNull);
|
|
expect(copied!.length, line.length + 4, reason: '줄 전체 + "\nend" (보이는 조각만이 아님)');
|
|
expect(copied, '$line\nend');
|
|
// 막대에 컨트롤러가 붙어 있다 (데스크톱에서 ScrollPosition 없음 assert 가 나지 않게)
|
|
final bar = t.widget<Scrollbar>(find.byType(Scrollbar));
|
|
expect(bar.controller, isNotNull);
|
|
expect(bar.controller!.hasClients, isTrue);
|
|
});
|
|
|
|
testWidgets('g 격자 보기: 그림 · 동영상만, 누르면 열기', (t) async {
|
|
final png = File(p.join(tmp.path, 'a.png'))..writeAsBytesSync([0x89, 0x50]);
|
|
final opened = <String>[];
|
|
final entries = [
|
|
FileEntry(png.path, isDir: false, size: 2, modified: DateTime(2026)),
|
|
FileEntry(p.join(tmp.path, 'b.mkv'), isDir: false, size: 2, modified: DateTime(2026)),
|
|
FileEntry(p.join(tmp.path, 'c.txt'), isDir: false, size: 2, modified: DateTime(2026)),
|
|
FileEntry(p.join(tmp.path, 'dir'), isDir: true, modified: DateTime(2026)),
|
|
];
|
|
expect(ExplorerGridPage.pick(entries, const ['png']).map((e) => e.name), ['a.png', 'b.mkv']);
|
|
await t.pumpWidget(MaterialApp(
|
|
home: ExplorerGridPage(
|
|
title: 'T',
|
|
entries: entries,
|
|
imageExts: const ['png'],
|
|
videoThumb: (_) async => null,
|
|
onOpen: (e) async => opened.add(e.name),
|
|
),
|
|
));
|
|
await t.pump();
|
|
expect(find.text('a.png'), findsOneWidget);
|
|
expect(find.text('c.txt'), findsNothing);
|
|
await t.tap(find.text('b.mkv'));
|
|
expect(opened, ['b.mkv']);
|
|
});
|
|
}
|