162 lines
7.9 KiB
Dart
162 lines
7.9 KiB
Dart
import 'dart:io';
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:jj_mkvmaker/core/vault_store.dart';
|
||
import 'package:jj_mkvmaker/core/vault_view.dart';
|
||
import 'package:jj_mkvmaker/ui/vault/vault_session.dart';
|
||
import 'package:jj_mkvmaker/ui/vault/vault_views.dart';
|
||
import 'package:path/path.dart' as p;
|
||
|
||
/// 보관함 나누기 (vault_page 1,095줄 → lib/ui/vault/) 의 모듈 약속 시험. 실제 시험 폴더 · 실제 보관함 (PBKDF2 1000번) 만 쓴다.
|
||
/// 화면 전체 흐름 (만들기 · 열기 · 넣기 · 보기 · 꺼내기 · 폴더 · 열쇠) 은 vault_page_test 가 본다
|
||
void main() {
|
||
TestWidgetsFlutterBinding.ensureInitialized();
|
||
late Directory tmp;
|
||
final wasView = vaultViewRootOf;
|
||
setUp(() {
|
||
tmp = Directory.systemTemp.createTempSync('jj_vaultmod_');
|
||
vaultViewRootOf = () async => Directory(p.join(tmp.path, 'view'));
|
||
});
|
||
tearDown(() async {
|
||
vaultViewRootOf = wasView;
|
||
await removeTempDirRetry(tmp);
|
||
});
|
||
|
||
test('vault_session (상태): 폴더 글 · 위 폴더 · 잇기 · 상태를 바꾸면 다시 그림', () {
|
||
expect(vaultParentOf('가/나/다'), '가/나');
|
||
expect(vaultParentOf('가'), '');
|
||
expect(vaultJoin('', '가'), '가');
|
||
expect(vaultJoin('가', '나'), '가/나');
|
||
expect(vaultFolderLabel('가/나'), contains('가 › 나'));
|
||
var n = 0;
|
||
final said = <String>[];
|
||
final s = VaultUiState(changed: () => n++, snack: said.add);
|
||
s.setBusy(true);
|
||
s.setProgress('1 / 2');
|
||
expect((s.busy, s.progress, n), (true, '1 / 2', 2));
|
||
s.setBusy(false);
|
||
expect(s.progress, '1 / 2', reason: 'setBusy 는 진행 글을 그대로 둔다');
|
||
s.setBusyProgress(false, null);
|
||
expect((s.busy, s.progress, n), (false, null, 4));
|
||
});
|
||
|
||
test('vault_session (보기 폴더): 한 번 연 동안 같은 폴더 · 지우면 잊고 다음은 새 폴더 · 없는 폴더 지우기는 조용히', () async {
|
||
final v = VaultViewSession();
|
||
await v.clear(); // 만든 적 없음
|
||
final a = await v.dir(), b = await v.dir();
|
||
expect(a.path, b.path);
|
||
expect(p.basename(a.path), startsWith(vaultViewPrefix));
|
||
expect(p.dirname(a.path), p.join(tmp.path, 'view'));
|
||
File(p.join(a.path, 'x.bin')).writeAsBytesSync([1, 2, 3]);
|
||
await v.clear();
|
||
expect(a.existsSync(), isFalse);
|
||
final c = await v.dir();
|
||
expect(c.path == a.path, isFalse);
|
||
await removeTempDirRetry(Directory(p.join(tmp.path, '없음')));
|
||
});
|
||
|
||
test('vault_session (넣기): 펼치기 (폴더 구조 · 겹치는 이름 " (2)" · 읽지 못한 것은 빼고) → 넣기 → 잠그면 멈춤', () async {
|
||
final vault = await Vault.create(Directory(p.join(tmp.path, 'vault')), 'pw-for-test', iterations: 1000);
|
||
final src = Directory(p.join(tmp.path, 'src'))..createSync();
|
||
final one = File(p.join(src.path, 'a.txt'))..writeAsStringSync('하나');
|
||
final dir = Directory(p.join(src.path, '사진'))..createSync();
|
||
File(p.join(dir.path, 'b.txt')).writeAsStringSync('둘');
|
||
Directory(p.join(dir.path, '안')).createSync();
|
||
File(p.join(dir.path, '안', 'c.txt')).writeAsStringSync('셋');
|
||
Directory(p.join(dir.path, '빈')).createSync();
|
||
await vault.mkdir('사진'); // 맨 위에 같은 이름이 이미 있음
|
||
final missing = p.join(src.path, '없는 폴더');
|
||
Directory(missing).createSync();
|
||
Directory(missing).deleteSync();
|
||
|
||
final plan = await planVaultAdd(vault, '', [one.path, dir.path, missing]);
|
||
expect(plan.folders.first, '사진 (2)');
|
||
expect(plan.folders, unorderedEquals(['사진 (2)', '사진 (2)/안', '사진 (2)/빈']), reason: '안의 순서는 폴더 목록 순서');
|
||
// 없는 것은 폴더가 아니라 파일로 보여 펼치기에서는 실패하지 않는다 - 넣기에서 실패해 bad 에 남는다
|
||
expect([for (final x in plan.files) '${x.folder}|${p.basename(x.src)}'],
|
||
unorderedEquals(['|a.txt', '사진 (2)|b.txt', '사진 (2)/안|c.txt', '|없는 폴더']));
|
||
final said = <String>[];
|
||
final s = VaultUiState(changed: () {}, snack: said.add)..vault = vault;
|
||
final thumbs = Directory(p.join(tmp.path, 'thumbs'))..createSync();
|
||
final r = await putVaultAdd(s, [one.path, dir.path, missing], plan, thumbTemp: () async => thumbs.path);
|
||
expect(r.done, 3);
|
||
expect(r.bad, {missing}, reason: '넣지 못한 것만 (그 원본은 화면이 지우지 않음)');
|
||
expect(said, hasLength(1), reason: '처음 실패만 알림');
|
||
expect((s.busy, s.progress), (false, null));
|
||
expect([for (final e in vault.filesUnder('사진 (2)')) e.path], unorderedEquals(['사진 (2)/b.txt', '사진 (2)/안/c.txt']));
|
||
expect(vault.folders, contains('사진 (2)/빈'));
|
||
expect(one.existsSync(), isTrue, reason: '넣기는 원본을 지우지 않는다 (지우기는 화면이 deleteOriginals 로)');
|
||
|
||
// 보관함이 잠겨 있으면 (vault = null) 하나도 넣지 않고 멈춘다
|
||
final p3 = VaultAddPlan()..files.add((src: one.path, folder: '', top: one.path));
|
||
final s3 = VaultUiState(changed: () {}, snack: said.add)..vault = null;
|
||
final r3 = await putVaultAdd(s3, [one.path], p3, thumbTemp: () async => thumbs.path);
|
||
expect(r3.done, 0);
|
||
});
|
||
|
||
testWidgets('vault_views (묻는 창): 옮길 곳은 자기와 그 안을 빼고 · 지금 있는 곳은 꺼짐 · 이름 창의 빈 칸은 null', (t) async {
|
||
String? picked = 'x', named = 'x';
|
||
late BuildContext ctx;
|
||
await t.pumpWidget(MaterialApp(home: Builder(builder: (c) {
|
||
ctx = c;
|
||
return const Scaffold();
|
||
})));
|
||
final f = pickVaultTarget(ctx, ['가', '가/나', '다'], except: '가', from: '');
|
||
await t.pumpAndSettle();
|
||
expect(find.text('나'), findsNothing);
|
||
expect(find.text('가'), findsNothing);
|
||
expect(t.widget<ListTile>(find.widgetWithText(ListTile, '보관함 맨 위')).enabled, isFalse);
|
||
await t.tap(find.text('다'));
|
||
await t.pumpAndSettle();
|
||
picked = await f;
|
||
expect(picked, '다');
|
||
final n = askVaultName(ctx, '새 폴더');
|
||
await t.pumpAndSettle();
|
||
await t.tap(find.text('확인'));
|
||
await t.pumpAndSettle();
|
||
named = await n;
|
||
expect(named, isNull);
|
||
});
|
||
|
||
testWidgets('vault_views (목록): 폴더 먼저 (이름 순) · 파일은 정렬 따라 · 바쁘면 누르기 꺼짐 · 누른 것은 콜백으로', (t) async {
|
||
final vault = await t.runAsync(() async {
|
||
final v = await Vault.create(Directory(p.join(tmp.path, 'vault')), 'pw-for-test', iterations: 1000);
|
||
final src = Directory(p.join(tmp.path, 'src'))..createSync();
|
||
for (final n in ['A.txt', 'b.txt']) {
|
||
File(p.join(src.path, n)).writeAsStringSync(n);
|
||
await v.add(p.join(src.path, n), name: n, folder: '');
|
||
await Future<void>.delayed(const Duration(milliseconds: 20));
|
||
}
|
||
await v.mkdir('나');
|
||
await v.mkdir('가');
|
||
return v;
|
||
});
|
||
final got = <String>[];
|
||
Future<void> show(String sort, {bool busy = false}) => t.pumpWidget(MaterialApp(
|
||
home: Scaffold(
|
||
body: VaultListView(
|
||
vault: vault!,
|
||
cwd: '',
|
||
sort: sort,
|
||
busy: busy,
|
||
onCwd: (f) => got.add('cwd $f'),
|
||
onSort: (x) => got.add('sort $x'),
|
||
onFolder: (f, w) => got.add('$w $f'),
|
||
onEntry: (e, w) => got.add('$w ${e.name}')))));
|
||
List<String> titles() => [
|
||
for (final w in t.widgetList<ListTile>(find.byType(ListTile))) ((w.title as Text).data ?? ''),
|
||
];
|
||
await show('name');
|
||
expect(titles(), ['가', '나', 'A.txt', 'b.txt']);
|
||
await show('added');
|
||
expect(titles(), ['가', '나', 'b.txt', 'A.txt'], reason: '넣은 날 새것 먼저 (b 를 나중에 넣음)');
|
||
await t.tap(find.text('가'));
|
||
await t.tap(find.text('b.txt'));
|
||
expect(got, ['cwd 가', 'view b.txt']);
|
||
await show('added', busy: true);
|
||
await t.tap(find.text('나'));
|
||
expect(got, hasLength(2), reason: '바쁜 동안은 누르기가 꺼짐');
|
||
});
|
||
}
|