Browse Source

Relecture + évolutions mineures du code.

master
Fabrice PENHOËT 5 months ago
parent
commit
56c852aca5
  1. 50
      src/vCarousel.ts
  2. 64
      tests/vCarouselSpec.ts

50
src/vCarousel.ts

@ -1,10 +1,10 @@
const errors=require("./errors.js");
// La classe vCarousel est instanciée en recevant un tableau d'ids des conteneurs HTML des vidéos à afficher successivement.
// Le tableau n'est pas dédoublonné, pour rester libre d'afficher plusieurs fois la même vidéo durant un tour du carrousel.
// La classe vCarousel est instanciée en recevant un tableau contenant les ids des conteneurs HTML des vidéos à afficher successivement.
// Le tableau n'est pas dédoublonné, de manière à rester libre d'afficher plusieurs fois la même vidéo durant un tour du carrousel.
// Plusieurs options sont possibles :
// - firstVideoId est une chaîne permettant de fournir l'id du conteneur de la première vidéo à afficher lors de l'appel. Par défaut la première vidéo de la liste sera affichée. Si on passe une chaîne vide, aucune ne sera affichée.
// - playFirstVideo et playNextVideos demandent de lancer automatiquement la lecture respectivement de la vidéo demandée et de celles automatiquement chargée par la fonction. Ce n'est pas le cas par défaut et peut être bloqué par le navigateur.
// - playFirstVideo et playNextVideos demandent de lancer automatiquement la lecture, respectivement de la première vidéo demandée et de celles ensuite chargées. Ce n'est pas le cas par défaut et peut être bloqué par le navigateur.
// - noStop permet de demander à ce que les vidéos continuent à tourner, même quand toutes ont été affichées. Par défaut, le carrousel s'arrête.
interface videoDOM
{
@ -23,33 +23,33 @@ export class vCarousel
private nbVContainers: number = 0;
private _currentVideo: HTMLMediaElement|undefined = undefined;
// Les ids reçus doivent correspondre à des éléments HTML contenant une vidéo.
// Les ids reçus doivent correspondre à des éléments HTML contenant une vidéo :
set vContainers(vContainersIds: string[])
{
for(let containerId of vContainersIds)
for(const containerId of vContainersIds)
{
let checkContainerExist=document.getElementById(containerId);
const checkContainerExist=document.getElementById(containerId);
if(checkContainerExist === null)
throw new Error(errors.elementNotFound+containerId);
else
{
let checkVideoExist=<HTMLMediaElement>document.querySelector("#"+containerId+" video");
const checkVideoExist=<HTMLMediaElement>document.querySelector("#"+containerId+" video");
if(checkVideoExist === null)
throw new Error(errors.videoNotFound+containerId);
else
this._vContainers.push({id:containerId, containerElt:checkContainerExist, videoElt:checkVideoExist});
}
}
// Il faut au moins 2 vidéos à faire tourner.
// Il faut au moins 2 vidéos à faire tourner :
this.nbVContainers=this._vContainers.length;
if(this.nbVContainers < 2)
throw new Error(errors.need2Videos);
}
// Si this._firstVideoId a été fourni, on vérifie qu'il est présent dans la liste des conteneurs de vidéos.
// this._firstVideoId doit être résent dans la liste des conteneurs de vidéos :
set firstVideoId(firstVideo: string|undefined)
{
if((firstVideo!==undefined && firstVideo!=="" && this._vContainers.findIndex(video => video.id === firstVideo) !== -1) || ( firstVideo===""))
if((firstVideo !== undefined && firstVideo !== "" && this._vContainers.findIndex(video => video.id === firstVideo) !== -1) || ( firstVideo === ""))
this._firstVideoId=firstVideo;
}
@ -81,47 +81,47 @@ export class vCarousel
public run(): void
{
const vCarousel=this; // évite les confusions avec le "this" des événements plus bas.
if(vCarousel.nbVContainers < 2)
if(vCarousel.nbVContainers < 2) // permet en fait de tester que le setter vContainers a bien été appelé
throw new Error(errors.needVideosProvided);
for (let i = 0; i < vCarousel.nbVContainers; i++)
{
let vContainer=vCarousel._vContainers[i].containerElt;
let video=vCarousel._vContainers[i].videoElt;
if((vCarousel._firstVideoId!==undefined && vCarousel._vContainers[i].id!==vCarousel._firstVideoId) || (vCarousel._firstVideoId===undefined && i!==0) || vCarousel._firstVideoId==="")
vContainer.style.display = "none";
const vContainer=vCarousel._vContainers[i].containerElt;
const video=vCarousel._vContainers[i].videoElt;
if((vCarousel._firstVideoId !== undefined && vCarousel._vContainers[i].id !== vCarousel._firstVideoId) || (vCarousel._firstVideoId === undefined && i !== 0) || vCarousel._firstVideoId === "")
vContainer.style.display="none";
else
{
// Si une vidéo est déjà visible et en cours de lecture, je la stoppe
// Sinon elle risque de continuer à être lue tout en étant cachée.
if(vCarousel._currentVideo!==undefined && !vCarousel._currentVideo.paused)
if(vCarousel._currentVideo !== undefined && ! vCarousel._currentVideo.paused)
vCarousel._currentVideo.pause();
// J'affiche la première vidéo et j'essaye de la lancer, si cela est demandé
vContainer.style.display = "block";
// On affiche la première vidéo et essaye de la lancer, si cela est demandé :
vContainer.style.display="block";
vCarousel._currentVideo=video;
if(vCarousel._playFirstVideo===true)
if(vCarousel._playFirstVideo)
video.play();
}
var nbTurn=0;
video.addEventListener("ended", function()
{
// Sauf si demandé, le carrousel s'arrête lorsque toutes les vidéos ont été affichées une fois.
if(nbTurn < (vCarousel.nbVContainers-1) || vCarousel._noStop===true)
// Sauf si demandé, le carrousel s'arrête lorsque toutes les vidéos ont été affichées une fois :
if(nbTurn < (vCarousel.nbVContainers-1) || vCarousel._noStop)
{
vContainer.style.display = "none";
vContainer.style.display="none";
let nextVContainer: HTMLElement, nextVideo: HTMLMediaElement, nextHash: string, nextOne=0;
if(i < (vCarousel.nbVContainers-1))
nextOne=i+1;
nextVContainer=vCarousel._vContainers[nextOne].containerElt;
nextVideo=vCarousel._vContainers[nextOne].videoElt;
nextHash=vCarousel._vContainers[nextOne].id;
nextVContainer.style.display = "block";
nextVContainer.style.display="block";
vCarousel._currentVideo=nextVideo;
// On adapte l'ancre de l'url de manière à ce qu'elle soit cohérente avec la vidéo affichée.
// Attention car cela peut provoquer un déplacement dans la page pour atteindre l'ancre.
window.location.assign("#"+nextHash);
// On essaye de lancer la vidéo, si demandé.
if(vCarousel._playNextVideos===true)
// On essaye de lancer la vidéo, si demandé :
if(vCarousel._playNextVideos)
nextVideo.play();
nbTurn++;
}

64
tests/vCarouselSpec.ts

@ -1,62 +1,62 @@
import { vCarousel } from "../src/vCarousel";
const errors=require("../src/errors.js");
describe("vCarousel", function()
describe("vCarousel", () =>
{
let vCarouselTest : vCarousel;
const videosWebDir="https://forge.chapril.org/Fab_Blab/vCarousel/src/branch/master/public/videos";
const fixture="<div id='fixture'><figure id='noVideo'></figure><figure id='video1'><video controls='controls' preload='metadata'><source src='"+videosWebDir+"/Lizio-Poete-Ferrailleur-clownfunanbule.m4v' type='video/mp4'></source></video></figure><figure id='video2'><video controls='controls' preload='metadata'><source src='"+videosWebDir+"/Lizio-Poete-Ferrailleur-forgeron.m4v' type='video/mp4'></source></video></figure><figure id='video3'><video controls='controls' preload='metadata'><source src='"+videosWebDir+"/Lizio-Poete-Ferrailleur-drole-de-circuit.m4v' type='video/mp4'></source></video></div>";
beforeEach(function()
beforeEach(() =>
{
vCarouselTest=new vCarousel();
document.body.insertAdjacentHTML('afterbegin', fixture);
});
afterEach(function()
afterEach(() =>
{
document.body.removeChild(document.getElementById('fixture'));
});
it("Should be an instance of vCarousel", function()
it("Doit être une instance de vCarousel", () =>
{
expect(vCarouselTest).toBeInstanceOf(vCarousel);
});
describe("Setting vCarousel", function()
describe("Configuration de vCarousel", () =>
{
it("Doit générer une erreur si tous les ids passés ne correspondent pas à des éléments HTML existants.", function()
it("Doit générer une erreur si tous les ids passés ne correspondent pas à des éléments HTML existants.", () =>
{
expect(function() { return vCarouselTest.vContainers=["dontExistId","video1","video2"]; }).toThrowError(errors.elementNotFound+"dontExistId");
expect(function() { return vCarouselTest.vContainers=["video1","dontExistId","video2"]; }).toThrowError(errors.elementNotFound+"dontExistId");
expect(function() { return vCarouselTest.vContainers=["video1","video2","dontExistId"]; }).toThrowError(errors.elementNotFound+"dontExistId");
expect(() => { return vCarouselTest.vContainers=["dontExistId","video1","video2"]; }).toThrowError(errors.elementNotFound+"dontExistId");
expect(() => { return vCarouselTest.vContainers=["video1","dontExistId","video2"]; }).toThrowError(errors.elementNotFound+"dontExistId");
expect(() => { return vCarouselTest.vContainers=["video1","video2","dontExistId"]; }).toThrowError(errors.elementNotFound+"dontExistId");
});
it("Doit générer une erreur si tous les ids passés ne correspondent pas à des éléments HTML contenant une balise <video>.", function()
it("Doit générer une erreur si tous les ids passés ne correspondent pas à des éléments HTML contenant une balise <video>.", () =>
{
expect(function() { return vCarouselTest.vContainers=["noVideo","video1","video2"]; }).toThrowError(errors.videoNotFound+"noVideo");
expect(function() { return vCarouselTest.vContainers=["video1","noVideo","video2"]; }).toThrowError(errors.videoNotFound+"noVideo");
expect(function() { return vCarouselTest.vContainers=["video1","video2","noVideo"]; }).toThrowError(errors.videoNotFound+"noVideo");
expect(() => { return vCarouselTest.vContainers=["noVideo","video1","video2"]; }).toThrowError(errors.videoNotFound+"noVideo");
expect(() => { return vCarouselTest.vContainers=["video1","noVideo","video2"]; }).toThrowError(errors.videoNotFound+"noVideo");
expect(() => { return vCarouselTest.vContainers=["video1","video2","noVideo"]; }).toThrowError(errors.videoNotFound+"noVideo");
});
it("Doit générer une erreur s'il n'y a pas au moins 2 ids valides passés pour les éléments HTML contenant les balises <video>.", function()
it("Doit générer une erreur s'il n'y a pas au moins 2 ids valides passés pour les éléments HTML contenant les balises <video>.", () =>
{
expect(function() { return vCarouselTest.vContainers=["video1"]; }).toThrowError(errors.need2Videos);
expect(() => { return vCarouselTest.vContainers=["video1"]; }).toThrowError(errors.need2Videos);
});
it("Ne doit pas générer d'erreur s'il y a au moins 2 ids passés correspondant à des éléments HTML valides et contenant chacun une balise video.", function()
it("Ne doit pas générer d'erreur s'il y a au moins 2 ids passés correspondant à des éléments HTML valides et contenant chacun une balise video.", () =>
{
expect(function() { return vCarouselTest.vContainers=["video1","video2"]; }).not.toThrowError();
expect(() => { return vCarouselTest.vContainers=["video1","video2"]; }).not.toThrowError();
});
it("Ne doit pas prendre en compte l'id fourni pour la première vidéo à afficher, s'il ne correspond pas à une des vidéos fournies.", function()
it("Ne doit pas prendre en compte l'id fourni pour la première vidéo à afficher, s'il ne correspond pas à une des vidéos fournies.", () =>
{
vCarouselTest.vContainers=["video1","video2"];
vCarouselTest.firstVideoId="video3";
expect(vCarouselTest.firstVideoId).toBeUndefined();
});
it("Doit accepter un firstVideoId vide permettant de cacher toutes les vidéos au lancement.", function()
it("Doit accepter un firstVideoId vide permettant de cacher toutes les vidéos au lancement.", () =>
{
vCarouselTest.vContainers=["video1","video2"];
vCarouselTest.firstVideoId="";
@ -64,25 +64,25 @@ describe("vCarousel", function()
});
});
describe("running vCarousel", function()
describe("Lancement de vCarousel", () =>
{
it("Doit générer une erreur si on lance le carrousel sans avoir fourni les éléments HTML contenant les vidéos.", function()
it("Doit générer une erreur si on lance le carrousel sans avoir fourni les éléments HTML contenant les vidéos.", () =>
{
expect(function() { return vCarouselTest.run(); }).toThrowError(errors.needVideosProvided);
expect(() => { return vCarouselTest.run(); }).toThrowError(errors.needVideosProvided);
});
it("Doit cacher toutes les vidéos si cela est demandé en option, aucune vidéo n'étant alors enregistrée comme en cours.", function()
it("Doit cacher toutes les vidéos si cela est demandé en option, aucune vidéo n'étant alors enregistrée comme en cours.", () =>
{
const videosId=["video1","video2","video3"];
vCarouselTest.vContainers=videosId;
vCarouselTest.firstVideoId="";
vCarouselTest.run();
for(let containerId of videosId)
for(const containerId of videosId)
expect(document.getElementById(containerId).style.display).toBe("none");
expect(vCarouselTest.currentVideo).toBeUndefined();
});
it("Doit cacher toutes les vidéos, sauf celle passée en option, qui doit être enregistrée comme celle en cours.", function()
it("Doit cacher toutes les vidéos, sauf celle passée en option, qui doit être enregistrée comme celle en cours.", () =>
{
vCarouselTest.vContainers=["video1","video2","video3"];
vCarouselTest.firstVideoId="video3";
@ -93,7 +93,7 @@ describe("vCarousel", function()
expect(vCarouselTest.currentVideo).toEqual(<HTMLMediaElement>document.querySelector("#video3 video"));
});
it("Si aucune indication n'est passée en option, doit cacher toutes les vidéos, sauf la première qui doit être enregistrée comme celle en cours.", function()
it("Si aucune indication n'est passée en option, doit cacher toutes les vidéos, sauf la première qui doit être enregistrée comme celle en cours.", () =>
{
vCarouselTest.vContainers=["video1","video2","video3"];
vCarouselTest.run();
@ -103,7 +103,7 @@ describe("vCarousel", function()
expect(vCarouselTest.currentVideo).toEqual(<HTMLMediaElement>document.querySelector("#video1 video"));
});
it("Doit demander le lancement de la première vidéo affichée, si demandé en option.", function()
it("Doit demander le lancement de la première vidéo affichée, si demandé en option.", () =>
{
vCarouselTest.vContainers=["video1","video2","video3"];
vCarouselTest.firstVideoId="video2";
@ -114,7 +114,7 @@ describe("vCarousel", function()
expect(currentVideo.play).toHaveBeenCalled();
});
it("Ne doit pas demander le lancement de la première vidéo affichée, si cela n'est pas demandé en option.", function()
it("Ne doit pas demander le lancement de la première vidéo affichée, si cela n'est pas demandé en option.", () =>
{
vCarouselTest.vContainers=["video1","video2","video3"];
vCarouselTest.firstVideoId="video3";
@ -124,7 +124,7 @@ describe("vCarousel", function()
expect(currentVideo.play).not.toHaveBeenCalled();
});
it("Doit passer d'une vidéo à l'autre et arrêter lorsqu'elles ont toutes été affichées.", function()
it("Doit passer d'une vidéo à l'autre et arrêter lorsqu'elles ont toutes été affichées.", () =>
{
vCarouselTest.vContainers=["video1","video2","video3"];
vCarouselTest.run();
@ -150,7 +150,7 @@ describe("vCarousel", function()
expect(window.location.hash).toEqual("#video3");
});
it("Si cela est demandé, le carrousel doit reprendre au début après avoir parcouru toutes les vidéos.", function()
it("Si cela est demandé, le carrousel doit reprendre au début après avoir parcouru toutes les vidéos.", () =>
{
vCarouselTest.vContainers=["video1","video2","video3"];
vCarouselTest.noStop=true;
@ -171,7 +171,7 @@ describe("vCarousel", function()
expect(window.location.hash).toEqual("#video1");
});
it("Doit demander le lancement des nouvelles vidéos affichées, si cela est demandé en option.", function()
it("Doit demander le lancement des nouvelles vidéos affichées, si cela est demandé en option.", () =>
{
vCarouselTest.vContainers=["video1","video2","video3"];
vCarouselTest.playNextVideos=true;
@ -184,7 +184,7 @@ describe("vCarousel", function()
expect(nextVideo.play).toHaveBeenCalled();
});
it("Ne doit pas demander le lancement des nouvelles vidéos affichées, si cela n'est pas demandé en option.", function()
it("Ne doit pas demander le lancement des nouvelles vidéos affichées, si cela n'est pas demandé en option.", () =>
{
vCarouselTest.vContainers=["video1","video2","video3"];
vCarouselTest.firstVideoId="video2";

Loading…
Cancel
Save