diff --git a/packages/google_adsense/CHANGELOG.md b/packages/google_adsense/CHANGELOG.md index 57f202439cc..6eac44ef1bd 100644 --- a/packages/google_adsense/CHANGELOG.md +++ b/packages/google_adsense/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.1.2 + +* Added a callback to the widget for testing to make unit tests more deterministic. + ## 0.1.1 * Adds `AdSenseCodeParameters` configuration object for `adSense.initialize`. diff --git a/packages/google_adsense/example/integration_test/experimental_ad_unit_widget_test.dart b/packages/google_adsense/example/integration_test/experimental_ad_unit_widget_test.dart index 4f7cb10562e..54fa26e0a7f 100644 --- a/packages/google_adsense/example/integration_test/experimental_ad_unit_widget_test.dart +++ b/packages/google_adsense/example/integration_test/experimental_ad_unit_widget_test.dart @@ -19,6 +19,17 @@ import 'js_interop_mocks/adsense_test_js_interop.dart'; const String testClient = 'test_client'; const String testSlot = 'test_slot'; +class CallbackTracker { + void Function() createCallback() { + final int callbackIndex = _callbackStates.length; + _callbackStates.add(false); + return () => _callbackStates[callbackIndex] = true; + } + + bool get allCalled => _callbackStates.every((bool state) => state); + final List _callbackStates = []; +} + void main() async { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); @@ -47,15 +58,17 @@ void main() async { await adSense.initialize(testClient); + final CallbackTracker tracker = CallbackTracker(); final Widget adUnitWidget = AdUnitWidget( configuration: AdUnitConfiguration.displayAdUnit( adSlot: testSlot, adFormat: AdFormat.AUTO, // Important! ), adClient: adSense.adClient, + onInjected: tracker.createCallback(), ); - await pumpAdWidget(adUnitWidget, tester); + await pumpAdWidget(adUnitWidget, tester, tracker); // Then // Widget level @@ -81,11 +94,13 @@ void main() async { await adSense.initialize(testClient); + final CallbackTracker tracker = CallbackTracker(); final Widget adUnitWidget = AdUnitWidget( configuration: AdUnitConfiguration.displayAdUnit( adSlot: testSlot, ), adClient: adSense.adClient, + onInjected: tracker.createCallback(), ); final Widget constrainedAd = Container( @@ -93,7 +108,7 @@ void main() async { child: adUnitWidget, ); - await pumpAdWidget(constrainedAd, tester); + await pumpAdWidget(constrainedAd, tester, tracker); // Then // Widget level @@ -110,14 +125,17 @@ void main() async { mockAdsByGoogle(mockAd(adStatus: AdStatus.UNFILLED)); await adSense.initialize(testClient); + + final CallbackTracker tracker = CallbackTracker(); final Widget adUnitWidget = AdUnitWidget( configuration: AdUnitConfiguration.displayAdUnit( adSlot: testSlot, ), adClient: adSense.adClient, + onInjected: tracker.createCallback(), ); - await pumpAdWidget(adUnitWidget, tester); + await pumpAdWidget(adUnitWidget, tester, tracker); // Then expect(find.byType(HtmlElementView), findsNothing, @@ -142,6 +160,7 @@ void main() async { await adSense.initialize(testClient); + final CallbackTracker tracker = CallbackTracker(); final Widget bunchOfAds = Column( children: [ AdUnitWidget( @@ -150,6 +169,7 @@ void main() async { adFormat: AdFormat.AUTO, ), adClient: adSense.adClient, + onInjected: tracker.createCallback(), ), AdUnitWidget( configuration: AdUnitConfiguration.displayAdUnit( @@ -157,6 +177,7 @@ void main() async { adFormat: AdFormat.AUTO, ), adClient: adSense.adClient, + onInjected: tracker.createCallback(), ), Container( constraints: const BoxConstraints(maxHeight: 100), @@ -165,12 +186,13 @@ void main() async { adSlot: testSlot, ), adClient: adSense.adClient, + onInjected: tracker.createCallback(), ), ), ], ); - await pumpAdWidget(bunchOfAds, tester); + await pumpAdWidget(bunchOfAds, tester, tracker); // Then // Widget level @@ -192,7 +214,8 @@ void main() async { } // Pumps an AdUnit Widget into a given tester, with some parameters -Future pumpAdWidget(Widget adUnit, WidgetTester tester) async { +Future pumpAdWidget( + Widget adUnit, WidgetTester tester, CallbackTracker tracker) async { await tester.pumpWidget( MaterialApp( home: Scaffold( @@ -203,10 +226,14 @@ Future pumpAdWidget(Widget adUnit, WidgetTester tester) async { ), ); - // This extra pump is needed for the platform view to actually render in the DOM. - await tester.pump(); - // One more for skwasm. - await tester.pump(); + final Stopwatch timer = Stopwatch()..start(); + while (!tracker.allCalled) { + if (timer.elapsedMilliseconds > 1000) { + fail('timeout while waiting for ad widget to be injected'); + } + // Pump until all the widgets have had their platform views injected into the dom. + await tester.pump(); + } // This extra pump is needed to simulate the async behavior of the adsense JS mock. await tester.pumpAndSettle(); } diff --git a/packages/google_adsense/lib/src/adsense/ad_unit_widget.dart b/packages/google_adsense/lib/src/adsense/ad_unit_widget.dart index 5e25dda6d76..f4a06c73a39 100644 --- a/packages/google_adsense/lib/src/adsense/ad_unit_widget.dart +++ b/packages/google_adsense/lib/src/adsense/ad_unit_widget.dart @@ -33,13 +33,16 @@ class AdUnitWidget extends StatefulWidget { super.key, required AdUnitConfiguration configuration, @visibleForTesting String? adClient, + @visibleForTesting void Function()? onInjected, }) : _adClient = adClient ?? adSense.adClient, + _onInjected = onInjected, _adUnitConfiguration = configuration { assert(_adClient != null, 'Attempted to render an AdUnitWidget before calling adSense.initialize'); } final String? _adClient; + final void Function()? _onInjected; final AdUnitConfiguration _adUnitConfiguration; @@ -58,17 +61,19 @@ class _AdUnitWidgetWebState extends State @override bool get wantKeepAlive => true; - static final web.ResizeObserver _adSenseResizeObserver = web.ResizeObserver( - (JSArray entries, web.ResizeObserver observer) { - for (final web.ResizeObserverEntry entry in entries.toDart) { - final web.Element target = entry.target; - if (target.isConnected) { - // First time resized since attached to DOM -> attachment callback from Flutter docs by David - _onElementAttached(target as web.HTMLElement); - observer.disconnect(); - } - } - }.toJS); + web.ResizeObserver get _adSenseResizeObserver => + web.ResizeObserver((JSArray entries, + web.ResizeObserver observer) { + for (final web.ResizeObserverEntry entry in entries.toDart) { + final web.Element target = entry.target; + if (target.isConnected) { + // First time resized since attached to DOM -> attachment callback from Flutter docs by David + _onElementAttached(target as web.HTMLElement); + widget._onInjected?.call(); + observer.disconnect(); + } + } + }.toJS); @override Widget build(BuildContext context) { diff --git a/packages/google_adsense/pubspec.yaml b/packages/google_adsense/pubspec.yaml index 26b865b5f4d..9dbb3897d33 100644 --- a/packages/google_adsense/pubspec.yaml +++ b/packages/google_adsense/pubspec.yaml @@ -2,7 +2,7 @@ name: google_adsense description: A wrapper plugin with convenience APIs allowing easier inserting Google Adsense HTML snippets withing a Flutter UI Web application repository: https://github.com/flutter/packages/tree/main/packages/google_adsense issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_adsense%22 -version: 0.1.1 +version: 0.1.2 environment: sdk: ^3.4.0