Skip to content

Commit

Permalink
Merge pull request #450 from THEOplayer/maintenance/e2e_extend
Browse files Browse the repository at this point in the history
E2E tests
  • Loading branch information
tvanlaerhoven authored Nov 29, 2024
2 parents b776647 + deb932b commit 6ad792c
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 104 deletions.
35 changes: 20 additions & 15 deletions e2e/src/tests/Ads.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,34 @@ import hls from '../res/hls.json';
import ads from '../res/ads.json';
import { getTestPlayer } from '../components/TestableTHEOplayerView';
import { waitForPlayerEvents, waitForPlayerEventTypes } from '../utils/Actions';
import { TestSourceDescription, TestSources } from '../utils/SourceUtils';

function extendSourceWithAds(source: SourceDescription, ad: AdDescription): SourceDescription {
return { ...source, ads: [ad] };
}

export default function (spec: TestScope) {
spec.describe('Set source with ads and auto-play', function () {
spec.it('dispatches sourcechange, play, playing and ad events', async function () {
const player = await getTestPlayer();
const playEventsPromise = waitForPlayerEventTypes(player, [PlayerEventType.SOURCE_CHANGE, PlayerEventType.PLAY, PlayerEventType.PLAYING]);
TestSources()
.withAds()
.forEach((testSource: TestSourceDescription) => {
spec.describe(`Set ${testSource.description} and auto-play`, function () {
spec.it('dispatches sourcechange, play, playing and ad events', async function () {
const player = await getTestPlayer();
const playEventsPromise = waitForPlayerEventTypes(player, [PlayerEventType.SOURCE_CHANGE, PlayerEventType.PLAY, PlayerEventType.PLAYING]);

const adEventsPromise = waitForPlayerEvents(player, [
{ type: PlayerEventType.AD_EVENT, subType: AdEventType.AD_BREAK_BEGIN } as AdEvent,
{ type: PlayerEventType.AD_EVENT, subType: AdEventType.AD_BEGIN } as AdEvent,
]);
const adEventsPromise = waitForPlayerEvents(player, [
{ type: PlayerEventType.AD_EVENT, subType: AdEventType.AD_BREAK_BEGIN } as AdEvent,
{ type: PlayerEventType.AD_EVENT, subType: AdEventType.AD_BEGIN } as AdEvent,
]);

// Start autoplay
player.autoplay = true;
player.source = extendSourceWithAds(hls[0], ads[0] as AdDescription);
// Start autoplay
player.autoplay = true;
player.source = extendSourceWithAds(hls[0], ads[0] as AdDescription);

// Expect events.
await playEventsPromise;
await adEventsPromise;
// Expect events.
await playEventsPromise;
await adEventsPromise;
});
});
});
});
}
99 changes: 46 additions & 53 deletions e2e/src/tests/Basic.spec.ts
Original file line number Diff line number Diff line change
@@ -1,72 +1,65 @@
import { TestScope } from 'cavy';
import { Platform } from 'react-native';
import { PlayerEventType, SourceDescription } from 'react-native-theoplayer';
import dash from '../res/dash.json';
import hls from '../res/hls.json';
import mp4 from '../res/mp4.json';
import { PlayerEventType } from 'react-native-theoplayer';
import { expect, preparePlayerWithSource, waitForPlayerEventType, waitForPlayerEventTypes } from '../utils/Actions';
import { TestSourceDescription, TestSources } from '../utils/SourceUtils';

const SEEK_THRESHOLD = 250;

function testBasicPlayout(spec: TestScope, title: string, source: SourceDescription) {
spec.describe(title, function () {
spec.it('dispatches sourcechange event on setting a source without autoplay', async function () {
// Set source and wait for playback
const player = await preparePlayerWithSource(source, false);
export default function (spec: TestScope) {
TestSources()
.withPlain()
.forEach((testSource: TestSourceDescription) => {
spec.describe(`Set ${testSource.description} and auto-play`, function () {
spec.it(`dispatches sourcechange event on setting the source without autoplay`, async function () {
// Set source and wait for playback
const player = await preparePlayerWithSource(testSource.source, false);

// Still playing
expect(player.paused).toBeTruthy();
});
// Still playing
expect(player.paused).toBeTruthy();
});

spec.it('dispatches sourcechange, play and playing events in order on setting a source with autoplay', async function () {
// Set source and wait for playback
const player = await preparePlayerWithSource(source);
spec.it(`dispatches sourcechange, play and playing events in order on setting the source with autoplay`, async function () {
// Set source and wait for playback
const player = await preparePlayerWithSource(testSource.source);

// Still playing
expect(player.paused).toBeFalsy();
});
// Still playing
expect(player.paused).toBeFalsy();
});

spec.it('dispatches a seeked event after seeking', async function () {
// Set source and wait for playback
const player = await preparePlayerWithSource(source);
spec.it('dispatches a seeked event after seeking', async function () {
// Set source and wait for playback
const player = await preparePlayerWithSource(testSource.source);

// Seek
const seekPromise = waitForPlayerEventType(player, PlayerEventType.SEEKED);
const seekTime = 10e3;
player.currentTime = seekTime;
// Seek
const seekPromise = waitForPlayerEventType(player, PlayerEventType.SEEKED);
const seekTime = 10e3;
player.currentTime = seekTime;

// Wait for `seeked` event.
await seekPromise;
// Wait for `seeked` event.
await seekPromise;

// Expect currentTime to be updated.
expect(player.currentTime).toBeSmallerThanOrEqual(seekTime + SEEK_THRESHOLD);
});
// Expect currentTime to be updated.
expect(player.currentTime).toBeSmallerThanOrEqual(seekTime + SEEK_THRESHOLD);
});

spec.it('dispatches paused, play and playing events after pausing & resuming playback', async function () {
// Set source and wait for playback
const player = await preparePlayerWithSource(source);
spec.it(`dispatches paused, play and playing events after pausing & resuming playback of the source`, async function () {
// Set source and wait for playback
const player = await preparePlayerWithSource(testSource.source);

// Pause play-out.
const pausePromise = waitForPlayerEventType(player, PlayerEventType.PAUSE);
player.pause();
// Pause play-out.
const pausePromise = waitForPlayerEventType(player, PlayerEventType.PAUSE);
player.pause();

// Wait for 'paused' event.
await pausePromise;
// Wait for 'paused' event.
await pausePromise;

// Resume play-out.
const playPromises = waitForPlayerEventTypes(player, [PlayerEventType.PLAY, PlayerEventType.PLAYING]);
player.play();
// Resume play-out.
const playPromises = waitForPlayerEventTypes(player, [PlayerEventType.PLAY, PlayerEventType.PLAYING]);
player.play();

// Wait for 'play' and 'playing' events.
await playPromises;
// Wait for 'play' and 'playing' events.
await playPromises;
});
});
});
});
}

export default function (spec: TestScope) {
if (Platform.OS === 'android' || Platform.OS === 'web') {
testBasicPlayout(spec, 'Set DASH source and auto-play', dash[0]);
}
testBasicPlayout(spec, 'Set HLS source and auto-play', hls[0]);
testBasicPlayout(spec, 'Set mp4 source and auto-play', mp4[0]);
}
96 changes: 60 additions & 36 deletions e2e/src/tests/PresentationMode.spec.ts
Original file line number Diff line number Diff line change
@@ -1,42 +1,66 @@
import { TestScope } from 'cavy';
import { PlayerEventType, PresentationMode, PresentationModeChangeEvent } from 'react-native-theoplayer';
import hls from '../res/hls.json';
import { expect, preparePlayerWithSource, waitForPlayerEvent } from '../utils/Actions';
import { PlayerEventType, PresentationMode, PresentationModeChangeEvent, RenderingTarget, THEOplayer } from 'react-native-theoplayer';
import { expect, preparePlayerWithSource, waitForPlayerEvent, waitForPlayerEventType } from '../utils/Actions';
import { sleep } from '../utils/TimeUtils';
import { Platform } from 'react-native';
import { TestSourceDescription, TestSources } from '../utils/SourceUtils';

export default function (spec: TestScope) {
spec.describe('Switch between presentation modes', function () {
spec.it('dispatches presentationmodechange events between inline and fullscreen.', async function () {
const player = await preparePlayerWithSource(hls[0]);

// Switch to fullscreen.
const fullscreenPromise = waitForPlayerEvent(player, {
type: PlayerEventType.PRESENTATIONMODE_CHANGE,
presentationMode: PresentationMode.fullscreen,
previousPresentationMode: PresentationMode.inline,
} as PresentationModeChangeEvent);
player.presentationMode = PresentationMode.fullscreen;

// Wait for 'presentationmodechange' event.
await fullscreenPromise;

// Play-out should not pause.
await sleep(500);
expect(player.paused).toBeFalsy();

// Switch back to inline.
const inlinePromise = waitForPlayerEvent(player, {
type: PlayerEventType.PRESENTATIONMODE_CHANGE,
presentationMode: PresentationMode.inline,
previousPresentationMode: PresentationMode.inline,
} as PresentationModeChangeEvent);
player.presentationMode = PresentationMode.inline;

// Wait for 'presentationmodechange' event.
await inlinePromise;

// Play-out should not pause.
expect(player.paused).toBeFalsy();
TestSources()
.withPlain()
.withAds()
.forEach((testSource: TestSourceDescription) => {
spec.describe(`Switch between presentation modes during play-out of a ${testSource.description}`, function () {
spec.it('dispatches presentationmodechange events between inline and fullscreen.', async function () {
const player = await preparePlayerWithSource(testSource.source);

// Switch to fullscreen.
const fullscreenPromise = waitForPlayerEvent(player, {
type: PlayerEventType.PRESENTATIONMODE_CHANGE,
presentationMode: PresentationMode.fullscreen,
previousPresentationMode: PresentationMode.inline,
} as PresentationModeChangeEvent);
player.presentationMode = PresentationMode.fullscreen;

// Wait for 'presentationmodechange' event.
await fullscreenPromise;

// Play-out should not pause.
await sleep(500);
expect(player.paused).toBeFalsy();

// Switch back to inline.
const inlinePromise = waitForPlayerEvent(player, {
type: PlayerEventType.PRESENTATIONMODE_CHANGE,
presentationMode: PresentationMode.inline,
previousPresentationMode: PresentationMode.inline,
} as PresentationModeChangeEvent);
player.presentationMode = PresentationMode.inline;

// Wait for 'presentationmodechange' event.
await inlinePromise;

// Play-out should not pause.
expect(player.paused).toBeFalsy();
});
});

if (Platform.OS === 'android') {
spec.describe(`Switch between rendering targets during play-out of a ${testSource.description}`, function () {
spec.it('continues play-out.', async function () {
const player = await preparePlayerWithSource(testSource.source);

await switchRenderingTarget(player, RenderingTarget.TEXTURE_VIEW);
await switchRenderingTarget(player, RenderingTarget.SURFACE_VIEW);
});
});
}
});
});
}

async function switchRenderingTarget(player: THEOplayer, renderingTarget: RenderingTarget, sleepTime: number = 500) {
console.debug(`Switching to ${renderingTarget}`);
await sleep(sleepTime);
player.renderingTarget = renderingTarget;
await waitForPlayerEventType(player, PlayerEventType.TIME_UPDATE);
}
84 changes: 84 additions & 0 deletions e2e/src/utils/SourceUtils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { AdDescription, SourceDescription } from 'react-native-theoplayer';
import dash from '../res/dash.json';
import hls from '../res/hls.json';
import mp4 from '../res/mp4.json';
import ads from '../res/ads.json';
import { Platform } from 'react-native';

export enum SourceType {
DASH,
HLS,
MP4,
}

export enum AdType {
IMA_PRE_ROLL,
}

export interface TestSourceDescription {
source: SourceDescription;
description: string;
}

type EnhancedSourceList = TestSourceDescription[] & SourceListMethods;

interface SourceListMethods {
withPlain: () => EnhancedSourceList;
withAds: () => EnhancedSourceList;
}

export function TestSources(): EnhancedSourceList {
const testSources: TestSourceDescription[] = [];

return Object.assign(testSources, {
withPlain() {
if (Platform.OS === 'android' || Platform.OS === 'web') {
testSources.push(getTestSource(SourceType.DASH));
}
testSources.push(getTestSource(SourceType.HLS));
testSources.push(getTestSource(SourceType.MP4));
return testSources as EnhancedSourceList;
},
withAds() {
if (Platform.OS === 'android' || Platform.OS === 'web') {
testSources.push(getTestSource(SourceType.DASH, AdType.IMA_PRE_ROLL));
}
testSources.push(getTestSource(SourceType.HLS, AdType.IMA_PRE_ROLL));
testSources.push(getTestSource(SourceType.MP4, AdType.IMA_PRE_ROLL));
return testSources as EnhancedSourceList;
},
});
}

export function getTestSource(sourceType: SourceType, adType?: AdType): TestSourceDescription {
let source: SourceDescription;
let description: string;
switch (sourceType) {
case SourceType.DASH:
source = dash[0];
description = 'DASH';
break;
case SourceType.HLS:
source = hls[0];
description = 'HLS';
break;
case SourceType.MP4:
source = mp4[0];
description = 'mp4';
break;
}
switch (adType) {
case AdType.IMA_PRE_ROLL:
source = extendSourceWithAds(source, ads[0] as AdDescription);
description += ' with IMA pre-roll';
break;
}
return {
source,
description,
};
}

function extendSourceWithAds(source: SourceDescription, ad: AdDescription): SourceDescription {
return { ...source, ads: [ad] };
}

0 comments on commit 6ad792c

Please sign in to comment.