138 lines
4.4 KiB
Dart
138 lines
4.4 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.byKey(const Key('project_page_header')), findsOneWidget);
|
|
expect(find.text('TermRemoteCtl'), findsOneWidget);
|
|
expect(find.text(r'C:\repo\termremotectl'), findsOneWidget);
|
|
expect(
|
|
find.text('Launch terminals in known working directories.'),
|
|
findsOneWidget,
|
|
);
|
|
expect(find.text('Stored terminal workspaces'), findsOneWidget);
|
|
expect(find.byKey(const Key('project_tile_project-1')), 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);
|
|
}
|
|
}
|