generated from ttipo/project-base
fix(flutter): 홈·일정 시안 점검 반영과 우리 아이 순서 바꾸기
홈 - 우리 아이를 꾹 눌러(320ms) 끌면 순서가 바뀌고 저장된다 (PetRepository.reorder). 보관함으로 간 아이는 목록에서 빠지므로 원래 순서대로 뒤에 둔다. - 오늘 할 일 진행 막대 끝을 둥글게, 아이 사진 겹침의 글자 아래 여백(inline-flex)을 반영하고 긴 일정 제목은 줄을 바꾼다. - 최근 다이어리 칸을 누르면 시안처럼 일정·기록 탭의 다이어리 목록으로 간다. 일정 - 하루 종일이 켜진 채 기간을 하루 2회로 정하면 회차 시각(08:00·20:00)이 저장되지 않던 문제를 고쳤다. - 예약 분류를 고른 뒤 같은 일반 일정 칩을 다시 누르면 분류 선택이 풀린다. - 주기 직접 설정은 2주마다로 시작하고, 매일·평일을 골랐다 돌아와도 단위·횟수가 남는다. - 12개월 보기에서는 일정 목록을 숨기고, 달력 점 묶음을 칸 가운데에 둔다. - 저장 버튼 흐림 .45, 일정 제거는 전용 확인창으로. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,5 +66,36 @@ void main() {
|
||||
expect(find.text('명함 만들기'), findsNothing);
|
||||
expect(find.bySemanticsLabel('콩이 명함 공유'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
testWidgets('우리 아이를 꾹 눌러 끌면 순서가 바뀌고 저장된다', (tester) async {
|
||||
final pets = [samplePet(id: 'pet-1', name: '콩이'), samplePet(id: 'pet-2', name: '보리')];
|
||||
final prefs = await launchSignedIn(tester, extra: {'pets.v1': jsonEncode([for (final p in pets) p.toJson()])});
|
||||
|
||||
final from = tester.getCenter(find.text('콩이'));
|
||||
final to = tester.getCenter(find.text('보리'));
|
||||
// 320ms 이상 누르면 들어 올리고, 끌어서 보리 자리에 놓는다
|
||||
final gesture = await tester.startGesture(from);
|
||||
await tester.pump(const Duration(milliseconds: 400));
|
||||
await gesture.moveTo(Offset.lerp(from, to, .5)!);
|
||||
await tester.pump();
|
||||
await gesture.moveTo(to);
|
||||
await tester.pump();
|
||||
await gesture.up();
|
||||
await settle(tester);
|
||||
|
||||
expect(tester.getCenter(find.text('보리')).dx, lessThan(tester.getCenter(find.text('콩이')).dx));
|
||||
final saved = (jsonDecode(prefs.getString('pets.v1')!) as List<Object?>).cast<Map<String, Object?>>();
|
||||
expect(saved.map((p) => p['name']), ['보리', '콩이']);
|
||||
});
|
||||
|
||||
testWidgets('짧게 누르면 순서는 그대로 두고 그 아이를 고른다', (tester) async {
|
||||
final pets = [samplePet(id: 'pet-1', name: '콩이'), samplePet(id: 'pet-2', name: '보리')];
|
||||
final prefs = await launchSignedIn(tester, extra: {'pets.v1': jsonEncode([for (final p in pets) p.toJson()])});
|
||||
|
||||
await tester.tap(find.text('보리'));
|
||||
await settle(tester);
|
||||
expect(find.text('보리의 펫 명함이 아직 없어요', findRichText: true), findsOneWidget);
|
||||
final saved = (jsonDecode(prefs.getString('pets.v1')!) as List<Object?>).cast<Map<String, Object?>>();
|
||||
expect(saved.map((p) => p['name']), ['콩이', '보리']);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import 'package:tma_app/features/pet/domain/pet.dart';
|
||||
import 'package:tma_app/features/pet/domain/pet_age.dart';
|
||||
import 'package:tma_app/features/pet/domain/pet_species.dart';
|
||||
|
||||
import 'support/app_harness.dart';
|
||||
|
||||
PetInput _input({String name = '콩이', double weight = 3.2}) => PetInput(
|
||||
name: name,
|
||||
species: '강아지',
|
||||
@@ -138,4 +140,20 @@ void main() {
|
||||
expect(await repo.fetchPets(), isEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group('reorderPets — 우리 아이 순서 바꾸기', () {
|
||||
final pets = [samplePet(id: 'a', name: '콩이'), samplePet(id: 'b', name: '보리'), samplePet(id: 'c', name: '초코')];
|
||||
|
||||
test('준 순서대로 줄 세운다', () {
|
||||
expect(reorderPets(pets, ['c', 'a', 'b']).map((p) => p.id), ['c', 'a', 'b']);
|
||||
});
|
||||
|
||||
test('목록에 없는 아이(추억 보관함 등)는 원래 순서대로 뒤에 둔다', () {
|
||||
expect(reorderPets(pets, ['b', 'a']).map((p) => p.id), ['b', 'a', 'c']);
|
||||
});
|
||||
|
||||
test('없는 id 는 건너뛴다', () {
|
||||
expect(reorderPets(pets, ['x', 'b']).map((p) => p.id), ['b', 'a', 'c']);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -138,4 +138,77 @@ void main() {
|
||||
expect(find.text('예방접종'), findsOneWidget);
|
||||
expect(find.text('예약된 일정이 없어요'), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('하루 종일이 켜진 채 기간을 하루 2회로 정하면 회차 시각(08:00·20:00)으로 저장된다', (tester) async {
|
||||
final prefs = await launchSignedIn(tester, extra: _withPet());
|
||||
await _openRecords(tester);
|
||||
await tester.tap(find.text('일정 추가'));
|
||||
await settle(tester);
|
||||
|
||||
await tester.enterText(find.byType(TextField).first, '영양제');
|
||||
await _tapVisible(tester, find.bySemanticsLabel('기간 설정'));
|
||||
await _tapVisible(tester, find.text('2회'));
|
||||
await _tapVisible(tester, find.text('일정 저장'));
|
||||
|
||||
final saved = jsonDecode(prefs.getString('schedule.events.v1')!) as List<Object?>;
|
||||
final starts = {for (final e in saved.cast<Map<String, Object?>>()) e['start']};
|
||||
expect(starts, {'08:00', '20:00'});
|
||||
});
|
||||
|
||||
testWidgets('예약 분류를 골랐다가 같은 일반 일정 칩을 다시 누르면 분류 선택이 풀린다', (tester) async {
|
||||
await launchSignedIn(tester, extra: _withPet());
|
||||
await _openRecords(tester);
|
||||
await tester.tap(find.text('일정 추가'));
|
||||
await settle(tester);
|
||||
|
||||
await _tapVisible(tester, find.text('목욕'));
|
||||
await _tapVisible(tester, find.bySemanticsLabel('병원·검진'));
|
||||
expect(tester.getSemantics(find.bySemanticsLabel('병원·검진')), isSemantics(isSelected: true));
|
||||
|
||||
// 이름 칸에도 '목욕' 이 들어 있으므로 위쪽의 칩을 누른다
|
||||
await _tapVisible(tester, find.text('목욕').first);
|
||||
expect(tester.getSemantics(find.bySemanticsLabel('병원·검진')), isSemantics(isSelected: false));
|
||||
});
|
||||
|
||||
testWidgets('주기 직접 설정은 2주마다로 시작하고, 매일·평일을 골랐다 돌아와도 단위·횟수가 남는다', (tester) async {
|
||||
await launchSignedIn(tester, extra: _withPet());
|
||||
await _openRecords(tester);
|
||||
await tester.tap(find.text('일정 추가'));
|
||||
await settle(tester);
|
||||
|
||||
await _tapVisible(tester, find.bySemanticsLabel('반복 일정'));
|
||||
await _tapVisible(tester, find.text('주기 직접 설정'));
|
||||
expect(find.text('2주 마다 반복'), findsWidgets);
|
||||
|
||||
await _tapVisible(tester, find.text('평일만'));
|
||||
expect(find.text('평일마다 반복'), findsOneWidget);
|
||||
await _tapVisible(tester, find.text('주기 직접 설정'));
|
||||
expect(find.text('2주 마다 반복'), findsWidgets);
|
||||
});
|
||||
|
||||
testWidgets('시간 휠의 가운데 숫자를 누르면 직접 입력할 수 있다', (tester) async {
|
||||
await launchSignedIn(tester, extra: _withPet());
|
||||
await _openRecords(tester);
|
||||
await tester.tap(find.text('일정 추가'));
|
||||
await settle(tester);
|
||||
|
||||
await _tapVisible(tester, find.text('시작'));
|
||||
expect(find.text('오후 2:00 선택'), findsOneWidget);
|
||||
|
||||
// 가운데(선택된) 시 — 02
|
||||
await tester.tap(find.text('02').first);
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byType(TextField).last, '7');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await settle(tester);
|
||||
expect(find.text('오후 7:00 선택'), findsOneWidget);
|
||||
|
||||
// 범위를 넘으면 가장 가까운 값으로 (분 99 → 59)
|
||||
await tester.tap(find.text('00').first);
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byType(TextField).last, '99');
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await settle(tester);
|
||||
expect(find.text('오후 7:59 선택'), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user