import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:iop_console/iop_console.dart'; void main() { Future pumpConsole( WidgetTester tester, { required Size viewport, Widget? executionLogs, IopConsoleConfig? config, IopConsoleThemeAdapter? themeAdapter, IopConsoleNavigationCallback? onNavigate, IopCapabilityPack? capabilities, }) async { tester.view.physicalSize = viewport; tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); await tester.pumpWidget( MaterialApp( home: IopConsoleShell( overview: const Center(child: Text('Overview content')), executionLogs: executionLogs, config: config, themeAdapter: themeAdapter, onNavigate: onNavigate, capabilities: capabilities, ), ), ); } testWidgets('public shell renders without an app-owned console wrapper', ( tester, ) async { await pumpConsole(tester, viewport: const Size(1024, 768)); expect(find.text('Overview content'), findsOneWidget); expect(find.byIcon(Icons.dashboard_outlined), findsOneWidget); await tester.tap(find.byIcon(Icons.smart_toy_outlined)); await tester.pump(); expect(find.byType(IopAgentPanel), findsOneWidget); expect(find.text('Ask about IOP operations'), findsOneWidget); expect(tester.takeException(), isNull); }); testWidgets('rail and content remain stable on narrow and wide viewports', ( tester, ) async { for (final viewport in const [Size(320, 640), Size(1200, 800)]) { await pumpConsole(tester, viewport: viewport); expect(find.text('Overview content'), findsOneWidget); expect(find.byIcon(Icons.tune), findsOneWidget); expect(tester.getSize(find.byIcon(Icons.dashboard_outlined)).height, 24); expect(tester.takeException(), isNull); } }); testWidgets( 'supports navigation to executionLogs and displays injected widget', (tester) async { var navigatedSection = IopConsoleSection.overview; await pumpConsole( tester, viewport: const Size(1024, 768), executionLogs: const Center(child: Text('Custom Logs Content')), onNavigate: (section) => navigatedSection = section, ); // Tap on Execution & Logs rail item await tester.tap(find.byIcon(Icons.list_alt_outlined)); await tester.pump(); expect(navigatedSection, IopConsoleSection.executionLogs); expect(find.text('Custom Logs Content'), findsOneWidget); expect(tester.takeException(), isNull); }, ); testWidgets('injects capabilities and displays them in IopAgentPanel', ( tester, ) async { const pack = IopCapabilityPack( capabilities: [ IopCapability( name: 'Target Command Execution', description: 'Run targeted terminal automation commands.', ), IopCapability( name: 'Session Maintenance', description: 'Establish and clean up persistent CLI sessions.', ), ], ); await pumpConsole( tester, viewport: const Size(1024, 768), capabilities: pack, ); // Go to Agent Tab await tester.tap(find.byIcon(Icons.smart_toy_outlined)); await tester.pump(); // Verify capability details are visible in the introductory message text expect(find.textContaining('Target Command Execution'), findsOneWidget); expect(find.textContaining('Session Maintenance'), findsOneWidget); expect(tester.takeException(), isNull); }); testWidgets( 'IopConsoleOverview renders endpoints, status badge, and triggers refresh', (tester) async { tester.view.physicalSize = const Size(1024, 768); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); const config = IopConsoleConfig( controlPlaneHttpUrl: 'https://api.iop.test', controlPlaneWireUrl: 'wss://wire.iop.test', authTokenReference: 'cp-auth-ref-main', ); var refreshCount = 0; await tester.pumpWidget( MaterialApp( home: Scaffold( body: IopConsoleOverview( config: config, statusText: 'Connected to Control Plane', onRefresh: () => refreshCount++, ), ), ), ); // Check header and status badge expect(find.text('Operations Overview'), findsOneWidget); expect(find.text('CONNECTED TO CONTROL PLANE'), findsOneWidget); // Check config values expect(find.text('https://api.iop.test'), findsOneWidget); expect(find.text('wss://wire.iop.test'), findsOneWidget); // Check auth token reference key value expect(find.text('cp-auth-ref-main'), findsOneWidget); // Check refresh button functionality final refreshBtn = find.text('Refresh Connection'); expect(refreshBtn, findsOneWidget); await tester.tap(refreshBtn); await tester.pump(); expect(refreshCount, 1); expect(tester.takeException(), isNull); }, ); testWidgets('IopConsoleOverview handles disconnected status correctly', ( tester, ) async { tester.view.physicalSize = const Size(1024, 768); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); const config = IopConsoleConfig( controlPlaneHttpUrl: 'https://api.iop.test', controlPlaneWireUrl: 'wss://wire.iop.test', ); await tester.pumpWidget( MaterialApp( home: Scaffold( body: IopConsoleOverview(config: config, statusText: 'Disconnected'), ), ), ); // Verify it displays OFFLINE status text and DISCONNECTED status badge expect(find.text('DISCONNECTED'), findsOneWidget); expect(find.text('OFFLINE'), findsOneWidget); expect(tester.takeException(), isNull); }); testWidgets( 'IopConsoleOverview preserves narrow and wide responsive sections', (tester) async { const config = IopConsoleConfig( controlPlaneHttpUrl: 'https://api.iop.test', controlPlaneWireUrl: 'wss://wire.iop.test', authTokenReference: 'cp-auth-ref-main', ); // 400px width drives the narrow (< 600) layout; 1024px drives the wide one. for (final viewport in const [Size(400, 1200), Size(1024, 1200)]) { tester.view.physicalSize = viewport; tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); await tester.pumpWidget( MaterialApp( home: Scaffold( body: IopConsoleOverview( config: config, statusText: 'Connected to Control Plane', ), ), ), ); await tester.pump(); // Header, status badge, and endpoint sections render on both viewports. expect(find.text('Operations Overview'), findsOneWidget); expect(find.text('CONNECTED TO CONTROL PLANE'), findsOneWidget); expect(find.text('SYSTEM ENDPOINTS'), findsOneWidget); expect(find.text('https://api.iop.test'), findsOneWidget); expect(find.text('wss://wire.iop.test'), findsOneWidget); expect(tester.takeException(), isNull); } }, ); }