Move more element-web tests over to vitest (#33845)

* Move more element-web tests over to vitest

Switches default env to happy-dom as SdkConfig needs a DOM and is part of the setupTests given a lot of code relies on it

* Restore jest variant of editor mock file for tests not yet moved over

* Migrate batch of tests tro vitest

* Iterate

* Migrate another test

* Simplify

* Iterate

* Iterate

* Fix lockfile

* Deduplicate

* Iterate
This commit is contained in:
Michael Telatynski
2026-07-06 17:42:24 +00:00
committed by GitHub
parent 3da74a98e3
commit 08419cf0cf
20 changed files with 223 additions and 124 deletions
+11 -10
View File
@@ -234,21 +234,27 @@ module.exports = {
{
files: [
"src/**/*.test.{ts,tsx}",
"src/test/**/*.{ts,tsx}",
"src/**/__mocks__/*.{ts,tsx}",
"src/test/*.ts",
"test/**/*.{ts,tsx}",
"playwright/**/*.ts",
],
extends: ["plugin:matrix-org/jest"],
extends: [],
rules: {
// We don't need super strict typing in test utilities
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/explicit-member-accessibility": "off",
"@typescript-eslint/no-empty-object-type": "off",
"@typescript-eslint/unbound-method": "off",
// Jest/Playwright specific
// These are fine in tests
"no-restricted-globals": "off",
"react-compiler/react-compiler": "off",
},
},
{
files: ["test/**/*.{ts,tsx}"],
extends: ["plugin:matrix-org/jest"],
rules: {
// Disabled tests are a reality for now but as soon as all of the xits are
// eliminated, we should enforce this.
"jest/no-disabled-tests": "off",
@@ -260,11 +266,6 @@ module.exports = {
additionalTestBlockFunctions: ["beforeAll", "beforeEach", "oldBackendOnly"],
},
],
// These are fine in tests
"no-restricted-globals": "off",
"react-compiler/react-compiler": "off",
"jest/no-mocks-import": "off",
},
},
{
@@ -6,13 +6,15 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { viewUserDeviceSettings } from "../../../../src/actions/handlers/viewUserDeviceSettings";
import { UserTab } from "../../../../src/components/views/dialogs/UserTab";
import { Action } from "../../../../src/dispatcher/actions";
import defaultDispatcher from "../../../../src/dispatcher/dispatcher";
import { vi, describe, it, expect, beforeEach } from "vitest";
import { viewUserDeviceSettings } from "./viewUserDeviceSettings";
import { UserTab } from "../../components/views/dialogs/UserTab";
import { Action } from "../../dispatcher/actions";
import defaultDispatcher from "../../dispatcher/dispatcher";
describe("viewUserDeviceSettings()", () => {
const dispatchSpy = jest.spyOn(defaultDispatcher, "dispatch");
const dispatchSpy = vi.spyOn(defaultDispatcher, "dispatch");
beforeEach(() => {
dispatchSpy.mockClear();
@@ -6,7 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import withValidation from "../../../../src/components/views/elements/Validation";
import { describe, it, expect } from "vitest";
import withValidation from "./Validation";
describe("Validation", () => {
it("should handle 0 rules", () => {
+8
View File
@@ -0,0 +1,8 @@
/*
Copyright 2026 Element Creations Ltd.
SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
Please see LICENSE files in the repository root for full details.
*/
export * from "../../../test/unit-tests/editor/mock";
@@ -6,16 +6,21 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { getLineAndNodePosition } from "../../../src/editor/caret";
import EditorModel from "../../../src/editor/model";
import { createPartCreator } from "./mock";
import { describe, it, expect } from "vitest";
import { getLineAndNodePosition } from "./caret";
import EditorModel from "./model";
import { createPartCreator } from "./__mocks__";
describe("editor/caret: DOM position for caret", function () {
describe("basic text handling", function () {
it("at end of single line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 5 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 0,
offset: 5,
});
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(0);
expect(offset).toBe(5);
@@ -23,7 +28,10 @@ describe("editor/caret: DOM position for caret", function () {
it("at start of single line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 0 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 0,
offset: 0,
});
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(0);
expect(offset).toBe(0);
@@ -31,7 +39,10 @@ describe("editor/caret: DOM position for caret", function () {
it("at middle of single line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 2 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 0,
offset: 2,
});
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(0);
expect(offset).toBe(2);
@@ -41,7 +52,10 @@ describe("editor/caret: DOM position for caret", function () {
it("at start of first line which is empty", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.newline(), pc.plain("hello world")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 0 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 0,
offset: 0,
});
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(-1);
expect(offset).toBe(0);
@@ -49,7 +63,10 @@ describe("editor/caret: DOM position for caret", function () {
it("at end of last line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.plain("world")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 2, offset: 5 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 2,
offset: 5,
});
expect(lineIndex).toBe(1);
expect(nodeIndex).toBe(0);
expect(offset).toBe(5);
@@ -57,7 +74,10 @@ describe("editor/caret: DOM position for caret", function () {
it("at start of last line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.plain("world")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 2, offset: 0 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 2,
offset: 0,
});
expect(lineIndex).toBe(1);
expect(nodeIndex).toBe(0);
expect(offset).toBe(0);
@@ -65,7 +85,10 @@ describe("editor/caret: DOM position for caret", function () {
it("before empty line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 5 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 0,
offset: 5,
});
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(0);
expect(offset).toBe(5);
@@ -73,7 +96,10 @@ describe("editor/caret: DOM position for caret", function () {
it("in empty line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 1 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 1,
offset: 1,
});
expect(lineIndex).toBe(1);
expect(nodeIndex).toBe(-1);
expect(offset).toBe(0);
@@ -81,7 +107,10 @@ describe("editor/caret: DOM position for caret", function () {
it("after empty line", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.plain("hello"), pc.newline(), pc.newline(), pc.plain("world")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 3, offset: 0 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 3,
offset: 0,
});
expect(lineIndex).toBe(2);
expect(nodeIndex).toBe(0);
expect(offset).toBe(0);
@@ -94,7 +123,10 @@ describe("editor/caret: DOM position for caret", function () {
[pc.plain("hello"), pc.userPill("Alice", "@alice:hs.tld"), pc.plain("!")],
pc,
);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 0 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 1,
offset: 0,
});
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(0);
expect(offset).toBe(5);
@@ -105,7 +137,10 @@ describe("editor/caret: DOM position for caret", function () {
[pc.plain("hello"), pc.userPill("Alice", "@alice:hs.tld"), pc.plain("!")],
pc,
);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 2 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 1,
offset: 2,
});
expect(lineIndex).toBe(0);
expect(nodeIndex).toBe(2);
expect(offset).toBe(0);
@@ -113,7 +148,10 @@ describe("editor/caret: DOM position for caret", function () {
it("at start of non-editable part (without plain text around)", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.userPill("Alice", "@alice:hs.tld")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 0 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 0,
offset: 0,
});
expect(lineIndex).toBe(0);
//presumed nodes on line are (caret, pill, caret)
expect(nodeIndex).toBe(0);
@@ -122,7 +160,10 @@ describe("editor/caret: DOM position for caret", function () {
it("in middle of non-editable part (without plain text around)", function () {
const pc = createPartCreator();
const model = new EditorModel([pc.userPill("Alice", "@alice:hs.tld")], pc);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 1 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 0,
offset: 1,
});
expect(lineIndex).toBe(0);
//presumed nodes on line are (caret, pill, caret)
expect(nodeIndex).toBe(2);
@@ -134,7 +175,10 @@ describe("editor/caret: DOM position for caret", function () {
[pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Bob", "@bob:hs.tld")],
pc,
);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 0, offset: 1 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 0,
offset: 1,
});
expect(lineIndex).toBe(0);
//presumed nodes on line are (caret, pill, caret, pill, caret)
expect(nodeIndex).toBe(2);
@@ -146,7 +190,10 @@ describe("editor/caret: DOM position for caret", function () {
[pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Bob", "@bob:hs.tld")],
pc,
);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 0 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 1,
offset: 0,
});
expect(lineIndex).toBe(0);
//presumed nodes on line are (caret, pill, caret, pill, caret)
expect(nodeIndex).toBe(2);
@@ -158,7 +205,10 @@ describe("editor/caret: DOM position for caret", function () {
[pc.userPill("Alice", "@alice:hs.tld"), pc.userPill("Bob", "@bob:hs.tld")],
pc,
);
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, { index: 1, offset: 1 });
const { offset, lineIndex, nodeIndex } = getLineAndNodePosition(model, {
index: 1,
offset: 1,
});
expect(lineIndex).toBe(0);
//presumed nodes on line are (caret, pill, caret, pill, caret)
expect(nodeIndex).toBe(4);
@@ -6,7 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { diffDeletion, diffAtCaret } from "../../../src/editor/diff";
import { describe, it, expect } from "vitest";
import { diffDeletion, diffAtCaret } from "./diff";
describe("editor/diff", function () {
describe("diffDeletion", function () {
@@ -6,9 +6,11 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import HistoryManager, { type IHistory, MAX_STEP_LENGTH } from "../../../src/editor/history";
import type EditorModel from "../../../src/editor/model";
import DocumentPosition from "../../../src/editor/position";
import { describe, it, expect } from "vitest";
import HistoryManager, { type IHistory, MAX_STEP_LENGTH } from "./history";
import type EditorModel from "./model";
import DocumentPosition from "./position";
describe("editor/history", function () {
it("push, then undo", function () {
@@ -107,7 +109,9 @@ describe("editor/history", function () {
const firstCaret = new DocumentPosition(0, 0);
history.tryPush(model, firstCaret, "insertText", {});
parts[0] = "helloo";
const result = history.tryPush(model, new DocumentPosition(0, 0), "insertText", { added: "o" });
const result = history.tryPush(model, new DocumentPosition(0, 0), "insertText", {
added: "o",
});
expect(result).toEqual(false);
expect(history.canUndo()).toEqual(true);
const undoState = history.undo(model) as IHistory;
@@ -6,11 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import EditorModel from "../../../src/editor/model";
import { createPartCreator, createRenderer, type MockAutoComplete } from "./mock";
import DocumentOffset from "../../../src/editor/offset";
import { type PillPart } from "../../../src/editor/parts";
import type DocumentPosition from "../../../src/editor/position";
import { describe, it, expect } from "vitest";
import EditorModel from "./model";
import { createPartCreator, createRenderer, type MockAutoComplete } from "./__mocks__";
import DocumentOffset from "./offset";
import { type PillPart } from "./parts";
import type DocumentPosition from "./position";
describe("editor/model", function () {
describe("plain text manipulation", function () {
@@ -6,18 +6,20 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import EditorModel from "../../../src/editor/model";
import { createPartCreator, createRenderer } from "./mock";
import { describe, it, expect } from "vitest";
import EditorModel from "./model";
import { createPartCreator, createRenderer } from "./__mocks__";
import {
formatRange,
formatRangeAsCode,
formatRangeAsLink,
selectRangeOfWordAtCaret,
toggleInlineFormat,
} from "../../../src/editor/operations";
import { Formatting } from "../../../src/components/views/rooms/MessageComposerFormatBar";
import { longestBacktickSequence } from "../../../src/editor/deserialize";
import type DocumentPosition from "../../../src/editor/position";
} from "./operations";
import { Formatting } from "../components/views/rooms/MessageComposerFormatBar";
import { longestBacktickSequence } from "./deserialize";
import type DocumentPosition from "./position";
const SERIALIZED_NEWLINE = { text: "\n", type: "newline" };
@@ -6,8 +6,12 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { EmojiPart, PlainPart } from "../../../src/editor/parts";
import { createPartCreator } from "./mock";
// @vitest-environment happy-dom
import { describe, it, expect } from "vitest";
import { EmojiPart, PlainPart } from "./parts";
import { createPartCreator } from "./__mocks__";
describe("editor/parts", () => {
describe("appendUntilRejected", () => {
@@ -6,8 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import EditorModel from "../../../src/editor/model";
import { createPartCreator, createRenderer } from "./mock";
import { describe, it, expect } from "vitest";
import EditorModel from "./model";
import { createPartCreator, createRenderer } from "./__mocks__";
describe("editor/position", function () {
it("move first position backward in empty model", function () {
@@ -6,8 +6,10 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import EditorModel from "../../../src/editor/model";
import { createPartCreator, createRenderer } from "./mock";
import { describe, it, expect } from "vitest";
import EditorModel from "./model";
import { createPartCreator, createRenderer } from "./__mocks__";
const pillChannel = "#riot-dev:matrix.org";
@@ -6,13 +6,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
// @vitest-environment happy-dom
import { describe, it, expect } from "vitest";
import { type MatrixEvent } from "matrix-js-sdk/src/matrix";
import { parseEvent } from "../../../src/editor/deserialize";
import EditorModel from "../../../src/editor/model";
import DocumentOffset from "../../../src/editor/offset";
import { htmlSerializeIfNeeded, textSerialize } from "../../../src/editor/serialize";
import { createPartCreator } from "./mock";
import { parseEvent } from "./deserialize";
import EditorModel from "./model";
import DocumentOffset from "./offset";
import { htmlSerializeIfNeeded, textSerialize } from "./serialize";
import { createPartCreator } from "./__mocks__";
function htmlMessage(formattedBody: string, msgtype = "m.text") {
return {
@@ -50,7 +53,7 @@ async function roundTripHtml(html: string): Promise<string> {
describe("editor/roundtrip", function () {
describe("markdown messages should round-trip if they contain", function () {
test.each([
it.each([
["newlines", "hello\nworld"],
["pills", "text message for @room"],
["pills with interesting characters in mxid", "text message for @alice\\\\\\_\\]#>&:hs.example.com"],
@@ -77,7 +80,7 @@ describe("editor/roundtrip", function () {
expect(await roundTripMarkdown(markdown)).toEqual(markdown);
});
test.skip.each([
it.skip.each([
// Removes trailing spaces
["a code block followed by newlines", "```\nfoo(bar).baz();\n\n3\n```\n\n"],
// Adds a space after the code block
@@ -115,7 +118,7 @@ describe("editor/roundtrip", function () {
});
describe("HTML messages should round-trip if they contain", function () {
test.each([
it.each([
["backslashes", "C:\\Program Files"],
[
"nested blockquotes",
@@ -140,7 +143,7 @@ describe("editor/roundtrip", function () {
expect(await roundTripHtml(html)).toEqual(html);
});
test.skip.each([
it.skip.each([
// Strips out the pill - maybe needs some user lookup to work?
["user pills", '<a href="https://matrix.to/#/@alice:hs.tld">Alice</a>'],
// Appends a slash to the URL
@@ -6,14 +6,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { mocked } from "jest-mock";
// @vitest-environment happy-dom
import EditorModel from "../../../src/editor/model";
import { htmlSerializeFromMdIfNeeded, htmlSerializeIfNeeded } from "../../../src/editor/serialize";
import { createPartCreator } from "./mock";
import { type IConfigOptions } from "../../../src/IConfigOptions";
import SettingsStore from "../../../src/settings/SettingsStore";
import SdkConfig from "../../../src/SdkConfig";
import { vi, describe, it, expect, beforeEach, afterEach } from "vitest";
import EditorModel from "./model";
import { htmlSerializeFromMdIfNeeded, htmlSerializeIfNeeded } from "./serialize";
import { createPartCreator } from "./__mocks__";
import { type IConfigOptions } from "../IConfigOptions";
import SettingsStore from "../settings/SettingsStore";
import SdkConfig from "../SdkConfig";
describe("editor/serialize", function () {
describe("with markdown", function () {
@@ -109,7 +111,7 @@ describe("editor/serialize", function () {
describe("with permalink_prefix set", function () {
const sdkConfigGet = SdkConfig.get;
beforeEach(() => {
jest.spyOn(SdkConfig, "get").mockImplementation((key: keyof IConfigOptions, altCaseName?: string) => {
vi.spyOn(SdkConfig, "get").mockImplementation((key: keyof IConfigOptions, altCaseName?: string) => {
if (key === "permalink_prefix") {
return "https://element.fs.tld";
} else return sdkConfigGet(key, altCaseName);
@@ -129,7 +131,7 @@ describe("editor/serialize", function () {
expect(html).toBe('<a href="https://matrix.to/#/#room:hs.tld">#room:hs.tld</a>');
});
afterEach(() => {
mocked(SdkConfig.get).mockRestore();
vi.mocked(SdkConfig.get).mockRestore();
});
});
});
@@ -171,7 +173,7 @@ describe("editor/serialize", function () {
describe("feature_latex_maths", () => {
beforeEach(() => {
jest.spyOn(SettingsStore, "getValue").mockImplementation((feature) => feature === "feature_latex_maths");
vi.spyOn(SettingsStore, "getValue").mockImplementation((feature) => feature === "feature_latex_maths");
});
it("should support inline katex", () => {
@@ -5,7 +5,9 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { translateLegacyEmojiData, mergeEmojiData } from "../../../../../src/emojipicker/recent.ts";
import { describe, it, expect } from "vitest";
import { translateLegacyEmojiData, mergeEmojiData } from "./recent.ts";
describe("recent", () => {
describe("translateLegacyEmojiData", () => {
@@ -6,13 +6,16 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { renderHook } from "jest-matrix-react";
// @vitest-environment happy-dom
import { useDebouncedCallback } from "../../../src/hooks/spotlight/useDebouncedCallback";
import { vi, describe, it, expect, beforeAll, afterAll } from "vitest";
import { renderHook } from "test-utils-rtl";
import { useDebouncedCallback } from "./useDebouncedCallback";
describe("useDebouncedCallback", () => {
beforeAll(() => jest.useFakeTimers());
afterAll(() => jest.useRealTimers());
beforeAll(() => vi.useFakeTimers());
afterAll(() => vi.useRealTimers());
function render(enabled: boolean, callback: (...params: any[]) => void, params: any[]) {
return renderHook(({ enabled, callback, params }) => useDebouncedCallback(enabled, callback, params), {
@@ -27,15 +30,15 @@ describe("useDebouncedCallback", () => {
it("should be able to handle empty parameters", async () => {
// When
const params: any[] = [];
const callback = jest.fn();
const callback = vi.fn();
render(true, callback, params);
jest.advanceTimersByTime(1);
vi.advanceTimersByTime(1);
// Then
expect(callback).toHaveBeenCalledTimes(0);
// When
jest.advanceTimersByTime(500);
vi.advanceTimersByTime(500);
// Then
expect(callback).toHaveBeenCalledTimes(1);
@@ -44,9 +47,9 @@ describe("useDebouncedCallback", () => {
it("should call the callback with the parameters", async () => {
// When
const params = ["USER NAME"];
const callback = jest.fn();
const callback = vi.fn();
render(true, callback, params);
jest.advanceTimersByTime(500);
vi.advanceTimersByTime(500);
// Then
expect(callback).toHaveBeenCalledTimes(1);
@@ -56,12 +59,12 @@ describe("useDebouncedCallback", () => {
it("should call the callback with the parameters when parameters change during the timeout", async () => {
// When
const params = ["USER NAME"];
const callback = jest.fn();
const callback = vi.fn();
const { rerender } = render(true, callback, []);
jest.advanceTimersByTime(1);
vi.advanceTimersByTime(1);
rerender({ enabled: true, callback, params });
jest.advanceTimersByTime(500);
vi.advanceTimersByTime(500);
// Then
expect(callback).toHaveBeenCalledTimes(1);
@@ -71,12 +74,12 @@ describe("useDebouncedCallback", () => {
it("should handle multiple parameters", async () => {
// When
const params = [4, 8, 15, 16, 23, 42];
const callback = jest.fn();
const callback = vi.fn();
const { rerender } = render(true, callback, []);
jest.advanceTimersByTime(1);
vi.advanceTimersByTime(1);
rerender({ enabled: true, callback, params });
jest.advanceTimersByTime(500);
vi.advanceTimersByTime(500);
// Then
expect(callback).toHaveBeenCalledTimes(1);
@@ -100,17 +103,17 @@ describe("useDebouncedCallback", () => {
"USER NAM",
"USER NAME",
];
const callback = jest.fn();
const callback = vi.fn();
const { rerender } = render(true, callback, []);
jest.advanceTimersByTime(1);
vi.advanceTimersByTime(1);
for (const query of queries) {
rerender({ enabled: true, callback, params: [query] });
jest.advanceTimersByTime(50);
vi.advanceTimersByTime(50);
}
jest.advanceTimersByTime(500);
vi.advanceTimersByTime(500);
// Then
const query = queries[queries.length - 1];
@@ -135,16 +138,16 @@ describe("useDebouncedCallback", () => {
"USER NAM",
"USER NAME",
];
const callback = jest.fn();
const callback = vi.fn();
const { rerender } = render(true, callback, []);
jest.advanceTimersByTime(1);
vi.advanceTimersByTime(1);
for (const query of queries) {
rerender({ enabled: true, callback, params: [query] });
jest.advanceTimersByTime(200);
vi.advanceTimersByTime(200);
}
jest.advanceTimersByTime(500);
vi.advanceTimersByTime(500);
// Then
const query = queries[queries.length - 1];
@@ -169,16 +172,16 @@ describe("useDebouncedCallback", () => {
"USER NAM",
"USER NAME",
];
const callback = jest.fn();
const callback = vi.fn();
const { rerender } = render(false, callback, []);
jest.advanceTimersByTime(1);
vi.advanceTimersByTime(1);
for (const query of queries) {
rerender({ enabled: false, callback, params: [query] });
jest.advanceTimersByTime(200);
vi.advanceTimersByTime(200);
}
jest.advanceTimersByTime(500);
vi.advanceTimersByTime(500);
// Then
expect(callback).toHaveBeenCalledTimes(0);
@@ -6,14 +6,17 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { renderHook, type RenderHookResult } from "jest-matrix-react";
// @vitest-environment happy-dom
import { useLatestResult } from "../../../src/hooks/useLatestResult";
import { vi, describe, it, expect, beforeEach } from "vitest";
import { renderHook, type RenderHookResult } from "test-utils-rtl";
import { useLatestResult } from "./useLatestResult";
// All tests use fake timers throughout, comments will show the elapsed time in ms
jest.useFakeTimers();
vi.useFakeTimers();
const mockSetter = jest.fn();
const mockSetter = vi.fn();
beforeEach(() => {
mockSetter.mockClear();
@@ -39,7 +42,7 @@ describe("renderhook tests", () => {
expect(mockSetter).not.toHaveBeenCalled();
// advance timer until the timeout elapses, check we have called the setter
jest.advanceTimersToNextTimer();
vi.advanceTimersToNextTimer();
expect(mockSetter).toHaveBeenCalledTimes(1);
expect(mockSetter).toHaveBeenLastCalledWith(query.result);
});
@@ -54,13 +57,13 @@ describe("renderhook tests", () => {
simulateRequest(hookResult, fastQuery);
// advance to fastQuery response, check the setter call
jest.advanceTimersToNextTimer();
vi.advanceTimersToNextTimer();
expect(mockSetter).toHaveBeenCalledTimes(1);
expect(mockSetter).toHaveBeenLastCalledWith(fastQuery.result);
// advance time to slowQuery response, check the setter has _not_ been
// called again and that the result is still from the fast query
jest.advanceTimersToNextTimer();
vi.advanceTimersToNextTimer();
expect(mockSetter).toHaveBeenCalledTimes(1);
expect(mockSetter).toHaveBeenLastCalledWith(fastQuery.result);
});
@@ -75,23 +78,23 @@ describe("renderhook tests", () => {
// ELAPSED: 0ms, no queries sent
simulateRequest(hookResult, query1);
jest.advanceTimersByTime(100);
vi.advanceTimersByTime(100);
// ELAPSED: 100ms, query1 sent, no responses
expect(mockSetter).not.toHaveBeenCalled();
simulateRequest(hookResult, query2);
jest.advanceTimersByTime(70);
vi.advanceTimersByTime(70);
// ELAPSED: 170ms, query1 and query2 sent, no responses
expect(mockSetter).not.toHaveBeenCalled();
simulateRequest(hookResult, query3);
jest.advanceTimersByTime(70);
vi.advanceTimersByTime(70);
// ELAPSED: 240ms, all queries sent, responses for query1 and query2
expect(mockSetter).not.toHaveBeenCalled();
// ELAPSED: 360ms, all queries sent, all queries have responses
jest.advanceTimersByTime(120);
vi.advanceTimersByTime(120);
expect(mockSetter).toHaveBeenLastCalledWith(query3.result);
});
@@ -104,17 +107,17 @@ describe("renderhook tests", () => {
// ELAPSED: 0ms, no queries sent
simulateRequest(hookResult, query1);
jest.advanceTimersByTime(5);
vi.advanceTimersByTime(5);
// ELAPSED: 5ms, query1 sent, response from query1
expect(mockSetter).toHaveBeenCalledTimes(1);
expect(mockSetter).toHaveBeenLastCalledWith(query1.result);
simulateRequest(hookResult, query2);
jest.advanceTimersByTime(5);
vi.advanceTimersByTime(5);
// ELAPSED: 10ms, query1 and query2 sent, response from query1
simulateRequest(hookResult, query3);
jest.advanceTimersByTime(5);
vi.advanceTimersByTime(5);
// ELAPSED: 15ms, all queries sent, responses from query1 and query3
expect(mockSetter).toHaveBeenCalledTimes(2);
@@ -122,7 +125,7 @@ describe("renderhook tests", () => {
// ELAPSED: 65ms, all queries sent, all queries have responses
// so check that the result is still from query3, not query2
jest.advanceTimersByTime(50);
vi.advanceTimersByTime(50);
expect(mockSetter).toHaveBeenLastCalledWith(query3.result);
});
});
@@ -6,10 +6,13 @@ SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Com
Please see LICENSE files in the repository root for full details.
*/
import { renderHook, act } from "jest-matrix-react";
// @vitest-environment happy-dom
import UIStore, { UI_EVENTS } from "../../../src/stores/UIStore";
import { useWindowWidth } from "../../../src/hooks/useWindowWidth";
import { describe, it, expect, beforeEach } from "vitest";
import { renderHook, act } from "test-utils-rtl";
import UIStore, { UI_EVENTS } from "../stores/UIStore";
import { useWindowWidth } from "./useWindowWidth";
describe("useWindowWidth", () => {
beforeEach(() => {
+1
View File
@@ -25,5 +25,6 @@ if (globalThis.window === undefined) {
// things try to access it before the beforeEach blocks run)
addEventListener: vi.fn(),
location: locationStub,
setTimeout: globalThis.setTimeout,
});
}
+3 -2
View File
@@ -13,6 +13,7 @@ import type AutocompleteWrapperModel from "../../../src/editor/autocomplete";
import { type Caret } from "../../../src/editor/caret";
import { type PillPart, type Part, PartCreator } from "../../../src/editor/parts";
import DocumentPosition from "../../../src/editor/position";
import { vi } from "../../setup/adapter.ts";
export class MockAutoComplete {
public _updateCallback;
@@ -68,8 +69,8 @@ export function createPartCreator(completions: PillPart[] = []) {
};
const room = new MockRoom() as unknown as Room;
const client = {
getRooms: jest.fn().mockReturnValue([]),
getRoom: jest.fn().mockReturnValue(null),
getRooms: vi.fn().mockReturnValue([]),
getRoom: vi.fn().mockReturnValue(null),
} as unknown as MatrixClient;
return new PartCreator(room, client, autoCompleteCreator);
}