test sweep: cover welcome_view tail paths (T-91)
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s
Eight new widget tests in test/builtin/welcome/widget_test.dart covering the WelcomeView paths the existing render-it test didn't reach: - TIPS card visibility: shown when viewport height > 640, hidden when shorter (LayoutBuilder branch). - Status line tri-state: "checking…" before toolchain resolution, "application ok" when all tools present, missing-tools listing when some are absent. - Theme-name link tap fires the theme.pick command via the registry. - Open folder tap with no native picker (returns null, function returns early) and with MissingPluginException (falls through to the path dialog via kernel.dialog.show). Coverage: builtin/welcome/src/welcome_view.dart 84/221 -> 120/221 (54%). Remaining 101 lines are inside the _OpenProjectDialog, _NotARepoDialog, and recent-project rows — they need either a DialogHost in the test harness or a populated recents list, which is materially more setup than the rest of this batch's pattern. Total coverage 79.34% -> 79.80%. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -2,8 +2,10 @@ import 'dart:ui';
|
|||||||
|
|
||||||
import 'package:clide/builtin/welcome/welcome.dart';
|
import 'package:clide/builtin/welcome/welcome.dart';
|
||||||
import 'package:clide/builtin/welcome/src/welcome_view.dart';
|
import 'package:clide/builtin/welcome/src/welcome_view.dart';
|
||||||
|
import 'package:clide/clide.dart';
|
||||||
import 'package:clide/extension/extension.dart';
|
import 'package:clide/extension/extension.dart';
|
||||||
import 'package:clide/kernel/kernel.dart';
|
import 'package:clide/kernel/kernel.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
import 'package:flutter_test/flutter_test.dart';
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
|
||||||
@@ -47,5 +49,107 @@ void main() {
|
|||||||
expect(find.text('IDE for Claude Code CLI'), findsOneWidget);
|
expect(find.text('IDE for Claude Code CLI'), findsOneWidget);
|
||||||
expect(find.text('Open folder…'), findsOneWidget);
|
expect(find.text('Open folder…'), findsOneWidget);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('TIPS card renders when the viewport is tall enough', (tester) async {
|
||||||
|
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();
|
||||||
|
expect(find.text('TIPS'), findsOneWidget);
|
||||||
|
expect(find.text('Quick open'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('TIPS card is hidden when the viewport is short', (tester) async {
|
||||||
|
tester.view.physicalSize = const Size(1200, 500);
|
||||||
|
tester.view.devicePixelRatio = 1.0;
|
||||||
|
addTearDown(tester.view.resetPhysicalSize);
|
||||||
|
addTearDown(tester.view.resetDevicePixelRatio);
|
||||||
|
await tester.pumpWidget(harness(f, const WelcomeView()));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('TIPS'), findsNothing);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('status line shows "checking…" before toolchain resolution', (tester) async {
|
||||||
|
await tester.pumpWidget(harness(f, const WelcomeView()));
|
||||||
|
expect(find.text('checking…'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('status line shows "application ok" when all tools resolved', (tester) async {
|
||||||
|
f.services.toolchain.applyResolved(const ResolvedPaths(
|
||||||
|
git: '/usr/bin/git',
|
||||||
|
pql: '/usr/bin/pql',
|
||||||
|
tmux: '/usr/bin/tmux',
|
||||||
|
shell: '/bin/bash',
|
||||||
|
));
|
||||||
|
await tester.pumpWidget(harness(f, const WelcomeView()));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.text('application ok'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('status line lists missing tools when some are absent', (tester) async {
|
||||||
|
tester.view.physicalSize = const Size(1600, 900);
|
||||||
|
tester.view.devicePixelRatio = 1.0;
|
||||||
|
addTearDown(tester.view.resetPhysicalSize);
|
||||||
|
addTearDown(tester.view.resetDevicePixelRatio);
|
||||||
|
f.services.toolchain.applyResolved(const ResolvedPaths(
|
||||||
|
pql: '/usr/bin/pql',
|
||||||
|
));
|
||||||
|
await tester.pumpWidget(harness(f, const WelcomeView()));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(find.textContaining('git not found'), findsOneWidget);
|
||||||
|
expect(find.textContaining('tmux not found'), findsOneWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('theme-name link fires the theme.pick command when tapped', (tester) async {
|
||||||
|
var invocations = 0;
|
||||||
|
f.services.commands.register(CommandContribution(
|
||||||
|
id: 'theme.pick',
|
||||||
|
command: 'theme.pick',
|
||||||
|
title: 'Theme: Pick',
|
||||||
|
run: (_) async {
|
||||||
|
invocations++;
|
||||||
|
return IpcResponse.ok(id: '', data: const {});
|
||||||
|
},
|
||||||
|
));
|
||||||
|
await tester.pumpWidget(harness(f, const WelcomeView()));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.textContaining('theme:'));
|
||||||
|
await tester.pump();
|
||||||
|
expect(invocations, 1);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Open folder tap kicks off the picker flow without throwing', (tester) async {
|
||||||
|
await tester.pumpWidget(harness(f, const WelcomeView()));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text('Open folder…'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
// In the test harness, the platform channel returns null (no native
|
||||||
|
// picker, no MissingPluginException), so the function returns
|
||||||
|
// early without raising. The point is just to exercise the path.
|
||||||
|
expect(tester.takeException(), isNull);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('Open folder opens the fallback dialog when the picker throws MissingPluginException', (tester) async {
|
||||||
|
// Pre-register a mock that throws — emulating a platform without
|
||||||
|
// native picker support.
|
||||||
|
tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||||
|
const MethodChannel('clide/window'),
|
||||||
|
(call) async {
|
||||||
|
if (call.method == 'pickDirectory') {
|
||||||
|
throw MissingPluginException();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
await tester.pumpWidget(harness(f, const WelcomeView()));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await tester.tap(find.text('Open folder…'));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(f.services.dialog.isOpen, isTrue);
|
||||||
|
f.services.dialog.dismiss();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user