Skip to content

Commit

Permalink
Add tests for components, fix structurednav using previous/next butto…
Browse files Browse the repository at this point in the history
…ns with empty canvases
  • Loading branch information
Dananji committed Sep 1, 2023
1 parent 10b6081 commit 9dbb61d
Show file tree
Hide file tree
Showing 8 changed files with 124 additions and 43 deletions.
14 changes: 8 additions & 6 deletions src/components/MediaPlayer/MediaPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,13 +129,13 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
*/
const updatePlayerSrcDetails = (duration, sources, isMultiSource) => {
let timeFragment = {};
setIsEmptyCanvas(false);
if (isMultiSource) {
playerDispatch({
start: 0,
end: duration,
type: 'setPlayerRange',
});
manifestDispatch({ type: 'setCanvasIsEmpty', isEmpty: false });
} else if (sources === undefined || sources.length === 0) {
playerDispatch({
type: 'updatePlayer'
Expand All @@ -145,7 +145,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
...playerConfig,
error: itemMessage
});
setIsEmptyCanvas(true);
manifestDispatch({ type: 'setCanvasIsEmpty', isEmpty: true });
} else {
const playerSrc = sources?.length > 0
? sources.filter((s) => s.selected)[0]
Expand All @@ -169,6 +169,8 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
end: timeFragment.end,
type: 'setPlayerRange',
});

manifestDispatch({ type: 'setCanvasIsEmpty', isEmpty: false });
}
}
};
Expand All @@ -193,7 +195,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
};

