mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-10 17:47:24 +01:00
Wire design_system into CI, and drop EmptyState's speculative action slot
The pre-push coverage gate only ran for whoever had the local hook installed, which is deployment hygiene rather than a gate. Adds the mirrored workflow sync_settings_ui already has, so the 100% bar is enforced on push and PR. EmptyState.action was invented surface: no donor had it. home_inventory's three call sites are pure placeholders, and kuhylog's action-bearing _empty was explicitly excluded from the survey as a different element. Shipping a parameter nobody asked for on a widget that has no consumer yet is the failure mode this package is supposed to avoid, so it goes, with a comment recording why rather than leaving the next reader to rediscover it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XS1m7b1piBtAL22USLW6jL
This commit is contained in:
parent
cae0870fd0
commit
522fa1dff0
36
.github/workflows/design-system-tests.yml
vendored
Normal file
36
.github/workflows/design-system-tests.yml
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
name: design_system tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['design_system/**']
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths: ['design_system/**']
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: design_system
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Flutter
|
||||
uses: subosito/flutter-action@v2
|
||||
with:
|
||||
channel: stable
|
||||
|
||||
- name: Install dependencies
|
||||
run: flutter pub get
|
||||
|
||||
- name: Analyze
|
||||
run: flutter analyze
|
||||
|
||||
- name: Run tests with coverage
|
||||
run: flutter test --coverage
|
||||
|
||||
- name: Enforce 100% line coverage
|
||||
run: scripts/check_coverage.sh coverage/lcov.info 100
|
||||
@ -14,7 +14,6 @@ class EmptyState extends StatelessWidget {
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.message,
|
||||
this.action,
|
||||
super.key,
|
||||
});
|
||||
|
||||
@ -27,15 +26,14 @@ class EmptyState extends StatelessWidget {
|
||||
/// One line explaining what to do about it.
|
||||
final String message;
|
||||
|
||||
/// Optional call to action below the message — usually the button that
|
||||
/// creates the first item. Omit it where the empty state is a *filter*
|
||||
/// result rather than an empty collection: there is nothing to create.
|
||||
final Widget? action;
|
||||
// Deliberately no `action` slot. No donor had one: home_inventory's three
|
||||
// call sites are pure placeholders, and kuhylog's action-bearing `_empty`
|
||||
// is a different element. Adding one here would be speculative surface --
|
||||
// the widget nobody imports, one parameter down.
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
final action = this.action;
|
||||
return Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(AppSpacing.xl),
|
||||
@ -57,10 +55,6 @@ class EmptyState extends StatelessWidget {
|
||||
color: theme.colorScheme.onSurfaceVariant,
|
||||
),
|
||||
),
|
||||
if (action != null) ...[
|
||||
const SizedBox(height: AppSpacing.lg),
|
||||
action,
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -2,16 +2,15 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
Future<void> _pumpEmptyState(WidgetTester tester, {Widget? action}) {
|
||||
Future<void> _pumpEmptyState(WidgetTester tester) {
|
||||
return tester.pumpWidget(
|
||||
MaterialApp(
|
||||
theme: buildDarkTheme(),
|
||||
home: Scaffold(
|
||||
home: const Scaffold(
|
||||
body: EmptyState(
|
||||
icon: Icons.inbox_outlined,
|
||||
title: 'Nothing here yet',
|
||||
message: 'Add your first item to get started.',
|
||||
action: action,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -49,17 +48,11 @@ void main() {
|
||||
expect(title.style?.color, isNot(scheme.onSurfaceVariant));
|
||||
});
|
||||
|
||||
testWidgets('omits the action slot by default', (tester) async {
|
||||
testWidgets('is a pure placeholder with no action', (tester) async {
|
||||
// Deliberate: no donor had an action slot, so adding one would be
|
||||
// speculative surface on a widget that has no consumer yet.
|
||||
await _pumpEmptyState(tester);
|
||||
expect(find.byType(ElevatedButton), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('renders an action when one is given', (tester) async {
|
||||
await _pumpEmptyState(
|
||||
tester,
|
||||
action: ElevatedButton(onPressed: () {}, child: const Text('Add item')),
|
||||
);
|
||||
expect(find.text('Add item'), findsOneWidget);
|
||||
expect(find.byType(ButtonStyleButton), findsNothing);
|
||||
});
|
||||
|
||||
testWidgets('stays centred and shrink-wrapped', (tester) async {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user