103 lines
4.1 KiB
Dart
103 lines
4.1 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:relationship_saver/core/config/app_theme.dart';
|
|
import 'package:relationship_saver/features/local/local_repository.dart';
|
|
import 'package:relationship_saver/features/local/storage/local_data_store_in_memory.dart';
|
|
import 'package:relationship_saver/features/local/storage/local_data_store_provider.dart';
|
|
import 'package:relationship_saver/features/sync/storage/sync_state_store_in_memory.dart';
|
|
import 'package:relationship_saver/features/sync/storage/sync_state_store_provider.dart';
|
|
import 'package:relationship_saver/features/sync/sync_queue_repository.dart';
|
|
import 'package:relationship_saver/features/sync/sync_view.dart';
|
|
import 'package:relationship_saver/integrations/backend/models/backend_models.dart';
|
|
|
|
void main() {
|
|
testWidgets(
|
|
'rejected person mutation can be repaired directly from Sync view',
|
|
(WidgetTester tester) async {
|
|
final ProviderContainer container = ProviderContainer(
|
|
overrides: [
|
|
localDataStoreProvider.overrideWithValue(InMemoryLocalDataStore()),
|
|
syncStateStoreProvider.overrideWithValue(InMemorySyncStateStore()),
|
|
],
|
|
);
|
|
addTearDown(container.dispose);
|
|
|
|
final localBefore = await container.read(localRepositoryProvider.future);
|
|
final person = localBefore.people.first;
|
|
|
|
final ChangeEnvelope rejectedChange = ChangeEnvelope(
|
|
schemaVersion: 1,
|
|
entityType: 'person',
|
|
entityId: person.id,
|
|
op: ChangeOperation.upsert,
|
|
modifiedAt: DateTime(2026, 2, 15, 12).toUtc(),
|
|
clientMutationId: 'cm-repair-1',
|
|
payload: <String, dynamic>{
|
|
'name': person.name,
|
|
'relationship': person.relationship,
|
|
'notes': person.notes,
|
|
'tags': person.tags,
|
|
},
|
|
);
|
|
|
|
final syncNotifier = container.read(syncQueueRepositoryProvider.notifier);
|
|
await syncNotifier.enqueue(rejectedChange);
|
|
await syncNotifier.applyPushResult(
|
|
const SyncPushResult(
|
|
cursor: 'cursor-1',
|
|
rejected: <MutationRejection>[
|
|
MutationRejection(
|
|
clientMutationId: 'cm-repair-1',
|
|
code: 'VALIDATION_FAILED',
|
|
message: 'Bad payload',
|
|
),
|
|
],
|
|
),
|
|
);
|
|
final syncState = await container.read(syncQueueRepositoryProvider.future);
|
|
expect(syncState.lastRejected.length, 1);
|
|
|
|
await tester.pumpWidget(
|
|
UncontrolledProviderScope(
|
|
container: container,
|
|
child: MaterialApp(theme: AppTheme.light(), home: const SyncView()),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
|
await tester.scrollUntilVisible(
|
|
find.text('Rejected Changes'),
|
|
240,
|
|
scrollable: find.byType(Scrollable).first,
|
|
);
|
|
expect(find.text('Rejected Changes'), findsOneWidget);
|
|
await tester.scrollUntilVisible(
|
|
find.widgetWithText(TextButton, 'Open Related Screen'),
|
|
120,
|
|
scrollable: find.byType(Scrollable).first,
|
|
);
|
|
final Finder openRelatedScreenButton = find.widgetWithText(
|
|
TextButton,
|
|
'Open Related Screen',
|
|
);
|
|
expect(openRelatedScreenButton, findsOneWidget);
|
|
await tester.ensureVisible(openRelatedScreenButton);
|
|
await tester.pumpAndSettle(const Duration(milliseconds: 200));
|
|
await tester.tap(openRelatedScreenButton, warnIfMissed: false);
|
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
|
|
|
expect(find.textContaining('Focus: person:'), findsOneWidget);
|
|
|
|
await tester.enterText(find.byType(TextField).first, 'Renamed Person');
|
|
await tester.tap(find.widgetWithText(FilledButton, 'Save Changes'));
|
|
await tester.pumpAndSettle(const Duration(milliseconds: 500));
|
|
|
|
final localAfter = await container.read(localRepositoryProvider.future);
|
|
final updated = localAfter.people.firstWhere(
|
|
(item) => item.id == person.id,
|
|
);
|
|
expect(updated.name, 'Renamed Person');
|
|
},
|
|
);
|
|
}
|