fix(controller): restore dynamic and fullscreen overlays

This commit is contained in:
joshpatra committed 2026-07-14 21:07:19 -04:00
1 parent 477e5ba8b2
commit a2ab1ad261
3 files changed
+270 -8

No files matched your search

+102
View File
@@ -214,4 +214,106 @@ describe("inject.js helper logic", () => {
expect(window.getControllerMount(video)).toBe(isolatedPlayer);
});
it("keeps the controller inside a nested fullscreen player subtree", async () => {
bootInject();
await flushAsyncWork(3);
const isolatedPlayer = document.createElement("div");
isolatedPlayer.style.isolation = "isolate";
const fullscreenPlayer = document.createElement("media-player");
const provider = document.createElement("media-provider");
const video = document.createElement("video");
provider.appendChild(video);
fullscreenPlayer.appendChild(provider);
isolatedPlayer.appendChild(fullscreenPlayer);
document.body.appendChild(isolatedPlayer);
const rect = {
left: 0,
top: 0,
right: 640,
bottom: 360,
width: 640,
height: 360
};
[video, provider, fullscreenPlayer, isolatedPlayer].forEach((element) => {
element.getBoundingClientRect = () => rect;
});
expect(window.getControllerMount(video)).toBe(isolatedPlayer);
expect(window.getControllerMount(video, fullscreenPlayer)).toBe(
fullscreenPlayer
);
});
it("remounts the controller on fullscreen entry and restores it on exit", async () => {
bootInject();
await flushAsyncWork(3);
const isolatedPlayer = document.createElement("div");
isolatedPlayer.style.isolation = "isolate";
const fullscreenPlayer = document.createElement("media-player");
const provider = document.createElement("media-provider");
const video = document.createElement("video");
const wrapper = document.createElement("div");
wrapper.className = "vsc-controller";
provider.appendChild(video);
fullscreenPlayer.appendChild(provider);
isolatedPlayer.append(fullscreenPlayer, wrapper);
document.body.appendChild(isolatedPlayer);
const rect = {
left: 0,
top: 0,
right: 640,
bottom: 360,
width: 640,
height: 360
};
[video, provider, fullscreenPlayer, isolatedPlayer].forEach((element) => {
element.getBoundingClientRect = () => rect;
});
const controller = {
video,
div: wrapper,
normalControllerMount: isolatedPlayer
};
window.setupControllerHostTracking(controller, wrapper, isolatedPlayer);
Object.defineProperty(document, "fullscreenElement", {
configurable: true,
value: fullscreenPlayer
});
expect(window.syncControllerFullscreenMount(controller)).toBe(true);
expect(wrapper.parentElement).toBe(fullscreenPlayer);
Object.defineProperty(document, "fullscreenElement", {
configurable: true,
value: null
});
expect(window.syncControllerFullscreenMount(controller)).toBe(true);
expect(wrapper.parentElement).toBe(isolatedPlayer);
wrapper.remove();
controller.controllerHostCleanup();
});
it("force-rescans media missed during initial hydration", async () => {
bootInject();
await flushAsyncWork(3);
const video = document.createElement("video");
const source = document.createElement("source");
source.src = "https://example.org/late-source.mp4";
video.appendChild(source);
document.body.appendChild(video);
expect(video.vsc).toBeUndefined();
window.initializeWhenReady(document, true);
expect(video.vsc).toBeDefined();
expect(video.vsc.div).toBeDefined();
});
});
+28
View File
@@ -190,6 +190,34 @@ describe("inject runtime", () => {
vi.useRealTimers();
});
it("attaches a controller when Vidstack appends a source after the video", async () => {
await bootInject();
vi.useFakeTimers();
const provider = document.createElement("media-provider");
const video = document.createElement("video");
provider.appendChild(video);
document.body.appendChild(provider);
await flushAsyncWork();
vi.runAllTimers();
await flushAsyncWork();
expect(video.vsc).toBeUndefined();
const source = document.createElement("source");
source.src = "https://example.org/vidstack.mp4";
video.appendChild(source);
await flushAsyncWork();
vi.runAllTimers();
await flushAsyncWork();
expect(video.vsc).toBeDefined();
expect(video.vsc.div).toBeDefined();
expect(video.vsc.div.classList.contains("vsc-nosource")).toBe(false);
vi.useRealTimers();
});
it("detects media in pre-existing shadow DOMs via delayed rescan", async () => {
vi.useFakeTimers();
loadHtmlString("<!doctype html><html><body></body></html>");