feat: import Chinese-localized Buzz source snapshot
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Docker image / Build (linux/amd64) (push) Has been cancelled
Docker image / Build (linux/arm64) (push) Has been cancelled
Docker image / Merge release multi-arch manifest (push) Has been cancelled
Docker image / Merge debug multi-arch manifest (push) Has been cancelled
Docker image / Build public push gateway (linux/amd64) (push) Has been cancelled
Docker image / Build public push gateway (linux/arm64) (push) Has been cancelled
Docker image / Publish public push gateway image (push) Has been cancelled
Sprig image / Build (linux/amd64) (push) Has been cancelled
Sprig image / Build (linux/arm64) (push) Has been cancelled
Sprig image / Merge multi-arch manifest (push) Has been cancelled
Harbor Buzz Orchestra / Python tests and lint (push) Has been cancelled
CI / Detect Changed Paths (push) Has been cancelled
CI / Rust Lint (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Desktop Core (push) Has been cancelled
CI / Desktop Smoke E2E (1) (push) Has been cancelled
CI / Desktop Smoke E2E (2) (push) Has been cancelled
CI / Desktop Smoke E2E (3) (push) Has been cancelled
CI / Desktop Smoke E2E (4) (push) Has been cancelled
CI / Desktop (push) Has been cancelled
CI / Desktop E2E Relay (push) Has been cancelled
CI / Desktop E2E Integration (1/2) (push) Has been cancelled
CI / Desktop E2E Integration (2/2) (push) Has been cancelled
CI / Desktop E2E Integration (push) Has been cancelled
CI / Backend Integration (relay e2e) (push) Has been cancelled
CI / Relay E2E (push) Has been cancelled
CI / Web (push) Has been cancelled
CI / Mobile (push) Has been cancelled
CI / Security (push) Has been cancelled
CI / Dead Token Reference Guard (push) Has been cancelled
CI / Server Cross-Compile (aarch64-unknown-linux-musl) (push) Has been cancelled
CI / Server Cross-Compile (x86_64-unknown-linux-musl) (push) Has been cancelled
CI / Windows Rust (x86_64-pc-windows-msvc) (push) Has been cancelled
CI / Desktop Build (macOS) (push) Has been cancelled
helm chart / lint + unittest + render matrix (push) Has been cancelled
helm chart / install on kind (gated) (push) Has been cancelled
helm chart / publish chart to GHCR (push) Has been cancelled
Mesh Lifecycle / Relay-Driven Mesh Lifecycle Smoke (push) Has been cancelled
Sprig / Build (aarch64-unknown-linux-musl) (push) Has been cancelled
Sprig / Build (x86_64-unknown-linux-musl) (push) Has been cancelled
Sprig / Publish rolling release (push) Has been cancelled
Sprig / Publish tagged release (push) Has been cancelled
Signed-off-by: cls_宁波本机 <908705107@qq.com>
This commit is contained in:
@@ -0,0 +1,277 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:buzz/features/pairing/pairing_qr_scanner.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mobile_scanner/mobile_scanner.dart';
|
||||
|
||||
const _qrScannerPlatformChannel = MethodChannel('buzz/qr_scanner');
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
final defaultScannerPlatform = MobileScannerPlatform.instance;
|
||||
late _FakeMobileScannerPlatform fakeScannerPlatform;
|
||||
|
||||
setUp(() {
|
||||
fakeScannerPlatform = _FakeMobileScannerPlatform();
|
||||
MobileScannerPlatform.instance = fakeScannerPlatform;
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
debugDefaultTargetPlatformOverride = null;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(_qrScannerPlatformChannel, null);
|
||||
await fakeScannerPlatform.dispose();
|
||||
MobileScannerPlatform.instance = defaultScannerPlatform;
|
||||
});
|
||||
|
||||
group('DynamicIslandQrScannerGeometry', () {
|
||||
const viewport = Size(393, 852);
|
||||
const geometry = DynamicIslandQrScannerGeometry(
|
||||
viewport: viewport,
|
||||
safeAreaTop: 59,
|
||||
);
|
||||
|
||||
test('starts at the physical Dynamic Island frame', () {
|
||||
expect(geometry.collapsedFrame, const Rect.fromLTWH(136.5, 11, 120, 36));
|
||||
});
|
||||
|
||||
test('keeps the top edge fixed while the camera grows down', () {
|
||||
final start = geometry.frameAt(0);
|
||||
final middle = geometry.frameAt(0.5);
|
||||
final end = geometry.frameAt(1);
|
||||
|
||||
expect(middle.top, start.top);
|
||||
expect(end.top, start.top);
|
||||
expect(middle.bottom, greaterThan(start.bottom));
|
||||
expect(end.bottom, greaterThan(middle.bottom));
|
||||
expect(end, const Rect.fromLTWH(15, 11, 363, 363));
|
||||
});
|
||||
|
||||
test('delays the camera until the portal has left the island', () {
|
||||
expect(geometry.scannerOpacityAt(0.18), 0);
|
||||
expect(geometry.scannerOpacityAt(0.5), greaterThan(0));
|
||||
expect(geometry.scannerOpacityAt(1), 1);
|
||||
});
|
||||
});
|
||||
|
||||
group('usesDynamicIslandQrScannerPortal', () {
|
||||
test('asks the native bridge on iOS', () async {
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(_qrScannerPlatformChannel, (call) async {
|
||||
expect(call.method, 'usesDynamicIslandQrScannerPortal');
|
||||
return true;
|
||||
});
|
||||
|
||||
expect(await usesDynamicIslandQrScannerPortal(), isTrue);
|
||||
});
|
||||
|
||||
test('uses the fallback without asking native code on Android', () async {
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.android;
|
||||
var nativeCalls = 0;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(_qrScannerPlatformChannel, (call) async {
|
||||
nativeCalls += 1;
|
||||
return true;
|
||||
});
|
||||
|
||||
expect(await usesDynamicIslandQrScannerPortal(), isFalse);
|
||||
expect(nativeCalls, 0);
|
||||
});
|
||||
});
|
||||
|
||||
testWidgets('fallback reveals the camera behind the current app surface', (
|
||||
tester,
|
||||
) async {
|
||||
await tester.binding.setSurfaceSize(const Size(375, 667));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
var scannerClosed = false;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: FallbackPairingQrScanner(
|
||||
appSurface: const ColoredBox(
|
||||
color: Colors.white,
|
||||
child: Center(child: Text('Current app surface')),
|
||||
),
|
||||
onClosed: (_) {
|
||||
scannerClosed = true;
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
final sheet = find.byKey(const ValueKey('fallback-qr-scanner-app-sheet'));
|
||||
expect(tester.getRect(sheet).top, 0);
|
||||
expect(find.text('Current app surface'), findsOneWidget);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 420));
|
||||
expect(tester.getRect(sheet).top, closeTo(555, 0.1));
|
||||
|
||||
await tester.tapAt(const Offset(187.5, 100));
|
||||
await tester.pump();
|
||||
expect(scannerClosed, isFalse);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 320));
|
||||
await tester.pumpAndSettle();
|
||||
expect(scannerClosed, isTrue);
|
||||
expect(tester.getRect(sheet).top, 0);
|
||||
});
|
||||
|
||||
testWidgets(
|
||||
'portal route opens from the island and an outside tap reverses it closed',
|
||||
(tester) async {
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(_qrScannerPlatformChannel, (call) async {
|
||||
return switch (call.method) {
|
||||
'usesDynamicIslandQrScannerPortal' => true,
|
||||
'setDynamicIslandScannerStatusBarHidden' => null,
|
||||
_ => null,
|
||||
};
|
||||
});
|
||||
await tester.binding.setSurfaceSize(const Size(393, 852));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
var scannerClosed = false;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
home: MediaQuery(
|
||||
data: const MediaQueryData(
|
||||
size: Size(393, 852),
|
||||
viewPadding: EdgeInsets.only(top: 59),
|
||||
),
|
||||
child: Builder(
|
||||
builder: (context) => TextButton(
|
||||
onPressed: () async {
|
||||
await showDynamicIslandPairingQrScanner(context);
|
||||
scannerClosed = true;
|
||||
},
|
||||
child: const Text('Open scanner'),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Open scanner'));
|
||||
await tester.pump();
|
||||
|
||||
final portal = find.byKey(
|
||||
const ValueKey('dynamic-island-qr-scanner-portal'),
|
||||
);
|
||||
expect(tester.getRect(portal), const Rect.fromLTWH(136.5, 11, 120, 36));
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 460));
|
||||
expect(tester.getRect(portal).top, 11);
|
||||
expect(
|
||||
find.byKey(const ValueKey('dynamic-island-qr-scanner-close')),
|
||||
findsNothing,
|
||||
);
|
||||
|
||||
await tester.tapAt(const Offset(196.5, 700));
|
||||
await tester.pump();
|
||||
expect(scannerClosed, isFalse);
|
||||
|
||||
await tester.pump(const Duration(milliseconds: 340));
|
||||
await tester.pumpAndSettle();
|
||||
expect(scannerClosed, isTrue);
|
||||
expect(portal, findsNothing);
|
||||
debugDefaultTargetPlatformOverride = null;
|
||||
},
|
||||
);
|
||||
|
||||
testWidgets('tapping the scanner closes it without a separate control', (
|
||||
tester,
|
||||
) async {
|
||||
debugDefaultTargetPlatformOverride = TargetPlatform.iOS;
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(_qrScannerPlatformChannel, (call) async {
|
||||
return switch (call.method) {
|
||||
'usesDynamicIslandQrScannerPortal' => true,
|
||||
'setDynamicIslandScannerStatusBarHidden' => null,
|
||||
_ => null,
|
||||
};
|
||||
});
|
||||
await tester.binding.setSurfaceSize(const Size(393, 852));
|
||||
addTearDown(() => tester.binding.setSurfaceSize(null));
|
||||
|
||||
var scannerClosed = false;
|
||||
await tester.pumpWidget(
|
||||
MaterialApp(
|
||||
builder: (context, child) => MediaQuery(
|
||||
data: MediaQuery.of(context).copyWith(disableAnimations: true),
|
||||
child: child!,
|
||||
),
|
||||
home: Builder(
|
||||
builder: (context) => TextButton(
|
||||
onPressed: () async {
|
||||
await showDynamicIslandPairingQrScanner(context);
|
||||
scannerClosed = true;
|
||||
},
|
||||
child: const Text('Open scanner'),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.text('Open scanner'));
|
||||
await tester.pump();
|
||||
|
||||
final portal = find.byKey(
|
||||
const ValueKey('dynamic-island-qr-scanner-portal'),
|
||||
);
|
||||
expect(tester.getRect(portal), const Rect.fromLTWH(15, 11, 363, 363));
|
||||
|
||||
await tester.tapAt(tester.getCenter(portal));
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
expect(scannerClosed, isTrue);
|
||||
expect(portal, findsNothing);
|
||||
debugDefaultTargetPlatformOverride = null;
|
||||
});
|
||||
}
|
||||
|
||||
class _FakeMobileScannerPlatform extends MobileScannerPlatform {
|
||||
final _barcodes = StreamController<BarcodeCapture?>.broadcast();
|
||||
var _isDisposed = false;
|
||||
|
||||
@override
|
||||
Stream<BarcodeCapture?> get barcodesStream => _barcodes.stream;
|
||||
|
||||
@override
|
||||
Stream<TorchState> get torchStateStream =>
|
||||
Stream.value(TorchState.unavailable);
|
||||
|
||||
@override
|
||||
Stream<double> get zoomScaleStateStream => Stream.value(1);
|
||||
|
||||
@override
|
||||
Future<MobileScannerViewAttributes> start(StartOptions startOptions) async {
|
||||
return const MobileScannerViewAttributes(
|
||||
cameraDirection: CameraFacing.back,
|
||||
currentTorchMode: TorchState.unavailable,
|
||||
size: Size(200, 200),
|
||||
numberOfCameras: 1,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildCameraView() => const SizedBox.expand();
|
||||
|
||||
@override
|
||||
Future<void> stop() async {}
|
||||
|
||||
@override
|
||||
Future<void> dispose() async {
|
||||
if (_isDisposed) {
|
||||
return;
|
||||
}
|
||||
_isDisposed = true;
|
||||
await _barcodes.close();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user