166 lines
6.0 KiB
Dart
166 lines
6.0 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
import 'package:words999/models/syncretic_session_model.dart';
|
|
import 'package:words999/models/word_of_day_model.dart';
|
|
import 'package:words999/services/word_of_the_day_service.dart';
|
|
import 'package:words999/services/words_repository.dart';
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
setUpAll(() async {
|
|
SharedPreferences.setMockInitialValues({});
|
|
await WordsRepository().init(remoteDailyShuffle: false);
|
|
await WordOfTheDayService().init();
|
|
});
|
|
|
|
group('WordOfTheDayService & Model Tests', () {
|
|
test('Date key format and parse consistency', () {
|
|
final date = DateTime(2026, 9, 28);
|
|
final key = WordOfTheDayService.formatDateKey(date);
|
|
expect(key, '2026-09-28');
|
|
|
|
final parsed = WordOfTheDayService.parseDateKey(key);
|
|
expect(parsed.year, 2026);
|
|
expect(parsed.month, 9);
|
|
expect(parsed.day, 28);
|
|
});
|
|
|
|
test('Deterministic daily word hash is consistent for identical dates', () {
|
|
final date1 = DateTime(2026, 9, 28);
|
|
final date2 = DateTime(2026, 9, 28, 23, 59, 59);
|
|
|
|
final idx1 = WordOfTheDayService.calculateWordIndexForDate(date1, 1000);
|
|
final idx2 = WordOfTheDayService.calculateWordIndexForDate(date2, 1000);
|
|
expect(idx1, equals(idx2));
|
|
|
|
final word1 = WordOfTheDayService().getWordForDate(date1);
|
|
final word2 = WordOfTheDayService().getWordForDate(date2);
|
|
expect(word1.id, equals(word2.id));
|
|
expect(word1.word, equals(word2.word));
|
|
});
|
|
|
|
test('Adjacent dates yield distinct words across calendar', () {
|
|
final day1 = DateTime(2026, 9, 27);
|
|
final day2 = DateTime(2026, 9, 28);
|
|
final day3 = DateTime(2026, 9, 29);
|
|
|
|
final idx1 = WordOfTheDayService.calculateWordIndexForDate(day1, 1000);
|
|
final idx2 = WordOfTheDayService.calculateWordIndexForDate(day2, 1000);
|
|
final idx3 = WordOfTheDayService.calculateWordIndexForDate(day3, 1000);
|
|
|
|
expect(idx1, isNot(equals(idx2)));
|
|
expect(idx2, isNot(equals(idx3)));
|
|
});
|
|
|
|
test('Triptych mode provides 3 distinct words for Morning, Day, and Evening', () {
|
|
final date = DateTime(2026, 9, 28);
|
|
final triptych = WordOfTheDayService().getTriptychWordsForDate(date);
|
|
|
|
expect(triptych.morning.id, isNot(equals(triptych.day.id)));
|
|
expect(triptych.day.id, isNot(equals(triptych.evening.id)));
|
|
expect(triptych.morning.id, isNot(equals(triptych.evening.id)));
|
|
});
|
|
|
|
test('WordOfDayRecord JSON serialization and copyWith with Triptych', () {
|
|
const rec = WordOfDayRecord(
|
|
dateKey: '2026-09-28',
|
|
wordId: 42,
|
|
isRevealed: true,
|
|
isStepCompleted: false,
|
|
diaryNote: 'Удивительная синхрония в разговоре',
|
|
isTriptychMode: true,
|
|
morningWordId: 10,
|
|
dayWordId: 20,
|
|
eveningWordId: 30,
|
|
isMorningRevealed: true,
|
|
isDayRevealed: true,
|
|
isEveningRevealed: false,
|
|
isDayStepCompleted: true,
|
|
);
|
|
|
|
final jsonStr = rec.toJson();
|
|
final decoded = WordOfDayRecord.fromJson(jsonStr);
|
|
|
|
expect(decoded.dateKey, '2026-09-28');
|
|
expect(decoded.wordId, 42);
|
|
expect(decoded.isTriptychMode, isTrue);
|
|
expect(decoded.morningWordId, 10);
|
|
expect(decoded.dayWordId, 20);
|
|
expect(decoded.eveningWordId, 30);
|
|
expect(decoded.isMorningRevealed, isTrue);
|
|
expect(decoded.isDayRevealed, isTrue);
|
|
expect(decoded.isEveningRevealed, isFalse);
|
|
expect(decoded.isDayStepCompleted, isTrue);
|
|
expect(decoded.isAnyRevealed, isTrue);
|
|
expect(decoded.areAllTriptychRevealed, isFalse);
|
|
|
|
final allRevealed = decoded.copyWith(isEveningRevealed: true);
|
|
expect(allRevealed.areAllTriptychRevealed, isTrue);
|
|
});
|
|
|
|
test('Reveal specific triptych phases and persist', () async {
|
|
final service = WordOfTheDayService();
|
|
final testDate = DateTime(2026, 11, 20);
|
|
|
|
await service.setRecordTriptychMode(testDate, true);
|
|
var rec = service.getRecord(testDate);
|
|
expect(rec.isTriptychMode, isTrue);
|
|
expect(rec.isMorningRevealed, isFalse);
|
|
|
|
await service.revealWord(testDate, phase: 'morning');
|
|
rec = service.getRecord(testDate);
|
|
expect(rec.isMorningRevealed, isTrue);
|
|
expect(rec.isDayRevealed, isFalse);
|
|
|
|
await service.revealWord(testDate, phase: 'day');
|
|
rec = service.getRecord(testDate);
|
|
expect(rec.isDayRevealed, isTrue);
|
|
expect(rec.isEveningRevealed, isFalse);
|
|
|
|
await service.revealWord(testDate, phase: 'evening');
|
|
rec = service.getRecord(testDate);
|
|
expect(rec.isEveningRevealed, isTrue);
|
|
expect(rec.areAllTriptychRevealed, isTrue);
|
|
|
|
await service.toggleStepCompleted(testDate, true, isDayStep: true);
|
|
rec = service.getRecord(testDate);
|
|
expect(rec.isDayStepCompleted, isTrue);
|
|
});
|
|
|
|
test('SyncreticSession can incorporate Triptych reading', () async {
|
|
final triptych = WordOfTheDayService().getTriptychWordsForDate(DateTime(2026, 9, 28));
|
|
|
|
final reading = SyncreticReading(
|
|
id: 'trip_test_1',
|
|
systemId: 'word_of_day',
|
|
systemName: 'Триптих Дня',
|
|
systemIcon: '⏳',
|
|
timestamp: DateTime.now(),
|
|
layoutTitle: 'Триптих Суток',
|
|
items: [
|
|
ReadingSignItem(
|
|
title: 'Утро: ${triptych.morning.word}',
|
|
linkedWord: triptych.morning,
|
|
),
|
|
ReadingSignItem(
|
|
title: 'День: ${triptych.day.word}',
|
|
linkedWord: triptych.day,
|
|
),
|
|
ReadingSignItem(
|
|
title: 'Вечер: ${triptych.evening.word}',
|
|
linkedWord: triptych.evening,
|
|
),
|
|
],
|
|
);
|
|
|
|
final repo = WordsRepository();
|
|
await repo.addReadingToSyncreticSession(reading);
|
|
|
|
final current = repo.currentSyncreticSession;
|
|
expect(current.readings.any((r) => r.id == 'trip_test_1'), isTrue);
|
|
expect(current.readings.firstWhere((r) => r.id == 'trip_test_1').items.length, 3);
|
|
});
|
|
});
|
|
}
|