0
0
Fork 0
mirror of https://github.com/discourse/discourse.git synced 2026-08-06 13:08:40 +08:00
discourse/plugins/discourse-calendar/test/javascripts/integration/components/livestream-test.gjs
Kris 350fd9f865
UX: prevent cloak on livestream post (#42330)
When in a livestream topic, the video in the first post should continue
playing regardless of how far you've scrolled within the topic. This
change uses `api.preventCloak(postId)` to prevent the first post from
cloaking, which will allow the video to play regardless of how far away
I've scrolled from it.
2026-08-05 09:01:09 -04:00

205 lines
6.6 KiB
Text
Vendored

import { on } from "@ember/modifier";
import { getOwner } from "@ember/owner";
import { click, render } from "@ember/test-helpers";
import { module, test } from "qunit";
import sinon from "sinon";
import { withPluginApi } from "discourse/lib/plugin-api";
import { setupRenderingTest } from "discourse/tests/helpers/component-test";
import Livestream from "../../discourse/components/discourse-post-event/livestream";
import ZoomMeetingSession from "../../discourse/lib/zoom-meeting-session";
const ZOOM_URL = "https://us06web.zoom.us/j/123456789?pwd=secret";
const ZOOM_ENTRY_SELECTOR = ".discourse-calendar-livestream-zoom-entry";
const FakeLazyVideo = <template>
<button type="button" class="fake-lazy-video" {{on "click" @onLoadedVideo}}>
play
</button>
</template>;
module(
"Integration | Component | DiscoursePostEvent::Livestream",
function (hooks) {
setupRenderingTest(hooks);
hooks.beforeEach(function () {
const owner = getOwner(this);
owner.unregister("service:capabilities");
owner.register(
"service:capabilities",
{ viewport: { lg: true } },
{ instantiate: false }
);
owner.lookup("service:site-settings").livestream_zoom_enabled = true;
this.event = {
livestream: true,
livestreamUrl: ZOOM_URL,
livestreamChatChannelId: 9,
isZoomLivestream: true,
post: { topic: { id: 1, slug: "test-topic", chat_channel_id: 9 } },
};
});
test("renders nothing when the event has no livestream", async function (assert) {
this.event.livestream = false;
await render(<template><Livestream @event={{this.event}} /></template>);
assert.dom(".event-livestream").doesNotExist();
});
test("renders nothing when the livestream has no URL", async function (assert) {
this.event.livestreamUrl = null;
await render(<template><Livestream @event={{this.event}} /></template>);
assert.dom(".event-livestream").doesNotExist();
});
test("renders the Zoom entry for a Zoom livestream", async function (assert) {
await render(<template><Livestream @event={{this.event}} /></template>);
assert.dom(ZOOM_ENTRY_SELECTOR).exists();
});
test("does not render the Zoom entry when Zoom is disabled", async function (assert) {
getOwner(this).lookup("service:site-settings").livestream_zoom_enabled =
false;
await render(<template><Livestream @event={{this.event}} /></template>);
assert.dom(ZOOM_ENTRY_SELECTOR).doesNotExist();
assert
.dom(".event-livestream")
.exists("still renders the livestream section");
});
test("renders the onebox for a non-Zoom livestream", async function (assert) {
this.event.isZoomLivestream = false;
this.event.livestreamUrl = "https://example.com/live";
this.event.livestreamOnebox = "<aside class='onebox'>cached</aside>";
await render(<template><Livestream @event={{this.event}} /></template>);
assert.dom(ZOOM_ENTRY_SELECTOR).doesNotExist();
assert
.dom(".event-livestream aside.onebox")
.exists("renders the cached onebox");
});
test("renders nothing inside the section when the onebox has not warmed yet", async function (assert) {
this.event.isZoomLivestream = false;
this.event.livestreamUrl = "https://example.com/live";
this.event.livestreamOnebox = null;
await render(<template><Livestream @event={{this.event}} /></template>);
assert.dom(".event-livestream").exists();
assert.dom(".event-livestream aside.onebox").doesNotExist();
});
module("with a playable video", function (nestedHooks) {
let preventCloak;
nestedHooks.beforeEach(function () {
this.event.isZoomLivestream = false;
this.event.livestreamUrl =
"https://www.youtube.com/watch?v=dQw4w9WgXcQ";
this.event.livestreamOnebox =
"<div class='lazy-video-container'></div>";
sinon.stub(Livestream.prototype, "lazyVideo").get(() => FakeLazyVideo);
sinon
.stub(Livestream.prototype, "videoAttributes")
.get(() => ({ providerName: "youtube", id: "dQw4w9WgXcQ" }));
withPluginApi((api) => {
preventCloak = sinon.stub(api, "preventCloak");
});
});
test("keeps the post rendered once the video is playing", async function (assert) {
this.post = { id: 42 };
await render(
<template>
<Livestream @event={{this.event}} @post={{this.post}} />
</template>
);
assert.dom(".event-livestream .fake-lazy-video").exists();
assert.false(
preventCloak.called,
"cloaking is still allowed before playback starts"
);
await click(".fake-lazy-video");
assert.true(
preventCloak.calledWith(42),
"prevents the post holding the player from being cloaked"
);
});
test("does not prevent cloaking when rendered without a post", async function (assert) {
await render(<template><Livestream @event={{this.event}} /></template>);
await click(".fake-lazy-video");
assert.false(
preventCloak.called,
"no post to keep rendered, e.g. in the calendar or onebox preview"
);
});
});
module("with a Zoom livestream", function (nestedHooks) {
let preventCloak;
nestedHooks.beforeEach(function () {
const owner = getOwner(this);
owner.unregister("service:discourse-post-event-api");
owner.register(
"service:discourse-post-event-api",
{ joinEvent: () => Promise.resolve() },
{ instantiate: false }
);
this.event.id = 7;
this.event.currentlyWithinEventTimeframe = true;
this.event.canUpdateAttendance = false;
sinon.stub(ZoomMeetingSession.prototype, "performJoin").resolves();
withPluginApi((api) => {
preventCloak = sinon.stub(api, "preventCloak");
});
});
test("keeps the post rendered once the webinar has joined", async function (assert) {
this.post = { id: 42 };
await render(
<template>
<Livestream @event={{this.event}} @post={{this.post}} />
</template>
);
assert.false(
preventCloak.called,
"cloaking is still allowed before the user joins"
);
await click(".discourse-calendar-livestream-zoom-entry .btn-primary");
assert.true(
preventCloak.calledWith(42),
"prevents the post holding the Zoom session from being cloaked"
);
});
});
}
);