Files
Qlyra/test/photo_hero_test.dart
T
sevenhill f74057ce9b
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
Rebrand application as Qlyra
2026-08-30 13:16:17 +03:00

228 lines
5.9 KiB
Dart

import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:qlyra/frontend/widgets/attachment/photo_hero.dart';
const Rect _origin = Rect.fromLTWH(50, 500, 100, 100);
class _TestImageProvider extends ImageProvider<_TestImageProvider> {
_TestImageProvider(this.image);
final ui.Image image;
@override
Future<_TestImageProvider> obtainKey(ImageConfiguration configuration) =>
SynchronousFuture<_TestImageProvider>(this);
@override
ImageStreamCompleter loadImage(
_TestImageProvider key,
ImageDecoderCallback decode,
) => OneFrameImageStreamCompleter(
SynchronousFuture<ImageInfo>(ImageInfo(image: image.clone())),
);
}
Widget _page() => Scaffold(
body: Column(
children: [
Expanded(
child: PhotoHeroTarget(
child: Center(
child: Container(key: const ValueKey('target'), color: Colors.red),
),
),
),
const SizedBox(height: 100),
],
),
);
Finder get _flying => find.byType(Image);
double _flyingWidth(WidgetTester tester) => tester.getSize(_flying).width;
bool _targetHidden(WidgetTester tester) => tester.any(
find.ancestor(
of: find.byKey(const ValueKey('target')),
matching: find.byType(Opacity),
),
);
double _pageOpacity(WidgetTester tester) => tester
.widget<FadeTransition>(
find
.ancestor(
of: find.byKey(const ValueKey('target')),
matching: find.byType(FadeTransition),
)
.first,
)
.opacity
.value;
void main() {
late ui.Image image;
late ui.Image wide;
setUpAll(() async {
image = await createTestImage(width: 4, height: 3);
wide = await createTestImage(width: 8, height: 2);
});
testWidgets('photo flies from the origin rect to the contained target', (
tester,
) async {
final hero = PhotoHeroController(
origin: () => _origin,
image: _TestImageProvider(image),
);
late BuildContext context;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (ctx) {
context = ctx;
return const Scaffold();
},
),
),
);
Navigator.of(
context,
).push(PhotoHeroRoute<void>(hero: hero, builder: (_) => _page()));
await tester.pump();
await tester.pump(const Duration(milliseconds: 16));
expect(_flying, findsOneWidget);
expect(_targetHidden(tester), isTrue);
final early = _flyingWidth(tester);
expect(early, greaterThanOrEqualTo(133));
await tester.pump(const Duration(milliseconds: 150));
final late_ = _flyingWidth(tester);
expect(late_, greaterThan(early));
expect(late_, lessThan(667));
await tester.pumpAndSettle();
expect(_flying, findsNothing);
expect(_targetHidden(tester), isFalse);
expect(tester.getSize(find.byKey(const ValueKey('target'))).height, 500);
});
testWidgets('photo flies back to the origin rect on pop', (tester) async {
final hero = PhotoHeroController(
origin: () => _origin,
image: _TestImageProvider(image),
);
late BuildContext context;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (ctx) {
context = ctx;
return const Scaffold();
},
),
),
);
final navigator = Navigator.of(context);
navigator.push(PhotoHeroRoute<void>(hero: hero, builder: (_) => _page()));
await tester.pumpAndSettle();
navigator.pop();
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
expect(_flying, findsOneWidget);
expect(_targetHidden(tester), isTrue);
expect(_flyingWidth(tester), lessThan(667));
await tester.pumpAndSettle();
expect(_flying, findsNothing);
});
testWidgets('disabled hero fades the page instead of flying', (tester) async {
final hero = PhotoHeroController(
origin: () => _origin,
image: _TestImageProvider(image),
)..enabled = false;
late BuildContext context;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (ctx) {
context = ctx;
return const Scaffold();
},
),
),
);
Navigator.of(
context,
).push(PhotoHeroRoute<void>(hero: hero, builder: (_) => _page()));
await tester.pump();
await tester.pump(const Duration(milliseconds: 100));
expect(_flying, findsNothing);
expect(_targetHidden(tester), isFalse);
expect(_pageOpacity(tester), lessThan(1));
await tester.pumpAndSettle();
expect(_pageOpacity(tester), 1);
});
testWidgets('target renders untouched without a hero scope', (tester) async {
await tester.pumpWidget(MaterialApp(home: _page()));
await tester.pumpAndSettle();
expect(find.byType(Opacity), findsNothing);
expect(tester.getSize(find.byKey(const ValueKey('target'))).height, 500);
});
testWidgets('замена кадра меняет пропорции обратного перелёта', (
tester,
) async {
final hero = PhotoHeroController(
origin: () => _origin,
image: RawImageProvider(image),
);
late BuildContext context;
await tester.pumpWidget(
MaterialApp(
home: Builder(
builder: (ctx) {
context = ctx;
return const Scaffold();
},
),
),
);
final navigator = Navigator.of(context);
navigator.push(PhotoHeroRoute<void>(hero: hero, builder: (_) => _page()));
await tester.pumpAndSettle();
hero.image.value = RawImageProvider(wide);
await tester.pump();
navigator.pop();
await tester.pump();
await tester.pump(const Duration(milliseconds: 16));
expect(_flying, findsOneWidget);
final size = tester.getSize(_flying);
expect(size.width / size.height, closeTo(4, 0.2));
await tester.pump(const Duration(milliseconds: 400));
});
}