blob: 5578c338986ac193f212d86b24f66dca9b9597bc [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:interactive_media_ads/src/android/android_ad_display_container.dart';
import 'package:interactive_media_ads/src/android/interactive_media_ads.g.dart'
as ima;
import 'package:interactive_media_ads/src/android/interactive_media_ads_proxy.dart';
import 'package:interactive_media_ads/src/android/platform_views_service_proxy.dart';
import 'package:mockito/annotations.dart';
import 'package:mockito/mockito.dart';
import 'ad_display_container_test.mocks.dart';
@GenerateNiceMocks(<MockSpec<Object>>[
MockSpec<ima.AdDisplayContainer>(),
MockSpec<ima.AdMediaInfo>(),
MockSpec<ima.AdPodInfo>(),
MockSpec<ima.FrameLayout>(),
MockSpec<ima.MediaPlayer>(),
MockSpec<ima.VideoAdPlayer>(),
MockSpec<ima.VideoAdPlayerCallback>(),
MockSpec<ima.VideoProgressUpdate>(),
MockSpec<ima.VideoView>(),
MockSpec<SurfaceAndroidViewController>(),
MockSpec<PlatformViewsServiceProxy>(),
])
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
group('AndroidAdDisplayContainer', () {
testWidgets('build with key', (WidgetTester tester) async {
final AndroidAdDisplayContainer container = AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
key: const Key('testKey'),
onContainerAdded: (_) {},
),
);
await tester.pumpWidget(Builder(
builder: (BuildContext context) => container.build(context),
));
expect(find.byType(PlatformViewLink), findsOneWidget);
expect(find.byKey(const Key('testKey')), findsOneWidget);
});
testWidgets('onContainerAdded is called', (WidgetTester tester) async {
final InteractiveMediaAdsProxy imaProxy = InteractiveMediaAdsProxy(
newFrameLayout: () => MockFrameLayout(),
newVideoView: ({
required dynamic onError,
dynamic onPrepared,
dynamic onCompletion,
}) =>
MockVideoView(),
createAdDisplayContainerImaSdkFactory: (
_,
__,
) async {
return MockAdDisplayContainer();
},
newVideoAdPlayer: ({
required dynamic addCallback,
required dynamic loadAd,
required dynamic pauseAd,
required dynamic playAd,
required dynamic release,
required dynamic removeCallback,
required dynamic stopAd,
}) =>
MockVideoAdPlayer(),
);
final MockPlatformViewsServiceProxy mockPlatformViewsProxy =
MockPlatformViewsServiceProxy();
final MockSurfaceAndroidViewController mockAndroidViewController =
MockSurfaceAndroidViewController();
late final int platformViewId;
when(
mockPlatformViewsProxy.initSurfaceAndroidView(
id: anyNamed('id'),
viewType: anyNamed('viewType'),
layoutDirection: anyNamed('layoutDirection'),
creationParams: anyNamed('creationParams'),
creationParamsCodec: anyNamed('creationParamsCodec'),
onFocus: anyNamed('onFocus'),
),
).thenAnswer((Invocation invocation) {
platformViewId = invocation.namedArguments[const Symbol('id')] as int;
return mockAndroidViewController;
});
final AndroidAdDisplayContainer container = AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
onContainerAdded: expectAsync1((_) {}),
platformViewsProxy: mockPlatformViewsProxy,
imaProxy: imaProxy,
),
);
await tester.pumpWidget(Builder(
builder: (BuildContext context) => container.build(context),
));
final void Function(int) onPlatformCreatedCallback = verify(
mockAndroidViewController
.addOnPlatformViewCreatedListener(captureAny))
.captured[0] as void Function(int);
onPlatformCreatedCallback(platformViewId);
await tester.pumpAndSettle();
});
test('completing the ad notifies IMA SDK the ad has ended', () {
late final void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAdCallback;
late final void Function(
ima.VideoAdPlayer,
ima.VideoAdPlayerCallback,
) addCallbackCallback;
late final void Function(
ima.VideoView,
ima.MediaPlayer,
) onCompletionCallback;
final InteractiveMediaAdsProxy imaProxy = InteractiveMediaAdsProxy(
newFrameLayout: () => MockFrameLayout(),
newVideoView: ({
required dynamic onError,
dynamic onPrepared,
void Function(
ima.VideoView,
ima.MediaPlayer,
)? onCompletion,
}) {
onCompletionCallback = onCompletion!;
return MockVideoView();
},
createAdDisplayContainerImaSdkFactory: (
_,
__,
) async {
return MockAdDisplayContainer();
},
newVideoAdPlayer: ({
required void Function(
ima.VideoAdPlayer,
ima.VideoAdPlayerCallback,
) addCallback,
required void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAd,
required dynamic pauseAd,
required dynamic playAd,
required dynamic release,
required dynamic removeCallback,
required dynamic stopAd,
}) {
loadAdCallback = loadAd;
addCallbackCallback = addCallback;
return MockVideoAdPlayer();
},
);
AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
onContainerAdded: (_) {},
imaProxy: imaProxy,
),
);
final ima.AdMediaInfo mockAdMediaInfo = MockAdMediaInfo();
loadAdCallback(MockVideoAdPlayer(), mockAdMediaInfo, MockAdPodInfo());
final MockVideoAdPlayerCallback mockPlayerCallback =
MockVideoAdPlayerCallback();
addCallbackCallback(MockVideoAdPlayer(), mockPlayerCallback);
onCompletionCallback(MockVideoView(), MockMediaPlayer());
verify(mockPlayerCallback.onEnded(mockAdMediaInfo));
});
test('error loading the ad notifies IMA SDK of error', () {
late final void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAdCallback;
late final void Function(
ima.VideoAdPlayer,
ima.VideoAdPlayerCallback,
) addCallbackCallback;
late final void Function(
ima.VideoView,
ima.MediaPlayer,
int,
int,
) onErrorCallback;
final InteractiveMediaAdsProxy imaProxy = InteractiveMediaAdsProxy(
newFrameLayout: () => MockFrameLayout(),
newVideoView: ({
required void Function(
ima.VideoView,
ima.MediaPlayer,
int,
int,
) onError,
dynamic onPrepared,
dynamic onCompletion,
}) {
onErrorCallback = onError;
return MockVideoView();
},
createAdDisplayContainerImaSdkFactory: (
_,
__,
) async {
return MockAdDisplayContainer();
},
newVideoAdPlayer: ({
required void Function(
ima.VideoAdPlayer,
ima.VideoAdPlayerCallback,
) addCallback,
required void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAd,
required dynamic pauseAd,
required dynamic playAd,
required dynamic release,
required dynamic removeCallback,
required dynamic stopAd,
}) {
loadAdCallback = loadAd;
addCallbackCallback = addCallback;
return MockVideoAdPlayer();
},
);
AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
onContainerAdded: (_) {},
imaProxy: imaProxy,
),
);
final ima.AdMediaInfo mockAdMediaInfo = MockAdMediaInfo();
loadAdCallback(MockVideoAdPlayer(), mockAdMediaInfo, MockAdPodInfo());
final MockVideoAdPlayerCallback mockPlayerCallback =
MockVideoAdPlayerCallback();
addCallbackCallback(MockVideoAdPlayer(), mockPlayerCallback);
onErrorCallback(MockVideoView(), MockMediaPlayer(), 0, 0);
verify(mockPlayerCallback.onError(mockAdMediaInfo));
});
test('play ad once when it is prepared', () async {
late final void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAdCallback;
late final void Function(
ima.VideoAdPlayer,
ima.VideoAdPlayerCallback,
) addCallbackCallback;
late final Future<void> Function(
ima.VideoView,
ima.MediaPlayer,
) onPreparedCallback;
const int adDuration = 100;
const int adProgress = 10;
final InteractiveMediaAdsProxy imaProxy = InteractiveMediaAdsProxy(
newFrameLayout: () => MockFrameLayout(),
newVideoView: ({
dynamic onError,
dynamic onPrepared,
dynamic onCompletion,
}) {
// VideoView.onPrepared returns void, but the implementation uses an
// async callback method.
onPreparedCallback = onPrepared! as Future<void> Function(
ima.VideoView,
ima.MediaPlayer,
);
final MockVideoView mockVideoView = MockVideoView();
when(mockVideoView.getCurrentPosition()).thenAnswer(
(_) async => adProgress,
);
return mockVideoView;
},
createAdDisplayContainerImaSdkFactory: (_, __) async {
return MockAdDisplayContainer();
},
newVideoAdPlayer: ({
required void Function(
ima.VideoAdPlayer,
ima.VideoAdPlayerCallback,
) addCallback,
required void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAd,
required dynamic pauseAd,
required dynamic playAd,
required dynamic release,
required dynamic removeCallback,
required dynamic stopAd,
}) {
loadAdCallback = loadAd;
addCallbackCallback = addCallback;
return MockVideoAdPlayer();
},
newVideoProgressUpdate: ({
required int currentTimeMs,
required int durationMs,
}) {
expect(currentTimeMs, adProgress);
expect(durationMs, adDuration);
return MockVideoProgressUpdate();
},
);
AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
onContainerAdded: (_) {},
imaProxy: imaProxy,
),
);
final ima.AdMediaInfo mockAdMediaInfo = MockAdMediaInfo();
loadAdCallback(MockVideoAdPlayer(), mockAdMediaInfo, MockAdPodInfo());
final MockVideoAdPlayerCallback mockPlayerCallback =
MockVideoAdPlayerCallback();
addCallbackCallback(MockVideoAdPlayer(), mockPlayerCallback);
final MockMediaPlayer mockMediaPlayer = MockMediaPlayer();
when(mockMediaPlayer.getDuration()).thenAnswer((_) async => adDuration);
await onPreparedCallback(MockVideoView(), mockMediaPlayer);
verify(mockMediaPlayer.start());
// Ad progress is updated with a reoccurring timer, so this waits for
// at least one update.
await Future<void>.delayed(const Duration(milliseconds: 300));
verify(mockPlayerCallback.onAdProgress(mockAdMediaInfo, any));
});
test('pause ad', () async {
late final void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAdCallback;
late final Future<void> Function(
ima.VideoView,
ima.MediaPlayer,
) onPreparedCallback;
late final Future<void> Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
) pauseAdCallback;
final InteractiveMediaAdsProxy imaProxy = InteractiveMediaAdsProxy(
newFrameLayout: () => MockFrameLayout(),
newVideoView: ({
dynamic onError,
void Function(
ima.VideoView,
ima.MediaPlayer,
)? onPrepared,
dynamic onCompletion,
}) {
// VideoView.onPrepared returns void, but the implementation uses an
// async callback method.
onPreparedCallback = onPrepared! as Future<void> Function(
ima.VideoView,
ima.MediaPlayer,
);
final MockVideoView mockVideoView = MockVideoView();
when(mockVideoView.getCurrentPosition()).thenAnswer((_) async => 10);
return mockVideoView;
},
createAdDisplayContainerImaSdkFactory: (_, __) async {
return MockAdDisplayContainer();
},
newVideoAdPlayer: ({
required dynamic addCallback,
required void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAd,
required dynamic pauseAd,
required dynamic playAd,
required dynamic release,
required dynamic removeCallback,
required dynamic stopAd,
}) {
loadAdCallback = loadAd;
// VideoAdPlayer.pauseAd returns void, but the implementation uses an
// async callback method.
pauseAdCallback = pauseAd as Future<void> Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
);
return MockVideoAdPlayer();
},
newVideoProgressUpdate: ({
required int currentTimeMs,
required int durationMs,
}) {
return MockVideoProgressUpdate();
},
);
AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
onContainerAdded: (_) {},
imaProxy: imaProxy,
),
);
final ima.AdMediaInfo mockAdMediaInfo = MockAdMediaInfo();
loadAdCallback(MockVideoAdPlayer(), mockAdMediaInfo, MockAdPodInfo());
final MockMediaPlayer mockMediaPlayer = MockMediaPlayer();
when(mockMediaPlayer.getDuration()).thenAnswer((_) async => 100);
await onPreparedCallback(MockVideoView(), mockMediaPlayer);
await pauseAdCallback(MockVideoAdPlayer(), mockAdMediaInfo);
verify(mockMediaPlayer.pause());
});
test('ad does not play automatically after calling pause', () async {
late final void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAdCallback;
late final Future<void> Function(
ima.VideoView,
ima.MediaPlayer,
) onPreparedCallback;
late final Future<void> Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
) pauseAdCallback;
late final void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
) playAdCallback;
final InteractiveMediaAdsProxy imaProxy = InteractiveMediaAdsProxy(
newFrameLayout: () => MockFrameLayout(),
newVideoView: ({
dynamic onError,
void Function(
ima.VideoView,
ima.MediaPlayer,
)? onPrepared,
dynamic onCompletion,
}) {
// VideoView.onPrepared returns void, but the implementation uses an
// async callback method.
onPreparedCallback = onPrepared! as Future<void> Function(
ima.VideoView,
ima.MediaPlayer,
);
final MockVideoView mockVideoView = MockVideoView();
when(mockVideoView.getCurrentPosition()).thenAnswer((_) async => 10);
return mockVideoView;
},
createAdDisplayContainerImaSdkFactory: (_, __) async {
return MockAdDisplayContainer();
},
newVideoAdPlayer: ({
required dynamic addCallback,
required void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
ima.AdPodInfo,
) loadAd,
required dynamic pauseAd,
required void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
) playAd,
required dynamic release,
required dynamic removeCallback,
required dynamic stopAd,
}) {
loadAdCallback = loadAd;
// VideoAdPlayer.pauseAd returns void, but the implementation uses an
// async callback method.
pauseAdCallback = pauseAd as Future<void> Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
);
playAdCallback = playAd;
return MockVideoAdPlayer();
},
newVideoProgressUpdate: ({
required int currentTimeMs,
required int durationMs,
}) {
return MockVideoProgressUpdate();
},
);
AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
onContainerAdded: (_) {},
imaProxy: imaProxy,
),
);
final ima.AdMediaInfo mockAdMediaInfo = MockAdMediaInfo();
when(mockAdMediaInfo.url).thenReturn('url');
loadAdCallback(MockVideoAdPlayer(), mockAdMediaInfo, MockAdPodInfo());
final MockMediaPlayer mockMediaPlayer = MockMediaPlayer();
when(mockMediaPlayer.getDuration()).thenAnswer((_) async => 100);
await onPreparedCallback(MockVideoView(), mockMediaPlayer);
// Pausing the ad prevents Ad from starting again automatically when it is
// prepared.
await pauseAdCallback(MockVideoAdPlayer(), mockAdMediaInfo);
reset(mockMediaPlayer);
await onPreparedCallback(MockVideoView(), mockMediaPlayer);
verifyNever(mockMediaPlayer.start());
// The playAd callback allows the Ad to start automatically once it is
// prepared.
playAdCallback(MockVideoAdPlayer(), mockAdMediaInfo);
await onPreparedCallback(MockVideoView(), mockMediaPlayer);
verify(mockMediaPlayer.start());
});
test('play ad', () async {
late final void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
) playAdCallback;
final MockVideoView mockVideoView = MockVideoView();
final InteractiveMediaAdsProxy imaProxy = InteractiveMediaAdsProxy(
newFrameLayout: () => MockFrameLayout(),
newVideoView: ({
dynamic onError,
dynamic onPrepared,
dynamic onCompletion,
}) {
return mockVideoView;
},
createAdDisplayContainerImaSdkFactory: (_, __) async {
return MockAdDisplayContainer();
},
newVideoAdPlayer: ({
required dynamic addCallback,
required dynamic loadAd,
required dynamic pauseAd,
required void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
) playAd,
required dynamic release,
required dynamic removeCallback,
required dynamic stopAd,
}) {
playAdCallback = playAd;
return MockVideoAdPlayer();
},
);
AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
onContainerAdded: (_) {},
imaProxy: imaProxy,
),
);
const String videoUrl = 'url';
final ima.AdMediaInfo mockAdMediaInfo = MockAdMediaInfo();
when(mockAdMediaInfo.url).thenReturn(videoUrl);
playAdCallback(MockVideoAdPlayer(), mockAdMediaInfo);
verify(mockVideoView.setVideoUri(videoUrl));
});
test('stop ad creates and sets a new VideoView', () async {
late final void Function(
ima.VideoAdPlayer,
ima.AdMediaInfo,
) stopAdCallback;
final MockFrameLayout mockFrameLayout = MockFrameLayout();
late final MockVideoView mockVideoView = MockVideoView();
late final MockVideoView mockVideoView2 = MockVideoView();
int newViewVideoCallCount = 0;
final InteractiveMediaAdsProxy imaProxy = InteractiveMediaAdsProxy(
newFrameLayout: () => mockFrameLayout,
newVideoView: ({
dynamic onError,
dynamic onPrepared,
dynamic onCompletion,
}) {
switch (newViewVideoCallCount) {
case 0:
newViewVideoCallCount++;
return mockVideoView;
case 1:
newViewVideoCallCount++;
return mockVideoView2;
default:
fail('newVideoView was called too many times');
}
},
createAdDisplayContainerImaSdkFactory: (_, __) async {
return MockAdDisplayContainer();
},
newVideoAdPlayer: ({
required dynamic addCallback,
required dynamic loadAd,
required dynamic pauseAd,
required dynamic playAd,
required dynamic release,
required dynamic removeCallback,
required void Function(ima.VideoAdPlayer, ima.AdMediaInfo) stopAd,
}) {
stopAdCallback = stopAd;
return MockVideoAdPlayer();
},
);
AndroidAdDisplayContainer(
AndroidAdDisplayContainerCreationParams(
onContainerAdded: (_) {},
imaProxy: imaProxy,
),
);
stopAdCallback(MockVideoAdPlayer(), MockAdMediaInfo());
verify(mockFrameLayout.removeView(mockVideoView));
verify(mockFrameLayout.addView(mockVideoView2));
});
});
}