JJ_MKVMaker/test/confirm_dialog_contract_tes...

138 lines
6.5 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/ui/confirm_dialog.dart';
/// E4 U7 약속 시험 (모듈 하나에 한 장): lib/ui/confirm_dialog.dart 맨 위 약속을 그대로 본다.
void main() {
testWidgets('[확인] 만 true · [취소] · Esc · 바깥 누르기는 false', (tester) async {
for (final (name, close, want) in <(String, Future<void> Function(), bool)>[
('확인', () => tester.tap(find.text('확인 단추')), true),
('취소', () => tester.tap(find.text('취소')), false),
('Esc', () => tester.sendKeyEvent(LogicalKeyboardKey.escape), false),
('바깥', () => tester.tapAt(const Offset(5, 5)), false),
]) {
late BuildContext ctx;
await tester.pumpWidget(MaterialApp(home: Builder(builder: (c) {
ctx = c;
return const Scaffold(body: SizedBox());
})));
bool? got;
unawaited(confirmDialog(ctx, title: () => '복사', confirmLabel: () => '확인 단추', body: (c, set) => const Text('내용'))
.then((v) => got = v));
await tester.pumpAndSettle();
await close();
await tester.pumpAndSettle();
expect(got, want, reason: name);
}
});
testWidgets('Enter = 첫 초점: 보통은 [확인], 되돌릴 수 없는 일은 [취소] (관리자 결정 10/22)', (tester) async {
for (final (danger, want) in [(false, true), (true, false)]) {
late BuildContext ctx;
await tester.pumpWidget(MaterialApp(home: Builder(builder: (c) {
ctx = c;
return const Scaffold(body: SizedBox());
})));
bool? got;
unawaited(confirmDialog(ctx,
title: () => '지우기', confirmLabel: () => '확인 단추', danger: () => danger, body: (c, set) => const Text('내용'))
.then((v) => got = v));
await tester.pumpAndSettle();
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
expect(got, want, reason: 'danger=$danger');
}
});
testWidgets('창 안에서 바뀌면 제목 · 빨강이 따라 바뀌고 첫 초점은 연 때 그대로', (tester) async {
final flag = ValueNotifier(false);
late BuildContext ctx;
await tester.pumpWidget(MaterialApp(home: Builder(builder: (c) {
ctx = c;
return const Scaffold(body: SizedBox());
})));
bool? got;
unawaited(confirmDialog(ctx,
title: () => flag.value ? '영구 삭제' : '휴지통으로 보내기',
confirmLabel: () => flag.value ? '영구 삭제 단추' : '휴지통으로',
danger: () => flag.value,
body: (c, set) => CheckboxListTile(
value: flag.value, onChanged: (v) => set(() => flag.value = v ?? false), title: const Text('영구로')))
.then((v) => got = v));
await tester.pumpAndSettle();
expect(find.text('휴지통으로 보내기'), findsOneWidget);
FilledButton confirm() => tester.widget<FilledButton>(find.byType(FilledButton));
expect(confirm().style, isNull, reason: '보통 단추');
await tester.tap(find.text('영구로'));
await tester.pumpAndSettle();
expect(find.text('영구 삭제'), findsOneWidget, reason: '제목이 바뀜');
expect(find.text('영구 삭제 단추'), findsOneWidget);
expect(confirm().style, isNotNull, reason: '빨강');
expect(tester.widget<FilledButton>(find.byType(FilledButton)).autofocus, isTrue, reason: '첫 초점은 연 때 그대로 ([확인])');
await tester.tap(find.text('영구 삭제 단추'));
await tester.pumpAndSettle();
expect(got, isTrue);
});
Future<BuildContext> host(WidgetTester tester) async {
late BuildContext ctx;
await tester.pumpWidget(MaterialApp(home: Builder(builder: (c) {
ctx = c;
return const Scaffold(body: SizedBox());
})));
return ctx;
}
testWidgets('W DangerDialog (관리자 10/25): 빨강이 아니어도 되돌리기 어려운 일 (cancelFirst) 은 첫 초점 [취소] - Enter 로 실행되지 않음', (tester) async {
final ctx = await host(tester);
bool? got;
unawaited(confirmDialog(ctx, title: () => '루트 폴더 보기', confirmLabel: () => '알고 봅니다', cancelFirst: true, body: (c, set) => const Text('내용'))
.then((v) => got = v));
await tester.pumpAndSettle();
expect(tester.widget<FilledButton>(find.byType(FilledButton)).style, isNull, reason: '빨강은 아님');
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
expect(got, isFalse);
});
testWidgets('W DangerDialog: canConfirm 이 false 인 동안 [확인] 꺼짐 (누르고 Enter 해도 실행 안 됨) - 체크하면 켜짐', (tester) async {
final ctx = await host(tester);
var sure = false;
bool? got;
unawaited(confirmDialog(ctx,
title: () => '시스템 폴더 보기',
confirmLabel: () => '켜기',
cancelFirst: true,
canConfirm: () => sure,
body: (c, set) => CheckboxListTile(value: sure, onChanged: (v) => set(() => sure = v ?? false), title: const Text('알고 켭니다')))
.then((v) => got = v));
await tester.pumpAndSettle();
expect(tester.widget<FilledButton>(find.byType(FilledButton)).onPressed, isNull, reason: '체크 전 꺼짐');
await tester.tap(find.text('켜기'));
await tester.pumpAndSettle();
expect(got, isNull, reason: '꺼진 단추는 아무것도 안 함');
await tester.tap(find.text('알고 켭니다'));
await tester.pumpAndSettle();
expect(tester.widget<FilledButton>(find.byType(FilledButton)).onPressed, isNotNull, reason: '체크하면 켜짐');
await tester.tap(find.text('켜기'));
await tester.pumpAndSettle();
expect(got, isTrue);
});
testWidgets('confirmAction (글만 묻는 꼴) 도 같은 창: danger 면 빨강 · 첫 초점 [취소] (Enter → false), 아니면 Enter → true', (tester) async {
for (final (danger, want) in [(true, false), (false, true)]) {
final ctx = await host(tester);
bool? got;
unawaited(confirmAction(ctx, title: '영구 삭제', body: '되돌릴 수 없습니다', ok: '지우기', danger: danger).then((v) => got = v));
await tester.pumpAndSettle();
expect(tester.widget<FilledButton>(find.byType(FilledButton)).style != null, danger, reason: '빨강 = danger');
await tester.sendKeyEvent(LogicalKeyboardKey.enter);
await tester.pumpAndSettle();
expect(got, want, reason: 'danger=$danger');
}
});
}