Files
BlockSeasons/test/data/streak_test.dart
T
airkjw 607278928b Add daily streak system and normalize bundle id to com.airkjw.blockseasons
Pure advanceStreak (1-day grace none, milestone flags at 3/7/14/30),
persisted in the save blob; StreakNotifier advances on every finished
attempt; home screen flame chip and milestone snackbar. Fix flutter
create's camelCased iOS bundle id and underscored Android application
id to the agreed lowercase form before any store registration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 17:25:39 +09:00

75 lines
2.7 KiB
Dart

import 'package:block_seasons/data/save_repository.dart';
import 'package:block_seasons/data/streak.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('advanceStreak', () {
test('first ever play starts the streak at 1', () {
final next = advanceStreak(StreakState.initial, DateTime(2026, 6, 11));
expect(next.current, 1);
expect(next.best, 1);
expect(next.lastYmd, '2026-06-11');
});
test('same-day repeat play changes nothing', () {
final day1 = advanceStreak(StreakState.initial, DateTime(2026, 6, 11));
final again = advanceStreak(day1, DateTime(2026, 6, 11, 23, 59));
expect(again.current, 1);
expect(again.best, 1);
});
test('consecutive days grow the streak and best follows', () {
var s = advanceStreak(StreakState.initial, DateTime(2026, 6, 11));
s = advanceStreak(s, DateTime(2026, 6, 12));
s = advanceStreak(s, DateTime(2026, 6, 13));
expect(s.current, 3);
expect(s.best, 3);
});
test('missing a day resets current but keeps best', () {
var s = advanceStreak(StreakState.initial, DateTime(2026, 6, 11));
s = advanceStreak(s, DateTime(2026, 6, 12));
s = advanceStreak(s, DateTime(2026, 6, 15));
expect(s.current, 1);
expect(s.best, 2);
});
test('month boundaries count as consecutive days', () {
var s = advanceStreak(StreakState.initial, DateTime(2026, 6, 30));
s = advanceStreak(s, DateTime(2026, 7, 1));
expect(s.current, 2);
});
test('milestone crossings are flagged once', () {
var s = StreakState(current: 2, best: 6, lastYmd: '2026-06-10');
final next = advanceStreak(s, DateTime(2026, 6, 11));
expect(next.current, 3);
expect(next.hitMilestone, 3);
s = StreakState(current: 3, best: 6, lastYmd: '2026-06-11');
final after = advanceStreak(s, DateTime(2026, 6, 12));
expect(after.hitMilestone, isNull, reason: '4 is not a milestone');
});
});
group('SaveRepository streak persistence', () {
test('round-trips streak state', () async {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
final repo = SaveRepository(prefs);
expect(repo.streak.current, 0);
await repo.saveStreak(
const StreakState(current: 5, best: 9, lastYmd: '2026-06-11'),
);
final reloaded = SaveRepository(prefs);
expect(reloaded.streak.current, 5);
expect(reloaded.streak.best, 9);
expect(reloaded.streak.lastYmd, '2026-06-11');
});
});
}