feat(claude): account login pane — host claude login in a modal terminal (T-485)
Completes the `login` verb + the UI add/re-login affordances. The accountActionChannel 'login' action opens ClaudeLoginDialog — a modal hosting a TerminalPane that runs `CLAUDE_CONFIG_DIR=<dir> claude login`, so the CLI drives the OAuth browser flow and credentials land in that account's config dir (D-64: one CLI-initiated browser flow, on explicit action). TerminalPane gains optional argv/env/cwdOverride (default stays the login shell), and its pane.spawn carries env when set. The login pane reuses the TerminalPane *widget* — a code import, not a runtime dependency, since it spawns through the always-present pane.spawn IPC rather than the terminal extension. Tests cover the parameterized spawn args and the dialog's host wiring (title, CLAUDE_CONFIG_DIR, close). Closes T-485. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
/// T-485: the account login dialog hosts `claude login` in a terminal pane with
|
||||
/// the account's CLAUDE_CONFIG_DIR, and its close affordance dismisses. The CLI
|
||||
/// owns the OAuth flow; this verifies the host wiring (title, env, close).
|
||||
library;
|
||||
|
||||
import 'package:clide/builtin/claude/src/account_login_dialog.dart';
|
||||
import 'package:clide/builtin/terminal/src/terminal_pane.dart';
|
||||
import 'package:clide/clide.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../helpers/kernel_fixture.dart';
|
||||
import '../../helpers/widget_harness.dart';
|
||||
|
||||
void main() {
|
||||
late KernelFixture fixture;
|
||||
setUp(() async => fixture = await KernelFixture.create());
|
||||
tearDown(() async => fixture.dispose());
|
||||
|
||||
testWidgets('renders the account title, spawns claude login with the config dir, and closes', (tester) async {
|
||||
var closed = false;
|
||||
Map<String, Object?>? spawnArgs;
|
||||
fixture.ipc.setConnected(true);
|
||||
fixture.ipc.stub('pane.spawn', (args) async {
|
||||
spawnArgs = args;
|
||||
return IpcResponse.ok(id: 'r1', data: {'id': 'p1', 'pid': 1});
|
||||
});
|
||||
fixture.ipc.stub('pane.close', (args) async => IpcResponse.ok(id: 'r2'));
|
||||
|
||||
await tester.pumpWidget(harness(fixture, ClaudeLoginDialog(name: 'work', dir: '/home/u/.claude-work', onClose: () => closed = true)));
|
||||
await pumpAsync(tester);
|
||||
|
||||
expect(find.text('Sign in: work'), findsOneWidget);
|
||||
final pane = tester.widget<TerminalPane>(find.byType(TerminalPane));
|
||||
expect(pane.argv, ['claude', 'login']);
|
||||
expect(pane.env, {'CLAUDE_CONFIG_DIR': '/home/u/.claude-work'});
|
||||
expect(spawnArgs?['env'], {'CLAUDE_CONFIG_DIR': '/home/u/.claude-work'});
|
||||
|
||||
await tester.tap(find.byKey(const Key('account-login-close')));
|
||||
await tester.pump();
|
||||
expect(closed, isTrue);
|
||||
});
|
||||
}
|
||||
@@ -75,6 +75,25 @@ void main() {
|
||||
expect(spawnedCwd, isNot(Directory.current.path));
|
||||
});
|
||||
|
||||
testWidgets('a caller-supplied argv + env + cwd reach pane.spawn (T-485)', (tester) async {
|
||||
Map<String, Object?>? spawnArgs;
|
||||
fixture.ipc.setConnected(true);
|
||||
fixture.ipc.stub('pane.spawn', (args) async {
|
||||
spawnArgs = args;
|
||||
return IpcResponse.ok(id: 'r1', data: {'id': 'pane-1', 'pid': 1});
|
||||
});
|
||||
fixture.ipc.stub('pane.close', (args) async => IpcResponse.ok(id: 'r2'));
|
||||
|
||||
await tester.pumpWidget(
|
||||
harness(fixture, const TerminalPane(argv: ['claude', 'login'], env: {'CLAUDE_CONFIG_DIR': '/home/u/.claude-work'}, cwdOverride: '/repo')),
|
||||
);
|
||||
await pumpAsync(tester);
|
||||
|
||||
expect(spawnArgs?['argv'], ['claude', 'login']);
|
||||
expect(spawnArgs?['env'], {'CLAUDE_CONFIG_DIR': '/home/u/.claude-work'});
|
||||
expect(spawnArgs?['cwd'], '/repo');
|
||||
});
|
||||
|
||||
testWidgets('disposing before spawn completes sends no close', (tester) async {
|
||||
final closed = <String>[];
|
||||
fixture.ipc.setConnected(false); // spawn bails out: no pane id
|
||||
|
||||
Reference in New Issue
Block a user