generated from ttipo/project-base
feat(flutter): 앱 뼈대와 홈 이번 주말 축제·행사 화면
데이터 기반(장소·행사 조회 API)이 생겨 앱 화면을 붙이기 시작한다. 디자인 캔버스의 1~7번(시작·둘러보기) 중 지금 API 로 바로 되는 것부터 만들었다. - 앱 뼈대: 디자인 캔버스의 색·글자 크기·선 아이콘을 옮긴 테마, go_router 하단 탭 (홈·지도·내 코스·내 정보 — 지도·내 코스·내 정보는 자리만), 한/영 문구(arb, 4개 언어 전제) - 첫 실행: 현재 위치를 쓰거나 대구 동네(도심·수성못·앞산 카페거리)를 고른다. 위치를 못 쓰면 대구 도심을 기준으로 시작하고 알린다. 좌표는 저장하지 않는다 - 홈 '이번 주말 축제·행사': 기준 위치 반경 120km 거리순, 비면 다가오는 30일 (2026-11-03 합의) - 행사 전체 목록(커서 페이지)·상세(시간·요금·예매·전화·소개·출처) - 한국어 문단은 단어 단위 줄바꿈(keepAll) — Flutter 기본은 글자 단위로 끊는다 - 공공누리 Type3 사진은 자르지 않고, 꼬리말에 관광공사·KOPIS·문화포털 출처를 적는다 - 한/영 생성물(lib/l10n/generated)은 flutter pub get 이 만들므로 git 에서 뺀다 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:wandergo/features/event/data/models/event_models.dart';
|
||||
import 'package:wandergo/features/location/domain/base_location.dart';
|
||||
|
||||
void main() {
|
||||
group('EventPage.fromJson', () {
|
||||
test('백엔드 목록 응답(data)을 읽는다 — 숫자는 정수·실수 모두 받는다', () {
|
||||
final page = EventPage.fromJson({
|
||||
'items': [
|
||||
{
|
||||
'id': 'a1',
|
||||
'name': '수성빛예술제',
|
||||
'kind': 'festival',
|
||||
'startDate': '2026-11-27',
|
||||
'endDate': '2026-12-06',
|
||||
'genre': null,
|
||||
'ongoing': false,
|
||||
'address': '대구광역시 수성구 두산동',
|
||||
'eventPlace': '수성못 일원',
|
||||
'lat': 35.8282,
|
||||
'lng': 128,
|
||||
'distanceM': 1240,
|
||||
'feeText': '무료',
|
||||
'imageThumbUrl': null,
|
||||
'imageLicense': 'Type3',
|
||||
},
|
||||
],
|
||||
'pageInfo': {'hasNext': true, 'nextCursor': 'abc', 'total': null},
|
||||
});
|
||||
|
||||
expect(page.hasNext, isTrue);
|
||||
expect(page.nextCursor, 'abc');
|
||||
final item = page.items.single;
|
||||
expect(item.kind, EventKind.festival);
|
||||
expect(item.lng, 128.0);
|
||||
expect(item.distanceM, 1240.0);
|
||||
expect(item.imageLicense, ImageLicense.type3);
|
||||
});
|
||||
|
||||
test('모르는 분류·사진 유형은 null 로 둔다(앱이 죽지 않게)', () {
|
||||
final item = EventItem.fromJson({
|
||||
'id': 'a2',
|
||||
'name': '새 분류',
|
||||
'kind': 'workshop',
|
||||
'startDate': '2026-11-07',
|
||||
'endDate': '2026-11-07',
|
||||
'ongoing': true,
|
||||
'imageLicense': 'Type9',
|
||||
});
|
||||
expect(item.kind, isNull);
|
||||
expect(item.imageLicense, isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('EventDetail.fromJson', () {
|
||||
test('상세 필드와 출처를 읽는다', () {
|
||||
final detail = EventDetail.fromJson({
|
||||
'id': 'b1',
|
||||
'name': '라보엠',
|
||||
'kind': 'performance',
|
||||
'startDate': '2026-11-07',
|
||||
'endDate': '2026-11-07',
|
||||
'genre': '오페라',
|
||||
'ongoing': false,
|
||||
'overview': '소개',
|
||||
'playtimeText': '토요일(19:30)',
|
||||
'homepage': null,
|
||||
'phone': '053-000-0000',
|
||||
'bookingUrl': 'https://tickets.test/1',
|
||||
'imageUrl': 'https://kopis.or.kr/poster.jpg',
|
||||
'attribution': {'provider': 'KOPIS', 'asOf': null},
|
||||
});
|
||||
expect(detail.item.genre, '오페라');
|
||||
expect(detail.bookingUrl, 'https://tickets.test/1');
|
||||
expect(detail.attribution.provider, 'KOPIS');
|
||||
});
|
||||
});
|
||||
|
||||
group('BaseLocationChoice', () {
|
||||
test('저장 문자열로 오가도 같은 값이다', () {
|
||||
const choices = [
|
||||
BaseLocationChoice.current(),
|
||||
BaseLocationChoice.neighborhood(Neighborhood.suseongmot),
|
||||
];
|
||||
for (final c in choices) {
|
||||
expect(BaseLocationChoice.fromStorageKey(c.storageKey), c);
|
||||
}
|
||||
expect(BaseLocationChoice.fromStorageKey('seoul'), isNull);
|
||||
expect(BaseLocationChoice.fromStorageKey(null), isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user