TermRemoteCtl/apps/mobile_app/test/project_home_test.dart

131 lines
4.1 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:term_remote_ctl/app/app.dart';
import 'package:term_remote_ctl/core/network/agent_api_client.dart';
import 'package:term_remote_ctl/core/network/agent_connection_providers.dart';
import 'package:term_remote_ctl/features/projects/project.dart';
import 'package:term_remote_ctl/features/projects/project_repository.dart';
import 'package:term_remote_ctl/features/sessions/session.dart';
import 'package:term_remote_ctl/features/sessions/session_repository.dart';
import 'package:term_remote_ctl/features/terminal/terminal_socket_session.dart';
void main() {
testWidgets(
'home renders projects and opens a new terminal from a project card',
(tester) async {
final projectRepository = _FakeProjectRepository();
final sessionRepository = _FakeSessionRepository();
final socketFactory = TerminalSocketSessionFactory(
transportFactory: (_) => _FakeTerminalSocketTransport(autoAttach: true),
);
await tester.pumpWidget(
ProviderScope(
overrides: [
agentApiClientProvider.overrideWithValue(_FakeAgentApiClient()),
projectRepositoryProvider.overrideWithValue(projectRepository),
sessionRepositoryProvider.overrideWithValue(sessionRepository),
terminalSocketSessionFactoryProvider.overrideWithValue(
socketFactory,
),
],
child: const TermRemoteCtlApp(),
),
);
await tester.pumpAndSettle();
expect(find.text('Projects'), findsOneWidget);
expect(find.text('TermRemoteCtl'), findsOneWidget);
expect(find.text(r'C:\repo\termremotectl'), findsOneWidget);
await tester.tap(find.widgetWithText(FilledButton, 'Open terminal'));
await tester.pumpAndSettle();
expect(sessionRepository.lastCreatedProjectId, 'project-1');
expect(find.text('TermRemoteCtl'), findsOneWidget);
expect(find.text(r'C:\repo\termremotectl'), findsOneWidget);
},
);
}
class _FakeProjectRepository extends ProjectRepository {
_FakeProjectRepository()
: _projects = [
Project(
projectId: 'project-1',
name: 'TermRemoteCtl',
workingDirectory: r'C:\repo\termremotectl',
createdAtUtc: DateTime.parse('2026-03-30T10:00:00Z'),
updatedAtUtc: DateTime.parse('2026-03-30T10:00:00Z'),
),
],
super(_FakeAgentApiClient());
final List<Project> _projects;
@override
Future<List<Project>> listProjects() async => List<Project>.of(_projects);
}
class _FakeSessionRepository extends SessionRepository {
_FakeSessionRepository() : super(_FakeAgentApiClient());
String? lastCreatedProjectId;
@override
Future<Session> createSession({
String? name,
String? projectId,
String? workingDirectory,
}) async {
lastCreatedProjectId = projectId;
return Session(
sessionId: 'session-1',
name: 'TermRemoteCtl',
status: 'created',
projectId: projectId,
workingDirectory: workingDirectory ?? r'C:\repo\termremotectl',
createdAtUtc: DateTime.parse('2026-03-30T10:00:00Z'),
updatedAtUtc: DateTime.parse('2026-03-30T10:00:00Z'),
);
}
@override
Future<List<Session>> listSessions() async => const <Session>[];
}
class _FakeAgentApiClient extends AgentApiClient {
_FakeAgentApiClient() : super(Uri.parse('http://10.0.2.2:5067'));
}
class _FakeTerminalSocketTransport implements TerminalSocketTransport {
_FakeTerminalSocketTransport({this.autoAttach = false}) {
if (autoAttach) {
Future<void>.microtask(() {
emit('{"type":"attached","sessionId":"session-1"}');
});
}
}
final bool autoAttach;
final _incoming = StreamController<dynamic>.broadcast();
@override
Stream<dynamic> get stream => _incoming.stream;
@override
void send(String message) {}
@override
Future<void> close() async {
await _incoming.close();
}
void emit(String message) {
_incoming.add(message);
}
}