Files
clide/test/builtin/welcome/dialog_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

114 lines
5.0 KiB
Dart

/// Tests for the welcome view's open-project + not-a-repo dialogs.
/// Uses a custom harness that wraps the standard widget harness in a
/// DialogHost so kernel.dialog.show calls actually render their
/// builder into the tree.
library;
import 'package:clide/builtin/welcome/src/welcome_view.dart';
import 'package:clide/kernel/kernel.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import '../../helpers/kernel_fixture.dart';
import '../../helpers/widget_harness.dart';
/// Wraps [harness] in a DialogHost rooted on the fixture's dialog
/// router so kernel.dialog.show(...) calls render into the tree.
Widget _harness(KernelFixture f, Widget child) {
return harness(f, DialogHost(router: f.services.dialog, child: child));
}
void main() {
group('WelcomeView dialogs', () {
late KernelFixture f;
setUp(() async {
f = await KernelFixture.create(
i18nCatalogs: {
'builtin.welcome': {
const Locale('en', 'US'): const {
'title': {'translation': 'clide'},
'subtitle': {'translation': 'IDE'},
'open-project': {'translation': 'Open project'},
'open-project.hint': {'translation': 'Pick a git repository'},
'tab.title': {'translation': 'Welcome'},
},
},
},
);
});
tearDown(() async => f.dispose());
testWidgets('MissingPluginException path renders the OpenProjectDialog', (tester) async {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(const MethodChannel('clide/window'), (call) async {
if (call.method == 'pickDirectory') {
throw MissingPluginException();
}
return null;
});
tester.view.physicalSize = const Size(1200, 800);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(_harness(f, const WelcomeView()));
await tester.pumpAndSettle();
await tester.tap(find.text('Open folder…'));
await tester.pumpAndSettle();
expect(find.text('Open project'), findsOneWidget);
expect(find.text('Enter the path to a git repository.'), findsOneWidget);
expect(find.text('Cancel'), findsOneWidget);
expect(find.text('Open'), findsOneWidget);
});
testWidgets('OpenProjectDialog Cancel dismisses the modal', (tester) async {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(const MethodChannel('clide/window'), (call) async => throw MissingPluginException());
tester.view.physicalSize = const Size(1200, 800);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(_harness(f, const WelcomeView()));
await tester.pumpAndSettle();
await tester.tap(find.text('Open folder…'));
await tester.pumpAndSettle();
await tester.tap(find.text('Cancel'));
await tester.pumpAndSettle();
expect(find.text('Open project'), findsNothing);
});
testWidgets('OpenProjectDialog Open with empty path is a no-op', (tester) async {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(const MethodChannel('clide/window'), (call) async => throw MissingPluginException());
tester.view.physicalSize = const Size(1200, 800);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(_harness(f, const WelcomeView()));
await tester.pumpAndSettle();
await tester.tap(find.text('Open folder…'));
await tester.pumpAndSettle();
// Press Open with the text field empty.
await tester.tap(find.text('Open'));
await tester.pumpAndSettle();
// Dialog stays.
expect(find.text('Open project'), findsOneWidget);
});
testWidgets('OpenProjectDialog Open with a non-repo path keeps the dialog (project.open returns false)', (tester) async {
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(const MethodChannel('clide/window'), (call) async => throw MissingPluginException());
tester.view.physicalSize = const Size(1200, 800);
tester.view.devicePixelRatio = 1.0;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
await tester.pumpWidget(_harness(f, const WelcomeView()));
await tester.pumpAndSettle();
await tester.tap(find.text('Open folder…'));
await tester.pumpAndSettle();
await tester.enterText(find.byType(EditableText).last, '/tmp/clide-not-a-repo-${DateTime.now().microsecondsSinceEpoch}');
await tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pumpAndSettle();
// project.open returns false → dialog stays open, "Opening…" no
// longer shows.
expect(find.text('Open project'), findsOneWidget);
});
});
}