diff --git a/packages/datadog_session_replay/example/golden_test/font_family_transform_golden_test.dart b/packages/datadog_session_replay/example/golden_test/font_family_transform_golden_test.dart new file mode 100644 index 00000000..12880784 --- /dev/null +++ b/packages/datadog_session_replay/example/golden_test/font_family_transform_golden_test.dart @@ -0,0 +1,174 @@ +// Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0. +// This product includes software developed at Datadog (https://www.datadoghq.com/). +// Copyright 2025-Present Datadog, Inc. + +// ignore_for_file: invalid_use_of_visible_for_testing_member + +import 'package:datadog_common_test/datadog_common_test.dart'; +import 'package:datadog_session_replay/datadog_session_replay.dart'; +import 'package:datadog_session_replay/src/capture/capture_node.dart'; +import 'package:datadog_session_replay/src/capture/recorder.dart'; +import 'package:datadog_session_replay/src/datadog_session_replay_platform_interface.dart'; +import 'package:datadog_session_replay/src/rum_context.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:flutter_test_goldens/flutter_test_goldens.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; + +import 'golden_test_helpers.dart'; + +class MockDatadogSessionReplayPlatform extends Mock + with MockPlatformInterfaceMixin + implements DatadogSessionReplayPlatform {} + +void main() { + late SessionReplayRecorder recorder; + late RUMContext context; + late MockDatadogSessionReplayPlatform platform; + + setUpAll(() async { + await TestFonts.loadAppFonts(); + }); + + setUp(() { + recorder = SessionReplayRecorder( + defaultCapturePrivacy: TreeCapturePrivacy( + textAndInputPrivacyLevel: TextAndInputPrivacyLevel.maskSensitiveInputs, + imagePrivacyLevel: ImagePrivacyLevel.maskNone, + ), + touchPrivacyLevel: TouchPrivacyLevel.show, + ); + platform = MockDatadogSessionReplayPlatform(); + DatadogSessionReplayPlatform.instance = platform; + + registerFallbackValue( + CapturedViewAttributes(paintBounds: Rect.zero, scaleX: 1.0, scaleY: 1.0), + ); + + context = RUMContext( + applicationId: randomString(), + sessionId: randomString(), + ); + recorder.updateContext(context); + }); + + testWidgets('font transform smart maps package font to open sans', + (tester) async { + final fixture = MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text( + 'Font Transform Test', + style: TextStyle(fontFamily: 'packages/my_app/CustomFont'), + ), + ), + body: Padding( + padding: const EdgeInsets.all(12.0), + child: Text( + 'This text uses a package font that gets transformed to OpenSans ' + 'by the font family transform. If you see readable text in the ' + 'golden, the transform is working correctly.', + style: TextStyle( + fontFamily: 'packages/my_app/CustomFont', + fontSize: 16, + ), + ), + ), + ), + ); + await snapshotTest( + tester, + recorder, + fixture, + fontFamilyTransform: FontFamilyTransformConfig( + strategy: FontFamilyStrategy.smart, + rules: { + 'CustomFont': TestFonts.openSans, + }, + ), + ); + }); + + testWidgets('font transform smart drops sentinel and uses fallback rule', + (tester) async { + final fixture = MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text( + 'Sentinel Transform', + style: TextStyle(fontFamily: '.AppleSystemUIFont'), + ), + ), + body: Padding( + padding: const EdgeInsets.all(12.0), + child: Text( + 'This text uses .AppleSystemUIFont which the smart strategy ' + 'drops as a Flutter sentinel. The empty-key fallback rule then ' + 'maps it to OpenSans. Readable text proves sentinel handling works.', + style: TextStyle( + fontFamily: '.AppleSystemUIFont', + fontSize: 16, + ), + ), + ), + ), + ); + await snapshotTest( + tester, + recorder, + fixture, + fontFamilyTransform: FontFamilyTransformConfig( + strategy: FontFamilyStrategy.smart, + rules: { + '': TestFonts.openSans, + }, + ), + ); + }); + + testWidgets('font transform fallback replaces all families', + (tester) async { + final fixture = MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text( + 'Fallback Strategy', + style: TextStyle(fontFamily: TestFonts.openSans), + ), + ), + body: Padding( + padding: const EdgeInsets.all(12.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'OpenSans text stays the same font stack with fallback.', + style: TextStyle( + fontFamily: TestFonts.openSans, + fontSize: 16, + ), + ), + SizedBox(height: 12), + Text( + 'Custom font text also gets the fallback stack.', + style: TextStyle( + fontFamily: 'packages/my_app/CustomFont', + fontSize: 16, + ), + ), + ], + ), + ), + ), + ); + await snapshotTest( + tester, + recorder, + fixture, + fontFamilyTransform: const FontFamilyTransformConfig( + strategy: FontFamilyStrategy.fallback, + ), + ); + }); +} diff --git a/packages/datadog_session_replay/example/golden_test/golden_test_helpers.dart b/packages/datadog_session_replay/example/golden_test/golden_test_helpers.dart index 9a826329..fe6b2cb0 100644 --- a/packages/datadog_session_replay/example/golden_test/golden_test_helpers.dart +++ b/packages/datadog_session_replay/example/golden_test/golden_test_helpers.dart @@ -5,6 +5,7 @@ import 'dart:typed_data'; import 'package:datadog_flutter_plugin/datadog_flutter_plugin.dart'; +import 'package:datadog_session_replay/datadog_session_replay.dart'; import 'package:datadog_session_replay/src/capture/recorder.dart'; import 'package:datadog_session_replay/src/processor/processor_worker.dart'; import 'package:datadog_session_replay/src/sr_data_models.dart'; @@ -61,9 +62,11 @@ Future snapshotTest( SessionReplayRecorder recorder, Widget fixture, { TestActions? testActions, + FontFamilyTransformConfig fontFamilyTransform = + const FontFamilyTransformConfig(), }) async { DatadogSdk.instance.sdkVerbosity = CoreLoggerLevel.debug; - final processor = ProcessorWorker(); + final processor = ProcessorWorker(fontFamilyTransform: fontFamilyTransform); await tester.pumpWidget( SimpleTestCapture(key: Key('key'), recorder: recorder, child: fixture), ); diff --git a/packages/datadog_session_replay/example/golden_test/goldens/font_transform_fallback_replaces_all_families.png b/packages/datadog_session_replay/example/golden_test/goldens/font_transform_fallback_replaces_all_families.png new file mode 100644 index 00000000..fe66da9f Binary files /dev/null and b/packages/datadog_session_replay/example/golden_test/goldens/font_transform_fallback_replaces_all_families.png differ diff --git a/packages/datadog_session_replay/example/golden_test/goldens/font_transform_smart_drops_sentinel_and_uses_fallback_rule.png b/packages/datadog_session_replay/example/golden_test/goldens/font_transform_smart_drops_sentinel_and_uses_fallback_rule.png new file mode 100644 index 00000000..6a4fccc2 Binary files /dev/null and b/packages/datadog_session_replay/example/golden_test/goldens/font_transform_smart_drops_sentinel_and_uses_fallback_rule.png differ diff --git a/packages/datadog_session_replay/example/golden_test/goldens/font_transform_smart_maps_package_font_to_open_sans.png b/packages/datadog_session_replay/example/golden_test/goldens/font_transform_smart_maps_package_font_to_open_sans.png new file mode 100644 index 00000000..8ccd813a Binary files /dev/null and b/packages/datadog_session_replay/example/golden_test/goldens/font_transform_smart_maps_package_font_to_open_sans.png differ