// VideoJS instance configurations
let videoJsOptions = !isEmptyCanvas ? {
let videoJsOptions = !canvasIsEmpty ? {
aspectRatio: isVideo ? '16:9' : '1:0',
autoplay: false,
bigPlayButton: isVideo,
Expand Down Expand Up @@ -239,7 +241,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
} : {}; // Empty configurations for empty canvases

// Add file download to toolbar when it is enabled via props
if (enableFileDownload) {
if (enableFileDownload && !canvasIsEmpty) {
videoJsOptions = {
...videoJsOptions,
controlBar: {
Expand All @@ -254,7 +256,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
};
}

if (isMultiCanvased) {
if (isMultiCanvased && !canvasIsEmpty) {
videoJsOptions = {
...videoJsOptions,
controlBar: {
Expand All @@ -272,7 +274,7 @@ const MediaPlayer = ({ enableFileDownload = false, enablePIP = false }) => {
};
}

if (isPlaylist && isEmptyCanvas) {
if (playlist.isPlaylist && canvasIsEmpty) {
return (
<div
data-testid="inaccessible-item"
Expand Down
2 changes: 1 addition & 1 deletion src/components/MediaPlayer/MediaPlayer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
margin: auto;

.message-display {
transform: translate(45%, 50%);
transform: translate(40%, 50%);
position: absolute;
margin: 0;
color: white;
Expand Down
22 changes: 19 additions & 3 deletions src/components/MediaPlayer/MediaPlayer.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { render, screen, waitFor } from '@testing-library/react';
import { withManifestAndPlayerProvider } from '../../services/testing-helpers';
import MediaPlayer from './MediaPlayer';
import audioManifest from '@TestData/transcript-canvas';
Expand Down Expand Up @@ -109,15 +109,31 @@ describe('MediaPlayer component', () => {
});

describe('with a playlist manifest', () => {
test('renders successfully', () => {
test('renders a message for an inaccessible Canvas', () => {
// Stub loading HTMLMediaElement for jsdom
window.HTMLMediaElement.prototype.load = () => { };

const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { manifest: playlistManifest, canvasIndex: 0, isPlaylist: true },
initialPlayerState: {},
});
render(<PlayerWithManifest />);
expect(screen.queryByTestId('inaccessible-item')).toBeInTheDocument();
expect(screen.getByText('You do not have permission to playback this item.')).toBeInTheDocument();
// expect(screen.queryByTestId('videojs-video-element')).not.toBeVisible();
});

test('renders player for a accessible Canvas', () => {
const PlayerWithManifest = withManifestAndPlayerProvider(MediaPlayer, {
initialManifestState: { manifest: playlistManifest, canvasIndex: 1, isPlaylist: true },
initialPlayerState: {},
});
render(<PlayerWithManifest />);
expect(screen.queryByTestId('inaccessible-item')).not.toBeInTheDocument();
expect(
screen.queryAllByTestId('videojs-video-element').length
).toBeGreaterThan(0);
expect(screen.queryByTestId('videojs-previous-button')).toBeInTheDocument();
expect(screen.queryByTestId('videojs-next-button')).toBeInTheDocument();
});
});
});
6 changes: 3 additions & 3 deletions src/components/MediaPlayer/VideoJS/VideoJSPlayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ function VideoJSPlayer({
]);
}
}
} else if (startTime === null && canvasSegments.length > 0) {
} else if (startTime === null && canvasSegments.length > 0 && isReady) {
// When canvas gets loaded into the player, set the currentNavItem and startTime
// if there's a media fragment starting from time 0.0.
// This then triggers the creation of a fragment highlight in the player's timerail
Expand All @@ -328,10 +328,10 @@ function VideoJSPlayer({
* Setting the current time of the player when using structure navigation
*/
React.useEffect(() => {
if (player !== null && player != undefined) {
if (player !== null && player != undefined && isReady) {
player.currentTime(currentTime, playerDispatch({ type: 'resetClick' }));
}
}, [isClicked]);
}, [isClicked, isReady]);

/**
* Remove existing timerail highlight if the player's currentTime
Expand Down
49 changes: 33 additions & 16 deletions src/components/StructuredNavigation/StructuredNavigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
getCanvasId,
canvasesInManifest,
getCustomStart,
getSegmentMap,
} from '@Services/iiif-parser';
import { getCanvasTarget, getMediaFragment } from '@Services/utility-helpers';
import './StructuredNavigation.scss';
Expand All @@ -21,13 +22,16 @@ const StructuredNavigation = () => {
const playerDispatch = usePlayerDispatch();

const { clickedUrl, isClicked, isPlaying, player } = usePlayerState();
const { canvasDuration, canvasIndex, hasMultiItems, targets, manifest } =
const { canvasDuration, canvasIndex, hasMultiItems, targets, manifest, isPlaylist, canvasIsEmpty } =
useManifestState();

const [canvasSegments, setCanvasSegments] = React.useState([]);

React.useEffect(() => {
// Update currentTime and canvasIndex in state if a
// custom start time and(or) canvas is given in manifest
if (manifest) {
setCanvasSegments(getSegmentMap({ manifest }));
const customStart = getCustomStart(manifest);
if (!customStart) {
return;
Expand All @@ -45,6 +49,13 @@ const StructuredNavigation = () => {
}
}, [manifest]);

// Set currentNavItem when current Canvas is an inaccessible item
React.useEffect(() => {
if (canvasIsEmpty && isPlaylist) {
manifestDispatch({ item: canvasSegments[canvasIndex], type: 'switchItem' });
}
}, [canvasIsEmpty, canvasIndex]);

React.useEffect(() => {
if (isClicked) {
const canvases = canvasesInManifest(manifest);
Expand Down Expand Up @@ -84,23 +95,29 @@ const StructuredNavigation = () => {
}
}

player.currentTime(timeFragmentStart);
playerDispatch({
startTime: timeFragment.start,
endTime: timeFragment.end,
type: 'setTimeFragment',
});
if (canvasIsEmpty) {
// Reset isClicked in state for
// inaccessible items (empty canvases)
playerDispatch({ type: 'resetClick' });
} else if (player) {
player.currentTime(timeFragmentStart);
playerDispatch({
startTime: timeFragment.start,
endTime: timeFragment.end,
type: 'setTimeFragment',
});

playerDispatch({
currentTime: timeFragmentStart,
type: 'setCurrentTime',
});
// Setting userActive to true shows timerail breifly, helps
// to visualize the structure in player while playing
if (isPlaying) player.userActive(true);
player.currentTime(timeFragmentStart);
playerDispatch({
currentTime: timeFragmentStart,
type: 'setCurrentTime',
});
// Setting userActive to true shows timerail breifly, helps
// to visualize the structure in player while playing
if (isPlaying) player.userActive(true);
player.currentTime(timeFragmentStart);
}
}
}, [isClicked]);
}, [isClicked, player]);

if (!manifest) {
return <p>No manifest - Please provide a valid manifest.</p>;
Expand Down
43 changes: 42 additions & 1 deletion src/components/StructuredNavigation/StructuredNavigation.test.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import React from 'react';
import { render, screen } from '@testing-library/react';
import { fireEvent, queryAllByTestId, render, screen, waitFor } from '@testing-library/react';
import StructuredNavigation from './StructuredNavigation';
import manifest from '@TestData/lunchroom-manners';
import {
withManifestProvider,
withManifestAndPlayerProvider,
withPlayerProvider,
} from '../../services/testing-helpers';
import playlist from '@TestData/playlist';

describe('StructuredNavigation component', () => {
describe('with manifest', () => {
Expand Down Expand Up @@ -100,4 +101,44 @@ describe('StructuredNavigation component', () => {
);
});
});

describe('with a playlist manifest', () => {
beforeEach(() => {
const NavWithPlayerAndManifest = withManifestAndPlayerProvider(StructuredNavigation, {
initialManifestState: { manifest: playlist, isPlaylist: true, canvasIsEmpty: true },
initialPlayerState: { playerRange: { start: null, end: null } },
});
render(<NavWithPlayerAndManifest />);
});

test('renders all playlist items', () => {
expect(screen.queryAllByTestId('list-item')).toHaveLength(3);
expect(screen.queryAllByTestId('list-item')[1]).toHaveTextContent('Playlist Item 1');
});

test('renders lock icon for inaccessible items', () => {
expect(screen.queryAllByTestId('list-item')[0]).toHaveTextContent('Restricted Item');
expect(screen.queryAllByTestId('list-item')[0].children[1]).toHaveClass('structure-item-locked');
});

test('renders first item as active', () => {
waitFor(() => {
expect(screen.queryAllByTestId('list-item')[0]).toHaveClass('active');
});
});

test('marks inaccessible items as active when clicked', () => {
const firstItem = screen.queryAllByTestId('list-item')[0];
const inaccessibleItem = screen.queryAllByTestId('list-item')[1];

waitFor(() => {
expect(firstItem).toHaveClass('active');

fireEvent.click(inaccessibleItem);

expect(inaccessibleItem).toHaveClass('active');
expect(firstItem).not.toHaveClass('active');
});
});
});
});
7 changes: 7 additions & 0 deletions src/context/manifest-context.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const defaultState = {
canvasIndex: 0, // index for active canvas
currentNavItem: null,
canvasDuration: 0,
canvasIsEmpty: false,
targets: [],
hasMultiItems: false, // multiple resources in a single canvas
srcIndex: 0, // index for multiple resources in a single canvas
Expand Down Expand Up @@ -106,6 +107,12 @@ function manifestReducer(state = defaultState, action) {
}
};
}
case 'setCanvasIsEmpty': {
return {
...state,
canvasIsEmpty: action.isEmpty,
};
}
default: {
throw new Error(`Unhandled action type: ${action.type}`);
}
Expand Down
24 changes: 11 additions & 13 deletions src/test_data/playlist.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,19 +202,17 @@ export default {
id: 'http://example.com/manifests/playlist/canvas/2/page/annotation',
type: 'Annotation',
motivation: 'painting',
items: [
{
id: 'http://example.com/volleyball/high/volleyball-for-boys.mp4#t=0,32.0',
type: 'Video',
format: 'video/mp4',
label: {
en: ['High'],
},
height: 360,
width: 480,
duration: 32.0
}
],
body: {
id: 'http://example.com/volleyball/high/volleyball-for-boys.mp4#t=0,32.0',
type: 'Video',
format: 'video/mp4',
label: {
en: ['High'],
},
height: 360,
width: 480,
duration: 32.0
},
target: 'http://example.com/manifests/playlist/canvas/2',
},
],
Expand Down

0 comments on commit 9dbb61d

Please sign in to comment.