UNPKG

rx-player

Version:
1,148 lines (1,080 loc) 32.4 kB
import type { MockInstance } from "vitest"; import { describe, beforeEach, afterEach, it, expect, vi } from "vitest"; import config from "../../../config"; import type { IAudioTrackSwitchingMode, IKeySystemOption, ILoadVideoOptions, IStartAtOption, } from "../../../public_types"; import type { parseConstructorOptions as IParseConstructorOptions, parseLoadVideoOptions as IParseLoadVideoOptions, checkReloadOptions as ICheckReloadOptions, } from "../option_utils"; let warnOnceMock: MockInstance; let logWarnMock: MockInstance; describe("API - parseConstructorOptions", () => { let parseConstructorOptions: typeof IParseConstructorOptions; beforeEach(async () => { warnOnceMock = vi.fn(); logWarnMock = vi.fn(); vi.doMock("../../../log", () => { return { default: { warn: logWarnMock, }, }; }); vi.doMock("../../../utils/languages"); vi.doMock("../../../utils/warn_once", () => { return { default: warnOnceMock }; }); parseConstructorOptions = (await vi.importActual("../option_utils")) .parseConstructorOptions as typeof IParseConstructorOptions; }); afterEach(() => { vi.resetModules(); }); const videoElement = document.createElement("video"); const { // DEFAULT_AUTO_PLAY, DEFAULT_BASE_BANDWIDTH, DEFAULT_VIDEO_RESOLUTION_LIMIT, // DEFAULT_MANUAL_BITRATE_SWITCHING_MODE, DEFAULT_MAX_BUFFER_AHEAD, DEFAULT_MAX_BUFFER_BEHIND, DEFAULT_MAX_VIDEO_BUFFER_SIZE, // DEFAULT_TEXT_TRACK_MODE, DEFAULT_THROTTLE_VIDEO_BITRATE_WHEN_HIDDEN, DEFAULT_WANTED_BUFFER_AHEAD, } = config.getCurrent(); const defaultConstructorOptions = { maxVideoBufferSize: DEFAULT_MAX_VIDEO_BUFFER_SIZE, maxBufferAhead: DEFAULT_MAX_BUFFER_AHEAD, maxBufferBehind: DEFAULT_MAX_BUFFER_BEHIND, wantedBufferAhead: DEFAULT_WANTED_BUFFER_AHEAD, videoResolutionLimit: DEFAULT_VIDEO_RESOLUTION_LIMIT, throttleVideoBitrateWhenHidden: DEFAULT_THROTTLE_VIDEO_BITRATE_WHEN_HIDDEN, videoElement, baseBandwidth: DEFAULT_BASE_BANDWIDTH, }; it("should create default values if no option is given", () => { expect(parseConstructorOptions({})).toEqual(defaultConstructorOptions); }); it("should authorize setting a maxBufferAhead", () => { expect(parseConstructorOptions({ maxBufferAhead: 0 })).toEqual({ ...defaultConstructorOptions, maxBufferAhead: 0, }); expect(parseConstructorOptions({ maxBufferAhead: 10 })).toEqual({ ...defaultConstructorOptions, maxBufferAhead: 10, }); expect(parseConstructorOptions({ maxBufferAhead: Infinity })).toEqual({ ...defaultConstructorOptions, maxBufferAhead: Infinity, }); }); it("should authorize setting a maxBufferBehind", () => { expect(parseConstructorOptions({ maxBufferBehind: 0 })).toEqual({ ...defaultConstructorOptions, maxBufferBehind: 0, }); expect(parseConstructorOptions({ maxBufferBehind: 10 })).toEqual({ ...defaultConstructorOptions, maxBufferBehind: 10, }); expect(parseConstructorOptions({ maxBufferBehind: Infinity })).toEqual({ ...defaultConstructorOptions, maxBufferBehind: Infinity, }); }); it("should authorize setting a wantedBufferAhead", () => { expect(parseConstructorOptions({ wantedBufferAhead: 0 })).toEqual({ ...defaultConstructorOptions, wantedBufferAhead: 0, }); expect(parseConstructorOptions({ wantedBufferAhead: 10 })).toEqual({ ...defaultConstructorOptions, wantedBufferAhead: 10, }); expect(parseConstructorOptions({ wantedBufferAhead: Infinity })).toEqual({ ...defaultConstructorOptions, wantedBufferAhead: Infinity, }); }); it("should authorize setting a videoResolutionLimit option", () => { expect(parseConstructorOptions({ videoResolutionLimit: "screen" })).toEqual({ ...defaultConstructorOptions, videoResolutionLimit: "screen", }); expect(parseConstructorOptions({ videoResolutionLimit: "videoElement" })).toEqual({ ...defaultConstructorOptions, videoResolutionLimit: "videoElement", }); expect(parseConstructorOptions({ videoResolutionLimit: "none" })).toEqual({ ...defaultConstructorOptions, videoResolutionLimit: "none", }); }); it("should authorize setting a throttleVideoBitrateWhenHidden option", () => { expect(parseConstructorOptions({ throttleVideoBitrateWhenHidden: false })).toEqual({ ...defaultConstructorOptions, throttleVideoBitrateWhenHidden: false, }); expect(parseConstructorOptions({ throttleVideoBitrateWhenHidden: true })).toEqual({ ...defaultConstructorOptions, throttleVideoBitrateWhenHidden: true, }); }); it("should authorize setting a videoElement option which can be any media element", () => { const _videoElement = document.createElement("video"); const parsed1 = parseConstructorOptions({ videoElement: _videoElement }); expect(parsed1).toEqual({ ...defaultConstructorOptions, videoElement: _videoElement, }); expect(parsed1.videoElement).toBe(_videoElement); const audioElement = document.createElement("audio"); const parsed2 = parseConstructorOptions({ videoElement: audioElement }); expect(parsed2).toEqual({ ...defaultConstructorOptions, videoElement: audioElement, }); expect(parsed2.videoElement).toBe(audioElement); }); it("should authorize setting an baseBandwidth", () => { expect(parseConstructorOptions({ baseBandwidth: -1 })).toEqual({ ...defaultConstructorOptions, baseBandwidth: -1, }); expect(parseConstructorOptions({ baseBandwidth: 0 })).toEqual({ ...defaultConstructorOptions, baseBandwidth: 0, }); expect(parseConstructorOptions({ baseBandwidth: 10 })).toEqual({ ...defaultConstructorOptions, baseBandwidth: 10, }); expect(parseConstructorOptions({ baseBandwidth: Infinity })).toEqual({ ...defaultConstructorOptions, baseBandwidth: Infinity, }); }); it("should throw if the maxBufferAhead given is not a number", () => { expect(() => parseConstructorOptions({ maxBufferAhead: "a" as unknown as number }), ).toThrow(); expect(() => parseConstructorOptions({ maxBufferAhead: /a/ as unknown as number }), ).toThrow(); expect(() => parseConstructorOptions({ maxBufferAhead: {} as unknown as number }), ).toThrow(); }); it("should throw if the maxBufferBehind given is not a number", () => { expect(() => parseConstructorOptions({ maxBufferBehind: "a" as unknown as number }), ).toThrow(); expect(() => parseConstructorOptions({ maxBufferBehind: /a/ as unknown as number }), ).toThrow(); expect(() => parseConstructorOptions({ maxBufferBehind: {} as unknown as number }), ).toThrow(); }); it("should throw if the wantedBufferAhead given is not a number", () => { expect(() => parseConstructorOptions({ wantedBufferAhead: "a" as unknown as number }), ).toThrow(); expect(() => parseConstructorOptions({ wantedBufferAhead: /a/ as unknown as number }), ).toThrow(); expect(() => parseConstructorOptions({ wantedBufferAhead: {} as unknown as number }), ).toThrow(); }); it("should throw if the videoElement given is not an HTMLMediaElement", () => { expect(() => // eslint-disable-next-line @typescript-eslint/no-restricted-types parseConstructorOptions({ videoElement: "a" as unknown as HTMLMediaElement }), ).toThrow(); expect(() => // eslint-disable-next-line @typescript-eslint/no-restricted-types parseConstructorOptions({ videoElement: /a/ as unknown as HTMLMediaElement }), ).toThrow(); expect(() => // eslint-disable-next-line @typescript-eslint/no-restricted-types parseConstructorOptions({ videoElement: {} as unknown as HTMLMediaElement }), ).toThrow(); expect(() => // eslint-disable-next-line @typescript-eslint/no-restricted-types parseConstructorOptions({ videoElement: [] as unknown as HTMLMediaElement }), ).toThrow(); expect(() => // eslint-disable-next-line @typescript-eslint/no-restricted-types parseConstructorOptions({ videoElement: 0 as unknown as HTMLMediaElement }), ).toThrow(); expect(() => parseConstructorOptions({ // eslint-disable-next-line @typescript-eslint/no-restricted-types videoElement: document.createElement("div") as unknown as HTMLMediaElement, }), ).toThrow(); }); it("should throw if the baseBandwidth given is not a number", () => { expect(() => parseConstructorOptions({ baseBandwidth: "a" as unknown as number }), ).toThrow(); expect(() => parseConstructorOptions({ baseBandwidth: /a/ as unknown as number }), ).toThrow(); expect(() => parseConstructorOptions({ baseBandwidth: {} as unknown as number }), ).toThrow(); }); }); describe("API - parseLoadVideoOptions", () => { let parseLoadVideoOptions: typeof IParseLoadVideoOptions; beforeEach(async () => { warnOnceMock = vi.fn(); logWarnMock = vi.fn(); vi.doMock("../../../log", () => { return { default: { warn: logWarnMock, }, }; }); vi.doMock("../../../utils/languages"); vi.doMock("../../../utils/warn_once", () => { return { default: warnOnceMock }; }); parseLoadVideoOptions = (await vi.importActual("../option_utils")) .parseLoadVideoOptions as typeof IParseLoadVideoOptions; }); afterEach(() => { vi.resetModules(); }); const defaultLoadVideoOptions = { __priv_manifestUpdateUrl: undefined, __priv_patchLastSegmentInSidx: undefined, checkMediaSegmentIntegrity: undefined, checkManifestIntegrity: undefined, defaultAudioTrackSwitchingMode: undefined, autoPlay: false, enableFastSwitching: true, initialManifest: undefined, keySystems: [], lowLatencyMode: false, manifestLoader: undefined, minimumManifestUpdateInterval: 0, mode: "auto", onCodecSwitch: "continue", onAudioTracksNotPlayable: "error", onVideoTracksNotPlayable: "error", requestConfig: {}, referenceDateTime: undefined, representationFilter: undefined, serverSyncInfos: undefined, startAt: undefined, textTrackElement: undefined, textTrackMode: "native", url: undefined, experimentalOptions: { enableRepresentationAvoidance: false, }, }; it("should throw if no option is given", () => { let err: unknown; let opt: unknown; try { opt = (parseLoadVideoOptions as unknown as () => void)(); } catch (e) { err = e; } expect(opt).not.toBeDefined(); expect(err).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err.message).toEqual("No option set on loadVideo"); }); it("should throw if no url nor custom Manifest loader is given", () => { let err1: unknown; let opt1: unknown; let err2: unknown; let opt2: unknown; try { opt1 = parseLoadVideoOptions({} as unknown as ILoadVideoOptions); } catch (e) { err1 = e; } try { opt2 = parseLoadVideoOptions({ transport: "dash" }); } catch (e) { err2 = e; } expect(opt1).not.toBeDefined(); expect(err1).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err1 instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err1.message).toEqual( "Unable to load a content: no url set on loadVideo.\n" + "Please provide at least either an `url` argument, a " + "`initialManifest` option or a " + "`manifestLoader` option so the RxPlayer can load the content.", ); expect(opt2).not.toBeDefined(); expect(err2).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err2 instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err2.message).toEqual( "Unable to load a content: no url set on loadVideo.\n" + "Please provide at least either an `url` argument, a " + "`initialManifest` option or a " + "`manifestLoader` option so the RxPlayer can load the content.", ); }); it("should throw if no transport is given", () => { let err: unknown; let opt: unknown; try { opt = parseLoadVideoOptions({ url: "foo" } as unknown as ILoadVideoOptions); } catch (e) { err = e; } expect(opt).not.toBeDefined(); expect(err).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err.message).toEqual("No transport set on loadVideo"); }); it("should set a default object if both an url and transport is given", () => { expect( parseLoadVideoOptions({ url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", }); }); it("should set a default object if both a Manifest loader and transport is given", () => { const manifestLoader = (): never => { throw new Error("Should not execute"); }; expect( parseLoadVideoOptions({ transport: "bar", manifestLoader, }), ).toEqual({ ...defaultLoadVideoOptions, transport: "bar", lowLatencyMode: false, manifestLoader, }); }); it("should set a default object if both an initialManifest and transport is given", () => { expect( parseLoadVideoOptions({ transport: "bar", initialManifest: "test", }), ).toEqual({ ...defaultLoadVideoOptions, transport: "bar", initialManifest: "test", }); }); it("should authorize setting an initialManifest option", () => { expect( parseLoadVideoOptions({ initialManifest: "baz", url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", initialManifest: "baz", }); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", initialManifest: "", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", initialManifest: "", }); }); it("should authorize setting a autoPlay option", () => { expect( parseLoadVideoOptions({ autoPlay: false, url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", autoPlay: false, }); expect( parseLoadVideoOptions({ autoPlay: true, url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", autoPlay: true, }); }); it("should authorize setting a keySystem option", () => { const keySystem1 = { type: "foo", getLicense: () => { return new Uint8Array([]); }, }; const keySystem2 = { type: "bar", getLicense: () => { return new Uint8Array([]); }, }; expect( parseLoadVideoOptions({ keySystems: keySystem1 as unknown as IKeySystemOption[], url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", keySystems: [keySystem1], }); expect( parseLoadVideoOptions({ keySystems: [keySystem1, keySystem2], url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", keySystems: [keySystem1, keySystem2], }); }); it("should throw when setting an invalid keySystems option", () => { { let err: unknown; let opt: unknown; try { opt = parseLoadVideoOptions({ url: "foo", transport: "bar", keySystems: {} as unknown as IKeySystemOption[], }); } catch (e) { err = e; } expect(opt).not.toBeDefined(); expect(err).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err.message).toEqual( "Invalid key system given: Missing type string or getLicense callback", ); } { let err: unknown; let opt: unknown; try { opt = parseLoadVideoOptions({ url: "foo", transport: "bar", keySystems: { type: "test" } as unknown as IKeySystemOption[], }); } catch (e) { err = e; } expect(opt).not.toBeDefined(); expect(err).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err.message).toEqual( "Invalid key system given: Missing type string or getLicense callback", ); } { let err: unknown; let opt: unknown; try { opt = parseLoadVideoOptions({ url: "foo", transport: "bar", keySystems: { getLicense: () => { return new Uint8Array([]); }, } as unknown as IKeySystemOption[], }); } catch (e) { err = e; } expect(opt).not.toBeDefined(); expect(err).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err.message).toEqual( "Invalid key system given: Missing type string or getLicense callback", ); } }); it("should authorize setting a lowLatencyMode option", () => { expect( parseLoadVideoOptions({ lowLatencyMode: false, url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", lowLatencyMode: false, }); expect( parseLoadVideoOptions({ lowLatencyMode: true, url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, lowLatencyMode: true, transport: "bar", url: "foo", }); }); it("should authorize setting a minimumManifestUpdateInterval option", () => { expect( parseLoadVideoOptions({ url: "foo", transport: "bar", minimumManifestUpdateInterval: 5400, }), ).toEqual({ ...defaultLoadVideoOptions, minimumManifestUpdateInterval: 5400, url: "foo", transport: "bar", lowLatencyMode: false, }); }); it("should authorize setting a valid defaultAudioTrackSwitchingMode option", () => { expect( parseLoadVideoOptions({ defaultAudioTrackSwitchingMode: "direct", url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", defaultAudioTrackSwitchingMode: "direct", }); expect( parseLoadVideoOptions({ defaultAudioTrackSwitchingMode: "reload", url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", defaultAudioTrackSwitchingMode: "reload", }); expect( parseLoadVideoOptions({ defaultAudioTrackSwitchingMode: "seamless", url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", defaultAudioTrackSwitchingMode: "seamless", }); }); it("should set an 'undefined' defaultAudioTrackSwitchingMode mode when the parameter is invalid or not specified", () => { logWarnMock.mockReturnValue(undefined); expect( parseLoadVideoOptions({ defaultAudioTrackSwitchingMode: "foo-bar" as unknown as IAudioTrackSwitchingMode, url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", defaultAudioTrackSwitchingMode: undefined, }); expect(logWarnMock).toHaveBeenCalledTimes(1); expect(logWarnMock).toHaveBeenCalledWith( "API", "The `defaultAudioTrackSwitchingMode` loadVideo option must match one of " + `the following strategy name: - \`seamless\` - \`direct\` - \`reload\``, ); logWarnMock.mockReset(); logWarnMock.mockReturnValue(undefined); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", defaultAudioTrackSwitchingMode: undefined, }); expect(logWarnMock).not.toHaveBeenCalled(); }); it("should authorize setting a valid onCodecSwitch option", () => { expect( parseLoadVideoOptions({ onCodecSwitch: "reload", url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", onCodecSwitch: "reload", }); expect( parseLoadVideoOptions({ onCodecSwitch: "continue", url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", onCodecSwitch: "continue", }); }); it("should set a 'continue' onCodecSwitch when the parameter is invalid or not specified", () => { logWarnMock.mockReturnValue(undefined); expect( parseLoadVideoOptions({ onCodecSwitch: "foo-bar" as unknown as "continue", url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", onCodecSwitch: "continue", }); expect(logWarnMock).toHaveBeenCalledTimes(1); expect(logWarnMock).toHaveBeenCalledWith( "API", "The `onCodecSwitch` loadVideo option must match one " + `of the following string: - \`continue\` - \`reload\` If badly set, continue will be used as default`, ); logWarnMock.mockReset(); logWarnMock.mockReturnValue(undefined); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", onCodecSwitch: "continue", }); expect(logWarnMock).not.toHaveBeenCalled(); }); it("should authorize setting a valid enableFastSwitching option", () => { expect( parseLoadVideoOptions({ enableFastSwitching: false, url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", enableFastSwitching: false, }); expect( parseLoadVideoOptions({ enableFastSwitching: true, url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", enableFastSwitching: true, }); }); it("should authorize setting a requestConfig", () => { expect( parseLoadVideoOptions({ url: "foo", transport: "bar", requestConfig: {}, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", requestConfig: {}, }); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", requestConfig: { manifest: { maxRetry: 4 } }, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", requestConfig: { manifest: { maxRetry: 4 } }, }); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", requestConfig: { segment: { maxRetry: 3 } }, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", requestConfig: { segment: { maxRetry: 3 } }, }); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", requestConfig: { segment: { maxRetry: 3 }, manifest: { maxRetry: 5 }, }, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", requestConfig: { segment: { maxRetry: 3 }, manifest: { maxRetry: 5 }, }, }); }); it("should authorize setting a valid startAt option", () => { expect( parseLoadVideoOptions({ url: "foo", transport: "bar", startAt: { a: 12 } as unknown as IStartAtOption, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", startAt: { a: 12 }, }); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", startAt: { wallClockTime: 19 }, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", startAt: { wallClockTime: 19 }, }); const a = new Date(); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", startAt: { wallClockTime: a }, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", startAt: { wallClockTime: a.getTime() / 1000 }, }); expect( parseLoadVideoOptions({ url: "foo", transport: "bar", startAt: { position: 4, wallClockTime: a }, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", startAt: { position: 4, wallClockTime: a.getTime() / 1000 }, }); }); it("should authorize setting a `segmentLoader` option", () => { const func = vi.fn(); expect( parseLoadVideoOptions({ segmentLoader: func, url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", lowLatencyMode: false, segmentLoader: func, }); }); it("should authorize setting a valid textTrackMode option", () => { expect( parseLoadVideoOptions({ textTrackMode: "native", url: "foo", transport: "bar", }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", textTrackMode: "native", }); const textTrackElement = document.createElement("div"); expect( parseLoadVideoOptions({ textTrackMode: "html", url: "foo", transport: "bar", textTrackElement, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", textTrackMode: "html", textTrackElement, }); }); it("should throw when setting an invalid textTrackMode option", () => { let err: unknown; let opt: unknown; try { opt = parseLoadVideoOptions({ textTrackMode: "toto" as unknown as "html", url: "foo", transport: "bar", }); } catch (e) { err = e; } expect(opt).not.toBeDefined(); expect(err).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err.message).toEqual("Invalid textTrackMode."); }); it("should throw when setting an html textTrackMode option with no textTrackElement", () => { let err: unknown; let opt: unknown; try { opt = parseLoadVideoOptions({ textTrackMode: "html", url: "foo", transport: "bar", }); } catch (e) { err = e; } expect(opt).not.toBeDefined(); expect(err).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err.message).toEqual( 'You have to provide a textTrackElement in "html" textTrackMode.', ); }); it("should throw when setting an html textTrackMode option with no textTrackElement", () => { let err: unknown; let opt: unknown; const textTrackElement = {}; try { opt = parseLoadVideoOptions({ textTrackMode: "html", url: "foo", transport: "bar", textTrackElement: textTrackElement as unknown as HTMLElement, }); } catch (e) { err = e; } expect(opt).not.toBeDefined(); expect(err).toBeInstanceOf(Error); // Impossible check to shut-up TypeScript if (!(err instanceof Error)) { throw new Error("Impossible: already checked it was an Error instance"); } expect(err.message).toEqual("textTrackElement should be an HTMLElement."); }); it("should warn when setting a textTrackElement with a `native` textTrackMode", () => { logWarnMock.mockReturnValue(undefined); const textTrackElement = document.createElement("div"); parseLoadVideoOptions({ textTrackMode: "native", url: "foo", transport: "bar", }); expect(logWarnMock).not.toHaveBeenCalled(); expect( parseLoadVideoOptions({ textTrackMode: "native", url: "foo", transport: "bar", textTrackElement, }), ).toEqual({ ...defaultLoadVideoOptions, url: "foo", transport: "bar", textTrackMode: "native", }); expect(logWarnMock).toHaveBeenCalledTimes(1); expect(logWarnMock).toHaveBeenCalledWith( "API", "You have set a textTrackElement " + 'without being in an "html" textTrackMode. It will be ignored.', ); }); }); describe("API - checkReloadOptions", () => { let checkReloadOptions: typeof ICheckReloadOptions; beforeEach(async () => { checkReloadOptions = (await vi.importActual("../option_utils")) .checkReloadOptions as typeof ICheckReloadOptions; }); it("Should valid undefined options", () => { const options = undefined; expect(() => checkReloadOptions(options)).not.toThrow(); }); it("Should valid empty options", () => { const options = {}; expect(() => checkReloadOptions(options)).not.toThrow(); }); it("Should valid options with defined reloadAt.position", () => { const options = { reloadAt: { position: 4, }, }; expect(() => checkReloadOptions(options)).not.toThrow(); }); it("Should valid options with defined reloadAt.relative", () => { const options = { reloadAt: { relative: 3, }, }; expect(() => checkReloadOptions(options)).not.toThrow(); }); it("Should throw when invalid options", () => { const options = null; expect(() => checkReloadOptions(options as unknown as object)).toThrow( "API: reload - Invalid options format.", ); }); it("Should throw when invalid reloatAt", () => { const options = { reloadAt: 3, }; expect(() => checkReloadOptions(options as unknown as object)).toThrow( "API: reload - Invalid 'reloadAt' option format.", ); }); it("Should throw when invalid position", () => { const options = { reloadAt: { position: "3", }, }; expect(() => checkReloadOptions(options as unknown as object)).toThrow( "API: reload - Invalid 'reloadAt.position' option format.", ); }); it("Should throw when invalid relative position", () => { const options = { reloadAt: { relative: "3", }, }; expect(() => checkReloadOptions(options as unknown as object)).toThrow( "API: reload - Invalid 'reloadAt.relative' option format.", ); }); });