Files
Garage-SDKs/garage_ui/test/property_row_copy_test.dart
ImBenjiandClaude Opus 5.5 b269201919 The Garage SDKs, in the open
garage_auth, garage_entitlements, garage_iap and garage_ui, moved out of
Garage-Services and Metro-Map-Maker into one public repo. MIT, one readme,
docs under docs/.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013F4NWNvYcdeSgqbWMT1VQ7
2026-09-23 18:49:21 +01:00

188 lines
6.3 KiB
Dart

import "package:flutter/widgets.dart";
import "package:flutter_test/flutter_test.dart";
import "package:garage_ui/garage_ui.dart";
import "support/test_scheme.dart";
// A property row used to be label + control and nothing else, so a page
// needing to explain a setting had to either fold it into the label or give up
// and hardcode a Text somewhere. Two slots now:
//
// subtitle - inside the label column, right aligned against the split
// description - full width under the whole row
//
// Both render at density.textXxs, which is the rung below the control font -
// it didnt exist before either, because in A&A the control font is already the
// smallest thing on screen.
Widget _host(Widget child, {Density density = const Density()}) => Directionality(
textDirection: TextDirection.ltr,
child: GarageTheme(
data: ThemeData(colorScheme: testScheme, density: density),
child: Center(child: SizedBox(width: 400, child: child)),
),
);
double _fontOf(WidgetTester tester, String text) {
final widget = tester.widget<Text>(find.text(text));
return widget.style!.fontSize!;
}
void main() {
testWidgets("subtitle and description both render at textXxs", (
tester,
) async {
await tester.pumpWidget(
_host(
PropertyRow(
label: "Username",
subtitle: "Display name used across dashboard",
description: "Changing this breaks any links to your old name.",
scheme: testScheme,
child: const SizedBox(),
),
),
);
const density = Density();
expect(density.textXxs, 9.0); // 10 * 0.875, rounded
expect(_fontOf(tester, "Display name used across dashboard"), 9.0);
expect(_fontOf(tester, "Changing this breaks any links to your old name."),
9.0);
});
testWidgets("both slots track the density", (tester) async {
await tester.pumpWidget(
_host(
PropertyRow(
label: "Username",
subtitle: "a subtitle",
scheme: testScheme,
child: const SizedBox(),
),
density: const Density(fontSize: 16.0, lineHeight: 1.25),
),
);
expect(_fontOf(tester, "a subtitle"), 14.0); // 16 * 0.875
});
testWidgets("a description makes the row taller, not the control shorter", (
tester,
) async {
Future<double> heightWith({String? description}) async {
await tester.pumpWidget(
_host(
PropertyRow(
key: const Key("row"),
label: "Username",
description: description,
scheme: testScheme,
child: const SizedBox(),
),
),
);
// the row eases between heights now, so the frame straight after the
// pump is still the old one.
await tester.pumpAndSettle();
return tester.getSize(find.byKey(const Key("row"))).height;
}
final bare = await heightWith();
final withCopy = await heightWith(description: "some copy");
// the bare row is the row minimum - controlHeight plus a step, so that
// neither the control nor the label column is the thing setting it. a
// description adds on top of that rather than squeezing into it.
expect(bare, const Density().propertyRowHeight);
expect(withCopy, greaterThan(bare));
});
testWidgets("the gap between section rows is gapXs, off the density", (
tester,
) async {
// the numbers PropertiesSection used to hardcode - 4 between rows, 8 under
// the last - are exactly gapXs and gapMd at compact, which is the tell they
// were always meant to be these tokens. at normal they should now move.
const compact = Density();
const normal = Density.normal();
expect(compact.gapXs, 4.0);
expect(normal.gapXs, 5.0);
Future<double> gapBetweenRows(Density density) async {
await tester.pumpWidget(
_host(
PropertiesSection(
title: "Profile",
scheme: testScheme,
collapsed: false,
onToggle: () {},
rows: [
for (var i = 0; i < 2; i++)
PropertyRow(
key: Key("row$i"),
label: "Row $i",
scheme: testScheme,
child: const SizedBox(),
),
],
),
density: density,
),
);
final first = tester.getRect(find.byKey(const Key("row0")));
final second = tester.getRect(find.byKey(const Key("row1")));
return second.top - first.bottom;
}
expect(await gapBetweenRows(compact), compact.gapXs);
expect(await gapBetweenRows(normal), normal.gapXs);
});
testWidgets("a subtitle doesnt change the row height", (tester) async {
// the whole reason the minimum sits above controlHeight. a label plus a
// subtitle is laid out on the font's own metrics; at plain controlHeight
// that made the LABEL COLUMN set the row height on some fonts and not
// others. both rows have to come out the same.
Future<double> h({String? subtitle}) async {
await tester.pumpWidget(
_host(
PropertyRow(
key: const Key("row"),
label: "Username",
subtitle: subtitle,
scheme: testScheme,
child: const TextField(),
),
),
);
// the row eases between heights now, so the frame straight after the
// pump is still the old one.
await tester.pumpAndSettle();
return tester.getSize(find.byKey(const Key("row"))).height;
}
expect(await h(subtitle: "a subtitle"), await h());
});
test("the row minimum clears whichever of the two is taller", () {
// at compact the LABEL COLUMN is the binding one - 26 against a 23px
// control - which is the whole reason this token exists. at the roomier
// tiers the control wins again. either way the row clears both, so a
// subtitle never changes the height.
const compact = Density();
expect(compact.labelColumnHeight, 26.0);
expect(compact.controlHeight, 23.0);
expect(compact.propertyRowHeight, 28.0);
const normal = Density.normal();
expect(normal.labelColumnHeight, 26.0);
expect(normal.propertyRowHeight, normal.controlHeight + normal.gapXxs);
for (final d in [compact, normal]) {
expect(d.propertyRowHeight, greaterThan(d.labelColumnHeight));
expect(d.propertyRowHeight, greaterThan(d.controlHeight));
}
});
}