Skip to content

Commit

Permalink
[WIP] Switch unit tests to use vitest
Browse files Browse the repository at this point in the history
To extrapolate on #1444, this PR shows how it would look like to switch
from relying on Jest to rely on vitest for our unit tests.

The end goal being to simplify our codebase by relying on a single
testing framework.

---

I only done so for testing files in the `src/compat` directory in this
demo. It can be tested right now by calling `npm run test:unit:vitest`.

---

Sadly for now, we are still forced to rely on a JSDom-ed Node.js
environment for unit tests and a browser environment for integration
tests - meaning we have very different configs for both.

This is because we want to mock imported files in unit tests - something
that is not possible for now in browser environment through vitest
(though vitest-dev/vitest#5765 seems to have
been merged very recently so maybe we could rely on the browser for both
soon), yet we want to replicate as much as a real browser as possible in
our integration tests (because we're also testing that media playback on the
tested browsers goes as expected).
  • Loading branch information
peaBerberian committed Jun 4, 2024
1 parent 7f6a8fe commit dad19e1
Show file tree
Hide file tree
Showing 28 changed files with 407 additions and 646 deletions.
4 changes: 3 additions & 1 deletion .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,9 @@ module.exports = {
"import/no-extraneous-dependencies": [
"error",
{
devDependencies: false,
// TODO This one is to make it work with unit tests.
// Perhaps a better solution could be a different config for both?
devDependencies: true,
},
],
"import/no-internal-modules": "off",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@
"standalone": "node ./scripts/run_standalone_demo.mjs",
"start": "node ./scripts/start_demo_web_server.mjs",
"start:wasm": "node ./scripts/start_demo_web_server.mjs --include-wasm",
"test:unit:vitest": "vitest --config vitest.config.unit.mjs",
"test:vitest": "node ./tests/integration/run_vitest.mjs",
"vitest": "vitest",
"test:integration": "vitest tests/integration",
"test:integration:chrome": "node tests/integration/run.mjs --bchrome",
"test:integration:chrome:watch": "node tests/integration/run.mjs --bchrome --watch",
Expand Down
54 changes: 20 additions & 34 deletions src/compat/__tests__/add_text_track.test.ts
Original file line number Diff line number Diff line change
@@ -1,80 +1,66 @@
/**
* Copyright 2015 CANAL+ Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, beforeEach, it, expect, vi } from "vitest";

// Needed for calling require (which itself is needed to mock properly) because
// Needed for calling require (which itself is needed to doMock properly) because
// it is not type-checked:
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-var-requires */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-argument */
/* eslint-disable @typescript-eslint/no-explicit-any */

describe("compat - addTextTrack", () => {
beforeEach(() => {
jest.resetModules();
vi.resetModules();
});

it("should re-use text track on IE / EDGE", () => {
it("should re-use text track on IE / EDGE", async () => {
const fakeTextTrack = {
id: "textTrack1",
HIDDEN: "hidden",
SHOWING: "showing",
} as unknown as TextTrack;
const mockAddTextTrack = jest.fn(() => null);
const mockAddTextTrack = vi.fn(() => null);
const fakeMediaElement = {
textTracks: [fakeTextTrack],
addTextTrack: mockAddTextTrack,
};
} as unknown as HTMLMediaElement;

jest.mock("../browser_detection", () => ({
vi.doMock("../browser_detection", () => ({
__esModule: true as const,
isIEOrEdge: true,
}));

const addTextTrack = jest.requireActual("../add_text_track").default;
const { default: addTextTrack } = (await vi.importActual("../add_text_track")) as any;
const { track, trackElement } = addTextTrack(fakeMediaElement);
expect(trackElement).toBe(undefined);
expect(track).toBe(fakeTextTrack);
expect(track.mode).toBe("showing");
expect(mockAddTextTrack).not.toHaveBeenCalled();
});

it("should add text track if no track on media element on IE / EDGE", () => {
it("should add text track if no track on media element on IE / EDGE", async () => {
const fakeTextTrack = {
id: "textTrack1",
HIDDEN: "hidden",
SHOWING: "showing",
} as unknown as TextTrack;
const fakeTextTracks: TextTrack[] = [];
const mockAddTextTrack = jest.fn(() => {
const mockAddTextTrack = vi.fn(() => {
fakeTextTracks.push(fakeTextTrack);
return fakeTextTrack;
});

const fakeMediaElement = {
textTracks: fakeTextTracks,
addTextTrack: mockAddTextTrack,
};
} as unknown as HTMLMediaElement;

jest.mock("../browser_detection", () => ({
vi.doMock("../browser_detection", () => ({
__esModule: true as const,
isIEOrEdge: true,
}));

const addTextTrack = jest.requireActual("../add_text_track").default;
const { default: addTextTrack } = (await vi.importActual("../add_text_track")) as any;
const { track, trackElement } = addTextTrack(fakeMediaElement);
expect(trackElement).toBe(undefined);
expect(track).toBe(fakeTextTrack);
Expand All @@ -84,11 +70,12 @@ describe("compat - addTextTrack", () => {
expect(mockAddTextTrack).toHaveBeenCalledTimes(1);
});

it("should create showing trackElement and set track on mediaElement", () => {
jest.mock("../browser_detection", () => ({
it("should create showing trackElement and set track on mediaElement", async () => {
vi.doMock("../browser_detection", () => ({
__esModule: true as const,
isIEOrEdge: false,
}));
const { default: addTextTrack } = (await vi.importActual("../add_text_track")) as any;

const fakeTextTrack = {
id: "textTrack1",
Expand All @@ -103,7 +90,7 @@ describe("compat - addTextTrack", () => {
const fakeTextTracks: TextTrack[] = [];
const fakeChildNodes: ChildNode[] = [];

const mockAppendChild = jest.fn((_trackElement) => {
const mockAppendChild = vi.fn((_trackElement) => {
fakeChildNodes.push(_trackElement);
fakeTextTracks.push(_trackElement.track);
});
Expand All @@ -112,13 +99,12 @@ describe("compat - addTextTrack", () => {
textTracks: fakeTextTracks,
appendChild: mockAppendChild,
childNodes: fakeChildNodes,
};
} as unknown as HTMLMediaElement;

const spyOnCreateElement = jest
const spyOnCreateElement = vi
.spyOn(document, "createElement")
.mockImplementation(() => fakeTextTrackElement as unknown as HTMLElement);

const addTextTrack = jest.requireActual("../add_text_track").default;
const { track, trackElement } = addTextTrack(fakeMediaElement);
expect(track).toBe(fakeTextTrack);
expect(track.mode).toBe("showing");
Expand Down
45 changes: 15 additions & 30 deletions src/compat/__tests__/browser_compatibility_types.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,7 @@
/**
* Copyright 2015 CANAL+ Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, beforeEach, it, expect, vi } from "vitest";
import globalScope from "../../utils/global_scope";

// Needed for calling require (which itself is needed to mock properly) because
// Needed for calling require (which itself is needed to doMock properly) because
// it is not type-checked:
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
Expand All @@ -34,11 +19,11 @@ describe("compat - browser compatibility types", () => {
}
const gs = globalScope as IFakeWindow;
beforeEach(() => {
jest.resetModules();
vi.resetModules();
});

it("should use the native MediaSource if defined", () => {
jest.mock("../../utils/is_node", () => ({
it("should use the native MediaSource if defined", async () => {
vi.doMock("../../utils/is_node", () => ({
__esModule: true as const,
default: false,
}));
Expand All @@ -53,7 +38,7 @@ describe("compat - browser compatibility types", () => {
gs.WebKitMediaSource = { a: 3 };
gs.MSMediaSource = { a: 4 };

const { MediaSource_ } = jest.requireActual("../browser_compatibility_types");
const { MediaSource_ } = await vi.importActual("../browser_compatibility_types");
expect(MediaSource_).toEqual({ a: 1 });

gs.MediaSource = origMediaSource;
Expand All @@ -62,8 +47,8 @@ describe("compat - browser compatibility types", () => {
gs.MSMediaSource = origMSMediaSource;
});

it("should use MozMediaSource if defined and MediaSource is not", () => {
jest.mock("../../utils/is_node", () => ({
it("should use MozMediaSource if defined and MediaSource is not", async () => {
vi.doMock("../../utils/is_node", () => ({
__esModule: true as const,
default: false,
}));
Expand All @@ -78,7 +63,7 @@ describe("compat - browser compatibility types", () => {
gs.WebKitMediaSource = undefined;
gs.MSMediaSource = undefined;

const { MediaSource_ } = jest.requireActual("../browser_compatibility_types");
const { MediaSource_ } = await vi.importActual("../browser_compatibility_types");
expect(MediaSource_).toEqual({ a: 2 });

gs.MediaSource = origMediaSource;
Expand All @@ -87,8 +72,8 @@ describe("compat - browser compatibility types", () => {
gs.MSMediaSource = origMSMediaSource;
});

it("should use WebKitMediaSource if defined and MediaSource is not", () => {
jest.mock("../../utils/is_node", () => ({
it("should use WebKitMediaSource if defined and MediaSource is not", async () => {
vi.doMock("../../utils/is_node", () => ({
__esModule: true as const,
default: false,
}));
Expand All @@ -103,7 +88,7 @@ describe("compat - browser compatibility types", () => {
gs.WebKitMediaSource = { a: 3 };
gs.MSMediaSource = undefined;

const { MediaSource_ } = jest.requireActual("../browser_compatibility_types");
const { MediaSource_ } = await vi.importActual("../browser_compatibility_types");
expect(MediaSource_).toEqual({ a: 3 });

gs.MediaSource = origMediaSource;
Expand All @@ -112,8 +97,8 @@ describe("compat - browser compatibility types", () => {
gs.MSMediaSource = origMSMediaSource;
});

it("should use MSMediaSource if defined and MediaSource is not", () => {
jest.mock("../../utils/is_node", () => ({
it("should use MSMediaSource if defined and MediaSource is not", async () => {
vi.doMock("../../utils/is_node", () => ({
__esModule: true as const,
default: false,
}));
Expand All @@ -128,7 +113,7 @@ describe("compat - browser compatibility types", () => {
gs.WebKitMediaSource = undefined;
gs.MSMediaSource = { a: 4 };

const { MediaSource_ } = jest.requireActual("../browser_compatibility_types");
const { MediaSource_ } = await vi.importActual("../browser_compatibility_types");
expect(MediaSource_).toEqual({ a: 4 });

gs.MediaSource = origMediaSource;
Expand Down
42 changes: 14 additions & 28 deletions src/compat/__tests__/browser_version.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
/**
* Copyright 2015 CANAL+ Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, afterEach, it, expect, vi } from "vitest";

/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
Expand Down Expand Up @@ -42,43 +28,43 @@ describe("Compat - Browser version", () => {

afterEach(() => {
nav.userAgent = origUserAgent;
jest.resetModules();
vi.resetModules();
});

it("Should return correct Firefox version (60)", () => {
jest.mock("../browser_detection", () => {
it("Should return correct Firefox version (60)", async () => {
vi.doMock("../browser_detection", () => {
return { __esModule: true as const, isFirefox: true };
});
const { getFirefoxVersion } = jest.requireActual("../browser_version");
const { getFirefoxVersion } = await vi.importActual("../browser_version");
nav.userAgent = "Firefox/60.0";
const version = getFirefoxVersion();
expect(version).toBe(60);
});

it("Should return correct Firefox version (80)", () => {
jest.mock("../browser_detection", () => {
it("Should return correct Firefox version (80)", async () => {
vi.doMock("../browser_detection", () => {
return { __esModule: true as const, isFirefox: true };
});
const { getFirefoxVersion } = jest.requireActual("../browser_version");
const { getFirefoxVersion } = await vi.importActual("../browser_version");
nav.userAgent = "Firefox/80.0";
const version = getFirefoxVersion();
expect(version).toBe(80);
});

it("Should return null when not on Firefox", () => {
jest.mock("../browser_detection", () => {
it("Should return null when not on Firefox", async () => {
vi.doMock("../browser_detection", () => {
return { __esModule: true as const, isFirefox: false };
});
const { getFirefoxVersion } = jest.requireActual("../browser_version");
const { getFirefoxVersion } = await vi.importActual("../browser_version");
const version = getFirefoxVersion();
expect(version).toBe(null);
});

it("Should return null when obscure Firefox user agent", () => {
jest.mock("../browser_detection", () => {
it("Should return null when obscure Firefox user agent", async () => {
vi.doMock("../browser_detection", () => {
return { __esModule: true as const, isFirefox: true };
});
const { getFirefoxVersion } = jest.requireActual("../browser_version");
const { getFirefoxVersion } = await vi.importActual("../browser_version");
nav.userAgent = "FireFennec/80.0";
const version = getFirefoxVersion();
expect(version).toBe(-1);
Expand Down
30 changes: 8 additions & 22 deletions src/compat/__tests__/can_patch_isobmff.test.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,4 @@
/**
* Copyright 2015 CANAL+ Group
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { describe, beforeEach, it, expect, vi } from "vitest";

/* eslint-disable @typescript-eslint/no-unsafe-assignment */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
Expand All @@ -23,28 +9,28 @@

describe("compat - canPatchISOBMFFSegment", () => {
beforeEach(() => {
jest.resetModules();
vi.resetModules();
});

it("should return true if we are not on IE11 nor Edge", () => {
jest.mock("../browser_detection", () => {
it("should return true if we are not on IE11 nor Edge", async () => {
vi.doMock("../browser_detection", () => {
return {
__esModule: true as const,
isIEOrEdge: false,
};
});
const canPatchISOBMFFSegment = jest.requireActual("../can_patch_isobmff");
const canPatchISOBMFFSegment = await vi.importActual("../can_patch_isobmff");
expect(canPatchISOBMFFSegment.default()).toBe(true);
});

it("should return false if we are on IE11 or Edge", () => {
jest.mock("../browser_detection", () => {
it("should return false if we are on IE11 or Edge", async () => {
vi.doMock("../browser_detection", () => {
return {
__esModule: true as const,
isIEOrEdge: true,
};
});
const canPatchISOBMFFSegment = jest.requireActual("../can_patch_isobmff");
const canPatchISOBMFFSegment = await vi.importActual("../can_patch_isobmff");
expect(canPatchISOBMFFSegment.default()).toBe(false);
});
});
Loading

0 comments on commit dad19e1

Please sign in to comment.