diff --git a/integration_test/tests/advertisingTest.ts b/integration_test/tests/advertisingTest.ts new file mode 100644 index 00000000..07afbfae --- /dev/null +++ b/integration_test/tests/advertisingTest.ts @@ -0,0 +1,181 @@ +import { TestScope } from 'cavy'; +import { + callPlayer, + callPlayerAndExpectEvent, + callPlayerAndExpectEvents, + EventSequence, + EventType, + expectEvent, + expectEvents, + loadSourceConfig, + RepeatedEvent, + startPlayerTest, +} from '../playertesting'; +import { Sources } from './helper/Sources'; +import { + AdItem, + AdSourceType, + AdvertisingConfig, +} from 'bitmovin-player-react-native'; +import { AdTags } from './helper/Ads'; +import { Platform } from 'react-native'; + +export default (spec: TestScope) => { + function commonAdvertisingTests( + adItems: AdItem[], + type: 'VAST' | 'VMAP' | 'progressive' + ) { + spec.describe(`scheduling ${type} ads via AdvertisingConfig`, () => { + spec.it('emits AdScheduled events', async () => { + const advertisingConfig = { + schedule: adItems, + } as AdvertisingConfig; + await startPlayerTest({ advertisingConfig }, async () => { + await callPlayerAndExpectEvents((player) => { + player.load(Sources.artOfMotionHls); + }, RepeatedEvent(EventType.AdScheduled, adItems.length)); + }); + }); + }); + spec.describe(`scheduling ${type} ads via scheduleAd API`, () => { + spec.it('emits AdScheduled events', async () => { + await startPlayerTest({}, async () => { + await loadSourceConfig(Sources.artOfMotionHls); + await callPlayerAndExpectEvents((player) => { + adItems.forEach((adItem) => player.scheduleAd(adItem)); + }, RepeatedEvent(EventType.AdScheduled, adItems.length)); + }); + }); + }); + spec.describe(`playing ${type} ads`, () => { + spec.it('emits Ad events', async () => { + const advertisingConfig = { + schedule: adItems, + } as AdvertisingConfig; + await startPlayerTest({ advertisingConfig }, async () => { + await callPlayer(async (player) => { + player.load(Sources.artOfMotionHls); + player.play(); + }); + if (type !== 'progressive') { + await expectEvent(EventType.AdManifestLoaded, 20); + } + await expectEvents( + EventSequence( + EventType.AdBreakStarted, + EventType.AdStarted, + EventType.AdQuartile, + EventType.AdQuartile, + EventType.AdQuartile, + EventType.AdFinished, + EventType.AdBreakFinished + ), + 18 + ); + await callPlayerAndExpectEvent((player) => { + player.seek(13); + }, EventType.Seeked); + if (Platform.OS === 'android' && type === 'VAST') { + await expectEvent(EventType.AdManifestLoaded, 20); + } + await expectEvents( + EventSequence( + EventType.AdBreakStarted, + EventType.AdStarted, + EventType.AdQuartile, + EventType.AdQuartile, + EventType.AdQuartile, + EventType.AdFinished, + EventType.AdBreakFinished + ), + 15 + ); + await callPlayerAndExpectEvent(async (player) => { + const duration = await player.getDuration(); + player.seek(duration - 5); + }, EventType.Seeked); + if (Platform.OS === 'android' && type === 'VAST') { + await expectEvent(EventType.AdManifestLoaded, 20); + } + await expectEvents( + EventSequence( + EventType.AdBreakStarted, + EventType.AdStarted, + EventType.AdQuartile, + EventType.AdQuartile, + EventType.AdQuartile, + EventType.AdFinished, + EventType.AdBreakFinished + ), + 18 + ); + }); + }); + }); + } + + commonAdvertisingTests( + [{ sources: [{ tag: AdTags.vmapPreMidPost, type: AdSourceType.IMA }] }], + 'VMAP' + ); + commonAdvertisingTests( + [ + { + sources: [{ tag: AdTags.vastSkippable, type: AdSourceType.IMA }], + position: 'pre', + }, + { + sources: [{ tag: AdTags.vast1, type: AdSourceType.IMA }], + position: '15', + }, + { + sources: [{ tag: AdTags.vast2, type: AdSourceType.IMA }], + position: 'post', + }, + ], + 'VAST' + ); + commonAdvertisingTests( + [ + { + sources: [{ tag: AdTags.progressive, type: AdSourceType.PROGRESSIVE }], + position: 'pre', + }, + { + sources: [{ tag: AdTags.progressive, type: AdSourceType.PROGRESSIVE }], + position: '15', + }, + { + sources: [{ tag: AdTags.progressive, type: AdSourceType.PROGRESSIVE }], + position: 'post', + }, + ], + 'progressive' + ); + + function advertisingErrorTests(adItem: AdItem, label: string) { + spec.describe(`scheduling erroneous ${label} ad`, () => { + spec.it('emits AdError events', async () => { + await startPlayerTest({}, async () => { + await loadSourceConfig(Sources.artOfMotionHls); + await callPlayerAndExpectEvent((player) => { + player.scheduleAd(adItem); + player.play(); + }, EventType.AdError); + }); + }); + }); + } + + advertisingErrorTests( + { sources: [{ tag: AdTags.error, type: AdSourceType.IMA }] }, + 'IMA' + ); + if (Platform.OS !== 'android') { + // AdError event is not emitted on Android when progressive ad playback fails + advertisingErrorTests( + { sources: [{ tag: AdTags.error, type: AdSourceType.PROGRESSIVE }] }, + 'progressive' + ); + } +}; diff --git a/integration_test/tests/helper/Ads.ts b/integration_test/tests/helper/Ads.ts new file mode 100644 index 00000000..1cbe3dbd --- /dev/null +++ b/integration_test/tests/helper/Ads.ts @@ -0,0 +1,20 @@ +const withCorrelator = (tag: string): string => + `${tag}${Math.floor(Math.random() * 100000)}`; + +export const AdTags = { + vastSkippable: withCorrelator( + 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dskippablelinear&correlator=' + ), + vast1: withCorrelator( + 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/single_ad_samples&ciu_szs=300x250&impl=s&gdfp_req=1&env=vp&output=vast&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ct%3Dlinear&correlator=' + ), + vast2: withCorrelator( + 'https://pubads.g.doubleclick.net/gampad/ads?slotname=/124319096/external/ad_rule_samples&sz=640x480&ciu_szs=300x250&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpostpod&url=&unviewed_position_start=1&output=xml_vast3&impl=s&env=vp&gdfp_req=1&ad_rule=0&useragent=curl/7.71.1,gzip(gfe)&vad_type=linear&vpos=postroll&pod=1&ppos=1&lip=true&min_ad_duration=0&max_ad_duration=30000&vrid=6376&cmsid=496&video_doc_id=short_onecue&kfa=0&tfcd=0&correlator=' + ), + vmapPreMidPost: withCorrelator( + 'https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpost&cmsid=496&vid=short_onecue&correlator=' + ), + progressive: + 'https://bitmovin-a.akamaihd.net/content/testing/ads/testad2s.mp4', + error: withCorrelator('https://mock.codes/404?correlator='), +}; diff --git a/integration_test/tests/index.ts b/integration_test/tests/index.ts index 42c656ac..64961e4a 100644 --- a/integration_test/tests/index.ts +++ b/integration_test/tests/index.ts @@ -1,5 +1,6 @@ -import PlaybackTest from './playbackTest'; +import AdvertisingTest from './advertisingTest'; import LoadingTest from './loadingTest'; +import PlaybackTest from './playbackTest'; import UnloadingTest from './unloadingTest'; -export default [PlaybackTest, LoadingTest, UnloadingTest]; +export default [AdvertisingTest, LoadingTest, PlaybackTest, UnloadingTest];