Skip to content

Commit c7cfdc2

Browse files
committed
Add tests
1 parent 7e4f1bf commit c7cfdc2

File tree

2 files changed

+184
-32
lines changed

2 files changed

+184
-32
lines changed
Lines changed: 180 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,192 @@
1-
/**
2-
* @vitest-environment jsdom
3-
*/
4-
1+
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
52
import { test, expect } from "vitest";
63
import React from "react";
7-
import { render, waitFor, screen } from "@testing-library/react";
8-
import { useVideoJS } from "./index";
9-
10-
const consoleError = console.error;
11-
console.error = (...err): void => {
12-
if (err[2] === "(CODE:4 MEDIA_ERR_SRC_NOT_SUPPORTED)") {
13-
// ignore error related to video file not supported by jsdom
14-
} else {
15-
consoleError(...err);
16-
}
17-
};
4+
import { render, waitFor, renderHook, cleanup } from "@testing-library/react";
5+
import { useVideoJS } from "./index.jsx";
186

19-
const App = (): JSX.Element => {
20-
const videoJsOptions = {
21-
sources: [{ src: "example.com/oceans.mp4" }],
22-
};
23-
const { Video, ready, player } = useVideoJS(videoJsOptions);
7+
import { afterEach } from "vitest";
8+
import { VideoJsPlayerOptions } from "video.js";
9+
10+
afterEach(() => {
11+
cleanup();
12+
});
13+
14+
Object.defineProperty(window.HTMLMediaElement.prototype, "load", {
15+
configurable: true,
16+
get() {
17+
// eslint-disable-next-line @typescript-eslint/no-empty-function
18+
return (): void => {};
19+
},
20+
});
21+
22+
Object.defineProperty(window.HTMLMediaElement.prototype, "canPlayType", {
23+
configurable: true,
24+
get() {
25+
return (): string => "maybe";
26+
},
27+
});
28+
29+
const App = ({
30+
options,
31+
mounted = true,
32+
}: {
33+
options: VideoJsPlayerOptions;
34+
mounted: boolean;
35+
}): JSX.Element => {
36+
const { Video, ready } = useVideoJS(options);
2437
return (
2538
<div>
26-
<div>{ready ? "Ready: true" : "Ready: false"}</div>
27-
<div>
28-
{typeof player === "object" && player !== null
29-
? "player is object"
30-
: "player is NOT object but should be"}
31-
</div>
32-
<Video />
39+
{ready ? "Ready: true" : "Ready: false"}
40+
{mounted ? <Video /> : null}
3341
</div>
3442
);
3543
};
3644

45+
test("can reinitialize video player with new url", async () => {
46+
const videoJsOptions = {
47+
sources: [{ src: "http://example.com/oceans.mp4" }],
48+
};
49+
const { getByText, getByTitle, getByRole, rerender } = render(
50+
<App options={videoJsOptions} />
51+
);
52+
53+
await waitFor(() => getByText("Ready: true"));
54+
expect(getByTitle("Play Video"));
55+
expect(getByText("Ready: true"));
56+
57+
const videoElements = getByRole(
58+
"region",
59+
"Video Player"
60+
).getElementsByTagName("video");
61+
expect(videoElements.length).toBe(1);
62+
const videoElement = videoElements[0];
63+
64+
await waitFor(() => {
65+
expect(videoElement.getAttribute("src")).toEqual(
66+
"http://example.com/oceans.mp4"
67+
);
68+
});
69+
rerender(
70+
<App options={{ sources: [{ src: "http://example.com/waves.mp4" }] }} />
71+
);
72+
const videoElementsAfterRerender = getByRole(
73+
"region",
74+
"Video Player"
75+
).getElementsByTagName("video");
76+
await waitFor(() => {
77+
expect(videoElementsAfterRerender.length).toBe(1);
78+
});
79+
const videoElementAfterRerender = videoElementsAfterRerender[0];
80+
await waitFor(() => {
81+
expect(videoElementAfterRerender.getAttribute("src")).toEqual(
82+
"http://example.com/waves.mp4"
83+
);
84+
});
85+
});
86+
3787
test("loads and displays a video", async () => {
38-
render(<App />);
88+
const videoJsOptions = {
89+
sources: [{ src: "http://example.com/oceans.mp4" }],
90+
};
91+
const { getByText, getByTitle, getByRole, unmount } = render(
92+
<App options={videoJsOptions} />
93+
);
94+
95+
await waitFor(() => getByText("Ready: true"));
96+
expect(getByTitle("Play Video"));
97+
expect(getByText("Ready: true"));
98+
99+
const videoElement = getByRole("region", "Video Player").getElementsByTagName(
100+
"video"
101+
)[0];
102+
103+
await waitFor(() => {
104+
expect(videoElement.getAttribute("src")).toEqual(
105+
"http://example.com/oceans.mp4"
106+
);
107+
});
108+
109+
// unmounting should remove all video elements that video.js have created
110+
111+
unmount();
112+
expect(document.body.innerHTML).toEqual("<div></div>");
113+
});
114+
115+
test("unmounting video should remove all videojs DOM nodes", async () => {
116+
const videoJsOptions = {
117+
sources: [{ src: "http://example.com/oceans.mp4" }],
118+
};
119+
const { getByText, getByTitle, getByRole, rerender } = render(
120+
<App options={videoJsOptions} />
121+
);
122+
123+
await waitFor(() => getByText("Ready: true"));
124+
expect(getByTitle("Play Video"));
125+
expect(getByText("Ready: true"));
126+
127+
const videoElement = getByRole("region", "Video Player").getElementsByTagName(
128+
"video"
129+
)[0];
130+
131+
await waitFor(() => {
132+
expect(videoElement.getAttribute("src")).toEqual(
133+
"http://example.com/oceans.mp4"
134+
);
135+
});
136+
137+
// removing video.js player should remove all video elements that video.js have created
138+
139+
rerender(<App options={videoJsOptions} mounted={false} />);
140+
expect(document.body.innerHTML).toEqual("<div><div>Ready: false</div></div>");
141+
});
142+
143+
test("useVideoJs initialization without rendering <Video/>", async () => {
144+
const videoJsOptions = {
145+
sources: [{ src: "example.com/oceans.mp4" }],
146+
};
147+
const { result } = renderHook(() => useVideoJS(videoJsOptions));
148+
expect(result.current).toEqual({
149+
Video: expect.any(Function),
150+
player: null,
151+
ready: false,
152+
});
153+
154+
// Since we don't render the Video dom, we never expect the player to be initialized
155+
await expect(
156+
waitFor(() => expect(result.current.player).not.toEqual(null))
157+
).rejects.toThrow(Error);
158+
});
159+
160+
test("useVideoJs initialization with rendering <Video/>", async () => {
161+
const videoJsOptions = {
162+
sources: [{ src: "http://example.com/oceans.mp4", controls: false }],
163+
};
164+
const { result, rerender } = renderHook((props) => useVideoJS(props), {
165+
initialProps: videoJsOptions,
166+
});
167+
expect(result.current).toEqual({
168+
Video: expect.any(Function),
169+
player: null,
170+
ready: false,
171+
});
172+
const Video = result.current.Video;
173+
const { getByRole } = render(<Video />);
174+
await waitFor(() => expect(result.current.player).not.toEqual(null));
175+
expect(result.current).toEqual({
176+
Video: expect.any(Function),
177+
player: expect.any(Object),
178+
ready: true,
179+
});
180+
181+
const videoElement = getByRole("region", "Video Player").getElementsByTagName(
182+
"video"
183+
)[0];
184+
185+
expect(videoElement.getAttribute("src")).toEqual(
186+
"http://example.com/oceans.mp4"
187+
);
39188

40-
await waitFor(() => screen.getByText("Ready: true"));
41-
expect(screen.getByTitle("Play Video"));
42-
expect(screen.getByText("Ready: true"));
43-
expect(screen.getByText("player is object"));
189+
rerender({
190+
sources: [{ src: "http://example.com/waves.mp4", controls: false }],
191+
});
44192
});

packages/react-hook-videojs/vite.config.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,8 @@ export default defineConfig({
3333
},
3434
},
3535
},
36+
test: {
37+
globals: true,
38+
environment: "jsdom",
39+
},
3640
});

0 commit comments

Comments
 (0)