CI - Sanity Checks / Code Analysis (push) Successful in 5m36s
CI - Sanity Checks / Unit Tests (push) Failing after 13m36s
CI - Sanity Checks / Android JVM and Native Tests (push) Failing after 1m13s
CI - Sanity Checks / Native Formatting (push) Failing after 22s
CI - Sanity Checks / Linux native reliability (thread) (push) Failing after 2m59s
CI - Sanity Checks / Linux native reliability (address) (push) Failing after 2m57s
CI - Sanity Checks / Apple native reliability (iOS) (push) Canceled after 0s
CI - Sanity Checks / Apple native reliability (macOS) (push) Canceled after 0s
CI - Sanity Checks / Apple native reliability (tvOS) (push) Canceled after 0s
CI - Sanity Checks / Windows native reliability (arm64) (push) Canceled after 0s
CI - Sanity Checks / Windows native reliability (x64) (push) Canceled after 0s
CI - Sanity Checks / Dependency Validation (push) Successful in 1m9s
CI - Sanity Checks / Server checks (push) Successful in 28s
CI - Sanity Checks / Website checks (push) Successful in 1m22s
CI - Sanity Checks / Linux package smoke build (push) Failing after 6m41s
Physical-keyboard Escape at root Home now exits window fullscreen on Windows and Linux the way it already did on macOS, and never arms the press-back-again quit — so Escape aimed at fullscreen can't close the app. Remotes, gamepad B, and system back keep the double-press exit. close #1748
197 lines
6.8 KiB
Dart
197 lines
6.8 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter/services.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:plezy/screens/main_screen.dart';
|
|
import 'package:plezy/widgets/side_navigation_rail.dart';
|
|
|
|
void main() {
|
|
test('side navigation pushes stable foreground off-screen while temporarily expanded', () {
|
|
const viewportWidth = 1280.0;
|
|
const reservedWidth = SideNavigationRailState.tvCollapsedWidth;
|
|
|
|
final collapsed = mainScreenSideNavigationContentLayout(
|
|
viewportWidth: viewportWidth,
|
|
currentSideNavigationWidth: SideNavigationRailState.tvCollapsedWidth,
|
|
reservedSideNavigationWidth: reservedWidth,
|
|
);
|
|
final expanded = mainScreenSideNavigationContentLayout(
|
|
viewportWidth: viewportWidth,
|
|
currentSideNavigationWidth: SideNavigationRailState.expandedWidth,
|
|
reservedSideNavigationWidth: reservedWidth,
|
|
);
|
|
|
|
expect(collapsed.width, viewportWidth - SideNavigationRailState.tvCollapsedWidth);
|
|
expect(expanded.width, collapsed.width);
|
|
expect(collapsed.left, SideNavigationRailState.tvCollapsedWidth);
|
|
expect(expanded.left, SideNavigationRailState.expandedWidth);
|
|
expect(collapsed.left + collapsed.width, viewportWidth);
|
|
expect(expanded.left + expanded.width, viewportWidth + SideNavigationRailState.expandedWidth - reservedWidth);
|
|
});
|
|
|
|
test('side navigation reserves expanded width when always open', () {
|
|
const viewportWidth = 1280.0;
|
|
|
|
final expanded = mainScreenSideNavigationContentLayout(
|
|
viewportWidth: viewportWidth,
|
|
currentSideNavigationWidth: SideNavigationRailState.expandedWidth,
|
|
reservedSideNavigationWidth: SideNavigationRailState.expandedWidth,
|
|
);
|
|
|
|
expect(expanded.left, SideNavigationRailState.expandedWidth);
|
|
expect(expanded.width, viewportWidth - SideNavigationRailState.expandedWidth);
|
|
expect(expanded.left + expanded.width, viewportWidth);
|
|
});
|
|
|
|
test('tvOS Menu pass-through only enables at root with sidebar focus', () {
|
|
bool shouldPass({
|
|
bool isAppleTV = true,
|
|
bool isShowingProfileSelection = false,
|
|
bool isOverlaySheetOpen = false,
|
|
bool isRouteCurrent = true,
|
|
bool isSidebarFocused = true,
|
|
bool hasVisibleTabs = true,
|
|
bool isCurrentTabRoot = true,
|
|
}) {
|
|
return shouldPassTvosMenuToSystem(
|
|
isAppleTV: isAppleTV,
|
|
isShowingProfileSelection: isShowingProfileSelection,
|
|
isOverlaySheetOpen: isOverlaySheetOpen,
|
|
isRouteCurrent: isRouteCurrent,
|
|
isSidebarFocused: isSidebarFocused,
|
|
hasVisibleTabs: hasVisibleTabs,
|
|
isCurrentTabRoot: isCurrentTabRoot,
|
|
);
|
|
}
|
|
|
|
expect(shouldPass(), isTrue);
|
|
expect(shouldPass(isSidebarFocused: false), isFalse);
|
|
expect(shouldPass(isCurrentTabRoot: false), isFalse);
|
|
expect(shouldPass(isOverlaySheetOpen: true), isFalse);
|
|
expect(shouldPass(isRouteCurrent: false), isFalse);
|
|
expect(shouldPass(isAppleTV: false), isFalse);
|
|
});
|
|
|
|
test('tvOS Menu policy transaction publishes only the settled navigation state', () {
|
|
var desired = false;
|
|
final published = <bool>[];
|
|
final publisher = TvosMenuPolicyPublisher(() => desired, published.add);
|
|
|
|
publisher.run(() {
|
|
desired = true;
|
|
publisher.update();
|
|
desired = false;
|
|
});
|
|
|
|
expect(published, [false]);
|
|
});
|
|
|
|
test('tvOS Menu policy publishes retained sidebar Home state immediately', () {
|
|
final desired = true;
|
|
final published = <bool>[];
|
|
final publisher = TvosMenuPolicyPublisher(() => desired, published.add);
|
|
|
|
publisher.update();
|
|
|
|
expect(published, [true]);
|
|
});
|
|
|
|
test('desktop physical Escape is reserved for window fullscreen only at root Home', () {
|
|
bool shouldHandle({
|
|
bool isDesktop = true,
|
|
bool isPhysicalKeyboardEvent = true,
|
|
LogicalKeyboardKey logicalKey = LogicalKeyboardKey.escape,
|
|
bool isCurrentRoute = true,
|
|
bool isHomeTab = true,
|
|
}) {
|
|
return shouldHandleDesktopRootEscape(
|
|
isDesktop: isDesktop,
|
|
isPhysicalKeyboardEvent: isPhysicalKeyboardEvent,
|
|
logicalKey: logicalKey,
|
|
isCurrentRoute: isCurrentRoute,
|
|
isHomeTab: isHomeTab,
|
|
);
|
|
}
|
|
|
|
expect(shouldHandle(), isTrue);
|
|
expect(shouldHandle(isHomeTab: false), isFalse);
|
|
expect(shouldHandle(isCurrentRoute: false), isFalse);
|
|
// A remote/gamepad-synthesized escape is not a physical keyboard Escape;
|
|
// it keeps the press-back-again exit path.
|
|
expect(shouldHandle(isPhysicalKeyboardEvent: false), isFalse);
|
|
expect(shouldHandle(isDesktop: false), isFalse);
|
|
expect(shouldHandle(logicalKey: LogicalKeyboardKey.gameButtonB), isFalse);
|
|
});
|
|
|
|
test('profile switch invalidates nothing here — the keyed session remount owns it', () {
|
|
expect(
|
|
profileInvalidationAction(
|
|
previousProfileId: 'owner',
|
|
currentProfileId: 'kids',
|
|
wasBindingPreviously: false,
|
|
isBindingNow: false,
|
|
),
|
|
ProfileInvalidationAction.none,
|
|
);
|
|
});
|
|
|
|
test('same-profile rebind invalidates once when binding settles', () {
|
|
expect(
|
|
profileInvalidationAction(
|
|
previousProfileId: 'owner',
|
|
currentProfileId: 'owner',
|
|
wasBindingPreviously: true,
|
|
isBindingNow: false,
|
|
),
|
|
ProfileInvalidationAction.invalidateNow,
|
|
);
|
|
|
|
expect(
|
|
profileInvalidationAction(
|
|
previousProfileId: 'owner',
|
|
currentProfileId: 'owner',
|
|
wasBindingPreviously: false,
|
|
isBindingNow: false,
|
|
),
|
|
ProfileInvalidationAction.none,
|
|
);
|
|
});
|
|
|
|
testWidgets('side navigation bleed animates from the previous value', (tester) async {
|
|
Widget build(double targetBleed) {
|
|
return Directionality(
|
|
textDirection: TextDirection.ltr,
|
|
child: Stack(
|
|
children: [
|
|
SideNavigationBleedBuilder(
|
|
targetBleed: targetBleed,
|
|
builder: (context, bleed, _) => Positioned(
|
|
key: const ValueKey('bleed-position'),
|
|
top: 0,
|
|
left: -bleed,
|
|
width: 1280,
|
|
height: 10,
|
|
child: const SizedBox.shrink(),
|
|
),
|
|
),
|
|
],
|
|
),
|
|
);
|
|
}
|
|
|
|
double left() => tester.widget<Positioned>(find.byKey(const ValueKey('bleed-position'))).left!;
|
|
|
|
await tester.pumpWidget(build(SideNavigationRailState.tvCollapsedWidth));
|
|
expect(left(), -SideNavigationRailState.tvCollapsedWidth);
|
|
|
|
await tester.pumpWidget(build(SideNavigationRailState.expandedWidth));
|
|
expect(left(), closeTo(-SideNavigationRailState.tvCollapsedWidth, 0.001));
|
|
|
|
await tester.pump(const Duration(milliseconds: 100));
|
|
expect(left(), lessThan(-SideNavigationRailState.tvCollapsedWidth));
|
|
expect(left(), greaterThan(-SideNavigationRailState.expandedWidth));
|
|
|
|
await tester.pumpAndSettle();
|
|
expect(left(), closeTo(-SideNavigationRailState.expandedWidth, 0.001));
|
|
});
|
|
}
|