animate the running turn indicator with rotating verbs (T-255)
The static gray `running…` gave no sense anything was happening. Replace it with a RunningIndicator: an animated ellipsis (Pondering → . → .. → ...) and a curated, on-brand verb that rotates every few seconds (Pondering, Conjuring, Brewing, …). The verbs are clide-owned, not the Claude Code CLI's — that list is a TUI cosmetic the stream-json protocol doesn't surface, and reusing the bundled strings is a licensing gray area, so a curated list keeps us self-contained (own-the-rendering-stack, D-75). Animation is driven off a single AnimationController's value (no timers) so tests advance it with bounded pumps; reduced-motion shows a static verb and the a11y label stays stable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
/// T-255: RunningIndicator — animated ellipsis + rotating verb, reduced-motion
|
||||
/// fallback. Driven off the AnimationController's value, so the test advances
|
||||
/// it with bounded pumps (no real timers).
|
||||
library;
|
||||
|
||||
import 'package:clide/builtin/claude/src/running_indicator.dart';
|
||||
import 'package:clide/widgets/widgets.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../../helpers/kernel_fixture.dart';
|
||||
import '../../helpers/widget_harness.dart';
|
||||
|
||||
String? _text(WidgetTester t) {
|
||||
final w = t.widget<ClideText>(find.byType(ClideText));
|
||||
return w.data;
|
||||
}
|
||||
|
||||
void main() {
|
||||
late KernelFixture f;
|
||||
setUp(() async => f = await KernelFixture.create());
|
||||
tearDown(() => f.dispose());
|
||||
|
||||
Widget wrap({bool reducedMotion = false}) => MediaQuery(
|
||||
data: MediaQueryData(disableAnimations: reducedMotion),
|
||||
child: const RunningIndicator(),
|
||||
);
|
||||
|
||||
testWidgets('animates the ellipsis and rotates the verb', (tester) async {
|
||||
await tester.pumpWidget(harness(f, wrap()));
|
||||
await tester.pump();
|
||||
expect(_text(tester), 'Pondering'); // t≈0: first verb, no dots
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 1100));
|
||||
expect(_text(tester), 'Pondering.'); // ~1.1s → 1 dot
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 1100));
|
||||
expect(_text(tester), 'Pondering..'); // ~2.2s → 2 dots
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 2000));
|
||||
expect(_text(tester), 'Conjuring'); // ~4.2s → second verb, dots wrapped to 0
|
||||
|
||||
// Dispose the infinite animation before the test ends.
|
||||
await tester.pumpWidget(harness(f, const SizedBox()));
|
||||
});
|
||||
|
||||
testWidgets('reduced motion shows a static verb that does not change', (tester) async {
|
||||
await tester.pumpWidget(harness(f, wrap(reducedMotion: true)));
|
||||
await tester.pump();
|
||||
expect(_text(tester), 'Pondering…');
|
||||
await tester.pump(const Duration(seconds: 6));
|
||||
expect(_text(tester), 'Pondering…'); // unchanged — no animation running
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user