Release 1.0.3: refine tablet layouts, pointer selection and account sync cleanup
Build iOS / build (push) Successful in 4m45s
Build App Bundle / build (push) Successful in 7m7s

This commit is contained in:
Ayushya Amitabh
2026-10-09 00:36:42 -04:00
parent 79085874a1
commit 0a4608e6c3
67 changed files with 1740 additions and 451 deletions
+138
View File
@@ -0,0 +1,138 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:noo/widgets/noo/lists/noo_selection_bar.dart';
import 'package:noo/models/selection_action.dart';
import 'package:noo/widgets/noo/noo_layout.dart';
import 'package:noo/widgets/noo/media/noo_grid_card.dart';
import 'package:noo/widgets/noo/files/noo_file_table.dart';
import 'package:noo/widgets/noo/files/noo_file_kind.dart';
import 'noo_test_utils.dart';
import 'package:noo/widgets/noo/media/noo_photo_group.dart';
void main() {
setUpNooTests();
testWidgets('photo columns follow the pane rather than the tablet width', (
tester,
) async {
await tester.pumpWidget(
MaterialApp(
home: Center(
child: SizedBox(
width: 400,
height: 400,
child: CustomScrollView(
slivers: [
NooPhotoGrid(
minTileWidth: 150,
itemCount: 6,
itemBuilder: (_, index) =>
SizedBox(key: ValueKey('photo-$index')),
),
],
),
),
),
),
);
final first = tester.getRect(find.byKey(const ValueKey('photo-0')));
final third = tester.getRect(find.byKey(const ValueKey('photo-2')));
final fourth = tester.getRect(find.byKey(const ValueKey('photo-3')));
expect(first.width, greaterThan(100));
expect(third.right, lessThanOrEqualTo(first.left + 400));
expect(fourth.top, greaterThan(first.top));
expect(tester.takeException(), isNull);
});
for (final width in [320.0, 430.0, 700.0]) {
testWidgets('selection actions fit portrait tablet pane $width', (
tester,
) async {
await tester.pumpWidget(
MaterialApp(
theme: nooTheme(Brightness.light),
builder: (_, child) => Material(child: child),
home: Center(
child: SizedBox(
width: width,
child: NooSelectionBar(
count: 100,
isDesktop: true,
onClose: () {},
actions: [
for (final kind in SelectionActionKind.values)
SelectionAction(
kind: kind,
icon: kind.icon,
label: kind.label,
onTap: () {},
),
],
),
),
),
),
);
expect(tester.takeException(), isNull);
expect(find.text('100 selected'), findsOneWidget);
});
testWidgets('file cards fit pane width $width', (tester) async {
await tester.pumpWidget(
MaterialApp(
theme: nooTheme(Brightness.light),
builder: (_, child) => Material(child: child),
home: MediaQuery(
data: const MediaQueryData(
size: Size(768, 1024),
textScaler: TextScaler.linear(1.4),
),
child: Builder(
builder: (context) => Center(
child: SizedBox(
width: width,
height: 500,
child: GridView.builder(
gridDelegate: NooLayout.fileGridDelegate(context),
itemCount: 6,
itemBuilder: (_, index) => const NooGridCard(
name: 'Long file name.pdf',
meta: '2 MB',
thumbnailHeight: 118,
),
),
),
),
),
),
),
);
expect(tester.takeException(), isNull);
expect(
tester.getSize(find.byType(NooGridCard).first).height,
lessThan(200),
);
});
testWidgets('file table preserves name space at $width', (tester) async {
await tester.pumpWidget(
MaterialApp(
theme: nooTheme(Brightness.light),
builder: (_, child) => Material(child: child),
home: Center(
child: SizedBox(
width: width,
child: const NooFileTableRow(
kind: NooFileKind.folder,
name: 'Important project documents',
col2: 'Yesterday',
col3: 'Folder',
),
),
),
),
);
expect(tester.takeException(), isNull);
expect(
tester.getSize(find.text('Important project documents')).width,
greaterThan(80),
);
});
}
}
+35
View File
@@ -0,0 +1,35 @@
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:noo/widgets/noo/noo_layout.dart';
void main() {
for (final platform in [TargetPlatform.iOS, TargetPlatform.android]) {
for (final size in [
const Size(600, 960),
const Size(768, 1024),
const Size(1024, 768),
const Size(430, 932),
const Size(932, 430),
const Size(599, 1024),
]) {
testWidgets('$platform sidebar at $size', (tester) async {
bool? sidebar;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(platform: platform),
home: MediaQuery(
data: MediaQueryData(size: size),
child: Builder(
builder: (context) {
sidebar = NooLayout.isDesktop(context);
return const SizedBox();
},
),
),
),
);
expect(sidebar, size.shortestSide >= 600);
});
}
}
}
@@ -0,0 +1,52 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:noo/widgets/noo/core/noo_pointer_selection.dart';
void main() {
testWidgets('mouse reveals checkbox; clicking selects without opening item', (
tester,
) async {
var opened = 0;
var toggled = 0;
await tester.pumpWidget(
MaterialApp(
home: NooPointerSelection(
child: Scaffold(
body: GestureDetector(
onTap: () => opened++,
child: NooPointerCheckbox(
selected: false,
onToggle: () => toggled++,
),
),
),
),
),
);
expect(find.byType(Checkbox), findsNothing);
final mouse = await tester.createGesture(kind: PointerDeviceKind.mouse);
await mouse.addPointer(location: const Offset(10, 10));
await tester.pumpAndSettle();
expect(find.byType(Checkbox), findsOneWidget);
await tester.tap(find.byType(Checkbox));
expect(toggled, 1);
expect(opened, 0);
await mouse.removePointer();
await tester.pumpAndSettle();
expect(find.byType(Checkbox), findsNothing);
});
testWidgets('nonselectable items never show checkboxes', (tester) async {
await tester.pumpWidget(
const MaterialApp(
home: NooPointerSelection(child: NooPointerCheckbox(selected: false)),
),
);
final mouse = await tester.createGesture(kind: PointerDeviceKind.mouse);
await mouse.addPointer(location: const Offset(10, 10));
await tester.pumpAndSettle();
expect(find.byType(Checkbox), findsNothing);
await mouse.removePointer();
await tester.pumpAndSettle();
});
}