Files
clide/test/builtin/menubar/file_actions_test.dart
T
jpmschweitzerandClaude Opus 4.8 6d0ebab721 chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)
Raise the declared minimums in pubspec.yaml to what our deps already
require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist
0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is
the binding floor. Pin the exact build toolchain in .fvmrc (Flutter
3.44.1).

Moving to the Dart 3.9 language level switches `dart format` to the new
"tall" style and enables two new lints. This commit is the resulting
mechanical churn, isolated from any behaviour change:
  - whole-tree `dart format` reformat (tall style)
  - `dart fix` for unnecessary_underscores + use_null_aware_elements

No runtime behaviour change; `make test` green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-11 12:11:53 +02:00

78 lines
2.8 KiB
Dart

/// Tests for FileActions + the typed-path Open dialog (T-48). The open/close
/// paths drive real `git` via project.open, so they run as plain async tests
/// (no fake-async).
library;
import 'dart:io';
import 'package:clide/builtin/menubar/src/file_actions.dart';
import 'package:clide/extension/extension.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../helpers/kernel_fixture.dart';
void main() {
late KernelFixture f;
setUp(() async {
f = await KernelFixture.create();
// A landing tab so openPath's activateTab has a real target.
f.services.panels.contribute(TabContribution(id: 'claude.primary', slot: Slots.workspace, title: 'Claude', build: (_) => const SizedBox()));
});
tearDown(() => f.dispose());
test('openPath opens a git repo and activates the landing tab', () async {
final ok = await FileActions(f.services).openPath(Directory.current.path);
expect(ok, isTrue);
expect(f.services.project.isOpen, isTrue);
expect(f.services.panels.activeTabIn(Slots.workspace), 'claude.primary');
});
test('openPath returns false for a non-repo directory', () async {
final tmp = await Directory.systemTemp.createTemp('clide-fa-');
addTearDown(() => tmp.delete(recursive: true));
expect(await FileActions(f.services).openPath(tmp.path), isFalse);
});
test('closeWorkspace closes the active project', () async {
final fa = FileActions(f.services);
await fa.openPath(Directory.current.path);
expect(f.services.project.isOpen, isTrue);
fa.closeWorkspace();
expect(f.services.project.isOpen, isFalse);
});
Widget harness(Widget child) => Directionality(
textDirection: TextDirection.ltr,
child: ClideKernel(
services: f.services,
child: ClideTheme(
controller: f.services.theme,
child: MediaQuery(
data: const MediaQueryData(),
child: Align(alignment: Alignment.topLeft, child: child),
),
),
),
);
testWidgets('OpenFolderDialog submits the typed path via onOpen', (tester) async {
String? opened;
await tester.pumpWidget(harness(OpenFolderDialog(onOpen: (p) async => opened = p, onCancel: () {})));
await tester.enterText(find.byType(EditableText), '/some/repo');
await tester.tap(find.text('Open'));
await tester.pump();
expect(opened, '/some/repo');
});
testWidgets('OpenFolderDialog surfaces an error when onOpen throws', (tester) async {
await tester.pumpWidget(harness(OpenFolderDialog(onOpen: (_) async => throw StateError('not a repo'), onCancel: () {})));
await tester.enterText(find.byType(EditableText), '/bad');
await tester.tap(find.text('Open'));
await tester.pump();
expect(find.text('Not a git repository'), findsOneWidget);
});
}