Skip to content

Commit 1324fec

Browse files
committed
[red] specify local recovery preview and explicit choices
Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent c9310c6 commit 1324fec

1 file changed

Lines changed: 140 additions & 0 deletions

File tree

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// SPDX-License-Identifier: MIT
2+
// Part of PyBLE (https://pyble.dev) — see /LICENSE.
3+
4+
import 'package:flutter/material.dart';
5+
import 'package:flutter/services.dart';
6+
import 'package:flutter_test/flutter_test.dart';
7+
8+
import 'package:pyble/editor/recovery_panel.dart';
9+
import 'package:pyble/pble/pble.dart';
10+
11+
import '../support/feature_harness.dart';
12+
13+
void main() {
14+
testWidgets('preview requires an explicit local Restore or confirmed Discard', (
15+
WidgetTester tester,
16+
) async {
17+
int restored = 0;
18+
int discarded = 0;
19+
final FakeConnection fake = FakeConnection();
20+
addTearDown(fake.dispose);
21+
await pumpSurface(
22+
tester,
23+
RecoveryPanel(
24+
name: 'main.py',
25+
content: 'print("retained")\n',
26+
checkpointTime: DateTime.utc(2026, 10, 31, 12),
27+
onRestore: () async { restored += 1; },
28+
onDiscard: () async { discarded += 1; },
29+
),
30+
connection: fake,
31+
);
32+
expect(find.text('print("retained")\n'), findsOneWidget);
33+
expect(restored, 0);
34+
expect(discarded, 0);
35+
await tester.tap(find.byKey(kRecoveryDiscardKey));
36+
await tester.pumpAndSettle();
37+
await tester.tap(find.text('Cancel'));
38+
await tester.pumpAndSettle();
39+
expect(discarded, 0);
40+
expect(find.byKey(kRecoveryRestoreKey), findsOneWidget);
41+
await tester.tap(find.byKey(kRecoveryDiscardKey));
42+
await tester.pumpAndSettle();
43+
await tester.tap(find.byKey(kRecoveryConfirmDiscardKey));
44+
await tester.pumpAndSettle();
45+
expect(discarded, 1);
46+
await tester.tap(find.byKey(kRecoveryRestoreKey));
47+
await tester.pumpAndSettle();
48+
expect(restored, 1);
49+
});
50+
51+
testWidgets('restored preview stays readable offline and copies only on tap', (
52+
WidgetTester tester,
53+
) async {
54+
int clipboardWrites = 0;
55+
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
56+
SystemChannels.platform,
57+
(MethodCall call) async {
58+
if (call.method == 'Clipboard.setData') clipboardWrites += 1;
59+
return null;
60+
},
61+
);
62+
addTearDown(() => tester.binding.defaultBinaryMessenger
63+
.setMockMethodCallHandler(SystemChannels.platform, null));
64+
final FakeConnection fake = FakeConnection();
65+
addTearDown(fake.dispose);
66+
await pumpSurface(
67+
tester,
68+
RecoveryPanel(
69+
name: 'main.py',
70+
content: 'print(42)',
71+
checkpointTime: DateTime.utc(2026),
72+
restored: true,
73+
onRestore: () async {},
74+
onDiscard: () async {},
75+
),
76+
connection: fake,
77+
);
78+
expect(find.text('print(42)'), findsOneWidget);
79+
expect(find.byKey(kRecoveryRestoreKey), findsNothing);
80+
expect(clipboardWrites, 0);
81+
await tester.tap(find.byKey(kRecoveryCopyKey));
82+
await tester.pump();
83+
expect(clipboardWrites, 1);
84+
});
85+
86+
testWidgets('preview and destructive confirmation fit 2x text with keyboard', (
87+
WidgetTester tester,
88+
) async {
89+
tester.platformDispatcher.textScaleFactorTestValue = 2;
90+
addTearDown(tester.platformDispatcher.clearTextScaleFactorTestValue);
91+
tester.view.viewInsets = const FakeViewPadding(bottom: 250);
92+
addTearDown(tester.view.resetViewInsets);
93+
final FakeConnection fake = FakeConnection();
94+
addTearDown(fake.dispose);
95+
await pumpSurface(
96+
tester,
97+
RecoveryPanel(
98+
name: 'unicode_µ.py',
99+
content: List<String>.filled(50, 'print("example")').join('\n'),
100+
checkpointTime: DateTime.utc(2026),
101+
onRestore: () async {},
102+
onDiscard: () async {},
103+
),
104+
connection: fake,
105+
size: const Size(400, 800),
106+
);
107+
await tester.ensureVisible(find.byKey(kRecoveryDiscardKey));
108+
await tester.tap(find.byKey(kRecoveryDiscardKey));
109+
await tester.pumpAndSettle();
110+
expect(find.byKey(kRecoveryConfirmDiscardKey), findsOneWidget);
111+
expect(tester.takeException(), isNull);
112+
});
113+
114+
testWidgets('board confirmation identifies destination and explicit Run', (
115+
WidgetTester tester,
116+
) async {
117+
bool? approved;
118+
final FakeConnection fake = FakeConnection();
119+
addTearDown(fake.dispose);
120+
await pumpSurface(
121+
tester,
122+
Builder(builder: (BuildContext context) => TextButton(
123+
onPressed: () async {
124+
approved = await confirmRecoveryBoardTarget(
125+
context, path: '/main.py', run: true,
126+
);
127+
},
128+
child: const Text('open'),
129+
)),
130+
connection: fake,
131+
);
132+
await tester.tap(find.text('open'));
133+
await tester.pumpAndSettle();
134+
expect(find.textContaining('/main.py'), findsOneWidget);
135+
expect(find.textContaining('run'), findsWidgets);
136+
await tester.tap(find.text('Cancel'));
137+
await tester.pumpAndSettle();
138+
expect(approved, isFalse);
139+
});
140+
}

0 commit comments

Comments
 (0)