Skip to content

Commit ac9efa5

Browse files
committed
[red] Require identity in connected status pill
Signed-off-by: Viwat Vchirawongkwin <viwat.v@chula.ac.th>
1 parent c957754 commit ac9efa5

2 files changed

Lines changed: 82 additions & 3 deletions

File tree

‎app/test/support/shell_harness.dart‎

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,28 +77,30 @@ const List<String> navDestinations = <String>[
7777
];
7878

7979
/// Builds a scriptable [DeviceInfo]. `deviceId`/`label` are DISPLAY-ONLY and
80-
/// never gate access (SEC-9) — the shell must render with them empty.
80+
/// never gate access (SEC-9); [agentVersion] is the PyBLE agent firmware SemVer.
8181
DeviceInfo fakeDeviceInfo({
8282
String chip = 'esp32-s3',
8383
String mpyVersion = '1.28.0',
8484
int freeMem = 48000,
8585
String fsRoot = '/',
86-
String deviceId = '',
86+
String deviceId = '5646',
8787
String label = '',
88+
String agentVersion = '0.6.0',
8889
}) => DeviceInfo(
8990
chip: chip,
9091
mpyVersion: mpyVersion,
9192
freeMem: freeMem,
9293
fsRoot: fsRoot,
9394
deviceId: deviceId,
9495
label: label,
96+
agentVersion: agentVersion,
9597
);
9698

9799
/// A scripted [FakeConnection] in a given initial [ConnState].
98100
FakeConnection fakeConnection({
99101
ConnState initial = ConnState.disconnected,
100102
DeviceInfo? info,
101-
}) => FakeConnection(initial: initial, deviceInfo: info);
103+
}) => FakeConnection(initial: initial, deviceInfo: info ?? fakeDeviceInfo());
102104

103105
/// Pumps the real [PybleApp] shell at [surface], with the Connection seam
104106
/// overridden by a scripted [connection]. Resets the view on teardown.
@@ -127,6 +129,12 @@ Future<void> pumpShell(
127129
connectionFactory: (String id) async => connection,
128130
);
129131
addTearDown(manager.dispose);
132+
// A connected shell must exercise the same live manager facade production
133+
// uses, so connectControllerProvider can retain/fetch the DeviceInfo proof.
134+
if (connection.state.value == ConnState.ready ||
135+
connection.state.value == ConnState.running) {
136+
await manager.connect('shell-harness-board');
137+
}
130138

131139
await tester.pumpWidget(
132140
ProviderScope(

‎app/test/widget/app_shell_test.dart‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import 'package:pyble/app/providers.dart';
3030
import 'package:pyble/console/console.dart';
3131
import 'package:pyble/editor/editor.dart';
3232
import 'package:pyble/files/files.dart';
33+
import 'package:pyble/localization/localization.dart';
3334
import 'package:pyble/pble/pble.dart';
3435

3536
import '../support/shell_harness.dart';
@@ -55,6 +56,9 @@ const ValueKey<String> _pillKey = ValueKey<String>('connStatusPill');
5556
Finder _pillLabel(String word) =>
5657
find.descendant(of: find.byKey(_pillKey), matching: find.text(word));
5758

59+
AppLocalizations _l10n(WidgetTester tester) =>
60+
AppLocalizations.of(tester.element(find.byType(Scaffold).first));
61+
5862
/// The run-control toolbar action tooltips (ARB: toolbar{Run,Stop,SoftReboot}).
5963
/// These always render; their ENABLED state is state-driven from run-control
6064
/// (A-16, covered by run_toolbar_test.dart). The connection action is separate:
@@ -213,6 +217,73 @@ void main() {
213217
expect(_pillLabel('Ready'), findsOneWidget);
214218
expect(_pillLabel('Disconnected'), findsNothing);
215219
});
220+
221+
testWidgets(
222+
'connected pill retains board ID and firmware after Connect unmounts',
223+
(tester) async {
224+
await pumpShell(
225+
tester,
226+
connection: fakeConnection(
227+
initial: ConnState.ready,
228+
info: fakeDeviceInfo(deviceId: '5646', agentVersion: '0.6.0'),
229+
),
230+
surface: ipadLandscape,
231+
);
232+
233+
final AppLocalizations l10n = _l10n(tester);
234+
expect(
235+
find.descendant(
236+
of: find.byKey(_pillKey),
237+
matching: find.text(
238+
l10n.connStatusWithBoardInfo('Ready', '5646', '0.6.0'),
239+
),
240+
),
241+
findsOneWidget,
242+
);
243+
expect(
244+
find.bySemanticsLabel(
245+
l10n.connStatusBoardInfoSemanticLabel('Ready', '5646', '0.6.0'),
246+
),
247+
findsOneWidget,
248+
);
249+
expect(
250+
find.text(l10n.connectDeviceInfoTitle),
251+
findsNothing,
252+
reason: 'ADR-0011 replaces the Connect surface once ready',
253+
);
254+
},
255+
);
256+
257+
testWidgets(
258+
'connected pill reports empty additive metadata without gating',
259+
(tester) async {
260+
await pumpShell(
261+
tester,
262+
connection: fakeConnection(
263+
initial: ConnState.ready,
264+
info: fakeDeviceInfo(deviceId: '', agentVersion: ''),
265+
),
266+
surface: phonePortrait,
267+
);
268+
269+
final AppLocalizations l10n = _l10n(tester);
270+
expect(
271+
find.descendant(
272+
of: find.byKey(_pillKey),
273+
matching: find.text(
274+
l10n.connStatusWithBoardInfo(
275+
'Ready',
276+
l10n.connectDeviceNotReported,
277+
l10n.connectDeviceNotReported,
278+
),
279+
),
280+
),
281+
findsOneWidget,
282+
);
283+
expect(tester.takeException(), isNull);
284+
expect(find.byTooltip('Disconnect'), findsOneWidget);
285+
},
286+
);
216287
});
217288

218289
group('FR-UI-1 collapsible landscape Files sidebar', () {

0 commit comments

Comments
 (0)