Files
clide/test/helpers/widget_harness.dart
T
jpmschweitzerandClaude Opus 4.8 1411e19ab4 ultrawide layout test coverage + shared setSurfaceSize helper (T-241)
Add setSurfaceSize(tester, width) to the widget harness — the reusable way to
test width-sensitive layout at an ultrawide surface (a wide SizedBox under the
default 800px surface is clamped, so the view's physicalSize must be set), the
foundation T-241 asked for.

Ultrawide cases on the surfaces most prone to width-proportional bugs (the T-239
class): ClideMarquee stays static when a line fits a 3440 slot (it was only
tested narrow); the quick-open palette stays width-capped, not stretched edge to
edge. The status bar already covers 600+3440 (T-239).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 22:34:16 +02:00

106 lines
4.1 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:clide/kernel/kernel.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'kernel_fixture.dart';
/// Wraps a widget in the minimum tree a primitive needs to resolve
/// theme + i18n + Overlay (for Draggable feedback / Tooltip / etc.):
/// `Directionality → ClideKernel → ClideTheme → MediaQuery →
/// Overlay → child`.
///
/// The Overlay is sized by the test view's bounds via the surrounding
/// MediaQuery; no extra SizedBox is added so existing tests that
/// query `find.byType(SizedBox).first` still find their target.
Widget harness(KernelFixture fixture, Widget child) {
return Directionality(
textDirection: TextDirection.ltr,
child: ClideKernel(
services: fixture.services,
child: ClideTheme(
controller: fixture.services.theme,
child: MediaQuery(
data: const MediaQueryData(),
child: Overlay(
initialEntries: [
OverlayEntry(
canSizeOverlay: true,
builder: (_) => child,
),
],
),
),
),
),
);
}
/// Harness for **anchored-overlay content** (ClideAnchoredOverlay popovers).
///
/// The shared [harness] wraps its child in `Overlay(canSizeOverlay)` + a
/// zero-size `MediaQuery` — fine for plain widgets, but it mispositions an
/// anchored follower off-screen and defeats `autoFlip`, so popover items aren't
/// reliably hit-testable. This builds a properly-sized Overlay tree instead:
/// `Directionality → ClideKernel → ClideTheme → MediaQuery(size) → Overlay`,
/// with [child] (the trigger) placed at [alignment]. Set
/// `tester.view.physicalSize = size` to match (the default 800×600 matches the
/// default test view, so no setup is needed unless you change [size]).
Widget anchoredHarness(
KernelFixture fixture,
Widget child, {
Size size = const Size(800, 600),
Alignment alignment = Alignment.topLeft,
}) {
return Directionality(
textDirection: TextDirection.ltr,
child: ClideKernel(
services: fixture.services,
child: ClideTheme(
controller: fixture.services.theme,
child: MediaQuery(
data: MediaQueryData(size: size),
child: Overlay(
initialEntries: [
OverlayEntry(builder: (_) => Align(alignment: alignment, child: child)),
],
),
),
),
),
);
}
/// Settle async-driven UI in a widget test WITHOUT the two patterns that have
/// repeatedly wedged this suite:
///
/// - **Never `pumpAndSettle()`** — it loops until the frame queue is quiescent,
/// so a perpetual animation or overlapping async loads hang it for its
/// ~10-minute default timeout (which wedged the pre-push gate).
/// - **Never `await Future.delayed(Duration.zero)`** — inside the fake-async
/// `testWidgets` zone a real timer never fires unless fake time is advanced,
/// so that line wedges the test until timeout (and even defeats `--timeout`).
///
/// Instead: pump one frame (draining the microtask queue — broadcast-stream and
/// async-IPC deliveries resolve here), then advance a tiny fake-time tick to
/// flush any follow-up `setState`. Bounded by construction — it cannot hang.
/// Use this after publishing a message / triggering a load in a reader/panel
/// widget test, in place of `pumpAndSettle`.
Future<void> pumpAsync(WidgetTester tester) async {
await tester.pump();
await tester.pump(const Duration(milliseconds: 20));
}
/// Size the test surface to [width]×[height] for width-sensitive layout checks
/// (T-241). A wide SizedBox under the default 800px surface is CLAMPED, so the
/// view's physicalSize must be set directly. Auto-resets after the test. Pass an
/// ultrawide width (e.g. 3440) alongside a normal one to catch width-PROPORTIONAL
/// bugs that hide at 800px (the T-239 class of bug).
void setSurfaceSize(WidgetTester tester, double width, {double height = 800}) {
tester.view.physicalSize = Size(width, height);
tester.view.devicePixelRatio = 1.0;
addTearDown(() {
tester.view.resetPhysicalSize();
tester.view.resetDevicePixelRatio();
});
}