diff --git a/client/src/components/controls/VideoControls.vue b/client/src/components/controls/VideoControls.vue
index 3a0ca9b5c..f49ef9fc8 100644
--- a/client/src/components/controls/VideoControls.vue
+++ b/client/src/components/controls/VideoControls.vue
@@ -12,7 +12,7 @@
-
+
diff --git a/client/src/components/players/PlyrPlayer.vue b/client/src/components/players/PlyrPlayer.vue
index 1faac4ce0..6568b2202 100644
--- a/client/src/components/players/PlyrPlayer.vue
+++ b/client/src/components/players/PlyrPlayer.vue
@@ -231,7 +231,7 @@ export default defineComponent({
console.info("PlyrPlayer: hls.js manifest parsed");
emit("ready");
store.commit("captions/SET_AVAILABLE_TRACKS", {
- tracks: getCaptionsTracks(),
+ tracks: playerImpl.getCaptionsTracks(),
});
});
hls.on(Hls.Events.ERROR, (event, data) => {
diff --git a/tests/e2e/integration/playback.spec.ts b/tests/e2e/integration/playback.spec.ts
index 641dcb507..ffa6c72e0 100644
--- a/tests/e2e/integration/playback.spec.ts
+++ b/tests/e2e/integration/playback.spec.ts
@@ -1,3 +1,4 @@
+import { fail } from "assert";
import "cypress-iframe";
// TODO: skip this test if youtube api key is not available AND/OR create another test that uses a different video source
@@ -63,4 +64,154 @@ describe("Video playback", () => {
expect(element[0].paused).to.be.false;
});
});
+
+ it("should add a direct video and control it in various ways", { scrollBehavior: false }, () => {
+ cy.contains("button", "Add a video").scrollIntoView().click();
+ cy.get('[data-cy="add-preview-input"]').type("https://vjs.zencdn.net/v/oceans.mp4");
+ cy.get(".video button").eq(1).click();
+ cy.get("video").should("exist").scrollIntoView();
+ cy.get("video").should(element => {
+ expect(element[0].paused).to.be.true;
+ });
+ cy.wait(500);
+ // seek to some time via the seek bar
+ cy.get('#videoSlider').ottSliderMove(0.1);
+ cy.get("video").should(element => {
+ expect(element[0].currentTime).to.be.greaterThan(0);
+ })
+
+ // seek to 10 seconds via click to edit timestamp
+ cy.get('[data-cy="timestamp-display"] .editable').click();
+ cy.get('[data-cy="timestamp-display"] .editor').type("{backspace}{backspace}10{enter}")
+ cy.get("video").should(element => {
+ expect(element[0].currentTime).to.be.equal(10);
+ })
+
+ // change the volume to 40%
+ cy.get('[data-cy="volume-slider"]').ottSliderMove(0.4);
+ cy.get("video").should(element => {
+ expect(element[0].volume).to.be.equal(0.4);
+ });
+ });
+
+ it("should add a hls video and control it's playback rate and captions in various ways", { scrollBehavior: false }, () => {
+ cy.contains("button", "Add a video").scrollIntoView().click();
+ cy.get('[data-cy="add-preview-input"]').type("https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8");
+ cy.get(".video button").eq(1).click();
+ cy.get("video").should("exist").scrollIntoView();
+ cy.get("video").should(element => {
+ expect(element[0].paused).to.be.true;
+ });
+ cy.ottCloseToasts();
+ cy.wait(500);
+
+ // should have both captions and playback rate controls enabled
+ cy.get('[aria-label="Playback Speed"]').should("exist").should("be.enabled");
+ cy.get('[aria-label="Closed Captions"]').should("exist").should("be.enabled");
+
+ // change the playback rate to 1.5x
+ cy.get('[aria-label="Playback Speed"]').click();
+ cy.get('.v-list').contains("1.5x").click();
+ cy.get("video").should(element => {
+ expect(element[0].playbackRate).to.be.equal(1.5);
+ });
+
+ // FIXME: change the volume to 10%
+ cy.get('[data-cy="volume-slider"]').ottSliderMove(0);
+ cy.get("video").should(element => {
+ expect(element[0].volume).to.be.equal(0);
+ });
+
+ // play the video
+ cy.get(".video-controls button").eq(1).click();
+ cy.get("video").should("exist").should(element => {
+ expect(element[0].paused).to.be.false;
+ });
+
+ // change the playback rate to 2x
+ cy.get('[aria-label="Playback Speed"]').click();
+ cy.get('.v-list').contains("2x").click();
+ cy.get("video").should(element => {
+ expect(element[0].playbackRate).to.be.equal(2);
+ });
+
+ // change the playback rate to 1x
+ cy.get('[aria-label="Playback Speed"]').click();
+ cy.get('.v-list').contains("1x").click();
+ cy.get("video").should(element => {
+ expect(element[0].playbackRate).to.be.equal(1);
+ });
+
+ // enable captions
+ cy.get('[aria-label="Closed Captions"]').click();
+ cy.get('.v-overlay__content > .v-list').contains("en").eq(0).click();
+ cy.get("video").should(element => {
+ expect(element[0].textTracks[0].mode).to.be.equal("showing");
+ expect(element[0].textTracks[0].language).to.be.equal("en");
+ });
+
+ // disable captions
+ cy.get('[aria-label="Closed Captions"]').click();
+ cy.get('.v-list').contains("Off").click();
+ cy.get("video").should(element => {
+ expect(element[0].textTracks[0].mode).to.be.equal("disabled");
+ });
+
+ // show a different caption track
+ cy.get('[aria-label="Closed Captions"]').click();
+ cy.get('.v-overlay__content > .v-list').contains("es").eq(0).click();
+ // cy.get("video")
+ // .then(element => {
+ // for (let i = 0; i < element[0].textTracks.length; i++) {
+ // if (element[0].textTracks[i].kind !== "captions") {
+ // continue;
+ // }
+ // cy.log(JSON.stringify({
+ // id: element[0].textTracks[i].id,
+ // label: element[0].textTracks[i].label,
+ // language: element[0].textTracks[i].language,
+ // mode: element[0].textTracks[i].mode,
+ // kind: element[0].textTracks[i].kind,
+ // }));
+ // }
+ // });
+ cy.get("video")
+ .should(element => {
+ for (let i = 0; i < element[0].textTracks.length; i++) {
+ if (element[0].textTracks[i].language === "es" && element[0].textTracks[i].kind === "captions") {
+ expect(element[0].textTracks[i].mode).to.be.equal("showing");
+ return;
+ }
+ }
+ fail("No caption track found");
+ });
+ });
+
+ ["https://d2zihajmogu5jn.cloudfront.net/bipbop-advanced/bipbop_16x9_variant.m3u8", "https://vjs.zencdn.net/v/oceans.mp4"].forEach((url, i) => {
+ it(`should add a couple videos and properly update the UI for things that are implemented for the current video player [${i}]`, () => {
+ cy.contains("button", "Add a video").scrollIntoView().click();
+ cy.get('[data-cy="add-preview-input"]').type("https://vimeo.com/94338566");
+ cy.get(".video button").eq(1).click();
+ cy.get('[data-cy="add-preview-input"]').type(url);
+ cy.get(".video button").eq(1).click();
+ cy.get("iframe").should("exist").scrollIntoView();
+ cy.wait(100);
+ cy.ottCloseToasts();
+
+ // should have both captions and playback rate controls disabled
+ cy.get('[aria-label="Playback Speed"]').should("exist").should("be.disabled");
+ cy.get('[aria-label="Closed Captions"]').should("exist").should("be.disabled");
+
+ // skip the video
+ cy.get(".video-controls button").eq(3).click();
+ cy.get("video").should("exist").scrollIntoView();
+ cy.get("video").should(element => {
+ expect(element[0].paused).to.be.true;
+ });
+
+ cy.get('[aria-label="Playback Speed"]').should("exist").should("be.enabled");
+ cy.get('[aria-label="Closed Captions"]').should("exist").should("be.enabled");
+ });
+ })
+
});
diff --git a/tests/e2e/support/commands.ts b/tests/e2e/support/commands.ts
index d177a0d18..8e318219a 100644
--- a/tests/e2e/support/commands.ts
+++ b/tests/e2e/support/commands.ts
@@ -69,4 +69,17 @@ Cypress.Commands.add("ottLogin", userCreds => {
});
});
+Cypress.Commands.add("ottSliderMove", { prevSubject: 'element' }, (subject, percent) => {
+ const slider = subject[0];
+ const rect = slider.getBoundingClientRect();
+ const x = rect.width * percent;
+ const y = rect.height / 2;
+ cy.log(`Moving slider to ${percent * 100}% at x: ${x} y: ${y}`);
+ return cy.wrap(slider).click(x, y);
+})
+
+Cypress.Commands.add("ottCloseToasts", () => {
+ cy.get('[data-cy="toast-close-all"]').click();
+})
+
export {};
diff --git a/tests/e2e/support/index.ts b/tests/e2e/support/index.ts
index fee75f7f5..854cded28 100644
--- a/tests/e2e/support/index.ts
+++ b/tests/e2e/support/index.ts
@@ -28,6 +28,12 @@ declare global {
ottResetRateLimit(): Chainable;
ottCreateUser(userCreds: any): Chainable;
ottLogin(userCreds: any): Chainable;
+ /**
+ * Helper function for moving a vue-slider component to a desired percentage.
+ * @param percent number between 0 and 1
+ */
+ ottSliderMove(percent: number): Chainable>;
+ ottCloseToasts(): Chainable;
}
}
}